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