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