xref: /illumos-gate/usr/src/cmd/ucodeadm/ucodeadm.c (revision 608eb926e14f4ba4736b2d59e891335f1cba9e1e)
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 2025 Oxide Computer Company
30  */
31 
32 #include <sys/bitext.h>
33 #include <sys/types.h>
34 #include <sys/processor.h>
35 #include <sys/sysmacros.h>
36 #include <sys/ucode.h>
37 #include <sys/ucode_intel.h>
38 #include <sys/ucode_amd.h>
39 #include <sys/utsname.h>
40 #include <sys/ioctl.h>
41 #include <sys/stat.h>
42 #include <unistd.h>
43 #include <dirent.h>
44 #include <fcntl.h>
45 #include <errno.h>
46 #include <stdbool.h>
47 #include <stdint.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <stdarg.h>
51 #include <string.h>
52 #include <strings.h>
53 #include <errno.h>
54 #include <syslog.h>
55 #include <time.h>
56 #include <ctype.h>
57 #include <assert.h>
58 #include <libgen.h>
59 #include <limits.h>
60 #include <locale.h>
61 #include <libintl.h>
62 #include <ucode/ucode_errno.h>
63 #include <ucode/ucode_utils_intel.h>
64 #include <ucode/ucode_utils_amd.h>
65 
66 #define	UCODE_OPT_INSTALL	0x0001
67 #define	UCODE_OPT_UPDATE	0x0002
68 #define	UCODE_OPT_VERSION	0x0004
69 #define	UCODE_OPT_LIST		0x0008
70 
71 static const char ucode_dev[] = "/dev/" UCODE_DRIVER_NAME;
72 
73 static char	*cmdname;
74 
75 #define	UCODE_INSTALL_COMMON_PATH ".f"
76 
77 /*
78  * The maximum directory path length that can be provided via -R has
79  * to allow for appending the files within the microcode bundles.
80  */
81 #define	UCODE_MAX_PATH_LEN (PATH_MAX - \
82     MAX(UCODE_MAX_NAME_LEN_INTEL, UCODE_MAX_NAME_LEN_AMD) - 1)
83 
84 static bool ucode_debug = false;
85 
86 static ucode_errno_t ucode_convert_amd(const char *, uint8_t **, size_t *);
87 static ucode_errno_t ucode_convert_intel(const char *, uint8_t **, size_t *);
88 
89 static ucode_errno_t ucode_gen_files_amd(uint8_t *, size_t, const char *);
90 static ucode_errno_t ucode_gen_files_intel(uint8_t *, size_t, const char *);
91 
92 static void ucode_list_amd(uint8_t *, size_t);
93 static void ucode_list_intel(uint8_t *, size_t);
94 
95 typedef struct ucode_source {
96 	const char	*us_prefix;
97 	const char	*us_vendor;
98 	ucode_errno_t	(*us_convert)(const char *, uint8_t **, size_t *);
99 	ucode_errno_t	(*us_gen_files)(uint8_t *, size_t, const char *);
100 	ucode_errno_t	(*us_validate)(uint8_t *, size_t);
101 	void		(*us_list)(uint8_t *, size_t);
102 } ucode_source_t;
103 
104 static const ucode_source_t ucode_sources[] = {
105 	{
106 		.us_prefix	= "intel",
107 		.us_vendor	= "GenuineIntel",
108 		.us_convert	= ucode_convert_intel,
109 		.us_gen_files	= ucode_gen_files_intel,
110 		.us_validate	= ucode_validate_intel,
111 		.us_list	= ucode_list_intel,
112 	},
113 	{
114 		.us_prefix	= "amd",
115 		.us_vendor	= "AuthenticAMD",
116 		.us_convert	= ucode_convert_amd,
117 		.us_gen_files	= ucode_gen_files_amd,
118 		.us_validate	= ucode_validate_amd,
119 		.us_list	= ucode_list_amd,
120 	}
121 };
122 
123 const ucode_source_t *ucode;
124 
125 static void
126 dbgprintf(const char *format, ...)
127 {
128 	if (ucode_debug) {
129 		va_list alist;
130 		va_start(alist, format);
131 		(void) vfprintf(stderr, format, alist);
132 		va_end(alist);
133 	}
134 }
135 
136 static void
137 usage(bool verbose)
138 {
139 	(void) fprintf(stderr, gettext("usage:\n"));
140 	(void) fprintf(stderr, "\t%s -v\n", cmdname);
141 	if (verbose) {
142 		(void) fprintf(stderr,
143 		    gettext("\t\t Shows running microcode version.\n\n"));
144 	}
145 
146 	(void) fprintf(stderr, "\t%s -u [-t type] microcode-file\n", cmdname);
147 	if (verbose) {
148 		(void) fprintf(stderr, gettext("\t\t Updates microcode to the "
149 		    "latest matching version found in\n"
150 		    "\t\t microcode-file.\n\n"));
151 	}
152 
153 	(void) fprintf(stderr, "\t%s -l [-t type] microcode-file\n", cmdname);
154 	if (verbose) {
155 		(void) fprintf(stderr, gettext("\t\t Displays details of the "
156 		    "microcode file's contents.\n\n"));
157 	}
158 
159 	(void) fprintf(stderr,
160 	    "\t%s -i [-t type] [-R path] microcode-file\n", cmdname);
161 	if (verbose) {
162 		(void) fprintf(stderr, gettext("\t\t Installs microcode to be "
163 		    "used for subsequent boots.\n"));
164 	}
165 	(void) fprintf(stderr, gettext(
166 	    "\nThe type of the microcode file must either be specified with "
167 	    "the -t option\nor microcode-file must start with the vendor name "
168 	    "prefix, either \"intel\"\nor \"amd\", so that the type can be "
169 	    "inferred from it.\n\n"));
170 }
171 
172 static void
173 ucode_perror(const char *str, ucode_errno_t rc)
174 {
175 	(void) fprintf(stderr, "%s: %s: %s\n", cmdname, str,
176 	    errno == 0 ? ucode_strerror(rc) : strerror(errno));
177 	errno = 0;
178 }
179 
180 static int
181 bcd_to_int(uint8_t b)
182 {
183 	int high = (b >> 4) & 0xf;
184 	int low = b & 0xf;
185 
186 	if (high > 9 || low > 9)
187 		return (-1);
188 	return (high * 10 + low);
189 }
190 
191 /*
192  * Extract the family, model and stepping values from a 32-bit CPU signature.
193  * These bit fields are defined by the Intel Application Note AP-485
194  * "Intel Processor Identification and the CPUID Instruction"
195  */
196 static void
197 ucode_fms(uint32_t sig, uint8_t *family, uint8_t *model, uint8_t *stepping)
198 {
199 	const uint8_t xfamily = bitx32(sig, 27, 20);
200 	const uint8_t bfamily = bitx32(sig, 11, 8);
201 	const uint8_t xmodel = bitx32(sig, 19, 16);
202 	const uint8_t bmodel = bitx32(sig, 7, 4);
203 
204 	*family = bfamily == 0xf ? bfamily + xfamily : bfamily;
205 	*model = bfamily == 0x6 || bfamily == 0xf ?
206 	    (xmodel << 4) | bmodel : bmodel;
207 	*stepping = bitx32(sig, 3, 0);
208 }
209 
210 /*
211  * AMD microcode updates use a compressed 16-bit CPU identifier called an
212  * Equivalent Processor ID. The compression is achieved by removing the base
213  * family field, and assuming it is always 0xf, and reducing the size of the
214  * extended family field to 4 bits such that only families up to 0x1e can be
215  * represented. The structure is:
216  *
217  *    [15:12] Extended Family (i.e. family - 0xf)
218  *    [11:4]  Model
219  *    [3:0]   Stepping
220  *
221  * This function expands an AMD Equivalent Processor ID to a traditional
222  * 32-bit CPU signature.
223  */
224 static uint32_t
225 amd_equivcpu_to_sig(uint16_t equiv)
226 {
227 	uint16_t xfamily, model, stepping;
228 	uint32_t sig = 0;
229 
230 	xfamily = bitx16(equiv, 15, 12);
231 	model = bitx16(equiv, 11, 4);
232 	stepping = bitx16(equiv, 3, 0);
233 
234 	sig = bitset32(sig, 27, 20, xfamily);			/* ext family */
235 	sig = bitset32(sig, 11, 8, 0xf);			/* family */
236 	sig = bitset32(sig, 19, 16, bitx16(model, 7, 4));	/* ext model */
237 	sig = bitset32(sig, 7, 4, bitx16(model, 3, 0));		/* model */
238 	sig = bitset32(sig, 3, 0, stepping);
239 
240 	return (sig);
241 }
242 
243 /*
244  * Load a microcode release which is in AMD's binary container format. If the
245  * provided file appears to be a raw binary update, cons up a container
246  * containing just that patch.
247  */
248 static ucode_errno_t
249 ucode_convert_amd(const char *infile, uint8_t **bufp, size_t *sizep)
250 {
251 	ucode_header_amd_t *patch;
252 	ucode_section_amd_t *section;
253 	ucode_eqtbl_amd_t *eq;
254 	int month, day, yearl, fd;
255 	size_t csize;
256 	ssize_t rsize;
257 	uint8_t *buf = *bufp;
258 	size_t size = *sizep;
259 
260 	if (infile == NULL || buf == NULL || size == 0)
261 		return (EM_INVALIDARG);
262 
263 	if ((fd = open(infile, O_RDONLY)) < 0)
264 		return (EM_SYS);
265 
266 	rsize = read(fd, buf, size);
267 	if (rsize < 0) {
268 		int _errno = errno;
269 		(void) close(fd);
270 		errno = _errno;
271 		return (EM_SYS);
272 	}
273 
274 	(void) close(fd);
275 
276 	if (rsize == 0)
277 		return (EM_FILEFORMAT);
278 
279 	size = rsize;
280 
281 	/*
282 	 * AMD microcode is distributed in two forms. As container/bundle files
283 	 * or as individual binary patches. If this looks like a container,
284 	 * we're done.
285 	 */
286 	if (ucode->us_validate(buf, size) == EM_OK) {
287 		*sizep = size;
288 		return (EM_OK);
289 	}
290 
291 	/*
292 	 * Otherwise, see if this looks like a binary patch. We're limited in
293 	 * what we can check here but we can look at the date field to see if
294 	 * it is plausible. That field is encoded as a kind of packed
295 	 * BCD 0xMMDDYYYY.
296 	 */
297 	patch = (ucode_header_amd_t *)*bufp;
298 	month = bcd_to_int(bitx32(patch->uh_date, 31, 24));
299 	day = bcd_to_int(bitx32(patch->uh_date, 23, 16));
300 	yearl = bcd_to_int(bitx32(patch->uh_date, 7, 0));
301 	if (day < 1 || day > 31 || month < 1 || month > 12 ||
302 	    yearl < 0 || yearl > 99) {
303 		dbgprintf("implausible date code: 0x%x\n", patch->uh_date);
304 		return (EM_FILEFORMAT);
305 	}
306 
307 	/* It's plausibly a binary patch; cons up a container */
308 	dbgprintf("creating microcode container\n");
309 	csize =
310 	    sizeof (uint32_t) +			/* Magic */
311 	    2 * sizeof (ucode_section_amd_t) +	/* TLV headers */
312 	    2 * sizeof (ucode_eqtbl_amd_t);	/* Equivalence table */
313 	if (size > SIZE_MAX - csize) {
314 		dbgprintf("container size too large (patch size %zu)\n", size);
315 		return (EM_FILEFORMAT);
316 	}
317 	csize += size;				/* Patch payload */
318 
319 	buf = realloc(*bufp, csize);
320 	if (buf == NULL)
321 		return (EM_SYS);
322 
323 	/* Relocate the patch data */
324 	patch = (ucode_header_amd_t *)(buf + csize - size);
325 	bcopy(buf, patch, size);
326 
327 	/* Build the container */
328 	*(uint32_t *)buf = UCODE_AMD_CONTAINER_MAGIC;
329 
330 	/* Equivalence table section */
331 	section = (ucode_section_amd_t *)(buf + sizeof (uint32_t));
332 	eq = (ucode_eqtbl_amd_t *)section->usa_data;
333 
334 	section->usa_type = UCODE_AMD_CONTAINER_TYPE_EQUIV;
335 	section->usa_size = 2 * sizeof (*eq);
336 	eq->ue_equiv_cpu = patch->uh_cpu_rev;
337 	eq->ue_inst_cpu = amd_equivcpu_to_sig(patch->uh_cpu_rev);
338 	/* Create the equivalence table terminator record */
339 	bzero(eq + 1, sizeof (*eq));
340 
341 	/* Patch section */
342 	section =
343 	    (ucode_section_amd_t *)(section->usa_data + section->usa_size);
344 	section->usa_type = UCODE_AMD_CONTAINER_TYPE_PATCH;
345 	section->usa_size = size;
346 
347 	*bufp = buf;
348 	*sizep = csize;
349 
350 	return (EM_OK);
351 }
352 
353 /*
354  * Convert text format microcode release into binary format.
355  */
356 #define	LINESIZE	120	/* copyright line sometimes is longer than 80 */
357 static ucode_errno_t
358 ucode_convert_intel(const char *infile, uint8_t **bufp, size_t *sizep)
359 {
360 	char linebuf[LINESIZE];
361 	FILE *infd = NULL;
362 	bool firstline = true;
363 	size_t count = 0;
364 	uint8_t	*buf = *bufp;
365 	size_t size = *sizep;
366 	uint32_t *intbuf = (uint32_t *)(uintptr_t)buf;
367 
368 	if (infile == NULL || buf == NULL || size == 0)
369 		return (EM_INVALIDARG);
370 
371 	if ((infd = fopen(infile, "r")) == NULL)
372 		return (EM_SYS);
373 
374 	while (fgets(linebuf, LINESIZE, infd)) {
375 
376 		/* Check to see if we are processing a binary file */
377 		if (firstline && !isprint(linebuf[0])) {
378 			if (fseek(infd, 0, SEEK_SET) == 0)
379 				count = fread(buf, 1, size, infd);
380 
381 			(void) fclose(infd);
382 
383 			if (count == 0)
384 				return (EM_FILEFORMAT);
385 
386 			*sizep = count;
387 			return (EM_OK);
388 		}
389 
390 		firstline = false;
391 
392 		/* Skip blank lines */
393 		if (strlen(linebuf) == 1)
394 			continue;
395 
396 		/* Skip lines with all spaces or tabs */
397 		if (strcspn(linebuf, " \t") == 0)
398 			continue;
399 
400 		/* Text file.  Skip comments. */
401 		if (linebuf[0] == '/')
402 			continue;
403 
404 		if (sscanf(linebuf, "%x, %x, %x, %x",
405 		    &intbuf[count], &intbuf[count+1],
406 		    &intbuf[count+2], &intbuf[count+3]) != 4)
407 			break;
408 
409 		count += 4;
410 	}
411 
412 	(void) fclose(infd);
413 
414 	/*
415 	 * If we get here, we are processing a text format file
416 	 * where "count" is used to count the number of integers
417 	 * read.  Convert it to number of characters read.
418 	 */
419 	*sizep = count * sizeof (int);
420 
421 	return (EM_OK);
422 }
423 
424 /*
425  * Returns 0 if no need to update the link; -1 otherwise
426  */
427 static int
428 ucode_should_update_intel(char *filename, uint32_t new_rev)
429 {
430 	int		fd;
431 	struct stat	statbuf;
432 	ucode_header_intel_t header;
433 
434 	/*
435 	 * If the file or link already exists, check to see if
436 	 * it is necessary to update it.
437 	 */
438 	if (stat(filename, &statbuf) == 0) {
439 		if ((fd = open(filename, O_RDONLY)) == -1)
440 			return (-1);
441 
442 		if (read(fd, &header, sizeof (header)) == -1) {
443 			(void) close(fd);
444 			return (-1);
445 		}
446 
447 		(void) close(fd);
448 
449 		if (header.uh_rev >= new_rev)
450 			return (0);
451 	}
452 
453 	return (-1);
454 }
455 
456 /*
457  * Generate microcode binary files.  Must be called after ucode_validate().
458  */
459 static ucode_errno_t
460 ucode_gen_files_amd(uint8_t *buf, size_t size, const char *path)
461 {
462 	char common_path[PATH_MAX];
463 	int fd;
464 	uint16_t last_cpu_rev = 0;
465 	uint32_t counter = 0;
466 	int n;
467 
468 	/* write container file */
469 	n = snprintf(common_path, sizeof (common_path), "%s/container", path);
470 	if (n >= sizeof (common_path)) {
471 		dbgprintf("failed to construct container path\n");
472 		return (EM_FILEFORMAT);
473 	}
474 
475 	dbgprintf("path = %s\n", common_path);
476 	fd = open(common_path, O_WRONLY | O_CREAT | O_TRUNC,
477 	    S_IRUSR | S_IRGRP | S_IROTH);
478 
479 	if (fd == -1) {
480 		ucode_perror(common_path, EM_SYS);
481 		return (EM_SYS);
482 	}
483 
484 	if (write(fd, buf, size) != size) {
485 		(void) close(fd);
486 		ucode_perror(common_path, EM_SYS);
487 		return (EM_SYS);
488 	}
489 
490 	(void) close(fd);
491 
492 	/* skip over magic number, the file has already been validated */
493 	buf += sizeof (uint32_t);
494 	size -= sizeof (uint32_t);
495 
496 	while (size > sizeof (ucode_section_amd_t)) {
497 		ucode_section_amd_t *section = (ucode_section_amd_t *)buf;
498 		int n;
499 
500 		switch (section->usa_type) {
501 		case UCODE_AMD_CONTAINER_TYPE_EQUIV:
502 			n = snprintf(common_path, sizeof (common_path), "%s/%s",
503 			    path, UCODE_AMD_EQUIVALENCE_TABLE_NAME);
504 			break;
505 		case UCODE_AMD_CONTAINER_TYPE_PATCH: {
506 			ucode_header_amd_t *uh =
507 			    (ucode_header_amd_t *)section->usa_data;
508 
509 			if (uh->uh_cpu_rev != last_cpu_rev) {
510 				last_cpu_rev = uh->uh_cpu_rev;
511 				counter = 0;
512 			}
513 
514 			n = snprintf(common_path, sizeof (common_path),
515 			    "%s/%04X-%02X", path, uh->uh_cpu_rev, counter++);
516 			break;
517 		}
518 		default:
519 			/*
520 			 * Since the container has already been validated, this
521 			 * should never happen.
522 			 */
523 			return (EM_FILEFORMAT);
524 		}
525 
526 		if (n >= sizeof (common_path)) {
527 			dbgprintf("failed to construct component path\n");
528 			return (EM_FILEFORMAT);
529 		}
530 
531 		dbgprintf("path = %s\n", common_path);
532 		fd = open(common_path, O_WRONLY | O_CREAT | O_TRUNC,
533 		    S_IRUSR | S_IRGRP | S_IROTH);
534 
535 		if (fd == -1) {
536 			ucode_perror(common_path, EM_SYS);
537 			return (EM_SYS);
538 		}
539 
540 		if (write(fd, section->usa_data, section->usa_size) !=
541 		    section->usa_size) {
542 			(void) close(fd);
543 			ucode_perror(common_path, EM_SYS);
544 			return (EM_SYS);
545 		}
546 
547 		(void) close(fd);
548 
549 		size -= section->usa_size + sizeof (ucode_section_amd_t);
550 		buf += section->usa_size + sizeof (ucode_section_amd_t);
551 	}
552 
553 	return (EM_OK);
554 }
555 
556 static ucode_errno_t
557 ucode_gen_files_intel(uint8_t *buf, size_t size, const char *path)
558 {
559 	size_t	remaining;
560 	char	common_path[PATH_MAX];
561 	DIR	*dirp;
562 	struct dirent *dp;
563 	int n;
564 
565 	n = snprintf(common_path, sizeof (common_path), "%s/%s", path,
566 	    UCODE_INSTALL_COMMON_PATH);
567 	if (n >= sizeof (common_path)) {
568 		dbgprintf("failed to construct path for %s\n",
569 		    UCODE_INSTALL_COMMON_PATH);
570 		return (EM_FILEFORMAT);
571 	}
572 
573 	if (mkdirp(common_path, 0755) == -1 && errno != EEXIST) {
574 		ucode_perror(common_path, EM_SYS);
575 		return (EM_SYS);
576 	}
577 
578 	for (remaining = size; remaining > 0; ) {
579 		uint32_t	total_size, body_size, offset;
580 		char		firstname[PATH_MAX];
581 		char		name[PATH_MAX];
582 		int		i;
583 		uint8_t		*curbuf = &buf[size - remaining];
584 		ucode_header_intel_t	*uhp;
585 		ucode_ext_table_intel_t *extp;
586 
587 		uhp = (ucode_header_intel_t *)(uintptr_t)curbuf;
588 
589 		total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size);
590 		body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size);
591 
592 		remaining -= total_size;
593 
594 		n = snprintf(firstname, sizeof (common_path), "%s/%08X-%02X",
595 		    common_path, uhp->uh_signature, uhp->uh_proc_flags);
596 		if (n >= sizeof (common_path)) {
597 			dbgprintf("failed to construct component path\n");
598 			return (EM_FILEFORMAT);
599 		}
600 		dbgprintf("firstname = %s\n", firstname);
601 
602 		if (ucode_should_update_intel(firstname, uhp->uh_rev) != 0) {
603 			int fd;
604 
605 			/* Remove the existing one first */
606 			(void) unlink(firstname);
607 
608 			if ((fd = open(firstname, O_WRONLY | O_CREAT | O_TRUNC,
609 			    S_IRUSR | S_IRGRP | S_IROTH)) == -1) {
610 				ucode_perror(firstname, EM_SYS);
611 				return (EM_SYS);
612 			}
613 
614 			if (write(fd, curbuf, total_size) != total_size) {
615 				(void) close(fd);
616 				ucode_perror(firstname, EM_SYS);
617 				return (EM_SYS);
618 			}
619 
620 			(void) close(fd);
621 		}
622 
623 		/*
624 		 * Only 1 byte of the proc_flags field is used, therefore
625 		 * we only need to match 8 potential platform ids.
626 		 */
627 		for (i = 0; i < 8; i++) {
628 			uint32_t platid = uhp->uh_proc_flags & (1 << i);
629 
630 			if (platid == 0 && uhp->uh_proc_flags != 0)
631 				continue;
632 
633 			n = snprintf(name, sizeof (common_path),
634 			    "%s/%08X-%02X", path, uhp->uh_signature, platid);
635 			if (n >= sizeof (common_path)) {
636 				dbgprintf("failed to construct platid path\n");
637 				return (EM_FILEFORMAT);
638 			}
639 
640 			dbgprintf("proc_flags = %x, platid = %x, name = %s\n",
641 			    uhp->uh_proc_flags, platid, name);
642 
643 			if (ucode_should_update_intel(name,
644 			    uhp->uh_rev) != 0) {
645 				/* Remove the existing one first */
646 				(void) unlink(name);
647 				if (link(firstname, name) == -1) {
648 					ucode_perror(name, EM_SYS);
649 					return (EM_SYS);
650 				}
651 			}
652 
653 			if (uhp->uh_proc_flags == 0)
654 				break;
655 		}
656 
657 		offset = UCODE_HEADER_SIZE_INTEL + body_size;
658 
659 		/* Check to see if there is extended signature table */
660 		if (total_size == offset)
661 			continue;
662 
663 		/* There is extended signature table.  More processing. */
664 		extp = (ucode_ext_table_intel_t *)&curbuf[offset];
665 
666 		for (i = 0; i < extp->uet_count; i++) {
667 			ucode_ext_sig_intel_t *uesp = &extp->uet_ext_sig[i];
668 			int j;
669 
670 			for (j = 0; j < 8; j++) {
671 				uint32_t id = uesp->ues_proc_flags & (1 << j);
672 
673 				if (id == 0 && uesp->ues_proc_flags)
674 					continue;
675 
676 				n = snprintf(name, sizeof (common_path),
677 				    "%s/%08X-%02X", path,
678 				    uesp->ues_signature, id);
679 				if (n >= sizeof (common_path)) {
680 					dbgprintf(
681 					    "failed to construct ext path\n");
682 					return (EM_FILEFORMAT);
683 				}
684 
685 				dbgprintf("extsig: proc_flags = %x, "
686 				    "platid = %x, name = %s\n",
687 				    uesp->ues_proc_flags, id, name);
688 
689 				if (ucode_should_update_intel(name,
690 				    uhp->uh_rev) != 0) {
691 					/* Remove the existing one first */
692 					(void) unlink(name);
693 					if (link(firstname, name) == -1) {
694 						ucode_perror(name, EM_SYS);
695 						return (EM_SYS);
696 					}
697 				}
698 
699 				if (uesp->ues_proc_flags == 0)
700 					break;
701 			}
702 		}
703 
704 	}
705 
706 	/*
707 	 * Remove files with no links to them.  These are probably
708 	 * obsolete microcode files.
709 	 */
710 	if ((dirp = opendir(common_path)) == NULL) {
711 		ucode_perror(common_path, EM_SYS);
712 		return (EM_SYS);
713 	}
714 
715 	while ((dp = readdir(dirp)) != NULL) {
716 		char filename[PATH_MAX];
717 		struct stat statbuf;
718 
719 		n = snprintf(filename, sizeof (common_path),
720 		    "%s/%s", common_path, dp->d_name);
721 		if (n >= sizeof (common_path) || stat(filename, &statbuf) == -1)
722 			continue;
723 
724 		if ((statbuf.st_mode & S_IFMT) == S_IFREG) {
725 			if (statbuf.st_nlink == 1)
726 				(void) unlink(filename);
727 		}
728 	}
729 
730 	(void) closedir(dirp);
731 
732 	return (EM_OK);
733 }
734 
735 static void
736 ucode_list_intel(uint8_t *buf, size_t size)
737 {
738 	size_t remaining;
739 
740 	printf("Microcode patches:\n");
741 	for (remaining = size; remaining > 0; ) {
742 		uint8_t *curbuf = &buf[size - remaining];
743 		uint8_t family, model, stepping;
744 		uint32_t total_size, body_size, offset;
745 		ucode_header_intel_t *uhp;
746 		ucode_ext_table_intel_t *extp;
747 
748 		uhp = (ucode_header_intel_t *)(uintptr_t)curbuf;
749 
750 		total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size);
751 		body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size);
752 
753 		remaining -= total_size;
754 
755 		ucode_fms(uhp->uh_signature, &family, &model, &stepping);
756 
757 		printf(
758 		    "    %08lX-%02lX -> Family=%02x Model=%02x Stepping=%02x\n",
759 		    uhp->uh_signature, uhp->uh_proc_flags,
760 		    family, model, stepping);
761 		printf(
762 		    "    %14s Date=%08lX Bytes=%lu\n", "",
763 		    uhp->uh_date, uhp->uh_body_size);
764 
765 		offset = UCODE_HEADER_SIZE_INTEL + body_size;
766 
767 		/* Check to see if there is extended signature table */
768 		if (total_size == offset)
769 			continue;
770 
771 		printf("Extended Signature Table:\n");
772 
773 		extp = (ucode_ext_table_intel_t *)&curbuf[offset];
774 
775 		for (uint32_t i = 0; i < extp->uet_count; i++) {
776 			ucode_ext_sig_intel_t *uesp = &extp->uet_ext_sig[i];
777 
778 			ucode_fms(uesp->ues_signature,
779 			    &family, &model, &stepping);
780 
781 			printf(
782 			    "    %08lX-%02lX -> Family=%02x Model=%02x "
783 			    "Stepping=%02x\n",
784 			    uesp->ues_signature, uesp->ues_proc_flags,
785 			    family, model, stepping);
786 		}
787 	}
788 }
789 
790 static void
791 ucode_list_amd(uint8_t *buf, size_t size)
792 {
793 	uint32_t last_type = UINT32_MAX;
794 
795 	/* The file has already been validated. Skip over magic number */
796 	buf += sizeof (uint32_t);
797 	size -= sizeof (uint32_t);
798 
799 	while (size > sizeof (ucode_section_amd_t)) {
800 		ucode_section_amd_t *section = (ucode_section_amd_t *)buf;
801 
802 		switch (section->usa_type) {
803 		case UCODE_AMD_CONTAINER_TYPE_EQUIV: {
804 			ucode_eqtbl_amd_t *eq =
805 			    (ucode_eqtbl_amd_t *)section->usa_data;
806 
807 			if (last_type != section->usa_type) {
808 				printf("Equivalence table:\n");
809 				last_type = section->usa_type;
810 			}
811 			for (uint_t i = 0; eq->ue_inst_cpu != 0 &&
812 			    i < section->usa_size / sizeof (*eq); eq++, i++) {
813 				uint8_t family, model, stepping;
814 
815 				ucode_fms(eq->ue_inst_cpu, &family, &model,
816 				    &stepping);
817 
818 				printf("    %08lX Family=%02x Model=%02x "
819 				    "Stepping=%02x -> %04X\n",
820 				    eq->ue_inst_cpu, family, model,
821 				    stepping, eq->ue_equiv_cpu);
822 			}
823 			break;
824 		}
825 		case UCODE_AMD_CONTAINER_TYPE_PATCH: {
826 			ucode_header_amd_t *uh =
827 			    (ucode_header_amd_t *)section->usa_data;
828 
829 			if (uh->uh_cpu_rev == 0)
830 				break;
831 
832 			if (last_type != section->usa_type) {
833 				printf("Microcode patches:\n");
834 				last_type = section->usa_type;
835 			}
836 
837 			printf("    %4X -> Patch=%08lX Date=%08lX Bytes=%lu\n",
838 			    uh->uh_cpu_rev, uh->uh_patch_id, uh->uh_date,
839 			    section->usa_size);
840 
841 			break;
842 		}
843 		default:
844 			break;
845 		}
846 
847 		size -= section->usa_size + sizeof (ucode_section_amd_t);
848 		buf += section->usa_size + sizeof (ucode_section_amd_t);
849 	}
850 }
851 
852 /*
853  * Returns 0 on success, 2 on usage error, and 3 on operation error.
854  */
855 int
856 main(int argc, char *argv[])
857 {
858 	int	c;
859 	int	action = 0;
860 	int	actcount = 0;
861 	int	typeindex = -1;
862 	char	*path = NULL;
863 	char	*filename = NULL;
864 	int	errflg = 0;
865 	int	dev_fd = -1;
866 	int	fd = -1;
867 	bool	verbose = false;
868 	bool	needfile = false;
869 	uint8_t	*buf = NULL;
870 	ucode_errno_t	rc = EM_OK;
871 	processorid_t	cpuid_max;
872 	struct stat filestat;
873 	size_t ucode_size = 0;
874 
875 	(void) setlocale(LC_ALL, "");
876 
877 #if !defined(TEXT_DOMAIN)
878 #define	TEXT_DOMAIN "SYS_TEST"
879 #endif
880 	(void) textdomain(TEXT_DOMAIN);
881 
882 	cmdname = basename(argv[0]);
883 
884 	while ((c = getopt(argc, argv, "idhluvVR:t:")) != EOF) {
885 		switch (c) {
886 
887 		case 'd':
888 			ucode_debug = true;
889 			break;
890 
891 		case 'i':
892 			action |= UCODE_OPT_INSTALL;
893 			actcount++;
894 			needfile = true;
895 			break;
896 
897 		case 'l':
898 			action |= UCODE_OPT_LIST;
899 			actcount++;
900 			needfile = true;
901 			break;
902 
903 		case 't':
904 			if (typeindex != -1) {
905 				(void) fprintf(stderr, gettext(
906 				    "-t can only be specified once\n"));
907 				errflg++;
908 				break;
909 			}
910 			for (uint_t i = 0; i < ARRAY_SIZE(ucode_sources); i++) {
911 				if (strcmp(optarg,
912 				    ucode_sources[i].us_prefix) == 0) {
913 					typeindex = i;
914 					break;
915 				}
916 			}
917 			if (typeindex == -1) {
918 				(void) fprintf(stderr,
919 				    gettext("Unknown microcode type, %s\n"),
920 				    optarg);
921 				errflg++;
922 			}
923 			break;
924 
925 		case 'u':
926 			action |= UCODE_OPT_UPDATE;
927 			actcount++;
928 			needfile = true;
929 			break;
930 
931 		case 'v':
932 			action |= UCODE_OPT_VERSION;
933 			actcount++;
934 			break;
935 
936 		case 'R':
937 			if (optarg[0] == '-') {
938 				errflg++;
939 			} else if (strlen(optarg) > UCODE_MAX_PATH_LEN) {
940 				(void) fprintf(stderr,
941 				    gettext("Alternate path too long\n"));
942 				errflg++;
943 			} else if ((path = strdup(optarg)) == NULL) {
944 				errflg++;
945 			}
946 
947 			break;
948 
949 		case 'V':
950 			verbose = true;
951 			break;
952 
953 		case 'h':
954 			usage(true);
955 			return (0);
956 
957 		default:
958 			usage(verbose);
959 			return (2);
960 		}
961 	}
962 
963 	if (actcount == 0) {
964 		(void) fprintf(stderr, gettext("%s: One of -i, -l, -u or -v "
965 		    "must be provided.\n"), cmdname);
966 		usage(verbose);
967 		return (2);
968 	}
969 
970 	if (actcount != 1) {
971 		(void) fprintf(stderr, gettext("%s: options -i, -l, -u and -v "
972 		    "are mutually exclusive.\n"), cmdname);
973 		usage(verbose);
974 		return (2);
975 	}
976 
977 	if (typeindex != -1 && !needfile) {
978 		(void) fprintf(stderr, gettext("%s: option -t requires one of "
979 		    "-i, -l or -u\n"), cmdname);
980 		usage(verbose);
981 		return (2);
982 	}
983 
984 	if (optind <= argc - 1)
985 		filename = argv[optind];
986 	else if (needfile)
987 		errflg++;
988 
989 	if (errflg || action == 0) {
990 		usage(verbose);
991 		return (2);
992 	}
993 
994 	/*
995 	 * Convert from the vendor-shipped format to individual microcode files.
996 	 */
997 	if (needfile) {
998 		if (typeindex != -1) {
999 			ucode = &ucode_sources[typeindex];
1000 		} else {
1001 			for (uint_t i = 0; i < ARRAY_SIZE(ucode_sources); i++) {
1002 				const ucode_source_t *src = &ucode_sources[i];
1003 
1004 				dbgprintf("i = %d, filestr = %s, "
1005 				    "filename = %s\n",
1006 				    i, src->us_prefix, filename);
1007 				if (strncasecmp(src->us_prefix,
1008 				    basename(filename),
1009 				    strlen(src->us_prefix)) == 0) {
1010 					ucode = src;
1011 					break;
1012 				}
1013 			}
1014 		}
1015 
1016 		if (ucode == NULL) {
1017 			rc = EM_NOVENDOR;
1018 			(void) fprintf(stderr, "%s: %s.\n\n"
1019 			    "Either specify the type with the -t option, "
1020 			    "or rename the file such that\nits name begins "
1021 			    "with a vendor string.\n",
1022 			    cmdname, ucode_strerror(rc));
1023 			goto out;
1024 		}
1025 
1026 		dbgprintf("Selected microcode type %s (%s)\n",
1027 		    ucode->us_prefix, ucode->us_vendor);
1028 
1029 		if ((stat(filename, &filestat)) < 0) {
1030 			rc = EM_SYS;
1031 			ucode_perror(filename, rc);
1032 			goto out;
1033 		}
1034 
1035 		if ((filestat.st_mode & S_IFMT) != S_IFREG &&
1036 		    (filestat.st_mode & S_IFMT) != S_IFLNK) {
1037 			rc = EM_FILEFORMAT;
1038 			ucode_perror(filename, rc);
1039 			goto out;
1040 		}
1041 
1042 		ucode_size = filestat.st_size;
1043 		if ((buf = malloc(ucode_size)) == NULL) {
1044 			rc = EM_SYS;
1045 			ucode_perror(filename, rc);
1046 			goto out;
1047 		}
1048 
1049 		rc = ucode->us_convert(filename, &buf, &ucode_size);
1050 		if (rc != EM_OK) {
1051 			ucode_perror(filename, rc);
1052 			goto out;
1053 		}
1054 
1055 		dbgprintf("ucode_size = %zd\n", ucode_size);
1056 
1057 		if ((rc = ucode->us_validate(buf, ucode_size)) != EM_OK) {
1058 			ucode_perror(filename, rc);
1059 			goto out;
1060 		}
1061 	}
1062 
1063 	if (action & UCODE_OPT_LIST) {
1064 		ucode->us_list(buf, ucode_size);
1065 		goto out;
1066 	}
1067 
1068 	if (action & UCODE_OPT_INSTALL) {
1069 		/*
1070 		 * If no path is provided by the -R option, put the files in
1071 		 * /platform/<arch>/ucode/<ucode_vendor_str>/.
1072 		 */
1073 		if (path == NULL) {
1074 			struct utsname uts;
1075 
1076 			if (uname(&uts) == -1) {
1077 				perror("Unable to retrieve system uname");
1078 				goto out;
1079 			}
1080 
1081 			if ((path = malloc(PATH_MAX)) == NULL) {
1082 				rc = EM_SYS;
1083 				ucode_perror("malloc", rc);
1084 				goto out;
1085 			}
1086 
1087 			(void) snprintf(path, PATH_MAX, "/platform/%s/ucode/%s",
1088 			    uts.machine, ucode->us_vendor);
1089 		}
1090 
1091 		if (mkdirp(path, 0755) == -1 && errno != EEXIST) {
1092 			rc = EM_SYS;
1093 			ucode_perror(path, rc);
1094 			goto out;
1095 		}
1096 
1097 		rc = ucode->us_gen_files(buf, ucode_size, path);
1098 
1099 		goto out;
1100 	}
1101 
1102 	if ((dev_fd = open(ucode_dev, O_RDONLY)) == -1) {
1103 		rc = EM_SYS;
1104 		ucode_perror(ucode_dev, rc);
1105 		goto out;
1106 	}
1107 
1108 	if (action & UCODE_OPT_VERSION) {
1109 		int tmprc;
1110 		uint32_t *revp = NULL;
1111 		int i;
1112 		struct ucode_get_rev_struct info;
1113 
1114 		cpuid_max = (processorid_t)sysconf(_SC_CPUID_MAX);
1115 
1116 		if ((revp = (uint32_t *)
1117 		    malloc(cpuid_max * sizeof (uint32_t))) == NULL) {
1118 			rc = EM_SYS;
1119 			ucode_perror("malloc", rc);
1120 			goto out;
1121 		}
1122 
1123 		for (i = 0; i < cpuid_max; i++)
1124 			revp[i] = (uint32_t)-1;
1125 
1126 		info.ugv_rev = revp;
1127 		info.ugv_size = cpuid_max;
1128 		info.ugv_errno = EM_OK;
1129 		tmprc = ioctl(dev_fd, UCODE_GET_VERSION, &info);
1130 		rc = info.ugv_errno;
1131 
1132 		if (tmprc && rc == EM_OK) {
1133 			rc = EM_SYS;
1134 		}
1135 
1136 		if (rc == EM_OK) {
1137 			(void) printf(gettext("CPU\tMicrocode Version\n"));
1138 			for (i = 0; i < cpuid_max; i++) {
1139 				if (info.ugv_rev[i] == (uint32_t)-1)
1140 					continue;
1141 				(void) printf("%d\t0x%x\n", i, info.ugv_rev[i]);
1142 			}
1143 		} else {
1144 			ucode_perror(gettext("get microcode version"), rc);
1145 		}
1146 
1147 		if (revp)
1148 			free(revp);
1149 	}
1150 
1151 	if (action & UCODE_OPT_UPDATE) {
1152 		int tmprc;
1153 		struct ucode_write_struct uw_struct;
1154 
1155 		uw_struct.uw_size = ucode_size;
1156 		uw_struct.uw_ucode = buf;
1157 		uw_struct.uw_errno = EM_OK;
1158 		tmprc = ioctl(dev_fd, UCODE_UPDATE, &uw_struct);
1159 		rc = uw_struct.uw_errno;
1160 
1161 		if (rc == EM_OK) {
1162 			if (tmprc) {
1163 				rc = EM_SYS;
1164 				ucode_perror(ucode_dev, rc);
1165 			}
1166 		} else if (rc == EM_NOMATCH || rc == EM_HIGHERREV) {
1167 			ucode_perror(filename, rc);
1168 		} else {
1169 			ucode_perror(gettext("microcode update"), rc);
1170 		}
1171 	}
1172 
1173 out:
1174 	if (dev_fd != -1)
1175 		(void) close(dev_fd);
1176 
1177 	if (fd != -1)
1178 		(void) close(fd);
1179 
1180 	free(buf);
1181 	free(path);
1182 
1183 	if (rc != EM_OK)
1184 		return (3);
1185 
1186 	return (0);
1187 }
1188