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