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