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