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