diff options
Diffstat (limited to 'arch/x86/cpu/intel_common')
-rw-r--r-- | arch/x86/cpu/intel_common/Makefile | 3 | ||||
-rw-r--r-- | arch/x86/cpu/intel_common/car.S | 2 | ||||
-rw-r--r-- | arch/x86/cpu/intel_common/microcode.c | 168 |
3 files changed, 172 insertions, 1 deletions
diff --git a/arch/x86/cpu/intel_common/Makefile b/arch/x86/cpu/intel_common/Makefile index 5dd95739a09..ca4e171d20c 100644 --- a/arch/x86/cpu/intel_common/Makefile +++ b/arch/x86/cpu/intel_common/Makefile @@ -5,3 +5,6 @@ # obj-$(CONFIG_HAVE_MRC) += car.o +ifndef CONFIG_TARGET_EFI +obj-y += microcode.o +endif diff --git a/arch/x86/cpu/intel_common/car.S b/arch/x86/cpu/intel_common/car.S index 1defabf91f0..81ac976ddd5 100644 --- a/arch/x86/cpu/intel_common/car.S +++ b/arch/x86/cpu/intel_common/car.S @@ -12,12 +12,12 @@ */ #include <common.h> +#include <asm/microcode.h> #include <asm/msr-index.h> #include <asm/mtrr.h> #include <asm/post.h> #include <asm/processor.h> #include <asm/processor-flags.h> -#include <asm/arch/microcode.h> #define MTRR_PHYS_BASE_MSR(reg) (0x200 + 2 * (reg)) #define MTRR_PHYS_MASK_MSR(reg) (0x200 + 2 * (reg) + 1) diff --git a/arch/x86/cpu/intel_common/microcode.c b/arch/x86/cpu/intel_common/microcode.c new file mode 100644 index 00000000000..3054fabfde6 --- /dev/null +++ b/arch/x86/cpu/intel_common/microcode.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2014 Google, Inc + * Copyright (C) 2000 Ronald G. Minnich + * + * Microcode update for Intel PIII and later CPUs + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <errno.h> +#include <fdtdec.h> +#include <libfdt.h> +#include <asm/cpu.h> +#include <asm/microcode.h> +#include <asm/msr.h> +#include <asm/msr-index.h> +#include <asm/processor.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * struct microcode_update - standard microcode header from Intel + * + * We read this information out of the device tree and use it to determine + * whether the update is applicable or not. We also use the same structure + * to read information from the CPU. + */ +struct microcode_update { + uint header_version; + uint update_revision; + uint date_code; + uint processor_signature; + uint checksum; + uint loader_revision; + uint processor_flags; + const void *data; + int size; +}; + +static int microcode_decode_node(const void *blob, int node, + struct microcode_update *update) +{ + update->data = fdt_getprop(blob, node, "data", &update->size); + if (!update->data) + return -EINVAL; + update->data += UCODE_HEADER_LEN; + update->size -= UCODE_HEADER_LEN; + + update->header_version = fdtdec_get_int(blob, node, + "intel,header-version", 0); + update->update_revision = fdtdec_get_int(blob, node, + "intel,update-revision", 0); + update->date_code = fdtdec_get_int(blob, node, + "intel,date-code", 0); + update->processor_signature = fdtdec_get_int(blob, node, + "intel,processor-signature", 0); + update->checksum = fdtdec_get_int(blob, node, "intel,checksum", 0); + update->loader_revision = fdtdec_get_int(blob, node, + "intel,loader-revision", 0); + update->processor_flags = fdtdec_get_int(blob, node, + "intel,processor-flags", 0); + + return 0; +} + +static inline uint32_t microcode_read_rev(void) +{ + /* + * Some Intel CPUs can be very finicky about the CPUID sequence used. + * So this is implemented in assembly so that it works reliably. + */ + uint32_t low, high; + + asm volatile ( + "xorl %%eax, %%eax\n" + "xorl %%edx, %%edx\n" + "movl %2, %%ecx\n" + "wrmsr\n" + "movl $0x01, %%eax\n" + "cpuid\n" + "movl %2, %%ecx\n" + "rdmsr\n" + : /* outputs */ + "=a" (low), "=d" (high) + : /* inputs */ + "i" (MSR_IA32_UCODE_REV) + : /* clobbers */ + "ebx", "ecx" + ); + + return high; +} + +static void microcode_read_cpu(struct microcode_update *cpu) +{ + /* CPUID sets MSR 0x8B iff a microcode update has been loaded. */ + unsigned int x86_model, x86_family; + struct cpuid_result result; + uint32_t low, high; + + wrmsr(MSR_IA32_UCODE_REV, 0, 0); + result = cpuid(1); + rdmsr(MSR_IA32_UCODE_REV, low, cpu->update_revision); + x86_model = (result.eax >> 4) & 0x0f; + x86_family = (result.eax >> 8) & 0x0f; + cpu->processor_signature = result.eax; + + cpu->processor_flags = 0; + if ((x86_model >= 5) || (x86_family > 6)) { + rdmsr(0x17, low, high); + cpu->processor_flags = 1 << ((high >> 18) & 7); + } + debug("microcode: sig=%#x pf=%#x revision=%#x\n", + cpu->processor_signature, cpu->processor_flags, + cpu->update_revision); +} + +/* Get a microcode update from the device tree and apply it */ +int microcode_update_intel(void) +{ + struct microcode_update cpu, update; + const void *blob = gd->fdt_blob; + int skipped; + int count; + int node; + int ret; + int rev; + + microcode_read_cpu(&cpu); + node = 0; + count = 0; + skipped = 0; + do { + node = fdtdec_next_compatible(blob, node, + COMPAT_INTEL_MICROCODE); + if (node < 0) { + debug("%s: Found %d updates\n", __func__, count); + return count ? 0 : skipped ? -EEXIST : -ENOENT; + } + + ret = microcode_decode_node(blob, node, &update); + if (ret) { + debug("%s: Unable to decode update: %d\n", __func__, + ret); + return ret; + } + if (!(update.processor_signature == cpu.processor_signature && + (update.processor_flags & cpu.processor_flags))) { + debug("%s: Skipping non-matching update, sig=%x, pf=%x\n", + __func__, update.processor_signature, + update.processor_flags); + skipped++; + continue; + } + wrmsr(MSR_IA32_UCODE_WRITE, (ulong)update.data, 0); + rev = microcode_read_rev(); + debug("microcode: updated to revision 0x%x date=%04x-%02x-%02x\n", + rev, update.date_code & 0xffff, + (update.date_code >> 24) & 0xff, + (update.date_code >> 16) & 0xff); + if (update.update_revision != rev) { + printf("Microcode update failed\n"); + return -EFAULT; + } + count++; + } while (1); +} |