xref: /illumos-gate/usr/src/uts/common/io/scsi/impl/scsi_subr.c (revision 9a7670889e9c36ec355371e6b02f2d9084f040dc)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/scsi/scsi.h>
30 #include <sys/file.h>
31 
32 /*
33  * Utility SCSI routines
34  */
35 
36 /*
37  * Polling support routines
38  */
39 
40 extern uintptr_t scsi_callback_id;
41 extern uchar_t	scsi_cdb_size[];
42 
43 /*
44  * Common buffer for scsi_log
45  */
46 
47 extern kmutex_t scsi_log_mutex;
48 static char scsi_log_buffer[MAXPATHLEN + 1];
49 
50 
51 #define	A_TO_TRAN(ap)	(ap->a_hba_tran)
52 #define	P_TO_TRAN(pkt)	((pkt)->pkt_address.a_hba_tran)
53 #define	P_TO_ADDR(pkt)	(&((pkt)->pkt_address))
54 
55 #define	CSEC		10000			/* usecs */
56 #define	SEC_TO_CSEC	(1000000/CSEC)
57 
58 extern ddi_dma_attr_t scsi_alloc_attr;
59 
60 /*PRINTFLIKE4*/
61 static void impl_scsi_log(dev_info_t *dev, char *label, uint_t level,
62     const char *fmt, ...) __KPRINTFLIKE(4);
63 /*PRINTFLIKE4*/
64 static void v_scsi_log(dev_info_t *dev, char *label, uint_t level,
65     const char *fmt, va_list ap) __KVPRINTFLIKE(4);
66 
67 static int
68 scsi_get_next_descr(uint8_t *sdsp,
69     int sense_buf_len, struct scsi_descr_template **descrpp);
70 
71 #define	DESCR_GOOD	0
72 #define	DESCR_PARTIAL	1
73 #define	DESCR_END	2
74 
75 static int
76 scsi_validate_descr(struct scsi_descr_sense_hdr *sdsp,
77     int valid_sense_length, struct scsi_descr_template *descrp);
78 
79 int
80 scsi_poll(struct scsi_pkt *pkt)
81 {
82 	register int busy_count, rval = -1, savef;
83 	long savet;
84 	void (*savec)();
85 	extern int do_polled_io;
86 
87 	/*
88 	 * save old flags..
89 	 */
90 	savef = pkt->pkt_flags;
91 	savec = pkt->pkt_comp;
92 	savet = pkt->pkt_time;
93 
94 	pkt->pkt_flags |= FLAG_NOINTR;
95 
96 	/*
97 	 * XXX there is nothing in the SCSA spec that states that we should not
98 	 * do a callback for polled cmds; however, removing this will break sd
99 	 * and probably other target drivers
100 	 */
101 	pkt->pkt_comp = 0;
102 
103 	/*
104 	 * we don't like a polled command without timeout.
105 	 * 60 seconds seems long enough.
106 	 */
107 	if (pkt->pkt_time == 0)
108 		pkt->pkt_time = SCSI_POLL_TIMEOUT;
109 
110 	/*
111 	 * Send polled cmd.
112 	 *
113 	 * We do some error recovery for various errors.  Tran_busy,
114 	 * queue full, and non-dispatched commands are retried every 10 msec.
115 	 * as they are typically transient failures.  Busy status is retried
116 	 * every second as this status takes a while to change.
117 	 */
118 	for (busy_count = 0; busy_count < (pkt->pkt_time * SEC_TO_CSEC);
119 		busy_count++) {
120 		int rc;
121 		int poll_delay;
122 
123 		/*
124 		 * Initialize pkt status variables.
125 		 */
126 		*pkt->pkt_scbp = pkt->pkt_reason = pkt->pkt_state = 0;
127 
128 		if ((rc = scsi_transport(pkt)) != TRAN_ACCEPT) {
129 			if (rc != TRAN_BUSY) {
130 				/* Transport failed - give up. */
131 				break;
132 			} else {
133 				/* Transport busy - try again. */
134 				poll_delay = 1 *CSEC;		/* 10 msec. */
135 			}
136 		} else {
137 			/*
138 			 * Transport accepted - check pkt status.
139 			 */
140 			rc = (*pkt->pkt_scbp) & STATUS_MASK;
141 
142 			if (pkt->pkt_reason == CMD_CMPLT &&
143 			    rc == STATUS_GOOD) {
144 				/* No error - we're done */
145 				rval = 0;
146 				break;
147 
148 			} else if (pkt->pkt_reason == CMD_INCOMPLETE &&
149 			    pkt->pkt_state == 0) {
150 				/* Pkt not dispatched - try again. */
151 				poll_delay = 1 *CSEC;		/* 10 msec. */
152 
153 			} else if (pkt->pkt_reason == CMD_CMPLT &&
154 			    rc == STATUS_QFULL) {
155 				/* Queue full - try again. */
156 				poll_delay = 1 *CSEC;		/* 10 msec. */
157 
158 			} else if (pkt->pkt_reason == CMD_CMPLT &&
159 			    rc == STATUS_BUSY) {
160 				/* Busy - try again. */
161 				poll_delay = 100 *CSEC;		/* 1 sec. */
162 				busy_count += (SEC_TO_CSEC - 1);
163 
164 			} else {
165 				/* BAD status - give up. */
166 				break;
167 			}
168 		}
169 
170 		if ((curthread->t_flag & T_INTR_THREAD) == 0 &&
171 		    !do_polled_io) {
172 			delay(drv_usectohz(poll_delay));
173 		} else {
174 			/* we busy wait during cpr_dump or interrupt threads */
175 			drv_usecwait(poll_delay);
176 		}
177 	}
178 
179 	pkt->pkt_flags = savef;
180 	pkt->pkt_comp = savec;
181 	pkt->pkt_time = savet;
182 	return (rval);
183 }
184 
185 /*
186  * Command packaging routines.
187  *
188  * makecom_g*() are original routines and scsi_setup_cdb()
189  * is the new and preferred routine.
190  */
191 
192 /*
193  * These routines put LUN information in CDB byte 1 bits 7-5.
194  * This was required in SCSI-1. SCSI-2 allowed it but it preferred
195  * sending LUN information as part of IDENTIFY message.
196  * This is not allowed in SCSI-3.
197  */
198 
199 void
200 makecom_g0(struct scsi_pkt *pkt, struct scsi_device *devp,
201     int flag, int cmd, int addr, int cnt)
202 {
203 	MAKECOM_G0(pkt, devp, flag, cmd, addr, (uchar_t)cnt);
204 }
205 
206 void
207 makecom_g0_s(struct scsi_pkt *pkt, struct scsi_device *devp,
208     int flag, int cmd, int cnt, int fixbit)
209 {
210 	MAKECOM_G0_S(pkt, devp, flag, cmd, cnt, (uchar_t)fixbit);
211 }
212 
213 void
214 makecom_g1(struct scsi_pkt *pkt, struct scsi_device *devp,
215     int flag, int cmd, int addr, int cnt)
216 {
217 	MAKECOM_G1(pkt, devp, flag, cmd, addr, cnt);
218 }
219 
220 void
221 makecom_g5(struct scsi_pkt *pkt, struct scsi_device *devp,
222     int flag, int cmd, int addr, int cnt)
223 {
224 	MAKECOM_G5(pkt, devp, flag, cmd, addr, cnt);
225 }
226 
227 /*
228  * Following routine does not put LUN information in CDB.
229  * This interface must be used for SCSI-2 targets having
230  * more than 8 LUNs or a SCSI-3 target.
231  */
232 int
233 scsi_setup_cdb(union scsi_cdb *cdbp, uchar_t cmd, uint_t addr, uint_t cnt,
234     uint_t addtl_cdb_data)
235 {
236 	uint_t	addr_cnt;
237 
238 	cdbp->scc_cmd = cmd;
239 
240 	switch (CDB_GROUPID(cmd)) {
241 		case CDB_GROUPID_0:
242 			/*
243 			 * The following calculation is to take care of
244 			 * the fact that format of some 6 bytes tape
245 			 * command is different (compare 6 bytes disk and
246 			 * tape read commands).
247 			 */
248 			addr_cnt = (addr << 8) + cnt;
249 			addr = (addr_cnt & 0x1fffff00) >> 8;
250 			cnt = addr_cnt & 0xff;
251 			FORMG0ADDR(cdbp, addr);
252 			FORMG0COUNT(cdbp, cnt);
253 			break;
254 
255 		case CDB_GROUPID_1:
256 		case CDB_GROUPID_2:
257 			FORMG1ADDR(cdbp, addr);
258 			FORMG1COUNT(cdbp, cnt);
259 			break;
260 
261 		case CDB_GROUPID_4:
262 			FORMG4ADDR(cdbp, addr);
263 			FORMG4COUNT(cdbp, cnt);
264 			FORMG4ADDTL(cdbp, addtl_cdb_data);
265 			break;
266 
267 		case CDB_GROUPID_5:
268 			FORMG5ADDR(cdbp, addr);
269 			FORMG5COUNT(cdbp, cnt);
270 			break;
271 
272 		default:
273 			return (0);
274 	}
275 
276 	return (1);
277 }
278 
279 
280 /*
281  * Common iopbmap data area packet allocation routines
282  */
283 
284 struct scsi_pkt *
285 get_pktiopb(struct scsi_address *ap, caddr_t *datap, int cdblen, int statuslen,
286     int datalen, int readflag, int (*func)())
287 {
288 	scsi_hba_tran_t	*tran = A_TO_TRAN(ap);
289 	dev_info_t	*pdip = tran->tran_hba_dip;
290 	struct scsi_pkt	*pkt = NULL;
291 	struct buf	local;
292 	size_t		rlen;
293 
294 	if (!datap)
295 		return (pkt);
296 	*datap = (caddr_t)0;
297 	bzero((caddr_t)&local, sizeof (struct buf));
298 
299 	/*
300 	 * use i_ddi_mem_alloc() for now until we have an interface to allocate
301 	 * memory for DMA which doesn't require a DMA handle. ddi_iopb_alloc()
302 	 * is obsolete and we want more flexibility in controlling the DMA
303 	 * address constraints.
304 	 */
305 	if (i_ddi_mem_alloc(pdip, &scsi_alloc_attr, datalen,
306 	    ((func == SLEEP_FUNC) ? 1 : 0), 0, NULL, &local.b_un.b_addr, &rlen,
307 	    NULL) != DDI_SUCCESS) {
308 		return (pkt);
309 	}
310 	if (readflag)
311 		local.b_flags = B_READ;
312 	local.b_bcount = datalen;
313 	pkt = (*tran->tran_init_pkt) (ap, NULL, &local,
314 		cdblen, statuslen, 0, PKT_CONSISTENT,
315 		(func == SLEEP_FUNC) ? SLEEP_FUNC : NULL_FUNC,
316 		NULL);
317 	if (!pkt) {
318 		i_ddi_mem_free(local.b_un.b_addr, NULL);
319 		if (func != NULL_FUNC) {
320 			ddi_set_callback(func, NULL, &scsi_callback_id);
321 		}
322 	} else {
323 		*datap = local.b_un.b_addr;
324 	}
325 	return (pkt);
326 }
327 
328 /*
329  *  Equivalent deallocation wrapper
330  */
331 
332 void
333 free_pktiopb(struct scsi_pkt *pkt, caddr_t datap, int datalen)
334 {
335 	register struct scsi_address	*ap = P_TO_ADDR(pkt);
336 	register scsi_hba_tran_t	*tran = A_TO_TRAN(ap);
337 
338 	(*tran->tran_destroy_pkt)(ap, pkt);
339 	if (datap && datalen) {
340 		i_ddi_mem_free(datap, NULL);
341 	}
342 	if (scsi_callback_id != 0) {
343 		ddi_run_callback(&scsi_callback_id);
344 	}
345 }
346 
347 /*
348  * Common naming functions
349  */
350 
351 static char scsi_tmpname[64];
352 
353 char *
354 scsi_dname(int dtyp)
355 {
356 	static char *dnames[] = {
357 		"Direct Access",
358 		"Sequential Access",
359 		"Printer",
360 		"Processor",
361 		"Write-Once/Read-Many",
362 		"Read-Only Direct Access",
363 		"Scanner",
364 		"Optical",
365 		"Changer",
366 		"Communications",
367 		"Array Controller"
368 	};
369 
370 	if ((dtyp & DTYPE_MASK) <= DTYPE_COMM) {
371 		return (dnames[dtyp&DTYPE_MASK]);
372 	} else if (dtyp == DTYPE_NOTPRESENT) {
373 		return ("Not Present");
374 	}
375 	return ("<unknown device type>");
376 
377 }
378 
379 char *
380 scsi_rname(uchar_t reason)
381 {
382 	static char *rnames[] = {
383 		"cmplt",
384 		"incomplete",
385 		"dma_derr",
386 		"tran_err",
387 		"reset",
388 		"aborted",
389 		"timeout",
390 		"data_ovr",
391 		"cmd_ovr",
392 		"sts_ovr",
393 		"badmsg",
394 		"nomsgout",
395 		"xid_fail",
396 		"ide_fail",
397 		"abort_fail",
398 		"reject_fail",
399 		"nop_fail",
400 		"per_fail",
401 		"bdr_fail",
402 		"id_fail",
403 		"unexpected_bus_free",
404 		"tag reject",
405 		"terminated"
406 	};
407 	if (reason > CMD_TAG_REJECT) {
408 		return ("<unknown reason>");
409 	} else {
410 		return (rnames[reason]);
411 	}
412 }
413 
414 char *
415 scsi_mname(uchar_t msg)
416 {
417 	static char *imsgs[23] = {
418 		"COMMAND COMPLETE",
419 		"EXTENDED",
420 		"SAVE DATA POINTER",
421 		"RESTORE POINTERS",
422 		"DISCONNECT",
423 		"INITIATOR DETECTED ERROR",
424 		"ABORT",
425 		"REJECT",
426 		"NO-OP",
427 		"MESSAGE PARITY",
428 		"LINKED COMMAND COMPLETE",
429 		"LINKED COMMAND COMPLETE (W/FLAG)",
430 		"BUS DEVICE RESET",
431 		"ABORT TAG",
432 		"CLEAR QUEUE",
433 		"INITIATE RECOVERY",
434 		"RELEASE RECOVERY",
435 		"TERMINATE PROCESS",
436 		"CONTINUE TASK",
437 		"TARGET TRANSFER DISABLE",
438 		"RESERVED (0x14)",
439 		"RESERVED (0x15)",
440 		"CLEAR ACA"
441 	};
442 	static char *imsgs_2[6] = {
443 		"SIMPLE QUEUE TAG",
444 		"HEAD OF QUEUE TAG",
445 		"ORDERED QUEUE TAG",
446 		"IGNORE WIDE RESIDUE",
447 		"ACA",
448 		"LOGICAL UNIT RESET"
449 	};
450 
451 	if (msg < 23) {
452 		return (imsgs[msg]);
453 	} else if (IS_IDENTIFY_MSG(msg)) {
454 		return ("IDENTIFY");
455 	} else if (IS_2BYTE_MSG(msg) &&
456 	    (int)((msg) & 0xF) < (sizeof (imsgs_2) / sizeof (char *))) {
457 		return (imsgs_2[msg & 0xF]);
458 	} else {
459 		return ("<unknown msg>");
460 	}
461 
462 }
463 
464 char *
465 scsi_cname(uchar_t cmd, register char **cmdvec)
466 {
467 	while (*cmdvec != (char *)0) {
468 		if (cmd == **cmdvec) {
469 			return ((char *)((long)(*cmdvec)+1));
470 		}
471 		cmdvec++;
472 	}
473 	return (sprintf(scsi_tmpname, "<undecoded cmd 0x%x>", cmd));
474 }
475 
476 char *
477 scsi_cmd_name(uchar_t cmd, struct scsi_key_strings *cmdlist, char *tmpstr)
478 {
479 	int i = 0;
480 
481 	while (cmdlist[i].key !=  -1) {
482 		if (cmd == cmdlist[i].key) {
483 			return ((char *)cmdlist[i].message);
484 		}
485 		i++;
486 	}
487 	return (sprintf(tmpstr, "<undecoded cmd 0x%x>", cmd));
488 }
489 
490 static struct scsi_asq_key_strings extended_sense_list[] = {
491 	0x00, 0x00, "no additional sense info",
492 	0x00, 0x01, "filemark detected",
493 	0x00, 0x02, "end of partition/medium detected",
494 	0x00, 0x03, "setmark detected",
495 	0x00, 0x04, "begining of partition/medium detected",
496 	0x00, 0x05, "end of data detected",
497 	0x00, 0x06, "i/o process terminated",
498 	0x00, 0x11, "audio play operation in progress",
499 	0x00, 0x12, "audio play operation paused",
500 	0x00, 0x13, "audio play operation successfully completed",
501 	0x00, 0x14, "audio play operation stopped due to error",
502 	0x00, 0x15, "no current audio status to return",
503 	0x00, 0x16, "operation in progress",
504 	0x00, 0x17, "cleaning requested",
505 	0x00, 0x18, "erase operation in progress",
506 	0x00, 0x19, "locate operation in progress",
507 	0x00, 0x1A, "rewind operation in progress",
508 	0x00, 0x1B, "set capacity operation in progress",
509 	0x00, 0x1C, "verify operation in progress",
510 	0x01, 0x00, "no index/sector signal",
511 	0x02, 0x00, "no seek complete",
512 	0x03, 0x00, "peripheral device write fault",
513 	0x03, 0x01, "no write current",
514 	0x03, 0x02, "excessive write errors",
515 	0x04, 0x00, "LUN not ready",
516 	0x04, 0x01, "LUN is becoming ready",
517 	0x04, 0x02, "LUN initializing command required",
518 	0x04, 0x03, "LUN not ready intervention required",
519 	0x04, 0x04, "LUN not ready format in progress",
520 	0x04, 0x05, "LUN not ready, rebuild in progress",
521 	0x04, 0x06, "LUN not ready, recalculation in progress",
522 	0x04, 0x07, "LUN not ready, operation in progress",
523 	0x04, 0x08, "LUN not ready, long write in progress",
524 	0x04, 0x09, "LUN not ready, self-test in progress",
525 	0x04, 0x0A, "LUN not accessible, asymmetric access state transition",
526 	0x04, 0x0B, "LUN not accessible, target port in standby state",
527 	0x04, 0x0C, "LUN not accessible, target port in unavailable state",
528 	0x04, 0x10, "LUN not ready, auxiliary memory not accessible",
529 	0x05, 0x00, "LUN does not respond to selection",
530 	0x06, 0x00, "reference position found",
531 	0x07, 0x00, "multiple peripheral devices selected",
532 	0x08, 0x00, "LUN communication failure",
533 	0x08, 0x01, "LUN communication time-out",
534 	0x08, 0x02, "LUN communication parity error",
535 	0x08, 0x03, "LUN communication crc error (ultra-DMA/32)",
536 	0x08, 0x04, "unreachable copy target",
537 	0x09, 0x00, "track following error",
538 	0x09, 0x01, "tracking servo failure",
539 	0x09, 0x02, "focus servo failure",
540 	0x09, 0x03, "spindle servo failure",
541 	0x09, 0x04, "head select fault",
542 	0x0a, 0x00, "error log overflow",
543 	0x0b, 0x00, "warning",
544 	0x0b, 0x01, "warning - specified temperature exceeded",
545 	0x0b, 0x02, "warning - enclosure degraded",
546 	0x0c, 0x00, "write error",
547 	0x0c, 0x01, "write error - recovered with auto reallocation",
548 	0x0c, 0x02, "write error - auto reallocation failed",
549 	0x0c, 0x03, "write error - recommend reassignment",
550 	0x0c, 0x04, "compression check miscompare error",
551 	0x0c, 0x05, "data expansion occurred during compression",
552 	0x0c, 0x06, "block not compressible",
553 	0x0c, 0x07, "write error - recovery needed",
554 	0x0c, 0x08, "write error - recovery failed",
555 	0x0c, 0x09, "write error - loss of streaming",
556 	0x0c, 0x0a, "write error - padding blocks added",
557 	0x0c, 0x0b, "auxiliary memory write error",
558 	0x0c, 0x0c, "write error - unexpected unsolicited data",
559 	0x0c, 0x0d, "write error - not enough unsolicited data",
560 	0x0d, 0x00, "error detected by third party temporary initiator",
561 	0x0d, 0x01, "third party device failure",
562 	0x0d, 0x02, "copy target device not reachable",
563 	0x0d, 0x03, "incorrect copy target device type",
564 	0x0d, 0x04, "copy target device data underrun",
565 	0x0d, 0x05, "copy target device data overrun",
566 	0x0e, 0x00, "invalid information unit",
567 	0x0e, 0x01, "information unit too short",
568 	0x0e, 0x02, "information unit too long",
569 	0x10, 0x00, "ID CRC or ECC error",
570 	0x11, 0x00, "unrecovered read error",
571 	0x11, 0x01, "read retries exhausted",
572 	0x11, 0x02, "error too long to correct",
573 	0x11, 0x03, "multiple read errors",
574 	0x11, 0x04, "unrecovered read error - auto reallocate failed",
575 	0x11, 0x05, "L-EC uncorrectable error",
576 	0x11, 0x06, "CIRC unrecovered error",
577 	0x11, 0x07, "data re-synchronization error",
578 	0x11, 0x08, "incomplete block read",
579 	0x11, 0x09, "no gap found",
580 	0x11, 0x0a, "miscorrected error",
581 	0x11, 0x0b, "unrecovered read error - recommend reassignment",
582 	0x11, 0x0c, "unrecovered read error - recommend rewrite the data",
583 	0x11, 0x0d, "de-compression crc error",
584 	0x11, 0x0e, "cannot decompress using declared algorithm",
585 	0x11, 0x0f, "error reading UPC/EAN number",
586 	0x11, 0x10, "error reading ISRC number",
587 	0x11, 0x11, "read error - loss of streaming",
588 	0x11, 0x12, "auxiliary memory read error",
589 	0x11, 0x13, "read error - failed retransmission request",
590 	0x12, 0x00, "address mark not found for ID field",
591 	0x13, 0x00, "address mark not found for data field",
592 	0x14, 0x00, "recorded entity not found",
593 	0x14, 0x01, "record not found",
594 	0x14, 0x02, "filemark or setmark not found",
595 	0x14, 0x03, "end-of-data not found",
596 	0x14, 0x04, "block sequence error",
597 	0x14, 0x05, "record not found - recommend reassignment",
598 	0x14, 0x06, "record not found - data auto-reallocated",
599 	0x14, 0x07, "locate operation failure",
600 	0x15, 0x00, "random positioning error",
601 	0x15, 0x01, "mechanical positioning error",
602 	0x15, 0x02, "positioning error detected by read of medium",
603 	0x16, 0x00, "data sync mark error",
604 	0x16, 0x01, "data sync error - data rewritten",
605 	0x16, 0x02, "data sync error - recommend rewrite",
606 	0x16, 0x03, "data sync error - data auto-reallocated",
607 	0x16, 0x04, "data sync error - recommend reassignment",
608 	0x17, 0x00, "recovered data with no error correction",
609 	0x17, 0x01, "recovered data with retries",
610 	0x17, 0x02, "recovered data with positive head offset",
611 	0x17, 0x03, "recovered data with negative head offset",
612 	0x17, 0x04, "recovered data with retries and/or CIRC applied",
613 	0x17, 0x05, "recovered data using previous sector id",
614 	0x17, 0x06, "recovered data without ECC - data auto-reallocated",
615 	0x17, 0x07, "recovered data without ECC - recommend reassignment",
616 	0x17, 0x08, "recovered data without ECC - recommend rewrite",
617 	0x17, 0x09, "recovered data without ECC - data rewritten",
618 	0x18, 0x00, "recovered data with error correction",
619 	0x18, 0x01, "recovered data with error corr. & retries applied",
620 	0x18, 0x02, "recovered data - data auto-reallocated",
621 	0x18, 0x03, "recovered data with CIRC",
622 	0x18, 0x04, "recovered data with L-EC",
623 	0x18, 0x05, "recovered data - recommend reassignment",
624 	0x18, 0x06, "recovered data - recommend rewrite",
625 	0x18, 0x07, "recovered data with ECC - data rewritten",
626 	0x18, 0x08, "recovered data with linking",
627 	0x19, 0x00, "defect list error",
628 	0x1a, 0x00, "parameter list length error",
629 	0x1b, 0x00, "synchronous data xfer error",
630 	0x1c, 0x00, "defect list not found",
631 	0x1c, 0x01, "primary defect list not found",
632 	0x1c, 0x02, "grown defect list not found",
633 	0x1d, 0x00, "miscompare during verify",
634 	0x1e, 0x00, "recovered ID with ECC",
635 	0x1f, 0x00, "partial defect list transfer",
636 	0x20, 0x00, "invalid command operation code",
637 	0x20, 0x01, "access denied - initiator pending-enrolled",
638 	0x20, 0x02, "access denied - no access rights",
639 	0x20, 0x03, "access denied - invalid mgmt id key",
640 	0x20, 0x04, "illegal command while in write capable state",
641 	0x20, 0x06, "illegal command while in explicit address mode",
642 	0x20, 0x07, "illegal command while in implicit address mode",
643 	0x20, 0x08, "access denied - enrollment conflict",
644 	0x20, 0x09, "access denied - invalid lu identifier",
645 	0x20, 0x0a, "access denied - invalid proxy token",
646 	0x20, 0x0b, "access denied - ACL LUN conflict",
647 	0x21, 0x00, "logical block address out of range",
648 	0x21, 0x01, "invalid element address",
649 	0x21, 0x02, "invalid address for write",
650 	0x22, 0x00, "illegal function",
651 	0x24, 0x00, "invalid field in cdb",
652 	0x24, 0x01, "cdb decryption error",
653 	0x25, 0x00, "LUN not supported",
654 	0x26, 0x00, "invalid field in param list",
655 	0x26, 0x01, "parameter not supported",
656 	0x26, 0x02, "parameter value invalid",
657 	0x26, 0x03, "threshold parameters not supported",
658 	0x26, 0x04, "invalid release of persistent reservation",
659 	0x26, 0x05, "data decryption error",
660 	0x26, 0x06, "too many target descriptors",
661 	0x26, 0x07, "unsupported target descriptor type code",
662 	0x26, 0x08, "too many segment descriptors",
663 	0x26, 0x09, "unsupported segment descriptor type code",
664 	0x26, 0x0a, "unexpected inexact segment",
665 	0x26, 0x0b, "inline data length exceeded",
666 	0x26, 0x0c, "invalid operation for copy source or destination",
667 	0x26, 0x0d, "copy segment granularity violation",
668 	0x27, 0x00, "write protected",
669 	0x27, 0x01, "hardware write protected",
670 	0x27, 0x02, "LUN software write protected",
671 	0x27, 0x03, "associated write protect",
672 	0x27, 0x04, "persistent write protect",
673 	0x27, 0x05, "permanent write protect",
674 	0x27, 0x06, "conditional write protect",
675 	0x27, 0x80, "unable to overwrite data",
676 	0x28, 0x00, "medium may have changed",
677 	0x28, 0x01, "import or export element accessed",
678 	0x29, 0x00, "power on, reset, or bus reset occurred",
679 	0x29, 0x01, "power on occurred",
680 	0x29, 0x02, "scsi bus reset occurred",
681 	0x29, 0x03, "bus device reset message occurred",
682 	0x29, 0x04, "device internal reset",
683 	0x29, 0x05, "transceiver mode changed to single-ended",
684 	0x29, 0x06, "transceiver mode changed to LVD",
685 	0x29, 0x07, "i_t nexus loss occurred",
686 	0x2a, 0x00, "parameters changed",
687 	0x2a, 0x01, "mode parameters changed",
688 	0x2a, 0x02, "log parameters changed",
689 	0x2a, 0x03, "reservations preempted",
690 	0x2a, 0x04, "reservations released",
691 	0x2a, 0x05, "registrations preempted",
692 	0x2a, 0x06, "asymmetric access state changed",
693 	0x2a, 0x07, "implicit asymmetric access state transition failed",
694 	0x2b, 0x00, "copy cannot execute since host cannot disconnect",
695 	0x2c, 0x00, "command sequence error",
696 	0x2c, 0x03, "current program area is not empty",
697 	0x2c, 0x04, "current program area is empty",
698 	0x2c, 0x06, "persistent prevent conflict",
699 	0x2c, 0x07, "previous busy status",
700 	0x2c, 0x08, "previous task set full status",
701 	0x2c, 0x09, "previous reservation conflict status",
702 	0x2d, 0x00, "overwrite error on update in place",
703 	0x2e, 0x00, "insufficient time for operation",
704 	0x2f, 0x00, "commands cleared by another initiator",
705 	0x30, 0x00, "incompatible medium installed",
706 	0x30, 0x01, "cannot read medium - unknown format",
707 	0x30, 0x02, "cannot read medium - incompatible format",
708 	0x30, 0x03, "cleaning cartridge installed",
709 	0x30, 0x04, "cannot write medium - unknown format",
710 	0x30, 0x05, "cannot write medium - incompatible format",
711 	0x30, 0x06, "cannot format medium - incompatible medium",
712 	0x30, 0x07, "cleaning failure",
713 	0x30, 0x08, "cannot write - application code mismatch",
714 	0x30, 0x09, "current session not fixated for append",
715 	0x30, 0x0b, "WORM medium - Overwrite attempted",
716 	0x30, 0x0c, "WORM medium - Cannot Erase",
717 	0x30, 0x0d, "WORM medium - Integrity Check",
718 	0x30, 0x10, "medium not formatted",
719 	0x31, 0x00, "medium format corrupted",
720 	0x31, 0x01, "format command failed",
721 	0x31, 0x02, "zoned formatting failed due to spare linking",
722 	0x31, 0x94, "WORM media corrupted",
723 	0x32, 0x00, "no defect spare location available",
724 	0x32, 0x01, "defect list update failure",
725 	0x33, 0x00, "tape length error",
726 	0x34, 0x00, "enclosure failure",
727 	0x35, 0x00, "enclosure services failure",
728 	0x35, 0x01, "unsupported enclosure function",
729 	0x35, 0x02, "enclosure services unavailable",
730 	0x35, 0x03, "enclosure services transfer failure",
731 	0x35, 0x04, "enclosure services transfer refused",
732 	0x36, 0x00, "ribbon, ink, or toner failure",
733 	0x37, 0x00, "rounded parameter",
734 	0x39, 0x00, "saving parameters not supported",
735 	0x3a, 0x00, "medium not present",
736 	0x3a, 0x01, "medium not present - tray closed",
737 	0x3a, 0x02, "medium not present - tray open",
738 	0x3a, 0x03, "medium not present - loadable",
739 	0x3a, 0x04, "medium not present - medium auxiliary memory accessible",
740 	0x3b, 0x00, "sequential positioning error",
741 	0x3b, 0x01, "tape position error at beginning-of-medium",
742 	0x3b, 0x02, "tape position error at end-of-medium",
743 	0x3b, 0x08, "reposition error",
744 	0x3b, 0x0c, "position past beginning of medium",
745 	0x3b, 0x0d, "medium destination element full",
746 	0x3b, 0x0e, "medium source element empty",
747 	0x3b, 0x0f, "end of medium reached",
748 	0x3b, 0x11, "medium magazine not accessible",
749 	0x3b, 0x12, "medium magazine removed",
750 	0x3b, 0x13, "medium magazine inserted",
751 	0x3b, 0x14, "medium magazine locked",
752 	0x3b, 0x15, "medium magazine unlocked",
753 	0x3b, 0x16, "mechanical positioning or changer error",
754 	0x3d, 0x00, "invalid bits in indentify message",
755 	0x3e, 0x00, "LUN has not self-configured yet",
756 	0x3e, 0x01, "LUN failure",
757 	0x3e, 0x02, "timeout on LUN",
758 	0x3e, 0x03, "LUN failed self-test",
759 	0x3e, 0x04, "LUN unable to update self-test log",
760 	0x3f, 0x00, "target operating conditions have changed",
761 	0x3f, 0x01, "microcode has been changed",
762 	0x3f, 0x02, "changed operating definition",
763 	0x3f, 0x03, "inquiry data has changed",
764 	0x3f, 0x04, "component device attached",
765 	0x3f, 0x05, "device identifier changed",
766 	0x3f, 0x06, "redundancy group created or modified",
767 	0x3f, 0x07, "redundancy group deleted",
768 	0x3f, 0x08, "spare created or modified",
769 	0x3f, 0x09, "spare deleted",
770 	0x3f, 0x0a, "volume set created or modified",
771 	0x3f, 0x0b, "volume set deleted",
772 	0x3f, 0x0c, "volume set deassigned",
773 	0x3f, 0x0d, "volume set reassigned",
774 	0x3f, 0x0e, "reported LUNs data has changed",
775 	0x3f, 0x0f, "echo buffer overwritten",
776 	0x3f, 0x10, "medium loadable",
777 	0x3f, 0x11, "medium auxiliary memory accessible",
778 	0x40, 0x00, "ram failure",
779 	0x41, 0x00, "data path failure",
780 	0x42, 0x00, "power-on or self-test failure",
781 	0x43, 0x00, "message error",
782 	0x44, 0x00, "internal target failure",
783 	0x45, 0x00, "select or reselect failure",
784 	0x46, 0x00, "unsuccessful soft reset",
785 	0x47, 0x00, "scsi parity error",
786 	0x47, 0x01, "data phase crc error detected",
787 	0x47, 0x02, "scsi parity error detected during st data phase",
788 	0x47, 0x03, "information unit iucrc error detected",
789 	0x47, 0x04, "asynchronous information protection error detected",
790 	0x47, 0x05, "protocol service crc error",
791 	0x47, 0x7f, "some commands cleared by iscsi protocol event",
792 	0x48, 0x00, "initiator detected error message received",
793 	0x49, 0x00, "invalid message error",
794 	0x4a, 0x00, "command phase error",
795 	0x4b, 0x00, "data phase error",
796 	0x4b, 0x01, "invalid target port transfer tag received",
797 	0x4b, 0x02, "too much write data",
798 	0x4b, 0x03, "ack/nak timeout",
799 	0x4b, 0x04, "nak received",
800 	0x4b, 0x05, "data offset error",
801 	0x4c, 0x00, "logical unit failed self-configuration",
802 	0x4d, 0x00, "tagged overlapped commands (ASCQ = queue tag)",
803 	0x4e, 0x00, "overlapped commands attempted",
804 	0x50, 0x00, "write append error",
805 	0x50, 0x01, "data protect write append error",
806 	0x50, 0x95, "data protect write append error",
807 	0x51, 0x00, "erase failure",
808 	0x52, 0x00, "cartridge fault",
809 	0x53, 0x00, "media load or eject failed",
810 	0x53, 0x01, "unload tape failure",
811 	0x53, 0x02, "medium removal prevented",
812 	0x54, 0x00, "scsi to host system interface failure",
813 	0x55, 0x00, "system resource failure",
814 	0x55, 0x01, "system buffer full",
815 	0x55, 0x02, "insufficient reservation resources",
816 	0x55, 0x03, "insufficient resources",
817 	0x55, 0x04, "insufficient registration resources",
818 	0x55, 0x05, "insufficient access control resources",
819 	0x55, 0x06, "auxiliary memory out of space",
820 	0x57, 0x00, "unable to recover TOC",
821 	0x58, 0x00, "generation does not exist",
822 	0x59, 0x00, "updated block read",
823 	0x5a, 0x00, "operator request or state change input",
824 	0x5a, 0x01, "operator medium removal request",
825 	0x5a, 0x02, "operator selected write protect",
826 	0x5a, 0x03, "operator selected write permit",
827 	0x5b, 0x00, "log exception",
828 	0x5b, 0x01, "threshold condition met",
829 	0x5b, 0x02, "log counter at maximum",
830 	0x5b, 0x03, "log list codes exhausted",
831 	0x5c, 0x00, "RPL status change",
832 	0x5c, 0x01, "spindles synchronized",
833 	0x5c, 0x02, "spindles not synchronized",
834 	0x5d, 0x00, "drive operation marginal, service immediately"
835 		    " (failure prediction threshold exceeded)",
836 	0x5d, 0x01, "media failure prediction threshold exceeded",
837 	0x5d, 0x02, "LUN failure prediction threshold exceeded",
838 	0x5d, 0x03, "spare area exhaustion prediction threshold exceeded",
839 	0x5d, 0x10, "hardware impending failure general hard drive failure",
840 	0x5d, 0x11, "hardware impending failure drive error rate too high",
841 	0x5d, 0x12, "hardware impending failure data error rate too high",
842 	0x5d, 0x13, "hardware impending failure seek error rate too high",
843 	0x5d, 0x14, "hardware impending failure too many block reassigns",
844 	0x5d, 0x15, "hardware impending failure access times too high",
845 	0x5d, 0x16, "hardware impending failure start unit times too high",
846 	0x5d, 0x17, "hardware impending failure channel parametrics",
847 	0x5d, 0x18, "hardware impending failure controller detected",
848 	0x5d, 0x19, "hardware impending failure throughput performance",
849 	0x5d, 0x1a, "hardware impending failure seek time performance",
850 	0x5d, 0x1b, "hardware impending failure spin-up retry count",
851 	0x5d, 0x1c, "hardware impending failure drive calibration retry count",
852 	0x5d, 0x20, "controller impending failure general hard drive failure",
853 	0x5d, 0x21, "controller impending failure drive error rate too high",
854 	0x5d, 0x22, "controller impending failure data error rate too high",
855 	0x5d, 0x23, "controller impending failure seek error rate too high",
856 	0x5d, 0x24, "controller impending failure too many block reassigns",
857 	0x5d, 0x25, "controller impending failure access times too high",
858 	0x5d, 0x26, "controller impending failure start unit times too high",
859 	0x5d, 0x27, "controller impending failure channel parametrics",
860 	0x5d, 0x28, "controller impending failure controller detected",
861 	0x5d, 0x29, "controller impending failure throughput performance",
862 	0x5d, 0x2a, "controller impending failure seek time performance",
863 	0x5d, 0x2b, "controller impending failure spin-up retry count",
864 	0x5d, 0x2c, "controller impending failure drive calibration retry cnt",
865 	0x5d, 0x30, "data channel impending failure general hard drive failure",
866 	0x5d, 0x31, "data channel impending failure drive error rate too high",
867 	0x5d, 0x32, "data channel impending failure data error rate too high",
868 	0x5d, 0x33, "data channel impending failure seek error rate too high",
869 	0x5d, 0x34, "data channel impending failure too many block reassigns",
870 	0x5d, 0x35, "data channel impending failure access times too high",
871 	0x5d, 0x36, "data channel impending failure start unit times too high",
872 	0x5d, 0x37, "data channel impending failure channel parametrics",
873 	0x5d, 0x38, "data channel impending failure controller detected",
874 	0x5d, 0x39, "data channel impending failure throughput performance",
875 	0x5d, 0x3a, "data channel impending failure seek time performance",
876 	0x5d, 0x3b, "data channel impending failure spin-up retry count",
877 	0x5d, 0x3c, "data channel impending failure drive calibrate retry cnt",
878 	0x5d, 0x40, "servo impending failure general hard drive failure",
879 	0x5d, 0x41, "servo impending failure drive error rate too high",
880 	0x5d, 0x42, "servo impending failure data error rate too high",
881 	0x5d, 0x43, "servo impending failure seek error rate too high",
882 	0x5d, 0x44, "servo impending failure too many block reassigns",
883 	0x5d, 0x45, "servo impending failure access times too high",
884 	0x5d, 0x46, "servo impending failure start unit times too high",
885 	0x5d, 0x47, "servo impending failure channel parametrics",
886 	0x5d, 0x48, "servo impending failure controller detected",
887 	0x5d, 0x49, "servo impending failure throughput performance",
888 	0x5d, 0x4a, "servo impending failure seek time performance",
889 	0x5d, 0x4b, "servo impending failure spin-up retry count",
890 	0x5d, 0x4c, "servo impending failure drive calibration retry count",
891 	0x5d, 0x50, "spindle impending failure general hard drive failure",
892 	0x5d, 0x51, "spindle impending failure drive error rate too high",
893 	0x5d, 0x52, "spindle impending failure data error rate too high",
894 	0x5d, 0x53, "spindle impending failure seek error rate too high",
895 	0x5d, 0x54, "spindle impending failure too many block reassigns",
896 	0x5d, 0x55, "spindle impending failure access times too high",
897 	0x5d, 0x56, "spindle impending failure start unit times too high",
898 	0x5d, 0x57, "spindle impending failure channel parametrics",
899 	0x5d, 0x58, "spindle impending failure controller detected",
900 	0x5d, 0x59, "spindle impending failure throughput performance",
901 	0x5d, 0x5a, "spindle impending failure seek time performance",
902 	0x5d, 0x5b, "spindle impending failure spin-up retry count",
903 	0x5d, 0x5c, "spindle impending failure drive calibration retry count",
904 	0x5d, 0x60, "firmware impending failure general hard drive failure",
905 	0x5d, 0x61, "firmware impending failure drive error rate too high",
906 	0x5d, 0x62, "firmware impending failure data error rate too high",
907 	0x5d, 0x63, "firmware impending failure seek error rate too high",
908 	0x5d, 0x64, "firmware impending failure too many block reassigns",
909 	0x5d, 0x65, "firmware impending failure access times too high",
910 	0x5d, 0x66, "firmware impending failure start unit times too high",
911 	0x5d, 0x67, "firmware impending failure channel parametrics",
912 	0x5d, 0x68, "firmware impending failure controller detected",
913 	0x5d, 0x69, "firmware impending failure throughput performance",
914 	0x5d, 0x6a, "firmware impending failure seek time performance",
915 	0x5d, 0x6b, "firmware impending failure spin-up retry count",
916 	0x5d, 0x6c, "firmware impending failure drive calibration retry count",
917 	0x5d, 0xff, "failure prediction threshold exceeded (false)",
918 	0x5e, 0x00, "low power condition active",
919 	0x5e, 0x01, "idle condition activated by timer",
920 	0x5e, 0x02, "standby condition activated by timer",
921 	0x5e, 0x03, "idle condition activated by command",
922 	0x5e, 0x04, "standby condition activated by command",
923 	0x60, 0x00, "lamp failure",
924 	0x61, 0x00, "video aquisition error",
925 	0x62, 0x00, "scan head positioning error",
926 	0x63, 0x00, "end of user area encountered on this track",
927 	0x63, 0x01, "packet does not fit in available space",
928 	0x64, 0x00, "illegal mode for this track",
929 	0x64, 0x01, "invalid packet size",
930 	0x65, 0x00, "voltage fault",
931 	0x66, 0x00, "automatic document feeder cover up",
932 	0x67, 0x00, "configuration failure",
933 	0x67, 0x01, "configuration of incapable LUNs failed",
934 	0x67, 0x02, "add LUN failed",
935 	0x67, 0x03, "modification of LUN failed",
936 	0x67, 0x04, "exchange of LUN failed",
937 	0x67, 0x05, "remove of LUN failed",
938 	0x67, 0x06, "attachment of LUN failed",
939 	0x67, 0x07, "creation of LUN failed",
940 	0x67, 0x08, "assign failure occurred",
941 	0x67, 0x09, "multiply assigned LUN",
942 	0x67, 0x0a, "set target port groups command failed",
943 	0x68, 0x00, "logical unit not configured",
944 	0x69, 0x00, "data loss on logical unit",
945 	0x69, 0x01, "multiple LUN failures",
946 	0x69, 0x02, "parity/data mismatch",
947 	0x6a, 0x00, "informational, refer to log",
948 	0x6b, 0x00, "state change has occured",
949 	0x6b, 0x01, "redundancy level got better",
950 	0x6b, 0x02, "redundancy level got worse",
951 	0x6c, 0x00, "rebuild failure occured",
952 	0x6d, 0x00, "recalculate failure occured",
953 	0x6e, 0x00, "command to logical unit failed",
954 	0x6f, 0x00, "copy protect key exchange failure authentication failure",
955 	0x6f, 0x01, "copy protect key exchange failure key not present",
956 	0x6f, 0x02, "copy protect key exchange failure key not established",
957 	0x6f, 0x03, "read of scrambled sector without authentication",
958 	0x6f, 0x04, "media region code is mismatched to LUN region",
959 	0x6f, 0x05, "drive region must be permanent/region reset count error",
960 	0x70, 0xffff, "decompression exception short algorithm id of ASCQ",
961 	0x71, 0x00, "decompression exception long algorithm id",
962 	0x72, 0x00, "session fixation error",
963 	0x72, 0x01, "session fixation error writing lead-in",
964 	0x72, 0x02, "session fixation error writing lead-out",
965 	0x72, 0x03, "session fixation error - incomplete track in session",
966 	0x72, 0x04, "empty or partially written reserved track",
967 	0x72, 0x05, "no more track reservations allowed",
968 	0x73, 0x00, "cd control error",
969 	0x73, 0x01, "power calibration area almost full",
970 	0x73, 0x02, "power calibration area is full",
971 	0x73, 0x03, "power calibration area error",
972 	0x73, 0x04, "program memory area update failure",
973 	0x73, 0x05, "program memory area is full",
974 	0x73, 0x06, "rma/pma is almost full",
975 	0xffff, 0xffff, NULL
976 };
977 
978 char *
979 scsi_esname(uint_t key, char *tmpstr)
980 {
981 	int i = 0;
982 
983 	while (extended_sense_list[i].asc != 0xffff) {
984 		if (key == extended_sense_list[i].asc) {
985 			return ((char *)extended_sense_list[i].message);
986 		}
987 		i++;
988 	}
989 	return (sprintf(tmpstr, "<vendor unique code 0x%x>", key));
990 }
991 
992 char *
993 scsi_asc_name(uint_t asc, uint_t ascq, char *tmpstr)
994 {
995 	int i = 0;
996 
997 	while (extended_sense_list[i].asc != 0xffff) {
998 		if ((asc == extended_sense_list[i].asc) &&
999 		    ((ascq == extended_sense_list[i].ascq) ||
1000 		    (extended_sense_list[i].ascq == 0xffff))) {
1001 			return ((char *)extended_sense_list[i].message);
1002 		}
1003 		i++;
1004 	}
1005 	return (sprintf(tmpstr, "<vendor unique code 0x%x>", asc));
1006 }
1007 
1008 char *
1009 scsi_sname(uchar_t sense_key)
1010 {
1011 	if (sense_key >= (uchar_t)(NUM_SENSE_KEYS+NUM_IMPL_SENSE_KEYS)) {
1012 		return ("<unknown sense key>");
1013 	} else {
1014 		return (sense_keys[sense_key]);
1015 	}
1016 }
1017 
1018 
1019 /*
1020  * Print a piece of inquiry data- cleaned up for non-printable characters.
1021  */
1022 
1023 static void
1024 inq_fill(char *p, int l, char *s)
1025 {
1026 	register unsigned i = 0;
1027 	char c;
1028 
1029 	if (!p)
1030 		return;
1031 
1032 	while (i++ < l) {
1033 		/* clean string of non-printing chars */
1034 		if ((c = *p++) < ' ' || c >= 0177) {
1035 			c = ' ';
1036 		}
1037 		*s++ = c;
1038 	}
1039 	*s++ = 0;
1040 }
1041 
1042 static char *
1043 scsi_asc_search(uint_t asc, uint_t ascq,
1044     struct scsi_asq_key_strings *list)
1045 {
1046 	int i = 0;
1047 
1048 	while (list[i].asc != 0xffff) {
1049 		if ((asc == list[i].asc) &&
1050 		    ((ascq == list[i].ascq) ||
1051 		    (list[i].ascq == 0xffff))) {
1052 			return ((char *)list[i].message);
1053 		}
1054 		i++;
1055 	}
1056 	return (NULL);
1057 }
1058 
1059 static char *
1060 scsi_asc_ascq_name(uint_t asc, uint_t ascq, char *tmpstr,
1061 	struct scsi_asq_key_strings *list)
1062 {
1063 	char *message;
1064 
1065 	if (list) {
1066 		if (message = scsi_asc_search(asc, ascq, list)) {
1067 			return (message);
1068 		}
1069 	}
1070 	if (message = scsi_asc_search(asc, ascq, extended_sense_list)) {
1071 		return (message);
1072 	}
1073 
1074 	return (sprintf(tmpstr, "<vendor unique code 0x%x>", asc));
1075 }
1076 
1077 /*
1078  * The first part/column of the error message will be at least this length.
1079  * This number has been calculated so that each line fits in 80 chars.
1080  */
1081 #define	SCSI_ERRMSG_COLUMN_LEN	42
1082 #define	SCSI_ERRMSG_BUF_LEN	256
1083 
1084 void
1085 scsi_vu_errmsg(struct scsi_device *devp, struct scsi_pkt *pkt, char *label,
1086     int severity, daddr_t blkno, daddr_t err_blkno,
1087     struct scsi_key_strings *cmdlist, struct scsi_extended_sense *sensep,
1088     struct scsi_asq_key_strings *asc_list,
1089     char *(*decode_fru)(struct scsi_device *, char *, int, uchar_t))
1090 {
1091 	uchar_t com;
1092 	static char buf[SCSI_ERRMSG_BUF_LEN];
1093 	static char buf1[SCSI_ERRMSG_BUF_LEN];
1094 	static char tmpbuf[64];
1095 	static char pad[SCSI_ERRMSG_COLUMN_LEN];
1096 	dev_info_t *dev = devp->sd_dev;
1097 	static char *error_classes[] = {
1098 		"All", "Unknown", "Informational",
1099 		"Recovered", "Retryable", "Fatal"
1100 	};
1101 	uchar_t sense_key, asc, ascq, fru_code;
1102 	uchar_t *fru_code_ptr;
1103 	int i, buflen;
1104 
1105 	mutex_enter(&scsi_log_mutex);
1106 
1107 	/*
1108 	 * We need to put our space padding code because kernel version
1109 	 * of sprintf(9F) doesn't support %-<number>s type of left alignment.
1110 	 */
1111 	for (i = 0; i < SCSI_ERRMSG_COLUMN_LEN; i++) {
1112 		pad[i] = ' ';
1113 	}
1114 
1115 	bzero(buf, 256);
1116 	com = ((union scsi_cdb *)pkt->pkt_cdbp)->scc_cmd;
1117 	(void) sprintf(buf, "Error for Command: %s",
1118 	    scsi_cmd_name(com, cmdlist, tmpbuf));
1119 	buflen = strlen(buf);
1120 	if (buflen < SCSI_ERRMSG_COLUMN_LEN) {
1121 		pad[SCSI_ERRMSG_COLUMN_LEN - buflen] = '\0';
1122 		(void) sprintf(&buf[buflen], "%s Error Level: %s",
1123 		    pad, error_classes[severity]);
1124 		pad[SCSI_ERRMSG_COLUMN_LEN - buflen] = ' ';
1125 	} else {
1126 		(void) sprintf(&buf[buflen], " Error Level: %s",
1127 		    error_classes[severity]);
1128 	}
1129 	impl_scsi_log(dev, label, CE_WARN, buf);
1130 
1131 	if (blkno != -1 || err_blkno != -1 &&
1132 	    ((com & 0xf) == SCMD_READ) || ((com & 0xf) == SCMD_WRITE)) {
1133 		bzero(buf, 256);
1134 		(void) sprintf(buf, "Requested Block: %ld", blkno);
1135 		buflen = strlen(buf);
1136 		if (buflen < SCSI_ERRMSG_COLUMN_LEN) {
1137 			pad[SCSI_ERRMSG_COLUMN_LEN - buflen] = '\0';
1138 			(void) sprintf(&buf[buflen], "%s Error Block: %ld\n",
1139 			    pad, err_blkno);
1140 			pad[SCSI_ERRMSG_COLUMN_LEN - buflen] = ' ';
1141 		} else {
1142 			(void) sprintf(&buf[buflen], " Error Block: %ld\n",
1143 			    err_blkno);
1144 		}
1145 		impl_scsi_log(dev, label, CE_CONT, buf);
1146 	}
1147 
1148 	bzero(buf, 256);
1149 	(void) strcpy(buf, "Vendor: ");
1150 	inq_fill(devp->sd_inq->inq_vid, 8, &buf[strlen(buf)]);
1151 	buflen = strlen(buf);
1152 	if (buflen < SCSI_ERRMSG_COLUMN_LEN) {
1153 		pad[SCSI_ERRMSG_COLUMN_LEN - buflen] = '\0';
1154 		(void) sprintf(&buf[strlen(buf)], "%s Serial Number: ", pad);
1155 		pad[SCSI_ERRMSG_COLUMN_LEN - buflen] = ' ';
1156 	} else {
1157 		(void) sprintf(&buf[strlen(buf)], " Serial Number: ");
1158 	}
1159 	inq_fill(devp->sd_inq->inq_serial, 12, &buf[strlen(buf)]);
1160 	impl_scsi_log(dev, label, CE_CONT, "%s\n", buf);
1161 
1162 	if (sensep) {
1163 		sense_key = scsi_sense_key((uint8_t *)sensep);
1164 		asc = scsi_sense_asc((uint8_t *)sensep);
1165 		ascq = scsi_sense_ascq((uint8_t *)sensep);
1166 		scsi_ext_sense_fields((uint8_t *)sensep, SENSE_LENGTH,
1167 		    NULL, NULL, &fru_code_ptr, NULL, NULL);
1168 		fru_code = (fru_code_ptr ? *fru_code_ptr : 0);
1169 
1170 		bzero(buf, 256);
1171 		(void) sprintf(buf, "Sense Key: %s\n",
1172 		    sense_keys[sense_key]);
1173 		impl_scsi_log(dev, label, CE_CONT, buf);
1174 
1175 		bzero(buf, 256);
1176 		if ((fru_code != 0) &&
1177 		    (decode_fru != NULL)) {
1178 			(*decode_fru)(devp, buf, SCSI_ERRMSG_BUF_LEN,
1179 			    fru_code);
1180 			if (buf[0] != NULL) {
1181 				bzero(buf1, 256);
1182 				(void) sprintf(&buf1[strlen(buf1)],
1183 				    "ASC: 0x%x (%s)", asc,
1184 				    scsi_asc_ascq_name(asc, ascq,
1185 					tmpbuf, asc_list));
1186 				buflen = strlen(buf1);
1187 				if (buflen < SCSI_ERRMSG_COLUMN_LEN) {
1188 				    pad[SCSI_ERRMSG_COLUMN_LEN - buflen] = '\0';
1189 				    (void) sprintf(&buf1[buflen],
1190 				    "%s ASCQ: 0x%x", pad, ascq);
1191 				} else {
1192 				    (void) sprintf(&buf1[buflen], " ASCQ: 0x%x",
1193 					ascq);
1194 				}
1195 				impl_scsi_log(dev,
1196 					label, CE_CONT, "%s\n", buf1);
1197 				impl_scsi_log(dev, label, CE_CONT,
1198 					"FRU: 0x%x (%s)\n",
1199 						fru_code, buf);
1200 				mutex_exit(&scsi_log_mutex);
1201 				return;
1202 			}
1203 		}
1204 		(void) sprintf(&buf[strlen(buf)],
1205 		    "ASC: 0x%x (%s), ASCQ: 0x%x, FRU: 0x%x",
1206 		    asc, scsi_asc_ascq_name(asc, ascq, tmpbuf, asc_list),
1207 		    ascq, fru_code);
1208 		impl_scsi_log(dev, label, CE_CONT, "%s\n", buf);
1209 	}
1210 	mutex_exit(&scsi_log_mutex);
1211 }
1212 
1213 void
1214 scsi_errmsg(struct scsi_device *devp, struct scsi_pkt *pkt, char *label,
1215     int severity, daddr_t blkno, daddr_t err_blkno,
1216     struct scsi_key_strings *cmdlist, struct scsi_extended_sense *sensep)
1217 {
1218 	scsi_vu_errmsg(devp, pkt, label, severity, blkno,
1219 		err_blkno, cmdlist, sensep, NULL, NULL);
1220 }
1221 
1222 /*PRINTFLIKE4*/
1223 void
1224 scsi_log(dev_info_t *dev, char *label, uint_t level,
1225     const char *fmt, ...)
1226 {
1227 	va_list ap;
1228 
1229 	va_start(ap, fmt);
1230 	mutex_enter(&scsi_log_mutex);
1231 	v_scsi_log(dev, label, level, fmt, ap);
1232 	mutex_exit(&scsi_log_mutex);
1233 	va_end(ap);
1234 }
1235 
1236 /*PRINTFLIKE4*/
1237 static void
1238 impl_scsi_log(dev_info_t *dev, char *label, uint_t level,
1239     const char *fmt, ...)
1240 {
1241 	va_list ap;
1242 
1243 	ASSERT(mutex_owned(&scsi_log_mutex));
1244 
1245 	va_start(ap, fmt);
1246 	v_scsi_log(dev, label, level, fmt, ap);
1247 	va_end(ap);
1248 }
1249 
1250 
1251 char *ddi_pathname(dev_info_t *dip, char *path);
1252 
1253 /*PRINTFLIKE4*/
1254 static void
1255 v_scsi_log(dev_info_t *dev, char *label, uint_t level,
1256     const char *fmt, va_list ap)
1257 {
1258 	static char name[256];
1259 	int log_only = 0;
1260 	int boot_only = 0;
1261 	int console_only = 0;
1262 
1263 	ASSERT(mutex_owned(&scsi_log_mutex));
1264 
1265 	if (dev) {
1266 		if (level == CE_PANIC || level == CE_WARN ||
1267 		    level == CE_NOTE) {
1268 			(void) sprintf(name, "%s (%s%d):\n",
1269 				ddi_pathname(dev, scsi_log_buffer),
1270 				label, ddi_get_instance(dev));
1271 		} else if (level >= (uint_t)SCSI_DEBUG) {
1272 			(void) sprintf(name,
1273 			    "%s%d:", label, ddi_get_instance(dev));
1274 		} else {
1275 			name[0] = '\0';
1276 		}
1277 	} else {
1278 		(void) sprintf(name, "%s:", label);
1279 	}
1280 
1281 	(void) vsprintf(scsi_log_buffer, fmt, ap);
1282 
1283 	switch (scsi_log_buffer[0]) {
1284 	case '!':
1285 		log_only = 1;
1286 		break;
1287 	case '?':
1288 		boot_only = 1;
1289 		break;
1290 	case '^':
1291 		console_only = 1;
1292 		break;
1293 	}
1294 
1295 	switch (level) {
1296 	case CE_NOTE:
1297 		level = CE_CONT;
1298 		/* FALLTHROUGH */
1299 	case CE_CONT:
1300 	case CE_WARN:
1301 	case CE_PANIC:
1302 		if (boot_only) {
1303 			cmn_err(level, "?%s\t%s", name,
1304 				&scsi_log_buffer[1]);
1305 		} else if (console_only) {
1306 			cmn_err(level, "^%s\t%s", name,
1307 				&scsi_log_buffer[1]);
1308 		} else if (log_only) {
1309 			cmn_err(level, "!%s\t%s", name,
1310 				&scsi_log_buffer[1]);
1311 		} else {
1312 			cmn_err(level, "%s\t%s", name,
1313 				scsi_log_buffer);
1314 		}
1315 		break;
1316 	case (uint_t)SCSI_DEBUG:
1317 	default:
1318 		cmn_err(CE_CONT, "^DEBUG: %s\t%s", name,
1319 				scsi_log_buffer);
1320 		break;
1321 	}
1322 }
1323 
1324 int
1325 scsi_get_device_type_scsi_options(dev_info_t *dip,
1326     struct scsi_device *devp, int default_scsi_options)
1327 {
1328 
1329 	caddr_t config_list	= NULL;
1330 	int options		= default_scsi_options;
1331 	struct scsi_inquiry  *inq = devp->sd_inq;
1332 	caddr_t vidptr, datanameptr;
1333 	int	vidlen, dupletlen;
1334 	int config_list_len, len;
1335 
1336 	/*
1337 	 * look up the device-type-scsi-options-list and walk thru
1338 	 * the list
1339 	 * compare the vendor ids of the earlier inquiry command and
1340 	 * with those vids in the list
1341 	 * if there is a match, lookup the scsi-options value
1342 	 */
1343 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1344 	    "device-type-scsi-options-list",
1345 	    (caddr_t)&config_list, &config_list_len) == DDI_PROP_SUCCESS) {
1346 
1347 		/*
1348 		 * Compare vids in each duplet - if it matches, get value for
1349 		 * dataname and then lookup scsi_options
1350 		 * dupletlen is calculated later.
1351 		 */
1352 		for (len = config_list_len, vidptr = config_list; len > 0;
1353 		    vidptr += dupletlen, len -= dupletlen) {
1354 
1355 			vidlen = strlen(vidptr);
1356 			datanameptr = vidptr + vidlen + 1;
1357 
1358 			if ((vidlen != 0) &&
1359 			    bcmp(inq->inq_vid, vidptr, vidlen) == 0) {
1360 				/*
1361 				 * get the data list
1362 				 */
1363 				options = ddi_prop_get_int(DDI_DEV_T_ANY,
1364 				    dip, 0,
1365 				    datanameptr, default_scsi_options);
1366 				break;
1367 			}
1368 			dupletlen = vidlen + strlen(datanameptr) + 2;
1369 		}
1370 		kmem_free(config_list, config_list_len);
1371 	}
1372 
1373 	return (options);
1374 }
1375 
1376 /*
1377  * Functions for format-neutral sense data functions
1378  */
1379 
1380 int
1381 scsi_validate_sense(uint8_t *sense_buffer, int sense_buf_len, int *flags)
1382 {
1383 	int result;
1384 	struct scsi_extended_sense *es =
1385 	    (struct scsi_extended_sense *)sense_buffer;
1386 
1387 	/*
1388 	 * Init flags if present
1389 	 */
1390 	if (flags != NULL) {
1391 		*flags = 0;
1392 	}
1393 
1394 	/*
1395 	 * Check response code (Solaris breaks this into a 3-bit class
1396 	 * and 4-bit code field.
1397 	 */
1398 	if ((es->es_class != CLASS_EXTENDED_SENSE) ||
1399 	    ((es->es_code != CODE_FMT_FIXED_CURRENT) &&
1400 		(es->es_code != CODE_FMT_FIXED_DEFERRED) &&
1401 		(es->es_code != CODE_FMT_DESCR_CURRENT) &&
1402 		(es->es_code != CODE_FMT_DESCR_DEFERRED))) {
1403 		/*
1404 		 * Sense data (if there's actually anything here) is not
1405 		 * in a format we can handle).
1406 		 */
1407 		return (SENSE_UNUSABLE);
1408 	}
1409 
1410 	/*
1411 	 * Check if this is deferred sense
1412 	 */
1413 	if ((flags != NULL) &&
1414 	    ((es->es_code == CODE_FMT_FIXED_DEFERRED) ||
1415 		(es->es_code == CODE_FMT_DESCR_DEFERRED))) {
1416 		*flags |= SNS_BUF_DEFERRED;
1417 	}
1418 
1419 	/*
1420 	 * Make sure length is OK
1421 	 */
1422 	if (es->es_code == CODE_FMT_FIXED_CURRENT ||
1423 	    es->es_code == CODE_FMT_FIXED_DEFERRED) {
1424 		/*
1425 		 * We can get by with a buffer that only includes the key,
1426 		 * asc, and ascq.  In reality the minimum length we should
1427 		 * ever see is 18 bytes.
1428 		 */
1429 		if ((sense_buf_len < MIN_FIXED_SENSE_LEN) ||
1430 		    ((es->es_add_len + ADDL_SENSE_ADJUST) <
1431 			MIN_FIXED_SENSE_LEN)) {
1432 			result = SENSE_UNUSABLE;
1433 		} else {
1434 			/*
1435 			 * The es_add_len field contains the number of sense
1436 			 * data bytes that follow the es_add_len field.
1437 			 */
1438 			if ((flags != NULL) &&
1439 			    (sense_buf_len <
1440 				(es->es_add_len + ADDL_SENSE_ADJUST))) {
1441 				*flags |= SNS_BUF_OVERFLOW;
1442 			}
1443 
1444 			result = SENSE_FIXED_FORMAT;
1445 		}
1446 	} else {
1447 		struct scsi_descr_sense_hdr *ds =
1448 		    (struct scsi_descr_sense_hdr *)sense_buffer;
1449 
1450 		/*
1451 		 * For descriptor format we need at least the descriptor
1452 		 * header
1453 		 */
1454 		if (sense_buf_len < sizeof (struct scsi_descr_sense_hdr)) {
1455 			result = SENSE_UNUSABLE;
1456 		} else {
1457 			/*
1458 			 * Check for overflow
1459 			 */
1460 			if ((flags != NULL) &&
1461 			    (sense_buf_len <
1462 				(ds->ds_addl_sense_length + sizeof (*ds)))) {
1463 				*flags |= SNS_BUF_OVERFLOW;
1464 			}
1465 
1466 			result = SENSE_DESCR_FORMAT;
1467 		}
1468 	}
1469 
1470 	return (result);
1471 }
1472 
1473 
1474 uint8_t
1475 scsi_sense_key(uint8_t *sense_buffer)
1476 {
1477 	uint8_t skey;
1478 	if (SCSI_IS_DESCR_SENSE(sense_buffer)) {
1479 		struct scsi_descr_sense_hdr *sdsp =
1480 		    (struct scsi_descr_sense_hdr *)sense_buffer;
1481 		skey = sdsp->ds_key;
1482 	} else {
1483 		struct scsi_extended_sense *ext_sensep =
1484 		    (struct scsi_extended_sense *)sense_buffer;
1485 		skey = ext_sensep->es_key;
1486 	}
1487 	return (skey);
1488 }
1489 
1490 uint8_t
1491 scsi_sense_asc(uint8_t *sense_buffer)
1492 {
1493 	uint8_t asc;
1494 	if (SCSI_IS_DESCR_SENSE(sense_buffer)) {
1495 		struct scsi_descr_sense_hdr *sdsp =
1496 		    (struct scsi_descr_sense_hdr *)sense_buffer;
1497 		asc = sdsp->ds_add_code;
1498 	} else {
1499 		struct scsi_extended_sense *ext_sensep =
1500 		    (struct scsi_extended_sense *)sense_buffer;
1501 		asc = ext_sensep->es_add_code;
1502 	}
1503 	return (asc);
1504 }
1505 
1506 uint8_t
1507 scsi_sense_ascq(uint8_t *sense_buffer)
1508 {
1509 	uint8_t ascq;
1510 	if (SCSI_IS_DESCR_SENSE(sense_buffer)) {
1511 		struct scsi_descr_sense_hdr *sdsp =
1512 		    (struct scsi_descr_sense_hdr *)sense_buffer;
1513 		ascq = sdsp->ds_qual_code;
1514 	} else {
1515 		struct scsi_extended_sense *ext_sensep =
1516 		    (struct scsi_extended_sense *)sense_buffer;
1517 		ascq = ext_sensep->es_qual_code;
1518 	}
1519 	return (ascq);
1520 }
1521 
1522 void scsi_ext_sense_fields(uint8_t *sense_buffer, int sense_buf_len,
1523     uint8_t **information, uint8_t **cmd_spec_info, uint8_t **fru_code,
1524     uint8_t **sk_specific, uint8_t **stream_flags)
1525 {
1526 	int sense_fmt;
1527 
1528 	/*
1529 	 * Sanity check sense data and determine the format
1530 	 */
1531 	sense_fmt = scsi_validate_sense(sense_buffer, sense_buf_len, NULL);
1532 
1533 	/*
1534 	 * Initialize any requested data to 0
1535 	 */
1536 	if (information) {
1537 		*information = NULL;
1538 	}
1539 	if (cmd_spec_info) {
1540 		*cmd_spec_info = NULL;
1541 	}
1542 	if (fru_code) {
1543 		*fru_code = NULL;
1544 	}
1545 	if (sk_specific) {
1546 		*sk_specific = NULL;
1547 	}
1548 	if (stream_flags) {
1549 		*stream_flags = NULL;
1550 	}
1551 
1552 	if (sense_fmt == SENSE_DESCR_FORMAT) {
1553 		struct scsi_descr_template *sdt = NULL;
1554 
1555 		while (scsi_get_next_descr(sense_buffer,
1556 		    sense_buf_len, &sdt) != -1) {
1557 			switch (sdt->sdt_descr_type) {
1558 			case DESCR_INFORMATION: {
1559 				struct scsi_information_sense_descr *isd =
1560 				    (struct scsi_information_sense_descr *)
1561 				    sdt;
1562 				if (information) {
1563 					*information =
1564 					    &isd->isd_information[0];
1565 				}
1566 				break;
1567 			}
1568 			case DESCR_COMMAND_SPECIFIC: {
1569 				struct scsi_cmd_specific_sense_descr *csd =
1570 				    (struct scsi_cmd_specific_sense_descr *)
1571 				    sdt;
1572 				if (cmd_spec_info) {
1573 					*cmd_spec_info =
1574 					    &csd->css_cmd_specific_info[0];
1575 				}
1576 				break;
1577 			}
1578 			case DESCR_SENSE_KEY_SPECIFIC: {
1579 				struct scsi_sk_specific_sense_descr *ssd =
1580 				    (struct scsi_sk_specific_sense_descr *)
1581 				    sdt;
1582 				if (sk_specific) {
1583 					*sk_specific =
1584 					    (uint8_t *)&ssd->sss_data;
1585 				}
1586 				break;
1587 			}
1588 			case DESCR_FRU: {
1589 				struct scsi_fru_sense_descr *fsd =
1590 				    (struct scsi_fru_sense_descr *)
1591 				    sdt;
1592 				if (fru_code) {
1593 					*fru_code = &fsd->fs_fru_code;
1594 				}
1595 				break;
1596 			}
1597 			case DESCR_STREAM_COMMANDS: {
1598 				struct scsi_stream_cmd_sense_descr *strsd =
1599 				    (struct scsi_stream_cmd_sense_descr *)
1600 				    sdt;
1601 				if (stream_flags) {
1602 					*stream_flags =
1603 					    (uint8_t *)&strsd->scs_data;
1604 				}
1605 				break;
1606 			}
1607 			case DESCR_BLOCK_COMMANDS: {
1608 				struct scsi_block_cmd_sense_descr *bsd =
1609 				    (struct scsi_block_cmd_sense_descr *)
1610 				    sdt;
1611 				/*
1612 				 * The "Block Command" sense descriptor
1613 				 * contains an ili bit that we can store
1614 				 * in the stream specific data if it is
1615 				 * available.  We shouldn't see both
1616 				 * a block command and a stream command
1617 				 * descriptor in the same collection
1618 				 * of sense data.
1619 				 */
1620 				if (stream_flags) {
1621 					/*
1622 					 * Can't take an address of a bitfield,
1623 					 * but the flags are just after the
1624 					 * bcs_reserved field.
1625 					 */
1626 					*stream_flags =
1627 					    (uint8_t *)&bsd->bcs_reserved + 1;
1628 				}
1629 				break;
1630 			}
1631 			}
1632 		}
1633 	} else {
1634 		struct scsi_extended_sense *es =
1635 		    (struct scsi_extended_sense *)sense_buffer;
1636 
1637 		/* Get data from fixed sense buffer */
1638 		if (information && es->es_valid) {
1639 			*information = &es->es_info_1;
1640 		}
1641 		if (cmd_spec_info && es->es_valid) {
1642 			*cmd_spec_info = &es->es_cmd_info[0];
1643 		}
1644 		if (fru_code) {
1645 			*fru_code = &es->es_fru_code;
1646 		}
1647 		if (sk_specific) {
1648 			*sk_specific = &es->es_skey_specific[0];
1649 		}
1650 		if (stream_flags) {
1651 			/*
1652 			 * Can't take the address of a bit field,
1653 			 * but the stream flags are located just after
1654 			 * the es_segnum field;
1655 			 */
1656 			*stream_flags = &es->es_segnum + 1;
1657 		}
1658 	}
1659 }
1660 
1661 boolean_t
1662 scsi_sense_info_uint64(uint8_t *sense_buffer, int sense_buf_len,
1663     uint64_t *information)
1664 {
1665 	boolean_t valid;
1666 	int sense_fmt;
1667 
1668 	ASSERT(sense_buffer != NULL);
1669 	ASSERT(information != NULL);
1670 
1671 	/* Validate sense data and get format */
1672 	sense_fmt = scsi_validate_sense(sense_buffer, sense_buf_len, NULL);
1673 
1674 	if (sense_fmt == SENSE_UNUSABLE) {
1675 		/* Information is not valid */
1676 		valid = 0;
1677 	} else if (sense_fmt == SENSE_FIXED_FORMAT) {
1678 		struct scsi_extended_sense *es =
1679 		    (struct scsi_extended_sense *)sense_buffer;
1680 
1681 		*information = (uint64_t)SCSI_READ32(&es->es_info_1);
1682 
1683 		valid = es->es_valid;
1684 	} else {
1685 		/* Sense data is descriptor format */
1686 		struct scsi_information_sense_descr *isd;
1687 
1688 		isd = (struct scsi_information_sense_descr *)
1689 		    scsi_find_sense_descr(sense_buffer, sense_buf_len,
1690 			DESCR_INFORMATION);
1691 
1692 		if (isd) {
1693 			*information = SCSI_READ64(isd->isd_information);
1694 			valid = 1;
1695 		} else {
1696 			valid = 0;
1697 		}
1698 	}
1699 
1700 	return (valid);
1701 }
1702 
1703 boolean_t
1704 scsi_sense_cmdspecific_uint64(uint8_t *sense_buffer, int sense_buf_len,
1705     uint64_t *cmd_specific_info)
1706 {
1707 	boolean_t valid;
1708 	int sense_fmt;
1709 
1710 	ASSERT(sense_buffer != NULL);
1711 	ASSERT(cmd_specific_info != NULL);
1712 
1713 	/* Validate sense data and get format */
1714 	sense_fmt = scsi_validate_sense(sense_buffer, sense_buf_len, NULL);
1715 
1716 	if (sense_fmt == SENSE_UNUSABLE) {
1717 		/* Command specific info is not valid */
1718 		valid = 0;
1719 	} else if (sense_fmt == SENSE_FIXED_FORMAT) {
1720 		struct scsi_extended_sense *es =
1721 		    (struct scsi_extended_sense *)sense_buffer;
1722 
1723 		*cmd_specific_info = (uint64_t)SCSI_READ32(es->es_cmd_info);
1724 
1725 		valid = es->es_valid;
1726 	} else {
1727 		/* Sense data is descriptor format */
1728 		struct scsi_cmd_specific_sense_descr *c;
1729 
1730 		c = (struct scsi_cmd_specific_sense_descr *)
1731 		    scsi_find_sense_descr(sense_buffer, sense_buf_len,
1732 			DESCR_COMMAND_SPECIFIC);
1733 
1734 		if (c) {
1735 			valid = 1;
1736 			*cmd_specific_info =
1737 			    SCSI_READ64(c->css_cmd_specific_info);
1738 		} else {
1739 			valid = 0;
1740 		}
1741 	}
1742 
1743 	return (valid);
1744 }
1745 
1746 uint8_t *
1747 scsi_find_sense_descr(uint8_t *sdsp, int sense_buf_len, int req_descr_type)
1748 {
1749 	struct scsi_descr_template *sdt = NULL;
1750 
1751 	while (scsi_get_next_descr(sdsp, sense_buf_len, &sdt) != -1) {
1752 		ASSERT(sdt != NULL);
1753 		if (sdt->sdt_descr_type == req_descr_type) {
1754 			/* Found requested descriptor type */
1755 			break;
1756 		}
1757 	}
1758 
1759 	return ((uint8_t *)sdt);
1760 }
1761 
1762 /*
1763  * Sense Descriptor format is:
1764  *
1765  * <Descriptor type> <Descriptor length> <Descriptor data> ...
1766  *
1767  * 2 must be added to the descriptor length value to get the
1768  * total descriptor length sense the stored length does not
1769  * include the "type" and "additional length" fields.
1770  */
1771 
1772 #define	NEXT_DESCR_PTR(ndp_descr) \
1773 	((struct scsi_descr_template *)(((uint8_t *)(ndp_descr)) + \
1774 	    ((ndp_descr)->sdt_addl_length + \
1775 	    sizeof (struct scsi_descr_template))))
1776 
1777 static int
1778 scsi_get_next_descr(uint8_t *sense_buffer,
1779     int sense_buf_len, struct scsi_descr_template **descrpp)
1780 {
1781 	struct scsi_descr_sense_hdr *sdsp =
1782 	    (struct scsi_descr_sense_hdr *)sense_buffer;
1783 	struct scsi_descr_template *cur_descr;
1784 	boolean_t find_first;
1785 	int valid_sense_length;
1786 
1787 	ASSERT(descrpp != NULL);
1788 	find_first = (*descrpp == NULL);
1789 
1790 	/*
1791 	 * If no descriptor is passed in then return the first
1792 	 * descriptor
1793 	 */
1794 	if (find_first) {
1795 		/*
1796 		 * The first descriptor will immediately follow the header
1797 		 * (Pointer arithmetic)
1798 		 */
1799 		cur_descr = (struct scsi_descr_template *)(sdsp+1);
1800 	} else {
1801 		cur_descr = *descrpp;
1802 		ASSERT(cur_descr > (struct scsi_descr_template *)sdsp);
1803 	}
1804 
1805 	/* Assume no more descriptors are available */
1806 	*descrpp = NULL;
1807 
1808 	/*
1809 	 * Calculate the amount of valid sense data -- make sure the length
1810 	 * byte in this descriptor lies within the valid sense data.
1811 	 */
1812 	valid_sense_length =
1813 	    min((sizeof (struct scsi_descr_sense_hdr) +
1814 	    sdsp->ds_addl_sense_length),
1815 	    sense_buf_len);
1816 
1817 	/*
1818 	 * Make sure this descriptor is complete (either the first
1819 	 * descriptor or the descriptor passed in)
1820 	 */
1821 	if (scsi_validate_descr(sdsp, valid_sense_length, cur_descr) !=
1822 	    DESCR_GOOD) {
1823 		return (-1);
1824 	}
1825 
1826 	/*
1827 	 * If we were looking for the first descriptor go ahead and return it
1828 	 */
1829 	if (find_first) {
1830 		*descrpp = cur_descr;
1831 		return ((*descrpp)->sdt_descr_type);
1832 	}
1833 
1834 	/*
1835 	 * Get pointer to next descriptor
1836 	 */
1837 	cur_descr = NEXT_DESCR_PTR(cur_descr);
1838 
1839 	/*
1840 	 * Make sure this descriptor is also complete.
1841 	 */
1842 	if (scsi_validate_descr(sdsp, valid_sense_length, cur_descr) !=
1843 	    DESCR_GOOD) {
1844 		return (-1);
1845 	}
1846 
1847 	*descrpp = (struct scsi_descr_template *)cur_descr;
1848 	return ((*descrpp)->sdt_descr_type);
1849 }
1850 
1851 static int
1852 scsi_validate_descr(struct scsi_descr_sense_hdr *sdsp,
1853     int valid_sense_length, struct scsi_descr_template *descrp)
1854 {
1855 	int descr_offset, next_descr_offset;
1856 
1857 	/*
1858 	 * Make sure length is present
1859 	 */
1860 	descr_offset = (uint8_t *)descrp - (uint8_t *)sdsp;
1861 	if (descr_offset + sizeof (struct scsi_descr_template) >
1862 	    valid_sense_length) {
1863 		return (DESCR_PARTIAL);
1864 	}
1865 
1866 	/*
1867 	 * Check if length is 0 (no more descriptors)
1868 	 */
1869 	if (descrp->sdt_addl_length == 0) {
1870 		return (DESCR_END);
1871 	}
1872 
1873 	/*
1874 	 * Make sure the rest of the descriptor is present
1875 	 */
1876 	next_descr_offset =
1877 	    (uint8_t *)NEXT_DESCR_PTR(descrp) - (uint8_t *)sdsp;
1878 	if (next_descr_offset > valid_sense_length) {
1879 		return (DESCR_PARTIAL);
1880 	}
1881 
1882 	return (DESCR_GOOD);
1883 }
1884 
1885 /*
1886  * Internal data structure for handling uscsi command.
1887  */
1888 typedef	struct	uscsi_i_cmd {
1889 	struct uscsi_cmd	uic_cmd;
1890 	caddr_t			uic_rqbuf;
1891 	uchar_t			uic_rqlen;
1892 	caddr_t			uic_cdb;
1893 	int			uic_flag;
1894 	struct scsi_address	*uic_ap;
1895 } uscsi_i_cmd_t;
1896 
1897 #if !defined(lint)
1898 _NOTE(SCHEME_PROTECTS_DATA("unshared data", uscsi_i_cmd))
1899 #endif
1900 
1901 /*ARGSUSED*/
1902 static void
1903 scsi_uscsi_mincnt(struct buf *bp)
1904 {
1905 	/*
1906 	 * Do not break up because the CDB count would then be
1907 	 * incorrect and create spurious data underrun errors.
1908 	 */
1909 }
1910 
1911 /*
1912  *    Function: scsi_uscsi_alloc_and_copyin
1913  *
1914  * Description: Target drivers call this function to allocate memeory,
1915  *    copy in, and convert ILP32/LP64 to make preparations for handling
1916  *    uscsi commands.
1917  *
1918  *   Arguments: arg - pointer to the caller's uscsi command struct
1919  *    flag      - mode, corresponds to ioctl(9e) 'mode'
1920  *    ap        - SCSI address structure
1921  *    uscmdp    - pointer to the converted uscsi command
1922  *
1923  * Return code: 0
1924  *    EFAULT
1925  *    EINVAL
1926  *
1927  *     Context: Never called at interrupt context.
1928  */
1929 
1930 int
1931 scsi_uscsi_alloc_and_copyin(intptr_t arg, int flag, struct scsi_address *ap,
1932     struct uscsi_cmd **uscmdp)
1933 {
1934 #ifdef _MULTI_DATAMODEL
1935 	/*
1936 	 * For use when a 32 bit app makes a call into a
1937 	 * 64 bit ioctl
1938 	 */
1939 	struct uscsi_cmd32	uscsi_cmd_32_for_64;
1940 	struct uscsi_cmd32	*ucmd32 = &uscsi_cmd_32_for_64;
1941 #endif /* _MULTI_DATAMODEL */
1942 	struct uscsi_i_cmd	*uicmd;
1943 	struct uscsi_cmd	*uscmd;
1944 	int	max_hba_cdb;
1945 	int	rval;
1946 
1947 	/*
1948 	 * In order to not worry about where the uscsi structure came
1949 	 * from (or where the cdb it points to came from) we're going
1950 	 * to make kmem_alloc'd copies of them here. This will also
1951 	 * allow reference to the data they contain long after this
1952 	 * process has gone to sleep and its kernel stack has been
1953 	 * unmapped, etc. First get some memory for the uscsi_cmd
1954 	 * struct and copy the contents of the given uscsi_cmd struct
1955 	 * into it. We also save infos of the uscsi command by using
1956 	 * uicmd to supply referrence for the copyout operation.
1957 	 */
1958 	uicmd = (struct uscsi_i_cmd *)
1959 	    kmem_zalloc(sizeof (struct uscsi_i_cmd), KM_SLEEP);
1960 	*uscmdp = &(uicmd->uic_cmd);
1961 	uscmd = *uscmdp;
1962 
1963 #ifdef _MULTI_DATAMODEL
1964 	switch (ddi_model_convert_from(flag & FMODELS)) {
1965 	case DDI_MODEL_ILP32:
1966 		if (ddi_copyin((void *)arg, ucmd32, sizeof (*ucmd32), flag)) {
1967 			rval = EFAULT;
1968 			goto done;
1969 		}
1970 		/*
1971 		 * Convert the ILP32 uscsi data from the
1972 		 * application to LP64 for internal use.
1973 		 */
1974 		uscsi_cmd32touscsi_cmd(ucmd32, uscmd);
1975 		break;
1976 	case DDI_MODEL_NONE:
1977 		if (ddi_copyin((void *)arg, uscmd, sizeof (*uscmd), flag)) {
1978 			rval = EFAULT;
1979 			goto done;
1980 		}
1981 		break;
1982 	}
1983 #else /* ! _MULTI_DATAMODEL */
1984 	if (ddi_copyin((void *)arg, uscmd, sizeof (*uscmd), flag)) {
1985 		rval = EFAULT;
1986 		goto done;
1987 	}
1988 #endif /* _MULTI_DATAMODEL */
1989 
1990 	uicmd->uic_rqbuf = uscmd->uscsi_rqbuf;
1991 	uicmd->uic_rqlen = uscmd->uscsi_rqlen;
1992 	uicmd->uic_cdb   = uscmd->uscsi_cdb;
1993 	uicmd->uic_flag  = flag;
1994 	uicmd->uic_ap    = ap;
1995 
1996 	/*
1997 	 * Skip the following steps if we meet RESET commands.
1998 	 */
1999 	if (uscmd->uscsi_flags &
2000 	    (USCSI_RESET_LUN | USCSI_RESET_TARGET | USCSI_RESET_ALL)) {
2001 		uscmd->uscsi_rqbuf = NULL;
2002 		uscmd->uscsi_cdb = NULL;
2003 		return (0);
2004 	}
2005 
2006 	/*
2007 	 * Perfunctory sanity checks. Get the maximum hba supported
2008 	 * cdb length first.
2009 	 */
2010 	max_hba_cdb = scsi_ifgetcap(ap, "max-cdb-length", 1);
2011 	if (max_hba_cdb < CDB_GROUP0) {
2012 		max_hba_cdb = CDB_GROUP4;
2013 	}
2014 	if (uscmd->uscsi_cdblen < CDB_GROUP0 ||
2015 	    uscmd->uscsi_cdblen > max_hba_cdb) {
2016 		rval = EINVAL;
2017 		goto done;
2018 	}
2019 	if ((uscmd->uscsi_flags & USCSI_RQENABLE) &&
2020 	    (uscmd->uscsi_rqlen == 0 || uscmd->uscsi_rqbuf == NULL)) {
2021 		rval = EINVAL;
2022 		goto done;
2023 	}
2024 
2025 	/*
2026 	 * Now we get some space for the CDB, and copy the given CDB into
2027 	 * it. Use ddi_copyin() in case the data is in user space.
2028 	 */
2029 	uscmd->uscsi_cdb = kmem_zalloc((size_t)uscmd->uscsi_cdblen, KM_SLEEP);
2030 	if (ddi_copyin(uicmd->uic_cdb, uscmd->uscsi_cdb,
2031 	    (uint_t)uscmd->uscsi_cdblen, flag) != 0) {
2032 		kmem_free(uscmd->uscsi_cdb, (size_t)uscmd->uscsi_cdblen);
2033 		rval = EFAULT;
2034 		goto done;
2035 	}
2036 
2037 	/*
2038 	 * If the length of the CDB is greater than 16 bytes, it must be
2039 	 * a variable length CDB (i.e. the opcode must be 0x7f).
2040 	 */
2041 	if (uscmd->uscsi_cdblen > SCSI_CDB_SIZE &&
2042 	    uscmd->uscsi_cdb[0] != SCMD_VAR_LEN) {
2043 		kmem_free(uscmd->uscsi_cdb, (size_t)uscmd->uscsi_cdblen);
2044 		rval = EINVAL;
2045 		goto done;
2046 	}
2047 
2048 	/*
2049 	 * Initialize Request Sense buffering, if requested.
2050 	 */
2051 	if (uscmd->uscsi_flags & USCSI_RQENABLE) {
2052 		/*
2053 		 * Here uscmd->uscsi_rqbuf currently points to the caller's
2054 		 * buffer, but we replace this with a kernel buffer that
2055 		 * we allocate to use with the sense data. The sense data
2056 		 * (if present) gets copied into this new buffer before the
2057 		 * command is completed.  Then we copy the sense data from
2058 		 * our allocated buf into the caller's buffer below. Note
2059 		 * that uscmd->uscsi_rqbuf and uscmd->uscsi_rqlen are used
2060 		 * below to perform the copy back to the caller's buf.
2061 		 */
2062 		uscmd->uscsi_rqlen = min(SENSE_LENGTH, uscmd->uscsi_rqlen);
2063 		uscmd->uscsi_rqbuf = kmem_zalloc(uscmd->uscsi_rqlen, KM_SLEEP);
2064 		uscmd->uscsi_rqresid = uscmd->uscsi_rqlen;
2065 	} else {
2066 		uscmd->uscsi_rqbuf = NULL;
2067 		uscmd->uscsi_rqlen = 0;
2068 		uscmd->uscsi_rqresid = 0;
2069 	}
2070 	return (0);
2071 
2072 done:
2073 	kmem_free(uicmd, sizeof (struct uscsi_i_cmd));
2074 	return (rval);
2075 }
2076 
2077 /*
2078  *    Function: scsi_uscsi_handle_cmd
2079  *
2080  * Description: Target drivers call this function to handle uscsi commands.
2081  *
2082  *   Arguments: dev - device number
2083  *    dataspace     - UIO_USERSPACE or UIO_SYSSPACE
2084  *    uscmd         - pointer to the converted uscsi command
2085  *    strat         - pointer to the driver's strategy routine
2086  *    bp            - buf struct ptr
2087  *    private_data  - pointer to bp->b_private
2088  *
2089  * Return code: 0
2090  *    EIO      - scsi_reset() failed, or see biowait()/physio() codes.
2091  *    EINVAL
2092  *    return code of biowait(9F) or physio(9F):
2093  *      EIO    - IO error
2094  *      ENXIO
2095  *      EACCES - reservation conflict
2096  *
2097  *     Context: Never called at interrupt context.
2098  */
2099 
2100 int
2101 scsi_uscsi_handle_cmd(dev_t dev, enum uio_seg dataspace,
2102     struct uscsi_cmd *uscmd, int (*strat)(struct buf *),
2103     struct buf *bp, void *private_data)
2104 {
2105 	struct uscsi_i_cmd	*uicmd = (struct uscsi_i_cmd *)uscmd;
2106 	int	bp_alloc_flag = 0;
2107 	int	rval;
2108 
2109 	/*
2110 	 * Perform resets directly; no need to generate a command to do it.
2111 	 */
2112 	if (uscmd->uscsi_flags &
2113 	    (USCSI_RESET_LUN | USCSI_RESET_TARGET | USCSI_RESET_ALL)) {
2114 		int flags = (uscmd->uscsi_flags & USCSI_RESET_ALL) ?
2115 		    RESET_ALL : ((uscmd->uscsi_flags & USCSI_RESET_TARGET) ?
2116 		    RESET_TARGET : RESET_LUN);
2117 		if (scsi_reset(uicmd->uic_ap, flags) == 0) {
2118 			/* Reset attempt was unsuccessful */
2119 			return (EIO);
2120 		}
2121 		return (0);
2122 	}
2123 
2124 	/*
2125 	 * Force asynchronous mode, if necessary.  Doing this here
2126 	 * has the unfortunate effect of running other queued
2127 	 * commands async also, but since the main purpose of this
2128 	 * capability is downloading new drive firmware, we can
2129 	 * probably live with it.
2130 	 */
2131 	if (uscmd->uscsi_flags & USCSI_ASYNC) {
2132 		if (scsi_ifgetcap(uicmd->uic_ap, "synchronous", 1) == 1) {
2133 			if (scsi_ifsetcap(uicmd->uic_ap, "synchronous",
2134 			    0, 1) != 1) {
2135 				return (EINVAL);
2136 			}
2137 		}
2138 	}
2139 
2140 	/*
2141 	 * Re-enable synchronous mode, if requested.
2142 	 */
2143 	if (uscmd->uscsi_flags & USCSI_SYNC) {
2144 		if (scsi_ifgetcap(uicmd->uic_ap, "synchronous", 1) == 0) {
2145 			rval = scsi_ifsetcap(uicmd->uic_ap, "synchronous",
2146 			    1, 1);
2147 		}
2148 	}
2149 
2150 	/*
2151 	 * If bp is NULL, allocate space here.
2152 	 */
2153 	if (bp == NULL) {
2154 		bp = getrbuf(KM_SLEEP);
2155 		bp->b_private = private_data;
2156 		bp_alloc_flag = 1;
2157 	}
2158 
2159 	/*
2160 	 * If we're going to do actual I/O, let physio do all the right things.
2161 	 */
2162 	if (uscmd->uscsi_buflen != 0) {
2163 		struct iovec	aiov;
2164 		struct uio	auio;
2165 		struct uio	*uio = &auio;
2166 
2167 		bzero(&auio, sizeof (struct uio));
2168 		bzero(&aiov, sizeof (struct iovec));
2169 		aiov.iov_base = uscmd->uscsi_bufaddr;
2170 		aiov.iov_len  = uscmd->uscsi_buflen;
2171 		uio->uio_iov  = &aiov;
2172 
2173 		uio->uio_iovcnt  = 1;
2174 		uio->uio_resid   = uscmd->uscsi_buflen;
2175 		uio->uio_segflg  = dataspace;
2176 
2177 		/*
2178 		 * physio() will block here until the command completes....
2179 		 */
2180 		rval = physio(strat, bp, dev,
2181 		    ((uscmd->uscsi_flags & USCSI_READ) ? B_READ : B_WRITE),
2182 		    scsi_uscsi_mincnt, uio);
2183 	} else {
2184 		/*
2185 		 * We have to mimic that physio would do here! Argh!
2186 		 */
2187 		bp->b_flags  = B_BUSY |
2188 		    ((uscmd->uscsi_flags & USCSI_READ) ? B_READ : B_WRITE);
2189 		bp->b_edev   = dev;
2190 		bp->b_dev    = cmpdev(dev);	/* maybe unnecessary? */
2191 		bp->b_bcount = 0;
2192 		bp->b_blkno  = 0;
2193 		bp->b_resid  = 0;
2194 
2195 		(void) (*strat)(bp);
2196 		rval = biowait(bp);
2197 	}
2198 	uscmd->uscsi_resid = bp->b_resid;
2199 
2200 	if (bp_alloc_flag == 1) {
2201 		bp_mapout(bp);
2202 		freerbuf(bp);
2203 	}
2204 
2205 	return (rval);
2206 }
2207 
2208 /*
2209  *    Function: scsi_uscsi_copyout_and_free
2210  *
2211  * Description: Target drivers call this function to undo what was done by
2212  *    scsi_uscsi_alloc_and_copyin.
2213  *
2214  *   Arguments: arg - pointer to the uscsi command to be returned
2215  *    uscmd     - pointer to the converted uscsi command
2216  *
2217  * Return code: 0
2218  *    EFAULT
2219  *
2220  *     Context: Never called at interrupt context.
2221  */
2222 
2223 int
2224 scsi_uscsi_copyout_and_free(intptr_t arg, struct uscsi_cmd *uscmd)
2225 {
2226 #ifdef _MULTI_DATAMODEL
2227 	/*
2228 	 * For use when a 32 bit app makes a call into a
2229 	 * 64 bit ioctl.
2230 	 */
2231 	struct uscsi_cmd32	uscsi_cmd_32_for_64;
2232 	struct uscsi_cmd32	*ucmd32 = &uscsi_cmd_32_for_64;
2233 #endif /* _MULTI_DATAMODEL */
2234 	struct uscsi_i_cmd	*uicmd = (struct uscsi_i_cmd *)uscmd;
2235 	caddr_t	k_rqbuf;
2236 	uchar_t	k_rqlen;
2237 	caddr_t	k_cdb;
2238 	int	rval = 0;
2239 
2240 	/*
2241 	 * If the caller wants sense data, copy back whatever sense data
2242 	 * we may have gotten, and update the relevant rqsense info.
2243 	 */
2244 	if ((uscmd->uscsi_flags & USCSI_RQENABLE) &&
2245 	    (uscmd->uscsi_rqbuf != NULL)) {
2246 		int rqlen = uscmd->uscsi_rqlen - uscmd->uscsi_rqresid;
2247 		rqlen = min(((int)uicmd->uic_rqlen), rqlen);
2248 		uscmd->uscsi_rqresid = uicmd->uic_rqlen - rqlen;
2249 		/*
2250 		 * Copy out the sense data for user process.
2251 		 */
2252 		if ((uicmd->uic_rqbuf != NULL) && (rqlen != 0)) {
2253 			if (ddi_copyout(uscmd->uscsi_rqbuf,
2254 			    uicmd->uic_rqbuf, rqlen, uicmd->uic_flag) != 0) {
2255 				rval = EFAULT;
2256 			}
2257 		}
2258 	}
2259 
2260 	/*
2261 	 * Free allocated resources and return, mapout the buf in case it was
2262 	 * mapped in by a lower layer.
2263 	 */
2264 	k_rqbuf = uscmd->uscsi_rqbuf;
2265 	k_rqlen = uscmd->uscsi_rqlen;
2266 	k_cdb   = uscmd->uscsi_cdb;
2267 	uscmd->uscsi_rqbuf = uicmd->uic_rqbuf;
2268 	uscmd->uscsi_rqlen = uicmd->uic_rqlen;
2269 	uscmd->uscsi_cdb   = uicmd->uic_cdb;
2270 
2271 #ifdef _MULTI_DATAMODEL
2272 	switch (ddi_model_convert_from(uicmd->uic_flag & FMODELS)) {
2273 	case DDI_MODEL_ILP32:
2274 		/*
2275 		 * Convert back to ILP32 before copyout to the
2276 		 * application
2277 		 */
2278 		uscsi_cmdtouscsi_cmd32(uscmd, ucmd32);
2279 		if (ddi_copyout(ucmd32, (void *)arg, sizeof (*ucmd32),
2280 		    uicmd->uic_flag)) {
2281 			rval = EFAULT;
2282 		}
2283 		break;
2284 	case DDI_MODEL_NONE:
2285 		if (ddi_copyout(uscmd, (void *)arg, sizeof (*uscmd),
2286 		    uicmd->uic_flag)) {
2287 			rval = EFAULT;
2288 		}
2289 		break;
2290 	}
2291 #else /* _MULTI_DATAMODE */
2292 	if (ddi_copyout(uscmd, (void *)arg, sizeof (*uscmd), uicmd->uic_flag)) {
2293 		rval = EFAULT;
2294 	}
2295 #endif /* _MULTI_DATAMODE */
2296 
2297 	if (k_rqbuf != NULL) {
2298 		kmem_free(k_rqbuf, k_rqlen);
2299 	}
2300 	if (k_cdb != NULL) {
2301 		kmem_free(k_cdb, (size_t)uscmd->uscsi_cdblen);
2302 	}
2303 	kmem_free(uicmd, sizeof (struct uscsi_i_cmd));
2304 
2305 	return (rval);
2306 }
2307