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