xref: /illumos-gate/usr/src/cmd/fwflash/plugins/transport/common/ses.c (revision 3d393ee6c37fa10ac512ed6d36109ad616dc7c1a)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
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 static int internalstatus;
137 
138 extern di_node_t rootnode;
139 extern int errno;
140 extern struct fw_plugin *self;
141 extern struct vrfyplugin *verifier;
142 extern int fwflash_debug;
143 
144 
145 /* required functions for this plugin */
146 int fw_readfw(struct devicelist *device, char *filename);
147 int fw_writefw(struct devicelist *device);
148 int fw_identify(int start);
149 int fw_devinfo(struct devicelist *thisdev);
150 
151 
152 /* helper functions */
153 static void print_updated_status(ses_node_t *np, void *arg);
154 static int get_status(nvlist_t *props, ucode_status_t *sp);
155 static int sendimg(ses_node_t *np, void *data);
156 static int scsi_writebuf();
157 
158 /*
159  * We don't currently support reading firmware from a SAS
160  * expander. If we do eventually support it, we would use
161  * the scsi READ BUFFER command to do so.
162  */
163 int
164 fw_readfw(struct devicelist *flashdev, char *filename)
165 {
166 
167 	logmsg(MSG_INFO,
168 	    "%s: not writing firmware for device %s to file %s\n",
169 	    flashdev->drvname, flashdev->access_devname, filename);
170 	logmsg(MSG_ERROR,
171 	    gettext("\n\nReading of firmware images from %s-attached "
172 	    "devices is not supported\n\n"),
173 	    flashdev->drvname);
174 
175 	return (FWFLASH_SUCCESS);
176 }
177 
178 
179 /*
180  * If we're invoking fw_writefw, then flashdev is a valid,
181  * flashable device supporting the SES2 Download Microcode Diagnostic
182  * Control page (0x0e).
183  *
184  * If verifier is null, then we haven't been called following a firmware
185  * image verification load operation.
186  *
187  * *THIS* function uses scsi SEND DIAGNOSTIC/download microcode to
188  * achieve the task... if you chase down to the bottom of libses you
189  * can see that too.
190  */
191 int
192 fw_writefw(struct devicelist *flashdev)
193 {
194 	int rv;
195 	nvlist_t *nvl;
196 	ses_snap_t *snapshot;
197 	ses_node_t *targetnode;
198 
199 	if ((verifier == NULL) || (verifier->imgsize == 0) ||
200 	    (verifier->fwimage == NULL)) {
201 		/* should _not_ happen */
202 		logmsg(MSG_ERROR,
203 		    gettext("%s: Firmware image has not "
204 		    "been verified.\n"),
205 		    flashdev->drvname);
206 		return (FWFLASH_FAILURE);
207 	}
208 
209 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
210 	    nvlist_add_uint64(nvl, SES_CTL_PROP_UCODE_MODE,
211 	    SES_DLUCODE_M_WITH_OFFS) != 0) {
212 		logmsg(MSG_ERROR,
213 		    gettext("%s: Unable to allocate "
214 		    "space for device prop list\n"),
215 		    flashdev->drvname);
216 		return (FWFLASH_FAILURE);
217 	}
218 
219 	fprintf(stdout, "\n"); /* get a fresh line for progress updates */
220 
221 	if (nvlist_add_uint64(nvl, SES_CTL_PROP_UCODE_BUFID,
222 	    verifier->flashbuf) != 0) {
223 		logmsg(MSG_ERROR,
224 		    gettext("%s: Unable to add buffer id "
225 		    "property, hence unable to flash device\n"),
226 		    flashdev->drvname);
227 		goto cancel;
228 	}
229 
230 	if (nvlist_add_byte_array(nvl, SES_CTL_PROP_UCODE_DATA,
231 	    (uint8_t *)verifier->fwimage, verifier->imgsize) != 0) {
232 		logmsg(MSG_ERROR,
233 		    "%s: Out of memory for property addition\n",
234 		    flashdev->drvname);
235 		goto cancel;
236 	}
237 
238 	if ((ses_target =
239 	    ses_open(LIBSES_VERSION, flashdev->access_devname)) == NULL) {
240 		logmsg(MSG_ERROR,
241 		    gettext("%s: Unable to open flashable device %s\n"),
242 		    flashdev->drvname, flashdev->access_devname);
243 		goto cancel;
244 	}
245 
246 	snapshot = ses_snap_hold(ses_target);
247 	internalstatus = FWFLASH_FAILURE;
248 
249 	if ((targetnode = ses_snap_primary_enclosure(snapshot)) == NULL) {
250 		logmsg(MSG_ERROR,
251 		    gettext("%s: Unable to locate primary enclosure for "
252 		    "device %s\n"),
253 		    flashdev->access_devname);
254 	} else {
255 		rv = sendimg(targetnode, nvl);
256 		if (rv == FWFLASH_SUCCESS) {
257 			logmsg(MSG_ERROR,
258 			    gettext("%s: Done. New image will be active "
259 			    "after the system is rebooted.\n\n"),
260 			    flashdev->drvname);
261 		} else {
262 			logmsg(MSG_INFO,
263 			    "%s: unable to flash image %s to device %s\n\n",
264 			    flashdev->drvname, verifier->imgfile,
265 			    flashdev->access_devname);
266 		}
267 	}
268 
269 	ses_snap_rele(snapshot);
270 	ses_close(ses_target);
271 cancel:
272 	nvlist_free(nvl);
273 
274 	return (internalstatus);
275 }
276 
277 
278 /*
279  * The fw_identify() function walks the device
280  * tree trying to find devices which this plugin
281  * can work with.
282  *
283  * The parameter "start" gives us the starting index number
284  * to give the device when we add it to the fw_devices list.
285  *
286  * firstdev is allocated by us and we add space as needed
287  */
288 int
289 fw_identify(int start)
290 {
291 
292 	int rv = FWFLASH_FAILURE;
293 	di_node_t thisnode;
294 	struct devicelist *newdev;
295 	char *devpath;
296 	char *devsuffix;
297 	char *driver;
298 	int idx = start;
299 	size_t devlength = 0;
300 	nvlist_t *props;
301 	ses_snap_t *snapshot;
302 	ses_node_t *rootnodep, *nodep;
303 
304 
305 	if (strcmp(self->drvname, "sgen") == 0) {
306 		devsuffix = sgensuffix;
307 		driver = self->drvname;
308 	} else {
309 		devsuffix = sessuffix;
310 		driver = drivername;
311 	}
312 
313 	thisnode = di_drv_first_node(driver, rootnode);
314 
315 	if (thisnode == DI_NODE_NIL) {
316 		logmsg(MSG_INFO, gettext("No %s nodes in this system\n"),
317 		    driver);
318 		return (FWFLASH_FAILURE);
319 	}
320 
321 	if ((devpath = calloc(1, MAXPATHLEN + 1)) == NULL) {
322 		logmsg(MSG_ERROR,
323 		    gettext("%s: Unable to allocate space "
324 		    "for a device node\n"),
325 		    driver);
326 		return (FWFLASH_FAILURE);
327 	}
328 
329 	/* we've found one, at least */
330 
331 	for (; thisnode != DI_NODE_NIL; thisnode = di_drv_next_node(thisnode)) {
332 
333 		devpath = di_devfs_path(thisnode);
334 
335 		if ((newdev = calloc(1, sizeof (struct devicelist)))
336 		    == NULL) {
337 			logmsg(MSG_ERROR,
338 			    gettext("%s: identification function unable "
339 			    "to allocate space for device entry\n"),
340 			    driver);
341 			free(devpath);
342 			return (FWFLASH_FAILURE);
343 		}
344 
345 		/* calloc enough for /devices + devpath + devsuffix + '\0' */
346 		devlength = strlen(devpath) + strlen(devprefix) +
347 		    strlen(devsuffix) + 2;
348 
349 		if ((newdev->access_devname = calloc(1, devlength)) == NULL) {
350 			logmsg(MSG_ERROR,
351 			    gettext("%s: Unable to allocate "
352 			    "space for a devfs name\n"),
353 			    driver);
354 			free(devpath);
355 			free(newdev);
356 			return (FWFLASH_FAILURE);
357 		}
358 		snprintf(newdev->access_devname, devlength,
359 		    "%s%s%s", devprefix, devpath, devsuffix);
360 
361 		if ((newdev->drvname = calloc(1, strlen(driver) + 1))
362 		    == NULL) {
363 			logmsg(MSG_ERROR,
364 			    gettext("%s: Unable to allocate "
365 			    "space to store a driver name\n"),
366 			    driver);
367 			free(newdev->access_devname);
368 			free(newdev);
369 			free(devpath);
370 			return (FWFLASH_FAILURE);
371 		}
372 		(void) strlcpy(newdev->drvname, driver,
373 		    strlen(driver) + 1);
374 
375 		if ((newdev->classname = calloc(1, strlen(driver) + 1))
376 		    == NULL) {
377 			logmsg(MSG_ERROR,
378 			    gettext("%s: Unable to malloc "
379 			    "space for a class name\n"),
380 			    drivername);
381 			free(newdev->access_devname);
382 			free(newdev->drvname);
383 			free(newdev);
384 			free(devpath);
385 			return (FWFLASH_FAILURE);
386 		}
387 		(void) strlcpy(newdev->classname, driver,
388 		    strlen(driver) + 1);
389 
390 		/*
391 		 * Only alloc as much as we truly need, and DON'T forget
392 		 * that libnvpair manages the memory for property lookups!
393 		 * The same goes for libdevinfo properties.
394 		 *
395 		 * Also note that we're allocating here before we try to
396 		 * ses_open() the target, because if we can't allocate
397 		 * sufficient space then we might as well go home.
398 		 */
399 		newdev->ident = calloc(1, VIDLEN + PIDLEN + REVLEN + 3);
400 		if (newdev->ident == NULL) {
401 			logmsg(MSG_ERROR,
402 			    gettext("%s: Unable to malloc space for"
403 			    "SCSI INQUIRY data\n"), driver);
404 			free(newdev->classname);
405 			free(newdev->drvname);
406 			free(newdev->access_devname);
407 			free(newdev);
408 			free(devpath);
409 			return (FWFLASH_FAILURE);
410 		}
411 
412 		if ((ses_target =
413 		    ses_open(LIBSES_VERSION, newdev->access_devname))
414 		    == NULL) {
415 			logmsg(MSG_INFO,
416 			    gettext("%s: Unable to open device %s\n"),
417 			    driver, newdev->access_devname);
418 			free(newdev->ident);
419 			free(newdev->classname);
420 			free(newdev->access_devname);
421 			free(newdev->drvname);
422 			free(newdev);
423 			free(devpath);
424 			continue;
425 		}
426 		snapshot = ses_snap_hold(ses_target);
427 		rootnodep = ses_root_node(snapshot);
428 
429 		/*
430 		 * If the node has no properties, or the INQUIRY properties
431 		 * don't exist, this device does not comply with SES2 so we
432 		 * won't touch it.
433 		 */
434 		if ((props = ses_node_props(rootnodep)) == NULL) {
435 			free(newdev->ident);
436 			ses_snap_rele(snapshot);
437 			ses_close(ses_target);
438 			free(newdev->classname);
439 			free(newdev->access_devname);
440 			free(newdev->drvname);
441 			free(newdev);
442 			free(devpath);
443 			continue;
444 		}
445 
446 		if ((nvlist_lookup_string(props, SCSI_PROP_VENDOR,
447 		    &newdev->ident->vid) != 0) ||
448 		    (nvlist_lookup_string(props, SCSI_PROP_PRODUCT,
449 		    &newdev->ident->pid) != 0) ||
450 		    (nvlist_lookup_string(props, SCSI_PROP_REVISION,
451 		    &newdev->ident->revid) != 0)) {
452 			free(newdev->ident);
453 			ses_snap_rele(snapshot);
454 			ses_close(ses_target);
455 			free(newdev->classname);
456 			free(newdev->access_devname);
457 			free(newdev->drvname);
458 			free(newdev);
459 			free(devpath);
460 			continue;
461 		}
462 
463 		nodep = ses_snap_primary_enclosure(snapshot);
464 
465 		if ((props = ses_node_props(nodep)) == NULL) {
466 			free(newdev->ident);
467 			ses_snap_rele(snapshot);
468 			ses_close(ses_target);
469 			free(newdev->classname);
470 			free(newdev->access_devname);
471 			free(newdev->drvname);
472 			free(newdev);
473 			free(devpath);
474 			continue;
475 		}
476 
477 		logmsg(MSG_INFO,
478 		    "\nvid: %s\npid: %s\nrevid: %s\n",
479 		    newdev->ident->vid,
480 		    newdev->ident->pid,
481 		    newdev->ident->revid);
482 
483 		if (nvlist_lookup_string(props, LIBSES_EN_PROP_CSN,
484 		    &newdev->addresses[0]) == 0) {
485 			logmsg(MSG_INFO,
486 			    "Chassis Serial Number: %s\n",
487 			    newdev->addresses[0]);
488 		} else {
489 			(void) strlcpy(newdev->addresses[0],
490 			    "(not supported)", 17);
491 		}
492 
493 
494 		rv = di_prop_lookup_strings(DDI_DEV_T_ANY,
495 		    thisnode, "target-port", &newdev->addresses[1]);
496 		if (rv < 0) {
497 			logmsg(MSG_INFO,
498 			    "%s: no target-port property "
499 			    "for device %s\n",
500 			    driver, newdev->access_devname);
501 			(void) strlcpy(newdev->addresses[1],
502 			    "(not supported)", 17);
503 		} else
504 			logmsg(MSG_INFO,
505 			    "target-port property: %s\n",
506 			    newdev->addresses[1]);
507 
508 
509 		newdev->index = idx;
510 		++idx;
511 		newdev->plugin = self;
512 
513 		ses_snap_rele(snapshot);
514 		TAILQ_INSERT_TAIL(fw_devices, newdev, nextdev);
515 	}
516 
517 
518 	if (fwflash_debug != 0) {
519 		struct devicelist *tempdev;
520 
521 		TAILQ_FOREACH(tempdev, fw_devices, nextdev) {
522 			logmsg(MSG_INFO, "%s:fw_identify:\n",
523 			    driver);
524 			logmsg(MSG_INFO,
525 			    "\ttempdev @ 0x%lx\n"
526 			    "\t\taccess_devname: %s\n"
527 			    "\t\tdrvname: %s\tclassname: %s\n"
528 			    "\t\tident->vid:   %s\n"
529 			    "\t\tident->pid:   %s\n"
530 			    "\t\tident->revid: %s\n"
531 			    "\t\tindex:        %d\n"
532 			    "\t\taddress[0]:   %s\n"
533 			    "\t\taddress[1]:   %s\n"
534 			    "\t\tplugin @ 0x%lx\n\n",
535 			    &tempdev,
536 			    tempdev->access_devname,
537 			    tempdev->drvname, newdev->classname,
538 			    tempdev->ident->vid,
539 			    tempdev->ident->pid,
540 			    tempdev->ident->revid,
541 			    tempdev->index,
542 			    tempdev->addresses[0],
543 			    tempdev->addresses[1],
544 			    &tempdev->plugin);
545 		}
546 	}
547 
548 	return (FWFLASH_SUCCESS);
549 }
550 
551 
552 
553 int
554 fw_devinfo(struct devicelist *thisdev)
555 {
556 
557 
558 	fprintf(stdout, gettext("Device[%d] %s\n  Class [%s]\n"),
559 	    thisdev->index, thisdev->access_devname, thisdev->classname);
560 
561 	fprintf(stdout,
562 	    gettext("\tVendor                 : %s\n"
563 	    "\tProduct                : %s\n"
564 	    "\tFirmware revision      : %s\n"
565 	    "\tChassis Serial Number  : %s\n"
566 	    "\tTarget-port identifier : %s\n"),
567 	    thisdev->ident->vid,
568 	    thisdev->ident->pid,
569 	    thisdev->ident->revid,
570 	    thisdev->addresses[0],
571 	    thisdev->addresses[1]);
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 		internalstatus = FWFLASH_FAILURE;
594 		return (-1);
595 	}
596 
597 	if (nvlist_lookup_uint64(props, SES_EN_PROP_UCODE_A,
598 	    &astatus) != 0) {
599 		logmsg(MSG_ERROR,
600 		    gettext("\nError: Unable to retrieve current status\n"));
601 		internalstatus = FWFLASH_FAILURE;
602 		return (-1);
603 	}
604 
605 	for (i = 0; i < NUCODE_STATUS; i++) {
606 		if (ucode_statdesc_table[i].us_value == status)
607 			break;
608 	}
609 
610 	sp->us_status = status;
611 
612 	if (i == NUCODE_STATUS) {
613 		(void) snprintf(sp->us_desc, sizeof (sp->us_desc),
614 		    "unknown (0x%02x)", (int)status);
615 		sp->us_iserr = sp->us_pending = B_FALSE;
616 	} else {
617 		/* LINTED */
618 		(void) snprintf(sp->us_desc, sizeof (sp->us_desc),
619 		    ucode_statdesc_table[i].us_desc, (int)astatus);
620 		sp->us_iserr = ucode_statdesc_table[i].us_iserr;
621 		sp->us_pending = ucode_statdesc_table[i].us_pending;
622 	}
623 
624 	return (0);
625 }
626 
627 
628 static void
629 print_updated_status(ses_node_t *np, void *arg)
630 {
631 	ucode_wait_t *uwp = arg;
632 	nvlist_t *props;
633 	ucode_status_t status;
634 
635 
636 	if ((props = ses_node_props(np)) == NULL) {
637 		internalstatus = FWFLASH_FAILURE;
638 		return;
639 	}
640 
641 	if (get_status(props, &status) != 0)
642 		/* internalstatus is already set to FWFLASH_FAILURE */
643 		return;
644 
645 	if (status.us_status != uwp->uw_prevstatus)
646 		(void) printf("%30s: %s\n", "status", status.us_desc);
647 
648 	uwp->uw_prevstatus = status.us_status;
649 	uwp->uw_pending = status.us_pending;
650 
651 	if (status.us_iserr) {
652 		logmsg(MSG_INFO,
653 		    "libses: status.us_iserr: 0x%0x\n",
654 		    status.us_iserr);
655 		internalstatus = FWFLASH_FAILURE;
656 	} else
657 		internalstatus = FWFLASH_SUCCESS;
658 
659 }
660 
661 /*ARGSUSED*/
662 static int
663 sendimg(ses_node_t *np, void *data)
664 {
665 	nvlist_t *props;
666 	nvlist_t *arg = data;
667 	char *vendor, *product, *revision, *csn;
668 	char buf[128];
669 	ses_snap_t *newsnap;
670 	int ret;
671 	ucode_status_t statdesc;
672 	ucode_wait_t wait;
673 	uint8_t *imagedata;
674 	uint_t len;
675 
676 
677 	/* If we've been called without data, eject */
678 	if (nvlist_lookup_byte_array(arg, SES_CTL_PROP_UCODE_DATA,
679 	    &imagedata, &len) != 0) {
680 		return (FWFLASH_FAILURE);
681 	}
682 
683 	props = ses_node_props(np);
684 	if ((props == NULL) ||
685 	    (nvlist_lookup_string(props, SES_EN_PROP_VID, &vendor) != 0) ||
686 	    (nvlist_lookup_string(props, SES_EN_PROP_PID, &product) != 0) ||
687 	    (nvlist_lookup_string(props, SES_EN_PROP_REV, &revision) != 0) ||
688 	    (nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, &csn) != 0)) {
689 		return (FWFLASH_FAILURE);
690 	}
691 
692 	(void) printf("%30s: %s\n", "vendor", vendor);
693 	(void) printf("%30s: %s\n", "product", product);
694 	(void) printf("%30s: %s\n", "revision", revision);
695 	(void) printf("%30s: %s\n", "serial", csn);
696 
697 	ret = get_status(props, &statdesc);
698 	(void) printf("%30s: %s\n", "current status", statdesc.us_desc);
699 	if (ret != 0) {
700 		return (FWFLASH_FAILURE);
701 	}
702 
703 	(void) snprintf(buf, sizeof (buf), "downloading %u bytes", len);
704 	(void) printf("\n%30s: ", buf);
705 
706 	/*
707 	 * If the bufferid isn't 2, then the verifier has already
708 	 * OK'd the image that the user has provided. That means
709 	 * we've got manufacturing_mode = 1 from the command line.
710 	 *
711 	 * At present the non-"standard" images need to be flashed
712 	 * using the scsi WRITE BUFFER command
713 	 */
714 	if (verifier->flashbuf != 2)
715 		return (scsi_writebuf());
716 
717 
718 	if (ses_node_ctl(np, SES_CTL_OP_DL_UCODE, arg) != 0) {
719 		(void) printf("failed!\n");
720 		(void) printf("%s\n", ses_errmsg());
721 		return (FWFLASH_FAILURE);
722 	} else {
723 		(void) printf("ok\n");
724 	}
725 
726 	wait.uw_prevstatus = -1ULL;
727 	wait.uw_oldnp = np;
728 
729 	if ((newsnap = ses_snap_new(ses_target)) == NULL)
730 		logmsg(MSG_ERROR,
731 		    "failed to update SES snapshot: %s",
732 		    ses_errmsg());
733 
734 	print_updated_status(ses_snap_primary_enclosure(newsnap),
735 	    &wait);
736 	ses_snap_rele(newsnap);
737 
738 	return (internalstatus);
739 }
740 
741 static int
742 scsi_writebuf()
743 {
744 	int ret;
745 	int i = 0;
746 	libscsi_action_t *action;
747 	spc3_write_buffer_cdb_t *wb_cdb;
748 	libscsi_hdl_t	*handle;
749 	libscsi_target_t *target;
750 	sam4_status_t samstatus;
751 
752 
753 	target = ses_scsi_target(ses_target);
754 	handle = libscsi_get_handle(target);
755 	action = libscsi_action_alloc(handle, SPC3_CMD_WRITE_BUFFER,
756 	    LIBSCSI_AF_WRITE|LIBSCSI_AF_RQSENSE,
757 	    (void *)verifier->fwimage, (size_t)verifier->imgsize);
758 
759 	wb_cdb = (spc3_write_buffer_cdb_t *)libscsi_action_get_cdb(action);
760 	wb_cdb->wbc_mode = SPC3_WB_MODE_DATA;
761 	wb_cdb->wbc_bufferid = verifier->flashbuf;
762 	SCSI_WRITE24(&wb_cdb->wbc_buffer_offset, 0);
763 	SCSI_WRITE24(&wb_cdb->wbc_parameter_list_len, verifier->imgsize);
764 
765 	ret = libscsi_exec(action, target);
766 	samstatus = libscsi_action_get_status(action);
767 
768 	logmsg(MSG_INFO,
769 	    "\nscsi_writebuffer: ret 0x%0x, samstatus 0x%0x\n",
770 	    ret, samstatus);
771 
772 	if ((ret != 0) || samstatus != 0) {
773 		libscsi_action_free(action);
774 		return (ret);
775 	} else {
776 		(void) printf("ok\n");
777 	}
778 
779 	for (i = 0; i < NSAM4_STATUS; i++) {
780 		if (sam4_status[i].status == samstatus) {
781 			(void) printf("%s\n", (sam4_status[i].message));
782 			break;
783 		}
784 	}
785 
786 	if (i == NSAM4_STATUS)
787 		(void) printf("Status: UNKNOWN\n");
788 
789 	if (samstatus == SAM4_STATUS_GOOD) {
790 		internalstatus = FWFLASH_SUCCESS;
791 		return (FWFLASH_SUCCESS);
792 	}
793 
794 	return (FWFLASH_FAILURE);
795 }
796