ses.c revision 7317:bd398a71a662
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
58typedef 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
65static 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
92typedef 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
99typedef 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
106typedef struct sam4_statdesc {
107	int status;
108	char *message;
109} sam4_statdesc_t;
110
111
112static 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
129char drivername[] = "ses\0";
130static char *devprefix = "/devices";
131static char *sessuffix = ":0";
132static char *sgensuffix = ":ses";
133
134
135static ses_target_t *ses_target;
136static int internalstatus;
137
138extern di_node_t rootnode;
139extern int errno;
140extern struct fw_plugin *self;
141extern struct vrfyplugin *verifier;
142extern int fwflash_debug;
143
144
145/* required functions for this plugin */
146int fw_readfw(struct devicelist *device, char *filename);
147int fw_writefw(struct devicelist *device);
148int fw_identify(int start);
149int fw_devinfo(struct devicelist *thisdev);
150
151
152/* helper functions */
153static void print_updated_status(ses_node_t *np, void *arg);
154static int get_status(nvlist_t *props, ucode_status_t *sp);
155static int sendimg(ses_node_t *np, void *data);
156static 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 */
163int
164fw_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 */
191int
192fw_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);
271cancel:
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 */
288int
289fw_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
553int
554fw_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*/
583static int
584get_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
628static void
629print_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*/
662static int
663sendimg(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
741static int
742scsi_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