diff options
author | Mark Johnson <Mark.Johnson@Sun.COM> | 2008-09-15 15:09:45 -0700 |
---|---|---|
committer | Mark Johnson <Mark.Johnson@Sun.COM> | 2008-09-15 15:09:45 -0700 |
commit | adc586debf12d2592024c0b8b9e44ffa104f858c (patch) | |
tree | 189b4a865d30882c4001014b69447e6bdaaa46f7 | |
parent | 875a4abcc45eff5fe347622080ebe938010f5acb (diff) | |
download | illumos-joyent-adc586debf12d2592024c0b8b9e44ffa104f858c.tar.gz |
6747590 microcode update support for AMD
Contributed by Hans Rosenfeld <hans.rosenfeld@amd.com>
-rw-r--r-- | usr/src/cmd/boot/bootadm/bootadm.c | 5 | ||||
-rw-r--r-- | usr/src/cmd/ucodeadm/ucodeadm.c | 189 | ||||
-rw-r--r-- | usr/src/common/ucode/ucode_utils.c | 100 | ||||
-rw-r--r-- | usr/src/uts/common/sys/ucode.h | 147 | ||||
-rw-r--r-- | usr/src/uts/i86pc/os/microcode.c | 826 | ||||
-rw-r--r-- | usr/src/uts/i86pc/os/mp_startup.c | 2 | ||||
-rw-r--r-- | usr/src/uts/intel/io/ucode_drv.c | 8 | ||||
-rw-r--r-- | usr/src/uts/intel/sys/controlregs.h | 5 | ||||
-rw-r--r-- | usr/src/uts/intel/sys/x86_archext.h | 2 |
9 files changed, 936 insertions, 348 deletions
diff --git a/usr/src/cmd/boot/bootadm/bootadm.c b/usr/src/cmd/boot/bootadm/bootadm.c index bea2a8a28e..e78f257cae 100644 --- a/usr/src/cmd/boot/bootadm/bootadm.c +++ b/usr/src/cmd/boot/bootadm/bootadm.c @@ -7859,8 +7859,9 @@ ucode_install(char *root) struct stat fstatus, tstatus; struct utimbuf u_times; - (void) snprintf(file, PATH_MAX, "%s/%s/%s-ucode.txt", - bam_root, UCODE_INSTALL_PATH, ucode_vendors[i].filestr); + (void) snprintf(file, PATH_MAX, "%s/%s/%s-ucode.%s", + bam_root, UCODE_INSTALL_PATH, ucode_vendors[i].filestr, + ucode_vendors[i].extstr); if (stat(file, &fstatus) != 0 || !(S_ISREG(fstatus.st_mode))) continue; diff --git a/usr/src/cmd/ucodeadm/ucodeadm.c b/usr/src/cmd/ucodeadm/ucodeadm.c index 0100bd15d7..765099dd09 100644 --- a/usr/src/cmd/ucodeadm/ucodeadm.c +++ b/usr/src/cmd/ucodeadm/ucodeadm.c @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/types.h> #include <sys/processor.h> #include <sys/ucode.h> @@ -60,6 +58,19 @@ static char ucode_install_path[] = UCODE_INSTALL_PATH; static int ucode_debug = 0; +static int ucode_convert_amd(const char *, uint8_t *, size_t); +static int ucode_convert_intel(const char *, uint8_t *, size_t); + +static ucode_errno_t ucode_gen_files_amd(uint8_t *, int, char *); +static ucode_errno_t ucode_gen_files_intel(uint8_t *, int, char *); + +static const struct ucode_ops ucode_ops[] = { + { ucode_convert_intel, ucode_gen_files_intel, ucode_validate_intel }, + { ucode_convert_amd, ucode_gen_files_amd, ucode_validate_amd }, +}; + +const struct ucode_ops *ucode; + static void dprintf(const char *format, ...) { @@ -81,20 +92,19 @@ usage(int verbose) gettext("\t\t Shows running microcode version.\n\n")); } - (void) fprintf(stderr, "\t%s -u microcode-text-file\n", cmdname); + (void) fprintf(stderr, "\t%s -u microcode-file\n", cmdname); if (verbose) { (void) fprintf(stderr, gettext("\t\t Updates microcode to the " "latest matching version found in\n" - "\t\t microcode-text-file.\n\n")); + "\t\t microcode-file.\n\n")); } - (void) fprintf(stderr, "\t%s -i [-R path] microcode-text-file\n", - cmdname); + (void) fprintf(stderr, "\t%s -i [-R path] microcode-file\n", cmdname); if (verbose) { (void) fprintf(stderr, gettext("\t\t Installs microcode to be " - "used for subsequent boots. Microcode\n" - "\t\t text file name must start with vendor name, " - "such as \"intel\".\n\n")); + "used for subsequent boots.\n\n")); + (void) fprintf(stderr, gettext("Microcode file name must start " + "with vendor name, such as \"intel\" or \"amd\".\n\n")); } } @@ -113,7 +123,25 @@ ucode_perror(const char *str, ucode_errno_t rc) * Return the number of characters read. */ static int -ucode_convert(const char *infile, uint8_t *buf, size_t size) +ucode_convert_amd(const char *infile, uint8_t *buf, size_t size) +{ + int fd; + + if (infile == NULL || buf == NULL || size == 0) + return (0); + + if ((fd = open(infile, O_RDONLY)) < 0) + return (0); + + size = read(fd, buf, size); + + (void) close(fd); + + return (size); +} + +static int +ucode_convert_intel(const char *infile, uint8_t *buf, size_t size) { char linebuf[LINESIZE]; FILE *infd = NULL; @@ -173,11 +201,11 @@ ucode_convert(const char *infile, uint8_t *buf, size_t size) * Returns 0 if no need to update the link; -1 otherwise */ static int -ucode_should_update(char *filename, uint32_t new_rev) +ucode_should_update_intel(char *filename, uint32_t new_rev) { int fd; struct stat statbuf; - ucode_header_t header; + ucode_header_intel_t header; /* * If the file or link already exists, check to see if @@ -205,7 +233,64 @@ ucode_should_update(char *filename, uint32_t new_rev) * Generate microcode binary files. Must be called after ucode_validate(). */ static ucode_errno_t -ucode_gen_files(uint8_t *buf, int size, char *path) +ucode_gen_files_amd(uint8_t *buf, int size, char *path) +{ + /* LINTED: pointer alignment */ + uint32_t *ptr = (uint32_t *)buf; + int plen = strlen(path); + int fd, count, counter; + ucode_header_amd_t *uh; + int last_cpu_rev = 0; + + /* skip over magic number & equivalence table header */ + ptr += 2; size -= 8; + + count = *ptr++; size -= 4; + + /* equivalence table uses special name */ + (void) strlcat(path, "/equivalence-table", PATH_MAX); + + for (;;) { + dprintf("path = %s\n", path); + fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IRGRP | S_IROTH); + + if (fd == -1) { + ucode_perror(path, EM_SYS); + return (EM_SYS); + } + + if (write(fd, ptr, count) != count) { + (void) close(fd); + ucode_perror(path, EM_SYS); + return (EM_SYS); + } + + (void) close(fd); + ptr += count >> 2; size -= count; + + if (!size) + return (EM_OK); + + ptr++; size -= 4; + count = *ptr++; size -= 4; + + /* construct name from header information */ + uh = (ucode_header_amd_t *)ptr; + + if (uh->uh_cpu_rev != last_cpu_rev) { + last_cpu_rev = uh->uh_cpu_rev; + counter = 0; + } + + path[plen] = '\0'; + (void) snprintf(path + plen, PATH_MAX - plen, "/%04X-%02X", + uh->uh_cpu_rev, counter++); + } +} + +static ucode_errno_t +ucode_gen_files_intel(uint8_t *buf, int size, char *path) { int remaining; char common_path[PATH_MAX]; @@ -226,11 +311,13 @@ ucode_gen_files(uint8_t *buf, int size, char *path) char name[PATH_MAX]; int i; uint8_t *curbuf = &buf[size - remaining]; - ucode_header_t *uhp = (ucode_header_t *)(intptr_t)curbuf; - ucode_ext_table_t *extp; + ucode_header_intel_t *uhp; + ucode_ext_table_intel_t *extp; + + uhp = (ucode_header_intel_t *)(intptr_t)curbuf; - total_size = UCODE_TOTAL_SIZE(uhp->uh_total_size); - body_size = UCODE_BODY_SIZE(uhp->uh_body_size); + total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size); + body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size); remaining -= total_size; @@ -238,7 +325,7 @@ ucode_gen_files(uint8_t *buf, int size, char *path) common_path, uhp->uh_signature, uhp->uh_proc_flags); dprintf("firstname = %s\n", firstname); - if (ucode_should_update(firstname, uhp->uh_rev) != 0) { + if (ucode_should_update_intel(firstname, uhp->uh_rev) != 0) { int fd; /* Remove the existing one first */ @@ -275,7 +362,7 @@ ucode_gen_files(uint8_t *buf, int size, char *path) dprintf("proc_flags = %x, platid = %x, name = %s\n", uhp->uh_proc_flags, platid, name); - if (ucode_should_update(name, uhp->uh_rev) != 0) { + if (ucode_should_update_intel(name, uhp->uh_rev) != 0) { /* Remove the existing one first */ (void) unlink(name); @@ -290,17 +377,17 @@ ucode_gen_files(uint8_t *buf, int size, char *path) break; } - offset = UCODE_HEADER_SIZE + body_size; + offset = UCODE_HEADER_SIZE_INTEL + body_size; /* Check to see if there is extended signature table */ if (total_size == offset) continue; /* There is extended signature table. More processing. */ - extp = (ucode_ext_table_t *)(uintptr_t)&curbuf[offset]; + extp = (ucode_ext_table_intel_t *)(uintptr_t)&curbuf[offset]; for (i = 0; i < extp->uet_count; i++) { - ucode_ext_sig_t *uesp = &extp->uet_ext_sig[i]; + ucode_ext_sig_intel_t *uesp = &extp->uet_ext_sig[i]; int j; for (j = 0; j < 8; j++) { @@ -313,8 +400,8 @@ ucode_gen_files(uint8_t *buf, int size, char *path) "%s/%08X-%02X", path, extp->uet_ext_sig[i], id); - if (ucode_should_update(name, uhp->uh_rev) != - 0) { + if (ucode_should_update_intel(name, uhp->uh_rev) + != 0) { /* Remove the existing one first */ (void) unlink(name); @@ -460,6 +547,29 @@ main(int argc, char *argv[]) * Convert from text format to binary format */ if ((action & UCODE_OPT_INSTALL) || (action & UCODE_OPT_UPDATE)) { + int i; + UCODE_VENDORS; + + for (i = 0; ucode_vendors[i].filestr != NULL; i++) { + dprintf("i = %d, filestr = %s, filename = %s\n", + i, ucode_vendors[i].filestr, filename); + if (strncasecmp(ucode_vendors[i].filestr, + basename(filename), + strlen(ucode_vendors[i].filestr)) == 0) { + ucode = &ucode_ops[i]; + (void) strncpy(ucode_vendor_str, + ucode_vendors[i].vendorstr, + sizeof (ucode_vendor_str)); + break; + } + } + + if (ucode_vendors[i].filestr == NULL) { + rc = EM_NOVENDOR; + ucode_perror(basename(filename), rc); + goto err_out; + } + if ((stat(filename, &filestat)) < 0) { rc = EM_SYS; ucode_perror(filename, rc); @@ -479,7 +589,7 @@ main(int argc, char *argv[]) goto err_out; } - ucode_size = ucode_convert(filename, buf, filestat.st_size); + ucode_size = ucode->convert(filename, buf, filestat.st_size); dprintf("ucode_size = %d\n", ucode_size); @@ -489,7 +599,7 @@ main(int argc, char *argv[]) goto err_out; } - if ((rc = ucode_validate(buf, ucode_size)) != EM_OK) { + if ((rc = ucode->validate(buf, ucode_size)) != EM_OK) { ucode_perror(filename, rc); goto err_out; } @@ -500,29 +610,6 @@ main(int argc, char *argv[]) * "intel" for Intel microcode, and "amd" for AMD microcode. */ if (action & UCODE_OPT_INSTALL) { - int i; - UCODE_VENDORS; - - for (i = 0; ucode_vendors[i].filestr != NULL; i++) { - dprintf("i = %d, filestr = %s, filename = %s\n", - i, ucode_vendors[i].filestr, filename); - if (strncasecmp(ucode_vendors[i].filestr, - basename(filename), - strlen(ucode_vendors[i].filestr)) == 0) { - - (void) strncpy(ucode_vendor_str, - ucode_vendors[i].vendorstr, - sizeof (ucode_vendor_str)); - break; - } - } - - if (ucode_vendors[i].filestr == NULL) { - rc = EM_NOVENDOR; - ucode_perror(basename(filename), rc); - goto err_out; - } - /* * If no path is provided by the -R option, put the files in * /ucode_install_path/ucode_vendor_str/. @@ -544,7 +631,7 @@ main(int argc, char *argv[]) goto err_out; } - rc = ucode_gen_files(buf, ucode_size, path); + rc = ucode->gen_files(buf, ucode_size, path); goto err_out; } diff --git a/usr/src/common/ucode/ucode_utils.c b/usr/src/common/ucode/ucode_utils.c index 48198a16e9..5878f4a4af 100644 --- a/usr/src/common/ucode/ucode_utils.c +++ b/usr/src/common/ucode/ucode_utils.c @@ -20,12 +20,10 @@ */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/types.h> #include <sys/ucode.h> #ifdef _KERNEL @@ -46,7 +44,7 @@ * Returns EM_OK on success, EM_HEADER on failure. */ ucode_errno_t -ucode_header_validate(ucode_header_t *uhp) +ucode_header_validate_intel(ucode_header_intel_t *uhp) { uint32_t header_size, body_size, total_size; @@ -59,9 +57,9 @@ ucode_header_validate(ucode_header_t *uhp) if (uhp->uh_header_ver != 0x1) return (EM_HEADER); - header_size = UCODE_HEADER_SIZE; - total_size = UCODE_TOTAL_SIZE(uhp->uh_total_size); - body_size = UCODE_BODY_SIZE(uhp->uh_body_size); + header_size = UCODE_HEADER_SIZE_INTEL; + total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size); + body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size); /* * The body size field of the microcode code header specifies the size @@ -91,7 +89,7 @@ ucode_header_validate(ucode_header_t *uhp) */ if (total_size > (header_size + body_size)) { if ((total_size - body_size - header_size - - UCODE_EXT_TABLE_SIZE) % UCODE_EXT_SIG_SIZE) { + UCODE_EXT_TABLE_SIZE_INTEL) % UCODE_EXT_SIG_SIZE_INTEL) { return (EM_HEADER); } @@ -104,7 +102,7 @@ ucode_header_validate(ucode_header_t *uhp) * Returns checksum. */ uint32_t -ucode_checksum(uint32_t sum, uint32_t size, uint8_t *code) +ucode_checksum_intel(uint32_t sum, uint32_t size, uint8_t *code) { int i; uint32_t *lcode = (uint32_t *)(intptr_t)code; @@ -117,9 +115,65 @@ ucode_checksum(uint32_t sum, uint32_t size, uint8_t *code) } ucode_errno_t -ucode_validate(uint8_t *ucodep, int size) +ucode_validate_amd(uint8_t *ucodep, int size) +{ + /* LINTED: pointer alignment */ + uint32_t *ptr = (uint32_t *)ucodep; + uint32_t count; + + if (ucodep == NULL || size <= 0) + return (EM_INVALIDARG); + + /* Magic Number: "AMD\0" */ + size -= 4; + if (*ptr++ != 0x00414d44) + return (EM_FILEFORMAT); + + /* equivalence table */ + size -= 4; + if (*ptr++) + return (EM_FILEFORMAT); + + size -= 4; + if (((count = *ptr++) > size) || (count % 16)) + return (EM_FILEFORMAT); + + /* LINTED: pointer alignment */ + ptr = (uint32_t *)(((uint8_t *)ptr) + count); + size -= count; + + /* + * minimum valid size: + * - type and size fields (8 bytes) + * - patch header (64 bytes) + * - one patch triad (28 bytes) + */ + while (size >= 100) { + /* microcode patch */ + size -= 4; + if (*ptr++ != 1) + return (EM_FILEFORMAT); + + size -= 4; + if (((count = *ptr++) > size) || + ((count - sizeof (ucode_header_amd_t)) % 28)) + return (EM_FILEFORMAT); + + /* LINTED: pointer alignment */ + ptr = (uint32_t *)(((uint8_t *)ptr) + count); + size -= count; + } + + if (size) + return (EM_FILEFORMAT); + + return (EM_OK); +} + +ucode_errno_t +ucode_validate_intel(uint8_t *ucodep, int size) { - uint32_t header_size = UCODE_HEADER_SIZE; + uint32_t header_size = UCODE_HEADER_SIZE_INTEL; int remaining; if (ucodep == NULL || size <= 0) @@ -127,36 +181,38 @@ ucode_validate(uint8_t *ucodep, int size) for (remaining = size; remaining > 0; ) { uint32_t total_size, body_size, ext_size; - ucode_header_t *uhp; + ucode_header_intel_t *uhp; uint8_t *curbuf = &ucodep[size - remaining]; ucode_errno_t rc; - uhp = (ucode_header_t *)(intptr_t)curbuf; + uhp = (ucode_header_intel_t *)(intptr_t)curbuf; - if ((rc = ucode_header_validate(uhp)) != EM_OK) + if ((rc = ucode_header_validate_intel(uhp)) != EM_OK) return (rc); - total_size = UCODE_TOTAL_SIZE(uhp->uh_total_size); + total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size); - if (ucode_checksum(0, total_size, curbuf)) + if (ucode_checksum_intel(0, total_size, curbuf)) return (EM_CHECKSUM); - body_size = UCODE_BODY_SIZE(uhp->uh_body_size); + body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size); ext_size = total_size - (header_size + body_size); if (ext_size > 0) { uint32_t i; - if (ucode_checksum(0, ext_size, + if (ucode_checksum_intel(0, ext_size, &curbuf[header_size + body_size])) { return (EM_CHECKSUM); } - ext_size -= UCODE_EXT_TABLE_SIZE; - for (i = 0; i < ext_size / UCODE_EXT_SIG_SIZE; i++) { - if (ucode_checksum(0, UCODE_EXT_SIG_SIZE, + ext_size -= UCODE_EXT_TABLE_SIZE_INTEL; + for (i = 0; i < ext_size / UCODE_EXT_SIG_SIZE_INTEL; + i++) { + if (ucode_checksum_intel(0, + UCODE_EXT_SIG_SIZE_INTEL, &curbuf[total_size - ext_size + - i * UCODE_EXT_SIG_SIZE])) { + i * UCODE_EXT_SIG_SIZE_INTEL])) { return (EM_CHECKSUM); } diff --git a/usr/src/uts/common/sys/ucode.h b/usr/src/uts/common/sys/ucode.h index 296a51f961..0f37745cae 100644 --- a/usr/src/uts/common/sys/ucode.h +++ b/usr/src/uts/common/sys/ucode.h @@ -19,15 +19,16 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_UCODE_H #define _SYS_UCODE_H -#pragma ident "%Z%%M% %I% %E% SMI" - +#ifdef _KERNEL +#include <sys/cpuvar.h> +#endif #include <sys/types.h> #include <sys/priv.h> #include <sys/processor.h> @@ -92,9 +93,41 @@ struct ucode_write_struct32 { #endif /* _SYSCALL32_IMPL */ /* - * Microcode file information + * AMD Microcode file information */ -typedef struct ucode_header { +typedef struct ucode_header_amd { + uint32_t uh_date; + uint32_t uh_patch_id; + uint32_t uh_internal; /* patch data id & length, init flag */ + uint32_t uh_cksum; + uint32_t uh_nb_id; + uint32_t uh_sb_id; + uint16_t uh_cpu_rev; + uint8_t uh_nb_rev; + uint8_t uh_sb_rev; + uint32_t uh_bios_rev; + uint32_t uh_match[8]; +} ucode_header_amd_t; + +typedef struct ucode_file_amd { + ucode_header_amd_t uf_header; + uint8_t uf_data[896]; + uint8_t uf_resv[896]; + uint8_t uf_code_present; + uint8_t uf_code[191]; +} ucode_file_amd_t; + +typedef struct ucode_eqtbl_amd { + uint32_t ue_inst_cpu; + uint32_t ue_fixed_mask; + uint32_t ue_fixed_comp; + uint32_t ue_equiv_cpu; +} ucode_eqtbl_amd_t; + +/* + * Intel Microcode file information + */ +typedef struct ucode_header_intel { uint32_t uh_header_ver; uint32_t uh_rev; uint32_t uh_date; @@ -105,25 +138,33 @@ typedef struct ucode_header { uint32_t uh_body_size; uint32_t uh_total_size; uint32_t uh_reserved[3]; -} ucode_header_t; +} ucode_header_intel_t; -typedef struct ucode_ext_sig { +typedef struct ucode_ext_sig_intel { uint32_t ues_signature; uint32_t ues_proc_flags; uint32_t ues_checksum; -} ucode_ext_sig_t; +} ucode_ext_sig_intel_t; -typedef struct ucode_ext_table { +typedef struct ucode_ext_table_intel { uint32_t uet_count; uint32_t uet_checksum; uint32_t uet_reserved[3]; - ucode_ext_sig_t uet_ext_sig[1]; -} ucode_ext_table_t; + ucode_ext_sig_intel_t uet_ext_sig[1]; +} ucode_ext_table_intel_t; -typedef struct ucode_file { - ucode_header_t uf_header; +typedef struct ucode_file_intel { + ucode_header_intel_t *uf_header; uint8_t *uf_body; - ucode_ext_table_t *uf_ext_table; + ucode_ext_table_intel_t *uf_ext_table; +} ucode_file_intel_t; + +/* + * common container + */ +typedef union ucode_file { + ucode_file_amd_t *amd; + ucode_file_intel_t intel; } ucode_file_t; @@ -139,14 +180,14 @@ typedef struct ucode_file { #define UCODE_MAX_PATH_LEN (PATH_MAX - UCODE_COMMON_NAME_LEN) -#define UCODE_HEADER_SIZE (sizeof (struct ucode_header)) -#define UCODE_EXT_TABLE_SIZE (20) /* 20-bytes */ -#define UCODE_EXT_SIG_SIZE (sizeof (struct ucode_ext_sig)) +#define UCODE_HEADER_SIZE_INTEL (sizeof (struct ucode_header_intel)) +#define UCODE_EXT_TABLE_SIZE_INTEL (20) /* 20-bytes */ +#define UCODE_EXT_SIG_SIZE_INTEL (sizeof (struct ucode_ext_sig_intel)) #define UCODE_KB(a) ((a) << 10) /* KB */ #define UCODE_MB(a) ((a) << 20) /* MB */ #define UCODE_DEFAULT_TOTAL_SIZE UCODE_KB(2) -#define UCODE_DEFAULT_BODY_SIZE (UCODE_KB(2) - UCODE_HEADER_SIZE) +#define UCODE_DEFAULT_BODY_SIZE (UCODE_KB(2) - UCODE_HEADER_SIZE_INTEL) /* * For a single microcode file, the minimum size is 1K, maximum size is 16K. @@ -164,35 +205,87 @@ typedef struct ucode_file { #define UCODE_SIZE_CONVERT(size, default_size) \ ((size) == 0 ? (default_size) : (size)) -#define UCODE_BODY_SIZE(size) \ +#define UCODE_BODY_SIZE_INTEL(size) \ UCODE_SIZE_CONVERT((size), UCODE_DEFAULT_BODY_SIZE) -#define UCODE_TOTAL_SIZE(size) \ +#define UCODE_TOTAL_SIZE_INTEL(size) \ UCODE_SIZE_CONVERT((size), UCODE_DEFAULT_TOTAL_SIZE) -#define UCODE_MATCH(sig1, sig2, pf1, pf2) \ +#define UCODE_MATCH_INTEL(sig1, sig2, pf1, pf2) \ (((sig1) == (sig2)) && \ (((pf1) & (pf2)) || (((pf1) == 0) && ((pf2) == 0)))) -extern ucode_errno_t ucode_header_validate(ucode_header_t *); -extern uint32_t ucode_checksum(uint32_t, uint32_t, uint8_t *); -extern ucode_errno_t ucode_validate(uint8_t *, int); +extern ucode_errno_t ucode_header_validate_intel(ucode_header_intel_t *); +extern uint32_t ucode_checksum_intel(uint32_t, uint32_t, uint8_t *); + +extern ucode_errno_t ucode_validate_amd(uint8_t *, int); +extern ucode_errno_t ucode_validate_intel(uint8_t *, int); + +#ifdef _KERNEL extern ucode_errno_t ucode_get_rev(uint32_t *); extern ucode_errno_t ucode_update(uint8_t *, int); +/* + * Microcode specific information per core + */ +typedef struct cpu_ucode_info { + uint32_t cui_platid; /* platform id */ + uint32_t cui_rev; /* microcode revision */ +} cpu_ucode_info_t; + +/* + * Data structure used for xcall + */ +typedef struct ucode_update { + uint32_t sig; /* signature */ + cpu_ucode_info_t info; /* ucode info */ + uint32_t expected_rev; + uint32_t new_rev; + uint8_t *ucodep; /* pointer to ucode */ + uint32_t usize; +} ucode_update_t; + +/* + * Microcode kernel operations + */ +struct ucode_ops { + uint32_t write_msr; + int (*capable)(cpu_t *); + void (*file_reset)(ucode_file_t *, processorid_t); + void (*read_rev)(cpu_ucode_info_t *); + uint32_t (*load)(ucode_file_t *, cpu_ucode_info_t *, cpu_t *); + ucode_errno_t (*validate)(uint8_t *, int); + ucode_errno_t (*extract)(ucode_update_t *, uint8_t *, int); + ucode_errno_t (*locate)(cpu_t *, cpu_ucode_info_t *, ucode_file_t *); +}; +#else + #define UCODE_MAX_VENDORS_NAME_LEN 20 #define UCODE_VENDORS \ static struct { \ char *filestr; \ + char *extstr; \ char *vendorstr; \ int supported; \ } ucode_vendors[] = { \ - { "intel", "GenuineIntel", 1 }, \ - { "amd", "AuthenticAMD", 0 }, \ - { NULL, NULL, 0 } \ + { "intel", "txt", "GenuineIntel", 1 }, \ + { "amd", "bin", "AuthenticAMD", 1 }, \ + { NULL, NULL, NULL, 0 } \ } +/* + * Microcode user operations + */ +struct ucode_ops { + int (*convert)(const char *, uint8_t *, size_t); + ucode_errno_t (*gen_files)(uint8_t *, int, char *); + ucode_errno_t (*validate)(uint8_t *, int); +}; +#endif + +extern const struct ucode_ops *ucode; + #ifdef __cplusplus } #endif diff --git a/usr/src/uts/i86pc/os/microcode.c b/usr/src/uts/i86pc/os/microcode.c index 6474bcce78..7dd2ef2e55 100644 --- a/usr/src/uts/i86pc/os/microcode.c +++ b/usr/src/uts/i86pc/os/microcode.c @@ -48,43 +48,76 @@ #endif /* - * Microcode specific information per core + * AMD-specific equivalence table */ -struct cpu_ucode_info { - uint32_t cui_platid; /* platform id */ - uint32_t cui_rev; /* microcode revision */ -}; - -/* - * Data structure used for xcall - */ -struct ucode_update_struct { - uint32_t sig; /* signature */ - struct cpu_ucode_info info; /* ucode info */ - uint32_t expected_rev; - uint32_t new_rev; - uint8_t *ucodep; /* pointer to ucode body */ -}; +static ucode_eqtbl_amd_t *ucode_eqtbl_amd; /* * mcpu_ucode_info for the boot CPU. Statically allocated. */ static struct cpu_ucode_info cpu_ucode_info0; -static ucode_file_t ucodefile = { 0 }; +static ucode_file_t ucodefile; + +static void* ucode_zalloc(processorid_t, size_t); +static void ucode_free(processorid_t, void *, size_t); + +static int ucode_capable_amd(cpu_t *); +static int ucode_capable_intel(cpu_t *); + +static ucode_errno_t ucode_extract_amd(ucode_update_t *, uint8_t *, int); +static ucode_errno_t ucode_extract_intel(ucode_update_t *, uint8_t *, + int); + +static void ucode_file_reset_amd(ucode_file_t *, processorid_t); +static void ucode_file_reset_intel(ucode_file_t *, processorid_t); + +static uint32_t ucode_load_amd(ucode_file_t *, cpu_ucode_info_t *, cpu_t *); +static uint32_t ucode_load_intel(ucode_file_t *, cpu_ucode_info_t *, cpu_t *); -static int ucode_capable(cpu_t *); -static void ucode_file_reset(ucode_file_t *, processorid_t); -static ucode_errno_t ucode_match(int, struct cpu_ucode_info *, - ucode_header_t *, ucode_ext_table_t *); -static ucode_errno_t ucode_locate(cpu_t *, struct cpu_ucode_info *, - ucode_file_t *); -static void ucode_update_intel(uint8_t *, struct cpu_ucode_info *); -static void ucode_read_rev(struct cpu_ucode_info *); #ifdef __xpv -static void ucode_update_xpv(struct ucode_update_struct *, uint8_t *, uint32_t); +static void ucode_load_xpv(ucode_update_t *); #endif +static int ucode_equiv_cpu_amd(cpu_t *, int *); + +static ucode_errno_t ucode_locate_amd(cpu_t *, cpu_ucode_info_t *, + ucode_file_t *); +static ucode_errno_t ucode_locate_intel(cpu_t *, cpu_ucode_info_t *, + ucode_file_t *); + +static ucode_errno_t ucode_match_amd(int, cpu_ucode_info_t *, + ucode_file_amd_t *, int); +static ucode_errno_t ucode_match_intel(int, cpu_ucode_info_t *, + ucode_header_intel_t *, ucode_ext_table_intel_t *); + +static void ucode_read_rev_amd(cpu_ucode_info_t *); +static void ucode_read_rev_intel(cpu_ucode_info_t *); + +static const struct ucode_ops ucode_amd = { + MSR_AMD_PATCHLOADER, + ucode_capable_amd, + ucode_file_reset_amd, + ucode_read_rev_amd, + ucode_load_amd, + ucode_validate_amd, + ucode_extract_amd, + ucode_locate_amd +}; + +static const struct ucode_ops ucode_intel = { + MSR_INTC_UCODE_WRITE, + ucode_capable_intel, + ucode_file_reset_intel, + ucode_read_rev_intel, + ucode_load_intel, + ucode_validate_intel, + ucode_extract_intel, + ucode_locate_intel +}; + +const struct ucode_ops *ucode; + static const char ucode_failure_fmt[] = "cpu%d: failed to update microcode from version 0x%x to 0x%x\n"; static const char ucode_success_fmt[] = @@ -122,42 +155,84 @@ ucode_free_space(cpu_t *cp) * space allocated for the microcode file. */ void -ucode_free() +ucode_cleanup() +{ + ASSERT(ucode); + + ucode->file_reset(&ucodefile, -1); +} + +/* + * Allocate/free a buffer used to hold ucode data. Space for the boot CPU is + * allocated with BOP_ALLOC() and does not require a free. + */ +static void* +ucode_zalloc(processorid_t id, size_t size) +{ + if (id) + return (kmem_zalloc(size, KM_NOSLEEP)); + + /* BOP_ALLOC() failure results in panic */ + return (BOP_ALLOC(bootops, NULL, size, MMU_PAGESIZE)); +} + +static void +ucode_free(processorid_t id, void* buf, size_t size) { - ucode_file_reset(&ucodefile, -1); + if (id) + kmem_free(buf, size); } /* * Check whether or not a processor is capable of microcode operations * Returns 1 if it is capable, 0 if not. + * + * At this point we only support microcode update for: + * - Intel processors family 6 and above, and + * - AMD processors family 0x10 and above. + * + * We also assume that we don't support a mix of Intel and + * AMD processors in the same box. + * + * An i86xpv guest domain can't update the microcode. */ +/*ARGSUSED*/ static int -ucode_capable(cpu_t *cp) +ucode_capable_amd(cpu_t *cp) { - /* i86xpv guest domain can't update microcode */ #ifndef __xpv extern int xpv_is_hvm; if (xpv_is_hvm) { return (0); } + + return (cpuid_getfamily(cp) >= 0x10); #else if (!DOMAIN_IS_INITDOMAIN(xen_info)) { return (0); } -#endif /* - * At this point we only support microcode update for Intel - * processors family 6 and above. - * - * We also assume that we don't support a mix of Intel and - * AMD processors in the same box. + * XXPV - change when microcode loading works in dom0. Don't support + * microcode loading in dom0 right now for AMD. */ - if (cpuid_getvendor(cp) != X86_VENDOR_Intel || - cpuid_getfamily(cp) < 6) + return (0); +#endif +} +static int +ucode_capable_intel(cpu_t *cp) +{ +#ifndef __xpv + extern int xpv_is_hvm; + if (xpv_is_hvm) { return (0); - else - return (1); + } +#else + if (!DOMAIN_IS_INITDOMAIN(xen_info)) { + return (0); + } +#endif + return (cpuid_getfamily(cp) >= 6); } /* @@ -165,37 +240,124 @@ ucode_capable(cpu_t *cp) * or when the cached microcode doesn't match the CPU being processed. */ static void -ucode_file_reset(ucode_file_t *ucodefp, processorid_t id) +ucode_file_reset_amd(ucode_file_t *ufp, processorid_t id) { - int total_size, body_size; + ucode_file_amd_t *ucodefp = ufp->amd; if (ucodefp == NULL) return; - total_size = UCODE_TOTAL_SIZE(ucodefp->uf_header.uh_total_size); - body_size = UCODE_BODY_SIZE(ucodefp->uf_header.uh_body_size); + ucode_free(id, ucodefp, sizeof (ucode_file_amd_t)); + ufp->amd = NULL; +} + +static void +ucode_file_reset_intel(ucode_file_t *ufp, processorid_t id) +{ + ucode_file_intel_t *ucodefp = &ufp->intel; + int total_size, body_size; + + if (ucodefp == NULL || ucodefp->uf_header == NULL) + return; + + total_size = UCODE_TOTAL_SIZE_INTEL(ucodefp->uf_header->uh_total_size); + body_size = UCODE_BODY_SIZE_INTEL(ucodefp->uf_header->uh_body_size); if (ucodefp->uf_body) { - /* - * Space for the boot CPU is allocated with BOP_ALLOC() - * and does not require a free. - */ - if (id != 0) - kmem_free(ucodefp->uf_body, body_size); + ucode_free(id, ucodefp->uf_body, body_size); ucodefp->uf_body = NULL; } if (ucodefp->uf_ext_table) { - int size = total_size - body_size - UCODE_HEADER_SIZE; - /* - * Space for the boot CPU is allocated with BOP_ALLOC() - * and does not require a free. - */ - if (id != 0) - kmem_free(ucodefp->uf_ext_table, size); + int size = total_size - body_size - UCODE_HEADER_SIZE_INTEL; + + ucode_free(id, ucodefp->uf_ext_table, size); ucodefp->uf_ext_table = NULL; } - bzero(&ucodefp->uf_header, UCODE_HEADER_SIZE); + ucode_free(id, ucodefp->uf_header, UCODE_HEADER_SIZE_INTEL); + ucodefp->uf_header = NULL; +} + +/* + * Find the equivalent CPU id in the equivalence table. + */ +static int +ucode_equiv_cpu_amd(cpu_t *cp, int *eq_sig) +{ + char name[MAXPATHLEN]; + intptr_t fd; + int count; + int offset = 0, cpi_sig = cpuid_getsig(cp); + ucode_eqtbl_amd_t *eqtbl = ucode_eqtbl_amd; + + (void) snprintf(name, MAXPATHLEN, "/%s/%s/equivalence-table", + UCODE_INSTALL_PATH, cpuid_getvendorstr(cp)); + + /* + * No kmem_zalloc() etc. available on boot cpu. + */ + if (cp->cpu_id == 0) { + if ((fd = kobj_open(name)) == -1) + return (EM_OPENFILE); + /* ucode_zalloc() cannot fail on boot cpu */ + eqtbl = ucode_zalloc(cp->cpu_id, sizeof (*eqtbl)); + ASSERT(eqtbl); + do { + count = kobj_read(fd, (int8_t *)eqtbl, + sizeof (*eqtbl), offset); + if (count != sizeof (*eqtbl)) { + (void) kobj_close(fd); + return (EM_HIGHERREV); + } + offset += count; + } while (eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != cpi_sig); + (void) kobj_close(fd); + } + + /* + * If not already done, load the equivalence table. + * Not done on boot CPU. + */ + if (eqtbl == NULL) { + struct _buf *eq; + uint64_t size; + + if ((eq = kobj_open_file(name)) == (struct _buf *)-1) + return (EM_OPENFILE); + + if (kobj_get_filesize(eq, &size) < 0) { + kobj_close_file(eq); + return (EM_OPENFILE); + } + + ucode_eqtbl_amd = kmem_zalloc(size, KM_NOSLEEP); + if (ucode_eqtbl_amd == NULL) { + kobj_close_file(eq); + return (EM_NOMEM); + } + + count = kobj_read_file(eq, (char *)ucode_eqtbl_amd, size, 0); + kobj_close_file(eq); + + if (count != size) + return (EM_FILESIZE); + } + + /* Get the equivalent CPU id. */ + if (cp->cpu_id) + for (eqtbl = ucode_eqtbl_amd; + eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != cpi_sig; + eqtbl++) + ; + + *eq_sig = eqtbl->ue_equiv_cpu; + *eq_sig = ((*eq_sig >> 8) & 0xff00) | (*eq_sig & 0xff); + + /* No equivalent CPU id found, assume outdated microcode file. */ + if (*eq_sig == 0) + return (EM_HIGHERREV); + + return (EM_OK); } /* @@ -205,19 +367,73 @@ ucode_file_reset(ucode_file_t *ucodefp, processorid_t id) * Return EM_OK on success, corresponding error code on failure. */ static ucode_errno_t -ucode_locate(cpu_t *cp, struct cpu_ucode_info *uinfop, ucode_file_t *ucodefp) +ucode_locate_amd(cpu_t *cp, cpu_ucode_info_t *uinfop, ucode_file_t *ufp) +{ + char name[MAXPATHLEN]; + intptr_t fd; + int count, i, rc; + int eq_sig = 0; + ucode_file_amd_t *ucodefp = ufp->amd; + + /* get equivalent CPU id */ + if ((rc = ucode_equiv_cpu_amd(cp, &eq_sig)) != EM_OK) + return (rc); + + /* + * Allocate a buffer for the microcode patch. If the buffer has been + * allocated before, check for a matching microcode to avoid loading + * the file again. + */ + if (ucodefp == NULL) + ucodefp = ucode_zalloc(cp->cpu_id, sizeof (*ucodefp)); + else if (ucode_match_amd(eq_sig, uinfop, ucodefp, sizeof (*ucodefp)) + == EM_OK) + return (EM_OK); + + if (ucodefp == NULL) + return (EM_NOMEM); + + ufp->amd = ucodefp; + + /* + * Find the patch for this CPU. The patch files are named XXXX-YY, where + * XXXX is the equivalent CPU id and YY is the running patch number. + * Patches specific to certain chipsets are guaranteed to have lower + * numbers than less specific patches, so we can just load the first + * patch that matches. + */ + + for (i = 0; i < 0xff; i++) { + (void) snprintf(name, MAXPATHLEN, "/%s/%s/%04X-%02X", + UCODE_INSTALL_PATH, cpuid_getvendorstr(cp), eq_sig, i); + if ((fd = kobj_open(name)) == -1) + return (EM_NOMATCH); + count = kobj_read(fd, (char *)ucodefp, sizeof (*ucodefp), 0); + (void) kobj_close(fd); + + if (ucode_match_amd(eq_sig, uinfop, ucodefp, count) == EM_OK) + return (EM_OK); + } + return (EM_NOMATCH); +} + +static ucode_errno_t +ucode_locate_intel(cpu_t *cp, cpu_ucode_info_t *uinfop, ucode_file_t *ufp) { char name[MAXPATHLEN]; intptr_t fd; int count; - int header_size = UCODE_HEADER_SIZE; + int header_size = UCODE_HEADER_SIZE_INTEL; int cpi_sig = cpuid_getsig(cp); ucode_errno_t rc = EM_OK; + ucode_file_intel_t *ucodefp = &ufp->intel; + + ASSERT(ucode); /* * If the microcode matches the CPU we are processing, use it. */ - if (ucode_match(cpi_sig, uinfop, &ucodefp->uf_header, + if (ucode_match_intel(cpi_sig, uinfop, ucodefp->uf_header, ucodefp->uf_ext_table) == EM_OK && ucodefp->uf_body != NULL) { return (EM_OK); } @@ -237,14 +453,18 @@ ucode_locate(cpu_t *cp, struct cpu_ucode_info *uinfop, ucode_file_t *ucodefp) * reset the microcode data structure and read in the new * file. */ - ucode_file_reset(ucodefp, cp->cpu_id); + ucode->file_reset(ufp, cp->cpu_id); + + ucodefp->uf_header = ucode_zalloc(cp->cpu_id, header_size); + if (ucodefp->uf_header == NULL) + return (EM_NOMEM); - count = kobj_read(fd, (char *)&ucodefp->uf_header, header_size, 0); + count = kobj_read(fd, (char *)ucodefp->uf_header, header_size, 0); switch (count) { - case UCODE_HEADER_SIZE: { + case UCODE_HEADER_SIZE_INTEL: { - ucode_header_t *uhp = &ucodefp->uf_header; + ucode_header_intel_t *uhp = ucodefp->uf_header; uint32_t offset = header_size; int total_size, body_size, ext_size; uint32_t sum = 0; @@ -252,23 +472,13 @@ ucode_locate(cpu_t *cp, struct cpu_ucode_info *uinfop, ucode_file_t *ucodefp) /* * Make sure that the header contains valid fields. */ - if ((rc = ucode_header_validate(uhp)) == EM_OK) { - total_size = UCODE_TOTAL_SIZE(uhp->uh_total_size); - body_size = UCODE_BODY_SIZE(uhp->uh_body_size); - if (cp->cpu_id != 0) { - if ((ucodefp->uf_body = kmem_zalloc(body_size, - KM_NOSLEEP)) == NULL) { - rc = EM_NOMEM; - break; - } - } else { - /* - * BOP_ALLOC() failure results in panic so we - * don't have to check for NULL return. - */ - ucodefp->uf_body = - (uint8_t *)BOP_ALLOC(bootops, - NULL, body_size, MMU_PAGESIZE); + if ((rc = ucode_header_validate_intel(uhp)) == EM_OK) { + total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size); + body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size); + ucodefp->uf_body = ucode_zalloc(cp->cpu_id, body_size); + if (ucodefp->uf_body == NULL) { + rc = EM_NOMEM; + break; } if (kobj_read(fd, (char *)ucodefp->uf_body, @@ -279,9 +489,9 @@ ucode_locate(cpu_t *cp, struct cpu_ucode_info *uinfop, ucode_file_t *ucodefp) if (rc) break; - sum = ucode_checksum(0, header_size, - (uint8_t *)&ucodefp->uf_header); - if (ucode_checksum(sum, body_size, ucodefp->uf_body)) { + sum = ucode_checksum_intel(0, header_size, + (uint8_t *)ucodefp->uf_header); + if (ucode_checksum_intel(sum, body_size, ucodefp->uf_body)) { rc = EM_CHECKSUM; break; } @@ -295,35 +505,26 @@ ucode_locate(cpu_t *cp, struct cpu_ucode_info *uinfop, ucode_file_t *ucodefp) if (ext_size <= 0) break; - if (cp->cpu_id != 0) { - if ((ucodefp->uf_ext_table = kmem_zalloc(ext_size, - KM_NOSLEEP)) == NULL) { - rc = EM_NOMEM; - break; - } - } else { - /* - * BOP_ALLOC() failure results in panic so we - * don't have to check for NULL return. - */ - ucodefp->uf_ext_table = - (ucode_ext_table_t *)BOP_ALLOC(bootops, NULL, - ext_size, MMU_PAGESIZE); + ucodefp->uf_ext_table = ucode_zalloc(cp->cpu_id, ext_size); + if (ucodefp->uf_ext_table == NULL) { + rc = EM_NOMEM; + break; } if (kobj_read(fd, (char *)ucodefp->uf_ext_table, ext_size, offset) != ext_size) { rc = EM_FILESIZE; - } else if (ucode_checksum(0, ext_size, + } else if (ucode_checksum_intel(0, ext_size, (uint8_t *)(ucodefp->uf_ext_table))) { rc = EM_CHECKSUM; } else { int i; - ext_size -= UCODE_EXT_TABLE_SIZE; + ext_size -= UCODE_EXT_TABLE_SIZE_INTEL; for (i = 0; i < ucodefp->uf_ext_table->uet_count; i++) { - if (ucode_checksum(0, UCODE_EXT_SIG_SIZE, + if (ucode_checksum_intel(0, + UCODE_EXT_SIG_SIZE_INTEL, (uint8_t *)(&(ucodefp->uf_ext_table-> uet_ext_sig[i])))) { rc = EM_CHECKSUM; @@ -344,23 +545,63 @@ ucode_locate(cpu_t *cp, struct cpu_ucode_info *uinfop, ucode_file_t *ucodefp) if (rc != EM_OK) return (rc); - rc = ucode_match(cpi_sig, uinfop, &ucodefp->uf_header, + rc = ucode_match_intel(cpi_sig, uinfop, ucodefp->uf_header, ucodefp->uf_ext_table); return (rc); } +static ucode_errno_t +ucode_match_amd(int eq_sig, cpu_ucode_info_t *uinfop, ucode_file_amd_t *ucodefp, + int size) +{ + ucode_header_amd_t *uh; + + if (ucodefp == NULL || size < sizeof (ucode_header_amd_t)) + return (EM_NOMATCH); + + /* + * Don't even think about loading patches that would require code + * execution. + */ + if (size > offsetof(ucode_file_amd_t, uf_code_present) && + ucodefp->uf_code_present) + return (EM_NOMATCH); + + uh = &ucodefp->uf_header; + + if (eq_sig != uh->uh_cpu_rev) + return (EM_NOMATCH); + + if (uh->uh_nb_id) { + cmn_err(CE_WARN, "ignoring northbridge-specific ucode: " + "chipset id %x, revision %x", uh->uh_nb_id, uh->uh_nb_rev); + return (EM_NOMATCH); + } + + if (uh->uh_sb_id) { + cmn_err(CE_WARN, "ignoring southbridge-specific ucode: " + "chipset id %x, revision %x", uh->uh_sb_id, uh->uh_sb_rev); + return (EM_NOMATCH); + } + + if (uh->uh_patch_id <= uinfop->cui_rev) + return (EM_HIGHERREV); + + return (EM_OK); +} /* * Returns 1 if the microcode is for this processor; 0 otherwise. */ static ucode_errno_t -ucode_match(int cpi_sig, struct cpu_ucode_info *uinfop, - ucode_header_t *uhp, ucode_ext_table_t *uetp) +ucode_match_intel(int cpi_sig, cpu_ucode_info_t *uinfop, + ucode_header_intel_t *uhp, ucode_ext_table_intel_t *uetp) { - ASSERT(uhp); + if (uhp == NULL) + return (EM_NOMATCH); - if (UCODE_MATCH(cpi_sig, uhp->uh_signature, + if (UCODE_MATCH_INTEL(cpi_sig, uhp->uh_signature, uinfop->cui_platid, uhp->uh_proc_flags)) { if (uinfop->cui_rev >= uhp->uh_rev && !ucode_force_update) @@ -373,11 +614,11 @@ ucode_match(int cpi_sig, struct cpu_ucode_info *uinfop, int i; for (i = 0; i < uetp->uet_count; i++) { - ucode_ext_sig_t *uesp; + ucode_ext_sig_intel_t *uesp; uesp = &uetp->uet_ext_sig[i]; - if (UCODE_MATCH(cpi_sig, uesp->ues_signature, + if (UCODE_MATCH_INTEL(cpi_sig, uesp->ues_signature, uinfop->cui_platid, uesp->ues_proc_flags)) { if (uinfop->cui_rev >= uhp->uh_rev && @@ -396,9 +637,10 @@ ucode_match(int cpi_sig, struct cpu_ucode_info *uinfop, static int ucode_write(xc_arg_t arg1, xc_arg_t unused2, xc_arg_t unused3) { - struct ucode_update_struct *uusp = (struct ucode_update_struct *)arg1; - struct cpu_ucode_info *uinfop = CPU->cpu_m.mcpu_ucode_info; + ucode_update_t *uusp = (ucode_update_t *)arg1; + cpu_ucode_info_t *uinfop = CPU->cpu_m.mcpu_ucode_info; + ASSERT(ucode); ASSERT(uusp->ucodep); #ifndef __xpv @@ -408,65 +650,134 @@ ucode_write(xc_arg_t arg1, xc_arg_t unused2, xc_arg_t unused3) * the threads share the same microcode. */ if (!ucode_force_update) { - ucode_read_rev(uinfop); + ucode->read_rev(uinfop); uusp->new_rev = uinfop->cui_rev; if (uinfop->cui_rev >= uusp->expected_rev) return (0); } - wrmsr(MSR_INTC_UCODE_WRITE, - (uint64_t)(intptr_t)(uusp->ucodep)); + wrmsr(ucode->write_msr, (uintptr_t)uusp->ucodep); #endif - ucode_read_rev(uinfop); + ucode->read_rev(uinfop); uusp->new_rev = uinfop->cui_rev; return (0); } +/*ARGSUSED*/ +static uint32_t +ucode_load_amd(ucode_file_t *ufp, cpu_ucode_info_t *uinfop, cpu_t *cp) +{ + ucode_file_amd_t *ucodefp = ufp->amd; +#ifdef __xpv + ucode_update_t uus; +#endif -static void -ucode_update_intel(uint8_t *ucode_body, struct cpu_ucode_info *uinfop) + ASSERT(ucode); + ASSERT(ucodefp); + +#ifndef __xpv + kpreempt_disable(); + wrmsr(ucode->write_msr, (uintptr_t)ucodefp); + ucode->read_rev(uinfop); + kpreempt_enable(); +#else + uus.ucodep = (uint8_t *)ucodefp; + uus.usize = sizeof (*ucodefp); + ucode_load_xpv(&uus); + ucode->read_rev(uinfop); + uus.new_rev = uinfop->cui_rev; +#endif + + return (ucodefp->uf_header.uh_patch_id); +} + +/*ARGSUSED2*/ +static uint32_t +ucode_load_intel(ucode_file_t *ufp, cpu_ucode_info_t *uinfop, cpu_t *cp) { + ucode_file_intel_t *ucodefp = &ufp->intel; +#ifdef __xpv + uint32_t ext_offset; + uint32_t body_size; + uint32_t ext_size; + uint8_t *ustart; + uint32_t usize; + ucode_update_t uus; +#endif + + ASSERT(ucode); + +#ifdef __xpv + /* + * the hypervisor wants the header, data, and extended + * signature tables. We can only get here from the boot + * CPU (cpu #0), we don't need to free as ucode_zalloc() will + * use BOP_ALLOC(). + */ + usize = UCODE_TOTAL_SIZE_INTEL(ucodefp->uf_header->uh_total_size); + ustart = ucode_zalloc(cp->cpu_id, usize); + ASSERT(ustart); + + body_size = UCODE_BODY_SIZE_INTEL(ucodefp->uf_header->uh_body_size); + ext_offset = body_size + UCODE_HEADER_SIZE_INTEL; + ext_size = usize - ext_offset; + ASSERT(ext_size >= 0); + + (void) memcpy(ustart, ucodefp->uf_header, UCODE_HEADER_SIZE_INTEL); + (void) memcpy(&ustart[UCODE_HEADER_SIZE_INTEL], ucodefp->uf_body, + body_size); + if (ext_size > 0) { + (void) memcpy(&ustart[ext_offset], + ucodefp->uf_ext_table, ext_size); + } + uus.ucodep = ustart; + uus.usize = usize; + ucode_load_xpv(&uus); + ucode->read_rev(uinfop); + uus.new_rev = uinfop->cui_rev; +#else kpreempt_disable(); - wrmsr(MSR_INTC_UCODE_WRITE, (uint64_t)(uintptr_t)ucode_body); - ucode_read_rev(uinfop); + wrmsr(ucode->write_msr, (uintptr_t)ucodefp->uf_body); + ucode->read_rev(uinfop); kpreempt_enable(); +#endif + + return (ucodefp->uf_header->uh_rev); } #ifdef __xpv static void -ucode_update_xpv(struct ucode_update_struct *uusp, uint8_t *ucode, - uint32_t size) +ucode_load_xpv(ucode_update_t *uusp) { - struct cpu_ucode_info *uinfop; xen_platform_op_t op; int e; ASSERT(DOMAIN_IS_INITDOMAIN(xen_info)); kpreempt_disable(); - uinfop = CPU->cpu_m.mcpu_ucode_info; op.cmd = XENPF_microcode_update; op.interface_version = XENPF_INTERFACE_VERSION; /*LINTED: constant in conditional context*/ - set_xen_guest_handle(op.u.microcode.data, ucode); - op.u.microcode.length = size; + set_xen_guest_handle(op.u.microcode.data, uusp->ucodep); + op.u.microcode.length = uusp->usize; e = HYPERVISOR_platform_op(&op); if (e != 0) { cmn_err(CE_WARN, "hypervisor failed to accept uCode update"); } - ucode_read_rev(uinfop); - if (uusp != NULL) { - uusp->new_rev = uinfop->cui_rev; - } kpreempt_enable(); } #endif /* __xpv */ +static void +ucode_read_rev_amd(cpu_ucode_info_t *uinfop) +{ + uinfop->cui_rev = rdmsr(MSR_AMD_PATCHLEVEL); +} static void -ucode_read_rev(struct cpu_ucode_info *uinfop) +ucode_read_rev_intel(cpu_ucode_info_t *uinfop) { struct cpuid_regs crs; @@ -480,6 +791,113 @@ ucode_read_rev(struct cpu_ucode_info *uinfop) uinfop->cui_rev = (rdmsr(MSR_INTC_UCODE_REV) >> INTC_UCODE_REV_SHIFT); } +static ucode_errno_t +ucode_extract_amd(ucode_update_t *uusp, uint8_t *ucodep, int size) +{ + uint32_t *ptr = (uint32_t *)ucodep; + ucode_eqtbl_amd_t *eqtbl; + ucode_file_amd_t *ufp; + int count, eq_sig; + + /* skip over magic number & equivalence table header */ + ptr += 2; size -= 8; + + count = *ptr++; size -= 4; + for (eqtbl = (ucode_eqtbl_amd_t *)ptr; + eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != uusp->sig; + eqtbl++) + ; + + eq_sig = eqtbl->ue_equiv_cpu; + eq_sig = ((eq_sig >> 8) & 0xff00) | (eq_sig & 0xff); + + /* No equivalent CPU id found, assume outdated microcode file. */ + if (eq_sig == 0) + return (EM_HIGHERREV); + + /* Use the first microcode patch that matches. */ + do { + ptr += count >> 2; size -= count; + + if (!size) + return (EM_NOMATCH); + + ptr++; size -= 4; + count = *ptr++; size -= 4; + ufp = (ucode_file_amd_t *)ptr; + } while (ucode_match_amd(eq_sig, &uusp->info, ufp, count) != EM_OK); + + uusp->ucodep = (uint8_t *)ufp; + uusp->usize = count; + uusp->expected_rev = ufp->uf_header.uh_patch_id; + + return (EM_OK); +} + +static ucode_errno_t +ucode_extract_intel(ucode_update_t *uusp, uint8_t *ucodep, int size) +{ + uint32_t header_size = UCODE_HEADER_SIZE_INTEL; + int remaining; + int found = 0; + ucode_errno_t search_rc = EM_NOMATCH; /* search result */ + + /* + * Go through the whole buffer in case there are + * multiple versions of matching microcode for this + * processor. + */ + for (remaining = size; remaining > 0; ) { + int total_size, body_size, ext_size; + uint8_t *curbuf = &ucodep[size - remaining]; + ucode_header_intel_t *uhp = (ucode_header_intel_t *)curbuf; + ucode_ext_table_intel_t *uetp = NULL; + ucode_errno_t tmprc; + + total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size); + body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size); + ext_size = total_size - (header_size + body_size); + + if (ext_size > 0) + uetp = (ucode_ext_table_intel_t *) + &curbuf[header_size + body_size]; + + tmprc = ucode_match_intel(uusp->sig, &uusp->info, uhp, uetp); + + /* + * Since we are searching through a big file + * containing microcode for pretty much all the + * processors, we are bound to get EM_NOMATCH + * at one point. However, if we return + * EM_NOMATCH to users, it will really confuse + * them. Therefore, if we ever find a match of + * a lower rev, we will set return code to + * EM_HIGHERREV. + */ + if (tmprc == EM_HIGHERREV) + search_rc = EM_HIGHERREV; + + if (tmprc == EM_OK && + uusp->expected_rev < uhp->uh_rev) { +#ifndef __xpv + uusp->ucodep = (uint8_t *)&curbuf[header_size]; +#else + uusp->ucodep = (uint8_t *)curbuf; +#endif + uusp->usize = + UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size); + uusp->expected_rev = uhp->uh_rev; + found = 1; + } + + remaining -= total_size; + } + + if (!found) + return (search_rc); + + return (EM_OK); +} /* * Entry point to microcode update from the ucode_drv driver. * @@ -488,32 +906,27 @@ ucode_read_rev(struct cpu_ucode_info *uinfop) ucode_errno_t ucode_update(uint8_t *ucodep, int size) { - uint32_t header_size = UCODE_HEADER_SIZE; - int remaining; int found = 0; processorid_t id; - struct ucode_update_struct cached = { 0 }; - struct ucode_update_struct *cachedp = NULL; + ucode_update_t cached = { 0 }; + ucode_update_t *cachedp = NULL; ucode_errno_t rc = EM_OK; ucode_errno_t search_rc = EM_NOMATCH; /* search result */ cpuset_t cpuset; -#ifdef __xpv - uint8_t *ustart; - uint32_t usize; -#endif + ASSERT(ucode); ASSERT(ucodep); CPUSET_ZERO(cpuset); - if (!ucode_capable(CPU)) + if (!ucode->capable(CPU)) return (EM_NOTSUP); mutex_enter(&cpu_lock); for (id = 0; id < max_ncpus; id++) { cpu_t *cpu; - struct ucode_update_struct uus = { 0 }; - struct ucode_update_struct *uusp = &uus; + ucode_update_t uus = { 0 }; + ucode_update_t *uusp = &uus; /* * If there is no such CPU or it is not xcall ready, skip it. @@ -542,61 +955,11 @@ ucode_update(uint8_t *ucodep, int size) * the other threads in an HT processor can update * the cpu_ucode_info structure in machcpu. */ - } else { - /* - * Go through the whole buffer in case there are - * multiple versions of matching microcode for this - * processor. - */ - for (remaining = size; remaining > 0; ) { - int total_size, body_size, ext_size; - uint8_t *curbuf = &ucodep[size - remaining]; - ucode_header_t *uhp = (ucode_header_t *)curbuf; - ucode_ext_table_t *uetp = NULL; - ucode_errno_t tmprc; - - total_size = - UCODE_TOTAL_SIZE(uhp->uh_total_size); - body_size = UCODE_BODY_SIZE(uhp->uh_body_size); - ext_size = total_size - - (header_size + body_size); - - if (ext_size > 0) - uetp = (ucode_ext_table_t *) - &curbuf[header_size + body_size]; - - tmprc = ucode_match(uusp->sig, &uusp->info, - uhp, uetp); - - /* - * Since we are searching through a big file - * containing microcode for pretty much all the - * processors, we are bound to get EM_NOMATCH - * at one point. However, if we return - * EM_NOMATCH to users, it will really confuse - * them. Therefore, if we ever find a match of - * a lower rev, we will set return code to - * EM_HIGHERREV. - */ - if (tmprc == EM_HIGHERREV) - search_rc = EM_HIGHERREV; - - if (tmprc == EM_OK && - uusp->expected_rev < uhp->uh_rev) { - uusp->ucodep = &curbuf[header_size]; -#ifdef __xpv - ustart = (uint8_t *)curbuf; - usize = UCODE_TOTAL_SIZE( - uhp->uh_total_size); -#endif - uusp->expected_rev = uhp->uh_rev; - bcopy(uusp, &cached, sizeof (cached)); - cachedp = &cached; - found = 1; - } - - remaining -= total_size; - } + } else if ((search_rc = ucode->extract(uusp, ucodep, size)) + == EM_OK) { + bcopy(uusp, &cached, sizeof (cached)); + cachedp = &cached; + found = 1; } /* Nothing to do */ @@ -612,7 +975,7 @@ ucode_update(uint8_t *ucodep, int size) * completed. */ if (id == 0) { - ucode_update_xpv(uusp, ustart, usize); + ucode_load_xpv(uusp); } #endif @@ -651,15 +1014,9 @@ ucode_update(uint8_t *ucodep, int size) void ucode_check(cpu_t *cp) { - struct cpu_ucode_info *uinfop; + cpu_ucode_info_t *uinfop; ucode_errno_t rc = EM_OK; -#ifdef __xpv - uint32_t ext_offset; - uint32_t body_size; - uint32_t ext_size; - uint8_t *ustart; - uint32_t usize; -#endif + uint32_t new_rev = 0; ASSERT(cp); if (cp->cpu_id == 0) @@ -668,19 +1025,33 @@ ucode_check(cpu_t *cp) uinfop = cp->cpu_m.mcpu_ucode_info; ASSERT(uinfop); - if (!ucode_capable(cp)) + /* set up function pointers if not already done */ + if (!ucode) + switch (cpuid_getvendor(cp)) { + case X86_VENDOR_AMD: + ucode = &ucode_amd; + break; + case X86_VENDOR_Intel: + ucode = &ucode_intel; + break; + default: + return; + } + + if (!ucode->capable(cp)) return; /* * The MSR_INTC_PLATFORM_ID is supported in Celeron and Xeon * (Family 6, model 5 and above) and all processors after. */ - if ((cpuid_getmodel(cp) >= 5) || (cpuid_getfamily(cp) > 6)) { + if ((cpuid_getvendor(cp) == X86_VENDOR_Intel) && + ((cpuid_getmodel(cp) >= 5) || (cpuid_getfamily(cp) > 6))) { uinfop->cui_platid = 1 << ((rdmsr(MSR_INTC_PLATFORM_ID) >> INTC_PLATFORM_ID_SHIFT) & INTC_PLATFORM_ID_MASK); } - ucode_read_rev(uinfop); + ucode->read_rev(uinfop); #ifdef __xpv /* @@ -695,38 +1066,12 @@ ucode_check(cpu_t *cp) /* * Check to see if we need ucode update */ - if ((rc = ucode_locate(cp, uinfop, &ucodefile)) == EM_OK) { -#ifndef __xpv - ucode_update_intel(ucodefile.uf_body, uinfop); -#else - /* - * the hypervisor wants the header, data, and extended - * signature tables. We can only get here from the boot - * CPU (cpu #0), so use BOP_ALLOC. Since we're using BOP_ALLOC, - * We don't need to free. - */ - usize = UCODE_TOTAL_SIZE(ucodefile.uf_header.uh_total_size); - ustart = (uint8_t *)BOP_ALLOC(bootops, NULL, usize, - MMU_PAGESIZE); - - body_size = UCODE_BODY_SIZE(ucodefile.uf_header.uh_body_size); - ext_offset = body_size + UCODE_HEADER_SIZE; - ext_size = usize - ext_offset; - ASSERT(ext_size >= 0); - - (void) memcpy(ustart, &ucodefile.uf_header, UCODE_HEADER_SIZE); - (void) memcpy(&ustart[UCODE_HEADER_SIZE], ucodefile.uf_body, - body_size); - if (ext_size > 0) { - (void) memcpy(&ustart[ext_offset], - ucodefile.uf_ext_table, ext_size); - } - ucode_update_xpv(NULL, ustart, usize); -#endif + if ((rc = ucode->locate(cp, uinfop, &ucodefile)) == EM_OK) { + new_rev = ucode->load(&ucodefile, uinfop, cp); - if (uinfop->cui_rev != ucodefile.uf_header.uh_rev) + if (uinfop->cui_rev != new_rev) cmn_err(CE_WARN, ucode_failure_fmt, cp->cpu_id, - uinfop->cui_rev, ucodefile.uf_header.uh_rev); + uinfop->cui_rev, new_rev); } /* @@ -740,7 +1085,7 @@ ucode_check(cpu_t *cp) * of the CPUs in start_other_cpus(). */ if (rc != EM_OK || cp->cpu_id == 0) - ucode_file_reset(&ucodefile, cp->cpu_id); + ucode->file_reset(&ucodefile, cp->cpu_id); } /* @@ -751,9 +1096,10 @@ ucode_get_rev(uint32_t *revp) { int i; + ASSERT(ucode); ASSERT(revp); - if (!ucode_capable(CPU)) + if (!ucode->capable(CPU)) return (EM_NOTSUP); mutex_enter(&cpu_lock); diff --git a/usr/src/uts/i86pc/os/mp_startup.c b/usr/src/uts/i86pc/os/mp_startup.c index 55e1d85f6e..10492414be 100644 --- a/usr/src/uts/i86pc/os/mp_startup.c +++ b/usr/src/uts/i86pc/os/mp_startup.c @@ -1396,7 +1396,7 @@ start_other_cpus(int cprboot) } /* Free the space allocated to hold the microcode file */ - ucode_free(); + ucode_cleanup(); affinity_clear(); diff --git a/usr/src/uts/intel/io/ucode_drv.c b/usr/src/uts/intel/io/ucode_drv.c index d71b0bcbe5..6a63bba8a9 100644 --- a/usr/src/uts/intel/io/ucode_drv.c +++ b/usr/src/uts/intel/io/ucode_drv.c @@ -138,6 +138,12 @@ ucode_open(dev_t *dev, int flag, int otyp, cred_t *cr) static int ucode_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval) { + /* + * Make sure that the ucode ops pointer has been set up. + */ + if (!ucode) + return (EIO); + switch (cmd) { case UCODE_GET_VERSION: { int size; @@ -218,7 +224,7 @@ ucode_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval) return (EFAULT); } - if ((rc = ucode_validate(ucodep, size)) != EM_OK) { + if ((rc = ucode->validate(ucodep, size)) != EM_OK) { kmem_free(ucodep, size); STRUCT_FSET(h, uw_errno, rc); if (ddi_copyout(STRUCT_BUF(h), (void *)arg, diff --git a/usr/src/uts/intel/sys/controlregs.h b/usr/src/uts/intel/sys/controlregs.h index 23e04d3286..dc8ec9c8c5 100644 --- a/usr/src/uts/intel/sys/controlregs.h +++ b/usr/src/uts/intel/sys/controlregs.h @@ -26,8 +26,6 @@ #ifndef _SYS_CONTROLREGS_H #define _SYS_CONTROLREGS_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifndef _ASM #include <sys/types.h> #endif @@ -224,8 +222,9 @@ extern "C" { */ #define AMD_GH_NB_CFG_EN_ECS (UINT64_C(1) << 46) -/* AMD */ +/* AMD microcode patch loader */ #define MSR_AMD_PATCHLEVEL 0x8b +#define MSR_AMD_PATCHLOADER 0xc0010020 #ifdef __cplusplus } diff --git a/usr/src/uts/intel/sys/x86_archext.h b/usr/src/uts/intel/sys/x86_archext.h index ba8cbf15ea..89f07ce6cc 100644 --- a/usr/src/uts/intel/sys/x86_archext.h +++ b/usr/src/uts/intel/sys/x86_archext.h @@ -608,7 +608,7 @@ struct cpu_ucode_info; extern void ucode_alloc_space(struct cpu *); extern void ucode_free_space(struct cpu *); extern void ucode_check(struct cpu *); -extern void ucode_free(); +extern void ucode_cleanup(); #if !defined(__xpv) extern char _tsc_mfence_start; |