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