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