xref: /haiku/src/add-ons/kernel/generic/scsi_periph/io.cpp (revision d12bb8b14803d030b4a8fba91131e4bb96c4f406)
1 /*
2  * Copyright 2004-2011, Haiku, Inc. All Rights Reserved.
3  * Copyright 2002-03, Thomas Kurschel. All rights reserved.
4  *
5  * Distributed under the terms of the MIT License.
6  */
7 
8 
9 //!	Everything doing the real input/output stuff.
10 
11 
12 #include "scsi_periph_int.h"
13 #include <scsi.h>
14 
15 #include <string.h>
16 #include <stdlib.h>
17 
18 #include <AutoDeleter.h>
19 #include <kernel.h>
20 #include <syscall_restart.h>
21 
22 
23 static status_t
24 inquiry(scsi_periph_device_info *device, scsi_inquiry *inquiry)
25 {
26 	const scsi_res_inquiry *device_inquiry = NULL;
27 	size_t inquiryLength;
28 
29 	if (gDeviceManager->get_attr_raw(device->node, SCSI_DEVICE_INQUIRY_ITEM,
30 			(const void **)&device_inquiry, &inquiryLength, true) != B_OK)
31 		return B_ERROR;
32 
33 	if (IS_USER_ADDRESS(inquiry)) {
34 		if (user_memcpy(&inquiry, device_inquiry,
35 			min_c(inquiryLength, sizeof(scsi_inquiry))) != B_OK) {
36 			return B_BAD_ADDRESS;
37 		}
38 	} else if (is_called_via_syscall()) {
39 		return B_BAD_ADDRESS;
40 	} else {
41 		memcpy(&inquiry, device_inquiry,
42 			min_c(inquiryLength, sizeof(scsi_inquiry)));
43 	}
44 	return B_OK;
45 }
46 
47 
48 static status_t
49 vpd_page_inquiry(scsi_periph_device_info *device, uint8 page, void* data,
50 	uint16 length)
51 {
52 	SHOW_FLOW0(0, "");
53 
54 	scsi_ccb* ccb = device->scsi->alloc_ccb(device->scsi_device);
55 	if (ccb == NULL)
56 		return B_NO_MEMORY;
57 
58 	scsi_cmd_inquiry *cmd = (scsi_cmd_inquiry *)ccb->cdb;
59 	memset(cmd, 0, sizeof(scsi_cmd_inquiry));
60 	cmd->opcode = SCSI_OP_INQUIRY;
61 	cmd->lun = ccb->target_lun;
62 	cmd->evpd = 1;
63 	cmd->page_code = page;
64 	cmd->allocation_length = length;
65 
66 	ccb->flags = SCSI_DIR_IN;
67 	ccb->cdb_length = sizeof(scsi_cmd_inquiry);
68 
69 	ccb->sort = -1;
70 	ccb->timeout = device->std_timeout;
71 
72 	ccb->data = (uint8*)data;
73 	ccb->sg_list = NULL;
74 	ccb->data_length = length;
75 
76 	status_t status = periph_safe_exec(device, ccb);
77 
78 	device->scsi->free_ccb(ccb);
79 
80 	return status;
81 }
82 
83 
84 status_t
85 vpd_page_get(scsi_periph_device_info *device, uint8 page, void* data,
86 	uint16 length)
87 {
88 	SHOW_FLOW0(0, "");
89 
90 	status_t status = vpd_page_inquiry(device, 0, data, length);
91 	if (status != B_OK)
92 		return status; // or B_BAD_VALUE
93 
94 	if (page == 0)
95 		return B_OK;
96 
97 	scsi_page_list *list_data = (scsi_page_list*)data;
98 	int page_length = min_c(list_data->page_length, length -
99 		offsetof(scsi_page_list, pages));
100 	for (int i = 0; i < page_length; i++) {
101 		if (list_data->pages[i] == page)
102 			return vpd_page_inquiry(device, page, data, length);
103 	}
104 
105 	// TODO buffer might be not big enough
106 
107 	return B_BAD_VALUE;
108 }
109 
110 
111 
112 static status_t
113 prevent_allow(scsi_periph_device_info *device, bool prevent)
114 {
115 	scsi_cmd_prevent_allow cmd;
116 
117 	SHOW_FLOW0(0, "");
118 
119 	memset(&cmd, 0, sizeof(cmd));
120 	cmd.opcode = SCSI_OP_PREVENT_ALLOW;
121 	cmd.prevent = prevent;
122 
123 	return periph_simple_exec(device, (uint8 *)&cmd, sizeof(cmd), NULL, 0,
124 		SCSI_DIR_NONE);
125 }
126 
127 
128 /*! Keep this in sync with scsi_raw driver!!! */
129 static status_t
130 raw_command(scsi_periph_device_info *device, raw_device_command *cmd)
131 {
132 	scsi_ccb *request;
133 
134 	SHOW_FLOW0(0, "");
135 
136 	request = device->scsi->alloc_ccb(device->scsi_device);
137 	if (request == NULL)
138 		return B_NO_MEMORY;
139 
140 	request->flags = 0;
141 	bool dataIn = (cmd->flags & B_RAW_DEVICE_DATA_IN) != 0;
142 	bool dataOut = !dataIn && cmd->data_length != 0;
143 
144 	void* buffer = cmd->data;
145 	MemoryDeleter bufferDeleter;
146 	if (buffer != NULL) {
147 		if (IS_USER_ADDRESS(buffer)) {
148 			buffer = malloc(cmd->data_length);
149 			if (buffer == NULL) {
150 				device->scsi->free_ccb(request);
151 				return B_NO_MEMORY;
152 			}
153 			bufferDeleter.SetTo(buffer);
154 			if (dataOut
155 				&& user_memcpy(buffer, cmd->data, cmd->data_length) != B_OK) {
156 				goto bad_address;
157 			}
158 		} else {
159 			if (is_called_via_syscall())
160 				goto bad_address;
161 		}
162 	}
163 
164 	if (dataIn)
165 		request->flags |= SCSI_DIR_IN;
166 	else if (dataOut)
167 		request->flags |= SCSI_DIR_OUT;
168 	else
169 		request->flags |= SCSI_DIR_NONE;
170 
171 	request->data = (uint8*)buffer;
172 	request->sg_list = NULL;
173 	request->data_length = cmd->data_length;
174 	request->sort = -1;
175 	request->timeout = cmd->timeout;
176 
177 	memcpy(request->cdb, cmd->command, SCSI_MAX_CDB_SIZE);
178 	request->cdb_length = cmd->command_length;
179 
180 	device->scsi->sync_io(request);
181 
182 	// TBD: should we call standard error handler here, or may the
183 	// actions done there (like starting the unit) confuse the application?
184 
185 	cmd->cam_status = request->subsys_status;
186 	cmd->scsi_status = request->device_status;
187 
188 	if ((request->subsys_status & SCSI_AUTOSNS_VALID) != 0
189 		&& cmd->sense_data != NULL) {
190 		size_t length = min_c(cmd->sense_data_length,
191 			(size_t)SCSI_MAX_SENSE_SIZE - request->sense_resid);
192 		if (IS_USER_ADDRESS(cmd->sense_data)) {
193 			if (user_memcpy(cmd->sense_data, request->sense, length) != B_OK)
194 				goto bad_address;
195 		} else if (is_called_via_syscall()) {
196 			goto bad_address;
197 		} else {
198 			memcpy(cmd->sense_data, request->sense, length);
199 		}
200 	}
201 
202 	if (dataIn && user_memcpy(cmd->data, buffer, cmd->data_length) != B_OK)
203 		goto bad_address;
204 
205 	if ((cmd->flags & B_RAW_DEVICE_REPORT_RESIDUAL) != 0) {
206 		// this is a bit strange, see Be's sample code where I pinched this from;
207 		// normally, residual means "number of unused bytes left"
208 		// but here, we have to return "number of used bytes", which is the opposite
209 		cmd->data_length = cmd->data_length - request->data_resid;
210 		cmd->sense_data_length = SCSI_MAX_SENSE_SIZE - request->sense_resid;
211 	}
212 
213 	device->scsi->free_ccb(request);
214 
215 	return B_OK;
216 
217 bad_address:
218 	device->scsi->free_ccb(request);
219 
220 	return B_BAD_ADDRESS;
221 }
222 
223 
224 /*! Universal read/write function */
225 static status_t
226 read_write(scsi_periph_device_info *device, scsi_ccb *request,
227 	io_operation *operation, uint64 offset, size_t originalNumBlocks,
228 	physical_entry* vecs, size_t vecCount, bool isWrite,
229 	size_t* _bytesTransferred)
230 {
231 	uint32 blockSize = device->block_size;
232 	size_t numBlocks = originalNumBlocks;
233 	uint32 pos = offset;
234 	err_res res;
235 	int retries = 0;
236 
237 	do {
238 		size_t numBytes;
239 		bool isReadWrite10 = false;
240 
241 		request->flags = isWrite ? SCSI_DIR_OUT : SCSI_DIR_IN;
242 
243 		// io_operations are generated by a DMAResource and thus contain DMA
244 		// safe physical vectors
245 		if (operation != NULL)
246 			request->flags |= SCSI_DMA_SAFE;
247 
248 		// make sure we avoid 10 byte commands if they aren't supported
249 		if (!device->rw10_enabled || device->preferred_ccb_size == 6) {
250 			// restricting transfer is OK - the block manager will
251 			// take care of transferring the rest
252 			if (numBlocks > 0x100)
253 				numBlocks = 0x100;
254 
255 			// no way to break the 21 bit address limit
256 			if (offset > 0x200000)
257 				return B_BAD_VALUE;
258 
259 			// don't allow transfer cross the 24 bit address limit
260 			// (I'm not sure whether this is allowed, but this way we
261 			// are sure to not ask for trouble)
262 			if (offset < 0x100000)
263 				numBlocks = min_c(numBlocks, 0x100000 - pos);
264 		}
265 
266 		numBytes = numBlocks * blockSize;
267 		if (numBlocks != originalNumBlocks)
268 			panic("I/O operation would need to be cut.");
269 
270 		request->data = NULL;
271 		request->sg_list = vecs;
272 		request->data_length = numBytes;
273 		request->sg_count = vecCount;
274 		request->io_operation = operation;
275 		request->sort = pos;
276 		request->timeout = device->std_timeout;
277 		// see whether daemon instructed us to post an ordered command;
278 		// reset flag after read
279 		SHOW_FLOW(3, "flag=%x, next_tag=%x, ordered: %s",
280 			(int)request->flags, (int)device->next_tag_action,
281 			(request->flags & SCSI_ORDERED_QTAG) != 0 ? "yes" : "no");
282 
283 		// use shortest commands whenever possible
284 		if (offset + numBlocks < 0x200000LL && numBlocks <= 0x100) {
285 			scsi_cmd_rw_6 *cmd = (scsi_cmd_rw_6 *)request->cdb;
286 
287 			isReadWrite10 = false;
288 
289 			memset(cmd, 0, sizeof(*cmd));
290 			cmd->opcode = isWrite ? SCSI_OP_WRITE_6 : SCSI_OP_READ_6;
291 			cmd->high_lba = (pos >> 16) & 0x1f;
292 			cmd->mid_lba = (pos >> 8) & 0xff;
293 			cmd->low_lba = pos & 0xff;
294 			cmd->length = numBlocks;
295 
296 			request->cdb_length = sizeof(*cmd);
297 		} else if (offset + numBlocks < 0x100000000LL && numBlocks <= 0x10000) {
298 			scsi_cmd_rw_10 *cmd = (scsi_cmd_rw_10 *)request->cdb;
299 
300 			isReadWrite10 = true;
301 
302 			memset(cmd, 0, sizeof(*cmd));
303 			cmd->opcode = isWrite ? SCSI_OP_WRITE_10 : SCSI_OP_READ_10;
304 			cmd->relative_address = 0;
305 			cmd->force_unit_access = 0;
306 			cmd->disable_page_out = 0;
307 			cmd->lba = B_HOST_TO_BENDIAN_INT32(pos);
308 			cmd->length = B_HOST_TO_BENDIAN_INT16(numBlocks);
309 
310 			request->cdb_length = sizeof(*cmd);
311 		} else if (offset + numBlocks < 0x100000000LL && numBlocks <= 0x10000000) {
312 			scsi_cmd_rw_12 *cmd = (scsi_cmd_rw_12 *)request->cdb;
313 
314 			memset(cmd, 0, sizeof(*cmd));
315 			cmd->opcode = isWrite ? SCSI_OP_WRITE_12 : SCSI_OP_READ_12;
316 			cmd->relative_address = 0;
317 			cmd->force_unit_access = 0;
318 			cmd->disable_page_out = 0;
319 			cmd->lba = B_HOST_TO_BENDIAN_INT32(pos);
320 			cmd->length = B_HOST_TO_BENDIAN_INT32(numBlocks);
321 
322 			request->cdb_length = sizeof(*cmd);
323 		} else {
324 			scsi_cmd_rw_16 *cmd = (scsi_cmd_rw_16 *)request->cdb;
325 
326 			memset(cmd, 0, sizeof(*cmd));
327 			cmd->opcode = isWrite ? SCSI_OP_WRITE_16 : SCSI_OP_READ_16;
328 			cmd->force_unit_access_non_volatile = 0;
329 			cmd->force_unit_access = 0;
330 			cmd->disable_page_out = 0;
331 			cmd->lba = B_HOST_TO_BENDIAN_INT64(offset);
332 			cmd->length = B_HOST_TO_BENDIAN_INT32(numBlocks);
333 
334 			request->cdb_length = sizeof(*cmd);
335 		}
336 
337 		// TODO: last chance to detect errors that occured during concurrent accesses
338 		//status_t status = handle->pending_error;
339 		//if (status != B_OK)
340 		//	return status;
341 
342 		device->scsi->async_io(request);
343 
344 		acquire_sem(request->completion_sem);
345 
346 		// ask generic peripheral layer what to do now
347 		res = periph_check_error(device, request);
348 
349 		// TODO: bytes might have been transferred even in the error case!
350 		switch (res.action) {
351 			case err_act_ok:
352 				*_bytesTransferred = numBytes - request->data_resid;
353 				break;
354 
355 			case err_act_start:
356 				res = periph_send_start_stop(device, request, 1,
357 					device->removable);
358 				if (res.action == err_act_ok)
359 					res.action = err_act_retry;
360 				break;
361 
362 			case err_act_invalid_req:
363 				// if this was a 10 byte command, the device probably doesn't
364 				// support them, so disable them and retry
365 				if (isReadWrite10) {
366 					atomic_and(&device->rw10_enabled, 0);
367 					res.action = err_act_retry;
368 				} else
369 					res.action = err_act_fail;
370 				break;
371 		}
372 	} while ((res.action == err_act_retry && retries++ < 3)
373 		|| (res.action == err_act_many_retries && retries++ < 30));
374 
375 	// peripheral layer only created "read" error, so we have to
376 	// map them to "write" errors if this was a write request
377 	if (res.error_code == B_DEV_READ_ERROR && isWrite)
378 		return B_DEV_WRITE_ERROR;
379 
380 	return res.error_code;
381 }
382 
383 
384 // #pragma mark - public functions
385 
386 
387 status_t
388 periph_ioctl(scsi_periph_handle_info *handle, int op, void *buffer,
389 	size_t length)
390 {
391 	switch (op) {
392 		case B_GET_MEDIA_STATUS:
393 		{
394 			status_t status = B_OK;
395 			if (length < sizeof(status))
396 				return B_BUFFER_OVERFLOW;
397 
398 			if (handle->device->removable)
399 				status = periph_get_media_status(handle);
400 
401 			SHOW_FLOW(2, "%s", strerror(status));
402 
403 			if (IS_USER_ADDRESS(buffer)) {
404 				if (user_memcpy(buffer, &status, sizeof(status_t)) != B_OK)
405 					return B_BAD_ADDRESS;
406 			} else if (is_called_via_syscall()) {
407 				return B_BAD_ADDRESS;
408 			} else
409 				*(status_t *)buffer = status;
410 			return B_OK;
411 		}
412 
413 		case B_GET_DEVICE_NAME:
414 		{
415 			// TODO: this should be written as an attribute to the node
416 			// Try driver further up first
417 			if (handle->device->scsi->ioctl != NULL) {
418 				status_t status = handle->device->scsi->ioctl(
419 					handle->device->scsi_device, op, buffer, length);
420 				if (status == B_OK)
421 					return B_OK;
422 			}
423 
424 			// If that fails, get SCSI vendor/product
425 			const char* vendor;
426 			if (gDeviceManager->get_attr_string(handle->device->node,
427 					SCSI_DEVICE_VENDOR_ITEM, &vendor, true) == B_OK) {
428 				char name[B_FILE_NAME_LENGTH];
429 				strlcpy(name, vendor, sizeof(name));
430 
431 				const char* product;
432 				if (gDeviceManager->get_attr_string(handle->device->node,
433 						SCSI_DEVICE_PRODUCT_ITEM, &product, true) == B_OK) {
434 					strlcat(name, " ", sizeof(name));
435 					strlcat(name, product, sizeof(name));
436 				}
437 
438 				return user_strlcpy((char*)buffer, name, length) >= 0
439 					? B_OK : B_BAD_ADDRESS;
440 			}
441 			return B_ERROR;
442 		}
443 
444 		case B_SCSI_INQUIRY:
445 			if (length < sizeof(scsi_inquiry))
446 				return B_BUFFER_OVERFLOW;
447 			return inquiry(handle->device, (scsi_inquiry *)buffer);
448 
449 		case B_SCSI_PREVENT_ALLOW:
450 			bool result;
451 			if (length < sizeof(result))
452 				return B_BUFFER_OVERFLOW;
453 			if (IS_USER_ADDRESS(buffer)) {
454 				if (user_memcpy(&result, buffer, sizeof(result)) != B_OK)
455 					return B_BAD_ADDRESS;
456 			} else if (is_called_via_syscall()) {
457 				return B_BAD_ADDRESS;
458 			} else
459 				result = *(bool*)buffer;
460 
461 			return prevent_allow(handle->device, result);
462 
463 		case B_RAW_DEVICE_COMMAND:
464 		{
465 			raw_device_command command;
466 			raw_device_command* commandBuffer;
467 			if (length < sizeof(command))
468 				return B_BUFFER_OVERFLOW;
469 			if (IS_USER_ADDRESS(buffer)) {
470 				if (user_memcpy(&command, buffer, sizeof(command)) != B_OK)
471 					return B_BAD_ADDRESS;
472 				commandBuffer = &command;
473 			} else if (is_called_via_syscall()) {
474 				return B_BAD_ADDRESS;
475 			} else {
476 				commandBuffer = (raw_device_command*)buffer;
477 			}
478 			status_t status = raw_command(handle->device, commandBuffer);
479 			if (status == B_OK && commandBuffer == &command) {
480 				if (user_memcpy(buffer, &command, sizeof(command)) != B_OK)
481 					status = B_BAD_ADDRESS;
482 			}
483 			return status;
484 		}
485 		default:
486 			if (handle->device->scsi->ioctl != NULL) {
487 				return handle->device->scsi->ioctl(handle->device->scsi_device,
488 					op, buffer, length);
489 			}
490 
491 			SHOW_ERROR(4, "Unknown ioctl: %x", op);
492 			return B_DEV_INVALID_IOCTL;
493 	}
494 }
495 
496 
497 /*!	Kernel daemon - once in a minute, it sets a flag so that the next command
498 	is executed ordered; this way, we avoid starvation of SCSI commands inside
499 	the SCSI queuing system - the ordered command waits for all previous
500 	commands and thus no command can starve longer then a minute
501 */
502 void
503 periph_sync_queue_daemon(void *arg, int iteration)
504 {
505 	scsi_periph_device_info *device = (scsi_periph_device_info *)arg;
506 
507 	SHOW_FLOW0(3, "Setting ordered flag for next R/W access");
508 	atomic_or(&device->next_tag_action, SCSI_ORDERED_QTAG);
509 }
510 
511 
512 status_t
513 periph_read_write(scsi_periph_device_info *device, scsi_ccb *request,
514 	uint64 offset, size_t numBlocks, physical_entry* vecs, size_t vecCount,
515 	bool isWrite, size_t* _bytesTransferred)
516 {
517 	return read_write(device, request, NULL, offset, numBlocks, vecs, vecCount,
518 		isWrite, _bytesTransferred);
519 }
520 
521 
522 status_t
523 periph_io(scsi_periph_device_info *device, io_operation *operation,
524 	size_t* _bytesTransferred)
525 {
526 	const uint32 blockSize = device->block_size;
527 	if (blockSize == 0)
528 		return B_BAD_VALUE;
529 
530 	// don't test rw10_enabled restrictions - this flag may get changed
531 	scsi_ccb *request = device->scsi->alloc_ccb(device->scsi_device);
532 	if (request == NULL)
533 		return B_NO_MEMORY;
534 
535 	status_t status = read_write(device, request, operation,
536 		operation->Offset() / blockSize, operation->Length() / blockSize,
537 		(physical_entry *)operation->Vecs(), operation->VecCount(),
538 		operation->IsWrite(), _bytesTransferred);
539 
540 	device->scsi->free_ccb(request);
541 	return status;
542 }
543 
544