diff --git a/sys/x86/include/ucode.h b/sys/x86/include/ucode.h index 0338d48a0832..75b9ff3afbd0 100644 --- a/sys/x86/include/ucode.h +++ b/sys/x86/include/ucode.h @@ -1,77 +1,77 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2018 The FreeBSD Foundation * * This software was developed by Mark Johnston under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #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; uint32_t dat; uint32_t processor_signature; uint32_t checksum; uint32_t loader_revision; uint32_t processor_flags; #define UCODE_INTEL_DEFAULT_DATA_SIZE 2000 uint32_t data_size; uint32_t total_size; uint32_t reserved[3]; }; struct ucode_intel_extsig_table { uint32_t signature_count; uint32_t signature_table_checksum; uint32_t reserved[3]; struct ucode_intel_extsig { uint32_t processor_signature; uint32_t processor_flags; uint32_t checksum; } 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, + 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); void * ucode_update(void *data); #endif /* _MACHINE_UCODE_H_ */ diff --git a/sys/x86/x86/ucode.c b/sys/x86/x86/ucode.c index 0c153c0b656c..1973047fafd1 100644 --- a/sys/x86/x86/ucode.c +++ b/sys/x86/x86/ucode.c @@ -1,454 +1,455 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2018 The FreeBSD Foundation * * This software was developed by Mark Johnston under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF 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 #include #include static const void *ucode_intel_match(const uint8_t *data, size_t *len); 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 *); const void *(*match)(const uint8_t *, size_t *); } loaders[] = { { .vendor = INTEL_VENDOR_ID, .load = ucode_intel_load, .match = ucode_intel_match, }, { .vendor = AMD_VENDOR_ID, .load = ucode_amd_load, .match = ucode_amd_match, }, }; /* Selected microcode update data. */ static const void *early_ucode_data; static const void *ucode_data; static struct ucode_ops *ucode_loader; /* Variables used for reporting success or failure. */ enum { NO_ERROR, NO_MATCH, VERIFICATION_FAILED, } ucode_error = NO_ERROR; static uint64_t ucode_nrev, ucode_orev; static void log_msg(void *arg __unused) { if (ucode_nrev != 0) { printf("CPU microcode: updated from %#jx to %#jx\n", (uintmax_t)ucode_orev, (uintmax_t)ucode_nrev); return; } switch (ucode_error) { case NO_MATCH: printf("CPU microcode: no matching update found\n"); break; case VERIFICATION_FAILED: printf("CPU microcode: microcode verification failed\n"); break; default: break; } } SYSINIT(ucode_log, SI_SUB_CPU, SI_ORDER_FIRST, log_msg, NULL); int ucode_intel_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) >> 32; /* * Perform update. Flush caches first to work around seemingly * undocumented errata applying to some Broadwell CPUs. */ wbinvd(); if (unsafe) wrmsr_safe(MSR_BIOS_UPDT_TRIG, (uint64_t)(uintptr_t)data); else wrmsr(MSR_BIOS_UPDT_TRIG, (uint64_t)(uintptr_t)data); wrmsr(MSR_BIOS_SIGN, 0); /* * Serialize instruction flow. */ do_cpuid(0, cpuid); /* * Verify that the microcode revision changed. */ nrev = rdmsr(MSR_BIOS_SIGN) >> 32; if (nrevp != NULL) *nrevp = nrev; if (orevp != NULL) *orevp = orev; if (nrev <= orev) return (EEXIST); return (0); } static int ucode_intel_verify(const struct ucode_intel_header *hdr, size_t resid) { const uint32_t *data; uint32_t cksum, size; int i; if (resid < sizeof(struct ucode_intel_header)) return (1); size = hdr->total_size; if (size == 0) size = UCODE_INTEL_DEFAULT_DATA_SIZE + sizeof(struct ucode_intel_header); if (hdr->header_version != 1) return (1); if (size % 16 != 0) return (1); if (resid < size) return (1); cksum = 0; data = (const uint32_t *)hdr; for (i = 0; i < size / sizeof(uint32_t); i++) cksum += data[i]; if (cksum != 0) return (1); return (0); } static const void * ucode_intel_match(const uint8_t *data, size_t *len) { const struct ucode_intel_header *hdr; const struct ucode_intel_extsig_table *table; const struct ucode_intel_extsig *entry; uint64_t platformid; size_t resid; uint32_t data_size, flags, regs[4], sig, total_size; int i; do_cpuid(1, regs); sig = regs[0]; platformid = rdmsr(MSR_IA32_PLATFORM_ID); flags = 1 << ((platformid >> 50) & 0x7); for (resid = *len; resid > 0; data += total_size, resid -= total_size) { hdr = (const struct ucode_intel_header *)data; if (ucode_intel_verify(hdr, resid) != 0) { ucode_error = VERIFICATION_FAILED; break; } data_size = hdr->data_size; total_size = hdr->total_size; if (data_size == 0) data_size = UCODE_INTEL_DEFAULT_DATA_SIZE; if (total_size == 0) total_size = UCODE_INTEL_DEFAULT_DATA_SIZE + sizeof(struct ucode_intel_header); if (data_size > total_size + sizeof(struct ucode_intel_header)) table = (const struct ucode_intel_extsig_table *) ((const uint8_t *)(hdr + 1) + data_size); else table = NULL; if (hdr->processor_signature == sig) { if ((hdr->processor_flags & flags) != 0) { *len = data_size; return (hdr + 1); } } else if (table != NULL) { for (i = 0; i < table->signature_count; i++) { entry = &table->entries[i]; if (entry->processor_signature == sig && (entry->processor_flags & flags) != 0) { *len = data_size; return (hdr + 1); } } } } 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)); + 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. */ static void ucode_release(void *arg __unused) { char *name, *type; caddr_t file; int release; if (early_ucode_data == NULL) return; release = 1; TUNABLE_INT_FETCH("debug.ucode.release", &release); if (!release) return; restart: file = 0; for (;;) { file = preload_search_next_name(file); if (file == 0) break; type = (char *)preload_search_info(file, MODINFO_TYPE); if (type == NULL || strcmp(type, "cpu_microcode") != 0) continue; name = preload_search_info(file, MODINFO_NAME); preload_delete_name(name); goto restart; } } SYSINIT(ucode_release, SI_SUB_SMP + 1, SI_ORDER_ANY, ucode_release, NULL); void ucode_load_ap(int cpu) { #ifdef SMP KASSERT(cpu_info[cpu_apic_ids[cpu]].cpu_present, ("cpu %d not present", cpu)); if (cpu_info[cpu_apic_ids[cpu]].cpu_hyperthread) return; #endif if (ucode_data != NULL) (void)ucode_loader->load(ucode_data, false, NULL, NULL); } static void * map_ucode(uintptr_t free, size_t len) { #ifdef __i386__ uintptr_t va; for (va = free; va < free + len; va += PAGE_SIZE) pmap_kenter(va, (vm_paddr_t)va); #else (void)len; #endif return ((void *)free); } static void unmap_ucode(uintptr_t free, size_t len) { #ifdef __i386__ uintptr_t va; for (va = free; va < free + len; va += PAGE_SIZE) pmap_kremove(va); #else (void)free; (void)len; #endif } /* * Search for an applicable microcode update, and load it. APs will load the * selected update once they come online. * * "free" is the address of the next free physical page. If a microcode update * is selected, it will be copied to this region prior to loading in order to * satisfy alignment requirements. */ size_t ucode_load_bsp(uintptr_t free) { union { uint32_t regs[4]; char vendor[13]; } cpuid; const uint8_t *fileaddr, *match; uint8_t *addr; char *type; uint64_t nrev, orev; caddr_t file; size_t i, len; int error; KASSERT(free % PAGE_SIZE == 0, ("unaligned boundary %p", (void *)free)); do_cpuid(0, cpuid.regs); cpuid.regs[0] = cpuid.regs[1]; cpuid.regs[1] = cpuid.regs[3]; cpuid.vendor[12] = '\0'; for (i = 0; i < nitems(loaders); i++) if (strcmp(cpuid.vendor, loaders[i].vendor) == 0) { ucode_loader = &loaders[i]; break; } if (ucode_loader == NULL) return (0); file = 0; fileaddr = match = NULL; for (;;) { file = preload_search_next_name(file); if (file == 0) break; type = (char *)preload_search_info(file, MODINFO_TYPE); if (type == NULL || strcmp(type, "cpu_microcode") != 0) continue; fileaddr = preload_fetch_addr(file); len = preload_fetch_size(file); match = ucode_loader->match(fileaddr, &len); if (match != NULL) { addr = map_ucode(free, len); /* We can't use memcpy() before ifunc resolution. */ memcpy_early(addr, match, len); match = addr; error = ucode_loader->load(match, false, &nrev, &orev); if (error == 0) { ucode_data = early_ucode_data = match; ucode_nrev = nrev; ucode_orev = orev; return (len); } unmap_ucode(free, len); } } if (fileaddr != NULL && ucode_error == NO_ERROR) ucode_error = NO_MATCH; return (0); } /* * Reload microcode following an ACPI resume. */ void ucode_reload(void) { ucode_load_ap(PCPU_GET(cpuid)); } /* * Replace an existing microcode update. */ void * ucode_update(void *newdata) { newdata = (void *)atomic_swap_ptr((void *)&ucode_data, (uintptr_t)newdata); if (newdata == early_ucode_data) newdata = NULL; return (newdata); } diff --git a/sys/x86/x86/ucode_subr.c b/sys/x86/x86/ucode_subr.c index 9e128ad2bf04..53d7cfc06769 100644 --- a/sys/x86/x86/ucode_subr.c +++ b/sys/x86/x86/ucode_subr.c @@ -1,239 +1,239 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2006, 2008 Stanislav Sedov . * All rights reserved. * Copyright (c) 2012 Andriy Gapon . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #ifndef _KERNEL #include #include "cpucontrol.h" #endif #ifdef _KERNEL #define WARNX(level, ...) \ if (bootverbose) { \ printf(__VA_ARGS__); \ printf("\n"); \ } #endif /* * 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. */ const void * -ucode_amd_find(const char *path, uint32_t signature, uint32_t revision, +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) { 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; size_t selected_size; uint16_t equiv_id; int i; 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); + WARNX(1, "microcode revision %#x", *revision); 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); return (NULL); } 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); return (NULL); } 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); return (NULL); } if (section_header->size == 0) { WARNX(2, "%s is not a valid amd firmware: " "first section is empty", path); return (NULL); } 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); 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); return (NULL); } 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++) { 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," " 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"); } 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); break; } if (section_header->size > fw_size) { WARNX(2, "%s is not a valid amd firmware: " "file is truncated", path); break; } if (section_header->size < sizeof(*fw_header)) { WARNX(2, "%s is not a valid amd firmware: " "section is too short", path); break; } 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) { + if (fw_header->patch_id <= *revision) { WARNX(1, "patch_id %x, revision %x", - fw_header->patch_id, revision); + 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; + *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); selected_fw = NULL; } found: *selected_sizep = selected_size; return (selected_fw); } diff --git a/usr.sbin/cpucontrol/amd10h.c b/usr.sbin/cpucontrol/amd10h.c index 4fda44f0b797..9fc861fe5914 100644 --- a/usr.sbin/cpucontrol/amd10h.c +++ b/usr.sbin/cpucontrol/amd10h.c @@ -1,157 +1,158 @@ /*- * Copyright (c) 2012 Andriy Gapon . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * 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 "cpucontrol.h" #include "amd.h" 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); } ((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); 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); } 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 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 new_rev, old_rev; uint32_t signature; int devfd; 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; + old_rev = revision = (uint32_t)msrargs.data; - selected_fw = ucode_amd_find(path, signature, revision, fw_image, + 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); - fprintf(stderr, "%s: updating cpu %s to revision %#x... ", - path, dev, revision); + fprintf(stderr, + "%s: updating cpu %s from rev %#x to rev %#x... ", + path, dev, old_rev, 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; } new_rev = (uint32_t)msrargs.data; if (new_rev != revision) WARNX(0, "revision after update %#x", new_rev); done: return; }