15a7763bfSjmcp /*
25a7763bfSjmcp  * CDDL HEADER START
35a7763bfSjmcp  *
45a7763bfSjmcp  * The contents of this file are subject to the terms of the
55a7763bfSjmcp  * Common Development and Distribution License (the "License").
65a7763bfSjmcp  * You may not use this file except in compliance with the License.
75a7763bfSjmcp  *
85a7763bfSjmcp  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95a7763bfSjmcp  * or http://www.opensolaris.org/os/licensing.
105a7763bfSjmcp  * See the License for the specific language governing permissions
115a7763bfSjmcp  * and limitations under the License.
125a7763bfSjmcp  *
135a7763bfSjmcp  * When distributing Covered Code, include this CDDL HEADER in each
145a7763bfSjmcp  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155a7763bfSjmcp  * If applicable, add the following below this CDDL HEADER, with the
165a7763bfSjmcp  * fields enclosed by brackets "[]" replaced with your own identifying
175a7763bfSjmcp  * information: Portions Copyright [yyyy] [name of copyright owner]
185a7763bfSjmcp  *
195a7763bfSjmcp  * CDDL HEADER END
205a7763bfSjmcp  */
215a7763bfSjmcp /*
220f59e5a7Speihong huang  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
235a7763bfSjmcp  * Use is subject to license terms.
245a7763bfSjmcp  */
255a7763bfSjmcp 
265a7763bfSjmcp /*
275a7763bfSjmcp  * ses (SCSI Generic Device) specific functions.
285a7763bfSjmcp  */
295a7763bfSjmcp 
305a7763bfSjmcp #include <libnvpair.h>
315a7763bfSjmcp #include <stdio.h>
325a7763bfSjmcp #include <stdlib.h>
335a7763bfSjmcp #include <unistd.h>
345a7763bfSjmcp #include <sys/types.h>
355a7763bfSjmcp #include <sys/sysmacros.h>
365a7763bfSjmcp #include <sys/queue.h>
375a7763bfSjmcp #include <fcntl.h>
385a7763bfSjmcp #include <string.h>
39c4800545Sjmcp #include <scsi/libscsi.h>
405a7763bfSjmcp #include <scsi/libses.h>
415a7763bfSjmcp #include <libintl.h> /* for gettext(3c) */
425a7763bfSjmcp #include <fwflash/fwflash.h>
435a7763bfSjmcp 
445a7763bfSjmcp 
455a7763bfSjmcp #define	VIDLEN		0x08
465a7763bfSjmcp #define	PIDLEN		0x10
475a7763bfSjmcp #define	REVLEN		0x04
485a7763bfSjmcp #define	SASADDRLEN	0x10
495a7763bfSjmcp #define	PCBUFLEN	0x40
505a7763bfSjmcp #define	RQBUFLEN	0xfe
515a7763bfSjmcp #define	STATBUFLEN	0xfe
525a7763bfSjmcp #define	INQBUFLEN	0x80
535a7763bfSjmcp 
545a7763bfSjmcp /* useful defines */
555a7763bfSjmcp #define	UCODE_CHECK_STATUS	0
565a7763bfSjmcp #define	UCODE_CHECK_SUPPORTED	1
575a7763bfSjmcp 
585a7763bfSjmcp typedef struct ucode_statdesc {
595a7763bfSjmcp 	uint64_t	us_value;
605a7763bfSjmcp 	const char	*us_desc;
615a7763bfSjmcp 	boolean_t	us_pending;
625a7763bfSjmcp 	boolean_t	us_iserr;
635a7763bfSjmcp } ucode_statdesc_t;
645a7763bfSjmcp 
655a7763bfSjmcp static ucode_statdesc_t ucode_statdesc_table[] = {
665a7763bfSjmcp 	{ SES2_DLUCODE_S_NOP,		"none",	B_FALSE, B_FALSE },
675a7763bfSjmcp 	{ SES2_DLUCODE_S_INPROGRESS,	"in progress", B_TRUE, B_FALSE },
685a7763bfSjmcp 	{ SES2_DLUCODE_S_SAVING,	"saved", B_TRUE, B_FALSE },
695a7763bfSjmcp 	{ SES2_DLUCODE_S_COMPLETE_NOW,	"completed (available)", B_FALSE,
705a7763bfSjmcp 	    B_FALSE },
715a7763bfSjmcp 	{ SES2_DLUCODE_S_COMPLETE_AT_RESET,
725a7763bfSjmcp 	    "completed (need reset or power on)", B_FALSE, B_FALSE },
735a7763bfSjmcp 	{ SES2_DLUCODE_S_COMPLETE_AT_POWERON,	"completed (need power on)",
745a7763bfSjmcp 	    B_FALSE, B_FALSE },
755a7763bfSjmcp 	{ SES2_DLUCODE_S_PAGE_ERR,	"page error (offset %d)",
765a7763bfSjmcp 	    B_FALSE, B_TRUE },
775a7763bfSjmcp 	{ SES2_DLUCODE_S_IMAGE_ERR,	"invalid image",
785a7763bfSjmcp 	    B_FALSE, B_TRUE },
795a7763bfSjmcp 	{ SES2_DLUCODE_S_TIMEOUT,	"download timeout",
805a7763bfSjmcp 	    B_FALSE, B_TRUE },
815a7763bfSjmcp 	{ SES2_DLUCODE_S_INTERNAL_NEEDIMAGE,
825a7763bfSjmcp 	    "internal error (NEED NEW IMAGE BEFORE RESET)",
835a7763bfSjmcp 	    B_FALSE, B_TRUE },
845a7763bfSjmcp 	{ SES2_DLUCODE_S_INTERNAL_SAFE,
855a7763bfSjmcp 	    "internal error (reset to revert to backup)",
865a7763bfSjmcp 	    B_FALSE, B_TRUE },
875a7763bfSjmcp };
885a7763bfSjmcp 
895a7763bfSjmcp #define	NUCODE_STATUS	\
905a7763bfSjmcp 	(sizeof (ucode_statdesc_table) / sizeof (ucode_statdesc_table[0]))
915a7763bfSjmcp 
925a7763bfSjmcp typedef struct ucode_status {
935a7763bfSjmcp 	uint64_t	us_status;
945a7763bfSjmcp 	boolean_t	us_iserr;
955a7763bfSjmcp 	boolean_t	us_pending;
965a7763bfSjmcp 	char		us_desc[128];
975a7763bfSjmcp } ucode_status_t;
985a7763bfSjmcp 
995a7763bfSjmcp typedef struct ucode_wait {
1005a7763bfSjmcp 	uint64_t	uw_prevstatus;
1015a7763bfSjmcp 	boolean_t	uw_pending;
1025a7763bfSjmcp 	ses_node_t	*uw_oldnp;
1035a7763bfSjmcp } ucode_wait_t;
1045a7763bfSjmcp 
1055a7763bfSjmcp 
106c4800545Sjmcp typedef struct sam4_statdesc {
107c4800545Sjmcp 	int status;
108c4800545Sjmcp 	char *message;
109c4800545Sjmcp } sam4_statdesc_t;
110c4800545Sjmcp 
111c4800545Sjmcp 
112c4800545Sjmcp static sam4_statdesc_t sam4_status[] = {
113c4800545Sjmcp 	{ SAM4_STATUS_GOOD, "Status: GOOD (success)" },
114c4800545Sjmcp 	{ SAM4_STATUS_CHECK_CONDITION, "Status: CHECK CONDITION" },
115c4800545Sjmcp 	{ SAM4_STATUS_CONDITION_MET, "Status: CONDITION MET" },
116c4800545Sjmcp 	{ SAM4_STATUS_BUSY, "Status: Device is BUSY" },
117c4800545Sjmcp 	{ SAM4_STATUS_RESERVATION_CONFLICT, "Status: Device is RESERVED" },
118c4800545Sjmcp 	{ SAM4_STATUS_TASK_SET_FULL,
119c4800545Sjmcp 	    "Status: TASK SET FULL (insufficient resources in command queue" },
120c4800545Sjmcp 	{ SAM4_STATUS_TASK_ABORTED, "Status: TASK ABORTED" },
121c4800545Sjmcp 	{ NULL, NULL }
122c4800545Sjmcp };
123c4800545Sjmcp 
124c4800545Sjmcp #define	NSAM4_STATUS	\
125c4800545Sjmcp 	(sizeof (sam4_status) / sizeof (sam4_status[0]))
126c4800545Sjmcp 
127c4800545Sjmcp 
128c4800545Sjmcp 
1295a7763bfSjmcp char drivername[] = "ses\0";
1305a7763bfSjmcp static char *devprefix = "/devices";
131c4800545Sjmcp static char *sessuffix = ":0";
132c4800545Sjmcp static char *sgensuffix = ":ses";
133c4800545Sjmcp 
134d73e86dbSsuha 
135c4800545Sjmcp static ses_target_t *ses_target;
1365a7763bfSjmcp 
1375a7763bfSjmcp extern di_node_t rootnode;
1385a7763bfSjmcp extern int errno;
1395a7763bfSjmcp extern struct fw_plugin *self;
1405a7763bfSjmcp extern struct vrfyplugin *verifier;
1415a7763bfSjmcp extern int fwflash_debug;
1425a7763bfSjmcp 
1435a7763bfSjmcp 
1445a7763bfSjmcp /* required functions for this plugin */
1455a7763bfSjmcp int fw_readfw(struct devicelist *device, char *filename);
1465a7763bfSjmcp int fw_writefw(struct devicelist *device);
1475a7763bfSjmcp int fw_identify(int start);
1485a7763bfSjmcp int fw_devinfo(struct devicelist *thisdev);
1495a7763bfSjmcp 
1505a7763bfSjmcp 
1515a7763bfSjmcp /* helper functions */
152*d65b419eSXinChen static int print_updated_status(ses_node_t *np, void *arg);
1535a7763bfSjmcp static int get_status(nvlist_t *props, ucode_status_t *sp);
154c4800545Sjmcp static int sendimg(ses_node_t *np, void *data);
155c4800545Sjmcp static int scsi_writebuf();
1565a7763bfSjmcp 
1575a7763bfSjmcp /*
158c4800545Sjmcp  * We don't currently support reading firmware from a SAS
159c4800545Sjmcp  * expander. If we do eventually support it, we would use
160c4800545Sjmcp  * the scsi READ BUFFER command to do so.
1615a7763bfSjmcp  */
1625a7763bfSjmcp int
1635a7763bfSjmcp fw_readfw(struct devicelist *flashdev, char *filename)
1645a7763bfSjmcp {
1655a7763bfSjmcp 
1665a7763bfSjmcp 	logmsg(MSG_INFO,
167c4800545Sjmcp 	    "%s: not writing firmware for device %s to file %s\n",
168c4800545Sjmcp 	    flashdev->drvname, flashdev->access_devname, filename);
169c4800545Sjmcp 	logmsg(MSG_ERROR,
170c4800545Sjmcp 	    gettext("\n\nReading of firmware images from %s-attached "
171c4800545Sjmcp 	    "devices is not supported\n\n"),
172c4800545Sjmcp 	    flashdev->drvname);
1735a7763bfSjmcp 
174c4800545Sjmcp 	return (FWFLASH_SUCCESS);
1755a7763bfSjmcp }
1765a7763bfSjmcp 
1775a7763bfSjmcp 
1785a7763bfSjmcp /*
1795a7763bfSjmcp  * If we're invoking fw_writefw, then flashdev is a valid,
1805a7763bfSjmcp  * flashable device supporting the SES2 Download Microcode Diagnostic
1815a7763bfSjmcp  * Control page (0x0e).
1825a7763bfSjmcp  *
1835a7763bfSjmcp  * If verifier is null, then we haven't been called following a firmware
1845a7763bfSjmcp  * image verification load operation.
1855a7763bfSjmcp  *
1865a7763bfSjmcp  * *THIS* function uses scsi SEND DIAGNOSTIC/download microcode to
1875a7763bfSjmcp  * achieve the task... if you chase down to the bottom of libses you
1885a7763bfSjmcp  * can see that too.
1895a7763bfSjmcp  */
1905a7763bfSjmcp int
1915a7763bfSjmcp fw_writefw(struct devicelist *flashdev)
1925a7763bfSjmcp {
193*d65b419eSXinChen 	int rv = FWFLASH_FAILURE;
1945a7763bfSjmcp 	nvlist_t *nvl;
1955a7763bfSjmcp 	ses_snap_t *snapshot;
196c4800545Sjmcp 	ses_node_t *targetnode;
1975a7763bfSjmcp 
198c4800545Sjmcp 	if ((verifier == NULL) || (verifier->imgsize == 0) ||
199c4800545Sjmcp 	    (verifier->fwimage == NULL)) {
200c4800545Sjmcp 		/* should _not_ happen */
201c4800545Sjmcp 		logmsg(MSG_ERROR,
202c4800545Sjmcp 		    gettext("%s: Firmware image has not "
203c4800545Sjmcp 		    "been verified.\n"),
204c4800545Sjmcp 		    flashdev->drvname);
205c4800545Sjmcp 		return (FWFLASH_FAILURE);
206c4800545Sjmcp 	}
2075a7763bfSjmcp 
2085a7763bfSjmcp 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
2095a7763bfSjmcp 	    nvlist_add_uint64(nvl, SES_CTL_PROP_UCODE_MODE,
2105a7763bfSjmcp 	    SES_DLUCODE_M_WITH_OFFS) != 0) {
211c4800545Sjmcp 		logmsg(MSG_ERROR,
212c4800545Sjmcp 		    gettext("%s: Unable to allocate "
213c4800545Sjmcp 		    "space for device prop list\n"),
214c4800545Sjmcp 		    flashdev->drvname);
2155a7763bfSjmcp 		return (FWFLASH_FAILURE);
2165a7763bfSjmcp 	}
2175a7763bfSjmcp 
2185a7763bfSjmcp 	fprintf(stdout, "\n"); /* get a fresh line for progress updates */
2195a7763bfSjmcp 
2205a7763bfSjmcp 	if (nvlist_add_uint64(nvl, SES_CTL_PROP_UCODE_BUFID,
2215a7763bfSjmcp 	    verifier->flashbuf) != 0) {
222c4800545Sjmcp 		logmsg(MSG_ERROR,
223c4800545Sjmcp 		    gettext("%s: Unable to add buffer id "
224c4800545Sjmcp 		    "property, hence unable to flash device\n"),
225c4800545Sjmcp 		    flashdev->drvname);
2265a7763bfSjmcp 		goto cancel;
2275a7763bfSjmcp 	}
2285a7763bfSjmcp 
2295a7763bfSjmcp 	if (nvlist_add_byte_array(nvl, SES_CTL_PROP_UCODE_DATA,
2305a7763bfSjmcp 	    (uint8_t *)verifier->fwimage, verifier->imgsize) != 0) {
2315a7763bfSjmcp 		logmsg(MSG_ERROR,
2325a7763bfSjmcp 		    "%s: Out of memory for property addition\n",
233c4800545Sjmcp 		    flashdev->drvname);
2345a7763bfSjmcp 		goto cancel;
2355a7763bfSjmcp 	}
2365a7763bfSjmcp 
2375a7763bfSjmcp 	if ((ses_target =
2385a7763bfSjmcp 	    ses_open(LIBSES_VERSION, flashdev->access_devname)) == NULL) {
2395a7763bfSjmcp 		logmsg(MSG_ERROR,
240c4800545Sjmcp 		    gettext("%s: Unable to open flashable device %s\n"),
241c4800545Sjmcp 		    flashdev->drvname, flashdev->access_devname);
2425a7763bfSjmcp 		goto cancel;
2435a7763bfSjmcp 	}
2445a7763bfSjmcp 
245c4800545Sjmcp 	snapshot = ses_snap_hold(ses_target);
246d73e86dbSsuha 
247c4800545Sjmcp 	if ((targetnode = ses_snap_primary_enclosure(snapshot)) == NULL) {
248c4800545Sjmcp 		logmsg(MSG_ERROR,
249c4800545Sjmcp 		    gettext("%s: Unable to locate primary enclosure for "
250c4800545Sjmcp 		    "device %s\n"),
251c4800545Sjmcp 		    flashdev->access_devname);
252c4800545Sjmcp 	} else {
253c4800545Sjmcp 		rv = sendimg(targetnode, nvl);
254c4800545Sjmcp 		if (rv == FWFLASH_SUCCESS) {
255c4800545Sjmcp 			logmsg(MSG_ERROR,
256c4800545Sjmcp 			    gettext("%s: Done. New image will be active "
257c4800545Sjmcp 			    "after the system is rebooted.\n\n"),
258c4800545Sjmcp 			    flashdev->drvname);
259c4800545Sjmcp 		} else {
260c4800545Sjmcp 			logmsg(MSG_INFO,
261c4800545Sjmcp 			    "%s: unable to flash image %s to device %s\n\n",
262c4800545Sjmcp 			    flashdev->drvname, verifier->imgfile,
263c4800545Sjmcp 			    flashdev->access_devname);
264c4800545Sjmcp 		}
265c4800545Sjmcp 	}
2665a7763bfSjmcp 
2675a7763bfSjmcp 	ses_snap_rele(snapshot);
2685a7763bfSjmcp 	ses_close(ses_target);
2695a7763bfSjmcp cancel:
2705a7763bfSjmcp 	nvlist_free(nvl);
2715a7763bfSjmcp 
272*d65b419eSXinChen 	return (rv);
2735a7763bfSjmcp }
2745a7763bfSjmcp 
2755a7763bfSjmcp 
2765a7763bfSjmcp /*
2775a7763bfSjmcp  * The fw_identify() function walks the device
2785a7763bfSjmcp  * tree trying to find devices which this plugin
2795a7763bfSjmcp  * can work with.
2805a7763bfSjmcp  *
2815a7763bfSjmcp  * The parameter "start" gives us the starting index number
2825a7763bfSjmcp  * to give the device when we add it to the fw_devices list.
2835a7763bfSjmcp  *
2845a7763bfSjmcp  * firstdev is allocated by us and we add space as needed
2855a7763bfSjmcp  */
2865a7763bfSjmcp int
2875a7763bfSjmcp fw_identify(int start)
2885a7763bfSjmcp {
2895a7763bfSjmcp 
2905a7763bfSjmcp 	int rv = FWFLASH_FAILURE;
2915a7763bfSjmcp 	di_node_t thisnode;
2925a7763bfSjmcp 	struct devicelist *newdev;
2935a7763bfSjmcp 	char *devpath;
294c4800545Sjmcp 	char *devsuffix;
295c4800545Sjmcp 	char *driver;
2965a7763bfSjmcp 	int idx = start;
297f1c23465SJames C. McPherson 	size_t devlength = 0;
298c4800545Sjmcp 	nvlist_t *props;
299c4800545Sjmcp 	ses_snap_t *snapshot;
300c4800545Sjmcp 	ses_node_t *rootnodep, *nodep;
301c4800545Sjmcp 
3025a7763bfSjmcp 
303c4800545Sjmcp 	if (strcmp(self->drvname, "sgen") == 0) {
304c4800545Sjmcp 		devsuffix = sgensuffix;
305c4800545Sjmcp 		driver = self->drvname;
306c4800545Sjmcp 	} else {
307c4800545Sjmcp 		devsuffix = sessuffix;
308c4800545Sjmcp 		driver = drivername;
309c4800545Sjmcp 	}
3105a7763bfSjmcp 
311c4800545Sjmcp 	thisnode = di_drv_first_node(driver, rootnode);
3125a7763bfSjmcp 
3135a7763bfSjmcp 	if (thisnode == DI_NODE_NIL) {
3145a7763bfSjmcp 		logmsg(MSG_INFO, gettext("No %s nodes in this system\n"),
315c4800545Sjmcp 		    driver);
316f1c23465SJames C. McPherson 		return (FWFLASH_FAILURE);
3175a7763bfSjmcp 	}
3185a7763bfSjmcp 
3195a7763bfSjmcp 	if ((devpath = calloc(1, MAXPATHLEN + 1)) == NULL) {
320c4800545Sjmcp 		logmsg(MSG_ERROR,
321c4800545Sjmcp 		    gettext("%s: Unable to allocate space "
322c4800545Sjmcp 		    "for a device node\n"),
323c4800545Sjmcp 		    driver);
324f1c23465SJames C. McPherson 		return (FWFLASH_FAILURE);
3255a7763bfSjmcp 	}
3265a7763bfSjmcp 
3275a7763bfSjmcp 	/* we've found one, at least */
3285a7763bfSjmcp 
3295a7763bfSjmcp 	for (; thisnode != DI_NODE_NIL; thisnode = di_drv_next_node(thisnode)) {
3305a7763bfSjmcp 
3315a7763bfSjmcp 		devpath = di_devfs_path(thisnode);
3325a7763bfSjmcp 
3335a7763bfSjmcp 		if ((newdev = calloc(1, sizeof (struct devicelist)))
3345a7763bfSjmcp 		    == NULL) {
3355a7763bfSjmcp 			logmsg(MSG_ERROR,
336c4800545Sjmcp 			    gettext("%s: identification function unable "
337c4800545Sjmcp 			    "to allocate space for device entry\n"),
338c4800545Sjmcp 			    driver);
339c4800545Sjmcp 			free(devpath);
340f1c23465SJames C. McPherson 			return (FWFLASH_FAILURE);
3415a7763bfSjmcp 		}
3425a7763bfSjmcp 
343c4800545Sjmcp 		/* calloc enough for /devices + devpath + devsuffix + '\0' */
3445a7763bfSjmcp 		devlength = strlen(devpath) + strlen(devprefix) +
3455a7763bfSjmcp 		    strlen(devsuffix) + 2;
3465a7763bfSjmcp 
3475a7763bfSjmcp 		if ((newdev->access_devname = calloc(1, devlength)) == NULL) {
348c4800545Sjmcp 			logmsg(MSG_ERROR,
349c4800545Sjmcp 			    gettext("%s: Unable to allocate "
350c4800545Sjmcp 			    "space for a devfs name\n"),
351c4800545Sjmcp 			    driver);
3525a7763bfSjmcp 			free(devpath);
353c4800545Sjmcp 			free(newdev);
3545a7763bfSjmcp 			return (FWFLASH_FAILURE);
3555a7763bfSjmcp 		}
3565a7763bfSjmcp 		snprintf(newdev->access_devname, devlength,
3575a7763bfSjmcp 		    "%s%s%s", devprefix, devpath, devsuffix);
3585a7763bfSjmcp 
359c4800545Sjmcp 		if ((newdev->drvname = calloc(1, strlen(driver) + 1))
3605a7763bfSjmcp 		    == NULL) {
361c4800545Sjmcp 			logmsg(MSG_ERROR,
362c4800545Sjmcp 			    gettext("%s: Unable to allocate "
363c4800545Sjmcp 			    "space to store a driver name\n"),
364c4800545Sjmcp 			    driver);
3655a7763bfSjmcp 			free(newdev->access_devname);
3665a7763bfSjmcp 			free(newdev);
367c4800545Sjmcp 			free(devpath);
3685a7763bfSjmcp 			return (FWFLASH_FAILURE);
3695a7763bfSjmcp 		}
370c4800545Sjmcp 		(void) strlcpy(newdev->drvname, driver,
371c4800545Sjmcp 		    strlen(driver) + 1);
3725a7763bfSjmcp 
373c4800545Sjmcp 		if ((newdev->classname = calloc(1, strlen(driver) + 1))
3745a7763bfSjmcp 		    == NULL) {
375c4800545Sjmcp 			logmsg(MSG_ERROR,
376c4800545Sjmcp 			    gettext("%s: Unable to malloc "
377c4800545Sjmcp 			    "space for a class name\n"),
378c4800545Sjmcp 			    drivername);
3795a7763bfSjmcp 			free(newdev->access_devname);
3805a7763bfSjmcp 			free(newdev->drvname);
3815a7763bfSjmcp 			free(newdev);
382c4800545Sjmcp 			free(devpath);
3835a7763bfSjmcp 			return (FWFLASH_FAILURE);
3845a7763bfSjmcp 		}
385c4800545Sjmcp 		(void) strlcpy(newdev->classname, driver,
386c4800545Sjmcp 		    strlen(driver) + 1);
3875a7763bfSjmcp 
3885a7763bfSjmcp 		/*
389c4800545Sjmcp 		 * Only alloc as much as we truly need, and DON'T forget
390c4800545Sjmcp 		 * that libnvpair manages the memory for property lookups!
391c4800545Sjmcp 		 * The same goes for libdevinfo properties.
392c4800545Sjmcp 		 *
393c4800545Sjmcp 		 * Also note that we're allocating here before we try to
394c4800545Sjmcp 		 * ses_open() the target, because if we can't allocate
395c4800545Sjmcp 		 * sufficient space then we might as well go home.
3965a7763bfSjmcp 		 */
397c4800545Sjmcp 		newdev->ident = calloc(1, VIDLEN + PIDLEN + REVLEN + 3);
398d73e86dbSsuha 		if (newdev->ident == NULL) {
399c4800545Sjmcp 			logmsg(MSG_ERROR,
400c4800545Sjmcp 			    gettext("%s: Unable to malloc space for"
401c4800545Sjmcp 			    "SCSI INQUIRY data\n"), driver);
402c4800545Sjmcp 			free(newdev->classname);
403c4800545Sjmcp 			free(newdev->drvname);
404c4800545Sjmcp 			free(newdev->access_devname);
405c4800545Sjmcp 			free(newdev);
406c4800545Sjmcp 			free(devpath);
407c4800545Sjmcp 			return (FWFLASH_FAILURE);
408c4800545Sjmcp 		}
409c4800545Sjmcp 
410c4800545Sjmcp 		if ((ses_target =
411c4800545Sjmcp 		    ses_open(LIBSES_VERSION, newdev->access_devname))
412c4800545Sjmcp 		    == NULL) {
4135a7763bfSjmcp 			logmsg(MSG_INFO,
414c4800545Sjmcp 			    gettext("%s: Unable to open device %s\n"),
415c4800545Sjmcp 			    driver, newdev->access_devname);
416c4800545Sjmcp 			free(newdev->ident);
417c4800545Sjmcp 			free(newdev->classname);
4185a7763bfSjmcp 			free(newdev->access_devname);
4195a7763bfSjmcp 			free(newdev->drvname);
4205a7763bfSjmcp 			free(newdev);
421c4800545Sjmcp 			free(devpath);
4225a7763bfSjmcp 			continue;
4235a7763bfSjmcp 		}
424c4800545Sjmcp 		snapshot = ses_snap_hold(ses_target);
425c4800545Sjmcp 		rootnodep = ses_root_node(snapshot);
4265a7763bfSjmcp 
4275a7763bfSjmcp 		/*
428c4800545Sjmcp 		 * If the node has no properties, or the INQUIRY properties
429c4800545Sjmcp 		 * don't exist, this device does not comply with SES2 so we
430c4800545Sjmcp 		 * won't touch it.
4315a7763bfSjmcp 		 */
432c4800545Sjmcp 		if ((props = ses_node_props(rootnodep)) == NULL) {
433c4800545Sjmcp 			free(newdev->ident);
434c4800545Sjmcp 			ses_snap_rele(snapshot);
435c4800545Sjmcp 			ses_close(ses_target);
436c4800545Sjmcp 			free(newdev->classname);
437c4800545Sjmcp 			free(newdev->access_devname);
438c4800545Sjmcp 			free(newdev->drvname);
439c4800545Sjmcp 			free(newdev);
440c4800545Sjmcp 			free(devpath);
441c4800545Sjmcp 			continue;
442c4800545Sjmcp 		}
443c4800545Sjmcp 
444c4800545Sjmcp 		if ((nvlist_lookup_string(props, SCSI_PROP_VENDOR,
445c4800545Sjmcp 		    &newdev->ident->vid) != 0) ||
446c4800545Sjmcp 		    (nvlist_lookup_string(props, SCSI_PROP_PRODUCT,
447c4800545Sjmcp 		    &newdev->ident->pid) != 0) ||
448c4800545Sjmcp 		    (nvlist_lookup_string(props, SCSI_PROP_REVISION,
449c4800545Sjmcp 		    &newdev->ident->revid) != 0)) {
450c4800545Sjmcp 			free(newdev->ident);
451c4800545Sjmcp 			ses_snap_rele(snapshot);
452c4800545Sjmcp 			ses_close(ses_target);
453c4800545Sjmcp 			free(newdev->classname);
4545a7763bfSjmcp 			free(newdev->access_devname);
4555a7763bfSjmcp 			free(newdev->drvname);
456c4800545Sjmcp 			free(newdev);
457c4800545Sjmcp 			free(devpath);
458c4800545Sjmcp 			continue;
459c4800545Sjmcp 		}
460c4800545Sjmcp 
461c4800545Sjmcp 		nodep = ses_snap_primary_enclosure(snapshot);
462c4800545Sjmcp 
463c4800545Sjmcp 		if ((props = ses_node_props(nodep)) == NULL) {
464c4800545Sjmcp 			free(newdev->ident);
465c4800545Sjmcp 			ses_snap_rele(snapshot);
466c4800545Sjmcp 			ses_close(ses_target);
4675a7763bfSjmcp 			free(newdev->classname);
468c4800545Sjmcp 			free(newdev->access_devname);
469c4800545Sjmcp 			free(newdev->drvname);
4705a7763bfSjmcp 			free(newdev);
471c4800545Sjmcp 			free(devpath);
4725a7763bfSjmcp 			continue;
473c4800545Sjmcp 		}
474c4800545Sjmcp 
475c4800545Sjmcp 		logmsg(MSG_INFO,
476c4800545Sjmcp 		    "\nvid: %s\npid: %s\nrevid: %s\n",
477c4800545Sjmcp 		    newdev->ident->vid,
478c4800545Sjmcp 		    newdev->ident->pid,
479c4800545Sjmcp 		    newdev->ident->revid);
480c4800545Sjmcp 
481c4800545Sjmcp 		if (nvlist_lookup_string(props, LIBSES_EN_PROP_CSN,
482c4800545Sjmcp 		    &newdev->addresses[0]) == 0) {
483c4800545Sjmcp 			logmsg(MSG_INFO,
484c4800545Sjmcp 			    "Chassis Serial Number: %s\n",
485c4800545Sjmcp 			    newdev->addresses[0]);
4860f59e5a7Speihong huang 		} else
4870f59e5a7Speihong huang 			logmsg(MSG_INFO,
4880f59e5a7Speihong huang 			    "%s: no chassis-serial-number property "
4890f59e5a7Speihong huang 			    "for device %s\n",
4900f59e5a7Speihong huang 			    driver, newdev->access_devname);
4915a7763bfSjmcp 
4925a7763bfSjmcp 
493f1c23465SJames C. McPherson 		rv = di_prop_lookup_strings(DDI_DEV_T_ANY,
494f1c23465SJames C. McPherson 		    thisnode, "target-port", &newdev->addresses[1]);
495f1c23465SJames C. McPherson 		if (rv < 0) {
496c4800545Sjmcp 			logmsg(MSG_INFO,
497c4800545Sjmcp 			    "%s: no target-port property "
498c4800545Sjmcp 			    "for device %s\n",
499c4800545Sjmcp 			    driver, newdev->access_devname);
500c4800545Sjmcp 		} else
501c4800545Sjmcp 			logmsg(MSG_INFO,
502c4800545Sjmcp 			    "target-port property: %s\n",
503c4800545Sjmcp 			    newdev->addresses[1]);
504c4800545Sjmcp 
505c4800545Sjmcp 
5065a7763bfSjmcp 		newdev->index = idx;
5075a7763bfSjmcp 		++idx;
5085a7763bfSjmcp 		newdev->plugin = self;
5095a7763bfSjmcp 
510c4800545Sjmcp 		ses_snap_rele(snapshot);
5115a7763bfSjmcp 		TAILQ_INSERT_TAIL(fw_devices, newdev, nextdev);
5125a7763bfSjmcp 	}
5135a7763bfSjmcp 
5145a7763bfSjmcp 
5155a7763bfSjmcp 	if (fwflash_debug != 0) {
5165a7763bfSjmcp 		struct devicelist *tempdev;
5175a7763bfSjmcp 
5185a7763bfSjmcp 		TAILQ_FOREACH(tempdev, fw_devices, nextdev) {
519c4800545Sjmcp 			logmsg(MSG_INFO, "%s:fw_identify:\n",
520c4800545Sjmcp 			    driver);
521c4800545Sjmcp 			logmsg(MSG_INFO,
522c4800545Sjmcp 			    "\ttempdev @ 0x%lx\n"
5235a7763bfSjmcp 			    "\t\taccess_devname: %s\n"
5245a7763bfSjmcp 			    "\t\tdrvname: %s\tclassname: %s\n"
5255a7763bfSjmcp 			    "\t\tident->vid:   %s\n"
5265a7763bfSjmcp 			    "\t\tident->pid:   %s\n"
5275a7763bfSjmcp 			    "\t\tident->revid: %s\n"
5285a7763bfSjmcp 			    "\t\tindex:        %d\n"
5295a7763bfSjmcp 			    "\t\taddress[0]:   %s\n"
5305a7763bfSjmcp 			    "\t\taddress[1]:   %s\n"
5315a7763bfSjmcp 			    "\t\tplugin @ 0x%lx\n\n",
5325a7763bfSjmcp 			    &tempdev,
5335a7763bfSjmcp 			    tempdev->access_devname,
5345a7763bfSjmcp 			    tempdev->drvname, newdev->classname,
5355a7763bfSjmcp 			    tempdev->ident->vid,
5365a7763bfSjmcp 			    tempdev->ident->pid,
5375a7763bfSjmcp 			    tempdev->ident->revid,
5385a7763bfSjmcp 			    tempdev->index,
5390f59e5a7Speihong huang 			    (tempdev->addresses[0] ? tempdev->addresses[0] :
5400f59e5a7Speihong huang 			    "(not supported)"),
5410f59e5a7Speihong huang 			    (tempdev->addresses[1] ? tempdev->addresses[1] :
5420f59e5a7Speihong huang 			    "(not supported)"),
543c4800545Sjmcp 			    &tempdev->plugin);
5445a7763bfSjmcp 		}
5455a7763bfSjmcp 	}
5465a7763bfSjmcp 
5475a7763bfSjmcp 	return (FWFLASH_SUCCESS);
5485a7763bfSjmcp }
5495a7763bfSjmcp 
5505a7763bfSjmcp 
5515a7763bfSjmcp 
5525a7763bfSjmcp int
5535a7763bfSjmcp fw_devinfo(struct devicelist *thisdev)
5545a7763bfSjmcp {
5555a7763bfSjmcp 
5565a7763bfSjmcp 	fprintf(stdout, gettext("Device[%d] %s\n  Class [%s]\n"),
5575a7763bfSjmcp 	    thisdev->index, thisdev->access_devname, thisdev->classname);
5585a7763bfSjmcp 
5595a7763bfSjmcp 	fprintf(stdout,
5605a7763bfSjmcp 	    gettext("\tVendor                 : %s\n"
5615a7763bfSjmcp 	    "\tProduct                : %s\n"
5625a7763bfSjmcp 	    "\tFirmware revision      : %s\n"
563c4800545Sjmcp 	    "\tChassis Serial Number  : %s\n"
5645a7763bfSjmcp 	    "\tTarget-port identifier : %s\n"),
5655a7763bfSjmcp 	    thisdev->ident->vid,
5665a7763bfSjmcp 	    thisdev->ident->pid,
5675a7763bfSjmcp 	    thisdev->ident->revid,
5680f59e5a7Speihong huang 	    (thisdev->addresses[0] ? thisdev->addresses[0] :
5690f59e5a7Speihong huang 	    "(not supported)"),
5700f59e5a7Speihong huang 	    (thisdev->addresses[1] ? thisdev->addresses[1] :
5710f59e5a7Speihong huang 	    "(not supported)"));
5725a7763bfSjmcp 
5735a7763bfSjmcp 	fprintf(stdout, "\n\n");
5745a7763bfSjmcp 
5755a7763bfSjmcp 	return (FWFLASH_SUCCESS);
5765a7763bfSjmcp }
5775a7763bfSjmcp 
5785a7763bfSjmcp 
5795a7763bfSjmcp 
5805a7763bfSjmcp 
5815a7763bfSjmcp 
5825a7763bfSjmcp /*ARGSUSED*/
5835a7763bfSjmcp static int
5845a7763bfSjmcp get_status(nvlist_t *props, ucode_status_t *sp)
5855a7763bfSjmcp {
5865a7763bfSjmcp 	int i;
5875a7763bfSjmcp 	uint64_t status, astatus;
5885a7763bfSjmcp 
5895a7763bfSjmcp 	if (nvlist_lookup_uint64(props, SES_EN_PROP_UCODE, &status) != 0) {
5905a7763bfSjmcp 		sp->us_status = -1ULL;
5915a7763bfSjmcp 		(void) snprintf(sp->us_desc, sizeof (sp->us_desc),
5925a7763bfSjmcp 		    "not supported");
593*d65b419eSXinChen 		return (FWFLASH_FAILURE);
5945a7763bfSjmcp 	}
5955a7763bfSjmcp 
596c4800545Sjmcp 	if (nvlist_lookup_uint64(props, SES_EN_PROP_UCODE_A,
597c4800545Sjmcp 	    &astatus) != 0) {
598c4800545Sjmcp 		logmsg(MSG_ERROR,
599c4800545Sjmcp 		    gettext("\nError: Unable to retrieve current status\n"));
600*d65b419eSXinChen 		return (FWFLASH_FAILURE);
601c4800545Sjmcp 	}
6025a7763bfSjmcp 
6035a7763bfSjmcp 	for (i = 0; i < NUCODE_STATUS; i++) {
6045a7763bfSjmcp 		if (ucode_statdesc_table[i].us_value == status)
6055a7763bfSjmcp 			break;
6065a7763bfSjmcp 	}
6075a7763bfSjmcp 
6085a7763bfSjmcp 	sp->us_status = status;
6095a7763bfSjmcp 
6105a7763bfSjmcp 	if (i == NUCODE_STATUS) {
6115a7763bfSjmcp 		(void) snprintf(sp->us_desc, sizeof (sp->us_desc),
6125a7763bfSjmcp 		    "unknown (0x%02x)", (int)status);
613*d65b419eSXinChen 		sp->us_iserr = sp->us_pending = B_TRUE;
614*d65b419eSXinChen 		return (FWFLASH_FAILURE);
6155a7763bfSjmcp 	} else {
6165a7763bfSjmcp 		/* LINTED */
6175a7763bfSjmcp 		(void) snprintf(sp->us_desc, sizeof (sp->us_desc),
6185a7763bfSjmcp 		    ucode_statdesc_table[i].us_desc, (int)astatus);
6195a7763bfSjmcp 		sp->us_iserr = ucode_statdesc_table[i].us_iserr;
6205a7763bfSjmcp 		sp->us_pending = ucode_statdesc_table[i].us_pending;
6215a7763bfSjmcp 	}
6225a7763bfSjmcp 
623*d65b419eSXinChen 	return (FWFLASH_SUCCESS);
6245a7763bfSjmcp }
6255a7763bfSjmcp 
6265a7763bfSjmcp 
627*d65b419eSXinChen static int
6285a7763bfSjmcp print_updated_status(ses_node_t *np, void *arg)
6295a7763bfSjmcp {
6305a7763bfSjmcp 	ucode_wait_t *uwp = arg;
631c4800545Sjmcp 	nvlist_t *props;
6325a7763bfSjmcp 	ucode_status_t status;
6335a7763bfSjmcp 
6345a7763bfSjmcp 
635c4800545Sjmcp 	if ((props = ses_node_props(np)) == NULL) {
636*d65b419eSXinChen 		return (FWFLASH_FAILURE);
637c4800545Sjmcp 	}
6385a7763bfSjmcp 
639*d65b419eSXinChen 	if (get_status(props, &status) != FWFLASH_SUCCESS)
640*d65b419eSXinChen 		return (FWFLASH_FAILURE);
6415a7763bfSjmcp 
6425a7763bfSjmcp 	if (status.us_status != uwp->uw_prevstatus)
6435a7763bfSjmcp 		(void) printf("%30s: %s\n", "status", status.us_desc);
644c4800545Sjmcp 
6455a7763bfSjmcp 	uwp->uw_prevstatus = status.us_status;
6465a7763bfSjmcp 	uwp->uw_pending = status.us_pending;
6475a7763bfSjmcp 
648c4800545Sjmcp 	if (status.us_iserr) {
6495a7763bfSjmcp 		logmsg(MSG_INFO,
650c4800545Sjmcp 		    "libses: status.us_iserr: 0x%0x\n",
6515a7763bfSjmcp 		    status.us_iserr);
652*d65b419eSXinChen 		return (FWFLASH_FAILURE);
653*d65b419eSXinChen 	}
654*d65b419eSXinChen 	return (FWFLASH_SUCCESS);
6555a7763bfSjmcp }
6565a7763bfSjmcp 
6575a7763bfSjmcp /*ARGSUSED*/
658c4800545Sjmcp static int
6595a7763bfSjmcp sendimg(ses_node_t *np, void *data)
6605a7763bfSjmcp {
6615a7763bfSjmcp 	nvlist_t *props;
6625a7763bfSjmcp 	nvlist_t *arg = data;
6635a7763bfSjmcp 	char *vendor, *product, *revision, *csn;
6645a7763bfSjmcp 	char buf[128];
6655a7763bfSjmcp 	ses_snap_t *newsnap;
6665a7763bfSjmcp 	int ret;
6675a7763bfSjmcp 	ucode_status_t statdesc;
6685a7763bfSjmcp 	ucode_wait_t wait;
6695a7763bfSjmcp 	uint8_t *imagedata;
670c4800545Sjmcp 	uint_t len;
6715a7763bfSjmcp 
6725a7763bfSjmcp 
673c4800545Sjmcp 	/* If we've been called without data, eject */
674c4800545Sjmcp 	if (nvlist_lookup_byte_array(arg, SES_CTL_PROP_UCODE_DATA,
675c4800545Sjmcp 	    &imagedata, &len) != 0) {
676c4800545Sjmcp 		return (FWFLASH_FAILURE);
677c4800545Sjmcp 	}
6785a7763bfSjmcp 
679c4800545Sjmcp 	props = ses_node_props(np);
680c4800545Sjmcp 	if ((props == NULL) ||
681c4800545Sjmcp 	    (nvlist_lookup_string(props, SES_EN_PROP_VID, &vendor) != 0) ||
682c4800545Sjmcp 	    (nvlist_lookup_string(props, SES_EN_PROP_PID, &product) != 0) ||
683c4800545Sjmcp 	    (nvlist_lookup_string(props, SES_EN_PROP_REV, &revision) != 0) ||
684c4800545Sjmcp 	    (nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, &csn) != 0)) {
685c4800545Sjmcp 		return (FWFLASH_FAILURE);
686c4800545Sjmcp 	}
6875a7763bfSjmcp 
6885a7763bfSjmcp 	(void) printf("%30s: %s\n", "vendor", vendor);
6895a7763bfSjmcp 	(void) printf("%30s: %s\n", "product", product);
6905a7763bfSjmcp 	(void) printf("%30s: %s\n", "revision", revision);
6915a7763bfSjmcp 	(void) printf("%30s: %s\n", "serial", csn);
6925a7763bfSjmcp 
6935a7763bfSjmcp 	ret = get_status(props, &statdesc);
6945a7763bfSjmcp 	(void) printf("%30s: %s\n", "current status", statdesc.us_desc);
695*d65b419eSXinChen 	if (ret != FWFLASH_SUCCESS) {
696c4800545Sjmcp 		return (FWFLASH_FAILURE);
6975a7763bfSjmcp 	}
6985a7763bfSjmcp 
6995a7763bfSjmcp 	(void) snprintf(buf, sizeof (buf), "downloading %u bytes", len);
7005a7763bfSjmcp 	(void) printf("\n%30s: ", buf);
701c4800545Sjmcp 
702c4800545Sjmcp 	/*
703c4800545Sjmcp 	 * If the bufferid isn't 2, then the verifier has already
70487d06e46Speihong huang 	 * OK'd the image that the user has provided.
705c4800545Sjmcp 	 *
706c4800545Sjmcp 	 * At present the non-"standard" images need to be flashed
707c4800545Sjmcp 	 * using the scsi WRITE BUFFER command
708c4800545Sjmcp 	 */
709c4800545Sjmcp 	if (verifier->flashbuf != 2)
710c4800545Sjmcp 		return (scsi_writebuf());
711c4800545Sjmcp 
712c4800545Sjmcp 
713*d65b419eSXinChen 	if (ses_node_ctl(np, SES_CTL_OP_DL_UCODE, arg) != FWFLASH_SUCCESS) {
7145a7763bfSjmcp 		(void) printf("failed!\n");
7155a7763bfSjmcp 		(void) printf("%s\n", ses_errmsg());
716c4800545Sjmcp 		return (FWFLASH_FAILURE);
7175a7763bfSjmcp 	} else {
7185a7763bfSjmcp 		(void) printf("ok\n");
7195a7763bfSjmcp 	}
7205a7763bfSjmcp 
7215a7763bfSjmcp 	wait.uw_prevstatus = -1ULL;
7225a7763bfSjmcp 	wait.uw_oldnp = np;
7235a7763bfSjmcp 
724*d65b419eSXinChen 	if ((newsnap = ses_snap_new(ses_target)) == NULL) {
725d73e86dbSsuha 		logmsg(MSG_ERROR,
726c4800545Sjmcp 		    "failed to update SES snapshot: %s",
727c4800545Sjmcp 		    ses_errmsg());
728*d65b419eSXinChen 		return (FWFLASH_FAILURE);
729*d65b419eSXinChen 	}
730d73e86dbSsuha 
731c4800545Sjmcp 	print_updated_status(ses_snap_primary_enclosure(newsnap),
732c4800545Sjmcp 	    &wait);
733c4800545Sjmcp 	ses_snap_rele(newsnap);
734d73e86dbSsuha 
735*d65b419eSXinChen 	return (FWFLASH_SUCCESS);
736d73e86dbSsuha }
737d73e86dbSsuha 
738c4800545Sjmcp static int
739c4800545Sjmcp scsi_writebuf()
7405a7763bfSjmcp {
741c4800545Sjmcp 	int ret;
742d73e86dbSsuha 	int i = 0;
743c4800545Sjmcp 	libscsi_action_t *action;
744c4800545Sjmcp 	spc3_write_buffer_cdb_t *wb_cdb;
745c4800545Sjmcp 	libscsi_hdl_t	*handle;
746c4800545Sjmcp 	libscsi_target_t *target;
747c4800545Sjmcp 	sam4_status_t samstatus;
748d73e86dbSsuha 
749d73e86dbSsuha 
750c4800545Sjmcp 	target = ses_scsi_target(ses_target);
751c4800545Sjmcp 	handle = libscsi_get_handle(target);
752c4800545Sjmcp 	action = libscsi_action_alloc(handle, SPC3_CMD_WRITE_BUFFER,
753c4800545Sjmcp 	    LIBSCSI_AF_WRITE|LIBSCSI_AF_RQSENSE,
754c4800545Sjmcp 	    (void *)verifier->fwimage, (size_t)verifier->imgsize);
755d73e86dbSsuha 
756c4800545Sjmcp 	wb_cdb = (spc3_write_buffer_cdb_t *)libscsi_action_get_cdb(action);
757*d65b419eSXinChen 
758c4800545Sjmcp 	wb_cdb->wbc_mode = SPC3_WB_MODE_DATA;
759c4800545Sjmcp 	wb_cdb->wbc_bufferid = verifier->flashbuf;
760*d65b419eSXinChen 
761*d65b419eSXinChen 	wb_cdb->wbc_buffer_offset[0] = 0;
762*d65b419eSXinChen 	wb_cdb->wbc_buffer_offset[1] = 0;
763*d65b419eSXinChen 	wb_cdb->wbc_buffer_offset[2] = 0;
764*d65b419eSXinChen 
765*d65b419eSXinChen 	wb_cdb->wbc_parameter_list_len[0] =
766*d65b419eSXinChen 	    (verifier->imgsize & 0xff0000) >> 16;
767*d65b419eSXinChen 	wb_cdb->wbc_parameter_list_len[1] = (verifier->imgsize & 0xff00) >> 8;
768*d65b419eSXinChen 	wb_cdb->wbc_parameter_list_len[2] = (verifier->imgsize & 0xff);
769d73e86dbSsuha 
770c4800545Sjmcp 	ret = libscsi_exec(action, target);
771c4800545Sjmcp 	samstatus = libscsi_action_get_status(action);
772d73e86dbSsuha 
773c4800545Sjmcp 	logmsg(MSG_INFO,
774c4800545Sjmcp 	    "\nscsi_writebuffer: ret 0x%0x, samstatus 0x%0x\n",
775c4800545Sjmcp 	    ret, samstatus);
776d73e86dbSsuha 
777*d65b419eSXinChen 	if ((ret != FWFLASH_SUCCESS) || samstatus != SAM4_STATUS_GOOD) {
778c4800545Sjmcp 		libscsi_action_free(action);
779*d65b419eSXinChen 		return (FWFLASH_FAILURE);
780c4800545Sjmcp 	} else {
781c4800545Sjmcp 		(void) printf("ok\n");
782d73e86dbSsuha 	}
783d73e86dbSsuha 
784c4800545Sjmcp 	for (i = 0; i < NSAM4_STATUS; i++) {
785c4800545Sjmcp 		if (sam4_status[i].status == samstatus) {
786c4800545Sjmcp 			(void) printf("%s\n", (sam4_status[i].message));
787c4800545Sjmcp 			break;
788d73e86dbSsuha 		}
789d73e86dbSsuha 	}
790d73e86dbSsuha 
791c4800545Sjmcp 	if (i == NSAM4_STATUS)
792c4800545Sjmcp 		(void) printf("Status: UNKNOWN\n");
793c4800545Sjmcp 
794c4800545Sjmcp 	if (samstatus == SAM4_STATUS_GOOD) {
795c4800545Sjmcp 		return (FWFLASH_SUCCESS);
796c4800545Sjmcp 	}
797c4800545Sjmcp 
798c4800545Sjmcp 	return (FWFLASH_FAILURE);
7995a7763bfSjmcp }
800