xref: /illumos-gate/usr/src/uts/common/io/scsi/impl/scsi_subr.c (revision 7ae111d47a973fff4c6e231cc31f271dd9cef473)
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 	0x28, 0x00, "medium may have changed",
674 	0x28, 0x01, "import or export element accessed",
675 	0x29, 0x00, "power on, reset, or bus reset occurred",
676 	0x29, 0x01, "power on occurred",
677 	0x29, 0x02, "scsi bus reset occurred",
678 	0x29, 0x03, "bus device reset message occurred",
679 	0x29, 0x04, "device internal reset",
680 	0x29, 0x05, "transceiver mode changed to single-ended",
681 	0x29, 0x06, "transceiver mode changed to LVD",
682 	0x29, 0x07, "i_t nexus loss occurred",
683 	0x2a, 0x00, "parameters changed",
684 	0x2a, 0x01, "mode parameters changed",
685 	0x2a, 0x02, "log parameters changed",
686 	0x2a, 0x03, "reservations preempted",
687 	0x2a, 0x04, "reservations released",
688 	0x2a, 0x05, "registrations preempted",
689 	0x2a, 0x06, "asymmetric access state changed",
690 	0x2a, 0x07, "implicit asymmetric access state transition failed",
691 	0x2b, 0x00, "copy cannot execute since host cannot disconnect",
692 	0x2c, 0x00, "command sequence error",
693 	0x2c, 0x03, "current program area is not empty",
694 	0x2c, 0x04, "current program area is empty",
695 	0x2c, 0x06, "persistent prevent conflict",
696 	0x2c, 0x07, "previous busy status",
697 	0x2c, 0x08, "previous task set full status",
698 	0x2c, 0x09, "previous reservation conflict status",
699 	0x2d, 0x00, "overwrite error on update in place",
700 	0x2e, 0x00, "insufficient time for operation",
701 	0x2f, 0x00, "commands cleared by another initiator",
702 	0x30, 0x00, "incompatible medium installed",
703 	0x30, 0x01, "cannot read medium - unknown format",
704 	0x30, 0x02, "cannot read medium - incompatible format",
705 	0x30, 0x03, "cleaning cartridge installed",
706 	0x30, 0x04, "cannot write medium - unknown format",
707 	0x30, 0x05, "cannot write medium - incompatible format",
708 	0x30, 0x06, "cannot format medium - incompatible medium",
709 	0x30, 0x07, "cleaning failure",
710 	0x30, 0x08, "cannot write - application code mismatch",
711 	0x30, 0x09, "current session not fixated for append",
712 	0x30, 0x10, "medium not formatted",
713 	0x31, 0x00, "medium format corrupted",
714 	0x31, 0x01, "format command failed",
715 	0x31, 0x02, "zoned formatting failed due to spare linking",
716 	0x32, 0x00, "no defect spare location available",
717 	0x32, 0x01, "defect list update failure",
718 	0x33, 0x00, "tape length error",
719 	0x34, 0x00, "enclosure failure",
720 	0x35, 0x00, "enclosure services failure",
721 	0x35, 0x01, "unsupported enclosure function",
722 	0x35, 0x02, "enclosure services unavailable",
723 	0x35, 0x03, "enclosure services transfer failure",
724 	0x35, 0x04, "enclosure services transfer refused",
725 	0x36, 0x00, "ribbon, ink, or toner failure",
726 	0x37, 0x00, "rounded parameter",
727 	0x39, 0x00, "saving parameters not supported",
728 	0x3a, 0x00, "medium not present",
729 	0x3a, 0x01, "medium not present - tray closed",
730 	0x3a, 0x02, "medium not present - tray open",
731 	0x3a, 0x03, "medium not present - loadable",
732 	0x3a, 0x04, "medium not present - medium auxiliary memory accessible",
733 	0x3b, 0x00, "sequential positioning error",
734 	0x3b, 0x01, "tape position error at beginning-of-medium",
735 	0x3b, 0x02, "tape position error at end-of-medium",
736 	0x3b, 0x08, "reposition error",
737 	0x3b, 0x0c, "position past beginning of medium",
738 	0x3b, 0x0d, "medium destination element full",
739 	0x3b, 0x0e, "medium source element empty",
740 	0x3b, 0x0f, "end of medium reached",
741 	0x3b, 0x11, "medium magazine not accessible",
742 	0x3b, 0x12, "medium magazine removed",
743 	0x3b, 0x13, "medium magazine inserted",
744 	0x3b, 0x14, "medium magazine locked",
745 	0x3b, 0x15, "medium magazine unlocked",
746 	0x3b, 0x16, "mechanical positioning or changer error",
747 	0x3d, 0x00, "invalid bits in indentify message",
748 	0x3e, 0x00, "LUN has not self-configured yet",
749 	0x3e, 0x01, "LUN failure",
750 	0x3e, 0x02, "timeout on LUN",
751 	0x3e, 0x03, "LUN failed self-test",
752 	0x3e, 0x04, "LUN unable to update self-test log",
753 	0x3f, 0x00, "target operating conditions have changed",
754 	0x3f, 0x01, "microcode has been changed",
755 	0x3f, 0x02, "changed operating definition",
756 	0x3f, 0x03, "inquiry data has changed",
757 	0x3f, 0x04, "component device attached",
758 	0x3f, 0x05, "device identifier changed",
759 	0x3f, 0x06, "redundancy group created or modified",
760 	0x3f, 0x07, "redundancy group deleted",
761 	0x3f, 0x08, "spare created or modified",
762 	0x3f, 0x09, "spare deleted",
763 	0x3f, 0x0a, "volume set created or modified",
764 	0x3f, 0x0b, "volume set deleted",
765 	0x3f, 0x0c, "volume set deassigned",
766 	0x3f, 0x0d, "volume set reassigned",
767 	0x3f, 0x0e, "reported LUNs data has changed",
768 	0x3f, 0x0f, "echo buffer overwritten",
769 	0x3f, 0x10, "medium loadable",
770 	0x3f, 0x11, "medium auxiliary memory accessible",
771 	0x40, 0x00, "ram failure",
772 	0x41, 0x00, "data path failure",
773 	0x42, 0x00, "power-on or self-test failure",
774 	0x43, 0x00, "message error",
775 	0x44, 0x00, "internal target failure",
776 	0x45, 0x00, "select or reselect failure",
777 	0x46, 0x00, "unsuccessful soft reset",
778 	0x47, 0x00, "scsi parity error",
779 	0x47, 0x01, "data phase crc error detected",
780 	0x47, 0x02, "scsi parity error detected during st data phase",
781 	0x47, 0x03, "information unit iucrc error detected",
782 	0x47, 0x04, "asynchronous information protection error detected",
783 	0x47, 0x05, "protocol service crc error",
784 	0x47, 0x7f, "some commands cleared by iscsi protocol event",
785 	0x48, 0x00, "initiator detected error message received",
786 	0x49, 0x00, "invalid message error",
787 	0x4a, 0x00, "command phase error",
788 	0x4b, 0x00, "data phase error",
789 	0x4b, 0x01, "invalid target port transfer tag received",
790 	0x4b, 0x02, "too much write data",
791 	0x4b, 0x03, "ack/nak timeout",
792 	0x4b, 0x04, "nak received",
793 	0x4b, 0x05, "data offset error",
794 	0x4c, 0x00, "logical unit failed self-configuration",
795 	0x4d, 0x00, "tagged overlapped commands (ASCQ = queue tag)",
796 	0x4e, 0x00, "overlapped commands attempted",
797 	0x50, 0x00, "write append error",
798 	0x51, 0x00, "erase failure",
799 	0x52, 0x00, "cartridge fault",
800 	0x53, 0x00, "media load or eject failed",
801 	0x53, 0x01, "unload tape failure",
802 	0x53, 0x02, "medium removal prevented",
803 	0x54, 0x00, "scsi to host system interface failure",
804 	0x55, 0x00, "system resource failure",
805 	0x55, 0x01, "system buffer full",
806 	0x55, 0x02, "insufficient reservation resources",
807 	0x55, 0x03, "insufficient resources",
808 	0x55, 0x04, "insufficient registration resources",
809 	0x55, 0x05, "insufficient access control resources",
810 	0x55, 0x06, "auxiliary memory out of space",
811 	0x57, 0x00, "unable to recover TOC",
812 	0x58, 0x00, "generation does not exist",
813 	0x59, 0x00, "updated block read",
814 	0x5a, 0x00, "operator request or state change input",
815 	0x5a, 0x01, "operator medium removal request",
816 	0x5a, 0x02, "operator selected write protect",
817 	0x5a, 0x03, "operator selected write permit",
818 	0x5b, 0x00, "log exception",
819 	0x5b, 0x01, "threshold condition met",
820 	0x5b, 0x02, "log counter at maximum",
821 	0x5b, 0x03, "log list codes exhausted",
822 	0x5c, 0x00, "RPL status change",
823 	0x5c, 0x01, "spindles synchronized",
824 	0x5c, 0x02, "spindles not synchronized",
825 	0x5d, 0x00, "drive operation marginal, service immediately"
826 		    " (failure prediction threshold exceeded)",
827 	0x5d, 0x01, "media failure prediction threshold exceeded",
828 	0x5d, 0x02, "LUN failure prediction threshold exceeded",
829 	0x5d, 0x03, "spare area exhaustion prediction threshold exceeded",
830 	0x5d, 0x10, "hardware impending failure general hard drive failure",
831 	0x5d, 0x11, "hardware impending failure drive error rate too high",
832 	0x5d, 0x12, "hardware impending failure data error rate too high",
833 	0x5d, 0x13, "hardware impending failure seek error rate too high",
834 	0x5d, 0x14, "hardware impending failure too many block reassigns",
835 	0x5d, 0x15, "hardware impending failure access times too high",
836 	0x5d, 0x16, "hardware impending failure start unit times too high",
837 	0x5d, 0x17, "hardware impending failure channel parametrics",
838 	0x5d, 0x18, "hardware impending failure controller detected",
839 	0x5d, 0x19, "hardware impending failure throughput performance",
840 	0x5d, 0x1a, "hardware impending failure seek time performance",
841 	0x5d, 0x1b, "hardware impending failure spin-up retry count",
842 	0x5d, 0x1c, "hardware impending failure drive calibration retry count",
843 	0x5d, 0x20, "controller impending failure general hard drive failure",
844 	0x5d, 0x21, "controller impending failure drive error rate too high",
845 	0x5d, 0x22, "controller impending failure data error rate too high",
846 	0x5d, 0x23, "controller impending failure seek error rate too high",
847 	0x5d, 0x24, "controller impending failure too many block reassigns",
848 	0x5d, 0x25, "controller impending failure access times too high",
849 	0x5d, 0x26, "controller impending failure start unit times too high",
850 	0x5d, 0x27, "controller impending failure channel parametrics",
851 	0x5d, 0x28, "controller impending failure controller detected",
852 	0x5d, 0x29, "controller impending failure throughput performance",
853 	0x5d, 0x2a, "controller impending failure seek time performance",
854 	0x5d, 0x2b, "controller impending failure spin-up retry count",
855 	0x5d, 0x2c, "controller impending failure drive calibration retry cnt",
856 	0x5d, 0x30, "data channel impending failure general hard drive failure",
857 	0x5d, 0x31, "data channel impending failure drive error rate too high",
858 	0x5d, 0x32, "data channel impending failure data error rate too high",
859 	0x5d, 0x33, "data channel impending failure seek error rate too high",
860 	0x5d, 0x34, "data channel impending failure too many block reassigns",
861 	0x5d, 0x35, "data channel impending failure access times too high",
862 	0x5d, 0x36, "data channel impending failure start unit times too high",
863 	0x5d, 0x37, "data channel impending failure channel parametrics",
864 	0x5d, 0x38, "data channel impending failure controller detected",
865 	0x5d, 0x39, "data channel impending failure throughput performance",
866 	0x5d, 0x3a, "data channel impending failure seek time performance",
867 	0x5d, 0x3b, "data channel impending failure spin-up retry count",
868 	0x5d, 0x3c, "data channel impending failure drive calibrate retry cnt",
869 	0x5d, 0x40, "servo impending failure general hard drive failure",
870 	0x5d, 0x41, "servo impending failure drive error rate too high",
871 	0x5d, 0x42, "servo impending failure data error rate too high",
872 	0x5d, 0x43, "servo impending failure seek error rate too high",
873 	0x5d, 0x44, "servo impending failure too many block reassigns",
874 	0x5d, 0x45, "servo impending failure access times too high",
875 	0x5d, 0x46, "servo impending failure start unit times too high",
876 	0x5d, 0x47, "servo impending failure channel parametrics",
877 	0x5d, 0x48, "servo impending failure controller detected",
878 	0x5d, 0x49, "servo impending failure throughput performance",
879 	0x5d, 0x4a, "servo impending failure seek time performance",
880 	0x5d, 0x4b, "servo impending failure spin-up retry count",
881 	0x5d, 0x4c, "servo impending failure drive calibration retry count",
882 	0x5d, 0x50, "spindle impending failure general hard drive failure",
883 	0x5d, 0x51, "spindle impending failure drive error rate too high",
884 	0x5d, 0x52, "spindle impending failure data error rate too high",
885 	0x5d, 0x53, "spindle impending failure seek error rate too high",
886 	0x5d, 0x54, "spindle impending failure too many block reassigns",
887 	0x5d, 0x55, "spindle impending failure access times too high",
888 	0x5d, 0x56, "spindle impending failure start unit times too high",
889 	0x5d, 0x57, "spindle impending failure channel parametrics",
890 	0x5d, 0x58, "spindle impending failure controller detected",
891 	0x5d, 0x59, "spindle impending failure throughput performance",
892 	0x5d, 0x5a, "spindle impending failure seek time performance",
893 	0x5d, 0x5b, "spindle impending failure spin-up retry count",
894 	0x5d, 0x5c, "spindle impending failure drive calibration retry count",
895 	0x5d, 0x60, "firmware impending failure general hard drive failure",
896 	0x5d, 0x61, "firmware impending failure drive error rate too high",
897 	0x5d, 0x62, "firmware impending failure data error rate too high",
898 	0x5d, 0x63, "firmware impending failure seek error rate too high",
899 	0x5d, 0x64, "firmware impending failure too many block reassigns",
900 	0x5d, 0x65, "firmware impending failure access times too high",
901 	0x5d, 0x66, "firmware impending failure start unit times too high",
902 	0x5d, 0x67, "firmware impending failure channel parametrics",
903 	0x5d, 0x68, "firmware impending failure controller detected",
904 	0x5d, 0x69, "firmware impending failure throughput performance",
905 	0x5d, 0x6a, "firmware impending failure seek time performance",
906 	0x5d, 0x6b, "firmware impending failure spin-up retry count",
907 	0x5d, 0x6c, "firmware impending failure drive calibration retry count",
908 	0x5d, 0xff, "failure prediction threshold exceeded (false)",
909 	0x5e, 0x00, "low power condition active",
910 	0x5e, 0x01, "idle condition activated by timer",
911 	0x5e, 0x02, "standby condition activated by timer",
912 	0x5e, 0x03, "idle condition activated by command",
913 	0x5e, 0x04, "standby condition activated by command",
914 	0x60, 0x00, "lamp failure",
915 	0x61, 0x00, "video aquisition error",
916 	0x62, 0x00, "scan head positioning error",
917 	0x63, 0x00, "end of user area encountered on this track",
918 	0x63, 0x01, "packet does not fit in available space",
919 	0x64, 0x00, "illegal mode for this track",
920 	0x64, 0x01, "invalid packet size",
921 	0x65, 0x00, "voltage fault",
922 	0x66, 0x00, "automatic document feeder cover up",
923 	0x67, 0x00, "configuration failure",
924 	0x67, 0x01, "configuration of incapable LUNs failed",
925 	0x67, 0x02, "add LUN failed",
926 	0x67, 0x03, "modification of LUN failed",
927 	0x67, 0x04, "exchange of LUN failed",
928 	0x67, 0x05, "remove of LUN failed",
929 	0x67, 0x06, "attachment of LUN failed",
930 	0x67, 0x07, "creation of LUN failed",
931 	0x67, 0x08, "assign failure occurred",
932 	0x67, 0x09, "multiply assigned LUN",
933 	0x67, 0x0a, "set target port groups command failed",
934 	0x68, 0x00, "logical unit not configured",
935 	0x69, 0x00, "data loss on logical unit",
936 	0x69, 0x01, "multiple LUN failures",
937 	0x69, 0x02, "parity/data mismatch",
938 	0x6a, 0x00, "informational, refer to log",
939 	0x6b, 0x00, "state change has occured",
940 	0x6b, 0x01, "redundancy level got better",
941 	0x6b, 0x02, "redundancy level got worse",
942 	0x6c, 0x00, "rebuild failure occured",
943 	0x6d, 0x00, "recalculate failure occured",
944 	0x6e, 0x00, "command to logical unit failed",
945 	0x6f, 0x00, "copy protect key exchange failure authentication failure",
946 	0x6f, 0x01, "copy protect key exchange failure key not present",
947 	0x6f, 0x02, "copy protect key exchange failure key not established",
948 	0x6f, 0x03, "read of scrambled sector without authentication",
949 	0x6f, 0x04, "media region code is mismatched to LUN region",
950 	0x6f, 0x05, "drive region must be permanent/region reset count error",
951 	0x70, 0xffff, "decompression exception short algorithm id of ASCQ",
952 	0x71, 0x00, "decompression exception long algorithm id",
953 	0x72, 0x00, "session fixation error",
954 	0x72, 0x01, "session fixation error writing lead-in",
955 	0x72, 0x02, "session fixation error writing lead-out",
956 	0x72, 0x03, "session fixation error - incomplete track in session",
957 	0x72, 0x04, "empty or partially written reserved track",
958 	0x72, 0x05, "no more track reservations allowed",
959 	0x73, 0x00, "cd control error",
960 	0x73, 0x01, "power calibration area almost full",
961 	0x73, 0x02, "power calibration area is full",
962 	0x73, 0x03, "power calibration area error",
963 	0x73, 0x04, "program memory area update failure",
964 	0x73, 0x05, "program memory area is full",
965 	0x73, 0x06, "rma/pma is almost full",
966 	0xffff, 0xffff, NULL
967 };
968 
969 char *
970 scsi_esname(uint_t key, char *tmpstr)
971 {
972 	int i = 0;
973 
974 	while (extended_sense_list[i].asc != 0xffff) {
975 		if (key == extended_sense_list[i].asc) {
976 			return ((char *)extended_sense_list[i].message);
977 		}
978 		i++;
979 	}
980 	return (sprintf(tmpstr, "<vendor unique code 0x%x>", key));
981 }
982 
983 char *
984 scsi_asc_name(uint_t asc, uint_t ascq, char *tmpstr)
985 {
986 	int i = 0;
987 
988 	while (extended_sense_list[i].asc != 0xffff) {
989 		if ((asc == extended_sense_list[i].asc) &&
990 		    ((ascq == extended_sense_list[i].ascq) ||
991 		    (extended_sense_list[i].ascq == 0xffff))) {
992 			return ((char *)extended_sense_list[i].message);
993 		}
994 		i++;
995 	}
996 	return (sprintf(tmpstr, "<vendor unique code 0x%x>", asc));
997 }
998 
999 char *
1000 scsi_sname(uchar_t sense_key)
1001 {
1002 	if (sense_key >= (uchar_t)(NUM_SENSE_KEYS+NUM_IMPL_SENSE_KEYS)) {
1003 		return ("<unknown sense key>");
1004 	} else {
1005 		return (sense_keys[sense_key]);
1006 	}
1007 }
1008 
1009 
1010 /*
1011  * Print a piece of inquiry data- cleaned up for non-printable characters.
1012  */
1013 
1014 static void
1015 inq_fill(char *p, int l, char *s)
1016 {
1017 	register unsigned i = 0;
1018 	char c;
1019 
1020 	if (!p)
1021 		return;
1022 
1023 	while (i++ < l) {
1024 		/* clean string of non-printing chars */
1025 		if ((c = *p++) < ' ' || c >= 0177) {
1026 			c = ' ';
1027 		}
1028 		*s++ = c;
1029 	}
1030 	*s++ = 0;
1031 }
1032 
1033 static char *
1034 scsi_asc_search(uint_t asc, uint_t ascq,
1035     struct scsi_asq_key_strings *list)
1036 {
1037 	int i = 0;
1038 
1039 	while (list[i].asc != 0xffff) {
1040 		if ((asc == list[i].asc) &&
1041 		    ((ascq == list[i].ascq) ||
1042 		    (list[i].ascq == 0xffff))) {
1043 			return ((char *)list[i].message);
1044 		}
1045 		i++;
1046 	}
1047 	return (NULL);
1048 }
1049 
1050 static char *
1051 scsi_asc_ascq_name(uint_t asc, uint_t ascq, char *tmpstr,
1052 	struct scsi_asq_key_strings *list)
1053 {
1054 	char *message;
1055 
1056 	if (list) {
1057 		if (message = scsi_asc_search(asc, ascq, list)) {
1058 			return (message);
1059 		}
1060 	}
1061 	if (message = scsi_asc_search(asc, ascq, extended_sense_list)) {
1062 		return (message);
1063 	}
1064 
1065 	return (sprintf(tmpstr, "<vendor unique code 0x%x>", asc));
1066 }
1067 
1068 /*
1069  * The first part/column of the error message will be at least this length.
1070  * This number has been calculated so that each line fits in 80 chars.
1071  */
1072 #define	SCSI_ERRMSG_COLUMN_LEN	42
1073 #define	SCSI_ERRMSG_BUF_LEN	256
1074 
1075 void
1076 scsi_vu_errmsg(struct scsi_device *devp, struct scsi_pkt *pkt, char *label,
1077     int severity, daddr_t blkno, daddr_t err_blkno,
1078     struct scsi_key_strings *cmdlist, struct scsi_extended_sense *sensep,
1079     struct scsi_asq_key_strings *asc_list,
1080     char *(*decode_fru)(struct scsi_device *, char *, int, uchar_t))
1081 {
1082 	uchar_t com;
1083 	static char buf[SCSI_ERRMSG_BUF_LEN];
1084 	static char buf1[SCSI_ERRMSG_BUF_LEN];
1085 	static char tmpbuf[64];
1086 	static char pad[SCSI_ERRMSG_COLUMN_LEN];
1087 	dev_info_t *dev = devp->sd_dev;
1088 	static char *error_classes[] = {
1089 		"All", "Unknown", "Informational",
1090 		"Recovered", "Retryable", "Fatal"
1091 	};
1092 	uchar_t sense_key, asc, ascq, fru_code;
1093 	uchar_t *fru_code_ptr;
1094 	int i, buflen;
1095 
1096 	mutex_enter(&scsi_log_mutex);
1097 
1098 	/*
1099 	 * We need to put our space padding code because kernel version
1100 	 * of sprintf(9F) doesn't support %-<number>s type of left alignment.
1101 	 */
1102 	for (i = 0; i < SCSI_ERRMSG_COLUMN_LEN; i++) {
1103 		pad[i] = ' ';
1104 	}
1105 
1106 	bzero(buf, 256);
1107 	com = ((union scsi_cdb *)pkt->pkt_cdbp)->scc_cmd;
1108 	(void) sprintf(buf, "Error for Command: %s",
1109 	    scsi_cmd_name(com, cmdlist, tmpbuf));
1110 	buflen = strlen(buf);
1111 	if (buflen < SCSI_ERRMSG_COLUMN_LEN) {
1112 		pad[SCSI_ERRMSG_COLUMN_LEN - buflen] = '\0';
1113 		(void) sprintf(&buf[buflen], "%s Error Level: %s",
1114 		    pad, error_classes[severity]);
1115 		pad[SCSI_ERRMSG_COLUMN_LEN - buflen] = ' ';
1116 	} else {
1117 		(void) sprintf(&buf[buflen], " Error Level: %s",
1118 		    error_classes[severity]);
1119 	}
1120 	impl_scsi_log(dev, label, CE_WARN, buf);
1121 
1122 	if (blkno != -1 || err_blkno != -1 &&
1123 	    ((com & 0xf) == SCMD_READ) || ((com & 0xf) == SCMD_WRITE)) {
1124 		bzero(buf, 256);
1125 		(void) sprintf(buf, "Requested Block: %ld", blkno);
1126 		buflen = strlen(buf);
1127 		if (buflen < SCSI_ERRMSG_COLUMN_LEN) {
1128 			pad[SCSI_ERRMSG_COLUMN_LEN - buflen] = '\0';
1129 			(void) sprintf(&buf[buflen], "%s Error Block: %ld\n",
1130 			    pad, err_blkno);
1131 			pad[SCSI_ERRMSG_COLUMN_LEN - buflen] = ' ';
1132 		} else {
1133 			(void) sprintf(&buf[buflen], " Error Block: %ld\n",
1134 			    err_blkno);
1135 		}
1136 		impl_scsi_log(dev, label, CE_CONT, buf);
1137 	}
1138 
1139 	bzero(buf, 256);
1140 	(void) strcpy(buf, "Vendor: ");
1141 	inq_fill(devp->sd_inq->inq_vid, 8, &buf[strlen(buf)]);
1142 	buflen = strlen(buf);
1143 	if (buflen < SCSI_ERRMSG_COLUMN_LEN) {
1144 		pad[SCSI_ERRMSG_COLUMN_LEN - buflen] = '\0';
1145 		(void) sprintf(&buf[strlen(buf)], "%s Serial Number: ", pad);
1146 		pad[SCSI_ERRMSG_COLUMN_LEN - buflen] = ' ';
1147 	} else {
1148 		(void) sprintf(&buf[strlen(buf)], " Serial Number: ");
1149 	}
1150 	inq_fill(devp->sd_inq->inq_serial, 12, &buf[strlen(buf)]);
1151 	impl_scsi_log(dev, label, CE_CONT, "%s\n", buf);
1152 
1153 	if (sensep) {
1154 		sense_key = scsi_sense_key((uint8_t *)sensep);
1155 		asc = scsi_sense_asc((uint8_t *)sensep);
1156 		ascq = scsi_sense_ascq((uint8_t *)sensep);
1157 		scsi_ext_sense_fields((uint8_t *)sensep, SENSE_LENGTH,
1158 		    NULL, NULL, &fru_code_ptr, NULL, NULL);
1159 		fru_code = (fru_code_ptr ? *fru_code_ptr : 0);
1160 
1161 		bzero(buf, 256);
1162 		(void) sprintf(buf, "Sense Key: %s\n",
1163 		    sense_keys[sense_key]);
1164 		impl_scsi_log(dev, label, CE_CONT, buf);
1165 
1166 		bzero(buf, 256);
1167 		if ((fru_code != 0) &&
1168 		    (decode_fru != NULL)) {
1169 			(*decode_fru)(devp, buf, SCSI_ERRMSG_BUF_LEN,
1170 			    fru_code);
1171 			if (buf[0] != NULL) {
1172 				bzero(buf1, 256);
1173 				(void) sprintf(&buf1[strlen(buf1)],
1174 				    "ASC: 0x%x (%s)", asc,
1175 				    scsi_asc_ascq_name(asc, ascq,
1176 					tmpbuf, asc_list));
1177 				buflen = strlen(buf1);
1178 				if (buflen < SCSI_ERRMSG_COLUMN_LEN) {
1179 				    pad[SCSI_ERRMSG_COLUMN_LEN - buflen] = '\0';
1180 				    (void) sprintf(&buf1[buflen],
1181 				    "%s ASCQ: 0x%x", pad, ascq);
1182 				} else {
1183 				    (void) sprintf(&buf1[buflen], " ASCQ: 0x%x",
1184 					ascq);
1185 				}
1186 				impl_scsi_log(dev,
1187 					label, CE_CONT, "%s\n", buf1);
1188 				impl_scsi_log(dev, label, CE_CONT,
1189 					"FRU: 0x%x (%s)\n",
1190 						fru_code, buf);
1191 				mutex_exit(&scsi_log_mutex);
1192 				return;
1193 			}
1194 		}
1195 		(void) sprintf(&buf[strlen(buf)],
1196 		    "ASC: 0x%x (%s), ASCQ: 0x%x, FRU: 0x%x",
1197 		    asc, scsi_asc_ascq_name(asc, ascq, tmpbuf, asc_list),
1198 		    ascq, fru_code);
1199 		impl_scsi_log(dev, label, CE_CONT, "%s\n", buf);
1200 	}
1201 	mutex_exit(&scsi_log_mutex);
1202 }
1203 
1204 void
1205 scsi_errmsg(struct scsi_device *devp, struct scsi_pkt *pkt, char *label,
1206     int severity, daddr_t blkno, daddr_t err_blkno,
1207     struct scsi_key_strings *cmdlist, struct scsi_extended_sense *sensep)
1208 {
1209 	scsi_vu_errmsg(devp, pkt, label, severity, blkno,
1210 		err_blkno, cmdlist, sensep, NULL, NULL);
1211 }
1212 
1213 /*PRINTFLIKE4*/
1214 void
1215 scsi_log(dev_info_t *dev, char *label, uint_t level,
1216     const char *fmt, ...)
1217 {
1218 	va_list ap;
1219 
1220 	va_start(ap, fmt);
1221 	mutex_enter(&scsi_log_mutex);
1222 	v_scsi_log(dev, label, level, fmt, ap);
1223 	mutex_exit(&scsi_log_mutex);
1224 	va_end(ap);
1225 }
1226 
1227 /*PRINTFLIKE4*/
1228 static void
1229 impl_scsi_log(dev_info_t *dev, char *label, uint_t level,
1230     const char *fmt, ...)
1231 {
1232 	va_list ap;
1233 
1234 	ASSERT(mutex_owned(&scsi_log_mutex));
1235 
1236 	va_start(ap, fmt);
1237 	v_scsi_log(dev, label, level, fmt, ap);
1238 	va_end(ap);
1239 }
1240 
1241 
1242 char *ddi_pathname(dev_info_t *dip, char *path);
1243 
1244 /*PRINTFLIKE4*/
1245 static void
1246 v_scsi_log(dev_info_t *dev, char *label, uint_t level,
1247     const char *fmt, va_list ap)
1248 {
1249 	static char name[256];
1250 	int log_only = 0;
1251 	int boot_only = 0;
1252 	int console_only = 0;
1253 
1254 	ASSERT(mutex_owned(&scsi_log_mutex));
1255 
1256 	if (dev) {
1257 		if (level == CE_PANIC || level == CE_WARN ||
1258 		    level == CE_NOTE) {
1259 			(void) sprintf(name, "%s (%s%d):\n",
1260 				ddi_pathname(dev, scsi_log_buffer),
1261 				label, ddi_get_instance(dev));
1262 		} else if (level >= (uint_t)SCSI_DEBUG) {
1263 			(void) sprintf(name,
1264 			    "%s%d:", label, ddi_get_instance(dev));
1265 		} else {
1266 			name[0] = '\0';
1267 		}
1268 	} else {
1269 		(void) sprintf(name, "%s:", label);
1270 	}
1271 
1272 	(void) vsprintf(scsi_log_buffer, fmt, ap);
1273 
1274 	switch (scsi_log_buffer[0]) {
1275 	case '!':
1276 		log_only = 1;
1277 		break;
1278 	case '?':
1279 		boot_only = 1;
1280 		break;
1281 	case '^':
1282 		console_only = 1;
1283 		break;
1284 	}
1285 
1286 	switch (level) {
1287 	case CE_NOTE:
1288 		level = CE_CONT;
1289 		/* FALLTHROUGH */
1290 	case CE_CONT:
1291 	case CE_WARN:
1292 	case CE_PANIC:
1293 		if (boot_only) {
1294 			cmn_err(level, "?%s\t%s", name,
1295 				&scsi_log_buffer[1]);
1296 		} else if (console_only) {
1297 			cmn_err(level, "^%s\t%s", name,
1298 				&scsi_log_buffer[1]);
1299 		} else if (log_only) {
1300 			cmn_err(level, "!%s\t%s", name,
1301 				&scsi_log_buffer[1]);
1302 		} else {
1303 			cmn_err(level, "%s\t%s", name,
1304 				scsi_log_buffer);
1305 		}
1306 		break;
1307 	case (uint_t)SCSI_DEBUG:
1308 	default:
1309 		cmn_err(CE_CONT, "^DEBUG: %s\t%s", name,
1310 				scsi_log_buffer);
1311 		break;
1312 	}
1313 }
1314 
1315 int
1316 scsi_get_device_type_scsi_options(dev_info_t *dip,
1317     struct scsi_device *devp, int default_scsi_options)
1318 {
1319 
1320 	caddr_t config_list	= NULL;
1321 	int options		= default_scsi_options;
1322 	struct scsi_inquiry  *inq = devp->sd_inq;
1323 	caddr_t vidptr, datanameptr;
1324 	int	vidlen, dupletlen;
1325 	int config_list_len, len;
1326 
1327 	/*
1328 	 * look up the device-type-scsi-options-list and walk thru
1329 	 * the list
1330 	 * compare the vendor ids of the earlier inquiry command and
1331 	 * with those vids in the list
1332 	 * if there is a match, lookup the scsi-options value
1333 	 */
1334 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1335 	    "device-type-scsi-options-list",
1336 	    (caddr_t)&config_list, &config_list_len) == DDI_PROP_SUCCESS) {
1337 
1338 		/*
1339 		 * Compare vids in each duplet - if it matches, get value for
1340 		 * dataname and then lookup scsi_options
1341 		 * dupletlen is calculated later.
1342 		 */
1343 		for (len = config_list_len, vidptr = config_list; len > 0;
1344 		    vidptr += dupletlen, len -= dupletlen) {
1345 
1346 			vidlen = strlen(vidptr);
1347 			datanameptr = vidptr + vidlen + 1;
1348 
1349 			if ((vidlen != 0) &&
1350 			    bcmp(inq->inq_vid, vidptr, vidlen) == 0) {
1351 				/*
1352 				 * get the data list
1353 				 */
1354 				options = ddi_prop_get_int(DDI_DEV_T_ANY,
1355 				    dip, 0,
1356 				    datanameptr, default_scsi_options);
1357 				break;
1358 			}
1359 			dupletlen = vidlen + strlen(datanameptr) + 2;
1360 		}
1361 		kmem_free(config_list, config_list_len);
1362 	}
1363 
1364 	return (options);
1365 }
1366 
1367 /*
1368  * Functions for format-neutral sense data functions
1369  */
1370 
1371 int
1372 scsi_validate_sense(uint8_t *sense_buffer, int sense_buf_len, int *flags)
1373 {
1374 	int result;
1375 	struct scsi_extended_sense *es =
1376 	    (struct scsi_extended_sense *)sense_buffer;
1377 
1378 	/*
1379 	 * Init flags if present
1380 	 */
1381 	if (flags != NULL) {
1382 		*flags = 0;
1383 	}
1384 
1385 	/*
1386 	 * Check response code (Solaris breaks this into a 3-bit class
1387 	 * and 4-bit code field.
1388 	 */
1389 	if ((es->es_class != CLASS_EXTENDED_SENSE) ||
1390 	    ((es->es_code != CODE_FMT_FIXED_CURRENT) &&
1391 		(es->es_code != CODE_FMT_FIXED_DEFERRED) &&
1392 		(es->es_code != CODE_FMT_DESCR_CURRENT) &&
1393 		(es->es_code != CODE_FMT_DESCR_DEFERRED))) {
1394 		/*
1395 		 * Sense data (if there's actually anything here) is not
1396 		 * in a format we can handle).
1397 		 */
1398 		return (SENSE_UNUSABLE);
1399 	}
1400 
1401 	/*
1402 	 * Check if this is deferred sense
1403 	 */
1404 	if ((flags != NULL) &&
1405 	    ((es->es_code == CODE_FMT_FIXED_DEFERRED) ||
1406 		(es->es_code == CODE_FMT_DESCR_DEFERRED))) {
1407 		*flags |= SNS_BUF_DEFERRED;
1408 	}
1409 
1410 	/*
1411 	 * Make sure length is OK
1412 	 */
1413 	if (es->es_code == CODE_FMT_FIXED_CURRENT ||
1414 	    es->es_code == CODE_FMT_FIXED_DEFERRED) {
1415 		/*
1416 		 * We can get by with a buffer that only includes the key,
1417 		 * asc, and ascq.  In reality the minimum length we should
1418 		 * ever see is 18 bytes.
1419 		 */
1420 		if ((sense_buf_len < MIN_FIXED_SENSE_LEN) ||
1421 		    ((es->es_add_len + ADDL_SENSE_ADJUST) <
1422 			MIN_FIXED_SENSE_LEN)) {
1423 			result = SENSE_UNUSABLE;
1424 		} else {
1425 			/*
1426 			 * The es_add_len field contains the number of sense
1427 			 * data bytes that follow the es_add_len field.
1428 			 */
1429 			if ((flags != NULL) &&
1430 			    (sense_buf_len <
1431 				(es->es_add_len + ADDL_SENSE_ADJUST))) {
1432 				*flags |= SNS_BUF_OVERFLOW;
1433 			}
1434 
1435 			result = SENSE_FIXED_FORMAT;
1436 		}
1437 	} else {
1438 		struct scsi_descr_sense_hdr *ds =
1439 		    (struct scsi_descr_sense_hdr *)sense_buffer;
1440 
1441 		/*
1442 		 * For descriptor format we need at least the descriptor
1443 		 * header
1444 		 */
1445 		if (sense_buf_len < sizeof (struct scsi_descr_sense_hdr)) {
1446 			result = SENSE_UNUSABLE;
1447 		} else {
1448 			/*
1449 			 * Check for overflow
1450 			 */
1451 			if ((flags != NULL) &&
1452 			    (sense_buf_len <
1453 				(ds->ds_addl_sense_length + sizeof (*ds)))) {
1454 				*flags |= SNS_BUF_OVERFLOW;
1455 			}
1456 
1457 			result = SENSE_DESCR_FORMAT;
1458 		}
1459 	}
1460 
1461 	return (result);
1462 }
1463 
1464 
1465 uint8_t
1466 scsi_sense_key(uint8_t *sense_buffer)
1467 {
1468 	uint8_t skey;
1469 	if (SCSI_IS_DESCR_SENSE(sense_buffer)) {
1470 		struct scsi_descr_sense_hdr *sdsp =
1471 		    (struct scsi_descr_sense_hdr *)sense_buffer;
1472 		skey = sdsp->ds_key;
1473 	} else {
1474 		struct scsi_extended_sense *ext_sensep =
1475 		    (struct scsi_extended_sense *)sense_buffer;
1476 		skey = ext_sensep->es_key;
1477 	}
1478 	return (skey);
1479 }
1480 
1481 uint8_t
1482 scsi_sense_asc(uint8_t *sense_buffer)
1483 {
1484 	uint8_t asc;
1485 	if (SCSI_IS_DESCR_SENSE(sense_buffer)) {
1486 		struct scsi_descr_sense_hdr *sdsp =
1487 		    (struct scsi_descr_sense_hdr *)sense_buffer;
1488 		asc = sdsp->ds_add_code;
1489 	} else {
1490 		struct scsi_extended_sense *ext_sensep =
1491 		    (struct scsi_extended_sense *)sense_buffer;
1492 		asc = ext_sensep->es_add_code;
1493 	}
1494 	return (asc);
1495 }
1496 
1497 uint8_t
1498 scsi_sense_ascq(uint8_t *sense_buffer)
1499 {
1500 	uint8_t ascq;
1501 	if (SCSI_IS_DESCR_SENSE(sense_buffer)) {
1502 		struct scsi_descr_sense_hdr *sdsp =
1503 		    (struct scsi_descr_sense_hdr *)sense_buffer;
1504 		ascq = sdsp->ds_qual_code;
1505 	} else {
1506 		struct scsi_extended_sense *ext_sensep =
1507 		    (struct scsi_extended_sense *)sense_buffer;
1508 		ascq = ext_sensep->es_qual_code;
1509 	}
1510 	return (ascq);
1511 }
1512 
1513 void scsi_ext_sense_fields(uint8_t *sense_buffer, int sense_buf_len,
1514     uint8_t **information, uint8_t **cmd_spec_info, uint8_t **fru_code,
1515     uint8_t **sk_specific, uint8_t **stream_flags)
1516 {
1517 	int sense_fmt;
1518 
1519 	/*
1520 	 * Sanity check sense data and determine the format
1521 	 */
1522 	sense_fmt = scsi_validate_sense(sense_buffer, sense_buf_len, NULL);
1523 
1524 	/*
1525 	 * Initialize any requested data to 0
1526 	 */
1527 	if (information) {
1528 		*information = NULL;
1529 	}
1530 	if (cmd_spec_info) {
1531 		*cmd_spec_info = NULL;
1532 	}
1533 	if (fru_code) {
1534 		*fru_code = NULL;
1535 	}
1536 	if (sk_specific) {
1537 		*sk_specific = NULL;
1538 	}
1539 	if (stream_flags) {
1540 		*stream_flags = NULL;
1541 	}
1542 
1543 	if (sense_fmt == SENSE_DESCR_FORMAT) {
1544 		struct scsi_descr_template *sdt = NULL;
1545 
1546 		while (scsi_get_next_descr(sense_buffer,
1547 		    sense_buf_len, &sdt) != -1) {
1548 			switch (sdt->sdt_descr_type) {
1549 			case DESCR_INFORMATION: {
1550 				struct scsi_information_sense_descr *isd =
1551 				    (struct scsi_information_sense_descr *)
1552 				    sdt;
1553 				if (information) {
1554 					*information =
1555 					    &isd->isd_information[0];
1556 				}
1557 				break;
1558 			}
1559 			case DESCR_COMMAND_SPECIFIC: {
1560 				struct scsi_cmd_specific_sense_descr *csd =
1561 				    (struct scsi_cmd_specific_sense_descr *)
1562 				    sdt;
1563 				if (cmd_spec_info) {
1564 					*cmd_spec_info =
1565 					    &csd->css_cmd_specific_info[0];
1566 				}
1567 				break;
1568 			}
1569 			case DESCR_SENSE_KEY_SPECIFIC: {
1570 				struct scsi_sk_specific_sense_descr *ssd =
1571 				    (struct scsi_sk_specific_sense_descr *)
1572 				    sdt;
1573 				if (sk_specific) {
1574 					*sk_specific =
1575 					    (uint8_t *)&ssd->sss_data;
1576 				}
1577 				break;
1578 			}
1579 			case DESCR_FRU: {
1580 				struct scsi_fru_sense_descr *fsd =
1581 				    (struct scsi_fru_sense_descr *)
1582 				    sdt;
1583 				if (fru_code) {
1584 					*fru_code = &fsd->fs_fru_code;
1585 				}
1586 				break;
1587 			}
1588 			case DESCR_STREAM_COMMANDS: {
1589 				struct scsi_stream_cmd_sense_descr *strsd =
1590 				    (struct scsi_stream_cmd_sense_descr *)
1591 				    sdt;
1592 				if (stream_flags) {
1593 					*stream_flags =
1594 					    (uint8_t *)&strsd->scs_data;
1595 				}
1596 				break;
1597 			}
1598 			case DESCR_BLOCK_COMMANDS: {
1599 				struct scsi_block_cmd_sense_descr *bsd =
1600 				    (struct scsi_block_cmd_sense_descr *)
1601 				    sdt;
1602 				/*
1603 				 * The "Block Command" sense descriptor
1604 				 * contains an ili bit that we can store
1605 				 * in the stream specific data if it is
1606 				 * available.  We shouldn't see both
1607 				 * a block command and a stream command
1608 				 * descriptor in the same collection
1609 				 * of sense data.
1610 				 */
1611 				if (stream_flags) {
1612 					/*
1613 					 * Can't take an address of a bitfield,
1614 					 * but the flags are just after the
1615 					 * bcs_reserved field.
1616 					 */
1617 					*stream_flags =
1618 					    (uint8_t *)&bsd->bcs_reserved + 1;
1619 				}
1620 				break;
1621 			}
1622 			}
1623 		}
1624 	} else {
1625 		struct scsi_extended_sense *es =
1626 		    (struct scsi_extended_sense *)sense_buffer;
1627 
1628 		/* Get data from fixed sense buffer */
1629 		if (information && es->es_valid) {
1630 			*information = &es->es_info_1;
1631 		}
1632 		if (cmd_spec_info && es->es_valid) {
1633 			*cmd_spec_info = &es->es_cmd_info[0];
1634 		}
1635 		if (fru_code) {
1636 			*fru_code = &es->es_fru_code;
1637 		}
1638 		if (sk_specific) {
1639 			*sk_specific = &es->es_skey_specific[0];
1640 		}
1641 		if (stream_flags) {
1642 			/*
1643 			 * Can't take the address of a bit field,
1644 			 * but the stream flags are located just after
1645 			 * the es_segnum field;
1646 			 */
1647 			*stream_flags = &es->es_segnum + 1;
1648 		}
1649 	}
1650 }
1651 
1652 boolean_t
1653 scsi_sense_info_uint64(uint8_t *sense_buffer, int sense_buf_len,
1654     uint64_t *information)
1655 {
1656 	boolean_t valid;
1657 	int sense_fmt;
1658 
1659 	ASSERT(sense_buffer != NULL);
1660 	ASSERT(information != NULL);
1661 
1662 	/* Validate sense data and get format */
1663 	sense_fmt = scsi_validate_sense(sense_buffer, sense_buf_len, NULL);
1664 
1665 	if (sense_fmt == SENSE_UNUSABLE) {
1666 		/* Information is not valid */
1667 		valid = 0;
1668 	} else if (sense_fmt == SENSE_FIXED_FORMAT) {
1669 		struct scsi_extended_sense *es =
1670 		    (struct scsi_extended_sense *)sense_buffer;
1671 
1672 		*information = (uint64_t)SCSI_READ32(&es->es_info_1);
1673 
1674 		valid = es->es_valid;
1675 	} else {
1676 		/* Sense data is descriptor format */
1677 		struct scsi_information_sense_descr *isd;
1678 
1679 		isd = (struct scsi_information_sense_descr *)
1680 		    scsi_find_sense_descr(sense_buffer, sense_buf_len,
1681 			DESCR_INFORMATION);
1682 
1683 		if (isd) {
1684 			*information = SCSI_READ64(isd->isd_information);
1685 			valid = 1;
1686 		} else {
1687 			valid = 0;
1688 		}
1689 	}
1690 
1691 	return (valid);
1692 }
1693 
1694 boolean_t
1695 scsi_sense_cmdspecific_uint64(uint8_t *sense_buffer, int sense_buf_len,
1696     uint64_t *cmd_specific_info)
1697 {
1698 	boolean_t valid;
1699 	int sense_fmt;
1700 
1701 	ASSERT(sense_buffer != NULL);
1702 	ASSERT(cmd_specific_info != NULL);
1703 
1704 	/* Validate sense data and get format */
1705 	sense_fmt = scsi_validate_sense(sense_buffer, sense_buf_len, NULL);
1706 
1707 	if (sense_fmt == SENSE_UNUSABLE) {
1708 		/* Command specific info is not valid */
1709 		valid = 0;
1710 	} else if (sense_fmt == SENSE_FIXED_FORMAT) {
1711 		struct scsi_extended_sense *es =
1712 		    (struct scsi_extended_sense *)sense_buffer;
1713 
1714 		*cmd_specific_info = (uint64_t)SCSI_READ32(es->es_cmd_info);
1715 
1716 		valid = es->es_valid;
1717 	} else {
1718 		/* Sense data is descriptor format */
1719 		struct scsi_cmd_specific_sense_descr *c;
1720 
1721 		c = (struct scsi_cmd_specific_sense_descr *)
1722 		    scsi_find_sense_descr(sense_buffer, sense_buf_len,
1723 			DESCR_COMMAND_SPECIFIC);
1724 
1725 		if (c) {
1726 			valid = 1;
1727 			*cmd_specific_info =
1728 			    SCSI_READ64(c->css_cmd_specific_info);
1729 		} else {
1730 			valid = 0;
1731 		}
1732 	}
1733 
1734 	return (valid);
1735 }
1736 
1737 uint8_t *
1738 scsi_find_sense_descr(uint8_t *sdsp, int sense_buf_len, int req_descr_type)
1739 {
1740 	struct scsi_descr_template *sdt = NULL;
1741 
1742 	while (scsi_get_next_descr(sdsp, sense_buf_len, &sdt) != -1) {
1743 		ASSERT(sdt != NULL);
1744 		if (sdt->sdt_descr_type == req_descr_type) {
1745 			/* Found requested descriptor type */
1746 			break;
1747 		}
1748 	}
1749 
1750 	return ((uint8_t *)sdt);
1751 }
1752 
1753 /*
1754  * Sense Descriptor format is:
1755  *
1756  * <Descriptor type> <Descriptor length> <Descriptor data> ...
1757  *
1758  * 2 must be added to the descriptor length value to get the
1759  * total descriptor length sense the stored length does not
1760  * include the "type" and "additional length" fields.
1761  */
1762 
1763 #define	NEXT_DESCR_PTR(ndp_descr) \
1764 	((struct scsi_descr_template *)(((uint8_t *)(ndp_descr)) + \
1765 	    ((ndp_descr)->sdt_addl_length + \
1766 	    sizeof (struct scsi_descr_template))))
1767 
1768 static int
1769 scsi_get_next_descr(uint8_t *sense_buffer,
1770     int sense_buf_len, struct scsi_descr_template **descrpp)
1771 {
1772 	struct scsi_descr_sense_hdr *sdsp =
1773 	    (struct scsi_descr_sense_hdr *)sense_buffer;
1774 	struct scsi_descr_template *cur_descr;
1775 	boolean_t find_first;
1776 	int valid_sense_length;
1777 
1778 	ASSERT(descrpp != NULL);
1779 	find_first = (*descrpp == NULL);
1780 
1781 	/*
1782 	 * If no descriptor is passed in then return the first
1783 	 * descriptor
1784 	 */
1785 	if (find_first) {
1786 		/*
1787 		 * The first descriptor will immediately follow the header
1788 		 * (Pointer arithmetic)
1789 		 */
1790 		cur_descr = (struct scsi_descr_template *)(sdsp+1);
1791 	} else {
1792 		cur_descr = *descrpp;
1793 		ASSERT(cur_descr > (struct scsi_descr_template *)sdsp);
1794 	}
1795 
1796 	/* Assume no more descriptors are available */
1797 	*descrpp = NULL;
1798 
1799 	/*
1800 	 * Calculate the amount of valid sense data -- make sure the length
1801 	 * byte in this descriptor lies within the valid sense data.
1802 	 */
1803 	valid_sense_length =
1804 	    min((sizeof (struct scsi_descr_sense_hdr) +
1805 	    sdsp->ds_addl_sense_length),
1806 	    sense_buf_len);
1807 
1808 	/*
1809 	 * Make sure this descriptor is complete (either the first
1810 	 * descriptor or the descriptor passed in)
1811 	 */
1812 	if (scsi_validate_descr(sdsp, valid_sense_length, cur_descr) !=
1813 	    DESCR_GOOD) {
1814 		return (-1);
1815 	}
1816 
1817 	/*
1818 	 * If we were looking for the first descriptor go ahead and return it
1819 	 */
1820 	if (find_first) {
1821 		*descrpp = cur_descr;
1822 		return ((*descrpp)->sdt_descr_type);
1823 	}
1824 
1825 	/*
1826 	 * Get pointer to next descriptor
1827 	 */
1828 	cur_descr = NEXT_DESCR_PTR(cur_descr);
1829 
1830 	/*
1831 	 * Make sure this descriptor is also complete.
1832 	 */
1833 	if (scsi_validate_descr(sdsp, valid_sense_length, cur_descr) !=
1834 	    DESCR_GOOD) {
1835 		return (-1);
1836 	}
1837 
1838 	*descrpp = (struct scsi_descr_template *)cur_descr;
1839 	return ((*descrpp)->sdt_descr_type);
1840 }
1841 
1842 static int
1843 scsi_validate_descr(struct scsi_descr_sense_hdr *sdsp,
1844     int valid_sense_length, struct scsi_descr_template *descrp)
1845 {
1846 	int descr_offset, next_descr_offset;
1847 
1848 	/*
1849 	 * Make sure length is present
1850 	 */
1851 	descr_offset = (uint8_t *)descrp - (uint8_t *)sdsp;
1852 	if (descr_offset + sizeof (struct scsi_descr_template) >
1853 	    valid_sense_length) {
1854 		return (DESCR_PARTIAL);
1855 	}
1856 
1857 	/*
1858 	 * Check if length is 0 (no more descriptors)
1859 	 */
1860 	if (descrp->sdt_addl_length == 0) {
1861 		return (DESCR_END);
1862 	}
1863 
1864 	/*
1865 	 * Make sure the rest of the descriptor is present
1866 	 */
1867 	next_descr_offset =
1868 	    (uint8_t *)NEXT_DESCR_PTR(descrp) - (uint8_t *)sdsp;
1869 	if (next_descr_offset > valid_sense_length) {
1870 		return (DESCR_PARTIAL);
1871 	}
1872 
1873 	return (DESCR_GOOD);
1874 }
1875