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 
46 #include <scsi/libses.h>
47 #include <sys/scsi/generic/commands.h>
48 #include <sys/scsi/impl/uscsi.h>
49 
50 #include <libintl.h> /* for gettext(3c) */
51 
52 #include <fwflash/fwflash.h>
53 
54 
55 
56 #ifdef NDEBUG
57 #define	verify(EX)	((void)(EX))
58 #else
59 #define	verify(EX)	assert(EX)
60 #endif
61 
62 
63 #define	VIDLEN		0x08
64 #define	PIDLEN		0x10
65 #define	REVLEN		0x04
66 #define	SASADDRLEN	0x10
67 #define	PCBUFLEN	0x40
68 #define	RQBUFLEN	0xfe
69 #define	STATBUFLEN	0xfe
70 #define	INQBUFLEN	0x80
71 
72 /* useful defines */
73 #define	UCODE_CHECK_STATUS	0
74 #define	UCODE_CHECK_SUPPORTED	1
75 
76 typedef struct ucode_statdesc {
77 	uint64_t	us_value;
78 	const char	*us_desc;
79 	boolean_t	us_pending;
80 	boolean_t	us_iserr;
81 } ucode_statdesc_t;
82 
83 static ucode_statdesc_t ucode_statdesc_table[] = {
84 	{ SES2_DLUCODE_S_NOP,		"none",	B_FALSE, B_FALSE },
85 	{ SES2_DLUCODE_S_INPROGRESS,	"in progress", B_TRUE, B_FALSE },
86 	{ SES2_DLUCODE_S_SAVING,	"saved", B_TRUE, B_FALSE },
87 	{ SES2_DLUCODE_S_COMPLETE_NOW,	"completed (available)", B_FALSE,
88 	    B_FALSE },
89 	{ SES2_DLUCODE_S_COMPLETE_AT_RESET,
90 	    "completed (need reset or power on)", B_FALSE, B_FALSE },
91 	{ SES2_DLUCODE_S_COMPLETE_AT_POWERON,	"completed (need power on)",
92 	    B_FALSE, B_FALSE },
93 	{ SES2_DLUCODE_S_PAGE_ERR,	"page error (offset %d)",
94 	    B_FALSE, B_TRUE },
95 	{ SES2_DLUCODE_S_IMAGE_ERR,	"invalid image",
96 	    B_FALSE, B_TRUE },
97 	{ SES2_DLUCODE_S_TIMEOUT,	"download timeout",
98 	    B_FALSE, B_TRUE },
99 	{ SES2_DLUCODE_S_INTERNAL_NEEDIMAGE,
100 	    "internal error (NEED NEW IMAGE BEFORE RESET)",
101 	    B_FALSE, B_TRUE },
102 	{ SES2_DLUCODE_S_INTERNAL_SAFE,
103 	    "internal error (reset to revert to backup)",
104 	    B_FALSE, B_TRUE },
105 };
106 
107 #define	NUCODE_STATUS	\
108 	(sizeof (ucode_statdesc_table) / sizeof (ucode_statdesc_table[0]))
109 
110 typedef struct ucode_status {
111 	uint64_t	us_status;
112 	boolean_t	us_iserr;
113 	boolean_t	us_pending;
114 	char		us_desc[128];
115 } ucode_status_t;
116 
117 typedef struct ucode_wait {
118 	uint64_t	uw_prevstatus;
119 	boolean_t	uw_pending;
120 	ses_node_t	*uw_oldnp;
121 } ucode_wait_t;
122 
123 
124 char drivername[] = "ses\0";
125 static char *devprefix = "/devices";
126 static char *devsuffix = ":0";
127 static ses_target_t *ses_target;
128 
129 
130 extern di_node_t rootnode;
131 extern int errno;
132 extern struct fw_plugin *self;
133 extern struct vrfyplugin *verifier;
134 extern int fwflash_debug;
135 
136 
137 /* required functions for this plugin */
138 int fw_readfw(struct devicelist *device, char *filename);
139 int fw_writefw(struct devicelist *device);
140 int fw_identify(int start);
141 int fw_devinfo(struct devicelist *thisdev);
142 
143 
144 /* helper functions */
145 static struct vpr *inquiry(char *path);
146 static int ses_dl_ucode_check(struct devicelist *flashdev);
147 static ses_walk_action_t  print_updated_status(ses_node_t *np, void *arg);
148 static int get_status(nvlist_t *props, ucode_status_t *sp);
149 static ses_walk_action_t sendimg(ses_node_t *np, void *data);
150 
151 
152 
153 /*
154  * SES2 does not actually allow us to read a firmware
155  * image from an SES device, so we just return success
156  * if this is requested, after printing a message.
157  */
158 int
159 fw_readfw(struct devicelist *flashdev, char *filename)
160 {
161 	int rv = FWFLASH_SUCCESS;
162 
163 	logmsg(MSG_INFO,
164 	    "ses: not writing firmware for device %s to file %s\n",
165 	    flashdev->access_devname, filename);
166 	logmsg(MSG_ERROR, gettext("\n\nSES2 does not support retrieval "
167 	    "of firmware images\n\n"));
168 
169 	return (rv);
170 }
171 
172 
173 /*
174  * If we're invoking fw_writefw, then flashdev is a valid,
175  * flashable device supporting the SES2 Download Microcode Diagnostic
176  * Control page (0x0e).
177  *
178  * If verifier is null, then we haven't been called following a firmware
179  * image verification load operation.
180  *
181  * *THIS* function uses scsi SEND DIAGNOSTIC/download microcode to
182  * achieve the task... if you chase down to the bottom of libses you
183  * can see that too.
184  */
185 int
186 fw_writefw(struct devicelist *flashdev)
187 {
188 
189 	nvlist_t *nvl;
190 	ses_snap_t *snapshot;
191 
192 
193 
194 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
195 	    nvlist_add_uint64(nvl, SES_CTL_PROP_UCODE_MODE,
196 	    SES_DLUCODE_M_WITH_OFFS) != 0) {
197 		logmsg(MSG_ERROR, gettext("ses: Unable to allocate "
198 		    "space for device prop list\n"));
199 		return (FWFLASH_FAILURE);
200 	}
201 
202 
203 	if ((verifier == NULL) || (verifier->imgsize == 0) ||
204 	    (verifier->fwimage == NULL)) {
205 		/* should _not_ happen */
206 		logmsg(MSG_ERROR, gettext("ses: Firmware image has not "
207 		    "been verified.\n"));
208 		return (FWFLASH_FAILURE);
209 	}
210 
211 	fprintf(stdout, "\n"); /* get a fresh line for progress updates */
212 
213 	if (nvlist_add_uint64(nvl, SES_CTL_PROP_UCODE_BUFID,
214 	    verifier->flashbuf) != 0) {
215 		logmsg(MSG_ERROR, gettext("ses: Unable to add buffer id "
216 		    "property\n"));
217 		goto cancel;
218 	}
219 
220 	if (nvlist_add_byte_array(nvl, SES_CTL_PROP_UCODE_DATA,
221 	    (uint8_t *)verifier->fwimage, verifier->imgsize) != 0) {
222 		logmsg(MSG_ERROR,
223 		    "%s: Out of memory for property addition\n",
224 		    drivername);
225 		goto cancel;
226 	}
227 
228 	if ((ses_target =
229 	    ses_open(LIBSES_VERSION, flashdev->access_devname)) == NULL) {
230 		logmsg(MSG_ERROR,
231 		    gettext("ses: Unable to open flashable device\n%s\n"),
232 		    flashdev->access_devname);
233 		goto cancel;
234 	}
235 	snapshot = ses_snap_hold(ses_target);
236 
237 	/*
238 	 * We flash via a walker callback function, because it's easier
239 	 * to do it this way when using libses.
240 	 */
241 
242 	(void) ses_walk(snapshot, sendimg, nvl);
243 
244 
245 	logmsg(MSG_ERROR,
246 	    gettext("ses: Done. New image will be active "
247 	    "after the system is rebooted.\n"));
248 
249 	fprintf(stdout, "\n");
250 
251 	ses_snap_rele(snapshot);
252 	ses_close(ses_target);
253 
254 cancel:
255 	nvlist_free(nvl);
256 
257 	return (FWFLASH_SUCCESS);
258 }
259 
260 
261 /*
262  * The fw_identify() function walks the device
263  * tree trying to find devices which this plugin
264  * can work with.
265  *
266  * The parameter "start" gives us the starting index number
267  * to give the device when we add it to the fw_devices list.
268  *
269  * firstdev is allocated by us and we add space as needed
270  */
271 int
272 fw_identify(int start)
273 {
274 
275 	int rv = FWFLASH_FAILURE;
276 	di_node_t thisnode;
277 	struct devicelist *newdev;
278 	char *devpath;
279 	int idx = start;
280 	int devlength = 0;
281 
282 
283 	thisnode = di_drv_first_node(drivername, rootnode);
284 
285 	if (thisnode == DI_NODE_NIL) {
286 		logmsg(MSG_INFO, gettext("No %s nodes in this system\n"),
287 		    drivername);
288 
289 		return (rv);
290 	}
291 
292 	if ((devpath = calloc(1, MAXPATHLEN + 1)) == NULL) {
293 		logmsg(MSG_ERROR, gettext("ses: Unable to malloc space "
294 		    "for a %s-attached device node\n"), drivername);
295 		return (rv);
296 	}
297 	bzero(devpath, MAXPATHLEN);
298 
299 	/* we've found one, at least */
300 
301 	for (; thisnode != DI_NODE_NIL; thisnode = di_drv_next_node(thisnode)) {
302 
303 		devpath = di_devfs_path(thisnode);
304 
305 		if ((newdev = calloc(1, sizeof (struct devicelist)))
306 		    == NULL) {
307 			logmsg(MSG_ERROR,
308 			    gettext("ses: identification function unable "
309 			    "to allocate space for device entry\n"));
310 			return (rv);
311 		}
312 
313 		/* malloc enough for /devices + devpath + ":0" + '\0' */
314 		devlength = strlen(devpath) + strlen(devprefix) +
315 		    strlen(devsuffix) + 2;
316 
317 		if ((newdev->access_devname = calloc(1, devlength)) == NULL) {
318 			logmsg(MSG_ERROR, gettext("ses: Unable to malloc "
319 			    "space for a devfs name\n"));
320 			free(devpath);
321 			return (FWFLASH_FAILURE);
322 		}
323 		snprintf(newdev->access_devname, devlength,
324 		    "%s%s%s", devprefix, devpath, devsuffix);
325 
326 		if ((newdev->drvname = calloc(1, strlen(drivername) + 1))
327 		    == NULL) {
328 			logmsg(MSG_ERROR, gettext("ses: Unable to malloc "
329 			    "space for a driver name\n"));
330 			free(newdev->access_devname);
331 			free(newdev);
332 			return (FWFLASH_FAILURE);
333 		}
334 		(void) strlcpy(newdev->drvname, drivername,
335 		    strlen(drivername) + 1);
336 
337 		if ((newdev->classname = calloc(1, strlen(drivername) + 1))
338 		    == NULL) {
339 			logmsg(MSG_ERROR, gettext("ses: Unable to malloc "
340 			    "space for a class name\n"));
341 			free(newdev->access_devname);
342 			free(newdev->drvname);
343 			free(newdev);
344 			return (FWFLASH_FAILURE);
345 		}
346 		(void) strlcpy(newdev->classname, drivername,
347 		    strlen(drivername) + 1);
348 
349 
350 		/*
351 		 * Check for friendly vendor names, and whether this device
352 		 * supports the Download Microcode Control page.
353 		 */
354 
355 		newdev->ident = inquiry(newdev->access_devname);
356 		rv = ses_dl_ucode_check(newdev);
357 		if ((rv == FWFLASH_FAILURE) || (newdev->ident == NULL))
358 			continue;
359 
360 
361 		if (newdev->ident == NULL) {
362 			logmsg(MSG_INFO,
363 			    "ses: unable to inquire on potentially "
364 			    "flashable device\n%s\n",
365 			    newdev->access_devname);
366 			free(newdev->access_devname);
367 			free(newdev->drvname);
368 			free(newdev->classname);
369 			free(newdev);
370 			continue;
371 		}
372 
373 		/*
374 		 * Look for the target-port property. We use addresses[1]
375 		 * because addresses[0] is already assigned by the inquiry
376 		 * function
377 		 */
378 		if ((newdev->addresses[1] = calloc(1, SASADDRLEN + 1))
379 		    == NULL) {
380 			logmsg(MSG_ERROR,
381 			    gettext("ses: Out of memory for target-port\n"));
382 			free(newdev->access_devname);
383 			free(newdev->drvname);
384 			free(newdev->classname);
385 			free(newdev);
386 			continue;
387 		} else {
388 			if (di_prop_lookup_strings(DDI_DEV_T_ANY, thisnode,
389 			    "target-port", &newdev->addresses[1]) < 0) {
390 				logmsg(MSG_INFO,
391 				    "ses: no target-port property for "
392 				    "device %s\n",
393 				    newdev->access_devname);
394 				strlcpy(newdev->addresses[1],
395 				    "0000000000000000", 17);
396 			}
397 		}
398 
399 
400 		newdev->index = idx;
401 		++idx;
402 		newdev->plugin = self;
403 
404 		TAILQ_INSERT_TAIL(fw_devices, newdev, nextdev);
405 	}
406 
407 
408 	if (fwflash_debug != 0) {
409 		struct devicelist *tempdev;
410 
411 		TAILQ_FOREACH(tempdev, fw_devices, nextdev) {
412 			logmsg(MSG_INFO, "ses:fw_writefw:\n");
413 			logmsg(MSG_INFO, "\ttempdev @ 0x%lx\n"
414 			    "\t\taccess_devname: %s\n"
415 			    "\t\tdrvname: %s\tclassname: %s\n"
416 			    "\t\tident->vid:   %s\n"
417 			    "\t\tident->pid:   %s\n"
418 			    "\t\tident->revid: %s\n"
419 			    "\t\tindex:        %d\n"
420 			    "\t\taddress[0]:   %s\n"
421 			    "\t\taddress[1]:   %s\n"
422 			    "\t\tplugin @ 0x%lx\n\n",
423 			    &tempdev,
424 			    tempdev->access_devname,
425 			    tempdev->drvname, newdev->classname,
426 			    tempdev->ident->vid,
427 			    tempdev->ident->pid,
428 			    tempdev->ident->revid,
429 			    tempdev->index,
430 			    (tempdev->addresses[0] != NULL) ?
431 			    (char *)tempdev->addresses[0] : "NULL",
432 			    (tempdev->addresses[1] != NULL) ?
433 			    (char *)tempdev->addresses[1] : "NULL",
434 			    tempdev->plugin);
435 		}
436 	}
437 
438 	return (FWFLASH_SUCCESS);
439 }
440 
441 
442 
443 int
444 fw_devinfo(struct devicelist *thisdev)
445 {
446 
447 
448 	fprintf(stdout, gettext("Device[%d] %s\n  Class [%s]\n"),
449 	    thisdev->index, thisdev->access_devname, thisdev->classname);
450 
451 	if (thisdev->addresses[0] != NULL) {
452 		fprintf(stdout,
453 		    gettext("\tChassis Serial Number  : %s\n"),
454 		    thisdev->addresses[0]);
455 	}
456 
457 	fprintf(stdout,
458 	    gettext("\tVendor                 : %s\n"
459 	    "\tProduct                : %s\n"
460 	    "\tFirmware revision      : %s\n"
461 	    "\tTarget-port identifier : %s\n"),
462 	    thisdev->ident->vid,
463 	    thisdev->ident->pid,
464 	    thisdev->ident->revid,
465 	    thisdev->addresses[1]);
466 
467 	fprintf(stdout, "\n\n");
468 
469 	return (FWFLASH_SUCCESS);
470 }
471 
472 
473 
474 
475 
476 /*ARGSUSED*/
477 static int
478 get_status(nvlist_t *props, ucode_status_t *sp)
479 {
480 	int i;
481 	uint64_t status, astatus;
482 
483 	if (nvlist_lookup_uint64(props, SES_EN_PROP_UCODE, &status) != 0) {
484 		sp->us_status = -1ULL;
485 		(void) snprintf(sp->us_desc, sizeof (sp->us_desc),
486 		    "not supported");
487 		return (-1);
488 	}
489 
490 	verify(nvlist_lookup_uint64(props, SES_EN_PROP_UCODE_A, &astatus) == 0);
491 
492 	for (i = 0; i < NUCODE_STATUS; i++) {
493 		if (ucode_statdesc_table[i].us_value == status)
494 			break;
495 	}
496 
497 	sp->us_status = status;
498 
499 	if (i == NUCODE_STATUS) {
500 		(void) snprintf(sp->us_desc, sizeof (sp->us_desc),
501 		    "unknown (0x%02x)", (int)status);
502 		sp->us_iserr = sp->us_pending = B_FALSE;
503 	} else {
504 		/* LINTED */
505 		(void) snprintf(sp->us_desc, sizeof (sp->us_desc),
506 		    ucode_statdesc_table[i].us_desc, (int)astatus);
507 		sp->us_iserr = ucode_statdesc_table[i].us_iserr;
508 		sp->us_pending = ucode_statdesc_table[i].us_pending;
509 	}
510 
511 	return (0);
512 }
513 
514 
515 static ses_walk_action_t
516 print_updated_status(ses_node_t *np, void *arg)
517 {
518 	ucode_wait_t *uwp = arg;
519 	ses_node_t *oldnp = uwp->uw_oldnp;
520 	nvlist_t *props, *oldprops;
521 	uint64_t id, oldid;
522 	ucode_status_t status;
523 
524 	if (ses_node_type(np) != SES_NODE_ENCLOSURE)
525 		return (SES_WALK_ACTION_CONTINUE);
526 
527 	verify((props = ses_node_props(np)) != NULL);
528 	verify((oldprops = ses_node_props(oldnp)) != NULL);
529 	verify(nvlist_lookup_uint64(props, SES_EN_PROP_EID, &id) == 0);
530 	verify(nvlist_lookup_uint64(oldprops, SES_EN_PROP_EID, &oldid) == 0);
531 
532 	if (oldid != id)
533 		return (SES_WALK_ACTION_CONTINUE);
534 
535 	(void) get_status(props, &status);
536 	if (status.us_status != uwp->uw_prevstatus)
537 		(void) printf("%30s: %s\n", "status", status.us_desc);
538 	uwp->uw_prevstatus = status.us_status;
539 	uwp->uw_pending = status.us_pending;
540 
541 	if (status.us_iserr)
542 		logmsg(MSG_INFO,
543 		    "ses: status.us_iserr: 0x%0x\n",
544 		    status.us_iserr);
545 
546 	return (SES_WALK_ACTION_CONTINUE);
547 }
548 
549 /*ARGSUSED*/
550 static ses_walk_action_t
551 sendimg(ses_node_t *np, void *data)
552 {
553 	nvlist_t *props;
554 	nvlist_t *arg = data;
555 	char *vendor, *product, *revision, *csn;
556 	char buf[128];
557 	ses_snap_t *newsnap;
558 	int ret;
559 	ucode_status_t statdesc;
560 	ucode_wait_t wait;
561 	uint8_t *imagedata;
562 	size_t len;
563 
564 	if (ses_node_type(np) != SES_NODE_ENCLOSURE)
565 		return (SES_WALK_ACTION_CONTINUE);
566 
567 	verify((props = ses_node_props(np)) != NULL);
568 
569 	verify(nvlist_lookup_string(props, SES_EN_PROP_VID, &vendor) == 0);
570 	verify(nvlist_lookup_string(props, SES_EN_PROP_PID, &product) == 0);
571 	verify(nvlist_lookup_string(props, SES_EN_PROP_REV, &revision) == 0);
572 	verify(nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, &csn) == 0);
573 
574 	(void) printf("%30s: %s\n", "vendor", vendor);
575 	(void) printf("%30s: %s\n", "product", product);
576 	(void) printf("%30s: %s\n", "revision", revision);
577 	(void) printf("%30s: %s\n", "serial", csn);
578 
579 	ret = get_status(props, &statdesc);
580 	(void) printf("%30s: %s\n", "current status", statdesc.us_desc);
581 	if (ret != 0) {
582 		if (arg != NULL)
583 			return (SES_WALK_ACTION_TERMINATE);
584 		else
585 			return (SES_WALK_ACTION_CONTINUE);
586 	}
587 
588 	verify(nvlist_lookup_byte_array(arg, SES_CTL_PROP_UCODE_DATA,
589 	    &imagedata, &len) == 0);
590 	(void) snprintf(buf, sizeof (buf), "downloading %u bytes", len);
591 	(void) printf("\n%30s: ", buf);
592 	if (ses_node_ctl(np, SES_CTL_OP_DL_UCODE, arg) != 0) {
593 		(void) printf("failed!\n");
594 		(void) printf("%s\n", ses_errmsg());
595 	} else {
596 		(void) printf("ok\n");
597 	}
598 
599 	wait.uw_prevstatus = -1ULL;
600 	wait.uw_oldnp = np;
601 	do {
602 		if ((newsnap = ses_snap_new(ses_target)) == NULL)
603 			logmsg(MSG_ERROR,
604 			    "failed to update SES snapshot: %s",
605 			    ses_errmsg());
606 
607 		(void) ses_walk(newsnap, print_updated_status,
608 		    &wait);
609 		ses_snap_rele(newsnap);
610 	} while (wait.uw_pending);
611 
612 	return (SES_WALK_ACTION_CONTINUE);
613 }
614 
615 /*
616  * Simple function to sent a standard SCSI INQUIRY(6) cdb out
617  * to thisnode and blat the response back into a struct vpr*
618  */
619 static struct vpr *
620 inquiry(char *path) {
621 
622 	struct uscsi_cmd *inqcmd;
623 	uchar_t inqbuf[INQBUFLEN];	/* inquiry response */
624 	uchar_t rqbuf[RQBUFLEN];	/* request sense data */
625 	struct vpr *inqvpr;
626 	int fd, rval;
627 
628 
629 	inqvpr = NULL;
630 	if ((inqcmd = calloc(1, sizeof (struct uscsi_cmd))) == NULL) {
631 		logmsg(MSG_ERROR,
632 		    gettext("ses: Unable to malloc %d bytes "
633 		    "for a SCSI INQUIRY(6) command\n"),
634 		    sizeof (struct uscsi_cmd));
635 		return (NULL);
636 	}
637 
638 	if ((inqvpr = calloc(1, sizeof (struct vpr))) == NULL) {
639 		logmsg(MSG_ERROR,
640 		    gettext("ses: Unable to malloc %d bytes "
641 		    "for SCSI INQUIRY(6) response\n"),
642 		    sizeof (struct vpr));
643 		free(inqcmd);
644 		return (NULL);
645 	}
646 
647 	if ((inqcmd->uscsi_cdb = calloc(1, CDB_GROUP0 * sizeof (caddr_t)))
648 	    == NULL) {
649 		logmsg(MSG_ERROR,
650 		    gettext("ses: Unable to malloc %d bytes "
651 		    "for SCSI INQUIRY(6)\n"),
652 		    CDB_GROUP0 * sizeof (caddr_t));
653 		free(inqcmd);
654 		free(inqvpr);
655 		return (NULL);
656 	}
657 
658 	logmsg(MSG_INFO, "ses:inquiry:opening device %s\n",
659 	    path);
660 
661 	if ((fd = open(path, O_RDONLY|O_SYNC)) < 0) {
662 		logmsg(MSG_INFO,
663 		    "ses: Unable to open device %s: %s\n",
664 		    path, strerror(errno));
665 		free(inqcmd->uscsi_cdb);
666 		free(inqcmd);
667 		free(inqvpr);
668 		return (NULL);
669 	}
670 
671 	if (((inqvpr->vid = calloc(1, VIDLEN + 1))
672 	    == NULL) ||
673 	    ((inqvpr->pid = calloc(1, PIDLEN + 1))
674 		== NULL) ||
675 	    ((inqvpr->revid = calloc(1, REVLEN + 1))
676 		== NULL)) {
677 		logmsg(MSG_ERROR,
678 		    gettext("ses: Unable to malloc %d bytes "
679 		    "for %s identification function.\n"),
680 		    VIDLEN+PIDLEN+REVLEN, drivername);
681 		free(inqcmd->uscsi_cdb);
682 		free(inqcmd);
683 		free(inqvpr->vid);
684 		free(inqvpr->pid);
685 		free(inqvpr->revid);
686 		return (NULL);
687 	}
688 
689 	/* just make sure these buffers are clean */
690 	bzero(inqbuf, INQBUFLEN);
691 	bzero(rqbuf, RQBUFLEN);
692 	bzero(inqcmd->uscsi_cdb, CDB_GROUP0);
693 	inqcmd->uscsi_flags = USCSI_READ;
694 	inqcmd->uscsi_timeout = 0;
695 	inqcmd->uscsi_bufaddr = (caddr_t)inqbuf;
696 	inqcmd->uscsi_buflen = INQBUFLEN;
697 	inqcmd->uscsi_cdblen = CDB_GROUP0; /* a GROUP 0 command */
698 	inqcmd->uscsi_cdb[0] = SCMD_INQUIRY;
699 	inqcmd->uscsi_cdb[1] = 0x00; /* EVPD = Enable Vital Product Data */
700 	inqcmd->uscsi_cdb[2] = 0x00; /* which pagecode to query? */
701 	inqcmd->uscsi_cdb[3] = 0x00; /* allocation length, msb */
702 	inqcmd->uscsi_cdb[4] = INQBUFLEN; /* allocation length, lsb */
703 	inqcmd->uscsi_cdb[5] = 0x0; /* control byte */
704 	inqcmd->uscsi_rqbuf = (caddr_t)&rqbuf;
705 	inqcmd->uscsi_rqlen = RQBUFLEN;
706 
707 
708 	rval = ioctl(fd, USCSICMD, inqcmd);
709 
710 	if (rval < 0) {
711 		/* ioctl failed */
712 		logmsg(MSG_INFO,
713 		    gettext("ses: Unable to retrieve SCSI INQUIRY(6) data "
714 			"from device %s: %s\n"),
715 		    path, strerror(errno));
716 		free(inqcmd->uscsi_cdb);
717 		free(inqcmd);
718 		free(inqvpr->vid);
719 		free(inqvpr->pid);
720 		free(inqvpr->revid);
721 		return (NULL);
722 	}
723 
724 
725 
726 	bcopy(&inqbuf[8], inqvpr->vid, VIDLEN);
727 	bcopy(&inqbuf[16], inqvpr->pid, PIDLEN);
728 	bcopy(&inqbuf[32], inqvpr->revid, REVLEN);
729 
730 	(void) close(fd);
731 
732 	logmsg(MSG_INFO,
733 	    "ses inquiry: vid %s ; pid %s ; revid %s\n",
734 	    inqvpr->vid, inqvpr->pid, inqvpr->revid);
735 
736 	if ((strncmp(inqvpr->vid, "SUN", 3) != 0) &&
737 	    (strncmp(inqvpr->vid, "LSI", 3) != 0) &&
738 	    (strncmp(inqvpr->vid, "Quanta", 6) != 0) &&
739 	    (strncmp(inqvpr->vid, "QUANTA", 6) != 0)) {
740 		free(inqvpr->vid);
741 		free(inqvpr->pid);
742 		free(inqvpr->revid);
743 		inqvpr->vid = NULL;
744 		inqvpr->pid = NULL;
745 		inqvpr->revid = NULL;
746 		logmsg(MSG_INFO,
747 		    "ses inquiry: unrecognised device\n");
748 		return (NULL);
749 	}
750 
751 	free(inqcmd->uscsi_cdb);
752 	free(inqcmd);
753 
754 	return (inqvpr);
755 }
756 
757 
758 /*
759  * ses_dl_ucode_check() lets us check whether SES2's Download
760  * Microcode Control diagnostic and status pages are supported
761  * by flashdev.
762  */
763 int
764 ses_dl_ucode_check(struct devicelist *flashdev)
765 {
766 	int rv;
767 	int limit;
768 	int i = 0;
769 	int fd;
770 	struct uscsi_cmd *usc;
771 	uchar_t sensebuf[RQBUFLEN]; /* for the request sense data */
772 	uchar_t pagesup[PCBUFLEN]; /* should be less than 64 pages */
773 
774 
775 	if ((fd = open(flashdev->access_devname,
776 	    O_RDONLY|O_NDELAY)) < 0) {
777 		logmsg(MSG_INFO,
778 		    gettext("ses:ses_dl_ucode_check: Unable to open %s\n"),
779 		    flashdev->access_devname);
780 		return (FWFLASH_FAILURE);
781 	}
782 
783 	if ((usc = calloc(1, sizeof (struct uscsi_cmd))) == NULL) {
784 		logmsg(MSG_ERROR,
785 		    gettext("ses: Unable to alloc %d bytes for "
786 		    "microcode download query: %s\n"),
787 		    sizeof (struct uscsi_cmd), strerror(errno));
788 		(void) close(fd);
789 		return (FWFLASH_FAILURE);
790 	}
791 
792 	if ((usc->uscsi_cdb = calloc(1, CDB_GROUP0 * sizeof (caddr_t)))
793 	    == NULL) {
794 		logmsg(MSG_ERROR,
795 		    gettext("ses: Unable to alloc %d bytes for "
796 		    "microcode download query: %s\n"),
797 		    CDB_GROUP0 * sizeof (caddr_t), strerror(errno));
798 		(void) close(fd);
799 		free(usc);
800 		return (FWFLASH_FAILURE);
801 	}
802 
803 
804 	bzero(sensebuf, RQBUFLEN);
805 
806 	usc->uscsi_flags = USCSI_READ | USCSI_RQENABLE;
807 	usc->uscsi_timeout = 0;
808 	usc->uscsi_cdblen = CDB_GROUP0;
809 	usc->uscsi_rqbuf = (caddr_t)&sensebuf;
810 	usc->uscsi_rqlen = RQBUFLEN;
811 
812 
813 	bzero(pagesup, PCBUFLEN);
814 	usc->uscsi_bufaddr = (caddr_t)&pagesup;
815 	usc->uscsi_buflen = PCBUFLEN;
816 
817 	usc->uscsi_cdb[0] = SCMD_GDIAG; /* "Get" or receive */
818 	usc->uscsi_cdb[1] = 1; /* PCV = Page Code Valid */
819 	usc->uscsi_cdb[2] = 0x00; /* list all Supported diag pages */
820 	usc->uscsi_cdb[3] = 0x00;
821 	usc->uscsi_cdb[4] = PCBUFLEN;
822 	usc->uscsi_cdb[5] = 0; /* control byte, reserved */
823 
824 	rv = ioctl(fd, USCSICMD, usc);
825 	if (rv < 0) {
826 		logmsg(MSG_INFO,
827 		    "ses: DL uCode checker ioctl error (%d): %s\n",
828 		    errno, strerror(errno));
829 		return (FWFLASH_FAILURE);
830 	}
831 
832 	rv = FWFLASH_FAILURE;
833 	/* in SPC4, this is an "n-3" field */
834 	limit = (pagesup[2] << 8) + pagesup[3] + 4;
835 	while (i < limit) {
836 		if (pagesup[4 + i] == 0x0E) {
837 			i = limit;
838 			logmsg(MSG_INFO, "ses: device %s "
839 			    "supports the Download Microcode "
840 			    "diagnostic control page\n",
841 			    flashdev->access_devname);
842 			rv = FWFLASH_SUCCESS;
843 		}
844 		++i;
845 	}
846 	(void) close(fd);
847 	free(usc->uscsi_cdb);
848 	free(usc);
849 
850 	return (rv);
851 }
852