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