xref: /illumos-gate/usr/src/cmd/fwflash/plugins/transport/common/ses.c (revision 3589c4f01c20349ca65899d209cdc0c17a641433)
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 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 			logmsg(MSG_INFO,
490 			    "%s: no chassis-serial-number property "
491 			    "for device %s\n",
492 			    driver, newdev->access_devname);
493 
494 
495 		rv = di_prop_lookup_strings(DDI_DEV_T_ANY,
496 		    thisnode, "target-port", &newdev->addresses[1]);
497 		if (rv < 0) {
498 			logmsg(MSG_INFO,
499 			    "%s: no target-port property "
500 			    "for device %s\n",
501 			    driver, newdev->access_devname);
502 		} else
503 			logmsg(MSG_INFO,
504 			    "target-port property: %s\n",
505 			    newdev->addresses[1]);
506 
507 
508 		newdev->index = idx;
509 		++idx;
510 		newdev->plugin = self;
511 
512 		ses_snap_rele(snapshot);
513 		TAILQ_INSERT_TAIL(fw_devices, newdev, nextdev);
514 	}
515 
516 
517 	if (fwflash_debug != 0) {
518 		struct devicelist *tempdev;
519 
520 		TAILQ_FOREACH(tempdev, fw_devices, nextdev) {
521 			logmsg(MSG_INFO, "%s:fw_identify:\n",
522 			    driver);
523 			logmsg(MSG_INFO,
524 			    "\ttempdev @ 0x%lx\n"
525 			    "\t\taccess_devname: %s\n"
526 			    "\t\tdrvname: %s\tclassname: %s\n"
527 			    "\t\tident->vid:   %s\n"
528 			    "\t\tident->pid:   %s\n"
529 			    "\t\tident->revid: %s\n"
530 			    "\t\tindex:        %d\n"
531 			    "\t\taddress[0]:   %s\n"
532 			    "\t\taddress[1]:   %s\n"
533 			    "\t\tplugin @ 0x%lx\n\n",
534 			    &tempdev,
535 			    tempdev->access_devname,
536 			    tempdev->drvname, newdev->classname,
537 			    tempdev->ident->vid,
538 			    tempdev->ident->pid,
539 			    tempdev->ident->revid,
540 			    tempdev->index,
541 			    (tempdev->addresses[0] ? tempdev->addresses[0] :
542 			    "(not supported)"),
543 			    (tempdev->addresses[1] ? tempdev->addresses[1] :
544 			    "(not supported)"),
545 			    &tempdev->plugin);
546 		}
547 	}
548 
549 	return (FWFLASH_SUCCESS);
550 }
551 
552 
553 
554 int
555 fw_devinfo(struct devicelist *thisdev)
556 {
557 
558 
559 	fprintf(stdout, gettext("Device[%d] %s\n  Class [%s]\n"),
560 	    thisdev->index, thisdev->access_devname, thisdev->classname);
561 
562 	fprintf(stdout,
563 	    gettext("\tVendor                 : %s\n"
564 	    "\tProduct                : %s\n"
565 	    "\tFirmware revision      : %s\n"
566 	    "\tChassis Serial Number  : %s\n"
567 	    "\tTarget-port identifier : %s\n"),
568 	    thisdev->ident->vid,
569 	    thisdev->ident->pid,
570 	    thisdev->ident->revid,
571 	    (thisdev->addresses[0] ? thisdev->addresses[0] :
572 	    "(not supported)"),
573 	    (thisdev->addresses[1] ? thisdev->addresses[1] :
574 	    "(not supported)"));
575 
576 	fprintf(stdout, "\n\n");
577 
578 	return (FWFLASH_SUCCESS);
579 }
580 
581 
582 
583 
584 
585 /*ARGSUSED*/
586 static int
587 get_status(nvlist_t *props, ucode_status_t *sp)
588 {
589 	int i;
590 	uint64_t status, astatus;
591 
592 	if (nvlist_lookup_uint64(props, SES_EN_PROP_UCODE, &status) != 0) {
593 		sp->us_status = -1ULL;
594 		(void) snprintf(sp->us_desc, sizeof (sp->us_desc),
595 		    "not supported");
596 		internalstatus = FWFLASH_FAILURE;
597 		return (-1);
598 	}
599 
600 	if (nvlist_lookup_uint64(props, SES_EN_PROP_UCODE_A,
601 	    &astatus) != 0) {
602 		logmsg(MSG_ERROR,
603 		    gettext("\nError: Unable to retrieve current status\n"));
604 		internalstatus = FWFLASH_FAILURE;
605 		return (-1);
606 	}
607 
608 	for (i = 0; i < NUCODE_STATUS; i++) {
609 		if (ucode_statdesc_table[i].us_value == status)
610 			break;
611 	}
612 
613 	sp->us_status = status;
614 
615 	if (i == NUCODE_STATUS) {
616 		(void) snprintf(sp->us_desc, sizeof (sp->us_desc),
617 		    "unknown (0x%02x)", (int)status);
618 		sp->us_iserr = sp->us_pending = B_FALSE;
619 	} else {
620 		/* LINTED */
621 		(void) snprintf(sp->us_desc, sizeof (sp->us_desc),
622 		    ucode_statdesc_table[i].us_desc, (int)astatus);
623 		sp->us_iserr = ucode_statdesc_table[i].us_iserr;
624 		sp->us_pending = ucode_statdesc_table[i].us_pending;
625 	}
626 
627 	return (0);
628 }
629 
630 
631 static void
632 print_updated_status(ses_node_t *np, void *arg)
633 {
634 	ucode_wait_t *uwp = arg;
635 	nvlist_t *props;
636 	ucode_status_t status;
637 
638 
639 	if ((props = ses_node_props(np)) == NULL) {
640 		internalstatus = FWFLASH_FAILURE;
641 		return;
642 	}
643 
644 	if (get_status(props, &status) != 0)
645 		/* internalstatus is already set to FWFLASH_FAILURE */
646 		return;
647 
648 	if (status.us_status != uwp->uw_prevstatus)
649 		(void) printf("%30s: %s\n", "status", status.us_desc);
650 
651 	uwp->uw_prevstatus = status.us_status;
652 	uwp->uw_pending = status.us_pending;
653 
654 	if (status.us_iserr) {
655 		logmsg(MSG_INFO,
656 		    "libses: status.us_iserr: 0x%0x\n",
657 		    status.us_iserr);
658 		internalstatus = FWFLASH_FAILURE;
659 	} else
660 		internalstatus = FWFLASH_SUCCESS;
661 
662 }
663 
664 /*ARGSUSED*/
665 static int
666 sendimg(ses_node_t *np, void *data)
667 {
668 	nvlist_t *props;
669 	nvlist_t *arg = data;
670 	char *vendor, *product, *revision, *csn;
671 	char buf[128];
672 	ses_snap_t *newsnap;
673 	int ret;
674 	ucode_status_t statdesc;
675 	ucode_wait_t wait;
676 	uint8_t *imagedata;
677 	uint_t len;
678 
679 
680 	/* If we've been called without data, eject */
681 	if (nvlist_lookup_byte_array(arg, SES_CTL_PROP_UCODE_DATA,
682 	    &imagedata, &len) != 0) {
683 		return (FWFLASH_FAILURE);
684 	}
685 
686 	props = ses_node_props(np);
687 	if ((props == NULL) ||
688 	    (nvlist_lookup_string(props, SES_EN_PROP_VID, &vendor) != 0) ||
689 	    (nvlist_lookup_string(props, SES_EN_PROP_PID, &product) != 0) ||
690 	    (nvlist_lookup_string(props, SES_EN_PROP_REV, &revision) != 0) ||
691 	    (nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, &csn) != 0)) {
692 		return (FWFLASH_FAILURE);
693 	}
694 
695 	(void) printf("%30s: %s\n", "vendor", vendor);
696 	(void) printf("%30s: %s\n", "product", product);
697 	(void) printf("%30s: %s\n", "revision", revision);
698 	(void) printf("%30s: %s\n", "serial", csn);
699 
700 	ret = get_status(props, &statdesc);
701 	(void) printf("%30s: %s\n", "current status", statdesc.us_desc);
702 	if (ret != 0) {
703 		return (FWFLASH_FAILURE);
704 	}
705 
706 	(void) snprintf(buf, sizeof (buf), "downloading %u bytes", len);
707 	(void) printf("\n%30s: ", buf);
708 
709 	/*
710 	 * If the bufferid isn't 2, then the verifier has already
711 	 * OK'd the image that the user has provided. That means
712 	 * we've got manufacturing_mode = 1 from the command line.
713 	 *
714 	 * At present the non-"standard" images need to be flashed
715 	 * using the scsi WRITE BUFFER command
716 	 */
717 	if (verifier->flashbuf != 2)
718 		return (scsi_writebuf());
719 
720 
721 	if (ses_node_ctl(np, SES_CTL_OP_DL_UCODE, arg) != 0) {
722 		(void) printf("failed!\n");
723 		(void) printf("%s\n", ses_errmsg());
724 		return (FWFLASH_FAILURE);
725 	} else {
726 		(void) printf("ok\n");
727 	}
728 
729 	wait.uw_prevstatus = -1ULL;
730 	wait.uw_oldnp = np;
731 
732 	if ((newsnap = ses_snap_new(ses_target)) == NULL)
733 		logmsg(MSG_ERROR,
734 		    "failed to update SES snapshot: %s",
735 		    ses_errmsg());
736 
737 	print_updated_status(ses_snap_primary_enclosure(newsnap),
738 	    &wait);
739 	ses_snap_rele(newsnap);
740 
741 	return (internalstatus);
742 }
743 
744 static int
745 scsi_writebuf()
746 {
747 	int ret;
748 	int i = 0;
749 	libscsi_action_t *action;
750 	spc3_write_buffer_cdb_t *wb_cdb;
751 	libscsi_hdl_t	*handle;
752 	libscsi_target_t *target;
753 	sam4_status_t samstatus;
754 
755 
756 	target = ses_scsi_target(ses_target);
757 	handle = libscsi_get_handle(target);
758 	action = libscsi_action_alloc(handle, SPC3_CMD_WRITE_BUFFER,
759 	    LIBSCSI_AF_WRITE|LIBSCSI_AF_RQSENSE,
760 	    (void *)verifier->fwimage, (size_t)verifier->imgsize);
761 
762 	wb_cdb = (spc3_write_buffer_cdb_t *)libscsi_action_get_cdb(action);
763 	wb_cdb->wbc_mode = SPC3_WB_MODE_DATA;
764 	wb_cdb->wbc_bufferid = verifier->flashbuf;
765 	SCSI_WRITE24(&wb_cdb->wbc_buffer_offset, 0);
766 	SCSI_WRITE24(&wb_cdb->wbc_parameter_list_len, verifier->imgsize);
767 
768 	ret = libscsi_exec(action, target);
769 	samstatus = libscsi_action_get_status(action);
770 
771 	logmsg(MSG_INFO,
772 	    "\nscsi_writebuffer: ret 0x%0x, samstatus 0x%0x\n",
773 	    ret, samstatus);
774 
775 	if ((ret != 0) || samstatus != 0) {
776 		libscsi_action_free(action);
777 		return (ret);
778 	} else {
779 		(void) printf("ok\n");
780 	}
781 
782 	for (i = 0; i < NSAM4_STATUS; i++) {
783 		if (sam4_status[i].status == samstatus) {
784 			(void) printf("%s\n", (sam4_status[i].message));
785 			break;
786 		}
787 	}
788 
789 	if (i == NSAM4_STATUS)
790 		(void) printf("Status: UNKNOWN\n");
791 
792 	if (samstatus == SAM4_STATUS_GOOD) {
793 		internalstatus = FWFLASH_SUCCESS;
794 		return (FWFLASH_SUCCESS);
795 	}
796 
797 	return (FWFLASH_FAILURE);
798 }
799