diff --git a/sys/conf/files.x86 b/sys/conf/files.x86 --- a/sys/conf/files.x86 +++ b/sys/conf/files.x86 @@ -347,6 +347,7 @@ x86/x86/stack_machdep.c optional ddb | stack x86/x86/tsc.c standard x86/x86/ucode.c standard +x86/x86/ucode_subr.c standard x86/x86/delay.c standard x86/xen/hvm.c optional xenhvm x86/xen/xen_apic.c optional xenhvm smp diff --git a/sys/x86/include/ucode.h b/sys/x86/include/ucode.h --- a/sys/x86/include/ucode.h +++ b/sys/x86/include/ucode.h @@ -31,6 +31,12 @@ #ifndef _MACHINE_UCODE_H_ #define _MACHINE_UCODE_H_ +#ifdef _KERNEL +#include +#else +#include +#endif + struct ucode_intel_header { uint32_t header_version; int32_t update_revision; @@ -56,8 +62,13 @@ } entries[0]; }; +const void *ucode_amd_find(const char *path, uint32_t signature, + uint32_t revision, const uint8_t *fw_data, size_t fw_size, + size_t *selected_sizep); int ucode_intel_load(const void *data, bool unsafe, uint64_t *nrevp, uint64_t *orevp); +int ucode_amd_load(const void *data, bool unsafe, + uint64_t *nrevp, uint64_t *orevp); size_t ucode_load_bsp(uintptr_t free); void ucode_load_ap(int cpu); void ucode_reload(void); diff --git a/sys/x86/x86/ucode.c b/sys/x86/x86/ucode.c --- a/sys/x86/x86/ucode.c +++ b/sys/x86/x86/ucode.c @@ -54,6 +54,8 @@ static int ucode_intel_verify(const struct ucode_intel_header *hdr, size_t resid); +static const void *ucode_amd_match(const uint8_t *data, size_t *len); + static struct ucode_ops { const char *vendor; int (*load)(const void *, bool, uint64_t *, uint64_t *); @@ -64,6 +66,11 @@ .load = ucode_intel_load, .match = ucode_intel_match, }, + { + .vendor = AMD_VENDOR_ID, + .load = ucode_amd_load, + .match = ucode_amd_match, + }, }; /* Selected microcode update data. */ @@ -225,6 +232,54 @@ return (NULL); } +int +ucode_amd_load(const void *data, bool unsafe, uint64_t *nrevp, uint64_t *orevp) +{ + uint64_t nrev, orev; + uint32_t cpuid[4]; + + orev = rdmsr(MSR_BIOS_SIGN); + + /* + * Perform update. + */ + if (unsafe) + wrmsr_safe(MSR_K8_UCODE_UPDATE, (uint64_t)(uintptr_t)data); + else + wrmsr(MSR_K8_UCODE_UPDATE, (uint64_t)(uintptr_t)data); + + /* + * Serialize instruction flow. + */ + do_cpuid(0, cpuid); + + /* + * Verify that the microcode revision changed. + */ + nrev = rdmsr(MSR_BIOS_SIGN); + if (nrevp != NULL) + *nrevp = nrev; + if (orevp != NULL) + *orevp = orev; + if (nrev <= orev) + return (EEXIST); + return (0); + +} + +static const void * +ucode_amd_match(const uint8_t *data, size_t *len) +{ + uint32_t signature, revision; + uint32_t regs[4]; + + do_cpuid(1, regs); + signature = regs[0]; + revision = rdmsr(MSR_BIOS_SIGN); + + return (ucode_amd_find("loader blob", signature, revision, data, *len, len)); +} + /* * Release any memory backing unused microcode blobs back to the system. * We copy the selected update and free the entire microcode file. diff --git a/usr.sbin/cpucontrol/amd10h.c b/sys/x86/x86/ucode_subr.c copy from usr.sbin/cpucontrol/amd10h.c copy to sys/x86/x86/ucode_subr.c --- a/usr.sbin/cpucontrol/amd10h.c +++ b/sys/x86/x86/ucode_subr.c @@ -1,4 +1,8 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2006, 2008 Stanislav Sedov . + * All rights reserved. * Copyright (c) 2012 Andriy Gapon . * All rights reserved. * @@ -23,115 +27,84 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include -#include -#include -#include -#include -#include +#include +#include -#include -#include +#include -#include -#include -#include -#include -#include -#include +#ifndef _KERNEL #include #include "cpucontrol.h" -#include "amd.h" +#endif -int -amd10h_probe(int fd) -{ - char vendor[13]; - cpuctl_cpuid_args_t idargs; - uint32_t family; - uint32_t signature; - int error; - idargs.level = 0; - error = ioctl(fd, CPUCTL_CPUID, &idargs); - if (error < 0) { - WARN(0, "ioctl()"); - return (1); +#ifdef _KERNEL +#define WARNX(level, ...) \ + if (bootverbose) { \ + printf(__VA_ARGS__); \ + printf("\n"); \ } - ((uint32_t *)vendor)[0] = idargs.data[1]; - ((uint32_t *)vendor)[1] = idargs.data[3]; - ((uint32_t *)vendor)[2] = idargs.data[2]; - vendor[12] = '\0'; - if (strncmp(vendor, AMD_VENDOR_ID, sizeof(AMD_VENDOR_ID)) != 0) - return (1); +#endif - idargs.level = 1; - error = ioctl(fd, CPUCTL_CPUID, &idargs); - if (error < 0) { - WARN(0, "ioctl()"); - return (1); - } - signature = idargs.data[0]; - family = ((signature >> 8) & 0x0f) + ((signature >> 20) & 0xff); - if (family < 0x10) - return (1); - return (0); -} +/* + * AMD family 10h and later. + */ +typedef struct amd_10h_fw_header { + uint32_t data_code; + uint32_t patch_id; + uint16_t mc_patch_data_id; + uint8_t mc_patch_data_len; + uint8_t init_flag; + uint32_t mc_patch_data_checksum; + uint32_t nb_dev_id; + uint32_t sb_dev_id; + uint16_t processor_rev_id; + uint8_t nb_rev_id; + uint8_t sb_rev_id; + uint8_t bios_api_rev; + uint8_t reserved1[3]; + uint32_t match_reg[8]; +} amd_10h_fw_header_t; + +typedef struct equiv_cpu_entry { + uint32_t installed_cpu; + uint32_t fixed_errata_mask; + uint32_t fixed_errata_compare; + uint16_t equiv_cpu; + uint16_t res; +} equiv_cpu_entry_t; + +typedef struct section_header { + uint32_t type; + uint32_t size; +} section_header_t; + +typedef struct container_header { + uint32_t magic; +} container_header_t; + +#define AMD_10H_MAGIC 0x414d44 +#define AMD_10H_EQUIV_TABLE_TYPE 0 +#define AMD_10H_uCODE_TYPE 1 /* * NB: the format of microcode update files is not documented by AMD. * It has been reverse engineered from studying Coreboot, illumos and Linux * source code. */ -void -amd10h_update(const struct ucode_update_params *params) +const void * +ucode_amd_find(const char *path, uint32_t signature, uint32_t revision, + const uint8_t *fw_data, size_t fw_size, size_t *selected_sizep) { - cpuctl_cpuid_args_t idargs; - cpuctl_msr_args_t msrargs; - cpuctl_update_args_t args; const amd_10h_fw_header_t *fw_header; const amd_10h_fw_header_t *selected_fw; const equiv_cpu_entry_t *equiv_cpu_table; const section_header_t *section_header; const container_header_t *container_header; - const uint8_t *fw_data; - const uint8_t *fw_image; - const char *dev, *path; - size_t fw_size; size_t selected_size; - uint32_t revision; - uint32_t new_rev; - uint32_t signature; uint16_t equiv_id; - int devfd; - unsigned int i; - int error; - - dev = params->dev_path; - path = params->fw_path; - devfd = params->devfd; - fw_image = params->fwimage; - fw_size = params->fwsize; - - assert(path); - assert(dev); - - idargs.level = 1; - error = ioctl(devfd, CPUCTL_CPUID, &idargs); - if (error < 0) { - WARN(0, "ioctl()"); - goto done; - } - signature = idargs.data[0]; - - msrargs.msr = MSR_BIOS_SIGN; - error = ioctl(devfd, CPUCTL_RDMSR, &msrargs); - if (error < 0) { - WARN(0, "ioctl(%s)", dev); - goto done; - } - revision = (uint32_t)msrargs.data; + int i; WARNX(1, "found cpu family %#x model %#x " "stepping %#x extfamily %#x extmodel %#x.", @@ -141,24 +114,19 @@ (signature >> 16) & 0x0f); WARNX(1, "microcode revision %#x", revision); - /* - * Open the firmware file. - */ +nextfile: WARNX(1, "checking %s for update.", path); + WARNX(3, "processing next container file"); if (fw_size < (sizeof(*container_header) + sizeof(*section_header))) { WARNX(2, "file too short: %s", path); - goto done; + return (NULL); } - /* - * mmap the whole image. - */ - fw_data = fw_image; container_header = (const container_header_t *)fw_data; if (container_header->magic != AMD_10H_MAGIC) { WARNX(2, "%s is not a valid amd firmware: bad magic", path); - goto done; + return (NULL); } fw_data += sizeof(*container_header); fw_size -= sizeof(*container_header); @@ -167,12 +135,12 @@ if (section_header->type != AMD_10H_EQUIV_TABLE_TYPE) { WARNX(2, "%s is not a valid amd firmware: " "first section is not CPU equivalence table", path); - goto done; + return (NULL); } if (section_header->size == 0) { WARNX(2, "%s is not a valid amd firmware: " "first section is empty", path); - goto done; + return (NULL); } fw_data += sizeof(*section_header); fw_size -= sizeof(*section_header); @@ -180,12 +148,12 @@ if (section_header->size > fw_size) { WARNX(2, "%s is not a valid amd firmware: " "file is truncated", path); - goto done; + return (NULL); } if (section_header->size < sizeof(*equiv_cpu_table)) { WARNX(2, "%s is not a valid amd firmware: " "first section is too short", path); - goto done; + return (NULL); } equiv_cpu_table = (const equiv_cpu_entry_t *)fw_data; fw_data += section_header->size; @@ -193,6 +161,9 @@ equiv_id = 0; for (i = 0; equiv_cpu_table[i].installed_cpu != 0; i++) { + WARNX(3, "signature 0x%x i %d installed_cpu 0x%x equiv 0x%x", + signature, i, equiv_cpu_table[i].installed_cpu, + equiv_cpu_table[i].equiv_cpu); if (signature == equiv_cpu_table[i].installed_cpu) { equiv_id = equiv_cpu_table[i].equiv_cpu; WARNX(3, "equiv_id: %x, signature %8x," @@ -203,29 +174,34 @@ } if (equiv_id == 0) { WARNX(2, "CPU is not found in the equivalence table"); - goto done; } - selected_fw = NULL; - selected_size = 0; while (fw_size >= sizeof(*section_header)) { section_header = (const section_header_t *)fw_data; + if (section_header->type == AMD_10H_MAGIC) { + WARNX(2, "%s next section is actually a new container", + path); + if (selected_fw != NULL) + goto found; + else + goto nextfile; + } fw_data += sizeof(*section_header); fw_size -= sizeof(*section_header); if (section_header->type != AMD_10H_uCODE_TYPE) { WARNX(2, "%s is not a valid amd firmware: " "section has incorrect type", path); - goto done; + break; } if (section_header->size > fw_size) { WARNX(2, "%s is not a valid amd firmware: " "file is truncated", path); - goto done; + break; } if (section_header->size < sizeof(*fw_header)) { WARNX(2, "%s is not a valid amd firmware: " "section is too short", path); - goto done; + break; } fw_header = (const amd_10h_fw_header_t *)fw_data; fw_data += section_header->size; @@ -254,35 +230,10 @@ if (fw_size != 0) { WARNX(2, "%s is not a valid amd firmware: " "file is truncated", path); - goto done; - } - - if (selected_fw != NULL) { - WARNX(1, "selected ucode size is %zu", selected_size); - fprintf(stderr, "%s: updating cpu %s to revision %#x... ", - path, dev, revision); - - args.data = __DECONST(void *, selected_fw); - args.size = selected_size; - error = ioctl(devfd, CPUCTL_UPDATE, &args); - if (error < 0) { - fprintf(stderr, "failed.\n"); - warn("ioctl()"); - goto done; - } - fprintf(stderr, "done.\n"); - } - - msrargs.msr = MSR_BIOS_SIGN; - error = ioctl(devfd, CPUCTL_RDMSR, &msrargs); - if (error < 0) { - WARN(0, "ioctl(%s)", dev); - goto done; + selected_fw = NULL; } - new_rev = (uint32_t)msrargs.data; - if (new_rev != revision) - WARNX(0, "revision after update %#x", new_rev); -done: - return; +found: + *selected_sizep = selected_size; + return (selected_fw); } diff --git a/usr.sbin/cpucontrol/Makefile b/usr.sbin/cpucontrol/Makefile --- a/usr.sbin/cpucontrol/Makefile +++ b/usr.sbin/cpucontrol/Makefile @@ -1,8 +1,12 @@ PROG= cpucontrol MAN= cpucontrol.8 -SRCS= cpucontrol.c intel.c amd.c amd10h.c via.c +SRCS= cpucontrol.c intel.c amd.c amd10h.c via.c ucode_subr.c + +.PATH: ${SRCTOP}/sys/x86/x86 NO_WCAST_ALIGN= +CFLAGS+= -I${.CURDIR} + .include diff --git a/usr.sbin/cpucontrol/amd.h b/usr.sbin/cpucontrol/amd.h --- a/usr.sbin/cpucontrol/amd.h +++ b/usr.sbin/cpucontrol/amd.h @@ -48,45 +48,4 @@ #define AMD_MAGIC 0xaaaaaa -/* - * AMD family 10h and later. - */ -typedef struct amd_10h_fw_header { - uint32_t data_code; - uint32_t patch_id; - uint16_t mc_patch_data_id; - uint8_t mc_patch_data_len; - uint8_t init_flag; - uint32_t mc_patch_data_checksum; - uint32_t nb_dev_id; - uint32_t sb_dev_id; - uint16_t processor_rev_id; - uint8_t nb_rev_id; - uint8_t sb_rev_id; - uint8_t bios_api_rev; - uint8_t reserved1[3]; - uint32_t match_reg[8]; -} amd_10h_fw_header_t; - -typedef struct equiv_cpu_entry { - uint32_t installed_cpu; - uint32_t fixed_errata_mask; - uint32_t fixed_errata_compare; - uint16_t equiv_cpu; - uint16_t res; -} equiv_cpu_entry_t; - -typedef struct section_header { - uint32_t type; - uint32_t size; -} section_header_t; - -typedef struct container_header { - uint32_t magic; -} container_header_t; - -#define AMD_10H_MAGIC 0x414d44 -#define AMD_10H_EQUIV_TABLE_TYPE 0 -#define AMD_10H_uCODE_TYPE 1 - #endif /* !AMD_H */ diff --git a/usr.sbin/cpucontrol/amd10h.c b/usr.sbin/cpucontrol/amd10h.c --- a/usr.sbin/cpucontrol/amd10h.c +++ b/usr.sbin/cpucontrol/amd10h.c @@ -33,6 +33,8 @@ #include #include +#include + #include #include #include @@ -79,33 +81,21 @@ return (0); } -/* - * NB: the format of microcode update files is not documented by AMD. - * It has been reverse engineered from studying Coreboot, illumos and Linux - * source code. - */ void amd10h_update(const struct ucode_update_params *params) { cpuctl_cpuid_args_t idargs; cpuctl_msr_args_t msrargs; cpuctl_update_args_t args; - const amd_10h_fw_header_t *fw_header; - const amd_10h_fw_header_t *selected_fw; - const equiv_cpu_entry_t *equiv_cpu_table; - const section_header_t *section_header; - const container_header_t *container_header; - const uint8_t *fw_data; const uint8_t *fw_image; const char *dev, *path; + const void *selected_fw; size_t fw_size; size_t selected_size; uint32_t revision; uint32_t new_rev; uint32_t signature; - uint16_t equiv_id; int devfd; - unsigned int i; int error; dev = params->dev_path; @@ -133,129 +123,8 @@ } revision = (uint32_t)msrargs.data; - WARNX(1, "found cpu family %#x model %#x " - "stepping %#x extfamily %#x extmodel %#x.", - ((signature >> 8) & 0x0f) + ((signature >> 20) & 0xff), - (signature >> 4) & 0x0f, - (signature >> 0) & 0x0f, (signature >> 20) & 0xff, - (signature >> 16) & 0x0f); - WARNX(1, "microcode revision %#x", revision); - - /* - * Open the firmware file. - */ - WARNX(1, "checking %s for update.", path); - if (fw_size < - (sizeof(*container_header) + sizeof(*section_header))) { - WARNX(2, "file too short: %s", path); - goto done; - } - - /* - * mmap the whole image. - */ - fw_data = fw_image; - container_header = (const container_header_t *)fw_data; - if (container_header->magic != AMD_10H_MAGIC) { - WARNX(2, "%s is not a valid amd firmware: bad magic", path); - goto done; - } - fw_data += sizeof(*container_header); - fw_size -= sizeof(*container_header); - - section_header = (const section_header_t *)fw_data; - if (section_header->type != AMD_10H_EQUIV_TABLE_TYPE) { - WARNX(2, "%s is not a valid amd firmware: " - "first section is not CPU equivalence table", path); - goto done; - } - if (section_header->size == 0) { - WARNX(2, "%s is not a valid amd firmware: " - "first section is empty", path); - goto done; - } - fw_data += sizeof(*section_header); - fw_size -= sizeof(*section_header); - - if (section_header->size > fw_size) { - WARNX(2, "%s is not a valid amd firmware: " - "file is truncated", path); - goto done; - } - if (section_header->size < sizeof(*equiv_cpu_table)) { - WARNX(2, "%s is not a valid amd firmware: " - "first section is too short", path); - goto done; - } - equiv_cpu_table = (const equiv_cpu_entry_t *)fw_data; - fw_data += section_header->size; - fw_size -= section_header->size; - - equiv_id = 0; - for (i = 0; equiv_cpu_table[i].installed_cpu != 0; i++) { - if (signature == equiv_cpu_table[i].installed_cpu) { - equiv_id = equiv_cpu_table[i].equiv_cpu; - WARNX(3, "equiv_id: %x, signature %8x," - " equiv_cpu_table[%d] %8x", equiv_id, signature, - i, equiv_cpu_table[i].installed_cpu); - break; - } - } - if (equiv_id == 0) { - WARNX(2, "CPU is not found in the equivalence table"); - goto done; - } - - selected_fw = NULL; - selected_size = 0; - while (fw_size >= sizeof(*section_header)) { - section_header = (const section_header_t *)fw_data; - fw_data += sizeof(*section_header); - fw_size -= sizeof(*section_header); - if (section_header->type != AMD_10H_uCODE_TYPE) { - WARNX(2, "%s is not a valid amd firmware: " - "section has incorrect type", path); - goto done; - } - if (section_header->size > fw_size) { - WARNX(2, "%s is not a valid amd firmware: " - "file is truncated", path); - goto done; - } - if (section_header->size < sizeof(*fw_header)) { - WARNX(2, "%s is not a valid amd firmware: " - "section is too short", path); - goto done; - } - fw_header = (const amd_10h_fw_header_t *)fw_data; - fw_data += section_header->size; - fw_size -= section_header->size; - - if (fw_header->processor_rev_id != equiv_id) { - WARNX(1, "firmware processor_rev_id %x, equiv_id %x", - fw_header->processor_rev_id, equiv_id); - continue; /* different cpu */ - } - if (fw_header->patch_id <= revision) { - WARNX(1, "patch_id %x, revision %x", - fw_header->patch_id, revision); - continue; /* not newer revision */ - } - if (fw_header->nb_dev_id != 0 || fw_header->sb_dev_id != 0) { - WARNX(2, "Chipset-specific microcode is not supported"); - } - - WARNX(3, "selecting revision: %x", fw_header->patch_id); - revision = fw_header->patch_id; - selected_fw = fw_header; - selected_size = section_header->size; - } - - if (fw_size != 0) { - WARNX(2, "%s is not a valid amd firmware: " - "file is truncated", path); - goto done; - } + selected_fw = ucode_amd_find(path, signature, revision, fw_image, + fw_size, &selected_size); if (selected_fw != NULL) { WARNX(1, "selected ucode size is %zu", selected_size);