fwflash.c revision 9862:e8921084ee49
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/*
27 * fwflash.c
28 */
29#include <stdio.h>
30#include <stdlib.h>
31#include <unistd.h>
32#include <strings.h>
33#include <errno.h>
34#include <sys/queue.h>
35#include <signal.h>
36#include <locale.h>
37#include <sys/stat.h>
38#include <sys/types.h>
39#include <sys/param.h>
40#include <fcntl.h>
41#include <dlfcn.h>
42#include <dirent.h>
43#include <sys/varargs.h>
44#include <libintl.h> /* for gettext(3c) */
45#include <libdevinfo.h>
46#include <libscf_priv.h>
47#include <fwflash/fwflash.h>
48#include <sys/modctl.h> /* for MAXMODCONFNAME */
49
50
51#if !defined(lint)
52/* embedded software license agreement */
53static char *sla [] = { "Copyright 2007 Sun Microsystems, Inc., 4150 Network "
54"Circle, Santa Clara, California 95054, U.S.A. All rights reserved. U.S. "
55"Government Rights - Commercial software.  Government users are subject to the "
56"Sun Microsystems, Inc. standard license agreement and applicable provisions "
57"of the FAR and its supplements.  Use is subject to license terms.  Parts of "
58"the product may be derived from Berkeley BSD systems, licensed from the "
59"University of California. UNIX is a registered trademark in the U.S. and in "
60"other countries, exclusively licensed through X/Open Company, Ltd.Sun, Sun "
61"Microsystems, the Sun logo and Solaris are trademarks or registered "
62"trademarks of Sun Microsystems, Inc. in the U.S. and other countries. This "
63"product is covered and controlled by U.S. Export Control laws and may be "
64"subject to the export or import laws in other countries.  Nuclear, missile, "
65"chemical biological weapons or nuclear maritime end uses or end users, "
66"whether direct or indirect, are strictly prohibited.  Export or reexport "
67"to countries subject to U.S. embargo or to entities identified on U.S. export "
68"exclusion lists, including, but not limited to, the denied persons and "
69"specially designated nationals lists is strictly prohibited." };
70#endif	/* lint */
71
72/* global arg list */
73int	fwflash_arg_list = 0;
74char	*filelist[10];
75
76/* are we writing to flash? */
77static int fwflash_in_write = 0;
78
79/*
80 * If we *must* track the version string for fwflash, then
81 * we should do so in this common file rather than the header
82 * file since it will then be in sync with what the customer
83 * sees. We should deprecate the "-v" option since it is not
84 * actually of any use - it doesn't line up with Mercurial's
85 * concept of the changeset.
86 */
87#define	FWFLASH_VERSION		"v1.8"
88#define	FWFLASH_PROG_NAME	"fwflash"
89
90static int get_fileopts(char *options);
91static int flash_device_list();
92static int flash_load_plugins();
93static int fwflash_update(char *device, char *filename, int flags);
94static int fwflash_read_file(char *device, char *filename);
95static int fwflash_list_fw(char *class);
96static int fwflash_load_verifier(char *drv, char *vendorid, char *fwimg);
97static void fwflash_intr(int sig);
98static void fwflash_handle_signals(void);
99static void fwflash_usage(char *arg);
100static void fwflash_version(void);
101static int confirm_target(struct devicelist *thisdev, char *file);
102
103/*
104 * FWFlash main code
105 */
106int
107main(int argc, char **argv)
108{
109	int		rv = FWFLASH_SUCCESS;
110	int		i;
111	char		ch;
112	char		*read_file;
113	extern char	*optarg;
114	char		*devclass = NULL;
115	char		*devpath = NULL;
116
117	/* local variables from env */
118	(void) setlocale(LC_ALL, "");
119
120#if !defined(TEXT_DOMAIN)		/* Should be defined by cc -D */
121#define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it isn't. */
122#endif
123
124	(void) textdomain(TEXT_DOMAIN);
125
126	read_file = NULL;
127
128	if (argc < 2) {
129		/* no args supplied */
130		fwflash_usage(NULL);
131		return (FWFLASH_FAILURE);
132	}
133
134	while ((ch = getopt(argc, argv, "hvylc:f:r:Qd:")) != EOF) {
135		switch (ch) {
136		case 'h':
137			fwflash_arg_list |= FWFLASH_HELP_FLAG;
138			break;
139		case 'v':
140			fwflash_arg_list |= FWFLASH_VER_FLAG;
141			break;
142		case 'y':
143			fwflash_arg_list |= FWFLASH_YES_FLAG;
144			break;
145		case 'l':
146			fwflash_arg_list |= FWFLASH_LIST_FLAG;
147			break;
148		case 'c':
149			fwflash_arg_list |= FWFLASH_CLASS_FLAG;
150			/* we validate later */
151			devclass = strdup(optarg);
152			break;
153		case 'd':
154			fwflash_arg_list |= FWFLASH_DEVICE_FLAG;
155			devpath = strdup(optarg);
156			break;
157		case 'f':
158			fwflash_arg_list |= FWFLASH_FW_FLAG;
159			if ((rv = get_fileopts(optarg)) != FWFLASH_SUCCESS) {
160				fwflash_usage(NULL);
161				return (FWFLASH_FAILURE);
162			}
163			break;
164		case 'r':
165			fwflash_arg_list |= FWFLASH_READ_FLAG;
166			read_file = strdup(optarg);
167			break;
168		case 'Q':
169			/* NOT in the manpage */
170			fwflash_debug = 1;
171			break;
172		/* illegal options */
173		default:
174			fwflash_usage(optarg);
175			return (FWFLASH_FAILURE);
176		}
177	}
178
179	/* Do Help */
180	if ((fwflash_arg_list & FWFLASH_HELP_FLAG) ||
181	    ((fwflash_arg_list & FWFLASH_DEVICE_FLAG) &&
182	    !((fwflash_arg_list & FWFLASH_FW_FLAG) ||
183	    (fwflash_arg_list & FWFLASH_READ_FLAG)))) {
184		fwflash_usage(NULL);
185		return (FWFLASH_SUCCESS);
186	}
187
188	/* Do Version */
189	if (fwflash_arg_list == FWFLASH_VER_FLAG) {
190		fwflash_version();
191		return (FWFLASH_SUCCESS);
192	}
193
194	/* generate global list of devices */
195	if ((rv = flash_load_plugins()) != FWFLASH_SUCCESS) {
196		logmsg(MSG_ERROR,
197		    gettext("Unable to load fwflash plugins\n"));
198		fwflash_intr(0);
199		return (rv);
200	}
201
202	if ((rv = flash_device_list()) != FWFLASH_SUCCESS) {
203		logmsg(MSG_ERROR,
204		    gettext("No flashable devices in this system\n"));
205		fwflash_intr(0);
206		return (rv);
207	}
208
209	/* Do list */
210	if (fwflash_arg_list == (FWFLASH_LIST_FLAG) ||
211	    fwflash_arg_list == (FWFLASH_LIST_FLAG | FWFLASH_CLASS_FLAG)) {
212		rv = fwflash_list_fw(devclass);
213		fwflash_intr(0);
214		return (rv);
215	}
216
217	fwflash_handle_signals();
218
219	/* Do flash update (write) */
220	if ((fwflash_arg_list == (FWFLASH_FW_FLAG | FWFLASH_DEVICE_FLAG)) ||
221	    (fwflash_arg_list == (FWFLASH_FW_FLAG | FWFLASH_DEVICE_FLAG |
222	    FWFLASH_YES_FLAG))) {
223		int fastreboot_disabled = 0;
224		/* the update function handles the real arg parsing */
225		i = 0;
226		while (filelist[i] != NULL) {
227			if ((rv = fwflash_update(devpath, filelist[i],
228			    fwflash_arg_list)) == FWFLASH_SUCCESS) {
229				/* failed ops have already been noted */
230				if (!fastreboot_disabled &&
231				    scf_fastreboot_default_set_transient(
232				    B_FALSE) != SCF_SUCCESS)
233					logmsg(MSG_ERROR, gettext(
234					    "Failed to disable fast "
235					    "reboot.\n"));
236				else
237					fastreboot_disabled = 1;
238				logmsg(MSG_ERROR,
239				    gettext("New firmware will be activated "
240				    "after you reboot\n\n"));
241			}
242			++i;
243		}
244
245		fwflash_intr(0);
246		return (rv);
247	}
248
249	/* Do flash read */
250	if ((fwflash_arg_list == (FWFLASH_READ_FLAG | FWFLASH_DEVICE_FLAG)) ||
251	    (fwflash_arg_list == (FWFLASH_READ_FLAG | FWFLASH_DEVICE_FLAG |
252	    FWFLASH_YES_FLAG))) {
253		rv = fwflash_read_file(devpath, read_file);
254		fwflash_intr(0);
255		return (rv);
256	}
257
258	fwflash_usage(NULL);
259
260	return (FWFLASH_FAILURE);
261}
262
263
264static int
265flash_load_plugins()
266{
267
268	int rval = FWFLASH_SUCCESS;
269	DIR *dirp;
270	struct dirent *plugdir;
271	char *plugname;
272	struct fw_plugin *tmpplug;
273	struct pluginlist *tmpelem;
274	void *sym;
275	char *fwplugdirpath, *tempdirpath;
276
277
278#define	CLOSEFREE()	{			\
279	(void) dlclose(tmpplug->handle);	\
280	free(tmpplug); }
281
282	/*
283	 * Procedure:
284	 *
285	 * cd /usr/lib/fwflash/identify
286	 * open each .so file found therein
287	 * dlopen(.sofile)
288	 * if it's one of our plugins, add it to fw_pluginlist;
289	 *
290	 * functions we need here include dlopen and dlsym.
291	 *
292	 * If we get to the end and fw_pluginlist struct is empty,
293	 * return FWFLASH_FAILURE so we return to the shell.
294	 */
295
296	if ((fwplugdirpath = calloc(1, MAXPATHLEN + 1)) == NULL) {
297		logmsg(MSG_ERROR,
298		    gettext("Unable to malloc %d bytes while "
299		    "trying to load plugins: %s\n"),
300		    MAXPATHLEN + 1, strerror(errno));
301		return (FWFLASH_FAILURE);
302	}
303
304	tempdirpath = getenv("FWPLUGINDIR");
305
306	if ((fwflash_debug > 0) && (tempdirpath != NULL)) {
307		(void) strlcpy(fwplugdirpath, tempdirpath,
308		    strlen(tempdirpath) + 1);
309	} else {
310		(void) strlcpy(fwplugdirpath, FWPLUGINDIR,
311		    strlen(FWPLUGINDIR) + 1);
312	}
313
314	if ((dirp = opendir(fwplugdirpath)) == NULL) {
315		logmsg(MSG_ERROR,
316		    gettext("Unable to open %s\n"),
317		    fwplugdirpath);
318		return (errno);
319	}
320
321	if ((plugdir = calloc(1, sizeof (struct dirent) + MAXPATHLEN + 1))
322	    == NULL) {
323		logmsg(MSG_ERROR,
324		    gettext("Unable to malloc %d bytes while "
325		    "trying to load plugins: %s\n"),
326		    MAXPATHLEN + 1 + sizeof (struct dirent),
327		    strerror(errno));
328		return (FWFLASH_FAILURE);
329	}
330
331	if ((fw_pluginlist = calloc(1, sizeof (struct fw_plugin)))
332	    == NULL) {
333		logmsg(MSG_ERROR,
334		    gettext("Unable to malloc %d bytes while "
335		    "trying to load plugins: %s\n"),
336		    sizeof (struct fw_plugin), strerror(errno));
337		return (FWFLASH_FAILURE);
338	}
339
340	TAILQ_INIT(fw_pluginlist);
341
342	while ((readdir_r(dirp, plugdir, &plugdir) == 0) && (plugdir != NULL)) {
343
344		errno = 0; /* remove chance of false results */
345
346		if ((plugdir->d_name[0] == '.') ||
347		    (strstr(plugdir->d_name, ".so") == NULL)) {
348			continue;
349		}
350
351		if ((plugname = calloc(1, MAXPATHLEN + 1)) == NULL) {
352			logmsg(MSG_ERROR,
353			    gettext("Unable to malloc %d bytes while "
354			    "trying to load plugins: %s\n"),
355			    MAXPATHLEN + 1, strerror(errno));
356			return (FWFLASH_FAILURE);
357		}
358
359		(void) snprintf(plugname, MAXPATHLEN, "%s/%s",
360		    fwplugdirpath, plugdir->d_name);
361
362		/* start allocating storage */
363		if ((tmpelem = calloc(1, sizeof (struct pluginlist)))
364		    == NULL) {
365			logmsg(MSG_ERROR,
366			    gettext("Unable to malloc %d bytes while "
367			    "trying to load plugins: %s\n"),
368			    sizeof (struct pluginlist), strerror(errno));
369			return (FWFLASH_FAILURE);
370		}
371
372		if ((tmpplug = calloc(1, sizeof (struct fw_plugin)))
373		    == NULL) {
374			logmsg(MSG_ERROR,
375			    gettext("Unable to malloc %d bytes while "
376			    "trying to load plugins: %s\n"),
377			    sizeof (struct fw_plugin), strerror(errno));
378			return (FWFLASH_FAILURE);
379		}
380
381		/* load 'er up! */
382		tmpplug->handle = dlopen(plugname, RTLD_NOW);
383		if (tmpplug->handle == NULL) {
384			free(tmpplug);
385			continue; /* assume there are other plugins */
386		}
387
388		if ((tmpplug->filename = calloc(1, strlen(plugname) + 1))
389		    == NULL) {
390			logmsg(MSG_ERROR,
391			    gettext("Unable to allocate %d bytes for plugin "
392			    "filename %s:%s\n"),
393			    strlen(plugname) + 1, plugname,
394			    strerror(errno));
395			return (rval);
396		}
397
398		(void) strlcpy(tmpplug->filename, plugname,
399		    strlen(plugname) + 1);
400
401		/* now sanity check the file */
402		if ((sym = dlsym(tmpplug->handle, "drivername"))
403		    != NULL) {
404			/* max length of drivername */
405			tmpplug->drvname = calloc(1, MAXMODCONFNAME);
406
407			/* are we doing double-time? */
408			if (strncmp((char *)sym, plugdir->d_name,
409			    MAXMODCONFNAME) != 0) {
410				char *tempnm = calloc(1, MAXMODCONFNAME);
411
412				(void) memcpy(tempnm, plugdir->d_name,
413				    MAXMODCONFNAME);
414				(void) strlcpy(tmpplug->drvname,
415				    strtok(tempnm, "."),
416				    strlen(plugdir->d_name) + 1);
417				free(tempnm);
418			} else {
419				(void) strlcpy(tmpplug->drvname,
420				    (char *)sym, strlen(sym) + 1);
421			}
422		} else {
423			CLOSEFREE();
424			continue;
425		}
426		if ((sym = dlsym(tmpplug->handle, "fw_readfw"))
427		    != NULL) {
428			tmpplug->fw_readfw = (int (*)())sym;
429		} else {
430			CLOSEFREE();
431			continue;
432		}
433		if ((sym = dlsym(tmpplug->handle, "fw_writefw"))
434		    != NULL) {
435			tmpplug->fw_writefw = (int (*)())sym;
436		} else {
437			CLOSEFREE();
438			continue;
439		}
440
441		if ((sym = dlsym(tmpplug->handle, "fw_identify"))
442		    != NULL) {
443			tmpplug->fw_identify =
444			    (int (*)(int))sym;
445		} else {
446			CLOSEFREE();
447			continue;
448		}
449		if ((sym = dlsym(tmpplug->handle, "fw_devinfo"))
450		    != NULL) {
451			tmpplug->fw_devinfo =
452			    (int (*)(struct devicelist *))sym;
453		} else {
454			CLOSEFREE();
455			continue;
456		}
457
458		if ((sym = dlsym(tmpplug->handle, "plugin_version"))
459		    != NULL) {
460			if ((*(int *)sym) >= FWPLUGIN_VERSION_2) {
461				if ((sym = dlsym(tmpplug->handle,
462				    "fw_cleanup")) != NULL) {
463					tmpplug->fw_cleanup =
464					    (void (*)(struct devicelist *))sym;
465				} else {
466					logmsg(MSG_ERROR,
467					    gettext("ERROR: v2 plugin (%s) "
468					    "has no fw_cleanup function\n"),
469					    tmpplug->filename);
470					CLOSEFREE();
471					continue;
472				}
473			} else {
474				logmsg(MSG_INFO,
475				    "Identification plugin %s defined "
476				    "plugin_version < FWPLUGIN_VERSION_2 !");
477			}
478		}
479
480		if ((tmpelem->drvname = calloc(1, MAXMODCONFNAME))
481		    == NULL) {
482			logmsg(MSG_ERROR,
483			    gettext("Unable to allocate space for a"
484			    "drivername %s\n"),
485			    tmpplug->drvname);
486			return (FWFLASH_FAILURE);
487		}
488
489		(void) strlcpy(tmpelem->drvname, tmpplug->drvname,
490		    strlen(tmpplug->drvname) + 1);
491
492		if ((tmpelem->filename = calloc(1,
493		    strlen(tmpplug->filename) + 1)) == NULL) {
494			logmsg(MSG_ERROR,
495			    gettext("Unable to allocate %d bytes for "
496			    "filename %s\n"),
497			    strlen(tmpplug->filename) + 1,
498			    tmpplug->filename);
499			return (FWFLASH_FAILURE);
500		}
501
502		(void) strlcpy(tmpelem->filename, plugname,
503		    strlen(plugname) + 1);
504		tmpelem->plugin = tmpplug;
505
506		/* CONSTCOND */
507		TAILQ_INSERT_TAIL(fw_pluginlist, tmpelem, nextplugin);
508	}
509
510	if ((plugdir == NULL) && TAILQ_EMPTY(fw_pluginlist)) {
511		return (FWFLASH_FAILURE);
512	}
513
514	if (errno != 0) {
515		logmsg(MSG_ERROR,
516		    gettext("Error reading directory entry in %s\n"),
517		    fwplugdirpath);
518		rval = errno;
519	}
520
521	free(fwplugdirpath);
522	free(plugdir);
523	(void) closedir(dirp);
524	return (rval);
525}
526
527/*
528 * fwflash_load_verifier dlload()s the appropriate firmware image
529 * verification plugin, and attaches the designated fwimg's fd to
530 * the vrfyplugin structure so we only have to load the image in
531 * one place.
532 */
533int
534fwflash_load_verifier(char *drv, char *vendorid, char *fwimg)
535{
536
537	int rv = FWFLASH_FAILURE;
538	int imgfd;
539	char *fwvrfydirpath, *tempdirpath, *filename;
540	char *clean; /* for the space-removed vid */
541	struct stat fwstat;
542	struct vrfyplugin *vrfy;
543	void *vrfysym;
544
545	/*
546	 * To make flashing multiple firmware images somewhat more
547	 * efficient, we start this function by checking whether a
548	 * verifier for this device has already been loaded. If it
549	 * has been loaded, we replace the imgfile information, and
550	 * then continue as if we were loading for the first time.
551	 */
552
553	if (verifier != NULL) {
554		verifier->imgsize = 0;
555		verifier->flashbuf = 0; /* set by the verifier function */
556
557		if (verifier->imgfile != NULL) {
558			free(verifier->imgfile);
559			verifier->imgfile = NULL;
560		}
561
562		if (verifier->fwimage != NULL) {
563			free(verifier->fwimage);
564			verifier->fwimage = NULL;
565		}
566	} else {
567		if ((fwvrfydirpath = calloc(1, MAXPATHLEN + 1)) == NULL) {
568			logmsg(MSG_ERROR,
569			    gettext("Unable to allocate space for a firmware "
570			    "verifier file(1)"));
571			return (rv);
572		}
573
574		if ((filename = calloc(1, MAXPATHLEN + 1)) == NULL) {
575			logmsg(MSG_ERROR,
576			    gettext("Unable to allocate space "
577			    "for a firmware verifier file(2)"));
578			free(fwvrfydirpath);
579			return (rv);
580		}
581
582		/*
583		 * Since SCSI devices can have a vendor id of up to 8
584		 * left-aligned and _space-padded_ characters, we first need to
585		 * strip off any space characters before we try to make a
586		 * filename out of it
587		 */
588		clean = strtok(vendorid, " ");
589		if (clean == NULL) {
590			/* invalid vendorid, something's really wrong */
591			logmsg(MSG_ERROR,
592			    gettext("Invalid vendorid (null) specified for "
593			    "device\n"));
594			free(filename);
595			free(fwvrfydirpath);
596			return (rv);
597		}
598
599		tempdirpath = getenv("FWVERIFYPLUGINDIR");
600
601		if ((fwflash_debug > 0) && (tempdirpath != NULL)) {
602			(void) strlcpy(fwvrfydirpath, tempdirpath,
603			    strlen(tempdirpath) + 1);
604		} else {
605			(void) strlcpy(fwvrfydirpath, FWVERIFYPLUGINDIR,
606			    strlen(FWVERIFYPLUGINDIR) + 1);
607		}
608
609		if ((vrfy = calloc(1, sizeof (struct vrfyplugin))) == NULL) {
610			logmsg(MSG_ERROR,
611			    gettext("Unable to allocate space "
612			    "for a firmware verifier structure"));
613			free(filename);
614			free(fwvrfydirpath);
615			return (rv);
616		}
617
618		errno = 0; /* false positive removal */
619
620		(void) snprintf(filename, MAXPATHLEN, "%s/%s-%s.so",
621		    fwvrfydirpath, drv, clean);
622		if ((vrfy->handle = dlopen(filename, RTLD_NOW)) == NULL) {
623			logmsg(MSG_INFO, gettext(dlerror()));
624			logmsg(MSG_INFO,
625			    gettext("\nUnable to open verification plugin "
626			    "%s. Looking for %s-GENERIC plugin instead.\n"),
627			    filename, drv);
628
629			/* Try the drv-GENERIC.so form, _then_ die */
630			bzero(filename, strlen(filename) + 1);
631			(void) snprintf(filename, MAXPATHLEN,
632			    "%s/%s-GENERIC.so", fwvrfydirpath, drv);
633
634			if ((vrfy->handle = dlopen(filename, RTLD_NOW))
635			    == NULL) {
636				logmsg(MSG_INFO, gettext(dlerror()));
637				logmsg(MSG_ERROR,
638				    gettext("\nUnable to open either "
639				    "verification plugin %s/%s-%s.so or "
640				    "generic plugin %s.\nUnable to verify "
641				    "firmware image. Aborting.\n"),
642				    fwvrfydirpath, drv, clean, filename);
643				free(filename);
644				free(fwvrfydirpath);
645				return (rv);
646			}
647		}
648
649		if ((vrfy->filename = calloc(1, strlen(filename) + 1))
650		    == NULL) {
651			logmsg(MSG_ERROR,
652			    gettext("Unable to allocate space to store "
653			    "a verifier filename\n"));
654			free(filename);
655			free(fwvrfydirpath);
656			free(vrfy->handle);
657			return (rv);
658		}
659		(void) strlcpy(vrfy->filename, filename, strlen(filename) + 1);
660
661		if ((vrfysym = dlsym(vrfy->handle, "vendorvrfy")) == NULL) {
662			logmsg(MSG_ERROR,
663			    gettext("%s is an invalid firmware verification "
664			    "plugin."), filename);
665			(void) dlclose(vrfy->handle);
666			free(filename);
667			free(fwvrfydirpath);
668			free(vrfy);
669			return (rv);
670		} else {
671			vrfy->vendorvrfy =
672			    (int (*)(struct devicelist *))vrfysym;
673		}
674
675		vrfysym = dlsym(vrfy->handle, "vendor");
676
677		if (vrfysym == NULL) {
678			logmsg(MSG_ERROR,
679			    gettext("Invalid vendor (null) in verification "
680			    "plugin %s\n"), filename);
681			(void) dlclose(vrfy->handle);
682			free(vrfy);
683			return (rv);
684		} else {
685			if (strncmp(vendorid, (char *)vrfysym,
686			    strlen(vendorid)) != 0) {
687				logmsg(MSG_INFO,
688				    "Using a sym-linked (%s -> %s) "
689				    "verification plugin\n",
690				    vendorid, vrfysym);
691				vrfy->vendor = calloc(1, strlen(vendorid) + 1);
692			} else {
693				vrfy->vendor = calloc(1, strlen(vrfysym) + 1);
694			}
695			(void) strlcpy(vrfy->vendor, (char *)vrfysym,
696			    strlen(vendorid) + 1);
697		}
698
699		verifier = vrfy; /* a convenience variable */
700		free(filename);
701		free(fwvrfydirpath);
702	}
703
704	/*
705	 * We don't do any verification that the fw image file is in
706	 * an approved location, but it's easy enough to modify this
707	 * function to do so. The verification plugin should provide
708	 * sufficient protection.
709	 */
710
711	if ((imgfd = open(fwimg, O_RDONLY)) < 0) {
712		logmsg(MSG_ERROR,
713		    gettext("Unable to open designated firmware "
714		    "image file %s: %s\n"),
715		    (fwimg != NULL) ? fwimg : "(null)",
716		    strerror(errno));
717		rv = FWFLASH_FAILURE;
718		goto cleanup;
719	}
720
721	if (stat(fwimg, &fwstat) == -1) {
722		logmsg(MSG_ERROR,
723		    gettext("Unable to stat() firmware image file "
724		    "%s: %s\n"),
725		    fwimg, strerror(errno));
726		rv = FWFLASH_FAILURE;
727		goto cleanup;
728	} else {
729		verifier->imgsize = fwstat.st_size;
730		if ((verifier->fwimage = calloc(1, verifier->imgsize))
731		    == NULL) {
732			logmsg(MSG_ERROR,
733			    gettext("Unable to load firmware image "
734			    "%s: %s\n"),
735			    fwimg, strerror(errno));
736			rv = FWFLASH_FAILURE;
737			goto cleanup;
738		}
739	}
740
741	errno = 0;
742	if ((rv = read(imgfd, verifier->fwimage,
743	    (size_t)verifier->imgsize)) < verifier->imgsize) {
744		/* we haven't read enough data, bail */
745		logmsg(MSG_ERROR,
746		    gettext("Failed to read sufficient data "
747		    "(got %d bytes, expected %d bytes) from "
748		    "firmware image file %s: %s\n"),
749		    rv, verifier->imgsize,
750		    verifier->filename, strerror(errno));
751		rv = FWFLASH_FAILURE;
752	} else {
753		rv = FWFLASH_SUCCESS;
754	}
755
756	if ((verifier->imgfile = calloc(1, strlen(fwimg) + 1)) == NULL) {
757		logmsg(MSG_ERROR,
758		    gettext("Unable to save name of firmware image\n"));
759		rv = FWFLASH_FAILURE;
760	} else {
761		(void) strlcpy(verifier->imgfile, fwimg, strlen(fwimg) + 1);
762	}
763
764	if (rv != FWFLASH_SUCCESS) {
765		/* cleanup and let's get outta here */
766cleanup:
767		free(verifier->filename);
768		free(verifier->vendor);
769
770		if (!(fwflash_arg_list & FWFLASH_READ_FLAG) &&
771		    verifier->fwimage)
772			free(verifier->fwimage);
773
774		verifier->filename = NULL;
775		verifier->vendor = NULL;
776		verifier->vendorvrfy = NULL;
777		verifier->fwimage = NULL;
778		(void) dlclose(verifier->handle);
779		verifier->handle = NULL;
780		free(verifier);
781		if (imgfd >= 0) {
782			(void) close(imgfd);
783		}
784		verifier = NULL;
785	}
786
787	return (rv);
788}
789
790/*
791 * cycles through the global list of plugins to find
792 * each flashable device, which is added to fw_devices
793 *
794 * Each plugin's identify routine must allocated storage
795 * as required.
796 *
797 * Each plugin's identify routine must return
798 * FWFLASH_FAILURE if it cannot find any devices
799 * which it handles.
800 */
801static int
802flash_device_list()
803{
804	int rv = FWFLASH_FAILURE;
805	int startidx = 0;
806	int sumrv = 0;
807	struct pluginlist *plugins;
808
809	/* we open rootnode here, and close it in fwflash_intr */
810	if ((rootnode = di_init("/", DINFOCPYALL|DINFOFORCE)) == DI_NODE_NIL) {
811		logmsg(MSG_ERROR,
812		    gettext("Unable to take device tree snapshot: %s\n"),
813		    strerror(errno));
814		return (rv);
815	}
816
817	if ((fw_devices = calloc(1, sizeof (struct devicelist))) == NULL) {
818		logmsg(MSG_ERROR,
819		    gettext("Unable to malloc %d bytes while "
820		    "trying to find devices: %s\n"),
821		    sizeof (struct devicelist), strerror(errno));
822		return (FWFLASH_FAILURE);
823	}
824
825	/* CONSTCOND */
826	TAILQ_INIT(fw_devices);
827
828	TAILQ_FOREACH(plugins, fw_pluginlist, nextplugin) {
829		self = plugins->plugin;
830		rv = plugins->plugin->fw_identify(startidx);
831
832		logmsg(MSG_INFO,
833		    gettext("fwflash:flash_device_list() got %d from "
834		    "identify routine\n"), rv);
835
836		/* only bump startidx if we've found at least one device */
837		if (rv == FWFLASH_SUCCESS) {
838			startidx += 100;
839			sumrv++;
840		} else {
841			logmsg(MSG_INFO,
842			    gettext("No flashable devices attached with "
843			    "the %s driver in this system\n"),
844			    plugins->drvname);
845		}
846	}
847
848	if (sumrv > 0)
849		rv = FWFLASH_SUCCESS;
850
851	return (rv);
852}
853
854static int
855fwflash_list_fw(char *class)
856{
857	int rv = 0;
858	struct devicelist *curdev;
859	int header = 1;
860
861	TAILQ_FOREACH(curdev, fw_devices, nextdev) {
862
863		/* we're either class-conscious, or we're not */
864		if (((class != NULL) &&
865		    ((strncmp(curdev->classname, "ALL", 3) == 0) ||
866		    (strcmp(curdev->classname, class) == 0))) ||
867		    (class == NULL)) {
868
869			if (header != 0) {
870				(void) fprintf(stdout,
871				    gettext("List of available devices:\n"));
872				header--;
873			}
874			/*
875			 * If any plugin's fw_devinfo() function returns
876			 * FWFLASH_FAILURE then we want to keep track of
877			 * it. _Most_ plugins should always return
878			 * FWFLASH_SUCCESS from this function. The only
879			 * exception known at this point is the tavor plugin.
880			 */
881			rv += curdev->plugin->fw_devinfo(curdev);
882		}
883	}
884	return (rv);
885}
886
887static int
888fwflash_update(char *device, char *filename, int flags)
889{
890
891	int rv = FWFLASH_FAILURE;
892	int needsfree = 0;
893	int found = 0;
894	struct devicelist *curdev;
895	char *realfile;
896
897	/*
898	 * Here's how we operate:
899	 *
900	 * We perform some basic checks on the args, then walk
901	 * through the device list looking for the device which
902	 * matches. We then load the appropriate verifier for the
903	 * image file and device, verify the image, then call the
904	 * fw_writefw() function of the appropriate plugin.
905	 *
906	 * There is no "force" flag to enable you to flash a firmware
907	 * image onto an incompatible device because the verifier
908	 * will return FWFLASH_FAILURE if the image doesn't match.
909	 */
910
911	/* new firmware filename and device desc */
912	if (filename == NULL) {
913		logmsg(MSG_ERROR,
914		    gettext("Invalid firmware filename (null)\n"));
915		return (FWFLASH_FAILURE);
916	}
917
918	if (device == NULL) {
919		logmsg(MSG_ERROR,
920		    gettext("Invalid device requested (null)\n"));
921		return (FWFLASH_FAILURE);
922	}
923
924	if ((realfile = calloc(1, PATH_MAX + 1)) == NULL) {
925		logmsg(MSG_ERROR,
926		    gettext("Unable to allocate space for device "
927		    "filename, operation might fail if %s is"
928		    "a symbolic link\n"),
929		    device);
930		realfile = device;
931	} else {
932		/*
933		 * If realpath() succeeds, then we have a valid
934		 * device filename in realfile.
935		 */
936		if (realpath(device, realfile) == NULL) {
937			logmsg(MSG_ERROR,
938			    gettext("Unable to resolve device filename"
939			    ": %s\n"),
940			    strerror(errno));
941			/* tidy up */
942			free(realfile);
943			/* realpath didn't succeed, use fallback */
944			realfile = device;
945		} else {
946			needsfree = 1;
947		}
948	}
949
950	logmsg(MSG_INFO,
951	    gettext("fwflash_update: fw_filename (%s) device (%s)\n"),
952	    filename, device);
953
954	TAILQ_FOREACH(curdev, fw_devices, nextdev) {
955		if (strcmp(curdev->access_devname, realfile) == 0) {
956			found++;
957			rv = fwflash_load_verifier(curdev->drvname,
958			    curdev->ident->vid, filename);
959			if (rv == FWFLASH_FAILURE) {
960				logmsg(MSG_ERROR,
961				    gettext("Unable to load verifier "
962				    "for device %s\n"),
963				    curdev->access_devname);
964				return (FWFLASH_FAILURE);
965			}
966			rv = verifier->vendorvrfy(curdev);
967			if (rv == FWFLASH_FAILURE) {
968				/* the verifier prints a message */
969				logmsg(MSG_INFO,
970				    "verifier (%s) for %s :: %s returned "
971				    "FWFLASH_FAILURE\n",
972				    verifier->filename,
973				    filename, curdev->access_devname);
974				return (rv);
975			}
976
977			if (((flags & FWFLASH_YES_FLAG) == FWFLASH_YES_FLAG) ||
978			    (rv = confirm_target(curdev, filename)) ==
979			    FWFLASH_YES_FLAG) {
980				logmsg(MSG_INFO,
981				    "about to flash using plugin %s\n",
982				    curdev->plugin->filename);
983				rv = curdev->plugin->fw_writefw(curdev,
984				    filename);
985				if (rv == FWFLASH_FAILURE) {
986					logmsg(MSG_ERROR,
987					    gettext("Failed to flash "
988					    "firmware file %s on "
989					    "device %s: %d\n"),
990					    filename,
991					    curdev->access_devname, rv);
992				}
993			} else {
994				logmsg(MSG_ERROR,
995				    gettext("Flash operation not confirmed "
996				    "by user\n"),
997				    curdev->access_devname);
998				rv = FWFLASH_FAILURE;
999			}
1000		}
1001	}
1002
1003	if (!found)
1004		/* report the same device that the user passed in */
1005		logmsg(MSG_ERROR,
1006		    gettext("Device %s does not appear "
1007		    "to be flashable\n"),
1008		    ((strncmp(device, realfile, strlen(device)) == 0) ?
1009		    realfile : device));
1010
1011	if (needsfree)
1012		free(realfile);
1013
1014	return (rv);
1015}
1016
1017/*
1018 * We validate that the device path is in our global device list and
1019 * that the filename exists, then palm things off to the relevant plugin.
1020 */
1021static int
1022fwflash_read_file(char *device, char *filename)
1023{
1024	struct devicelist *curdev;
1025	int rv;
1026	int found = 0;
1027
1028	/* new firmware filename and device desc */
1029
1030	TAILQ_FOREACH(curdev, fw_devices, nextdev) {
1031		if (strncmp(curdev->access_devname, device,
1032		    MAXPATHLEN) == 0) {
1033			rv = curdev->plugin->fw_readfw(curdev, filename);
1034
1035			if (rv != FWFLASH_SUCCESS)
1036				logmsg(MSG_ERROR,
1037				    gettext("Unable to write out firmware "
1038				    "image for %s to file %s\n"),
1039				    curdev->access_devname, filename);
1040			found++;
1041		}
1042
1043	}
1044
1045	if (!found) {
1046		logmsg(MSG_ERROR,
1047		    gettext("No device matching %s was found.\n"),
1048		    device);
1049		rv = FWFLASH_FAILURE;
1050	}
1051
1052	return (rv);
1053}
1054
1055static void
1056fwflash_usage(char *arg)
1057{
1058
1059	(void) fprintf(stderr, "\n");
1060	if (arg != NULL) {
1061		logmsg(MSG_ERROR,
1062		    gettext("Invalid argument (%s) supplied\n"), arg);
1063	}
1064
1065	(void) fprintf(stderr, "\n");
1066
1067	(void) fprintf(stdout, gettext("Usage:\n\t"));
1068	(void) fprintf(stdout, gettext("fwflash [-l [-c device_class "
1069	    "| ALL]] | [-v] | [-h]\n\t"));
1070	(void) fprintf(stdout, gettext("fwflash [-f file1,file2,file3"
1071	    ",... | -r file] [-y] -d device_path\n\n"));
1072	(void) fprintf(stdout, "\n"); /* workaround for xgettext */
1073
1074	(void) fprintf(stdout,
1075	    gettext("\t-l\t\tlist flashable devices in this system\n"
1076	    "\t-c device_class limit search to a specific class\n"
1077	    "\t\t\teg IB for InfiniBand, ses for SCSI Enclosures\n"
1078	    "\t-v\t\tprint version number of fwflash utility\n"
1079	    "\t-h\t\tprint this usage message\n\n"));
1080	(void) fprintf(stdout,
1081	    gettext("\t-f file1,file2,file3,...\n"
1082	    "\t\t\tfirmware image file list to flash\n"
1083	    "\t-r file\t\tfile to dump device firmware to\n"
1084	    "\t-y\t\tanswer Yes/Y/y to prompts\n"
1085	    "\t-d device_path\tpathname of device to be flashed\n\n"));
1086
1087	(void) fprintf(stdout,
1088	    gettext("\tIf -d device_path is specified, then one of -f "
1089	    "<files>\n"
1090	    "\tor -r <file> must also be specified\n\n"));
1091
1092	(void) fprintf(stdout,
1093	    gettext("\tIf multiple firmware images are required to be "
1094	    "flashed\n"
1095	    "\tthey must be listed together, separated by commas. The\n"
1096	    "\timages will be flashed in the order specified.\n\n"));
1097
1098	(void) fprintf(stdout, "\n");
1099}
1100
1101static void
1102fwflash_version(void)
1103{
1104	(void) fprintf(stdout, gettext("\n%s: "), FWFLASH_PROG_NAME);
1105	(void) fprintf(stdout, gettext("version %s\n"),
1106	    FWFLASH_VERSION);
1107}
1108
1109static void
1110fwflash_intr(int sig)
1111{
1112
1113	struct devicelist *thisdev;
1114	struct pluginlist *thisplug;
1115
1116	(void) signal(SIGINT, SIG_IGN);
1117	(void) signal(SIGTERM, SIG_IGN);
1118	(void) signal(SIGABRT, SIG_IGN);
1119
1120	if (fwflash_in_write) {
1121		(void) fprintf(stderr,
1122		    gettext("WARNING: firmware image may be corrupted\n\t"));
1123		(void) fprintf(stderr,
1124		    gettext("Reflash firmware before rebooting!\n"));
1125	}
1126
1127	if (sig > 0) {
1128		(void) logmsg(MSG_ERROR, gettext("\n"));
1129		(void) logmsg(MSG_ERROR,
1130		    gettext("fwflash exiting due to signal (%d)\n"), sig);
1131	}
1132
1133	/*
1134	 * we need to close everything down properly, so
1135	 * call the plugin closure routines
1136	 */
1137	if (fw_devices != NULL) {
1138		TAILQ_FOREACH(thisdev, fw_devices, nextdev) {
1139			if (thisdev->plugin->fw_cleanup != NULL) {
1140				/*
1141				 * If we've got a cleanup routine, it
1142				 * cleans up _everything_ for thisdev
1143				 */
1144				thisdev->plugin->fw_cleanup(thisdev);
1145			} else {
1146				/* free the components first */
1147				free(thisdev->access_devname);
1148				free(thisdev->drvname);
1149				free(thisdev->classname);
1150				if (thisdev->ident != NULL)
1151					free(thisdev->ident);
1152				/* We don't free address[] for old plugins */
1153				thisdev->ident = NULL;
1154				thisdev->plugin = NULL;
1155			}
1156			/* CONSTCOND */
1157			TAILQ_REMOVE(fw_devices, thisdev, nextdev);
1158		}
1159	}
1160
1161	if (fw_pluginlist != NULL) {
1162		TAILQ_FOREACH(thisplug, fw_pluginlist, nextplugin) {
1163			free(thisplug->filename);
1164			free(thisplug->drvname);
1165			free(thisplug->plugin->filename);
1166			free(thisplug->plugin->drvname);
1167			thisplug->filename = NULL;
1168			thisplug->drvname = NULL;
1169			thisplug->plugin->filename = NULL;
1170			thisplug->plugin->drvname = NULL;
1171			thisplug->plugin->fw_readfw = NULL;
1172			thisplug->plugin->fw_writefw = NULL;
1173			thisplug->plugin->fw_identify = NULL;
1174			thisplug->plugin->fw_devinfo = NULL;
1175			thisplug->plugin->fw_cleanup = NULL;
1176			(void) dlclose(thisplug->plugin->handle);
1177			thisplug->plugin->handle = NULL;
1178			free(thisplug->plugin);
1179			thisplug->plugin = NULL;
1180			/* CONSTCOND */
1181			TAILQ_REMOVE(fw_pluginlist, thisplug, nextplugin);
1182		}
1183	}
1184
1185	if (verifier != NULL) {
1186		free(verifier->filename);
1187		free(verifier->vendor);
1188		free(verifier->imgfile);
1189		free(verifier->fwimage);
1190		verifier->filename = NULL;
1191		verifier->vendor = NULL;
1192		verifier->vendorvrfy = NULL;
1193		verifier->imgfile = NULL;
1194		verifier->fwimage = NULL;
1195		(void) dlclose(verifier->handle);
1196		verifier->handle = NULL;
1197		free(verifier);
1198	}
1199	di_fini(rootnode);
1200
1201	if (sig > 0)
1202		exit(FWFLASH_FAILURE);
1203}
1204
1205static void
1206fwflash_handle_signals(void)
1207{
1208	if (signal(SIGINT, fwflash_intr) == SIG_ERR) {
1209		perror("signal");
1210		exit(FWFLASH_FAILURE);
1211	}
1212
1213	if (signal(SIGTERM, fwflash_intr) == SIG_ERR) {
1214		perror("signal");
1215		exit(FWFLASH_FAILURE);
1216	}
1217}
1218
1219static int
1220confirm_target(struct devicelist *thisdev, char *file)
1221{
1222	int resp;
1223
1224	(void) fflush(stdin);
1225	(void) printf(gettext("About to update firmware on %s\n"),
1226	    thisdev->access_devname);
1227	(void) printf(gettext("with file %s.\n"
1228	    "Do you want to continue? (Y/N): "), file);
1229
1230	resp = getchar();
1231	if (resp == 'Y' || resp == 'y') {
1232		return (FWFLASH_YES_FLAG);
1233	} else {
1234		logmsg(MSG_INFO, "flash operation NOT confirmed.\n");
1235	}
1236
1237	(void) fflush(stdin);
1238	return (FWFLASH_FAILURE);
1239}
1240
1241int
1242get_fileopts(char *options)
1243{
1244
1245	int i;
1246	char *files;
1247
1248	if (files = strtok(options, ",")) {
1249		/* we have more than one */
1250		if ((filelist[0] = calloc(1, MAXPATHLEN + 1)) == NULL) {
1251			logmsg(MSG_ERROR,
1252			    gettext("Unable to allocate space for "
1253			    "a firmware image filename\n"));
1254			return (FWFLASH_FAILURE);
1255		}
1256		(void) strlcpy(filelist[0], files, strlen(files) + 1);
1257		i = 1;
1258
1259		logmsg(MSG_INFO, "fwflash: filelist[0]: %s\n",
1260		    filelist[0]);
1261
1262
1263		while (files = strtok(NULL, ",")) {
1264			if ((filelist[i] = calloc(1, MAXPATHLEN + 1))
1265			    == NULL) {
1266				logmsg(MSG_ERROR,
1267				    gettext("Unable to allocate space for "
1268				    "a firmware image filename\n"));
1269				return (FWFLASH_FAILURE);
1270			}
1271			(void) strlcpy(filelist[i], files,
1272			    strlen(files) + 1);
1273			logmsg(MSG_INFO, "fwflash: filelist[%d]: %s\n",
1274			    i, filelist[i]);
1275			++i;
1276		}
1277	} else {
1278		if ((filelist[0] = calloc(1, MAXPATHLEN + 1)) == NULL) {
1279			logmsg(MSG_ERROR,
1280			    gettext("Unable to allocate space for "
1281			    "a firmware image filename\n"));
1282			return (FWFLASH_FAILURE);
1283		}
1284		(void) strlcpy(filelist[0], options, strlen(files) + 1);
1285		logmsg(MSG_INFO, "fwflash: filelist[0]: %s\n",
1286		    filelist[0]);
1287	}
1288	return (FWFLASH_SUCCESS);
1289}
1290
1291/*
1292 * code reuse - cheerfully borrowed from stmsboot_util.c
1293 */
1294void
1295logmsg(int severity, const char *msg, ...)
1296{
1297	va_list ap;
1298
1299	if ((severity > MSG_INFO) ||
1300	    ((severity == MSG_INFO) && (fwflash_debug > 0))) {
1301		(void) fprintf(stderr, "%s: ", FWFLASH_PROG_NAME);
1302		va_start(ap, msg);
1303		(void) vfprintf(stderr, msg, ap);
1304		va_end(ap);
1305	}
1306}
1307