fwflash.c revision f8bf33c3108f55c2de4da206ddef8650742d3465
/*
* 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.
*/
/*
* fwflash.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.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 <sys/varargs.h>
#include <libintl.h> /* for gettext(3c) */
#include <libdevinfo.h>
#include <fwflash/fwflash.h>
#include <sys/modctl.h> /* for MAXMODCONFNAME */
#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. We should deprecate the "-v" option since it is not
* actually of any use - it doesn't line up with Mercurial's
* concept of the changeset.
*/
#define FWFLASH_VERSION "v1.7"
#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(char *arg);
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:M")) != 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_usage(NULL);
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;
case 'M':
/* NOT in the manpage */
#if (MANUFACTURING_MODE > 0)
manufacturing_mode = 1;
logmsg(MSG_WARN, "Enabling Manufacturing Mode "
"operations. This can be destructive!\n");
break;
#endif
/* 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_usage(NULL);
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);
}
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, MAXMODCONFNAME);
/* are we doing double-time? */
if (strncmp((char *)sym, plugdir->d_name,
MAXMODCONFNAME) != 0) {
char *tempnm = calloc(1, MAXMODCONFNAME);
memcpy(tempnm, plugdir->d_name, MAXMODCONFNAME);
(void) strlcpy(tmpplug->drvname,
strtok(tempnm, "."),
strlen(plugdir->d_name) + 1);
free(tempnm);
} else {
(void) strlcpy(tmpplug->drvname,
(char *)sym, strlen(sym) + 1);
}
} 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, MAXMODCONFNAME))
== NULL) {
logmsg(MSG_ERROR,
gettext("Unable to allocate space for a"
"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->filename);
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. Unable 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;
int found = 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 if %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) {
found++;
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) == 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 (!found)
/* report the same device that the user passed in */
logmsg(MSG_ERROR,
gettext("Device %s does not appear "
"to be flashable\n"),
((strncmp(device, realfile, strlen(device)) == 0) ?
device : realfile));
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 found = 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);
found++;
}
}
if (!found) {
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 message\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_intr(int sig)
{
struct devicelist *thisdev;
struct pluginlist *thisplug;
(void) signal(SIGINT, SIG_IGN);
(void) signal(SIGTERM, SIG_IGN);
(void) signal(SIGABRT, 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);
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);
free(verifier->imgfile);
free(verifier->fwimage);
verifier->filename = NULL;
verifier->vendor = NULL;
verifier->vendorvrfy = NULL;
verifier->imgfile = NULL;
verifier->fwimage = 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) fflush(stdin);
(void) printf(gettext("About to update firmware on %s\n"),
thisdev->access_devname);
(void) printf(gettext("with file %s. Do you want to continue? "
"(Y/N): "), 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, const 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);
(void) vfprintf(stderr, msg, ap);
va_end(ap);
}
}