diff options
author | Robert Mustacchi <rm@joyent.com> | 2016-04-21 13:58:50 +0000 |
---|---|---|
committer | Robert Mustacchi <rm@joyent.com> | 2016-11-16 01:58:14 +0000 |
commit | 289e41bc2a2b4d5d99886bea0534b96ace98d833 (patch) | |
tree | adaa78e42f9645518d6754fd3cf79ab9aaeda0d8 | |
parent | 31047669e3f3d1dcd6f9421034e92b6490cd793c (diff) | |
download | illumos-joyent-289e41bc2a2b4d5d99886bea0534b96ace98d833.tar.gz |
OS-5679 fwflash for sd needs to handle partial writes
OS-5680 fwflash sd plugin shouldn't hardcode maximum image size
OS-5766 libscsi improperly translates request sense and isolate options to uscsi
Reviewed by: Joshua M. Clulow <jmc@joyent.com>
Approved by: Patrick Mooney <patrick.mooney@joyent.com>
-rw-r--r-- | usr/src/cmd/fwflash/Makefile.com | 2 | ||||
-rw-r--r-- | usr/src/cmd/fwflash/Makefile.targ | 6 | ||||
-rw-r--r-- | usr/src/cmd/fwflash/plugins/Makefile.targ | 3 | ||||
-rw-r--r-- | usr/src/cmd/fwflash/plugins/transport/common/sd.c | 413 | ||||
-rw-r--r-- | usr/src/cmd/fwflash/plugins/vendor/sd-GENERIC.c | 195 | ||||
-rw-r--r-- | usr/src/lib/scsi/libscsi/common/libscsi.h | 3 | ||||
-rw-r--r-- | usr/src/lib/scsi/libscsi/common/scsi_engine.c | 14 | ||||
-rw-r--r-- | usr/src/lib/scsi/libscsi/mapfile-vers | 1 | ||||
-rw-r--r-- | usr/src/lib/scsi/plugins/scsi/engines/uscsi/uscsi.c | 42 | ||||
-rw-r--r-- | usr/src/man/man7i/uscsi.7i | 40 | ||||
-rw-r--r-- | usr/src/uts/common/io/scsi/targets/sd.c | 27 | ||||
-rw-r--r-- | usr/src/uts/common/sys/scsi/impl/spc3_types.h | 25 | ||||
-rw-r--r-- | usr/src/uts/common/sys/scsi/impl/uscsi.h | 10 |
13 files changed, 609 insertions, 172 deletions
diff --git a/usr/src/cmd/fwflash/Makefile.com b/usr/src/cmd/fwflash/Makefile.com index 5f0873b244..59e2bcdb36 100644 --- a/usr/src/cmd/fwflash/Makefile.com +++ b/usr/src/cmd/fwflash/Makefile.com @@ -26,8 +26,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..6dcff9d0f1 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 %u\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 %u 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 %llu (%s) additional sense code " + "0x%llx/0x%llx (%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 %u, required " + "alignment is 2^%d\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 " + "%u 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 %u\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); } diff --git a/usr/src/lib/scsi/libscsi/common/libscsi.h b/usr/src/lib/scsi/libscsi/common/libscsi.h index 4d57d1299c..dea6972332 100644 --- a/usr/src/lib/scsi/libscsi/common/libscsi.h +++ b/usr/src/lib/scsi/libscsi/common/libscsi.h @@ -21,6 +21,7 @@ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2016 Joyent, Inc. */ #ifndef _LIBSCSI_H @@ -97,6 +98,7 @@ typedef struct libscsi_engine_ops { void (*lseo_close)(libscsi_hdl_t *, void *); int (*lseo_exec)(libscsi_hdl_t *, void *, libscsi_action_t *); void (*lseo_target_name)(libscsi_hdl_t *, void *, char *, size_t); + int (*lseo_max_transfer)(libscsi_hdl_t *, void *, size_t *); } libscsi_engine_ops_t; typedef struct libscsi_engine { @@ -116,6 +118,7 @@ extern libscsi_hdl_t *libscsi_get_handle(libscsi_target_t *); extern const char *libscsi_vendor(libscsi_target_t *); extern const char *libscsi_product(libscsi_target_t *); extern const char *libscsi_revision(libscsi_target_t *); +extern int libscsi_max_transfer(libscsi_target_t *, size_t *); extern libscsi_errno_t libscsi_errno(libscsi_hdl_t *); extern const char *libscsi_errmsg(libscsi_hdl_t *); diff --git a/usr/src/lib/scsi/libscsi/common/scsi_engine.c b/usr/src/lib/scsi/libscsi/common/scsi_engine.c index 3c6d5ecee9..e0e4f0a5f3 100644 --- a/usr/src/lib/scsi/libscsi/common/scsi_engine.c +++ b/usr/src/lib/scsi/libscsi/common/scsi_engine.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2016 Joyent, Inc. */ #include <sys/types.h> @@ -616,3 +617,16 @@ libscsi_exec(libscsi_action_t *ap, libscsi_target_t *tp) return (ret); } + +int +libscsi_max_transfer(libscsi_target_t *tp, size_t *sizep) +{ + libscsi_hdl_t *hp = tp->lst_hdl; + if (tp->lst_engine->lse_ops->lseo_max_transfer == NULL) { + return (libscsi_error(hp, ESCSI_NOTSUP, "max transfer " + "request not supported by engine")); + } + + return (tp->lst_engine->lse_ops->lseo_max_transfer(hp, tp->lst_priv, + sizep)); +} diff --git a/usr/src/lib/scsi/libscsi/mapfile-vers b/usr/src/lib/scsi/libscsi/mapfile-vers index 7e0d8e251c..067756f44b 100644 --- a/usr/src/lib/scsi/libscsi/mapfile-vers +++ b/usr/src/lib/scsi/libscsi/mapfile-vers @@ -71,6 +71,7 @@ SYMBOL_VERSION SUNWprivate_1.1 { libscsi_vendor; libscsi_product; libscsi_revision; + libscsi_max_transfer; libscsi_get_handle; libscsi_alloc; diff --git a/usr/src/lib/scsi/plugins/scsi/engines/uscsi/uscsi.c b/usr/src/lib/scsi/plugins/scsi/engines/uscsi/uscsi.c index 06cdb0b339..6952a032ad 100644 --- a/usr/src/lib/scsi/plugins/scsi/engines/uscsi/uscsi.c +++ b/usr/src/lib/scsi/plugins/scsi/engines/uscsi/uscsi.c @@ -22,10 +22,10 @@ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2016 Joyent, Inc. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/types.h> #include <sys/scsi/impl/uscsi.h> #include <sys/scsi/generic/commands.h> @@ -111,10 +111,10 @@ xlate_flags(libscsi_hdl_t *hp, uint_t flags, int *uf) f |= USCSI_DIAGNOSE; break; case LIBSCSI_AF_ISOLATE: - f = USCSI_ISOLATE; + f |= USCSI_ISOLATE; break; case LIBSCSI_AF_RQSENSE: - f = USCSI_RQENABLE; + f |= USCSI_RQENABLE; break; default: return (libscsi_error(hp, ESCSI_BOGUSFLAGS, @@ -214,11 +214,43 @@ uscsi_target_name(libscsi_hdl_t *hp, void *private, char *buf, size_t len) (void) snprintf(buf, len, "%s", dp->dev); } +static int +uscsi_max_transfer(libscsi_hdl_t *hp, void *private, size_t *sizep) +{ + uscsi_xfer_t xfer; + struct uscsi_dev *dp = (struct uscsi_dev *)private; + + if (ioctl(dp->fd, USCSIMAXXFER, &xfer) < 0) { + ASSERT(errno != EFAULT); + switch (errno) { + case EINVAL: + return (libscsi_error(hp, ESCSI_BADCMD, "internal " + "uscsi error")); + case EPERM: + return (libscsi_error(hp, ESCSI_PERM, "insufficient " + "privileges ")); + case ENOTTY: + return (libscsi_error(hp, ESCSI_NOTSUP, "max transfer " + "request not supported on device")); + default: + return (libscsi_error(hp, ESCSI_SYS, "uscsi ioctl " + "failed: %s", strerror(errno))); + } + } + + if (xfer > SIZE_MAX) + xfer = SIZE_MAX; + + *sizep = (size_t)xfer; + return (0); +} + static const libscsi_engine_ops_t uscsi_ops = { .lseo_open = uscsi_open, .lseo_close = uscsi_close, .lseo_exec = uscsi_exec, - .lseo_target_name = uscsi_target_name + .lseo_target_name = uscsi_target_name, + .lseo_max_transfer = uscsi_max_transfer }; static const libscsi_engine_t uscsi_engine = { diff --git a/usr/src/man/man7i/uscsi.7i b/usr/src/man/man7i/uscsi.7i index 3247f53cc1..f246590ca4 100644 --- a/usr/src/man/man7i/uscsi.7i +++ b/usr/src/man/man7i/uscsi.7i @@ -1,9 +1,10 @@ '\" te .\" Copyright (c) 2007 by Sun Microsystems, Inc. All rights reserved. +.\" Copyright 2016 Joyent, Inc. .\" 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] -.TH USCSI 7I "May 29, 2007" +.TH USCSI 7I "Sep 23, 2016" .SH NAME uscsi \- user SCSI command interface .SH SYNOPSIS @@ -15,7 +16,6 @@ uscsi \- user SCSI command interface .fi .SH DESCRIPTION -.sp .LP The \fBuscsi\fR command is very powerful and somewhat dangerous; therefore it has some permission restrictions. See \fBWARNINGS\fR for more details. @@ -349,8 +349,13 @@ See the \fBscsi_pkt\fR(9S) flag \fBFLAG_RENEGOTIATE_WIDE_SYNC\fR for more information. .RE +The \fBuscsi_xfer_t\fR is a type definition that corresponds to a 64-bit +unsigned integer. It should be used for the \fBUSCSIMAXXFER\fR ioctls. This is +used for determining the maximum transfer size that can be performed in a single +\fBUSCSICMD\fR ioctl. If the SCSI request is larger than the specified size, +then it may not work, depending on the hardware platform. + .SH IOCTLS -.sp .LP The \fBioctl\fR supported by drivers providing the \fBuscsi\fR interface is: .sp @@ -371,10 +376,24 @@ status, and Request Sense is enabled, the sense data itself is returned in Sense data transfer. .RE -.SH ERRORS .sp .ne 2 .na +.B USCSIMAXXFER +.ad +.RS 12n +The argument is a pointer to a \fBuscsi_xfer_t\fR value. The maximum transfer +size that can be used with the \fBUSCSICMD\fR ioctl for the current device will +be returned in the \fBuscsi_xfer_t\fR. +.sp +.LP +Not all devices which support the \fBUSCSICMD\fR ioctl also support the +\fBUSCSIMAXXFER\fR ioctl. +.RE + +.SH ERRORS +.ne 2 +.na \fB\fBEINVAL\fR\fR .ad .RS 10n @@ -396,7 +415,8 @@ An error occurred during the execution of the command. \fB\fBEPERM\fR\fR .ad .RS 10n -A process without root credentials tried to execute the \fBUSCSICMD\fR ioctl. +A process without root credentials tried to execute the \fBUSCSICMD\fR or +\fRUSCSIMAXXFER\fR ioctl. .RE .sp @@ -405,12 +425,11 @@ A process without root credentials tried to execute the \fBUSCSICMD\fR ioctl. \fB\fBEFAULT\fR\fR .ad .RS 10n -The \fBuscsi_cmd\fR itself, the \fBuscsi_cdb\fR, the \fBuscsi_buf\fR, or the -\fBuscsi_rqbuf\fR point to an invalid address. +The \fBuscsi_cmd\fR itself, the \fBuscsi_cdb\fR, the \fBuscsi_buf\fR, the +\fBuscsi_rqbuf\fR, or the \fBuscsi_xfer_t\fR point to an invalid address. .RE .SH ATTRIBUTES -.sp .LP See \fBattributes\fR(5) for descriptions of the following attributes: .sp @@ -426,14 +445,12 @@ Interface Stability Committed .TE .SH SEE ALSO -.sp .LP \fBioctl\fR(2), \fBattributes\fR(5), \fBsd\fR(7D), \fBst\fR(7D) .sp .LP \fIANSI Small Computer System Interface-2 (SCSI-2)\fR .SH WARNINGS -.sp .LP The \fBuscsi\fR command is very powerful, but somewhat dangerous, and so its use is restricted to processes running as root, regardless of the file @@ -450,7 +467,8 @@ number is used to send the command. The \fBuscsi\fR interface is not recommended for very large data transfers (typically more than 16MB). If the requested transfer size exceeds the maximum transfer size of the DMA engine, it will not be broken up into multiple -transfers and DMA errors may result. +transfers and DMA errors may result. The \fBUSCSIMAXXFER\fR ioctl can be used +to determine the maximum transfer size. .sp .LP The \fBUSCSICMD\fR ioctl associates a \fBstruct uscsi_cmd\fR with a device by diff --git a/usr/src/uts/common/io/scsi/targets/sd.c b/usr/src/uts/common/io/scsi/targets/sd.c index dc5dc22e37..0e97d9125b 100644 --- a/usr/src/uts/common/io/scsi/targets/sd.c +++ b/usr/src/uts/common/io/scsi/targets/sd.c @@ -27,6 +27,7 @@ * Copyright (c) 2012 by Delphix. All rights reserved. * Copyright 2014 Nexenta Systems, Inc. All rights reserved. * Copyright 2012 DEY Storage Systems, Inc. All rights reserved. + * Copyright 2016 Joyent, Inc. */ /* * Copyright 2011 cyril.galibern@opensvc.com @@ -14175,15 +14176,21 @@ sd_initpkt_for_uscsi(struct buf *bp, struct scsi_pkt **pktpp) /* * Allocate the scsi_pkt for the command. + * * Note: If PKT_DMA_PARTIAL flag is set, scsi_vhci binds a path * during scsi_init_pkt time and will continue to use the * same path as long as the same scsi_pkt is used without - * intervening scsi_dma_free(). Since uscsi command does + * intervening scsi_dmafree(). Since uscsi command does * not call scsi_dmafree() before retry failed command, it * is necessary to make sure PKT_DMA_PARTIAL flag is NOT * set such that scsi_vhci can use other available path for * retry. Besides, ucsci command does not allow DMA breakup, * so there is no need to set PKT_DMA_PARTIAL flag. + * + * More fundamentally, we can't support breaking up this DMA into + * multiple windows on x86. There is, in general, no guarantee + * that arbitrary SCSI commands are idempotent, which is required + * if we want to use multiple windows for a given command. */ if (uscmd->uscsi_rqlen > SENSE_LENGTH) { pktp = scsi_init_pkt(SD_ADDRESS(un), NULL, @@ -22408,6 +22415,7 @@ sdioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cred_p, int *rval_p) case MHIOCGRP_REGISTERANDIGNOREKEY: case CDROMCLOSETRAY: case USCSICMD: + case USCSIMAXXFER: goto skip_ready_valid; default: break; @@ -22844,6 +22852,23 @@ skip_ready_valid: } break; + case USCSIMAXXFER: + SD_TRACE(SD_LOG_IOCTL, un, "USCSIMAXXFER\n"); + cr = ddi_get_cred(); + if ((drv_priv(cred_p) != 0) && (drv_priv(cr) != 0)) { + err = EPERM; + } else { + const uscsi_xfer_t xfer = un->un_max_xfer_size; + + if (ddi_copyout(&xfer, (void *)arg, sizeof (xfer), + flag) != 0) { + err = EFAULT; + } else { + err = 0; + } + } + break; + case CDROMPAUSE: case CDROMRESUME: SD_TRACE(SD_LOG_IOCTL, un, "PAUSE-RESUME\n"); diff --git a/usr/src/uts/common/sys/scsi/impl/spc3_types.h b/usr/src/uts/common/sys/scsi/impl/spc3_types.h index 456a0e336b..4b687eb9c6 100644 --- a/usr/src/uts/common/sys/scsi/impl/spc3_types.h +++ b/usr/src/uts/common/sys/scsi/impl/spc3_types.h @@ -21,6 +21,7 @@ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2016 Joyent, Inc. */ #ifndef _SPC3_TYPES_H @@ -812,6 +813,30 @@ typedef struct spc3_persistent_reserve_in_cdb { } spc3_persistent_reserve_in_cdb_t; /* + * SPC-3 6.15 READ BUFFER + */ +typedef struct spc3_read_buffer_cdb { + uint8_t rbc_opcode; + DECL_BITFIELD2( + rbc_mode :5, + _reserved :3); + uint8_t rbc_bufferid; + uint8_t rbc_buffer_offset[3]; + uint8_t rbc_allocation_len[3]; + spc3_control_t rbc_control; +} spc3_read_buffer_cdb_t; + +typedef enum spc3_read_buffer_mode { + SPC3_RB_MODE_COMB_HDR_DATA = 0x00, + SPC3_RB_MODE_VENDOR_SPECIFIC = 0x01, + SPC3_RB_MODE_DATA = 0x02, + SPC3_RB_MODE_DESCRIPTOR = 0x03, + SPC3_RB_MODE_ECHO_BUF = 0x0a, + SPC3_RB_MODE_ECHO_BUF_DESC = 0x0b, + SPC3_RB_MODE_ENABLE_EXPANDER_ECHO_BUF = 0x1a +} spc3_read_buffer_mode_t; + +/* * SPC-3 6.16 READ MEDIA SERIAL NUMBER */ typedef struct spc3_read_media_serial_number_cdb { diff --git a/usr/src/uts/common/sys/scsi/impl/uscsi.h b/usr/src/uts/common/sys/scsi/impl/uscsi.h index afce8ec081..f416b1bb5a 100644 --- a/usr/src/uts/common/sys/scsi/impl/uscsi.h +++ b/usr/src/uts/common/sys/scsi/impl/uscsi.h @@ -21,10 +21,12 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2016 Joyent, Inc. */ /* - * Defines for user SCSI commands * + * Defines for user SCSI commands */ #ifndef _SYS_SCSI_IMPL_USCSI_H @@ -186,10 +188,16 @@ struct uscsi_rqs32 { #define RQS_VALID 0x02 /* RQS data is valid */ /* + * Structure for USCSIMAXXFER ioctls + */ +typedef uint64_t uscsi_xfer_t; + +/* * User SCSI io control command */ #define USCSIIOC (0x04 << 8) #define USCSICMD (USCSIIOC|201) /* user scsi command */ +#define USCSIMAXXFER (USCSIIOC|202) /* get max transfer size */ #ifdef _KERNEL |