diff options
| author | Mark Johnston <markj@FreeBSD.org> | 2018-08-13 17:13:09 +0000 |
|---|---|---|
| committer | Mark Johnston <markj@FreeBSD.org> | 2018-08-13 17:13:09 +0000 |
| commit | 97edfc1b456be505af1e592ddad4c0bac6bc0b46 (patch) | |
| tree | b4d056c289650dab6ad6d86bcdd8bcc59c275d4c /sys | |
| parent | c1344d2bbe832b2755c9f5a44d6fc30e3b7a0ddb (diff) | |
Notes
Diffstat (limited to 'sys')
| -rw-r--r-- | sys/amd64/amd64/machdep.c | 4 | ||||
| -rw-r--r-- | sys/amd64/amd64/mp_machdep.c | 4 | ||||
| -rw-r--r-- | sys/conf/files.amd64 | 1 | ||||
| -rw-r--r-- | sys/conf/files.i386 | 1 | ||||
| -rw-r--r-- | sys/dev/cpuctl/cpuctl.c | 37 | ||||
| -rw-r--r-- | sys/i386/i386/locore.s | 6 | ||||
| -rw-r--r-- | sys/i386/i386/machdep.c | 12 | ||||
| -rw-r--r-- | sys/i386/i386/mp_machdep.c | 6 | ||||
| -rw-r--r-- | sys/x86/acpica/acpi_wakeup.c | 4 | ||||
| -rw-r--r-- | sys/x86/include/ucode.h | 67 | ||||
| -rw-r--r-- | sys/x86/x86/mp_x86.c | 6 | ||||
| -rw-r--r-- | sys/x86/x86/ucode.c | 383 |
12 files changed, 503 insertions, 28 deletions
diff --git a/sys/amd64/amd64/machdep.c b/sys/amd64/amd64/machdep.c index c7391a26bbc1..e1f148be16fa 100644 --- a/sys/amd64/amd64/machdep.c +++ b/sys/amd64/amd64/machdep.c @@ -130,6 +130,7 @@ __FBSDID("$FreeBSD$"); #include <machine/specialreg.h> #include <machine/trap.h> #include <machine/tss.h> +#include <x86/ucode.h> #ifdef SMP #include <machine/smp.h> #endif @@ -1568,6 +1569,9 @@ hammer_time(u_int64_t modulep, u_int64_t physfree) kmdp = init_ops.parse_preload_data(modulep); + physfree += ucode_load_bsp(physfree + KERNBASE); + physfree = roundup2(physfree, PAGE_SIZE); + identify_cpu1(); identify_hypervisor(); /* diff --git a/sys/amd64/amd64/mp_machdep.c b/sys/amd64/amd64/mp_machdep.c index 8062ed62161d..c3fc2109568d 100644 --- a/sys/amd64/amd64/mp_machdep.c +++ b/sys/amd64/amd64/mp_machdep.c @@ -71,6 +71,7 @@ __FBSDID("$FreeBSD$"); #include <machine/smp.h> #include <machine/specialreg.h> #include <machine/tss.h> +#include <x86/ucode.h> #include <machine/cpu.h> #include <x86/init.h> @@ -242,6 +243,9 @@ init_secondary(void) /* Set by the startup code for us to use */ cpu = bootAP; + /* Update microcode before doing anything else. */ + ucode_load_ap(cpu); + /* Init tss */ common_tss[cpu] = common_tss[0]; common_tss[cpu].tss_iobase = sizeof(struct amd64tss) + diff --git a/sys/conf/files.amd64 b/sys/conf/files.amd64 index 9fdde8098c9c..93bf50f9502f 100644 --- a/sys/conf/files.amd64 +++ b/sys/conf/files.amd64 @@ -745,6 +745,7 @@ x86/x86/nexus.c standard x86/x86/pvclock.c standard x86/x86/stack_machdep.c optional ddb | stack x86/x86/tsc.c standard +x86/x86/ucode.c standard x86/x86/delay.c standard x86/xen/hvm.c optional xenhvm x86/xen/xen_intr.c optional xenhvm diff --git a/sys/conf/files.i386 b/sys/conf/files.i386 index a5dabaac2e0d..d2591137a990 100644 --- a/sys/conf/files.i386 +++ b/sys/conf/files.i386 @@ -621,6 +621,7 @@ x86/x86/msi.c optional apic pci x86/x86/nexus.c standard x86/x86/stack_machdep.c optional ddb | stack x86/x86/tsc.c standard +x86/x86/ucode.c standard x86/x86/pvclock.c standard x86/x86/delay.c standard x86/xen/hvm.c optional xenhvm diff --git a/sys/dev/cpuctl/cpuctl.c b/sys/dev/cpuctl/cpuctl.c index 3893b4d6ce74..33ac83eb731a 100644 --- a/sys/dev/cpuctl/cpuctl.c +++ b/sys/dev/cpuctl/cpuctl.c @@ -53,6 +53,7 @@ __FBSDID("$FreeBSD$"); #include <machine/cpufunc.h> #include <machine/md_var.h> #include <machine/specialreg.h> +#include <x86/ucode.h> static d_open_t cpuctl_open; static d_ioctl_t cpuctl_ioctl; @@ -333,11 +334,7 @@ static int update_intel(int cpu, cpuctl_update_args_t *args, struct thread *td) { void *ptr; - uint64_t rev0, rev1; - uint32_t tmp[4]; - int is_bound; - int oldcpu; - int ret; + int is_bound, oldcpu, ret; if (args->size == 0 || args->data == NULL) { DPRINTF("[cpuctl,%d]: zero-sized firmware image", __LINE__); @@ -358,34 +355,26 @@ update_intel(int cpu, cpuctl_update_args_t *args, struct thread *td) DPRINTF("[cpuctl,%d]: copyin %p->%p of %zd bytes failed", __LINE__, args->data, ptr, args->size); ret = EFAULT; - goto fail; + goto out; } oldcpu = td->td_oncpu; is_bound = cpu_sched_is_bound(td); set_cpu(cpu, td); critical_enter(); - rdmsr_safe(MSR_BIOS_SIGN, &rev0); /* Get current microcode revision. */ - /* - * Perform update. Flush caches first to work around seemingly - * undocumented errata applying to some Broadwell CPUs. - */ - wbinvd(); - wrmsr_safe(MSR_BIOS_UPDT_TRIG, (uintptr_t)(ptr)); - wrmsr_safe(MSR_BIOS_SIGN, 0); + ret = ucode_intel_load(ptr, true); - /* - * Serialize instruction flow. - */ - do_cpuid(0, tmp); critical_exit(); - rdmsr_safe(MSR_BIOS_SIGN, &rev1); /* Get new microcode revision. */ restore_cpu(oldcpu, is_bound, td); - if (rev1 > rev0) - ret = 0; - else - ret = EEXIST; -fail: + + /* + * Replace any existing update. This ensures that the new update + * will be reloaded automatically during ACPI resume. + */ + if (ret == 0) + ptr = ucode_update(ptr); + +out: free(ptr, M_CPUCTL); return (ret); } diff --git a/sys/i386/i386/locore.s b/sys/i386/i386/locore.s index 1c0f789e4c2c..e8b677ce3a8a 100644 --- a/sys/i386/i386/locore.s +++ b/sys/i386/i386/locore.s @@ -328,7 +328,9 @@ olddiskboot: * Identify the CPU and initialize anything special about it * */ -identify_cpu: +ENTRY(identify_cpu) + + pushl %ebx /* Try to toggle alignment check flag; does not exist on 386. */ pushfl @@ -449,7 +451,9 @@ trycpuid: /* Use the `cpuid' instruction. */ /* Greater than Pentium...call it a Pentium Pro */ movl $CPU_686,cpu 3: + popl %ebx ret +END(identify_cpu) #ifdef XENHVM /* Xen Hypercall page */ diff --git a/sys/i386/i386/machdep.c b/sys/i386/i386/machdep.c index 8fa6de021cba..23b82b311391 100644 --- a/sys/i386/i386/machdep.c +++ b/sys/i386/i386/machdep.c @@ -133,6 +133,7 @@ __FBSDID("$FreeBSD$"); #include <machine/specialreg.h> #include <machine/sysarch.h> #include <machine/trap.h> +#include <x86/ucode.h> #include <machine/vm86.h> #include <x86/init.h> #ifdef PERFMON @@ -158,6 +159,7 @@ CTASSERT(offsetof(struct pcpu, pc_curthread) == 0); register_t init386(int first); void dblfault_handler(void); +void identify_cpu(void); static void cpu_startup(void *); static void fpstate_drop(struct thread *td); @@ -2311,6 +2313,7 @@ init386(int first) struct xstate_hdr *xhdr; caddr_t kmdp; vm_offset_t addend; + size_t ucode_len; int late_console; thread0.td_kstack = proc0kstack; @@ -2340,6 +2343,15 @@ init386(int first) init_static_kenv(NULL, 0); } + /* + * Re-evaluate CPU features if we loaded a microcode update. + */ + ucode_len = ucode_load_bsp(first); + if (ucode_len != 0) { + identify_cpu(); + first = roundup2(first + ucode_len, PAGE_SIZE); + } + identify_hypervisor(); /* Init basic tunables, hz etc */ diff --git a/sys/i386/i386/mp_machdep.c b/sys/i386/i386/mp_machdep.c index f325d4dc3a26..85ebbd259526 100644 --- a/sys/i386/i386/mp_machdep.c +++ b/sys/i386/i386/mp_machdep.c @@ -73,6 +73,7 @@ __FBSDID("$FreeBSD$"); #include <x86/apicreg.h> #include <machine/clock.h> +#include <machine/cpu.h> #include <machine/cputypes.h> #include <x86/mca.h> #include <machine/md_var.h> @@ -80,7 +81,7 @@ __FBSDID("$FreeBSD$"); #include <machine/psl.h> #include <machine/smp.h> #include <machine/specialreg.h> -#include <machine/cpu.h> +#include <x86/ucode.h> #define WARMBOOT_TARGET 0 #define WARMBOOT_OFF (PMAP_MAP_LOW + 0x0467) @@ -217,6 +218,9 @@ init_secondary(void) /* bootAP is set in start_ap() to our ID. */ myid = bootAP; + /* Update microcode before doing anything else. */ + ucode_load_ap(myid); + /* Get per-cpu data */ pc = &__pcpu[myid]; diff --git a/sys/x86/acpica/acpi_wakeup.c b/sys/x86/acpica/acpi_wakeup.c index 699512266255..222fd4fb055b 100644 --- a/sys/x86/acpica/acpi_wakeup.c +++ b/sys/x86/acpica/acpi_wakeup.c @@ -54,10 +54,11 @@ __FBSDID("$FreeBSD$"); #include <machine/clock.h> #include <machine/cpu.h> #include <machine/intr_machdep.h> +#include <machine/md_var.h> #include <x86/mca.h> #include <machine/pcb.h> #include <machine/specialreg.h> -#include <machine/md_var.h> +#include <x86/ucode.h> #ifdef DEV_APIC #include <x86/apicreg.h> @@ -324,6 +325,7 @@ acpi_wakeup_machdep(struct acpi_softc *sc, int state, int sleep_result, if (!intr_enabled) { /* Wakeup MD procedures in interrupt disabled context */ if (sleep_result == 1) { + ucode_reload(); pmap_init_pat(); initializecpu(); PCPU_SET(switchtime, 0); diff --git a/sys/x86/include/ucode.h b/sys/x86/include/ucode.h new file mode 100644 index 000000000000..d9c860d20ca8 --- /dev/null +++ b/sys/x86/include/ucode.h @@ -0,0 +1,67 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * 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. + * + * $FreeBSD$ + */ + +#ifndef _MACHINE_UCODE_H_ +#define _MACHINE_UCODE_H_ + +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]; +}; + +int ucode_intel_load(void *data, bool unsafe); +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/mp_x86.c b/sys/x86/x86/mp_x86.c index 55544a00f7f4..e153a38530b8 100644 --- a/sys/x86/x86/mp_x86.c +++ b/sys/x86/x86/mp_x86.c @@ -65,6 +65,7 @@ __FBSDID("$FreeBSD$"); #include <x86/apicreg.h> #include <machine/clock.h> +#include <machine/cpu.h> #include <machine/cputypes.h> #include <x86/mca.h> #include <machine/md_var.h> @@ -72,7 +73,7 @@ __FBSDID("$FreeBSD$"); #include <machine/psl.h> #include <machine/smp.h> #include <machine/specialreg.h> -#include <machine/cpu.h> +#include <x86/ucode.h> static MALLOC_DEFINE(M_CPUS, "cpus", "CPU items"); @@ -1472,6 +1473,9 @@ cpususpend_handler(void) while (!CPU_ISSET(cpu, &toresume_cpus)) ia32_pause(); + /* Re-apply microcode updates. */ + ucode_reload(); + #ifdef __i386__ /* Finish removing the identity mapping of low memory for this AP. */ invltlb_glob(); diff --git a/sys/x86/x86/ucode.c b/sys/x86/x86/ucode.c new file mode 100644 index 000000000000..84581b45fbfc --- /dev/null +++ b/sys/x86/x86/ucode.c @@ -0,0 +1,383 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/cpuset.h> +#include <sys/kernel.h> +#include <sys/linker.h> +#include <sys/malloc.h> +#include <sys/pcpu.h> +#include <sys/smp.h> +#include <sys/systm.h> + +#include <machine/atomic.h> +#include <machine/cpufunc.h> +#include <x86/specialreg.h> +#include <machine/stdarg.h> +#include <x86/ucode.h> +#include <x86/x86_smp.h> + +#include <vm/vm.h> +#include <vm/pmap.h> +#include <vm/vm_extern.h> +#include <vm/vm_kern.h> +#include <vm/vm_param.h> + +static void *ucode_intel_match(uint8_t *data, size_t *len); +static int ucode_intel_verify(struct ucode_intel_header *hdr, + size_t resid); + +static struct ucode_ops { + const char *vendor; + int (*load)(void *, bool); + void *(*match)(uint8_t *, size_t *); +} loaders[] = { + { + .vendor = INTEL_VENDOR_ID, + .load = ucode_intel_load, + .match = ucode_intel_match, + }, +}; + +/* Selected microcode update data. */ +static void *early_ucode_data; +static void *ucode_data; + +static char errbuf[128]; + +static void __printflike(1, 2) +log_err(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vsnprintf(errbuf, sizeof(errbuf), fmt, ap); + va_end(ap); +} + +static void +print_err(void *arg __unused) +{ + + if (errbuf[0] != '\0') + printf("microcode load error: %s\n", errbuf); +} +SYSINIT(ucode_print_err, SI_SUB_CPU, SI_ORDER_FIRST, print_err, NULL); + +int +ucode_intel_load(void *data, bool unsafe) +{ + uint64_t rev0, rev1; + uint32_t cpuid[4]; + + rev0 = rdmsr(MSR_BIOS_SIGN); + + /* + * 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); + + rev1 = rdmsr(MSR_BIOS_SIGN); + if (rev1 <= rev0) + return (EEXIST); + return (0); +} + +static int +ucode_intel_verify(struct ucode_intel_header *hdr, size_t resid) +{ + uint32_t cksum, *data, size; + int i; + + if (resid < sizeof(struct ucode_intel_header)) { + log_err("truncated update 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) { + log_err("unexpected header version %u", hdr->header_version); + return (1); + } + if (size % 16 != 0) { + log_err("unexpected update size %u", hdr->total_size); + return (1); + } + if (resid < size) { + log_err("truncated update"); + return (1); + } + + cksum = 0; + data = (uint32_t *)hdr; + for (i = 0; i < size / sizeof(uint32_t); i++) + cksum += data[i]; + if (cksum != 0) { + log_err("checksum failed"); + return (1); + } + return (0); +} + +static void * +ucode_intel_match(uint8_t *data, size_t *len) +{ + struct ucode_intel_header *hdr; + struct ucode_intel_extsig_table *table; + 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 = (struct ucode_intel_header *)data; + if (ucode_intel_verify(hdr, resid) != 0) + 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 = (struct ucode_intel_extsig_table *) + ((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); +} + +/* + * 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_KMEM + 1, SI_ORDER_ANY, ucode_release, NULL); + +void +ucode_load_ap(int cpu) +{ + + KASSERT(cpu_info[cpu_apic_ids[cpu]].cpu_present, + ("cpu %d not present", cpu)); + + if (ucode_data != NULL && !cpu_info[cpu_apic_ids[cpu]].cpu_hyperthread) + (void)ucode_intel_load(ucode_data, false); +} + +static void * +map_ucode(vm_paddr_t free, size_t len) +{ + +#ifdef __i386__ + for (vm_paddr_t pa = free; pa < free + len; pa += PAGE_SIZE) + pmap_kenter(pa, pa); +#else + (void)len; +#endif + return ((void *)free); +} + +static void +unmap_ucode(vm_paddr_t free, size_t len) +{ + +#ifdef __i386__ + for (vm_paddr_t pa = free; pa < free + len; pa += PAGE_SIZE) + pmap_kremove((vm_offset_t)pa); +#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; + struct ucode_ops *loader; + uint8_t *addr, *fileaddr, *match; + char *type; + caddr_t file; + size_t len, ucode_len; + int i; + + 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, loader = NULL; i < nitems(loaders); i++) + if (strcmp(cpuid.vendor, loaders[i].vendor) == 0) { + loader = &loaders[i]; + break; + } + if (loader == NULL) + return (0); + + file = 0; + fileaddr = match = NULL; + ucode_len = 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; + + fileaddr = preload_fetch_addr(file); + len = preload_fetch_size(file); + match = loader->match(fileaddr, &len); + if (match != NULL) { + addr = map_ucode(free, len); + memcpy(addr, match, len); + match = addr; + + if (loader->load(match, false) == 0) { + ucode_data = match; + ucode_len = len; + early_ucode_data = ucode_data; + break; + } + unmap_ucode(free, len); + } + } + if (fileaddr != NULL && ucode_data == NULL) + log_err("no matching update found"); + return (ucode_len); +} + +/* + * 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); +} |
