xref: /onnv-gate/usr/src/cmd/fwflash/plugins/transport/common/ses.c (revision 9683:6900c60549c1)
16489Sjmcp /*
26489Sjmcp  * CDDL HEADER START
36489Sjmcp  *
46489Sjmcp  * The contents of this file are subject to the terms of the
56489Sjmcp  * Common Development and Distribution License (the "License").
66489Sjmcp  * You may not use this file except in compliance with the License.
76489Sjmcp  *
86489Sjmcp  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
96489Sjmcp  * or http://www.opensolaris.org/os/licensing.
106489Sjmcp  * See the License for the specific language governing permissions
116489Sjmcp  * and limitations under the License.
126489Sjmcp  *
136489Sjmcp  * When distributing Covered Code, include this CDDL HEADER in each
146489Sjmcp  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
156489Sjmcp  * If applicable, add the following below this CDDL HEADER, with the
166489Sjmcp  * fields enclosed by brackets "[]" replaced with your own identifying
176489Sjmcp  * information: Portions Copyright [yyyy] [name of copyright owner]
186489Sjmcp  *
196489Sjmcp  * CDDL HEADER END
206489Sjmcp  */
216489Sjmcp /*
228920SPei-Hong.Huang@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
236489Sjmcp  * Use is subject to license terms.
246489Sjmcp  */
256489Sjmcp 
266489Sjmcp /*
276489Sjmcp  * ses (SCSI Generic Device) specific functions.
286489Sjmcp  */
296489Sjmcp 
306489Sjmcp #include <libnvpair.h>
316489Sjmcp #include <stdio.h>
326489Sjmcp #include <stdlib.h>
336489Sjmcp #include <unistd.h>
346489Sjmcp #include <sys/types.h>
356489Sjmcp #include <sys/sysmacros.h>
366489Sjmcp #include <sys/queue.h>
376489Sjmcp #include <fcntl.h>
386489Sjmcp #include <string.h>
396846Sjmcp #include <scsi/libscsi.h>
406489Sjmcp #include <scsi/libses.h>
416489Sjmcp #include <libintl.h> /* for gettext(3c) */
426489Sjmcp #include <fwflash/fwflash.h>
436489Sjmcp 
446489Sjmcp 
456489Sjmcp #define	VIDLEN		0x08
466489Sjmcp #define	PIDLEN		0x10
476489Sjmcp #define	REVLEN		0x04
486489Sjmcp #define	SASADDRLEN	0x10
496489Sjmcp #define	PCBUFLEN	0x40
506489Sjmcp #define	RQBUFLEN	0xfe
516489Sjmcp #define	STATBUFLEN	0xfe
526489Sjmcp #define	INQBUFLEN	0x80
536489Sjmcp 
546489Sjmcp /* useful defines */
556489Sjmcp #define	UCODE_CHECK_STATUS	0
566489Sjmcp #define	UCODE_CHECK_SUPPORTED	1
576489Sjmcp 
586489Sjmcp typedef struct ucode_statdesc {
596489Sjmcp 	uint64_t	us_value;
606489Sjmcp 	const char	*us_desc;
616489Sjmcp 	boolean_t	us_pending;
626489Sjmcp 	boolean_t	us_iserr;
636489Sjmcp } ucode_statdesc_t;
646489Sjmcp 
656489Sjmcp static ucode_statdesc_t ucode_statdesc_table[] = {
666489Sjmcp 	{ SES2_DLUCODE_S_NOP,		"none",	B_FALSE, B_FALSE },
676489Sjmcp 	{ SES2_DLUCODE_S_INPROGRESS,	"in progress", B_TRUE, B_FALSE },
686489Sjmcp 	{ SES2_DLUCODE_S_SAVING,	"saved", B_TRUE, B_FALSE },
696489Sjmcp 	{ SES2_DLUCODE_S_COMPLETE_NOW,	"completed (available)", B_FALSE,
706489Sjmcp 	    B_FALSE },
716489Sjmcp 	{ SES2_DLUCODE_S_COMPLETE_AT_RESET,
726489Sjmcp 	    "completed (need reset or power on)", B_FALSE, B_FALSE },
736489Sjmcp 	{ SES2_DLUCODE_S_COMPLETE_AT_POWERON,	"completed (need power on)",
746489Sjmcp 	    B_FALSE, B_FALSE },
756489Sjmcp 	{ SES2_DLUCODE_S_PAGE_ERR,	"page error (offset %d)",
766489Sjmcp 	    B_FALSE, B_TRUE },
776489Sjmcp 	{ SES2_DLUCODE_S_IMAGE_ERR,	"invalid image",
786489Sjmcp 	    B_FALSE, B_TRUE },
796489Sjmcp 	{ SES2_DLUCODE_S_TIMEOUT,	"download timeout",
806489Sjmcp 	    B_FALSE, B_TRUE },
816489Sjmcp 	{ SES2_DLUCODE_S_INTERNAL_NEEDIMAGE,
826489Sjmcp 	    "internal error (NEED NEW IMAGE BEFORE RESET)",
836489Sjmcp 	    B_FALSE, B_TRUE },
846489Sjmcp 	{ SES2_DLUCODE_S_INTERNAL_SAFE,
856489Sjmcp 	    "internal error (reset to revert to backup)",
866489Sjmcp 	    B_FALSE, B_TRUE },
876489Sjmcp };
886489Sjmcp 
896489Sjmcp #define	NUCODE_STATUS	\
906489Sjmcp 	(sizeof (ucode_statdesc_table) / sizeof (ucode_statdesc_table[0]))
916489Sjmcp 
926489Sjmcp typedef struct ucode_status {
936489Sjmcp 	uint64_t	us_status;
946489Sjmcp 	boolean_t	us_iserr;
956489Sjmcp 	boolean_t	us_pending;
966489Sjmcp 	char		us_desc[128];
976489Sjmcp } ucode_status_t;
986489Sjmcp 
996489Sjmcp typedef struct ucode_wait {
1006489Sjmcp 	uint64_t	uw_prevstatus;
1016489Sjmcp 	boolean_t	uw_pending;
1026489Sjmcp 	ses_node_t	*uw_oldnp;
1036489Sjmcp } ucode_wait_t;
1046489Sjmcp 
1056489Sjmcp 
1066846Sjmcp typedef struct sam4_statdesc {
1076846Sjmcp 	int status;
1086846Sjmcp 	char *message;
1096846Sjmcp } sam4_statdesc_t;
1106846Sjmcp 
1116846Sjmcp 
1126846Sjmcp static sam4_statdesc_t sam4_status[] = {
1136846Sjmcp 	{ SAM4_STATUS_GOOD, "Status: GOOD (success)" },
1146846Sjmcp 	{ SAM4_STATUS_CHECK_CONDITION, "Status: CHECK CONDITION" },
1156846Sjmcp 	{ SAM4_STATUS_CONDITION_MET, "Status: CONDITION MET" },
1166846Sjmcp 	{ SAM4_STATUS_BUSY, "Status: Device is BUSY" },
1176846Sjmcp 	{ SAM4_STATUS_RESERVATION_CONFLICT, "Status: Device is RESERVED" },
1186846Sjmcp 	{ SAM4_STATUS_TASK_SET_FULL,
1196846Sjmcp 	    "Status: TASK SET FULL (insufficient resources in command queue" },
1206846Sjmcp 	{ SAM4_STATUS_TASK_ABORTED, "Status: TASK ABORTED" },
1216846Sjmcp 	{ NULL, NULL }
1226846Sjmcp };
1236846Sjmcp 
1246846Sjmcp #define	NSAM4_STATUS	\
1256846Sjmcp 	(sizeof (sam4_status) / sizeof (sam4_status[0]))
1266846Sjmcp 
1276846Sjmcp 
1286846Sjmcp 
1296489Sjmcp char drivername[] = "ses\0";
1306489Sjmcp static char *devprefix = "/devices";
1316846Sjmcp static char *sessuffix = ":0";
1326846Sjmcp static char *sgensuffix = ":ses";
1336846Sjmcp 
1346846Sjmcp 
1356736Ssuha static ses_target_t *ses_target;
1366708Sjmcp 
1376489Sjmcp extern di_node_t rootnode;
1386489Sjmcp extern int errno;
1396489Sjmcp extern struct fw_plugin *self;
1406489Sjmcp extern struct vrfyplugin *verifier;
1416489Sjmcp extern int fwflash_debug;
1426489Sjmcp 
1436489Sjmcp 
1446489Sjmcp /* required functions for this plugin */
1456489Sjmcp int fw_readfw(struct devicelist *device, char *filename);
1466489Sjmcp int fw_writefw(struct devicelist *device);
1476489Sjmcp int fw_identify(int start);
1486489Sjmcp int fw_devinfo(struct devicelist *thisdev);
1496489Sjmcp 
1506489Sjmcp 
1516489Sjmcp /* helper functions */
152*9683SXin.Chen@Sun.COM static int print_updated_status(ses_node_t *np, void *arg);
1536489Sjmcp static int get_status(nvlist_t *props, ucode_status_t *sp);
1546846Sjmcp static int sendimg(ses_node_t *np, void *data);
1556846Sjmcp static int scsi_writebuf();
1566489Sjmcp 
1576489Sjmcp /*
1586846Sjmcp  * We don't currently support reading firmware from a SAS
1596846Sjmcp  * expander. If we do eventually support it, we would use
1606846Sjmcp  * the scsi READ BUFFER command to do so.
1616489Sjmcp  */
1626489Sjmcp int
fw_readfw(struct devicelist * flashdev,char * filename)1636489Sjmcp fw_readfw(struct devicelist *flashdev, char *filename)
1646489Sjmcp {
1656489Sjmcp 
1666489Sjmcp 	logmsg(MSG_INFO,
1676846Sjmcp 	    "%s: not writing firmware for device %s to file %s\n",
1686846Sjmcp 	    flashdev->drvname, flashdev->access_devname, filename);
1696846Sjmcp 	logmsg(MSG_ERROR,
1706846Sjmcp 	    gettext("\n\nReading of firmware images from %s-attached "
1716846Sjmcp 	    "devices is not supported\n\n"),
1726846Sjmcp 	    flashdev->drvname);
1736489Sjmcp 
1746846Sjmcp 	return (FWFLASH_SUCCESS);
1756489Sjmcp }
1766489Sjmcp 
1776489Sjmcp 
1786489Sjmcp /*
1796489Sjmcp  * If we're invoking fw_writefw, then flashdev is a valid,
1806489Sjmcp  * flashable device supporting the SES2 Download Microcode Diagnostic
1816489Sjmcp  * Control page (0x0e).
1826489Sjmcp  *
1836489Sjmcp  * If verifier is null, then we haven't been called following a firmware
1846489Sjmcp  * image verification load operation.
1856489Sjmcp  *
1866489Sjmcp  * *THIS* function uses scsi SEND DIAGNOSTIC/download microcode to
1876489Sjmcp  * achieve the task... if you chase down to the bottom of libses you
1886489Sjmcp  * can see that too.
1896489Sjmcp  */
1906489Sjmcp int
fw_writefw(struct devicelist * flashdev)1916489Sjmcp fw_writefw(struct devicelist *flashdev)
1926489Sjmcp {
193*9683SXin.Chen@Sun.COM 	int rv = FWFLASH_FAILURE;
1946489Sjmcp 	nvlist_t *nvl;
1956489Sjmcp 	ses_snap_t *snapshot;
1966846Sjmcp 	ses_node_t *targetnode;
1976489Sjmcp 
1986846Sjmcp 	if ((verifier == NULL) || (verifier->imgsize == 0) ||
1996846Sjmcp 	    (verifier->fwimage == NULL)) {
2006846Sjmcp 		/* should _not_ happen */
2016846Sjmcp 		logmsg(MSG_ERROR,
2026846Sjmcp 		    gettext("%s: Firmware image has not "
2036846Sjmcp 		    "been verified.\n"),
2046846Sjmcp 		    flashdev->drvname);
2056846Sjmcp 		return (FWFLASH_FAILURE);
2066846Sjmcp 	}
2076489Sjmcp 
2086489Sjmcp 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
2096489Sjmcp 	    nvlist_add_uint64(nvl, SES_CTL_PROP_UCODE_MODE,
2106489Sjmcp 	    SES_DLUCODE_M_WITH_OFFS) != 0) {
2116846Sjmcp 		logmsg(MSG_ERROR,
2126846Sjmcp 		    gettext("%s: Unable to allocate "
2136846Sjmcp 		    "space for device prop list\n"),
2146846Sjmcp 		    flashdev->drvname);
2156489Sjmcp 		return (FWFLASH_FAILURE);
2166489Sjmcp 	}
2176489Sjmcp 
2186489Sjmcp 	fprintf(stdout, "\n"); /* get a fresh line for progress updates */
2196489Sjmcp 
2206489Sjmcp 	if (nvlist_add_uint64(nvl, SES_CTL_PROP_UCODE_BUFID,
2216489Sjmcp 	    verifier->flashbuf) != 0) {
2226846Sjmcp 		logmsg(MSG_ERROR,
2236846Sjmcp 		    gettext("%s: Unable to add buffer id "
2246846Sjmcp 		    "property, hence unable to flash device\n"),
2256846Sjmcp 		    flashdev->drvname);
2266489Sjmcp 		goto cancel;
2276489Sjmcp 	}
2286489Sjmcp 
2296489Sjmcp 	if (nvlist_add_byte_array(nvl, SES_CTL_PROP_UCODE_DATA,
2306489Sjmcp 	    (uint8_t *)verifier->fwimage, verifier->imgsize) != 0) {
2316489Sjmcp 		logmsg(MSG_ERROR,
2326489Sjmcp 		    "%s: Out of memory for property addition\n",
2336846Sjmcp 		    flashdev->drvname);
2346489Sjmcp 		goto cancel;
2356489Sjmcp 	}
2366489Sjmcp 
2376489Sjmcp 	if ((ses_target =
2386489Sjmcp 	    ses_open(LIBSES_VERSION, flashdev->access_devname)) == NULL) {
2396489Sjmcp 		logmsg(MSG_ERROR,
2406846Sjmcp 		    gettext("%s: Unable to open flashable device %s\n"),
2416846Sjmcp 		    flashdev->drvname, flashdev->access_devname);
2426489Sjmcp 		goto cancel;
2436489Sjmcp 	}
2446846Sjmcp 
2456489Sjmcp 	snapshot = ses_snap_hold(ses_target);
2466489Sjmcp 
2476846Sjmcp 	if ((targetnode = ses_snap_primary_enclosure(snapshot)) == NULL) {
2486846Sjmcp 		logmsg(MSG_ERROR,
2496846Sjmcp 		    gettext("%s: Unable to locate primary enclosure for "
2506846Sjmcp 		    "device %s\n"),
2516846Sjmcp 		    flashdev->access_devname);
2526846Sjmcp 	} else {
2536846Sjmcp 		rv = sendimg(targetnode, nvl);
2546846Sjmcp 		if (rv == FWFLASH_SUCCESS) {
2556846Sjmcp 			logmsg(MSG_ERROR,
2566846Sjmcp 			    gettext("%s: Done. New image will be active "
2576846Sjmcp 			    "after the system is rebooted.\n\n"),
2586846Sjmcp 			    flashdev->drvname);
2596846Sjmcp 		} else {
2606846Sjmcp 			logmsg(MSG_INFO,
2616846Sjmcp 			    "%s: unable to flash image %s to device %s\n\n",
2626846Sjmcp 			    flashdev->drvname, verifier->imgfile,
2636846Sjmcp 			    flashdev->access_devname);
2646846Sjmcp 		}
2656846Sjmcp 	}
2666489Sjmcp 
2676489Sjmcp 	ses_snap_rele(snapshot);
2686489Sjmcp 	ses_close(ses_target);
2696489Sjmcp cancel:
2706489Sjmcp 	nvlist_free(nvl);
2716489Sjmcp 
272*9683SXin.Chen@Sun.COM 	return (rv);
2736489Sjmcp }
2746489Sjmcp 
2756489Sjmcp 
2766489Sjmcp /*
2776489Sjmcp  * The fw_identify() function walks the device
2786489Sjmcp  * tree trying to find devices which this plugin
2796489Sjmcp  * can work with.
2806489Sjmcp  *
2816489Sjmcp  * The parameter "start" gives us the starting index number
2826489Sjmcp  * to give the device when we add it to the fw_devices list.
2836489Sjmcp  *
2846489Sjmcp  * firstdev is allocated by us and we add space as needed
2856489Sjmcp  */
2866489Sjmcp int
fw_identify(int start)2876489Sjmcp fw_identify(int start)
2886489Sjmcp {
2896489Sjmcp 
2906489Sjmcp 	int rv = FWFLASH_FAILURE;
2916489Sjmcp 	di_node_t thisnode;
2926489Sjmcp 	struct devicelist *newdev;
2936489Sjmcp 	char *devpath;
2946846Sjmcp 	char *devsuffix;
2956846Sjmcp 	char *driver;
2966489Sjmcp 	int idx = start;
2977317SJames.McPherson@Sun.COM 	size_t devlength = 0;
2986846Sjmcp 	nvlist_t *props;
2996846Sjmcp 	ses_snap_t *snapshot;
3006846Sjmcp 	ses_node_t *rootnodep, *nodep;
3016489Sjmcp 
3026489Sjmcp 
3036846Sjmcp 	if (strcmp(self->drvname, "sgen") == 0) {
3046846Sjmcp 		devsuffix = sgensuffix;
3056846Sjmcp 		driver = self->drvname;
3066846Sjmcp 	} else {
3076846Sjmcp 		devsuffix = sessuffix;
3086846Sjmcp 		driver = drivername;
3096846Sjmcp 	}
3106846Sjmcp 
3116846Sjmcp 	thisnode = di_drv_first_node(driver, rootnode);
3126489Sjmcp 
3136489Sjmcp 	if (thisnode == DI_NODE_NIL) {
3146489Sjmcp 		logmsg(MSG_INFO, gettext("No %s nodes in this system\n"),
3156846Sjmcp 		    driver);
3167317SJames.McPherson@Sun.COM 		return (FWFLASH_FAILURE);
3176489Sjmcp 	}
3186489Sjmcp 
3196489Sjmcp 	if ((devpath = calloc(1, MAXPATHLEN + 1)) == NULL) {
3206846Sjmcp 		logmsg(MSG_ERROR,
3216846Sjmcp 		    gettext("%s: Unable to allocate space "
3226846Sjmcp 		    "for a device node\n"),
3236846Sjmcp 		    driver);
3247317SJames.McPherson@Sun.COM 		return (FWFLASH_FAILURE);
3256489Sjmcp 	}
3266489Sjmcp 
3276489Sjmcp 	/* we've found one, at least */
3286489Sjmcp 
3296489Sjmcp 	for (; thisnode != DI_NODE_NIL; thisnode = di_drv_next_node(thisnode)) {
3306489Sjmcp 
3316489Sjmcp 		devpath = di_devfs_path(thisnode);
3326489Sjmcp 
3336489Sjmcp 		if ((newdev = calloc(1, sizeof (struct devicelist)))
3346489Sjmcp 		    == NULL) {
3356489Sjmcp 			logmsg(MSG_ERROR,
3366846Sjmcp 			    gettext("%s: identification function unable "
3376846Sjmcp 			    "to allocate space for device entry\n"),
3386846Sjmcp 			    driver);
3396846Sjmcp 			free(devpath);
3407317SJames.McPherson@Sun.COM 			return (FWFLASH_FAILURE);
3416489Sjmcp 		}
3426489Sjmcp 
3436846Sjmcp 		/* calloc enough for /devices + devpath + devsuffix + '\0' */
3446489Sjmcp 		devlength = strlen(devpath) + strlen(devprefix) +
3456489Sjmcp 		    strlen(devsuffix) + 2;
3466489Sjmcp 
3476489Sjmcp 		if ((newdev->access_devname = calloc(1, devlength)) == NULL) {
3486846Sjmcp 			logmsg(MSG_ERROR,
3496846Sjmcp 			    gettext("%s: Unable to allocate "
3506846Sjmcp 			    "space for a devfs name\n"),
3516846Sjmcp 			    driver);
3526489Sjmcp 			free(devpath);
3536846Sjmcp 			free(newdev);
3546489Sjmcp 			return (FWFLASH_FAILURE);
3556489Sjmcp 		}
3566489Sjmcp 		snprintf(newdev->access_devname, devlength,
3576489Sjmcp 		    "%s%s%s", devprefix, devpath, devsuffix);
3586489Sjmcp 
3596846Sjmcp 		if ((newdev->drvname = calloc(1, strlen(driver) + 1))
3606489Sjmcp 		    == NULL) {
3616846Sjmcp 			logmsg(MSG_ERROR,
3626846Sjmcp 			    gettext("%s: Unable to allocate "
3636846Sjmcp 			    "space to store a driver name\n"),
3646846Sjmcp 			    driver);
3656489Sjmcp 			free(newdev->access_devname);
3666489Sjmcp 			free(newdev);
3676846Sjmcp 			free(devpath);
3686846Sjmcp 			return (FWFLASH_FAILURE);
3696846Sjmcp 		}
3706846Sjmcp 		(void) strlcpy(newdev->drvname, driver,
3716846Sjmcp 		    strlen(driver) + 1);
3726846Sjmcp 
3736846Sjmcp 		if ((newdev->classname = calloc(1, strlen(driver) + 1))
3746846Sjmcp 		    == NULL) {
3756846Sjmcp 			logmsg(MSG_ERROR,
3766846Sjmcp 			    gettext("%s: Unable to malloc "
3776846Sjmcp 			    "space for a class name\n"),
3786846Sjmcp 			    drivername);
3796846Sjmcp 			free(newdev->access_devname);
3806846Sjmcp 			free(newdev->drvname);
3816846Sjmcp 			free(newdev);
3826846Sjmcp 			free(devpath);
3836489Sjmcp 			return (FWFLASH_FAILURE);
3846489Sjmcp 		}
3856846Sjmcp 		(void) strlcpy(newdev->classname, driver,
3866846Sjmcp 		    strlen(driver) + 1);
3876489Sjmcp 
3886846Sjmcp 		/*
3896846Sjmcp 		 * Only alloc as much as we truly need, and DON'T forget
3906846Sjmcp 		 * that libnvpair manages the memory for property lookups!
3916846Sjmcp 		 * The same goes for libdevinfo properties.
3926846Sjmcp 		 *
3936846Sjmcp 		 * Also note that we're allocating here before we try to
3946846Sjmcp 		 * ses_open() the target, because if we can't allocate
3956846Sjmcp 		 * sufficient space then we might as well go home.
3966846Sjmcp 		 */
3976846Sjmcp 		newdev->ident = calloc(1, VIDLEN + PIDLEN + REVLEN + 3);
3986846Sjmcp 		if (newdev->ident == NULL) {
3996846Sjmcp 			logmsg(MSG_ERROR,
4006846Sjmcp 			    gettext("%s: Unable to malloc space for"
4016846Sjmcp 			    "SCSI INQUIRY data\n"), driver);
4026846Sjmcp 			free(newdev->classname);
4036846Sjmcp 			free(newdev->drvname);
4046846Sjmcp 			free(newdev->access_devname);
4056846Sjmcp 			free(newdev);
4066846Sjmcp 			free(devpath);
4076846Sjmcp 			return (FWFLASH_FAILURE);
4086846Sjmcp 		}
4096846Sjmcp 
4106846Sjmcp 		if ((ses_target =
4116846Sjmcp 		    ses_open(LIBSES_VERSION, newdev->access_devname))
4126489Sjmcp 		    == NULL) {
4136846Sjmcp 			logmsg(MSG_INFO,
4146846Sjmcp 			    gettext("%s: Unable to open device %s\n"),
4156846Sjmcp 			    driver, newdev->access_devname);
4166846Sjmcp 			free(newdev->ident);
4176846Sjmcp 			free(newdev->classname);
4186489Sjmcp 			free(newdev->access_devname);
4196489Sjmcp 			free(newdev->drvname);
4206489Sjmcp 			free(newdev);
4216846Sjmcp 			free(devpath);
4226846Sjmcp 			continue;
4236489Sjmcp 		}
4246846Sjmcp 		snapshot = ses_snap_hold(ses_target);
4256846Sjmcp 		rootnodep = ses_root_node(snapshot);
4266489Sjmcp 
4276736Ssuha 		/*
4286846Sjmcp 		 * If the node has no properties, or the INQUIRY properties
4296846Sjmcp 		 * don't exist, this device does not comply with SES2 so we
4306846Sjmcp 		 * won't touch it.
4316736Ssuha 		 */
4326846Sjmcp 		if ((props = ses_node_props(rootnodep)) == NULL) {
4336846Sjmcp 			free(newdev->ident);
4346846Sjmcp 			ses_snap_rele(snapshot);
4356846Sjmcp 			ses_close(ses_target);
4366846Sjmcp 			free(newdev->classname);
4376489Sjmcp 			free(newdev->access_devname);
4386489Sjmcp 			free(newdev->drvname);
4396846Sjmcp 			free(newdev);
4406846Sjmcp 			free(devpath);
4416846Sjmcp 			continue;
4426846Sjmcp 		}
4436846Sjmcp 
4446846Sjmcp 		if ((nvlist_lookup_string(props, SCSI_PROP_VENDOR,
4456846Sjmcp 		    &newdev->ident->vid) != 0) ||
4466846Sjmcp 		    (nvlist_lookup_string(props, SCSI_PROP_PRODUCT,
4476846Sjmcp 		    &newdev->ident->pid) != 0) ||
4486846Sjmcp 		    (nvlist_lookup_string(props, SCSI_PROP_REVISION,
4496846Sjmcp 		    &newdev->ident->revid) != 0)) {
4506846Sjmcp 			free(newdev->ident);
4516846Sjmcp 			ses_snap_rele(snapshot);
4526846Sjmcp 			ses_close(ses_target);
4536736Ssuha 			free(newdev->classname);
4546846Sjmcp 			free(newdev->access_devname);
4556846Sjmcp 			free(newdev->drvname);
4566708Sjmcp 			free(newdev);
4576846Sjmcp 			free(devpath);
4586489Sjmcp 			continue;
4596489Sjmcp 		}
4606489Sjmcp 
4616846Sjmcp 		nodep = ses_snap_primary_enclosure(snapshot);
4626846Sjmcp 
4636846Sjmcp 		if ((props = ses_node_props(nodep)) == NULL) {
4646846Sjmcp 			free(newdev->ident);
4656846Sjmcp 			ses_snap_rele(snapshot);
4666846Sjmcp 			ses_close(ses_target);
4676846Sjmcp 			free(newdev->classname);
4686489Sjmcp 			free(newdev->access_devname);
4696489Sjmcp 			free(newdev->drvname);
4706489Sjmcp 			free(newdev);
4716846Sjmcp 			free(devpath);
4726489Sjmcp 			continue;
4736846Sjmcp 		}
4746846Sjmcp 
4756846Sjmcp 		logmsg(MSG_INFO,
4766846Sjmcp 		    "\nvid: %s\npid: %s\nrevid: %s\n",
4776846Sjmcp 		    newdev->ident->vid,
4786846Sjmcp 		    newdev->ident->pid,
4796846Sjmcp 		    newdev->ident->revid);
4806846Sjmcp 
4816846Sjmcp 		if (nvlist_lookup_string(props, LIBSES_EN_PROP_CSN,
4826846Sjmcp 		    &newdev->addresses[0]) == 0) {
4836846Sjmcp 			logmsg(MSG_INFO,
4846846Sjmcp 			    "Chassis Serial Number: %s\n",
4856846Sjmcp 			    newdev->addresses[0]);
4868920SPei-Hong.Huang@Sun.COM 		} else
4878920SPei-Hong.Huang@Sun.COM 			logmsg(MSG_INFO,
4888920SPei-Hong.Huang@Sun.COM 			    "%s: no chassis-serial-number property "
4898920SPei-Hong.Huang@Sun.COM 			    "for device %s\n",
4908920SPei-Hong.Huang@Sun.COM 			    driver, newdev->access_devname);
4916489Sjmcp 
4926489Sjmcp 
4937317SJames.McPherson@Sun.COM 		rv = di_prop_lookup_strings(DDI_DEV_T_ANY,
4947317SJames.McPherson@Sun.COM 		    thisnode, "target-port", &newdev->addresses[1]);
4957317SJames.McPherson@Sun.COM 		if (rv < 0) {
4966846Sjmcp 			logmsg(MSG_INFO,
4976846Sjmcp 			    "%s: no target-port property "
4986846Sjmcp 			    "for device %s\n",
4996846Sjmcp 			    driver, newdev->access_devname);
5006846Sjmcp 		} else
5016846Sjmcp 			logmsg(MSG_INFO,
5026846Sjmcp 			    "target-port property: %s\n",
5036846Sjmcp 			    newdev->addresses[1]);
5046846Sjmcp 
5056846Sjmcp 
5066489Sjmcp 		newdev->index = idx;
5076489Sjmcp 		++idx;
5086489Sjmcp 		newdev->plugin = self;
5096489Sjmcp 
5106846Sjmcp 		ses_snap_rele(snapshot);
5116489Sjmcp 		TAILQ_INSERT_TAIL(fw_devices, newdev, nextdev);
5126489Sjmcp 	}
5136489Sjmcp 
5146489Sjmcp 
5156489Sjmcp 	if (fwflash_debug != 0) {
5166489Sjmcp 		struct devicelist *tempdev;
5176489Sjmcp 
5186489Sjmcp 		TAILQ_FOREACH(tempdev, fw_devices, nextdev) {
5196846Sjmcp 			logmsg(MSG_INFO, "%s:fw_identify:\n",
5206846Sjmcp 			    driver);
5216846Sjmcp 			logmsg(MSG_INFO,
5226846Sjmcp 			    "\ttempdev @ 0x%lx\n"
5236489Sjmcp 			    "\t\taccess_devname: %s\n"
5246489Sjmcp 			    "\t\tdrvname: %s\tclassname: %s\n"
5256489Sjmcp 			    "\t\tident->vid:   %s\n"
5266489Sjmcp 			    "\t\tident->pid:   %s\n"
5276489Sjmcp 			    "\t\tident->revid: %s\n"
5286489Sjmcp 			    "\t\tindex:        %d\n"
5296489Sjmcp 			    "\t\taddress[0]:   %s\n"
5306489Sjmcp 			    "\t\taddress[1]:   %s\n"
5316489Sjmcp 			    "\t\tplugin @ 0x%lx\n\n",
5326489Sjmcp 			    &tempdev,
5336489Sjmcp 			    tempdev->access_devname,
5346489Sjmcp 			    tempdev->drvname, newdev->classname,
5356489Sjmcp 			    tempdev->ident->vid,
5366489Sjmcp 			    tempdev->ident->pid,
5376489Sjmcp 			    tempdev->ident->revid,
5386489Sjmcp 			    tempdev->index,
5398920SPei-Hong.Huang@Sun.COM 			    (tempdev->addresses[0] ? tempdev->addresses[0] :
5408920SPei-Hong.Huang@Sun.COM 			    "(not supported)"),
5418920SPei-Hong.Huang@Sun.COM 			    (tempdev->addresses[1] ? tempdev->addresses[1] :
5428920SPei-Hong.Huang@Sun.COM 			    "(not supported)"),
5436846Sjmcp 			    &tempdev->plugin);
5446489Sjmcp 		}
5456489Sjmcp 	}
5466489Sjmcp 
5476489Sjmcp 	return (FWFLASH_SUCCESS);
5486489Sjmcp }
5496489Sjmcp 
5506489Sjmcp 
5516489Sjmcp 
5526489Sjmcp int
fw_devinfo(struct devicelist * thisdev)5536489Sjmcp fw_devinfo(struct devicelist *thisdev)
5546489Sjmcp {
5556489Sjmcp 
5566489Sjmcp 	fprintf(stdout, gettext("Device[%d] %s\n  Class [%s]\n"),
5576489Sjmcp 	    thisdev->index, thisdev->access_devname, thisdev->classname);
5586489Sjmcp 
5596489Sjmcp 	fprintf(stdout,
5606489Sjmcp 	    gettext("\tVendor                 : %s\n"
5616489Sjmcp 	    "\tProduct                : %s\n"
5626489Sjmcp 	    "\tFirmware revision      : %s\n"
5636846Sjmcp 	    "\tChassis Serial Number  : %s\n"
5646489Sjmcp 	    "\tTarget-port identifier : %s\n"),
5656489Sjmcp 	    thisdev->ident->vid,
5666489Sjmcp 	    thisdev->ident->pid,
5676489Sjmcp 	    thisdev->ident->revid,
5688920SPei-Hong.Huang@Sun.COM 	    (thisdev->addresses[0] ? thisdev->addresses[0] :
5698920SPei-Hong.Huang@Sun.COM 	    "(not supported)"),
5708920SPei-Hong.Huang@Sun.COM 	    (thisdev->addresses[1] ? thisdev->addresses[1] :
5718920SPei-Hong.Huang@Sun.COM 	    "(not supported)"));
5726489Sjmcp 
5736489Sjmcp 	fprintf(stdout, "\n\n");
5746489Sjmcp 
5756489Sjmcp 	return (FWFLASH_SUCCESS);
5766489Sjmcp }
5776489Sjmcp 
5786489Sjmcp 
5796489Sjmcp 
5806489Sjmcp 
5816489Sjmcp 
5826489Sjmcp /*ARGSUSED*/
5836489Sjmcp static int
get_status(nvlist_t * props,ucode_status_t * sp)5846489Sjmcp get_status(nvlist_t *props, ucode_status_t *sp)
5856489Sjmcp {
5866489Sjmcp 	int i;
5876489Sjmcp 	uint64_t status, astatus;
5886489Sjmcp 
5896489Sjmcp 	if (nvlist_lookup_uint64(props, SES_EN_PROP_UCODE, &status) != 0) {
5906489Sjmcp 		sp->us_status = -1ULL;
5916489Sjmcp 		(void) snprintf(sp->us_desc, sizeof (sp->us_desc),
5926489Sjmcp 		    "not supported");
593*9683SXin.Chen@Sun.COM 		return (FWFLASH_FAILURE);
5946489Sjmcp 	}
5956489Sjmcp 
5966846Sjmcp 	if (nvlist_lookup_uint64(props, SES_EN_PROP_UCODE_A,
5976846Sjmcp 	    &astatus) != 0) {
5986846Sjmcp 		logmsg(MSG_ERROR,
5996846Sjmcp 		    gettext("\nError: Unable to retrieve current status\n"));
600*9683SXin.Chen@Sun.COM 		return (FWFLASH_FAILURE);
6016846Sjmcp 	}
6026489Sjmcp 
6036489Sjmcp 	for (i = 0; i < NUCODE_STATUS; i++) {
6046489Sjmcp 		if (ucode_statdesc_table[i].us_value == status)
6056489Sjmcp 			break;
6066489Sjmcp 	}
6076489Sjmcp 
6086489Sjmcp 	sp->us_status = status;
6096489Sjmcp 
6106489Sjmcp 	if (i == NUCODE_STATUS) {
6116489Sjmcp 		(void) snprintf(sp->us_desc, sizeof (sp->us_desc),
6126489Sjmcp 		    "unknown (0x%02x)", (int)status);
613*9683SXin.Chen@Sun.COM 		sp->us_iserr = sp->us_pending = B_TRUE;
614*9683SXin.Chen@Sun.COM 		return (FWFLASH_FAILURE);
6156489Sjmcp 	} else {
6166489Sjmcp 		/* LINTED */
6176489Sjmcp 		(void) snprintf(sp->us_desc, sizeof (sp->us_desc),
6186489Sjmcp 		    ucode_statdesc_table[i].us_desc, (int)astatus);
6196489Sjmcp 		sp->us_iserr = ucode_statdesc_table[i].us_iserr;
6206489Sjmcp 		sp->us_pending = ucode_statdesc_table[i].us_pending;
6216489Sjmcp 	}
6226489Sjmcp 
623*9683SXin.Chen@Sun.COM 	return (FWFLASH_SUCCESS);
6246489Sjmcp }
6256489Sjmcp 
6266489Sjmcp 
627*9683SXin.Chen@Sun.COM static int
print_updated_status(ses_node_t * np,void * arg)6286489Sjmcp print_updated_status(ses_node_t *np, void *arg)
6296489Sjmcp {
6306489Sjmcp 	ucode_wait_t *uwp = arg;
6316846Sjmcp 	nvlist_t *props;
6326489Sjmcp 	ucode_status_t status;
6336489Sjmcp 
6346489Sjmcp 
6356846Sjmcp 	if ((props = ses_node_props(np)) == NULL) {
636*9683SXin.Chen@Sun.COM 		return (FWFLASH_FAILURE);
6376846Sjmcp 	}
6386489Sjmcp 
639*9683SXin.Chen@Sun.COM 	if (get_status(props, &status) != FWFLASH_SUCCESS)
640*9683SXin.Chen@Sun.COM 		return (FWFLASH_FAILURE);
6416489Sjmcp 
6426489Sjmcp 	if (status.us_status != uwp->uw_prevstatus)
6436489Sjmcp 		(void) printf("%30s: %s\n", "status", status.us_desc);
6446846Sjmcp 
6456489Sjmcp 	uwp->uw_prevstatus = status.us_status;
6466489Sjmcp 	uwp->uw_pending = status.us_pending;
6476489Sjmcp 
6486846Sjmcp 	if (status.us_iserr) {
6496489Sjmcp 		logmsg(MSG_INFO,
6506846Sjmcp 		    "libses: status.us_iserr: 0x%0x\n",
6516489Sjmcp 		    status.us_iserr);
652*9683SXin.Chen@Sun.COM 		return (FWFLASH_FAILURE);
653*9683SXin.Chen@Sun.COM 	}
654*9683SXin.Chen@Sun.COM 	return (FWFLASH_SUCCESS);
6556489Sjmcp }
6566489Sjmcp 
6576489Sjmcp /*ARGSUSED*/
6586846Sjmcp static int
sendimg(ses_node_t * np,void * data)6596489Sjmcp sendimg(ses_node_t *np, void *data)
6606489Sjmcp {
6616489Sjmcp 	nvlist_t *props;
6626489Sjmcp 	nvlist_t *arg = data;
6636489Sjmcp 	char *vendor, *product, *revision, *csn;
6646489Sjmcp 	char buf[128];
6656489Sjmcp 	ses_snap_t *newsnap;
6666489Sjmcp 	int ret;
6676489Sjmcp 	ucode_status_t statdesc;
6686489Sjmcp 	ucode_wait_t wait;
6696489Sjmcp 	uint8_t *imagedata;
6706846Sjmcp 	uint_t len;
6716846Sjmcp 
6726489Sjmcp 
6736846Sjmcp 	/* If we've been called without data, eject */
6746846Sjmcp 	if (nvlist_lookup_byte_array(arg, SES_CTL_PROP_UCODE_DATA,
6756846Sjmcp 	    &imagedata, &len) != 0) {
6766846Sjmcp 		return (FWFLASH_FAILURE);
6776846Sjmcp 	}
6786489Sjmcp 
6796846Sjmcp 	props = ses_node_props(np);
6806846Sjmcp 	if ((props == NULL) ||
6816846Sjmcp 	    (nvlist_lookup_string(props, SES_EN_PROP_VID, &vendor) != 0) ||
6826846Sjmcp 	    (nvlist_lookup_string(props, SES_EN_PROP_PID, &product) != 0) ||
6836846Sjmcp 	    (nvlist_lookup_string(props, SES_EN_PROP_REV, &revision) != 0) ||
6846846Sjmcp 	    (nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, &csn) != 0)) {
6856846Sjmcp 		return (FWFLASH_FAILURE);
6866846Sjmcp 	}
6876489Sjmcp 
6886489Sjmcp 	(void) printf("%30s: %s\n", "vendor", vendor);
6896489Sjmcp 	(void) printf("%30s: %s\n", "product", product);
6906489Sjmcp 	(void) printf("%30s: %s\n", "revision", revision);
6916489Sjmcp 	(void) printf("%30s: %s\n", "serial", csn);
6926489Sjmcp 
6936489Sjmcp 	ret = get_status(props, &statdesc);
6946489Sjmcp 	(void) printf("%30s: %s\n", "current status", statdesc.us_desc);
695*9683SXin.Chen@Sun.COM 	if (ret != FWFLASH_SUCCESS) {
6966846Sjmcp 		return (FWFLASH_FAILURE);
6976489Sjmcp 	}
6986489Sjmcp 
6996489Sjmcp 	(void) snprintf(buf, sizeof (buf), "downloading %u bytes", len);
7006489Sjmcp 	(void) printf("\n%30s: ", buf);
7016846Sjmcp 
7026846Sjmcp 	/*
7036846Sjmcp 	 * If the bufferid isn't 2, then the verifier has already
7049560SPei-Hong.Huang@Sun.COM 	 * OK'd the image that the user has provided.
7056846Sjmcp 	 *
7066846Sjmcp 	 * At present the non-"standard" images need to be flashed
7076846Sjmcp 	 * using the scsi WRITE BUFFER command
7086846Sjmcp 	 */
7096846Sjmcp 	if (verifier->flashbuf != 2)
7106846Sjmcp 		return (scsi_writebuf());
7116846Sjmcp 
7126846Sjmcp 
713*9683SXin.Chen@Sun.COM 	if (ses_node_ctl(np, SES_CTL_OP_DL_UCODE, arg) != FWFLASH_SUCCESS) {
7146489Sjmcp 		(void) printf("failed!\n");
7156489Sjmcp 		(void) printf("%s\n", ses_errmsg());
7166846Sjmcp 		return (FWFLASH_FAILURE);
7176489Sjmcp 	} else {
7186489Sjmcp 		(void) printf("ok\n");
7196489Sjmcp 	}
7206489Sjmcp 
7216489Sjmcp 	wait.uw_prevstatus = -1ULL;
7226489Sjmcp 	wait.uw_oldnp = np;
7236846Sjmcp 
724*9683SXin.Chen@Sun.COM 	if ((newsnap = ses_snap_new(ses_target)) == NULL) {
7256846Sjmcp 		logmsg(MSG_ERROR,
7266846Sjmcp 		    "failed to update SES snapshot: %s",
7276846Sjmcp 		    ses_errmsg());
728*9683SXin.Chen@Sun.COM 		return (FWFLASH_FAILURE);
729*9683SXin.Chen@Sun.COM 	}
7306489Sjmcp 
7316846Sjmcp 	print_updated_status(ses_snap_primary_enclosure(newsnap),
7326846Sjmcp 	    &wait);
7336846Sjmcp 	ses_snap_rele(newsnap);
7346489Sjmcp 
735*9683SXin.Chen@Sun.COM 	return (FWFLASH_SUCCESS);
7366489Sjmcp }
7376489Sjmcp 
7386846Sjmcp static int
scsi_writebuf()7396846Sjmcp scsi_writebuf()
7406846Sjmcp {
7416846Sjmcp 	int ret;
7426846Sjmcp 	int i = 0;
7436846Sjmcp 	libscsi_action_t *action;
7446846Sjmcp 	spc3_write_buffer_cdb_t *wb_cdb;
7456846Sjmcp 	libscsi_hdl_t	*handle;
7466846Sjmcp 	libscsi_target_t *target;
7476846Sjmcp 	sam4_status_t samstatus;
7486736Ssuha 
7496736Ssuha 
7506846Sjmcp 	target = ses_scsi_target(ses_target);
7516846Sjmcp 	handle = libscsi_get_handle(target);
7526846Sjmcp 	action = libscsi_action_alloc(handle, SPC3_CMD_WRITE_BUFFER,
7536846Sjmcp 	    LIBSCSI_AF_WRITE|LIBSCSI_AF_RQSENSE,
7546846Sjmcp 	    (void *)verifier->fwimage, (size_t)verifier->imgsize);
7556736Ssuha 
7566846Sjmcp 	wb_cdb = (spc3_write_buffer_cdb_t *)libscsi_action_get_cdb(action);
757*9683SXin.Chen@Sun.COM 
7586846Sjmcp 	wb_cdb->wbc_mode = SPC3_WB_MODE_DATA;
7596846Sjmcp 	wb_cdb->wbc_bufferid = verifier->flashbuf;
760*9683SXin.Chen@Sun.COM 
761*9683SXin.Chen@Sun.COM 	wb_cdb->wbc_buffer_offset[0] = 0;
762*9683SXin.Chen@Sun.COM 	wb_cdb->wbc_buffer_offset[1] = 0;
763*9683SXin.Chen@Sun.COM 	wb_cdb->wbc_buffer_offset[2] = 0;
764*9683SXin.Chen@Sun.COM 
765*9683SXin.Chen@Sun.COM 	wb_cdb->wbc_parameter_list_len[0] =
766*9683SXin.Chen@Sun.COM 	    (verifier->imgsize & 0xff0000) >> 16;
767*9683SXin.Chen@Sun.COM 	wb_cdb->wbc_parameter_list_len[1] = (verifier->imgsize & 0xff00) >> 8;
768*9683SXin.Chen@Sun.COM 	wb_cdb->wbc_parameter_list_len[2] = (verifier->imgsize & 0xff);
7696846Sjmcp 
7706846Sjmcp 	ret = libscsi_exec(action, target);
7716846Sjmcp 	samstatus = libscsi_action_get_status(action);
7726736Ssuha 
7736736Ssuha 	logmsg(MSG_INFO,
7746846Sjmcp 	    "\nscsi_writebuffer: ret 0x%0x, samstatus 0x%0x\n",
7756846Sjmcp 	    ret, samstatus);
7766736Ssuha 
777*9683SXin.Chen@Sun.COM 	if ((ret != FWFLASH_SUCCESS) || samstatus != SAM4_STATUS_GOOD) {
7786846Sjmcp 		libscsi_action_free(action);
779*9683SXin.Chen@Sun.COM 		return (FWFLASH_FAILURE);
7806846Sjmcp 	} else {
7816846Sjmcp 		(void) printf("ok\n");
7826736Ssuha 	}
7836736Ssuha 
7846846Sjmcp 	for (i = 0; i < NSAM4_STATUS; i++) {
7856846Sjmcp 		if (sam4_status[i].status == samstatus) {
7866846Sjmcp 			(void) printf("%s\n", (sam4_status[i].message));
7876846Sjmcp 			break;
7886846Sjmcp 		}
7896736Ssuha 	}
7906736Ssuha 
7916846Sjmcp 	if (i == NSAM4_STATUS)
7926846Sjmcp 		(void) printf("Status: UNKNOWN\n");
7936736Ssuha 
7946846Sjmcp 	if (samstatus == SAM4_STATUS_GOOD) {
7956846Sjmcp 		return (FWFLASH_SUCCESS);
7966736Ssuha 	}
7976736Ssuha 
7986846Sjmcp 	return (FWFLASH_FAILURE);
7996489Sjmcp }
800