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