xref: /illumos-gate/usr/src/cmd/ucodeadm/ucodeadm.c (revision b07ce584f4e28873b8927d7f83d9d3275a0f3ed2)
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 #include <sys/types.h>
27 #include <sys/processor.h>
28 #include <sys/ucode.h>
29 #include <sys/ioctl.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
32 #include <dirent.h>
33 #include <fcntl.h>
34 #include <errno.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <stdarg.h>
38 #include <string.h>
39 #include <errno.h>
40 #include <syslog.h>
41 #include <time.h>
42 #include <ctype.h>
43 #include <assert.h>
44 #include <libgen.h>
45 #include <locale.h>
46 #include <libintl.h>
47 
48 #define	UCODE_OPT_INSTALL	0x0001
49 #define	UCODE_OPT_UPDATE	0x0002
50 #define	UCODE_OPT_VERSION	0x0004
51 
52 static const char ucode_dev[] = "/dev/" UCODE_DRIVER_NAME;
53 
54 static char	*cmdname;
55 
56 static char	ucode_vendor_str[UCODE_MAX_VENDORS_NAME_LEN];
57 static char	ucode_install_path[] = UCODE_INSTALL_PATH;
58 
59 static int	ucode_debug = 0;
60 
61 static int ucode_convert_amd(const char *, uint8_t *, size_t);
62 static int ucode_convert_intel(const char *, uint8_t *, size_t);
63 
64 static ucode_errno_t ucode_gen_files_amd(uint8_t *, int, char *);
65 static ucode_errno_t ucode_gen_files_intel(uint8_t *, int, char *);
66 
67 static const struct ucode_ops ucode_ops[] = {
68 	{ ucode_convert_intel, ucode_gen_files_intel, ucode_validate_intel },
69 	{ ucode_convert_amd, ucode_gen_files_amd, ucode_validate_amd },
70 };
71 
72 const struct ucode_ops *ucode;
73 
74 static void
75 dprintf(const char *format, ...)
76 {
77 	if (ucode_debug) {
78 		va_list alist;
79 		va_start(alist, format);
80 		(void) vfprintf(stderr, format, alist);
81 		va_end(alist);
82 	}
83 }
84 
85 static void
86 usage(int verbose)
87 {
88 	(void) fprintf(stderr, gettext("usage:\n"));
89 	(void) fprintf(stderr, "\t%s -v\n", cmdname);
90 	if (verbose) {
91 		(void) fprintf(stderr,
92 		    gettext("\t\t Shows running microcode version.\n\n"));
93 	}
94 
95 	(void) fprintf(stderr, "\t%s -u microcode-file\n", cmdname);
96 	if (verbose) {
97 		(void) fprintf(stderr, gettext("\t\t Updates microcode to the "
98 		    "latest matching version found in\n"
99 		    "\t\t microcode-file.\n\n"));
100 	}
101 
102 	(void) fprintf(stderr, "\t%s -i [-R path] microcode-file\n", cmdname);
103 	if (verbose) {
104 		(void) fprintf(stderr, gettext("\t\t Installs microcode to be "
105 		    "used for subsequent boots.\n\n"));
106 		(void) fprintf(stderr, gettext("Microcode file name must start "
107 		    "with vendor name, such as \"intel\" or \"amd\".\n\n"));
108 	}
109 }
110 
111 static void
112 ucode_perror(const char *str, ucode_errno_t rc)
113 {
114 	(void) fprintf(stderr, "%s: %s: %s\n", cmdname, str,
115 	    errno == 0 ? ucode_strerror(rc) : strerror(errno));
116 	errno = 0;
117 }
118 
119 #define	LINESIZE	120	/* copyright line sometimes is longer than 80 */
120 
121 /*
122  * Convert text format microcode release into binary format.
123  * Return the number of characters read.
124  */
125 static int
126 ucode_convert_amd(const char *infile, uint8_t *buf, size_t size)
127 {
128 	int fd;
129 
130 	if (infile == NULL || buf == NULL || size == 0)
131 		return (0);
132 
133 	if ((fd = open(infile, O_RDONLY)) < 0)
134 		return (0);
135 
136 	size = read(fd, buf, size);
137 
138 	(void) close(fd);
139 
140 	return (size);
141 }
142 
143 static int
144 ucode_convert_intel(const char *infile, uint8_t *buf, size_t size)
145 {
146 	char	linebuf[LINESIZE];
147 	FILE	*infd = NULL;
148 	int	count = 0, firstline = 1;
149 	uint32_t *intbuf = (uint32_t *)(intptr_t)buf;
150 
151 	if (infile == NULL || buf == NULL || size == 0)
152 		return (0);
153 
154 	if ((infd = fopen(infile, "r")) == NULL)
155 		return (0);
156 
157 	while (fgets(linebuf, LINESIZE, infd)) {
158 
159 		/* Check to see if we are processing a binary file */
160 		if (firstline && !isprint(linebuf[0])) {
161 			if (fseek(infd, 0, SEEK_SET) == 0)
162 				count = fread(buf, 1, size, infd);
163 
164 			(void) fclose(infd);
165 			return (count);
166 		}
167 
168 		firstline = 0;
169 
170 		/* Skip blank lines */
171 		if (strlen(linebuf) == 1)
172 			continue;
173 
174 		/* Skip lines with all spaces or tabs */
175 		if (strcspn(linebuf, " \t") == 0)
176 			continue;
177 
178 		/* Text file.  Skip comments. */
179 		if (linebuf[0] == '/')
180 			continue;
181 
182 		if (sscanf(linebuf, "%x, %x, %x, %x",
183 		    &intbuf[count], &intbuf[count+1],
184 		    &intbuf[count+2], &intbuf[count+3]) != 4)
185 			break;
186 
187 		count += 4;
188 	}
189 
190 	(void) fclose(infd);
191 
192 	/*
193 	 * If we get here, we are processing a text format file
194 	 * where "count" is used to count the number of integers
195 	 * read.  Convert it to number of characters read.
196 	 */
197 	return (count * sizeof (int));
198 }
199 
200 /*
201  * Returns 0 if no need to update the link; -1 otherwise
202  */
203 static int
204 ucode_should_update_intel(char *filename, uint32_t new_rev)
205 {
206 	int		fd;
207 	struct stat	statbuf;
208 	ucode_header_intel_t header;
209 
210 	/*
211 	 * If the file or link already exists, check to see if
212 	 * it is necessary to update it.
213 	 */
214 	if (stat(filename, &statbuf) == 0) {
215 		if ((fd = open(filename, O_RDONLY)) == -1)
216 			return (-1);
217 
218 		if (read(fd, &header, sizeof (header)) == -1) {
219 			(void) close(fd);
220 			return (-1);
221 		}
222 
223 		(void) close(fd);
224 
225 		if (header.uh_rev >= new_rev)
226 			return (0);
227 	}
228 
229 	return (-1);
230 }
231 
232 /*
233  * Generate microcode binary files.  Must be called after ucode_validate().
234  */
235 static ucode_errno_t
236 ucode_gen_files_amd(uint8_t *buf, int size, char *path)
237 {
238 	/* LINTED: pointer alignment */
239 	uint32_t *ptr = (uint32_t *)buf;
240 	char common_path[PATH_MAX];
241 	int fd, count, counter;
242 	ucode_header_amd_t *uh;
243 	int last_cpu_rev = 0;
244 
245 
246 	/* write container file */
247 	(void) snprintf(common_path, PATH_MAX, "%s/%s", path, "container");
248 
249 	dprintf("path = %s\n", common_path);
250 	fd = open(common_path, O_WRONLY | O_CREAT | O_TRUNC,
251 	    S_IRUSR | S_IRGRP | S_IROTH);
252 
253 	if (fd == -1) {
254 		ucode_perror(common_path, EM_SYS);
255 		return (EM_SYS);
256 	}
257 
258 	if (write(fd, buf, size) != size) {
259 		(void) close(fd);
260 		ucode_perror(common_path, EM_SYS);
261 		return (EM_SYS);
262 	}
263 
264 	(void) close(fd);
265 
266 	/* skip over magic number & equivalence table header */
267 	ptr += 2; size -= 8;
268 
269 	count = *ptr++; size -= 4;
270 
271 	/* equivalence table uses special name */
272 	(void) snprintf(common_path, PATH_MAX, "%s/%s", path,
273 	    "equivalence-table");
274 
275 	for (;;) {
276 		dprintf("path = %s\n", common_path);
277 		fd = open(common_path, O_WRONLY | O_CREAT | O_TRUNC,
278 		    S_IRUSR | S_IRGRP | S_IROTH);
279 
280 		if (fd == -1) {
281 			ucode_perror(common_path, EM_SYS);
282 			return (EM_SYS);
283 		}
284 
285 		if (write(fd, ptr, count) != count) {
286 			(void) close(fd);
287 			ucode_perror(common_path, EM_SYS);
288 			return (EM_SYS);
289 		}
290 
291 		(void) close(fd);
292 		ptr += count >> 2; size -= count;
293 
294 		if (!size)
295 			return (EM_OK);
296 
297 		ptr++; size -= 4;
298 		count = *ptr++; size -= 4;
299 
300 		/* construct name from header information */
301 		uh = (ucode_header_amd_t *)ptr;
302 
303 		if (uh->uh_cpu_rev != last_cpu_rev) {
304 			last_cpu_rev = uh->uh_cpu_rev;
305 			counter = 0;
306 		}
307 
308 		(void) snprintf(common_path, PATH_MAX, "%s/%04X-%02X", path,
309 		    uh->uh_cpu_rev, counter++);
310 	}
311 }
312 
313 static ucode_errno_t
314 ucode_gen_files_intel(uint8_t *buf, int size, char *path)
315 {
316 	int	remaining;
317 	char	common_path[PATH_MAX];
318 	DIR	*dirp;
319 	struct dirent *dp;
320 
321 	(void) snprintf(common_path, PATH_MAX, "%s/%s", path,
322 	    UCODE_INSTALL_COMMON_PATH);
323 
324 	if (mkdirp(common_path, 0755) == -1 && errno != EEXIST) {
325 		ucode_perror(common_path, EM_SYS);
326 		return (EM_SYS);
327 	}
328 
329 	for (remaining = size; remaining > 0; ) {
330 		uint32_t	total_size, body_size, offset;
331 		char		firstname[PATH_MAX];
332 		char		name[PATH_MAX];
333 		int		i;
334 		uint8_t		*curbuf = &buf[size - remaining];
335 		ucode_header_intel_t	*uhp;
336 		ucode_ext_table_intel_t *extp;
337 
338 		uhp = (ucode_header_intel_t *)(intptr_t)curbuf;
339 
340 		total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size);
341 		body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size);
342 
343 		remaining -= total_size;
344 
345 		(void) snprintf(firstname, PATH_MAX, "%s/%08X-%02X",
346 		    common_path, uhp->uh_signature, uhp->uh_proc_flags);
347 		dprintf("firstname = %s\n", firstname);
348 
349 		if (ucode_should_update_intel(firstname, uhp->uh_rev) != 0) {
350 			int fd;
351 
352 			/* Remove the existing one first */
353 			(void) unlink(firstname);
354 
355 			if ((fd = open(firstname, O_WRONLY | O_CREAT | O_TRUNC,
356 			    S_IRUSR | S_IRGRP | S_IROTH)) == -1) {
357 				ucode_perror(firstname, EM_SYS);
358 				return (EM_SYS);
359 			}
360 
361 			if (write(fd, curbuf, total_size) != total_size) {
362 				(void) close(fd);
363 				ucode_perror(firstname, EM_SYS);
364 				return (EM_SYS);
365 			}
366 
367 			(void) close(fd);
368 		}
369 
370 		/*
371 		 * Only 1 byte of the proc_flags field is used, therefore
372 		 * we only need to match 8 potential platform ids.
373 		 */
374 		for (i = 0; i < 8; i++) {
375 			uint32_t platid = uhp->uh_proc_flags & (1 << i);
376 
377 			if (platid == 0 && uhp->uh_proc_flags != 0)
378 				continue;
379 
380 			(void) snprintf(name, PATH_MAX,
381 			    "%s/%08X-%02X", path, uhp->uh_signature, platid);
382 
383 			dprintf("proc_flags = %x, platid = %x, name = %s\n",
384 			    uhp->uh_proc_flags, platid, name);
385 
386 			if (ucode_should_update_intel(name, uhp->uh_rev) != 0) {
387 
388 				/* Remove the existing one first */
389 				(void) unlink(name);
390 
391 				if (link(firstname, name) == -1) {
392 					ucode_perror(name, EM_SYS);
393 					return (EM_SYS);
394 				}
395 			}
396 
397 			if (uhp->uh_proc_flags == 0)
398 				break;
399 		}
400 
401 		offset = UCODE_HEADER_SIZE_INTEL + body_size;
402 
403 		/* Check to see if there is extended signature table */
404 		if (total_size == offset)
405 			continue;
406 
407 		/* There is extended signature table.  More processing. */
408 		extp = (ucode_ext_table_intel_t *)(uintptr_t)&curbuf[offset];
409 
410 		for (i = 0; i < extp->uet_count; i++) {
411 			ucode_ext_sig_intel_t *uesp = &extp->uet_ext_sig[i];
412 			int j;
413 
414 			for (j = 0; j < 8; j++) {
415 				uint32_t id = uesp->ues_proc_flags & (1 << j);
416 
417 				if (id == 0 && uesp->ues_proc_flags)
418 					continue;
419 
420 				(void) snprintf(name, PATH_MAX,
421 				    "%s/%08X-%02X", path, extp->uet_ext_sig[i],
422 				    id);
423 
424 				if (ucode_should_update_intel(name, uhp->uh_rev)
425 				    != 0) {
426 
427 					/* Remove the existing one first */
428 					(void) unlink(name);
429 					if (link(firstname, name) == -1) {
430 						ucode_perror(name, EM_SYS);
431 						return (EM_SYS);
432 					}
433 				}
434 
435 				if (uesp->ues_proc_flags == 0)
436 					break;
437 			}
438 		}
439 
440 	}
441 
442 	/*
443 	 * Remove files with no links to them.  These are probably
444 	 * obsolete microcode files.
445 	 */
446 	if ((dirp = opendir(common_path)) == NULL) {
447 		ucode_perror(common_path, EM_SYS);
448 		return (EM_SYS);
449 	}
450 
451 	while ((dp = readdir(dirp)) != NULL) {
452 		char filename[PATH_MAX];
453 		struct stat statbuf;
454 
455 		(void) snprintf(filename, PATH_MAX,
456 		    "%s/%s", common_path, dp->d_name);
457 		if (stat(filename, &statbuf) == -1)
458 			continue;
459 
460 		if ((statbuf.st_mode & S_IFMT) == S_IFREG) {
461 			if (statbuf.st_nlink == 1)
462 				(void) unlink(filename);
463 		}
464 	}
465 
466 	(void) closedir(dirp);
467 
468 	return (EM_OK);
469 }
470 
471 /*
472  * Returns 0 on success, 2 on usage error, and 3 on operation error.
473  */
474 int
475 main(int argc, char *argv[])
476 {
477 	int	c;
478 	int	action = 0;
479 	int	actcount = 0;
480 	char	*path = NULL;
481 	char	*filename = NULL;
482 	int	errflg = 0;
483 	int	dev_fd = -1;
484 	int	fd = -1;
485 	int	verbose = 0;
486 	uint8_t	*buf = NULL;
487 	ucode_errno_t	rc = EM_OK;
488 	processorid_t	cpuid_max;
489 	struct stat filestat;
490 	uint32_t ucode_size;
491 
492 	(void) setlocale(LC_ALL, "");
493 
494 #if !defined(TEXT_DOMAIN)
495 #define	TEXT_DOMAIN "SYS_TEST"
496 #endif
497 	(void) textdomain(TEXT_DOMAIN);
498 
499 	cmdname = basename(argv[0]);
500 
501 	while ((c = getopt(argc, argv, "idhuvVR:")) != EOF) {
502 		switch (c) {
503 
504 		case 'i':
505 			action |= UCODE_OPT_INSTALL;
506 			actcount++;
507 			break;
508 
509 		case 'u':
510 			action |= UCODE_OPT_UPDATE;
511 			actcount++;
512 			break;
513 
514 		case 'v':
515 			action |= UCODE_OPT_VERSION;
516 			actcount++;
517 			break;
518 
519 		case 'd':
520 			ucode_debug = 1;
521 			break;
522 
523 		case 'R':
524 			if (optarg[0] == '-')
525 				errflg++;
526 			else if (strlen(optarg) > UCODE_MAX_PATH_LEN) {
527 				(void) fprintf(stderr,
528 				    gettext("Alternate path too long\n"));
529 				errflg++;
530 			} else if ((path = strdup(optarg)) == NULL) {
531 				errflg++;
532 			}
533 
534 			break;
535 
536 		case 'V':
537 			verbose = 1;
538 			break;
539 
540 		case 'h':
541 			usage(1);
542 			return (0);
543 
544 		default:
545 			usage(verbose);
546 			return (2);
547 		}
548 	}
549 
550 	if (actcount != 1) {
551 		(void) fprintf(stderr, gettext("%s: options -v, -i and -u "
552 		    "are mutually exclusive.\n"), cmdname);
553 		usage(verbose);
554 		return (2);
555 	}
556 
557 	if (optind <= argc - 1)
558 		filename = argv[optind];
559 	else if (!(action & UCODE_OPT_VERSION))
560 		errflg++;
561 
562 	if (errflg || action == 0) {
563 		usage(verbose);
564 		return (2);
565 	}
566 
567 	/*
568 	 * Convert from text format to binary format
569 	 */
570 	if ((action & UCODE_OPT_INSTALL) || (action & UCODE_OPT_UPDATE)) {
571 		int i;
572 		UCODE_VENDORS;
573 
574 		for (i = 0; ucode_vendors[i].filestr != NULL; i++) {
575 			dprintf("i = %d, filestr = %s, filename = %s\n",
576 			    i, ucode_vendors[i].filestr, filename);
577 			if (strncasecmp(ucode_vendors[i].filestr,
578 			    basename(filename),
579 			    strlen(ucode_vendors[i].filestr)) == 0) {
580 				ucode = &ucode_ops[i];
581 				(void) strncpy(ucode_vendor_str,
582 				    ucode_vendors[i].vendorstr,
583 				    sizeof (ucode_vendor_str));
584 				break;
585 			}
586 		}
587 
588 		if (ucode_vendors[i].filestr == NULL) {
589 			rc = EM_NOVENDOR;
590 			ucode_perror(basename(filename), rc);
591 			goto err_out;
592 		}
593 
594 		if ((stat(filename, &filestat)) < 0) {
595 			rc = EM_SYS;
596 			ucode_perror(filename, rc);
597 			goto err_out;
598 		}
599 
600 		if ((filestat.st_mode & S_IFMT) != S_IFREG &&
601 		    (filestat.st_mode & S_IFMT) != S_IFLNK) {
602 			rc = EM_FILEFORMAT;
603 			ucode_perror(filename, rc);
604 			goto err_out;
605 		}
606 
607 		if ((buf = malloc(filestat.st_size)) == NULL) {
608 			rc = EM_SYS;
609 			ucode_perror(filename, rc);
610 			goto err_out;
611 		}
612 
613 		ucode_size = ucode->convert(filename, buf, filestat.st_size);
614 
615 		dprintf("ucode_size = %d\n", ucode_size);
616 
617 		if (ucode_size == 0) {
618 			rc = EM_FILEFORMAT;
619 			ucode_perror(filename, rc);
620 			goto err_out;
621 		}
622 
623 		if ((rc = ucode->validate(buf, ucode_size)) != EM_OK) {
624 			ucode_perror(filename, rc);
625 			goto err_out;
626 		}
627 	}
628 
629 	/*
630 	 * For the install option, the microcode file must start with
631 	 * "intel" for Intel microcode, and "amd" for AMD microcode.
632 	 */
633 	if (action & UCODE_OPT_INSTALL) {
634 		/*
635 		 * If no path is provided by the -R option, put the files in
636 		 * /ucode_install_path/ucode_vendor_str/.
637 		 */
638 		if (path == NULL) {
639 			if ((path = malloc(PATH_MAX)) == NULL) {
640 				rc = EM_SYS;
641 				ucode_perror("malloc", rc);
642 				goto err_out;
643 			}
644 
645 			(void) snprintf(path, PATH_MAX, "/%s/%s",
646 			    ucode_install_path, ucode_vendor_str);
647 		}
648 
649 		if (mkdirp(path, 0755) == -1 && errno != EEXIST) {
650 			rc = EM_SYS;
651 			ucode_perror(path, rc);
652 			goto err_out;
653 		}
654 
655 		rc = ucode->gen_files(buf, ucode_size, path);
656 
657 		goto err_out;
658 	}
659 
660 	if ((dev_fd = open(ucode_dev, O_RDONLY)) == -1) {
661 		rc = EM_SYS;
662 		ucode_perror(ucode_dev, rc);
663 		goto err_out;
664 	}
665 
666 	if (action & UCODE_OPT_VERSION) {
667 		int tmprc;
668 		uint32_t *revp = NULL;
669 		int i;
670 #if defined(_SYSCALL32_IMPL)
671 	struct ucode_get_rev_struct32 inf32;
672 #else
673 	struct ucode_get_rev_struct info;
674 #endif
675 
676 		cpuid_max = (processorid_t)sysconf(_SC_CPUID_MAX);
677 
678 		if ((revp = (uint32_t *)
679 		    malloc(cpuid_max * sizeof (uint32_t))) == NULL) {
680 			rc = EM_SYS;
681 			ucode_perror("malloc", rc);
682 			goto err_out;
683 		}
684 
685 		for (i = 0; i < cpuid_max; i++)
686 			revp[i] = (uint32_t)-1;
687 
688 #if defined(_SYSCALL32_IMPL)
689 		info32.ugv_rev = (caddr32_t)revp;
690 		info32.ugv_size = cpuid_max;
691 		info32.ugv_errno = EM_OK;
692 		tmprc = ioctl(dev_fd, UCODE_GET_VERSION, &info32);
693 		rc = info32.ugv_errno;
694 #else
695 		info.ugv_rev = revp;
696 		info.ugv_size = cpuid_max;
697 		info.ugv_errno = EM_OK;
698 		tmprc = ioctl(dev_fd, UCODE_GET_VERSION, &info);
699 		rc = info.ugv_errno;
700 #endif
701 
702 		if (tmprc && rc == EM_OK) {
703 			rc = EM_SYS;
704 		}
705 
706 		if (rc == EM_OK) {
707 			(void) printf(gettext("CPU\tMicrocode Version\n"));
708 			for (i = 0; i < cpuid_max; i++) {
709 				if (info.ugv_rev[i] == (uint32_t)-1)
710 					continue;
711 				(void) printf("%d\t0x%x\n", i, info.ugv_rev[i]);
712 			}
713 		} else {
714 			ucode_perror(gettext("get microcode version"), rc);
715 		}
716 
717 		if (revp)
718 			free(revp);
719 	}
720 
721 	if (action & UCODE_OPT_UPDATE) {
722 		int tmprc;
723 #if defined(_SYSCALL32_IMPL)
724 	struct ucode_write_struct32 uw_struct32;
725 #else
726 	struct ucode_write_struct uw_struct;
727 #endif
728 
729 #if defined(_SYSCALL32_IMPL)
730 		uw_struct32.uw_size = ucode_size;
731 		uw_struct32.uw_ucode = (caddr32_t)buf;
732 		uw_struct32.uw_errno = EM_OK;
733 		tmprc = ioctl(dev_fd, UCODE_UPDATE, &uw_struct32);
734 		rc = uw_struct32.uw_errno;
735 
736 #else
737 		uw_struct.uw_size = ucode_size;
738 		uw_struct.uw_ucode = buf;
739 		uw_struct.uw_errno = EM_OK;
740 		tmprc = ioctl(dev_fd, UCODE_UPDATE, &uw_struct);
741 		rc = uw_struct.uw_errno;
742 #endif
743 
744 		if (rc == EM_OK) {
745 			if (tmprc) {
746 				rc = EM_SYS;
747 				ucode_perror(ucode_dev, rc);
748 			}
749 		} else if (rc == EM_NOMATCH || rc == EM_HIGHERREV) {
750 			ucode_perror(filename, rc);
751 		} else {
752 			ucode_perror(gettext("microcode update"), rc);
753 		}
754 	}
755 
756 err_out:
757 	if (dev_fd != -1)
758 		(void) close(dev_fd);
759 
760 	if (fd != -1)
761 		(void) close(fd);
762 
763 	free(buf);
764 	free(path);
765 
766 	if (rc != EM_OK)
767 		return (3);
768 
769 	return (0);
770 }
771