diff options
| author | jmcp <none@none> | 2008-04-26 16:02:09 -0700 |
|---|---|---|
| committer | jmcp <none@none> | 2008-04-26 16:02:09 -0700 |
| commit | 5a7763bf3e9db4cfe6cb523b096cb74af71e3793 (patch) | |
| tree | 588f9b2047f5b952a44eef7d0f86361d0bdd56cd /usr/src/cmd/fwflash | |
| parent | 47fc6f3c5b3c55e162497f2704e8a30366037e6d (diff) | |
| download | illumos-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/Makefile | 54 | ||||
| -rw-r--r-- | usr/src/cmd/fwflash/Makefile.com | 91 | ||||
| -rw-r--r-- | usr/src/cmd/fwflash/common/Makefile | 41 | ||||
| -rw-r--r-- | usr/src/cmd/fwflash/common/fwflash.c | 1291 | ||||
| -rw-r--r-- | usr/src/cmd/fwflash/common/fwflash.h | 398 | ||||
| -rw-r--r-- | usr/src/cmd/fwflash/i386/Makefile | 91 | ||||
| -rw-r--r-- | usr/src/cmd/fwflash/plugins/Makefile | 69 | ||||
| -rw-r--r-- | usr/src/cmd/fwflash/plugins/transport/Makefile | 71 | ||||
| -rw-r--r-- | usr/src/cmd/fwflash/plugins/transport/common/ses.c | 851 | ||||
| -rw-r--r-- | usr/src/cmd/fwflash/plugins/transport/i386/Makefile | 92 | ||||
| -rw-r--r-- | usr/src/cmd/fwflash/plugins/transport/sparc/Makefile | 92 | ||||
| -rw-r--r-- | usr/src/cmd/fwflash/sparc/Makefile | 91 |
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) |
