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