summaryrefslogtreecommitdiff
path: root/usr/src/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd')
-rw-r--r--usr/src/cmd/fwflash/Makefile.com2
-rw-r--r--usr/src/cmd/fwflash/Makefile.targ6
-rw-r--r--usr/src/cmd/fwflash/plugins/Makefile.targ3
-rw-r--r--usr/src/cmd/fwflash/plugins/transport/common/sd.c413
-rw-r--r--usr/src/cmd/fwflash/plugins/vendor/sd-GENERIC.c195
5 files changed, 465 insertions, 154 deletions
diff --git a/usr/src/cmd/fwflash/Makefile.com b/usr/src/cmd/fwflash/Makefile.com
index 6aecb4abf0..3a5d9c36a2 100644
--- a/usr/src/cmd/fwflash/Makefile.com
+++ b/usr/src/cmd/fwflash/Makefile.com
@@ -25,8 +25,6 @@
#
# common rules for $SRC/cmd/fwflash
-CLOSED= $(SRC)/../closed
-
CERRWARN += -_gcc=-Wno-parentheses
CERRWARN += -_gcc=-Wno-uninitialized
CERRWARN += -_gcc=-Wno-address
diff --git a/usr/src/cmd/fwflash/Makefile.targ b/usr/src/cmd/fwflash/Makefile.targ
index b0da734aff..57efebf6ac 100644
--- a/usr/src/cmd/fwflash/Makefile.targ
+++ b/usr/src/cmd/fwflash/Makefile.targ
@@ -28,6 +28,7 @@
include $(SRC)/Makefile.master
include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/cmd/Makefile.ctf
SRCS= fwflash.c
HDRS= fwflash.h
@@ -56,7 +57,7 @@ CFLAGS += -D_POSIX_PTHREAD_SEMANTICS
LDLIBS += -ldevinfo -lumem -lscf
-$(ROOTUSRINCLDFWFLASH)/%: $(HDRDIR)/%
+$(ROOTUSRINCLDFWFLASH)/%: $(HDRDIR)/%
$(INS.file)
@@ -66,11 +67,12 @@ $(PROG): install_h $(OBJS)
%.o: $(SRCDIR)/%.c
$(COMPILE.c) $(CFLAGS) -o $@ $<
+ $(CTFCONVERT_O)
all: $(PROG)
-clean:
+clean:
$(RM) $(POFILE) $(POFILES) $(LINTFILE) $(PROG)
clobber: clean
diff --git a/usr/src/cmd/fwflash/plugins/Makefile.targ b/usr/src/cmd/fwflash/plugins/Makefile.targ
index 8650ee545b..8edeaccd66 100644
--- a/usr/src/cmd/fwflash/plugins/Makefile.targ
+++ b/usr/src/cmd/fwflash/plugins/Makefile.targ
@@ -61,6 +61,9 @@ $(SD-GENERIC_LIB):= SONAME = $(SD-GENERIC_LIB)
$(HERMON-MELLANOX_LIB):= DYNFLAGS += -R/usr/lib/fwflash/identify
$(HERMON-MELLANOX_LIB):= LDLIBS += -L. $(ROOT)/usr/lib/fwflash/identify/hermon.so
+$(SD-GENERIC_LIB):= LDLIBS += -L$(ROOT)/usr/lib/scsi -lscsi
+$(SD-GENERIC_LIB):= DYNFLAGS += -R/usr/lib/scsi
+
.KEEP STATE:
diff --git a/usr/src/cmd/fwflash/plugins/transport/common/sd.c b/usr/src/cmd/fwflash/plugins/transport/common/sd.c
index 46faad0877..6ebbdaeee1 100644
--- a/usr/src/cmd/fwflash/plugins/transport/common/sd.c
+++ b/usr/src/cmd/fwflash/plugins/transport/common/sd.c
@@ -18,14 +18,18 @@
*
* CDDL HEADER END
*/
+
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ *
+ * Copyright 2016 Joyent, Inc.
*/
/*
* sd / ssd (SCSI Direct-attached Device) specific functions.
*/
+
#include <libnvpair.h>
#include <stdio.h>
#include <stdlib.h>
@@ -33,16 +37,20 @@
#include <sys/types.h>
#include <sys/sysmacros.h>
#include <sys/queue.h>
+#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <scsi/libscsi.h>
+#include <sys/scsi/scsi_types.h>
#include <libintl.h> /* for gettext(3c) */
#include <fwflash/fwflash.h>
+#include <sys/debug.h>
+#include <umem.h>
typedef struct sam4_statdesc {
- int status;
- char *message;
+ int sam_status;
+ char *sam_message;
} sam4_statdesc_t;
static sam4_statdesc_t sam4_status[] = {
@@ -53,12 +61,10 @@ static sam4_statdesc_t sam4_status[] = {
{ SAM4_STATUS_RESERVATION_CONFLICT, "Status: Device is RESERVED" },
{ SAM4_STATUS_TASK_SET_FULL,
"Status: TASK SET FULL (insufficient resources in command queue" },
- { SAM4_STATUS_TASK_ABORTED, "Status: TASK ABORTED" },
- { NULL, NULL }
+ { SAM4_STATUS_TASK_ABORTED, "Status: TASK ABORTED" }
};
-#define NSAM4_STATUS \
- (sizeof (sam4_status) / sizeof (sam4_status[0]))
+#define NSAM4_STATUS (sizeof (sam4_status) / sizeof (sam4_status[0]))
#define FW_SD_FREE_DEVPATH(devpath) { \
di_devfs_path_free((devpath)); \
@@ -100,27 +106,35 @@ static sam4_statdesc_t sam4_status[] = {
FW_SD_FREE_IDENT_PID((thisdev), (devpath)) \
}
-int errno;
+/*
+ * This is our default partial write size when we encounter a situation where we
+ * need to upgrade disks whose firmware image cannot be done in a single write.
+ * While in theory we should just use the maximum transfer size and make sure
+ * it's aligned, that's proven to be problematic for some Seagate disks. Hence
+ * we just make sure that if partial writes are required that this value fits in
+ * the required alignment and in the actual maximum transfer size.
+ */
+#define FW_SD_PARTIAL_WRITE_SIZE (64 * 1024)
+
+/*
+ * Declarations required for fwflash
+ */
char drivername[] = "sd\0";
int plugin_version = FWPLUGIN_VERSION_2;
-static char *devprefix = "/devices";
+/*
+ * Data provided by fwflash
+ */
extern di_node_t rootnode;
extern struct fw_plugin *self;
extern struct vrfyplugin *verifier;
extern int fwflash_debug;
-/* required functions for this plugin */
-int fw_readfw(struct devicelist *device, char *filename);
-int fw_writefw(struct devicelist *device);
-int fw_identify(int start);
-int fw_devinfo(struct devicelist *thisdev);
-void fw_cleanup(struct devicelist *thisdev);
+static char *sdfw_devprefix = "/devices";
-/* helper functions */
-static char *find_link(di_node_t bnode, char *acc_devname);
-static int link_cb(di_devlink_t devlink, void *arg);
-static int sd_idtfy_custmz(struct devicelist *device, char *sp);
+static char *sdfw_find_link(di_node_t bnode, char *acc_devname);
+static int sdfw_link_cb(di_devlink_t devlink, void *arg);
+static int sdfw_idtfy_custmz(struct devicelist *device, char *sp);
/*
* We don't currently support reading firmware from a disk. If we do eventually
@@ -141,17 +155,215 @@ fw_readfw(struct devicelist *flashdev, char *filename)
return (FWFLASH_SUCCESS);
}
+
+static int
+sdfw_read_descriptor(struct devicelist *flashdev, libscsi_hdl_t *hdl,
+ libscsi_target_t *targ, uint8_t *align)
+{
+ spc3_read_buffer_cdb_t *rb_cdb;
+ size_t nwritten;
+ libscsi_action_t *action = NULL;
+ uint8_t descbuf[4];
+ sam4_status_t samstatus;
+
+ VERIFY3P(hdl, !=, NULL);
+ VERIFY3P(targ, !=, NULL);
+ VERIFY3P(align, !=, NULL);
+
+ if ((action = libscsi_action_alloc(hdl, SPC3_CMD_READ_BUFFER,
+ LIBSCSI_AF_READ, descbuf, sizeof (descbuf))) == NULL) {
+ logmsg(MSG_ERROR, gettext("%s: failed to alloc scsi action: "
+ "%s\n"),
+ flashdev->drvname, libscsi_errmsg(hdl));
+ return (FWFLASH_FAILURE);
+ }
+
+ rb_cdb = (spc3_read_buffer_cdb_t *)libscsi_action_get_cdb(action);
+
+ rb_cdb->rbc_mode = SPC3_RB_MODE_DESCRIPTOR;
+
+ /*
+ * Microcode upgrade usually only uses the first buffer ID which is
+ * sequentially indexed from zero. Strictly speaking these are all
+ * vendor defined, but so far most vendors we've seen use index zero
+ * for this.
+ */
+ rb_cdb->rbc_bufferid = 0;
+
+ rb_cdb->rbc_allocation_len[0] = 0;
+ rb_cdb->rbc_allocation_len[1] = 0;
+ rb_cdb->rbc_allocation_len[2] = sizeof (descbuf);
+
+ if (libscsi_exec(action, targ) != 0) {
+ logmsg(MSG_ERROR, gettext("%s: failed to execute SCSI buffer "
+ "data read: %s\n"),
+ flashdev->drvname, libscsi_errmsg(hdl));
+ libscsi_action_free(action);
+ return (FWFLASH_FAILURE);
+ }
+
+ if ((samstatus = libscsi_action_get_status(action)) !=
+ SAM4_STATUS_GOOD) {
+ int i;
+ for (i = 0; i < NSAM4_STATUS; i++) {
+ if (samstatus == sam4_status[i].sam_status) {
+ logmsg(MSG_ERROR, gettext("%s: SCSI buffer "
+ "data read failed: %s\n"),
+ flashdev->drvname,
+ sam4_status[i].sam_message);
+ libscsi_action_free(action);
+ return (FWFLASH_FAILURE);
+ }
+ }
+ logmsg(MSG_ERROR, gettext("%s: SCSI buffer data read failed: "
+ "unknown error: %d\n"), flashdev->drvname, samstatus);
+ libscsi_action_free(action);
+ return (FWFLASH_FAILURE);
+ }
+
+ if (libscsi_action_get_buffer(action, NULL, NULL, &nwritten) != 0) {
+ logmsg(MSG_ERROR, gettext("%s: failed to get actual data "
+ "size: %s\n"),
+ flashdev->drvname, libscsi_errmsg(hdl));
+ libscsi_action_free(action);
+ return (FWFLASH_FAILURE);
+ }
+ libscsi_action_free(action);
+
+ if (nwritten != sizeof (descbuf)) {
+ logmsg(MSG_ERROR, gettext("%s: received a short read from the "
+ "SCSI READ BUFFER command, expected %u bytes, read %zu\n"),
+ flashdev->drvname, sizeof (descbuf), nwritten);
+ return (FWFLASH_FAILURE);
+ }
+
+ if (descbuf[0] == 0 && descbuf[1] == 0 && descbuf[2] == 0 &&
+ descbuf[3] == 0) {
+ logmsg(MSG_ERROR, gettext("%s: devices %s does not support "
+ "firmware upgrade\n"), verifier->vendor,
+ flashdev->access_devname);
+ return (FWFLASH_FAILURE);
+ }
+
+ *align = descbuf[0];
+
+ return (FWFLASH_SUCCESS);
+}
+
+static int
+sdfw_write(struct devicelist *flashdev, libscsi_hdl_t *handle,
+ libscsi_target_t *target, size_t len, size_t off, void *buf)
+{
+ sam4_status_t samstatus;
+ libscsi_action_t *action = NULL;
+ spc3_write_buffer_cdb_t *wb_cdb;
+
+ logmsg(MSG_INFO, "%s: writing %u bytes of image %s at offset %zu from "
+ "address %p\n", flashdev->drvname, len, verifier->imgfile, off,
+ buf);
+ logmsg(MSG_INFO, "%s: writing to buffer id %u\n",
+ flashdev->drvname, verifier->flashbuf);
+
+ VERIFY3P(flashdev, !=, NULL);
+ VERIFY3P(handle, !=, NULL);
+ VERIFY3P(target, !=, NULL);
+ VERIFY3P(buf, !=, NULL);
+ VERIFY3U(len, >, 0);
+ VERIFY3U(off + len, <=, verifier->imgsize);
+
+ action = libscsi_action_alloc(handle, SPC3_CMD_WRITE_BUFFER,
+ LIBSCSI_AF_WRITE | LIBSCSI_AF_RQSENSE | LIBSCSI_AF_ISOLATE, buf,
+ len);
+ if (action == NULL) {
+ logmsg(MSG_ERROR, gettext("%s: failed to alloc scsi action: "
+ "%s\n"), flashdev->drvname, libscsi_errmsg(handle));
+ goto err;
+ }
+
+ wb_cdb = (spc3_write_buffer_cdb_t *)libscsi_action_get_cdb(action);
+
+ wb_cdb->wbc_mode = SPC3_WB_MODE_DL_UCODE_OFFS_SAVE;
+
+ wb_cdb->wbc_buffer_offset[0] = (off >> 16) & 0xff;
+ wb_cdb->wbc_buffer_offset[1] = (off >> 8) & 0xff;
+ wb_cdb->wbc_buffer_offset[2] = off & 0xff;
+
+ wb_cdb->wbc_bufferid = verifier->flashbuf;
+
+ wb_cdb->wbc_parameter_list_len[0] = (len >> 16) & 0xff;
+ wb_cdb->wbc_parameter_list_len[1] = (len >> 8) & 0xff;
+ wb_cdb->wbc_parameter_list_len[2] = len & 0xff;
+
+ logmsg(MSG_INFO, "%s: spc3_write_buffer_cdb_t opcode: %u\n",
+ flashdev->drvname, wb_cdb->wbc_opcode);
+
+ if (libscsi_exec(action, target) != 0) {
+ logmsg(MSG_ERROR, gettext("%s: failed to execute SCSI WRITE "
+ "BUFFER: %s\n"),
+ flashdev->drvname, libscsi_errmsg(handle));
+ goto err;
+ }
+
+ if ((samstatus = libscsi_action_get_status(action)) ==
+ SAM4_STATUS_CHECK_CONDITION) {
+ uint64_t asc = 0, ascq = 0, key = 0;
+ const char *code, *keystr;
+
+ if (libscsi_action_parse_sense(action, &key, &asc, &ascq,
+ NULL) != 0) {
+ logmsg(MSG_ERROR, gettext("%s: failed to write "
+ "firmware. Received CHECK_CONDITION that cannot be "
+ "parsed.\n"),
+ flashdev->drvname);
+ goto err;
+ }
+
+ code = libscsi_sense_code_name(asc, ascq);
+ keystr = libscsi_sense_key_name(key);
+
+ logmsg(MSG_ERROR, gettext("%s: failed to write firmware: "
+ "received sense key %" PRIu64 " (%s) additional sense code "
+ "0x%" PRIx64 "/0x%" PRIx64 " (%s)\n"), flashdev->drvname,
+ key, keystr != NULL ? keystr : "<unknown>",
+ asc, ascq, code != NULL ? code : "<unknown>");
+ goto err;
+ } else if (samstatus != SAM4_STATUS_GOOD) {
+ int i;
+
+ logmsg(MSG_ERROR, gettext("%s: SCSI buffer data write failed:"),
+ flashdev->drvname);
+ for (i = 0; i < NSAM4_STATUS; i++) {
+ if (samstatus == sam4_status[i].sam_status) {
+ logmsg(MSG_ERROR, gettext("%s\n"),
+ sam4_status[i].sam_message);
+ goto err;
+ }
+ }
+ logmsg(MSG_ERROR, gettext("unknown error: %d\n"), samstatus);
+ goto err;
+ } else {
+ logmsg(MSG_INFO, "%s: received STATUS GOOD\n",
+ flashdev->drvname);
+ }
+
+ libscsi_action_free(action);
+ return (FWFLASH_SUCCESS);
+
+err:
+ if (action != NULL)
+ libscsi_action_free(action);
+ return (FWFLASH_FAILURE);
+}
+
int
fw_writefw(struct devicelist *flashdev)
{
- int rv;
- int i = 0;
libscsi_hdl_t *handle;
libscsi_target_t *target;
- libscsi_action_t *action;
libscsi_errno_t serr;
- spc3_write_buffer_cdb_t *wb_cdb;
- sam4_status_t samstatus;
+ size_t maxxfer, nwrite;
+ uint8_t align;
+ int ret = FWFLASH_FAILURE;
if ((verifier == NULL) || (verifier->imgsize == 0) ||
(verifier->fwimage == NULL)) {
@@ -168,8 +380,8 @@ fw_writefw(struct devicelist *flashdev)
return (FWFLASH_FAILURE);
}
- if ((target = libscsi_open(handle, NULL, flashdev->access_devname))
- == NULL) {
+ if ((target = libscsi_open(handle, NULL, flashdev->access_devname)) ==
+ NULL) {
logmsg(MSG_ERROR,
gettext("%s: unable to open device %s\n"),
flashdev->drvname, flashdev->access_devname);
@@ -177,55 +389,90 @@ fw_writefw(struct devicelist *flashdev)
return (FWFLASH_FAILURE);
}
- action = libscsi_action_alloc(handle, SPC3_CMD_WRITE_BUFFER,
- LIBSCSI_AF_WRITE|LIBSCSI_AF_RQSENSE,
- (void *)verifier->fwimage, (size_t)verifier->imgsize);
-
- wb_cdb = (spc3_write_buffer_cdb_t *)libscsi_action_get_cdb(action);
+ if (libscsi_max_transfer(target, &maxxfer) != 0) {
+ logmsg(MSG_ERROR, gettext("%s: failed to determine device "
+ "maximum transfer size: %s\n"), flashdev->drvname,
+ libscsi_errmsg(handle));
+ goto err;
+ }
- wb_cdb->wbc_mode = SPC3_WB_MODE_DL_UCODE_SAVE;
- wb_cdb->wbc_bufferid = verifier->flashbuf;
+ if (sdfw_read_descriptor(flashdev, handle, target, &align) !=
+ FWFLASH_SUCCESS) {
+ goto err;
+ }
- wb_cdb->wbc_buffer_offset[0] = 0;
- wb_cdb->wbc_buffer_offset[1] = 0;
- wb_cdb->wbc_buffer_offset[2] = 0;
+ /*
+ * If the maximum transfer size is less than the maximum image size then
+ * we have to do some additional work. We need to read the descriptor
+ * via a READ BUFFER command and make sure that we support the required
+ * offset alignment. Note that an alignment of 0xff indicates that the
+ * device does not support partial writes and must receive the firmware
+ * in a single WRITE BUFFER. Otherwise a value in align represents a
+ * required offset alignment of 2^off. From there, we make sure that
+ * this works for our partial write size and that our partial write size
+ * fits in the maximum transfer size.
+ */
+ if (maxxfer < verifier->imgsize) {
+ logmsg(MSG_INFO, "%s: Maximum transfer is %zu, required "
+ "alignment is 2^%u\n", flashdev->drvname, maxxfer, align);
+ if (FW_SD_PARTIAL_WRITE_SIZE > maxxfer) {
+ logmsg(MSG_ERROR, gettext("%s: cannot write firmware "
+ "image: HBA enforces a maximum transfer size of "
+ "%zu bytes, but the default partial transfer size "
+ "is %u bytes\n"), flashdev->drvname, maxxfer,
+ FW_SD_PARTIAL_WRITE_SIZE);
+ goto err;
+ }
+ maxxfer = FW_SD_PARTIAL_WRITE_SIZE;
- wb_cdb->wbc_parameter_list_len[0] =
- (verifier->imgsize & 0xff0000) >> 16;
- wb_cdb->wbc_parameter_list_len[1] = (verifier->imgsize & 0xff00) >> 8;
- wb_cdb->wbc_parameter_list_len[2] = (verifier->imgsize & 0xff);
+ if (ffsll(maxxfer) < align || align == 0xff) {
+ logmsg(MSG_ERROR, gettext("%s: cannot write firmware "
+ "image: device requires partial writes aligned "
+ "to an unsupported value\n"), flashdev->drvname);
+ goto err;
+ }
- rv = libscsi_exec(action, target);
- samstatus = libscsi_action_get_status(action);
+ logmsg(MSG_INFO, "%s: final transfer block size is %zu\n",
+ flashdev->drvname, maxxfer);
+ }
- logmsg(MSG_INFO, "\nscsi_writebuffer: ret 0x%0x, samstatus 0x%0x\n",
- rv, samstatus);
+ logmsg(MSG_INFO, "%s: Writing out %u bytes to %s\n", flashdev->drvname,
+ verifier->imgsize, flashdev->access_devname);
+ nwrite = 0;
+ for (;;) {
+ uintptr_t buf;
+ size_t towrite = MIN(maxxfer, verifier->imgsize - nwrite);
- libscsi_action_free(action);
- libscsi_close(handle, target);
- libscsi_fini(handle);
+ if (towrite == 0)
+ break;
- if (rv != FWFLASH_SUCCESS)
- return (FWFLASH_FAILURE);
+ buf = (uintptr_t)verifier->fwimage;
+ buf += nwrite;
- for (i = 0; i < NSAM4_STATUS; i++) {
- if (sam4_status[i].status == samstatus) {
- logmsg(MSG_ERROR, gettext("RETURN STATUS: %s\n"),
- (sam4_status[i].message));
- break;
+ if (sdfw_write(flashdev, handle, target, towrite, nwrite,
+ (void *)buf) != FWFLASH_SUCCESS) {
+ logmsg(MSG_ERROR, gettext("%s: failed to write to %s "
+ "successfully: %s\n"), flashdev->drvname,
+ flashdev->access_devname, libscsi_errmsg(handle));
+ goto err;
}
- }
- if (i == NSAM4_STATUS)
- logmsg(MSG_ERROR, gettext("Status UNKNOWN\n"));
- if (samstatus == SAM4_STATUS_GOOD) {
- logmsg(MSG_ERROR, gettext("Note: For flash based disks "
- "(SSD, etc). You may need power off the system to wait a "
- "few minutes for supercap to fully discharge, then power "
- "on the system again to activate the new firmware\n"));
- return (FWFLASH_SUCCESS);
+ nwrite += towrite;
}
- return (FWFLASH_FAILURE);
+
+ logmsg(MSG_ERROR, gettext("Note: For flash based disks "
+ "(SSD, etc). You may need power off the system to wait a "
+ "few minutes for supercap to fully discharge, then power "
+ "on the system again to activate the new firmware\n"));
+ ret = FWFLASH_SUCCESS;
+
+err:
+ if (target != NULL)
+ libscsi_close(handle, target);
+ if (handle != NULL)
+ libscsi_fini(handle);
+
+ return (ret);
}
/*
@@ -300,8 +547,8 @@ fw_identify(int start)
continue;
}
- if ((newdev = calloc(1, sizeof (struct devicelist)))
- == NULL) {
+ if ((newdev = calloc(1, sizeof (struct devicelist))) ==
+ NULL) {
logmsg(MSG_ERROR,
gettext("%s: identification function unable "
"to allocate space for device entry\n"),
@@ -311,8 +558,8 @@ fw_identify(int start)
return (FWFLASH_FAILURE);
}
- if ((newdev->drvname = calloc(1, strlen(driver) + 1))
- == NULL) {
+ if ((newdev->drvname = calloc(1, strlen(driver) + 1)) ==
+ NULL) {
logmsg(MSG_ERROR,
gettext("%s: Unable to allocate space to store a "
"driver name\n"), driver);
@@ -322,8 +569,8 @@ fw_identify(int start)
}
(void) strlcpy(newdev->drvname, driver, strlen(driver) + 1);
- if ((newdev->classname = calloc(1, strlen(driver) + 1))
- == NULL) {
+ if ((newdev->classname = calloc(1, strlen(driver) + 1)) ==
+ NULL) {
logmsg(MSG_ERROR,
gettext("%s: Unable to allocate space for a class "
"name\n"), drivername);
@@ -345,12 +592,12 @@ fw_identify(int start)
/* The slice number may be 2 or 0, we will try 2 first */
(void) snprintf(newdev->access_devname, MAXPATHLEN,
- "%s%s:c,raw", devprefix, devpath);
+ "%s%s:c,raw", sdfw_devprefix, devpath);
if ((target = libscsi_open(handle, NULL,
newdev->access_devname)) == NULL) {
/* try 0 for EFI label */
(void) snprintf(newdev->access_devname, MAXPATHLEN,
- "%s%s:a,raw", devprefix, devpath);
+ "%s%s:a,raw", sdfw_devprefix, devpath);
if ((target = libscsi_open(handle, NULL,
newdev->access_devname)) == NULL) {
logmsg(MSG_INFO,
@@ -362,7 +609,7 @@ fw_identify(int start)
}
/* and the /dev/rdsk/ name */
- if ((newdev->addresses[0] = find_link(thisnode,
+ if ((newdev->addresses[0] = sdfw_find_link(thisnode,
newdev->access_devname)) == NULL) {
libscsi_fini(handle);
FW_SD_FREE_ACC_NAME(newdev, devpath)
@@ -423,7 +670,7 @@ fw_identify(int start)
* There is no SPACE character in the PID field
* Customize strings for special SATA disks
*/
- if (sd_idtfy_custmz(newdev, sp_temp)
+ if (sdfw_idtfy_custmz(newdev, sp_temp)
!= FWFLASH_SUCCESS) {
libscsi_close(handle, target);
libscsi_fini(handle);
@@ -483,8 +730,8 @@ fw_identify(int start)
/* Revision ID */
sp_temp = (char *)libscsi_revision(target);
- if ((newdev->ident->revid = calloc(1, strlen(sp_temp) + 1))
- == NULL || sp_temp == NULL) {
+ if ((newdev->ident->revid = calloc(1, strlen(sp_temp) + 1)) ==
+ NULL || sp_temp == NULL) {
logmsg(MSG_ERROR, gettext("%s: unable to get revision "
"id of %s\n"), newdev->drvname,
newdev->access_devname);
@@ -634,7 +881,7 @@ fw_cleanup(struct devicelist *thisdev)
* Helper functions
*/
static int
-link_cb(di_devlink_t devlink, void *arg)
+sdfw_link_cb(di_devlink_t devlink, void *arg)
{
const char *result;
@@ -645,14 +892,14 @@ link_cb(di_devlink_t devlink, void *arg)
(void) strlcpy(arg, result, strlen(result) + 1);
}
- logmsg(MSG_INFO, "\nlink_cb::linkdata->resultstr = %s\n",
+ logmsg(MSG_INFO, "\nsdfw_link_cb::linkdata->resultstr = %s\n",
((result != NULL) ? result : "(null)"));
return (DI_WALK_CONTINUE);
}
static char *
-find_link(di_node_t bnode, char *acc_devname)
+sdfw_find_link(di_node_t bnode, char *acc_devname)
{
di_minor_t devminor = DI_MINOR_NIL;
di_devlink_handle_t hdl;
@@ -661,7 +908,7 @@ find_link(di_node_t bnode, char *acc_devname)
if (bnode == DI_NODE_NIL) {
logmsg(MSG_ERROR,
- gettext("find_link must be called with non-null "
+ gettext("sdfw_find_link must be called with non-null "
"di_node_t\n"));
return (NULL);
}
@@ -690,8 +937,8 @@ find_link(di_node_t bnode, char *acc_devname)
}
errno = 0;
- if (di_devlink_walk(hdl, linkname, acc_devname + strlen(devprefix),
- DI_PRIMARY_LINK, (void *)cbresult, link_cb) < 0) {
+ if (di_devlink_walk(hdl, linkname, acc_devname + strlen(sdfw_devprefix),
+ DI_PRIMARY_LINK, (void *)cbresult, sdfw_link_cb) < 0) {
logmsg(MSG_ERROR,
gettext("Unable to walk devlink snapshot for %s: %s\n"),
acc_devname, strerror(errno));
@@ -710,7 +957,7 @@ find_link(di_node_t bnode, char *acc_devname)
}
static int
-sd_idtfy_custmz(struct devicelist *device, char *sp)
+sdfw_idtfy_custmz(struct devicelist *device, char *sp)
{
/* vid customization */
if (strncmp(sp, "ST", 2) == 0) {
@@ -724,7 +971,7 @@ sd_idtfy_custmz(struct devicelist *device, char *sp)
return (FWFLASH_FAILURE);
}
} else {
- /* disks to do in the furture, fill 'ATA' first */
+ /* disks to do in the future, fill 'ATA' first */
if ((device->ident->vid = strdup("ATA")) == NULL) {
return (FWFLASH_FAILURE);
}
diff --git a/usr/src/cmd/fwflash/plugins/vendor/sd-GENERIC.c b/usr/src/cmd/fwflash/plugins/vendor/sd-GENERIC.c
index d66339226f..e99655d3e5 100644
--- a/usr/src/cmd/fwflash/plugins/vendor/sd-GENERIC.c
+++ b/usr/src/cmd/fwflash/plugins/vendor/sd-GENERIC.c
@@ -1,89 +1,150 @@
/*
- * CDDL HEADER START
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
*
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
*/
+
/*
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
*/
+/*
+ * This is a general firmware flash plugin that does basic verification for
+ * devices backed by sd(7D).
+ *
+ * The sd(7D) target for firmware flashing uses the general SCSI WRITE BUFFER
+ * options with various modes to instruct the drive to download and install
+ * microcode (what SPC-3 calls firmware). To verify that something fits, we can
+ * use the READ BUFFER command with mode 03h to indicate that we want to
+ * buffer's descriptor. This gives us both the buffer's total size and the
+ * required alignment for writes.
+ *
+ * Unfortunately, it's impossible to know for certain if that size is supposed
+ * to be equivalent to the microcode's. While a READ BUFFER is supposed to
+ * return the same data as with a WRITE BUFFER command, experimental evidence
+ * has shown that this isn't always the case. Especially as the firmware buffer
+ * usually leverages buffer zero, but has custom modes to access it.
+ */
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/sysmacros.h>
-#include <fcntl.h>
-#include <sys/condvar.h>
-#include <string.h>
-#include <strings.h>
-
-#include <sys/byteorder.h>
-
-#include <libintl.h> /* for gettext(3c) */
+#include <libintl.h>
#include <fwflash/fwflash.h>
+#include <scsi/libscsi.h>
-char vendor[] = "GENERIC \0";
-
-/* MAXIMGSIZE = 1.4 * 1024 * 1024 bytes */
-/* Currently the largest firmware image size is 1.4 MB */
-/* 1468006 = 1.4 * 1024 * 1024 */
-#define MAXIMGSIZE ((unsigned int)(1468006))
-
+/*
+ * The fwflash plugin interface is a bit odd for a modern committed interface
+ * and requires us to refer to data objects in the parent explicitly to get
+ * access to and set various information. It also doesn't allow us a means of
+ * setting data for our transport layer.
+ */
extern struct vrfyplugin *verifier;
-/* required functions for this plugin */
-int vendorvrfy(struct devicelist *devicenode);
-
/*
- * Important information about how this verification plugin works
- *
- * Direct-attached disks (sd instances) which support firmware
- * download accept image files up to 1.4 * 1024 * 1024 bytes in
- * size, and do their own verification of the image, rejecting the
- * file if it is not appropriate for them.
- *
- * All that we need to do here is set the various verifier fields
- * correctly, and check that the filesize as read from the filesystem
- * is less than 1.4 * 1024 * 1024 bytes.
+ * Declare the name of our vendor. This is required by the fwflash
+ * plugin interface. Note it must be a character array. Using a pointer may
+ * confuse the framework and its use of dlsym.
*/
+char vendor[] = "GENERIC";
int
-vendorvrfy(struct devicelist *devicenode)
+vendorvrfy(struct devicelist *dvp)
{
- if (verifier->imgsize > MAXIMGSIZE) {
- logmsg(MSG_ERROR,
- gettext("\nsd-GENERIC firmware image verifier: "
- "supplied filename %s exceeds maximum allowable "
- "size of %d bytes\n"),
- verifier->imgfile, MAXIMGSIZE);
+ libscsi_hdl_t *hdl = NULL;
+ libscsi_target_t *targ = NULL;
+ libscsi_action_t *act = NULL;
+ libscsi_errno_t serr;
+ spc3_read_buffer_cdb_t *rb_cdb;
+ uint8_t descbuf[4];
+ uint32_t size;
+
+ int ret = FWFLASH_FAILURE;
+
+ if ((hdl = libscsi_init(LIBSCSI_VERSION, &serr)) == NULL) {
+ logmsg(MSG_ERROR, gettext("%s: failed to initialize "
+ "libscsi: %s\n"),
+ verifier->vendor, libscsi_strerror(serr));
return (FWFLASH_FAILURE);
}
- logmsg(MSG_INFO,
- "sd-GENERIC verifier for device\n"
- "vid %s, pid %s, rev %s\npath %s\n",
- devicenode->ident->vid,
- devicenode->ident->pid,
- devicenode->ident->revid,
- devicenode->addresses[0]);
+ if ((targ = libscsi_open(hdl, NULL, dvp->access_devname)) ==
+ NULL) {
+ logmsg(MSG_ERROR,
+ gettext("%s: unable to open device %s\n"),
+ verifier->vendor, dvp->access_devname);
+ goto cleanup;
+ }
+
+ if ((act = libscsi_action_alloc(hdl, SPC3_CMD_READ_BUFFER,
+ LIBSCSI_AF_READ, descbuf, sizeof (descbuf))) == NULL) {
+ logmsg(MSG_ERROR, "%s: failed to alloc scsi action: %s\n",
+ verifier->vendor, libscsi_errmsg(hdl));
+ goto cleanup;
+ }
+
+ rb_cdb = (spc3_read_buffer_cdb_t *)libscsi_action_get_cdb(act);
+
+ rb_cdb->rbc_mode = SPC3_RB_MODE_DESCRIPTOR;
+
+ /*
+ * Microcode upgrade usually only uses the first buffer ID which are
+ * sequentially indexed from zero. Strictly speaking these are all
+ * vendor defined, but so far most vendors we've seen use index zero
+ * for this.
+ */
+ rb_cdb->rbc_bufferid = 0;
+
+ rb_cdb->rbc_allocation_len[0] = 0;
+ rb_cdb->rbc_allocation_len[1] = 0;
+ rb_cdb->rbc_allocation_len[2] = sizeof (descbuf);
+
+ if (libscsi_exec(act, targ) != 0) {
+ logmsg(MSG_ERROR, gettext("%s: failed to execute SCSI buffer "
+ "descriptor read: %s\n"), verifier->vendor,
+ libscsi_errmsg(hdl));
+ goto cleanup;
+ }
+
+ if (libscsi_action_get_status(act) != SAM4_STATUS_GOOD) {
+ logmsg(MSG_ERROR, gettext("%s: SCSI READ BUFFER command to "
+ "determine maximum image size failed\n"), verifier->vendor);
+ goto cleanup;
+ }
+
+ if (descbuf[0] == 0 && descbuf[1] == 0 && descbuf[2] == 0 &&
+ descbuf[3] == 0) {
+ logmsg(MSG_ERROR, gettext("%s: devices %s does not support "
+ "firmware upgrade\n"), verifier->vendor,
+ dvp->access_devname);
+ goto cleanup;
+ }
+
+ size = (descbuf[1] << 16) | (descbuf[2] << 8) | descbuf[3];
+ logmsg(MSG_INFO, gettext("%s: checking maximum image size %u against "
+ "actual image size: %u\n"), verifier->vendor, size,
+ verifier->imgsize);
+ if (size < verifier->imgsize) {
+ logmsg(MSG_ERROR, gettext("%s: supplied firmware image %s "
+ "exceeds maximum image size of %u\n"),
+ verifier->vendor, verifier->imgfile, size);
+ goto cleanup;
+ }
+
+ logmsg(MSG_INFO, gettext("%s: successfully validated images %s\n"),
+ verifier->vendor, verifier->imgfile);
+
verifier->flashbuf = 0;
+ ret = FWFLASH_SUCCESS;
+cleanup:
+ if (act != NULL)
+ libscsi_action_free(act);
+ if (targ != NULL)
+ libscsi_close(hdl, targ);
+ if (hdl != NULL)
+ libscsi_fini(hdl);
- return (FWFLASH_SUCCESS);
+ return (ret);
}