summaryrefslogtreecommitdiff
path: root/usr/src/cmd/fwflash
diff options
context:
space:
mode:
authorjmcp <none@none>2008-04-26 16:02:09 -0700
committerjmcp <none@none>2008-04-26 16:02:09 -0700
commit5a7763bf3e9db4cfe6cb523b096cb74af71e3793 (patch)
tree588f9b2047f5b952a44eef7d0f86361d0bdd56cd /usr/src/cmd/fwflash
parent47fc6f3c5b3c55e162497f2704e8a30366037e6d (diff)
downloadillumos-joyent-5a7763bf3e9db4cfe6cb523b096cb74af71e3793.tar.gz
PSARC 2008/151 pluggable fwflash(1M)
6616085 fwflash should be generic and pluggable
Diffstat (limited to 'usr/src/cmd/fwflash')
-rw-r--r--usr/src/cmd/fwflash/Makefile54
-rw-r--r--usr/src/cmd/fwflash/Makefile.com91
-rw-r--r--usr/src/cmd/fwflash/common/Makefile41
-rw-r--r--usr/src/cmd/fwflash/common/fwflash.c1291
-rw-r--r--usr/src/cmd/fwflash/common/fwflash.h398
-rw-r--r--usr/src/cmd/fwflash/i386/Makefile91
-rw-r--r--usr/src/cmd/fwflash/plugins/Makefile69
-rw-r--r--usr/src/cmd/fwflash/plugins/transport/Makefile71
-rw-r--r--usr/src/cmd/fwflash/plugins/transport/common/ses.c851
-rw-r--r--usr/src/cmd/fwflash/plugins/transport/i386/Makefile92
-rw-r--r--usr/src/cmd/fwflash/plugins/transport/sparc/Makefile92
-rw-r--r--usr/src/cmd/fwflash/sparc/Makefile91
12 files changed, 3232 insertions, 0 deletions
diff --git a/usr/src/cmd/fwflash/Makefile b/usr/src/cmd/fwflash/Makefile
new file mode 100644
index 0000000000..bf3e3e73e2
--- /dev/null
+++ b/usr/src/cmd/fwflash/Makefile
@@ -0,0 +1,54 @@
+#
+# CDDL HEADER START
+#
+# 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
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/fwflash/Makefile
+#
+
+
+PROG= fwflash
+SUBDIRS= $(MACH) plugins
+
+all := TARGET= all
+clean := TARGET= clean
+clobber := TARGET= clobber
+install := TARGET= install
+install_h := TARGET= install_h
+lint := TARGET= lint
+_msg := TARGET= _msg
+msg := TARGET= msg
+
+
+all clean clobber install lint msg _msg: $(SUBDIRS)
+
+
+$(SUBDIRS): FRC
+ @if [ -f $@/Makefile ]; then \
+ cd $@; pwd; $(MAKE) $(TARGET); \
+ else \
+ true; \
+ fi
+
+# empty rule
+FRC:
diff --git a/usr/src/cmd/fwflash/Makefile.com b/usr/src/cmd/fwflash/Makefile.com
new file mode 100644
index 0000000000..9b9a066e9e
--- /dev/null
+++ b/usr/src/cmd/fwflash/Makefile.com
@@ -0,0 +1,91 @@
+#
+# CDDL HEADER START
+#
+# 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
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/fwflash/Makefile.com
+#
+#
+
+# common rules for $SRC/cmd/fwflash
+
+CLOSED= $(SRC)/../closed
+
+ROOTLIB= $(ROOT)/usr/lib
+ROOTLIBFWFLASH= $(ROOTLIB)/fwflash
+ROOTLIBFWFLASHPLUGINS= $(ROOTLIBFWFLASH)/identify
+ROOTLIBFWFLASHVERIFY= $(ROOTLIBFWFLASH)/verify
+ROOTUSR= $(ROOT)/usr
+ROOTUSRINCLUDE= $(ROOTUSR)/include
+ROOTUSRINCLUDEFWFLASH= $(ROOTUSRINCLUDE)/fwflash
+ROOTUSRSBIN= $(ROOT)/usr/sbin
+
+
+
+$(ROOTLIB):
+ $(INS.dir)
+
+$(ROOTLIBFWFLASH): $(ROOTLIB)
+ $(INS.dir)
+
+$(ROOTLIBFWFLASH)/%: $(ROOTLIB) %
+ $(INS.dir)
+
+$(ROOTLIBFWFLASHPLUGINS): $(ROOTLIBFWFLASH)
+ $(INS.dir)
+
+$(ROOTLIBFWFLASHPLUGINS)/%: $(ROOTLIBFWFLASHPLUGINS) %
+ $(INS.file)
+
+$(ROOTLIBFWFLASHVERIFY): $(ROOTLIBFWFLASH)
+ $(INS.dir)
+
+$(ROOTLIBFWFLASHVERIFY)/%: $(ROOTLIBFWFLASHVERIFY) %
+ $(INS.file)
+
+$(ROOTUSR):
+ $(INS.dir)
+
+$(ROOTUSRINCLUDE): $(ROOTUSR)
+ $(INS.dir)
+
+$(ROOTUSRINCLUDEFWFLASH):
+ $(INS.dir)
+
+$(ROOTUSRINCLUDEFWFLASH)/%: $(ROOTUSRINCLUDEFWFLASH) %
+ $(INS.file)
+
+$(ROOTUSRSBIN): $(ROOTUSR)
+ $(INS.dir)
+
+$(ROOTUSRSBIN)/%: %
+ $(INS.file)
+
+BUILD.SO= $(CC) -o $@ $(GSHARED) $(DYNFLAGS) $(PICS) $(LDLIBS)
+POST_PROCESS_O += ; $(CTFCONVERT_POST)
+POST_PROCESS_SO += ; $(CTFMERGE_POST)
+
+LINTFLAGS += -erroff=E_CONSTANT_CONDITION -D_POSIX_PTHREAD_SEMANTICS \
+ -erroff=E_CONST_TRUNCATED_BY_ASSIGN \
+ -erroff=E_SUPPRESSION_DIRECTIVE_UNUSED \
+ -erroff=E_BAD_PTR_CAST_ALIGN
diff --git a/usr/src/cmd/fwflash/common/Makefile b/usr/src/cmd/fwflash/common/Makefile
new file mode 100644
index 0000000000..39032700fa
--- /dev/null
+++ b/usr/src/cmd/fwflash/common/Makefile
@@ -0,0 +1,41 @@
+#
+# CDDL HEADER START
+#
+# 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
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/fwflash/common/Makefile
+#
+#
+
+HDRS= fwflash.h
+
+all: install_h
+
+include $(SRC)/Makefile.master
+include ../Makefile.com
+
+$(ROOTUSRINCLUDEFWFLASH)/%.h := FILEMODE= 0644
+
+install_h: $(ROOTUSRINCLUDEFWFLASH) \
+ $(ROOTUSRINCLUDEFWFLASH)/$(HDRS)
+
diff --git a/usr/src/cmd/fwflash/common/fwflash.c b/usr/src/cmd/fwflash/common/fwflash.c
new file mode 100644
index 0000000000..2b5bd75d0a
--- /dev/null
+++ b/usr/src/cmd/fwflash/common/fwflash.c
@@ -0,0 +1,1291 @@
+/*
+ * CDDL HEADER START
+ *
+ * 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
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * fwflash.c
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <strings.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/queue.h>
+#include <signal.h>
+#include <locale.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <dlfcn.h>
+#include <dirent.h>
+#include <link.h>
+#include <sys/varargs.h>
+#include <libintl.h> /* for gettext(3c) */
+#include <libdevinfo.h>
+#include <note.h>
+
+#include <fwflash/fwflash.h>
+
+
+#if !defined(lint)
+/* embedded software license agreement */
+static char *sla [] = { "Copyright 2007 Sun Microsystems, Inc., 4150 Network "
+"Circle, Santa Clara, California 95054, U.S.A. All rights reserved. U.S. "
+"Government Rights - Commercial software. Government users are subject to the "
+"Sun Microsystems, Inc. standard license agreement and applicable provisions "
+"of the FAR and its supplements. Use is subject to license terms. Parts of "
+"the product may be derived from Berkeley BSD systems, licensed from the "
+"University of California. UNIX is a registered trademark in the U.S. and in "
+"other countries, exclusively licensed through X/Open Company, Ltd.Sun, Sun "
+"Microsystems, the Sun logo and Solaris are trademarks or registered "
+"trademarks of Sun Microsystems, Inc. in the U.S. and other countries. This "
+"product is covered and controlled by U.S. Export Control laws and may be "
+"subject to the export or import laws in other countries. Nuclear, missile, "
+"chemical biological weapons or nuclear maritime end uses or end users, "
+"whether direct or indirect, are strictly prohibited. Export or reexport "
+"to countries subject to U.S. embargo or to entities identified on U.S. export "
+"exclusion lists, including, but not limited to, the denied persons and "
+"specially designated nationals lists is strictly prohibited." };
+#endif /* lint */
+
+/* global arg list */
+int fwflash_arg_list = 0;
+char *filelist[10];
+
+
+/* are we writing to flash? */
+static int fwflash_in_write = 0;
+
+/*
+ * If we *must* track the version string for fwflash, then
+ * we should do so in this common file rather than the header
+ * file since it will then be in sync with what the customer
+ * sees
+ */
+
+
+#define FWFLASH_VERSION "%I%"
+#define FWFLASH_PROG_NAME "fwflash"
+
+
+
+static int get_fileopts(char *options);
+static int flash_device_list();
+static int flash_load_plugins();
+static int fwflash_update(char *device, char *filename, int flags);
+static int fwflash_read_file(char *device, char *filename);
+static int fwflash_list_fw(char *class);
+static int fwflash_load_verifier(char *drv, char *vendorid, char *fwimg);
+static void fwflash_intr(int sig);
+static void fwflash_handle_signals(void);
+static void fwflash_usage();
+static void fwflash_help(void);
+static void fwflash_version(void);
+static int confirm_target(struct devicelist *thisdev, char *file);
+
+
+
+
+/*
+ * FWFlash main code
+ */
+int
+main(int argc, char **argv) {
+ int rv = FWFLASH_SUCCESS;
+ int i;
+ char ch;
+ char *read_file;
+ extern char *optarg;
+ char *devclass = NULL;
+ char *devpath = NULL;
+
+
+
+ /* local variables from env */
+ (void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it isn't. */
+#endif
+
+ (void) textdomain(TEXT_DOMAIN);
+
+
+ read_file = NULL;
+
+ if (argc < 2) {
+ /* no args supplied */
+ fwflash_usage(NULL);
+ return (FWFLASH_FAILURE);
+ }
+
+
+ while ((ch = getopt(argc, argv, "hvylc:f:r:Qd:")) != EOF) {
+ switch (ch) {
+ case 'h':
+ fwflash_arg_list |= FWFLASH_HELP_FLAG;
+ break;
+
+ case 'v':
+ fwflash_arg_list |= FWFLASH_VER_FLAG;
+ break;
+
+ case 'y':
+ fwflash_arg_list |= FWFLASH_YES_FLAG;
+ break;
+
+ case 'l':
+ fwflash_arg_list |= FWFLASH_LIST_FLAG;
+ break;
+
+ case 'c':
+ fwflash_arg_list |= FWFLASH_CLASS_FLAG;
+ /* we validate later */
+ devclass = strdup(optarg);
+ break;
+
+ case 'd':
+ fwflash_arg_list |= FWFLASH_DEVICE_FLAG;
+ devpath = strdup(optarg);
+ break;
+
+ case 'f':
+ fwflash_arg_list |= FWFLASH_FW_FLAG;
+ if ((rv = get_fileopts(optarg)) != FWFLASH_SUCCESS) {
+ fwflash_help();
+ return (FWFLASH_FAILURE);
+ }
+ break;
+
+ case 'r':
+ fwflash_arg_list |= FWFLASH_READ_FLAG;
+ read_file = strdup(optarg);
+ break;
+
+ case 'Q':
+ /* NOT in the manpage */
+ fwflash_debug = 1;
+ break;
+
+ /* illegal options */
+ default:
+ fwflash_usage(optarg);
+ return (FWFLASH_FAILURE);
+ }
+ }
+
+ /* Do Help */
+ if ((fwflash_arg_list & FWFLASH_HELP_FLAG) ||
+ ((fwflash_arg_list & FWFLASH_DEVICE_FLAG) &&
+ !((fwflash_arg_list & FWFLASH_FW_FLAG) ||
+ (fwflash_arg_list & FWFLASH_READ_FLAG)))) {
+ fwflash_help();
+ return (FWFLASH_SUCCESS);
+ }
+
+ /* Do Version */
+ if (fwflash_arg_list == FWFLASH_VER_FLAG) {
+ fwflash_version();
+ return (FWFLASH_SUCCESS);
+ }
+
+ /* generate global list of devices */
+ if ((rv = flash_load_plugins()) != FWFLASH_SUCCESS) {
+ logmsg(MSG_ERROR,
+ gettext("Unable to load fwflash plugins\n"));
+ fwflash_intr(0);
+ return (rv);
+ }
+
+ if ((rv = flash_device_list()) != FWFLASH_SUCCESS) {
+ logmsg(MSG_ERROR,
+ gettext("No flashable devices in this system\n"));
+ fwflash_intr(0);
+ return (rv);
+ }
+
+ /* Do list */
+ if (fwflash_arg_list == (FWFLASH_LIST_FLAG) ||
+ fwflash_arg_list == (FWFLASH_LIST_FLAG | FWFLASH_CLASS_FLAG)) {
+ rv = fwflash_list_fw(devclass);
+ fwflash_intr(0);
+ return (rv);
+ }
+
+ fwflash_handle_signals();
+
+ /* Do flash update (write) */
+ if ((fwflash_arg_list == (FWFLASH_FW_FLAG | FWFLASH_DEVICE_FLAG)) ||
+ (fwflash_arg_list == (FWFLASH_FW_FLAG | FWFLASH_DEVICE_FLAG |
+ FWFLASH_YES_FLAG))) {
+ /* the update function handles the real arg parsing */
+ i = 0;
+ while (filelist[i] != NULL) {
+ if ((rv = fwflash_update(devpath, filelist[i],
+ fwflash_arg_list)) == FWFLASH_SUCCESS) {
+ /* failed ops have already been noted */
+ logmsg(MSG_ERROR,
+ gettext("New firmware will be activated "
+ "after you reboot\n\n"));
+ }
+ ++i;
+ }
+
+ fwflash_intr(0);
+ return (rv);
+ }
+
+ /* Do flash read */
+ if ((fwflash_arg_list == (FWFLASH_READ_FLAG | FWFLASH_DEVICE_FLAG)) ||
+ (fwflash_arg_list == (FWFLASH_READ_FLAG | FWFLASH_DEVICE_FLAG |
+ FWFLASH_YES_FLAG))) {
+ rv = fwflash_read_file(devpath, read_file);
+ fwflash_intr(0);
+ return (rv);
+ }
+
+ fwflash_usage(NULL);
+
+ return (FWFLASH_FAILURE);
+}
+
+
+
+
+
+static int
+flash_load_plugins() {
+
+ int rval = FWFLASH_SUCCESS;
+ DIR *dirp;
+ struct dirent *plugdir;
+ char *plugname;
+ struct fw_plugin *tmpplug;
+ struct pluginlist *tmpelem;
+ void *sym;
+ char *fwplugdirpath, *tempdirpath;
+
+
+#define CLOSEFREE() { \
+ (void) dlclose(tmpplug->handle); \
+ free(tmpplug); }
+
+
+ /*
+ * Procedure:
+ *
+ * cd /usr/lib/fwflash/identify
+ * open each .so file found therein
+ * dlopen(.sofile)
+ * if it's one of our plugins, add it to fw_pluginlist;
+ *
+ * functions we need here include dlopen and dlsym.
+ *
+ * If we get to the end and fw_pluginlist struct is empty,
+ * return FWFLASH_FAILURE so we return to the shell.
+ */
+
+ if ((fwplugdirpath = calloc(1, MAXPATHLEN + 1)) == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("Unable to malloc %d bytes while "
+ "trying to load plugins: %s\n"),
+ MAXPATHLEN + 1, strerror(errno));
+ return (FWFLASH_FAILURE);
+ }
+
+ tempdirpath = getenv("FWPLUGINDIR");
+
+ if ((fwflash_debug > 0) && (tempdirpath != NULL)) {
+ (void) strlcpy(fwplugdirpath, tempdirpath,
+ strlen(tempdirpath) + 1);
+ } else {
+ (void) strlcpy(fwplugdirpath, FWPLUGINDIR,
+ strlen(FWPLUGINDIR) + 1);
+ }
+
+ if ((dirp = opendir(fwplugdirpath)) == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("Unable to open %s!\n"),
+ fwplugdirpath);
+ return (errno);
+ }
+
+ if ((plugdir = calloc(1, sizeof (struct dirent) + MAXPATHLEN + 1))
+ == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("Unable to malloc %d bytes while "
+ "trying to load plugins: %s\n"),
+ MAXPATHLEN + 1 + sizeof (struct dirent),
+ strerror(errno));
+ return (FWFLASH_FAILURE);
+ }
+
+ if ((fw_pluginlist = calloc(1, sizeof (struct fw_plugin)))
+ == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("Unable to malloc %d bytes while "
+ "trying to load plugins: %s\n"),
+ sizeof (struct fw_plugin) + 1, strerror(errno));
+ return (FWFLASH_FAILURE);
+ }
+
+ NOTE(CONSTCOND)
+ TAILQ_INIT(fw_pluginlist);
+
+ while ((readdir_r(dirp, plugdir, &plugdir) == 0) && (plugdir != NULL)) {
+
+ errno = 0; /* remove chance of false results */
+
+ if ((plugdir->d_name[0] == '.') ||
+ (strstr(plugdir->d_name, ".so") == NULL)) {
+ continue;
+ }
+
+ if ((plugname = calloc(1, MAXPATHLEN + 1)) == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("Unable to malloc %d bytes while "
+ "trying to load plugins: %s\n"),
+ MAXPATHLEN + 1, strerror(errno));
+ return (FWFLASH_FAILURE);
+ }
+
+ (void) snprintf(plugname, MAXPATHLEN, "%s/%s",
+ fwplugdirpath, plugdir->d_name);
+
+ /* start allocating storage */
+ if ((tmpelem = calloc(1, sizeof (struct pluginlist)))
+ == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("Unable to malloc %d bytes while "
+ "trying to load plugins: %s\n"),
+ sizeof (struct pluginlist), strerror(errno));
+ return (FWFLASH_FAILURE);
+ }
+
+ if ((tmpplug = calloc(1, sizeof (struct fw_plugin)))
+ == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("Unable to malloc %d bytes while "
+ "trying to load plugins: %s\n"),
+ sizeof (struct fw_plugin), strerror(errno));
+ return (FWFLASH_FAILURE);
+ }
+
+ /* load 'er up! */
+ tmpplug->handle = dlopen(plugname, RTLD_NOW);
+ if (tmpplug->handle == NULL) {
+ free(tmpplug);
+ continue; /* assume there are other plugins */
+ }
+
+ if ((tmpplug->filename = calloc(1, strlen(plugname) + 1))
+ == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("Unable to allocate %d bytes for plugin "
+ "filename %s:%s\n"),
+ strlen(plugname) + 1, plugname,
+ strerror(errno));
+ return (rval);
+ }
+
+ (void) strlcpy(tmpplug->filename, plugname,
+ strlen(plugname) + 1);
+
+ /* now sanity check the file */
+ if ((sym = dlsym(tmpplug->handle, "drivername"))
+ != NULL) {
+ /* max length of drivername */
+ tmpplug->drvname = calloc(1, 17);
+ (void) strlcpy(tmpplug->drvname, (char *)sym, 17);
+ } else {
+ CLOSEFREE();
+ continue;
+ }
+ if ((sym = dlsym(tmpplug->handle, "fw_readfw"))
+ != NULL) {
+ tmpplug->fw_readfw = (int (*)())sym;
+ } else {
+ CLOSEFREE();
+ continue;
+ }
+ if ((sym = dlsym(tmpplug->handle, "fw_writefw"))
+ != NULL) {
+ tmpplug->fw_writefw = (int (*)())sym;
+ } else {
+ CLOSEFREE();
+ continue;
+ }
+
+ if ((sym = dlsym(tmpplug->handle, "fw_identify"))
+ != NULL) {
+ tmpplug->fw_identify =
+ (int (*)(int))sym;
+ } else {
+ CLOSEFREE();
+ continue;
+ }
+ if ((sym = dlsym(tmpplug->handle, "fw_devinfo"))
+ != NULL) {
+ tmpplug->fw_devinfo =
+ (int (*)(struct devicelist *))sym;
+ } else {
+ CLOSEFREE();
+ continue;
+ }
+
+ if ((tmpelem->drvname = calloc(1, 17))
+ == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("Unable to allocate 17 bytes for "
+ "drivername %s\n"),
+ tmpplug->drvname);
+ return (FWFLASH_FAILURE);
+ }
+
+ (void) strlcpy(tmpelem->drvname, tmpplug->drvname,
+ strlen(tmpplug->drvname) + 1);
+
+ if ((tmpelem->filename = calloc(1,
+ strlen(tmpplug->filename) + 1)) == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("Unable to allocate %d bytes for "
+ "filename %s\n"),
+ strlen(tmpplug->filename) + 1,
+ tmpplug->drvname);
+ return (FWFLASH_FAILURE);
+ }
+
+ (void) strlcpy(tmpelem->filename, plugname,
+ strlen(plugname) + 1);
+ tmpelem->plugin = tmpplug;
+
+ /* CONSTCOND */
+ TAILQ_INSERT_TAIL(fw_pluginlist, tmpelem, nextplugin);
+ }
+
+ if ((plugdir == NULL) && TAILQ_EMPTY(fw_pluginlist)) {
+ return (FWFLASH_FAILURE);
+ }
+
+
+ if (errno != 0) {
+ logmsg(MSG_ERROR,
+ gettext("Error reading directory entry in %s\n"),
+ fwplugdirpath);
+ (void) closedir(dirp);
+ rval = errno;
+ }
+
+ (void) free(fwplugdirpath);
+ (void) free(plugdir);
+ (void) closedir(dirp);
+ return (rval);
+}
+
+
+
+/*
+ * fwflash_load_verifier dlload()s the appropriate firmware image
+ * verification plugin, and attaches the designated fwimg's fd to
+ * the vrfyplugin structure so we only have to load the image in
+ * one place.
+ */
+int
+fwflash_load_verifier(char *drv, char *vendorid, char *fwimg) {
+
+ int rv = FWFLASH_FAILURE;
+ int imgfd;
+ char *fwvrfydirpath, *tempdirpath, *filename;
+ char *clean; /* for the space-removed vid */
+
+ struct stat fwstat;
+ struct vrfyplugin *vrfy;
+ void *vrfysym;
+
+
+ /*
+ * To make flashing multiple firmware images somewhat more
+ * efficient, we start this function by checking whether a
+ * verifier for this device has already been loaded. If it
+ * has been loaded, we replace the imgfile information, and
+ * then continue as if we were loading for the first time.
+ */
+
+ if (verifier != NULL) {
+ verifier->imgsize = 0;
+ verifier->flashbuf = 0; /* set by the verifier function */
+
+ if (verifier->imgfile != NULL)
+ (void) free(verifier->imgfile);
+
+ if (verifier->fwimage != NULL)
+ (void) free(verifier->fwimage);
+ } else {
+ if ((fwvrfydirpath = calloc(1, MAXPATHLEN + 1)) == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("Unable to allocate space for a firmware "
+ "verifier file(1)"));
+ return (rv);
+ }
+
+ if ((filename = calloc(1, MAXPATHLEN + 1)) == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("Unable to allocate space "
+ "for a firmware verifier file(2)"));
+ return (rv);
+ }
+
+ /*
+ * Since SCSI devices can have a vendor id of up to 8 left-aligned
+ * and _space-padded_ characters, we first need to strip off any
+ * space characters before we try to make a filename out of it
+ */
+ clean = strtok(vendorid, " ");
+ if (clean == NULL) {
+ /* invalid vendorid, something's really wrong */
+ logmsg(MSG_ERROR,
+ gettext("Invalid vendorid (null) specified for "
+ "device\n"));
+ return (rv);
+ }
+
+ tempdirpath = getenv("FWVERIFYPLUGINDIR");
+
+ if ((fwflash_debug > 0) && (tempdirpath != NULL)) {
+ (void) strlcpy(fwvrfydirpath, tempdirpath,
+ strlen(tempdirpath) + 1);
+ } else {
+ (void) strlcpy(fwvrfydirpath, FWVERIFYPLUGINDIR,
+ strlen(FWVERIFYPLUGINDIR) + 1);
+ }
+
+ if ((vrfy = calloc(1, sizeof (struct vrfyplugin))) == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("Unable to allocate space "
+ "for a firmware verifier structure"));
+ free(filename);
+ free(fwvrfydirpath);
+ return (FWFLASH_FAILURE);
+ }
+
+ errno = 0; /* false positive removal */
+
+ (void) snprintf(filename, strlen(fwvrfydirpath) +
+ strlen(drv) + 7 + strlen(clean), "%s/%s-%s.so\0",
+ fwvrfydirpath, drv, clean);
+
+ if ((vrfy->filename = calloc(1, strlen(filename) + 1))
+ == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("Unable to allocate space to store "
+ "a verifier filename\n"));
+ free(filename);
+ free(fwvrfydirpath);
+ free(vrfy->handle);
+ return (FWFLASH_FAILURE);
+ }
+
+ (void) strlcpy(vrfy->filename, filename, strlen(filename) + 1);
+
+ if ((vrfy->handle = dlopen(filename, RTLD_NOW)) == NULL) {
+ logmsg(MSG_ERROR, gettext(dlerror()));
+ logmsg(MSG_ERROR,
+ gettext("Unable to open verification plugin "
+ "%s.\nUnable to verify firmware image. "
+ "Aborting.\n"),
+ filename);
+ free(filename);
+ free(fwvrfydirpath);
+ return (FWFLASH_FAILURE);
+ }
+
+ if ((vrfysym = dlsym(vrfy->handle, "vendorvrfy")) == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("%s is an invalid firmware verification "
+ "plugin."), filename);
+ (void) dlclose(vrfy->handle);
+ free(filename);
+ free(fwvrfydirpath);
+ free(vrfy);
+ return (FWFLASH_FAILURE);
+ } else {
+ vrfy->vendorvrfy =
+ (int (*)(struct devicelist *))vrfysym;
+ }
+
+ vrfysym = dlsym(vrfy->handle, "vendor");
+
+ if (vrfysym == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("Invalid vendor (null) in verification "
+ "plugin %s\n"), filename);
+ (void) dlclose(vrfy->handle);
+ free(vrfy);
+ return (NULL);
+ } else {
+ if (strncmp(vendorid, (char *)vrfysym,
+ strlen(vendorid)) != 0) {
+ logmsg(MSG_INFO,
+ "Using a sym-linked (%s -> %s) "
+ "verification plugin",
+ vendorid, vrfysym);
+ vrfy->vendor = calloc(1, strlen(vendorid) + 1);
+ } else {
+ vrfy->vendor = calloc(1, strlen(vrfysym) + 1);
+ }
+ (void) strlcpy(vrfy->vendor, (char *)vrfysym,
+ strlen(vendorid) + 1);
+ }
+
+ verifier = vrfy; /* a convenience variable */
+ }
+
+
+ /*
+ * We don't do any verification that the fw image file is in
+ * an approved location, but it's easy enough to modify this
+ * function to do so. The verification plugin should provide
+ * sufficient protection.
+ */
+
+ if ((imgfd = open(fwimg, O_RDONLY)) < 0) {
+ logmsg(MSG_ERROR,
+ gettext("Unable to open designated firmware "
+ "image file %s: %s\n"),
+ (fwimg != NULL) ? fwimg : "(null)",
+ strerror(errno));
+ rv = FWFLASH_FAILURE;
+ goto cleanup;
+ }
+
+ if (stat(fwimg, &fwstat) == -1) {
+ logmsg(MSG_ERROR,
+ gettext("Unable to stat() firmware image file "
+ "%s: %s\n"),
+ fwimg, strerror(errno));
+ rv = FWFLASH_FAILURE;
+ goto cleanup;
+ } else {
+ verifier->imgsize = fwstat.st_size;
+ if ((verifier->fwimage = calloc(1, verifier->imgsize))
+ == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("Unable to load firmware image "
+ "%s: %s\n"),
+ fwimg, strerror(errno));
+ rv = FWFLASH_FAILURE;
+ goto cleanup;
+ }
+ }
+
+ errno = 0;
+ if ((rv = read(imgfd, verifier->fwimage,
+ (size_t)verifier->imgsize)) < verifier->imgsize) {
+ /* we haven't read enough data, bail */
+ logmsg(MSG_ERROR,
+ gettext("Failed to read sufficient data "
+ "(got %d bytes, expected %d bytes) from "
+ "firmware image file %s: %s\n"),
+ rv, verifier->imgsize,
+ filename, strerror(errno));
+ rv = FWFLASH_FAILURE;
+ } else {
+ rv = FWFLASH_SUCCESS;
+ }
+
+ if ((verifier->imgfile = calloc(1, strlen(fwimg) + 1)) == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("Unable to save name of firmware image\n"));
+ rv = FWFLASH_FAILURE;
+ } else {
+ (void) strlcpy(verifier->imgfile, fwimg, strlen(fwimg) + 1);
+ }
+
+ if (rv != FWFLASH_SUCCESS) {
+ /* cleanup and let's get outta here */
+cleanup:
+ free(verifier->filename);
+ free(verifier->vendor);
+
+ if (!(fwflash_arg_list & FWFLASH_READ_FLAG))
+ free(verifier->fwimage);
+
+ verifier->filename = NULL;
+ verifier->vendor = NULL;
+ verifier->vendorvrfy = NULL;
+ verifier->fwimage = NULL;
+ (void) dlclose(verifier->handle);
+ verifier->handle = NULL;
+ free(verifier);
+ if (imgfd >= 0) {
+ (void) close(imgfd);
+ }
+ verifier = NULL;
+ }
+
+ return (rv);
+}
+
+
+
+/*
+ * cycles through the global list of plugins to find
+ * each flashable device, which is added to fw_devices
+ *
+ * Each plugin's identify routine must allocated storage
+ * as required.
+ *
+ * Each plugin's identify routine must return
+ * FWFLASH_FAILURE if it cannot find any devices
+ * which it handles.
+ */
+static int
+flash_device_list()
+{
+ int rv = FWFLASH_FAILURE;
+ int startidx = 0;
+ int sumrv = 0;
+ struct pluginlist *plugins;
+
+
+ /* we open rootnode here, and close it in fwflash_intr */
+ if ((rootnode = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) {
+ logmsg(MSG_ERROR,
+ gettext("Unable to take device tree snapshot: %s\n"),
+ strerror(errno));
+ return (rv);
+ }
+
+ if ((fw_devices = calloc(1, sizeof (struct devicelist))) == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("Unable to malloc %d bytes while "
+ "trying to find devices: %s\n"),
+ sizeof (struct devicelist), strerror(errno));
+ return (FWFLASH_FAILURE);
+ }
+
+ /* CONSTCOND */
+ TAILQ_INIT(fw_devices);
+
+ TAILQ_FOREACH(plugins, fw_pluginlist, nextplugin) {
+ self = plugins->plugin;
+ rv = plugins->plugin->fw_identify(startidx);
+
+ logmsg(MSG_INFO,
+ gettext("fwflash:flash_device_list() got %d from "
+ "identify routine\n"), rv);
+
+ /* only bump startidx if we've found at least one device */
+ if (rv == FWFLASH_SUCCESS) {
+ startidx += 100;
+ sumrv++;
+ } else {
+ logmsg(MSG_INFO,
+ gettext("No flashable devices attached with "
+ "the %s driver in this system\n"),
+ plugins->drvname);
+ }
+ }
+
+ if (sumrv > 0)
+ rv = FWFLASH_SUCCESS;
+
+ return (rv);
+}
+
+
+
+
+static int
+fwflash_list_fw(char *class)
+{
+ int rv = 0;
+ struct devicelist *curdev;
+ int header = 1;
+
+
+ TAILQ_FOREACH(curdev, fw_devices, nextdev) {
+
+ /* we're either class-conscious, or we're not */
+ if (((class != NULL) &&
+ ((strncmp(curdev->classname, "ALL", 3) == 0) ||
+ (strcmp(curdev->classname, class) == 0))) ||
+ (class == NULL)) {
+
+ if (header != 0) {
+ (void) fprintf(stdout,
+ gettext("List of available devices:\n"));
+ header--;
+ }
+ /*
+ * If any plugin's fw_devinfo() function returns
+ * FWFLASH_FAILURE then we want to keep track of
+ * it. _Most_ plugins should always return
+ * FWFLASH_SUCCESS from this function. The only
+ * exception known at this point is the tavor plugin.
+ */
+ rv += curdev->plugin->fw_devinfo(curdev);
+ }
+ }
+
+
+ return (rv);
+}
+
+
+static int
+fwflash_update(char *device, char *filename, int flags) {
+
+
+ int rv = FWFLASH_FAILURE;
+ int needsfree = 0;
+ struct devicelist *curdev;
+ char *realfile;
+
+
+ /*
+ * Here's how we operate:
+ *
+ * We perform some basic checks on the args, then walk
+ * through the device list looking for the device which
+ * matches. We then load the appropriate verifier for the
+ * image file and device, verify the image, then call the
+ * fw_writefw() function of the appropriate plugin.
+ *
+ * There is no "force" flag to enable you to flash a firmware
+ * image onto an incompatible device because the verifier
+ * will return FWFLASH_FAILURE if the image doesn't match.
+ *
+ */
+
+
+ /* new firmware filename and device desc */
+ if (filename == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("Invalid firmware filename (null)\n"));
+ return (FWFLASH_FAILURE);
+ }
+
+ if (device == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("Invalid device requested (null)\n"));
+ return (FWFLASH_FAILURE);
+ }
+
+ if ((realfile = calloc(1, PATH_MAX + 1)) == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("Unable to allocate space for device "
+ "filename, operation might fail\nif %s is"
+ "a symbolic link\n"),
+ device);
+ realfile = device;
+ } else {
+ /*
+ * If realpath() succeeds, then we have a valid
+ * device filename in realfile.
+ */
+ if (realpath(device, realfile) == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("Unable to resolve device filename"
+ ": %s\n"),
+ strerror(errno));
+ /* tidy up */
+ free(realfile);
+ /* realpath didn't succeed, use fallback */
+ realfile = device;
+ } else {
+ needsfree = 1;
+ }
+ }
+
+ logmsg(MSG_INFO,
+ gettext("fwflash_update: fw_filename (%s) device (%s)\n"),
+ filename, device);
+
+ TAILQ_FOREACH(curdev, fw_devices, nextdev) {
+
+ if (strcmp(curdev->access_devname, realfile) == 0) {
+ rv = fwflash_load_verifier(curdev->drvname,
+ curdev->ident->vid, filename);
+ if (rv == FWFLASH_FAILURE) {
+ logmsg(MSG_ERROR,
+ gettext("Unable to load verifier "
+ "for device %s\n"),
+ curdev->access_devname);
+ return (FWFLASH_FAILURE);
+ }
+ rv = verifier->vendorvrfy(curdev);
+ if (rv == FWFLASH_FAILURE) {
+ /* the verifier prints a message */
+ logmsg(MSG_INFO,
+ "verifier (%s) for %s :: %s returned "
+ "FWFLASH_FAILURE\n",
+ verifier->filename,
+ filename, curdev->access_devname);
+ return (rv);
+ }
+
+ if ((flags == FWFLASH_YES_FLAG) ||
+ (rv = confirm_target(curdev, filename)) ==
+ FWFLASH_YES_FLAG) {
+ logmsg(MSG_INFO,
+ "about to flash using plugin %s\n",
+ curdev->plugin->filename);
+ rv = curdev->plugin->fw_writefw(curdev,
+ filename);
+ if (rv == FWFLASH_FAILURE) {
+ logmsg(MSG_ERROR,
+ gettext("Failed to flash "
+ "firmware file %s on "
+ "device %s: %d\n"),
+ filename,
+ curdev->access_devname, rv);
+ }
+ } else {
+ logmsg(MSG_ERROR,
+ gettext("Flash operation not confirmed "
+ "by user\n"),
+ curdev->access_devname);
+ rv = FWFLASH_FAILURE;
+ }
+ }
+ }
+
+ if (needsfree)
+ free(realfile);
+
+ return (rv);
+}
+
+/*
+ * We validate that the device path is in our global device list and
+ * that the filename exists, then palm things off to the relevant plugin.
+ */
+
+static int
+fwflash_read_file(char *device, char *filename)
+{
+ struct devicelist *curdev;
+ int rv;
+ int notfound = 0;
+
+ /* new firmware filename and device desc */
+
+ TAILQ_FOREACH(curdev, fw_devices, nextdev) {
+ if (strncmp(curdev->access_devname, device,
+ MAXPATHLEN) == 0) {
+ rv = curdev->plugin->fw_readfw(curdev, filename);
+
+ if (rv != FWFLASH_SUCCESS)
+ logmsg(MSG_ERROR,
+ gettext("Unable to write out firmware "
+ "image for %s to file %s\n"),
+ curdev->access_devname, filename);
+ } else {
+ notfound++;
+ }
+
+ }
+ if (notfound) {
+ logmsg(MSG_ERROR,
+ gettext("No device matching %s was found.\n"),
+ device);
+ rv = FWFLASH_FAILURE;
+ }
+
+ return (rv);
+}
+
+static void
+fwflash_usage(char *arg)
+{
+
+ (void) fprintf(stderr, "\n");
+ if (arg != NULL) {
+ logmsg(MSG_ERROR,
+ gettext("Invalid argument (%s) supplied\n"), arg);
+ }
+
+ (void) fprintf(stderr, "\n");
+
+ (void) fprintf(stdout, gettext("Usage:\n\t"));
+ (void) fprintf(stdout, gettext("fwflash [-l [-c device_class "
+ "| ALL]] | [-v] | [-h]\n\t"));
+ (void) fprintf(stdout, gettext("fwflash [-f file1,file2,file3"
+ ",... | -r file] [-y] -d device_path\n\n"));
+ (void) fprintf(stdout, "\n"); /* workaround for xgettext */
+
+ (void) fprintf(stdout,
+ gettext("\t-l\t\tlist flashable devices in this system\n"
+ "\t-c device_class limit search to a specific class\n"
+ "\t\t\teg IB for InfiniBand, ses for SCSI Enclosures\n"
+ "\t-v\t\tprint version number of fwflash utility\n"
+ "\t-h\t\tprint this usage mesage\n\n"));
+ (void) fprintf(stdout,
+ gettext("\t-f file1,file2,file3,...\n"
+ "\t\t\tfirmware image file list to flash\n"
+ "\t-r file\t\tfile to dump device firmware to\n"
+ "\t-y\t\tanswer Yes/Y/y to prompts\n"
+ "\t-d device_path\tpathname of device to be flashed\n\n"));
+
+ (void) fprintf(stdout,
+ gettext("\tIf -d device_path is specified, then one of -f "
+ "<files>\n"
+ "\tor -r <file> must also be specified\n\n"));
+
+ (void) fprintf(stdout,
+ gettext("\tIf multiple firmware images are required to be "
+ "flashed\n"
+ "\tthey must be listed together, separated by commas. The\n"
+ "\timages will be flashed in the order specified.\n\n"));
+
+
+ (void) fprintf(stdout, "\n");
+}
+
+
+
+
+
+
+
+static void
+fwflash_version(void)
+{
+ (void) fprintf(stdout, gettext("\n%s: "), FWFLASH_PROG_NAME);
+ (void) fprintf(stdout, gettext("version %s\n"),
+ FWFLASH_VERSION);
+
+
+}
+
+static void
+fwflash_help(void)
+{
+ fwflash_usage(NULL);
+}
+
+/* ARGSUSED */
+static void
+fwflash_intr(int sig)
+{
+
+ struct devicelist *thisdev;
+ struct pluginlist *thisplug;
+
+
+ (void) signal(SIGINT, SIG_IGN);
+ (void) signal(SIGTERM, SIG_IGN);
+ if (fwflash_in_write) {
+ (void) fprintf(stderr,
+ gettext("WARNING: firmware image may be corrupted\n\t"));
+ (void) fprintf(stderr,
+ gettext("Reflash firmware before rebooting!\n"));
+ }
+
+ if (sig > 0) {
+ (void) logmsg(MSG_ERROR, gettext("\n"));
+ (void) logmsg(MSG_ERROR,
+ gettext("fwflash exiting due to signal (%d)\n"), sig);
+ }
+
+ /*
+ * we need to close everything down properly, so
+ * call the plugin closure routines
+ */
+
+ if (fw_devices != NULL) {
+
+ TAILQ_FOREACH(thisdev, fw_devices, nextdev) {
+ /* free the components first */
+ free(thisdev->access_devname);
+ free(thisdev->drvname);
+ free(thisdev->classname);
+ if (thisdev->ident != NULL) {
+ free(thisdev->ident->vid);
+ free(thisdev->ident->pid);
+ free(thisdev->ident->revid);
+ free(thisdev->ident);
+ }
+ thisdev->ident = NULL;
+ thisdev->plugin = NULL; /* we free this elsewhere */
+ /* CONSTCOND */
+ TAILQ_REMOVE(fw_devices, thisdev, nextdev);
+ }
+ }
+
+
+ if (fw_pluginlist != NULL) {
+
+ TAILQ_FOREACH(thisplug, fw_pluginlist, nextplugin) {
+ free(thisplug->filename);
+ free(thisplug->drvname);
+ free(thisplug->plugin->filename);
+ free(thisplug->plugin->drvname);
+ thisplug->filename = NULL;
+ thisplug->drvname = NULL;
+ thisplug->plugin->filename = NULL;
+ thisplug->plugin->drvname = NULL;
+ thisplug->plugin->fw_readfw = NULL;
+ thisplug->plugin->fw_writefw = NULL;
+ thisplug->plugin->fw_identify = NULL;
+ thisplug->plugin->fw_devinfo = NULL;
+ (void) dlclose(thisplug->plugin->handle);
+ thisplug->plugin->handle = NULL;
+ free(thisplug->plugin);
+ thisplug->plugin = NULL;
+ /* CONSTCOND */
+ TAILQ_REMOVE(fw_pluginlist, thisplug, nextplugin);
+
+ }
+ }
+
+
+ if (verifier != NULL) {
+ free(verifier->filename);
+ free(verifier->vendor);
+ verifier->filename = NULL;
+ verifier->vendor = NULL;
+ verifier->vendorvrfy = NULL;
+ (void) dlclose(verifier->handle);
+ verifier->handle = NULL;
+ free(verifier);
+ }
+
+ di_fini(rootnode);
+}
+
+static void
+fwflash_handle_signals(void)
+{
+ if (signal(SIGINT, fwflash_intr) == SIG_ERR) {
+ perror("signal");
+ exit(FWFLASH_FAILURE);
+ }
+
+ if (signal(SIGTERM, fwflash_intr) == SIG_ERR) {
+ perror("signal");
+ exit(FWFLASH_FAILURE);
+ }
+}
+
+static int
+confirm_target(struct devicelist *thisdev, char *file)
+{
+ int resp;
+
+ (void) printf(gettext("About to update firmware on:\n "
+ "%s\nwith file %s.\n"
+ "Do you want to continue? (Y/N): "),
+ thisdev->access_devname, file);
+
+ resp = getchar();
+ if (resp == 'Y' || resp == 'y') {
+ return (FWFLASH_YES_FLAG);
+ } else {
+ logmsg(MSG_INFO, "flash operation NOT confirmed.\n");
+ }
+
+ (void) fflush(stdin);
+
+ return (FWFLASH_FAILURE);
+}
+
+int
+get_fileopts(char *options)
+{
+
+ int i;
+ char *files;
+
+
+ if (files = strtok(options, ",")) {
+ /* we have more than one */
+ if ((filelist[0] = calloc(1, MAXPATHLEN + 1)) == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("Unable to allocate space for "
+ "a firmware image filename\n"));
+ return (FWFLASH_FAILURE);
+ }
+ (void) strlcpy(filelist[0], files, strlen(files) + 1);
+ i = 1;
+
+ logmsg(MSG_INFO, "fwflash: filelist[0]: %s\n",
+ filelist[0]);
+
+
+ while (files = strtok(NULL, ",")) {
+ if ((filelist[i] = calloc(1, MAXPATHLEN + 1))
+ == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("Unable to allocate space for "
+ "a firmware image filename\n"));
+ return (FWFLASH_FAILURE);
+ }
+ (void) strlcpy(filelist[i], files,
+ strlen(files) + 1);
+ logmsg(MSG_INFO, "fwflash: filelist[%d]: %s\n",
+ i, filelist[i]);
+ ++i;
+ }
+ } else {
+ if ((filelist[0] = calloc(1, MAXPATHLEN + 1)) == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("Unable to allocate space for "
+ "a firmware image filename\n"));
+ return (FWFLASH_FAILURE);
+ }
+ (void) strlcpy(filelist[0], options, strlen(files) + 1);
+ logmsg(MSG_INFO, "fwflash: filelist[0]: %s\n",
+ filelist[0]);
+ }
+ return (FWFLASH_SUCCESS);
+
+}
+
+
+
+/*
+ * code reuse - cheerfully borrowed from stmsboot_util.c
+ */
+void
+logmsg(int severity, char *msg, ...) {
+
+ va_list ap;
+
+
+ if ((severity > MSG_INFO) ||
+ ((severity == MSG_INFO) && (fwflash_debug > 0))) {
+
+ (void) fprintf(stderr, "%s: ", FWFLASH_PROG_NAME);
+ va_start(ap, msg);
+ /* LINTED - format specifier */
+ (void) vfprintf(stderr, msg, ap);
+ va_end(ap);
+ }
+}
diff --git a/usr/src/cmd/fwflash/common/fwflash.h b/usr/src/cmd/fwflash/common/fwflash.h
new file mode 100644
index 0000000000..6bb54035e8
--- /dev/null
+++ b/usr/src/cmd/fwflash/common/fwflash.h
@@ -0,0 +1,398 @@
+/*
+ * CDDL HEADER START
+ *
+ * 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
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _FWFLASH_H
+#define _FWFLASH_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * fwflash.h
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/queue.h>
+#include <libdevinfo.h>
+
+
+#define MSG_INFO 0
+#define MSG_WARN 1
+#define MSG_ERROR 2
+int fwflash_debug;
+
+#define FWFLASH_SUCCESS 0
+#define FWFLASH_FAILURE 1
+
+#define FWFLASH_FLASH_IMAGES 2
+
+
+#define FWPLUGINDIR "/usr/lib/fwflash/identify"
+#define FWVERIFYPLUGINDIR "/usr/lib/fwflash/verify"
+
+/*
+ * we search for a variable (fwplugin_version, type uint32_t)
+ * which should equal FWPLUGIN_VERSION_1
+ */
+
+#define FWPLUGIN_VERSION_1 1
+
+struct devicelist;
+
+struct fw_plugin {
+
+ /*
+ * An opaque handle for dlopen()/dlclose() to use.
+ */
+ void *handle;
+
+ /*
+ * fully-qualified filename in /usr/lib/fwflash/identify
+ * made up of [drivername].so
+ *
+ * eg /usr/lib/fwflash/identify/ses.so
+ * is the identification plugin for devices attached to
+ * the host using the ses(7D) driver.
+ */
+ char *filename;
+
+ /*
+ * The driver name that this plugin will search for in
+ * the device tree snapshot using di_drv_first_node(3DEVINFO)
+ * and di_drv_next_node(3DEVINFO).
+ */
+ char *drvname; /* "ses" or "tavor" or .... */
+
+ /*
+ * Function entry point to support the command-line "-r"
+ * option - read image from device to persistent storage.
+ *
+ * Not all plugins and devices will support this operation.
+ */
+ int (*fw_readfw)(struct devicelist *device, char *filename);
+
+
+ /*
+ * Function entry point to support the command-line "-f"
+ * option - writes from persistent storage to device
+ *
+ * All identification plugins must support this operation.
+ */
+ int (*fw_writefw)(struct devicelist *device, char *filename);
+
+
+ /*
+ * Function entry point used to build the list of valid, flashable
+ * devices attached to the system using the loadable module drvname.
+ * (Not all devices attached using drvname will be valid for this
+ * plugin to report.
+ *
+ * start allows us to display flashable devices attached with
+ * different drivers and provide the user with a visual clue
+ * that these devices are different to others that are detected.
+ *
+ * All identification plugins must support this operation.
+ */
+ int (*fw_identify)(int start);
+
+ /*
+ * Function entry point to support the command-line "-l"
+ * option - list/report flashable devices attached to the system.
+ *
+ * All identification plugins must support this operation.
+ */
+ int (*fw_devinfo)(struct devicelist *thisdev);
+};
+
+
+struct pluginlist {
+ /*
+ * fully qualified filename in /usr/lib/fwflash/identify
+ * made up of fwflash-[drivername].so
+ *
+ * eg /usr/lib/fwflash/identify/ses.so
+ * is the identification plugin for devices attached to
+ * the host using the ses(7D) driver.
+ */
+ char *filename;
+
+ /*
+ * The driver name that this plugin will search for in
+ * the device tree snapshot using di_drv_first_node(3DEVINFO)
+ * and di_drv_next_node(3DEVINFO).
+ */
+ char *drvname;
+
+ /*
+ * pointer to the actual plugin, so we can access its
+ * function entry points
+ */
+ struct fw_plugin *plugin;
+
+ /* pointer to the next element in the list */
+ TAILQ_ENTRY(pluginlist) nextplugin;
+};
+
+
+struct vpr {
+ /* vendor ID, eg "HITACHI " */
+ char *vid;
+
+ /* product ID, eg "DK32EJ36NSUN36G " */
+ char *pid;
+
+ /* revision, eg "PQ08" */
+ char *revid;
+
+ /*
+ * Additional, encapsulated identifying information.
+ * This pointer allows us to add details such as the
+ * IB hba sector size, which command set should be
+ * used or a part number.
+ */
+ void *encap_ident;
+};
+
+
+
+
+struct fwfile {
+ /*
+ * The fully qualified filename. No default location for
+ * for the firmware image file is mandated.
+ */
+ char *filename;
+
+ /* Pointer to the identification plugin required */
+ struct fw_plugin *plugin;
+
+ /* pointer to the identification summary structure */
+ struct vpr *ident;
+};
+
+
+
+struct devicelist {
+ /*
+ * fully qualified pathname, with /devices/.... prefix
+ */
+ char *access_devname;
+
+ /*
+ * Which drivername did we find this device attached with
+ * in our device tree walk? Eg, ses or tavor or sgen...
+ */
+ char *drvname;
+
+ /*
+ * What class of device is this? For tavor-attached devices,
+ * we set this to "IB". For other devices, unless there is
+ * a common name to use, just make this the same as the
+ * drvname field.
+ */
+ char *classname;
+
+ /* pointer to the VPR structure */
+ struct vpr *ident;
+
+ /*
+ * In the original fwflash(1M), it was possible to select a
+ * device for flashing by using an index number called a
+ * dev_num. We retain that concept for pluggable fwflash, with
+ * the following change - whenever our identification plugin has
+ * finished and found at least one acceptable device, we bump the
+ * index number by 100. This provides the user with another key
+ * to distinguish the desired device from a potentially very large
+ * list of similar-looking devices.
+ */
+ unsigned int index;
+
+ /*
+ * Contains SAS or FC Port-WWNs, or IB GUIDS. Both SAS and FC only
+ * need one entry in this array since they really only have one
+ * address which we should track. IB devices can have 4 GUIDs
+ * (System Image, Node Image, Port 1 and Port 2).
+ */
+ char *addresses[4];
+
+ /*
+ * Pointer to the plugin needed to flash this device, and
+ * to use for printing appropriate device-specific information
+ * as required by the "-l" option to fwflash(1M).
+ */
+ struct fw_plugin *plugin;
+
+ /* Next entry in the list */
+ TAILQ_ENTRY(devicelist) nextdev;
+};
+
+
+/*
+ * this type of plugin is for the firmware image vendor-specific
+ * verification functions, which we load from FWVERIFYPLUGINDIR
+ */
+
+struct vrfyplugin {
+
+ /*
+ * fully-qualified filename in /usr/lib/fwflash/verify,
+ * made up of [drivername]-[vendorname].so
+ *
+ * eg /usr/lib/fwflash/verify/ses-SUN.so
+ * is the verification plugin for ses-attached devices which
+ * have a vendorname of "SUN".
+ */
+ char *filename;
+
+ /*
+ * The vendor name, such as "SUN" or "MELLANOX"
+ */
+ char *vendor;
+
+ /*
+ * An opaque handle for dlopen()/dlclose() to use.
+ */
+ void *handle;
+
+ /*
+ * Firmware image size in bytes, as reported by
+ * stat().
+ */
+ unsigned int imgsize;
+
+ /*
+ * Flashable devices frequently have different buffers
+ * to use for different image types. We track the buffer
+ * required for this particular image with this variable.
+ *
+ * Once the verifier has figured out what sort of image
+ * it's been passed, it will know what value to use for
+ * this variable.
+ */
+ unsigned int flashbuf;
+
+ /*
+ * Points to the entire firmware image in memory.
+ * We do this so we can avoid multiple open()/close()
+ * operations, and to make it easier for checksum
+ * calculations.
+ */
+ int *fwimage;
+
+ /*
+ * We also store the name of the firmware file that
+ * we point to with *fwimage. This is needed in cases
+ * where we need to key off the name of the file to
+ * determine whether a different buffer in the target
+ * device should be targeted.
+ *
+ * For example, our "standard" firmware image (file.fw)
+ * might require use of buffer id 0, but a boot image
+ * (boot.fw) might require use of buffer id 17. In each
+ * case, it is the verifier plugin that determines the
+ * specific bufferid that is needed by that firmware image.
+ */
+ char *imgfile;
+
+ /*
+ * The verification function entry point. The code
+ * in fwflash.c calls this function to verify that
+ * the nominated firmware image file is valid for
+ * the selected devicenode.
+ *
+ * Note that if the verification fails, the image
+ * does _not_ get force-flashed to the device.
+ */
+ int (*vendorvrfy)(struct devicelist *devicenode);
+};
+
+
+
+/* Flags for argument parsing */
+#define FWFLASH_HELP_FLAG 0x01
+#define FWFLASH_VER_FLAG 0x02
+#define FWFLASH_YES_FLAG 0x04
+#define FWFLASH_LIST_FLAG 0x08
+#define FWFLASH_CLASS_FLAG 0x10
+#define FWFLASH_DEVICE_FLAG 0x20
+#define FWFLASH_FW_FLAG 0x40
+#define FWFLASH_READ_FLAG 0x80
+
+/* global variables for fwflash */
+
+TAILQ_HEAD(PLUGINLIST, pluginlist);
+TAILQ_HEAD(DEVICELIST, devicelist);
+struct PLUGINLIST *fw_pluginlist;
+struct DEVICELIST *fw_devices;
+
+
+struct vrfyplugin *verifier;
+di_node_t rootnode;
+struct fw_plugin *self;
+
+
+/*
+ * utility defines and macros, since the firmware image we get
+ * from LSI is ARM-format and that means byte- and short-swapping
+ * on sparc
+ */
+
+#define HIGHBITS16 0xff00
+#define HIGHBITS32 0xffff0000
+#define HIGHBITS64 0xffffffff00000000ULL
+#define LOWBITS16 0x00ff
+#define LOWBITS32 0x0000ffff
+#define LOWBITS64 0x00000000ffffffffULL
+
+
+#if defined(_LITTLE_ENDIAN)
+#define ARMSWAPBITS(bs) (bs)
+#define MLXSWAPBITS16(bs) \
+ (BE_16(((bs) & LOWBITS16)) | BE_16(((bs) & HIGHBITS16)))
+#define MLXSWAPBITS32(bs) \
+ (BE_32(((bs) & LOWBITS32)) | BE_32(((bs) & HIGHBITS32)))
+#define MLXSWAPBITS64(bs) \
+ (BE_64(((bs) & LOWBITS64)) | BE_64(((bs) & HIGHBITS64)))
+#else
+#define ARMSWAPBITS(bs) (LE_32(((bs) & LOWBITS32)) | LE_32(((bs) & HIGHBITS32)))
+#define MLXSWAPBITS16(bs) (bs)
+#define MLXSWAPBITS32(bs) (bs)
+#define MLXSWAPBITS64(bs) (bs)
+
+#endif
+
+
+/* common functions for fwflash */
+
+void logmsg(int severity, char *msg, ...);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FWFLASH_H */
diff --git a/usr/src/cmd/fwflash/i386/Makefile b/usr/src/cmd/fwflash/i386/Makefile
new file mode 100644
index 0000000000..01aae0d27a
--- /dev/null
+++ b/usr/src/cmd/fwflash/i386/Makefile
@@ -0,0 +1,91 @@
+#
+# CDDL HEADER START
+#
+# 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
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/fwflash/i386/Makefile
+#
+#
+
+
+PROG= fwflash
+OBJS= fwflash.o
+SRCS= fwflash.c
+LINTFILE= fwflash.ln
+
+
+all: $(PROG)
+lint: lint_SRCS
+
+include $(SRC)/Makefile.master
+include $(SRC)/cmd/Makefile.cmd
+include ../Makefile.com
+
+$(ROOTUSRSBIN)/$(PROG) := FILEMODE = 0555
+
+#
+# Message catalog
+#
+POFILES= $(SRCS:%.c=%.po)
+POFILE= fwflash_msg.po
+
+CFLAGS += -g -D_POSIX_PTHREAD_SEMANTICS
+LDLIBS += -ldevinfo -lumem
+
+#
+# Message catalog
+#
+
+$(POFILES): ../common/$(SRCS)
+ $(RM) messages.po
+ $(XGETTEXT) $(XGETFLAGS) \
+ `($(GREP) -l gettext ../common/fwflash.c || echo /dev/null)`
+ $(SED) "/^domain/d" messages.po > $@
+ $(RM) messages.po
+
+$(POFILE): $(POFILES)
+ $(RM) $@
+ cat $(POFILES) > $@
+
+
+clean:
+ $(RM) $(PROG) $(OBJS)
+
+clobber: clean
+ $(RM) $(POFILE) $(POFILES) $(LINTFILE)
+
+install_h:
+ @cd ../common ; $(MAKE) install_h
+
+$(PROG): install_h $(OBJS)
+ $(LINK.c) -o $(PROG) $(OBJS) $(COMMON_OBJS) $(LDFLAGS) $(LDLIBS)
+ $(POST_PROCESS)
+
+%.o: ../common/%.c
+ $(COMPILE.c) $(CFLAGS) -o $@ $<
+
+install: all $(ROOTUSRSBIN) $(ROOTUSRSBIN)/$(PROG)
+
+lint_SRCS: ../common/$(SRCS:%.c=%.ln)
+
+_msg msg: $(POFILE)
diff --git a/usr/src/cmd/fwflash/plugins/Makefile b/usr/src/cmd/fwflash/plugins/Makefile
new file mode 100644
index 0000000000..53b9d8ccf3
--- /dev/null
+++ b/usr/src/cmd/fwflash/plugins/Makefile
@@ -0,0 +1,69 @@
+#
+# CDDL HEADER START
+#
+# 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
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# cmd/fwflash/plugins
+#
+
+include $(SRC)/cmd/Makefile.cmd
+
+COMMON_SUBDIRS= transport
+CLOSED_SUBDIRS= $(CLOSED)/cmd/fwflash/plugins
+
+SUBDIRS= $(COMMON_SUBDIRS) $(CLOSED_SUBDIRS)
+
+MSGSUBDIRS= $(COMMON_SUBDIRS) $(CLOSED_SUBDIRS)
+
+all := TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+clobber := TARGET= clobber
+lint := TARGET= lint
+_msg := TARGET= _msg
+msg := TARGET= msg
+
+include $(SRC)/cmd/fwflash/Makefile.com
+
+
+.KEEP_STATE:
+
+all: $(SUBDIRS)
+lint: $(SUBDIRS)
+install: $(SUBDIRS)
+clean: $(SUBDIRS)
+clobber: $(SUBDIRS)
+
+
+msg _msg: $(MSGSUBDIRS)
+
+
+$(SUBDIRS): FRC
+ @if [ -f $@/Makefile ]; then \
+ cd $@; pwd; $(MAKE) $(TARGET); \
+ else \
+ true; \
+ fi
+
+FRC:
diff --git a/usr/src/cmd/fwflash/plugins/transport/Makefile b/usr/src/cmd/fwflash/plugins/transport/Makefile
new file mode 100644
index 0000000000..d70dedc310
--- /dev/null
+++ b/usr/src/cmd/fwflash/plugins/transport/Makefile
@@ -0,0 +1,71 @@
+#
+# CDDL HEADER START
+#
+# 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
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# cmd/fwflash/plugins/transport
+#
+
+include $(SRC)/cmd/Makefile.cmd
+
+COMMON_SUBDIRS= $(MACH)
+
+SUBDIRS= $(COMMON_SUBDIRS)
+
+MSGSUBDIRS= $(COMMON_SUBDIRS)
+
+all := TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+clobber := TARGET= clobber
+lint := TARGET= lint
+_msg := TARGET= _msg
+msg := TARGET= msg
+
+include $(SRC)/cmd/fwflash/Makefile.com
+
+
+.KEEP_STATE:
+
+all: $(SUBDIRS)
+lint: $(SUBDIRS)
+
+msg _msg: $(MSGSUBDIRS)
+
+
+install: $(SUBDIRS)
+clean: $(SUBDIRS)
+clobber: $(SUBDIRS)
+
+
+$(SUBDIRS): FRC
+ @if [ -f $@/Makefile ]; then \
+ cd $@; pwd; $(MAKE) $(TARGET); \
+ else \
+ true; \
+ fi
+
+FRC:
+
+
diff --git a/usr/src/cmd/fwflash/plugins/transport/common/ses.c b/usr/src/cmd/fwflash/plugins/transport/common/ses.c
new file mode 100644
index 0000000000..b01f8f225b
--- /dev/null
+++ b/usr/src/cmd/fwflash/plugins/transport/common/ses.c
@@ -0,0 +1,851 @@
+/*
+ * CDDL HEADER START
+ *
+ * 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
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * ses (SCSI Generic Device) specific functions.
+ */
+
+
+#include <assert.h>
+#include <libnvpair.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/queue.h>
+#include <fcntl.h>
+#include <string.h>
+#include <strings.h>
+
+#include <scsi/libses.h>
+#include <sys/scsi/generic/commands.h>
+#include <sys/scsi/impl/uscsi.h>
+
+#include <libintl.h> /* for gettext(3c) */
+
+#include <fwflash/fwflash.h>
+
+
+
+#ifdef NDEBUG
+#define verify(EX) ((void)(EX))
+#else
+#define verify(EX) assert(EX)
+#endif
+
+
+#define VIDLEN 0x08
+#define PIDLEN 0x10
+#define REVLEN 0x04
+#define SASADDRLEN 0x10
+#define PCBUFLEN 0x40
+#define RQBUFLEN 0xfe
+#define STATBUFLEN 0xfe
+#define INQBUFLEN 0x80
+
+/* useful defines */
+#define UCODE_CHECK_STATUS 0
+#define UCODE_CHECK_SUPPORTED 1
+
+typedef struct ucode_statdesc {
+ uint64_t us_value;
+ const char *us_desc;
+ boolean_t us_pending;
+ boolean_t us_iserr;
+} ucode_statdesc_t;
+
+static ucode_statdesc_t ucode_statdesc_table[] = {
+ { SES2_DLUCODE_S_NOP, "none", B_FALSE, B_FALSE },
+ { SES2_DLUCODE_S_INPROGRESS, "in progress", B_TRUE, B_FALSE },
+ { SES2_DLUCODE_S_SAVING, "saved", B_TRUE, B_FALSE },
+ { SES2_DLUCODE_S_COMPLETE_NOW, "completed (available)", B_FALSE,
+ B_FALSE },
+ { SES2_DLUCODE_S_COMPLETE_AT_RESET,
+ "completed (need reset or power on)", B_FALSE, B_FALSE },
+ { SES2_DLUCODE_S_COMPLETE_AT_POWERON, "completed (need power on)",
+ B_FALSE, B_FALSE },
+ { SES2_DLUCODE_S_PAGE_ERR, "page error (offset %d)",
+ B_FALSE, B_TRUE },
+ { SES2_DLUCODE_S_IMAGE_ERR, "invalid image",
+ B_FALSE, B_TRUE },
+ { SES2_DLUCODE_S_TIMEOUT, "download timeout",
+ B_FALSE, B_TRUE },
+ { SES2_DLUCODE_S_INTERNAL_NEEDIMAGE,
+ "internal error (NEED NEW IMAGE BEFORE RESET)",
+ B_FALSE, B_TRUE },
+ { SES2_DLUCODE_S_INTERNAL_SAFE,
+ "internal error (reset to revert to backup)",
+ B_FALSE, B_TRUE },
+};
+
+#define NUCODE_STATUS \
+ (sizeof (ucode_statdesc_table) / sizeof (ucode_statdesc_table[0]))
+
+typedef struct ucode_status {
+ uint64_t us_status;
+ boolean_t us_iserr;
+ boolean_t us_pending;
+ char us_desc[128];
+} ucode_status_t;
+
+typedef struct ucode_wait {
+ uint64_t uw_prevstatus;
+ boolean_t uw_pending;
+ ses_node_t *uw_oldnp;
+} ucode_wait_t;
+
+
+char drivername[] = "ses\0";
+static char *devprefix = "/devices";
+static char *devsuffix = ":0";
+static ses_target_t *ses_target;
+
+
+extern di_node_t rootnode;
+extern int errno;
+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);
+
+
+/* helper functions */
+static struct vpr *inquiry(char *path);
+static int ses_dl_ucode_check(struct devicelist *flashdev);
+static ses_walk_action_t print_updated_status(ses_node_t *np, void *arg);
+static int get_status(nvlist_t *props, ucode_status_t *sp);
+static ses_walk_action_t sendimg(ses_node_t *np, void *data);
+
+
+
+/*
+ * SES2 does not actually allow us to read a firmware
+ * image from an SES device, so we just return success
+ * if this is requested, after printing a message.
+ */
+int
+fw_readfw(struct devicelist *flashdev, char *filename)
+{
+ int rv = FWFLASH_SUCCESS;
+
+ logmsg(MSG_INFO,
+ "ses: not writing firmware for device %s to file %s\n",
+ flashdev->access_devname, filename);
+ logmsg(MSG_ERROR, gettext("\n\nSES2 does not support retrieval "
+ "of firmware images\n\n"));
+
+ return (rv);
+}
+
+
+/*
+ * If we're invoking fw_writefw, then flashdev is a valid,
+ * flashable device supporting the SES2 Download Microcode Diagnostic
+ * Control page (0x0e).
+ *
+ * If verifier is null, then we haven't been called following a firmware
+ * image verification load operation.
+ *
+ * *THIS* function uses scsi SEND DIAGNOSTIC/download microcode to
+ * achieve the task... if you chase down to the bottom of libses you
+ * can see that too.
+ */
+int
+fw_writefw(struct devicelist *flashdev)
+{
+
+ nvlist_t *nvl;
+ ses_snap_t *snapshot;
+
+
+
+ if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
+ nvlist_add_uint64(nvl, SES_CTL_PROP_UCODE_MODE,
+ SES_DLUCODE_M_WITH_OFFS) != 0) {
+ logmsg(MSG_ERROR, gettext("ses: Unable to allocate "
+ "space for device prop list\n"));
+ return (FWFLASH_FAILURE);
+ }
+
+
+ if ((verifier == NULL) || (verifier->imgsize == 0) ||
+ (verifier->fwimage == NULL)) {
+ /* should _not_ happen */
+ logmsg(MSG_ERROR, gettext("ses: Firmware image has not "
+ "been verified.\n"));
+ return (FWFLASH_FAILURE);
+ }
+
+ fprintf(stdout, "\n"); /* get a fresh line for progress updates */
+
+ if (nvlist_add_uint64(nvl, SES_CTL_PROP_UCODE_BUFID,
+ verifier->flashbuf) != 0) {
+ logmsg(MSG_ERROR, gettext("ses: Unable to add buffer id "
+ "property\n"));
+ goto cancel;
+ }
+
+ if (nvlist_add_byte_array(nvl, SES_CTL_PROP_UCODE_DATA,
+ (uint8_t *)verifier->fwimage, verifier->imgsize) != 0) {
+ logmsg(MSG_ERROR,
+ "%s: Out of memory for property addition\n",
+ drivername);
+ goto cancel;
+ }
+
+ if ((ses_target =
+ ses_open(LIBSES_VERSION, flashdev->access_devname)) == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("ses: Unable to open flashable device\n%s\n"),
+ flashdev->access_devname);
+ goto cancel;
+ }
+ snapshot = ses_snap_hold(ses_target);
+
+ /*
+ * We flash via a walker callback function, because it's easier
+ * to do it this way when using libses.
+ */
+
+ (void) ses_walk(snapshot, sendimg, nvl);
+
+
+ logmsg(MSG_ERROR,
+ gettext("ses: Done. New image will be active "
+ "after the system is rebooted.\n"));
+
+ fprintf(stdout, "\n");
+
+ ses_snap_rele(snapshot);
+ ses_close(ses_target);
+
+cancel:
+ nvlist_free(nvl);
+
+ return (FWFLASH_SUCCESS);
+}
+
+
+/*
+ * The fw_identify() function walks the device
+ * tree trying to find devices which this plugin
+ * can work with.
+ *
+ * The parameter "start" gives us the starting index number
+ * to give the device when we add it to the fw_devices list.
+ *
+ * firstdev is allocated by us and we add space as needed
+ */
+int
+fw_identify(int start)
+{
+
+ int rv = FWFLASH_FAILURE;
+ di_node_t thisnode;
+ struct devicelist *newdev;
+ char *devpath;
+ int idx = start;
+ int devlength = 0;
+
+
+ thisnode = di_drv_first_node(drivername, rootnode);
+
+ if (thisnode == DI_NODE_NIL) {
+ logmsg(MSG_INFO, gettext("No %s nodes in this system\n"),
+ drivername);
+
+ return (rv);
+ }
+
+ if ((devpath = calloc(1, MAXPATHLEN + 1)) == NULL) {
+ logmsg(MSG_ERROR, gettext("ses: Unable to malloc space "
+ "for a %s-attached device node\n"), drivername);
+ return (rv);
+ }
+ bzero(devpath, MAXPATHLEN);
+
+ /* we've found one, at least */
+
+ for (; thisnode != DI_NODE_NIL; thisnode = di_drv_next_node(thisnode)) {
+
+ devpath = di_devfs_path(thisnode);
+
+ if ((newdev = calloc(1, sizeof (struct devicelist)))
+ == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("ses: identification function unable "
+ "to allocate space for device entry\n"));
+ return (rv);
+ }
+
+ /* malloc enough for /devices + devpath + ":0" + '\0' */
+ devlength = strlen(devpath) + strlen(devprefix) +
+ strlen(devsuffix) + 2;
+
+ if ((newdev->access_devname = calloc(1, devlength)) == NULL) {
+ logmsg(MSG_ERROR, gettext("ses: Unable to malloc "
+ "space for a devfs name\n"));
+ free(devpath);
+ return (FWFLASH_FAILURE);
+ }
+ snprintf(newdev->access_devname, devlength,
+ "%s%s%s", devprefix, devpath, devsuffix);
+
+ if ((newdev->drvname = calloc(1, strlen(drivername) + 1))
+ == NULL) {
+ logmsg(MSG_ERROR, gettext("ses: Unable to malloc "
+ "space for a driver name\n"));
+ free(newdev->access_devname);
+ free(newdev);
+ return (FWFLASH_FAILURE);
+ }
+ (void) strlcpy(newdev->drvname, drivername,
+ strlen(drivername) + 1);
+
+ if ((newdev->classname = calloc(1, strlen(drivername) + 1))
+ == NULL) {
+ logmsg(MSG_ERROR, gettext("ses: Unable to malloc "
+ "space for a class name\n"));
+ free(newdev->access_devname);
+ free(newdev->drvname);
+ free(newdev);
+ return (FWFLASH_FAILURE);
+ }
+ (void) strlcpy(newdev->classname, drivername,
+ strlen(drivername) + 1);
+
+
+ /*
+ * Check for friendly vendor names, and whether this device
+ * supports the Download Microcode Control page.
+ */
+
+ newdev->ident = inquiry(newdev->access_devname);
+ rv = ses_dl_ucode_check(newdev);
+ if ((rv == FWFLASH_FAILURE) || (newdev->ident == NULL))
+ continue;
+
+
+ if (newdev->ident == NULL) {
+ logmsg(MSG_INFO,
+ "ses: unable to inquire on potentially "
+ "flashable device\n%s\n",
+ newdev->access_devname);
+ free(newdev->access_devname);
+ free(newdev->drvname);
+ free(newdev->classname);
+ free(newdev);
+ continue;
+ }
+
+ /*
+ * Look for the target-port property. We use addresses[1]
+ * because addresses[0] is already assigned by the inquiry
+ * function
+ */
+ if ((newdev->addresses[1] = calloc(1, SASADDRLEN + 1))
+ == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("ses: Out of memory for target-port\n"));
+ free(newdev->access_devname);
+ free(newdev->drvname);
+ free(newdev->classname);
+ free(newdev);
+ continue;
+ } else {
+ if (di_prop_lookup_strings(DDI_DEV_T_ANY, thisnode,
+ "target-port", &newdev->addresses[1]) < 0) {
+ logmsg(MSG_INFO,
+ "ses: no target-port property for "
+ "device %s\n",
+ newdev->access_devname);
+ strlcpy(newdev->addresses[1],
+ "0000000000000000", 17);
+ }
+ }
+
+
+ newdev->index = idx;
+ ++idx;
+ newdev->plugin = self;
+
+ TAILQ_INSERT_TAIL(fw_devices, newdev, nextdev);
+ }
+
+
+ if (fwflash_debug != 0) {
+ struct devicelist *tempdev;
+
+ TAILQ_FOREACH(tempdev, fw_devices, nextdev) {
+ logmsg(MSG_INFO, "ses:fw_writefw:\n");
+ logmsg(MSG_INFO, "\ttempdev @ 0x%lx\n"
+ "\t\taccess_devname: %s\n"
+ "\t\tdrvname: %s\tclassname: %s\n"
+ "\t\tident->vid: %s\n"
+ "\t\tident->pid: %s\n"
+ "\t\tident->revid: %s\n"
+ "\t\tindex: %d\n"
+ "\t\taddress[0]: %s\n"
+ "\t\taddress[1]: %s\n"
+ "\t\tplugin @ 0x%lx\n\n",
+ &tempdev,
+ tempdev->access_devname,
+ tempdev->drvname, newdev->classname,
+ tempdev->ident->vid,
+ tempdev->ident->pid,
+ tempdev->ident->revid,
+ tempdev->index,
+ (tempdev->addresses[0] != NULL) ?
+ (char *)tempdev->addresses[0] : "NULL",
+ (tempdev->addresses[1] != NULL) ?
+ (char *)tempdev->addresses[1] : "NULL",
+ tempdev->plugin);
+ }
+ }
+
+ return (FWFLASH_SUCCESS);
+}
+
+
+
+int
+fw_devinfo(struct devicelist *thisdev)
+{
+
+
+ fprintf(stdout, gettext("Device[%d] %s\n Class [%s]\n"),
+ thisdev->index, thisdev->access_devname, thisdev->classname);
+
+ if (thisdev->addresses[0] != NULL) {
+ fprintf(stdout,
+ gettext("\tChassis Serial Number : %s\n"),
+ thisdev->addresses[0]);
+ }
+
+ fprintf(stdout,
+ gettext("\tVendor : %s\n"
+ "\tProduct : %s\n"
+ "\tFirmware revision : %s\n"
+ "\tTarget-port identifier : %s\n"),
+ thisdev->ident->vid,
+ thisdev->ident->pid,
+ thisdev->ident->revid,
+ thisdev->addresses[1]);
+
+ fprintf(stdout, "\n\n");
+
+ return (FWFLASH_SUCCESS);
+}
+
+
+
+
+
+/*ARGSUSED*/
+static int
+get_status(nvlist_t *props, ucode_status_t *sp)
+{
+ int i;
+ uint64_t status, astatus;
+
+ if (nvlist_lookup_uint64(props, SES_EN_PROP_UCODE, &status) != 0) {
+ sp->us_status = -1ULL;
+ (void) snprintf(sp->us_desc, sizeof (sp->us_desc),
+ "not supported");
+ return (-1);
+ }
+
+ verify(nvlist_lookup_uint64(props, SES_EN_PROP_UCODE_A, &astatus) == 0);
+
+ for (i = 0; i < NUCODE_STATUS; i++) {
+ if (ucode_statdesc_table[i].us_value == status)
+ break;
+ }
+
+ sp->us_status = status;
+
+ if (i == NUCODE_STATUS) {
+ (void) snprintf(sp->us_desc, sizeof (sp->us_desc),
+ "unknown (0x%02x)", (int)status);
+ sp->us_iserr = sp->us_pending = B_FALSE;
+ } else {
+ /* LINTED */
+ (void) snprintf(sp->us_desc, sizeof (sp->us_desc),
+ ucode_statdesc_table[i].us_desc, (int)astatus);
+ sp->us_iserr = ucode_statdesc_table[i].us_iserr;
+ sp->us_pending = ucode_statdesc_table[i].us_pending;
+ }
+
+ return (0);
+}
+
+
+static ses_walk_action_t
+print_updated_status(ses_node_t *np, void *arg)
+{
+ ucode_wait_t *uwp = arg;
+ ses_node_t *oldnp = uwp->uw_oldnp;
+ nvlist_t *props, *oldprops;
+ uint64_t id, oldid;
+ ucode_status_t status;
+
+ if (ses_node_type(np) != SES_NODE_ENCLOSURE)
+ return (SES_WALK_ACTION_CONTINUE);
+
+ verify((props = ses_node_props(np)) != NULL);
+ verify((oldprops = ses_node_props(oldnp)) != NULL);
+ verify(nvlist_lookup_uint64(props, SES_EN_PROP_EID, &id) == 0);
+ verify(nvlist_lookup_uint64(oldprops, SES_EN_PROP_EID, &oldid) == 0);
+
+ if (oldid != id)
+ return (SES_WALK_ACTION_CONTINUE);
+
+ (void) get_status(props, &status);
+ if (status.us_status != uwp->uw_prevstatus)
+ (void) printf("%30s: %s\n", "status", status.us_desc);
+ uwp->uw_prevstatus = status.us_status;
+ uwp->uw_pending = status.us_pending;
+
+ if (status.us_iserr)
+ logmsg(MSG_INFO,
+ "ses: status.us_iserr: 0x%0x\n",
+ status.us_iserr);
+
+ return (SES_WALK_ACTION_CONTINUE);
+}
+
+/*ARGSUSED*/
+static ses_walk_action_t
+sendimg(ses_node_t *np, void *data)
+{
+ nvlist_t *props;
+ nvlist_t *arg = data;
+ char *vendor, *product, *revision, *csn;
+ char buf[128];
+ ses_snap_t *newsnap;
+ int ret;
+ ucode_status_t statdesc;
+ ucode_wait_t wait;
+ uint8_t *imagedata;
+ size_t len;
+
+ if (ses_node_type(np) != SES_NODE_ENCLOSURE)
+ return (SES_WALK_ACTION_CONTINUE);
+
+ verify((props = ses_node_props(np)) != NULL);
+
+ verify(nvlist_lookup_string(props, SES_EN_PROP_VID, &vendor) == 0);
+ verify(nvlist_lookup_string(props, SES_EN_PROP_PID, &product) == 0);
+ verify(nvlist_lookup_string(props, SES_EN_PROP_REV, &revision) == 0);
+ verify(nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, &csn) == 0);
+
+ (void) printf("%30s: %s\n", "vendor", vendor);
+ (void) printf("%30s: %s\n", "product", product);
+ (void) printf("%30s: %s\n", "revision", revision);
+ (void) printf("%30s: %s\n", "serial", csn);
+
+ ret = get_status(props, &statdesc);
+ (void) printf("%30s: %s\n", "current status", statdesc.us_desc);
+ if (ret != 0) {
+ if (arg != NULL)
+ return (SES_WALK_ACTION_TERMINATE);
+ else
+ return (SES_WALK_ACTION_CONTINUE);
+ }
+
+ verify(nvlist_lookup_byte_array(arg, SES_CTL_PROP_UCODE_DATA,
+ &imagedata, &len) == 0);
+ (void) snprintf(buf, sizeof (buf), "downloading %u bytes", len);
+ (void) printf("\n%30s: ", buf);
+ if (ses_node_ctl(np, SES_CTL_OP_DL_UCODE, arg) != 0) {
+ (void) printf("failed!\n");
+ (void) printf("%s\n", ses_errmsg());
+ } else {
+ (void) printf("ok\n");
+ }
+
+ wait.uw_prevstatus = -1ULL;
+ wait.uw_oldnp = np;
+ do {
+ if ((newsnap = ses_snap_new(ses_target)) == NULL)
+ logmsg(MSG_ERROR,
+ "failed to update SES snapshot: %s",
+ ses_errmsg());
+
+ (void) ses_walk(newsnap, print_updated_status,
+ &wait);
+ ses_snap_rele(newsnap);
+ } while (wait.uw_pending);
+
+ return (SES_WALK_ACTION_CONTINUE);
+}
+
+/*
+ * Simple function to sent a standard SCSI INQUIRY(6) cdb out
+ * to thisnode and blat the response back into a struct vpr*
+ */
+static struct vpr *
+inquiry(char *path) {
+
+ struct uscsi_cmd *inqcmd;
+ uchar_t inqbuf[INQBUFLEN]; /* inquiry response */
+ uchar_t rqbuf[RQBUFLEN]; /* request sense data */
+ struct vpr *inqvpr;
+ int fd, rval;
+
+
+ inqvpr = NULL;
+ if ((inqcmd = calloc(1, sizeof (struct uscsi_cmd))) == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("ses: Unable to malloc %d bytes "
+ "for a SCSI INQUIRY(6) command\n"),
+ sizeof (struct uscsi_cmd));
+ return (NULL);
+ }
+
+ if ((inqvpr = calloc(1, sizeof (struct vpr))) == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("ses: Unable to malloc %d bytes "
+ "for SCSI INQUIRY(6) response\n"),
+ sizeof (struct vpr));
+ free(inqcmd);
+ return (NULL);
+ }
+
+ if ((inqcmd->uscsi_cdb = calloc(1, CDB_GROUP0 * sizeof (caddr_t)))
+ == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("ses: Unable to malloc %d bytes "
+ "for SCSI INQUIRY(6)\n"),
+ CDB_GROUP0 * sizeof (caddr_t));
+ free(inqcmd);
+ free(inqvpr);
+ return (NULL);
+ }
+
+ logmsg(MSG_INFO, "ses:inquiry:opening device %s\n",
+ path);
+
+ if ((fd = open(path, O_RDONLY|O_SYNC)) < 0) {
+ logmsg(MSG_INFO,
+ "ses: Unable to open device %s: %s\n",
+ path, strerror(errno));
+ free(inqcmd->uscsi_cdb);
+ free(inqcmd);
+ free(inqvpr);
+ return (NULL);
+ }
+
+ if (((inqvpr->vid = calloc(1, VIDLEN + 1))
+ == NULL) ||
+ ((inqvpr->pid = calloc(1, PIDLEN + 1))
+ == NULL) ||
+ ((inqvpr->revid = calloc(1, REVLEN + 1))
+ == NULL)) {
+ logmsg(MSG_ERROR,
+ gettext("ses: Unable to malloc %d bytes "
+ "for %s identification function.\n"),
+ VIDLEN+PIDLEN+REVLEN, drivername);
+ free(inqcmd->uscsi_cdb);
+ free(inqcmd);
+ free(inqvpr->vid);
+ free(inqvpr->pid);
+ free(inqvpr->revid);
+ return (NULL);
+ }
+
+ /* just make sure these buffers are clean */
+ bzero(inqbuf, INQBUFLEN);
+ bzero(rqbuf, RQBUFLEN);
+ bzero(inqcmd->uscsi_cdb, CDB_GROUP0);
+ inqcmd->uscsi_flags = USCSI_READ;
+ inqcmd->uscsi_timeout = 0;
+ inqcmd->uscsi_bufaddr = (caddr_t)inqbuf;
+ inqcmd->uscsi_buflen = INQBUFLEN;
+ inqcmd->uscsi_cdblen = CDB_GROUP0; /* a GROUP 0 command */
+ inqcmd->uscsi_cdb[0] = SCMD_INQUIRY;
+ inqcmd->uscsi_cdb[1] = 0x00; /* EVPD = Enable Vital Product Data */
+ inqcmd->uscsi_cdb[2] = 0x00; /* which pagecode to query? */
+ inqcmd->uscsi_cdb[3] = 0x00; /* allocation length, msb */
+ inqcmd->uscsi_cdb[4] = INQBUFLEN; /* allocation length, lsb */
+ inqcmd->uscsi_cdb[5] = 0x0; /* control byte */
+ inqcmd->uscsi_rqbuf = (caddr_t)&rqbuf;
+ inqcmd->uscsi_rqlen = RQBUFLEN;
+
+
+ rval = ioctl(fd, USCSICMD, inqcmd);
+
+ if (rval < 0) {
+ /* ioctl failed */
+ logmsg(MSG_INFO,
+ gettext("ses: Unable to retrieve SCSI INQUIRY(6) data "
+ "from device %s: %s\n"),
+ path, strerror(errno));
+ free(inqcmd->uscsi_cdb);
+ free(inqcmd);
+ free(inqvpr->vid);
+ free(inqvpr->pid);
+ free(inqvpr->revid);
+ return (NULL);
+ }
+
+
+
+ bcopy(&inqbuf[8], inqvpr->vid, VIDLEN);
+ bcopy(&inqbuf[16], inqvpr->pid, PIDLEN);
+ bcopy(&inqbuf[32], inqvpr->revid, REVLEN);
+
+ (void) close(fd);
+
+ logmsg(MSG_INFO,
+ "ses inquiry: vid %s ; pid %s ; revid %s\n",
+ inqvpr->vid, inqvpr->pid, inqvpr->revid);
+
+ if ((strncmp(inqvpr->vid, "SUN", 3) != 0) &&
+ (strncmp(inqvpr->vid, "LSI", 3) != 0) &&
+ (strncmp(inqvpr->vid, "Quanta", 6) != 0) &&
+ (strncmp(inqvpr->vid, "QUANTA", 6) != 0)) {
+ free(inqvpr->vid);
+ free(inqvpr->pid);
+ free(inqvpr->revid);
+ inqvpr->vid = NULL;
+ inqvpr->pid = NULL;
+ inqvpr->revid = NULL;
+ logmsg(MSG_INFO,
+ "ses inquiry: unrecognised device\n");
+ return (NULL);
+ }
+
+ free(inqcmd->uscsi_cdb);
+ free(inqcmd);
+
+ return (inqvpr);
+}
+
+
+/*
+ * ses_dl_ucode_check() lets us check whether SES2's Download
+ * Microcode Control diagnostic and status pages are supported
+ * by flashdev.
+ */
+int
+ses_dl_ucode_check(struct devicelist *flashdev)
+{
+ int rv;
+ int limit;
+ int i = 0;
+ int fd;
+ struct uscsi_cmd *usc;
+ uchar_t sensebuf[RQBUFLEN]; /* for the request sense data */
+ uchar_t pagesup[PCBUFLEN]; /* should be less than 64 pages */
+
+
+ if ((fd = open(flashdev->access_devname,
+ O_RDONLY|O_NDELAY)) < 0) {
+ logmsg(MSG_INFO,
+ gettext("ses:ses_dl_ucode_check: Unable to open %s\n"),
+ flashdev->access_devname);
+ return (FWFLASH_FAILURE);
+ }
+
+ if ((usc = calloc(1, sizeof (struct uscsi_cmd))) == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("ses: Unable to alloc %d bytes for "
+ "microcode download query: %s\n"),
+ sizeof (struct uscsi_cmd), strerror(errno));
+ (void) close(fd);
+ return (FWFLASH_FAILURE);
+ }
+
+ if ((usc->uscsi_cdb = calloc(1, CDB_GROUP0 * sizeof (caddr_t)))
+ == NULL) {
+ logmsg(MSG_ERROR,
+ gettext("ses: Unable to alloc %d bytes for "
+ "microcode download query: %s\n"),
+ CDB_GROUP0 * sizeof (caddr_t), strerror(errno));
+ (void) close(fd);
+ free(usc);
+ return (FWFLASH_FAILURE);
+ }
+
+
+ bzero(sensebuf, RQBUFLEN);
+
+ usc->uscsi_flags = USCSI_READ | USCSI_RQENABLE;
+ usc->uscsi_timeout = 0;
+ usc->uscsi_cdblen = CDB_GROUP0;
+ usc->uscsi_rqbuf = (caddr_t)&sensebuf;
+ usc->uscsi_rqlen = RQBUFLEN;
+
+
+ bzero(pagesup, PCBUFLEN);
+ usc->uscsi_bufaddr = (caddr_t)&pagesup;
+ usc->uscsi_buflen = PCBUFLEN;
+
+ usc->uscsi_cdb[0] = SCMD_GDIAG; /* "Get" or receive */
+ usc->uscsi_cdb[1] = 1; /* PCV = Page Code Valid */
+ usc->uscsi_cdb[2] = 0x00; /* list all Supported diag pages */
+ usc->uscsi_cdb[3] = 0x00;
+ usc->uscsi_cdb[4] = PCBUFLEN;
+ usc->uscsi_cdb[5] = 0; /* control byte, reserved */
+
+ rv = ioctl(fd, USCSICMD, usc);
+ if (rv < 0) {
+ logmsg(MSG_INFO,
+ "ses: DL uCode checker ioctl error (%d): %s\n",
+ errno, strerror(errno));
+ return (FWFLASH_FAILURE);
+ }
+
+ rv = FWFLASH_FAILURE;
+ /* in SPC4, this is an "n-3" field */
+ limit = (pagesup[2] << 8) + pagesup[3] + 4;
+ while (i < limit) {
+ if (pagesup[4 + i] == 0x0E) {
+ i = limit;
+ logmsg(MSG_INFO, "ses: device %s "
+ "supports the Download Microcode "
+ "diagnostic control page\n",
+ flashdev->access_devname);
+ rv = FWFLASH_SUCCESS;
+ }
+ ++i;
+ }
+ (void) close(fd);
+ free(usc->uscsi_cdb);
+ free(usc);
+
+ return (rv);
+}
diff --git a/usr/src/cmd/fwflash/plugins/transport/i386/Makefile b/usr/src/cmd/fwflash/plugins/transport/i386/Makefile
new file mode 100644
index 0000000000..4deb1f78de
--- /dev/null
+++ b/usr/src/cmd/fwflash/plugins/transport/i386/Makefile
@@ -0,0 +1,92 @@
+#
+# CDDL HEADER START
+#
+# 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
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+#
+# cmd/fwflash/plugins/transport/i386
+#
+
+SRCS= ses.c
+
+OBJECTS= $(SRCS:%.c=%.o)
+PLUGINS= $(SRCS:%.c=%.so)
+POFILES= ses.po
+IDENTPOFILE= fwflash_transport_identify_ses.po
+LINTFILE= ses.ln
+
+CLEANFILES= $(OBJECTS)
+CLOBBERFILES= $(PLUGINS) $(POFILES) $(IDENTPOFILE) $(LINTFILE)
+
+
+
+all: $(PLUGINS)
+_msg msg: $(IDENTPOFILE)
+
+
+include $(SRC)/Makefile.master
+include $(SRC)/cmd/fwflash/Makefile.com
+
+CFLAGS += -g -D_POSIX_PTHREAD_SEMANTICS
+DYNFLAGS += -Bdynamic
+LDLIBS += -L$(ROOT)/usr/lib/scsi -ldevinfo -lumem -lc -lscsi -lses
+LDFLAGS += -R/usr/lib/scsi
+
+BUILD.SO= $(LD) -o $@ -G $(DYNFLAGS) $(LDFLAGS) $(LDLIBS)
+
+$(PLUGINS) := FILEMODE = 0555
+
+
+install: $(ROOTLIBFWFLASHPLUGINS) \
+ $(ROOTLIBFWFLASHPLUGINS)/ses.so
+
+clobber clean:
+ $(RM) $(CLEANFILES) $(CLOBBERFILES)
+
+lint: lint_SRCS
+
+%.o: ../common/%.c
+ $(COMPILE.c) $<
+ $(POST_PROCESS_O)
+
+%.so: %.o
+ $(BUILD.SO) $<
+
+
+#
+# Message catalog
+#
+$(POFILES): ../common/$(SRCS)
+ $(RM) messages.po
+ $(XGETTEXT) $(XGETFLAGS) \
+ `($(GREP) -l gettext ../common/ses.c || echo /dev/null)`
+ $(SED) "/^domain/d" messages.po > $@
+ $(RM) messages.po
+
+$(IDENTPOFILE): $(POFILES)
+ $(RM) $@
+ cat $(POFILES) > $@
+
+lint_SRCS: ../common/$(SRCS:%.c=%.ln)
+
+msg: $(IDENTPOFILE)
diff --git a/usr/src/cmd/fwflash/plugins/transport/sparc/Makefile b/usr/src/cmd/fwflash/plugins/transport/sparc/Makefile
new file mode 100644
index 0000000000..810c17a28a
--- /dev/null
+++ b/usr/src/cmd/fwflash/plugins/transport/sparc/Makefile
@@ -0,0 +1,92 @@
+#
+# CDDL HEADER START
+#
+# 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
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+#
+# cmd/fwflash/plugins/transport/sparc
+#
+
+SRCS= ses.c
+
+OBJECTS= $(SRCS:%.c=%.o)
+PLUGINS= $(SRCS:%.c=%.so)
+POFILES= ses.po
+IDENTPOFILE= fwflash_transport_identify_ses.po
+LINTFILE= ses.ln
+
+CLEANFILES= $(OBJECTS)
+CLOBBERFILES= $(PLUGINS) $(POFILES) $(IDENTPOFILE) $(LINTFILE)
+
+
+
+all: $(PLUGINS)
+_msg msg: $(IDENTPOFILE)
+
+
+include $(SRC)/Makefile.master
+include $(SRC)/cmd/fwflash/Makefile.com
+
+CFLAGS += -g -D_POSIX_PTHREAD_SEMANTICS
+DYNFLAGS += -Bdynamic
+LDLIBS += -L$(ROOT)/usr/lib/scsi -ldevinfo -lumem -lc -lscsi -lses
+LDFLAGS += -R/usr/lib/scsi
+
+BUILD.SO= $(LD) -o $@ -G $(DYNFLAGS) $(LDFLAGS) $(LDLIBS)
+
+$(PLUGINS) := FILEMODE = 0555
+
+
+install: $(ROOTLIBFWFLASHPLUGINS) \
+ $(ROOTLIBFWFLASHPLUGINS)/ses.so
+
+clobber clean:
+ $(RM) $(CLEANFILES) $(CLOBBERFILES)
+
+lint: lint_SRCS
+
+%.o: ../common/%.c
+ $(COMPILE.c) $<
+ $(POST_PROCESS_O)
+
+%.so: %.o
+ $(BUILD.SO) $<
+
+
+#
+# Message catalog
+#
+$(POFILES): ../common/$(SRCS)
+ $(RM) messages.po
+ $(XGETTEXT) $(XGETFLAGS) \
+ `($(GREP) -l gettext ../common/ses.c || echo /dev/null)`
+ $(SED) "/^domain/d" messages.po > $@
+ $(RM) messages.po
+
+$(IDENTPOFILE): $(POFILES)
+ $(RM) $@
+ cat $(POFILES) > $@
+
+lint_SRCS: ../common/$(SRCS:%.c=%.ln)
+
+msg: $(IDENTPOFILE)
diff --git a/usr/src/cmd/fwflash/sparc/Makefile b/usr/src/cmd/fwflash/sparc/Makefile
new file mode 100644
index 0000000000..37cf90d423
--- /dev/null
+++ b/usr/src/cmd/fwflash/sparc/Makefile
@@ -0,0 +1,91 @@
+#
+# CDDL HEADER START
+#
+# 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
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/fwflash/sparc/Makefile
+#
+#
+
+
+PROG= fwflash
+OBJS= fwflash.o
+SRCS= fwflash.c
+LINTFILE= fwflash.ln
+
+
+all: $(PROG)
+lint: lint_SRCS
+
+include $(SRC)/Makefile.master
+include $(SRC)/cmd/Makefile.cmd
+include ../Makefile.com
+
+$(ROOTUSRSBIN)/$(PROG) := FILEMODE = 0555
+
+#
+# Message catalog
+#
+POFILES= $(SRCS:%.c=%.po)
+POFILE= fwflash_msg.po
+
+CFLAGS += -g -D_POSIX_PTHREAD_SEMANTICS
+LDLIBS += -ldevinfo -lumem
+
+#
+# Message catalog
+#
+
+$(POFILES): ../common/$(SRCS)
+ $(RM) messages.po
+ $(XGETTEXT) $(XGETFLAGS) \
+ `($(GREP) -l gettext ../common/fwflash.c || echo /dev/null)`
+ $(SED) "/^domain/d" messages.po > $@
+ $(RM) messages.po
+
+$(POFILE): $(POFILES)
+ $(RM) $@
+ cat $(POFILES) > $@
+
+
+clean:
+ $(RM) $(PROG) $(OBJS)
+
+clobber: clean
+ $(RM) $(POFILE) $(POFILES) $(LINTFILE)
+
+install_h:
+ @cd ../common ; $(MAKE) install_h
+
+$(PROG): install_h $(OBJS)
+ $(LINK.c) -o $(PROG) $(OBJS) $(COMMON_OBJS) $(LDFLAGS) $(LDLIBS)
+ $(POST_PROCESS)
+
+%.o: ../common/%.c
+ $(COMPILE.c) $(CFLAGS) -o $@ $<
+
+install: all $(ROOTUSRSBIN) $(ROOTUSRSBIN)/$(PROG)
+
+lint_SRCS: ../common/$(SRCS:%.c=%.ln)
+
+_msg msg: $(POFILE)