xref: /onnv-gate/usr/src/cmd/ucodeadm/ucodeadm.c (revision 8836:61649353c39d)
14581Ssherrym /*
24581Ssherrym  * CDDL HEADER START
34581Ssherrym  *
44581Ssherrym  * The contents of this file are subject to the terms of the
54581Ssherrym  * Common Development and Distribution License (the "License").
64581Ssherrym  * You may not use this file except in compliance with the License.
74581Ssherrym  *
84581Ssherrym  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94581Ssherrym  * or http://www.opensolaris.org/os/licensing.
104581Ssherrym  * See the License for the specific language governing permissions
114581Ssherrym  * and limitations under the License.
124581Ssherrym  *
134581Ssherrym  * When distributing Covered Code, include this CDDL HEADER in each
144581Ssherrym  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154581Ssherrym  * If applicable, add the following below this CDDL HEADER, with the
164581Ssherrym  * fields enclosed by brackets "[]" replaced with your own identifying
174581Ssherrym  * information: Portions Copyright [yyyy] [name of copyright owner]
184581Ssherrym  *
194581Ssherrym  * CDDL HEADER END
204581Ssherrym  */
214581Ssherrym /*
228647SMark.Johnson@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
234581Ssherrym  * Use is subject to license terms.
244581Ssherrym  */
254581Ssherrym 
264581Ssherrym #include <sys/types.h>
274581Ssherrym #include <sys/processor.h>
284581Ssherrym #include <sys/ucode.h>
294581Ssherrym #include <sys/ioctl.h>
304581Ssherrym #include <sys/stat.h>
314581Ssherrym #include <unistd.h>
324581Ssherrym #include <dirent.h>
334581Ssherrym #include <fcntl.h>
344581Ssherrym #include <errno.h>
354581Ssherrym #include <stdio.h>
364581Ssherrym #include <stdlib.h>
374581Ssherrym #include <stdarg.h>
384581Ssherrym #include <string.h>
394581Ssherrym #include <errno.h>
404581Ssherrym #include <syslog.h>
414581Ssherrym #include <time.h>
424581Ssherrym #include <ctype.h>
434581Ssherrym #include <assert.h>
444581Ssherrym #include <libgen.h>
454581Ssherrym #include <locale.h>
464581Ssherrym #include <libintl.h>
474581Ssherrym 
484581Ssherrym #define	UCODE_OPT_INSTALL	0x0001
494581Ssherrym #define	UCODE_OPT_UPDATE	0x0002
504581Ssherrym #define	UCODE_OPT_VERSION	0x0004
514581Ssherrym 
524581Ssherrym static const char ucode_dev[] = "/dev/" UCODE_DRIVER_NAME;
534581Ssherrym 
544581Ssherrym static char	*cmdname;
554581Ssherrym 
564581Ssherrym static char	ucode_vendor_str[UCODE_MAX_VENDORS_NAME_LEN];
574581Ssherrym static char	ucode_install_path[] = UCODE_INSTALL_PATH;
584581Ssherrym 
594581Ssherrym static int	ucode_debug = 0;
604581Ssherrym 
617605SMark.Johnson@Sun.COM static int ucode_convert_amd(const char *, uint8_t *, size_t);
627605SMark.Johnson@Sun.COM static int ucode_convert_intel(const char *, uint8_t *, size_t);
637605SMark.Johnson@Sun.COM 
647605SMark.Johnson@Sun.COM static ucode_errno_t ucode_gen_files_amd(uint8_t *, int, char *);
657605SMark.Johnson@Sun.COM static ucode_errno_t ucode_gen_files_intel(uint8_t *, int, char *);
667605SMark.Johnson@Sun.COM 
677605SMark.Johnson@Sun.COM static const struct ucode_ops ucode_ops[] = {
687605SMark.Johnson@Sun.COM 	{ ucode_convert_intel, ucode_gen_files_intel, ucode_validate_intel },
697605SMark.Johnson@Sun.COM 	{ ucode_convert_amd, ucode_gen_files_amd, ucode_validate_amd },
707605SMark.Johnson@Sun.COM };
717605SMark.Johnson@Sun.COM 
727605SMark.Johnson@Sun.COM const struct ucode_ops *ucode;
737605SMark.Johnson@Sun.COM 
744581Ssherrym static void
dprintf(const char * format,...)754581Ssherrym dprintf(const char *format, ...)
764581Ssherrym {
774581Ssherrym 	if (ucode_debug) {
784581Ssherrym 		va_list alist;
794581Ssherrym 		va_start(alist, format);
804581Ssherrym 		(void) vfprintf(stderr, format, alist);
814581Ssherrym 		va_end(alist);
824581Ssherrym 	}
834581Ssherrym }
844581Ssherrym 
854581Ssherrym static void
usage(int verbose)864581Ssherrym usage(int verbose)
874581Ssherrym {
884581Ssherrym 	(void) fprintf(stderr, gettext("usage:\n"));
894581Ssherrym 	(void) fprintf(stderr, "\t%s -v\n", cmdname);
904581Ssherrym 	if (verbose) {
914581Ssherrym 		(void) fprintf(stderr,
924581Ssherrym 		    gettext("\t\t Shows running microcode version.\n\n"));
934581Ssherrym 	}
944581Ssherrym 
957605SMark.Johnson@Sun.COM 	(void) fprintf(stderr, "\t%s -u microcode-file\n", cmdname);
964581Ssherrym 	if (verbose) {
974581Ssherrym 		(void) fprintf(stderr, gettext("\t\t Updates microcode to the "
984581Ssherrym 		    "latest matching version found in\n"
997605SMark.Johnson@Sun.COM 		    "\t\t microcode-file.\n\n"));
1004581Ssherrym 	}
1014581Ssherrym 
1027605SMark.Johnson@Sun.COM 	(void) fprintf(stderr, "\t%s -i [-R path] microcode-file\n", cmdname);
1034581Ssherrym 	if (verbose) {
1044581Ssherrym 		(void) fprintf(stderr, gettext("\t\t Installs microcode to be "
1057605SMark.Johnson@Sun.COM 		    "used for subsequent boots.\n\n"));
1067605SMark.Johnson@Sun.COM 		(void) fprintf(stderr, gettext("Microcode file name must start "
1077605SMark.Johnson@Sun.COM 		    "with vendor name, such as \"intel\" or \"amd\".\n\n"));
1084581Ssherrym 	}
1094581Ssherrym }
1104581Ssherrym 
1114581Ssherrym static void
ucode_perror(const char * str,ucode_errno_t rc)1124581Ssherrym ucode_perror(const char *str, ucode_errno_t rc)
1134581Ssherrym {
1144581Ssherrym 	(void) fprintf(stderr, "%s: %s: %s\n", cmdname, str,
1154581Ssherrym 	    errno == 0 ? ucode_strerror(rc) : strerror(errno));
1164581Ssherrym 	errno = 0;
1174581Ssherrym }
1184581Ssherrym 
1194581Ssherrym #define	LINESIZE	120	/* copyright line sometimes is longer than 80 */
1204581Ssherrym 
1214581Ssherrym /*
1224581Ssherrym  * Convert text format microcode release into binary format.
1234581Ssherrym  * Return the number of characters read.
1244581Ssherrym  */
1254581Ssherrym static int
ucode_convert_amd(const char * infile,uint8_t * buf,size_t size)1267605SMark.Johnson@Sun.COM ucode_convert_amd(const char *infile, uint8_t *buf, size_t size)
1277605SMark.Johnson@Sun.COM {
1287605SMark.Johnson@Sun.COM 	int fd;
1297605SMark.Johnson@Sun.COM 
1307605SMark.Johnson@Sun.COM 	if (infile == NULL || buf == NULL || size == 0)
1317605SMark.Johnson@Sun.COM 		return (0);
1327605SMark.Johnson@Sun.COM 
1337605SMark.Johnson@Sun.COM 	if ((fd = open(infile, O_RDONLY)) < 0)
1347605SMark.Johnson@Sun.COM 		return (0);
1357605SMark.Johnson@Sun.COM 
1367605SMark.Johnson@Sun.COM 	size = read(fd, buf, size);
1377605SMark.Johnson@Sun.COM 
1387605SMark.Johnson@Sun.COM 	(void) close(fd);
1397605SMark.Johnson@Sun.COM 
1407605SMark.Johnson@Sun.COM 	return (size);
1417605SMark.Johnson@Sun.COM }
1427605SMark.Johnson@Sun.COM 
1437605SMark.Johnson@Sun.COM static int
ucode_convert_intel(const char * infile,uint8_t * buf,size_t size)1447605SMark.Johnson@Sun.COM ucode_convert_intel(const char *infile, uint8_t *buf, size_t size)
1454581Ssherrym {
1464581Ssherrym 	char	linebuf[LINESIZE];
1474581Ssherrym 	FILE	*infd = NULL;
1484581Ssherrym 	int	count = 0, firstline = 1;
1494581Ssherrym 	uint32_t *intbuf = (uint32_t *)(intptr_t)buf;
1504581Ssherrym 
1514581Ssherrym 	if (infile == NULL || buf == NULL || size == 0)
1524581Ssherrym 		return (0);
1534581Ssherrym 
1544581Ssherrym 	if ((infd = fopen(infile, "r")) == NULL)
1554581Ssherrym 		return (0);
1564581Ssherrym 
1574581Ssherrym 	while (fgets(linebuf, LINESIZE, infd)) {
1584581Ssherrym 
1594581Ssherrym 		/* Check to see if we are processing a binary file */
1604581Ssherrym 		if (firstline && !isprint(linebuf[0])) {
1614581Ssherrym 			if (fseek(infd, 0, SEEK_SET) == 0)
1624581Ssherrym 				count = fread(buf, 1, size, infd);
1634581Ssherrym 
1644581Ssherrym 			(void) fclose(infd);
1654581Ssherrym 			return (count);
1664581Ssherrym 		}
1674581Ssherrym 
1684581Ssherrym 		firstline = 0;
1694581Ssherrym 
1704581Ssherrym 		/* Skip blank lines */
1714581Ssherrym 		if (strlen(linebuf) == 1)
1724581Ssherrym 			continue;
1734581Ssherrym 
1744581Ssherrym 		/* Skip lines with all spaces or tabs */
1754581Ssherrym 		if (strcspn(linebuf, " \t") == 0)
1764581Ssherrym 			continue;
1774581Ssherrym 
1784581Ssherrym 		/* Text file.  Skip comments. */
1794581Ssherrym 		if (linebuf[0] == '/')
1804581Ssherrym 			continue;
1814581Ssherrym 
1824581Ssherrym 		if (sscanf(linebuf, "%x, %x, %x, %x",
1834581Ssherrym 		    &intbuf[count], &intbuf[count+1],
1844581Ssherrym 		    &intbuf[count+2], &intbuf[count+3]) != 4)
1854581Ssherrym 			break;
1864581Ssherrym 
1874581Ssherrym 		count += 4;
1884581Ssherrym 	}
1894581Ssherrym 
1904581Ssherrym 	(void) fclose(infd);
1914581Ssherrym 
1924581Ssherrym 	/*
1934581Ssherrym 	 * If we get here, we are processing a text format file
1944581Ssherrym 	 * where "count" is used to count the number of integers
1954581Ssherrym 	 * read.  Convert it to number of characters read.
1964581Ssherrym 	 */
1974581Ssherrym 	return (count * sizeof (int));
1984581Ssherrym }
1994581Ssherrym 
2004581Ssherrym /*
2014581Ssherrym  * Returns 0 if no need to update the link; -1 otherwise
2024581Ssherrym  */
2034581Ssherrym static int
ucode_should_update_intel(char * filename,uint32_t new_rev)2047605SMark.Johnson@Sun.COM ucode_should_update_intel(char *filename, uint32_t new_rev)
2054581Ssherrym {
2064581Ssherrym 	int		fd;
2074581Ssherrym 	struct stat	statbuf;
2087605SMark.Johnson@Sun.COM 	ucode_header_intel_t header;
2094581Ssherrym 
2104581Ssherrym 	/*
2114581Ssherrym 	 * If the file or link already exists, check to see if
2124581Ssherrym 	 * it is necessary to update it.
2134581Ssherrym 	 */
2144581Ssherrym 	if (stat(filename, &statbuf) == 0) {
2154581Ssherrym 		if ((fd = open(filename, O_RDONLY)) == -1)
2164581Ssherrym 			return (-1);
2174581Ssherrym 
2184581Ssherrym 		if (read(fd, &header, sizeof (header)) == -1) {
2194581Ssherrym 			(void) close(fd);
2204581Ssherrym 			return (-1);
2214581Ssherrym 		}
2224581Ssherrym 
2234581Ssherrym 		(void) close(fd);
2244581Ssherrym 
2254581Ssherrym 		if (header.uh_rev >= new_rev)
2264581Ssherrym 			return (0);
2274581Ssherrym 	}
2284581Ssherrym 
2294581Ssherrym 	return (-1);
2304581Ssherrym }
2314581Ssherrym 
2324581Ssherrym /*
2334581Ssherrym  * Generate microcode binary files.  Must be called after ucode_validate().
2344581Ssherrym  */
2354581Ssherrym static ucode_errno_t
ucode_gen_files_amd(uint8_t * buf,int size,char * path)2367605SMark.Johnson@Sun.COM ucode_gen_files_amd(uint8_t *buf, int size, char *path)
2377605SMark.Johnson@Sun.COM {
2387605SMark.Johnson@Sun.COM 	/* LINTED: pointer alignment */
2397605SMark.Johnson@Sun.COM 	uint32_t *ptr = (uint32_t *)buf;
240*8836SMark.Johnson@Sun.COM 	char common_path[PATH_MAX];
2417605SMark.Johnson@Sun.COM 	int fd, count, counter;
2427605SMark.Johnson@Sun.COM 	ucode_header_amd_t *uh;
2437605SMark.Johnson@Sun.COM 	int last_cpu_rev = 0;
2447605SMark.Johnson@Sun.COM 
2458647SMark.Johnson@Sun.COM 
246*8836SMark.Johnson@Sun.COM 	/* write container file */
247*8836SMark.Johnson@Sun.COM 	(void) snprintf(common_path, PATH_MAX, "%s/%s", path, "container");
248*8836SMark.Johnson@Sun.COM 
249*8836SMark.Johnson@Sun.COM 	dprintf("path = %s\n", common_path);
250*8836SMark.Johnson@Sun.COM 	fd = open(common_path, O_WRONLY | O_CREAT | O_TRUNC,
2518647SMark.Johnson@Sun.COM 	    S_IRUSR | S_IRGRP | S_IROTH);
2528647SMark.Johnson@Sun.COM 
2538647SMark.Johnson@Sun.COM 	if (fd == -1) {
254*8836SMark.Johnson@Sun.COM 		ucode_perror(common_path, EM_SYS);
2558647SMark.Johnson@Sun.COM 		return (EM_SYS);
2568647SMark.Johnson@Sun.COM 	}
2578647SMark.Johnson@Sun.COM 
2588647SMark.Johnson@Sun.COM 	if (write(fd, buf, size) != size) {
2598647SMark.Johnson@Sun.COM 		(void) close(fd);
260*8836SMark.Johnson@Sun.COM 		ucode_perror(common_path, EM_SYS);
2618647SMark.Johnson@Sun.COM 		return (EM_SYS);
2628647SMark.Johnson@Sun.COM 	}
2638647SMark.Johnson@Sun.COM 
2648647SMark.Johnson@Sun.COM 	(void) close(fd);
2658647SMark.Johnson@Sun.COM 
2667605SMark.Johnson@Sun.COM 	/* skip over magic number & equivalence table header */
2677605SMark.Johnson@Sun.COM 	ptr += 2; size -= 8;
2687605SMark.Johnson@Sun.COM 
2697605SMark.Johnson@Sun.COM 	count = *ptr++; size -= 4;
2707605SMark.Johnson@Sun.COM 
2717605SMark.Johnson@Sun.COM 	/* equivalence table uses special name */
272*8836SMark.Johnson@Sun.COM 	(void) snprintf(common_path, PATH_MAX, "%s/%s", path,
273*8836SMark.Johnson@Sun.COM 	    "equivalence-table");
2747605SMark.Johnson@Sun.COM 
2757605SMark.Johnson@Sun.COM 	for (;;) {
276*8836SMark.Johnson@Sun.COM 		dprintf("path = %s\n", common_path);
277*8836SMark.Johnson@Sun.COM 		fd = open(common_path, O_WRONLY | O_CREAT | O_TRUNC,
2787605SMark.Johnson@Sun.COM 		    S_IRUSR | S_IRGRP | S_IROTH);
2797605SMark.Johnson@Sun.COM 
2807605SMark.Johnson@Sun.COM 		if (fd == -1) {
281*8836SMark.Johnson@Sun.COM 			ucode_perror(common_path, EM_SYS);
2827605SMark.Johnson@Sun.COM 			return (EM_SYS);
2837605SMark.Johnson@Sun.COM 		}
2847605SMark.Johnson@Sun.COM 
2857605SMark.Johnson@Sun.COM 		if (write(fd, ptr, count) != count) {
2867605SMark.Johnson@Sun.COM 			(void) close(fd);
287*8836SMark.Johnson@Sun.COM 			ucode_perror(common_path, EM_SYS);
2887605SMark.Johnson@Sun.COM 			return (EM_SYS);
2897605SMark.Johnson@Sun.COM 		}
2907605SMark.Johnson@Sun.COM 
2917605SMark.Johnson@Sun.COM 		(void) close(fd);
2927605SMark.Johnson@Sun.COM 		ptr += count >> 2; size -= count;
2937605SMark.Johnson@Sun.COM 
2947605SMark.Johnson@Sun.COM 		if (!size)
2957605SMark.Johnson@Sun.COM 			return (EM_OK);
2967605SMark.Johnson@Sun.COM 
2977605SMark.Johnson@Sun.COM 		ptr++; size -= 4;
2987605SMark.Johnson@Sun.COM 		count = *ptr++; size -= 4;
2997605SMark.Johnson@Sun.COM 
3007605SMark.Johnson@Sun.COM 		/* construct name from header information */
3017605SMark.Johnson@Sun.COM 		uh = (ucode_header_amd_t *)ptr;
3027605SMark.Johnson@Sun.COM 
3037605SMark.Johnson@Sun.COM 		if (uh->uh_cpu_rev != last_cpu_rev) {
3047605SMark.Johnson@Sun.COM 			last_cpu_rev = uh->uh_cpu_rev;
3057605SMark.Johnson@Sun.COM 			counter = 0;
3067605SMark.Johnson@Sun.COM 		}
3077605SMark.Johnson@Sun.COM 
308*8836SMark.Johnson@Sun.COM 		(void) snprintf(common_path, PATH_MAX, "%s/%04X-%02X", path,
3097605SMark.Johnson@Sun.COM 		    uh->uh_cpu_rev, counter++);
3107605SMark.Johnson@Sun.COM 	}
3117605SMark.Johnson@Sun.COM }
3127605SMark.Johnson@Sun.COM 
3137605SMark.Johnson@Sun.COM static ucode_errno_t
ucode_gen_files_intel(uint8_t * buf,int size,char * path)3147605SMark.Johnson@Sun.COM ucode_gen_files_intel(uint8_t *buf, int size, char *path)
3154581Ssherrym {
3164581Ssherrym 	int	remaining;
3174581Ssherrym 	char	common_path[PATH_MAX];
3184581Ssherrym 	DIR	*dirp;
3194581Ssherrym 	struct dirent *dp;
3204581Ssherrym 
3214581Ssherrym 	(void) snprintf(common_path, PATH_MAX, "%s/%s", path,
3224581Ssherrym 	    UCODE_INSTALL_COMMON_PATH);
3234581Ssherrym 
3244581Ssherrym 	if (mkdirp(common_path, 0755) == -1 && errno != EEXIST) {
3254581Ssherrym 		ucode_perror(common_path, EM_SYS);
3264581Ssherrym 		return (EM_SYS);
3274581Ssherrym 	}
3284581Ssherrym 
3294581Ssherrym 	for (remaining = size; remaining > 0; ) {
3304581Ssherrym 		uint32_t	total_size, body_size, offset;
3314581Ssherrym 		char		firstname[PATH_MAX];
3324581Ssherrym 		char		name[PATH_MAX];
3334581Ssherrym 		int		i;
3344581Ssherrym 		uint8_t		*curbuf = &buf[size - remaining];
3357605SMark.Johnson@Sun.COM 		ucode_header_intel_t	*uhp;
3367605SMark.Johnson@Sun.COM 		ucode_ext_table_intel_t *extp;
3374581Ssherrym 
3387605SMark.Johnson@Sun.COM 		uhp = (ucode_header_intel_t *)(intptr_t)curbuf;
3397605SMark.Johnson@Sun.COM 
3407605SMark.Johnson@Sun.COM 		total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size);
3417605SMark.Johnson@Sun.COM 		body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size);
3424581Ssherrym 
3434581Ssherrym 		remaining -= total_size;
3444581Ssherrym 
3454581Ssherrym 		(void) snprintf(firstname, PATH_MAX, "%s/%08X-%02X",
3464581Ssherrym 		    common_path, uhp->uh_signature, uhp->uh_proc_flags);
3474581Ssherrym 		dprintf("firstname = %s\n", firstname);
3484581Ssherrym 
3497605SMark.Johnson@Sun.COM 		if (ucode_should_update_intel(firstname, uhp->uh_rev) != 0) {
3504581Ssherrym 			int fd;
3514581Ssherrym 
3524581Ssherrym 			/* Remove the existing one first */
3534581Ssherrym 			(void) unlink(firstname);
3544581Ssherrym 
3554581Ssherrym 			if ((fd = open(firstname, O_WRONLY | O_CREAT | O_TRUNC,
3564581Ssherrym 			    S_IRUSR | S_IRGRP | S_IROTH)) == -1) {
3574581Ssherrym 				ucode_perror(firstname, EM_SYS);
3584581Ssherrym 				return (EM_SYS);
3594581Ssherrym 			}
3604581Ssherrym 
3614581Ssherrym 			if (write(fd, curbuf, total_size) != total_size) {
3624581Ssherrym 				(void) close(fd);
3634581Ssherrym 				ucode_perror(firstname, EM_SYS);
3644581Ssherrym 				return (EM_SYS);
3654581Ssherrym 			}
3664581Ssherrym 
3674581Ssherrym 			(void) close(fd);
3684581Ssherrym 		}
3694581Ssherrym 
3704581Ssherrym 		/*
3714581Ssherrym 		 * Only 1 byte of the proc_flags field is used, therefore
3724581Ssherrym 		 * we only need to match 8 potential platform ids.
3734581Ssherrym 		 */
3744581Ssherrym 		for (i = 0; i < 8; i++) {
3754581Ssherrym 			uint32_t platid = uhp->uh_proc_flags & (1 << i);
3764581Ssherrym 
3774581Ssherrym 			if (platid == 0 && uhp->uh_proc_flags != 0)
3784581Ssherrym 				continue;
3794581Ssherrym 
3804581Ssherrym 			(void) snprintf(name, PATH_MAX,
3814581Ssherrym 			    "%s/%08X-%02X", path, uhp->uh_signature, platid);
3824581Ssherrym 
3834581Ssherrym 			dprintf("proc_flags = %x, platid = %x, name = %s\n",
3844581Ssherrym 			    uhp->uh_proc_flags, platid, name);
3854581Ssherrym 
3867605SMark.Johnson@Sun.COM 			if (ucode_should_update_intel(name, uhp->uh_rev) != 0) {
3874581Ssherrym 
3884581Ssherrym 				/* Remove the existing one first */
3894581Ssherrym 				(void) unlink(name);
3904581Ssherrym 
3914581Ssherrym 				if (link(firstname, name) == -1) {
3924581Ssherrym 					ucode_perror(name, EM_SYS);
3934581Ssherrym 					return (EM_SYS);
3944581Ssherrym 				}
3954581Ssherrym 			}
3964581Ssherrym 
3974581Ssherrym 			if (uhp->uh_proc_flags == 0)
3984581Ssherrym 				break;
3994581Ssherrym 		}
4004581Ssherrym 
4017605SMark.Johnson@Sun.COM 		offset = UCODE_HEADER_SIZE_INTEL + body_size;
4024581Ssherrym 
4034581Ssherrym 		/* Check to see if there is extended signature table */
4044581Ssherrym 		if (total_size == offset)
4054581Ssherrym 			continue;
4064581Ssherrym 
4074581Ssherrym 		/* There is extended signature table.  More processing. */
4087605SMark.Johnson@Sun.COM 		extp = (ucode_ext_table_intel_t *)(uintptr_t)&curbuf[offset];
4094581Ssherrym 
4104581Ssherrym 		for (i = 0; i < extp->uet_count; i++) {
4117605SMark.Johnson@Sun.COM 			ucode_ext_sig_intel_t *uesp = &extp->uet_ext_sig[i];
4124581Ssherrym 			int j;
4134581Ssherrym 
4144581Ssherrym 			for (j = 0; j < 8; j++) {
4154581Ssherrym 				uint32_t id = uesp->ues_proc_flags & (1 << j);
4164581Ssherrym 
4174581Ssherrym 				if (id == 0 && uesp->ues_proc_flags)
4184581Ssherrym 					continue;
4194581Ssherrym 
4204581Ssherrym 				(void) snprintf(name, PATH_MAX,
4214581Ssherrym 				    "%s/%08X-%02X", path, extp->uet_ext_sig[i],
4224581Ssherrym 				    id);
4234581Ssherrym 
4247605SMark.Johnson@Sun.COM 				if (ucode_should_update_intel(name, uhp->uh_rev)
4257605SMark.Johnson@Sun.COM 				    != 0) {
4264581Ssherrym 
4274581Ssherrym 					/* Remove the existing one first */
4284581Ssherrym 					(void) unlink(name);
4294581Ssherrym 					if (link(firstname, name) == -1) {
4304581Ssherrym 						ucode_perror(name, EM_SYS);
4314581Ssherrym 						return (EM_SYS);
4324581Ssherrym 					}
4334581Ssherrym 				}
4344581Ssherrym 
4354581Ssherrym 				if (uesp->ues_proc_flags == 0)
4364581Ssherrym 					break;
4374581Ssherrym 			}
4384581Ssherrym 		}
4394581Ssherrym 
4404581Ssherrym 	}
4414581Ssherrym 
4424581Ssherrym 	/*
4434581Ssherrym 	 * Remove files with no links to them.  These are probably
4444581Ssherrym 	 * obsolete microcode files.
4454581Ssherrym 	 */
4464581Ssherrym 	if ((dirp = opendir(common_path)) == NULL) {
4474581Ssherrym 		ucode_perror(common_path, EM_SYS);
4484581Ssherrym 		return (EM_SYS);
4494581Ssherrym 	}
4504581Ssherrym 
4514581Ssherrym 	while ((dp = readdir(dirp)) != NULL) {
4524581Ssherrym 		char filename[PATH_MAX];
4534581Ssherrym 		struct stat statbuf;
4544581Ssherrym 
4554581Ssherrym 		(void) snprintf(filename, PATH_MAX,
4564581Ssherrym 		    "%s/%s", common_path, dp->d_name);
4574581Ssherrym 		if (stat(filename, &statbuf) == -1)
4584581Ssherrym 			continue;
4594581Ssherrym 
4604581Ssherrym 		if ((statbuf.st_mode & S_IFMT) == S_IFREG) {
4614581Ssherrym 			if (statbuf.st_nlink == 1)
4624581Ssherrym 				(void) unlink(filename);
4634581Ssherrym 		}
4644581Ssherrym 	}
4654581Ssherrym 
4664581Ssherrym 	(void) closedir(dirp);
4674581Ssherrym 
4684581Ssherrym 	return (EM_OK);
4694581Ssherrym }
4704581Ssherrym 
4714581Ssherrym /*
4724581Ssherrym  * Returns 0 on success, 2 on usage error, and 3 on operation error.
4734581Ssherrym  */
4744581Ssherrym int
main(int argc,char * argv[])4754581Ssherrym main(int argc, char *argv[])
4764581Ssherrym {
4774581Ssherrym 	int	c;
4784581Ssherrym 	int	action = 0;
4794581Ssherrym 	int	actcount = 0;
4804581Ssherrym 	char	*path = NULL;
4814581Ssherrym 	char	*filename = NULL;
4824581Ssherrym 	int	errflg = 0;
4834581Ssherrym 	int	dev_fd = -1;
4844581Ssherrym 	int	fd = -1;
4854581Ssherrym 	int	verbose = 0;
4864581Ssherrym 	uint8_t	*buf = NULL;
4874581Ssherrym 	ucode_errno_t	rc = EM_OK;
4884581Ssherrym 	processorid_t	cpuid_max;
4894581Ssherrym 	struct stat filestat;
4904581Ssherrym 	uint32_t ucode_size;
4914581Ssherrym 
4924581Ssherrym 	(void) setlocale(LC_ALL, "");
4934581Ssherrym 
4944581Ssherrym #if !defined(TEXT_DOMAIN)
4954581Ssherrym #define	TEXT_DOMAIN "SYS_TEST"
4964581Ssherrym #endif
4974581Ssherrym 	(void) textdomain(TEXT_DOMAIN);
4984581Ssherrym 
4994581Ssherrym 	cmdname = basename(argv[0]);
5004581Ssherrym 
5014581Ssherrym 	while ((c = getopt(argc, argv, "idhuvVR:")) != EOF) {
5024581Ssherrym 		switch (c) {
5034581Ssherrym 
5044581Ssherrym 		case 'i':
5054581Ssherrym 			action |= UCODE_OPT_INSTALL;
5064581Ssherrym 			actcount++;
5074581Ssherrym 			break;
5084581Ssherrym 
5094581Ssherrym 		case 'u':
5104581Ssherrym 			action |= UCODE_OPT_UPDATE;
5114581Ssherrym 			actcount++;
5124581Ssherrym 			break;
5134581Ssherrym 
5144581Ssherrym 		case 'v':
5154581Ssherrym 			action |= UCODE_OPT_VERSION;
5164581Ssherrym 			actcount++;
5174581Ssherrym 			break;
5184581Ssherrym 
5194581Ssherrym 		case 'd':
5204581Ssherrym 			ucode_debug = 1;
5214581Ssherrym 			break;
5224581Ssherrym 
5234581Ssherrym 		case 'R':
5244581Ssherrym 			if (optarg[0] == '-')
5254581Ssherrym 				errflg++;
5264581Ssherrym 			else if (strlen(optarg) > UCODE_MAX_PATH_LEN) {
5274581Ssherrym 				(void) fprintf(stderr,
5284581Ssherrym 				    gettext("Alternate path too long\n"));
5294581Ssherrym 				errflg++;
5304581Ssherrym 			} else if ((path = strdup(optarg)) == NULL) {
5314581Ssherrym 				errflg++;
5324581Ssherrym 			}
5334581Ssherrym 
5344581Ssherrym 			break;
5354581Ssherrym 
5364581Ssherrym 		case 'V':
5374581Ssherrym 			verbose = 1;
5384581Ssherrym 			break;
5394581Ssherrym 
5404581Ssherrym 		case 'h':
5414581Ssherrym 			usage(1);
5424581Ssherrym 			return (0);
5434581Ssherrym 
5444581Ssherrym 		default:
5454581Ssherrym 			usage(verbose);
5464581Ssherrym 			return (2);
5474581Ssherrym 		}
5484581Ssherrym 	}
5494581Ssherrym 
5504581Ssherrym 	if (actcount != 1) {
5514581Ssherrym 		(void) fprintf(stderr, gettext("%s: options -v, -i and -u "
5524581Ssherrym 		    "are mutually exclusive.\n"), cmdname);
5534581Ssherrym 		usage(verbose);
5544581Ssherrym 		return (2);
5554581Ssherrym 	}
5564581Ssherrym 
5574581Ssherrym 	if (optind <= argc - 1)
5584581Ssherrym 		filename = argv[optind];
5594581Ssherrym 	else if (!(action & UCODE_OPT_VERSION))
5604581Ssherrym 		errflg++;
5614581Ssherrym 
5624581Ssherrym 	if (errflg || action == 0) {
5634581Ssherrym 		usage(verbose);
5644581Ssherrym 		return (2);
5654581Ssherrym 	}
5664581Ssherrym 
5674581Ssherrym 	/*
5684581Ssherrym 	 * Convert from text format to binary format
5694581Ssherrym 	 */
5704581Ssherrym 	if ((action & UCODE_OPT_INSTALL) || (action & UCODE_OPT_UPDATE)) {
5717605SMark.Johnson@Sun.COM 		int i;
5727605SMark.Johnson@Sun.COM 		UCODE_VENDORS;
5737605SMark.Johnson@Sun.COM 
5747605SMark.Johnson@Sun.COM 		for (i = 0; ucode_vendors[i].filestr != NULL; i++) {
5757605SMark.Johnson@Sun.COM 			dprintf("i = %d, filestr = %s, filename = %s\n",
5767605SMark.Johnson@Sun.COM 			    i, ucode_vendors[i].filestr, filename);
5777605SMark.Johnson@Sun.COM 			if (strncasecmp(ucode_vendors[i].filestr,
5787605SMark.Johnson@Sun.COM 			    basename(filename),
5797605SMark.Johnson@Sun.COM 			    strlen(ucode_vendors[i].filestr)) == 0) {
5807605SMark.Johnson@Sun.COM 				ucode = &ucode_ops[i];
5817605SMark.Johnson@Sun.COM 				(void) strncpy(ucode_vendor_str,
5827605SMark.Johnson@Sun.COM 				    ucode_vendors[i].vendorstr,
5837605SMark.Johnson@Sun.COM 				    sizeof (ucode_vendor_str));
5847605SMark.Johnson@Sun.COM 				break;
5857605SMark.Johnson@Sun.COM 			}
5867605SMark.Johnson@Sun.COM 		}
5877605SMark.Johnson@Sun.COM 
5887605SMark.Johnson@Sun.COM 		if (ucode_vendors[i].filestr == NULL) {
5897605SMark.Johnson@Sun.COM 			rc = EM_NOVENDOR;
5907605SMark.Johnson@Sun.COM 			ucode_perror(basename(filename), rc);
5917605SMark.Johnson@Sun.COM 			goto err_out;
5927605SMark.Johnson@Sun.COM 		}
5937605SMark.Johnson@Sun.COM 
5944581Ssherrym 		if ((stat(filename, &filestat)) < 0) {
5954581Ssherrym 			rc = EM_SYS;
5964581Ssherrym 			ucode_perror(filename, rc);
5974581Ssherrym 			goto err_out;
5984581Ssherrym 		}
5994581Ssherrym 
6004581Ssherrym 		if ((filestat.st_mode & S_IFMT) != S_IFREG &&
6014581Ssherrym 		    (filestat.st_mode & S_IFMT) != S_IFLNK) {
6024581Ssherrym 			rc = EM_FILEFORMAT;
6034581Ssherrym 			ucode_perror(filename, rc);
6044581Ssherrym 			goto err_out;
6054581Ssherrym 		}
6064581Ssherrym 
6074581Ssherrym 		if ((buf = malloc(filestat.st_size)) == NULL) {
6084581Ssherrym 			rc = EM_SYS;
6094581Ssherrym 			ucode_perror(filename, rc);
6104581Ssherrym 			goto err_out;
6114581Ssherrym 		}
6124581Ssherrym 
6137605SMark.Johnson@Sun.COM 		ucode_size = ucode->convert(filename, buf, filestat.st_size);
6144581Ssherrym 
6154581Ssherrym 		dprintf("ucode_size = %d\n", ucode_size);
6164581Ssherrym 
6174581Ssherrym 		if (ucode_size == 0) {
6184581Ssherrym 			rc = EM_FILEFORMAT;
6194581Ssherrym 			ucode_perror(filename, rc);
6204581Ssherrym 			goto err_out;
6214581Ssherrym 		}
6224581Ssherrym 
6237605SMark.Johnson@Sun.COM 		if ((rc = ucode->validate(buf, ucode_size)) != EM_OK) {
6244581Ssherrym 			ucode_perror(filename, rc);
6254581Ssherrym 			goto err_out;
6264581Ssherrym 		}
6274581Ssherrym 	}
6284581Ssherrym 
6294581Ssherrym 	/*
6304581Ssherrym 	 * For the install option, the microcode file must start with
6314581Ssherrym 	 * "intel" for Intel microcode, and "amd" for AMD microcode.
6324581Ssherrym 	 */
6334581Ssherrym 	if (action & UCODE_OPT_INSTALL) {
6344581Ssherrym 		/*
6354581Ssherrym 		 * If no path is provided by the -R option, put the files in
6364581Ssherrym 		 * /ucode_install_path/ucode_vendor_str/.
6374581Ssherrym 		 */
6384581Ssherrym 		if (path == NULL) {
6394581Ssherrym 			if ((path = malloc(PATH_MAX)) == NULL) {
6404581Ssherrym 				rc = EM_SYS;
6414581Ssherrym 				ucode_perror("malloc", rc);
6424581Ssherrym 				goto err_out;
6434581Ssherrym 			}
6444581Ssherrym 
6454581Ssherrym 			(void) snprintf(path, PATH_MAX, "/%s/%s",
6464581Ssherrym 			    ucode_install_path, ucode_vendor_str);
6474581Ssherrym 		}
6484581Ssherrym 
6494581Ssherrym 		if (mkdirp(path, 0755) == -1 && errno != EEXIST) {
6504581Ssherrym 			rc = EM_SYS;
6514581Ssherrym 			ucode_perror(path, rc);
6524581Ssherrym 			goto err_out;
6534581Ssherrym 		}
6544581Ssherrym 
6557605SMark.Johnson@Sun.COM 		rc = ucode->gen_files(buf, ucode_size, path);
6564581Ssherrym 
6574581Ssherrym 		goto err_out;
6584581Ssherrym 	}
6594581Ssherrym 
6604581Ssherrym 	if ((dev_fd = open(ucode_dev, O_RDONLY)) == -1) {
6614581Ssherrym 		rc = EM_SYS;
6624581Ssherrym 		ucode_perror(ucode_dev, rc);
6634581Ssherrym 		goto err_out;
6644581Ssherrym 	}
6654581Ssherrym 
6664581Ssherrym 	if (action & UCODE_OPT_VERSION) {
6674581Ssherrym 		int tmprc;
6684581Ssherrym 		uint32_t *revp = NULL;
6694581Ssherrym 		int i;
6704581Ssherrym #if defined(_SYSCALL32_IMPL)
6714581Ssherrym 	struct ucode_get_rev_struct32 inf32;
6724581Ssherrym #else
6734581Ssherrym 	struct ucode_get_rev_struct info;
6744581Ssherrym #endif
6754581Ssherrym 
6764581Ssherrym 		cpuid_max = (processorid_t)sysconf(_SC_CPUID_MAX);
6774581Ssherrym 
6784581Ssherrym 		if ((revp = (uint32_t *)
6794581Ssherrym 		    malloc(cpuid_max * sizeof (uint32_t))) == NULL) {
6804581Ssherrym 			rc = EM_SYS;
6814581Ssherrym 			ucode_perror("malloc", rc);
6824581Ssherrym 			goto err_out;
6834581Ssherrym 		}
6844581Ssherrym 
6854581Ssherrym 		for (i = 0; i < cpuid_max; i++)
6864581Ssherrym 			revp[i] = (uint32_t)-1;
6874581Ssherrym 
6884581Ssherrym #if defined(_SYSCALL32_IMPL)
6894581Ssherrym 		info32.ugv_rev = (caddr32_t)revp;
6904581Ssherrym 		info32.ugv_size = cpuid_max;
6914581Ssherrym 		info32.ugv_errno = EM_OK;
6924581Ssherrym 		tmprc = ioctl(dev_fd, UCODE_GET_VERSION, &info32);
6934581Ssherrym 		rc = info32.ugv_errno;
6944581Ssherrym #else
6954581Ssherrym 		info.ugv_rev = revp;
6964581Ssherrym 		info.ugv_size = cpuid_max;
6974581Ssherrym 		info.ugv_errno = EM_OK;
6984581Ssherrym 		tmprc = ioctl(dev_fd, UCODE_GET_VERSION, &info);
6994581Ssherrym 		rc = info.ugv_errno;
7004581Ssherrym #endif
7014581Ssherrym 
7024581Ssherrym 		if (tmprc && rc == EM_OK) {
7034581Ssherrym 			rc = EM_SYS;
7044581Ssherrym 		}
7054581Ssherrym 
7064581Ssherrym 		if (rc == EM_OK) {
7074581Ssherrym 			(void) printf(gettext("CPU\tMicrocode Version\n"));
7084581Ssherrym 			for (i = 0; i < cpuid_max; i++) {
7094581Ssherrym 				if (info.ugv_rev[i] == (uint32_t)-1)
7104581Ssherrym 					continue;
7114581Ssherrym 				(void) printf("%d\t0x%x\n", i, info.ugv_rev[i]);
7124581Ssherrym 			}
7134581Ssherrym 		} else {
7144581Ssherrym 			ucode_perror(gettext("get microcode version"), rc);
7154581Ssherrym 		}
7164581Ssherrym 
7174581Ssherrym 		if (revp)
7184581Ssherrym 			free(revp);
7194581Ssherrym 	}
7204581Ssherrym 
7214581Ssherrym 	if (action & UCODE_OPT_UPDATE) {
7224581Ssherrym 		int tmprc;
7234581Ssherrym #if defined(_SYSCALL32_IMPL)
7244581Ssherrym 	struct ucode_write_struct32 uw_struct32;
7254581Ssherrym #else
7264581Ssherrym 	struct ucode_write_struct uw_struct;
7274581Ssherrym #endif
7284581Ssherrym 
7294581Ssherrym #if defined(_SYSCALL32_IMPL)
7304581Ssherrym 		uw_struct32.uw_size = ucode_size;
7314581Ssherrym 		uw_struct32.uw_ucode = (caddr32_t)buf;
7324581Ssherrym 		uw_struct32.uw_errno = EM_OK;
7334581Ssherrym 		tmprc = ioctl(dev_fd, UCODE_UPDATE, &uw_struct32);
7344581Ssherrym 		rc = uw_struct32.uw_errno;
7354581Ssherrym 
7364581Ssherrym #else
7374581Ssherrym 		uw_struct.uw_size = ucode_size;
7384581Ssherrym 		uw_struct.uw_ucode = buf;
7394581Ssherrym 		uw_struct.uw_errno = EM_OK;
7404581Ssherrym 		tmprc = ioctl(dev_fd, UCODE_UPDATE, &uw_struct);
7414581Ssherrym 		rc = uw_struct.uw_errno;
7424581Ssherrym #endif
7434581Ssherrym 
7444581Ssherrym 		if (rc == EM_OK) {
7454581Ssherrym 			if (tmprc) {
7464581Ssherrym 				rc = EM_SYS;
7474581Ssherrym 				ucode_perror(ucode_dev, rc);
7484581Ssherrym 			}
7494581Ssherrym 		} else if (rc == EM_NOMATCH || rc == EM_HIGHERREV) {
7504581Ssherrym 			ucode_perror(filename, rc);
7514581Ssherrym 		} else {
7524581Ssherrym 			ucode_perror(gettext("microcode update"), rc);
7534581Ssherrym 		}
7544581Ssherrym 	}
7554581Ssherrym 
7564581Ssherrym err_out:
7574581Ssherrym 	if (dev_fd != -1)
7584581Ssherrym 		(void) close(dev_fd);
7594581Ssherrym 
7604581Ssherrym 	if (fd != -1)
7614581Ssherrym 		(void) close(fd);
7624581Ssherrym 
7634581Ssherrym 	free(buf);
7644581Ssherrym 	free(path);
7654581Ssherrym 
7664581Ssherrym 	if (rc != EM_OK)
7674581Ssherrym 		return (3);
7684581Ssherrym 
7694581Ssherrym 	return (0);
7704581Ssherrym }
771