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