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