From 448c348715714ee8319c8eeb2af24d38ba34ecdf Mon Sep 17 00:00:00 2001 From: patrick Date: Thu, 11 Jan 2018 22:31:09 +0000 Subject: [PATCH] Update the Intel microcode once the root filesystem has been mounted. This depends on the intel-firmware package that contains newer Intel microcode which will be installed automatically by fw_update(1). The update should happen much earlier since updating the microcode can add or remove not only feature flags but also whole features. For now only update feature flags that are relevant to Spectre. Initial diff from sf@ Tested by bluhm@ ok deraadt@ --- sys/arch/amd64/amd64/acpi_machdep.c | 3 +- sys/arch/amd64/amd64/cpu.c | 23 ++- sys/arch/amd64/amd64/ucode.c | 309 ++++++++++++++++++++++++++++ sys/arch/amd64/conf/files.amd64 | 3 +- sys/arch/amd64/include/cpufunc.h | 5 +- sys/arch/amd64/include/specialreg.h | 3 +- 6 files changed, 341 insertions(+), 5 deletions(-) create mode 100644 sys/arch/amd64/amd64/ucode.c diff --git a/sys/arch/amd64/amd64/acpi_machdep.c b/sys/arch/amd64/amd64/acpi_machdep.c index 5bd946454dc..268bab12c7c 100644 --- a/sys/arch/amd64/amd64/acpi_machdep.c +++ b/sys/arch/amd64/amd64/acpi_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: acpi_machdep.c,v 1.79 2017/10/14 04:44:43 jsg Exp $ */ +/* $OpenBSD: acpi_machdep.c,v 1.80 2018/01/11 22:31:09 patrick Exp $ */ /* * Copyright (c) 2005 Thorsten Lockert * @@ -429,6 +429,7 @@ acpi_resume_cpu(struct acpi_softc *sc) fpuinit(&cpu_info_primary); cpu_init(&cpu_info_primary); + cpu_ucode_apply(&cpu_info_primary); /* Re-initialise memory range handling on BSP */ if (mem_range_softc.mr_op != NULL) diff --git a/sys/arch/amd64/amd64/cpu.c b/sys/arch/amd64/amd64/cpu.c index 7afed65768f..6454ecd746d 100644 --- a/sys/arch/amd64/amd64/cpu.c +++ b/sys/arch/amd64/amd64/cpu.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cpu.c,v 1.108 2017/10/14 04:44:43 jsg Exp $ */ +/* $OpenBSD: cpu.c,v 1.109 2018/01/11 22:31:09 patrick Exp $ */ /* $NetBSD: cpu.c,v 1.1 2003/04/26 18:39:26 fvdl Exp $ */ /*- @@ -412,6 +412,7 @@ cpu_attach(struct device *parent, struct device *self, void *aux) #endif /* MTRR */ cpu_init(ci); cpu_init_mwait(sc); + config_mountroot(NULL, cpu_ucode_attachhook); break; case CPU_ROLE_BP: @@ -435,6 +436,7 @@ cpu_attach(struct device *parent, struct device *self, void *aux) ioapic_bsp_id = caa->cpu_apicid; #endif cpu_init_mwait(sc); + config_mountroot(NULL, cpu_ucode_attachhook); break; case CPU_ROLE_AP: @@ -732,6 +734,8 @@ cpu_hatch(void *v) lldt(0); cpu_init(ci); + cpu_ucode_apply(ci); + cpu_flags_update(ci); #if NPVBUS > 0 pvbus_init_cpu(); #endif @@ -931,3 +935,20 @@ cpu_activate(struct device *self, int act) return (0); } + +void +cpu_flags_update(struct cpu_info *ci) +{ + uint32_t dummy; + + /* XXX this update is much later than we want it to be */ + if (cpuid_level >= 0x07) { + CPUID_LEAF(0x7, 0, dummy, dummy, dummy, + ci->ci_feature_sefflags_edx); + } + if (!strcmp(cpu_vendor, "AuthenticAMD") && + ci->ci_pnfeatset >= 0x80000008) { + CPUID(0x80000008, dummy, ci->ci_feature_amdspec_ebx, + dummy, dummy); + } +} diff --git a/sys/arch/amd64/amd64/ucode.c b/sys/arch/amd64/amd64/ucode.c new file mode 100644 index 00000000000..a52e3b49995 --- /dev/null +++ b/sys/arch/amd64/amd64/ucode.c @@ -0,0 +1,309 @@ +/* $OpenBSD: ucode.c,v 1.1 2018/01/11 22:31:09 patrick Exp $ */ +/* + * Copyright (c) 2018 Stefan Fritsch + * Copyright (c) 2018 Patrick Wildt + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include +#include +#include + +/* #define UCODE_DEBUG */ +#ifdef UCODE_DEBUG +#define DPRINTF(x) do { if (cpu_ucode_debug > 0) printf x; } while (0) +#define DPRINTFN(n, x) do { if (cpu_ucode_debug >= (n)) printf x; } while (0) +static int cpu_ucode_debug = 1; +#else +#define DPRINTF(x) do { ; } while (0) +#define DPRINTFN(n, x) do { ; } while (0) +#endif + +struct intel_ucode_header { + uint32_t header_version; + uint32_t update_revision; + uint32_t date; + uint32_t processor_sig; + uint32_t checksum; + uint32_t loader_rev; + uint32_t processor_flags; + uint32_t data_size; + uint32_t total_size; + uint32_t reserved[3]; +}; + +struct intel_ucode_ext_sig_header { + uint32_t ext_sig_count; + uint32_t checksum; + uint32_t reserved[3]; +}; + +struct intel_ucode_ext_sig { + uint32_t processor_sig; + uint32_t processor_flags; + uint32_t checksum; +}; + +#define INTEL_UCODE_DEFAULT_DATA_SIZE 2000 + +/* Generic */ +char * cpu_ucode_data; +size_t cpu_ucode_size; + +void cpu_ucode_setup(struct cpu_info *); +void cpu_ucode_apply(struct cpu_info *); + +/* Intel */ +void cpu_ucode_intel_apply(struct cpu_info *); +struct intel_ucode_header * + cpu_ucode_intel_find(char *, size_t, uint32_t); +int cpu_ucode_intel_verify(struct intel_ucode_header *); +int cpu_ucode_intel_match(struct intel_ucode_header *, uint32_t, uint32_t, + uint32_t); +uint32_t cpu_ucode_intel_rev(void); + +struct intel_ucode_header *cpu_ucode_intel_applied; +struct mutex cpu_ucode_intel_mtx = MUTEX_INITIALIZER(IPL_HIGH); + +void +cpu_ucode_attachhook(struct device *dv) +{ + struct cpu_info *ci = curcpu(); + + cpu_ucode_setup(ci); + cpu_ucode_apply(ci); + cpu_flags_update(ci); +} + +void +cpu_ucode_setup(struct cpu_info *ci) +{ + char name[128]; + u_char *ucode; + size_t size; + + snprintf(name, sizeof(name), "intel/%02x-%02x-%02x", ci->ci_family, + ci->ci_model, (ci->ci_signature >> 0) & 0x0f); + + if (loadfirmware(name, &ucode, &size) != 0) { + DPRINTF(("%s: no microcode found: %s\n", __func__, name)); + return; + } + + cpu_ucode_data = ucode; + cpu_ucode_size = size; +} + +/* + * Called per-CPU. + */ +void +cpu_ucode_apply(struct cpu_info *ci) +{ + if (strcmp(cpu_vendor, "GenuineIntel") == 0) + cpu_ucode_intel_apply(ci); +} + +void +cpu_ucode_intel_apply(struct cpu_info *ci) +{ + struct intel_ucode_header *update; + uint32_t old_rev, new_rev; + paddr_t data; + + if (cpu_ucode_data == NULL || cpu_ucode_size == 0) + return; + + /* + * Grab a mutex, because we are not allowed to run updates + * simultaneously on HT siblings. + */ + mtx_enter(&cpu_ucode_intel_mtx); + + old_rev = cpu_ucode_intel_rev(); + update = cpu_ucode_intel_applied; + if (update == NULL) + update = cpu_ucode_intel_find(cpu_ucode_data, + cpu_ucode_size, old_rev); + if (update == NULL) + goto out; + if (update->update_revision == old_rev) + goto out; + + /* Apply microcode. */ + data = (paddr_t)update; + data += sizeof(struct intel_ucode_header); + wrmsr(MSR_BIOS_UPDT_TRIG, data); + + new_rev = cpu_ucode_intel_rev(); + if (new_rev != old_rev) { + DPRINTF(("%s: microcode updated cpu %d rev %#x->%#x (%x)\n", + __func__, ci->ci_cpuid, old_rev, new_rev, update->date)); + if (cpu_ucode_intel_applied == NULL) + cpu_ucode_intel_applied = update; + } else { + DPRINTF(("%s: microcode update failed cpu %d rev %#x->%#x != %#x\n", + __func__, ci->ci_cpuid, old_rev, update->update_revision, new_rev)); + } + +out: + mtx_leave(&cpu_ucode_intel_mtx); +} + +struct intel_ucode_header * +cpu_ucode_intel_find(char *data, size_t left, uint32_t current) +{ + uint64_t platform_id = (rdmsr(MSR_PLATFORM_ID) >> 50) & 0xff; + uint32_t sig, dummy1, dummy2, dummy3; + uint32_t mask = 1UL << platform_id; + struct intel_ucode_header *hdr; + uint32_t total_size; + int n = 0; + + CPUID(1, sig, dummy1, dummy2, dummy3); + + while (left > 0) { + hdr = (struct intel_ucode_header *)data; + if (left < sizeof(struct intel_ucode_header)) { + DPRINTF(("%s:%d: not enough data for header (%zd)\n", + __func__, n, left)); + break; + } + /* + * Older microcode has an empty length. In that case we + * have to use the default length of 2000. + */ + if (hdr->data_size) + total_size = hdr->total_size; + else + total_size = INTEL_UCODE_DEFAULT_DATA_SIZE + + sizeof(struct intel_ucode_header); + if (total_size > left) { + DPRINTF(("%s:%d: size %u out of range (%zd)\n", + __func__, n, total_size, left)); + break; + } + if (cpu_ucode_intel_verify(hdr)) { + DPRINTF(("%s:%d: broken data\n", __func__, n)); + break; + } + if (cpu_ucode_intel_match(hdr, sig, mask, current)) + return hdr; + n++; + left -= total_size; + data += total_size; + } + DPRINTF(("%s: no update found\n", __func__)); + return NULL; +} + +int +cpu_ucode_intel_verify(struct intel_ucode_header *hdr) +{ + uint32_t *data = (uint32_t *)hdr; + size_t total_size; + uint32_t sum; + int i; + + CTASSERT(sizeof(struct intel_ucode_header) == 48); + + if ((paddr_t)data % 16 != 0) { + DPRINTF(("%s: misaligned microcode update\n", __func__)); + return 1; + } + if (hdr->loader_rev != 1) { + DPRINTF(("%s: unsupported loader rev\n", __func__)); + return 1; + } + + if (hdr->data_size) + total_size = hdr->total_size; + else + total_size = INTEL_UCODE_DEFAULT_DATA_SIZE + + sizeof(struct intel_ucode_header); + if (total_size % 4 != 0) { + DPRINTF(("%s: inconsistent size\n", __func__)); + return 1; + } + + sum = 0; + for (i = 0; i < total_size / 4; i++) + sum += data[i]; + if (sum != 0) { + DPRINTF(("%s: wrong checksum (%#x)\n", __func__, sum)); + return 1; + } + + return 0; +} + +int +cpu_ucode_intel_match(struct intel_ucode_header *hdr, + uint32_t processor_sig, uint32_t processor_mask, + uint32_t ucode_revision) +{ + struct intel_ucode_ext_sig_header *ehdr; + struct intel_ucode_ext_sig *esig; + uint32_t data_size, total_size; + + data_size = hdr->data_size; + total_size = hdr->total_size; + + /* + * Older microcode has an empty length. In that case we + * have to use the default length of 2000. + */ + if (!data_size) { + data_size = INTEL_UCODE_DEFAULT_DATA_SIZE; + total_size = INTEL_UCODE_DEFAULT_DATA_SIZE + + sizeof(struct intel_ucode_header); + } + + if (ucode_revision > hdr->update_revision) + return 0; + if (hdr->processor_sig == processor_sig && + (hdr->processor_flags & processor_mask)) + return 1; + if (total_size <= sizeof(struct intel_ucode_header) + + data_size + sizeof(struct intel_ucode_ext_sig_header)) + return 0; + + ehdr = (void *)((char *)hdr + sizeof(struct intel_ucode_header) + + data_size); + esig = (void *)&ehdr[1]; + + for (unsigned i = 0; i < ehdr->ext_sig_count; i++) { + if (esig->processor_sig == processor_sig && + (esig->processor_flags & processor_mask)) + return 1; + } + + return 0; +} + +uint32_t +cpu_ucode_intel_rev(void) +{ + uint32_t eax, ebx, ecx, edx; + uint64_t rev; + + wrmsr(MSR_BIOS_SIGN, 0); + CPUID(1, eax, ebx, ecx, edx); + rev = rdmsr(MSR_BIOS_SIGN); + return rev >> 32; +} diff --git a/sys/arch/amd64/conf/files.amd64 b/sys/arch/amd64/conf/files.amd64 index 5e742fae046..68e39ffaf9c 100644 --- a/sys/arch/amd64/conf/files.amd64 +++ b/sys/arch/amd64/conf/files.amd64 @@ -1,4 +1,4 @@ -# $OpenBSD: files.amd64,v 1.92 2017/12/20 11:08:44 mpi Exp $ +# $OpenBSD: files.amd64,v 1.93 2018/01/11 22:31:09 patrick Exp $ maxpartitions 16 maxusers 2 16 128 @@ -16,6 +16,7 @@ file arch/amd64/amd64/locore.S file arch/amd64/amd64/aes_intel.S crypto file arch/amd64/amd64/aesni.c crypto file arch/amd64/amd64/amd64errata.c +file arch/amd64/amd64/ucode.c file arch/amd64/amd64/mem.c file arch/amd64/amd64/amd64_mem.c mtrr file arch/amd64/amd64/mtrr.c mtrr diff --git a/sys/arch/amd64/include/cpufunc.h b/sys/arch/amd64/include/cpufunc.h index 427a337a7dc..a86ac711a80 100644 --- a/sys/arch/amd64/include/cpufunc.h +++ b/sys/arch/amd64/include/cpufunc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: cpufunc.h,v 1.21 2017/10/17 20:38:49 deraadt Exp $ */ +/* $OpenBSD: cpufunc.h,v 1.22 2018/01/11 22:31:09 patrick Exp $ */ /* $NetBSD: cpufunc.h,v 1.3 2003/05/08 10:27:43 fvdl Exp $ */ /*- @@ -314,6 +314,9 @@ breakpoint(void) } void amd64_errata(struct cpu_info *); +void cpu_flags_update(struct cpu_info *); +void cpu_ucode_apply(struct cpu_info *); +void cpu_ucode_attachhook(struct device *); #endif /* _KERNEL */ diff --git a/sys/arch/amd64/include/specialreg.h b/sys/arch/amd64/include/specialreg.h index c1c0a47e46b..85a8ecdc2a7 100644 --- a/sys/arch/amd64/include/specialreg.h +++ b/sys/arch/amd64/include/specialreg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: specialreg.h,v 1.63 2018/01/07 01:08:20 mlarkin Exp $ */ +/* $OpenBSD: specialreg.h,v 1.64 2018/01/11 22:31:09 patrick Exp $ */ /* $NetBSD: specialreg.h,v 1.1 2003/04/26 18:39:48 fvdl Exp $ */ /* $NetBSD: x86/specialreg.h,v 1.2 2003/04/25 21:54:30 fvdl Exp $ */ @@ -318,6 +318,7 @@ #define MSR_CESR 0x011 /* P5 only (trap on P6) */ #define MSR_CTR0 0x012 /* P5 only (trap on P6) */ #define MSR_CTR1 0x013 /* P5 only (trap on P6) */ +#define MSR_PLATFORM_ID 0x017 /* Platform ID for microcode */ #define MSR_APICBASE 0x01b #define APICBASE_BSP 0x100 #define APICBASE_ENABLE_X2APIC 0x400 -- 2.20.1