From 83b325f1b4d00035494bc18a7a131b2197b3c718 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Thu, 30 May 2013 14:09:16 -0500 Subject: x86, microcode, intel: Correct typo in printk User-visible so correct it. Signed-off-by: Borislav Petkov Link: http://lkml.kernel.org/r/1369940959-2077-2-git-send-email-jacob.shin@amd.com Signed-off-by: Jacob Shin Signed-off-by: H. Peter Anvin Cc: Fenghua Yu --- arch/x86/kernel/microcode_intel_early.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/kernel/microcode_intel_early.c b/arch/x86/kernel/microcode_intel_early.c index 2e9e12871c2b..a07f0a026e7b 100644 --- a/arch/x86/kernel/microcode_intel_early.c +++ b/arch/x86/kernel/microcode_intel_early.c @@ -529,7 +529,7 @@ int save_mc_for_early(u8 *mc) */ ret = save_microcode(&mc_saved_data, mc_saved_tmp, mc_saved_count); if (ret) { - pr_err("Can not save microcode patch.\n"); + pr_err("Cannot save microcode patch.\n"); goto out; } @@ -711,7 +711,7 @@ int __init save_microcode_in_initrd(void) microcode_pointer(mc_saved, mc_saved_in_initrd, initrd_start, count); ret = save_microcode(&mc_saved_data, mc_saved, count); if (ret) - pr_err("Can not save microcod patches from initrd"); + pr_err("Cannot save microcode patches from initrd.\n"); show_saved_mc(); -- cgit v1.2.3 From f2b3ee820a9f2368d7f8842ad7da062dfe86e199 Mon Sep 17 00:00:00 2001 From: Jacob Shin Date: Thu, 30 May 2013 14:09:17 -0500 Subject: x86, microcode: Vendor abstract out save_microcode_in_initrd() Currently save_microcode_in_initrd() is declared in vendor neutural microcode.h file, but defined in vendor specific microcode_intel_early.c file. Vendor abstract it out to microcode_core_early.c with a wrapper function. Signed-off-by: Jacob Shin Link: http://lkml.kernel.org/r/1369940959-2077-3-git-send-email-jacob.shin@amd.com Signed-off-by: H. Peter Anvin Cc: Fenghua Yu --- arch/x86/include/asm/microcode_intel.h | 2 ++ arch/x86/kernel/microcode_core_early.c | 10 ++++++++++ arch/x86/kernel/microcode_intel_early.c | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/arch/x86/include/asm/microcode_intel.h b/arch/x86/include/asm/microcode_intel.h index 5356f927d411..87a085333cbf 100644 --- a/arch/x86/include/asm/microcode_intel.h +++ b/arch/x86/include/asm/microcode_intel.h @@ -67,10 +67,12 @@ update_match_revision(struct microcode_header_intel *mc_header, int rev); extern void __init load_ucode_intel_bsp(void); extern void __cpuinit load_ucode_intel_ap(void); extern void show_ucode_info_early(void); +extern int __init save_microcode_in_initrd_intel(void); #else static inline __init void load_ucode_intel_bsp(void) {} static inline __cpuinit void load_ucode_intel_ap(void) {} static inline void show_ucode_info_early(void) {} +static inline int __init save_microcode_in_initrd_intel(void) { return -EINVAL; } #endif #if defined(CONFIG_MICROCODE_INTEL_EARLY) && defined(CONFIG_HOTPLUG_CPU) diff --git a/arch/x86/kernel/microcode_core_early.c b/arch/x86/kernel/microcode_core_early.c index 833d51d6ee06..0d19ac5016a7 100644 --- a/arch/x86/kernel/microcode_core_early.c +++ b/arch/x86/kernel/microcode_core_early.c @@ -98,3 +98,13 @@ void __cpuinit load_ucode_ap(void) if (vendor == X86_VENDOR_INTEL && x86 >= 6) load_ucode_intel_ap(); } + +int __init save_microcode_in_initrd(void) +{ + struct cpuinfo_x86 *c = &boot_cpu_data; + + if (c->x86_vendor == X86_VENDOR_INTEL && c->x86 >= 6) + return save_microcode_in_initrd_intel(); + + return 0; +} diff --git a/arch/x86/kernel/microcode_intel_early.c b/arch/x86/kernel/microcode_intel_early.c index a07f0a026e7b..dabef95506f3 100644 --- a/arch/x86/kernel/microcode_intel_early.c +++ b/arch/x86/kernel/microcode_intel_early.c @@ -699,7 +699,7 @@ static int __cpuinit apply_microcode_early(struct mc_saved_data *mc_saved_data, * This function converts microcode patch offsets previously stored in * mc_saved_in_initrd to pointers and stores the pointers in mc_saved_data. */ -int __init save_microcode_in_initrd(void) +int __init save_microcode_in_initrd_intel(void) { unsigned int count = mc_saved_data.mc_saved_count; struct microcode_intel *mc_saved[MAX_UCODE_COUNT]; -- cgit v1.2.3 From a76096a6571d5389376753c2e18b30a9791fa072 Mon Sep 17 00:00:00 2001 From: Jacob Shin Date: Thu, 30 May 2013 14:09:18 -0500 Subject: x86, microcode, amd: Refactor functions to prepare for early loading In preparation work for early loading, refactor some common functions that will be shared, and move some struct defines to a common header file. Signed-off-by: Jacob Shin Link: http://lkml.kernel.org/r/1369940959-2077-4-git-send-email-jacob.shin@amd.com Signed-off-by: H. Peter Anvin Cc: Fenghua Yu --- arch/x86/include/asm/microcode_amd.h | 64 ++++++++++++++++++++ arch/x86/kernel/microcode_amd.c | 111 +++++++++++++---------------------- 2 files changed, 106 insertions(+), 69 deletions(-) create mode 100644 arch/x86/include/asm/microcode_amd.h diff --git a/arch/x86/include/asm/microcode_amd.h b/arch/x86/include/asm/microcode_amd.h new file mode 100644 index 000000000000..a57d9b75f30b --- /dev/null +++ b/arch/x86/include/asm/microcode_amd.h @@ -0,0 +1,64 @@ +#ifndef _ASM_X86_MICROCODE_AMD_H +#define _ASM_X86_MICROCODE_AMD_H + +#include + +#define UCODE_MAGIC 0x00414d44 +#define UCODE_EQUIV_CPU_TABLE_TYPE 0x00000000 +#define UCODE_UCODE_TYPE 0x00000001 + +#define SECTION_HDR_SIZE 8 +#define CONTAINER_HDR_SZ 12 + +struct equiv_cpu_entry { + u32 installed_cpu; + u32 fixed_errata_mask; + u32 fixed_errata_compare; + u16 equiv_cpu; + u16 res; +} __attribute__((packed)); + +struct microcode_header_amd { + u32 data_code; + u32 patch_id; + u16 mc_patch_data_id; + u8 mc_patch_data_len; + u8 init_flag; + u32 mc_patch_data_checksum; + u32 nb_dev_id; + u32 sb_dev_id; + u16 processor_rev_id; + u8 nb_rev_id; + u8 sb_rev_id; + u8 bios_api_rev; + u8 reserved1[3]; + u32 match_reg[8]; +} __attribute__((packed)); + +struct microcode_amd { + struct microcode_header_amd hdr; + unsigned int mpb[0]; +}; + +static inline u16 find_equiv_id(struct equiv_cpu_entry *equiv_cpu_table, + unsigned int sig) +{ + int i = 0; + + if (!equiv_cpu_table) + return 0; + + while (equiv_cpu_table[i].installed_cpu != 0) { + if (sig == equiv_cpu_table[i].installed_cpu) + return equiv_cpu_table[i].equiv_cpu; + + i++; + } + return 0; +} + +extern int __apply_microcode_amd(struct microcode_amd *mc_amd); +extern int apply_microcode_amd(int cpu); +extern enum ucode_state load_microcode_amd(int cpu, const u8 *data, size_t size); + +#endif /* _ASM_X86_MICROCODE_AMD_H */ diff --git a/arch/x86/kernel/microcode_amd.c b/arch/x86/kernel/microcode_amd.c index efdec7cd8e01..8f11f8f90e98 100644 --- a/arch/x86/kernel/microcode_amd.c +++ b/arch/x86/kernel/microcode_amd.c @@ -31,48 +31,12 @@ #include #include #include +#include MODULE_DESCRIPTION("AMD Microcode Update Driver"); MODULE_AUTHOR("Peter Oruba"); MODULE_LICENSE("GPL v2"); -#define UCODE_MAGIC 0x00414d44 -#define UCODE_EQUIV_CPU_TABLE_TYPE 0x00000000 -#define UCODE_UCODE_TYPE 0x00000001 - -struct equiv_cpu_entry { - u32 installed_cpu; - u32 fixed_errata_mask; - u32 fixed_errata_compare; - u16 equiv_cpu; - u16 res; -} __attribute__((packed)); - -struct microcode_header_amd { - u32 data_code; - u32 patch_id; - u16 mc_patch_data_id; - u8 mc_patch_data_len; - u8 init_flag; - u32 mc_patch_data_checksum; - u32 nb_dev_id; - u32 sb_dev_id; - u16 processor_rev_id; - u8 nb_rev_id; - u8 sb_rev_id; - u8 bios_api_rev; - u8 reserved1[3]; - u32 match_reg[8]; -} __attribute__((packed)); - -struct microcode_amd { - struct microcode_header_amd hdr; - unsigned int mpb[0]; -}; - -#define SECTION_HDR_SIZE 8 -#define CONTAINER_HDR_SZ 12 - static struct equiv_cpu_entry *equiv_cpu_table; struct ucode_patch { @@ -84,21 +48,10 @@ struct ucode_patch { static LIST_HEAD(pcache); -static u16 find_equiv_id(unsigned int cpu) +static u16 __find_equiv_id(unsigned int cpu) { struct ucode_cpu_info *uci = ucode_cpu_info + cpu; - int i = 0; - - if (!equiv_cpu_table) - return 0; - - while (equiv_cpu_table[i].installed_cpu != 0) { - if (uci->cpu_sig.sig == equiv_cpu_table[i].installed_cpu) - return equiv_cpu_table[i].equiv_cpu; - - i++; - } - return 0; + return find_equiv_id(equiv_cpu_table, uci->cpu_sig.sig); } static u32 find_cpu_family_by_equiv_cpu(u16 equiv_cpu) @@ -163,7 +116,7 @@ static struct ucode_patch *find_patch(unsigned int cpu) { u16 equiv_id; - equiv_id = find_equiv_id(cpu); + equiv_id = __find_equiv_id(cpu); if (!equiv_id) return NULL; @@ -215,7 +168,21 @@ static unsigned int verify_patch_size(int cpu, u32 patch_size, return patch_size; } -static int apply_microcode_amd(int cpu) +int __apply_microcode_amd(struct microcode_amd *mc_amd) +{ + u32 rev, dummy; + + wrmsrl(MSR_AMD64_PATCH_LOADER, (u64)(long)&mc_amd->hdr.data_code); + + /* verify patch application was successful */ + rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy); + if (rev != mc_amd->hdr.patch_id) + return -1; + + return 0; +} + +int apply_microcode_amd(int cpu) { struct cpuinfo_x86 *c = &cpu_data(cpu); struct microcode_amd *mc_amd; @@ -242,19 +209,15 @@ static int apply_microcode_amd(int cpu) return 0; } - wrmsrl(MSR_AMD64_PATCH_LOADER, (u64)(long)&mc_amd->hdr.data_code); - - /* verify patch application was successful */ - rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy); - if (rev != mc_amd->hdr.patch_id) { + if (__apply_microcode_amd(mc_amd)) pr_err("CPU%d: update failed for patch_level=0x%08x\n", - cpu, mc_amd->hdr.patch_id); - return -1; - } + cpu, mc_amd->hdr.patch_id); + else + pr_info("CPU%d: new patch_level=0x%08x\n", cpu, + mc_amd->hdr.patch_id); - pr_info("CPU%d: new patch_level=0x%08x\n", cpu, rev); - uci->cpu_sig.rev = rev; - c->microcode = rev; + uci->cpu_sig.rev = mc_amd->hdr.patch_id; + c->microcode = mc_amd->hdr.patch_id; return 0; } @@ -364,7 +327,7 @@ static int verify_and_add_patch(unsigned int cpu, u8 *fw, unsigned int leftover) return crnt_size; } -static enum ucode_state load_microcode_amd(int cpu, const u8 *data, size_t size) +static enum ucode_state __load_microcode_amd(int cpu, const u8 *data, size_t size) { enum ucode_state ret = UCODE_ERROR; unsigned int leftover; @@ -398,6 +361,21 @@ static enum ucode_state load_microcode_amd(int cpu, const u8 *data, size_t size) return UCODE_OK; } +enum ucode_state load_microcode_amd(int cpu, const u8 *data, size_t size) +{ + enum ucode_state ret; + + /* free old equiv table */ + free_equiv_cpu_table(); + + ret = __load_microcode_amd(cpu, data, size); + + if (ret != UCODE_OK) + cleanup(); + + return ret; +} + /* * AMD microcode firmware naming convention, up to family 15h they are in * the legacy file: @@ -440,12 +418,7 @@ static enum ucode_state request_microcode_amd(int cpu, struct device *device, goto fw_release; } - /* free old equiv table */ - free_equiv_cpu_table(); - ret = load_microcode_amd(cpu, fw->data, fw->size); - if (ret != UCODE_OK) - cleanup(); fw_release: release_firmware(fw); -- cgit v1.2.3 From 757885e94a22bcc82beb9b1445c95218cb20ceab Mon Sep 17 00:00:00 2001 From: Jacob Shin Date: Thu, 30 May 2013 14:09:19 -0500 Subject: x86, microcode, amd: Early microcode patch loading support for AMD Add early microcode patch loading support for AMD. Signed-off-by: Jacob Shin Link: http://lkml.kernel.org/r/1369940959-2077-5-git-send-email-jacob.shin@amd.com Signed-off-by: H. Peter Anvin Cc: Fenghua Yu --- Documentation/x86/early-microcode.txt | 11 +- arch/x86/Kconfig | 14 ++- arch/x86/include/asm/microcode_amd.h | 14 +++ arch/x86/kernel/Makefile | 1 + arch/x86/kernel/microcode_amd.c | 22 ++++ arch/x86/kernel/microcode_amd_early.c | 222 +++++++++++++++++++++++++++++++++ arch/x86/kernel/microcode_core_early.c | 43 ++++++- 7 files changed, 310 insertions(+), 17 deletions(-) create mode 100644 arch/x86/kernel/microcode_amd_early.c diff --git a/Documentation/x86/early-microcode.txt b/Documentation/x86/early-microcode.txt index 4aaf0dfb0cb8..d62bea6796da 100644 --- a/Documentation/x86/early-microcode.txt +++ b/Documentation/x86/early-microcode.txt @@ -11,7 +11,8 @@ file and loaded to CPUs during boot time. The format of the combined initrd image is microcode in cpio format followed by the initrd image (maybe compressed). Kernel parses the combined initrd image during boot time. The microcode file in cpio name space is: -kernel/x86/microcode/GenuineIntel.bin +on Intel: kernel/x86/microcode/GenuineIntel.bin +on AMD : kernel/x86/microcode/AuthenticAMD.bin During BSP boot (before SMP starts), if the kernel finds the microcode file in the initrd file, it parses the microcode and saves matching microcode in memory. @@ -34,10 +35,8 @@ original initrd image /boot/initrd-3.5.0.img. mkdir initrd cd initrd -mkdir kernel -mkdir kernel/x86 -mkdir kernel/x86/microcode -cp ../microcode.bin kernel/x86/microcode/GenuineIntel.bin -find .|cpio -oc >../ucode.cpio +mkdir -p kernel/x86/microcode +cp ../microcode.bin kernel/x86/microcode/GenuineIntel.bin (or AuthenticAMD.bin) +find . | cpio -o -H newc >../ucode.cpio cd .. cat ucode.cpio /boot/initrd-3.5.0.img >/boot/initrd-3.5.0.ucode.img diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 685692c94f05..28dba52af514 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -1058,8 +1058,16 @@ config MICROCODE_INTEL_LIB depends on MICROCODE_INTEL config MICROCODE_INTEL_EARLY + def_bool n + +config MICROCODE_AMD_EARLY + def_bool n + +config MICROCODE_EARLY bool "Early load microcode" - depends on MICROCODE_INTEL && BLK_DEV_INITRD + depends on (MICROCODE_INTEL || MICROCODE_AMD) && BLK_DEV_INITRD + select MICROCODE_INTEL_EARLY if MICROCODE_INTEL + select MICROCODE_AMD_EARLY if MICROCODE_AMD default y help This option provides functionality to read additional microcode data @@ -1067,10 +1075,6 @@ config MICROCODE_INTEL_EARLY microcode to CPU's as early as possible. No functional change if no microcode data is glued to the initrd, therefore it's safe to say Y. -config MICROCODE_EARLY - def_bool y - depends on MICROCODE_INTEL_EARLY - config X86_MSR tristate "/dev/cpu/*/msr - Model-specific register support" ---help--- diff --git a/arch/x86/include/asm/microcode_amd.h b/arch/x86/include/asm/microcode_amd.h index a57d9b75f30b..24254aaa857f 100644 --- a/arch/x86/include/asm/microcode_amd.h +++ b/arch/x86/include/asm/microcode_amd.h @@ -61,4 +61,18 @@ extern int __apply_microcode_amd(struct microcode_amd *mc_amd); extern int apply_microcode_amd(int cpu); extern enum ucode_state load_microcode_amd(int cpu, const u8 *data, size_t size); +#ifdef CONFIG_MICROCODE_AMD_EARLY +#ifdef CONFIG_X86_32 +#define MPB_MAX_SIZE PAGE_SIZE +extern u8 __cpuinitdata amd_bsp_mpb[MPB_MAX_SIZE]; +#endif +extern void __init load_ucode_amd_bsp(void); +extern void __cpuinit load_ucode_amd_ap(void); +extern int __init save_microcode_in_initrd_amd(void); +#else +static inline void __init load_ucode_amd_bsp(void) {} +static inline void __cpuinit load_ucode_amd_ap(void) {} +static inline int __init save_microcode_in_initrd_amd(void) { return -EINVAL; } +#endif + #endif /* _ASM_X86_MICROCODE_AMD_H */ diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 7bd3bd310106..6c3fceaf8ff3 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -93,6 +93,7 @@ obj-$(CONFIG_MICROCODE_INTEL_LIB) += microcode_intel_lib.o microcode-y := microcode_core.o microcode-$(CONFIG_MICROCODE_INTEL) += microcode_intel.o microcode-$(CONFIG_MICROCODE_AMD) += microcode_amd.o +obj-$(CONFIG_MICROCODE_AMD_EARLY) += microcode_amd_early.o obj-$(CONFIG_MICROCODE) += microcode.o obj-$(CONFIG_X86_CHECK_BIOS_CORRUPTION) += check.o diff --git a/arch/x86/kernel/microcode_amd.c b/arch/x86/kernel/microcode_amd.c index 8f11f8f90e98..47ebb1dbfbcb 100644 --- a/arch/x86/kernel/microcode_amd.c +++ b/arch/x86/kernel/microcode_amd.c @@ -126,9 +126,20 @@ static struct ucode_patch *find_patch(unsigned int cpu) static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig) { struct cpuinfo_x86 *c = &cpu_data(cpu); + struct ucode_cpu_info *uci = ucode_cpu_info + cpu; + struct ucode_patch *p; csig->sig = cpuid_eax(0x00000001); csig->rev = c->microcode; + + /* + * a patch could have been loaded early, set uci->mc so that + * mc_bp_resume() can call apply_microcode() + */ + p = find_patch(cpu); + if (p && (p->patch_id == csig->rev)) + uci->mc = p->data; + pr_info("CPU%d: patch_level=0x%08x\n", cpu, csig->rev); return 0; @@ -373,6 +384,17 @@ enum ucode_state load_microcode_amd(int cpu, const u8 *data, size_t size) if (ret != UCODE_OK) cleanup(); +#if defined(CONFIG_MICROCODE_AMD_EARLY) && defined(CONFIG_X86_32) + /* save BSP's matching patch for early load */ + if (cpu_data(cpu).cpu_index == boot_cpu_data.cpu_index) { + struct ucode_patch *p = find_patch(cpu); + if (p) { + memset(amd_bsp_mpb, 0, MPB_MAX_SIZE); + memcpy(amd_bsp_mpb, p->data, min_t(u32, ksize(p->data), + MPB_MAX_SIZE)); + } + } +#endif return ret; } diff --git a/arch/x86/kernel/microcode_amd_early.c b/arch/x86/kernel/microcode_amd_early.c new file mode 100644 index 000000000000..7e54d97c714a --- /dev/null +++ b/arch/x86/kernel/microcode_amd_early.c @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2013 Advanced Micro Devices, Inc. + * + * Author: Jacob Shin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include + +#include +#include +#include + +static bool ucode_loaded; +static u32 ucode_new_rev; + +/* + * Microcode patch container file is prepended to the initrd in cpio format. + * See Documentation/x86/early-microcode.txt + */ +static __initdata char ucode_path[] = "kernel/x86/microcode/AuthenticAMD.bin"; + +static struct cpio_data __init find_ucode_in_initrd(void) +{ + long offset = 0; + struct cpio_data cd; + +#ifdef CONFIG_X86_32 + /* + * On 32-bit, early load occurs before paging is turned on so we need + * to use physical addresses. + */ + if (!(read_cr0() & X86_CR0_PG)) { + struct boot_params *p; + p = (struct boot_params *)__pa_nodebug(&boot_params); + cd = find_cpio_data((char *)__pa_nodebug(ucode_path), + (void *)p->hdr.ramdisk_image, p->hdr.ramdisk_size, + &offset); + } else +#endif + cd = find_cpio_data(ucode_path, + (void *)(boot_params.hdr.ramdisk_image + PAGE_OFFSET), + boot_params.hdr.ramdisk_size, &offset); + + if (*(u32 *)cd.data != UCODE_MAGIC) { + cd.data = NULL; + cd.size = 0; + } + + return cd; +} + +/* + * Early load occurs before we can vmalloc(). So we look for the microcode + * patch container file in initrd, traverse equivalent cpu table, look for a + * matching microcode patch, and update, all in initrd memory in place. + * When vmalloc() is available for use later -- on 64-bit during first AP load, + * and on 32-bit during save_microcode_in_initrd_amd() -- we can call + * load_microcode_amd() to save equivalent cpu table and microcode patches in + * kernel heap memory. + */ +static void __init apply_ucode_in_initrd(void) +{ + struct cpio_data cd; + struct equiv_cpu_entry *eq; + u32 *header; + u8 *data; + u16 eq_id; + int offset, left; + u32 rev, dummy; + u32 *new_rev; + +#ifdef CONFIG_X86_32 + new_rev = (u32 *)__pa_nodebug(&ucode_new_rev); +#else + new_rev = &ucode_new_rev; +#endif + cd = find_ucode_in_initrd(); + if (!cd.data) + return; + + data = cd.data; + left = cd.size; + header = (u32 *)data; + + /* find equiv cpu table */ + + if (header[1] != UCODE_EQUIV_CPU_TABLE_TYPE || /* type */ + header[2] == 0) /* size */ + return; + + eq = (struct equiv_cpu_entry *)(data + CONTAINER_HDR_SZ); + offset = header[2] + CONTAINER_HDR_SZ; + data += offset; + left -= offset; + + eq_id = find_equiv_id(eq, cpuid_eax(0x00000001)); + if (!eq_id) + return; + + /* find ucode and update if needed */ + + rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy); + + while (left > 0) { + struct microcode_amd *mc; + + header = (u32 *)data; + if (header[0] != UCODE_UCODE_TYPE || /* type */ + header[1] == 0) /* size */ + break; + + mc = (struct microcode_amd *)(data + SECTION_HDR_SIZE); + if (eq_id == mc->hdr.processor_rev_id && rev < mc->hdr.patch_id) + if (__apply_microcode_amd(mc) == 0) { + if (!(*new_rev)) + *new_rev = mc->hdr.patch_id; + break; + } + + offset = header[1] + SECTION_HDR_SIZE; + data += offset; + left -= offset; + } +} + +void __init load_ucode_amd_bsp(void) +{ + apply_ucode_in_initrd(); +} + +#ifdef CONFIG_X86_32 +u8 __cpuinitdata amd_bsp_mpb[MPB_MAX_SIZE]; + +/* + * On 32-bit, since AP's early load occurs before paging is turned on, we + * cannot traverse cpu_equiv_table and pcache in kernel heap memory. So during + * cold boot, AP will apply_ucode_in_initrd() just like the BSP. During + * save_microcode_in_initrd_amd() BSP's patch is copied to amd_bsp_mpb, which + * is used upon resume from suspend. + */ +void __cpuinit load_ucode_amd_ap(void) +{ + struct microcode_amd *mc; + + mc = (struct microcode_amd *)__pa_nodebug(amd_bsp_mpb); + if (mc->hdr.patch_id && mc->hdr.processor_rev_id) + __apply_microcode_amd(mc); + else + apply_ucode_in_initrd(); +} + +static void __init collect_cpu_sig_on_bsp(void *arg) +{ + unsigned int cpu = smp_processor_id(); + struct ucode_cpu_info *uci = ucode_cpu_info + cpu; + uci->cpu_sig.sig = cpuid_eax(0x00000001); +} +#else +static void __cpuinit collect_cpu_info_amd_early(struct cpuinfo_x86 *c, + struct ucode_cpu_info *uci) +{ + u32 rev, eax; + + rdmsr(MSR_AMD64_PATCH_LEVEL, rev, eax); + eax = cpuid_eax(0x00000001); + + uci->cpu_sig.sig = eax; + uci->cpu_sig.rev = rev; + c->microcode = rev; + c->x86 = ((eax >> 8) & 0xf) + ((eax >> 20) & 0xff); +} + +void __cpuinit load_ucode_amd_ap(void) +{ + unsigned int cpu = smp_processor_id(); + + collect_cpu_info_amd_early(&cpu_data(cpu), ucode_cpu_info + cpu); + + if (cpu && !ucode_loaded) { + struct cpio_data cd = find_ucode_in_initrd(); + if (load_microcode_amd(0, cd.data, cd.size) != UCODE_OK) + return; + ucode_loaded = true; + } + + apply_microcode_amd(cpu); +} +#endif + +int __init save_microcode_in_initrd_amd(void) +{ + enum ucode_state ret; + struct cpio_data cd; +#ifdef CONFIG_X86_32 + unsigned int bsp = boot_cpu_data.cpu_index; + struct ucode_cpu_info *uci = ucode_cpu_info + bsp; + + if (!uci->cpu_sig.sig) + smp_call_function_single(bsp, collect_cpu_sig_on_bsp, NULL, 1); +#endif + if (ucode_new_rev) + pr_info("microcode: updated early to new patch_level=0x%08x\n", + ucode_new_rev); + + if (ucode_loaded) + return 0; + + cd = find_ucode_in_initrd(); + if (!cd.data) + return -EINVAL; + + ret = load_microcode_amd(0, cd.data, cd.size); + if (ret != UCODE_OK) + return -EINVAL; + + ucode_loaded = true; + return 0; +} diff --git a/arch/x86/kernel/microcode_core_early.c b/arch/x86/kernel/microcode_core_early.c index 0d19ac5016a7..86119f63db0c 100644 --- a/arch/x86/kernel/microcode_core_early.c +++ b/arch/x86/kernel/microcode_core_early.c @@ -18,6 +18,7 @@ */ #include #include +#include #include #define QCHAR(a, b, c, d) ((a) + ((b) << 8) + ((c) << 16) + ((d) << 24)) @@ -81,8 +82,18 @@ void __init load_ucode_bsp(void) vendor = x86_vendor(); x86 = x86_family(); - if (vendor == X86_VENDOR_INTEL && x86 >= 6) - load_ucode_intel_bsp(); + switch (vendor) { + case X86_VENDOR_INTEL: + if (x86 >= 6) + load_ucode_intel_bsp(); + break; + case X86_VENDOR_AMD: + if (x86 >= 0x10) + load_ucode_amd_bsp(); + break; + default: + break; + } } void __cpuinit load_ucode_ap(void) @@ -95,16 +106,36 @@ void __cpuinit load_ucode_ap(void) vendor = x86_vendor(); x86 = x86_family(); - if (vendor == X86_VENDOR_INTEL && x86 >= 6) - load_ucode_intel_ap(); + switch (vendor) { + case X86_VENDOR_INTEL: + if (x86 >= 6) + load_ucode_intel_ap(); + break; + case X86_VENDOR_AMD: + if (x86 >= 0x10) + load_ucode_amd_ap(); + break; + default: + break; + } } int __init save_microcode_in_initrd(void) { struct cpuinfo_x86 *c = &boot_cpu_data; - if (c->x86_vendor == X86_VENDOR_INTEL && c->x86 >= 6) - return save_microcode_in_initrd_intel(); + switch (c->x86_vendor) { + case X86_VENDOR_INTEL: + if (c->x86 >= 6) + save_microcode_in_initrd_intel(); + break; + case X86_VENDOR_AMD: + if (c->x86 >= 0x10) + save_microcode_in_initrd_amd(); + break; + default: + break; + } return 0; } -- cgit v1.2.3 From 6b3389ac21b5e557b957f1497d0ff22bf733e8c3 Mon Sep 17 00:00:00 2001 From: Jacob Shin Date: Fri, 31 May 2013 01:53:24 -0500 Subject: x86, microcode, amd: Fix warnings and errors on with CONFIG_MICROCODE=m Fix section mismatch warnings on microcode_amd_early. Compile error occurs when CONFIG_MICROCODE=m, change so that early loading depends on microcode_core. Reported-by: Yinghai Lu Reported-by: Borislav Petkov Signed-off-by: Jacob Shin Link: http://lkml.kernel.org/r/20130531150241.GA12006@jshin-Toonie Signed-off-by: H. Peter Anvin --- arch/x86/Kconfig | 2 +- arch/x86/include/asm/microcode_amd.h | 2 +- arch/x86/kernel/microcode_amd_early.c | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 28dba52af514..270553266297 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -1065,7 +1065,7 @@ config MICROCODE_AMD_EARLY config MICROCODE_EARLY bool "Early load microcode" - depends on (MICROCODE_INTEL || MICROCODE_AMD) && BLK_DEV_INITRD + depends on MICROCODE=y && BLK_DEV_INITRD select MICROCODE_INTEL_EARLY if MICROCODE_INTEL select MICROCODE_AMD_EARLY if MICROCODE_AMD default y diff --git a/arch/x86/include/asm/microcode_amd.h b/arch/x86/include/asm/microcode_amd.h index 24254aaa857f..c6b043f40271 100644 --- a/arch/x86/include/asm/microcode_amd.h +++ b/arch/x86/include/asm/microcode_amd.h @@ -64,7 +64,7 @@ extern enum ucode_state load_microcode_amd(int cpu, const u8 *data, size_t size) #ifdef CONFIG_MICROCODE_AMD_EARLY #ifdef CONFIG_X86_32 #define MPB_MAX_SIZE PAGE_SIZE -extern u8 __cpuinitdata amd_bsp_mpb[MPB_MAX_SIZE]; +extern u8 amd_bsp_mpb[MPB_MAX_SIZE]; #endif extern void __init load_ucode_amd_bsp(void); extern void __cpuinit load_ucode_amd_ap(void); diff --git a/arch/x86/kernel/microcode_amd_early.c b/arch/x86/kernel/microcode_amd_early.c index 7e54d97c714a..9618805ce6d7 100644 --- a/arch/x86/kernel/microcode_amd_early.c +++ b/arch/x86/kernel/microcode_amd_early.c @@ -21,9 +21,9 @@ static u32 ucode_new_rev; * Microcode patch container file is prepended to the initrd in cpio format. * See Documentation/x86/early-microcode.txt */ -static __initdata char ucode_path[] = "kernel/x86/microcode/AuthenticAMD.bin"; +static __cpuinitdata char ucode_path[] = "kernel/x86/microcode/AuthenticAMD.bin"; -static struct cpio_data __init find_ucode_in_initrd(void) +static struct cpio_data __cpuinit find_ucode_in_initrd(void) { long offset = 0; struct cpio_data cd; @@ -62,7 +62,7 @@ static struct cpio_data __init find_ucode_in_initrd(void) * load_microcode_amd() to save equivalent cpu table and microcode patches in * kernel heap memory. */ -static void __init apply_ucode_in_initrd(void) +static void __cpuinit apply_ucode_in_initrd(void) { struct cpio_data cd; struct equiv_cpu_entry *eq; @@ -133,7 +133,7 @@ void __init load_ucode_amd_bsp(void) } #ifdef CONFIG_X86_32 -u8 __cpuinitdata amd_bsp_mpb[MPB_MAX_SIZE]; +u8 amd_bsp_mpb[MPB_MAX_SIZE]; /* * On 32-bit, since AP's early load occurs before paging is turned on, we -- cgit v1.2.3 From 275bbe2e299f1820ec8faa443d689469a9e6ecc5 Mon Sep 17 00:00:00 2001 From: Jacob Shin Date: Wed, 5 Jun 2013 15:13:55 -0500 Subject: x86, microcode, amd: Make find_ucode_in_initrd() __init Change find_ucode_in_initrd() to __init and only let BSP call it during cold boot. This is the right thing to do because only BSP will see initrd loaded by the boot loader. APs will offset into initrd_start to find the microcode patch binary. Reported-by: Yinghai Lu Signed-off-by: Jacob Shin Link: http://lkml.kernel.org/r/1370463236-2115-2-git-send-email-jacob.shin@amd.com Cc: Fenghua Yu Signed-off-by: H. Peter Anvin --- arch/x86/kernel/microcode_amd_early.c | 98 ++++++++++++++++++++++++----------- 1 file changed, 68 insertions(+), 30 deletions(-) diff --git a/arch/x86/kernel/microcode_amd_early.c b/arch/x86/kernel/microcode_amd_early.c index 9618805ce6d7..7db1e16ca582 100644 --- a/arch/x86/kernel/microcode_amd_early.c +++ b/arch/x86/kernel/microcode_amd_early.c @@ -9,6 +9,7 @@ */ #include +#include #include #include @@ -16,40 +17,59 @@ static bool ucode_loaded; static u32 ucode_new_rev; +static unsigned long ucode_offset; +static size_t ucode_size; /* * Microcode patch container file is prepended to the initrd in cpio format. * See Documentation/x86/early-microcode.txt */ -static __cpuinitdata char ucode_path[] = "kernel/x86/microcode/AuthenticAMD.bin"; +static __initdata char ucode_path[] = "kernel/x86/microcode/AuthenticAMD.bin"; -static struct cpio_data __cpuinit find_ucode_in_initrd(void) +static struct cpio_data __init find_ucode_in_initrd(void) { long offset = 0; + char *path; + void *start; + size_t size; + unsigned long *uoffset; + size_t *usize; struct cpio_data cd; #ifdef CONFIG_X86_32 + struct boot_params *p; + /* * On 32-bit, early load occurs before paging is turned on so we need * to use physical addresses. */ - if (!(read_cr0() & X86_CR0_PG)) { - struct boot_params *p; - p = (struct boot_params *)__pa_nodebug(&boot_params); - cd = find_cpio_data((char *)__pa_nodebug(ucode_path), - (void *)p->hdr.ramdisk_image, p->hdr.ramdisk_size, - &offset); - } else + p = (struct boot_params *)__pa_nodebug(&boot_params); + path = (char *)__pa_nodebug(ucode_path); + start = (void *)p->hdr.ramdisk_image; + size = p->hdr.ramdisk_size; + uoffset = (unsigned long *)__pa_nodebug(&ucode_offset); + usize = (size_t *)__pa_nodebug(&ucode_size); +#else + path = ucode_path; + start = (void *)(boot_params.hdr.ramdisk_image + PAGE_OFFSET); + size = boot_params.hdr.ramdisk_size; + uoffset = &ucode_offset; + usize = &ucode_size; #endif - cd = find_cpio_data(ucode_path, - (void *)(boot_params.hdr.ramdisk_image + PAGE_OFFSET), - boot_params.hdr.ramdisk_size, &offset); + + cd = find_cpio_data(path, start, size, &offset); + if (!cd.data) + return cd; if (*(u32 *)cd.data != UCODE_MAGIC) { cd.data = NULL; cd.size = 0; + return cd; } + *uoffset = (u8 *)cd.data - (u8 *)start; + *usize = cd.size; + return cd; } @@ -62,9 +82,8 @@ static struct cpio_data __cpuinit find_ucode_in_initrd(void) * load_microcode_amd() to save equivalent cpu table and microcode patches in * kernel heap memory. */ -static void __cpuinit apply_ucode_in_initrd(void) +static void __cpuinit apply_ucode_in_initrd(void *ucode, size_t size) { - struct cpio_data cd; struct equiv_cpu_entry *eq; u32 *header; u8 *data; @@ -78,12 +97,9 @@ static void __cpuinit apply_ucode_in_initrd(void) #else new_rev = &ucode_new_rev; #endif - cd = find_ucode_in_initrd(); - if (!cd.data) - return; - data = cd.data; - left = cd.size; + data = ucode; + left = size; header = (u32 *)data; /* find equiv cpu table */ @@ -129,7 +145,11 @@ static void __cpuinit apply_ucode_in_initrd(void) void __init load_ucode_amd_bsp(void) { - apply_ucode_in_initrd(); + struct cpio_data cd = find_ucode_in_initrd(); + if (!cd.data) + return; + + apply_ucode_in_initrd(cd.data, cd.size); } #ifdef CONFIG_X86_32 @@ -145,12 +165,25 @@ u8 amd_bsp_mpb[MPB_MAX_SIZE]; void __cpuinit load_ucode_amd_ap(void) { struct microcode_amd *mc; + unsigned long *initrd; + unsigned long *uoffset; + size_t *usize; + void *ucode; mc = (struct microcode_amd *)__pa_nodebug(amd_bsp_mpb); - if (mc->hdr.patch_id && mc->hdr.processor_rev_id) + if (mc->hdr.patch_id && mc->hdr.processor_rev_id) { __apply_microcode_amd(mc); - else - apply_ucode_in_initrd(); + return; + } + + initrd = (unsigned long *)__pa_nodebug(&initrd_start); + uoffset = (unsigned long *)__pa_nodebug(&ucode_offset); + usize = (size_t *)__pa_nodebug(&ucode_size); + if (!*usize) + return; + + ucode = (void *)((unsigned long)__pa_nodebug(*initrd) + *uoffset); + apply_ucode_in_initrd(ucode, *usize); } static void __init collect_cpu_sig_on_bsp(void *arg) @@ -181,8 +214,13 @@ void __cpuinit load_ucode_amd_ap(void) collect_cpu_info_amd_early(&cpu_data(cpu), ucode_cpu_info + cpu); if (cpu && !ucode_loaded) { - struct cpio_data cd = find_ucode_in_initrd(); - if (load_microcode_amd(0, cd.data, cd.size) != UCODE_OK) + void *ucode; + + if (!ucode_size) + return; + + ucode = (void *)(initrd_start + ucode_offset); + if (load_microcode_amd(0, ucode, ucode_size) != UCODE_OK) return; ucode_loaded = true; } @@ -194,7 +232,7 @@ void __cpuinit load_ucode_amd_ap(void) int __init save_microcode_in_initrd_amd(void) { enum ucode_state ret; - struct cpio_data cd; + void *ucode; #ifdef CONFIG_X86_32 unsigned int bsp = boot_cpu_data.cpu_index; struct ucode_cpu_info *uci = ucode_cpu_info + bsp; @@ -209,11 +247,11 @@ int __init save_microcode_in_initrd_amd(void) if (ucode_loaded) return 0; - cd = find_ucode_in_initrd(); - if (!cd.data) - return -EINVAL; + if (!ucode_size) + return 0; - ret = load_microcode_amd(0, cd.data, cd.size); + ucode = (void *)(initrd_start + ucode_offset); + ret = load_microcode_amd(0, ucode, ucode_size); if (ret != UCODE_OK) return -EINVAL; -- cgit v1.2.3 From cd1c32ca969ebfd65e61312c988223bb14f09c2e Mon Sep 17 00:00:00 2001 From: Jacob Shin Date: Wed, 5 Jun 2013 15:13:56 -0500 Subject: x86, microcode, amd: Allow multiple families' bin files appended together Add support for parsing through multiple families' microcode patch container binary files appended together when early loading. This is already supported on Intel. Reported-by: Henrique de Moraes Holschuh Signed-off-by: Jacob Shin Link: http://lkml.kernel.org/r/1370463236-2115-3-git-send-email-jacob.shin@amd.com Signed-off-by: H. Peter Anvin --- arch/x86/kernel/microcode_amd_early.c | 50 +++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/arch/x86/kernel/microcode_amd_early.c b/arch/x86/kernel/microcode_amd_early.c index 7db1e16ca582..4c593c276114 100644 --- a/arch/x86/kernel/microcode_amd_early.c +++ b/arch/x86/kernel/microcode_amd_early.c @@ -87,15 +87,21 @@ static void __cpuinit apply_ucode_in_initrd(void *ucode, size_t size) struct equiv_cpu_entry *eq; u32 *header; u8 *data; - u16 eq_id; + u16 eq_id = 0; int offset, left; - u32 rev, dummy; + u32 rev, eax; u32 *new_rev; + unsigned long *uoffset; + size_t *usize; #ifdef CONFIG_X86_32 new_rev = (u32 *)__pa_nodebug(&ucode_new_rev); + uoffset = (unsigned long *)__pa_nodebug(&ucode_offset); + usize = (size_t *)__pa_nodebug(&ucode_size); #else new_rev = &ucode_new_rev; + uoffset = &ucode_offset; + usize = &ucode_size; #endif data = ucode; @@ -108,18 +114,46 @@ static void __cpuinit apply_ucode_in_initrd(void *ucode, size_t size) header[2] == 0) /* size */ return; - eq = (struct equiv_cpu_entry *)(data + CONTAINER_HDR_SZ); - offset = header[2] + CONTAINER_HDR_SZ; - data += offset; - left -= offset; + eax = cpuid_eax(0x00000001); + + while (left > 0) { + eq = (struct equiv_cpu_entry *)(data + CONTAINER_HDR_SZ); + + offset = header[2] + CONTAINER_HDR_SZ; + data += offset; + left -= offset; + + eq_id = find_equiv_id(eq, eax); + if (eq_id) + break; + + /* + * support multiple container files appended together. if this + * one does not have a matching equivalent cpu entry, we fast + * forward to the next container file. + */ + while (left > 0) { + header = (u32 *)data; + if (header[0] == UCODE_MAGIC && + header[1] == UCODE_EQUIV_CPU_TABLE_TYPE) + break; + + offset = header[1] + SECTION_HDR_SIZE; + data += offset; + left -= offset; + } + + offset = data - (u8 *)ucode; + *uoffset += offset; + *usize -= offset; + } - eq_id = find_equiv_id(eq, cpuid_eax(0x00000001)); if (!eq_id) return; /* find ucode and update if needed */ - rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy); + rdmsr(MSR_AMD64_PATCH_LEVEL, rev, eax); while (left > 0) { struct microcode_amd *mc; -- cgit v1.2.3 From 9608d33b8210c993af4430d661a6474946480c9b Mon Sep 17 00:00:00 2001 From: Jacob Shin Date: Thu, 20 Jun 2013 09:52:50 -0500 Subject: x86, microcode, amd: Another early loading fixup commit cd1c32ca969ebfd65e61312c988223bb14f09c2e is an early premature rendition of the patch. Augment it with this delta patch to: * correctly mark offset and size of the matching bin file * use __pa instead of __pa_nodebug during AP load * check for !initrd_start before using it Signed-off-by: Jacob Shin Link: http://lkml.kernel.org/r/20130620152414.GA6676@jshin-Toonie Signed-off-by: H. Peter Anvin --- arch/x86/kernel/microcode_amd_early.c | 40 +++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/arch/x86/kernel/microcode_amd_early.c b/arch/x86/kernel/microcode_amd_early.c index 4c593c276114..1ac6e9aee766 100644 --- a/arch/x86/kernel/microcode_amd_early.c +++ b/arch/x86/kernel/microcode_amd_early.c @@ -143,13 +143,17 @@ static void __cpuinit apply_ucode_in_initrd(void *ucode, size_t size) left -= offset; } - offset = data - (u8 *)ucode; + /* mark where the next microcode container file starts */ + offset = data - (u8 *)ucode; *uoffset += offset; *usize -= offset; + ucode = data; } - if (!eq_id) + if (!eq_id) { + *usize = 0; return; + } /* find ucode and update if needed */ @@ -166,15 +170,21 @@ static void __cpuinit apply_ucode_in_initrd(void *ucode, size_t size) mc = (struct microcode_amd *)(data + SECTION_HDR_SIZE); if (eq_id == mc->hdr.processor_rev_id && rev < mc->hdr.patch_id) if (__apply_microcode_amd(mc) == 0) { - if (!(*new_rev)) - *new_rev = mc->hdr.patch_id; - break; + rev = mc->hdr.patch_id; + *new_rev = rev; } offset = header[1] + SECTION_HDR_SIZE; data += offset; left -= offset; } + + /* mark where this microcode container file ends */ + offset = *usize - (data - (u8 *)ucode); + *usize -= offset; + + if (!(*new_rev)) + *usize = 0; } void __init load_ucode_amd_bsp(void) @@ -204,19 +214,20 @@ void __cpuinit load_ucode_amd_ap(void) size_t *usize; void *ucode; - mc = (struct microcode_amd *)__pa_nodebug(amd_bsp_mpb); + mc = (struct microcode_amd *)__pa(amd_bsp_mpb); if (mc->hdr.patch_id && mc->hdr.processor_rev_id) { __apply_microcode_amd(mc); return; } - initrd = (unsigned long *)__pa_nodebug(&initrd_start); - uoffset = (unsigned long *)__pa_nodebug(&ucode_offset); - usize = (size_t *)__pa_nodebug(&ucode_size); - if (!*usize) + initrd = (unsigned long *)__pa(&initrd_start); + uoffset = (unsigned long *)__pa(&ucode_offset); + usize = (size_t *)__pa(&ucode_size); + + if (!*usize || !*initrd) return; - ucode = (void *)((unsigned long)__pa_nodebug(*initrd) + *uoffset); + ucode = (void *)((unsigned long)__pa(*initrd) + *uoffset); apply_ucode_in_initrd(ucode, *usize); } @@ -250,7 +261,7 @@ void __cpuinit load_ucode_amd_ap(void) if (cpu && !ucode_loaded) { void *ucode; - if (!ucode_size) + if (!ucode_size || !initrd_start) return; ucode = (void *)(initrd_start + ucode_offset); @@ -278,10 +289,7 @@ int __init save_microcode_in_initrd_amd(void) pr_info("microcode: updated early to new patch_level=0x%08x\n", ucode_new_rev); - if (ucode_loaded) - return 0; - - if (!ucode_size) + if (ucode_loaded || !ucode_size || !initrd_start) return 0; ucode = (void *)(initrd_start + ucode_offset); -- cgit v1.2.3