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