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 #include <scsi/libscsi.h> 46 #include <scsi/libses.h> 47 #include <sys/scsi/generic/commands.h> 48 #include <sys/scsi/impl/uscsi.h> 49 #include <libintl.h> /* for gettext(3c) */ 50 #include <fwflash/fwflash.h> 51 52 53 54 #ifdef NDEBUG 55 #define verify(EX) ((void)(EX)) 56 #else 57 #define verify(EX) assert(EX) 58 #endif 59 60 61 #define VIDLEN 0x08 62 #define PIDLEN 0x10 63 #define REVLEN 0x04 64 #define SASADDRLEN 0x10 65 #define PCBUFLEN 0x40 66 #define RQBUFLEN 0xfe 67 #define STATBUFLEN 0xfe 68 #define INQBUFLEN 0x80 69 70 /* useful defines */ 71 #define UCODE_CHECK_STATUS 0 72 #define UCODE_CHECK_SUPPORTED 1 73 74 typedef struct ucode_statdesc { 75 uint64_t us_value; 76 const char *us_desc; 77 boolean_t us_pending; 78 boolean_t us_iserr; 79 } ucode_statdesc_t; 80 81 static ucode_statdesc_t ucode_statdesc_table[] = { 82 { SES2_DLUCODE_S_NOP, "none", B_FALSE, B_FALSE }, 83 { SES2_DLUCODE_S_INPROGRESS, "in progress", B_TRUE, B_FALSE }, 84 { SES2_DLUCODE_S_SAVING, "saved", B_TRUE, B_FALSE }, 85 { SES2_DLUCODE_S_COMPLETE_NOW, "completed (available)", B_FALSE, 86 B_FALSE }, 87 { SES2_DLUCODE_S_COMPLETE_AT_RESET, 88 "completed (need reset or power on)", B_FALSE, B_FALSE }, 89 { SES2_DLUCODE_S_COMPLETE_AT_POWERON, "completed (need power on)", 90 B_FALSE, B_FALSE }, 91 { SES2_DLUCODE_S_PAGE_ERR, "page error (offset %d)", 92 B_FALSE, B_TRUE }, 93 { SES2_DLUCODE_S_IMAGE_ERR, "invalid image", 94 B_FALSE, B_TRUE }, 95 { SES2_DLUCODE_S_TIMEOUT, "download timeout", 96 B_FALSE, B_TRUE }, 97 { SES2_DLUCODE_S_INTERNAL_NEEDIMAGE, 98 "internal error (NEED NEW IMAGE BEFORE RESET)", 99 B_FALSE, B_TRUE }, 100 { SES2_DLUCODE_S_INTERNAL_SAFE, 101 "internal error (reset to revert to backup)", 102 B_FALSE, B_TRUE }, 103 }; 104 105 #define NUCODE_STATUS \ 106 (sizeof (ucode_statdesc_table) / sizeof (ucode_statdesc_table[0])) 107 108 typedef struct ucode_status { 109 uint64_t us_status; 110 boolean_t us_iserr; 111 boolean_t us_pending; 112 char us_desc[128]; 113 } ucode_status_t; 114 115 typedef struct ucode_wait { 116 uint64_t uw_prevstatus; 117 boolean_t uw_pending; 118 ses_node_t *uw_oldnp; 119 } ucode_wait_t; 120 121 122 char drivername[] = "ses\0"; 123 static char *devprefix = "/devices"; 124 static char *sessuffix = ":0"; 125 static char *sgensuffix = ":ses"; 126 127 128 static ses_target_t *ses_target; 129 static int internalstatus; 130 131 extern di_node_t rootnode; 132 extern int errno; 133 extern struct fw_plugin *self; 134 extern struct vrfyplugin *verifier; 135 extern int fwflash_debug; 136 137 138 /* required functions for this plugin */ 139 int fw_readfw(struct devicelist *device, char *filename); 140 int fw_writefw(struct devicelist *device); 141 int fw_identify(int start); 142 int fw_devinfo(struct devicelist *thisdev); 143 144 145 /* helper functions */ 146 static ses_walk_action_t print_updated_status(ses_node_t *np, void *arg); 147 static int get_status(nvlist_t *props, ucode_status_t *sp); 148 static ses_walk_action_t sendimg(ses_node_t *np, void *data); 149 static void tidyup(struct devicelist *thisdev); 150 151 152 /* 153 * SES2 does not actually allow us to read a firmware 154 * image from an SES device, so we just return success 155 * if this is requested, after printing a message. 156 */ 157 int 158 fw_readfw(struct devicelist *flashdev, char *filename) 159 { 160 161 logmsg(MSG_INFO, 162 "%s: not writing firmware for device %s to file %s\n", 163 flashdev->drvname, flashdev->access_devname, filename); 164 logmsg(MSG_ERROR, gettext("\n\nReading of firmware images from " 165 "your device is not currently supported\n\n")); 166 167 return (FWFLASH_SUCCESS); 168 } 169 170 171 /* 172 * If we're invoking fw_writefw, then flashdev is a valid, 173 * flashable device supporting the SES2 Download Microcode Diagnostic 174 * Control page (0x0e). 175 * 176 * If verifier is null, then we haven't been called following a firmware 177 * image verification load operation. 178 * 179 * *THIS* function uses scsi SEND DIAGNOSTIC/download microcode to 180 * achieve the task... if you chase down to the bottom of libses you 181 * can see that too. 182 */ 183 int 184 fw_writefw(struct devicelist *flashdev) 185 { 186 187 nvlist_t *nvl; 188 ses_snap_t *snapshot; 189 190 191 192 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || 193 nvlist_add_uint64(nvl, SES_CTL_PROP_UCODE_MODE, 194 SES_DLUCODE_M_WITH_OFFS) != 0) { 195 logmsg(MSG_ERROR, 196 gettext("%s: Unable to allocate " 197 "space for device prop list\n"), 198 flashdev->drvname); 199 tidyup(flashdev); 200 return (FWFLASH_FAILURE); 201 } 202 203 204 if ((verifier == NULL) || (verifier->imgsize == 0) || 205 (verifier->fwimage == NULL)) { 206 /* should _not_ happen */ 207 logmsg(MSG_ERROR, 208 gettext("%s: Firmware image has not " 209 "been verified.\n"), 210 flashdev->drvname); 211 tidyup(flashdev); 212 return (FWFLASH_FAILURE); 213 } 214 215 fprintf(stdout, "\n"); /* get a fresh line for progress updates */ 216 217 if (nvlist_add_uint64(nvl, SES_CTL_PROP_UCODE_BUFID, 218 verifier->flashbuf) != 0) { 219 logmsg(MSG_ERROR, 220 gettext("%s: Unable to add buffer id " 221 "property, hence unable to flash device\n"), 222 flashdev->drvname); 223 goto cancel; 224 } 225 226 if (nvlist_add_byte_array(nvl, SES_CTL_PROP_UCODE_DATA, 227 (uint8_t *)verifier->fwimage, verifier->imgsize) != 0) { 228 logmsg(MSG_ERROR, 229 "%s: Out of memory for property addition\n", 230 flashdev->drvname); 231 goto cancel; 232 } 233 234 if ((ses_target = 235 ses_open(LIBSES_VERSION, flashdev->access_devname)) == NULL) { 236 logmsg(MSG_ERROR, 237 gettext("%s: Unable to open flashable device\n%s\n"), 238 flashdev->drvname, flashdev->access_devname); 239 goto cancel; 240 } 241 snapshot = ses_snap_hold(ses_target); 242 243 /* 244 * We flash via a walker callback function, because it's easier 245 * to do it this way when using libses. 246 */ 247 248 internalstatus = FWFLASH_SUCCESS; 249 (void) ses_walk(snapshot, sendimg, nvl); 250 251 if (internalstatus == FWFLASH_SUCCESS) { 252 logmsg(MSG_ERROR, 253 gettext("%s: Done. New image will be active " 254 "after the system is rebooted.\n"), 255 flashdev->drvname); 256 fprintf(stdout, "\n"); 257 } 258 259 ses_snap_rele(snapshot); 260 ses_close(ses_target); 261 262 cancel: 263 nvlist_free(nvl); 264 tidyup(flashdev); 265 266 return (internalstatus); 267 } 268 269 270 /* 271 * The fw_identify() function walks the device 272 * tree trying to find devices which this plugin 273 * can work with. 274 * 275 * The parameter "start" gives us the starting index number 276 * to give the device when we add it to the fw_devices list. 277 * 278 * firstdev is allocated by us and we add space as needed 279 */ 280 int 281 fw_identify(int start) 282 { 283 284 int rv = FWFLASH_FAILURE; 285 di_node_t thisnode; 286 struct devicelist *newdev; 287 char *devpath; 288 char *devsuffix; 289 char *driver; 290 int idx = start; 291 int devlength = 0; 292 nvlist_t *props; 293 ses_snap_t *snapshot; 294 ses_node_t *rootnodep, *nodep, *tnodep; 295 296 297 if (strcmp(self->drvname, "sgen") == 0) { 298 devsuffix = sgensuffix; 299 driver = self->drvname; 300 301 } else { 302 devsuffix = sessuffix; 303 driver = drivername; 304 } 305 306 thisnode = di_drv_first_node(driver, rootnode); 307 308 if (thisnode == DI_NODE_NIL) { 309 logmsg(MSG_INFO, gettext("No %s nodes in this system\n"), 310 driver); 311 312 return (rv); 313 } 314 315 if ((devpath = calloc(1, MAXPATHLEN + 1)) == NULL) { 316 logmsg(MSG_ERROR, 317 gettext("%s: Unable to allocate space " 318 "for a device node\n"), 319 driver); 320 return (rv); 321 } 322 323 /* we've found one, at least */ 324 325 for (; thisnode != DI_NODE_NIL; thisnode = di_drv_next_node(thisnode)) { 326 327 devpath = di_devfs_path(thisnode); 328 329 if ((newdev = calloc(1, sizeof (struct devicelist))) 330 == NULL) { 331 logmsg(MSG_ERROR, 332 gettext("%s: identification function unable " 333 "to allocate space for device entry\n"), 334 driver); 335 tidyup(newdev); 336 free(devpath); 337 return (rv); 338 } 339 340 /* calloc enough for /devices + devpath + devsuffix + '\0' */ 341 devlength = strlen(devpath) + strlen(devprefix) + 342 strlen(devsuffix) + 2; 343 344 if ((newdev->access_devname = calloc(1, devlength)) == NULL) { 345 logmsg(MSG_ERROR, 346 gettext("%s: Unable to allocate " 347 "space for a devfs name\n"), 348 driver); 349 tidyup(newdev); 350 free(devpath); 351 free(newdev); 352 return (FWFLASH_FAILURE); 353 } 354 snprintf(newdev->access_devname, devlength, 355 "%s%s%s", devprefix, devpath, devsuffix); 356 357 if ((newdev->drvname = calloc(1, strlen(driver) + 1)) 358 == NULL) { 359 logmsg(MSG_ERROR, 360 gettext("%s: Unable to allocate " 361 "space to store a driver name\n"), 362 driver); 363 tidyup(newdev); 364 free(newdev->access_devname); 365 free(newdev); 366 free(devpath); 367 return (FWFLASH_FAILURE); 368 } 369 (void) strlcpy(newdev->drvname, driver, 370 strlen(driver) + 1); 371 372 if ((newdev->classname = calloc(1, strlen(driver) + 1)) 373 == NULL) { 374 logmsg(MSG_ERROR, 375 gettext("%s: Unable to malloc " 376 "space for a class name\n"), 377 drivername); 378 tidyup(newdev); 379 free(newdev->access_devname); 380 free(newdev->drvname); 381 free(newdev); 382 free(devpath); 383 return (FWFLASH_FAILURE); 384 } 385 (void) strlcpy(newdev->classname, driver, 386 strlen(driver) + 1); 387 388 /* 389 * Only alloc as much as we truly need, and DON'T forget 390 * that libnvpair manages the memory for property lookups! 391 * That means we need to call tidyup() after fw_writefw() 392 * and fw_devinfo(). 393 */ 394 newdev->ident = calloc(1, VIDLEN + PIDLEN + REVLEN + 3); 395 if (newdev->ident == NULL) { 396 logmsg(MSG_ERROR, 397 gettext("%s: Unable to malloc %d bytes " 398 "for SCSI INQUIRY data\n"), 399 driver, sizeof (struct vpr)); 400 tidyup(newdev); 401 free(newdev->classname); 402 free(newdev->drvname); 403 free(newdev->access_devname); 404 free(newdev); 405 free(devpath); 406 return (FWFLASH_FAILURE); 407 } 408 409 410 if ((ses_target = 411 ses_open(LIBSES_VERSION, newdev->access_devname)) 412 == NULL) { 413 logmsg(MSG_INFO, 414 gettext("%s: Unable to open device\n%s\n"), 415 driver, 416 newdev->access_devname); 417 tidyup(newdev); 418 free(newdev->ident); 419 free(newdev->classname); 420 free(newdev->access_devname); 421 free(newdev->drvname); 422 free(newdev); 423 free(devpath); 424 continue; 425 } 426 snapshot = ses_snap_hold(ses_target); 427 rootnodep = ses_root_node(snapshot); 428 429 /* 430 * The root node of the snapshot is likely to be of 431 * type SES_NODE_TARGET, so use Shank's Pony to get 432 * where we need to go 433 */ 434 nodep = ses_node_child(rootnodep); 435 tnodep = nodep; 436 437 if ((props = ses_node_props(rootnodep)) == NULL) { 438 tidyup(newdev); 439 free(newdev->ident); 440 ses_snap_rele(snapshot); 441 ses_close(ses_target); 442 free(newdev->classname); 443 free(newdev->access_devname); 444 free(newdev->drvname); 445 free(newdev); 446 free(devpath); 447 continue; 448 } 449 450 /* 451 * If these properties don't exist, this device does 452 * not comply with SES2 so we won't touch it. 453 */ 454 if ((nvlist_lookup_string(props, SCSI_PROP_VENDOR, 455 &newdev->ident->vid) != 0) || 456 (nvlist_lookup_string(props, SCSI_PROP_PRODUCT, 457 &newdev->ident->pid) != 0) || 458 (nvlist_lookup_string(props, SCSI_PROP_REVISION, 459 &newdev->ident->revid) != 0)) { 460 tidyup(newdev); 461 free(newdev->ident); 462 ses_snap_rele(snapshot); 463 ses_close(ses_target); 464 free(newdev->classname); 465 free(newdev->access_devname); 466 free(newdev->drvname); 467 free(newdev); 468 free(devpath); 469 continue; 470 } 471 472 473 while (ses_node_type(tnodep) != SES_NODE_ENCLOSURE) { 474 tnodep = ses_node_child(nodep); 475 nodep = tnodep; 476 } 477 478 if ((nodep == NULL) || 479 (props = ses_node_props(nodep)) == NULL) { 480 tidyup(newdev); 481 free(newdev->ident); 482 ses_snap_rele(snapshot); 483 ses_close(ses_target); 484 free(newdev->classname); 485 free(newdev->access_devname); 486 free(newdev->drvname); 487 free(newdev); 488 free(devpath); 489 continue; 490 } 491 492 logmsg(MSG_INFO, 493 "\nvid: %s\npid: %s\nrevid: %s\n", 494 newdev->ident->vid, 495 newdev->ident->pid, 496 newdev->ident->revid); 497 498 if (nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, 499 &newdev->addresses[0]) == 0) { 500 logmsg(MSG_INFO, 501 "Chassis Serial Number: %s\n", 502 newdev->addresses[0]); 503 } else { 504 (void) strlcpy(newdev->addresses[0], 505 "(not supported)", 17); 506 } 507 508 509 if (di_prop_lookup_strings(DDI_DEV_T_ANY, 510 thisnode, "target-port", 511 &newdev->addresses[1]) < 0) { 512 logmsg(MSG_INFO, 513 "%s: no target-port property " 514 "for device %s\n", 515 driver, newdev->access_devname); 516 (void) strlcpy(newdev->addresses[1], 517 "(not supported)", 17); 518 } else 519 logmsg(MSG_INFO, 520 "target-port property: %s\n", 521 newdev->addresses[1]); 522 523 524 newdev->index = idx; 525 ++idx; 526 newdev->plugin = self; 527 528 ses_snap_rele(snapshot); 529 TAILQ_INSERT_TAIL(fw_devices, newdev, nextdev); 530 } 531 532 533 if (fwflash_debug != 0) { 534 struct devicelist *tempdev; 535 536 TAILQ_FOREACH(tempdev, fw_devices, nextdev) { 537 logmsg(MSG_INFO, "%s:fw_identify:\n", 538 driver); 539 logmsg(MSG_INFO, 540 "\ttempdev @ 0x%lx\n" 541 "\t\taccess_devname: %s\n" 542 "\t\tdrvname: %s\tclassname: %s\n" 543 "\t\tident->vid: %s\n" 544 "\t\tident->pid: %s\n" 545 "\t\tident->revid: %s\n" 546 "\t\tindex: %d\n" 547 "\t\taddress[0]: %s\n" 548 "\t\taddress[1]: %s\n" 549 "\t\tplugin @ 0x%lx\n\n", 550 &tempdev, 551 tempdev->access_devname, 552 tempdev->drvname, newdev->classname, 553 tempdev->ident->vid, 554 tempdev->ident->pid, 555 tempdev->ident->revid, 556 tempdev->index, 557 tempdev->addresses[0], 558 tempdev->addresses[1], 559 &tempdev->plugin); 560 } 561 } 562 563 return (FWFLASH_SUCCESS); 564 } 565 566 567 568 int 569 fw_devinfo(struct devicelist *thisdev) 570 { 571 572 573 fprintf(stdout, gettext("Device[%d] %s\n Class [%s]\n"), 574 thisdev->index, thisdev->access_devname, thisdev->classname); 575 576 fprintf(stdout, 577 gettext("\tVendor : %s\n" 578 "\tProduct : %s\n" 579 "\tFirmware revision : %s\n" 580 "\tChassis Serial Number : %s\n" 581 "\tTarget-port identifier : %s\n"), 582 thisdev->ident->vid, 583 thisdev->ident->pid, 584 thisdev->ident->revid, 585 thisdev->addresses[0], 586 thisdev->addresses[1]); 587 588 fprintf(stdout, "\n\n"); 589 590 /* Don't leave any bits behind... */ 591 tidyup(thisdev); 592 593 return (FWFLASH_SUCCESS); 594 } 595 596 597 598 599 600 /*ARGSUSED*/ 601 static int 602 get_status(nvlist_t *props, ucode_status_t *sp) 603 { 604 int i; 605 uint64_t status, astatus; 606 607 if (nvlist_lookup_uint64(props, SES_EN_PROP_UCODE, &status) != 0) { 608 sp->us_status = -1ULL; 609 (void) snprintf(sp->us_desc, sizeof (sp->us_desc), 610 "not supported"); 611 internalstatus = FWFLASH_FAILURE; 612 return (-1); 613 } 614 615 if (nvlist_lookup_uint64(props, SES_EN_PROP_UCODE_A, 616 &astatus) != 0) { 617 logmsg(MSG_ERROR, 618 gettext("\nError: Unable to retrieve current status\n")); 619 internalstatus = FWFLASH_FAILURE; 620 return (-1); 621 } 622 623 624 for (i = 0; i < NUCODE_STATUS; i++) { 625 if (ucode_statdesc_table[i].us_value == status) 626 break; 627 } 628 629 sp->us_status = status; 630 631 if (i == NUCODE_STATUS) { 632 (void) snprintf(sp->us_desc, sizeof (sp->us_desc), 633 "unknown (0x%02x)", (int)status); 634 sp->us_iserr = sp->us_pending = B_FALSE; 635 internalstatus = FWFLASH_FAILURE; 636 } else { 637 /* LINTED */ 638 (void) snprintf(sp->us_desc, sizeof (sp->us_desc), 639 ucode_statdesc_table[i].us_desc, (int)astatus); 640 sp->us_iserr = ucode_statdesc_table[i].us_iserr; 641 sp->us_pending = ucode_statdesc_table[i].us_pending; 642 } 643 644 return (0); 645 } 646 647 648 static ses_walk_action_t 649 print_updated_status(ses_node_t *np, void *arg) 650 { 651 ucode_wait_t *uwp = arg; 652 ses_node_t *oldnp = uwp->uw_oldnp; 653 nvlist_t *props, *oldprops; 654 uint64_t id, oldid; 655 ucode_status_t status; 656 657 if (ses_node_type(np) != SES_NODE_ENCLOSURE) 658 return (SES_WALK_ACTION_CONTINUE); 659 660 verify((props = ses_node_props(np)) != NULL); 661 verify((oldprops = ses_node_props(oldnp)) != NULL); 662 verify(nvlist_lookup_uint64(props, SES_EN_PROP_EID, &id) == 0); 663 verify(nvlist_lookup_uint64(oldprops, SES_EN_PROP_EID, &oldid) == 0); 664 665 if (oldid != id) 666 return (SES_WALK_ACTION_CONTINUE); 667 668 (void) get_status(props, &status); 669 if (status.us_status != uwp->uw_prevstatus) 670 (void) printf("%30s: %s\n", "status", status.us_desc); 671 uwp->uw_prevstatus = status.us_status; 672 uwp->uw_pending = status.us_pending; 673 674 if (status.us_iserr) { 675 logmsg(MSG_INFO, 676 "libses: status.us_iserr: 0x%0x\n", 677 status.us_iserr); 678 internalstatus = FWFLASH_FAILURE; 679 } 680 681 return (SES_WALK_ACTION_CONTINUE); 682 } 683 684 /*ARGSUSED*/ 685 static ses_walk_action_t 686 sendimg(ses_node_t *np, void *data) 687 { 688 nvlist_t *props; 689 nvlist_t *arg = data; 690 char *vendor, *product, *revision, *csn; 691 char buf[128]; 692 ses_snap_t *newsnap; 693 int ret; 694 ucode_status_t statdesc; 695 ucode_wait_t wait; 696 uint8_t *imagedata; 697 uint_t len; 698 699 if (ses_node_type(np) != SES_NODE_ENCLOSURE) 700 return (SES_WALK_ACTION_CONTINUE); 701 702 verify((props = ses_node_props(np)) != NULL); 703 704 verify(nvlist_lookup_string(props, SES_EN_PROP_VID, &vendor) == 0); 705 verify(nvlist_lookup_string(props, SES_EN_PROP_PID, &product) == 0); 706 verify(nvlist_lookup_string(props, SES_EN_PROP_REV, &revision) == 0); 707 verify(nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, &csn) == 0); 708 709 (void) printf("%30s: %s\n", "vendor", vendor); 710 (void) printf("%30s: %s\n", "product", product); 711 (void) printf("%30s: %s\n", "revision", revision); 712 (void) printf("%30s: %s\n", "serial", csn); 713 714 ret = get_status(props, &statdesc); 715 (void) printf("%30s: %s\n", "current status", statdesc.us_desc); 716 if (ret != 0) { 717 /* internalstatus is already set */ 718 if (arg != NULL) 719 return (SES_WALK_ACTION_TERMINATE); 720 else 721 return (SES_WALK_ACTION_CONTINUE); 722 } 723 724 verify(nvlist_lookup_byte_array(arg, SES_CTL_PROP_UCODE_DATA, 725 &imagedata, &len) == 0); 726 (void) snprintf(buf, sizeof (buf), "downloading %u bytes", len); 727 (void) printf("\n%30s: ", buf); 728 if (ses_node_ctl(np, SES_CTL_OP_DL_UCODE, arg) != 0) { 729 (void) printf("failed!\n"); 730 (void) printf("%s\n", ses_errmsg()); 731 internalstatus = FWFLASH_FAILURE; 732 } else { 733 (void) printf("ok\n"); 734 } 735 736 wait.uw_prevstatus = -1ULL; 737 wait.uw_oldnp = np; 738 do { 739 if ((newsnap = ses_snap_new(ses_target)) == NULL) 740 logmsg(MSG_ERROR, 741 "failed to update SES snapshot: %s", 742 ses_errmsg()); 743 744 (void) ses_walk(newsnap, print_updated_status, 745 &wait); 746 ses_snap_rele(newsnap); 747 } while (wait.uw_pending); 748 749 return (SES_WALK_ACTION_CONTINUE); 750 } 751 752 static void 753 tidyup(struct devicelist *thisdev) 754 { 755 /* 756 * Since we didn't allocate the space for the ident->* and 757 * addresses[*], set them to NULL and let the libraries 758 * (libnvpair and libdevinfo) handle them. 759 */ 760 thisdev->ident->vid = NULL; 761 thisdev->ident->pid = NULL; 762 thisdev->ident->revid = NULL; 763 thisdev->addresses[0] = NULL; 764 thisdev->addresses[1] = NULL; 765 } 766