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