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