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