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