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