xref: /illumos-gate/usr/src/uts/common/io/scsi/impl/scsi_subr.c (revision 4de2612967d06c4fdbf524a62556a1e8118a006f)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 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 
57 /*PRINTFLIKE4*/
58 static void impl_scsi_log(dev_info_t *dev, char *label, uint_t level,
59     const char *fmt, ...) __KPRINTFLIKE(4);
60 /*PRINTFLIKE4*/
61 static void v_scsi_log(dev_info_t *dev, char *label, uint_t level,
62     const char *fmt, va_list ap) __KVPRINTFLIKE(4);
63 
64 int
65 scsi_poll(struct scsi_pkt *pkt)
66 {
67 	register int busy_count, rval = -1, savef;
68 	long savet;
69 	void (*savec)();
70 	extern int do_polled_io;
71 
72 	/*
73 	 * save old flags..
74 	 */
75 	savef = pkt->pkt_flags;
76 	savec = pkt->pkt_comp;
77 	savet = pkt->pkt_time;
78 
79 	pkt->pkt_flags |= FLAG_NOINTR;
80 
81 	/*
82 	 * XXX there is nothing in the SCSA spec that states that we should not
83 	 * do a callback for polled cmds; however, removing this will break sd
84 	 * and probably other target drivers
85 	 */
86 	pkt->pkt_comp = 0;
87 
88 	/*
89 	 * we don't like a polled command without timeout.
90 	 * 60 seconds seems long enough.
91 	 */
92 	if (pkt->pkt_time == 0)
93 		pkt->pkt_time = SCSI_POLL_TIMEOUT;
94 
95 	/*
96 	 * Send polled cmd.
97 	 *
98 	 * We do some error recovery for various errors.  Tran_busy,
99 	 * queue full, and non-dispatched commands are retried every 10 msec.
100 	 * as they are typically transient failures.  Busy status is retried
101 	 * every second as this status takes a while to change.
102 	 */
103 	for (busy_count = 0; busy_count < (pkt->pkt_time * SEC_TO_CSEC);
104 		busy_count++) {
105 		int rc;
106 		int poll_delay;
107 
108 		/*
109 		 * Initialize pkt status variables.
110 		 */
111 		*pkt->pkt_scbp = pkt->pkt_reason = pkt->pkt_state = 0;
112 
113 		if ((rc = scsi_transport(pkt)) != TRAN_ACCEPT) {
114 			if (rc != TRAN_BUSY) {
115 				/* Transport failed - give up. */
116 				break;
117 			} else {
118 				/* Transport busy - try again. */
119 				poll_delay = 1 *CSEC;		/* 10 msec. */
120 			}
121 		} else {
122 			/*
123 			 * Transport accepted - check pkt status.
124 			 */
125 			rc = (*pkt->pkt_scbp) & STATUS_MASK;
126 
127 			if (pkt->pkt_reason == CMD_CMPLT &&
128 			    rc == STATUS_GOOD) {
129 				/* No error - we're done */
130 				rval = 0;
131 				break;
132 
133 			} else if (pkt->pkt_reason == CMD_INCOMPLETE &&
134 			    pkt->pkt_state == 0) {
135 				/* Pkt not dispatched - try again. */
136 				poll_delay = 1 *CSEC;		/* 10 msec. */
137 
138 			} else if (pkt->pkt_reason == CMD_CMPLT &&
139 			    rc == STATUS_QFULL) {
140 				/* Queue full - try again. */
141 				poll_delay = 1 *CSEC;		/* 10 msec. */
142 
143 			} else if (pkt->pkt_reason == CMD_CMPLT &&
144 			    rc == STATUS_BUSY) {
145 				/* Busy - try again. */
146 				poll_delay = 100 *CSEC;		/* 1 sec. */
147 				busy_count += (SEC_TO_CSEC - 1);
148 
149 			} else {
150 				/* BAD status - give up. */
151 				break;
152 			}
153 		}
154 
155 		if ((curthread->t_flag & T_INTR_THREAD) == 0 &&
156 		    !do_polled_io) {
157 			delay(drv_usectohz(poll_delay));
158 		} else {
159 			/* we busy wait during cpr_dump or interrupt threads */
160 			drv_usecwait(poll_delay);
161 		}
162 	}
163 
164 	pkt->pkt_flags = savef;
165 	pkt->pkt_comp = savec;
166 	pkt->pkt_time = savet;
167 	return (rval);
168 }
169 
170 /*
171  * Command packaging routines.
172  *
173  * makecom_g*() are original routines and scsi_setup_cdb()
174  * is the new and preferred routine.
175  */
176 
177 /*
178  * These routines put LUN information in CDB byte 1 bits 7-5.
179  * This was required in SCSI-1. SCSI-2 allowed it but it preferred
180  * sending LUN information as part of IDENTIFY message.
181  * This is not allowed in SCSI-3.
182  */
183 
184 void
185 makecom_g0(struct scsi_pkt *pkt, struct scsi_device *devp,
186     int flag, int cmd, int addr, int cnt)
187 {
188 	MAKECOM_G0(pkt, devp, flag, cmd, addr, (uchar_t)cnt);
189 }
190 
191 void
192 makecom_g0_s(struct scsi_pkt *pkt, struct scsi_device *devp,
193     int flag, int cmd, int cnt, int fixbit)
194 {
195 	MAKECOM_G0_S(pkt, devp, flag, cmd, cnt, (uchar_t)fixbit);
196 }
197 
198 void
199 makecom_g1(struct scsi_pkt *pkt, struct scsi_device *devp,
200     int flag, int cmd, int addr, int cnt)
201 {
202 	MAKECOM_G1(pkt, devp, flag, cmd, addr, cnt);
203 }
204 
205 void
206 makecom_g5(struct scsi_pkt *pkt, struct scsi_device *devp,
207     int flag, int cmd, int addr, int cnt)
208 {
209 	MAKECOM_G5(pkt, devp, flag, cmd, addr, cnt);
210 }
211 
212 /*
213  * Following routine does not put LUN information in CDB.
214  * This interface must be used for SCSI-2 targets having
215  * more than 8 LUNs or a SCSI-3 target.
216  */
217 int
218 scsi_setup_cdb(union scsi_cdb *cdbp, uchar_t cmd, uint_t addr, uint_t cnt,
219     uint_t addtl_cdb_data)
220 {
221 	uint_t	addr_cnt;
222 
223 	cdbp->scc_cmd = cmd;
224 
225 	switch (CDB_GROUPID(cmd)) {
226 		case CDB_GROUPID_0:
227 			/*
228 			 * The following calculation is to take care of
229 			 * the fact that format of some 6 bytes tape
230 			 * command is different (compare 6 bytes disk and
231 			 * tape read commands).
232 			 */
233 			addr_cnt = (addr << 8) + cnt;
234 			addr = (addr_cnt & 0x1fffff00) >> 8;
235 			cnt = addr_cnt & 0xff;
236 			FORMG0ADDR(cdbp, addr);
237 			FORMG0COUNT(cdbp, cnt);
238 			break;
239 
240 		case CDB_GROUPID_1:
241 		case CDB_GROUPID_2:
242 			FORMG1ADDR(cdbp, addr);
243 			FORMG1COUNT(cdbp, cnt);
244 			break;
245 
246 		case CDB_GROUPID_4:
247 			FORMG4ADDR(cdbp, addr);
248 			FORMG4COUNT(cdbp, cnt);
249 			FORMG4ADDTL(cdbp, addtl_cdb_data);
250 			break;
251 
252 		case CDB_GROUPID_5:
253 			FORMG5ADDR(cdbp, addr);
254 			FORMG5COUNT(cdbp, cnt);
255 			break;
256 
257 		default:
258 			return (0);
259 	}
260 
261 	return (1);
262 }
263 
264 
265 /*
266  * Common iopbmap data area packet allocation routines
267  */
268 
269 struct scsi_pkt *
270 get_pktiopb(struct scsi_address *ap, caddr_t *datap, int cdblen, int statuslen,
271     int datalen, int readflag, int (*func)())
272 {
273 	scsi_hba_tran_t	*tran = A_TO_TRAN(ap);
274 	dev_info_t	*pdip = tran->tran_hba_dip;
275 	struct scsi_pkt	*pkt = NULL;
276 	struct buf	local;
277 
278 	if (!datap)
279 		return (pkt);
280 	*datap = (caddr_t)0;
281 	bzero((caddr_t)&local, sizeof (struct buf));
282 	if (ddi_iopb_alloc(pdip, (ddi_dma_lim_t *)0,
283 	    (uint_t)datalen, &local.b_un.b_addr)) {
284 		return (pkt);
285 	}
286 	if (readflag)
287 		local.b_flags = B_READ;
288 	local.b_bcount = datalen;
289 	pkt = (*tran->tran_init_pkt) (ap, NULL, &local,
290 		cdblen, statuslen, 0, PKT_CONSISTENT,
291 		(func == SLEEP_FUNC) ? SLEEP_FUNC : NULL_FUNC,
292 		NULL);
293 	if (!pkt) {
294 		ddi_iopb_free(local.b_un.b_addr);
295 		if (func != NULL_FUNC) {
296 			ddi_set_callback(func, NULL, &scsi_callback_id);
297 		}
298 	} else {
299 		*datap = local.b_un.b_addr;
300 	}
301 	return (pkt);
302 }
303 
304 /*
305  *  Equivalent deallocation wrapper
306  */
307 
308 void
309 free_pktiopb(struct scsi_pkt *pkt, caddr_t datap, int datalen)
310 {
311 	register struct scsi_address	*ap = P_TO_ADDR(pkt);
312 	register scsi_hba_tran_t	*tran = A_TO_TRAN(ap);
313 
314 	(*tran->tran_destroy_pkt)(ap, pkt);
315 	if (datap && datalen) {
316 		ddi_iopb_free(datap);
317 	}
318 	if (scsi_callback_id != 0) {
319 		ddi_run_callback(&scsi_callback_id);
320 	}
321 }
322 
323 /*
324  * Common naming functions
325  */
326 
327 static char scsi_tmpname[64];
328 
329 char *
330 scsi_dname(int dtyp)
331 {
332 	static char *dnames[] = {
333 		"Direct Access",
334 		"Sequential Access",
335 		"Printer",
336 		"Processor",
337 		"Write-Once/Read-Many",
338 		"Read-Only Direct Access",
339 		"Scanner",
340 		"Optical",
341 		"Changer",
342 		"Communications",
343 		"Array Controller"
344 	};
345 
346 	if ((dtyp & DTYPE_MASK) <= DTYPE_COMM) {
347 		return (dnames[dtyp&DTYPE_MASK]);
348 	} else if (dtyp == DTYPE_NOTPRESENT) {
349 		return ("Not Present");
350 	}
351 	return ("<unknown device type>");
352 
353 }
354 
355 char *
356 scsi_rname(uchar_t reason)
357 {
358 	static char *rnames[] = {
359 		"cmplt",
360 		"incomplete",
361 		"dma_derr",
362 		"tran_err",
363 		"reset",
364 		"aborted",
365 		"timeout",
366 		"data_ovr",
367 		"cmd_ovr",
368 		"sts_ovr",
369 		"badmsg",
370 		"nomsgout",
371 		"xid_fail",
372 		"ide_fail",
373 		"abort_fail",
374 		"reject_fail",
375 		"nop_fail",
376 		"per_fail",
377 		"bdr_fail",
378 		"id_fail",
379 		"unexpected_bus_free",
380 		"tag reject",
381 		"terminated"
382 	};
383 	if (reason > CMD_TAG_REJECT) {
384 		return ("<unknown reason>");
385 	} else {
386 		return (rnames[reason]);
387 	}
388 }
389 
390 char *
391 scsi_mname(uchar_t msg)
392 {
393 	static char *imsgs[23] = {
394 		"COMMAND COMPLETE",
395 		"EXTENDED",
396 		"SAVE DATA POINTER",
397 		"RESTORE POINTERS",
398 		"DISCONNECT",
399 		"INITIATOR DETECTED ERROR",
400 		"ABORT",
401 		"REJECT",
402 		"NO-OP",
403 		"MESSAGE PARITY",
404 		"LINKED COMMAND COMPLETE",
405 		"LINKED COMMAND COMPLETE (W/FLAG)",
406 		"BUS DEVICE RESET",
407 		"ABORT TAG",
408 		"CLEAR QUEUE",
409 		"INITIATE RECOVERY",
410 		"RELEASE RECOVERY",
411 		"TERMINATE PROCESS",
412 		"CONTINUE TASK",
413 		"TARGET TRANSFER DISABLE",
414 		"RESERVED (0x14)",
415 		"RESERVED (0x15)",
416 		"CLEAR ACA"
417 	};
418 	static char *imsgs_2[6] = {
419 		"SIMPLE QUEUE TAG",
420 		"HEAD OF QUEUE TAG",
421 		"ORDERED QUEUE TAG",
422 		"IGNORE WIDE RESIDUE",
423 		"ACA",
424 		"LOGICAL UNIT RESET"
425 	};
426 
427 	if (msg < 23) {
428 		return (imsgs[msg]);
429 	} else if (IS_IDENTIFY_MSG(msg)) {
430 		return ("IDENTIFY");
431 	} else if (IS_2BYTE_MSG(msg) &&
432 	    (int)((msg) & 0xF) < (sizeof (imsgs_2) / sizeof (char *))) {
433 		return (imsgs_2[msg & 0xF]);
434 	} else {
435 		return ("<unknown msg>");
436 	}
437 
438 }
439 
440 char *
441 scsi_cname(uchar_t cmd, register char **cmdvec)
442 {
443 	while (*cmdvec != (char *)0) {
444 		if (cmd == **cmdvec) {
445 			return ((char *)((long)(*cmdvec)+1));
446 		}
447 		cmdvec++;
448 	}
449 	return (sprintf(scsi_tmpname, "<undecoded cmd 0x%x>", cmd));
450 }
451 
452 char *
453 scsi_cmd_name(uchar_t cmd, struct scsi_key_strings *cmdlist, char *tmpstr)
454 {
455 	int i = 0;
456 
457 	while (cmdlist[i].key !=  -1) {
458 		if (cmd == cmdlist[i].key) {
459 			return ((char *)cmdlist[i].message);
460 		}
461 		i++;
462 	}
463 	return (sprintf(tmpstr, "<undecoded cmd 0x%x>", cmd));
464 }
465 
466 static struct scsi_asq_key_strings extended_sense_list[] = {
467 	0x00, 0x00, "no additional sense info",
468 	0x00, 0x01, "filemark detected",
469 	0x00, 0x02, "end of partition/medium detected",
470 	0x00, 0x03, "setmark detected",
471 	0x00, 0x04, "begining of partition/medium detected",
472 	0x00, 0x05, "end of data detected",
473 	0x00, 0x06, "i/o process terminated",
474 	0x00, 0x11, "audio play operation in progress",
475 	0x00, 0x12, "audio play operation paused",
476 	0x00, 0x13, "audio play operation successfully completed",
477 	0x00, 0x14, "audio play operation stopped due to error",
478 	0x00, 0x15, "no current audio status to return",
479 	0x00, 0x16, "operation in progress",
480 	0x00, 0x17, "cleaning requested",
481 	0x00, 0x18, "erase operation in progress",
482 	0x00, 0x19, "locate operation in progress",
483 	0x00, 0x1A, "rewind operation in progress",
484 	0x00, 0x1B, "set capacity operation in progress",
485 	0x00, 0x1C, "verify operation in progress",
486 	0x01, 0x00, "no index/sector signal",
487 	0x02, 0x00, "no seek complete",
488 	0x03, 0x00, "peripheral device write fault",
489 	0x03, 0x01, "no write current",
490 	0x03, 0x02, "excessive write errors",
491 	0x04, 0x00, "LUN not ready",
492 	0x04, 0x01, "LUN is becoming ready",
493 	0x04, 0x02, "LUN initializing command required",
494 	0x04, 0x03, "LUN not ready intervention required",
495 	0x04, 0x04, "LUN not ready format in progress",
496 	0x04, 0x05, "LUN not ready, rebuild in progress",
497 	0x04, 0x06, "LUN not ready, recalculation in progress",
498 	0x04, 0x07, "LUN not ready, operation in progress",
499 	0x04, 0x08, "LUN not ready, long write in progress",
500 	0x04, 0x09, "LUN not ready, self-test in progress",
501 	0x04, 0x0A, "LUN not accessible, asymmetric access state transition",
502 	0x04, 0x0B, "LUN not accessible, target port in standby state",
503 	0x04, 0x0C, "LUN not accessible, target port in unavailable state",
504 	0x04, 0x10, "LUN not ready, auxiliary memory not accessible",
505 	0x05, 0x00, "LUN does not respond to selection",
506 	0x06, 0x00, "reference position found",
507 	0x07, 0x00, "multiple peripheral devices selected",
508 	0x08, 0x00, "LUN communication failure",
509 	0x08, 0x01, "LUN communication time-out",
510 	0x08, 0x02, "LUN communication parity error",
511 	0x08, 0x03, "LUN communication crc error (ultra-DMA/32)",
512 	0x08, 0x04, "unreachable copy target",
513 	0x09, 0x00, "track following error",
514 	0x09, 0x01, "tracking servo failure",
515 	0x09, 0x02, "focus servo failure",
516 	0x09, 0x03, "spindle servo failure",
517 	0x09, 0x04, "head select fault",
518 	0x0a, 0x00, "error log overflow",
519 	0x0b, 0x00, "warning",
520 	0x0b, 0x01, "warning - specified temperature exceeded",
521 	0x0b, 0x02, "warning - enclosure degraded",
522 	0x0c, 0x00, "write error",
523 	0x0c, 0x01, "write error - recovered with auto reallocation",
524 	0x0c, 0x02, "write error - auto reallocation failed",
525 	0x0c, 0x03, "write error - recommend reassignment",
526 	0x0c, 0x04, "compression check miscompare error",
527 	0x0c, 0x05, "data expansion occurred during compression",
528 	0x0c, 0x06, "block not compressible",
529 	0x0c, 0x07, "write error - recovery needed",
530 	0x0c, 0x08, "write error - recovery failed",
531 	0x0c, 0x09, "write error - loss of streaming",
532 	0x0c, 0x0a, "write error - padding blocks added",
533 	0x0c, 0x0b, "auxiliary memory write error",
534 	0x0c, 0x0c, "write error - unexpected unsolicited data",
535 	0x0c, 0x0d, "write error - not enough unsolicited data",
536 	0x0d, 0x00, "error detected by third party temporary initiator",
537 	0x0d, 0x01, "third party device failure",
538 	0x0d, 0x02, "copy target device not reachable",
539 	0x0d, 0x03, "incorrect copy target device type",
540 	0x0d, 0x04, "copy target device data underrun",
541 	0x0d, 0x05, "copy target device data overrun",
542 	0x0e, 0x00, "invalid information unit",
543 	0x0e, 0x01, "information unit too short",
544 	0x0e, 0x02, "information unit too long",
545 	0x10, 0x00, "ID CRC or ECC error",
546 	0x11, 0x00, "unrecovered read error",
547 	0x11, 0x01, "read retries exhausted",
548 	0x11, 0x02, "error too long to correct",
549 	0x11, 0x03, "multiple read errors",
550 	0x11, 0x04, "unrecovered read error - auto reallocate failed",
551 	0x11, 0x05, "L-EC uncorrectable error",
552 	0x11, 0x06, "CIRC unrecovered error",
553 	0x11, 0x07, "data re-synchronization error",
554 	0x11, 0x08, "incomplete block read",
555 	0x11, 0x09, "no gap found",
556 	0x11, 0x0a, "miscorrected error",
557 	0x11, 0x0b, "unrecovered read error - recommend reassignment",
558 	0x11, 0x0c, "unrecovered read error - recommend rewrite the data",
559 	0x11, 0x0d, "de-compression crc error",
560 	0x11, 0x0e, "cannot decompress using declared algorithm",
561 	0x11, 0x0f, "error reading UPC/EAN number",
562 	0x11, 0x10, "error reading ISRC number",
563 	0x11, 0x11, "read error - loss of streaming",
564 	0x11, 0x12, "auxiliary memory read error",
565 	0x11, 0x13, "read error - failed retransmission request",
566 	0x12, 0x00, "address mark not found for ID field",
567 	0x13, 0x00, "address mark not found for data field",
568 	0x14, 0x00, "recorded entity not found",
569 	0x14, 0x01, "record not found",
570 	0x14, 0x02, "filemark or setmark not found",
571 	0x14, 0x03, "end-of-data not found",
572 	0x14, 0x04, "block sequence error",
573 	0x14, 0x05, "record not found - recommend reassignment",
574 	0x14, 0x06, "record not found - data auto-reallocated",
575 	0x14, 0x07, "locate operation failure",
576 	0x15, 0x00, "random positioning error",
577 	0x15, 0x01, "mechanical positioning error",
578 	0x15, 0x02, "positioning error detected by read of medium",
579 	0x16, 0x00, "data sync mark error",
580 	0x16, 0x01, "data sync error - data rewritten",
581 	0x16, 0x02, "data sync error - recommend rewrite",
582 	0x16, 0x03, "data sync error - data auto-reallocated",
583 	0x16, 0x04, "data sync error - recommend reassignment",
584 	0x17, 0x00, "recovered data with no error correction",
585 	0x17, 0x01, "recovered data with retries",
586 	0x17, 0x02, "recovered data with positive head offset",
587 	0x17, 0x03, "recovered data with negative head offset",
588 	0x17, 0x04, "recovered data with retries and/or CIRC applied",
589 	0x17, 0x05, "recovered data using previous sector id",
590 	0x17, 0x06, "recovered data without ECC - data auto-reallocated",
591 	0x17, 0x07, "recovered data without ECC - recommend reassignment",
592 	0x17, 0x08, "recovered data without ECC - recommend rewrite",
593 	0x17, 0x09, "recovered data without ECC - data rewritten",
594 	0x18, 0x00, "recovered data with error correction",
595 	0x18, 0x01, "recovered data with error corr. & retries applied",
596 	0x18, 0x02, "recovered data - data auto-reallocated",
597 	0x18, 0x03, "recovered data with CIRC",
598 	0x18, 0x04, "recovered data with L-EC",
599 	0x18, 0x05, "recovered data - recommend reassignment",
600 	0x18, 0x06, "recovered data - recommend rewrite",
601 	0x18, 0x07, "recovered data with ECC - data rewritten",
602 	0x18, 0x08, "recovered data with linking",
603 	0x19, 0x00, "defect list error",
604 	0x1a, 0x00, "parameter list length error",
605 	0x1b, 0x00, "synchronous data xfer error",
606 	0x1c, 0x00, "defect list not found",
607 	0x1c, 0x01, "primary defect list not found",
608 	0x1c, 0x02, "grown defect list not found",
609 	0x1d, 0x00, "miscompare during verify",
610 	0x1e, 0x00, "recovered ID with ECC",
611 	0x1f, 0x00, "partial defect list transfer",
612 	0x20, 0x00, "invalid command operation code",
613 	0x20, 0x01, "access denied - initiator pending-enrolled",
614 	0x20, 0x02, "access denied - no access rights",
615 	0x20, 0x03, "access denied - invalid mgmt id key",
616 	0x20, 0x04, "illegal command while in write capable state",
617 	0x20, 0x06, "illegal command while in explicit address mode",
618 	0x20, 0x07, "illegal command while in implicit address mode",
619 	0x20, 0x08, "access denied - enrollment conflict",
620 	0x20, 0x09, "access denied - invalid lu identifier",
621 	0x20, 0x0a, "access denied - invalid proxy token",
622 	0x20, 0x0b, "access denied - ACL LUN conflict",
623 	0x21, 0x00, "logical block address out of range",
624 	0x21, 0x01, "invalid element address",
625 	0x21, 0x02, "invalid address for write",
626 	0x22, 0x00, "illegal function",
627 	0x24, 0x00, "invalid field in cdb",
628 	0x24, 0x01, "cdb decryption error",
629 	0x25, 0x00, "LUN not supported",
630 	0x26, 0x00, "invalid field in param list",
631 	0x26, 0x01, "parameter not supported",
632 	0x26, 0x02, "parameter value invalid",
633 	0x26, 0x03, "threshold parameters not supported",
634 	0x26, 0x04, "invalid release of persistent reservation",
635 	0x26, 0x05, "data decryption error",
636 	0x26, 0x06, "too many target descriptors",
637 	0x26, 0x07, "unsupported target descriptor type code",
638 	0x26, 0x08, "too many segment descriptors",
639 	0x26, 0x09, "unsupported segment descriptor type code",
640 	0x26, 0x0a, "unexpected inexact segment",
641 	0x26, 0x0b, "inline data length exceeded",
642 	0x26, 0x0c, "invalid operation for copy source or destination",
643 	0x26, 0x0d, "copy segment granularity violation",
644 	0x27, 0x00, "write protected",
645 	0x27, 0x01, "hardware write protected",
646 	0x27, 0x02, "LUN software write protected",
647 	0x27, 0x03, "associated write protect",
648 	0x27, 0x04, "persistent write protect",
649 	0x27, 0x05, "permanent write protect",
650 	0x27, 0x06, "conditional write protect",
651 	0x28, 0x00, "medium may have changed",
652 	0x28, 0x01, "import or export element accessed",
653 	0x29, 0x00, "power on, reset, or bus reset occurred",
654 	0x29, 0x01, "power on occurred",
655 	0x29, 0x02, "scsi bus reset occurred",
656 	0x29, 0x03, "bus device reset message occurred",
657 	0x29, 0x04, "device internal reset",
658 	0x29, 0x05, "transceiver mode changed to single-ended",
659 	0x29, 0x06, "transceiver mode changed to LVD",
660 	0x29, 0x07, "i_t nexus loss occurred",
661 	0x2a, 0x00, "parameters changed",
662 	0x2a, 0x01, "mode parameters changed",
663 	0x2a, 0x02, "log parameters changed",
664 	0x2a, 0x03, "reservations preempted",
665 	0x2a, 0x04, "reservations released",
666 	0x2a, 0x05, "registrations preempted",
667 	0x2a, 0x06, "asymmetric access state changed",
668 	0x2a, 0x07, "implicit asymmetric access state transition failed",
669 	0x2b, 0x00, "copy cannot execute since host cannot disconnect",
670 	0x2c, 0x00, "command sequence error",
671 	0x2c, 0x03, "current program area is not empty",
672 	0x2c, 0x04, "current program area is empty",
673 	0x2c, 0x06, "persistent prevent conflict",
674 	0x2c, 0x07, "previous busy status",
675 	0x2c, 0x08, "previous task set full status",
676 	0x2c, 0x09, "previous reservation conflict status",
677 	0x2d, 0x00, "overwrite error on update in place",
678 	0x2e, 0x00, "insufficient time for operation",
679 	0x2f, 0x00, "commands cleared by another initiator",
680 	0x30, 0x00, "incompatible medium installed",
681 	0x30, 0x01, "cannot read medium - unknown format",
682 	0x30, 0x02, "cannot read medium - incompatible format",
683 	0x30, 0x03, "cleaning cartridge installed",
684 	0x30, 0x04, "cannot write medium - unknown format",
685 	0x30, 0x05, "cannot write medium - incompatible format",
686 	0x30, 0x06, "cannot format medium - incompatible medium",
687 	0x30, 0x07, "cleaning failure",
688 	0x30, 0x08, "cannot write - application code mismatch",
689 	0x30, 0x09, "current session not fixated for append",
690 	0x30, 0x10, "medium not formatted",
691 	0x31, 0x00, "medium format corrupted",
692 	0x31, 0x01, "format command failed",
693 	0x31, 0x02, "zoned formatting failed due to spare linking",
694 	0x32, 0x00, "no defect spare location available",
695 	0x32, 0x01, "defect list update failure",
696 	0x33, 0x00, "tape length error",
697 	0x34, 0x00, "enclosure failure",
698 	0x35, 0x00, "enclosure services failure",
699 	0x35, 0x01, "unsupported enclosure function",
700 	0x35, 0x02, "enclosure services unavailable",
701 	0x35, 0x03, "enclosure services transfer failure",
702 	0x35, 0x04, "enclosure services transfer refused",
703 	0x36, 0x00, "ribbon, ink, or toner failure",
704 	0x37, 0x00, "rounded parameter",
705 	0x39, 0x00, "saving parameters not supported",
706 	0x3a, 0x00, "medium not present",
707 	0x3a, 0x01, "medium not present - tray closed",
708 	0x3a, 0x02, "medium not present - tray open",
709 	0x3a, 0x03, "medium not present - loadable",
710 	0x3a, 0x04, "medium not present - medium auxiliary memory accessible",
711 	0x3b, 0x00, "sequential positioning error",
712 	0x3b, 0x01, "tape position error at beginning-of-medium",
713 	0x3b, 0x02, "tape position error at end-of-medium",
714 	0x3b, 0x08, "reposition error",
715 	0x3b, 0x0c, "position past beginning of medium",
716 	0x3b, 0x0d, "medium destination element full",
717 	0x3b, 0x0e, "medium source element empty",
718 	0x3b, 0x0f, "end of medium reached",
719 	0x3b, 0x11, "medium magazine not accessible",
720 	0x3b, 0x12, "medium magazine removed",
721 	0x3b, 0x13, "medium magazine inserted",
722 	0x3b, 0x14, "medium magazine locked",
723 	0x3b, 0x15, "medium magazine unlocked",
724 	0x3b, 0x16, "mechanical positioning or changer error",
725 	0x3d, 0x00, "invalid bits in indentify message",
726 	0x3e, 0x00, "LUN has not self-configured yet",
727 	0x3e, 0x01, "LUN failure",
728 	0x3e, 0x02, "timeout on LUN",
729 	0x3e, 0x03, "LUN failed self-test",
730 	0x3e, 0x04, "LUN unable to update self-test log",
731 	0x3f, 0x00, "target operating conditions have changed",
732 	0x3f, 0x01, "microcode has been changed",
733 	0x3f, 0x02, "changed operating definition",
734 	0x3f, 0x03, "inquiry data has changed",
735 	0x3f, 0x04, "component device attached",
736 	0x3f, 0x05, "device identifier changed",
737 	0x3f, 0x06, "redundancy group created or modified",
738 	0x3f, 0x07, "redundancy group deleted",
739 	0x3f, 0x08, "spare created or modified",
740 	0x3f, 0x09, "spare deleted",
741 	0x3f, 0x0a, "volume set created or modified",
742 	0x3f, 0x0b, "volume set deleted",
743 	0x3f, 0x0c, "volume set deassigned",
744 	0x3f, 0x0d, "volume set reassigned",
745 	0x3f, 0x0e, "reported LUNs data has changed",
746 	0x3f, 0x0f, "echo buffer overwritten",
747 	0x3f, 0x10, "medium loadable",
748 	0x3f, 0x11, "medium auxiliary memory accessible",
749 	0x40, 0x00, "ram failure",
750 	0x41, 0x00, "data path failure",
751 	0x42, 0x00, "power-on or self-test failure",
752 	0x43, 0x00, "message error",
753 	0x44, 0x00, "internal target failure",
754 	0x45, 0x00, "select or reselect failure",
755 	0x46, 0x00, "unsuccessful soft reset",
756 	0x47, 0x00, "scsi parity error",
757 	0x47, 0x01, "data phase crc error detected",
758 	0x47, 0x02, "scsi parity error detected during st data phase",
759 	0x47, 0x03, "information unit iucrc error detected",
760 	0x47, 0x04, "asynchronous information protection error detected",
761 	0x47, 0x05, "protocol service crc error",
762 	0x47, 0x7f, "some commands cleared by iscsi protocol event",
763 	0x48, 0x00, "initiator detected error message received",
764 	0x49, 0x00, "invalid message error",
765 	0x4a, 0x00, "command phase error",
766 	0x4b, 0x00, "data phase error",
767 	0x4b, 0x01, "invalid target port transfer tag received",
768 	0x4b, 0x02, "too much write data",
769 	0x4b, 0x03, "ack/nak timeout",
770 	0x4b, 0x04, "nak received",
771 	0x4b, 0x05, "data offset error",
772 	0x4c, 0x00, "logical unit failed self-configuration",
773 	0x4d, 0x00, "tagged overlapped commands (ASCQ = queue tag)",
774 	0x4e, 0x00, "overlapped commands attempted",
775 	0x50, 0x00, "write append error",
776 	0x51, 0x00, "erase failure",
777 	0x52, 0x00, "cartridge fault",
778 	0x53, 0x00, "media load or eject failed",
779 	0x53, 0x01, "unload tape failure",
780 	0x53, 0x02, "medium removal prevented",
781 	0x54, 0x00, "scsi to host system interface failure",
782 	0x55, 0x00, "system resource failure",
783 	0x55, 0x01, "system buffer full",
784 	0x55, 0x02, "insufficient reservation resources",
785 	0x55, 0x03, "insufficient resources",
786 	0x55, 0x04, "insufficient registration resources",
787 	0x55, 0x05, "insufficient access control resources",
788 	0x55, 0x06, "auxiliary memory out of space",
789 	0x57, 0x00, "unable to recover TOC",
790 	0x58, 0x00, "generation does not exist",
791 	0x59, 0x00, "updated block read",
792 	0x5a, 0x00, "operator request or state change input",
793 	0x5a, 0x01, "operator medium removal request",
794 	0x5a, 0x02, "operator selected write protect",
795 	0x5a, 0x03, "operator selected write permit",
796 	0x5b, 0x00, "log exception",
797 	0x5b, 0x01, "threshold condition met",
798 	0x5b, 0x02, "log counter at maximum",
799 	0x5b, 0x03, "log list codes exhausted",
800 	0x5c, 0x00, "RPL status change",
801 	0x5c, 0x01, "spindles synchronized",
802 	0x5c, 0x02, "spindles not synchronized",
803 	0x5d, 0x00, "drive operation marginal, service immediately"
804 		    " (failure prediction threshold exceeded)",
805 	0x5d, 0x01, "media failure prediction threshold exceeded",
806 	0x5d, 0x02, "LUN failure prediction threshold exceeded",
807 	0x5d, 0x03, "spare area exhaustion prediction threshold exceeded",
808 	0x5d, 0x10, "hardware impending failure general hard drive failure",
809 	0x5d, 0x11, "hardware impending failure drive error rate too high",
810 	0x5d, 0x12, "hardware impending failure data error rate too high",
811 	0x5d, 0x13, "hardware impending failure seek error rate too high",
812 	0x5d, 0x14, "hardware impending failure too many block reassigns",
813 	0x5d, 0x15, "hardware impending failure access times too high",
814 	0x5d, 0x16, "hardware impending failure start unit times too high",
815 	0x5d, 0x17, "hardware impending failure channel parametrics",
816 	0x5d, 0x18, "hardware impending failure controller detected",
817 	0x5d, 0x19, "hardware impending failure throughput performance",
818 	0x5d, 0x1a, "hardware impending failure seek time performance",
819 	0x5d, 0x1b, "hardware impending failure spin-up retry count",
820 	0x5d, 0x1c, "hardware impending failure drive calibration retry count",
821 	0x5d, 0x20, "controller impending failure general hard drive failure",
822 	0x5d, 0x21, "controller impending failure drive error rate too high",
823 	0x5d, 0x22, "controller impending failure data error rate too high",
824 	0x5d, 0x23, "controller impending failure seek error rate too high",
825 	0x5d, 0x24, "controller impending failure too many block reassigns",
826 	0x5d, 0x25, "controller impending failure access times too high",
827 	0x5d, 0x26, "controller impending failure start unit times too high",
828 	0x5d, 0x27, "controller impending failure channel parametrics",
829 	0x5d, 0x28, "controller impending failure controller detected",
830 	0x5d, 0x29, "controller impending failure throughput performance",
831 	0x5d, 0x2a, "controller impending failure seek time performance",
832 	0x5d, 0x2b, "controller impending failure spin-up retry count",
833 	0x5d, 0x2c, "controller impending failure drive calibration retry cnt",
834 	0x5d, 0x30, "data channel impending failure general hard drive failure",
835 	0x5d, 0x31, "data channel impending failure drive error rate too high",
836 	0x5d, 0x32, "data channel impending failure data error rate too high",
837 	0x5d, 0x33, "data channel impending failure seek error rate too high",
838 	0x5d, 0x34, "data channel impending failure too many block reassigns",
839 	0x5d, 0x35, "data channel impending failure access times too high",
840 	0x5d, 0x36, "data channel impending failure start unit times too high",
841 	0x5d, 0x37, "data channel impending failure channel parametrics",
842 	0x5d, 0x38, "data channel impending failure controller detected",
843 	0x5d, 0x39, "data channel impending failure throughput performance",
844 	0x5d, 0x3a, "data channel impending failure seek time performance",
845 	0x5d, 0x3b, "data channel impending failure spin-up retry count",
846 	0x5d, 0x3c, "data channel impending failure drive calibrate retry cnt",
847 	0x5d, 0x40, "servo impending failure general hard drive failure",
848 	0x5d, 0x41, "servo impending failure drive error rate too high",
849 	0x5d, 0x42, "servo impending failure data error rate too high",
850 	0x5d, 0x43, "servo impending failure seek error rate too high",
851 	0x5d, 0x44, "servo impending failure too many block reassigns",
852 	0x5d, 0x45, "servo impending failure access times too high",
853 	0x5d, 0x46, "servo impending failure start unit times too high",
854 	0x5d, 0x47, "servo impending failure channel parametrics",
855 	0x5d, 0x48, "servo impending failure controller detected",
856 	0x5d, 0x49, "servo impending failure throughput performance",
857 	0x5d, 0x4a, "servo impending failure seek time performance",
858 	0x5d, 0x4b, "servo impending failure spin-up retry count",
859 	0x5d, 0x4c, "servo impending failure drive calibration retry count",
860 	0x5d, 0x50, "spindle impending failure general hard drive failure",
861 	0x5d, 0x51, "spindle impending failure drive error rate too high",
862 	0x5d, 0x52, "spindle impending failure data error rate too high",
863 	0x5d, 0x53, "spindle impending failure seek error rate too high",
864 	0x5d, 0x54, "spindle impending failure too many block reassigns",
865 	0x5d, 0x55, "spindle impending failure access times too high",
866 	0x5d, 0x56, "spindle impending failure start unit times too high",
867 	0x5d, 0x57, "spindle impending failure channel parametrics",
868 	0x5d, 0x58, "spindle impending failure controller detected",
869 	0x5d, 0x59, "spindle impending failure throughput performance",
870 	0x5d, 0x5a, "spindle impending failure seek time performance",
871 	0x5d, 0x5b, "spindle impending failure spin-up retry count",
872 	0x5d, 0x5c, "spindle impending failure drive calibration retry count",
873 	0x5d, 0x60, "firmware impending failure general hard drive failure",
874 	0x5d, 0x61, "firmware impending failure drive error rate too high",
875 	0x5d, 0x62, "firmware impending failure data error rate too high",
876 	0x5d, 0x63, "firmware impending failure seek error rate too high",
877 	0x5d, 0x64, "firmware impending failure too many block reassigns",
878 	0x5d, 0x65, "firmware impending failure access times too high",
879 	0x5d, 0x66, "firmware impending failure start unit times too high",
880 	0x5d, 0x67, "firmware impending failure channel parametrics",
881 	0x5d, 0x68, "firmware impending failure controller detected",
882 	0x5d, 0x69, "firmware impending failure throughput performance",
883 	0x5d, 0x6a, "firmware impending failure seek time performance",
884 	0x5d, 0x6b, "firmware impending failure spin-up retry count",
885 	0x5d, 0x6c, "firmware impending failure drive calibration retry count",
886 	0x5d, 0xff, "failure prediction threshold exceeded (false)",
887 	0x5e, 0x00, "low power condition active",
888 	0x5e, 0x01, "idle condition activated by timer",
889 	0x5e, 0x02, "standby condition activated by timer",
890 	0x5e, 0x03, "idle condition activated by command",
891 	0x5e, 0x04, "standby condition activated by command",
892 	0x60, 0x00, "lamp failure",
893 	0x61, 0x00, "video aquisition error",
894 	0x62, 0x00, "scan head positioning error",
895 	0x63, 0x00, "end of user area encountered on this track",
896 	0x63, 0x01, "packet does not fit in available space",
897 	0x64, 0x00, "illegal mode for this track",
898 	0x64, 0x01, "invalid packet size",
899 	0x65, 0x00, "voltage fault",
900 	0x66, 0x00, "automatic document feeder cover up",
901 	0x67, 0x00, "configuration failure",
902 	0x67, 0x01, "configuration of incapable LUNs failed",
903 	0x67, 0x02, "add LUN failed",
904 	0x67, 0x03, "modification of LUN failed",
905 	0x67, 0x04, "exchange of LUN failed",
906 	0x67, 0x05, "remove of LUN failed",
907 	0x67, 0x06, "attachment of LUN failed",
908 	0x67, 0x07, "creation of LUN failed",
909 	0x67, 0x08, "assign failure occurred",
910 	0x67, 0x09, "multiply assigned LUN",
911 	0x67, 0x0a, "set target port groups command failed",
912 	0x68, 0x00, "logical unit not configured",
913 	0x69, 0x00, "data loss on logical unit",
914 	0x69, 0x01, "multiple LUN failures",
915 	0x69, 0x02, "parity/data mismatch",
916 	0x6a, 0x00, "informational, refer to log",
917 	0x6b, 0x00, "state change has occured",
918 	0x6b, 0x01, "redundancy level got better",
919 	0x6b, 0x02, "redundancy level got worse",
920 	0x6c, 0x00, "rebuild failure occured",
921 	0x6d, 0x00, "recalculate failure occured",
922 	0x6e, 0x00, "command to logical unit failed",
923 	0x6f, 0x00, "copy protect key exchange failure authentication failure",
924 	0x6f, 0x01, "copy protect key exchange failure key not present",
925 	0x6f, 0x02, "copy protect key exchange failure key not established",
926 	0x6f, 0x03, "read of scrambled sector without authentication",
927 	0x6f, 0x04, "media region code is mismatched to LUN region",
928 	0x6f, 0x05, "drive region must be permanent/region reset count error",
929 	0x70, 0xffff, "decompression exception short algorithm id of ASCQ",
930 	0x71, 0x00, "decompression exception long algorithm id",
931 	0x72, 0x00, "session fixation error",
932 	0x72, 0x01, "session fixation error writing lead-in",
933 	0x72, 0x02, "session fixation error writing lead-out",
934 	0x72, 0x03, "session fixation error - incomplete track in session",
935 	0x72, 0x04, "empty or partially written reserved track",
936 	0x72, 0x05, "no more track reservations allowed",
937 	0x73, 0x00, "cd control error",
938 	0x73, 0x01, "power calibration area almost full",
939 	0x73, 0x02, "power calibration area is full",
940 	0x73, 0x03, "power calibration area error",
941 	0x73, 0x04, "program memory area update failure",
942 	0x73, 0x05, "program memory area is full",
943 	0x73, 0x06, "rma/pma is almost full",
944 	0xffff, 0xffff, NULL
945 };
946 
947 char *
948 scsi_esname(uint_t key, char *tmpstr)
949 {
950 	int i = 0;
951 
952 	while (extended_sense_list[i].asc != 0xffff) {
953 		if (key == extended_sense_list[i].asc) {
954 			return ((char *)extended_sense_list[i].message);
955 		}
956 		i++;
957 	}
958 	return (sprintf(tmpstr, "<vendor unique code 0x%x>", key));
959 }
960 
961 char *
962 scsi_asc_name(uint_t asc, uint_t ascq, char *tmpstr)
963 {
964 	int i = 0;
965 
966 	while (extended_sense_list[i].asc != 0xffff) {
967 		if ((asc == extended_sense_list[i].asc) &&
968 		    ((ascq == extended_sense_list[i].ascq) ||
969 		    (extended_sense_list[i].ascq == 0xffff))) {
970 			return ((char *)extended_sense_list[i].message);
971 		}
972 		i++;
973 	}
974 	return (sprintf(tmpstr, "<vendor unique code 0x%x>", asc));
975 }
976 
977 char *
978 scsi_sname(uchar_t sense_key)
979 {
980 	if (sense_key >= (uchar_t)(NUM_SENSE_KEYS+NUM_IMPL_SENSE_KEYS)) {
981 		return ("<unknown sense key>");
982 	} else {
983 		return (sense_keys[sense_key]);
984 	}
985 }
986 
987 
988 /*
989  * Print a piece of inquiry data- cleaned up for non-printable characters.
990  */
991 
992 static void
993 inq_fill(char *p, int l, char *s)
994 {
995 	register unsigned i = 0;
996 	char c;
997 
998 	if (!p)
999 		return;
1000 
1001 	while (i++ < l) {
1002 		/* clean string of non-printing chars */
1003 		if ((c = *p++) < ' ' || c >= 0177) {
1004 			c = ' ';
1005 		}
1006 		*s++ = c;
1007 	}
1008 	*s++ = 0;
1009 }
1010 
1011 static char *
1012 scsi_asc_search(uint_t asc, uint_t ascq,
1013     struct scsi_asq_key_strings *list)
1014 {
1015 	int i = 0;
1016 
1017 	while (list[i].asc != 0xffff) {
1018 		if ((asc == list[i].asc) &&
1019 		    ((ascq == list[i].ascq) ||
1020 		    (list[i].ascq == 0xffff))) {
1021 			return ((char *)list[i].message);
1022 		}
1023 		i++;
1024 	}
1025 	return (NULL);
1026 }
1027 
1028 static char *
1029 scsi_asc_ascq_name(uint_t asc, uint_t ascq, char *tmpstr,
1030 	struct scsi_asq_key_strings *list)
1031 {
1032 	char *message;
1033 
1034 	if (list) {
1035 		if (message = scsi_asc_search(asc, ascq, list)) {
1036 			return (message);
1037 		}
1038 	}
1039 	if (message = scsi_asc_search(asc, ascq, extended_sense_list)) {
1040 		return (message);
1041 	}
1042 
1043 	return (sprintf(tmpstr, "<vendor unique code 0x%x>", asc));
1044 }
1045 
1046 /*
1047  * The first part/column of the error message will be at least this length.
1048  * This number has been calculated so that each line fits in 80 chars.
1049  */
1050 #define	SCSI_ERRMSG_COLUMN_LEN	42
1051 #define	SCSI_ERRMSG_BUF_LEN	256
1052 
1053 void
1054 scsi_vu_errmsg(struct scsi_device *devp, struct scsi_pkt *pkt, char *label,
1055     int severity, daddr_t blkno, daddr_t err_blkno,
1056     struct scsi_key_strings *cmdlist, struct scsi_extended_sense *sensep,
1057     struct scsi_asq_key_strings *asc_list,
1058     char *(*decode_fru)(struct scsi_device *, char *, int, uchar_t))
1059 {
1060 	uchar_t com;
1061 	static char buf[SCSI_ERRMSG_BUF_LEN];
1062 	static char buf1[SCSI_ERRMSG_BUF_LEN];
1063 	static char tmpbuf[64];
1064 	static char pad[SCSI_ERRMSG_COLUMN_LEN];
1065 	dev_info_t *dev = devp->sd_dev;
1066 	static char *error_classes[] = {
1067 		"All", "Unknown", "Informational",
1068 		"Recovered", "Retryable", "Fatal"
1069 	};
1070 	int i, buflen;
1071 
1072 	mutex_enter(&scsi_log_mutex);
1073 
1074 	/*
1075 	 * We need to put our space padding code because kernel version
1076 	 * of sprintf(9F) doesn't support %-<number>s type of left alignment.
1077 	 */
1078 	for (i = 0; i < SCSI_ERRMSG_COLUMN_LEN; i++) {
1079 		pad[i] = ' ';
1080 	}
1081 
1082 	bzero(buf, 256);
1083 	com = ((union scsi_cdb *)pkt->pkt_cdbp)->scc_cmd;
1084 	(void) sprintf(buf, "Error for Command: %s",
1085 	    scsi_cmd_name(com, cmdlist, tmpbuf));
1086 	buflen = strlen(buf);
1087 	if (buflen < SCSI_ERRMSG_COLUMN_LEN) {
1088 		pad[SCSI_ERRMSG_COLUMN_LEN - buflen] = '\0';
1089 		(void) sprintf(&buf[buflen], "%s Error Level: %s",
1090 		    pad, error_classes[severity]);
1091 		pad[SCSI_ERRMSG_COLUMN_LEN - buflen] = ' ';
1092 	} else {
1093 		(void) sprintf(&buf[buflen], " Error Level: %s",
1094 		    error_classes[severity]);
1095 	}
1096 	impl_scsi_log(dev, label, CE_WARN, buf);
1097 
1098 	if (blkno != -1 || err_blkno != -1 &&
1099 	    ((com & 0xf) == SCMD_READ) || ((com & 0xf) == SCMD_WRITE)) {
1100 		bzero(buf, 256);
1101 		(void) sprintf(buf, "Requested Block: %ld", blkno);
1102 		buflen = strlen(buf);
1103 		if (buflen < SCSI_ERRMSG_COLUMN_LEN) {
1104 			pad[SCSI_ERRMSG_COLUMN_LEN - buflen] = '\0';
1105 			(void) sprintf(&buf[buflen], "%s Error Block: %ld\n",
1106 			    pad, err_blkno);
1107 			pad[SCSI_ERRMSG_COLUMN_LEN - buflen] = ' ';
1108 		} else {
1109 			(void) sprintf(&buf[buflen], " Error Block: %ld\n",
1110 			    err_blkno);
1111 		}
1112 		impl_scsi_log(dev, label, CE_CONT, buf);
1113 	}
1114 
1115 	bzero(buf, 256);
1116 	(void) strcpy(buf, "Vendor: ");
1117 	inq_fill(devp->sd_inq->inq_vid, 8, &buf[strlen(buf)]);
1118 	buflen = strlen(buf);
1119 	if (buflen < SCSI_ERRMSG_COLUMN_LEN) {
1120 		pad[SCSI_ERRMSG_COLUMN_LEN - buflen] = '\0';
1121 		(void) sprintf(&buf[strlen(buf)], "%s Serial Number: ", pad);
1122 		pad[SCSI_ERRMSG_COLUMN_LEN - buflen] = ' ';
1123 	} else {
1124 		(void) sprintf(&buf[strlen(buf)], " Serial Number: ");
1125 	}
1126 	inq_fill(devp->sd_inq->inq_serial, 12, &buf[strlen(buf)]);
1127 	impl_scsi_log(dev, label, CE_CONT, "%s\n", buf);
1128 
1129 	if (sensep) {
1130 		bzero(buf, 256);
1131 		(void) sprintf(buf, "Sense Key: %s\n",
1132 		    sense_keys[sensep->es_key]);
1133 		impl_scsi_log(dev, label, CE_CONT, buf);
1134 
1135 		bzero(buf, 256);
1136 		if ((sensep->es_fru_code != 0) &&
1137 		    (decode_fru != NULL)) {
1138 			(*decode_fru)(devp, buf, SCSI_ERRMSG_BUF_LEN,
1139 			    sensep->es_fru_code);
1140 			if (buf[0] != NULL) {
1141 				bzero(buf1, 256);
1142 				(void) sprintf(&buf1[strlen(buf1)],
1143 				    "ASC: 0x%x (%s)", sensep->es_add_code,
1144 				    scsi_asc_ascq_name(sensep->es_add_code,
1145 				    sensep->es_qual_code, tmpbuf, asc_list));
1146 				buflen = strlen(buf1);
1147 				if (buflen < SCSI_ERRMSG_COLUMN_LEN) {
1148 				    pad[SCSI_ERRMSG_COLUMN_LEN - buflen] = '\0';
1149 				    (void) sprintf(&buf1[buflen],
1150 				    "%s ASCQ: 0x%x", pad, sensep->es_qual_code);
1151 				} else {
1152 				    (void) sprintf(&buf1[buflen], " ASCQ: 0x%x",
1153 					sensep->es_qual_code);
1154 				}
1155 				impl_scsi_log(dev,
1156 					label, CE_CONT, "%s\n", buf1);
1157 				impl_scsi_log(dev, label, CE_CONT,
1158 					"FRU: 0x%x (%s)\n",
1159 						sensep->es_fru_code, buf);
1160 				mutex_exit(&scsi_log_mutex);
1161 				return;
1162 			}
1163 		}
1164 		(void) sprintf(&buf[strlen(buf)],
1165 		    "ASC: 0x%x (%s), ASCQ: 0x%x, FRU: 0x%x",
1166 		    sensep->es_add_code,
1167 		    scsi_asc_ascq_name(sensep->es_add_code,
1168 			sensep->es_qual_code, tmpbuf, asc_list),
1169 		    sensep->es_qual_code, sensep->es_fru_code);
1170 		impl_scsi_log(dev, label, CE_CONT, "%s\n", buf);
1171 	}
1172 	mutex_exit(&scsi_log_mutex);
1173 }
1174 
1175 void
1176 scsi_errmsg(struct scsi_device *devp, struct scsi_pkt *pkt, char *label,
1177     int severity, daddr_t blkno, daddr_t err_blkno,
1178     struct scsi_key_strings *cmdlist, struct scsi_extended_sense *sensep)
1179 {
1180 	scsi_vu_errmsg(devp, pkt, label, severity, blkno,
1181 		err_blkno, cmdlist, sensep, NULL, NULL);
1182 }
1183 
1184 /*PRINTFLIKE4*/
1185 void
1186 scsi_log(dev_info_t *dev, char *label, uint_t level,
1187     const char *fmt, ...)
1188 {
1189 	va_list ap;
1190 
1191 	va_start(ap, fmt);
1192 	mutex_enter(&scsi_log_mutex);
1193 	v_scsi_log(dev, label, level, fmt, ap);
1194 	mutex_exit(&scsi_log_mutex);
1195 	va_end(ap);
1196 }
1197 
1198 /*PRINTFLIKE4*/
1199 static void
1200 impl_scsi_log(dev_info_t *dev, char *label, uint_t level,
1201     const char *fmt, ...)
1202 {
1203 	va_list ap;
1204 
1205 	ASSERT(mutex_owned(&scsi_log_mutex));
1206 
1207 	va_start(ap, fmt);
1208 	v_scsi_log(dev, label, level, fmt, ap);
1209 	va_end(ap);
1210 }
1211 
1212 
1213 char *ddi_pathname(dev_info_t *dip, char *path);
1214 
1215 /*PRINTFLIKE4*/
1216 static void
1217 v_scsi_log(dev_info_t *dev, char *label, uint_t level,
1218     const char *fmt, va_list ap)
1219 {
1220 	static char name[256];
1221 	int log_only = 0;
1222 	int boot_only = 0;
1223 	int console_only = 0;
1224 
1225 	ASSERT(mutex_owned(&scsi_log_mutex));
1226 
1227 	if (dev) {
1228 		if (level == CE_PANIC || level == CE_WARN ||
1229 		    level == CE_NOTE) {
1230 			(void) sprintf(name, "%s (%s%d):\n",
1231 				ddi_pathname(dev, scsi_log_buffer),
1232 				label, ddi_get_instance(dev));
1233 		} else if (level >= (uint_t)SCSI_DEBUG) {
1234 			(void) sprintf(name,
1235 			    "%s%d:", label, ddi_get_instance(dev));
1236 		} else {
1237 			name[0] = '\0';
1238 		}
1239 	} else {
1240 		(void) sprintf(name, "%s:", label);
1241 	}
1242 
1243 	(void) vsprintf(scsi_log_buffer, fmt, ap);
1244 
1245 	switch (scsi_log_buffer[0]) {
1246 	case '!':
1247 		log_only = 1;
1248 		break;
1249 	case '?':
1250 		boot_only = 1;
1251 		break;
1252 	case '^':
1253 		console_only = 1;
1254 		break;
1255 	}
1256 
1257 	switch (level) {
1258 	case CE_NOTE:
1259 		level = CE_CONT;
1260 		/* FALLTHROUGH */
1261 	case CE_CONT:
1262 	case CE_WARN:
1263 	case CE_PANIC:
1264 		if (boot_only) {
1265 			cmn_err(level, "?%s\t%s", name,
1266 				&scsi_log_buffer[1]);
1267 		} else if (console_only) {
1268 			cmn_err(level, "^%s\t%s", name,
1269 				&scsi_log_buffer[1]);
1270 		} else if (log_only) {
1271 			cmn_err(level, "!%s\t%s", name,
1272 				&scsi_log_buffer[1]);
1273 		} else {
1274 			cmn_err(level, "%s\t%s", name,
1275 				scsi_log_buffer);
1276 		}
1277 		break;
1278 	case (uint_t)SCSI_DEBUG:
1279 	default:
1280 		cmn_err(CE_CONT, "^DEBUG: %s\t%s", name,
1281 				scsi_log_buffer);
1282 		break;
1283 	}
1284 }
1285 
1286 int
1287 scsi_get_device_type_scsi_options(dev_info_t *dip,
1288     struct scsi_device *devp, int default_scsi_options)
1289 {
1290 
1291 	caddr_t config_list	= NULL;
1292 	int options		= default_scsi_options;
1293 	struct scsi_inquiry  *inq = devp->sd_inq;
1294 	caddr_t vidptr, datanameptr;
1295 	int	vidlen, dupletlen;
1296 	int config_list_len, len;
1297 
1298 	/*
1299 	 * look up the device-type-scsi-options-list and walk thru
1300 	 * the list
1301 	 * compare the vendor ids of the earlier inquiry command and
1302 	 * with those vids in the list
1303 	 * if there is a match, lookup the scsi-options value
1304 	 */
1305 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1306 	    "device-type-scsi-options-list",
1307 	    (caddr_t)&config_list, &config_list_len) == DDI_PROP_SUCCESS) {
1308 
1309 		/*
1310 		 * Compare vids in each duplet - if it matches, get value for
1311 		 * dataname and then lookup scsi_options
1312 		 * dupletlen is calculated later.
1313 		 */
1314 		for (len = config_list_len, vidptr = config_list; len > 0;
1315 		    vidptr += dupletlen, len -= dupletlen) {
1316 
1317 			vidlen = strlen(vidptr);
1318 			datanameptr = vidptr + vidlen + 1;
1319 
1320 			if ((vidlen != 0) &&
1321 			    bcmp(inq->inq_vid, vidptr, vidlen) == 0) {
1322 				/*
1323 				 * get the data list
1324 				 */
1325 				options = ddi_prop_get_int(DDI_DEV_T_ANY,
1326 				    dip, 0,
1327 				    datanameptr, default_scsi_options);
1328 				break;
1329 			}
1330 			dupletlen = vidlen + strlen(datanameptr) + 2;
1331 		}
1332 		kmem_free(config_list, config_list_len);
1333 	}
1334 
1335 	return (options);
1336 }
1337