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 2006 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 
31 /*
32  * Utility SCSI routines
33  */
34 
35 /*
36  * Polling support routines
37  */
38 
39 extern uintptr_t scsi_callback_id;
40 
41 /*
42  * Common buffer for scsi_log
43  */
44 
45 extern kmutex_t scsi_log_mutex;
46 static char scsi_log_buffer[MAXPATHLEN + 1];
47 
48 
49 #define	A_TO_TRAN(ap)	(ap->a_hba_tran)
50 #define	P_TO_TRAN(pkt)	((pkt)->pkt_address.a_hba_tran)
51 #define	P_TO_ADDR(pkt)	(&((pkt)->pkt_address))
52 
53 #define	CSEC		10000			/* usecs */
54 #define	SEC_TO_CSEC	(1000000/CSEC)
55 
56 extern ddi_dma_attr_t scsi_alloc_attr;
57 
58 /*PRINTFLIKE4*/
59 static void impl_scsi_log(dev_info_t *dev, char *label, uint_t level,
60     const char *fmt, ...) __KPRINTFLIKE(4);
61 /*PRINTFLIKE4*/
62 static void v_scsi_log(dev_info_t *dev, char *label, uint_t level,
63     const char *fmt, va_list ap) __KVPRINTFLIKE(4);
64 
65 static int
66 scsi_get_next_descr(uint8_t *sdsp,
67     int sense_buf_len, struct scsi_descr_template **descrpp);
68 
69 #define	DESCR_GOOD	0
70 #define	DESCR_PARTIAL	1
71 #define	DESCR_END	2
72 
73 static int
74 scsi_validate_descr(struct scsi_descr_sense_hdr *sdsp,
75     int valid_sense_length, struct scsi_descr_template *descrp);
76 
77 int
78 scsi_poll(struct scsi_pkt *pkt)
79 {
80 	register int busy_count, rval = -1, savef;
81 	long savet;
82 	void (*savec)();
83 	extern int do_polled_io;
84 
85 	/*
86 	 * save old flags..
87 	 */
88 	savef = pkt->pkt_flags;
89 	savec = pkt->pkt_comp;
90 	savet = pkt->pkt_time;
91 
92 	pkt->pkt_flags |= FLAG_NOINTR;
93 
94 	/*
95 	 * XXX there is nothing in the SCSA spec that states that we should not
96 	 * do a callback for polled cmds; however, removing this will break sd
97 	 * and probably other target drivers
98 	 */
99 	pkt->pkt_comp = 0;
100 
101 	/*
102 	 * we don't like a polled command without timeout.
103 	 * 60 seconds seems long enough.
104 	 */
105 	if (pkt->pkt_time == 0)
106 		pkt->pkt_time = SCSI_POLL_TIMEOUT;
107 
108 	/*
109 	 * Send polled cmd.
110 	 *
111 	 * We do some error recovery for various errors.  Tran_busy,
112 	 * queue full, and non-dispatched commands are retried every 10 msec.
113 	 * as they are typically transient failures.  Busy status is retried
114 	 * every second as this status takes a while to change.
115 	 */
116 	for (busy_count = 0; busy_count < (pkt->pkt_time * SEC_TO_CSEC);
117 		busy_count++) {
118 		int rc;
119 		int poll_delay;
120 
121 		/*
122 		 * Initialize pkt status variables.
123 		 */
124 		*pkt->pkt_scbp = pkt->pkt_reason = pkt->pkt_state = 0;
125 
126 		if ((rc = scsi_transport(pkt)) != TRAN_ACCEPT) {
127 			if (rc != TRAN_BUSY) {
128 				/* Transport failed - give up. */
129 				break;
130 			} else {
131 				/* Transport busy - try again. */
132 				poll_delay = 1 *CSEC;		/* 10 msec. */
133 			}
134 		} else {
135 			/*
136 			 * Transport accepted - check pkt status.
137 			 */
138 			rc = (*pkt->pkt_scbp) & STATUS_MASK;
139 
140 			if (pkt->pkt_reason == CMD_CMPLT &&
141 			    rc == STATUS_GOOD) {
142 				/* No error - we're done */
143 				rval = 0;
144 				break;
145 
146 			} else if (pkt->pkt_reason == CMD_INCOMPLETE &&
147 			    pkt->pkt_state == 0) {
148 				/* Pkt not dispatched - try again. */
149 				poll_delay = 1 *CSEC;		/* 10 msec. */
150 
151 			} else if (pkt->pkt_reason == CMD_CMPLT &&
152 			    rc == STATUS_QFULL) {
153 				/* Queue full - try again. */
154 				poll_delay = 1 *CSEC;		/* 10 msec. */
155 
156 			} else if (pkt->pkt_reason == CMD_CMPLT &&
157 			    rc == STATUS_BUSY) {
158 				/* Busy - try again. */
159 				poll_delay = 100 *CSEC;		/* 1 sec. */
160 				busy_count += (SEC_TO_CSEC - 1);
161 
162 			} else {
163 				/* BAD status - give up. */
164 				break;
165 			}
166 		}
167 
168 		if ((curthread->t_flag & T_INTR_THREAD) == 0 &&
169 		    !do_polled_io) {
170 			delay(drv_usectohz(poll_delay));
171 		} else {
172 			/* we busy wait during cpr_dump or interrupt threads */
173 			drv_usecwait(poll_delay);
174 		}
175 	}
176 
177 	pkt->pkt_flags = savef;
178 	pkt->pkt_comp = savec;
179 	pkt->pkt_time = savet;
180 	return (rval);
181 }
182 
183 /*
184  * Command packaging routines.
185  *
186  * makecom_g*() are original routines and scsi_setup_cdb()
187  * is the new and preferred routine.
188  */
189 
190 /*
191  * These routines put LUN information in CDB byte 1 bits 7-5.
192  * This was required in SCSI-1. SCSI-2 allowed it but it preferred
193  * sending LUN information as part of IDENTIFY message.
194  * This is not allowed in SCSI-3.
195  */
196 
197 void
198 makecom_g0(struct scsi_pkt *pkt, struct scsi_device *devp,
199     int flag, int cmd, int addr, int cnt)
200 {
201 	MAKECOM_G0(pkt, devp, flag, cmd, addr, (uchar_t)cnt);
202 }
203 
204 void
205 makecom_g0_s(struct scsi_pkt *pkt, struct scsi_device *devp,
206     int flag, int cmd, int cnt, int fixbit)
207 {
208 	MAKECOM_G0_S(pkt, devp, flag, cmd, cnt, (uchar_t)fixbit);
209 }
210 
211 void
212 makecom_g1(struct scsi_pkt *pkt, struct scsi_device *devp,
213     int flag, int cmd, int addr, int cnt)
214 {
215 	MAKECOM_G1(pkt, devp, flag, cmd, addr, cnt);
216 }
217 
218 void
219 makecom_g5(struct scsi_pkt *pkt, struct scsi_device *devp,
220     int flag, int cmd, int addr, int cnt)
221 {
222 	MAKECOM_G5(pkt, devp, flag, cmd, addr, cnt);
223 }
224 
225 /*
226  * Following routine does not put LUN information in CDB.
227  * This interface must be used for SCSI-2 targets having
228  * more than 8 LUNs or a SCSI-3 target.
229  */
230 int
231 scsi_setup_cdb(union scsi_cdb *cdbp, uchar_t cmd, uint_t addr, uint_t cnt,
232     uint_t addtl_cdb_data)
233 {
234 	uint_t	addr_cnt;
235 
236 	cdbp->scc_cmd = cmd;
237 
238 	switch (CDB_GROUPID(cmd)) {
239 		case CDB_GROUPID_0:
240 			/*
241 			 * The following calculation is to take care of
242 			 * the fact that format of some 6 bytes tape
243 			 * command is different (compare 6 bytes disk and
244 			 * tape read commands).
245 			 */
246 			addr_cnt = (addr << 8) + cnt;
247 			addr = (addr_cnt & 0x1fffff00) >> 8;
248 			cnt = addr_cnt & 0xff;
249 			FORMG0ADDR(cdbp, addr);
250 			FORMG0COUNT(cdbp, cnt);
251 			break;
252 
253 		case CDB_GROUPID_1:
254 		case CDB_GROUPID_2:
255 			FORMG1ADDR(cdbp, addr);
256 			FORMG1COUNT(cdbp, cnt);
257 			break;
258 
259 		case CDB_GROUPID_4:
260 			FORMG4ADDR(cdbp, addr);
261 			FORMG4COUNT(cdbp, cnt);
262 			FORMG4ADDTL(cdbp, addtl_cdb_data);
263 			break;
264 
265 		case CDB_GROUPID_5:
266 			FORMG5ADDR(cdbp, addr);
267 			FORMG5COUNT(cdbp, cnt);
268 			break;
269 
270 		default:
271 			return (0);
272 	}
273 
274 	return (1);
275 }
276 
277 
278 /*
279  * Common iopbmap data area packet allocation routines
280  */
281 
282 struct scsi_pkt *
283 get_pktiopb(struct scsi_address *ap, caddr_t *datap, int cdblen, int statuslen,
284     int datalen, int readflag, int (*func)())
285 {
286 	scsi_hba_tran_t	*tran = A_TO_TRAN(ap);
287 	dev_info_t	*pdip = tran->tran_hba_dip;
288 	struct scsi_pkt	*pkt = NULL;
289 	struct buf	local;
290 	size_t		rlen;
291 
292 	if (!datap)
293 		return (pkt);
294 	*datap = (caddr_t)0;
295 	bzero((caddr_t)&local, sizeof (struct buf));
296 
297 	/*
298 	 * use i_ddi_mem_alloc() for now until we have an interface to allocate
299 	 * memory for DMA which doesn't require a DMA handle. ddi_iopb_alloc()
300 	 * is obsolete and we want more flexibility in controlling the DMA
301 	 * address constraints.
302 	 */
303 	if (i_ddi_mem_alloc(pdip, &scsi_alloc_attr, datalen,
304 	    ((func == SLEEP_FUNC) ? 1 : 0), 0, NULL, &local.b_un.b_addr, &rlen,
305 	    NULL) != DDI_SUCCESS) {
306 		return (pkt);
307 	}
308 	if (readflag)
309 		local.b_flags = B_READ;
310 	local.b_bcount = datalen;
311 	pkt = (*tran->tran_init_pkt) (ap, NULL, &local,
312 		cdblen, statuslen, 0, PKT_CONSISTENT,
313 		(func == SLEEP_FUNC) ? SLEEP_FUNC : NULL_FUNC,
314 		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] = '\0';
1187 				    (void) sprintf(&buf1[buflen],
1188 				    "%s ASCQ: 0x%x", pad, ascq);
1189 				} else {
1190 				    (void) sprintf(&buf1[buflen], " ASCQ: 0x%x",
1191 					ascq);
1192 				}
1193 				impl_scsi_log(dev,
1194 					label, CE_CONT, "%s\n", buf1);
1195 				impl_scsi_log(dev, label, CE_CONT,
1196 					"FRU: 0x%x (%s)\n",
1197 						fru_code, buf);
1198 				mutex_exit(&scsi_log_mutex);
1199 				return;
1200 			}
1201 		}
1202 		(void) sprintf(&buf[strlen(buf)],
1203 		    "ASC: 0x%x (%s), ASCQ: 0x%x, FRU: 0x%x",
1204 		    asc, scsi_asc_ascq_name(asc, ascq, tmpbuf, asc_list),
1205 		    ascq, fru_code);
1206 		impl_scsi_log(dev, label, CE_CONT, "%s\n", buf);
1207 	}
1208 	mutex_exit(&scsi_log_mutex);
1209 }
1210 
1211 void
1212 scsi_errmsg(struct scsi_device *devp, struct scsi_pkt *pkt, char *label,
1213     int severity, daddr_t blkno, daddr_t err_blkno,
1214     struct scsi_key_strings *cmdlist, struct scsi_extended_sense *sensep)
1215 {
1216 	scsi_vu_errmsg(devp, pkt, label, severity, blkno,
1217 		err_blkno, cmdlist, sensep, NULL, NULL);
1218 }
1219 
1220 /*PRINTFLIKE4*/
1221 void
1222 scsi_log(dev_info_t *dev, char *label, uint_t level,
1223     const char *fmt, ...)
1224 {
1225 	va_list ap;
1226 
1227 	va_start(ap, fmt);
1228 	mutex_enter(&scsi_log_mutex);
1229 	v_scsi_log(dev, label, level, fmt, ap);
1230 	mutex_exit(&scsi_log_mutex);
1231 	va_end(ap);
1232 }
1233 
1234 /*PRINTFLIKE4*/
1235 static void
1236 impl_scsi_log(dev_info_t *dev, char *label, uint_t level,
1237     const char *fmt, ...)
1238 {
1239 	va_list ap;
1240 
1241 	ASSERT(mutex_owned(&scsi_log_mutex));
1242 
1243 	va_start(ap, fmt);
1244 	v_scsi_log(dev, label, level, fmt, ap);
1245 	va_end(ap);
1246 }
1247 
1248 
1249 char *ddi_pathname(dev_info_t *dip, char *path);
1250 
1251 /*PRINTFLIKE4*/
1252 static void
1253 v_scsi_log(dev_info_t *dev, char *label, uint_t level,
1254     const char *fmt, va_list ap)
1255 {
1256 	static char name[256];
1257 	int log_only = 0;
1258 	int boot_only = 0;
1259 	int console_only = 0;
1260 
1261 	ASSERT(mutex_owned(&scsi_log_mutex));
1262 
1263 	if (dev) {
1264 		if (level == CE_PANIC || level == CE_WARN ||
1265 		    level == CE_NOTE) {
1266 			(void) sprintf(name, "%s (%s%d):\n",
1267 				ddi_pathname(dev, scsi_log_buffer),
1268 				label, ddi_get_instance(dev));
1269 		} else if (level >= (uint_t)SCSI_DEBUG) {
1270 			(void) sprintf(name,
1271 			    "%s%d:", label, ddi_get_instance(dev));
1272 		} else {
1273 			name[0] = '\0';
1274 		}
1275 	} else {
1276 		(void) sprintf(name, "%s:", label);
1277 	}
1278 
1279 	(void) vsprintf(scsi_log_buffer, fmt, ap);
1280 
1281 	switch (scsi_log_buffer[0]) {
1282 	case '!':
1283 		log_only = 1;
1284 		break;
1285 	case '?':
1286 		boot_only = 1;
1287 		break;
1288 	case '^':
1289 		console_only = 1;
1290 		break;
1291 	}
1292 
1293 	switch (level) {
1294 	case CE_NOTE:
1295 		level = CE_CONT;
1296 		/* FALLTHROUGH */
1297 	case CE_CONT:
1298 	case CE_WARN:
1299 	case CE_PANIC:
1300 		if (boot_only) {
1301 			cmn_err(level, "?%s\t%s", name,
1302 				&scsi_log_buffer[1]);
1303 		} else if (console_only) {
1304 			cmn_err(level, "^%s\t%s", name,
1305 				&scsi_log_buffer[1]);
1306 		} else if (log_only) {
1307 			cmn_err(level, "!%s\t%s", name,
1308 				&scsi_log_buffer[1]);
1309 		} else {
1310 			cmn_err(level, "%s\t%s", name,
1311 				scsi_log_buffer);
1312 		}
1313 		break;
1314 	case (uint_t)SCSI_DEBUG:
1315 	default:
1316 		cmn_err(CE_CONT, "^DEBUG: %s\t%s", name,
1317 				scsi_log_buffer);
1318 		break;
1319 	}
1320 }
1321 
1322 int
1323 scsi_get_device_type_scsi_options(dev_info_t *dip,
1324     struct scsi_device *devp, int default_scsi_options)
1325 {
1326 
1327 	caddr_t config_list	= NULL;
1328 	int options		= default_scsi_options;
1329 	struct scsi_inquiry  *inq = devp->sd_inq;
1330 	caddr_t vidptr, datanameptr;
1331 	int	vidlen, dupletlen;
1332 	int config_list_len, len;
1333 
1334 	/*
1335 	 * look up the device-type-scsi-options-list and walk thru
1336 	 * the list
1337 	 * compare the vendor ids of the earlier inquiry command and
1338 	 * with those vids in the list
1339 	 * if there is a match, lookup the scsi-options value
1340 	 */
1341 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1342 	    "device-type-scsi-options-list",
1343 	    (caddr_t)&config_list, &config_list_len) == DDI_PROP_SUCCESS) {
1344 
1345 		/*
1346 		 * Compare vids in each duplet - if it matches, get value for
1347 		 * dataname and then lookup scsi_options
1348 		 * dupletlen is calculated later.
1349 		 */
1350 		for (len = config_list_len, vidptr = config_list; len > 0;
1351 		    vidptr += dupletlen, len -= dupletlen) {
1352 
1353 			vidlen = strlen(vidptr);
1354 			datanameptr = vidptr + vidlen + 1;
1355 
1356 			if ((vidlen != 0) &&
1357 			    bcmp(inq->inq_vid, vidptr, vidlen) == 0) {
1358 				/*
1359 				 * get the data list
1360 				 */
1361 				options = ddi_prop_get_int(DDI_DEV_T_ANY,
1362 				    dip, 0,
1363 				    datanameptr, default_scsi_options);
1364 				break;
1365 			}
1366 			dupletlen = vidlen + strlen(datanameptr) + 2;
1367 		}
1368 		kmem_free(config_list, config_list_len);
1369 	}
1370 
1371 	return (options);
1372 }
1373 
1374 /*
1375  * Functions for format-neutral sense data functions
1376  */
1377 
1378 int
1379 scsi_validate_sense(uint8_t *sense_buffer, int sense_buf_len, int *flags)
1380 {
1381 	int result;
1382 	struct scsi_extended_sense *es =
1383 	    (struct scsi_extended_sense *)sense_buffer;
1384 
1385 	/*
1386 	 * Init flags if present
1387 	 */
1388 	if (flags != NULL) {
1389 		*flags = 0;
1390 	}
1391 
1392 	/*
1393 	 * Check response code (Solaris breaks this into a 3-bit class
1394 	 * and 4-bit code field.
1395 	 */
1396 	if ((es->es_class != CLASS_EXTENDED_SENSE) ||
1397 	    ((es->es_code != CODE_FMT_FIXED_CURRENT) &&
1398 		(es->es_code != CODE_FMT_FIXED_DEFERRED) &&
1399 		(es->es_code != CODE_FMT_DESCR_CURRENT) &&
1400 		(es->es_code != CODE_FMT_DESCR_DEFERRED))) {
1401 		/*
1402 		 * Sense data (if there's actually anything here) is not
1403 		 * in a format we can handle).
1404 		 */
1405 		return (SENSE_UNUSABLE);
1406 	}
1407 
1408 	/*
1409 	 * Check if this is deferred sense
1410 	 */
1411 	if ((flags != NULL) &&
1412 	    ((es->es_code == CODE_FMT_FIXED_DEFERRED) ||
1413 		(es->es_code == CODE_FMT_DESCR_DEFERRED))) {
1414 		*flags |= SNS_BUF_DEFERRED;
1415 	}
1416 
1417 	/*
1418 	 * Make sure length is OK
1419 	 */
1420 	if (es->es_code == CODE_FMT_FIXED_CURRENT ||
1421 	    es->es_code == CODE_FMT_FIXED_DEFERRED) {
1422 		/*
1423 		 * We can get by with a buffer that only includes the key,
1424 		 * asc, and ascq.  In reality the minimum length we should
1425 		 * ever see is 18 bytes.
1426 		 */
1427 		if ((sense_buf_len < MIN_FIXED_SENSE_LEN) ||
1428 		    ((es->es_add_len + ADDL_SENSE_ADJUST) <
1429 			MIN_FIXED_SENSE_LEN)) {
1430 			result = SENSE_UNUSABLE;
1431 		} else {
1432 			/*
1433 			 * The es_add_len field contains the number of sense
1434 			 * data bytes that follow the es_add_len field.
1435 			 */
1436 			if ((flags != NULL) &&
1437 			    (sense_buf_len <
1438 				(es->es_add_len + ADDL_SENSE_ADJUST))) {
1439 				*flags |= SNS_BUF_OVERFLOW;
1440 			}
1441 
1442 			result = SENSE_FIXED_FORMAT;
1443 		}
1444 	} else {
1445 		struct scsi_descr_sense_hdr *ds =
1446 		    (struct scsi_descr_sense_hdr *)sense_buffer;
1447 
1448 		/*
1449 		 * For descriptor format we need at least the descriptor
1450 		 * header
1451 		 */
1452 		if (sense_buf_len < sizeof (struct scsi_descr_sense_hdr)) {
1453 			result = SENSE_UNUSABLE;
1454 		} else {
1455 			/*
1456 			 * Check for overflow
1457 			 */
1458 			if ((flags != NULL) &&
1459 			    (sense_buf_len <
1460 				(ds->ds_addl_sense_length + sizeof (*ds)))) {
1461 				*flags |= SNS_BUF_OVERFLOW;
1462 			}
1463 
1464 			result = SENSE_DESCR_FORMAT;
1465 		}
1466 	}
1467 
1468 	return (result);
1469 }
1470 
1471 
1472 uint8_t
1473 scsi_sense_key(uint8_t *sense_buffer)
1474 {
1475 	uint8_t skey;
1476 	if (SCSI_IS_DESCR_SENSE(sense_buffer)) {
1477 		struct scsi_descr_sense_hdr *sdsp =
1478 		    (struct scsi_descr_sense_hdr *)sense_buffer;
1479 		skey = sdsp->ds_key;
1480 	} else {
1481 		struct scsi_extended_sense *ext_sensep =
1482 		    (struct scsi_extended_sense *)sense_buffer;
1483 		skey = ext_sensep->es_key;
1484 	}
1485 	return (skey);
1486 }
1487 
1488 uint8_t
1489 scsi_sense_asc(uint8_t *sense_buffer)
1490 {
1491 	uint8_t asc;
1492 	if (SCSI_IS_DESCR_SENSE(sense_buffer)) {
1493 		struct scsi_descr_sense_hdr *sdsp =
1494 		    (struct scsi_descr_sense_hdr *)sense_buffer;
1495 		asc = sdsp->ds_add_code;
1496 	} else {
1497 		struct scsi_extended_sense *ext_sensep =
1498 		    (struct scsi_extended_sense *)sense_buffer;
1499 		asc = ext_sensep->es_add_code;
1500 	}
1501 	return (asc);
1502 }
1503 
1504 uint8_t
1505 scsi_sense_ascq(uint8_t *sense_buffer)
1506 {
1507 	uint8_t ascq;
1508 	if (SCSI_IS_DESCR_SENSE(sense_buffer)) {
1509 		struct scsi_descr_sense_hdr *sdsp =
1510 		    (struct scsi_descr_sense_hdr *)sense_buffer;
1511 		ascq = sdsp->ds_qual_code;
1512 	} else {
1513 		struct scsi_extended_sense *ext_sensep =
1514 		    (struct scsi_extended_sense *)sense_buffer;
1515 		ascq = ext_sensep->es_qual_code;
1516 	}
1517 	return (ascq);
1518 }
1519 
1520 void scsi_ext_sense_fields(uint8_t *sense_buffer, int sense_buf_len,
1521     uint8_t **information, uint8_t **cmd_spec_info, uint8_t **fru_code,
1522     uint8_t **sk_specific, uint8_t **stream_flags)
1523 {
1524 	int sense_fmt;
1525 
1526 	/*
1527 	 * Sanity check sense data and determine the format
1528 	 */
1529 	sense_fmt = scsi_validate_sense(sense_buffer, sense_buf_len, NULL);
1530 
1531 	/*
1532 	 * Initialize any requested data to 0
1533 	 */
1534 	if (information) {
1535 		*information = NULL;
1536 	}
1537 	if (cmd_spec_info) {
1538 		*cmd_spec_info = NULL;
1539 	}
1540 	if (fru_code) {
1541 		*fru_code = NULL;
1542 	}
1543 	if (sk_specific) {
1544 		*sk_specific = NULL;
1545 	}
1546 	if (stream_flags) {
1547 		*stream_flags = NULL;
1548 	}
1549 
1550 	if (sense_fmt == SENSE_DESCR_FORMAT) {
1551 		struct scsi_descr_template *sdt = NULL;
1552 
1553 		while (scsi_get_next_descr(sense_buffer,
1554 		    sense_buf_len, &sdt) != -1) {
1555 			switch (sdt->sdt_descr_type) {
1556 			case DESCR_INFORMATION: {
1557 				struct scsi_information_sense_descr *isd =
1558 				    (struct scsi_information_sense_descr *)
1559 				    sdt;
1560 				if (information) {
1561 					*information =
1562 					    &isd->isd_information[0];
1563 				}
1564 				break;
1565 			}
1566 			case DESCR_COMMAND_SPECIFIC: {
1567 				struct scsi_cmd_specific_sense_descr *csd =
1568 				    (struct scsi_cmd_specific_sense_descr *)
1569 				    sdt;
1570 				if (cmd_spec_info) {
1571 					*cmd_spec_info =
1572 					    &csd->css_cmd_specific_info[0];
1573 				}
1574 				break;
1575 			}
1576 			case DESCR_SENSE_KEY_SPECIFIC: {
1577 				struct scsi_sk_specific_sense_descr *ssd =
1578 				    (struct scsi_sk_specific_sense_descr *)
1579 				    sdt;
1580 				if (sk_specific) {
1581 					*sk_specific =
1582 					    (uint8_t *)&ssd->sss_data;
1583 				}
1584 				break;
1585 			}
1586 			case DESCR_FRU: {
1587 				struct scsi_fru_sense_descr *fsd =
1588 				    (struct scsi_fru_sense_descr *)
1589 				    sdt;
1590 				if (fru_code) {
1591 					*fru_code = &fsd->fs_fru_code;
1592 				}
1593 				break;
1594 			}
1595 			case DESCR_STREAM_COMMANDS: {
1596 				struct scsi_stream_cmd_sense_descr *strsd =
1597 				    (struct scsi_stream_cmd_sense_descr *)
1598 				    sdt;
1599 				if (stream_flags) {
1600 					*stream_flags =
1601 					    (uint8_t *)&strsd->scs_data;
1602 				}
1603 				break;
1604 			}
1605 			case DESCR_BLOCK_COMMANDS: {
1606 				struct scsi_block_cmd_sense_descr *bsd =
1607 				    (struct scsi_block_cmd_sense_descr *)
1608 				    sdt;
1609 				/*
1610 				 * The "Block Command" sense descriptor
1611 				 * contains an ili bit that we can store
1612 				 * in the stream specific data if it is
1613 				 * available.  We shouldn't see both
1614 				 * a block command and a stream command
1615 				 * descriptor in the same collection
1616 				 * of sense data.
1617 				 */
1618 				if (stream_flags) {
1619 					/*
1620 					 * Can't take an address of a bitfield,
1621 					 * but the flags are just after the
1622 					 * bcs_reserved field.
1623 					 */
1624 					*stream_flags =
1625 					    (uint8_t *)&bsd->bcs_reserved + 1;
1626 				}
1627 				break;
1628 			}
1629 			}
1630 		}
1631 	} else {
1632 		struct scsi_extended_sense *es =
1633 		    (struct scsi_extended_sense *)sense_buffer;
1634 
1635 		/* Get data from fixed sense buffer */
1636 		if (information && es->es_valid) {
1637 			*information = &es->es_info_1;
1638 		}
1639 		if (cmd_spec_info && es->es_valid) {
1640 			*cmd_spec_info = &es->es_cmd_info[0];
1641 		}
1642 		if (fru_code) {
1643 			*fru_code = &es->es_fru_code;
1644 		}
1645 		if (sk_specific) {
1646 			*sk_specific = &es->es_skey_specific[0];
1647 		}
1648 		if (stream_flags) {
1649 			/*
1650 			 * Can't take the address of a bit field,
1651 			 * but the stream flags are located just after
1652 			 * the es_segnum field;
1653 			 */
1654 			*stream_flags = &es->es_segnum + 1;
1655 		}
1656 	}
1657 }
1658 
1659 boolean_t
1660 scsi_sense_info_uint64(uint8_t *sense_buffer, int sense_buf_len,
1661     uint64_t *information)
1662 {
1663 	boolean_t valid;
1664 	int sense_fmt;
1665 
1666 	ASSERT(sense_buffer != NULL);
1667 	ASSERT(information != NULL);
1668 
1669 	/* Validate sense data and get format */
1670 	sense_fmt = scsi_validate_sense(sense_buffer, sense_buf_len, NULL);
1671 
1672 	if (sense_fmt == SENSE_UNUSABLE) {
1673 		/* Information is not valid */
1674 		valid = 0;
1675 	} else if (sense_fmt == SENSE_FIXED_FORMAT) {
1676 		struct scsi_extended_sense *es =
1677 		    (struct scsi_extended_sense *)sense_buffer;
1678 
1679 		*information = (uint64_t)SCSI_READ32(&es->es_info_1);
1680 
1681 		valid = es->es_valid;
1682 	} else {
1683 		/* Sense data is descriptor format */
1684 		struct scsi_information_sense_descr *isd;
1685 
1686 		isd = (struct scsi_information_sense_descr *)
1687 		    scsi_find_sense_descr(sense_buffer, sense_buf_len,
1688 			DESCR_INFORMATION);
1689 
1690 		if (isd) {
1691 			*information = SCSI_READ64(isd->isd_information);
1692 			valid = 1;
1693 		} else {
1694 			valid = 0;
1695 		}
1696 	}
1697 
1698 	return (valid);
1699 }
1700 
1701 boolean_t
1702 scsi_sense_cmdspecific_uint64(uint8_t *sense_buffer, int sense_buf_len,
1703     uint64_t *cmd_specific_info)
1704 {
1705 	boolean_t valid;
1706 	int sense_fmt;
1707 
1708 	ASSERT(sense_buffer != NULL);
1709 	ASSERT(cmd_specific_info != NULL);
1710 
1711 	/* Validate sense data and get format */
1712 	sense_fmt = scsi_validate_sense(sense_buffer, sense_buf_len, NULL);
1713 
1714 	if (sense_fmt == SENSE_UNUSABLE) {
1715 		/* Command specific info is not valid */
1716 		valid = 0;
1717 	} else if (sense_fmt == SENSE_FIXED_FORMAT) {
1718 		struct scsi_extended_sense *es =
1719 		    (struct scsi_extended_sense *)sense_buffer;
1720 
1721 		*cmd_specific_info = (uint64_t)SCSI_READ32(es->es_cmd_info);
1722 
1723 		valid = es->es_valid;
1724 	} else {
1725 		/* Sense data is descriptor format */
1726 		struct scsi_cmd_specific_sense_descr *c;
1727 
1728 		c = (struct scsi_cmd_specific_sense_descr *)
1729 		    scsi_find_sense_descr(sense_buffer, sense_buf_len,
1730 			DESCR_COMMAND_SPECIFIC);
1731 
1732 		if (c) {
1733 			valid = 1;
1734 			*cmd_specific_info =
1735 			    SCSI_READ64(c->css_cmd_specific_info);
1736 		} else {
1737 			valid = 0;
1738 		}
1739 	}
1740 
1741 	return (valid);
1742 }
1743 
1744 uint8_t *
1745 scsi_find_sense_descr(uint8_t *sdsp, int sense_buf_len, int req_descr_type)
1746 {
1747 	struct scsi_descr_template *sdt = NULL;
1748 
1749 	while (scsi_get_next_descr(sdsp, sense_buf_len, &sdt) != -1) {
1750 		ASSERT(sdt != NULL);
1751 		if (sdt->sdt_descr_type == req_descr_type) {
1752 			/* Found requested descriptor type */
1753 			break;
1754 		}
1755 	}
1756 
1757 	return ((uint8_t *)sdt);
1758 }
1759 
1760 /*
1761  * Sense Descriptor format is:
1762  *
1763  * <Descriptor type> <Descriptor length> <Descriptor data> ...
1764  *
1765  * 2 must be added to the descriptor length value to get the
1766  * total descriptor length sense the stored length does not
1767  * include the "type" and "additional length" fields.
1768  */
1769 
1770 #define	NEXT_DESCR_PTR(ndp_descr) \
1771 	((struct scsi_descr_template *)(((uint8_t *)(ndp_descr)) + \
1772 	    ((ndp_descr)->sdt_addl_length + \
1773 	    sizeof (struct scsi_descr_template))))
1774 
1775 static int
1776 scsi_get_next_descr(uint8_t *sense_buffer,
1777     int sense_buf_len, struct scsi_descr_template **descrpp)
1778 {
1779 	struct scsi_descr_sense_hdr *sdsp =
1780 	    (struct scsi_descr_sense_hdr *)sense_buffer;
1781 	struct scsi_descr_template *cur_descr;
1782 	boolean_t find_first;
1783 	int valid_sense_length;
1784 
1785 	ASSERT(descrpp != NULL);
1786 	find_first = (*descrpp == NULL);
1787 
1788 	/*
1789 	 * If no descriptor is passed in then return the first
1790 	 * descriptor
1791 	 */
1792 	if (find_first) {
1793 		/*
1794 		 * The first descriptor will immediately follow the header
1795 		 * (Pointer arithmetic)
1796 		 */
1797 		cur_descr = (struct scsi_descr_template *)(sdsp+1);
1798 	} else {
1799 		cur_descr = *descrpp;
1800 		ASSERT(cur_descr > (struct scsi_descr_template *)sdsp);
1801 	}
1802 
1803 	/* Assume no more descriptors are available */
1804 	*descrpp = NULL;
1805 
1806 	/*
1807 	 * Calculate the amount of valid sense data -- make sure the length
1808 	 * byte in this descriptor lies within the valid sense data.
1809 	 */
1810 	valid_sense_length =
1811 	    min((sizeof (struct scsi_descr_sense_hdr) +
1812 	    sdsp->ds_addl_sense_length),
1813 	    sense_buf_len);
1814 
1815 	/*
1816 	 * Make sure this descriptor is complete (either the first
1817 	 * descriptor or the descriptor passed in)
1818 	 */
1819 	if (scsi_validate_descr(sdsp, valid_sense_length, cur_descr) !=
1820 	    DESCR_GOOD) {
1821 		return (-1);
1822 	}
1823 
1824 	/*
1825 	 * If we were looking for the first descriptor go ahead and return it
1826 	 */
1827 	if (find_first) {
1828 		*descrpp = cur_descr;
1829 		return ((*descrpp)->sdt_descr_type);
1830 	}
1831 
1832 	/*
1833 	 * Get pointer to next descriptor
1834 	 */
1835 	cur_descr = NEXT_DESCR_PTR(cur_descr);
1836 
1837 	/*
1838 	 * Make sure this descriptor is also complete.
1839 	 */
1840 	if (scsi_validate_descr(sdsp, valid_sense_length, cur_descr) !=
1841 	    DESCR_GOOD) {
1842 		return (-1);
1843 	}
1844 
1845 	*descrpp = (struct scsi_descr_template *)cur_descr;
1846 	return ((*descrpp)->sdt_descr_type);
1847 }
1848 
1849 static int
1850 scsi_validate_descr(struct scsi_descr_sense_hdr *sdsp,
1851     int valid_sense_length, struct scsi_descr_template *descrp)
1852 {
1853 	int descr_offset, next_descr_offset;
1854 
1855 	/*
1856 	 * Make sure length is present
1857 	 */
1858 	descr_offset = (uint8_t *)descrp - (uint8_t *)sdsp;
1859 	if (descr_offset + sizeof (struct scsi_descr_template) >
1860 	    valid_sense_length) {
1861 		return (DESCR_PARTIAL);
1862 	}
1863 
1864 	/*
1865 	 * Check if length is 0 (no more descriptors)
1866 	 */
1867 	if (descrp->sdt_addl_length == 0) {
1868 		return (DESCR_END);
1869 	}
1870 
1871 	/*
1872 	 * Make sure the rest of the descriptor is present
1873 	 */
1874 	next_descr_offset =
1875 	    (uint8_t *)NEXT_DESCR_PTR(descrp) - (uint8_t *)sdsp;
1876 	if (next_descr_offset > valid_sense_length) {
1877 		return (DESCR_PARTIAL);
1878 	}
1879 
1880 	return (DESCR_GOOD);
1881 }
1882