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.8"
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), 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 				(void) memcpy(tempnm, plugdir->d_name,
403 				    MAXMODCONFNAME);
404 				(void) strlcpy(tmpplug->drvname,
405 				    strtok(tempnm, "."),
406 				    strlen(plugdir->d_name) + 1);
407 				free(tempnm);
408 			} else {
409 				(void) strlcpy(tmpplug->drvname,
410 				    (char *)sym, strlen(sym) + 1);
411 			}
412 		} else {
413 			CLOSEFREE();
414 			continue;
415 		}
416 		if ((sym = dlsym(tmpplug->handle, "fw_readfw"))
417 		    != NULL) {
418 			tmpplug->fw_readfw = (int (*)())sym;
419 		} else {
420 			CLOSEFREE();
421 			continue;
422 		}
423 		if ((sym = dlsym(tmpplug->handle, "fw_writefw"))
424 		    != NULL) {
425 			tmpplug->fw_writefw = (int (*)())sym;
426 		} else {
427 			CLOSEFREE();
428 			continue;
429 		}
430 
431 		if ((sym = dlsym(tmpplug->handle, "fw_identify"))
432 		    != NULL) {
433 			tmpplug->fw_identify =
434 			    (int (*)(int))sym;
435 		} else {
436 			CLOSEFREE();
437 			continue;
438 		}
439 		if ((sym = dlsym(tmpplug->handle, "fw_devinfo"))
440 		    != NULL) {
441 			tmpplug->fw_devinfo =
442 			    (int (*)(struct devicelist *))sym;
443 		} else {
444 			CLOSEFREE();
445 			continue;
446 		}
447 
448 		if ((sym = dlsym(tmpplug->handle, "plugin_version"))
449 		    != NULL) {
450 			if ((*(int *)sym) >= FWPLUGIN_VERSION_2) {
451 				if ((sym = dlsym(tmpplug->handle,
452 				    "fw_cleanup")) != NULL) {
453 					tmpplug->fw_cleanup =
454 					    (void (*)(struct devicelist *))sym;
455 				} else {
456 					logmsg(MSG_ERROR,
457 					    gettext("ERROR: v2 plugin (%s) "
458 					    "has no fw_cleanup function\n"),
459 					    tmpplug->filename);
460 					CLOSEFREE();
461 					continue;
462 				}
463 			} else {
464 				logmsg(MSG_INFO,
465 				    "Identification plugin %s defined "
466 				    "plugin_version < FWPLUGIN_VERSION_2 !");
467 			}
468 		}
469 
470 		if ((tmpelem->drvname = calloc(1, MAXMODCONFNAME))
471 		    == NULL) {
472 			logmsg(MSG_ERROR,
473 			    gettext("Unable to allocate space for a"
474 			    "drivername %s\n"),
475 			    tmpplug->drvname);
476 			return (FWFLASH_FAILURE);
477 		}
478 
479 		(void) strlcpy(tmpelem->drvname, tmpplug->drvname,
480 		    strlen(tmpplug->drvname) + 1);
481 
482 		if ((tmpelem->filename = calloc(1,
483 		    strlen(tmpplug->filename) + 1)) == NULL) {
484 			logmsg(MSG_ERROR,
485 			    gettext("Unable to allocate %d bytes for "
486 			    "filename %s\n"),
487 			    strlen(tmpplug->filename) + 1,
488 			    tmpplug->filename);
489 			return (FWFLASH_FAILURE);
490 		}
491 
492 		(void) strlcpy(tmpelem->filename, plugname,
493 		    strlen(plugname) + 1);
494 		tmpelem->plugin = tmpplug;
495 
496 		/* CONSTCOND */
497 		TAILQ_INSERT_TAIL(fw_pluginlist, tmpelem, nextplugin);
498 	}
499 
500 	if ((plugdir == NULL) && TAILQ_EMPTY(fw_pluginlist)) {
501 		return (FWFLASH_FAILURE);
502 	}
503 
504 	if (errno != 0) {
505 		logmsg(MSG_ERROR,
506 		    gettext("Error reading directory entry in %s\n"),
507 		    fwplugdirpath);
508 		rval = errno;
509 	}
510 
511 	free(fwplugdirpath);
512 	free(plugdir);
513 	(void) closedir(dirp);
514 	return (rval);
515 }
516 
517 /*
518  * fwflash_load_verifier dlload()s the appropriate firmware image
519  * verification plugin, and attaches the designated fwimg's fd to
520  * the vrfyplugin structure so we only have to load the image in
521  * one place.
522  */
523 int
524 fwflash_load_verifier(char *drv, char *vendorid, char *fwimg)
525 {
526 
527 	int rv = FWFLASH_FAILURE;
528 	int imgfd;
529 	char *fwvrfydirpath, *tempdirpath, *filename;
530 	char *clean; /* for the space-removed vid */
531 	struct stat fwstat;
532 	struct vrfyplugin *vrfy;
533 	void *vrfysym;
534 
535 	/*
536 	 * To make flashing multiple firmware images somewhat more
537 	 * efficient, we start this function by checking whether a
538 	 * verifier for this device has already been loaded. If it
539 	 * has been loaded, we replace the imgfile information, and
540 	 * then continue as if we were loading for the first time.
541 	 */
542 
543 	if (verifier != NULL) {
544 		verifier->imgsize = 0;
545 		verifier->flashbuf = 0; /* set by the verifier function */
546 
547 		if (verifier->imgfile != NULL) {
548 			free(verifier->imgfile);
549 			verifier->imgfile = NULL;
550 		}
551 
552 		if (verifier->fwimage != NULL) {
553 			free(verifier->fwimage);
554 			verifier->fwimage = NULL;
555 		}
556 	} else {
557 		if ((fwvrfydirpath = calloc(1, MAXPATHLEN + 1)) == NULL) {
558 			logmsg(MSG_ERROR,
559 			    gettext("Unable to allocate space for a firmware "
560 			    "verifier file(1)"));
561 			return (rv);
562 		}
563 
564 		if ((filename = calloc(1, MAXPATHLEN + 1)) == NULL) {
565 			logmsg(MSG_ERROR,
566 			    gettext("Unable to allocate space "
567 			    "for a firmware verifier file(2)"));
568 			free(fwvrfydirpath);
569 			return (rv);
570 		}
571 
572 		/*
573 		 * Since SCSI devices can have a vendor id of up to 8
574 		 * left-aligned and _space-padded_ characters, we first need to
575 		 * strip off any space characters before we try to make a
576 		 * filename out of it
577 		 */
578 		clean = strtok(vendorid, " ");
579 		if (clean == NULL) {
580 			/* invalid vendorid, something's really wrong */
581 			logmsg(MSG_ERROR,
582 			    gettext("Invalid vendorid (null) specified for "
583 			    "device\n"));
584 			free(filename);
585 			free(fwvrfydirpath);
586 			return (rv);
587 		}
588 
589 		tempdirpath = getenv("FWVERIFYPLUGINDIR");
590 
591 		if ((fwflash_debug > 0) && (tempdirpath != NULL)) {
592 			(void) strlcpy(fwvrfydirpath, tempdirpath,
593 			    strlen(tempdirpath) + 1);
594 		} else {
595 			(void) strlcpy(fwvrfydirpath, FWVERIFYPLUGINDIR,
596 			    strlen(FWVERIFYPLUGINDIR) + 1);
597 		}
598 
599 		if ((vrfy = calloc(1, sizeof (struct vrfyplugin))) == NULL) {
600 			logmsg(MSG_ERROR,
601 			    gettext("Unable to allocate space "
602 			    "for a firmware verifier structure"));
603 			free(filename);
604 			free(fwvrfydirpath);
605 			return (rv);
606 		}
607 
608 		errno = 0; /* false positive removal */
609 
610 		(void) snprintf(filename, MAXPATHLEN, "%s/%s-%s.so",
611 		    fwvrfydirpath, drv, clean);
612 		if ((vrfy->handle = dlopen(filename, RTLD_NOW)) == NULL) {
613 			logmsg(MSG_INFO, gettext(dlerror()));
614 			logmsg(MSG_INFO,
615 			    gettext("\nUnable to open verification plugin "
616 			    "%s. Looking for %s-GENERIC plugin instead.\n"),
617 			    filename, drv);
618 
619 			/* Try the drv-GENERIC.so form, _then_ die */
620 			bzero(filename, strlen(filename) + 1);
621 			(void) snprintf(filename, MAXPATHLEN,
622 			    "%s/%s-GENERIC.so", fwvrfydirpath, drv);
623 
624 			if ((vrfy->handle = dlopen(filename, RTLD_NOW))
625 			    == NULL) {
626 				logmsg(MSG_INFO, gettext(dlerror()));
627 				logmsg(MSG_ERROR,
628 				    gettext("\nUnable to open either "
629 				    "verification plugin %s/%s-%s.so or "
630 				    "generic plugin %s.\nUnable to verify "
631 				    "firmware image. Aborting.\n"),
632 				    fwvrfydirpath, drv, clean, filename);
633 				free(filename);
634 				free(fwvrfydirpath);
635 				return (rv);
636 			}
637 		}
638 
639 		if ((vrfy->filename = calloc(1, strlen(filename) + 1))
640 		    == NULL) {
641 			logmsg(MSG_ERROR,
642 			    gettext("Unable to allocate space to store "
643 			    "a verifier filename\n"));
644 			free(filename);
645 			free(fwvrfydirpath);
646 			free(vrfy->handle);
647 			return (rv);
648 		}
649 		(void) strlcpy(vrfy->filename, filename, strlen(filename) + 1);
650 
651 		if ((vrfysym = dlsym(vrfy->handle, "vendorvrfy")) == NULL) {
652 			logmsg(MSG_ERROR,
653 			    gettext("%s is an invalid firmware verification "
654 			    "plugin."), filename);
655 			(void) dlclose(vrfy->handle);
656 			free(filename);
657 			free(fwvrfydirpath);
658 			free(vrfy);
659 			return (rv);
660 		} else {
661 			vrfy->vendorvrfy =
662 			    (int (*)(struct devicelist *))vrfysym;
663 		}
664 
665 		vrfysym = dlsym(vrfy->handle, "vendor");
666 
667 		if (vrfysym == NULL) {
668 			logmsg(MSG_ERROR,
669 			    gettext("Invalid vendor (null) in verification "
670 			    "plugin %s\n"), filename);
671 			(void) dlclose(vrfy->handle);
672 			free(vrfy);
673 			return (rv);
674 		} else {
675 			if (strncmp(vendorid, (char *)vrfysym,
676 			    strlen(vendorid)) != 0) {
677 				logmsg(MSG_INFO,
678 				    "Using a sym-linked (%s -> %s) "
679 				    "verification plugin\n",
680 				    vendorid, vrfysym);
681 				vrfy->vendor = calloc(1, strlen(vendorid) + 1);
682 			} else {
683 				vrfy->vendor = calloc(1, strlen(vrfysym) + 1);
684 			}
685 			(void) strlcpy(vrfy->vendor, (char *)vrfysym,
686 			    strlen(vendorid) + 1);
687 		}
688 
689 		verifier = vrfy; /* a convenience variable */
690 		free(filename);
691 		free(fwvrfydirpath);
692 	}
693 
694 	/*
695 	 * We don't do any verification that the fw image file is in
696 	 * an approved location, but it's easy enough to modify this
697 	 * function to do so. The verification plugin should provide
698 	 * sufficient protection.
699 	 */
700 
701 	if ((imgfd = open(fwimg, O_RDONLY)) < 0) {
702 		logmsg(MSG_ERROR,
703 		    gettext("Unable to open designated firmware "
704 		    "image file %s: %s\n"),
705 		    (fwimg != NULL) ? fwimg : "(null)",
706 		    strerror(errno));
707 		rv = FWFLASH_FAILURE;
708 		goto cleanup;
709 	}
710 
711 	if (stat(fwimg, &fwstat) == -1) {
712 		logmsg(MSG_ERROR,
713 		    gettext("Unable to stat() firmware image file "
714 		    "%s: %s\n"),
715 		    fwimg, strerror(errno));
716 		rv = FWFLASH_FAILURE;
717 		goto cleanup;
718 	} else {
719 		verifier->imgsize = fwstat.st_size;
720 		if ((verifier->fwimage = calloc(1, verifier->imgsize))
721 		    == NULL) {
722 			logmsg(MSG_ERROR,
723 			    gettext("Unable to load firmware image "
724 			    "%s: %s\n"),
725 			    fwimg, strerror(errno));
726 			rv = FWFLASH_FAILURE;
727 			goto cleanup;
728 		}
729 	}
730 
731 	errno = 0;
732 	if ((rv = read(imgfd, verifier->fwimage,
733 	    (size_t)verifier->imgsize)) < verifier->imgsize) {
734 		/* we haven't read enough data, bail */
735 		logmsg(MSG_ERROR,
736 		    gettext("Failed to read sufficient data "
737 		    "(got %d bytes, expected %d bytes) from "
738 		    "firmware image file %s: %s\n"),
739 		    rv, verifier->imgsize,
740 		    verifier->filename, strerror(errno));
741 		rv = FWFLASH_FAILURE;
742 	} else {
743 		rv = FWFLASH_SUCCESS;
744 	}
745 
746 	if ((verifier->imgfile = calloc(1, strlen(fwimg) + 1)) == NULL) {
747 		logmsg(MSG_ERROR,
748 		    gettext("Unable to save name of firmware image\n"));
749 		rv = FWFLASH_FAILURE;
750 	} else {
751 		(void) strlcpy(verifier->imgfile, fwimg, strlen(fwimg) + 1);
752 	}
753 
754 	if (rv != FWFLASH_SUCCESS) {
755 		/* cleanup and let's get outta here */
756 cleanup:
757 		free(verifier->filename);
758 		free(verifier->vendor);
759 
760 		if (!(fwflash_arg_list & FWFLASH_READ_FLAG) &&
761 		    verifier->fwimage)
762 			free(verifier->fwimage);
763 
764 		verifier->filename = NULL;
765 		verifier->vendor = NULL;
766 		verifier->vendorvrfy = NULL;
767 		verifier->fwimage = NULL;
768 		(void) dlclose(verifier->handle);
769 		verifier->handle = NULL;
770 		free(verifier);
771 		if (imgfd >= 0) {
772 			(void) close(imgfd);
773 		}
774 		verifier = NULL;
775 	}
776 
777 	return (rv);
778 }
779 
780 /*
781  * cycles through the global list of plugins to find
782  * each flashable device, which is added to fw_devices
783  *
784  * Each plugin's identify routine must allocated storage
785  * as required.
786  *
787  * Each plugin's identify routine must return
788  * FWFLASH_FAILURE if it cannot find any devices
789  * which it handles.
790  */
791 static int
792 flash_device_list()
793 {
794 	int rv = FWFLASH_FAILURE;
795 	int startidx = 0;
796 	int sumrv = 0;
797 	struct pluginlist *plugins;
798 
799 	/* we open rootnode here, and close it in fwflash_intr */
800 	if ((rootnode = di_init("/", DINFOCPYALL|DINFOFORCE)) == DI_NODE_NIL) {
801 		logmsg(MSG_ERROR,
802 		    gettext("Unable to take device tree snapshot: %s\n"),
803 		    strerror(errno));
804 		return (rv);
805 	}
806 
807 	if ((fw_devices = calloc(1, sizeof (struct devicelist))) == NULL) {
808 		logmsg(MSG_ERROR,
809 		    gettext("Unable to malloc %d bytes while "
810 		    "trying to find devices: %s\n"),
811 		    sizeof (struct devicelist), strerror(errno));
812 		return (FWFLASH_FAILURE);
813 	}
814 
815 	/* CONSTCOND */
816 	TAILQ_INIT(fw_devices);
817 
818 	TAILQ_FOREACH(plugins, fw_pluginlist, nextplugin) {
819 		self = plugins->plugin;
820 		rv = plugins->plugin->fw_identify(startidx);
821 
822 		logmsg(MSG_INFO,
823 		    gettext("fwflash:flash_device_list() got %d from "
824 		    "identify routine\n"), rv);
825 
826 		/* only bump startidx if we've found at least one device */
827 		if (rv == FWFLASH_SUCCESS) {
828 			startidx += 100;
829 			sumrv++;
830 		} else {
831 			logmsg(MSG_INFO,
832 			    gettext("No flashable devices attached with "
833 			    "the %s driver in this system\n"),
834 			    plugins->drvname);
835 		}
836 	}
837 
838 	if (sumrv > 0)
839 		rv = FWFLASH_SUCCESS;
840 
841 	return (rv);
842 }
843 
844 static int
845 fwflash_list_fw(char *class)
846 {
847 	int rv = 0;
848 	struct devicelist *curdev;
849 	int header = 1;
850 
851 	TAILQ_FOREACH(curdev, fw_devices, nextdev) {
852 
853 		/* we're either class-conscious, or we're not */
854 		if (((class != NULL) &&
855 		    ((strncmp(curdev->classname, "ALL", 3) == 0) ||
856 		    (strcmp(curdev->classname, class) == 0))) ||
857 		    (class == NULL)) {
858 
859 			if (header != 0) {
860 				(void) fprintf(stdout,
861 				    gettext("List of available devices:\n"));
862 				header--;
863 			}
864 			/*
865 			 * If any plugin's fw_devinfo() function returns
866 			 * FWFLASH_FAILURE then we want to keep track of
867 			 * it. _Most_ plugins should always return
868 			 * FWFLASH_SUCCESS from this function. The only
869 			 * exception known at this point is the tavor plugin.
870 			 */
871 			rv += curdev->plugin->fw_devinfo(curdev);
872 		}
873 	}
874 	return (rv);
875 }
876 
877 static int
878 fwflash_update(char *device, char *filename, int flags)
879 {
880 
881 	int rv = FWFLASH_FAILURE;
882 	int needsfree = 0;
883 	int found = 0;
884 	struct devicelist *curdev;
885 	char *realfile;
886 
887 	/*
888 	 * Here's how we operate:
889 	 *
890 	 * We perform some basic checks on the args, then walk
891 	 * through the device list looking for the device which
892 	 * matches. We then load the appropriate verifier for the
893 	 * image file and device, verify the image, then call the
894 	 * fw_writefw() function of the appropriate plugin.
895 	 *
896 	 * There is no "force" flag to enable you to flash a firmware
897 	 * image onto an incompatible device because the verifier
898 	 * will return FWFLASH_FAILURE if the image doesn't match.
899 	 */
900 
901 	/* new firmware filename and device desc */
902 	if (filename == NULL) {
903 		logmsg(MSG_ERROR,
904 		    gettext("Invalid firmware filename (null)\n"));
905 		return (FWFLASH_FAILURE);
906 	}
907 
908 	if (device == NULL) {
909 		logmsg(MSG_ERROR,
910 		    gettext("Invalid device requested (null)\n"));
911 		return (FWFLASH_FAILURE);
912 	}
913 
914 	if ((realfile = calloc(1, PATH_MAX + 1)) == NULL) {
915 		logmsg(MSG_ERROR,
916 		    gettext("Unable to allocate space for device "
917 		    "filename, operation might fail if %s is"
918 		    "a symbolic link\n"),
919 		    device);
920 		realfile = device;
921 	} else {
922 		/*
923 		 * If realpath() succeeds, then we have a valid
924 		 * device filename in realfile.
925 		 */
926 		if (realpath(device, realfile) == NULL) {
927 			logmsg(MSG_ERROR,
928 			    gettext("Unable to resolve device filename"
929 			    ": %s\n"),
930 			    strerror(errno));
931 			/* tidy up */
932 			free(realfile);
933 			/* realpath didn't succeed, use fallback */
934 			realfile = device;
935 		} else {
936 			needsfree = 1;
937 		}
938 	}
939 
940 	logmsg(MSG_INFO,
941 	    gettext("fwflash_update: fw_filename (%s) device (%s)\n"),
942 	    filename, device);
943 
944 	TAILQ_FOREACH(curdev, fw_devices, nextdev) {
945 		if (strcmp(curdev->access_devname, realfile) == 0) {
946 			found++;
947 			rv = fwflash_load_verifier(curdev->drvname,
948 			    curdev->ident->vid, filename);
949 			if (rv == FWFLASH_FAILURE) {
950 				logmsg(MSG_ERROR,
951 				    gettext("Unable to load verifier "
952 				    "for device %s\n"),
953 				    curdev->access_devname);
954 				return (FWFLASH_FAILURE);
955 			}
956 			rv = verifier->vendorvrfy(curdev);
957 			if (rv == FWFLASH_FAILURE) {
958 				/* the verifier prints a message */
959 				logmsg(MSG_INFO,
960 				    "verifier (%s) for %s :: %s returned "
961 				    "FWFLASH_FAILURE\n",
962 				    verifier->filename,
963 				    filename, curdev->access_devname);
964 				return (rv);
965 			}
966 
967 			if (((flags & FWFLASH_YES_FLAG) == FWFLASH_YES_FLAG) ||
968 			    (rv = confirm_target(curdev, filename)) ==
969 			    FWFLASH_YES_FLAG) {
970 				logmsg(MSG_INFO,
971 				    "about to flash using plugin %s\n",
972 				    curdev->plugin->filename);
973 				rv = curdev->plugin->fw_writefw(curdev,
974 				    filename);
975 				if (rv == FWFLASH_FAILURE) {
976 					logmsg(MSG_ERROR,
977 					    gettext("Failed to flash "
978 					    "firmware file %s on "
979 					    "device %s: %d\n"),
980 					    filename,
981 					    curdev->access_devname, rv);
982 				}
983 			} else {
984 				logmsg(MSG_ERROR,
985 				    gettext("Flash operation not confirmed "
986 				    "by user\n"),
987 				    curdev->access_devname);
988 				rv = FWFLASH_FAILURE;
989 			}
990 		}
991 	}
992 
993 	if (!found)
994 		/* report the same device that the user passed in */
995 		logmsg(MSG_ERROR,
996 		    gettext("Device %s does not appear "
997 		    "to be flashable\n"),
998 		    ((strncmp(device, realfile, strlen(device)) == 0) ?
999 		    realfile : device));
1000 
1001 	if (needsfree)
1002 		free(realfile);
1003 
1004 	return (rv);
1005 }
1006 
1007 /*
1008  * We validate that the device path is in our global device list and
1009  * that the filename exists, then palm things off to the relevant plugin.
1010  */
1011 static int
1012 fwflash_read_file(char *device, char *filename)
1013 {
1014 	struct devicelist *curdev;
1015 	int rv;
1016 	int found = 0;
1017 
1018 	/* new firmware filename and device desc */
1019 
1020 	TAILQ_FOREACH(curdev, fw_devices, nextdev) {
1021 		if (strncmp(curdev->access_devname, device,
1022 		    MAXPATHLEN) == 0) {
1023 			rv = curdev->plugin->fw_readfw(curdev, filename);
1024 
1025 			if (rv != FWFLASH_SUCCESS)
1026 				logmsg(MSG_ERROR,
1027 				    gettext("Unable to write out firmware "
1028 				    "image for %s to file %s\n"),
1029 				    curdev->access_devname, filename);
1030 			found++;
1031 		}
1032 
1033 	}
1034 
1035 	if (!found) {
1036 		logmsg(MSG_ERROR,
1037 		    gettext("No device matching %s was found.\n"),
1038 		    device);
1039 		rv = FWFLASH_FAILURE;
1040 	}
1041 
1042 	return (rv);
1043 }
1044 
1045 static void
1046 fwflash_usage(char *arg)
1047 {
1048 
1049 	(void) fprintf(stderr, "\n");
1050 	if (arg != NULL) {
1051 		logmsg(MSG_ERROR,
1052 		    gettext("Invalid argument (%s) supplied\n"), arg);
1053 	}
1054 
1055 	(void) fprintf(stderr, "\n");
1056 
1057 	(void) fprintf(stdout, gettext("Usage:\n\t"));
1058 	(void) fprintf(stdout, gettext("fwflash [-l [-c device_class "
1059 	    "| ALL]] | [-v] | [-h]\n\t"));
1060 	(void) fprintf(stdout, gettext("fwflash [-f file1,file2,file3"
1061 	    ",... | -r file] [-y] -d device_path\n\n"));
1062 	(void) fprintf(stdout, "\n"); /* workaround for xgettext */
1063 
1064 	(void) fprintf(stdout,
1065 	    gettext("\t-l\t\tlist flashable devices in this system\n"
1066 	    "\t-c device_class limit search to a specific class\n"
1067 	    "\t\t\teg IB for InfiniBand, ses for SCSI Enclosures\n"
1068 	    "\t-v\t\tprint version number of fwflash utility\n"
1069 	    "\t-h\t\tprint this usage message\n\n"));
1070 	(void) fprintf(stdout,
1071 	    gettext("\t-f file1,file2,file3,...\n"
1072 	    "\t\t\tfirmware image file list to flash\n"
1073 	    "\t-r file\t\tfile to dump device firmware to\n"
1074 	    "\t-y\t\tanswer Yes/Y/y to prompts\n"
1075 	    "\t-d device_path\tpathname of device to be flashed\n\n"));
1076 
1077 	(void) fprintf(stdout,
1078 	    gettext("\tIf -d device_path is specified, then one of -f "
1079 	    "<files>\n"
1080 	    "\tor -r <file> must also be specified\n\n"));
1081 
1082 	(void) fprintf(stdout,
1083 	    gettext("\tIf multiple firmware images are required to be "
1084 	    "flashed\n"
1085 	    "\tthey must be listed together, separated by commas. The\n"
1086 	    "\timages will be flashed in the order specified.\n\n"));
1087 
1088 	(void) fprintf(stdout, "\n");
1089 }
1090 
1091 static void
1092 fwflash_version(void)
1093 {
1094 	(void) fprintf(stdout, gettext("\n%s: "), FWFLASH_PROG_NAME);
1095 	(void) fprintf(stdout, gettext("version %s\n"),
1096 	    FWFLASH_VERSION);
1097 }
1098 
1099 static void
1100 fwflash_intr(int sig)
1101 {
1102 
1103 	struct devicelist *thisdev;
1104 	struct pluginlist *thisplug;
1105 
1106 	(void) signal(SIGINT, SIG_IGN);
1107 	(void) signal(SIGTERM, SIG_IGN);
1108 	(void) signal(SIGABRT, SIG_IGN);
1109 
1110 	if (fwflash_in_write) {
1111 		(void) fprintf(stderr,
1112 		    gettext("WARNING: firmware image may be corrupted\n\t"));
1113 		(void) fprintf(stderr,
1114 		    gettext("Reflash firmware before rebooting!\n"));
1115 	}
1116 
1117 	if (sig > 0) {
1118 		(void) logmsg(MSG_ERROR, gettext("\n"));
1119 		(void) logmsg(MSG_ERROR,
1120 		    gettext("fwflash exiting due to signal (%d)\n"), sig);
1121 	}
1122 
1123 	/*
1124 	 * we need to close everything down properly, so
1125 	 * call the plugin closure routines
1126 	 */
1127 	if (fw_devices != NULL) {
1128 		TAILQ_FOREACH(thisdev, fw_devices, nextdev) {
1129 			if (thisdev->plugin->fw_cleanup != NULL) {
1130 				/*
1131 				 * If we've got a cleanup routine, it
1132 				 * cleans up _everything_ for thisdev
1133 				 */
1134 				thisdev->plugin->fw_cleanup(thisdev);
1135 			} else {
1136 				/* free the components first */
1137 				free(thisdev->access_devname);
1138 				free(thisdev->drvname);
1139 				free(thisdev->classname);
1140 				if (thisdev->ident != NULL)
1141 					free(thisdev->ident);
1142 				/* We don't free address[] for old plugins */
1143 				thisdev->ident = NULL;
1144 				thisdev->plugin = NULL;
1145 			}
1146 			/* CONSTCOND */
1147 			TAILQ_REMOVE(fw_devices, thisdev, nextdev);
1148 		}
1149 	}
1150 
1151 	if (fw_pluginlist != NULL) {
1152 		TAILQ_FOREACH(thisplug, fw_pluginlist, nextplugin) {
1153 			free(thisplug->filename);
1154 			free(thisplug->drvname);
1155 			free(thisplug->plugin->filename);
1156 			free(thisplug->plugin->drvname);
1157 			thisplug->filename = NULL;
1158 			thisplug->drvname = NULL;
1159 			thisplug->plugin->filename = NULL;
1160 			thisplug->plugin->drvname = NULL;
1161 			thisplug->plugin->fw_readfw = NULL;
1162 			thisplug->plugin->fw_writefw = NULL;
1163 			thisplug->plugin->fw_identify = NULL;
1164 			thisplug->plugin->fw_devinfo = NULL;
1165 			thisplug->plugin->fw_cleanup = NULL;
1166 			(void) dlclose(thisplug->plugin->handle);
1167 			thisplug->plugin->handle = NULL;
1168 			free(thisplug->plugin);
1169 			thisplug->plugin = NULL;
1170 			/* CONSTCOND */
1171 			TAILQ_REMOVE(fw_pluginlist, thisplug, nextplugin);
1172 		}
1173 	}
1174 
1175 	if (verifier != NULL) {
1176 		free(verifier->filename);
1177 		free(verifier->vendor);
1178 		free(verifier->imgfile);
1179 		free(verifier->fwimage);
1180 		verifier->filename = NULL;
1181 		verifier->vendor = NULL;
1182 		verifier->vendorvrfy = NULL;
1183 		verifier->imgfile = NULL;
1184 		verifier->fwimage = NULL;
1185 		(void) dlclose(verifier->handle);
1186 		verifier->handle = NULL;
1187 		free(verifier);
1188 	}
1189 	di_fini(rootnode);
1190 
1191 	if (sig > 0)
1192 		exit(FWFLASH_FAILURE);
1193 }
1194 
1195 static void
1196 fwflash_handle_signals(void)
1197 {
1198 	if (signal(SIGINT, fwflash_intr) == SIG_ERR) {
1199 		perror("signal");
1200 		exit(FWFLASH_FAILURE);
1201 	}
1202 
1203 	if (signal(SIGTERM, fwflash_intr) == SIG_ERR) {
1204 		perror("signal");
1205 		exit(FWFLASH_FAILURE);
1206 	}
1207 }
1208 
1209 static int
1210 confirm_target(struct devicelist *thisdev, char *file)
1211 {
1212 	int resp;
1213 
1214 	(void) fflush(stdin);
1215 	(void) printf(gettext("About to update firmware on %s\n"),
1216 	    thisdev->access_devname);
1217 	(void) printf(gettext("with file %s.\n"
1218 	    "Do you want to continue? (Y/N): "), file);
1219 
1220 	resp = getchar();
1221 	if (resp == 'Y' || resp == 'y') {
1222 		return (FWFLASH_YES_FLAG);
1223 	} else {
1224 		logmsg(MSG_INFO, "flash operation NOT confirmed.\n");
1225 	}
1226 
1227 	(void) fflush(stdin);
1228 	return (FWFLASH_FAILURE);
1229 }
1230 
1231 int
1232 get_fileopts(char *options)
1233 {
1234 
1235 	int i;
1236 	char *files;
1237 
1238 	if (files = strtok(options, ",")) {
1239 		/* we have more than one */
1240 		if ((filelist[0] = calloc(1, MAXPATHLEN + 1)) == NULL) {
1241 			logmsg(MSG_ERROR,
1242 			    gettext("Unable to allocate space for "
1243 			    "a firmware image filename\n"));
1244 			return (FWFLASH_FAILURE);
1245 		}
1246 		(void) strlcpy(filelist[0], files, strlen(files) + 1);
1247 		i = 1;
1248 
1249 		logmsg(MSG_INFO, "fwflash: filelist[0]: %s\n",
1250 		    filelist[0]);
1251 
1252 
1253 		while (files = strtok(NULL, ",")) {
1254 			if ((filelist[i] = calloc(1, MAXPATHLEN + 1))
1255 			    == NULL) {
1256 				logmsg(MSG_ERROR,
1257 				    gettext("Unable to allocate space for "
1258 				    "a firmware image filename\n"));
1259 				return (FWFLASH_FAILURE);
1260 			}
1261 			(void) strlcpy(filelist[i], files,
1262 			    strlen(files) + 1);
1263 			logmsg(MSG_INFO, "fwflash: filelist[%d]: %s\n",
1264 			    i, filelist[i]);
1265 			++i;
1266 		}
1267 	} else {
1268 		if ((filelist[0] = calloc(1, MAXPATHLEN + 1)) == NULL) {
1269 			logmsg(MSG_ERROR,
1270 			    gettext("Unable to allocate space for "
1271 			    "a firmware image filename\n"));
1272 			return (FWFLASH_FAILURE);
1273 		}
1274 		(void) strlcpy(filelist[0], options, strlen(files) + 1);
1275 		logmsg(MSG_INFO, "fwflash: filelist[0]: %s\n",
1276 		    filelist[0]);
1277 	}
1278 	return (FWFLASH_SUCCESS);
1279 }
1280 
1281 /*
1282  * code reuse - cheerfully borrowed from stmsboot_util.c
1283  */
1284 void
1285 logmsg(int severity, const char *msg, ...)
1286 {
1287 	va_list ap;
1288 
1289 	if ((severity > MSG_INFO) ||
1290 	    ((severity == MSG_INFO) && (fwflash_debug > 0))) {
1291 		(void) fprintf(stderr, "%s: ", FWFLASH_PROG_NAME);
1292 		va_start(ap, msg);
1293 		(void) vfprintf(stderr, msg, ap);
1294 		va_end(ap);
1295 	}
1296 }
1297