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 * Copyright (c) 2019, Joyent, Inc.
26 */
27
28 /*
29 * This file contains routines for sending and receiving SCSI commands. The
30 * higher level logic is contained in ds_scsi.c.
31 */
32
33 #include <assert.h>
34 #include <sys/types.h>
35 #include <sys/param.h>
36 #include <inttypes.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <errno.h>
41 #include <stdarg.h>
42 #include <limits.h>
43 #include <utility.h>
44 #include <unistd.h>
45 #include <stropts.h>
46 #include <alloca.h>
47
48 #include "ds_scsi.h"
49 #include "ds_scsi_uscsi.h"
50
51 #define MSGBUFLEN 64
52 #define USCSI_DEFAULT_TIMEOUT 45
53 #define USCSI_TIMEOUT_MAX INT_MAX
54
55 static diskaddr_t scsi_extract_sense_info_descr(
56 struct scsi_descr_sense_hdr *sdsp, int rqlen);
57 static void scsi_print_extended_sense(struct scsi_extended_sense *rq,
58 int rqlen);
59 static void scsi_print_descr_sense(struct scsi_descr_sense_hdr *rq, int rqlen);
60
61 typedef struct slist {
62 char *str;
63 int value;
64 } slist_t;
65
66 static char *
find_string(slist_t * slist,int match_value)67 find_string(slist_t *slist, int match_value)
68 {
69 for (; slist->str != NULL; slist++) {
70 if (slist->value == match_value) {
71 return (slist->str);
72 }
73 }
74
75 return ((char *)NULL);
76 }
77
78 /*
79 * Strings for printing mode sense page control values
80 */
81 static slist_t page_control_strings[] = {
82 { "current", PC_CURRENT },
83 { "changeable", PC_CHANGEABLE },
84 { "default", PC_DEFAULT },
85 { "saved", PC_SAVED },
86 { NULL, 0 }
87 };
88
89 /*
90 * Strings for printing the mode select options
91 */
92 static slist_t mode_select_strings[] = {
93 { "", 0 },
94 { "(pf)", MODE_SELECT_PF },
95 { "(sp)", MODE_SELECT_SP },
96 { "(pf,sp)", MODE_SELECT_PF|MODE_SELECT_SP },
97 { NULL, 0 }
98 };
99
100 static slist_t sensekey_strings[] = {
101 { "No sense error", KEY_NO_SENSE },
102 { "Recoverable error", KEY_RECOVERABLE_ERROR },
103 { "Not ready error", KEY_NOT_READY },
104 { "Medium error", KEY_MEDIUM_ERROR },
105 { "Hardware error", KEY_HARDWARE_ERROR },
106 { "Illegal request", KEY_ILLEGAL_REQUEST },
107 { "Unit attention error", KEY_UNIT_ATTENTION },
108 { "Write protect error", KEY_WRITE_PROTECT },
109 { "Blank check error", KEY_BLANK_CHECK },
110 { "Vendor unique error", KEY_VENDOR_UNIQUE },
111 { "Copy aborted error", KEY_COPY_ABORTED },
112 { "Aborted command", KEY_ABORTED_COMMAND },
113 { "Equal error", KEY_EQUAL },
114 { "Volume overflow", KEY_VOLUME_OVERFLOW },
115 { "Miscompare error", KEY_MISCOMPARE },
116 { "Reserved error", KEY_RESERVED },
117 { NULL, 0 }
118 };
119
120 static slist_t scsi_cmdname_strings[] = {
121 { "mode select", SCMD_MODE_SELECT },
122 { "mode sense", SCMD_MODE_SENSE },
123 { "mode select(10)", SCMD_MODE_SELECT_G1 },
124 { "mode sense(10)", SCMD_MODE_SENSE_G1 },
125 { "log sense", SCMD_LOG_SENSE_G1 },
126 { "request sense", SCMD_REQUEST_SENSE },
127 { NULL, 0 }
128 };
129
130 static struct _scsi_asq_key_strings {
131 uint_t asc;
132 uint_t ascq;
133 const char *message;
134 } extended_sense_list[] = {
135 { 0x00, 0x00, "no additional sense info" },
136 { 0x00, 0x01, "filemark detected" },
137 { 0x00, 0x02, "end of partition/medium detected" },
138 { 0x00, 0x03, "setmark detected" },
139 { 0x00, 0x04, "begining of partition/medium detected" },
140 { 0x00, 0x05, "end of data detected" },
141 { 0x00, 0x06, "i/o process terminated" },
142 { 0x00, 0x11, "audio play operation in progress" },
143 { 0x00, 0x12, "audio play operation paused" },
144 { 0x00, 0x13, "audio play operation successfully completed" },
145 { 0x00, 0x14, "audio play operation stopped due to error" },
146 { 0x00, 0x15, "no current audio status to return" },
147 { 0x00, 0x16, "operation in progress" },
148 { 0x00, 0x17, "cleaning requested" },
149 { 0x00, 0x18, "erase operation in progress" },
150 { 0x00, 0x19, "locate operation in progress" },
151 { 0x00, 0x1A, "rewind operation in progress" },
152 { 0x00, 0x1B, "set capacity operation in progress" },
153 { 0x00, 0x1C, "verify operation in progress" },
154 { 0x01, 0x00, "no index/sector signal" },
155 { 0x02, 0x00, "no seek complete" },
156 { 0x03, 0x00, "peripheral device write fault" },
157 { 0x03, 0x01, "no write current" },
158 { 0x03, 0x02, "excessive write errors" },
159 { 0x04, 0x00, "LUN not ready" },
160 { 0x04, 0x01, "LUN is becoming ready" },
161 { 0x04, 0x02, "LUN initializing command required" },
162 { 0x04, 0x03, "LUN not ready intervention required" },
163 { 0x04, 0x04, "LUN not ready format in progress" },
164 { 0x04, 0x05, "LUN not ready, rebuild in progress" },
165 { 0x04, 0x06, "LUN not ready, recalculation in progress" },
166 { 0x04, 0x07, "LUN not ready, operation in progress" },
167 { 0x04, 0x08, "LUN not ready, long write in progress" },
168 { 0x04, 0x09, "LUN not ready, self-test in progress" },
169 { 0x04, 0x0A, "LUN not accessible, asymmetric access state "
170 "transition" },
171 { 0x04, 0x0B, "LUN not accessible, target port in standby state" },
172 { 0x04, 0x0C, "LUN not accessible, target port in unavailable state" },
173 { 0x04, 0x10, "LUN not ready, auxiliary memory not accessible" },
174 { 0x05, 0x00, "LUN does not respond to selection" },
175 { 0x06, 0x00, "reference position found" },
176 { 0x07, 0x00, "multiple peripheral devices selected" },
177 { 0x08, 0x00, "LUN communication failure" },
178 { 0x08, 0x01, "LUN communication time-out" },
179 { 0x08, 0x02, "LUN communication parity error" },
180 { 0x08, 0x03, "LUN communication crc error (ultra-DMA/32)" },
181 { 0x08, 0x04, "unreachable copy target" },
182 { 0x09, 0x00, "track following error" },
183 { 0x09, 0x01, "tracking servo failure" },
184 { 0x09, 0x02, "focus servo failure" },
185 { 0x09, 0x03, "spindle servo failure" },
186 { 0x09, 0x04, "head select fault" },
187 { 0x0a, 0x00, "error log overflow" },
188 { 0x0b, 0x00, "warning" },
189 { 0x0b, 0x01, "warning - specified temperature exceeded" },
190 { 0x0b, 0x02, "warning - enclosure degraded" },
191 { 0x0c, 0x00, "write error" },
192 { 0x0c, 0x01, "write error - recovered with auto reallocation" },
193 { 0x0c, 0x02, "write error - auto reallocation failed" },
194 { 0x0c, 0x03, "write error - recommend reassignment" },
195 { 0x0c, 0x04, "compression check miscompare error" },
196 { 0x0c, 0x05, "data expansion occurred during compression" },
197 { 0x0c, 0x06, "block not compressible" },
198 { 0x0c, 0x07, "write error - recovery needed" },
199 { 0x0c, 0x08, "write error - recovery failed" },
200 { 0x0c, 0x09, "write error - loss of streaming" },
201 { 0x0c, 0x0a, "write error - padding blocks added" },
202 { 0x0c, 0x0b, "auxiliary memory write error" },
203 { 0x0c, 0x0c, "write error - unexpected unsolicited data" },
204 { 0x0c, 0x0d, "write error - not enough unsolicited data" },
205 { 0x0d, 0x00, "error detected by third party temporary initiator" },
206 { 0x0d, 0x01, "third party device failure" },
207 { 0x0d, 0x02, "copy target device not reachable" },
208 { 0x0d, 0x03, "incorrect copy target device type" },
209 { 0x0d, 0x04, "copy target device data underrun" },
210 { 0x0d, 0x05, "copy target device data overrun" },
211 { 0x0e, 0x00, "invalid information unit" },
212 { 0x0e, 0x01, "information unit too short" },
213 { 0x0e, 0x02, "information unit too long" },
214 { 0x10, 0x00, "ID CRC or ECC error" },
215 { 0x11, 0x00, "unrecovered read error" },
216 { 0x11, 0x01, "read retries exhausted" },
217 { 0x11, 0x02, "error too long to correct" },
218 { 0x11, 0x03, "multiple read errors" },
219 { 0x11, 0x04, "unrecovered read error - auto reallocate failed" },
220 { 0x11, 0x05, "L-EC uncorrectable error" },
221 { 0x11, 0x06, "CIRC unrecovered error" },
222 { 0x11, 0x07, "data re-synchronization error" },
223 { 0x11, 0x08, "incomplete block read" },
224 { 0x11, 0x09, "no gap found" },
225 { 0x11, 0x0a, "miscorrected error" },
226 { 0x11, 0x0b, "unrecovered read error - recommend reassignment" },
227 { 0x11, 0x0c, "unrecovered read error - recommend rewrite the data" },
228 { 0x11, 0x0d, "de-compression crc error" },
229 { 0x11, 0x0e, "cannot decompress using declared algorithm" },
230 { 0x11, 0x0f, "error reading UPC/EAN number" },
231 { 0x11, 0x10, "error reading ISRC number" },
232 { 0x11, 0x11, "read error - loss of streaming" },
233 { 0x11, 0x12, "auxiliary memory read error" },
234 { 0x11, 0x13, "read error - failed retransmission request" },
235 { 0x12, 0x00, "address mark not found for ID field" },
236 { 0x13, 0x00, "address mark not found for data field" },
237 { 0x14, 0x00, "recorded entity not found" },
238 { 0x14, 0x01, "record not found" },
239 { 0x14, 0x02, "filemark or setmark not found" },
240 { 0x14, 0x03, "end-of-data not found" },
241 { 0x14, 0x04, "block sequence error" },
242 { 0x14, 0x05, "record not found - recommend reassignment" },
243 { 0x14, 0x06, "record not found - data auto-reallocated" },
244 { 0x14, 0x07, "locate operation failure" },
245 { 0x15, 0x00, "random positioning error" },
246 { 0x15, 0x01, "mechanical positioning error" },
247 { 0x15, 0x02, "positioning error detected by read of medium" },
248 { 0x16, 0x00, "data sync mark error" },
249 { 0x16, 0x01, "data sync error - data rewritten" },
250 { 0x16, 0x02, "data sync error - recommend rewrite" },
251 { 0x16, 0x03, "data sync error - data auto-reallocated" },
252 { 0x16, 0x04, "data sync error - recommend reassignment" },
253 { 0x17, 0x00, "recovered data with no error correction" },
254 { 0x17, 0x01, "recovered data with retries" },
255 { 0x17, 0x02, "recovered data with positive head offset" },
256 { 0x17, 0x03, "recovered data with negative head offset" },
257 { 0x17, 0x04, "recovered data with retries and/or CIRC applied" },
258 { 0x17, 0x05, "recovered data using previous sector id" },
259 { 0x17, 0x06, "recovered data without ECC - data auto-reallocated" },
260 { 0x17, 0x07, "recovered data without ECC - recommend reassignment" },
261 { 0x17, 0x08, "recovered data without ECC - recommend rewrite" },
262 { 0x17, 0x09, "recovered data without ECC - data rewritten" },
263 { 0x18, 0x00, "recovered data with error correction" },
264 { 0x18, 0x01, "recovered data with error corr. & retries applied" },
265 { 0x18, 0x02, "recovered data - data auto-reallocated" },
266 { 0x18, 0x03, "recovered data with CIRC" },
267 { 0x18, 0x04, "recovered data with L-EC" },
268 { 0x18, 0x05, "recovered data - recommend reassignment" },
269 { 0x18, 0x06, "recovered data - recommend rewrite" },
270 { 0x18, 0x07, "recovered data with ECC - data rewritten" },
271 { 0x18, 0x08, "recovered data with linking" },
272 { 0x19, 0x00, "defect list error" },
273 { 0x1a, 0x00, "parameter list length error" },
274 { 0x1b, 0x00, "synchronous data xfer error" },
275 { 0x1c, 0x00, "defect list not found" },
276 { 0x1c, 0x01, "primary defect list not found" },
277 { 0x1c, 0x02, "grown defect list not found" },
278 { 0x1d, 0x00, "miscompare during verify" },
279 { 0x1e, 0x00, "recovered ID with ECC" },
280 { 0x1f, 0x00, "partial defect list transfer" },
281 { 0x20, 0x00, "invalid command operation code" },
282 { 0x20, 0x01, "access denied - initiator pending-enrolled" },
283 { 0x20, 0x02, "access denied - no access rights" },
284 { 0x20, 0x03, "access denied - invalid mgmt id key" },
285 { 0x20, 0x04, "illegal command while in write capable state" },
286 { 0x20, 0x06, "illegal command while in explicit address mode" },
287 { 0x20, 0x07, "illegal command while in implicit address mode" },
288 { 0x20, 0x08, "access denied - enrollment conflict" },
289 { 0x20, 0x09, "access denied - invalid lu identifier" },
290 { 0x20, 0x0a, "access denied - invalid proxy token" },
291 { 0x20, 0x0b, "access denied - ACL LUN conflict" },
292 { 0x21, 0x00, "logical block address out of range" },
293 { 0x21, 0x01, "invalid element address" },
294 { 0x21, 0x02, "invalid address for write" },
295 { 0x22, 0x00, "illegal function" },
296 { 0x24, 0x00, "invalid field in cdb" },
297 { 0x24, 0x01, "cdb decryption error" },
298 { 0x25, 0x00, "LUN not supported" },
299 { 0x26, 0x00, "invalid field in param list" },
300 { 0x26, 0x01, "parameter not supported" },
301 { 0x26, 0x02, "parameter value invalid" },
302 { 0x26, 0x03, "threshold parameters not supported" },
303 { 0x26, 0x04, "invalid release of persistent reservation" },
304 { 0x26, 0x05, "data decryption error" },
305 { 0x26, 0x06, "too many target descriptors" },
306 { 0x26, 0x07, "unsupported target descriptor type code" },
307 { 0x26, 0x08, "too many segment descriptors" },
308 { 0x26, 0x09, "unsupported segment descriptor type code" },
309 { 0x26, 0x0a, "unexpected inexact segment" },
310 { 0x26, 0x0b, "inline data length exceeded" },
311 { 0x26, 0x0c, "invalid operation for copy source or destination" },
312 { 0x26, 0x0d, "copy segment granularity violation" },
313 { 0x27, 0x00, "write protected" },
314 { 0x27, 0x01, "hardware write protected" },
315 { 0x27, 0x02, "LUN software write protected" },
316 { 0x27, 0x03, "associated write protect" },
317 { 0x27, 0x04, "persistent write protect" },
318 { 0x27, 0x05, "permanent write protect" },
319 { 0x27, 0x06, "conditional write protect" },
320 { 0x28, 0x00, "medium may have changed" },
321 { 0x28, 0x01, "import or export element accessed" },
322 { 0x29, 0x00, "power on, reset, or bus reset occurred" },
323 { 0x29, 0x01, "power on occurred" },
324 { 0x29, 0x02, "scsi bus reset occurred" },
325 { 0x29, 0x03, "bus device reset message occurred" },
326 { 0x29, 0x04, "device internal reset" },
327 { 0x29, 0x05, "transceiver mode changed to single-ended" },
328 { 0x29, 0x06, "transceiver mode changed to LVD" },
329 { 0x29, 0x07, "i_t nexus loss occurred" },
330 { 0x2a, 0x00, "parameters changed" },
331 { 0x2a, 0x01, "mode parameters changed" },
332 { 0x2a, 0x02, "log parameters changed" },
333 { 0x2a, 0x03, "reservations preempted" },
334 { 0x2a, 0x04, "reservations released" },
335 { 0x2a, 0x05, "registrations preempted" },
336 { 0x2a, 0x06, "asymmetric access state changed" },
337 { 0x2a, 0x07, "implicit asymmetric access state transition failed" },
338 { 0x2b, 0x00, "copy cannot execute since host cannot disconnect" },
339 { 0x2c, 0x00, "command sequence error" },
340 { 0x2c, 0x03, "current program area is not empty" },
341 { 0x2c, 0x04, "current program area is empty" },
342 { 0x2c, 0x06, "persistent prevent conflict" },
343 { 0x2c, 0x07, "previous busy status" },
344 { 0x2c, 0x08, "previous task set full status" },
345 { 0x2c, 0x09, "previous reservation conflict status" },
346 { 0x2d, 0x00, "overwrite error on update in place" },
347 { 0x2e, 0x00, "insufficient time for operation" },
348 { 0x2f, 0x00, "commands cleared by another initiator" },
349 { 0x30, 0x00, "incompatible medium installed" },
350 { 0x30, 0x01, "cannot read medium - unknown format" },
351 { 0x30, 0x02, "cannot read medium - incompatible format" },
352 { 0x30, 0x03, "cleaning cartridge installed" },
353 { 0x30, 0x04, "cannot write medium - unknown format" },
354 { 0x30, 0x05, "cannot write medium - incompatible format" },
355 { 0x30, 0x06, "cannot format medium - incompatible medium" },
356 { 0x30, 0x07, "cleaning failure" },
357 { 0x30, 0x08, "cannot write - application code mismatch" },
358 { 0x30, 0x09, "current session not fixated for append" },
359 { 0x30, 0x10, "medium not formatted" },
360 { 0x31, 0x00, "medium format corrupted" },
361 { 0x31, 0x01, "format command failed" },
362 { 0x31, 0x02, "zoned formatting failed due to spare linking" },
363 { 0x32, 0x00, "no defect spare location available" },
364 { 0x32, 0x01, "defect list update failure" },
365 { 0x33, 0x00, "tape length error" },
366 { 0x34, 0x00, "enclosure failure" },
367 { 0x35, 0x00, "enclosure services failure" },
368 { 0x35, 0x01, "unsupported enclosure function" },
369 { 0x35, 0x02, "enclosure services unavailable" },
370 { 0x35, 0x03, "enclosure services transfer failure" },
371 { 0x35, 0x04, "enclosure services transfer refused" },
372 { 0x36, 0x00, "ribbon, ink, or toner failure" },
373 { 0x37, 0x00, "rounded parameter" },
374 { 0x39, 0x00, "saving parameters not supported" },
375 { 0x3a, 0x00, "medium not present" },
376 { 0x3a, 0x01, "medium not present - tray closed" },
377 { 0x3a, 0x02, "medium not present - tray open" },
378 { 0x3a, 0x03, "medium not present - loadable" },
379 { 0x3a, 0x04, "medium not present - medium auxiliary memory "
380 "accessible" },
381 { 0x3b, 0x00, "sequential positioning error" },
382 { 0x3b, 0x01, "tape position error at beginning-of-medium" },
383 { 0x3b, 0x02, "tape position error at end-of-medium" },
384 { 0x3b, 0x08, "reposition error" },
385 { 0x3b, 0x0c, "position past beginning of medium" },
386 { 0x3b, 0x0d, "medium destination element full" },
387 { 0x3b, 0x0e, "medium source element empty" },
388 { 0x3b, 0x0f, "end of medium reached" },
389 { 0x3b, 0x11, "medium magazine not accessible" },
390 { 0x3b, 0x12, "medium magazine removed" },
391 { 0x3b, 0x13, "medium magazine inserted" },
392 { 0x3b, 0x14, "medium magazine locked" },
393 { 0x3b, 0x15, "medium magazine unlocked" },
394 { 0x3b, 0x16, "mechanical positioning or changer error" },
395 { 0x3d, 0x00, "invalid bits in indentify message" },
396 { 0x3e, 0x00, "LUN has not self-configured yet" },
397 { 0x3e, 0x01, "LUN failure" },
398 { 0x3e, 0x02, "timeout on LUN" },
399 { 0x3e, 0x03, "LUN failed self-test" },
400 { 0x3e, 0x04, "LUN unable to update self-test log" },
401 { 0x3f, 0x00, "target operating conditions have changed" },
402 { 0x3f, 0x01, "microcode has been changed" },
403 { 0x3f, 0x02, "changed operating definition" },
404 { 0x3f, 0x03, "inquiry data has changed" },
405 { 0x3f, 0x04, "component device attached" },
406 { 0x3f, 0x05, "device identifier changed" },
407 { 0x3f, 0x06, "redundancy group created or modified" },
408 { 0x3f, 0x07, "redundancy group deleted" },
409 { 0x3f, 0x08, "spare created or modified" },
410 { 0x3f, 0x09, "spare deleted" },
411 { 0x3f, 0x0a, "volume set created or modified" },
412 { 0x3f, 0x0b, "volume set deleted" },
413 { 0x3f, 0x0c, "volume set deassigned" },
414 { 0x3f, 0x0d, "volume set reassigned" },
415 { 0x3f, 0x0e, "reported LUNs data has changed" },
416 { 0x3f, 0x0f, "echo buffer overwritten" },
417 { 0x3f, 0x10, "medium loadable" },
418 { 0x3f, 0x11, "medium auxiliary memory accessible" },
419 { 0x40, 0x00, "ram failure" },
420 { 0x41, 0x00, "data path failure" },
421 { 0x42, 0x00, "power-on or self-test failure" },
422 { 0x43, 0x00, "message error" },
423 { 0x44, 0x00, "internal target failure" },
424 { 0x45, 0x00, "select or reselect failure" },
425 { 0x46, 0x00, "unsuccessful soft reset" },
426 { 0x47, 0x00, "scsi parity error" },
427 { 0x47, 0x01, "data phase crc error detected" },
428 { 0x47, 0x02, "scsi parity error detected during st data phase" },
429 { 0x47, 0x03, "information unit iucrc error detected" },
430 { 0x47, 0x04, "asynchronous information protection error detected" },
431 { 0x47, 0x05, "protocol service crc error" },
432 { 0x47, 0x7f, "some commands cleared by iscsi protocol event" },
433 { 0x48, 0x00, "initiator detected error message received" },
434 { 0x49, 0x00, "invalid message error" },
435 { 0x4a, 0x00, "command phase error" },
436 { 0x4b, 0x00, "data phase error" },
437 { 0x4b, 0x01, "invalid target port transfer tag received" },
438 { 0x4b, 0x02, "too much write data" },
439 { 0x4b, 0x03, "ack/nak timeout" },
440 { 0x4b, 0x04, "nak received" },
441 { 0x4b, 0x05, "data offset error" },
442 { 0x4c, 0x00, "logical unit failed self-configuration" },
443 { 0x4d, 0x00, "tagged overlapped commands (ASCQ = queue tag)" },
444 { 0x4e, 0x00, "overlapped commands attempted" },
445 { 0x50, 0x00, "write append error" },
446 { 0x51, 0x00, "erase failure" },
447 { 0x52, 0x00, "cartridge fault" },
448 { 0x53, 0x00, "media load or eject failed" },
449 { 0x53, 0x01, "unload tape failure" },
450 { 0x53, 0x02, "medium removal prevented" },
451 { 0x54, 0x00, "scsi to host system interface failure" },
452 { 0x55, 0x00, "system resource failure" },
453 { 0x55, 0x01, "system buffer full" },
454 { 0x55, 0x02, "insufficient reservation resources" },
455 { 0x55, 0x03, "insufficient resources" },
456 { 0x55, 0x04, "insufficient registration resources" },
457 { 0x55, 0x05, "insufficient access control resources" },
458 { 0x55, 0x06, "auxiliary memory out of space" },
459 { 0x57, 0x00, "unable to recover TOC" },
460 { 0x58, 0x00, "generation does not exist" },
461 { 0x59, 0x00, "updated block read" },
462 { 0x5a, 0x00, "operator request or state change input" },
463 { 0x5a, 0x01, "operator medium removal request" },
464 { 0x5a, 0x02, "operator selected write protect" },
465 { 0x5a, 0x03, "operator selected write permit" },
466 { 0x5b, 0x00, "log exception" },
467 { 0x5b, 0x01, "threshold condition met" },
468 { 0x5b, 0x02, "log counter at maximum" },
469 { 0x5b, 0x03, "log list codes exhausted" },
470 { 0x5c, 0x00, "RPL status change" },
471 { 0x5c, 0x01, "spindles synchronized" },
472 { 0x5c, 0x02, "spindles not synchronized" },
473 { 0x5d, 0x00, "drive operation marginal, service immediately"
474 " (failure prediction threshold exceeded)" },
475 { 0x5d, 0x01, "media failure prediction threshold exceeded" },
476 { 0x5d, 0x02, "LUN failure prediction threshold exceeded" },
477 { 0x5d, 0x03, "spare area exhaustion prediction threshold exceeded" },
478 { 0x5d, 0x10, "hardware impending failure general hard drive failure" },
479 { 0x5d, 0x11, "hardware impending failure drive error rate too high" },
480 { 0x5d, 0x12, "hardware impending failure data error rate too high" },
481 { 0x5d, 0x13, "hardware impending failure seek error rate too high" },
482 { 0x5d, 0x14, "hardware impending failure too many block reassigns" },
483 { 0x5d, 0x15, "hardware impending failure access times too high" },
484 { 0x5d, 0x16, "hardware impending failure start unit times too high" },
485 { 0x5d, 0x17, "hardware impending failure channel parametrics" },
486 { 0x5d, 0x18, "hardware impending failure controller detected" },
487 { 0x5d, 0x19, "hardware impending failure throughput performance" },
488 { 0x5d, 0x1a, "hardware impending failure seek time performance" },
489 { 0x5d, 0x1b, "hardware impending failure spin-up retry count" },
490 { 0x5d, 0x1c, "hardware impending failure drive calibration retry "
491 "count" },
492 { 0x5d, 0x20, "controller impending failure general hard drive "
493 "failure" },
494 { 0x5d, 0x21, "controller impending failure drive error rate too "
495 "high" },
496 { 0x5d, 0x22, "controller impending failure data error rate too high" },
497 { 0x5d, 0x23, "controller impending failure seek error rate too high" },
498 { 0x5d, 0x24, "controller impending failure too many block reassigns" },
499 { 0x5d, 0x25, "controller impending failure access times too high" },
500 { 0x5d, 0x26, "controller impending failure start unit times too "
501 "high" },
502 { 0x5d, 0x27, "controller impending failure channel parametrics" },
503 { 0x5d, 0x28, "controller impending failure controller detected" },
504 { 0x5d, 0x29, "controller impending failure throughput performance" },
505 { 0x5d, 0x2a, "controller impending failure seek time performance" },
506 { 0x5d, 0x2b, "controller impending failure spin-up retry count" },
507 { 0x5d, 0x2c, "controller impending failure drive calibration retry "
508 "cnt" },
509 { 0x5d, 0x30, "data channel impending failure general hard drive "
510 "failure" },
511 { 0x5d, 0x31, "data channel impending failure drive error rate too "
512 "high" },
513 { 0x5d, 0x32, "data channel impending failure data error rate too "
514 "high" },
515 { 0x5d, 0x33, "data channel impending failure seek error rate too "
516 "high" },
517 { 0x5d, 0x34, "data channel impending failure too many block "
518 "reassigns" },
519 { 0x5d, 0x35, "data channel impending failure access times too high" },
520 { 0x5d, 0x36, "data channel impending failure start unit times too "
521 "high" },
522 { 0x5d, 0x37, "data channel impending failure channel parametrics" },
523 { 0x5d, 0x38, "data channel impending failure controller detected" },
524 { 0x5d, 0x39, "data channel impending failure throughput performance" },
525 { 0x5d, 0x3a, "data channel impending failure seek time performance" },
526 { 0x5d, 0x3b, "data channel impending failure spin-up retry count" },
527 { 0x5d, 0x3c, "data channel impending failure drive calibrate retry "
528 "cnt" },
529 { 0x5d, 0x40, "servo impending failure general hard drive failure" },
530 { 0x5d, 0x41, "servo impending failure drive error rate too high" },
531 { 0x5d, 0x42, "servo impending failure data error rate too high" },
532 { 0x5d, 0x43, "servo impending failure seek error rate too high" },
533 { 0x5d, 0x44, "servo impending failure too many block reassigns" },
534 { 0x5d, 0x45, "servo impending failure access times too high" },
535 { 0x5d, 0x46, "servo impending failure start unit times too high" },
536 { 0x5d, 0x47, "servo impending failure channel parametrics" },
537 { 0x5d, 0x48, "servo impending failure controller detected" },
538 { 0x5d, 0x49, "servo impending failure throughput performance" },
539 { 0x5d, 0x4a, "servo impending failure seek time performance" },
540 { 0x5d, 0x4b, "servo impending failure spin-up retry count" },
541 { 0x5d, 0x4c, "servo impending failure drive calibration retry count" },
542 { 0x5d, 0x50, "spindle impending failure general hard drive failure" },
543 { 0x5d, 0x51, "spindle impending failure drive error rate too high" },
544 { 0x5d, 0x52, "spindle impending failure data error rate too high" },
545 { 0x5d, 0x53, "spindle impending failure seek error rate too high" },
546 { 0x5d, 0x54, "spindle impending failure too many block reassigns" },
547 { 0x5d, 0x55, "spindle impending failure access times too high" },
548 { 0x5d, 0x56, "spindle impending failure start unit times too high" },
549 { 0x5d, 0x57, "spindle impending failure channel parametrics" },
550 { 0x5d, 0x58, "spindle impending failure controller detected" },
551 { 0x5d, 0x59, "spindle impending failure throughput performance" },
552 { 0x5d, 0x5a, "spindle impending failure seek time performance" },
553 { 0x5d, 0x5b, "spindle impending failure spin-up retry count" },
554 { 0x5d, 0x5c, "spindle impending failure drive calibration retry "
555 "count" },
556 { 0x5d, 0x60, "firmware impending failure general hard drive failure" },
557 { 0x5d, 0x61, "firmware impending failure drive error rate too high" },
558 { 0x5d, 0x62, "firmware impending failure data error rate too high" },
559 { 0x5d, 0x63, "firmware impending failure seek error rate too high" },
560 { 0x5d, 0x64, "firmware impending failure too many block reassigns" },
561 { 0x5d, 0x65, "firmware impending failure access times too high" },
562 { 0x5d, 0x66, "firmware impending failure start unit times too high" },
563 { 0x5d, 0x67, "firmware impending failure channel parametrics" },
564 { 0x5d, 0x68, "firmware impending failure controller detected" },
565 { 0x5d, 0x69, "firmware impending failure throughput performance" },
566 { 0x5d, 0x6a, "firmware impending failure seek time performance" },
567 { 0x5d, 0x6b, "firmware impending failure spin-up retry count" },
568 { 0x5d, 0x6c, "firmware impending failure drive calibration retry "
569 "count" },
570 { 0x5d, 0xff, "failure prediction threshold exceeded (false)" },
571 { 0x5e, 0x00, "low power condition active" },
572 { 0x5e, 0x01, "idle condition activated by timer" },
573 { 0x5e, 0x02, "standby condition activated by timer" },
574 { 0x5e, 0x03, "idle condition activated by command" },
575 { 0x5e, 0x04, "standby condition activated by command" },
576 { 0x60, 0x00, "lamp failure" },
577 { 0x61, 0x00, "video aquisition error" },
578 { 0x62, 0x00, "scan head positioning error" },
579 { 0x63, 0x00, "end of user area encountered on this track" },
580 { 0x63, 0x01, "packet does not fit in available space" },
581 { 0x64, 0x00, "illegal mode for this track" },
582 { 0x64, 0x01, "invalid packet size" },
583 { 0x65, 0x00, "voltage fault" },
584 { 0x66, 0x00, "automatic document feeder cover up" },
585 { 0x67, 0x00, "configuration failure" },
586 { 0x67, 0x01, "configuration of incapable LUNs failed" },
587 { 0x67, 0x02, "add LUN failed" },
588 { 0x67, 0x03, "modification of LUN failed" },
589 { 0x67, 0x04, "exchange of LUN failed" },
590 { 0x67, 0x05, "remove of LUN failed" },
591 { 0x67, 0x06, "attachment of LUN failed" },
592 { 0x67, 0x07, "creation of LUN failed" },
593 { 0x67, 0x08, "assign failure occurred" },
594 { 0x67, 0x09, "multiply assigned LUN" },
595 { 0x67, 0x0a, "set target port groups command failed" },
596 { 0x68, 0x00, "logical unit not configured" },
597 { 0x69, 0x00, "data loss on logical unit" },
598 { 0x69, 0x01, "multiple LUN failures" },
599 { 0x69, 0x02, "parity/data mismatch" },
600 { 0x6a, 0x00, "informational, refer to log" },
601 { 0x6b, 0x00, "state change has occured" },
602 { 0x6b, 0x01, "redundancy level got better" },
603 { 0x6b, 0x02, "redundancy level got worse" },
604 { 0x6c, 0x00, "rebuild failure occured" },
605 { 0x6d, 0x00, "recalculate failure occured" },
606 { 0x6e, 0x00, "command to logical unit failed" },
607 { 0x6f, 0x00, "copy protect key exchange failure authentication "
608 "failure" },
609 { 0x6f, 0x01, "copy protect key exchange failure key not present" },
610 { 0x6f, 0x02, "copy protect key exchange failure key not established" },
611 { 0x6f, 0x03, "read of scrambled sector without authentication" },
612 { 0x6f, 0x04, "media region code is mismatched to LUN region" },
613 { 0x6f, 0x05, "drive region must be permanent/region reset count "
614 "error" },
615 { 0x70, 0xffff, "decompression exception short algorithm id of ASCQ" },
616 { 0x71, 0x00, "decompression exception long algorithm id" },
617 { 0x72, 0x00, "session fixation error" },
618 { 0x72, 0x01, "session fixation error writing lead-in" },
619 { 0x72, 0x02, "session fixation error writing lead-out" },
620 { 0x72, 0x03, "session fixation error - incomplete track in session" },
621 { 0x72, 0x04, "empty or partially written reserved track" },
622 { 0x72, 0x05, "no more track reservations allowed" },
623 { 0x73, 0x00, "cd control error" },
624 { 0x73, 0x01, "power calibration area almost full" },
625 { 0x73, 0x02, "power calibration area is full" },
626 { 0x73, 0x03, "power calibration area error" },
627 { 0x73, 0x04, "program memory area update failure" },
628 { 0x73, 0x05, "program memory area is full" },
629 { 0x73, 0x06, "rma/pma is almost full" },
630 { 0xffff, 0xffff, NULL }
631 };
632
633 /*
634 * Given an asc (Additional Sense Code) and ascq (Additional Sense Code
635 * Qualifier), return a string describing the error information.
636 */
637 static char *
scsi_util_asc_ascq_name(uint_t asc,uint_t ascq,char * buf,int buflen)638 scsi_util_asc_ascq_name(uint_t asc, uint_t ascq, char *buf, int buflen)
639 {
640 int i = 0;
641
642 while (extended_sense_list[i].asc != 0xffff) {
643 if ((asc == extended_sense_list[i].asc) &&
644 ((ascq == extended_sense_list[i].ascq) ||
645 (extended_sense_list[i].ascq == 0xffff))) {
646 return ((char *)extended_sense_list[i].message);
647 }
648 i++;
649 }
650 (void) snprintf(buf, buflen, "<vendor unique code 0x%x>", asc);
651 return (buf);
652 }
653
654 /*
655 * Dumps detailed information about a particular SCSI error condition.
656 */
657 static void
scsi_printerr(struct uscsi_cmd * ucmd,struct scsi_extended_sense * rq,int rqlen)658 scsi_printerr(struct uscsi_cmd *ucmd, struct scsi_extended_sense *rq, int rqlen)
659 {
660 diskaddr_t blkno;
661 struct scsi_descr_sense_hdr *sdsp = (struct scsi_descr_sense_hdr *)rq;
662 char msgbuf[MSGBUFLEN];
663
664 if (find_string(sensekey_strings, rq->es_key) == NULL)
665 dprintf("unknown error");
666
667 dprintf("during %s:",
668 find_string(scsi_cmdname_strings, ucmd->uscsi_cdb[0]));
669
670 /*
671 * Get asc, ascq and info field from sense data. There are two
672 * possible formats (fixed sense data and descriptor sense data)
673 * depending on the value of es_code.
674 */
675 switch (rq->es_code) {
676 case CODE_FMT_DESCR_CURRENT:
677 case CODE_FMT_DESCR_DEFERRED:
678 blkno = (diskaddr_t)scsi_extract_sense_info_descr(sdsp, rqlen);
679 if (blkno != (diskaddr_t)-1)
680 dprintf(": block %lld (0x%llx)", blkno, blkno);
681 dprintf("\n");
682 dprintf("ASC: 0x%x ASCQ: 0x%x (%s)\n",
683 sdsp->ds_add_code, sdsp->ds_qual_code,
684 scsi_util_asc_ascq_name(sdsp->ds_add_code,
685 sdsp->ds_qual_code, msgbuf, MSGBUFLEN));
686
687 break;
688
689 case CODE_FMT_FIXED_CURRENT:
690 case CODE_FMT_FIXED_DEFERRED:
691 default:
692 if (rq->es_valid) {
693 blkno = (rq->es_info_1 << 24) |
694 (rq->es_info_2 << 16) |
695 (rq->es_info_3 << 8) | rq->es_info_4;
696 dprintf(": block %lld (0x%llx)", blkno, blkno);
697 }
698 dprintf("\n");
699 if (rq->es_add_len >= 6) {
700 dprintf("ASC: 0x%x ASCQ: 0x%x (%s)\n",
701 rq->es_add_code,
702 rq->es_qual_code,
703 scsi_util_asc_ascq_name(rq->es_add_code,
704 rq->es_qual_code, msgbuf, MSGBUFLEN));
705 }
706 break;
707 }
708
709 if (rq->es_key == KEY_ILLEGAL_REQUEST) {
710 ddump("cmd:", (caddr_t)ucmd,
711 sizeof (struct uscsi_cmd));
712 ddump("cdb:", (caddr_t)ucmd->uscsi_cdb,
713 ucmd->uscsi_cdblen);
714 }
715 ddump("sense:", (caddr_t)rq, rqlen);
716
717 switch (rq->es_code) {
718 case CODE_FMT_DESCR_CURRENT:
719 case CODE_FMT_DESCR_DEFERRED:
720 scsi_print_descr_sense(sdsp, rqlen);
721 break;
722 case CODE_FMT_FIXED_CURRENT:
723 case CODE_FMT_FIXED_DEFERRED:
724 default:
725 scsi_print_extended_sense(rq, rqlen);
726 break;
727 }
728 }
729
730 /*
731 * Retrieve "information" field from descriptor format sense data. Iterates
732 * through each sense descriptor looking for the information descriptor and
733 * returns the information field from that descriptor.
734 */
735 static diskaddr_t
scsi_extract_sense_info_descr(struct scsi_descr_sense_hdr * sdsp,int rqlen)736 scsi_extract_sense_info_descr(struct scsi_descr_sense_hdr *sdsp, int rqlen)
737 {
738 diskaddr_t result;
739 uint8_t *descr_offset;
740 int valid_sense_length;
741 struct scsi_information_sense_descr *isd;
742
743 /*
744 * Initialize result to -1 indicating there is no information
745 * descriptor
746 */
747 result = (diskaddr_t)-1;
748
749 /*
750 * The first descriptor will immediately follow the header
751 */
752 descr_offset = (uint8_t *)(sdsp+1);
753
754 /*
755 * Calculate the amount of valid sense data
756 */
757 valid_sense_length =
758 MIN((sizeof (struct scsi_descr_sense_hdr) +
759 sdsp->ds_addl_sense_length), rqlen);
760
761 /*
762 * Iterate through the list of descriptors, stopping when we run out of
763 * sense data
764 */
765 while ((descr_offset + sizeof (struct scsi_information_sense_descr)) <=
766 (uint8_t *)sdsp + valid_sense_length) {
767 /*
768 * Check if this is an information descriptor. We can use the
769 * scsi_information_sense_descr structure as a template since
770 * the first two fields are always the same
771 */
772 isd = (struct scsi_information_sense_descr *)descr_offset;
773 if (isd->isd_descr_type == DESCR_INFORMATION) {
774 /*
775 * Found an information descriptor. Copy the
776 * information field. There will only be one
777 * information descriptor so we can stop looking.
778 */
779 result =
780 (((diskaddr_t)isd->isd_information[0] << 56) |
781 ((diskaddr_t)isd->isd_information[1] << 48) |
782 ((diskaddr_t)isd->isd_information[2] << 40) |
783 ((diskaddr_t)isd->isd_information[3] << 32) |
784 ((diskaddr_t)isd->isd_information[4] << 24) |
785 ((diskaddr_t)isd->isd_information[5] << 16) |
786 ((diskaddr_t)isd->isd_information[6] << 8) |
787 ((diskaddr_t)isd->isd_information[7]));
788 break;
789 }
790
791 /*
792 * Get pointer to the next descriptor. The "additional length"
793 * field holds the length of the descriptor except for the
794 * "type" and "additional length" fields, so we need to add 2 to
795 * get the total length.
796 */
797 descr_offset += (isd->isd_addl_length + 2);
798 }
799
800 return (result);
801 }
802
803 /*
804 * Display the full scsi_extended_sense as returned by the device
805 */
806 static void
scsi_print_extended_sense(struct scsi_extended_sense * rq,int rqlen)807 scsi_print_extended_sense(struct scsi_extended_sense *rq, int rqlen)
808 {
809 static char *scsi_extended_sense_labels[] = {
810 "Request sense valid: ",
811 "Error class and code: ",
812 "Segment number: ",
813 "Filemark: ",
814 "End-of-medium: ",
815 "Incorrect length indicator: ",
816 "Sense key: ",
817 "Information field: ",
818 "Additional sense length: ",
819 "Command-specific information: ",
820 "Additional sense code: ",
821 "Additional sense code qualifier: ",
822 "Field replaceable unit code: ",
823 "Sense-key specific: ",
824 "Additional sense bytes: "
825 };
826
827 char **p = scsi_extended_sense_labels;
828
829 if (rqlen < (sizeof (*rq) - 2) || !rq->es_valid) {
830 /*
831 * target should be capable of returning at least 18
832 * bytes of data, i.e upto rq->es_skey_specific field.
833 * The additional sense bytes (2 or more ...) are optional.
834 */
835 return;
836 }
837
838 dprintf("\n%s%s\n", *p++, rq->es_valid ? "yes" : "no");
839 dprintf("%s0x%02x\n", *p++, (rq->es_class << 4) + rq->es_code);
840 dprintf("%s%d\n", *p++, rq->es_segnum);
841 dprintf("%s%s\n", *p++, rq->es_filmk ? "yes" : "no");
842 dprintf("%s%s\n", *p++, rq->es_eom ? "yes" : "no");
843 dprintf("%s%s\n", *p++, rq->es_ili ? "yes" : "no");
844 dprintf("%s%d\n", *p++, rq->es_key);
845
846 dprintf("%s0x%02x 0x%02x 0x%02x 0x%02x\n", *p++, rq->es_info_1,
847 rq->es_info_2, rq->es_info_3, rq->es_info_4);
848 dprintf("%s%d\n", *p++, rq->es_add_len);
849 dprintf("%s0x%02x 0x%02x 0x%02x 0x%02x\n", *p++,
850 rq->es_cmd_info[0], rq->es_cmd_info[1], rq->es_cmd_info[2],
851 rq->es_cmd_info[3]);
852 dprintf("%s0x%02x = %d\n", *p++, rq->es_add_code,
853 rq->es_add_code);
854 dprintf("%s0x%02x = %d\n", *p++, rq->es_qual_code,
855 rq->es_qual_code);
856 dprintf("%s%d\n", *p++, rq->es_fru_code);
857 dprintf("%s0x%02x 0x%02x 0x%02x\n", *p++,
858 rq->es_skey_specific[0], rq->es_skey_specific[1],
859 rq->es_skey_specific[2]);
860 if (rqlen >= sizeof (*rq)) {
861 dprintf("%s0x%02x 0x%02x%s\n", *p, rq->es_add_info[0],
862 rq->es_add_info[1], (rqlen > sizeof (*rq)) ? " ..." : "");
863 }
864
865 dprintf("\n");
866 }
867
868 /*
869 * Display the full descriptor sense data as returned by the device
870 */
871 static void
scsi_print_descr_sense(struct scsi_descr_sense_hdr * rq,int rqlen)872 scsi_print_descr_sense(struct scsi_descr_sense_hdr *rq, int rqlen)
873 {
874 /*
875 * Labels for the various fields of the scsi_descr_sense_hdr structure
876 */
877 static char *scsi_descr_sense_labels[] = {
878 "Error class and code: ",
879 "Sense key: ",
880 "Additional sense length: ",
881 "Additional sense code: ",
882 "Additional sense code qualifier: ",
883 "Additional sense bytes: "
884 };
885
886 struct scsi_information_sense_descr *isd;
887 uint8_t *descr_offset;
888 int valid_sense_length;
889 char **p = scsi_descr_sense_labels;
890
891 /* Target must return at least 8 bytes of data */
892 if (rqlen < sizeof (struct scsi_descr_sense_hdr))
893 return;
894
895 /* Print descriptor sense header */
896 dprintf("%s0x%02x\n", *p++, (rq->ds_class << 4) + rq->ds_code);
897 dprintf("%s%d\n", *p++, rq->ds_key);
898
899 dprintf("%s%d\n", *p++, rq->ds_addl_sense_length);
900 dprintf("%s0x%02x = %d\n", *p++, rq->ds_add_code,
901 rq->ds_add_code);
902 dprintf("%s0x%02x = %d\n", *p++, rq->ds_qual_code,
903 rq->ds_qual_code);
904 dprintf("\n");
905
906 /*
907 * Now print any sense descriptors. The first descriptor will
908 * immediately follow the header
909 */
910 descr_offset = (uint8_t *)(rq+1); /* Pointer arithmetic */
911
912 /*
913 * Calculate the amount of valid sense data
914 */
915 valid_sense_length =
916 MIN((sizeof (struct scsi_descr_sense_hdr) +
917 rq->ds_addl_sense_length), rqlen);
918
919 /*
920 * Iterate through the list of descriptors, stopping when we
921 * run out of sense data. Descriptor format is:
922 *
923 * <Descriptor type> <Descriptor length> <Descriptor data> ...
924 */
925 while ((descr_offset + *(descr_offset + 1)) <=
926 (uint8_t *)rq + valid_sense_length) {
927 /*
928 * Determine descriptor type. We can use the
929 * scsi_information_sense_descr structure as a
930 * template since the first two fields are always the
931 * same.
932 */
933 isd = (struct scsi_information_sense_descr *)descr_offset;
934 switch (isd->isd_descr_type) {
935 case DESCR_INFORMATION: {
936 uint64_t information;
937
938 information =
939 (((uint64_t)isd->isd_information[0] << 56) |
940 ((uint64_t)isd->isd_information[1] << 48) |
941 ((uint64_t)isd->isd_information[2] << 40) |
942 ((uint64_t)isd->isd_information[3] << 32) |
943 ((uint64_t)isd->isd_information[4] << 24) |
944 ((uint64_t)isd->isd_information[5] << 16) |
945 ((uint64_t)isd->isd_information[6] << 8) |
946 ((uint64_t)isd->isd_information[7]));
947 dprintf("Information field: "
948 "%0" PRIx64 "\n", information);
949 break;
950 }
951 case DESCR_COMMAND_SPECIFIC: {
952 struct scsi_cmd_specific_sense_descr *c =
953 (struct scsi_cmd_specific_sense_descr *)isd;
954 uint64_t cmd_specific;
955
956 cmd_specific =
957 (((uint64_t)c->css_cmd_specific_info[0] << 56) |
958 ((uint64_t)c->css_cmd_specific_info[1] << 48) |
959 ((uint64_t)c->css_cmd_specific_info[2] << 40) |
960 ((uint64_t)c->css_cmd_specific_info[3] << 32) |
961 ((uint64_t)c->css_cmd_specific_info[4] << 24) |
962 ((uint64_t)c->css_cmd_specific_info[5] << 16) |
963 ((uint64_t)c->css_cmd_specific_info[6] << 8) |
964 ((uint64_t)c->css_cmd_specific_info[7]));
965 dprintf("Command-specific information: "
966 "%0" PRIx64 "\n", cmd_specific);
967 break;
968 }
969 case DESCR_SENSE_KEY_SPECIFIC: {
970 struct scsi_sk_specific_sense_descr *ssd =
971 (struct scsi_sk_specific_sense_descr *)isd;
972 uint8_t *sk_spec_ptr = (uint8_t *)&ssd->sss_data;
973 dprintf("Sense-key specific: "
974 "0x%02x 0x%02x 0x%02x\n", sk_spec_ptr[0],
975 sk_spec_ptr[1], sk_spec_ptr[2]);
976 break;
977 }
978 case DESCR_FRU: {
979 struct scsi_fru_sense_descr *fsd =
980 (struct scsi_fru_sense_descr *)isd;
981 dprintf("Field replaceable unit code: "
982 "%d\n", fsd->fs_fru_code);
983 break;
984 }
985 case DESCR_BLOCK_COMMANDS: {
986 struct scsi_block_cmd_sense_descr *bsd =
987 (struct scsi_block_cmd_sense_descr *)isd;
988 dprintf("Incorrect length indicator: "
989 "%s\n", bsd->bcs_ili ? "yes" : "no");
990 break;
991 }
992 default:
993 /* Ignore */
994 break;
995 }
996
997 /*
998 * Get pointer to the next descriptor. The "additional
999 * length" field holds the length of the descriptor except
1000 * for the "type" and "additional length" fields, so
1001 * we need to add 2 to get the total length.
1002 */
1003 descr_offset += (isd->isd_addl_length + 2);
1004 }
1005
1006 dprintf("\n");
1007 }
1008
1009 static int
uscsi_timeout(void)1010 uscsi_timeout(void)
1011 {
1012 const char *env = getenv("USCSI_TIMEOUT");
1013 static int timeo = -1;
1014 int i;
1015
1016 if (timeo > 0)
1017 return (timeo);
1018
1019 if (env != NULL) {
1020 i = atoi(env);
1021 if (i > USCSI_TIMEOUT_MAX)
1022 i = USCSI_TIMEOUT_MAX;
1023 else if (i < 0)
1024 i = USCSI_DEFAULT_TIMEOUT;
1025 } else
1026 i = USCSI_DEFAULT_TIMEOUT;
1027
1028 timeo = i;
1029 return (i);
1030 }
1031
1032 /*
1033 * Execute a command and determine the result. Uses the "uscsi" ioctl
1034 * interface, which is fully supported.
1035 *
1036 * If the user wants request sense data to be returned in case of error then ,
1037 * the "uscsi_cmd" structure should have the request sense buffer allocated in
1038 * uscsi_rqbuf.
1039 */
1040 static int
uscsi_cmd(int fd,struct uscsi_cmd * ucmd,void * rqbuf,int * rqlen)1041 uscsi_cmd(int fd, struct uscsi_cmd *ucmd, void *rqbuf, int *rqlen)
1042 {
1043 struct scsi_extended_sense *rq;
1044 int status;
1045
1046 /*
1047 * Set function flags for driver.
1048 */
1049 ucmd->uscsi_flags = USCSI_ISOLATE;
1050 if (!ds_debug)
1051 ucmd->uscsi_flags |= USCSI_SILENT;
1052
1053 /*
1054 * If this command will perform a read, set the USCSI_READ flag
1055 */
1056 if (ucmd->uscsi_buflen > 0) {
1057 /*
1058 * uscsi_cdb is declared as a caddr_t, so any CDB
1059 * command byte with the MSB set will result in a
1060 * compiler error unless we cast to an unsigned value.
1061 */
1062 switch ((uint8_t)ucmd->uscsi_cdb[0]) {
1063 case SCMD_MODE_SENSE:
1064 case SCMD_MODE_SENSE_G1:
1065 case SCMD_LOG_SENSE_G1:
1066 case SCMD_REQUEST_SENSE:
1067 ucmd->uscsi_flags |= USCSI_READ;
1068 break;
1069
1070 case SCMD_MODE_SELECT:
1071 case SCMD_MODE_SELECT_G1:
1072 /* LINTED */
1073 ucmd->uscsi_flags |= USCSI_WRITE;
1074 break;
1075 default:
1076 assert(0);
1077 break;
1078 }
1079 }
1080
1081 /* Set timeout */
1082 ucmd->uscsi_timeout = uscsi_timeout();
1083
1084 /*
1085 * Set up Request Sense buffer
1086 */
1087
1088 if (ucmd->uscsi_rqbuf == NULL) {
1089 ucmd->uscsi_rqbuf = rqbuf;
1090 ucmd->uscsi_rqlen = *rqlen;
1091 ucmd->uscsi_rqresid = *rqlen;
1092 }
1093 if (ucmd->uscsi_rqbuf)
1094 ucmd->uscsi_flags |= USCSI_RQENABLE;
1095 ucmd->uscsi_rqstatus = IMPOSSIBLE_SCSI_STATUS;
1096
1097 if (ucmd->uscsi_rqbuf != NULL && ucmd->uscsi_rqlen > 0)
1098 (void) memset(ucmd->uscsi_rqbuf, 0, ucmd->uscsi_rqlen);
1099
1100 /*
1101 * Execute the ioctl
1102 */
1103 status = ioctl(fd, USCSICMD, ucmd);
1104 if (status == 0 && ucmd->uscsi_status == 0)
1105 return (status);
1106
1107 /*
1108 * If an automatic Request Sense gave us valid info about the error, we
1109 * may be able to use that to print a reasonable error msg.
1110 */
1111 if (ucmd->uscsi_rqstatus == IMPOSSIBLE_SCSI_STATUS) {
1112 dprintf("No request sense for command %s\n",
1113 find_string(scsi_cmdname_strings,
1114 ucmd->uscsi_cdb[0]));
1115 return (-1);
1116 }
1117 if (ucmd->uscsi_rqstatus != STATUS_GOOD) {
1118 dprintf("Request sense status for command %s: 0x%x\n",
1119 find_string(scsi_cmdname_strings,
1120 ucmd->uscsi_cdb[0]),
1121 ucmd->uscsi_rqstatus);
1122 return (-1);
1123 }
1124
1125 rq = (struct scsi_extended_sense *)ucmd->uscsi_rqbuf;
1126 *rqlen = ucmd->uscsi_rqlen - ucmd->uscsi_rqresid;
1127
1128 if ((((int)rq->es_add_len) + 8) < MIN_REQUEST_SENSE_LEN ||
1129 rq->es_class != CLASS_EXTENDED_SENSE ||
1130 *rqlen < MIN_REQUEST_SENSE_LEN) {
1131 dprintf("Request sense for command %s failed\n",
1132 find_string(scsi_cmdname_strings,
1133 ucmd->uscsi_cdb[0]));
1134
1135 dprintf("Sense data:\n");
1136 ddump(NULL, (caddr_t)rqbuf, *rqlen);
1137
1138 return (-1);
1139 }
1140
1141 /*
1142 * If the failed command is a Mode Select, and the
1143 * target is indicating that it has rounded one of
1144 * the mode select parameters, as defined in the SCSI-2
1145 * specification, then we should accept the command
1146 * as successful.
1147 */
1148 if (ucmd->uscsi_cdb[0] == SCMD_MODE_SELECT ||
1149 ucmd->uscsi_cdb[0] == SCMD_MODE_SELECT_G1) {
1150 if (rq->es_key == KEY_RECOVERABLE_ERROR &&
1151 rq->es_add_code == ROUNDED_PARAMETER &&
1152 rq->es_qual_code == 0) {
1153 return (0);
1154 }
1155 }
1156
1157 if (ds_debug)
1158 scsi_printerr(ucmd, rq, *rqlen);
1159 if (rq->es_key != KEY_RECOVERABLE_ERROR)
1160 return (-1);
1161 return (0);
1162 }
1163
1164 int
uscsi_request_sense(int fd,caddr_t buf,int buflen,void * rqbuf,int * rqblen)1165 uscsi_request_sense(int fd, caddr_t buf, int buflen, void *rqbuf, int *rqblen)
1166 {
1167 struct uscsi_cmd ucmd;
1168 union scsi_cdb cdb;
1169 int status;
1170
1171 (void) memset(buf, 0, buflen);
1172 (void) memset(&ucmd, 0, sizeof (ucmd));
1173 (void) memset(&cdb, 0, sizeof (union scsi_cdb));
1174 cdb.scc_cmd = SCMD_REQUEST_SENSE;
1175 FORMG0COUNT(&cdb, (uchar_t)buflen);
1176 ucmd.uscsi_cdb = (caddr_t)&cdb;
1177 ucmd.uscsi_cdblen = CDB_GROUP0;
1178 ucmd.uscsi_bufaddr = buf;
1179 ucmd.uscsi_buflen = buflen;
1180 status = uscsi_cmd(fd, &ucmd, rqbuf, rqblen);
1181 if (status)
1182 dprintf("Request sense failed\n");
1183 if (status == 0)
1184 ddump("Request Sense data:", buf, buflen);
1185
1186 return (status);
1187 }
1188
1189 /*
1190 * Execute a uscsi mode sense command. This can only be used to return one page
1191 * at a time. Return the mode header/block descriptor and the actual page data
1192 * separately - this allows us to support devices which return either 0 or 1
1193 * block descriptors. Whatever a device gives us in the mode header/block
1194 * descriptor will be returned to it upon subsequent mode selects.
1195 */
1196 int
uscsi_mode_sense(int fd,int page_code,int page_control,caddr_t page_data,int page_size,struct scsi_ms_header * header,void * rqbuf,int * rqblen)1197 uscsi_mode_sense(int fd, int page_code, int page_control, caddr_t page_data,
1198 int page_size, struct scsi_ms_header *header, void *rqbuf, int *rqblen)
1199 {
1200 caddr_t mode_sense_buf;
1201 struct mode_header *hdr;
1202 struct mode_page *pg;
1203 int nbytes;
1204 struct uscsi_cmd ucmd;
1205 union scsi_cdb cdb;
1206 int status;
1207 int maximum;
1208 char *pc;
1209
1210 assert(page_size >= 0 && page_size < 256);
1211 assert(page_control == PC_CURRENT || page_control == PC_CHANGEABLE ||
1212 page_control == PC_DEFAULT || page_control == PC_SAVED);
1213
1214 nbytes = sizeof (struct scsi_ms_header) + page_size;
1215 mode_sense_buf = alloca((uint_t)nbytes);
1216
1217 /*
1218 * Build and execute the uscsi ioctl
1219 */
1220 (void) memset(mode_sense_buf, 0, nbytes);
1221 (void) memset(&ucmd, 0, sizeof (ucmd));
1222 (void) memset(&cdb, 0, sizeof (union scsi_cdb));
1223 cdb.scc_cmd = SCMD_MODE_SENSE;
1224 FORMG0COUNT(&cdb, (uchar_t)nbytes);
1225 cdb.cdb_opaque[2] = page_control | page_code;
1226 ucmd.uscsi_cdb = (caddr_t)&cdb;
1227 ucmd.uscsi_cdblen = CDB_GROUP0;
1228 ucmd.uscsi_bufaddr = mode_sense_buf;
1229 ucmd.uscsi_buflen = nbytes;
1230 status = uscsi_cmd(fd, &ucmd, rqbuf, rqblen);
1231 if (status) {
1232 dprintf("Mode sense page 0x%x failed\n", page_code);
1233 return (-1);
1234 }
1235
1236 ddump("RAW MODE SENSE BUFFER", mode_sense_buf, nbytes);
1237
1238 /*
1239 * Verify that the returned data looks reasonable, find the actual page
1240 * data, and copy it into the user's buffer. Copy the mode_header and
1241 * block_descriptor into the header structure, which can then be used to
1242 * return the same data to the drive when issuing a mode select.
1243 */
1244 hdr = (struct mode_header *)mode_sense_buf;
1245 (void) memset((caddr_t)header, 0, sizeof (struct scsi_ms_header));
1246
1247 /*
1248 * Check to see if we have a valid header length. We've occasionally
1249 * seen hardware return zero here, even though they filled in the media
1250 * type.
1251 */
1252 if (hdr->length == 0) {
1253 dprintf("\nMode sense page 0x%x: has header length for zero\n",
1254 page_code);
1255 ddump("Mode sense:", mode_sense_buf, nbytes);
1256 return (-1);
1257 }
1258
1259 if (hdr->bdesc_length != sizeof (struct block_descriptor) &&
1260 hdr->bdesc_length != 0) {
1261 dprintf("\nMode sense page 0x%x: block descriptor "
1262 "length %d incorrect\n", page_code, hdr->bdesc_length);
1263 ddump("Mode sense:", mode_sense_buf, nbytes);
1264 return (-1);
1265 }
1266 (void) memcpy((caddr_t)header, mode_sense_buf,
1267 (int)(MODE_HEADER_LENGTH + hdr->bdesc_length));
1268 pg = (struct mode_page *)((ulong_t)mode_sense_buf +
1269 MODE_HEADER_LENGTH + hdr->bdesc_length);
1270
1271 if (page_code == MODEPAGE_ALLPAGES) {
1272 /* special case */
1273
1274 if ((hdr->length + sizeof (header->ms_header.length)) <
1275 (MODE_HEADER_LENGTH + hdr->bdesc_length)) {
1276 dprintf("\nHeader length would spiral into a "
1277 "negative bcopy\n");
1278 return (-1);
1279 }
1280
1281 (void) memcpy(page_data, (caddr_t)pg,
1282 (hdr->length + sizeof (header->ms_header.length)) -
1283 (MODE_HEADER_LENGTH + hdr->bdesc_length));
1284
1285 pc = find_string(page_control_strings, page_control);
1286 dprintf("\nMode sense page 0x%x (%s):\n", page_code,
1287 pc != NULL ? pc : "");
1288 ddump("header:", (caddr_t)header,
1289 sizeof (struct scsi_ms_header));
1290 ddump("data:", page_data,
1291 (hdr->length +
1292 sizeof (header->ms_header.length)) -
1293 (MODE_HEADER_LENGTH + hdr->bdesc_length));
1294
1295 return (0);
1296 }
1297
1298 if (pg->code != page_code) {
1299 dprintf("\nMode sense page 0x%x: incorrect page code 0x%x\n",
1300 page_code, pg->code);
1301 ddump("Mode sense:", mode_sense_buf, nbytes);
1302 return (-1);
1303 }
1304
1305 /*
1306 * Accept up to "page_size" bytes of mode sense data. This allows us to
1307 * accept both CCS and SCSI-2 structures, as long as we request the
1308 * greater of the two.
1309 */
1310 maximum = page_size - sizeof (struct mode_page);
1311 if (((int)pg->length) > maximum) {
1312 dprintf("Mode sense page 0x%x: incorrect page "
1313 "length %d - expected max %d\n",
1314 page_code, pg->length, maximum);
1315 ddump("Mode sense:", mode_sense_buf, nbytes);
1316 return (-1);
1317 }
1318
1319 (void) memcpy(page_data, (caddr_t)pg, MODESENSE_PAGE_LEN(pg));
1320
1321 pc = find_string(page_control_strings, page_control);
1322 dprintf("\nMode sense page 0x%x (%s):\n", page_code,
1323 pc != NULL ? pc : "");
1324 ddump("header:", (caddr_t)header, sizeof (struct scsi_ms_header));
1325 ddump("data:", page_data, MODESENSE_PAGE_LEN(pg));
1326
1327 return (0);
1328 }
1329
1330 /*
1331 * Execute a uscsi MODE SENSE(10) command. This can only be used to return one
1332 * page at a time. Return the mode header/block descriptor and the actual page
1333 * data separately - this allows us to support devices which return either 0 or
1334 * 1 block descriptors. Whatever a device gives us in the mode header/block
1335 * descriptor will be returned to it upon subsequent mode selects.
1336 */
1337 int
uscsi_mode_sense_10(int fd,int page_code,int page_control,caddr_t page_data,int page_size,struct scsi_ms_header_g1 * header,void * rqbuf,int * rqblen)1338 uscsi_mode_sense_10(int fd, int page_code, int page_control,
1339 caddr_t page_data, int page_size, struct scsi_ms_header_g1 *header,
1340 void *rqbuf, int *rqblen)
1341 {
1342 caddr_t mode_sense_buf;
1343 struct mode_header_g1 *hdr;
1344 struct mode_page *pg;
1345 int nbytes;
1346 struct uscsi_cmd ucmd;
1347 union scsi_cdb cdb;
1348 int status;
1349 int maximum;
1350 ushort_t length, bdesc_length;
1351 char *pc;
1352
1353 assert(page_size >= 0 && page_size < UINT16_MAX);
1354 assert(page_control == PC_CURRENT || page_control == PC_CHANGEABLE ||
1355 page_control == PC_DEFAULT || page_control == PC_SAVED);
1356
1357 nbytes = sizeof (struct scsi_ms_header_g1) + page_size;
1358 mode_sense_buf = alloca((uint_t)nbytes);
1359
1360 (void) memset(mode_sense_buf, 0, nbytes);
1361 (void) memset((char *)&ucmd, 0, sizeof (ucmd));
1362 (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
1363 cdb.scc_cmd = SCMD_MODE_SENSE_G1;
1364 FORMG1COUNT(&cdb, (uint16_t)nbytes);
1365 cdb.cdb_opaque[2] = page_control | page_code;
1366 ucmd.uscsi_cdb = (caddr_t)&cdb;
1367 ucmd.uscsi_cdblen = CDB_GROUP1;
1368 ucmd.uscsi_bufaddr = mode_sense_buf;
1369 ucmd.uscsi_buflen = nbytes;
1370
1371 status = uscsi_cmd(fd, &ucmd, rqbuf, rqblen);
1372 if (status) {
1373 dprintf("Mode sense(10) page 0x%x failed\n",
1374 page_code);
1375 return (-1);
1376 }
1377
1378 ddump("RAW MODE SENSE(10) BUFFER", mode_sense_buf, nbytes);
1379
1380 /*
1381 * Verify that the returned data looks reasonable, find the actual page
1382 * data, and copy it into the user's buffer. Copy the mode_header and
1383 * block_descriptor into the header structure, which can then be used to
1384 * return the same data to the drive when issuing a mode select.
1385 */
1386 /* LINTED */
1387 hdr = (struct mode_header_g1 *)mode_sense_buf;
1388
1389 length = BE_16(hdr->length);
1390 bdesc_length = BE_16(hdr->bdesc_length);
1391
1392 (void) memset((caddr_t)header, 0, sizeof (struct scsi_ms_header_g1));
1393 if (bdesc_length != sizeof (struct block_descriptor) &&
1394 bdesc_length != 0) {
1395 dprintf("\nMode sense(10) page 0x%x: block descriptor "
1396 "length %d incorrect\n", page_code, bdesc_length);
1397 ddump("Mode sense(10):", mode_sense_buf, nbytes);
1398 return (-1);
1399 }
1400 (void) memcpy((caddr_t)header, mode_sense_buf,
1401 (int)(MODE_HEADER_LENGTH_G1 + bdesc_length));
1402 pg = (struct mode_page *)((ulong_t)mode_sense_buf +
1403 MODE_HEADER_LENGTH_G1 + bdesc_length);
1404
1405 if (page_code == MODEPAGE_ALLPAGES) {
1406 /* special case */
1407
1408 (void) memcpy(page_data, (caddr_t)pg,
1409 (length + sizeof (header->ms_header.length)) -
1410 (MODE_HEADER_LENGTH_G1 + bdesc_length));
1411
1412 pc = find_string(page_control_strings, page_control);
1413 dprintf("\nMode sense(10) page 0x%x (%s):\n",
1414 page_code, pc != NULL ? pc : "");
1415 ddump("header:", (caddr_t)header,
1416 MODE_HEADER_LENGTH_G1 + bdesc_length);
1417
1418 ddump("data:", page_data,
1419 (length + sizeof (header->ms_header.length)) -
1420 (MODE_HEADER_LENGTH_G1 + bdesc_length));
1421
1422 return (0);
1423 }
1424
1425 if (pg->code != page_code) {
1426 dprintf("\nMode sense(10) page 0x%x: incorrect page "
1427 "code 0x%x\n", page_code, pg->code);
1428 ddump("Mode sense(10):", mode_sense_buf, nbytes);
1429 return (-1);
1430 }
1431
1432 /*
1433 * Accept up to "page_size" bytes of mode sense data. This allows us to
1434 * accept both CCS and SCSI-2 structures, as long as we request the
1435 * greater of the two.
1436 */
1437 maximum = page_size - sizeof (struct mode_page);
1438 if (((int)pg->length) > maximum) {
1439 dprintf("Mode sense(10) page 0x%x: incorrect page "
1440 "length %d - expected max %d\n",
1441 page_code, pg->length, maximum);
1442 ddump("Mode sense(10):", mode_sense_buf,
1443 nbytes);
1444 return (-1);
1445 }
1446
1447 (void) memcpy(page_data, (caddr_t)pg, MODESENSE_PAGE_LEN(pg));
1448
1449 pc = find_string(page_control_strings, page_control);
1450 dprintf("\nMode sense(10) page 0x%x (%s):\n", page_code,
1451 pc != NULL ? pc : "");
1452 ddump("header:", (caddr_t)header,
1453 sizeof (struct scsi_ms_header_g1));
1454 ddump("data:", page_data, MODESENSE_PAGE_LEN(pg));
1455
1456 return (0);
1457 }
1458
1459 /*
1460 * Execute a uscsi mode select command.
1461 */
1462 int
uscsi_mode_select(int fd,int page_code,int options,caddr_t page_data,int page_size,struct scsi_ms_header * header,void * rqbuf,int * rqblen)1463 uscsi_mode_select(int fd, int page_code, int options, caddr_t page_data,
1464 int page_size, struct scsi_ms_header *header, void *rqbuf, int *rqblen)
1465 {
1466 caddr_t mode_select_buf;
1467 int nbytes;
1468 struct uscsi_cmd ucmd;
1469 union scsi_cdb cdb;
1470 int status;
1471 char *s;
1472
1473 assert(((struct mode_page *)page_data)->ps == 0);
1474 assert(header->ms_header.length == 0);
1475 assert(header->ms_header.device_specific == 0);
1476 assert((options & ~(MODE_SELECT_SP|MODE_SELECT_PF)) == 0);
1477
1478 nbytes = sizeof (struct scsi_ms_header) + page_size;
1479 mode_select_buf = alloca((uint_t)nbytes);
1480
1481 /*
1482 * Build the mode select data out of the header and page data This
1483 * allows us to support devices which return either 0 or 1 block
1484 * descriptors.
1485 */
1486 (void) memset(mode_select_buf, 0, nbytes);
1487 nbytes = MODE_HEADER_LENGTH;
1488 if (header->ms_header.bdesc_length ==
1489 sizeof (struct block_descriptor)) {
1490 nbytes += sizeof (struct block_descriptor);
1491 }
1492
1493 s = find_string(mode_select_strings,
1494 options & (MODE_SELECT_SP|MODE_SELECT_PF));
1495 dprintf("\nMode select page 0x%x%s:\n", page_code,
1496 s != NULL ? s : "");
1497 ddump("header:", (caddr_t)header, nbytes);
1498 ddump("data:", (caddr_t)page_data, page_size);
1499
1500 /*
1501 * Put the header and data together
1502 */
1503 (void) memcpy(mode_select_buf, (caddr_t)header, nbytes);
1504 (void) memcpy(mode_select_buf + nbytes, page_data, page_size);
1505 nbytes += page_size;
1506
1507 /*
1508 * Build and execute the uscsi ioctl
1509 */
1510 (void) memset((char *)&ucmd, 0, sizeof (ucmd));
1511 (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
1512 cdb.scc_cmd = SCMD_MODE_SELECT;
1513 FORMG0COUNT(&cdb, (uchar_t)nbytes);
1514 cdb.cdb_opaque[1] = (uchar_t)options;
1515 ucmd.uscsi_cdb = (caddr_t)&cdb;
1516 ucmd.uscsi_cdblen = CDB_GROUP0;
1517 ucmd.uscsi_bufaddr = mode_select_buf;
1518 ucmd.uscsi_buflen = nbytes;
1519 status = uscsi_cmd(fd, &ucmd, rqbuf, rqblen);
1520
1521 if (status)
1522 dprintf("Mode select page 0x%x failed\n", page_code);
1523
1524 return (status);
1525 }
1526
1527 /*
1528 * Execute a uscsi mode select(10) command.
1529 */
1530 int
uscsi_mode_select_10(int fd,int page_code,int options,caddr_t page_data,int page_size,struct scsi_ms_header_g1 * header,void * rqbuf,int * rqblen)1531 uscsi_mode_select_10(int fd, int page_code, int options,
1532 caddr_t page_data, int page_size, struct scsi_ms_header_g1 *header,
1533 void *rqbuf, int *rqblen)
1534 {
1535 caddr_t mode_select_buf;
1536 int nbytes;
1537 struct uscsi_cmd ucmd;
1538 union scsi_cdb cdb;
1539 int status;
1540 char *s;
1541
1542 assert(((struct mode_page *)page_data)->ps == 0);
1543 assert(header->ms_header.length == 0);
1544 assert(header->ms_header.device_specific == 0);
1545 assert((options & ~(MODE_SELECT_SP|MODE_SELECT_PF)) == 0);
1546
1547 nbytes = sizeof (struct scsi_ms_header_g1) + page_size;
1548 mode_select_buf = alloca((uint_t)nbytes);
1549
1550 /*
1551 * Build the mode select data out of the header and page data
1552 * This allows us to support devices which return either
1553 * 0 or 1 block descriptors.
1554 */
1555 (void) memset(mode_select_buf, 0, nbytes);
1556 nbytes = sizeof (struct mode_header_g1);
1557 if (BE_16(header->ms_header.bdesc_length) ==
1558 sizeof (struct block_descriptor)) {
1559 nbytes += sizeof (struct block_descriptor);
1560 }
1561
1562 /*
1563 * Dump the structures
1564 */
1565 s = find_string(mode_select_strings,
1566 options & (MODE_SELECT_SP|MODE_SELECT_PF));
1567 dprintf("\nMode select(10) page 0x%x%s:\n", page_code,
1568 s != NULL ? s : "");
1569 ddump("header:", (caddr_t)header, nbytes);
1570 ddump("data:", (caddr_t)page_data, page_size);
1571
1572 /*
1573 * Put the header and data together
1574 */
1575 (void) memcpy(mode_select_buf, (caddr_t)header, nbytes);
1576 (void) memcpy(mode_select_buf + nbytes, page_data, page_size);
1577 nbytes += page_size;
1578
1579 /*
1580 * Build and execute the uscsi ioctl
1581 */
1582 (void) memset((char *)&ucmd, 0, sizeof (ucmd));
1583 (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
1584 cdb.scc_cmd = SCMD_MODE_SELECT_G1;
1585 FORMG1COUNT(&cdb, (uint16_t)nbytes);
1586 cdb.cdb_opaque[1] = (uchar_t)options;
1587 ucmd.uscsi_cdb = (caddr_t)&cdb;
1588 ucmd.uscsi_cdblen = CDB_GROUP1;
1589 ucmd.uscsi_bufaddr = mode_select_buf;
1590 ucmd.uscsi_buflen = nbytes;
1591 status = uscsi_cmd(fd, &ucmd, rqbuf, rqblen);
1592
1593 if (status)
1594 dprintf("Mode select(10) page 0x%x failed\n", page_code);
1595
1596 return (status);
1597 }
1598
1599 int
uscsi_log_sense(int fd,int page_code,int page_control,caddr_t page_data,int page_size,void * rqbuf,int * rqblen)1600 uscsi_log_sense(int fd, int page_code, int page_control, caddr_t page_data,
1601 int page_size, void *rqbuf, int *rqblen)
1602 {
1603 caddr_t log_sense_buf;
1604 scsi_log_header_t *hdr;
1605 struct uscsi_cmd ucmd;
1606 union scsi_cdb cdb;
1607 int status;
1608 ushort_t len;
1609 char *pc;
1610
1611 assert(page_size >= 0 && page_size < UINT16_MAX);
1612 assert(page_control == PC_CURRENT || page_control == PC_CHANGEABLE ||
1613 page_control == PC_DEFAULT || page_control == PC_SAVED);
1614
1615 if (page_size < sizeof (scsi_log_header_t))
1616 return (-1);
1617
1618 log_sense_buf = calloc(1, page_size);
1619 if (log_sense_buf == NULL)
1620 return (-1);
1621
1622 /*
1623 * Build and execute the uscsi ioctl
1624 */
1625 (void) memset((char *)&ucmd, 0, sizeof (ucmd));
1626 (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
1627 cdb.scc_cmd = SCMD_LOG_SENSE_G1;
1628 FORMG1COUNT(&cdb, (uint16_t)page_size);
1629 cdb.cdb_opaque[2] = page_control | page_code;
1630 ucmd.uscsi_cdb = (caddr_t)&cdb;
1631 ucmd.uscsi_cdblen = CDB_GROUP1;
1632 ucmd.uscsi_bufaddr = log_sense_buf;
1633 ucmd.uscsi_buflen = page_size;
1634 status = uscsi_cmd(fd, &ucmd, rqbuf, rqblen);
1635 if (status) {
1636 dprintf("Log sense page 0x%x failed\n", page_code);
1637 free(log_sense_buf);
1638 return (-1);
1639 }
1640
1641 /*
1642 * Verify that the returned data looks reasonable, then copy it into the
1643 * user's buffer.
1644 */
1645 hdr = (scsi_log_header_t *)log_sense_buf;
1646
1647 /*
1648 * Ensure we have a host-understandable length field
1649 */
1650 len = BE_16(hdr->lh_length);
1651
1652 if (hdr->lh_code != page_code) {
1653 dprintf("\nLog sense page 0x%x: incorrect page code 0x%x\n",
1654 page_code, hdr->lh_code);
1655 ddump("Log sense:", log_sense_buf, page_size);
1656 free(log_sense_buf);
1657 return (-1);
1658 }
1659
1660 ddump("LOG SENSE RAW OUTPUT", log_sense_buf,
1661 sizeof (scsi_log_header_t) + len);
1662
1663 /*
1664 * Accept up to "page_size" bytes of mode sense data. This allows us to
1665 * accept both CCS and SCSI-2 structures, as long as we request the
1666 * greater of the two.
1667 */
1668 (void) memcpy(page_data, (caddr_t)hdr, len +
1669 sizeof (scsi_log_header_t));
1670
1671 pc = find_string(page_control_strings, page_control);
1672 dprintf("\nLog sense page 0x%x (%s):\n", page_code,
1673 pc != NULL ? pc : "");
1674 ddump("header:", (caddr_t)hdr,
1675 sizeof (scsi_log_header_t));
1676 ddump("data:", (caddr_t)hdr +
1677 sizeof (scsi_log_header_t), len);
1678 free(log_sense_buf);
1679
1680 return (0);
1681 }
1682