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 * 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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