xref: /illumos-gate/usr/src/cmd/ucodeadm/ucodeadm.c (revision d32f26ee)
12449e17fSsherrym /*
22449e17fSsherrym  * CDDL HEADER START
32449e17fSsherrym  *
42449e17fSsherrym  * The contents of this file are subject to the terms of the
52449e17fSsherrym  * Common Development and Distribution License (the "License").
62449e17fSsherrym  * You may not use this file except in compliance with the License.
72449e17fSsherrym  *
82449e17fSsherrym  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92449e17fSsherrym  * or http://www.opensolaris.org/os/licensing.
102449e17fSsherrym  * See the License for the specific language governing permissions
112449e17fSsherrym  * and limitations under the License.
122449e17fSsherrym  *
132449e17fSsherrym  * When distributing Covered Code, include this CDDL HEADER in each
142449e17fSsherrym  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152449e17fSsherrym  * If applicable, add the following below this CDDL HEADER, with the
162449e17fSsherrym  * fields enclosed by brackets "[]" replaced with your own identifying
172449e17fSsherrym  * information: Portions Copyright [yyyy] [name of copyright owner]
182449e17fSsherrym  *
192449e17fSsherrym  * CDDL HEADER END
202449e17fSsherrym  */
212449e17fSsherrym /*
220ba6f73dSMark Johnson  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
232449e17fSsherrym  * Use is subject to license terms.
242449e17fSsherrym  */
252449e17fSsherrym 
26bc54f855SJohn Levon /*
27bc54f855SJohn Levon  * Copyright (c) 2018, Joyent, Inc.
28be672c8eSAndy Fiddaman  * Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
29*d32f26eeSAndy Fiddaman  * Copyright 2023 Oxide Computer Company
30bc54f855SJohn Levon  */
31bc54f855SJohn Levon 
322449e17fSsherrym #include <sys/types.h>
332449e17fSsherrym #include <sys/processor.h>
34*d32f26eeSAndy Fiddaman #include <sys/sysmacros.h>
352449e17fSsherrym #include <sys/ucode.h>
36*d32f26eeSAndy Fiddaman #include <sys/ucode_intel.h>
37*d32f26eeSAndy Fiddaman #include <sys/ucode_amd.h>
38*d32f26eeSAndy Fiddaman #include <sys/utsname.h>
392449e17fSsherrym #include <sys/ioctl.h>
402449e17fSsherrym #include <sys/stat.h>
412449e17fSsherrym #include <unistd.h>
422449e17fSsherrym #include <dirent.h>
432449e17fSsherrym #include <fcntl.h>
442449e17fSsherrym #include <errno.h>
45*d32f26eeSAndy Fiddaman #include <stdbool.h>
462449e17fSsherrym #include <stdio.h>
472449e17fSsherrym #include <stdlib.h>
482449e17fSsherrym #include <stdarg.h>
492449e17fSsherrym #include <string.h>
502449e17fSsherrym #include <errno.h>
512449e17fSsherrym #include <syslog.h>
522449e17fSsherrym #include <time.h>
532449e17fSsherrym #include <ctype.h>
542449e17fSsherrym #include <assert.h>
552449e17fSsherrym #include <libgen.h>
56*d32f26eeSAndy Fiddaman #include <limits.h>
572449e17fSsherrym #include <locale.h>
582449e17fSsherrym #include <libintl.h>
59*d32f26eeSAndy Fiddaman #include <ucode/ucode_errno.h>
60*d32f26eeSAndy Fiddaman #include <ucode/ucode_utils_intel.h>
61*d32f26eeSAndy Fiddaman #include <ucode/ucode_utils_amd.h>
622449e17fSsherrym 
632449e17fSsherrym #define	UCODE_OPT_INSTALL	0x0001
642449e17fSsherrym #define	UCODE_OPT_UPDATE	0x0002
652449e17fSsherrym #define	UCODE_OPT_VERSION	0x0004
66be672c8eSAndy Fiddaman #define	UCODE_OPT_LIST		0x0008
672449e17fSsherrym 
682449e17fSsherrym static const char ucode_dev[] = "/dev/" UCODE_DRIVER_NAME;
692449e17fSsherrym 
702449e17fSsherrym static char	*cmdname;
712449e17fSsherrym 
72*d32f26eeSAndy Fiddaman #define	UCODE_INSTALL_COMMON_PATH ".f"
732449e17fSsherrym 
74*d32f26eeSAndy Fiddaman /*
75*d32f26eeSAndy Fiddaman  * The maximum directory path length that can be provided via -R has
76*d32f26eeSAndy Fiddaman  * to allow for appending the files within the microcode bundles.
77*d32f26eeSAndy Fiddaman  */
78*d32f26eeSAndy Fiddaman #define	UCODE_MAX_PATH_LEN (PATH_MAX - \
79*d32f26eeSAndy Fiddaman     MAX(UCODE_MAX_NAME_LEN_INTEL, UCODE_MAX_NAME_LEN_AMD) - 1)
80*d32f26eeSAndy Fiddaman 
81*d32f26eeSAndy Fiddaman static bool ucode_debug = false;
822449e17fSsherrym 
83adc586deSMark Johnson static int ucode_convert_amd(const char *, uint8_t *, size_t);
84adc586deSMark Johnson static int ucode_convert_intel(const char *, uint8_t *, size_t);
85adc586deSMark Johnson 
86adc586deSMark Johnson static ucode_errno_t ucode_gen_files_amd(uint8_t *, int, char *);
87adc586deSMark Johnson static ucode_errno_t ucode_gen_files_intel(uint8_t *, int, char *);
88adc586deSMark Johnson 
89be672c8eSAndy Fiddaman static void ucode_list_amd(uint8_t *, int);
90be672c8eSAndy Fiddaman static void ucode_list_intel(uint8_t *, int);
91be672c8eSAndy Fiddaman 
92*d32f26eeSAndy Fiddaman typedef struct ucode_source {
93*d32f26eeSAndy Fiddaman 	const char	*us_prefix;
94*d32f26eeSAndy Fiddaman 	const char	*us_vendor;
95*d32f26eeSAndy Fiddaman 	int		(*us_convert)(const char *, uint8_t *, size_t);
96*d32f26eeSAndy Fiddaman 	ucode_errno_t	(*us_gen_files)(uint8_t *, int, char *);
97*d32f26eeSAndy Fiddaman 	ucode_errno_t	(*us_validate)(uint8_t *, int);
98*d32f26eeSAndy Fiddaman 	void		(*us_list)(uint8_t *, int);
99*d32f26eeSAndy Fiddaman } ucode_source_t;
100*d32f26eeSAndy Fiddaman 
101*d32f26eeSAndy Fiddaman static const ucode_source_t ucode_sources[] = {
102be672c8eSAndy Fiddaman 	{
103*d32f26eeSAndy Fiddaman 		.us_prefix	= "intel",
104*d32f26eeSAndy Fiddaman 		.us_vendor	= "GenuineIntel",
105*d32f26eeSAndy Fiddaman 		.us_convert	= ucode_convert_intel,
106*d32f26eeSAndy Fiddaman 		.us_gen_files	= ucode_gen_files_intel,
107*d32f26eeSAndy Fiddaman 		.us_validate	= ucode_validate_intel,
108*d32f26eeSAndy Fiddaman 		.us_list	= ucode_list_intel,
109be672c8eSAndy Fiddaman 	},
110be672c8eSAndy Fiddaman 	{
111*d32f26eeSAndy Fiddaman 		.us_prefix	= "amd",
112*d32f26eeSAndy Fiddaman 		.us_vendor	= "AuthenticAMD",
113*d32f26eeSAndy Fiddaman 		.us_convert	= ucode_convert_amd,
114*d32f26eeSAndy Fiddaman 		.us_gen_files	= ucode_gen_files_amd,
115*d32f26eeSAndy Fiddaman 		.us_validate	= ucode_validate_amd,
116*d32f26eeSAndy Fiddaman 		.us_list	= ucode_list_amd,
117be672c8eSAndy Fiddaman 	},
118adc586deSMark Johnson };
119adc586deSMark Johnson 
120*d32f26eeSAndy Fiddaman const ucode_source_t *ucode;
121adc586deSMark Johnson 
1222449e17fSsherrym static void
dprintf(const char * format,...)1232449e17fSsherrym dprintf(const char *format, ...)
1242449e17fSsherrym {
1252449e17fSsherrym 	if (ucode_debug) {
1262449e17fSsherrym 		va_list alist;
1272449e17fSsherrym 		va_start(alist, format);
1282449e17fSsherrym 		(void) vfprintf(stderr, format, alist);
1292449e17fSsherrym 		va_end(alist);
1302449e17fSsherrym 	}
1312449e17fSsherrym }
1322449e17fSsherrym 
1332449e17fSsherrym static void
usage(bool verbose)134*d32f26eeSAndy Fiddaman usage(bool verbose)
1352449e17fSsherrym {
1362449e17fSsherrym 	(void) fprintf(stderr, gettext("usage:\n"));
1372449e17fSsherrym 	(void) fprintf(stderr, "\t%s -v\n", cmdname);
1382449e17fSsherrym 	if (verbose) {
1392449e17fSsherrym 		(void) fprintf(stderr,
1402449e17fSsherrym 		    gettext("\t\t Shows running microcode version.\n\n"));
1412449e17fSsherrym 	}
1422449e17fSsherrym 
143*d32f26eeSAndy Fiddaman 	(void) fprintf(stderr, "\t%s -u [-t type] microcode-file\n", cmdname);
1442449e17fSsherrym 	if (verbose) {
1452449e17fSsherrym 		(void) fprintf(stderr, gettext("\t\t Updates microcode to the "
1462449e17fSsherrym 		    "latest matching version found in\n"
147adc586deSMark Johnson 		    "\t\t microcode-file.\n\n"));
1482449e17fSsherrym 	}
1492449e17fSsherrym 
150*d32f26eeSAndy Fiddaman 	(void) fprintf(stderr, "\t%s -l [-t type] microcode-file\n", cmdname);
151be672c8eSAndy Fiddaman 	if (verbose) {
152be672c8eSAndy Fiddaman 		(void) fprintf(stderr, gettext("\t\t Displays details of the "
153be672c8eSAndy Fiddaman 		    "microcode file's contents.\n\n"));
154be672c8eSAndy Fiddaman 	}
155be672c8eSAndy Fiddaman 
156*d32f26eeSAndy Fiddaman 	(void) fprintf(stderr,
157*d32f26eeSAndy Fiddaman 	    "\t%s -i [-t type] [-R path] microcode-file\n", cmdname);
1582449e17fSsherrym 	if (verbose) {
1592449e17fSsherrym 		(void) fprintf(stderr, gettext("\t\t Installs microcode to be "
160*d32f26eeSAndy Fiddaman 		    "used for subsequent boots.\n"));
1612449e17fSsherrym 	}
162*d32f26eeSAndy Fiddaman 	(void) fprintf(stderr, gettext(
163*d32f26eeSAndy Fiddaman 	    "\nThe type of the microcode file must either be specified with "
164*d32f26eeSAndy Fiddaman 	    "the -t option\nor microcode-file must start with the vendor name "
165*d32f26eeSAndy Fiddaman 	    "prefix, either \"intel\"\nor \"amd\", so that the type can be "
166*d32f26eeSAndy Fiddaman 	    "inferred from it.\n\n"));
1672449e17fSsherrym }
1682449e17fSsherrym 
1692449e17fSsherrym static void
ucode_perror(const char * str,ucode_errno_t rc)1702449e17fSsherrym ucode_perror(const char *str, ucode_errno_t rc)
1712449e17fSsherrym {
1722449e17fSsherrym 	(void) fprintf(stderr, "%s: %s: %s\n", cmdname, str,
1732449e17fSsherrym 	    errno == 0 ? ucode_strerror(rc) : strerror(errno));
1742449e17fSsherrym 	errno = 0;
1752449e17fSsherrym }
1762449e17fSsherrym 
1772449e17fSsherrym #define	LINESIZE	120	/* copyright line sometimes is longer than 80 */
1782449e17fSsherrym 
1792449e17fSsherrym /*
1802449e17fSsherrym  * Convert text format microcode release into binary format.
1812449e17fSsherrym  * Return the number of characters read.
182be672c8eSAndy Fiddaman  *
183be672c8eSAndy Fiddaman  * AMD microcode is already in binary format.
1842449e17fSsherrym  */
1852449e17fSsherrym static int
ucode_convert_amd(const char * infile,uint8_t * buf,size_t size)186adc586deSMark Johnson ucode_convert_amd(const char *infile, uint8_t *buf, size_t size)
187adc586deSMark Johnson {
188adc586deSMark Johnson 	int fd;
189adc586deSMark Johnson 
190adc586deSMark Johnson 	if (infile == NULL || buf == NULL || size == 0)
191adc586deSMark Johnson 		return (0);
192adc586deSMark Johnson 
193adc586deSMark Johnson 	if ((fd = open(infile, O_RDONLY)) < 0)
194adc586deSMark Johnson 		return (0);
195adc586deSMark Johnson 
196adc586deSMark Johnson 	size = read(fd, buf, size);
197adc586deSMark Johnson 
198adc586deSMark Johnson 	(void) close(fd);
199adc586deSMark Johnson 
200adc586deSMark Johnson 	return (size);
201adc586deSMark Johnson }
202adc586deSMark Johnson 
203adc586deSMark Johnson static int
ucode_convert_intel(const char * infile,uint8_t * buf,size_t size)204adc586deSMark Johnson ucode_convert_intel(const char *infile, uint8_t *buf, size_t size)
2052449e17fSsherrym {
2062449e17fSsherrym 	char	linebuf[LINESIZE];
2072449e17fSsherrym 	FILE	*infd = NULL;
2082449e17fSsherrym 	int	count = 0, firstline = 1;
209be672c8eSAndy Fiddaman 	uint32_t *intbuf = (uint32_t *)(uintptr_t)buf;
2102449e17fSsherrym 
2112449e17fSsherrym 	if (infile == NULL || buf == NULL || size == 0)
2122449e17fSsherrym 		return (0);
2132449e17fSsherrym 
2142449e17fSsherrym 	if ((infd = fopen(infile, "r")) == NULL)
2152449e17fSsherrym 		return (0);
2162449e17fSsherrym 
2172449e17fSsherrym 	while (fgets(linebuf, LINESIZE, infd)) {
2182449e17fSsherrym 
2192449e17fSsherrym 		/* Check to see if we are processing a binary file */
2202449e17fSsherrym 		if (firstline && !isprint(linebuf[0])) {
2212449e17fSsherrym 			if (fseek(infd, 0, SEEK_SET) == 0)
2222449e17fSsherrym 				count = fread(buf, 1, size, infd);
2232449e17fSsherrym 
2242449e17fSsherrym 			(void) fclose(infd);
2252449e17fSsherrym 			return (count);
2262449e17fSsherrym 		}
2272449e17fSsherrym 
2282449e17fSsherrym 		firstline = 0;
2292449e17fSsherrym 
2302449e17fSsherrym 		/* Skip blank lines */
2312449e17fSsherrym 		if (strlen(linebuf) == 1)
2322449e17fSsherrym 			continue;
2332449e17fSsherrym 
2342449e17fSsherrym 		/* Skip lines with all spaces or tabs */
2352449e17fSsherrym 		if (strcspn(linebuf, " \t") == 0)
2362449e17fSsherrym 			continue;
2372449e17fSsherrym 
2382449e17fSsherrym 		/* Text file.  Skip comments. */
2392449e17fSsherrym 		if (linebuf[0] == '/')
2402449e17fSsherrym 			continue;
2412449e17fSsherrym 
2422449e17fSsherrym 		if (sscanf(linebuf, "%x, %x, %x, %x",
2432449e17fSsherrym 		    &intbuf[count], &intbuf[count+1],
2442449e17fSsherrym 		    &intbuf[count+2], &intbuf[count+3]) != 4)
2452449e17fSsherrym 			break;
2462449e17fSsherrym 
2472449e17fSsherrym 		count += 4;
2482449e17fSsherrym 	}
2492449e17fSsherrym 
2502449e17fSsherrym 	(void) fclose(infd);
2512449e17fSsherrym 
2522449e17fSsherrym 	/*
2532449e17fSsherrym 	 * If we get here, we are processing a text format file
2542449e17fSsherrym 	 * where "count" is used to count the number of integers
2552449e17fSsherrym 	 * read.  Convert it to number of characters read.
2562449e17fSsherrym 	 */
2572449e17fSsherrym 	return (count * sizeof (int));
2582449e17fSsherrym }
2592449e17fSsherrym 
2602449e17fSsherrym /*
2612449e17fSsherrym  * Returns 0 if no need to update the link; -1 otherwise
2622449e17fSsherrym  */
2632449e17fSsherrym static int
ucode_should_update_intel(char * filename,uint32_t new_rev)264adc586deSMark Johnson ucode_should_update_intel(char *filename, uint32_t new_rev)
2652449e17fSsherrym {
2662449e17fSsherrym 	int		fd;
2672449e17fSsherrym 	struct stat	statbuf;
268adc586deSMark Johnson 	ucode_header_intel_t header;
2692449e17fSsherrym 
2702449e17fSsherrym 	/*
2712449e17fSsherrym 	 * If the file or link already exists, check to see if
2722449e17fSsherrym 	 * it is necessary to update it.
2732449e17fSsherrym 	 */
2742449e17fSsherrym 	if (stat(filename, &statbuf) == 0) {
2752449e17fSsherrym 		if ((fd = open(filename, O_RDONLY)) == -1)
2762449e17fSsherrym 			return (-1);
2772449e17fSsherrym 
2782449e17fSsherrym 		if (read(fd, &header, sizeof (header)) == -1) {
2792449e17fSsherrym 			(void) close(fd);
2802449e17fSsherrym 			return (-1);
2812449e17fSsherrym 		}
2822449e17fSsherrym 
2832449e17fSsherrym 		(void) close(fd);
2842449e17fSsherrym 
2852449e17fSsherrym 		if (header.uh_rev >= new_rev)
2862449e17fSsherrym 			return (0);
2872449e17fSsherrym 	}
2882449e17fSsherrym 
2892449e17fSsherrym 	return (-1);
2902449e17fSsherrym }
2912449e17fSsherrym 
2922449e17fSsherrym /*
2932449e17fSsherrym  * Generate microcode binary files.  Must be called after ucode_validate().
2942449e17fSsherrym  */
2952449e17fSsherrym static ucode_errno_t
ucode_gen_files_amd(uint8_t * buf,int size,char * path)296adc586deSMark Johnson ucode_gen_files_amd(uint8_t *buf, int size, char *path)
297adc586deSMark Johnson {
298adc586deSMark Johnson 	uint32_t *ptr = (uint32_t *)buf;
29908d2fdc2SMark Johnson 	char common_path[PATH_MAX];
300be672c8eSAndy Fiddaman 	int fd, count, counter = 0;
301adc586deSMark Johnson 	ucode_header_amd_t *uh;
302adc586deSMark Johnson 	int last_cpu_rev = 0;
303adc586deSMark Johnson 
30408d2fdc2SMark Johnson 	/* write container file */
30508d2fdc2SMark Johnson 	(void) snprintf(common_path, PATH_MAX, "%s/%s", path, "container");
30608d2fdc2SMark Johnson 
30708d2fdc2SMark Johnson 	dprintf("path = %s\n", common_path);
30808d2fdc2SMark Johnson 	fd = open(common_path, O_WRONLY | O_CREAT | O_TRUNC,
3090ba6f73dSMark Johnson 	    S_IRUSR | S_IRGRP | S_IROTH);
3100ba6f73dSMark Johnson 
3110ba6f73dSMark Johnson 	if (fd == -1) {
31208d2fdc2SMark Johnson 		ucode_perror(common_path, EM_SYS);
3130ba6f73dSMark Johnson 		return (EM_SYS);
3140ba6f73dSMark Johnson 	}
3150ba6f73dSMark Johnson 
3160ba6f73dSMark Johnson 	if (write(fd, buf, size) != size) {
3170ba6f73dSMark Johnson 		(void) close(fd);
31808d2fdc2SMark Johnson 		ucode_perror(common_path, EM_SYS);
3190ba6f73dSMark Johnson 		return (EM_SYS);
3200ba6f73dSMark Johnson 	}
3210ba6f73dSMark Johnson 
3220ba6f73dSMark Johnson 	(void) close(fd);
3230ba6f73dSMark Johnson 
324adc586deSMark Johnson 	/* skip over magic number & equivalence table header */
325adc586deSMark Johnson 	ptr += 2; size -= 8;
326adc586deSMark Johnson 
327adc586deSMark Johnson 	count = *ptr++; size -= 4;
328adc586deSMark Johnson 
329adc586deSMark Johnson 	/* equivalence table uses special name */
33008d2fdc2SMark Johnson 	(void) snprintf(common_path, PATH_MAX, "%s/%s", path,
331*d32f26eeSAndy Fiddaman 	    UCODE_AMD_EQUIVALENCE_TABLE_NAME);
332adc586deSMark Johnson 
333adc586deSMark Johnson 	for (;;) {
33408d2fdc2SMark Johnson 		dprintf("path = %s\n", common_path);
33508d2fdc2SMark Johnson 		fd = open(common_path, O_WRONLY | O_CREAT | O_TRUNC,
336adc586deSMark Johnson 		    S_IRUSR | S_IRGRP | S_IROTH);
337adc586deSMark Johnson 
338adc586deSMark Johnson 		if (fd == -1) {
33908d2fdc2SMark Johnson 			ucode_perror(common_path, EM_SYS);
340adc586deSMark Johnson 			return (EM_SYS);
341adc586deSMark Johnson 		}
342adc586deSMark Johnson 
343adc586deSMark Johnson 		if (write(fd, ptr, count) != count) {
344adc586deSMark Johnson 			(void) close(fd);
34508d2fdc2SMark Johnson 			ucode_perror(common_path, EM_SYS);
346adc586deSMark Johnson 			return (EM_SYS);
347adc586deSMark Johnson 		}
348adc586deSMark Johnson 
349adc586deSMark Johnson 		(void) close(fd);
350adc586deSMark Johnson 		ptr += count >> 2; size -= count;
351adc586deSMark Johnson 
352adc586deSMark Johnson 		if (!size)
353adc586deSMark Johnson 			return (EM_OK);
354adc586deSMark Johnson 
355adc586deSMark Johnson 		ptr++; size -= 4;
356adc586deSMark Johnson 		count = *ptr++; size -= 4;
357adc586deSMark Johnson 
358adc586deSMark Johnson 		/* construct name from header information */
359adc586deSMark Johnson 		uh = (ucode_header_amd_t *)ptr;
360adc586deSMark Johnson 
361adc586deSMark Johnson 		if (uh->uh_cpu_rev != last_cpu_rev) {
362adc586deSMark Johnson 			last_cpu_rev = uh->uh_cpu_rev;
363adc586deSMark Johnson 			counter = 0;
364adc586deSMark Johnson 		}
365adc586deSMark Johnson 
36608d2fdc2SMark Johnson 		(void) snprintf(common_path, PATH_MAX, "%s/%04X-%02X", path,
367adc586deSMark Johnson 		    uh->uh_cpu_rev, counter++);
368adc586deSMark Johnson 	}
369adc586deSMark Johnson }
370adc586deSMark Johnson 
371adc586deSMark Johnson static ucode_errno_t
ucode_gen_files_intel(uint8_t * buf,int size,char * path)372adc586deSMark Johnson ucode_gen_files_intel(uint8_t *buf, int size, char *path)
3732449e17fSsherrym {
3742449e17fSsherrym 	int	remaining;
3752449e17fSsherrym 	char	common_path[PATH_MAX];
3762449e17fSsherrym 	DIR	*dirp;
3772449e17fSsherrym 	struct dirent *dp;
3782449e17fSsherrym 
3792449e17fSsherrym 	(void) snprintf(common_path, PATH_MAX, "%s/%s", path,
3802449e17fSsherrym 	    UCODE_INSTALL_COMMON_PATH);
3812449e17fSsherrym 
3822449e17fSsherrym 	if (mkdirp(common_path, 0755) == -1 && errno != EEXIST) {
3832449e17fSsherrym 		ucode_perror(common_path, EM_SYS);
3842449e17fSsherrym 		return (EM_SYS);
3852449e17fSsherrym 	}
3862449e17fSsherrym 
3872449e17fSsherrym 	for (remaining = size; remaining > 0; ) {
3882449e17fSsherrym 		uint32_t	total_size, body_size, offset;
3892449e17fSsherrym 		char		firstname[PATH_MAX];
3902449e17fSsherrym 		char		name[PATH_MAX];
3912449e17fSsherrym 		int		i;
3922449e17fSsherrym 		uint8_t		*curbuf = &buf[size - remaining];
393adc586deSMark Johnson 		ucode_header_intel_t	*uhp;
394adc586deSMark Johnson 		ucode_ext_table_intel_t *extp;
3952449e17fSsherrym 
396be672c8eSAndy Fiddaman 		uhp = (ucode_header_intel_t *)(uintptr_t)curbuf;
397adc586deSMark Johnson 
398adc586deSMark Johnson 		total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size);
399adc586deSMark Johnson 		body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size);
4002449e17fSsherrym 
4012449e17fSsherrym 		remaining -= total_size;
4022449e17fSsherrym 
4032449e17fSsherrym 		(void) snprintf(firstname, PATH_MAX, "%s/%08X-%02X",
4042449e17fSsherrym 		    common_path, uhp->uh_signature, uhp->uh_proc_flags);
4052449e17fSsherrym 		dprintf("firstname = %s\n", firstname);
4062449e17fSsherrym 
407adc586deSMark Johnson 		if (ucode_should_update_intel(firstname, uhp->uh_rev) != 0) {
4082449e17fSsherrym 			int fd;
4092449e17fSsherrym 
4102449e17fSsherrym 			/* Remove the existing one first */
4112449e17fSsherrym 			(void) unlink(firstname);
4122449e17fSsherrym 
4132449e17fSsherrym 			if ((fd = open(firstname, O_WRONLY | O_CREAT | O_TRUNC,
4142449e17fSsherrym 			    S_IRUSR | S_IRGRP | S_IROTH)) == -1) {
4152449e17fSsherrym 				ucode_perror(firstname, EM_SYS);
4162449e17fSsherrym 				return (EM_SYS);
4172449e17fSsherrym 			}
4182449e17fSsherrym 
4192449e17fSsherrym 			if (write(fd, curbuf, total_size) != total_size) {
4202449e17fSsherrym 				(void) close(fd);
4212449e17fSsherrym 				ucode_perror(firstname, EM_SYS);
4222449e17fSsherrym 				return (EM_SYS);
4232449e17fSsherrym 			}
4242449e17fSsherrym 
4252449e17fSsherrym 			(void) close(fd);
4262449e17fSsherrym 		}
4272449e17fSsherrym 
4282449e17fSsherrym 		/*
4292449e17fSsherrym 		 * Only 1 byte of the proc_flags field is used, therefore
4302449e17fSsherrym 		 * we only need to match 8 potential platform ids.
4312449e17fSsherrym 		 */
4322449e17fSsherrym 		for (i = 0; i < 8; i++) {
4332449e17fSsherrym 			uint32_t platid = uhp->uh_proc_flags & (1 << i);
4342449e17fSsherrym 
4352449e17fSsherrym 			if (platid == 0 && uhp->uh_proc_flags != 0)
4362449e17fSsherrym 				continue;
4372449e17fSsherrym 
4382449e17fSsherrym 			(void) snprintf(name, PATH_MAX,
4392449e17fSsherrym 			    "%s/%08X-%02X", path, uhp->uh_signature, platid);
4402449e17fSsherrym 
4412449e17fSsherrym 			dprintf("proc_flags = %x, platid = %x, name = %s\n",
4422449e17fSsherrym 			    uhp->uh_proc_flags, platid, name);
4432449e17fSsherrym 
444afcdc73aSAndy Fiddaman 			if (ucode_should_update_intel(name,
445afcdc73aSAndy Fiddaman 			    uhp->uh_rev) != 0) {
4462449e17fSsherrym 				/* Remove the existing one first */
4472449e17fSsherrym 				(void) unlink(name);
4482449e17fSsherrym 				if (link(firstname, name) == -1) {
4492449e17fSsherrym 					ucode_perror(name, EM_SYS);
4502449e17fSsherrym 					return (EM_SYS);
4512449e17fSsherrym 				}
4522449e17fSsherrym 			}
4532449e17fSsherrym 
4542449e17fSsherrym 			if (uhp->uh_proc_flags == 0)
4552449e17fSsherrym 				break;
4562449e17fSsherrym 		}
4572449e17fSsherrym 
458adc586deSMark Johnson 		offset = UCODE_HEADER_SIZE_INTEL + body_size;
4592449e17fSsherrym 
4602449e17fSsherrym 		/* Check to see if there is extended signature table */
4612449e17fSsherrym 		if (total_size == offset)
4622449e17fSsherrym 			continue;
4632449e17fSsherrym 
4642449e17fSsherrym 		/* There is extended signature table.  More processing. */
465be672c8eSAndy Fiddaman 		extp = (ucode_ext_table_intel_t *)&curbuf[offset];
4662449e17fSsherrym 
4672449e17fSsherrym 		for (i = 0; i < extp->uet_count; i++) {
468adc586deSMark Johnson 			ucode_ext_sig_intel_t *uesp = &extp->uet_ext_sig[i];
4692449e17fSsherrym 			int j;
4702449e17fSsherrym 
4712449e17fSsherrym 			for (j = 0; j < 8; j++) {
4722449e17fSsherrym 				uint32_t id = uesp->ues_proc_flags & (1 << j);
4732449e17fSsherrym 
4742449e17fSsherrym 				if (id == 0 && uesp->ues_proc_flags)
4752449e17fSsherrym 					continue;
4762449e17fSsherrym 
4772449e17fSsherrym 				(void) snprintf(name, PATH_MAX,
478afcdc73aSAndy Fiddaman 				    "%s/%08X-%02X", path,
479afcdc73aSAndy Fiddaman 				    uesp->ues_signature, id);
4802449e17fSsherrym 
481afcdc73aSAndy Fiddaman 				dprintf("extsig: proc_flags = %x, "
482afcdc73aSAndy Fiddaman 				    "platid = %x, name = %s\n",
483afcdc73aSAndy Fiddaman 				    uesp->ues_proc_flags, id, name);
4842449e17fSsherrym 
485afcdc73aSAndy Fiddaman 				if (ucode_should_update_intel(name,
486afcdc73aSAndy Fiddaman 				    uhp->uh_rev) != 0) {
4872449e17fSsherrym 					/* Remove the existing one first */
4882449e17fSsherrym 					(void) unlink(name);
4892449e17fSsherrym 					if (link(firstname, name) == -1) {
4902449e17fSsherrym 						ucode_perror(name, EM_SYS);
4912449e17fSsherrym 						return (EM_SYS);
4922449e17fSsherrym 					}
4932449e17fSsherrym 				}
4942449e17fSsherrym 
4952449e17fSsherrym 				if (uesp->ues_proc_flags == 0)
4962449e17fSsherrym 					break;
4972449e17fSsherrym 			}
4982449e17fSsherrym 		}
4992449e17fSsherrym 
5002449e17fSsherrym 	}
5012449e17fSsherrym 
5022449e17fSsherrym 	/*
5032449e17fSsherrym 	 * Remove files with no links to them.  These are probably
5042449e17fSsherrym 	 * obsolete microcode files.
5052449e17fSsherrym 	 */
5062449e17fSsherrym 	if ((dirp = opendir(common_path)) == NULL) {
5072449e17fSsherrym 		ucode_perror(common_path, EM_SYS);
5082449e17fSsherrym 		return (EM_SYS);
5092449e17fSsherrym 	}
5102449e17fSsherrym 
5112449e17fSsherrym 	while ((dp = readdir(dirp)) != NULL) {
5122449e17fSsherrym 		char filename[PATH_MAX];
5132449e17fSsherrym 		struct stat statbuf;
5142449e17fSsherrym 
5152449e17fSsherrym 		(void) snprintf(filename, PATH_MAX,
5162449e17fSsherrym 		    "%s/%s", common_path, dp->d_name);
5172449e17fSsherrym 		if (stat(filename, &statbuf) == -1)
5182449e17fSsherrym 			continue;
5192449e17fSsherrym 
5202449e17fSsherrym 		if ((statbuf.st_mode & S_IFMT) == S_IFREG) {
5212449e17fSsherrym 			if (statbuf.st_nlink == 1)
5222449e17fSsherrym 				(void) unlink(filename);
5232449e17fSsherrym 		}
5242449e17fSsherrym 	}
5252449e17fSsherrym 
5262449e17fSsherrym 	(void) closedir(dirp);
5272449e17fSsherrym 
5282449e17fSsherrym 	return (EM_OK);
5292449e17fSsherrym }
5302449e17fSsherrym 
531be672c8eSAndy Fiddaman static void
ucode_fms(uint32_t sig,uint8_t * family,uint8_t * model,uint8_t * stepping)532be672c8eSAndy Fiddaman ucode_fms(uint32_t sig, uint8_t *family, uint8_t *model, uint8_t *stepping)
533be672c8eSAndy Fiddaman {
534be672c8eSAndy Fiddaman 	*family = ((sig >> 8) & 0xf) + ((sig >> 20) & 0xff);
535be672c8eSAndy Fiddaman 	*model = ((sig >> 4) & 0xf) | ((sig >> 12) & 0xf0);
536be672c8eSAndy Fiddaman 	*stepping = sig & 0xf;
537be672c8eSAndy Fiddaman }
538be672c8eSAndy Fiddaman 
539be672c8eSAndy Fiddaman static void
ucode_list_intel(uint8_t * buf,int size)540be672c8eSAndy Fiddaman ucode_list_intel(uint8_t *buf, int size)
541be672c8eSAndy Fiddaman {
542be672c8eSAndy Fiddaman 	int	remaining;
543be672c8eSAndy Fiddaman 
544be672c8eSAndy Fiddaman 	printf("Microcode patches:\n");
545be672c8eSAndy Fiddaman 	for (remaining = size; remaining > 0; ) {
546be672c8eSAndy Fiddaman 		uint8_t *curbuf = &buf[size - remaining];
547be672c8eSAndy Fiddaman 		uint8_t family, model, stepping;
548be672c8eSAndy Fiddaman 		uint32_t total_size, body_size, offset;
549be672c8eSAndy Fiddaman 		ucode_header_intel_t *uhp;
550be672c8eSAndy Fiddaman 		ucode_ext_table_intel_t *extp;
551be672c8eSAndy Fiddaman 
552be672c8eSAndy Fiddaman 		uhp = (ucode_header_intel_t *)(uintptr_t)curbuf;
553be672c8eSAndy Fiddaman 
554be672c8eSAndy Fiddaman 		total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size);
555be672c8eSAndy Fiddaman 		body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size);
556be672c8eSAndy Fiddaman 
557be672c8eSAndy Fiddaman 		remaining -= total_size;
558be672c8eSAndy Fiddaman 
559be672c8eSAndy Fiddaman 		ucode_fms(uhp->uh_signature, &family, &model, &stepping);
560be672c8eSAndy Fiddaman 
561be672c8eSAndy Fiddaman 		printf(
562be672c8eSAndy Fiddaman 		    "    %08lX-%02lX -> Family=%02x Model=%02x Stepping=%02x\n",
563be672c8eSAndy Fiddaman 		    uhp->uh_signature, uhp->uh_proc_flags,
564be672c8eSAndy Fiddaman 		    family, model, stepping);
565be672c8eSAndy Fiddaman 		printf(
566be672c8eSAndy Fiddaman 		    "    %14s Date=%08lX Bytes=%lu\n", "",
567be672c8eSAndy Fiddaman 		    uhp->uh_date, uhp->uh_body_size);
568be672c8eSAndy Fiddaman 
569be672c8eSAndy Fiddaman 		offset = UCODE_HEADER_SIZE_INTEL + body_size;
570be672c8eSAndy Fiddaman 
571be672c8eSAndy Fiddaman 		/* Check to see if there is extended signature table */
572be672c8eSAndy Fiddaman 		if (total_size == offset)
573be672c8eSAndy Fiddaman 			continue;
574be672c8eSAndy Fiddaman 
575be672c8eSAndy Fiddaman 		printf("Extended Signature Table:\n");
576be672c8eSAndy Fiddaman 
577be672c8eSAndy Fiddaman 		extp = (ucode_ext_table_intel_t *)&curbuf[offset];
578be672c8eSAndy Fiddaman 
579be672c8eSAndy Fiddaman 		for (uint32_t i = 0; i < extp->uet_count; i++) {
580be672c8eSAndy Fiddaman 			ucode_ext_sig_intel_t *uesp = &extp->uet_ext_sig[i];
581be672c8eSAndy Fiddaman 
582be672c8eSAndy Fiddaman 			ucode_fms(uesp->ues_signature,
583be672c8eSAndy Fiddaman 			    &family, &model, &stepping);
584be672c8eSAndy Fiddaman 
585be672c8eSAndy Fiddaman 			printf(
586be672c8eSAndy Fiddaman 			    "    %08lX-%02lX -> Family=%02x Model=%02x "
587be672c8eSAndy Fiddaman 			    "Stepping=%02x\n",
588be672c8eSAndy Fiddaman 			    uesp->ues_signature, uesp->ues_proc_flags,
589be672c8eSAndy Fiddaman 			    family, model, stepping);
590be672c8eSAndy Fiddaman 		}
591be672c8eSAndy Fiddaman 	}
592be672c8eSAndy Fiddaman }
593be672c8eSAndy Fiddaman 
594be672c8eSAndy Fiddaman static void
ucode_list_amd(uint8_t * buf,int size)595be672c8eSAndy Fiddaman ucode_list_amd(uint8_t *buf, int size)
596be672c8eSAndy Fiddaman {
597be672c8eSAndy Fiddaman 	ucode_eqtbl_amd_t *eq;
598be672c8eSAndy Fiddaman 	ucode_header_amd_t *uh;
599be672c8eSAndy Fiddaman 	uint32_t tsz;
600be672c8eSAndy Fiddaman 
601be672c8eSAndy Fiddaman 	/*
602be672c8eSAndy Fiddaman 	 * The file has already been validated so we can skip straight to
603be672c8eSAndy Fiddaman 	 * the equivalence table.
604be672c8eSAndy Fiddaman 	 */
605be672c8eSAndy Fiddaman 	tsz = *(uint32_t *)(buf + 8);
606be672c8eSAndy Fiddaman 	eq = (ucode_eqtbl_amd_t *)(buf + 12);
607be672c8eSAndy Fiddaman 	size -= 12;
608be672c8eSAndy Fiddaman 
609be672c8eSAndy Fiddaman 	printf("Equivalence table:\n");
610be672c8eSAndy Fiddaman 	while (size >= sizeof (ucode_eqtbl_amd_t) && eq->ue_inst_cpu != 0) {
611be672c8eSAndy Fiddaman 		uint8_t family, model, stepping;
612be672c8eSAndy Fiddaman 
613be672c8eSAndy Fiddaman 		ucode_fms(eq->ue_inst_cpu, &family, &model, &stepping);
614be672c8eSAndy Fiddaman 
615be672c8eSAndy Fiddaman 		printf(
616be672c8eSAndy Fiddaman 		    "    %08lX Family=%02x Model=%02x Stepping=%02x -> %04X\n",
617be672c8eSAndy Fiddaman 		    eq->ue_inst_cpu, family, model, stepping, eq->ue_equiv_cpu);
618be672c8eSAndy Fiddaman 		eq++;
619be672c8eSAndy Fiddaman 		size -= sizeof (*eq);
620be672c8eSAndy Fiddaman 	}
621be672c8eSAndy Fiddaman 
622be672c8eSAndy Fiddaman 	/* Move past the equivalence table terminating record */
623be672c8eSAndy Fiddaman 	eq++;
624be672c8eSAndy Fiddaman 	size -= sizeof (*eq);
625be672c8eSAndy Fiddaman 	buf = (uint8_t *)eq;
626be672c8eSAndy Fiddaman 
627be672c8eSAndy Fiddaman 	printf("Microcode patches:\n");
628be672c8eSAndy Fiddaman 	while (size > sizeof (ucode_header_amd_t) + 8) {
629be672c8eSAndy Fiddaman 		tsz = *(uint32_t *)(buf + 4);
630be672c8eSAndy Fiddaman 		uh = (ucode_header_amd_t *)(buf + 8);
631be672c8eSAndy Fiddaman 
632be672c8eSAndy Fiddaman 		if (uh->uh_cpu_rev == 0)
633be672c8eSAndy Fiddaman 			break;
634be672c8eSAndy Fiddaman 
635be672c8eSAndy Fiddaman 		printf("    %4X -> Patch=%08lX Date=%08lX Bytes=%lu\n",
636be672c8eSAndy Fiddaman 		    uh->uh_cpu_rev, uh->uh_patch_id, uh->uh_date, tsz);
637be672c8eSAndy Fiddaman 
638be672c8eSAndy Fiddaman 		buf += (tsz + 8);
639be672c8eSAndy Fiddaman 		size -= (tsz + 8);
640be672c8eSAndy Fiddaman 	}
641be672c8eSAndy Fiddaman }
642be672c8eSAndy Fiddaman 
6432449e17fSsherrym /*
6442449e17fSsherrym  * Returns 0 on success, 2 on usage error, and 3 on operation error.
6452449e17fSsherrym  */
6462449e17fSsherrym int
main(int argc,char * argv[])6472449e17fSsherrym main(int argc, char *argv[])
6482449e17fSsherrym {
6492449e17fSsherrym 	int	c;
6502449e17fSsherrym 	int	action = 0;
6512449e17fSsherrym 	int	actcount = 0;
652*d32f26eeSAndy Fiddaman 	int	typeindex = -1;
6532449e17fSsherrym 	char	*path = NULL;
6542449e17fSsherrym 	char	*filename = NULL;
6552449e17fSsherrym 	int	errflg = 0;
6562449e17fSsherrym 	int	dev_fd = -1;
6572449e17fSsherrym 	int	fd = -1;
658*d32f26eeSAndy Fiddaman 	bool	verbose = false;
659*d32f26eeSAndy Fiddaman 	bool	needfile = false;
6602449e17fSsherrym 	uint8_t	*buf = NULL;
6612449e17fSsherrym 	ucode_errno_t	rc = EM_OK;
6622449e17fSsherrym 	processorid_t	cpuid_max;
6632449e17fSsherrym 	struct stat filestat;
664be672c8eSAndy Fiddaman 	int ucode_size = 0;
6652449e17fSsherrym 
6662449e17fSsherrym 	(void) setlocale(LC_ALL, "");
6672449e17fSsherrym 
6682449e17fSsherrym #if !defined(TEXT_DOMAIN)
6692449e17fSsherrym #define	TEXT_DOMAIN "SYS_TEST"
6702449e17fSsherrym #endif
6712449e17fSsherrym 	(void) textdomain(TEXT_DOMAIN);
6722449e17fSsherrym 
6732449e17fSsherrym 	cmdname = basename(argv[0]);
6742449e17fSsherrym 
675*d32f26eeSAndy Fiddaman 	while ((c = getopt(argc, argv, "idhluvVR:t:")) != EOF) {
6762449e17fSsherrym 		switch (c) {
6772449e17fSsherrym 
678*d32f26eeSAndy Fiddaman 		case 'd':
679*d32f26eeSAndy Fiddaman 			ucode_debug = true;
680*d32f26eeSAndy Fiddaman 			break;
681*d32f26eeSAndy Fiddaman 
6822449e17fSsherrym 		case 'i':
6832449e17fSsherrym 			action |= UCODE_OPT_INSTALL;
6842449e17fSsherrym 			actcount++;
685*d32f26eeSAndy Fiddaman 			needfile = true;
6862449e17fSsherrym 			break;
6872449e17fSsherrym 
688be672c8eSAndy Fiddaman 		case 'l':
689be672c8eSAndy Fiddaman 			action |= UCODE_OPT_LIST;
690be672c8eSAndy Fiddaman 			actcount++;
691*d32f26eeSAndy Fiddaman 			needfile = true;
692*d32f26eeSAndy Fiddaman 			break;
693*d32f26eeSAndy Fiddaman 
694*d32f26eeSAndy Fiddaman 		case 't':
695*d32f26eeSAndy Fiddaman 			if (typeindex != -1) {
696*d32f26eeSAndy Fiddaman 				(void) fprintf(stderr, gettext(
697*d32f26eeSAndy Fiddaman 				    "-t can only be specified once\n"));
698*d32f26eeSAndy Fiddaman 				errflg++;
699*d32f26eeSAndy Fiddaman 				break;
700*d32f26eeSAndy Fiddaman 			}
701*d32f26eeSAndy Fiddaman 			for (uint_t i = 0; i < ARRAY_SIZE(ucode_sources); i++) {
702*d32f26eeSAndy Fiddaman 				if (strcmp(optarg,
703*d32f26eeSAndy Fiddaman 				    ucode_sources[i].us_prefix) == 0) {
704*d32f26eeSAndy Fiddaman 					typeindex = i;
705*d32f26eeSAndy Fiddaman 					break;
706*d32f26eeSAndy Fiddaman 				}
707*d32f26eeSAndy Fiddaman 			}
708*d32f26eeSAndy Fiddaman 			if (typeindex == -1) {
709*d32f26eeSAndy Fiddaman 				(void) fprintf(stderr,
710*d32f26eeSAndy Fiddaman 				    gettext("Unknown microcode type, %s\n"),
711*d32f26eeSAndy Fiddaman 				    optarg);
712*d32f26eeSAndy Fiddaman 				errflg++;
713*d32f26eeSAndy Fiddaman 			}
714be672c8eSAndy Fiddaman 			break;
715be672c8eSAndy Fiddaman 
7162449e17fSsherrym 		case 'u':
7172449e17fSsherrym 			action |= UCODE_OPT_UPDATE;
7182449e17fSsherrym 			actcount++;
719*d32f26eeSAndy Fiddaman 			needfile = true;
7202449e17fSsherrym 			break;
7212449e17fSsherrym 
7222449e17fSsherrym 		case 'v':
7232449e17fSsherrym 			action |= UCODE_OPT_VERSION;
7242449e17fSsherrym 			actcount++;
7252449e17fSsherrym 			break;
7262449e17fSsherrym 
7272449e17fSsherrym 		case 'R':
728be672c8eSAndy Fiddaman 			if (optarg[0] == '-') {
7292449e17fSsherrym 				errflg++;
730be672c8eSAndy Fiddaman 			} else if (strlen(optarg) > UCODE_MAX_PATH_LEN) {
7312449e17fSsherrym 				(void) fprintf(stderr,
7322449e17fSsherrym 				    gettext("Alternate path too long\n"));
7332449e17fSsherrym 				errflg++;
7342449e17fSsherrym 			} else if ((path = strdup(optarg)) == NULL) {
7352449e17fSsherrym 				errflg++;
7362449e17fSsherrym 			}
7372449e17fSsherrym 
7382449e17fSsherrym 			break;
7392449e17fSsherrym 
7402449e17fSsherrym 		case 'V':
741*d32f26eeSAndy Fiddaman 			verbose = true;
7422449e17fSsherrym 			break;
7432449e17fSsherrym 
7442449e17fSsherrym 		case 'h':
745*d32f26eeSAndy Fiddaman 			usage(true);
7462449e17fSsherrym 			return (0);
7472449e17fSsherrym 
7482449e17fSsherrym 		default:
7492449e17fSsherrym 			usage(verbose);
7502449e17fSsherrym 			return (2);
7512449e17fSsherrym 		}
7522449e17fSsherrym 	}
7532449e17fSsherrym 
754be672c8eSAndy Fiddaman 	if (actcount == 0) {
755be672c8eSAndy Fiddaman 		(void) fprintf(stderr, gettext("%s: One of -i, -l, -u or -v "
756be672c8eSAndy Fiddaman 		    "must be provided.\n"), cmdname);
757be672c8eSAndy Fiddaman 		usage(verbose);
758be672c8eSAndy Fiddaman 		return (2);
759be672c8eSAndy Fiddaman 	}
760be672c8eSAndy Fiddaman 
7612449e17fSsherrym 	if (actcount != 1) {
762be672c8eSAndy Fiddaman 		(void) fprintf(stderr, gettext("%s: options -i, -l, -u and -v "
7632449e17fSsherrym 		    "are mutually exclusive.\n"), cmdname);
7642449e17fSsherrym 		usage(verbose);
7652449e17fSsherrym 		return (2);
7662449e17fSsherrym 	}
7672449e17fSsherrym 
768*d32f26eeSAndy Fiddaman 	if (typeindex != -1 && !needfile) {
769*d32f26eeSAndy Fiddaman 		(void) fprintf(stderr, gettext("%s: option -t requires one of "
770*d32f26eeSAndy Fiddaman 		    "-i, -l or -u\n"), cmdname);
771*d32f26eeSAndy Fiddaman 		usage(verbose);
772*d32f26eeSAndy Fiddaman 		return (2);
773*d32f26eeSAndy Fiddaman 	}
774*d32f26eeSAndy Fiddaman 
7752449e17fSsherrym 	if (optind <= argc - 1)
7762449e17fSsherrym 		filename = argv[optind];
777*d32f26eeSAndy Fiddaman 	else if (needfile)
7782449e17fSsherrym 		errflg++;
7792449e17fSsherrym 
7802449e17fSsherrym 	if (errflg || action == 0) {
7812449e17fSsherrym 		usage(verbose);
7822449e17fSsherrym 		return (2);
7832449e17fSsherrym 	}
7842449e17fSsherrym 
7852449e17fSsherrym 	/*
786*d32f26eeSAndy Fiddaman 	 * Convert from the vendor-shipped format to individual microcode files.
7872449e17fSsherrym 	 */
788*d32f26eeSAndy Fiddaman 	if (needfile) {
789*d32f26eeSAndy Fiddaman 		if (typeindex != -1) {
790*d32f26eeSAndy Fiddaman 			ucode = &ucode_sources[typeindex];
791*d32f26eeSAndy Fiddaman 		} else {
792*d32f26eeSAndy Fiddaman 			for (uint_t i = 0; i < ARRAY_SIZE(ucode_sources); i++) {
793*d32f26eeSAndy Fiddaman 				const ucode_source_t *src = &ucode_sources[i];
794adc586deSMark Johnson 
795adc586deSMark Johnson 				dprintf("i = %d, filestr = %s, filename = %s\n",
796*d32f26eeSAndy Fiddaman 				    i, src->us_prefix, filename);
797*d32f26eeSAndy Fiddaman 				if (strncasecmp(src->us_prefix,
798adc586deSMark Johnson 				    basename(filename),
799*d32f26eeSAndy Fiddaman 				    strlen(src->us_prefix)) == 0) {
800*d32f26eeSAndy Fiddaman 					ucode = src;
801adc586deSMark Johnson 					break;
802adc586deSMark Johnson 				}
803adc586deSMark Johnson 			}
804*d32f26eeSAndy Fiddaman 		}
805adc586deSMark Johnson 
806*d32f26eeSAndy Fiddaman 		if (ucode == NULL) {
807adc586deSMark Johnson 			rc = EM_NOVENDOR;
808*d32f26eeSAndy Fiddaman 			(void) fprintf(stderr, "%s: %s.\n\n"
809*d32f26eeSAndy Fiddaman 			    "Either specify the type with the -t option, "
810*d32f26eeSAndy Fiddaman 			    "or rename the file such that\nits name begins "
811*d32f26eeSAndy Fiddaman 			    "with a vendor string.\n",
812*d32f26eeSAndy Fiddaman 			    cmdname, ucode_strerror(rc));
813be672c8eSAndy Fiddaman 			goto out;
814adc586deSMark Johnson 		}
815adc586deSMark Johnson 
816*d32f26eeSAndy Fiddaman 		dprintf("Selected microcode type %s (%s)\n",
817*d32f26eeSAndy Fiddaman 		    ucode->us_prefix, ucode->us_vendor);
818*d32f26eeSAndy Fiddaman 
8192449e17fSsherrym 		if ((stat(filename, &filestat)) < 0) {
8202449e17fSsherrym 			rc = EM_SYS;
8212449e17fSsherrym 			ucode_perror(filename, rc);
822be672c8eSAndy Fiddaman 			goto out;
8232449e17fSsherrym 		}
8242449e17fSsherrym 
8252449e17fSsherrym 		if ((filestat.st_mode & S_IFMT) != S_IFREG &&
8262449e17fSsherrym 		    (filestat.st_mode & S_IFMT) != S_IFLNK) {
8272449e17fSsherrym 			rc = EM_FILEFORMAT;
8282449e17fSsherrym 			ucode_perror(filename, rc);
829be672c8eSAndy Fiddaman 			goto out;
8302449e17fSsherrym 		}
8312449e17fSsherrym 
8322449e17fSsherrym 		if ((buf = malloc(filestat.st_size)) == NULL) {
8332449e17fSsherrym 			rc = EM_SYS;
8342449e17fSsherrym 			ucode_perror(filename, rc);
835be672c8eSAndy Fiddaman 			goto out;
8362449e17fSsherrym 		}
8372449e17fSsherrym 
838*d32f26eeSAndy Fiddaman 		ucode_size = ucode->us_convert(filename, buf, filestat.st_size);
8392449e17fSsherrym 
8402449e17fSsherrym 		dprintf("ucode_size = %d\n", ucode_size);
8412449e17fSsherrym 
8422449e17fSsherrym 		if (ucode_size == 0) {
8432449e17fSsherrym 			rc = EM_FILEFORMAT;
8442449e17fSsherrym 			ucode_perror(filename, rc);
845be672c8eSAndy Fiddaman 			goto out;
8462449e17fSsherrym 		}
8472449e17fSsherrym 
848*d32f26eeSAndy Fiddaman 		if ((rc = ucode->us_validate(buf, ucode_size)) != EM_OK) {
8492449e17fSsherrym 			ucode_perror(filename, rc);
850be672c8eSAndy Fiddaman 			goto out;
8512449e17fSsherrym 		}
8522449e17fSsherrym 	}
8532449e17fSsherrym 
854be672c8eSAndy Fiddaman 	if (action & UCODE_OPT_LIST) {
855*d32f26eeSAndy Fiddaman 		ucode->us_list(buf, ucode_size);
856be672c8eSAndy Fiddaman 		goto out;
857be672c8eSAndy Fiddaman 	}
858be672c8eSAndy Fiddaman 
8592449e17fSsherrym 	if (action & UCODE_OPT_INSTALL) {
8602449e17fSsherrym 		/*
8612449e17fSsherrym 		 * If no path is provided by the -R option, put the files in
862*d32f26eeSAndy Fiddaman 		 * /platform/<arch>/ucode/<ucode_vendor_str>/.
8632449e17fSsherrym 		 */
8642449e17fSsherrym 		if (path == NULL) {
865*d32f26eeSAndy Fiddaman 			struct utsname uts;
866*d32f26eeSAndy Fiddaman 
867*d32f26eeSAndy Fiddaman 			if (uname(&uts) == -1) {
868*d32f26eeSAndy Fiddaman 				perror("Unable to retrieve system uname");
869*d32f26eeSAndy Fiddaman 				goto out;
870*d32f26eeSAndy Fiddaman 			}
871*d32f26eeSAndy Fiddaman 
8722449e17fSsherrym 			if ((path = malloc(PATH_MAX)) == NULL) {
8732449e17fSsherrym 				rc = EM_SYS;
8742449e17fSsherrym 				ucode_perror("malloc", rc);
875be672c8eSAndy Fiddaman 				goto out;
8762449e17fSsherrym 			}
8772449e17fSsherrym 
878*d32f26eeSAndy Fiddaman 			(void) snprintf(path, PATH_MAX, "/platform/%s/ucode/%s",
879*d32f26eeSAndy Fiddaman 			    uts.machine, ucode->us_vendor);
8802449e17fSsherrym 		}
8812449e17fSsherrym 
8822449e17fSsherrym 		if (mkdirp(path, 0755) == -1 && errno != EEXIST) {
8832449e17fSsherrym 			rc = EM_SYS;
8842449e17fSsherrym 			ucode_perror(path, rc);
885be672c8eSAndy Fiddaman 			goto out;
8862449e17fSsherrym 		}
8872449e17fSsherrym 
888*d32f26eeSAndy Fiddaman 		rc = ucode->us_gen_files(buf, ucode_size, path);
8892449e17fSsherrym 
890be672c8eSAndy Fiddaman 		goto out;
8912449e17fSsherrym 	}
8922449e17fSsherrym 
8932449e17fSsherrym 	if ((dev_fd = open(ucode_dev, O_RDONLY)) == -1) {
8942449e17fSsherrym 		rc = EM_SYS;
8952449e17fSsherrym 		ucode_perror(ucode_dev, rc);
896be672c8eSAndy Fiddaman 		goto out;
8972449e17fSsherrym 	}
8982449e17fSsherrym 
8992449e17fSsherrym 	if (action & UCODE_OPT_VERSION) {
9002449e17fSsherrym 		int tmprc;
9012449e17fSsherrym 		uint32_t *revp = NULL;
9022449e17fSsherrym 		int i;
9032449e17fSsherrym 		struct ucode_get_rev_struct info;
9042449e17fSsherrym 
9052449e17fSsherrym 		cpuid_max = (processorid_t)sysconf(_SC_CPUID_MAX);
9062449e17fSsherrym 
9072449e17fSsherrym 		if ((revp = (uint32_t *)
9082449e17fSsherrym 		    malloc(cpuid_max * sizeof (uint32_t))) == NULL) {
9092449e17fSsherrym 			rc = EM_SYS;
9102449e17fSsherrym 			ucode_perror("malloc", rc);
911be672c8eSAndy Fiddaman 			goto out;
9122449e17fSsherrym 		}
9132449e17fSsherrym 
9142449e17fSsherrym 		for (i = 0; i < cpuid_max; i++)
9152449e17fSsherrym 			revp[i] = (uint32_t)-1;
9162449e17fSsherrym 
9172449e17fSsherrym 		info.ugv_rev = revp;
9182449e17fSsherrym 		info.ugv_size = cpuid_max;
9192449e17fSsherrym 		info.ugv_errno = EM_OK;
9202449e17fSsherrym 		tmprc = ioctl(dev_fd, UCODE_GET_VERSION, &info);
9212449e17fSsherrym 		rc = info.ugv_errno;
9222449e17fSsherrym 
9232449e17fSsherrym 		if (tmprc && rc == EM_OK) {
9242449e17fSsherrym 			rc = EM_SYS;
9252449e17fSsherrym 		}
9262449e17fSsherrym 
9272449e17fSsherrym 		if (rc == EM_OK) {
9282449e17fSsherrym 			(void) printf(gettext("CPU\tMicrocode Version\n"));
9292449e17fSsherrym 			for (i = 0; i < cpuid_max; i++) {
9302449e17fSsherrym 				if (info.ugv_rev[i] == (uint32_t)-1)
9312449e17fSsherrym 					continue;
9322449e17fSsherrym 				(void) printf("%d\t0x%x\n", i, info.ugv_rev[i]);
9332449e17fSsherrym 			}
9342449e17fSsherrym 		} else {
9352449e17fSsherrym 			ucode_perror(gettext("get microcode version"), rc);
9362449e17fSsherrym 		}
9372449e17fSsherrym 
9382449e17fSsherrym 		if (revp)
9392449e17fSsherrym 			free(revp);
9402449e17fSsherrym 	}
9412449e17fSsherrym 
9422449e17fSsherrym 	if (action & UCODE_OPT_UPDATE) {
9432449e17fSsherrym 		int tmprc;
9442449e17fSsherrym 		struct ucode_write_struct uw_struct;
9452449e17fSsherrym 
9462449e17fSsherrym 		uw_struct.uw_size = ucode_size;
9472449e17fSsherrym 		uw_struct.uw_ucode = buf;
9482449e17fSsherrym 		uw_struct.uw_errno = EM_OK;
9492449e17fSsherrym 		tmprc = ioctl(dev_fd, UCODE_UPDATE, &uw_struct);
9502449e17fSsherrym 		rc = uw_struct.uw_errno;
9512449e17fSsherrym 
9522449e17fSsherrym 		if (rc == EM_OK) {
9532449e17fSsherrym 			if (tmprc) {
9542449e17fSsherrym 				rc = EM_SYS;
9552449e17fSsherrym 				ucode_perror(ucode_dev, rc);
9562449e17fSsherrym 			}
9572449e17fSsherrym 		} else if (rc == EM_NOMATCH || rc == EM_HIGHERREV) {
9582449e17fSsherrym 			ucode_perror(filename, rc);
9592449e17fSsherrym 		} else {
9602449e17fSsherrym 			ucode_perror(gettext("microcode update"), rc);
9612449e17fSsherrym 		}
9622449e17fSsherrym 	}
9632449e17fSsherrym 
964be672c8eSAndy Fiddaman out:
9652449e17fSsherrym 	if (dev_fd != -1)
9662449e17fSsherrym 		(void) close(dev_fd);
9672449e17fSsherrym 
9682449e17fSsherrym 	if (fd != -1)
9692449e17fSsherrym 		(void) close(fd);
9702449e17fSsherrym 
9712449e17fSsherrym 	free(buf);
9722449e17fSsherrym 	free(path);
9732449e17fSsherrym 
9742449e17fSsherrym 	if (rc != EM_OK)
9752449e17fSsherrym 		return (3);
9762449e17fSsherrym 
9772449e17fSsherrym 	return (0);
9782449e17fSsherrym }
979