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