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