1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
14 */
15
16 #include <stdio.h>
17 #include <unistd.h>
18 #include <ctype.h>
19 #include <sys/types.h>
20 #include <stddef.h>
21 #include <strings.h>
22 #include <sys/list.h>
23 #include <libdevinfo.h>
24 #include <sys/stat.h>
25 #include <sys/scsi/impl/uscsi.h>
26 #include <sys/scsi/generic/inquiry.h>
27 #include <fcntl.h>
28 #include <sys/scsi/scsi.h>
29 #include <assert.h>
30 #include <libdiskmgt.h>
31 #include <libintl.h>
32
33 #define VERSION "1.2"
34
35 #define DISK_DRIVER "sd"
36
37 #define DEVICES_DIR "/devices"
38
39 #define DEFAULT_WAIT 5
40
41 /* download, save and activate microcode in one write buffer command */
42 #define DEFAULT_WB_MODE 0x05
43
44 /* will set timeout to 30 sec for all USCSI commands that we will issue */
45 #define DISK_CMD_TIMEOUT 30
46
47 #define IMPOSSIBLE_RQ_STATUS 0xff
48
49 /* length bigger then this can not be represented by 3 bytes */
50 #define MAX_FW_SIZE_IN_BYTES 16777215
51
52 /* used for mode 7 write buffer (32K) */
53 #define BYTES_IN_CHUNK 32768
54
55 /* 10 is strlen("/dev/rdsk/") */
56 #define CTD_START_IN_PATH(path) (path + 10)
57
58 #define MAX_DISK_LEN 9 /* enough space for a million disk names: sd0-sd999999 */
59 #define INQ_VENDOR_LEN 9 /* struct scsi_inquiry defines these fields at: */
60 #define INQ_MODEL_LEN 17 /* 8, 16 and 4 chars */
61 #define INQ_REV_LEN 5 /* without string terminating zeros */
62
63 #define MAX_INUSE_LEN 255
64
65 /* to reduce clutter and fprintf length */
66 #define _(x) (gettext(x))
67
68 /* struct to hold relevant disk info */
69 typedef struct disk_info {
70 list_node_t link;
71 char device[MAX_DISK_LEN];
72 char vendor[INQ_VENDOR_LEN];
73 char model[INQ_MODEL_LEN];
74 char rev[INQ_REV_LEN];
75 char path[MAXPATHLEN];
76 char inuse_msg[MAX_INUSE_LEN];
77 boolean_t inuse;
78 int bad_info;
79 } disk_info_t;
80
81 /* prints sense data and status */
82 void
83 print_status_and_sense(char *cmd, int rc, int status,
84 struct scsi_extended_sense *sense)
85 {
86 (void) fprintf(stdout, _("%s ioctl() returned: %d status: 0x%02x, "
87 "sense - skey: 0x%02x, asc: 0x%02x, ascq: 0x%02x\n"), cmd, rc,
88 status & STATUS_MASK, sense->es_key, sense->es_add_code,
89 sense->es_qual_code);
90 }
91
92 /* determine if the uscsi cmd failed or not */
93 boolean_t
94 uscsi_parse_status(struct uscsi_cmd *ucmd, int rc, boolean_t verbose)
95 {
96 struct scsi_extended_sense *sense;
97
98 if (rc == -1 && errno == EAGAIN) {
99 if (verbose)
100 (void) fprintf(stderr, _("Drive is temporarily "
101 "unavailable.\n"));
102 return (B_FALSE); /* unavailable */
103 }
104
105 if ((ucmd->uscsi_status & STATUS_MASK) == STATUS_RESERVATION_CONFLICT) {
106 if (verbose)
107 (void) fprintf(stderr, _("Drive is reserved.\n"));
108 return (B_FALSE); /* reserved by another system */
109 }
110
111 if (rc == -1 && ucmd->uscsi_status == 0 && errno == EIO) {
112 if (verbose)
113 (void) fprintf(stderr, _("Drive is unavailable.\n"));
114 return (B_FALSE); /* unavailable */
115 }
116
117 /* check if we have valid sense status */
118 if (ucmd->uscsi_rqstatus == IMPOSSIBLE_RQ_STATUS) {
119 if (verbose) {
120 (void) fprintf(stderr, _("No sense data for command "
121 "0x%02x.\n"), ucmd->uscsi_cdb[0]);
122 }
123 return (B_FALSE);
124 }
125
126 if (ucmd->uscsi_rqstatus != STATUS_GOOD) {
127 if (verbose) {
128 (void) fprintf(stderr, _("Sense status for command "
129 "0x%02x: 0x%02x.\n"), ucmd->uscsi_cdb[0],
130 ucmd->uscsi_rqstatus);
131 }
132 return (B_FALSE);
133 }
134
135 sense = (struct scsi_extended_sense *)ucmd->uscsi_rqbuf;
136 if ((sense->es_key != KEY_RECOVERABLE_ERROR) &&
137 (sense->es_key != KEY_NO_SENSE)) {
138 if (verbose && sense->es_key == KEY_ILLEGAL_REQUEST &&
139 sense->es_add_code == 0x2C && sense->es_qual_code == 0x0) {
140 (void) fprintf(stderr, _(" Illegal Request - Command "
141 "0x%02x sequence error.\n"), ucmd->uscsi_cdb[0]);
142 } else if (verbose && sense->es_key == KEY_ILLEGAL_REQUEST &&
143 sense->es_add_code == 0x24 && sense->es_qual_code == 0x0) {
144 (void) fprintf(stderr, _(" cmd 0x%02x: Illegal Request"
145 " - Invalid field in CDB for command.\n"),
146 ucmd->uscsi_cdb[0]);
147 } else if (verbose && sense->es_key == KEY_UNIT_ATTENTION &&
148 sense->es_add_code == 0x3F && sense->es_qual_code == 0x01) {
149 (void) fprintf(stderr, _(" cmd 0x%02x: Unit Attention "
150 "- Microcode changed.\n"), ucmd->uscsi_cdb[0]);
151 } else if (verbose && sense->es_key == KEY_UNIT_ATTENTION &&
152 sense->es_add_code == 0x29 && sense->es_qual_code == 0x0) {
153 (void) fprintf(stderr, _(" cmd 0x%02x: Unit Attention "
154 "- Reset Occurred.\n"), ucmd->uscsi_cdb[0]);
155 } else if (verbose) {
156 (void) fprintf(stderr, _("Command 0x%02x produced "
157 "sense data that indicated an error.\n"),
158 ucmd->uscsi_cdb[0]);
159 }
160 return (B_FALSE);
161 }
162
163 if (rc == -1 && errno == EIO) {
164 if (verbose) {
165 (void) fprintf(stderr, _("Command 0x%02x resulted in "
166 "I/O error.\n"), ucmd->uscsi_cdb[0]);
167 }
168 return (B_FALSE);
169 }
170
171 if (rc != 0) {
172 if (verbose) {
173 (void) fprintf(stderr, _("cmd 0x%02x: Unknown "
174 "error.\n"), ucmd->uscsi_cdb[0]);
175 }
176 return (B_FALSE);
177 }
178
179 if (verbose) {
180 (void) fprintf(stderr, _("USCSI command 0x%02x completed "
181 "successfully.\n"), ucmd->uscsi_cdb[0]);
182 }
183 return (B_TRUE);
184 }
185
186 /* dump raw hex cdb */
187 void
188 print_cdb(union scsi_cdb *cdb, const char *cmd, int cdb_len)
189 {
190 int i;
191
192 (void) fprintf(stderr, "\n%s CDB (hex): ", cmd);
193 for (i = 0; i < cdb_len; i++)
194 (void) fprintf(stderr, "%02x ", cdb->cdb_opaque[i]);
195 (void) fprintf(stderr, "\n");
196 }
197
198 /* will write microcode located in fw_img to the disk_fd in chunk_size chunks */
199 boolean_t
200 uscsi_write_buffer_mode7(int disk_fd, uint8_t *fw_img,
201 size_t fw_len, struct scsi_extended_sense *sense, boolean_t verbose)
202 {
203 struct uscsi_cmd ucmd;
204 union scsi_cdb cdb;
205 int i;
206 boolean_t rc = B_FALSE;
207 size_t chunk_size = BYTES_IN_CHUNK;
208 size_t xfer_len = chunk_size;
209 size_t remainder = fw_len % xfer_len;
210
211 for (i = 0; (int32_t)(fw_len - (i * chunk_size + remainder)) >= 0;
212 i++) {
213 if (fw_len - i * chunk_size == remainder)
214 xfer_len = remainder;
215 (void) memset(&ucmd, 0, sizeof (struct uscsi_cmd));
216 (void) memset(sense, 0, sizeof (struct scsi_extended_sense));
217 (void) memset(&cdb, 0, sizeof (union scsi_cdb));
218
219 cdb.scc_cmd = SCMD_WRITE_BUFFER;
220 /* bits 0-4 (inclusive) of byte 1 contains mode field */
221 cdb.cdb_opaque[1] = (0x07 & 0x1f);
222 /* bytes 3-5 contain drive buffer offset */
223 cdb.cdb_opaque[3] = (uint8_t)((i * chunk_size >> 16) & 0xff);
224 cdb.cdb_opaque[4] = (uint8_t)((i * chunk_size >> 8) & 0xff);
225 cdb.cdb_opaque[5] = (uint8_t)(i * chunk_size & 0xff);
226 /* bytes 6-8 contain fw chunk length */
227 cdb.cdb_opaque[6] = (uint8_t)((xfer_len >> 16) & 0xff);
228 cdb.cdb_opaque[7] = (uint8_t)((xfer_len >> 8) & 0xff);
229 cdb.cdb_opaque[8] = (uint8_t)(xfer_len & 0xff);
230
231 ucmd.uscsi_cdb = (caddr_t)&cdb;
232 ucmd.uscsi_cdblen = CDB_GROUP1;
233 ucmd.uscsi_flags = USCSI_SILENT | USCSI_WRITE | USCSI_ISOLATE;
234 ucmd.uscsi_flags |= USCSI_RQENABLE | USCSI_DIAGNOSE;
235 ucmd.uscsi_bufaddr = (void *)(fw_img + i * chunk_size);
236 ucmd.uscsi_buflen = xfer_len;
237 ucmd.uscsi_timeout = DISK_CMD_TIMEOUT;
238 ucmd.uscsi_rqbuf = (caddr_t)sense;
239 ucmd.uscsi_rqlen = sizeof (struct scsi_extended_sense);
240 ucmd.uscsi_rqstatus = IMPOSSIBLE_RQ_STATUS;
241
242 if (verbose)
243 print_cdb(&cdb, "write buffer mode 0x07", CDB_GROUP1);
244
245 rc = ioctl(disk_fd, USCSICMD, &ucmd);
246 if (verbose)
247 print_status_and_sense("write buf 0x07", rc,
248 ucmd.uscsi_status, sense);
249
250 rc = uscsi_parse_status(&ucmd, rc, verbose);
251
252 if (sense->es_key == KEY_ILLEGAL_REQUEST &&
253 sense->es_add_code == 0x2C && sense->es_qual_code == 0x0) {
254 (void) fprintf(stderr, _(" Downloading of microcode has"
255 " failed - Command sequence error.\n"));
256 return (B_FALSE);
257 } else if (sense->es_key == KEY_ILLEGAL_REQUEST &&
258 sense->es_add_code == 0x24 && sense->es_qual_code == 0x0) {
259 (void) fprintf(stderr, _(" Downloading of microcode has"
260 " failed - Invalid field in CDB.\n"));
261 return (B_FALSE);
262 }
263 if (sense->es_key == KEY_UNIT_ATTENTION &&
264 sense->es_add_code == 0x3F && sense->es_qual_code == 0x01) {
265 (void) fprintf(stderr, _(" Microcode download "
266 "successful\n"));
267 return (B_TRUE);
268 }
269 }
270 return (rc);
271 }
272
273 /* will write whole microcode located in fw_img to the disk_fd */
274 boolean_t
275 uscsi_write_buffer_mode5(int disk_fd, void * fw_img,
276 size_t fw_len, struct scsi_extended_sense *sense, boolean_t verbose)
277 {
278 struct uscsi_cmd ucmd;
279 union scsi_cdb cdb;
280 int rc;
281
282 (void) memset(&ucmd, 0, sizeof (struct uscsi_cmd));
283 (void) memset(sense, 0, sizeof (struct scsi_extended_sense));
284 (void) memset(&cdb, 0, sizeof (union scsi_cdb));
285
286 cdb.scc_cmd = SCMD_WRITE_BUFFER;
287 /* bits 0-4 (inclusive) of byte 1 contains mode field */
288 cdb.cdb_opaque[1] = (0x05 & 0x1f);
289 /* bytes 6-8 contain fw file length */
290 cdb.cdb_opaque[6] = (uint8_t)((fw_len >> 16) & 0xff);
291 cdb.cdb_opaque[7] = (uint8_t)((fw_len >> 8) & 0xff);
292 cdb.cdb_opaque[8] = (uint8_t)(fw_len & 0xff);
293
294 ucmd.uscsi_cdb = (caddr_t)&cdb;
295 ucmd.uscsi_cdblen = CDB_GROUP1;
296 ucmd.uscsi_flags = USCSI_SILENT | USCSI_WRITE | USCSI_ISOLATE;
297 ucmd.uscsi_flags |= USCSI_RQENABLE | USCSI_DIAGNOSE;
298 ucmd.uscsi_bufaddr = fw_img;
299 ucmd.uscsi_buflen = fw_len;
300 ucmd.uscsi_timeout = DISK_CMD_TIMEOUT;
301 ucmd.uscsi_rqbuf = (caddr_t)sense;
302 ucmd.uscsi_rqlen = sizeof (struct scsi_extended_sense);
303 ucmd.uscsi_rqstatus = IMPOSSIBLE_RQ_STATUS;
304
305 if (verbose)
306 print_cdb(&cdb, "write buffer mode 0x05", CDB_GROUP1);
307
308 rc = ioctl(disk_fd, USCSICMD, &ucmd);
309 if (verbose)
310 print_status_and_sense("write buf 0x05", rc, ucmd.uscsi_status,
311 sense);
312
313 rc = uscsi_parse_status(&ucmd, rc, verbose);
314
315 if (sense->es_key == KEY_ILLEGAL_REQUEST &&
316 sense->es_add_code == 0x2C && sense->es_qual_code == 0x0) {
317 (void) fprintf(stderr, _(" Downloading of microcode has failed"
318 " - Command sequence error.\n"));
319 rc = B_FALSE;
320 } else if (sense->es_key == KEY_ILLEGAL_REQUEST &&
321 sense->es_add_code == 0x24 && sense->es_qual_code == 0x0) {
322 (void) fprintf(stderr, _(" Downloading of microcode has failed"
323 " - Invalid field in CDB.\n"));
324 rc = B_FALSE;
325 } else if (sense->es_key == KEY_UNIT_ATTENTION &&
326 sense->es_add_code == 0x3F && sense->es_qual_code == 0x01) {
327 (void) fprintf(stderr, _(" Microcode download successful\n"));
328 rc = B_TRUE;
329 }
330
331 return (rc);
332 }
333
334 /* Issue TUR command to device identified by file descriptor disk_fd */
335 boolean_t
336 uscsi_test_unit_ready(int disk_fd, struct scsi_extended_sense *sense,
337 boolean_t verbose)
338 {
339 struct uscsi_cmd ucmd;
340 int rc;
341 union scsi_cdb cdb;
342
343 (void) memset(&ucmd, 0, sizeof (struct uscsi_cmd));
344 (void) memset(sense, 0, sizeof (struct scsi_extended_sense));
345 (void) memset(&cdb, 0, sizeof (union scsi_cdb));
346
347 cdb.scc_cmd = SCMD_TEST_UNIT_READY;
348
349 ucmd.uscsi_cdb = (caddr_t)&cdb;
350 ucmd.uscsi_cdblen = CDB_GROUP0;
351 ucmd.uscsi_flags = USCSI_SILENT | USCSI_DIAGNOSE | USCSI_RQENABLE;
352 ucmd.uscsi_timeout = DISK_CMD_TIMEOUT;
353 ucmd.uscsi_rqbuf = (caddr_t)sense;
354 ucmd.uscsi_rqlen = sizeof (struct scsi_extended_sense);
355 ucmd.uscsi_rqstatus = IMPOSSIBLE_RQ_STATUS;
356
357 if (verbose)
358 print_cdb(&cdb, "test unit ready", CDB_GROUP0);
359
360 rc = ioctl(disk_fd, USCSICMD, &ucmd);
361 if (verbose)
362 print_status_and_sense("test unit ready", rc, ucmd.uscsi_status,
363 sense);
364
365 return (uscsi_parse_status(&ucmd, rc, verbose));
366 }
367
368 /* Execute a uscsi inquiry command and put the resulting data into inqbuf. */
369 boolean_t
370 uscsi_inquiry(int disk_fd, struct scsi_inquiry *inqbuf,
371 struct scsi_extended_sense *sense, boolean_t verbose)
372 {
373 struct uscsi_cmd ucmd;
374 union scsi_cdb cdb;
375 int rc;
376
377 (void) memset(inqbuf, 0, sizeof (struct scsi_inquiry));
378 (void) memset(sense, 0, sizeof (struct scsi_extended_sense));
379 (void) memset(&ucmd, 0, sizeof (ucmd));
380 (void) memset(&cdb, 0, sizeof (union scsi_cdb));
381
382 cdb.scc_cmd = SCMD_INQUIRY;
383 /* bytes 3-4 contain data-in buf len */
384 cdb.cdb_opaque[3] = (uint8_t)((sizeof (struct scsi_inquiry) >> 8) &
385 0xff);
386 cdb.cdb_opaque[4] = (uint8_t)((sizeof (struct scsi_inquiry)) & 0xff);
387
388 ucmd.uscsi_cdb = (caddr_t)&cdb;
389 ucmd.uscsi_cdblen = CDB_GROUP0;
390 ucmd.uscsi_flags = USCSI_READ | USCSI_SILENT | USCSI_ISOLATE;
391 ucmd.uscsi_flags |= USCSI_RQENABLE | USCSI_DIAGNOSE;
392 ucmd.uscsi_bufaddr = (caddr_t)inqbuf;
393 ucmd.uscsi_buflen = sizeof (struct scsi_inquiry);
394 ucmd.uscsi_timeout = DISK_CMD_TIMEOUT;
395 ucmd.uscsi_rqbuf = (caddr_t)sense;
396 ucmd.uscsi_rqlen = sizeof (struct scsi_extended_sense);
397 ucmd.uscsi_rqstatus = IMPOSSIBLE_RQ_STATUS;
398
399 if (verbose)
400 print_cdb(&cdb, "inquiry", CDB_GROUP1);
401
402 rc = ioctl(disk_fd, USCSICMD, &ucmd);
403 if (verbose)
404 print_status_and_sense("inquiry", rc, ucmd.uscsi_status, sense);
405
406 return (uscsi_parse_status(&ucmd, rc, verbose));
407 }
408
409 /* Issue start command to device identified by file descriptor disk_fd */
410 boolean_t
411 uscsi_start_unit(int disk_fd, struct scsi_extended_sense *sense,
412 boolean_t verbose)
413 {
414 struct uscsi_cmd ucmd;
415 union scsi_cdb cdb;
416 int rc;
417
418 (void) memset(&ucmd, 0, sizeof (struct uscsi_cmd));
419 (void) memset(&cdb, 0, sizeof (union scsi_cdb));
420 (void) memset(sense, 0, sizeof (struct scsi_extended_sense));
421
422 cdb.scc_cmd = SCMD_START_STOP;
423 /* bit 0 of byte 4 - start bit. bits 4-7 - power condition field */
424 cdb.cdb_opaque[4] = 0x01;
425
426 ucmd.uscsi_cdb = (caddr_t)&cdb;
427 ucmd.uscsi_cdblen = CDB_GROUP0;
428 ucmd.uscsi_flags = USCSI_SILENT | USCSI_DIAGNOSE | USCSI_RQENABLE;
429 ucmd.uscsi_flags |= USCSI_ISOLATE;
430 ucmd.uscsi_timeout = DISK_CMD_TIMEOUT;
431 ucmd.uscsi_rqbuf = (caddr_t)sense;
432 ucmd.uscsi_rqlen = sizeof (struct scsi_extended_sense);
433 ucmd.uscsi_rqstatus = IMPOSSIBLE_RQ_STATUS;
434
435 if (verbose)
436 print_cdb(&cdb, "start", CDB_GROUP0);
437
438 rc = ioctl(disk_fd, USCSICMD, &ucmd);
439 if (verbose)
440 print_status_and_sense("start", rc, ucmd.uscsi_status, sense);
441
442 return (uscsi_parse_status(&ucmd, rc, verbose));
443 }
444
445 /* copy chars to out without trailing and leading "space" chars */
446 void
447 mem_trim_and_cpy(char *out, const char *buf, size_t buf_len) {
448
449 while (buf_len != 0 && isspace(*buf) != 0) {
450 /* ignore leading space(s) */
451 buf++;
452 buf_len--;
453 }
454 while (buf_len && isspace(buf[buf_len - 1]))
455 buf_len--; /* ignore trailing space(s) */
456
457 (void) memcpy(out, buf, buf_len);
458 out[buf_len] = '\0'; /* stringify */
459 }
460
461 /* given a disk - check if it's in use */
462 /* define NOINUSE_CHECK environment variable to turn of disk inuse check. */
463 void
464 set_disk_inuse(disk_info_t *disk, boolean_t verbose)
465 {
466 char *msg, *slice, *character, *plz;
467 char dev[MAXPATHLEN];
468 int error = 0;
469 int i;
470 dm_descriptor_t *slices = NULL;
471 /*
472 * Let libdiskmgt think we are the zpool consumer, meaning slices will
473 * be considered "inuse" if they have a filesystem or an exported zpool
474 */
475 dm_who_type_t who = DM_WHO_ZPOOL;
476
477 /* need to give dm_get_slices() a "whole" disk name, ie - c#t...d#p0 */
478 (void) strncpy(dev, CTD_START_IN_PATH(disk->path), MAXPATHLEN);
479 if ((character = strrchr(dev, 'd')) != NULL) {
480 character++;
481 while (isdigit(*character))
482 character++;
483 *character = '\0'; /* terminate the string after d# */
484 }
485
486 disk->inuse = B_FALSE;
487 disk->inuse_msg[0] = '\0';
488 dm_get_slices(dev, &slices, &error);
489 if (error != 0) {
490 if (verbose) {
491 (void) fprintf(stderr, _("dm_get_slices() failed: "
492 "%s.\n Marking drive in use.\n"), strerror(error));
493 }
494 /*
495 * Marking disk as "in use" on errors since it's
496 * better to not touch a disk that MAY be in use by something.
497 */
498 disk->inuse = B_TRUE;
499 return;
500 }
501
502 /* loop through slices - set inuse */
503 for (i = 0; slices[i]; i++) {
504 slice = dm_get_name(slices[i], &error);
505 if (dm_inuse(slice, &msg, who, &error) != 0 || error != 0) {
506 /*
507 * Marking disk as "in use" even if we got here by way
508 * of (error != 0) since it's better to not touch a
509 * drive that MAY be in use by something. User can
510 * still use the disk by defining the NOINUSE_CHECK
511 * environment variable which will skip "in use"
512 * checking.
513 */
514 disk->inuse = B_TRUE;
515 if (error == 0) {
516 if (verbose) {
517 (void) fprintf(stderr, _("Drive '%s' is"
518 " in use: %s"), disk->device, msg);
519 }
520 if (strlen(msg) < MAX_INUSE_LEN) {
521 (void) strncpy(disk->inuse_msg, msg,
522 MAX_INUSE_LEN);
523 /* Remove 'Please see ...' from msg */
524 plz = strstr(disk->inuse_msg, "Please");
525 if (plz != NULL) {
526 plz--;
527 *plz = '\0';
528 }
529 }
530 free(msg);
531 } else if (verbose) {
532 (void) fprintf(stderr, _("dm_inuse() failed: "
533 "%s.\nMarking drive in use.\n"),
534 strerror(error));
535 }
536 dm_free_name(slice);
537 break;
538 }
539
540 dm_free_name(slice);
541 }
542 dm_free_descriptors(slices);
543 }
544
545 /* obtain devlink (c*d*t* path) from /device path */
546 int
547 devlink_walk_cb(di_devlink_t devlink, void *arg)
548 {
549 const char *path;
550 if ((path = di_devlink_path(devlink)) != NULL) {
551 assert(strlen(path) < MAXPATHLEN);
552 (void) strncpy(arg, path, strlen(path)+1);
553 } else {
554 (void) strcpy(arg, "unknown_device_path");
555 }
556
557 return (DI_WALK_TERMINATE);
558 }
559
560 /* get relevant disk info for char devices */
561 boolean_t
562 set_disk_info(const di_node_t *node, disk_info_t *disk, boolean_t verbose)
563 {
564 int instance;
565 char *m_path = NULL;
566 di_minor_t di_minor;
567 di_devlink_handle_t devlink_h;
568 struct scsi_inquiry inq;
569 int disk_fd;
570 struct scsi_extended_sense sense;
571
572 disk->bad_info = 1;
573
574 /* populate device field with sd */
575 assert(MAX_DISK_LEN > strlen(DISK_DRIVER) + sizeof (int) + 1);
576 (void) strncpy(disk->device, DISK_DRIVER, strlen(DISK_DRIVER)+1);
577
578 instance = di_instance(*node);
579 if (instance == -1) {
580 (void) fprintf(stderr, _("Could not get the instance number "
581 "of the device - di_instance() failed\n"));
582 (void) strcpy(disk->device + strlen(DISK_DRIVER), "?");
583 disk->bad_info = -1;
584 return (B_FALSE);
585 }
586
587 /* append instance # to device name making sd# */
588 (void) snprintf(disk->device + strlen(DISK_DRIVER),
589 MAX_DISK_LEN - strlen(DISK_DRIVER), "%d", instance);
590
591 /* take a snapshot of devlinks bound to sd driver */
592 if ((devlink_h = di_devlink_init(DISK_DRIVER, DI_MAKE_LINK)) ==
593 DI_LINK_NIL) {
594 (void) fprintf(stderr, _("di_link_init() failed for drive "
595 "%s.\n"), disk->device);
596 return (B_FALSE);
597 }
598 /* traverse devlink minor nodes */
599 di_minor = di_minor_next(*node, DI_MINOR_NIL);
600 for (; di_minor != DI_MINOR_NIL;
601 di_minor = di_minor_next(*node, di_minor)) {
602 if (di_minor_spectype(di_minor) != S_IFCHR)
603 continue; /* skip minor nodes that are not char devs */
604
605 /* path to minor node (/devices/...) */
606 if ((m_path = di_devfs_minor_path(di_minor)) == NULL) {
607 (void) fprintf(stderr, _("couldn't get path for a "
608 "minor node of drive '%s' - di_devfs_minor_path() "
609 "failed.\n"), disk->device);
610 break;
611 }
612 if (strstr(m_path, ":q,raw") == NULL) {
613 di_devfs_path_free(m_path);
614 continue; /* skip minor nodes that aren't partition 0 */
615 }
616
617 /*
618 * lookup /dev/rdsk/ path for minor node defined by m_path
619 * The reason we're not just using the /devices m_path for
620 * WRITE_BUFFER is we need the c#t...d#p0 lun to display to
621 * the user anyway
622 */
623 if (di_devlink_walk(devlink_h, "^rdsk/", m_path,
624 DI_PRIMARY_LINK, disk->path, devlink_walk_cb) == -1) {
625 (void) fprintf(stderr, _("di_devlink_walk() failed for"
626 " disk '%s'\n"), disk->device);
627 di_devfs_path_free(m_path);
628 break;
629 }
630 di_devfs_path_free(m_path);
631
632 /*
633 * check that the devlink (/dev/rdsk/) path was found by the
634 * above
635 */
636 if (strcmp(disk->path, "unknown_device_path") == 0) {
637 (void) fprintf(stderr, _("di_devlink_path() for '%s'"
638 " failed"), disk->device);
639 break;
640 }
641
642 disk_fd = open(disk->path, O_RDONLY | O_NDELAY);
643 if (disk_fd < 0) {
644 (void) fprintf(stderr, _("open() on drive '%s', dev "
645 "path '%s' failed: %s\n"), disk->device,
646 disk->path, strerror(errno));
647 break;
648 }
649
650 /* found p0 for a char disk, now get vendor, model, and rev */
651 if (uscsi_inquiry(disk_fd, &inq, &sense, verbose)) {
652 disk->bad_info = 0;
653 mem_trim_and_cpy(disk->vendor, inq.inq_vid,
654 sizeof (inq.inq_vid));
655 mem_trim_and_cpy(disk->model, inq.inq_pid,
656 sizeof (inq.inq_pid));
657 mem_trim_and_cpy(disk->rev, inq.inq_revision,
658 sizeof (inq.inq_revision));
659 set_disk_inuse(disk, verbose);
660 } else {
661 if (verbose) {
662 (void) fprintf(stderr, _("uscsi_inquiry() "
663 "failed, drive '%s' will be skipped\n"),
664 disk->device);
665 }
666 }
667 (void) close(disk_fd);
668 break; /* have found slice 0 for a char device, can now exit */
669 }
670 (void) di_devlink_fini(&devlink_h);
671
672 return (disk->bad_info == 0 ? B_TRUE : B_FALSE);
673 }
674
675 /* kernel device tree walk */
676 int
677 walk_nodes(di_node_t *root_node, list_t *disks, boolean_t verbose)
678 {
679 int *dtype;
680 int disks_found = 0;
681 disk_info_t *disk;
682 di_node_t node;
683
684 (void) fprintf(stdout, _("Searching for drives, found: "));
685 /* start at root node and walk all sd nodes */
686 node = di_drv_first_node(DISK_DRIVER, *root_node);
687 for (; node != DI_NODE_NIL; node = di_drv_next_node(node)) {
688 (void) fflush(stdout);
689 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
690 "inquiry-device-type", &dtype) >= 0) {
691 if (((*dtype) & DTYPE_MASK) != DTYPE_DIRECT)
692 /* skip devs that are not type 0 (not disks) */
693 continue;
694 } else {
695 if (verbose) {
696 (void) fprintf(stderr,
697 _("di_prop_lookup_ints(inquiry-device-type)"
698 " failed, ignoring this node\n"));
699 }
700 continue;
701 }
702 disks_found++;
703 if ((disk = (disk_info_t *)malloc(sizeof (disk_info_t))) ==
704 NULL) {
705 if (verbose) {
706 (void) fprintf(stderr, _("malloc(%d) "
707 "failed\n"), sizeof (disk_info_t));
708 }
709 return (-disks_found);
710 }
711 list_insert_tail(disks, disk); /* preserves discovery order */
712 if (set_disk_info(&node, disk, verbose) || verbose) {
713 (void) fprintf(stdout, "%s ", disk->device);
714 if ((disks_found % 10) == 0)
715 (void) fprintf(stdout, "\n");
716 } else {
717 disks_found--;
718 }
719 }
720 (void) fprintf(stdout, _("(%d total).\n"), disks_found);
721
722 return (disks_found);
723 }
724
725 /* allocate space and read into it the fw image */
726 char *
727 get_fw_image(const char *fw_file, size_t *fw_len, boolean_t verbose)
728 {
729 struct stat stat_buf;
730 char *fw_buf = NULL;
731 FILE *fw_stream = fopen(fw_file, "r");
732
733 *fw_len = 0;
734 if (fw_stream != NULL) {
735 if (fstat(fileno(fw_stream), &stat_buf) == -1) {
736 /* fstat failed */
737 if (verbose) {
738 (void) fprintf(stderr, _("fstat() on file '%s'"
739 " failed\n"), fw_file);
740 }
741 } else if (stat_buf.st_size < 1 ||
742 stat_buf.st_size > MAX_FW_SIZE_IN_BYTES) {
743 /*
744 * fw file empty or too big to fit into
745 * 1 SCSI WRITEBUFFER buffer
746 */
747 (void) fprintf(stderr, _("'%s' is of size %lld bytes."
748 "\ndisk-dmc v" VERSION " does not support fw files"
749 " bigger then %d bytes or smaller then 1 byte.\n"),
750 fw_file, (intmax_t)stat_buf.st_size,
751 MAX_FW_SIZE_IN_BYTES);
752 } else { /* alloc buffer to hold fw img */
753 if ((fw_buf = malloc(stat_buf.st_size)) != NULL)
754 *fw_len = stat_buf.st_size;
755 else if (verbose)
756 (void) fprintf(stderr, _("malloc() failed\n"));
757 }
758 } else { /* fopen failed */
759 if (verbose) {
760 (void) fprintf(stderr, _("fopen() on fw image '%s' "
761 "failed: %s\n"), fw_file, strerror(errno));
762 }
763 }
764
765 if (fw_buf != NULL && fread(fw_buf, 1, *fw_len, fw_stream) != *fw_len) {
766 free(fw_buf);
767 fw_buf = NULL;
768 *fw_len = 0;
769 if (verbose)
770 (void) fprintf(stderr, _("fread() failed\n"));
771 }
772
773 if (fw_stream != NULL)
774 (void) fclose(fw_stream);
775
776 return (fw_buf);
777 }
778
779 /*
780 * print a dot for every second we wait as to not look like process is hanging
781 */
782 void
783 wait_print(int sec)
784 {
785 (void) fprintf(stdout, _(" waiting for %d seconds:\n"), sec);
786 while (sec--) {
787 (void) fprintf(stdout, " .");
788 (void) fflush(stdout);
789 if (! (sec % 30))
790 (void) fprintf(stdout, "\n");
791 (void) sleep(1);
792 }
793 }
794
795 /* perform some housekeeping before write buffer */
796 boolean_t
797 prep_disk_for_fw_dl(int disk_fd, int wait, boolean_t verbose)
798 {
799 struct scsi_extended_sense sense;
800
801 if (verbose)
802 (void) fprintf(stderr, _(" before dl prep\n"));
803
804 sync(); /* schedule in flight stuff to be put onto disk before dmc */
805
806 if (uscsi_test_unit_ready(disk_fd, &sense, verbose)) {
807 return (B_TRUE);
808 } else {
809 if (sense.es_key == KEY_NOT_READY &&
810 sense.es_add_code == 0x04 && sense.es_qual_code == 0x02) {
811 /* above sense data indicates disk needs a start cmd */
812 (void) uscsi_start_unit(disk_fd, &sense, verbose);
813 wait_print(wait);
814 } else if (sense.es_key == KEY_UNIT_ATTENTION &&
815 sense.es_add_code == 0x29 && sense.es_qual_code == 0x0) {
816 wait_print(wait); /* reset occurred */
817 } else if (sense.es_key == KEY_NOT_READY &&
818 sense.es_add_code == 0x04 && sense.es_qual_code == 0x0) {
819 wait_print(wait); /* becoming ready */
820 } else {
821 wait_print(wait);
822 }
823 }
824
825 /* trying again */
826 return (uscsi_test_unit_ready(disk_fd, &sense, verbose));
827 }
828
829 /* test disk after buffer write to make sure it's ready for operation */
830 boolean_t
831 after_dl_prep(int disk_fd, int wait, boolean_t verbose)
832 {
833 struct scsi_extended_sense sense;
834
835 if (verbose)
836 (void) fprintf(stderr, _(" after dl prep\n"));
837 wait_print(wait); /* give disk time to become ready after dmc */
838
839 if (uscsi_test_unit_ready(disk_fd, &sense, verbose)) {
840 return (B_TRUE);
841 } else {
842 if (sense.es_key == KEY_NOT_READY &&
843 sense.es_add_code == 0x04 && sense.es_qual_code == 0x02) {
844 /* above sense data indicates disk needs a start cmd */
845 (void) uscsi_start_unit(disk_fd, &sense, verbose);
846 wait_print(wait);
847 } else if (sense.es_key == KEY_UNIT_ATTENTION &&
848 sense.es_add_code == 0x63 && sense.es_qual_code == 0x01) {
849 wait_print(wait); /* microcode changed */
850 } else if (sense.es_key == KEY_UNIT_ATTENTION &&
851 sense.es_add_code == 0x29 && sense.es_qual_code == 0x0) {
852 wait_print(wait); /* reset occurred */
853 } else if (sense.es_key == KEY_NOT_READY &&
854 sense.es_add_code == 0x04 && sense.es_qual_code == 0x0) {
855 wait_print(wait); /* becoming ready */
856 } else if (sense.es_key == KEY_UNIT_ATTENTION &&
857 sense.es_add_code == 0x3F && sense.es_qual_code == 0x01) {
858 (void) uscsi_test_unit_ready(disk_fd, &sense, verbose);
859 } else {
860 wait_print(wait);
861 }
862 }
863
864 /* trying again */
865 return (uscsi_test_unit_ready(disk_fd, &sense, verbose));
866 }
867
868 /* check if the bad_info field is set, if so print the error */
869 boolean_t
870 has_bad_info(disk_info_t *disk, boolean_t verbose)
871 {
872 if (disk->bad_info == -1) {
873 if (verbose) {
874 (void) fprintf(stderr, _("Encountered a device "
875 "without an instance, it will be skipped.\n"));
876 }
877 return (B_TRUE);
878 } else if (disk->bad_info == 1) {
879 if (verbose) {
880 (void) fprintf(stderr, _("Was not able to get all of "
881 "the needed info for drive '%s', it will be "
882 "skipped.\n"), disk->device);
883 }
884 return (B_TRUE);
885 }
886
887 return (B_FALSE);
888 }
889
890 /* print info for a single disk entry */
891 void
892 print_disk(const disk_info_t *disk, boolean_t verbose)
893 {
894 if (verbose)
895 (void) fprintf(stdout, "%-8s %-35s %-9s %-17s %-6s %s\n",
896 disk->device, CTD_START_IN_PATH(disk->path), disk->vendor,
897 disk->model, disk->rev, disk->inuse_msg);
898 else
899 (void) fprintf(stdout, "%-8s %-35s %-9s %-17s %-6s\n",
900 disk->device, CTD_START_IN_PATH(disk->path), disk->vendor,
901 disk->model, disk->rev);
902 }
903
904 /* print header and info for all disks */
905 void
906 print_disks(list_t *disks, boolean_t verbose)
907 {
908 disk_info_t *disk;
909 if (verbose)
910 (void) fprintf(stdout, "%-8s %-35s %-9s %-17s %-6s %s\n"
911 "==============================================="
912 "========================================\n",
913 "DEVICE", "LUN", "VENDOR", "MODEL", "FW REV", "INUSE");
914 else
915 (void) fprintf(stdout, "%-8s %-35s %-9s %-17s %-6s\n"
916 "========================================"
917 "=======================================\n",
918 "DEVICE", "LUN", "VENDOR", "MODEL", "FW REV");
919 for (disk = list_head(disks); disk; disk = list_next(disks, disk))
920 if (!has_bad_info(disk, verbose))
921 print_disk(disk, verbose);
922 }
923
924 /* perform fw update on one disk */
925 boolean_t
926 process_disk(disk_info_t *disk, uint8_t mode, char *fw_buf, size_t fw_len,
927 int wait, boolean_t verbose)
928 {
929 int disk_fd;
930 struct scsi_extended_sense sense;
931 boolean_t status;
932 struct scsi_inquiry inq;
933
934 (void) fprintf(stdout, _("Processing drive %s:\n\n"), disk->device);
935
936 if (disk == DI_NODE_NIL) {
937 (void) fprintf(stdout, _("Drive was not found, will not "
938 "continue.\n"));
939 return (B_FALSE);
940 }
941 assert(fw_buf != NULL);
942 assert(fw_len > 0);
943
944 if (disk->inuse) {
945 (void) fprintf(stdout, _("Drive '%s' is in use, will not "
946 "continue.\n"),
947 disk->device);
948 return (B_FALSE);
949 }
950
951 disk_fd = open(disk->path, O_RDWR | O_NONBLOCK);
952 if (disk_fd < 0) {
953 (void) fprintf(stderr, _("open() on drive '%s' failed: %s\n"),
954 disk->path, strerror(errno));
955 return (B_FALSE);
956 }
957
958 status = prep_disk_for_fw_dl(disk_fd, wait, verbose);
959 if (verbose) {
960 (void) fprintf(stderr, _(" Prep drive status: %s\n"),
961 status ? _("success") : _("failure"));
962 }
963
964 if (!status) {
965 (void) close(disk_fd);
966 return (B_FALSE);
967 }
968
969 /* write the fw onto the disk */
970 if (mode == 0x05) {
971 status = uscsi_write_buffer_mode5(disk_fd, fw_buf, fw_len,
972 &sense, verbose);
973 } else if (mode == 0x07) {
974 status = uscsi_write_buffer_mode7(disk_fd, (uint8_t *)fw_buf,
975 fw_len, &sense, verbose);
976 } else {
977 (void) fprintf(stderr, _(" unexpected write buffer mode\n"));
978 (void) close(disk_fd);
979 return (B_FALSE);
980 }
981
982 if (after_dl_prep(disk_fd, wait, verbose) && verbose) {
983 (void) fprintf(stderr, _(" After fw download drive seems to be "
984 "online.\n"));
985 } else if (verbose) {
986 (void) fprintf(stderr, _(" After fw download drive doesn't "
987 "seem to be online.\n"));
988 }
989
990 (void) fprintf(stdout, _("\nFlashing %s firmware: %s\n"), disk->device,
991 status ? _("successful") : _("failed"));
992
993 if (verbose) { /* get and display the "new" disk revision */
994 if (uscsi_inquiry(disk_fd, &inq, &sense, verbose)) {
995 mem_trim_and_cpy(disk->rev, inq.inq_revision,
996 sizeof (inq.inq_revision));
997 print_disk(disk, verbose);
998 } else {
999 (void) fprintf(stderr, _("uscsi_inquiry() failed on "
1000 "disk '%s', can't get revision.\n"), disk->device);
1001 }
1002 }
1003 (void) close(disk_fd);
1004
1005 return (status);
1006 }
1007
1008 /* get a path to the drive that we can feed to di_lookup_node() */
1009 boolean_t
1010 massage_drive_path(const char * const user_path, char *trgt_path, boolean_t
1011 verbose)
1012 {
1013 char *cp;
1014 size_t len;
1015
1016 if (strlen(user_path) >= PATH_MAX) {
1017 (void) fprintf(stderr, _("drive name given (%s) is too "
1018 "long (%d)\n"), user_path, MAXPATHLEN);
1019 return (B_FALSE);
1020 }
1021
1022 if (strstr(user_path, "/dev/rdsk/") != NULL) {
1023 if (realpath(user_path, trgt_path) == NULL) {
1024 (void) fprintf(stderr, _("error processing drive "
1025 "%s: %s.\n"), user_path, strerror(errno));
1026 return (B_FALSE);
1027 }
1028 if (verbose) {
1029 (void) fprintf(stderr, _("translated drive path %s to"
1030 " %s.\n"), user_path, trgt_path);
1031 }
1032 } else {
1033 (void) fprintf(stderr, _("path to drive must include "
1034 "'/dev/rdsk/'\n"));
1035 return (B_FALSE);
1036 }
1037
1038 if (verbose && strstr(trgt_path, "/scsi_vhci/") != NULL)
1039 (void) fprintf(stderr, _("warning drive %s is multipathed.\n"),
1040 trgt_path);
1041
1042
1043 /* Remove devices prefix (if any) */
1044 len = strlen(DEVICES_DIR);
1045 if (strncmp(trgt_path, DEVICES_DIR "/", len + strlen("/")) == 0) {
1046 (void) memmove(trgt_path, trgt_path + len,
1047 strlen(trgt_path + len) + 1);
1048 }
1049
1050 /* Remove dynamic component if any */
1051 if ((cp = strstr(trgt_path, "::")) != NULL) {
1052 *cp = '\0';
1053 }
1054
1055 /* Remove minor name (if any) */
1056 if ((cp = strrchr(trgt_path, ':')) != NULL) {
1057 *cp = '\0';
1058 }
1059
1060 return (B_TRUE);
1061 }
1062
1063 void
1064 usage(const char *prog_name)
1065 {
1066 (void) fprintf(stdout, _("%s v" VERSION "\n"
1067 "\nUsage: %s <-d /dev/rdsk/c#t...d# | "
1068 "-m model_string> <-p /path/to/fw/img> <-h> <-l> <-v> <-s 5|7> "
1069 "<-w #sec>\n"
1070 "\t-h\tPrint this help message and exit.\n"
1071 "\t-l\tList discovered drives.\n"
1072 "\t-d dsk\tSpecify single drive to use for firmware "
1073 "download in /dev/rdsk/c#t...d#p0 format.\n"
1074 "\t-s 5|7\tSpecify which write buffer mode to use. "
1075 "%d is the default.\n"
1076 "\t-m str\tSpecify model of drive(s) to download "
1077 "firmware to. Drives whose model exactly matches model str "
1078 "provided will be upgraded.\n\t-p img\tPath to the firmware file "
1079 "that will be downloaded onto the specified drive(s).\n"
1080 "\t-v\tVerbose mode: turns on extra debug messages.\n"
1081 "\t-w #sec\tNumber of seconds to delay checking "
1082 "for drive readiness after downloading the firmware. Also "
1083 "used for drive preparation timeouts. %d is the default.\n"),
1084 prog_name, prog_name, DEFAULT_WB_MODE, DEFAULT_WAIT);
1085 exit(1);
1086 }
1087
1088
1089 int
1090 main(int argc, char *argv[])
1091 {
1092 di_node_t root_node, node;
1093 int opt, i, wait = DEFAULT_WAIT;
1094 uint8_t mode = DEFAULT_WB_MODE;
1095 boolean_t ctd = B_FALSE, list = B_FALSE, model = B_FALSE;
1096 boolean_t change_mode = B_FALSE, user_wait = B_FALSE, verbose = B_FALSE;
1097 char *user_drive = NULL, *fw_img = NULL;
1098 size_t fw_len;
1099 char dl_fw_to_disk[MAXPATHLEN];
1100 char dl_fw_to_model[INQ_MODEL_LEN];
1101 list_t disks; /* list of disk_info_t structures */
1102 disk_info_t *disk = NULL;
1103
1104 if (geteuid() > 1) {
1105 (void) fprintf(stderr, _("This utility requires root level "
1106 "permissions due to the use of USCSI(7i).\n"));
1107 return (1);
1108 }
1109
1110 /* if only verbose or no command line args received do a list */
1111 if (argc == 1 || (argc == 2 && strcmp("-v", argv[1]) == 0))
1112 list = B_TRUE;
1113
1114 while ((opt = getopt(argc, argv, "d:p:hils:m:vw:")) != -1) {
1115 switch (opt) {
1116 case 'd': /* single disk mode */
1117 user_drive = optarg;
1118 ctd = B_TRUE;
1119 break;
1120 case 'p': /* fw file */
1121 if (strlen(optarg) < MAXPATHLEN) {
1122 fw_img = get_fw_image(optarg, &fw_len, verbose);
1123 if (fw_img == NULL) {
1124 (void) fprintf(stderr, _("could not use"
1125 " fw file '%s', check file size, "
1126 "permissions and/or existence.\n"),
1127 optarg);
1128 usage(argv[0]);
1129 }
1130 } else {
1131 (void) fprintf(stderr, _("fw path provided to"
1132 " '-%c' is too long.\n"), optopt);
1133 usage(argv[0]);
1134 }
1135 break;
1136 case 'h': /* print usage and exit */
1137 usage(argv[0]);
1138 break;
1139 case 'i': /* ignore the lagacy -i flag */
1140 break;
1141 case 'l': /* list drives */
1142 list = B_TRUE;
1143 break;
1144 case 's': /* change mode from default */
1145 if (atoi(optarg) == 7)
1146 mode = 0x07;
1147 else if (atoi(optarg) == 5)
1148 mode = 0x05;
1149 else {
1150 (void) fprintf(stderr, _("mode provided to "
1151 "'-%c' (%s) is not supported. Supported "
1152 "modes are 5 and 7.\n"), optopt, optarg);
1153 usage(argv[0]);
1154 }
1155 change_mode = B_TRUE;
1156 break;
1157 case 'm': /* only flash this model */
1158 if (strlen(optarg) < INQ_MODEL_LEN) {
1159 (void) strncpy(dl_fw_to_model, optarg,
1160 strlen(optarg) + 1);
1161 model = B_TRUE;
1162 } else {
1163 (void) fprintf(stderr, _("model provided to "
1164 "'-%c' is too long (%d)\n"), optopt,
1165 strlen(optarg));
1166 usage(argv[0]);
1167 }
1168 break;
1169 case 'v': /* verbose mode */
1170 verbose = B_TRUE;
1171 break;
1172 case 'w': /* wait time after dl */
1173 wait = atoi(optarg);
1174 if (wait < 0 || wait > 300) {
1175 (void) fprintf(stderr, _("'-%c %d' - time to "
1176 "wait after microcode download is too long"
1177 " ( > 5min ) or negative.\n"),
1178 optopt, wait);
1179 usage(argv[0]);
1180 }
1181 user_wait = B_TRUE;
1182 break;
1183 case ':':
1184 case '?':
1185 usage(argv[0]);
1186 break;
1187 }
1188 }
1189
1190 for (i = optind; i < argc; i++) {
1191 (void) printf(_("Unexpected argument '%s'\n"), argv[i]);
1192 usage(argv[0]);
1193 }
1194
1195 if (fw_img && (!ctd && !model)) {
1196 /* fw file was given, need to associate it w/ a disk or model */
1197 (void) fprintf(stderr, _("Expected a drive specification "
1198 "('-m model' or '-d dsk') to associate fw file with.\n"));
1199 usage(argv[0]);
1200 }
1201 if (!fw_img && (ctd || model)) {
1202 /*
1203 * no fw file is given, having model or disk
1204 * doesn't make sense
1205 */
1206 (void) fprintf(stderr, _("Expected a fw file "
1207 "('-p /path/to/fw/file') to associate '-m model' or "
1208 "'-d dsk' with.\n"));
1209 usage(argv[0]);
1210 }
1211
1212 if (model && ctd) {
1213 /* can't have both disk and model for fw download specified */
1214 (void) fprintf(stderr, _("-m and -d are mutually "
1215 "exclusive.\n"));
1216 usage(argv[0]);
1217 }
1218
1219 if ((change_mode || user_wait) && !fw_img) {
1220 /* need firmware to work with if above args are given */
1221 (void) fprintf(stderr, _("Expected '-p /path/to/fw.'\n"));
1222 usage(argv[0]);
1223 }
1224
1225 /*
1226 * create a snapshot of kernel device tree include minor nodes,
1227 * properties and subtree
1228 */
1229 if ((root_node = di_init("/", DINFOCPYALL | DINFOPATH)) ==
1230 DI_NODE_NIL) {
1231 (void) fprintf(stderr, _("di_init() failed\n"));
1232 free(fw_img);
1233 return (1);
1234 }
1235
1236 if (ctd) { /* if we were given a disk */
1237 if (!massage_drive_path(user_drive, dl_fw_to_disk, verbose)) {
1238 free(fw_img);
1239 usage(argv[0]);
1240 }
1241 node = di_lookup_node(root_node, dl_fw_to_disk);
1242 if (node == DI_NODE_NIL) {
1243 (void) fprintf(stderr, _("di_lookup_node(%s) failed. "
1244 "Please check path to drive.\n"), dl_fw_to_disk);
1245 free(fw_img);
1246 return (1);
1247 }
1248 if ((disk = (disk_info_t *)malloc(sizeof (disk_info_t)))
1249 == NULL) {
1250 if (verbose) {
1251 (void) fprintf(stderr, _("malloc(%d) failed\n"),
1252 sizeof (disk_info_t));
1253 }
1254 free(fw_img);
1255 return (1);
1256 } else {
1257 if (!set_disk_info(&node, disk, verbose)) {
1258 if (verbose)
1259 (void) fprintf(stderr, _(
1260 "set_disk_info(%s) indicated a "
1261 "problem.\n"), dl_fw_to_disk);
1262 free(disk);
1263 free(fw_img);
1264 return (1);
1265 }
1266 (void) process_disk(disk, mode, fw_img, fw_len, wait,
1267 verbose);
1268 }
1269 free(disk);
1270 free(fw_img);
1271 return (0);
1272 }
1273
1274
1275 list_create(&disks, sizeof (disk_info_t), offsetof(disk_info_t, link));
1276 i = walk_nodes(&root_node, &disks, verbose); /* walk device tree */
1277 di_fini(root_node);
1278
1279 if (i < 1) {
1280 while ((disk = (list_head(&disks))) != NULL) {
1281 list_remove(&disks, disk);
1282 free(disk);
1283 }
1284 if (fw_img)
1285 free(fw_img);
1286 }
1287 if (i == 0) {
1288 (void) fprintf(stdout, _("No drive(s) serviced by '"
1289 DISK_DRIVER "' driver were found.\n)"));
1290 return (1);
1291 }
1292 if (i < 0) {
1293 (void) fprintf(stderr, _("malloc() failed so not all drive(s) "
1294 "serviced by '" DISK_DRIVER "' driver were walked.\n"
1295 "Encountered %d drive(s) before failure.\n"), -i);
1296 return (1);
1297 }
1298 if (verbose) {
1299 (void) fprintf(stderr, _("Found %d drive(s) serviced by '"
1300 DISK_DRIVER "' driver.\n"), i);
1301 }
1302
1303 if (list)
1304 print_disks(&disks, verbose);
1305
1306 if (model) { /* if we were given a model */
1307 i = 0;
1308 disk = list_head(&disks);
1309 for (; disk; disk = list_next(&disks, disk)) {
1310 if (has_bad_info(disk, verbose))
1311 continue;
1312 if (strcmp(dl_fw_to_model, disk->model) == 0) {
1313 i++;
1314 if (verbose) {
1315 (void) fprintf(stderr, _("matched "
1316 "provided model: '%s' to drive "
1317 "'%s'.\n"), dl_fw_to_model,
1318 disk->device);
1319 }
1320 (void) process_disk(disk, mode, fw_img, fw_len,
1321 wait, verbose);
1322 } else if (verbose) {
1323 (void) fprintf(stderr, _("did not match "
1324 "provided model: '%s' to drive '%s'.\n"),
1325 dl_fw_to_model, disk->device);
1326 }
1327 }
1328 if (i == 0) {
1329 (void) fprintf(stdout, _("drive model '%s' was not "
1330 "found.\n"), dl_fw_to_model);
1331 }
1332 }
1333
1334 /* cleanup start */
1335 if (fw_img)
1336 free(fw_img);
1337 while ((disk = (list_head(&disks))) != NULL) {
1338 list_remove(&disks, disk);
1339 free(disk);
1340 }
1341 list_destroy(&disks);
1342
1343 return (0);
1344 }