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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * ses (SCSI Generic Device) specific functions. 30 */ 31 32 33 #include <assert.h> 34 #include <libnvpair.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <unistd.h> 38 #include <sys/types.h> 39 #include <sys/stat.h> 40 #include <sys/sysmacros.h> 41 #include <sys/queue.h> 42 #include <fcntl.h> 43 #include <string.h> 44 #include <strings.h> 45 46 #include <scsi/libses.h> 47 #include <sys/scsi/generic/commands.h> 48 #include <sys/scsi/impl/uscsi.h> 49 50 #include <libintl.h> /* for gettext(3c) */ 51 52 #include <fwflash/fwflash.h> 53 54 55 56 #ifdef NDEBUG 57 #define verify(EX) ((void)(EX)) 58 #else 59 #define verify(EX) assert(EX) 60 #endif 61 62 63 #define VIDLEN 0x08 64 #define PIDLEN 0x10 65 #define REVLEN 0x04 66 #define SASADDRLEN 0x10 67 #define PCBUFLEN 0x40 68 #define RQBUFLEN 0xfe 69 #define STATBUFLEN 0xfe 70 #define INQBUFLEN 0x80 71 72 /* useful defines */ 73 #define UCODE_CHECK_STATUS 0 74 #define UCODE_CHECK_SUPPORTED 1 75 76 typedef struct ucode_statdesc { 77 uint64_t us_value; 78 const char *us_desc; 79 boolean_t us_pending; 80 boolean_t us_iserr; 81 } ucode_statdesc_t; 82 83 static ucode_statdesc_t ucode_statdesc_table[] = { 84 { SES2_DLUCODE_S_NOP, "none", B_FALSE, B_FALSE }, 85 { SES2_DLUCODE_S_INPROGRESS, "in progress", B_TRUE, B_FALSE }, 86 { SES2_DLUCODE_S_SAVING, "saved", B_TRUE, B_FALSE }, 87 { SES2_DLUCODE_S_COMPLETE_NOW, "completed (available)", B_FALSE, 88 B_FALSE }, 89 { SES2_DLUCODE_S_COMPLETE_AT_RESET, 90 "completed (need reset or power on)", B_FALSE, B_FALSE }, 91 { SES2_DLUCODE_S_COMPLETE_AT_POWERON, "completed (need power on)", 92 B_FALSE, B_FALSE }, 93 { SES2_DLUCODE_S_PAGE_ERR, "page error (offset %d)", 94 B_FALSE, B_TRUE }, 95 { SES2_DLUCODE_S_IMAGE_ERR, "invalid image", 96 B_FALSE, B_TRUE }, 97 { SES2_DLUCODE_S_TIMEOUT, "download timeout", 98 B_FALSE, B_TRUE }, 99 { SES2_DLUCODE_S_INTERNAL_NEEDIMAGE, 100 "internal error (NEED NEW IMAGE BEFORE RESET)", 101 B_FALSE, B_TRUE }, 102 { SES2_DLUCODE_S_INTERNAL_SAFE, 103 "internal error (reset to revert to backup)", 104 B_FALSE, B_TRUE }, 105 }; 106 107 #define NUCODE_STATUS \ 108 (sizeof (ucode_statdesc_table) / sizeof (ucode_statdesc_table[0])) 109 110 typedef struct ucode_status { 111 uint64_t us_status; 112 boolean_t us_iserr; 113 boolean_t us_pending; 114 char us_desc[128]; 115 } ucode_status_t; 116 117 typedef struct ucode_wait { 118 uint64_t uw_prevstatus; 119 boolean_t uw_pending; 120 ses_node_t *uw_oldnp; 121 } ucode_wait_t; 122 123 124 char drivername[] = "ses\0"; 125 static char *devprefix = "/devices"; 126 static char *devsuffix = ":0"; 127 static ses_target_t *ses_target; 128 129 130 extern di_node_t rootnode; 131 extern int errno; 132 extern struct fw_plugin *self; 133 extern struct vrfyplugin *verifier; 134 extern int fwflash_debug; 135 136 137 /* required functions for this plugin */ 138 int fw_readfw(struct devicelist *device, char *filename); 139 int fw_writefw(struct devicelist *device); 140 int fw_identify(int start); 141 int fw_devinfo(struct devicelist *thisdev); 142 143 144 /* helper functions */ 145 static struct vpr *inquiry(char *path); 146 static int ses_dl_ucode_check(struct devicelist *flashdev); 147 static ses_walk_action_t print_updated_status(ses_node_t *np, void *arg); 148 static int get_status(nvlist_t *props, ucode_status_t *sp); 149 static ses_walk_action_t sendimg(ses_node_t *np, void *data); 150 151 152 153 /* 154 * SES2 does not actually allow us to read a firmware 155 * image from an SES device, so we just return success 156 * if this is requested, after printing a message. 157 */ 158 int 159 fw_readfw(struct devicelist *flashdev, char *filename) 160 { 161 int rv = FWFLASH_SUCCESS; 162 163 logmsg(MSG_INFO, 164 "ses: not writing firmware for device %s to file %s\n", 165 flashdev->access_devname, filename); 166 logmsg(MSG_ERROR, gettext("\n\nSES2 does not support retrieval " 167 "of firmware images\n\n")); 168 169 return (rv); 170 } 171 172 173 /* 174 * If we're invoking fw_writefw, then flashdev is a valid, 175 * flashable device supporting the SES2 Download Microcode Diagnostic 176 * Control page (0x0e). 177 * 178 * If verifier is null, then we haven't been called following a firmware 179 * image verification load operation. 180 * 181 * *THIS* function uses scsi SEND DIAGNOSTIC/download microcode to 182 * achieve the task... if you chase down to the bottom of libses you 183 * can see that too. 184 */ 185 int 186 fw_writefw(struct devicelist *flashdev) 187 { 188 189 nvlist_t *nvl; 190 ses_snap_t *snapshot; 191 192 193 194 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || 195 nvlist_add_uint64(nvl, SES_CTL_PROP_UCODE_MODE, 196 SES_DLUCODE_M_WITH_OFFS) != 0) { 197 logmsg(MSG_ERROR, gettext("ses: Unable to allocate " 198 "space for device prop list\n")); 199 return (FWFLASH_FAILURE); 200 } 201 202 203 if ((verifier == NULL) || (verifier->imgsize == 0) || 204 (verifier->fwimage == NULL)) { 205 /* should _not_ happen */ 206 logmsg(MSG_ERROR, gettext("ses: Firmware image has not " 207 "been verified.\n")); 208 return (FWFLASH_FAILURE); 209 } 210 211 fprintf(stdout, "\n"); /* get a fresh line for progress updates */ 212 213 if (nvlist_add_uint64(nvl, SES_CTL_PROP_UCODE_BUFID, 214 verifier->flashbuf) != 0) { 215 logmsg(MSG_ERROR, gettext("ses: Unable to add buffer id " 216 "property\n")); 217 goto cancel; 218 } 219 220 if (nvlist_add_byte_array(nvl, SES_CTL_PROP_UCODE_DATA, 221 (uint8_t *)verifier->fwimage, verifier->imgsize) != 0) { 222 logmsg(MSG_ERROR, 223 "%s: Out of memory for property addition\n", 224 drivername); 225 goto cancel; 226 } 227 228 if ((ses_target = 229 ses_open(LIBSES_VERSION, flashdev->access_devname)) == NULL) { 230 logmsg(MSG_ERROR, 231 gettext("ses: Unable to open flashable device\n%s\n"), 232 flashdev->access_devname); 233 goto cancel; 234 } 235 snapshot = ses_snap_hold(ses_target); 236 237 /* 238 * We flash via a walker callback function, because it's easier 239 * to do it this way when using libses. 240 */ 241 242 (void) ses_walk(snapshot, sendimg, nvl); 243 244 245 logmsg(MSG_ERROR, 246 gettext("ses: Done. New image will be active " 247 "after the system is rebooted.\n")); 248 249 fprintf(stdout, "\n"); 250 251 ses_snap_rele(snapshot); 252 ses_close(ses_target); 253 254 cancel: 255 nvlist_free(nvl); 256 257 return (FWFLASH_SUCCESS); 258 } 259 260 261 /* 262 * The fw_identify() function walks the device 263 * tree trying to find devices which this plugin 264 * can work with. 265 * 266 * The parameter "start" gives us the starting index number 267 * to give the device when we add it to the fw_devices list. 268 * 269 * firstdev is allocated by us and we add space as needed 270 */ 271 int 272 fw_identify(int start) 273 { 274 275 int rv = FWFLASH_FAILURE; 276 di_node_t thisnode; 277 struct devicelist *newdev; 278 char *devpath; 279 int idx = start; 280 int devlength = 0; 281 282 283 thisnode = di_drv_first_node(drivername, rootnode); 284 285 if (thisnode == DI_NODE_NIL) { 286 logmsg(MSG_INFO, gettext("No %s nodes in this system\n"), 287 drivername); 288 289 return (rv); 290 } 291 292 if ((devpath = calloc(1, MAXPATHLEN + 1)) == NULL) { 293 logmsg(MSG_ERROR, gettext("ses: Unable to malloc space " 294 "for a %s-attached device node\n"), drivername); 295 return (rv); 296 } 297 bzero(devpath, MAXPATHLEN); 298 299 /* we've found one, at least */ 300 301 for (; thisnode != DI_NODE_NIL; thisnode = di_drv_next_node(thisnode)) { 302 303 devpath = di_devfs_path(thisnode); 304 305 if ((newdev = calloc(1, sizeof (struct devicelist))) 306 == NULL) { 307 logmsg(MSG_ERROR, 308 gettext("ses: identification function unable " 309 "to allocate space for device entry\n")); 310 return (rv); 311 } 312 313 /* malloc enough for /devices + devpath + ":0" + '\0' */ 314 devlength = strlen(devpath) + strlen(devprefix) + 315 strlen(devsuffix) + 2; 316 317 if ((newdev->access_devname = calloc(1, devlength)) == NULL) { 318 logmsg(MSG_ERROR, gettext("ses: Unable to malloc " 319 "space for a devfs name\n")); 320 free(devpath); 321 return (FWFLASH_FAILURE); 322 } 323 snprintf(newdev->access_devname, devlength, 324 "%s%s%s", devprefix, devpath, devsuffix); 325 326 if ((newdev->drvname = calloc(1, strlen(drivername) + 1)) 327 == NULL) { 328 logmsg(MSG_ERROR, gettext("ses: Unable to malloc " 329 "space for a driver name\n")); 330 free(newdev->access_devname); 331 free(newdev); 332 return (FWFLASH_FAILURE); 333 } 334 (void) strlcpy(newdev->drvname, drivername, 335 strlen(drivername) + 1); 336 337 if ((newdev->classname = calloc(1, strlen(drivername) + 1)) 338 == NULL) { 339 logmsg(MSG_ERROR, gettext("ses: Unable to malloc " 340 "space for a class name\n")); 341 free(newdev->access_devname); 342 free(newdev->drvname); 343 free(newdev); 344 return (FWFLASH_FAILURE); 345 } 346 (void) strlcpy(newdev->classname, drivername, 347 strlen(drivername) + 1); 348 349 350 /* 351 * Check for friendly vendor names, and whether this device 352 * supports the Download Microcode Control page. 353 */ 354 355 newdev->ident = inquiry(newdev->access_devname); 356 rv = ses_dl_ucode_check(newdev); 357 if ((rv == FWFLASH_FAILURE) || (newdev->ident == NULL)) 358 continue; 359 360 361 if (newdev->ident == NULL) { 362 logmsg(MSG_INFO, 363 "ses: unable to inquire on potentially " 364 "flashable device\n%s\n", 365 newdev->access_devname); 366 free(newdev->access_devname); 367 free(newdev->drvname); 368 free(newdev->classname); 369 free(newdev); 370 continue; 371 } 372 373 /* 374 * Look for the target-port property. We use addresses[1] 375 * because addresses[0] is already assigned by the inquiry 376 * function 377 */ 378 if ((newdev->addresses[1] = calloc(1, SASADDRLEN + 1)) 379 == NULL) { 380 logmsg(MSG_ERROR, 381 gettext("ses: Out of memory for target-port\n")); 382 free(newdev->access_devname); 383 free(newdev->drvname); 384 free(newdev->classname); 385 free(newdev); 386 continue; 387 } else { 388 if (di_prop_lookup_strings(DDI_DEV_T_ANY, thisnode, 389 "target-port", &newdev->addresses[1]) < 0) { 390 logmsg(MSG_INFO, 391 "ses: no target-port property for " 392 "device %s\n", 393 newdev->access_devname); 394 strlcpy(newdev->addresses[1], 395 "0000000000000000", 17); 396 } 397 } 398 399 400 newdev->index = idx; 401 ++idx; 402 newdev->plugin = self; 403 404 TAILQ_INSERT_TAIL(fw_devices, newdev, nextdev); 405 } 406 407 408 if (fwflash_debug != 0) { 409 struct devicelist *tempdev; 410 411 TAILQ_FOREACH(tempdev, fw_devices, nextdev) { 412 logmsg(MSG_INFO, "ses:fw_writefw:\n"); 413 logmsg(MSG_INFO, "\ttempdev @ 0x%lx\n" 414 "\t\taccess_devname: %s\n" 415 "\t\tdrvname: %s\tclassname: %s\n" 416 "\t\tident->vid: %s\n" 417 "\t\tident->pid: %s\n" 418 "\t\tident->revid: %s\n" 419 "\t\tindex: %d\n" 420 "\t\taddress[0]: %s\n" 421 "\t\taddress[1]: %s\n" 422 "\t\tplugin @ 0x%lx\n\n", 423 &tempdev, 424 tempdev->access_devname, 425 tempdev->drvname, newdev->classname, 426 tempdev->ident->vid, 427 tempdev->ident->pid, 428 tempdev->ident->revid, 429 tempdev->index, 430 (tempdev->addresses[0] != NULL) ? 431 (char *)tempdev->addresses[0] : "NULL", 432 (tempdev->addresses[1] != NULL) ? 433 (char *)tempdev->addresses[1] : "NULL", 434 tempdev->plugin); 435 } 436 } 437 438 return (FWFLASH_SUCCESS); 439 } 440 441 442 443 int 444 fw_devinfo(struct devicelist *thisdev) 445 { 446 447 448 fprintf(stdout, gettext("Device[%d] %s\n Class [%s]\n"), 449 thisdev->index, thisdev->access_devname, thisdev->classname); 450 451 if (thisdev->addresses[0] != NULL) { 452 fprintf(stdout, 453 gettext("\tChassis Serial Number : %s\n"), 454 thisdev->addresses[0]); 455 } 456 457 fprintf(stdout, 458 gettext("\tVendor : %s\n" 459 "\tProduct : %s\n" 460 "\tFirmware revision : %s\n" 461 "\tTarget-port identifier : %s\n"), 462 thisdev->ident->vid, 463 thisdev->ident->pid, 464 thisdev->ident->revid, 465 thisdev->addresses[1]); 466 467 fprintf(stdout, "\n\n"); 468 469 return (FWFLASH_SUCCESS); 470 } 471 472 473 474 475 476 /*ARGSUSED*/ 477 static int 478 get_status(nvlist_t *props, ucode_status_t *sp) 479 { 480 int i; 481 uint64_t status, astatus; 482 483 if (nvlist_lookup_uint64(props, SES_EN_PROP_UCODE, &status) != 0) { 484 sp->us_status = -1ULL; 485 (void) snprintf(sp->us_desc, sizeof (sp->us_desc), 486 "not supported"); 487 return (-1); 488 } 489 490 verify(nvlist_lookup_uint64(props, SES_EN_PROP_UCODE_A, &astatus) == 0); 491 492 for (i = 0; i < NUCODE_STATUS; i++) { 493 if (ucode_statdesc_table[i].us_value == status) 494 break; 495 } 496 497 sp->us_status = status; 498 499 if (i == NUCODE_STATUS) { 500 (void) snprintf(sp->us_desc, sizeof (sp->us_desc), 501 "unknown (0x%02x)", (int)status); 502 sp->us_iserr = sp->us_pending = B_FALSE; 503 } else { 504 /* LINTED */ 505 (void) snprintf(sp->us_desc, sizeof (sp->us_desc), 506 ucode_statdesc_table[i].us_desc, (int)astatus); 507 sp->us_iserr = ucode_statdesc_table[i].us_iserr; 508 sp->us_pending = ucode_statdesc_table[i].us_pending; 509 } 510 511 return (0); 512 } 513 514 515 static ses_walk_action_t 516 print_updated_status(ses_node_t *np, void *arg) 517 { 518 ucode_wait_t *uwp = arg; 519 ses_node_t *oldnp = uwp->uw_oldnp; 520 nvlist_t *props, *oldprops; 521 uint64_t id, oldid; 522 ucode_status_t status; 523 524 if (ses_node_type(np) != SES_NODE_ENCLOSURE) 525 return (SES_WALK_ACTION_CONTINUE); 526 527 verify((props = ses_node_props(np)) != NULL); 528 verify((oldprops = ses_node_props(oldnp)) != NULL); 529 verify(nvlist_lookup_uint64(props, SES_EN_PROP_EID, &id) == 0); 530 verify(nvlist_lookup_uint64(oldprops, SES_EN_PROP_EID, &oldid) == 0); 531 532 if (oldid != id) 533 return (SES_WALK_ACTION_CONTINUE); 534 535 (void) get_status(props, &status); 536 if (status.us_status != uwp->uw_prevstatus) 537 (void) printf("%30s: %s\n", "status", status.us_desc); 538 uwp->uw_prevstatus = status.us_status; 539 uwp->uw_pending = status.us_pending; 540 541 if (status.us_iserr) 542 logmsg(MSG_INFO, 543 "ses: status.us_iserr: 0x%0x\n", 544 status.us_iserr); 545 546 return (SES_WALK_ACTION_CONTINUE); 547 } 548 549 /*ARGSUSED*/ 550 static ses_walk_action_t 551 sendimg(ses_node_t *np, void *data) 552 { 553 nvlist_t *props; 554 nvlist_t *arg = data; 555 char *vendor, *product, *revision, *csn; 556 char buf[128]; 557 ses_snap_t *newsnap; 558 int ret; 559 ucode_status_t statdesc; 560 ucode_wait_t wait; 561 uint8_t *imagedata; 562 size_t len; 563 564 if (ses_node_type(np) != SES_NODE_ENCLOSURE) 565 return (SES_WALK_ACTION_CONTINUE); 566 567 verify((props = ses_node_props(np)) != NULL); 568 569 verify(nvlist_lookup_string(props, SES_EN_PROP_VID, &vendor) == 0); 570 verify(nvlist_lookup_string(props, SES_EN_PROP_PID, &product) == 0); 571 verify(nvlist_lookup_string(props, SES_EN_PROP_REV, &revision) == 0); 572 verify(nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, &csn) == 0); 573 574 (void) printf("%30s: %s\n", "vendor", vendor); 575 (void) printf("%30s: %s\n", "product", product); 576 (void) printf("%30s: %s\n", "revision", revision); 577 (void) printf("%30s: %s\n", "serial", csn); 578 579 ret = get_status(props, &statdesc); 580 (void) printf("%30s: %s\n", "current status", statdesc.us_desc); 581 if (ret != 0) { 582 if (arg != NULL) 583 return (SES_WALK_ACTION_TERMINATE); 584 else 585 return (SES_WALK_ACTION_CONTINUE); 586 } 587 588 verify(nvlist_lookup_byte_array(arg, SES_CTL_PROP_UCODE_DATA, 589 &imagedata, &len) == 0); 590 (void) snprintf(buf, sizeof (buf), "downloading %u bytes", len); 591 (void) printf("\n%30s: ", buf); 592 if (ses_node_ctl(np, SES_CTL_OP_DL_UCODE, arg) != 0) { 593 (void) printf("failed!\n"); 594 (void) printf("%s\n", ses_errmsg()); 595 } else { 596 (void) printf("ok\n"); 597 } 598 599 wait.uw_prevstatus = -1ULL; 600 wait.uw_oldnp = np; 601 do { 602 if ((newsnap = ses_snap_new(ses_target)) == NULL) 603 logmsg(MSG_ERROR, 604 "failed to update SES snapshot: %s", 605 ses_errmsg()); 606 607 (void) ses_walk(newsnap, print_updated_status, 608 &wait); 609 ses_snap_rele(newsnap); 610 } while (wait.uw_pending); 611 612 return (SES_WALK_ACTION_CONTINUE); 613 } 614 615 /* 616 * Simple function to sent a standard SCSI INQUIRY(6) cdb out 617 * to thisnode and blat the response back into a struct vpr* 618 */ 619 static struct vpr * 620 inquiry(char *path) { 621 622 struct uscsi_cmd *inqcmd; 623 uchar_t inqbuf[INQBUFLEN]; /* inquiry response */ 624 uchar_t rqbuf[RQBUFLEN]; /* request sense data */ 625 struct vpr *inqvpr; 626 int fd, rval; 627 628 629 inqvpr = NULL; 630 if ((inqcmd = calloc(1, sizeof (struct uscsi_cmd))) == NULL) { 631 logmsg(MSG_ERROR, 632 gettext("ses: Unable to malloc %d bytes " 633 "for a SCSI INQUIRY(6) command\n"), 634 sizeof (struct uscsi_cmd)); 635 return (NULL); 636 } 637 638 if ((inqvpr = calloc(1, sizeof (struct vpr))) == NULL) { 639 logmsg(MSG_ERROR, 640 gettext("ses: Unable to malloc %d bytes " 641 "for SCSI INQUIRY(6) response\n"), 642 sizeof (struct vpr)); 643 free(inqcmd); 644 return (NULL); 645 } 646 647 if ((inqcmd->uscsi_cdb = calloc(1, CDB_GROUP0 * sizeof (caddr_t))) 648 == NULL) { 649 logmsg(MSG_ERROR, 650 gettext("ses: Unable to malloc %d bytes " 651 "for SCSI INQUIRY(6)\n"), 652 CDB_GROUP0 * sizeof (caddr_t)); 653 free(inqcmd); 654 free(inqvpr); 655 return (NULL); 656 } 657 658 logmsg(MSG_INFO, "ses:inquiry:opening device %s\n", 659 path); 660 661 if ((fd = open(path, O_RDONLY|O_SYNC)) < 0) { 662 logmsg(MSG_INFO, 663 "ses: Unable to open device %s: %s\n", 664 path, strerror(errno)); 665 free(inqcmd->uscsi_cdb); 666 free(inqcmd); 667 free(inqvpr); 668 return (NULL); 669 } 670 671 if (((inqvpr->vid = calloc(1, VIDLEN + 1)) 672 == NULL) || 673 ((inqvpr->pid = calloc(1, PIDLEN + 1)) 674 == NULL) || 675 ((inqvpr->revid = calloc(1, REVLEN + 1)) 676 == NULL)) { 677 logmsg(MSG_ERROR, 678 gettext("ses: Unable to malloc %d bytes " 679 "for %s identification function.\n"), 680 VIDLEN+PIDLEN+REVLEN, drivername); 681 free(inqcmd->uscsi_cdb); 682 free(inqcmd); 683 free(inqvpr->vid); 684 free(inqvpr->pid); 685 free(inqvpr->revid); 686 return (NULL); 687 } 688 689 /* just make sure these buffers are clean */ 690 bzero(inqbuf, INQBUFLEN); 691 bzero(rqbuf, RQBUFLEN); 692 bzero(inqcmd->uscsi_cdb, CDB_GROUP0); 693 inqcmd->uscsi_flags = USCSI_READ; 694 inqcmd->uscsi_timeout = 0; 695 inqcmd->uscsi_bufaddr = (caddr_t)inqbuf; 696 inqcmd->uscsi_buflen = INQBUFLEN; 697 inqcmd->uscsi_cdblen = CDB_GROUP0; /* a GROUP 0 command */ 698 inqcmd->uscsi_cdb[0] = SCMD_INQUIRY; 699 inqcmd->uscsi_cdb[1] = 0x00; /* EVPD = Enable Vital Product Data */ 700 inqcmd->uscsi_cdb[2] = 0x00; /* which pagecode to query? */ 701 inqcmd->uscsi_cdb[3] = 0x00; /* allocation length, msb */ 702 inqcmd->uscsi_cdb[4] = INQBUFLEN; /* allocation length, lsb */ 703 inqcmd->uscsi_cdb[5] = 0x0; /* control byte */ 704 inqcmd->uscsi_rqbuf = (caddr_t)&rqbuf; 705 inqcmd->uscsi_rqlen = RQBUFLEN; 706 707 708 rval = ioctl(fd, USCSICMD, inqcmd); 709 710 if (rval < 0) { 711 /* ioctl failed */ 712 logmsg(MSG_INFO, 713 gettext("ses: Unable to retrieve SCSI INQUIRY(6) data " 714 "from device %s: %s\n"), 715 path, strerror(errno)); 716 free(inqcmd->uscsi_cdb); 717 free(inqcmd); 718 free(inqvpr->vid); 719 free(inqvpr->pid); 720 free(inqvpr->revid); 721 return (NULL); 722 } 723 724 725 726 bcopy(&inqbuf[8], inqvpr->vid, VIDLEN); 727 bcopy(&inqbuf[16], inqvpr->pid, PIDLEN); 728 bcopy(&inqbuf[32], inqvpr->revid, REVLEN); 729 730 (void) close(fd); 731 732 logmsg(MSG_INFO, 733 "ses inquiry: vid %s ; pid %s ; revid %s\n", 734 inqvpr->vid, inqvpr->pid, inqvpr->revid); 735 736 if ((strncmp(inqvpr->vid, "SUN", 3) != 0) && 737 (strncmp(inqvpr->vid, "LSI", 3) != 0) && 738 (strncmp(inqvpr->vid, "Quanta", 6) != 0) && 739 (strncmp(inqvpr->vid, "QUANTA", 6) != 0)) { 740 free(inqvpr->vid); 741 free(inqvpr->pid); 742 free(inqvpr->revid); 743 inqvpr->vid = NULL; 744 inqvpr->pid = NULL; 745 inqvpr->revid = NULL; 746 logmsg(MSG_INFO, 747 "ses inquiry: unrecognised device\n"); 748 return (NULL); 749 } 750 751 free(inqcmd->uscsi_cdb); 752 free(inqcmd); 753 754 return (inqvpr); 755 } 756 757 758 /* 759 * ses_dl_ucode_check() lets us check whether SES2's Download 760 * Microcode Control diagnostic and status pages are supported 761 * by flashdev. 762 */ 763 int 764 ses_dl_ucode_check(struct devicelist *flashdev) 765 { 766 int rv; 767 int limit; 768 int i = 0; 769 int fd; 770 struct uscsi_cmd *usc; 771 uchar_t sensebuf[RQBUFLEN]; /* for the request sense data */ 772 uchar_t pagesup[PCBUFLEN]; /* should be less than 64 pages */ 773 774 775 if ((fd = open(flashdev->access_devname, 776 O_RDONLY|O_NDELAY)) < 0) { 777 logmsg(MSG_INFO, 778 gettext("ses:ses_dl_ucode_check: Unable to open %s\n"), 779 flashdev->access_devname); 780 return (FWFLASH_FAILURE); 781 } 782 783 if ((usc = calloc(1, sizeof (struct uscsi_cmd))) == NULL) { 784 logmsg(MSG_ERROR, 785 gettext("ses: Unable to alloc %d bytes for " 786 "microcode download query: %s\n"), 787 sizeof (struct uscsi_cmd), strerror(errno)); 788 (void) close(fd); 789 return (FWFLASH_FAILURE); 790 } 791 792 if ((usc->uscsi_cdb = calloc(1, CDB_GROUP0 * sizeof (caddr_t))) 793 == NULL) { 794 logmsg(MSG_ERROR, 795 gettext("ses: Unable to alloc %d bytes for " 796 "microcode download query: %s\n"), 797 CDB_GROUP0 * sizeof (caddr_t), strerror(errno)); 798 (void) close(fd); 799 free(usc); 800 return (FWFLASH_FAILURE); 801 } 802 803 804 bzero(sensebuf, RQBUFLEN); 805 806 usc->uscsi_flags = USCSI_READ | USCSI_RQENABLE; 807 usc->uscsi_timeout = 0; 808 usc->uscsi_cdblen = CDB_GROUP0; 809 usc->uscsi_rqbuf = (caddr_t)&sensebuf; 810 usc->uscsi_rqlen = RQBUFLEN; 811 812 813 bzero(pagesup, PCBUFLEN); 814 usc->uscsi_bufaddr = (caddr_t)&pagesup; 815 usc->uscsi_buflen = PCBUFLEN; 816 817 usc->uscsi_cdb[0] = SCMD_GDIAG; /* "Get" or receive */ 818 usc->uscsi_cdb[1] = 1; /* PCV = Page Code Valid */ 819 usc->uscsi_cdb[2] = 0x00; /* list all Supported diag pages */ 820 usc->uscsi_cdb[3] = 0x00; 821 usc->uscsi_cdb[4] = PCBUFLEN; 822 usc->uscsi_cdb[5] = 0; /* control byte, reserved */ 823 824 rv = ioctl(fd, USCSICMD, usc); 825 if (rv < 0) { 826 logmsg(MSG_INFO, 827 "ses: DL uCode checker ioctl error (%d): %s\n", 828 errno, strerror(errno)); 829 return (FWFLASH_FAILURE); 830 } 831 832 rv = FWFLASH_FAILURE; 833 /* in SPC4, this is an "n-3" field */ 834 limit = (pagesup[2] << 8) + pagesup[3] + 4; 835 while (i < limit) { 836 if (pagesup[4 + i] == 0x0E) { 837 i = limit; 838 logmsg(MSG_INFO, "ses: device %s " 839 "supports the Download Microcode " 840 "diagnostic control page\n", 841 flashdev->access_devname); 842 rv = FWFLASH_SUCCESS; 843 } 844 ++i; 845 } 846 (void) close(fd); 847 free(usc->uscsi_cdb); 848 free(usc); 849 850 return (rv); 851 } 852