x86/microcode: Cleanup and late loading enhancements
Subsystems (6)
v4 cover x86/microcode: Cleanup and late loading enhancements
This is a follow up on:
https://lore.kernel.org/lkml/[email protected]
Late microcode loading is desired by enterprise users. Late loading is problematic as it requires detailed knowledge about the change and an analysis whether this change modifies something which is already in use by the kernel. Large enterprise customers have engineering teams and access to deep technical vendor support. The regular admin does not have such resources, so the kernel has always tainted the kernel after late loading.
Intel recently added a new previously reserved field to the microcode header which contains the minimal microcode revision which must be running on the CPU to make the load safe. This field is 0 in all older microcode revisions, which the kernel assumes to be unsafe. Minimal revision checking can be enforced via Kconfig or kernel command line. It then refuses to load an unsafe revision. The default loads unsafe revisions like before and taints the kernel. If a safe revision is loaded the kernel is not tainted.
But that does not solve all other known problems with late loading:
- Late loading on current Intel CPUs is unsafe vs. NMI when hyperthreading is enabled. If a NMI hits the secondary sibling while the primary loads the microcode, the machine can crash.
- Soft offline SMT siblings which are playing dead with MWAIT can cause damage too when the microcode update modifies MWAIT. That's a realistic scenario in the context of 'nosmt' mitigations. :(
Neither the core code nor the Intel specific code handles any of this at all.
While trying to implement this, I stumbled over disfunctional, horribly complex and redundant code, which I decided to clean up first so the new functionality can be added on a clean slate.
So the series has several sections:
1) Move the 32bit early loading after paging enable
2) Cleanup of the Intel specific code
3) Implementation of proper core control logic to handle the NMI safe requirements
4) Support for minimal revision check in the core and the Intel specific parts.
Changes vs. V3:
- Rebased on v6.6-rc1
- Remove the early load magic which was required for physical address mode from the AMD code.
- Address the review comments from Borislav, which is mostly naming, comments and change logs. No functional changes vs. v3
The series is also available from git:
git://git.kernel.org/pub/scm/linux/kernel/git/tglx/devel.git ucode-v4
Thanks,
tglx
--- Documentation/admin-guide/kernel-parameters.txt | 5 arch/x86/Kconfig | 25 arch/x86/include/asm/apic.h | 5 arch/x86/include/asm/cpu.h | 20 arch/x86/include/asm/microcode.h | 19 arch/x86/kernel/Makefile | 1 arch/x86/kernel/apic/apic_flat_64.c | 2 arch/x86/kernel/apic/ipi.c | 8 arch/x86/kernel/apic/x2apic_cluster.c | 1 arch/x86/kernel/apic/x2apic_phys.c | 1 arch/x86/kernel/cpu/common.c | 12 arch/x86/kernel/cpu/microcode/amd.c | 129 +--- arch/x86/kernel/cpu/microcode/core.c | 637 ++++++++++++++-------- arch/x86/kernel/cpu/microcode/intel.c | 682 +++++++----------------- arch/x86/kernel/cpu/microcode/internal.h | 32 - arch/x86/kernel/head32.c | 6 arch/x86/kernel/head_32.S | 10 arch/x86/kernel/nmi.c | 9 arch/x86/kernel/smpboot.c | 12 drivers/platform/x86/intel/ifs/load.c | 8 include/linux/cpuhotplug.h | 1 21 files changed, 788 insertions(+), 837 deletions(-)
PATCH v4 1/30 x86/microcode/32: Move early loading after paging enable
32-bit loads microcode before paging is enabled. The commit which introduced that has zero justification in the changelog. The cover letter has slightly more content, but it does not give any technical justification either:
"The problem in current microcode loading method is that we load a
microcode way, way too late; ideally we should load it before turning paging on. This may only be practical on 32 bits since we can't get to 64-bit mode without paging on, but we should still do it as early as at all possible."
Handwaving word salad with zero technical content.
Someone claimed in an offlist conversation that this is required for curing the ATOM erratum AAE44/AAF40/AAG38/AAH41. That erratum requires an microcode update in order to make the usage of PSE safe. But during early boot PSE is completely irrelevant and it is evaluated way later.
Neither is it relevant for the AP on single core HT enabled CPUs as the microcode loading on the AP is not doing anything.
On dual core CPUs there is a theoretical problem if a split of an executable large page between enabling paging including PSE and loading the microcode happens. But that's only theoretical, it's practically irrelevant because the affected dual core CPUs are 64bit enabled and therefore have paging and PSE enabled before loading the microcode on the second core. So why would it work on 64-bit but not on 32-bit?
The erratum:
"AAG38 Code Fetch May Occur to Incorrect Address After a Large Page is
Split Into 4-Kbyte Pages
Problem: If software clears the PS (page size) bit in a present PDE (page directory entry), that will cause linear addresses mapped through this PDE to use 4-KByte pages instead of using a large page after old TLB entries are invalidated. Due to this erratum, if a code fetch uses this PDE before the TLB entry for the large page is invalidated then it may fetch from a different physical address than specified by either the old large page translation or the new 4-KByte page translation. This erratum may also cause speculative code fetches from incorrect addresses."
The practical relevance for this is exactly zero because there is no splitting of large text pages during early boot-time, i.e. between paging enable and microcode loading, and neither during CPU hotplug.
IOW, this load microcode before paging enable is yet another voodoo programming solution in search of a problem. What's worse is that it causes at least two serious problems:
1) When stackprotector is enabled then the microcode loader code has the stackprotector mechanics enabled. The read from the per CPU variable __stack_chk_guard is always accessing the virtual address either directly on UP or via FS on SMP. In physical address mode this results in an access to memory above 3GB. So this works by chance as the hardware returns the same value when there is no RAM at this physical address. When there is RAM populated above 3G then the read is by chance the same as nothing changes that memory during the very early boot stage. That's not necessarily true during runtime CPU hotplug.
2) When function tracing is enabled, then the relevant microcode loader functions and the functions invoked from there will call into the tracing code and evaluate global and per CPU variables in physical address mode. What could potentially go wrong?
Cure this and move the microcode loading after the early paging enable and remove the gunk in the microcode loader which is required to handle physical address mode.
---
V3: Remove the early arguments - Chang
Fixup the core code - 0day
--- arch/x86/include/asm/microcode.h | 5 - arch/x86/kernel/cpu/common.c | 12 --- arch/x86/kernel/cpu/microcode/amd.c | 103 ++++++++------------------- arch/x86/kernel/cpu/microcode/core.c | 73 ++++--------------- arch/x86/kernel/cpu/microcode/intel.c | 116 ++++--------------------------- arch/x86/kernel/cpu/microcode/internal.h | 2 arch/x86/kernel/head32.c | 3 arch/x86/kernel/head_32.S | 10 -- arch/x86/kernel/smpboot.c | 12 +-- 9 files changed, 71 insertions(+), 265 deletions(-)
arch/x86/include/asm/microcode.h +0 -5
@@ -68,11 +68,6 @@ static inline u32 intel_get_microcode_rereturn rev; } - -void show_ucode_info_early (void ); - -#else /* CONFIG_CPU_SUP_INTEL */ -static inline void show_ucode_info_early (void ) { }#endif /* !CONFIG_CPU_SUP_INTEL */ #endif /* _ASM_X86_MICROCODE_H */
arch/x86/kernel/cpu/common.c +0 -12
@@ -2166,8 +2166,6 @@ static inline void setup_getcpu(int cpu) }#ifdef CONFIG_X86_64 -static inline void ucode_cpu_init (int cpu) { } -static inline void tss_setup_ist (struct tss_struct *tss) {/* Set up the per-CPU TSS IST stacks */
@@ -2178,16 +2176,8 @@ static inline void tss_setup_ist(struct/* Only mapped when SEV-ES is active */ tss->x86_tss.ist[IST_INDEX_VC] = __this_cpu_ist_top_va(VC); } -#else /* CONFIG_X86_64 */ - -static inline void ucode_cpu_init (int cpu) -{ -show_ucode_info_early (); -} -static inline void tss_setup_ist (struct tss_struct *tss) { } -#endif /* !CONFIG_X86_64 */ static inline void tss_setup_io_bitmap (struct tss_struct *tss)
@@ -2243,8 +2233,6 @@ void cpu_init(void)struct task_struct *cur = current;int cpu =raw_smp_processor_id (); -ucode_cpu_init (cpu); -#ifdef CONFIG_NUMA if (this_cpu_read (numa_node) ==0 &&early_cpu_to_node (cpu) !=NUMA_NO_NODE )
arch/x86/kernel/cpu/microcode/amd.c +32 -71
255 lines not on this page · raw message
arch/x86/kernel/cpu/microcode/core.c +15 -58
142 lines not on this page · raw message
arch/x86/kernel/cpu/microcode/intel.c +16 -100
173 lines not on this page · raw message
arch/x86/kernel/cpu/microcode/internal.h +1 -1
@@ -44,7 +44,7 @@ struct microcode_ops { };extern struct ucode_cpu_info ucode_cpu_info[]; -struct cpio_data find_microcode_in_initrd (const char *path,bool use_pa); +struct cpio_data find_microcode_in_initrd (const char *path);#define MAX_UCODE_COUNT 128
arch/x86/kernel/head32.c +3 -0
@@ -19,6 +19,7 @@#include <asm/apic.h> #include <asm/io_apic.h> #include <asm/bios_ebda.h> +#include <asm/microcode.h> #include <asm/tlbflush.h> #include <asm/bootparam_utils.h>
@@ -34,6 +35,8 @@ asmlinkage __visible void __init __noret/* Make sure IDT is set up before any exception happens */ idt_setup_early_handler (); +load_ucode_bsp (); +cr4_init_shadow ();sanitize_boot_params (&boot_params);
arch/x86/kernel/head_32.S +0 -10
@@ -118,11 +118,6 @@ SYM_CODE_START(startup_32) movl %eax, pa(olpc_ofw_pgd) #endif -#ifdef CONFIG_MICROCODE - /* Early load ucode on BSP. */ - call load_ucode_bsp -#endif - /* Create early pagetables. */ call mk_early_pgtbl_32
@@ -157,11 +152,6 @@ SYM_FUNC_START(startup_32_smp) movl %eax,%ss leal -__PAGE_OFFSET(%ecx),%esp -#ifdef CONFIG_MICROCODE - /* Early load ucode on AP. */ - call load_ucode_ap -#endif - .Ldefault_entry: movl $(CR0_STATE & ~X86_CR0_PG),%eax movl %eax,%cr0
arch/x86/kernel/smpboot.c +4 -8
@@ -258,12 +258,9 @@ static void notrace start_secondary(voidcpu_init_exception_handling ();/* -* 32-bit systems load the microcode from the ASM startup code for -* historical reasons. -* -* On 64-bit systems load it before reaching the AP alive -* synchronization point below so it is not part of the full per -* CPU serialized bringup part when "parallel" bringup is enabled. +* Load the microcode before reaching the AP alive synchronization +* point below so it is not part of the full per CPU serialized +* bringup part when "parallel" bringup is enabled. * * That's even safe when hyperthreading is enabled in the CPU as * the core code starts the primary threads first and leaves the
@@ -276,8 +273,7 @@ static void notrace start_secondary(void* CPUID, MSRs etc. must be strictly serialized to maintain * software state correctness. */ -if (IS_ENABLED (CONFIG_X86_64 )) -load_ucode_ap (); +load_ucode_ap ();/* * Synchronization point with the hotplug core. Sets this CPUs
PATCH v4 2/30 x86/boot/32: Disable stackprotector and tracing for mk_early_pgtbl_32()
In reply to Thomas GleixnerStackprotector cannot work before paging is enabled. The read from the per CPU variable __stack_chk_guard is always accessing the virtual address either directly on UP or via FS on SMP. In physical address mode this results in an access to memory above 3GB.
So this works by chance as the hardware returns the same value when there is no RAM at this physical address. When there is RAM populated above 3G then the read is by chance the same as nothing changes that memory during the very early boot stage.
Stop relying on pure luck and disable the stack protector for the only C function which is called during early boot before paging is enabled.
Remove function tracing from the whole source file as there is no way to trace this at all, but in case of CONFIG_DYNAMIC_FTRACE=n mk_early_pgtbl_32() would access global function tracer variables in physical address mode which again might work by chance.
--- arch/x86/kernel/Makefile | 1 + arch/x86/kernel/head32.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-)
arch/x86/kernel/Makefile +1 -0
@@ -16,6 +16,7 @@ CFLAGS_REMOVE_kvmclock.o = -pg CFLAGS_REMOVE_ftrace.o = -pg CFLAGS_REMOVE_early_printk.o = -pg CFLAGS_REMOVE_head64.o = -pg +CFLAGS_REMOVE_head32.o = -pg CFLAGS_REMOVE_sev.o = -pg CFLAGS_REMOVE_rethook.o = -pg endif
arch/x86/kernel/head32.c +2 -1
@@ -73,7 +73,8 @@ asmlinkage __visible void __init __noret* always zero at this stage. */ void __init mk_early_pgtbl_32 (void ); -void __init mk_early_pgtbl_32 (void ) + +void __init __no_stack_protector mk_early_pgtbl_32(void) {#ifdef __pa#undef __pa
[tip: x86/microcode] x86/boot/32: Disable stackprotector and tracing for mk_early_pgtbl_32()
The following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: b6bedf0825b8a74edef6fa7100cb078e879bbce7 Gitweb: https://git.kernel.org/tip/b6bedf0825b8a74edef6fa7100cb078e879bbce7 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:36 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Mon, 02 Oct 2023 18:25:49 +02:00
x86/boot/32: Disable stackprotector and tracing for mk_early_pgtbl_32()
Stackprotector cannot work before paging is enabled. The read from the per CPU variable __stack_chk_guard is always accessing the virtual address either directly on UP or via FS on SMP. In physical address mode this results in an access to memory above 3GB.
So this works by chance as the hardware returns the same value when there is no RAM at this physical address. When there is RAM populated above 3G then the read is by chance the same as nothing changes that memory during the very early boot stage.
Stop relying on pure luck and disable the stack protector for the only C function which is called during early boot before paging is enabled.
Remove function tracing from the whole source file as there is no way to trace this at all, but in case of CONFIG_DYNAMIC_FTRACE=n mk_early_pgtbl_32() would access global function tracer variables in physical address mode which again might work by chance.
--- arch/x86/kernel/Makefile | 1 + arch/x86/kernel/head32.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-)
arch/x86/kernel/Makefile +1 -0
@@ -16,6 +16,7 @@ CFLAGS_REMOVE_kvmclock.o = -pg CFLAGS_REMOVE_ftrace.o = -pg CFLAGS_REMOVE_early_printk.o = -pg CFLAGS_REMOVE_head64.o = -pg +CFLAGS_REMOVE_head32.o = -pg CFLAGS_REMOVE_sev.o = -pg CFLAGS_REMOVE_rethook.o = -pg endif
arch/x86/kernel/head32.c +2 -1
@@ -73,7 +73,8 @@ asmlinkage __visible void __init __noreturn i386_start_kernel(void)* always zero at this stage. */ void __init mk_early_pgtbl_32 (void ); -void __init mk_early_pgtbl_32 (void ) + +void __init __no_stack_protector mk_early_pgtbl_32(void) {#ifdef __pa#undef __pa
[tip: x86/microcode] x86/boot/32: Disable stackprotector and tracing for mk_early_pgtbl_32()
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 242db7589460ca94e28c51ffbddd621756f97e11 Gitweb: https://git.kernel.org/tip/242db7589460ca94e28c51ffbddd621756f97e11 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:36 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Wed, 18 Oct 2023 11:11:43 +02:00
x86/boot/32: Disable stackprotector and tracing for mk_early_pgtbl_32()
Stackprotector cannot work before paging is enabled. The read from the per CPU variable __stack_chk_guard is always accessing the virtual address either directly on UP or via FS on SMP. In physical address mode this results in an access to memory above 3GB.
So this works by chance as the hardware returns the same value when there is no RAM at this physical address. When there is RAM populated above 3G then the read is by chance the same as nothing changes that memory during the very early boot stage.
Stop relying on pure luck and disable the stack protector for the only C function which is called during early boot before paging is enabled.
Remove function tracing from the whole source file as there is no way to trace this at all, but in case of CONFIG_DYNAMIC_FTRACE=n mk_early_pgtbl_32() would access global function tracer variables in physical address mode which again might work by chance.
--- arch/x86/kernel/Makefile | 1 + arch/x86/kernel/head32.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-)
arch/x86/kernel/Makefile +1 -0
@@ -16,6 +16,7 @@ CFLAGS_REMOVE_kvmclock.o = -pg CFLAGS_REMOVE_ftrace.o = -pg CFLAGS_REMOVE_early_printk.o = -pg CFLAGS_REMOVE_head64.o = -pg +CFLAGS_REMOVE_head32.o = -pg CFLAGS_REMOVE_sev.o = -pg CFLAGS_REMOVE_rethook.o = -pg endif
arch/x86/kernel/head32.c +2 -1
@@ -70,7 +70,8 @@ asmlinkage __visible void __init __noreturn i386_start_kernel(void)* always zero at this stage. */ void __init mk_early_pgtbl_32 (void ); -void __init mk_early_pgtbl_32 (void ) + +void __init __no_stack_protector mk_early_pgtbl_32(void) {#ifdef __pa#undef __pa
PATCH v4 3/30 x86/microcode/intel: Rip out mixed stepping support for Intel CPUs
In reply to Thomas GleixnerFrom: Ashok Raj <[email protected]>
Mixed steppings aren't supported on Intel CPUs. Only one patch is required for the entire system. The caching of micro code blobs which match the family and model is therefore pointless and in fact it is disfunctional as CPU hotplug updates use only a single microcode blob, i.e. the one where *intel_ucode_patch points to.
Remove the microcode cache and make it an AMD local feature.
[ tglx: Save only at the end. Otherwise random microcode ends up in the
pointer for early loading ]
---
V2: Fix the bogus condition - Borislav
--- arch/x86/kernel/cpu/microcode/amd.c | 10 ++ arch/x86/kernel/cpu/microcode/core.c | 2 arch/x86/kernel/cpu/microcode/intel.c | 133 +++++-------------------------- arch/x86/kernel/cpu/microcode/internal.h | 10 -- 4 files changed, 35 insertions(+), 120 deletions(-)
---
arch/x86/kernel/cpu/microcode/amd.c +10 -0
@@ -37,6 +37,16 @@#include "internal.h" +struct ucode_patch { +struct list_head plist; +void *data; +unsigned int size; +u32 patch_id; +u16 equiv_cpu; +}; + +static LIST_HEAD (microcode_cache); +#define UCODE_MAGIC 0x00414d44#define UCODE_EQUIV_CPU_TABLE_TYPE 0x00000000#define UCODE_UCODE_TYPE 0x00000001
arch/x86/kernel/cpu/microcode/core.c +0 -2
@@ -46,8 +46,6 @@ static bool dis_ucode_ldr = true;bool initrd_gone; -LIST_HEAD (microcode_cache); -/* * Synchronization. *
arch/x86/kernel/cpu/microcode/intel.c +25 -108
207 lines not on this page · raw message
arch/x86/kernel/cpu/microcode/internal.h +0 -10
@@ -8,16 +8,6 @@#include <asm/cpu.h> #include <asm/microcode.h> -struct ucode_patch { -struct list_head plist; -void *data;/* Intel uses only this one */ -unsigned int size; -u32 patch_id; -u16 equiv_cpu; -}; - -extern struct list_head microcode_cache; -struct device ;enum ucode_state {
[tip: x86/microcode] x86/microcode/intel: Rip out mixed stepping support for Intel CPUs
The following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: b06a37b1970f580c05b79465b37913093b62ce0d Gitweb: https://git.kernel.org/tip/b06a37b1970f580c05b79465b37913093b62ce0d Author: Ashok Raj <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:38 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Mon, 02 Oct 2023 18:28:55 +02:00
x86/microcode/intel: Rip out mixed stepping support for Intel CPUs
Mixed steppings aren't supported on Intel CPUs. Only one microcode patch is required for the entire system. The caching of microcode blobs which match the family and model is therefore pointless and in fact is dysfunctional as CPU hotplug updates use only a single microcode blob, i.e. the one where *intel_ucode_patch points to.
Remove the microcode cache and make it an AMD local feature.
[ tglx: Save only at the end. Otherwise random microcode ends up in the
pointer for early loading ]
--- arch/x86/kernel/cpu/microcode/amd.c | 10 ++- arch/x86/kernel/cpu/microcode/core.c | 2 +- arch/x86/kernel/cpu/microcode/intel.c | 133 ++++------------------ arch/x86/kernel/cpu/microcode/internal.h | 10 +-- 4 files changed, 35 insertions(+), 120 deletions(-)
arch/x86/kernel/cpu/microcode/amd.c +10 -0
@@ -37,6 +37,16 @@#include "internal.h" +struct ucode_patch { +struct list_head plist; +void *data; +unsigned int size; +u32 patch_id; +u16 equiv_cpu; +}; + +static LIST_HEAD (microcode_cache); +#define UCODE_MAGIC 0x00414d44#define UCODE_EQUIV_CPU_TABLE_TYPE 0x00000000#define UCODE_UCODE_TYPE 0x00000001
arch/x86/kernel/cpu/microcode/core.c +0 -2
@@ -46,8 +46,6 @@ static bool dis_ucode_ldr = true;bool initrd_gone; -LIST_HEAD (microcode_cache); -/* * Synchronization. *
arch/x86/kernel/cpu/microcode/intel.c +25 -108
207 lines not on this page · raw message
arch/x86/kernel/cpu/microcode/internal.h +0 -10
@@ -8,16 +8,6 @@#include <asm/cpu.h> #include <asm/microcode.h> -struct ucode_patch { -struct list_head plist; -void *data;/* Intel uses only this one */ -unsigned int size; -u32 patch_id; -u16 equiv_cpu; -}; - -extern struct list_head microcode_cache; -struct device ;enum ucode_state {
PATCH v4 4/30 x86/microcode/intel: Simplify scan_microcode()
In reply to Thomas GleixnerMake it readable and comprehensible.
--- arch/x86/kernel/cpu/microcode/intel.c | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-)
arch/x86/kernel/cpu/microcode/intel.c +7 -21
@@ -268,22 +268,16 @@ static void save_microcode_patch(void *d intel_ucode_patch = (struct microcode_intel *)p; } -/* -* Get microcode matching with BSP's model. Only CPUs with the same model as -* BSP can stay in the platform. -*/ -static struct microcode_intel * -scan_microcode (void *data,size_t size,struct ucode_cpu_info *uci,bool save) +/* Scan CPIO for microcode matching the boot CPUs family, model, stepping */ +static struct microcode_intel *scan_microcode (void *data,size_t size, +struct ucode_cpu_info *uci,bool save) {struct microcode_header_intel *mc_header;struct microcode_intel *patch =NULL ;u32 cur_rev = uci->cpu_sig.rev;unsigned int mc_size; -while (size) { -if (size <sizeof (struct microcode_header_intel )) -break ; - +for (; size >=sizeof (struct microcode_header_intel ); size -= mc_size, data += mc_size) { mc_header = (struct microcode_header_intel *)data; mc_size =get_totalsize (mc_header);
@@ -291,27 +285,19 @@ scan_microcode(void *data, size_t size, intel_microcode_sanity_check(data, false, MC_HEADER_TYPE_MICROCODE) < 0) break; - size -= mc_size; - -if (!intel_find_matching_signature (data, uci->cpu_sig.sig, - uci->cpu_sig.pf)) { - data += mc_size; +if (!intel_find_matching_signature (data, uci->cpu_sig.sig, uci->cpu_sig.pf))continue ; - }/* BSP scan: Check whether there is newer microcode */ if (!save && cur_rev >= mc_header->rev) - goto next; +continue ;/* Save scan: Check whether there is newer or matching microcode */ if (save && cur_rev != mc_header->rev) - goto next; +continue ; patch = data; cur_rev = mc_header->rev; - -next: - data += mc_size; }if (size)
[tip: x86/microcode] x86/microcode/intel: Simplify scan_microcode()
The following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 6a92484124b21e24aaa04be33fc4938f42d56733 Gitweb: https://git.kernel.org/tip/6a92484124b21e24aaa04be33fc4938f42d56733 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:39 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Mon, 02 Oct 2023 18:30:44 +02:00
x86/microcode/intel: Simplify scan_microcode()
Make it readable and comprehensible.
--- arch/x86/kernel/cpu/microcode/intel.c | 28 ++++++-------------------- 1 file changed, 7 insertions(+), 21 deletions(-)
arch/x86/kernel/cpu/microcode/intel.c +7 -21
@@ -268,22 +268,16 @@ static void save_microcode_patch(void *data, unsigned int size) intel_ucode_patch = (struct microcode_intel *)p; } -/* -* Get microcode matching with BSP's model. Only CPUs with the same model as -* BSP can stay in the platform. -*/ -static struct microcode_intel * -scan_microcode (void *data,size_t size,struct ucode_cpu_info *uci,bool save) +/* Scan CPIO for microcode matching the boot CPU's family, model, stepping */ +static struct microcode_intel *scan_microcode (void *data,size_t size, +struct ucode_cpu_info *uci,bool save) {struct microcode_header_intel *mc_header;struct microcode_intel *patch =NULL ;u32 cur_rev = uci->cpu_sig.rev;unsigned int mc_size; -while (size) { -if (size <sizeof (struct microcode_header_intel )) -break ; - +for (; size >=sizeof (struct microcode_header_intel ); size -= mc_size, data += mc_size) { mc_header = (struct microcode_header_intel *)data; mc_size =get_totalsize (mc_header);
@@ -291,27 +285,19 @@ scan_microcode(void *data, size_t size, struct ucode_cpu_info *uci, bool save) intel_microcode_sanity_check(data, false, MC_HEADER_TYPE_MICROCODE) < 0) break; - size -= mc_size; - -if (!intel_find_matching_signature (data, uci->cpu_sig.sig, - uci->cpu_sig.pf)) { - data += mc_size; +if (!intel_find_matching_signature (data, uci->cpu_sig.sig, uci->cpu_sig.pf))continue ; - }/* BSP scan: Check whether there is newer microcode */ if (!save && cur_rev >= mc_header->rev) - goto next; +continue ;/* Save scan: Check whether there is newer or matching microcode */ if (save && cur_rev != mc_header->rev) - goto next; +continue ; patch = data; cur_rev = mc_header->rev; - -next: - data += mc_size; }if (size)
[tip: x86/microcode] x86/microcode/intel: Simplify scan_microcode()
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: b0f0bf5eef5fac6ba30b7cac15ca4cb01f8a6ca9 Gitweb: https://git.kernel.org/tip/b0f0bf5eef5fac6ba30b7cac15ca4cb01f8a6ca9 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:39 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Thu, 19 Oct 2023 12:32:40 +02:00
x86/microcode/intel: Simplify scan_microcode()
Make it readable and comprehensible.
--- arch/x86/kernel/cpu/microcode/intel.c | 28 ++++++-------------------- 1 file changed, 7 insertions(+), 21 deletions(-)
arch/x86/kernel/cpu/microcode/intel.c +7 -21
@@ -266,22 +266,16 @@ static void save_microcode_patch(void *data, unsigned int size) intel_ucode_patch = (struct microcode_intel *)p; } -/* -* Get microcode matching with BSP's model. Only CPUs with the same model as -* BSP can stay in the platform. -*/ -static struct microcode_intel * -scan_microcode (void *data,size_t size,struct ucode_cpu_info *uci,bool save) +/* Scan CPIO for microcode matching the boot CPU's family, model, stepping */ +static struct microcode_intel *scan_microcode (void *data,size_t size, +struct ucode_cpu_info *uci,bool save) {struct microcode_header_intel *mc_header;struct microcode_intel *patch =NULL ;u32 cur_rev = uci->cpu_sig.rev;unsigned int mc_size; -while (size) { -if (size <sizeof (struct microcode_header_intel )) -break ; - +for (; size >=sizeof (struct microcode_header_intel ); size -= mc_size, data += mc_size) { mc_header = (struct microcode_header_intel *)data; mc_size =get_totalsize (mc_header);
@@ -289,27 +283,19 @@ scan_microcode(void *data, size_t size, struct ucode_cpu_info *uci, bool save) intel_microcode_sanity_check(data, false, MC_HEADER_TYPE_MICROCODE) < 0) break; - size -= mc_size; - -if (!intel_find_matching_signature (data, uci->cpu_sig.sig, - uci->cpu_sig.pf)) { - data += mc_size; +if (!intel_find_matching_signature (data, uci->cpu_sig.sig, uci->cpu_sig.pf))continue ; - }/* BSP scan: Check whether there is newer microcode */ if (!save && cur_rev >= mc_header->rev) - goto next; +continue ;/* Save scan: Check whether there is newer or matching microcode */ if (save && cur_rev != mc_header->rev) - goto next; +continue ; patch = data; cur_rev = mc_header->rev; - -next: - data += mc_size; }if (size)
PATCH v4 5/30 x86/microcode/intel: Simplify and rename generic_load_microcode()
In reply to Thomas Gleixnerso it becomes less obfuscated and rename it because there is nothing generic about it.
---
V3: Rename to parse_microcode_blobs() - Borislav
--- arch/x86/kernel/cpu/microcode/intel.c | 47 ++++++++++++---------------------- 1 file changed, 17 insertions(+), 30 deletions(-)
arch/x86/kernel/cpu/microcode/intel.c +17 -30
@@ -240,19 +240,6 @@ int intel_microcode_sanity_check(void *m }EXPORT_SYMBOL_GPL (intel_microcode_sanity_check); -/* -* Returns 1 if update has been found, 0 otherwise. -*/ -static int has_newer_microcode (void *mc,unsigned int csig,int cpf,int new_rev) -{ -struct microcode_header_intel *mc_hdr = mc; - -if (mc_hdr->rev <= new_rev) -return 0 ; - -return intel_find_matching_signature (mc, csig, cpf); -} -static void save_microcode_patch (void *data,unsigned int size) {struct microcode_header_intel *p;
@@ -561,14 +548,12 @@ static enum ucode_state apply_microcode_return ret; } -static enum ucode_state generic_load_microcode (int cpu,struct iov_iter *iter) +static enum ucode_state parse_microcode_blobs (int cpu,struct iov_iter *iter) {struct ucode_cpu_info *uci = ucode_cpu_info + cpu;unsigned int curr_mc_size =0 , new_mc_size =0 ; -enum ucode_state ret =UCODE_OK ; -int new_rev = uci->cpu_sig.rev; +int cur_rev = uci->cpu_sig.rev;u8 *new_mc =NULL , *mc =NULL ; -unsigned int csig, cpf;while (iov_iter_count (iter)) {struct microcode_header_intel mc_header;
@@ -585,6 +570,7 @@ static enum ucode_state generic_load_micpr_err ("error! Bad data in microcode data file (totalsize too small)\n" );break ; } + data_size = mc_size -sizeof (mc_header);if (data_size >iov_iter_count (iter)) {pr_err ("error! Bad data in microcode data file (truncated file?)\n" );
@@ -607,16 +593,17 @@ static enum ucode_state generic_load_micbreak ; } - csig = uci->cpu_sig.sig; - cpf = uci->cpu_sig.pf; -if (has_newer_microcode (mc, csig, cpf, new_rev)) { -vfree (new_mc); - new_rev = mc_header.rev; - new_mc = mc; - new_mc_size = mc_size; - mc =NULL ;/* trigger new vmalloc */ - ret =UCODE_NEW ; - } +if (cur_rev >= mc_header.rev) +continue ; + +if (!intel_find_matching_signature (mc, uci->cpu_sig.sig, uci->cpu_sig.pf)) +continue ; + +vfree (new_mc); + cur_rev = mc_header.rev; + new_mc = mc; + new_mc_size = mc_size; + mc =NULL ; }vfree (mc);
@@ -636,9 +623,9 @@ static enum ucode_state generic_load_micsave_microcode_patch (new_mc, new_mc_size);pr_debug ("CPU%d found a matching microcode update with version 0x%x (current=0x%x)\n" , - cpu, new_rev, uci->cpu_sig.rev); + cpu, cur_rev, uci->cpu_sig.rev); -return ret; +return UCODE_NEW ; }static bool is_blacklisted (unsigned int cpu)
@@ -687,7 +674,7 @@ static enum ucode_state request_microcod kvec.iov_base = (void *)firmware->data; kvec.iov_len = firmware->size;iov_iter_kvec (&iter,ITER_SOURCE , &kvec,1 , firmware->size); - ret =generic_load_microcode (cpu, &iter); + ret =parse_microcode_blobs (cpu, &iter);release_firmware (firmware);
[tip: x86/microcode] x86/microcode/intel: Simplify and rename generic_load_microcode()
The following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 6dea0b60b5b3f7d810877d1d0ccf3778bb142d00 Gitweb: https://git.kernel.org/tip/6dea0b60b5b3f7d810877d1d0ccf3778bb142d00 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:40 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Mon, 02 Oct 2023 18:31:20 +02:00
x86/microcode/intel: Simplify and rename generic_load_microcode()
so it becomes less obfuscated and rename it because there is nothing generic about it.
--- arch/x86/kernel/cpu/microcode/intel.c | 47 +++++++++----------------- 1 file changed, 17 insertions(+), 30 deletions(-)
arch/x86/kernel/cpu/microcode/intel.c +17 -30
@@ -240,19 +240,6 @@ int intel_microcode_sanity_check(void *mc, bool print_err, int hdr_type) }EXPORT_SYMBOL_GPL (intel_microcode_sanity_check); -/* -* Returns 1 if update has been found, 0 otherwise. -*/ -static int has_newer_microcode (void *mc,unsigned int csig,int cpf,int new_rev) -{ -struct microcode_header_intel *mc_hdr = mc; - -if (mc_hdr->rev <= new_rev) -return 0 ; - -return intel_find_matching_signature (mc, csig, cpf); -} -static void save_microcode_patch (void *data,unsigned int size) {struct microcode_header_intel *p;
@@ -561,14 +548,12 @@ out:return ret; } -static enum ucode_state generic_load_microcode (int cpu,struct iov_iter *iter) +static enum ucode_state parse_microcode_blobs (int cpu,struct iov_iter *iter) {struct ucode_cpu_info *uci = ucode_cpu_info + cpu;unsigned int curr_mc_size =0 , new_mc_size =0 ; -enum ucode_state ret =UCODE_OK ; -int new_rev = uci->cpu_sig.rev; +int cur_rev = uci->cpu_sig.rev;u8 *new_mc =NULL , *mc =NULL ; -unsigned int csig, cpf;while (iov_iter_count (iter)) {struct microcode_header_intel mc_header;
@@ -585,6 +570,7 @@ static enum ucode_state generic_load_microcode(int cpu, struct iov_iter *iter)pr_err ("error! Bad data in microcode data file (totalsize too small)\n" );break ; } + data_size = mc_size -sizeof (mc_header);if (data_size >iov_iter_count (iter)) {pr_err ("error! Bad data in microcode data file (truncated file?)\n" );
@@ -607,16 +593,17 @@ static enum ucode_state generic_load_microcode(int cpu, struct iov_iter *iter)break ; } - csig = uci->cpu_sig.sig; - cpf = uci->cpu_sig.pf; -if (has_newer_microcode (mc, csig, cpf, new_rev)) { -vfree (new_mc); - new_rev = mc_header.rev; - new_mc = mc; - new_mc_size = mc_size; - mc =NULL ;/* trigger new vmalloc */ - ret =UCODE_NEW ; - } +if (cur_rev >= mc_header.rev) +continue ; + +if (!intel_find_matching_signature (mc, uci->cpu_sig.sig, uci->cpu_sig.pf)) +continue ; + +vfree (new_mc); + cur_rev = mc_header.rev; + new_mc = mc; + new_mc_size = mc_size; + mc =NULL ; }vfree (mc);
@@ -636,9 +623,9 @@ static enum ucode_state generic_load_microcode(int cpu, struct iov_iter *iter)save_microcode_patch (new_mc, new_mc_size);pr_debug ("CPU%d found a matching microcode update with version 0x%x (current=0x%x)\n" , - cpu, new_rev, uci->cpu_sig.rev); + cpu, cur_rev, uci->cpu_sig.rev); -return ret; +return UCODE_NEW ; }static bool is_blacklisted (unsigned int cpu)
@@ -687,7 +674,7 @@ static enum ucode_state request_microcode_fw(int cpu, struct device *device) kvec.iov_base = (void *)firmware->data; kvec.iov_len = firmware->size;iov_iter_kvec (&iter,ITER_SOURCE , &kvec,1 , firmware->size); - ret =generic_load_microcode (cpu, &iter); + ret =parse_microcode_blobs (cpu, &iter);release_firmware (firmware);
[tip: x86/microcode] x86/microcode/intel: Simplify and rename generic_load_microcode()
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 6b072022ab2e1e83b7588144ee0080f7197b71da Gitweb: https://git.kernel.org/tip/6b072022ab2e1e83b7588144ee0080f7197b71da Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:40 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Thu, 19 Oct 2023 14:10:00 +02:00
x86/microcode/intel: Simplify and rename generic_load_microcode()
so it becomes less obfuscated and rename it because there is nothing generic about it.
--- arch/x86/kernel/cpu/microcode/intel.c | 47 +++++++++----------------- 1 file changed, 17 insertions(+), 30 deletions(-)
arch/x86/kernel/cpu/microcode/intel.c +17 -30
@@ -240,19 +240,6 @@ int intel_microcode_sanity_check(void *mc, bool print_err, int hdr_type) }EXPORT_SYMBOL_GPL (intel_microcode_sanity_check); -/* -* Returns 1 if update has been found, 0 otherwise. -*/ -static int has_newer_microcode (void *mc,unsigned int csig,int cpf,int new_rev) -{ -struct microcode_header_intel *mc_hdr = mc; - -if (mc_hdr->rev <= new_rev) -return 0 ; - -return intel_find_matching_signature (mc, csig, cpf); -} -static void save_microcode_patch (void *data,unsigned int size) {struct microcode_header_intel *p;
@@ -559,14 +546,12 @@ out:return ret; } -static enum ucode_state generic_load_microcode (int cpu,struct iov_iter *iter) +static enum ucode_state parse_microcode_blobs (int cpu,struct iov_iter *iter) {struct ucode_cpu_info *uci = ucode_cpu_info + cpu;unsigned int curr_mc_size =0 , new_mc_size =0 ; -enum ucode_state ret =UCODE_OK ; -int new_rev = uci->cpu_sig.rev; +int cur_rev = uci->cpu_sig.rev;u8 *new_mc =NULL , *mc =NULL ; -unsigned int csig, cpf;while (iov_iter_count (iter)) {struct microcode_header_intel mc_header;
@@ -583,6 +568,7 @@ static enum ucode_state generic_load_microcode(int cpu, struct iov_iter *iter)pr_err ("error! Bad data in microcode data file (totalsize too small)\n" );break ; } + data_size = mc_size -sizeof (mc_header);if (data_size >iov_iter_count (iter)) {pr_err ("error! Bad data in microcode data file (truncated file?)\n" );
@@ -605,16 +591,17 @@ static enum ucode_state generic_load_microcode(int cpu, struct iov_iter *iter)break ; } - csig = uci->cpu_sig.sig; - cpf = uci->cpu_sig.pf; -if (has_newer_microcode (mc, csig, cpf, new_rev)) { -vfree (new_mc); - new_rev = mc_header.rev; - new_mc = mc; - new_mc_size = mc_size; - mc =NULL ;/* trigger new vmalloc */ - ret =UCODE_NEW ; - } +if (cur_rev >= mc_header.rev) +continue ; + +if (!intel_find_matching_signature (mc, uci->cpu_sig.sig, uci->cpu_sig.pf)) +continue ; + +vfree (new_mc); + cur_rev = mc_header.rev; + new_mc = mc; + new_mc_size = mc_size; + mc =NULL ; }vfree (mc);
@@ -634,9 +621,9 @@ static enum ucode_state generic_load_microcode(int cpu, struct iov_iter *iter)save_microcode_patch (new_mc, new_mc_size);pr_debug ("CPU%d found a matching microcode update with version 0x%x (current=0x%x)\n" , - cpu, new_rev, uci->cpu_sig.rev); + cpu, cur_rev, uci->cpu_sig.rev); -return ret; +return UCODE_NEW ; }static bool is_blacklisted (unsigned int cpu)
@@ -685,7 +672,7 @@ static enum ucode_state request_microcode_fw(int cpu, struct device *device) kvec.iov_base = (void *)firmware->data; kvec.iov_len = firmware->size;iov_iter_kvec (&iter,ITER_SOURCE , &kvec,1 , firmware->size); - ret =generic_load_microcode (cpu, &iter); + ret =parse_microcode_blobs (cpu, &iter);release_firmware (firmware);
PATCH v4 6/30 x86/microcode/intel: Cleanup code further
In reply to Thomas GleixnerFrom: Thomas Gleixner <[email protected]>
Sanitize the microcode scan loop, fixup printks and move the loading function for builtin microcode next to the place where it is used and mark it __init.
---
V2: Fix changelog - Nikolay
--- arch/x86/kernel/cpu/microcode/intel.c | 76 ++++++++++++++-------------------- 1 file changed, 32 insertions(+), 44 deletions(-)
---
arch/x86/kernel/cpu/microcode/intel.c +32 -44
@@ -36,7 +36,7 @@ static const char ucode_path[] = "kernelstatic struct microcode_intel *intel_ucode_patch__read_mostly ;/* last level cache size per core */ -static int llc_size_per_core__ro_after_init ; +static unsigned int llc_size_per_core__ro_after_init ;/* microcode format is extended from prescott processors */ struct extended_signature {
@@ -296,29 +296,6 @@ static struct microcode_intel *scan_micrreturn patch; } -static bool load_builtin_intel_microcode (struct cpio_data *cp) -{ -unsigned int eax =1 , ebx, ecx =0 , edx; -struct firmware fw; -char name[30 ]; - -if (IS_ENABLED (CONFIG_X86_32 )) -return false; - -native_cpuid (&eax, &ebx, &ecx, &edx); - -sprintf (name,"intel-ucode/%02x-%02x-%02x" , -x86_family (eax),x86_model (eax),x86_stepping (eax)); - -if (firmware_request_builtin (&fw, name)) { - cp->size = fw.size; - cp->data = (void *)fw.data; -return true; - } - -return false; -} -static int apply_microcode_early (struct ucode_cpu_info *uci) {struct microcode_intel *mc;
@@ -362,6 +339,28 @@ static int apply_microcode_early(structreturn 0 ; } +static bool load_builtin_intel_microcode (struct cpio_data *cp) +{ +unsigned int eax =1 , ebx, ecx =0 , edx; +struct firmware fw; +char name[30 ]; + +if (IS_ENABLED (CONFIG_X86_32 )) +return false; + +native_cpuid (&eax, &ebx, &ecx, &edx); + +sprintf (name,"intel-ucode/%02x-%02x-%02x" , +x86_family (eax),x86_model (eax),x86_stepping (eax)); + +if (firmware_request_builtin (&fw, name)) { + cp->size = fw.size; + cp->data = (void *)fw.data; +return true; + } +return false; +} +int __init save_microcode_in_initrd_intel (void ) {struct ucode_cpu_info uci;
@@ -434,25 +433,16 @@ void load_ucode_intel_ap(void)apply_microcode_early (&uci); } -/* Accessor for microcode pointer */ -static struct microcode_intel *ucode_get_patch (void ) -{ -return intel_ucode_patch; -} -void reload_ucode_intel (void ) { -struct microcode_intel *p;struct ucode_cpu_info uci;intel_cpu_collect_info (&uci); - p =ucode_get_patch (); -if (!p) + uci.mc = intel_ucode_patch; +if (!uci.mc)return ; - uci.mc = p; -apply_microcode_early (&uci); }
@@ -490,8 +480,7 @@ static enum ucode_state apply_microcode_if (WARN_ON (raw_smp_processor_id () != cpu))return UCODE_ERROR ; -/* Look for a newer patch in our cache: */ - mc =ucode_get_patch (); + mc = intel_ucode_patch;if (!mc) { mc = uci->mc;if (!mc)
@@ -682,18 +671,17 @@ static enum ucode_state request_microcod }static struct microcode_ops microcode_intel_ops = { - .request_microcode_fw = request_microcode_fw, - .collect_cpu_info = collect_cpu_info, - .apply_microcode = apply_microcode_intel, + .request_microcode_fw = request_microcode_fw, + .collect_cpu_info = collect_cpu_info, + .apply_microcode = apply_microcode_intel, }; -static int __init calc_llc_size_per_core (struct cpuinfo_x86 *c) +static __init void calc_llc_size_per_core (struct cpuinfo_x86 *c) {u64 llc_size = c->x86_cache_size *1024ULL ;do_div (llc_size, c->x86_max_cores); - -return (int )llc_size; + llc_size_per_core = (unsigned int )llc_size; }struct microcode_ops *__init init_intel_microcode (void )
@@ -706,7 +694,7 @@ struct microcode_ops * __init init_intelreturn NULL ; } - llc_size_per_core =calc_llc_size_per_core (c); +calc_llc_size_per_core (c);return µcode_intel_ops; }
[tip: x86/microcode] x86/microcode/intel: Cleanup code further
The following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 5017d6d28ad46c2ff677d5bea13f4358082cd86f Gitweb: https://git.kernel.org/tip/5017d6d28ad46c2ff677d5bea13f4358082cd86f Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:41 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Tue, 03 Oct 2023 11:27:27 +02:00
x86/microcode/intel: Cleanup code further
Sanitize the microcode scan loop, fixup printks and move the loading function for builtin microcode next to the place where it is used and mark it __init.
--- arch/x86/kernel/cpu/microcode/intel.c | 76 ++++++++++---------------- 1 file changed, 32 insertions(+), 44 deletions(-)
arch/x86/kernel/cpu/microcode/intel.c +32 -44
@@ -36,7 +36,7 @@ static const char ucode_path[] = "kernel/x86/microcode/GenuineIntel.bin";static struct microcode_intel *intel_ucode_patch__read_mostly ;/* last level cache size per core */ -static int llc_size_per_core__ro_after_init ; +static unsigned int llc_size_per_core__ro_after_init ;/* microcode format is extended from prescott processors */ struct extended_signature {
@@ -296,29 +296,6 @@ static struct microcode_intel *scan_microcode(void *data, size_t size,return patch; } -static bool load_builtin_intel_microcode (struct cpio_data *cp) -{ -unsigned int eax =1 , ebx, ecx =0 , edx; -struct firmware fw; -char name[30 ]; - -if (IS_ENABLED (CONFIG_X86_32 )) -return false; - -native_cpuid (&eax, &ebx, &ecx, &edx); - -sprintf (name,"intel-ucode/%02x-%02x-%02x" , -x86_family (eax),x86_model (eax),x86_stepping (eax)); - -if (firmware_request_builtin (&fw, name)) { - cp->size = fw.size; - cp->data = (void *)fw.data; -return true; - } - -return false; -} -static int apply_microcode_early (struct ucode_cpu_info *uci) {struct microcode_intel *mc;
@@ -362,6 +339,28 @@ static int apply_microcode_early(struct ucode_cpu_info *uci)return 0 ; } +static bool load_builtin_intel_microcode (struct cpio_data *cp) +{ +unsigned int eax =1 , ebx, ecx =0 , edx; +struct firmware fw; +char name[30 ]; + +if (IS_ENABLED (CONFIG_X86_32 )) +return false; + +native_cpuid (&eax, &ebx, &ecx, &edx); + +sprintf (name,"intel-ucode/%02x-%02x-%02x" , +x86_family (eax),x86_model (eax),x86_stepping (eax)); + +if (firmware_request_builtin (&fw, name)) { + cp->size = fw.size; + cp->data = (void *)fw.data; +return true; + } +return false; +} +int __init save_microcode_in_initrd_intel (void ) {struct ucode_cpu_info uci;
@@ -434,25 +433,16 @@ void load_ucode_intel_ap(void)apply_microcode_early (&uci); } -/* Accessor for microcode pointer */ -static struct microcode_intel *ucode_get_patch (void ) -{ -return intel_ucode_patch; -} -void reload_ucode_intel (void ) { -struct microcode_intel *p;struct ucode_cpu_info uci;intel_cpu_collect_info (&uci); - p =ucode_get_patch (); -if (!p) + uci.mc = intel_ucode_patch; +if (!uci.mc)return ; - uci.mc = p; -apply_microcode_early (&uci); }
@@ -490,8 +480,7 @@ static enum ucode_state apply_microcode_intel(int cpu)if (WARN_ON (raw_smp_processor_id () != cpu))return UCODE_ERROR ; -/* Look for a newer patch in our cache: */ - mc =ucode_get_patch (); + mc = intel_ucode_patch;if (!mc) { mc = uci->mc;if (!mc)
@@ -682,18 +671,17 @@ static enum ucode_state request_microcode_fw(int cpu, struct device *device) }static struct microcode_ops microcode_intel_ops = { - .request_microcode_fw = request_microcode_fw, - .collect_cpu_info = collect_cpu_info, - .apply_microcode = apply_microcode_intel, + .request_microcode_fw = request_microcode_fw, + .collect_cpu_info = collect_cpu_info, + .apply_microcode = apply_microcode_intel, }; -static int __init calc_llc_size_per_core (struct cpuinfo_x86 *c) +static __init void calc_llc_size_per_core (struct cpuinfo_x86 *c) {u64 llc_size = c->x86_cache_size *1024ULL ;do_div (llc_size, c->x86_max_cores); - -return (int )llc_size; + llc_size_per_core = (unsigned int )llc_size; }struct microcode_ops *__init init_intel_microcode (void )
@@ -706,7 +694,7 @@ struct microcode_ops * __init init_intel_microcode(void)return NULL ; } - llc_size_per_core =calc_llc_size_per_core (c); +calc_llc_size_per_core (c);return µcode_intel_ops; }
[tip: x86/microcode] x86/microcode/intel: Cleanup code further
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 0177669ee61de4dc641f9ad86a3df6f22327cf6c Gitweb: https://git.kernel.org/tip/0177669ee61de4dc641f9ad86a3df6f22327cf6c Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:41 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Thu, 19 Oct 2023 14:10:50 +02:00
x86/microcode/intel: Cleanup code further
Sanitize the microcode scan loop, fixup printks and move the loading function for builtin microcode next to the place where it is used and mark it __init.
--- arch/x86/kernel/cpu/microcode/intel.c | 76 ++++++++++---------------- 1 file changed, 32 insertions(+), 44 deletions(-)
arch/x86/kernel/cpu/microcode/intel.c +32 -44
@@ -36,7 +36,7 @@ static const char ucode_path[] = "kernel/x86/microcode/GenuineIntel.bin";static struct microcode_intel *intel_ucode_patch__read_mostly ;/* last level cache size per core */ -static int llc_size_per_core__ro_after_init ; +static unsigned int llc_size_per_core__ro_after_init ;/* microcode format is extended from prescott processors */ struct extended_signature {
@@ -294,29 +294,6 @@ static struct microcode_intel *scan_microcode(void *data, size_t size,return patch; } -static bool load_builtin_intel_microcode (struct cpio_data *cp) -{ -unsigned int eax =1 , ebx, ecx =0 , edx; -struct firmware fw; -char name[30 ]; - -if (IS_ENABLED (CONFIG_X86_32 )) -return false; - -native_cpuid (&eax, &ebx, &ecx, &edx); - -sprintf (name,"intel-ucode/%02x-%02x-%02x" , -x86_family (eax),x86_model (eax),x86_stepping (eax)); - -if (firmware_request_builtin (&fw, name)) { - cp->size = fw.size; - cp->data = (void *)fw.data; -return true; - } - -return false; -} -static int apply_microcode_early (struct ucode_cpu_info *uci) {struct microcode_intel *mc;
@@ -360,6 +337,28 @@ static int apply_microcode_early(struct ucode_cpu_info *uci)return 0 ; } +static bool load_builtin_intel_microcode (struct cpio_data *cp) +{ +unsigned int eax =1 , ebx, ecx =0 , edx; +struct firmware fw; +char name[30 ]; + +if (IS_ENABLED (CONFIG_X86_32 )) +return false; + +native_cpuid (&eax, &ebx, &ecx, &edx); + +sprintf (name,"intel-ucode/%02x-%02x-%02x" , +x86_family (eax),x86_model (eax),x86_stepping (eax)); + +if (firmware_request_builtin (&fw, name)) { + cp->size = fw.size; + cp->data = (void *)fw.data; +return true; + } +return false; +} +int __init save_microcode_in_initrd_intel (void ) {struct ucode_cpu_info uci;
@@ -432,25 +431,16 @@ void load_ucode_intel_ap(void)apply_microcode_early (&uci); } -/* Accessor for microcode pointer */ -static struct microcode_intel *ucode_get_patch (void ) -{ -return intel_ucode_patch; -} -void reload_ucode_intel (void ) { -struct microcode_intel *p;struct ucode_cpu_info uci;intel_cpu_collect_info (&uci); - p =ucode_get_patch (); -if (!p) + uci.mc = intel_ucode_patch; +if (!uci.mc)return ; - uci.mc = p; -apply_microcode_early (&uci); }
@@ -488,8 +478,7 @@ static enum ucode_state apply_microcode_intel(int cpu)if (WARN_ON (raw_smp_processor_id () != cpu))return UCODE_ERROR ; -/* Look for a newer patch in our cache: */ - mc =ucode_get_patch (); + mc = intel_ucode_patch;if (!mc) { mc = uci->mc;if (!mc)
@@ -680,18 +669,17 @@ static enum ucode_state request_microcode_fw(int cpu, struct device *device) }static struct microcode_ops microcode_intel_ops = { - .request_microcode_fw = request_microcode_fw, - .collect_cpu_info = collect_cpu_info, - .apply_microcode = apply_microcode_intel, + .request_microcode_fw = request_microcode_fw, + .collect_cpu_info = collect_cpu_info, + .apply_microcode = apply_microcode_intel, }; -static int __init calc_llc_size_per_core (struct cpuinfo_x86 *c) +static __init void calc_llc_size_per_core (struct cpuinfo_x86 *c) {u64 llc_size = c->x86_cache_size *1024ULL ;do_div (llc_size, c->x86_max_cores); - -return (int )llc_size; + llc_size_per_core = (unsigned int )llc_size; }struct microcode_ops *__init init_intel_microcode (void )
@@ -704,7 +692,7 @@ struct microcode_ops * __init init_intel_microcode(void)return NULL ; } - llc_size_per_core =calc_llc_size_per_core (c); +calc_llc_size_per_core (c);return µcode_intel_ops; }
PATCH v4 7/30 x86/microcode/intel: Simplify early loading
In reply to Thomas GleixnerFrom: Thomas Gleixner <[email protected]>
The early loading code is overly complicated:
- It scans the builtin/initrd for microcode not only on the BSP, but also on all APs during early boot and then later in the boot process it scans again to duplicate and save the microcode before initrd goes away.
That's a pointless exercise because this can be simply done before bringing up the APs when the memory allocator is up and running.
- Saving the microcode from within the scan loop is completely non-obvious and a left over of the microcode cache.
This can be done at the call site now which makes it obvious.
Rework the code so that only the BSP scans the builtin/initrd microcode once during early boot and save it away in an early initcall for later use.
---
V4: Drop CPIO references - Borislav
--- arch/x86/kernel/cpu/microcode/core.c | 4 arch/x86/kernel/cpu/microcode/intel.c | 150 +++++++++++++------------------ arch/x86/kernel/cpu/microcode/internal.h | 2 3 files changed, 65 insertions(+), 91 deletions(-)
---
arch/x86/kernel/cpu/microcode/core.c +0 -4
@@ -186,10 +186,6 @@ static int __init save_microcode_in_initint ret = -EINVAL ;switch (c->x86_vendor) { -case X86_VENDOR_INTEL : -if (c->x86 >=6 ) - ret =save_microcode_in_initrd_intel (); -break ;case X86_VENDOR_AMD :if (c->x86 >=0x10 ) ret =save_microcode_in_initrd_amd (cpuid_eax (1 ));
arch/x86/kernel/cpu/microcode/intel.c +65 -85
270 lines not on this page · raw message
arch/x86/kernel/cpu/microcode/internal.h +0 -2
@@ -107,13 +107,11 @@ static inline void exit_amd_microcode(vo#ifdef CONFIG_CPU_SUP_INTEL void load_ucode_intel_bsp (void );void load_ucode_intel_ap (void ); -int save_microcode_in_initrd_intel (void );void reload_ucode_intel (void );struct microcode_ops *init_intel_microcode (void );#else /* CONFIG_CPU_SUP_INTEL */ static inline void load_ucode_intel_bsp (void ) { }static inline void load_ucode_intel_ap (void ) { } -static inline int save_microcode_in_initrd_intel (void ) {return -EINVAL ; }static inline void reload_ucode_intel (void ) { }static inline struct microcode_ops *init_intel_microcode (void ) {return NULL ; }#endif /* !CONFIG_CPU_SUP_INTEL */
[tip: x86/microcode] x86/microcode/intel: Simplify early loading
The following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 34825609b868a5201f4dedd0b2abbef2e60d7ae4 Gitweb: https://git.kernel.org/tip/34825609b868a5201f4dedd0b2abbef2e60d7ae4 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:43 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Tue, 03 Oct 2023 11:31:46 +02:00
x86/microcode/intel: Simplify early loading
The early loading code is overly complicated:
- It scans the builtin/initrd for microcode not only on the BSP, but also on all APs during early boot and then later in the boot process it scans again to duplicate and save the microcode before initrd goes away.
That's a pointless exercise because this can be simply done before bringing up the APs when the memory allocator is up and running.
- Saving the microcode from within the scan loop is completely non-obvious and a left over of the microcode cache.
This can be done at the call site now which makes it obvious.
Rework the code so that only the BSP scans the builtin/initrd microcode once during early boot and save it away in an early initcall for later use.
--- arch/x86/kernel/cpu/microcode/core.c | 4 +- arch/x86/kernel/cpu/microcode/intel.c | 150 +++++++++------------- arch/x86/kernel/cpu/microcode/internal.h | 2 +- 3 files changed, 65 insertions(+), 91 deletions(-)
arch/x86/kernel/cpu/microcode/core.c +0 -4
@@ -186,10 +186,6 @@ static int __init save_microcode_in_initrd(void)int ret = -EINVAL ;switch (c->x86_vendor) { -case X86_VENDOR_INTEL : -if (c->x86 >=6 ) - ret =save_microcode_in_initrd_intel (); -break ;case X86_VENDOR_AMD :if (c->x86 >=0x10 ) ret =save_microcode_in_initrd_amd (cpuid_eax (1 ));
arch/x86/kernel/cpu/microcode/intel.c +65 -85
270 lines not on this page · raw message
arch/x86/kernel/cpu/microcode/internal.h +0 -2
@@ -107,13 +107,11 @@ static inline void exit_amd_microcode(void) { }#ifdef CONFIG_CPU_SUP_INTEL void load_ucode_intel_bsp (void );void load_ucode_intel_ap (void ); -int save_microcode_in_initrd_intel (void );void reload_ucode_intel (void );struct microcode_ops *init_intel_microcode (void );#else /* CONFIG_CPU_SUP_INTEL */ static inline void load_ucode_intel_bsp (void ) { }static inline void load_ucode_intel_ap (void ) { } -static inline int save_microcode_in_initrd_intel (void ) {return -EINVAL ; }static inline void reload_ucode_intel (void ) { }static inline struct microcode_ops *init_intel_microcode (void ) {return NULL ; }#endif /* !CONFIG_CPU_SUP_INTEL */
PATCH v4 8/30 x86/microcode/intel: Save the microcode only after a successful late-load
In reply to Thomas GleixnerFrom: Thomas Gleixner <[email protected]>
There are situations where the late microcode is loaded into memory, but is not applied:
1) The rendezvouz fails
2) The microcode is rejected by the CPUs
If any of this happens then the pointer which was updated at firmware load time is stale and subsequent CPU hotplug operations either fail to update or create inconsistent microcode state.
Save the loaded microcode in a separate pointer from with the late load is attempted and when successful, update the hotplug pointer accordingly via a new microcode_ops callback.
Remove the pointless fallback in the loader to a microcode pointer which is never populated.
--- arch/x86/kernel/cpu/microcode/core.c | 4 ++++ arch/x86/kernel/cpu/microcode/intel.c | 30 +++++++++++++++--------------- arch/x86/kernel/cpu/microcode/internal.h | 1 + 3 files changed, 20 insertions(+), 15 deletions(-)
---
arch/x86/kernel/cpu/microcode/core.c +4 -0
@@ -400,6 +400,10 @@ static int microcode_reload_late(void)store_cpu_caps (&prev_info); ret =stop_machine_cpuslocked (__reload_late,NULL , cpu_online_mask); + +if (microcode_ops->finalize_late_load) + microcode_ops->finalize_late_load (ret); +if (!ret) {pr_info ("Reload succeeded, microcode revision: 0x%x -> 0x%x\n" , old, boot_cpu_data.microcode);
arch/x86/kernel/cpu/microcode/intel.c +15 -15
@@ -34,6 +34,7 @@ static const char ucode_path[] = "kernel/* Current microcode patch used in early patching on the APs. */ static struct microcode_intel *ucode_patch_va__read_mostly ; +static struct microcode_intel *ucode_patch_late__read_mostly ;/* last level cache size per core */ static unsigned int llc_size_per_core__ro_after_init ;
@@ -461,12 +462,9 @@ static enum ucode_state apply_microcode_if (WARN_ON (raw_smp_processor_id () != cpu))return UCODE_ERROR ; - mc = ucode_patch_va; -if (!mc) { - mc = uci->mc; -if (!mc) -return UCODE_NFOUND ; - } + mc = ucode_patch_late; +if (!mc) +return UCODE_NFOUND ;/* * Save us the MSR write below - which is a particular expensive
@@ -585,15 +583,7 @@ static enum ucode_state parse_microcode_if (!new_mc)return UCODE_NFOUND ; -/* Save for CPU hotplug */ -save_microcode_patch ((struct microcode_intel *)new_mc); - uci->mc = ucode_patch_va; - -vfree (new_mc); - -pr_debug ("CPU%d found a matching microcode update with version 0x%x (current=0x%x)\n" , - cpu, cur_rev, uci->cpu_sig.rev); - + ucode_patch_late = (struct microcode_intel *)new_mc;return UCODE_NEW ; }
@@ -650,10 +640,20 @@ static enum ucode_state request_microcodreturn ret; } +static void finalize_late_load (int result) +{ +if (!result) +save_microcode_patch (ucode_patch_late); + +vfree (ucode_patch_late); + ucode_patch_late =NULL ; +} +static struct microcode_ops microcode_intel_ops = { .request_microcode_fw = request_microcode_fw, .collect_cpu_info = collect_cpu_info, .apply_microcode = apply_microcode_intel, + .finalize_late_load = finalize_late_load, };static __init void calc_llc_size_per_core (struct cpuinfo_x86 *c)
arch/x86/kernel/cpu/microcode/internal.h +1 -0
@@ -31,6 +31,7 @@ struct microcode_ops {*/ enum ucode_state (*apply_microcode)(int cpu);int (*collect_cpu_info)(int cpu,struct cpu_signature *csig); +void (*finalize_late_load)(int result); };extern struct ucode_cpu_info ucode_cpu_info[];
[tip: x86/microcode] x86/microcode/intel: Save the microcode only after a successful late-load
The following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: ed1a85d984ac1543349da617dce65f4190839f9f Gitweb: https://git.kernel.org/tip/ed1a85d984ac1543349da617dce65f4190839f9f Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:44 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Tue, 03 Oct 2023 15:58:25 +02:00
x86/microcode/intel: Save the microcode only after a successful late-load
There are situations where the late microcode is loaded into memory but is not applied:
1) The rendezvous fails
2) The microcode is rejected by the CPUs
If any of this happens then the pointer which was updated at firmware load time is stale and subsequent CPU hotplug operations either fail to update or create inconsistent microcode state.
Save the loaded microcode in a separate pointer before the late load is attempted and when successful, update the hotplug pointer accordingly via a new microcode_ops callback.
Remove the pointless fallback in the loader to a microcode pointer which is never populated.
--- arch/x86/kernel/cpu/microcode/core.c | 4 +++- arch/x86/kernel/cpu/microcode/intel.c | 30 +++++++++++------------ arch/x86/kernel/cpu/microcode/internal.h | 1 +- 3 files changed, 20 insertions(+), 15 deletions(-)
arch/x86/kernel/cpu/microcode/core.c +4 -0
@@ -400,6 +400,10 @@ static int microcode_reload_late(void)store_cpu_caps (&prev_info); ret =stop_machine_cpuslocked (__reload_late,NULL , cpu_online_mask); + +if (microcode_ops->finalize_late_load) + microcode_ops->finalize_late_load (ret); +if (!ret) {pr_info ("Reload succeeded, microcode revision: 0x%x -> 0x%x\n" , old, boot_cpu_data.microcode);
arch/x86/kernel/cpu/microcode/intel.c +15 -15
@@ -34,6 +34,7 @@ static const char ucode_path[] = "kernel/x86/microcode/GenuineIntel.bin";/* Current microcode patch used in early patching on the APs. */ static struct microcode_intel *ucode_patch_va__read_mostly ; +static struct microcode_intel *ucode_patch_late__read_mostly ;/* last level cache size per core */ static unsigned int llc_size_per_core__ro_after_init ;
@@ -461,12 +462,9 @@ static enum ucode_state apply_microcode_intel(int cpu)if (WARN_ON (raw_smp_processor_id () != cpu))return UCODE_ERROR ; - mc = ucode_patch_va; -if (!mc) { - mc = uci->mc; -if (!mc) -return UCODE_NFOUND ; - } + mc = ucode_patch_late; +if (!mc) +return UCODE_NFOUND ;/* * Save us the MSR write below - which is a particular expensive
@@ -585,15 +583,7 @@ static enum ucode_state parse_microcode_blobs(int cpu, struct iov_iter *iter)if (!new_mc)return UCODE_NFOUND ; -/* Save for CPU hotplug */ -save_microcode_patch ((struct microcode_intel *)new_mc); - uci->mc = ucode_patch_va; - -vfree (new_mc); - -pr_debug ("CPU%d found a matching microcode update with version 0x%x (current=0x%x)\n" , - cpu, cur_rev, uci->cpu_sig.rev); - + ucode_patch_late = (struct microcode_intel *)new_mc;return UCODE_NEW ; }
@@ -650,10 +640,20 @@ static enum ucode_state request_microcode_fw(int cpu, struct device *device)return ret; } +static void finalize_late_load (int result) +{ +if (!result) +save_microcode_patch (ucode_patch_late); + +vfree (ucode_patch_late); + ucode_patch_late =NULL ; +} +static struct microcode_ops microcode_intel_ops = { .request_microcode_fw = request_microcode_fw, .collect_cpu_info = collect_cpu_info, .apply_microcode = apply_microcode_intel, + .finalize_late_load = finalize_late_load, };static __init void calc_llc_size_per_core (struct cpuinfo_x86 *c)
arch/x86/kernel/cpu/microcode/internal.h +1 -0
@@ -31,6 +31,7 @@ struct microcode_ops {*/ enum ucode_state (*apply_microcode)(int cpu);int (*collect_cpu_info)(int cpu,struct cpu_signature *csig); +void (*finalize_late_load)(int result); };extern struct ucode_cpu_info ucode_cpu_info[];
[tip: x86/microcode] x86/microcode/intel: Save the microcode only after a successful late-load
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 81473520ac894010c012c6c1ecc9ed86d91a234b Gitweb: https://git.kernel.org/tip/81473520ac894010c012c6c1ecc9ed86d91a234b Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:44 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Thu, 19 Oct 2023 14:21:29 +02:00
x86/microcode/intel: Save the microcode only after a successful late-load
There are situations where the late microcode is loaded into memory but is not applied:
1) The rendezvous fails
2) The microcode is rejected by the CPUs
If any of this happens then the pointer which was updated at firmware load time is stale and subsequent CPU hotplug operations either fail to update or create inconsistent microcode state.
Save the loaded microcode in a separate pointer before the late load is attempted and when successful, update the hotplug pointer accordingly via a new microcode_ops callback.
Remove the pointless fallback in the loader to a microcode pointer which is never populated.
--- arch/x86/kernel/cpu/microcode/core.c | 4 +++- arch/x86/kernel/cpu/microcode/intel.c | 30 +++++++++++------------ arch/x86/kernel/cpu/microcode/internal.h | 1 +- 3 files changed, 20 insertions(+), 15 deletions(-)
arch/x86/kernel/cpu/microcode/core.c +4 -0
@@ -397,6 +397,10 @@ static int microcode_reload_late(void)store_cpu_caps (&prev_info); ret =stop_machine_cpuslocked (__reload_late,NULL , cpu_online_mask); + +if (microcode_ops->finalize_late_load) + microcode_ops->finalize_late_load (ret); +if (!ret) {pr_info ("Reload succeeded, microcode revision: 0x%x -> 0x%x\n" , old, boot_cpu_data.microcode);
arch/x86/kernel/cpu/microcode/intel.c +15 -15
@@ -34,6 +34,7 @@ static const char ucode_path[] = "kernel/x86/microcode/GenuineIntel.bin";/* Current microcode patch used in early patching on the APs. */ static struct microcode_intel *ucode_patch_va__read_mostly ; +static struct microcode_intel *ucode_patch_late__read_mostly ;/* last level cache size per core */ static unsigned int llc_size_per_core__ro_after_init ;
@@ -454,12 +455,9 @@ static enum ucode_state apply_microcode_intel(int cpu)if (WARN_ON (raw_smp_processor_id () != cpu))return UCODE_ERROR ; - mc = ucode_patch_va; -if (!mc) { - mc = uci->mc; -if (!mc) -return UCODE_NFOUND ; - } + mc = ucode_patch_late; +if (!mc) +return UCODE_NFOUND ;/* * Save us the MSR write below - which is a particular expensive
@@ -578,15 +576,7 @@ static enum ucode_state parse_microcode_blobs(int cpu, struct iov_iter *iter)if (!new_mc)return UCODE_NFOUND ; -/* Save for CPU hotplug */ -save_microcode_patch ((struct microcode_intel *)new_mc); - uci->mc = ucode_patch_va; - -vfree (new_mc); - -pr_debug ("CPU%d found a matching microcode update with version 0x%x (current=0x%x)\n" , - cpu, cur_rev, uci->cpu_sig.rev); - + ucode_patch_late = (struct microcode_intel *)new_mc;return UCODE_NEW ; }
@@ -643,10 +633,20 @@ static enum ucode_state request_microcode_fw(int cpu, struct device *device)return ret; } +static void finalize_late_load (int result) +{ +if (!result) +save_microcode_patch (ucode_patch_late); + +vfree (ucode_patch_late); + ucode_patch_late =NULL ; +} +static struct microcode_ops microcode_intel_ops = { .request_microcode_fw = request_microcode_fw, .collect_cpu_info = collect_cpu_info, .apply_microcode = apply_microcode_intel, + .finalize_late_load = finalize_late_load, };static __init void calc_llc_size_per_core (struct cpuinfo_x86 *c)
arch/x86/kernel/cpu/microcode/internal.h +1 -0
@@ -31,6 +31,7 @@ struct microcode_ops {*/ enum ucode_state (*apply_microcode)(int cpu);int (*collect_cpu_info)(int cpu,struct cpu_signature *csig); +void (*finalize_late_load)(int result); };extern struct ucode_cpu_info ucode_cpu_info[];
[tip: x86/microcode] x86/microcode/intel: Save the microcode only after a successful late-load
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 2a1dada3d1cf8f80a27663653a371d99dbf5d540 Gitweb: https://git.kernel.org/tip/2a1dada3d1cf8f80a27663653a371d99dbf5d540 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:44 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Tue, 24 Oct 2023 15:05:53 +02:00
x86/microcode/intel: Save the microcode only after a successful late-load
There are situations where the late microcode is loaded into memory but is not applied:
1) The rendezvous fails
2) The microcode is rejected by the CPUs
If any of this happens then the pointer which was updated at firmware load time is stale and subsequent CPU hotplug operations either fail to update or create inconsistent microcode state.
Save the loaded microcode in a separate pointer before the late load is attempted and when successful, update the hotplug pointer accordingly via a new microcode_ops callback.
Remove the pointless fallback in the loader to a microcode pointer which is never populated.
--- arch/x86/kernel/cpu/microcode/core.c | 4 +++- arch/x86/kernel/cpu/microcode/intel.c | 30 +++++++++++------------ arch/x86/kernel/cpu/microcode/internal.h | 1 +- 3 files changed, 20 insertions(+), 15 deletions(-)
arch/x86/kernel/cpu/microcode/core.c +4 -0
@@ -397,6 +397,10 @@ static int microcode_reload_late(void)store_cpu_caps (&prev_info); ret =stop_machine_cpuslocked (__reload_late,NULL , cpu_online_mask); + +if (microcode_ops->finalize_late_load) + microcode_ops->finalize_late_load (ret); +if (!ret) {pr_info ("Reload succeeded, microcode revision: 0x%x -> 0x%x\n" , old, boot_cpu_data.microcode);
arch/x86/kernel/cpu/microcode/intel.c +15 -15
@@ -36,6 +36,7 @@ static const char ucode_path[] = "kernel/x86/microcode/GenuineIntel.bin";/* Current microcode patch used in early patching on the APs. */ static struct microcode_intel *ucode_patch_va__read_mostly ; +static struct microcode_intel *ucode_patch_late__read_mostly ;/* last level cache size per core */ static unsigned int llc_size_per_core__ro_after_init ;
@@ -470,12 +471,9 @@ static enum ucode_state apply_microcode_intel(int cpu)if (WARN_ON (raw_smp_processor_id () != cpu))return UCODE_ERROR ; - mc = ucode_patch_va; -if (!mc) { - mc = uci->mc; -if (!mc) -return UCODE_NFOUND ; - } + mc = ucode_patch_late; +if (!mc) +return UCODE_NFOUND ;/* * Save us the MSR write below - which is a particular expensive
@@ -594,15 +592,7 @@ static enum ucode_state parse_microcode_blobs(int cpu, struct iov_iter *iter)if (!new_mc)return UCODE_NFOUND ; -/* Save for CPU hotplug */ -save_microcode_patch ((struct microcode_intel *)new_mc); - uci->mc = ucode_patch_va; - -vfree (new_mc); - -pr_debug ("CPU%d found a matching microcode update with version 0x%x (current=0x%x)\n" , - cpu, cur_rev, uci->cpu_sig.rev); - + ucode_patch_late = (struct microcode_intel *)new_mc;return UCODE_NEW ; }
@@ -659,10 +649,20 @@ static enum ucode_state request_microcode_fw(int cpu, struct device *device)return ret; } +static void finalize_late_load (int result) +{ +if (!result) +save_microcode_patch (ucode_patch_late); + +vfree (ucode_patch_late); + ucode_patch_late =NULL ; +} +static struct microcode_ops microcode_intel_ops = { .request_microcode_fw = request_microcode_fw, .collect_cpu_info = collect_cpu_info, .apply_microcode = apply_microcode_intel, + .finalize_late_load = finalize_late_load, };static __init void calc_llc_size_per_core (struct cpuinfo_x86 *c)
arch/x86/kernel/cpu/microcode/internal.h +1 -0
@@ -31,6 +31,7 @@ struct microcode_ops {*/ enum ucode_state (*apply_microcode)(int cpu);int (*collect_cpu_info)(int cpu,struct cpu_signature *csig); +void (*finalize_late_load)(int result); };extern struct ucode_cpu_info ucode_cpu_info[];
PATCH v4 9/30 x86/microcode/intel: Switch to kvmalloc()
In reply to Thomas GleixnerFrom: Thomas Gleixner <[email protected]>
Microcode blobs are getting larger and might soon reach the kmalloc() limit. Switch over kvmalloc().
--- arch/x86/kernel/cpu/microcode/intel.c | 48 +++++++++++++++++----------------- 1 file changed, 25 insertions(+), 23 deletions(-)
---
arch/x86/kernel/cpu/microcode/intel.c +25 -23
@@ -14,7 +14,6 @@#include <linux/earlycpio.h> #include <linux/firmware.h> #include <linux/uaccess.h> -#include <linux/vmalloc.h> #include <linux/initrd.h> #include <linux/kernel.h> #include <linux/slab.h>
@@ -243,7 +242,7 @@ EXPORT_SYMBOL_GPL(intel_microcode_sanitystatic void update_ucode_pointer (struct microcode_intel *mc) { -kfree (ucode_patch_va); +kvfree (ucode_patch_va);/* * Save the virtual address for early loading and for eventual free
@@ -254,11 +253,14 @@ static void update_ucode_pointer(structstatic void save_microcode_patch (struct microcode_intel *patch) { +unsigned int size =get_totalsize (&patch->hdr);struct microcode_intel *mc; - mc =kmemdup (patch,get_totalsize (&patch->hdr),GFP_KERNEL ); + mc =kvmemdup (patch, size,GFP_KERNEL );if (mc)update_ucode_pointer (mc); +else +pr_err ("Unable to allocate microcode memory size: %u\n" , size); }/* Scan blob for microcode matching the boot CPUs family, model, stepping */
@@ -530,36 +532,34 @@ static enum ucode_state parse_microcode_if (!copy_from_iter_full (&mc_header,sizeof (mc_header), iter)) {pr_err ("error! Truncated or inaccessible header in microcode data file\n" ); -break ; + goto fail; } mc_size =get_totalsize (&mc_header);if (mc_size <sizeof (mc_header)) {pr_err ("error! Bad data in microcode data file (totalsize too small)\n" ); -break ; + goto fail; } - data_size = mc_size -sizeof (mc_header);if (data_size >iov_iter_count (iter)) {pr_err ("error! Bad data in microcode data file (truncated file?)\n" ); -break ; + goto fail; }/* For performance reasons, reuse mc area when possible */ if (!mc || mc_size > curr_mc_size) { -vfree (mc); - mc =vmalloc (mc_size); +kvfree (mc); + mc =kvmalloc (mc_size,GFP_KERNEL );if (!mc) -break ; + goto fail; curr_mc_size = mc_size; }memcpy (mc, &mc_header,sizeof (mc_header)); data = mc +sizeof (mc_header);if (!copy_from_iter_full (data, data_size, iter) || -intel_microcode_sanity_check (mc, true,MC_HEADER_TYPE_MICROCODE ) <0 ) { -break ; - } +intel_microcode_sanity_check (mc, true,MC_HEADER_TYPE_MICROCODE ) <0 ) + goto fail;if (cur_rev >= mc_header.rev)continue ;
@@ -567,24 +567,26 @@ static enum ucode_state parse_microcode_if (!intel_find_matching_signature (mc, uci->cpu_sig.sig, uci->cpu_sig.pf))continue ; -vfree (new_mc); +kvfree (new_mc); cur_rev = mc_header.rev; new_mc = mc; mc =NULL ; } -vfree (mc); - -if (iov_iter_count (iter)) { -vfree (new_mc); -return UCODE_ERROR ; - } +if (iov_iter_count (iter)) + goto fail; +kvfree (mc);if (!new_mc)return UCODE_NFOUND ; ucode_patch_late = (struct microcode_intel *)new_mc;return UCODE_NEW ; + +fail: +kvfree (mc); +kvfree (new_mc); +return UCODE_ERROR ; }static bool is_blacklisted (unsigned int cpu)
@@ -643,9 +645,9 @@ static enum ucode_state request_microcodstatic void finalize_late_load (int result) {if (!result) -save_microcode_patch (ucode_patch_late); - -vfree (ucode_patch_late); +update_ucode_pointer (ucode_patch_late); +else +kvfree (ucode_patch_late); ucode_patch_late =NULL ; }
[tip: x86/microcode] x86/microcode/intel: Switch to kvmalloc()
The following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 2ef5bac8c869ac24f51c94df1ae6d23c7679d905 Gitweb: https://git.kernel.org/tip/2ef5bac8c869ac24f51c94df1ae6d23c7679d905 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:45 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Tue, 03 Oct 2023 15:59:30 +02:00
x86/microcode/intel: Switch to kvmalloc()
Microcode blobs are getting larger and might soon reach the kmalloc() limit. Switch over kvmalloc().
--- arch/x86/kernel/cpu/microcode/intel.c | 48 +++++++++++++------------- 1 file changed, 25 insertions(+), 23 deletions(-)
arch/x86/kernel/cpu/microcode/intel.c +25 -23
@@ -14,7 +14,6 @@#include <linux/earlycpio.h> #include <linux/firmware.h> #include <linux/uaccess.h> -#include <linux/vmalloc.h> #include <linux/initrd.h> #include <linux/kernel.h> #include <linux/slab.h>
@@ -243,7 +242,7 @@ EXPORT_SYMBOL_GPL(intel_microcode_sanity_check);static void update_ucode_pointer (struct microcode_intel *mc) { -kfree (ucode_patch_va); +kvfree (ucode_patch_va);/* * Save the virtual address for early loading and for eventual free
@@ -254,11 +253,14 @@ static void update_ucode_pointer(struct microcode_intel *mc)static void save_microcode_patch (struct microcode_intel *patch) { +unsigned int size =get_totalsize (&patch->hdr);struct microcode_intel *mc; - mc =kmemdup (patch,get_totalsize (&patch->hdr),GFP_KERNEL ); + mc =kvmemdup (patch, size,GFP_KERNEL );if (mc)update_ucode_pointer (mc); +else +pr_err ("Unable to allocate microcode memory size: %u\n" , size); }/* Scan blob for microcode matching the boot CPUs family, model, stepping */
@@ -530,36 +532,34 @@ static enum ucode_state parse_microcode_blobs(int cpu, struct iov_iter *iter)if (!copy_from_iter_full (&mc_header,sizeof (mc_header), iter)) {pr_err ("error! Truncated or inaccessible header in microcode data file\n" ); -break ; + goto fail; } mc_size =get_totalsize (&mc_header);if (mc_size <sizeof (mc_header)) {pr_err ("error! Bad data in microcode data file (totalsize too small)\n" ); -break ; + goto fail; } - data_size = mc_size -sizeof (mc_header);if (data_size >iov_iter_count (iter)) {pr_err ("error! Bad data in microcode data file (truncated file?)\n" ); -break ; + goto fail; }/* For performance reasons, reuse mc area when possible */ if (!mc || mc_size > curr_mc_size) { -vfree (mc); - mc =vmalloc (mc_size); +kvfree (mc); + mc =kvmalloc (mc_size,GFP_KERNEL );if (!mc) -break ; + goto fail; curr_mc_size = mc_size; }memcpy (mc, &mc_header,sizeof (mc_header)); data = mc +sizeof (mc_header);if (!copy_from_iter_full (data, data_size, iter) || -intel_microcode_sanity_check (mc, true,MC_HEADER_TYPE_MICROCODE ) <0 ) { -break ; - } +intel_microcode_sanity_check (mc, true,MC_HEADER_TYPE_MICROCODE ) <0 ) + goto fail;if (cur_rev >= mc_header.rev)continue ;
@@ -567,24 +567,26 @@ static enum ucode_state parse_microcode_blobs(int cpu, struct iov_iter *iter)if (!intel_find_matching_signature (mc, uci->cpu_sig.sig, uci->cpu_sig.pf))continue ; -vfree (new_mc); +kvfree (new_mc); cur_rev = mc_header.rev; new_mc = mc; mc =NULL ; } -vfree (mc); - -if (iov_iter_count (iter)) { -vfree (new_mc); -return UCODE_ERROR ; - } +if (iov_iter_count (iter)) + goto fail; +kvfree (mc);if (!new_mc)return UCODE_NFOUND ; ucode_patch_late = (struct microcode_intel *)new_mc;return UCODE_NEW ; + +fail: +kvfree (mc); +kvfree (new_mc); +return UCODE_ERROR ; }static bool is_blacklisted (unsigned int cpu)
@@ -643,9 +645,9 @@ static enum ucode_state request_microcode_fw(int cpu, struct device *device)static void finalize_late_load (int result) {if (!result) -save_microcode_patch (ucode_patch_late); - -vfree (ucode_patch_late); +update_ucode_pointer (ucode_patch_late); +else +kvfree (ucode_patch_late); ucode_patch_late =NULL ; }
[tip: x86/microcode] x86/microcode/intel: Switch to kvmalloc()
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: afa939303a913c522152617563152e7387ee7b26 Gitweb: https://git.kernel.org/tip/afa939303a913c522152617563152e7387ee7b26 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:45 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Thu, 19 Oct 2023 14:22:54 +02:00
x86/microcode/intel: Switch to kvmalloc()
Microcode blobs are getting larger and might soon reach the kmalloc() limit. Switch over kvmalloc().
--- arch/x86/kernel/cpu/microcode/intel.c | 48 +++++++++++++------------- 1 file changed, 25 insertions(+), 23 deletions(-)
arch/x86/kernel/cpu/microcode/intel.c +25 -23
@@ -14,7 +14,6 @@#include <linux/earlycpio.h> #include <linux/firmware.h> #include <linux/uaccess.h> -#include <linux/vmalloc.h> #include <linux/initrd.h> #include <linux/kernel.h> #include <linux/slab.h>
@@ -243,7 +242,7 @@ EXPORT_SYMBOL_GPL(intel_microcode_sanity_check);static void update_ucode_pointer (struct microcode_intel *mc) { -kfree (ucode_patch_va); +kvfree (ucode_patch_va);/* * Save the virtual address for early loading and for eventual free
@@ -254,11 +253,14 @@ static void update_ucode_pointer(struct microcode_intel *mc)static void save_microcode_patch (struct microcode_intel *patch) { +unsigned int size =get_totalsize (&patch->hdr);struct microcode_intel *mc; - mc =kmemdup (patch,get_totalsize (&patch->hdr),GFP_KERNEL ); + mc =kvmemdup (patch, size,GFP_KERNEL );if (mc)update_ucode_pointer (mc); +else +pr_err ("Unable to allocate microcode memory size: %u\n" , size); }/* Scan blob for microcode matching the boot CPUs family, model, stepping */
@@ -523,36 +525,34 @@ static enum ucode_state parse_microcode_blobs(int cpu, struct iov_iter *iter)if (!copy_from_iter_full (&mc_header,sizeof (mc_header), iter)) {pr_err ("error! Truncated or inaccessible header in microcode data file\n" ); -break ; + goto fail; } mc_size =get_totalsize (&mc_header);if (mc_size <sizeof (mc_header)) {pr_err ("error! Bad data in microcode data file (totalsize too small)\n" ); -break ; + goto fail; } - data_size = mc_size -sizeof (mc_header);if (data_size >iov_iter_count (iter)) {pr_err ("error! Bad data in microcode data file (truncated file?)\n" ); -break ; + goto fail; }/* For performance reasons, reuse mc area when possible */ if (!mc || mc_size > curr_mc_size) { -vfree (mc); - mc =vmalloc (mc_size); +kvfree (mc); + mc =kvmalloc (mc_size,GFP_KERNEL );if (!mc) -break ; + goto fail; curr_mc_size = mc_size; }memcpy (mc, &mc_header,sizeof (mc_header)); data = mc +sizeof (mc_header);if (!copy_from_iter_full (data, data_size, iter) || -intel_microcode_sanity_check (mc, true,MC_HEADER_TYPE_MICROCODE ) <0 ) { -break ; - } +intel_microcode_sanity_check (mc, true,MC_HEADER_TYPE_MICROCODE ) <0 ) + goto fail;if (cur_rev >= mc_header.rev)continue ;
@@ -560,24 +560,26 @@ static enum ucode_state parse_microcode_blobs(int cpu, struct iov_iter *iter)if (!intel_find_matching_signature (mc, uci->cpu_sig.sig, uci->cpu_sig.pf))continue ; -vfree (new_mc); +kvfree (new_mc); cur_rev = mc_header.rev; new_mc = mc; mc =NULL ; } -vfree (mc); - -if (iov_iter_count (iter)) { -vfree (new_mc); -return UCODE_ERROR ; - } +if (iov_iter_count (iter)) + goto fail; +kvfree (mc);if (!new_mc)return UCODE_NFOUND ; ucode_patch_late = (struct microcode_intel *)new_mc;return UCODE_NEW ; + +fail: +kvfree (mc); +kvfree (new_mc); +return UCODE_ERROR ; }static bool is_blacklisted (unsigned int cpu)
@@ -636,9 +638,9 @@ static enum ucode_state request_microcode_fw(int cpu, struct device *device)static void finalize_late_load (int result) {if (!result) -save_microcode_patch (ucode_patch_late); - -vfree (ucode_patch_late); +update_ucode_pointer (ucode_patch_late); +else +kvfree (ucode_patch_late); ucode_patch_late =NULL ; }
[tip: x86/microcode] x86/microcode/intel: Switch to kvmalloc()
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: f24f204405f9875bc539c6e88553fd5ac913c867 Gitweb: https://git.kernel.org/tip/f24f204405f9875bc539c6e88553fd5ac913c867 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:45 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Tue, 24 Oct 2023 15:05:53 +02:00
x86/microcode/intel: Switch to kvmalloc()
Microcode blobs are getting larger and might soon reach the kmalloc() limit. Switch over kvmalloc().
--- arch/x86/kernel/cpu/microcode/intel.c | 48 +++++++++++++------------- 1 file changed, 25 insertions(+), 23 deletions(-)
arch/x86/kernel/cpu/microcode/intel.c +25 -23
@@ -14,7 +14,6 @@#include <linux/earlycpio.h> #include <linux/firmware.h> #include <linux/uaccess.h> -#include <linux/vmalloc.h> #include <linux/initrd.h> #include <linux/kernel.h> #include <linux/slab.h>
@@ -245,7 +244,7 @@ EXPORT_SYMBOL_GPL(intel_microcode_sanity_check);static void update_ucode_pointer (struct microcode_intel *mc) { -kfree (ucode_patch_va); +kvfree (ucode_patch_va);/* * Save the virtual address for early loading and for eventual free
@@ -256,11 +255,14 @@ static void update_ucode_pointer(struct microcode_intel *mc)static void save_microcode_patch (struct microcode_intel *patch) { +unsigned int size =get_totalsize (&patch->hdr);struct microcode_intel *mc; - mc =kmemdup (patch,get_totalsize (&patch->hdr),GFP_KERNEL ); + mc =kvmemdup (patch, size,GFP_KERNEL );if (mc)update_ucode_pointer (mc); +else +pr_err ("Unable to allocate microcode memory size: %u\n" , size); }/* Scan blob for microcode matching the boot CPUs family, model, stepping */
@@ -539,36 +541,34 @@ static enum ucode_state parse_microcode_blobs(int cpu, struct iov_iter *iter)if (!copy_from_iter_full (&mc_header,sizeof (mc_header), iter)) {pr_err ("error! Truncated or inaccessible header in microcode data file\n" ); -break ; + goto fail; } mc_size =get_totalsize (&mc_header);if (mc_size <sizeof (mc_header)) {pr_err ("error! Bad data in microcode data file (totalsize too small)\n" ); -break ; + goto fail; } - data_size = mc_size -sizeof (mc_header);if (data_size >iov_iter_count (iter)) {pr_err ("error! Bad data in microcode data file (truncated file?)\n" ); -break ; + goto fail; }/* For performance reasons, reuse mc area when possible */ if (!mc || mc_size > curr_mc_size) { -vfree (mc); - mc =vmalloc (mc_size); +kvfree (mc); + mc =kvmalloc (mc_size,GFP_KERNEL );if (!mc) -break ; + goto fail; curr_mc_size = mc_size; }memcpy (mc, &mc_header,sizeof (mc_header)); data = mc +sizeof (mc_header);if (!copy_from_iter_full (data, data_size, iter) || -intel_microcode_sanity_check (mc, true,MC_HEADER_TYPE_MICROCODE ) <0 ) { -break ; - } +intel_microcode_sanity_check (mc, true,MC_HEADER_TYPE_MICROCODE ) <0 ) + goto fail;if (cur_rev >= mc_header.rev)continue ;
@@ -576,24 +576,26 @@ static enum ucode_state parse_microcode_blobs(int cpu, struct iov_iter *iter)if (!intel_find_matching_signature (mc, uci->cpu_sig.sig, uci->cpu_sig.pf))continue ; -vfree (new_mc); +kvfree (new_mc); cur_rev = mc_header.rev; new_mc = mc; mc =NULL ; } -vfree (mc); - -if (iov_iter_count (iter)) { -vfree (new_mc); -return UCODE_ERROR ; - } +if (iov_iter_count (iter)) + goto fail; +kvfree (mc);if (!new_mc)return UCODE_NFOUND ; ucode_patch_late = (struct microcode_intel *)new_mc;return UCODE_NEW ; + +fail: +kvfree (mc); +kvfree (new_mc); +return UCODE_ERROR ; }static bool is_blacklisted (unsigned int cpu)
@@ -652,9 +654,9 @@ static enum ucode_state request_microcode_fw(int cpu, struct device *device)static void finalize_late_load (int result) {if (!result) -save_microcode_patch (ucode_patch_late); - -vfree (ucode_patch_late); +update_ucode_pointer (ucode_patch_late); +else +kvfree (ucode_patch_late); ucode_patch_late =NULL ; }
PATCH v4 10/30 x86/microcode/intel: Unify microcode apply() functions
In reply to Thomas GleixnerDeduplicate the early and late apply() functions.
--- arch/x86/kernel/cpu/microcode/intel.c | 105 +++++++++++----------------------- 1 file changed, 36 insertions(+), 69 deletions(-)
arch/x86/kernel/cpu/microcode/intel.c +36 -69
@@ -294,12 +294,11 @@ static __init struct microcode_intel *screturn size ?NULL : patch; } -static enum ucode_state apply_microcode_early (struct ucode_cpu_info *uci) +static enum ucode_state apply_microcode (struct ucode_cpu_info *uci,struct microcode_intel *mc, +u32 *cur_rev) { -struct microcode_intel *mc; -u32 rev, old_rev, date; +u32 rev; - mc = uci->mc;if (!mc)return UCODE_NFOUND ;
@@ -308,14 +307,12 @@ static enum ucode_state apply_microcode_* operation - when the other hyperthread has updated the microcode * already. */ - rev =intel_get_microcode_revision (); -if (rev >= mc->hdr.rev) { - uci->cpu_sig.rev = rev; + *cur_rev =intel_get_microcode_revision (); +if (*cur_rev >= mc->hdr.rev) { + uci->cpu_sig.rev = *cur_rev;return UCODE_OK ; } - old_rev = rev; -/* * Writeback and invalidate caches before updating microcode to avoid * internal issues depending on what the microcode is updating.
@@ -330,13 +327,24 @@ static enum ucode_state apply_microcode_return UCODE_ERROR ; uci->cpu_sig.rev = rev; - - date = mc->hdr.date; -pr_info_once ("updated early: 0x%x -> 0x%x, date = %04x-%02x-%02x\n" , - old_rev, rev, date &0xffff , date >>24 , (date >>16 ) &0xff );return UCODE_UPDATED ; } +static enum ucode_state apply_microcode_early (struct ucode_cpu_info *uci) +{ +struct microcode_intel *mc = uci->mc; +enum ucode_state ret; +u32 cur_rev, date; + + ret =apply_microcode (uci, mc, &cur_rev); +if (ret ==UCODE_UPDATED ) { + date = mc->hdr.date; +pr_info_once ("updated early: 0x%x -> 0x%x, date = %04x-%02x-%02x\n" , + cur_rev, mc->hdr.rev, date &0xffff , date >>24 , (date >>16 ) &0xff ); + } +return ret; +} +static __init bool load_builtin_intel_microcode (struct cpio_data *cp) {unsigned int eax =1 , ebx, ecx =0 , edx;
@@ -450,70 +458,29 @@ static int collect_cpu_info(int cpu_num,return 0 ; } -static enum ucode_state apply_microcode_intel (int cpu) +static enum ucode_state apply_microcode_late (int cpu) {struct ucode_cpu_info *uci = ucode_cpu_info + cpu; -struct cpuinfo_x86 *c = &cpu_data (cpu); -bool bsp = c->cpu_index == boot_cpu_data.cpu_index; -struct microcode_intel *mc; +struct microcode_intel *mc = ucode_patch_late;enum ucode_state ret; -static int prev_rev; -u32 rev; - -/* We should bind the task to the CPU */ -if (WARN_ON (raw_smp_processor_id () != cpu)) -return UCODE_ERROR ; - - mc = ucode_patch_late; -if (!mc) -return UCODE_NFOUND ; +u32 cur_rev; -/* -* Save us the MSR write below - which is a particular expensive -* operation - when the other hyperthread has updated the microcode -* already. -*/ - rev =intel_get_microcode_revision (); -if (rev >= mc->hdr.rev) { - ret =UCODE_OK ; - goto out; - } - -/* -* Writeback and invalidate caches before updating microcode to avoid -* internal issues depending on what the microcode is updating. -*/ -native_wbinvd (); - -/* write microcode via MSR 0x79 */ -wrmsrl (MSR_IA32_UCODE_WRITE , (unsigned long )mc->bits); - - rev =intel_get_microcode_revision (); - -if (rev != mc->hdr.rev) { -pr_err ("CPU%d update to revision 0x%x failed\n" , - cpu, mc->hdr.rev); +if (WARN_ON_ONCE (smp_processor_id () != cpu))return UCODE_ERROR ; - } -if (bsp && rev != prev_rev) { -pr_info ("updated to revision 0x%x, date = %04x-%02x-%02x\n" , - rev, - mc->hdr.date &0xffff , - mc->hdr.date >>24 , + ret =apply_microcode (uci, mc, &cur_rev); +if (ret !=UCODE_UPDATED && ret !=UCODE_OK ) +return ret; + +if (!cpu && uci->cpu_sig.rev != cur_rev) { +pr_info ("Updated to revision 0x%x, date = %04x-%02x-%02x\n" , + uci->cpu_sig.rev, mc->hdr.date &0xffff , mc->hdr.date >>24 , (mc->hdr.date >>16 ) &0xff ); - prev_rev = rev; } - ret =UCODE_UPDATED ; - -out: - uci->cpu_sig.rev = rev; - c->microcode = rev; - -/* Update boot_cpu_data's revision too, if we're on the BSP: */ -if (bsp) - boot_cpu_data.microcode = rev; +cpu_data (cpu).microcode = uci->cpu_sig.rev; +if (!cpu) + boot_cpu_data.microcode = uci->cpu_sig.rev;return ret; }
@@ -654,7 +621,7 @@ static void finalize_late_load(int resulstatic struct microcode_ops microcode_intel_ops = { .request_microcode_fw = request_microcode_fw, .collect_cpu_info = collect_cpu_info, - .apply_microcode = apply_microcode_intel, + .apply_microcode = apply_microcode_late, .finalize_late_load = finalize_late_load, };
[tip: x86/microcode] x86/microcode/intel: Unify microcode apply() functions
The following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 1542922ce22a1da1a8b70b1d6e79cc92d229f061 Gitweb: https://git.kernel.org/tip/1542922ce22a1da1a8b70b1d6e79cc92d229f061 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:46 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Tue, 03 Oct 2023 16:03:40 +02:00
x86/microcode/intel: Unify microcode apply() functions
Deduplicate the early and late apply() functions.
[ bp: Rename the function which does the actual application to
__apply_microcode() to differentiate it from microcode_ops.apply_microcode(). ]
--- arch/x86/kernel/cpu/microcode/intel.c | 104 ++++++++----------------- 1 file changed, 36 insertions(+), 68 deletions(-)
arch/x86/kernel/cpu/microcode/intel.c +36 -68
@@ -294,12 +294,12 @@ static __init struct microcode_intel *scan_microcode(void *data, size_t size,return size ?NULL : patch; } -static enum ucode_state apply_microcode_early (struct ucode_cpu_info *uci) +static enum ucode_state __apply_microcode (struct ucode_cpu_info *uci, +struct microcode_intel *mc, +u32 *cur_rev) { -struct microcode_intel *mc; -u32 rev, old_rev, date; +u32 rev; - mc = uci->mc;if (!mc)return UCODE_NFOUND ;
@@ -308,14 +308,12 @@ static enum ucode_state apply_microcode_early(struct ucode_cpu_info *uci)* operation - when the other hyperthread has updated the microcode * already. */ - rev =intel_get_microcode_revision (); -if (rev >= mc->hdr.rev) { - uci->cpu_sig.rev = rev; + *cur_rev =intel_get_microcode_revision (); +if (*cur_rev >= mc->hdr.rev) { + uci->cpu_sig.rev = *cur_rev;return UCODE_OK ; } - old_rev = rev; -/* * Writeback and invalidate caches before updating microcode to avoid * internal issues depending on what the microcode is updating.
@@ -330,13 +328,24 @@ static enum ucode_state apply_microcode_early(struct ucode_cpu_info *uci)return UCODE_ERROR ; uci->cpu_sig.rev = rev; - - date = mc->hdr.date; -pr_info_once ("updated early: 0x%x -> 0x%x, date = %04x-%02x-%02x\n" , - old_rev, rev, date &0xffff , date >>24 , (date >>16 ) &0xff );return UCODE_UPDATED ; } +static enum ucode_state apply_microcode_early (struct ucode_cpu_info *uci) +{ +struct microcode_intel *mc = uci->mc; +enum ucode_state ret; +u32 cur_rev, date; + + ret =__apply_microcode (uci, mc, &cur_rev); +if (ret ==UCODE_UPDATED ) { + date = mc->hdr.date; +pr_info_once ("updated early: 0x%x -> 0x%x, date = %04x-%02x-%02x\n" , + cur_rev, mc->hdr.rev, date &0xffff , date >>24 , (date >>16 ) &0xff ); + } +return ret; +} +static __init bool load_builtin_intel_microcode (struct cpio_data *cp) {unsigned int eax =1 , ebx, ecx =0 , edx;
@@ -450,70 +459,29 @@ static int collect_cpu_info(int cpu_num, struct cpu_signature *csig)return 0 ; } -static enum ucode_state apply_microcode_intel (int cpu) +static enum ucode_state apply_microcode_late (int cpu) {struct ucode_cpu_info *uci = ucode_cpu_info + cpu; -struct cpuinfo_x86 *c = &cpu_data (cpu); -bool bsp = c->cpu_index == boot_cpu_data.cpu_index; -struct microcode_intel *mc; +struct microcode_intel *mc = ucode_patch_late;enum ucode_state ret; -static int prev_rev; -u32 rev; +u32 cur_rev; -/* We should bind the task to the CPU */ -if (WARN_ON (raw_smp_processor_id () != cpu)) +if (WARN_ON_ONCE (smp_processor_id () != cpu))return UCODE_ERROR ; - mc = ucode_patch_late; -if (!mc) -return UCODE_NFOUND ; + ret =__apply_microcode (uci, mc, &cur_rev); +if (ret !=UCODE_UPDATED && ret !=UCODE_OK ) +return ret; -/* -* Save us the MSR write below - which is a particular expensive -* operation - when the other hyperthread has updated the microcode -* already. -*/ - rev =intel_get_microcode_revision (); -if (rev >= mc->hdr.rev) { - ret =UCODE_OK ; - goto out; - } - -/* -* Writeback and invalidate caches before updating microcode to avoid -* internal issues depending on what the microcode is updating. -*/ -native_wbinvd (); - -/* write microcode via MSR 0x79 */ -wrmsrl (MSR_IA32_UCODE_WRITE , (unsigned long )mc->bits); - - rev =intel_get_microcode_revision (); - -if (rev != mc->hdr.rev) { -pr_err ("CPU%d update to revision 0x%x failed\n" , - cpu, mc->hdr.rev); -return UCODE_ERROR ; - } - -if (bsp && rev != prev_rev) { -pr_info ("updated to revision 0x%x, date = %04x-%02x-%02x\n" , - rev, - mc->hdr.date &0xffff , - mc->hdr.date >>24 , +if (!cpu && uci->cpu_sig.rev != cur_rev) { +pr_info ("Updated to revision 0x%x, date = %04x-%02x-%02x\n" , + uci->cpu_sig.rev, mc->hdr.date &0xffff , mc->hdr.date >>24 , (mc->hdr.date >>16 ) &0xff ); - prev_rev = rev; } - ret =UCODE_UPDATED ; - -out: - uci->cpu_sig.rev = rev; - c->microcode = rev; - -/* Update boot_cpu_data's revision too, if we're on the BSP: */ -if (bsp) - boot_cpu_data.microcode = rev; +cpu_data (cpu).microcode = uci->cpu_sig.rev; +if (!cpu) + boot_cpu_data.microcode = uci->cpu_sig.rev;return ret; }
@@ -654,7 +622,7 @@ static void finalize_late_load(int result)static struct microcode_ops microcode_intel_ops = { .request_microcode_fw = request_microcode_fw, .collect_cpu_info = collect_cpu_info, - .apply_microcode = apply_microcode_intel, + .apply_microcode = apply_microcode_late, .finalize_late_load = finalize_late_load, };
PATCH v4 11/30 x86/microcode/intel: Rework intel_cpu_collect_info()
In reply to Thomas GleixnerNothing needs struct ucode_cpu_info. Make it take struct cpu_signature, let it return a boolean and simplify the implementation. Rename it now that the silly name clash with collect_cpu_info() is gone.
---
V2: New patch
--- arch/x86/include/asm/cpu.h | 4 ++-- arch/x86/kernel/cpu/microcode/intel.c | 33 +++++++++------------------------ drivers/platform/x86/intel/ifs/load.c | 8 +++----- 3 files changed, 14 insertions(+), 31 deletions(-)
arch/x86/include/asm/cpu.h +2 -2
@@ -73,9 +73,9 @@ static inline void init_ia32_feat_ctl(st extern __noendbr void cet_disable(void); -struct ucode_cpu_info ; +struct cpu_signature ; -int intel_cpu_collect_info (struct ucode_cpu_info *uci); +void intel_collect_cpu_info (struct cpu_signature *sig);static inline bool intel_cpu_signatures_match (unsigned int s1,unsigned int p1,unsigned int s2,unsigned int p2)
arch/x86/kernel/cpu/microcode/intel.c +9 -24
@@ -66,36 +66,21 @@ static inline unsigned int exttable_sizereturn et->count *EXT_SIGNATURE_SIZE +EXT_HEADER_SIZE ; } -int intel_cpu_collect_info (struct ucode_cpu_info *uci) +void intel_collect_cpu_info (struct cpu_signature *sig) { -unsigned int val[2 ]; -unsigned int family, model; -struct cpu_signature csig = {0 }; -unsigned int eax, ebx, ecx, edx; + sig->sig =cpuid_eax (1 ); + sig->pf =0 ; + sig->rev =intel_get_microcode_revision (); -memset (uci,0 ,sizeof (*uci)); +if (x86_model (sig->sig) >=5 ||x86_family (sig->sig) >6 ) { +unsigned int val[2 ]; - eax =0x00000001 ; - ecx =0 ; -native_cpuid (&eax, &ebx, &ecx, &edx); - csig.sig = eax; - - family =x86_family (eax); - model =x86_model (eax); - -if (model >=5 || family >6 ) {/* get processor flags from MSR 0x17 */ native_rdmsr (MSR_IA32_PLATFORM_ID , val[0 ], val[1 ]); - csig.pf =1 << ((val[1 ] >>18 ) &7 ); + sig->pf =1 << ((val[1 ] >>18 ) &7 ); } - - csig.rev =intel_get_microcode_revision (); - - uci->cpu_sig = csig; - -return 0 ; } -EXPORT_SYMBOL_GPL (intel_cpu_collect_info); +EXPORT_SYMBOL_GPL (intel_collect_cpu_info);/* * Returns 1 if update has been found, 0 otherwise.
@@ -377,7 +362,7 @@ static __init struct microcode_intel *geif (!(cp.data && cp.size))return NULL ; -intel_cpu_collect_info (uci); +intel_collect_cpu_info (&uci->cpu_sig);return scan_microcode (cp.data, cp.size, uci); }
drivers/platform/x86/intel/ifs/load.c +3 -5
@@ -227,7 +227,7 @@ static int scan_chunks_sanity_check(strustatic int image_sanity_check (struct device *dev,const struct microcode_header_intel *data) { -struct ucode_cpu_info uci; +struct cpu_signature sig;/* Provide a specific error message when loading an older/unsupported image */ if (data->hdrver !=MC_HEADER_TYPE_IFS ) {
@@ -240,11 +240,9 @@ static int image_sanity_check(struct devreturn -EINVAL ; } -intel_cpu_collect_info (&uci); +intel_collect_cpu_info (&sig); -if (!intel_find_matching_signature ((void *)data, - uci.cpu_sig.sig, - uci.cpu_sig.pf)) { +if (!intel_find_matching_signature ((void *)data, sig.sig, sig.pf)) {dev_err (dev,"cpu signature, processor flags not matching\n" );return -EINVAL ; }
[tip: x86/microcode] x86/microcode/intel: Rework intel_cpu_collect_info()
The following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 38ab73d770143460b1b85d01ead39cd6eeb857c8 Gitweb: https://git.kernel.org/tip/38ab73d770143460b1b85d01ead39cd6eeb857c8 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:47 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Tue, 03 Oct 2023 16:06:07 +02:00
x86/microcode/intel: Rework intel_cpu_collect_info()
Nothing needs struct ucode_cpu_info. Make it take struct cpu_signature, let it return a boolean and simplify the implementation. Rename it now that the silly name clash with collect_cpu_info() is gone.
--- arch/x86/include/asm/cpu.h | 4 +-- arch/x86/kernel/cpu/microcode/intel.c | 33 +++++++------------------- drivers/platform/x86/intel/ifs/load.c | 8 ++---- 3 files changed, 14 insertions(+), 31 deletions(-)
arch/x86/include/asm/cpu.h +2 -2
@@ -73,9 +73,9 @@ static inline void init_ia32_feat_ctl(struct cpuinfo_x86 *c) {} extern __noendbr void cet_disable(void); -struct ucode_cpu_info ; +struct cpu_signature ; -int intel_cpu_collect_info (struct ucode_cpu_info *uci); +void intel_collect_cpu_info (struct cpu_signature *sig);static inline bool intel_cpu_signatures_match (unsigned int s1,unsigned int p1,unsigned int s2,unsigned int p2)
arch/x86/kernel/cpu/microcode/intel.c +9 -24
@@ -66,36 +66,21 @@ static inline unsigned int exttable_size(struct extended_sigtable *et)return et->count *EXT_SIGNATURE_SIZE +EXT_HEADER_SIZE ; } -int intel_cpu_collect_info (struct ucode_cpu_info *uci) +void intel_collect_cpu_info (struct cpu_signature *sig) { -unsigned int val[2 ]; -unsigned int family, model; -struct cpu_signature csig = {0 }; -unsigned int eax, ebx, ecx, edx; - -memset (uci,0 ,sizeof (*uci)); - - eax =0x00000001 ; - ecx =0 ; -native_cpuid (&eax, &ebx, &ecx, &edx); - csig.sig = eax; + sig->sig =cpuid_eax (1 ); + sig->pf =0 ; + sig->rev =intel_get_microcode_revision (); - family =x86_family (eax); - model =x86_model (eax); +if (x86_model (sig->sig) >=5 ||x86_family (sig->sig) >6 ) { +unsigned int val[2 ]; -if (model >=5 || family >6 ) {/* get processor flags from MSR 0x17 */ native_rdmsr (MSR_IA32_PLATFORM_ID , val[0 ], val[1 ]); - csig.pf =1 << ((val[1 ] >>18 ) &7 ); + sig->pf =1 << ((val[1 ] >>18 ) &7 ); } - - csig.rev =intel_get_microcode_revision (); - - uci->cpu_sig = csig; - -return 0 ; } -EXPORT_SYMBOL_GPL (intel_cpu_collect_info); +EXPORT_SYMBOL_GPL (intel_collect_cpu_info);/* * Returns 1 if update has been found, 0 otherwise.
@@ -378,7 +363,7 @@ static __init struct microcode_intel *get_microcode_blob(struct ucode_cpu_info *if (!(cp.data && cp.size))return NULL ; -intel_cpu_collect_info (uci); +intel_collect_cpu_info (&uci->cpu_sig);return scan_microcode (cp.data, cp.size, uci); }
drivers/platform/x86/intel/ifs/load.c +3 -5
@@ -227,7 +227,7 @@ out:static int image_sanity_check (struct device *dev,const struct microcode_header_intel *data) { -struct ucode_cpu_info uci; +struct cpu_signature sig;/* Provide a specific error message when loading an older/unsupported image */ if (data->hdrver !=MC_HEADER_TYPE_IFS ) {
@@ -240,11 +240,9 @@ static int image_sanity_check(struct device *dev, const struct microcode_header_return -EINVAL ; } -intel_cpu_collect_info (&uci); +intel_collect_cpu_info (&sig); -if (!intel_find_matching_signature ((void *)data, - uci.cpu_sig.sig, - uci.cpu_sig.pf)) { +if (!intel_find_matching_signature ((void *)data, sig.sig, sig.pf)) {dev_err (dev,"cpu signature, processor flags not matching\n" );return -EINVAL ; }
PATCH v4 12/30 x86/microcode/intel: Reuse intel_cpu_collect_info()
In reply to Thomas GleixnerNo point for an almost duplicate function.
---
V2: New patch
--- arch/x86/kernel/cpu/microcode/intel.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-)
arch/x86/kernel/cpu/microcode/intel.c +1 -15
@@ -425,21 +425,7 @@ void reload_ucode_intel(void)static int collect_cpu_info (int cpu_num,struct cpu_signature *csig) { -struct cpuinfo_x86 *c = &cpu_data (cpu_num); -unsigned int val[2 ]; - -memset (csig,0 ,sizeof (*csig)); - - csig->sig =cpuid_eax (0x00000001 ); - -if ((c->x86_model >=5 ) || (c->x86 >6 )) { -/* get processor flags from MSR 0x17 */ -rdmsr (MSR_IA32_PLATFORM_ID , val[0 ], val[1 ]); - csig->pf =1 << ((val[1 ] >>18 ) &7 ); - } - - csig->rev = c->microcode; - +intel_collect_cpu_info (csig);return 0 ; }
[tip: x86/microcode] x86/microcode/intel: Reuse intel_cpu_collect_info()
The following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: ac39a61afe4f452fa9dab568b810f6c410554242 Gitweb: https://git.kernel.org/tip/ac39a61afe4f452fa9dab568b810f6c410554242 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:49 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Tue, 03 Oct 2023 16:08:19 +02:00
x86/microcode/intel: Reuse intel_cpu_collect_info()
No point for an almost duplicate function.
--- arch/x86/kernel/cpu/microcode/intel.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-)
arch/x86/kernel/cpu/microcode/intel.c +1 -15
@@ -426,21 +426,7 @@ void reload_ucode_intel(void)static int collect_cpu_info (int cpu_num,struct cpu_signature *csig) { -struct cpuinfo_x86 *c = &cpu_data (cpu_num); -unsigned int val[2 ]; - -memset (csig,0 ,sizeof (*csig)); - - csig->sig =cpuid_eax (0x00000001 ); - -if ((c->x86_model >=5 ) || (c->x86 >6 )) { -/* get processor flags from MSR 0x17 */ -rdmsr (MSR_IA32_PLATFORM_ID , val[0 ], val[1 ]); - csig->pf =1 << ((val[1 ] >>18 ) &7 ); - } - - csig->rev = c->microcode; - +intel_collect_cpu_info (csig);return 0 ; }
[tip: x86/microcode] x86/microcode/intel: Reuse intel_cpu_collect_info()
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: c7ebbcef2052ac09e4fb511926c7275b96d3cc1c Gitweb: https://git.kernel.org/tip/c7ebbcef2052ac09e4fb511926c7275b96d3cc1c Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:49 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Thu, 19 Oct 2023 14:28:03 +02:00
x86/microcode/intel: Reuse intel_cpu_collect_info()
No point for an almost duplicate function.
--- arch/x86/kernel/cpu/microcode/intel.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-)
arch/x86/kernel/cpu/microcode/intel.c +1 -15
@@ -419,21 +419,7 @@ void reload_ucode_intel(void)static int collect_cpu_info (int cpu_num,struct cpu_signature *csig) { -struct cpuinfo_x86 *c = &cpu_data (cpu_num); -unsigned int val[2 ]; - -memset (csig,0 ,sizeof (*csig)); - - csig->sig =cpuid_eax (0x00000001 ); - -if ((c->x86_model >=5 ) || (c->x86 >6 )) { -/* get processor flags from MSR 0x17 */ -rdmsr (MSR_IA32_PLATFORM_ID , val[0 ], val[1 ]); - csig->pf =1 << ((val[1 ] >>18 ) &7 ); - } - - csig->rev = c->microcode; - +intel_collect_cpu_info (csig);return 0 ; }
[tip: x86/microcode] x86/microcode/intel: Reuse intel_cpu_collect_info()
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 11f96ac4c21e701650c7d8349b252973185ac6ce Gitweb: https://git.kernel.org/tip/11f96ac4c21e701650c7d8349b252973185ac6ce Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:49 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Tue, 24 Oct 2023 15:05:54 +02:00
x86/microcode/intel: Reuse intel_cpu_collect_info()
No point for an almost duplicate function.
--- arch/x86/kernel/cpu/microcode/intel.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-)
arch/x86/kernel/cpu/microcode/intel.c +1 -15
@@ -435,21 +435,7 @@ void reload_ucode_intel(void)static int collect_cpu_info (int cpu_num,struct cpu_signature *csig) { -struct cpuinfo_x86 *c = &cpu_data (cpu_num); -unsigned int val[2 ]; - -memset (csig,0 ,sizeof (*csig)); - - csig->sig =cpuid_eax (0x00000001 ); - -if ((c->x86_model >=5 ) || (c->x86 >6 )) { -/* get processor flags from MSR 0x17 */ -rdmsr (MSR_IA32_PLATFORM_ID , val[0 ], val[1 ]); - csig->pf =1 << ((val[1 ] >>18 ) &7 ); - } - - csig->rev = c->microcode; - +intel_collect_cpu_info (csig);return 0 ; }
PATCH v4 13/30 x86/microcode/intel: Rework intel_find_matching_signature()
In reply to Thomas GleixnerTake a cpu_signature argument and work from there. Move the match() helper next to the callsite as there is no point for having it in a header.
---
V2: New patch
--- arch/x86/include/asm/cpu.h | 16 +--------------- arch/x86/kernel/cpu/microcode/intel.c | 31 +++++++++++++++++++------------ drivers/platform/x86/intel/ifs/load.c | 2 +- 3 files changed, 21 insertions(+), 28 deletions(-)
arch/x86/include/asm/cpu.h +1 -15
@@ -77,22 +77,8 @@ struct cpu_signature;void intel_collect_cpu_info (struct cpu_signature *sig); -static inline bool intel_cpu_signatures_match (unsigned int s1,unsigned int p1, -unsigned int s2,unsigned int p2) -{ -if (s1 != s2) -return false; - -/* Processor flags are either both 0 ... */ -if (!p1 && !p2) -return true; - -/* ... or they intersect. */ -return p1 & p2; -} -extern u64 x86_read_arch_cap_msr (void ); -int intel_find_matching_signature (void *mc,unsigned int csig,int cpf); +bool intel_find_matching_signature (void *mc,struct cpu_signature *sig);int intel_microcode_sanity_check (void *mc,bool print_err,int hdr_type);extern struct cpumask cpus_stop_mask;
arch/x86/kernel/cpu/microcode/intel.c +19 -12
@@ -82,29 +82,36 @@ void intel_collect_cpu_info(struct cpu_s }EXPORT_SYMBOL_GPL (intel_collect_cpu_info); -/* -* Returns 1 if update has been found, 0 otherwise. -*/ -int intel_find_matching_signature (void *mc,unsigned int csig,int cpf) +static inline bool cpu_signatures_match (struct cpu_signature *s1,unsigned int sig2, +unsigned int pf2) +{ +if (s1->sig != sig2) +return false; + +/* Processor flags are either both 0 or they intersect. */ +return ((!s1->pf && !pf2) || (s1->pf & pf2)); +} + +bool intel_find_matching_signature (void *mc,struct cpu_signature *sig) {struct microcode_header_intel *mc_hdr = mc; -struct extended_sigtable *ext_hdr;struct extended_signature *ext_sig; +struct extended_sigtable *ext_hdr;int i; -if (intel_cpu_signatures_match (csig, cpf, mc_hdr->sig, mc_hdr->pf)) -return 1 ; +if (cpu_signatures_match (sig, mc_hdr->sig, mc_hdr->pf)) +return true;/* Look for ext. headers: */ if (get_totalsize (mc_hdr) <=intel_microcode_get_datasize (mc_hdr) +MC_HEADER_SIZE ) -return 0 ; +return false; ext_hdr = mc +intel_microcode_get_datasize (mc_hdr) +MC_HEADER_SIZE ; ext_sig = (void *)ext_hdr +EXT_HEADER_SIZE ;for (i =0 ; i < ext_hdr->count; i++) { -if (intel_cpu_signatures_match (csig, cpf, ext_sig->sig, ext_sig->pf)) -return 1 ; +if (cpu_signatures_match (sig, ext_sig->sig, ext_sig->pf)) +return true; ext_sig++; }return 0 ;
@@ -265,7 +272,7 @@ static __init struct microcode_intel *sc intel_microcode_sanity_check(data, false, MC_HEADER_TYPE_MICROCODE) < 0)break ; -if (!intel_find_matching_signature (data, uci->cpu_sig.sig, uci->cpu_sig.pf)) +if (!intel_find_matching_signature (data, &uci->cpu_sig))continue ;/* Check whether there is newer microcode */
@@ -502,7 +509,7 @@ static enum ucode_state parse_microcode_if (cur_rev >= mc_header.rev)continue ; -if (!intel_find_matching_signature (mc, uci->cpu_sig.sig, uci->cpu_sig.pf)) +if (!intel_find_matching_signature (mc, &uci->cpu_sig))continue ;kvfree (new_mc);
drivers/platform/x86/intel/ifs/load.c +1 -1
@@ -242,7 +242,7 @@ static int image_sanity_check(struct devintel_collect_cpu_info (&sig); -if (!intel_find_matching_signature ((void *)data, sig.sig, sig.pf)) { +if (!intel_find_matching_signature ((void *)data, &sig)) {dev_err (dev,"cpu signature, processor flags not matching\n" );return -EINVAL ; }
[tip: x86/microcode] x86/microcode/intel: Rework intel_find_matching_signature()
The following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 24e6c7324d24b63eac8818cb2f0e8fdffe828715 Gitweb: https://git.kernel.org/tip/24e6c7324d24b63eac8818cb2f0e8fdffe828715 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:50 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Tue, 03 Oct 2023 16:25:15 +02:00
x86/microcode/intel: Rework intel_find_matching_signature()
Take a cpu_signature argument and work from there. Move the match() helper next to the callsite as there is no point for having it in a header.
--- arch/x86/include/asm/cpu.h | 16 +------------- arch/x86/kernel/cpu/microcode/intel.c | 31 +++++++++++++++----------- drivers/platform/x86/intel/ifs/load.c | 2 +- 3 files changed, 21 insertions(+), 28 deletions(-)
arch/x86/include/asm/cpu.h +1 -15
@@ -77,22 +77,8 @@ struct cpu_signature;void intel_collect_cpu_info (struct cpu_signature *sig); -static inline bool intel_cpu_signatures_match (unsigned int s1,unsigned int p1, -unsigned int s2,unsigned int p2) -{ -if (s1 != s2) -return false; - -/* Processor flags are either both 0 ... */ -if (!p1 && !p2) -return true; - -/* ... or they intersect. */ -return p1 & p2; -} -extern u64 x86_read_arch_cap_msr (void ); -int intel_find_matching_signature (void *mc,unsigned int csig,int cpf); +bool intel_find_matching_signature (void *mc,struct cpu_signature *sig);int intel_microcode_sanity_check (void *mc,bool print_err,int hdr_type);extern struct cpumask cpus_stop_mask;
arch/x86/kernel/cpu/microcode/intel.c +19 -12
@@ -82,29 +82,36 @@ void intel_collect_cpu_info(struct cpu_signature *sig) }EXPORT_SYMBOL_GPL (intel_collect_cpu_info); -/* -* Returns 1 if update has been found, 0 otherwise. -*/ -int intel_find_matching_signature (void *mc,unsigned int csig,int cpf) +static inline bool cpu_signatures_match (struct cpu_signature *s1,unsigned int sig2, +unsigned int pf2) +{ +if (s1->sig != sig2) +return false; + +/* Processor flags are either both 0 or they intersect. */ +return ((!s1->pf && !pf2) || (s1->pf & pf2)); +} + +bool intel_find_matching_signature (void *mc,struct cpu_signature *sig) {struct microcode_header_intel *mc_hdr = mc; -struct extended_sigtable *ext_hdr;struct extended_signature *ext_sig; +struct extended_sigtable *ext_hdr;int i; -if (intel_cpu_signatures_match (csig, cpf, mc_hdr->sig, mc_hdr->pf)) -return 1 ; +if (cpu_signatures_match (sig, mc_hdr->sig, mc_hdr->pf)) +return true;/* Look for ext. headers: */ if (get_totalsize (mc_hdr) <=intel_microcode_get_datasize (mc_hdr) +MC_HEADER_SIZE ) -return 0 ; +return false; ext_hdr = mc +intel_microcode_get_datasize (mc_hdr) +MC_HEADER_SIZE ; ext_sig = (void *)ext_hdr +EXT_HEADER_SIZE ;for (i =0 ; i < ext_hdr->count; i++) { -if (intel_cpu_signatures_match (csig, cpf, ext_sig->sig, ext_sig->pf)) -return 1 ; +if (cpu_signatures_match (sig, ext_sig->sig, ext_sig->pf)) +return true; ext_sig++; }return 0 ;
@@ -265,7 +272,7 @@ static __init struct microcode_intel *scan_microcode(void *data, size_t size, intel_microcode_sanity_check(data, false, MC_HEADER_TYPE_MICROCODE) < 0)break ; -if (!intel_find_matching_signature (data, uci->cpu_sig.sig, uci->cpu_sig.pf)) +if (!intel_find_matching_signature (data, &uci->cpu_sig))continue ;/* Check whether there is newer microcode */
@@ -503,7 +510,7 @@ static enum ucode_state parse_microcode_blobs(int cpu, struct iov_iter *iter)if (cur_rev >= mc_header.rev)continue ; -if (!intel_find_matching_signature (mc, uci->cpu_sig.sig, uci->cpu_sig.pf)) +if (!intel_find_matching_signature (mc, &uci->cpu_sig))continue ;kvfree (new_mc);
drivers/platform/x86/intel/ifs/load.c +1 -1
@@ -242,7 +242,7 @@ static int image_sanity_check(struct device *dev, const struct microcode_header_intel_collect_cpu_info (&sig); -if (!intel_find_matching_signature ((void *)data, sig.sig, sig.pf)) { +if (!intel_find_matching_signature ((void *)data, &sig)) {dev_err (dev,"cpu signature, processor flags not matching\n" );return -EINVAL ; }
[tip: x86/microcode] x86/microcode/intel: Rework intel_find_matching_signature()
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 50b58fd4678dec0b1c948f9e9172d2f30e43b93f Gitweb: https://git.kernel.org/tip/50b58fd4678dec0b1c948f9e9172d2f30e43b93f Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:50 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Thu, 19 Oct 2023 15:35:57 +02:00
x86/microcode/intel: Rework intel_find_matching_signature()
Take a cpu_signature argument and work from there. Move the match() helper next to the callsite as there is no point for having it in a header.
--- arch/x86/include/asm/cpu.h | 16 +------------- arch/x86/kernel/cpu/microcode/intel.c | 31 +++++++++++++++----------- drivers/platform/x86/intel/ifs/load.c | 2 +- 3 files changed, 21 insertions(+), 28 deletions(-)
arch/x86/include/asm/cpu.h +1 -15
@@ -75,22 +75,8 @@ struct cpu_signature;void intel_collect_cpu_info (struct cpu_signature *sig); -static inline bool intel_cpu_signatures_match (unsigned int s1,unsigned int p1, -unsigned int s2,unsigned int p2) -{ -if (s1 != s2) -return false; - -/* Processor flags are either both 0 ... */ -if (!p1 && !p2) -return true; - -/* ... or they intersect. */ -return p1 & p2; -} -extern u64 x86_read_arch_cap_msr (void ); -int intel_find_matching_signature (void *mc,unsigned int csig,int cpf); +bool intel_find_matching_signature (void *mc,struct cpu_signature *sig);int intel_microcode_sanity_check (void *mc,bool print_err,int hdr_type);extern struct cpumask cpus_stop_mask;
arch/x86/kernel/cpu/microcode/intel.c +19 -12
@@ -82,29 +82,36 @@ void intel_collect_cpu_info(struct cpu_signature *sig) }EXPORT_SYMBOL_GPL (intel_collect_cpu_info); -/* -* Returns 1 if update has been found, 0 otherwise. -*/ -int intel_find_matching_signature (void *mc,unsigned int csig,int cpf) +static inline bool cpu_signatures_match (struct cpu_signature *s1,unsigned int sig2, +unsigned int pf2) +{ +if (s1->sig != sig2) +return false; + +/* Processor flags are either both 0 or they intersect. */ +return ((!s1->pf && !pf2) || (s1->pf & pf2)); +} + +bool intel_find_matching_signature (void *mc,struct cpu_signature *sig) {struct microcode_header_intel *mc_hdr = mc; -struct extended_sigtable *ext_hdr;struct extended_signature *ext_sig; +struct extended_sigtable *ext_hdr;int i; -if (intel_cpu_signatures_match (csig, cpf, mc_hdr->sig, mc_hdr->pf)) -return 1 ; +if (cpu_signatures_match (sig, mc_hdr->sig, mc_hdr->pf)) +return true;/* Look for ext. headers: */ if (get_totalsize (mc_hdr) <=intel_microcode_get_datasize (mc_hdr) +MC_HEADER_SIZE ) -return 0 ; +return false; ext_hdr = mc +intel_microcode_get_datasize (mc_hdr) +MC_HEADER_SIZE ; ext_sig = (void *)ext_hdr +EXT_HEADER_SIZE ;for (i =0 ; i < ext_hdr->count; i++) { -if (intel_cpu_signatures_match (csig, cpf, ext_sig->sig, ext_sig->pf)) -return 1 ; +if (cpu_signatures_match (sig, ext_sig->sig, ext_sig->pf)) +return true; ext_sig++; }return 0 ;
@@ -265,7 +272,7 @@ static __init struct microcode_intel *scan_microcode(void *data, size_t size, intel_microcode_sanity_check(data, false, MC_HEADER_TYPE_MICROCODE) < 0)break ; -if (!intel_find_matching_signature (data, uci->cpu_sig.sig, uci->cpu_sig.pf)) +if (!intel_find_matching_signature (data, &uci->cpu_sig))continue ;/* Check whether there is newer microcode */
@@ -496,7 +503,7 @@ static enum ucode_state parse_microcode_blobs(int cpu, struct iov_iter *iter)if (cur_rev >= mc_header.rev)continue ; -if (!intel_find_matching_signature (mc, uci->cpu_sig.sig, uci->cpu_sig.pf)) +if (!intel_find_matching_signature (mc, &uci->cpu_sig))continue ;kvfree (new_mc);
drivers/platform/x86/intel/ifs/load.c +1 -1
@@ -242,7 +242,7 @@ static int image_sanity_check(struct device *dev, const struct microcode_header_intel_collect_cpu_info (&sig); -if (!intel_find_matching_signature ((void *)data, sig.sig, sig.pf)) { +if (!intel_find_matching_signature ((void *)data, &sig)) {dev_err (dev,"cpu signature, processor flags not matching\n" );return -EINVAL ; }
[tip: x86/microcode] x86/microcode/intel: Rework intel_find_matching_signature()
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: b7fcd995b261c9976e05f47554529c98a0f1cbb0 Gitweb: https://git.kernel.org/tip/b7fcd995b261c9976e05f47554529c98a0f1cbb0 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:50 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Tue, 24 Oct 2023 15:05:54 +02:00
x86/microcode/intel: Rework intel_find_matching_signature()
Take a cpu_signature argument and work from there. Move the match() helper next to the callsite as there is no point for having it in a header.
--- arch/x86/include/asm/cpu.h | 16 +------------- arch/x86/kernel/cpu/microcode/intel.c | 31 +++++++++++++++----------- drivers/platform/x86/intel/ifs/load.c | 2 +- 3 files changed, 21 insertions(+), 28 deletions(-)
arch/x86/include/asm/cpu.h +1 -15
@@ -75,22 +75,8 @@ struct cpu_signature;void intel_collect_cpu_info (struct cpu_signature *sig); -static inline bool intel_cpu_signatures_match (unsigned int s1,unsigned int p1, -unsigned int s2,unsigned int p2) -{ -if (s1 != s2) -return false; - -/* Processor flags are either both 0 ... */ -if (!p1 && !p2) -return true; - -/* ... or they intersect. */ -return p1 & p2; -} -extern u64 x86_read_arch_cap_msr (void ); -int intel_find_matching_signature (void *mc,unsigned int csig,int cpf); +bool intel_find_matching_signature (void *mc,struct cpu_signature *sig);int intel_microcode_sanity_check (void *mc,bool print_err,int hdr_type);extern struct cpumask cpus_stop_mask;
arch/x86/kernel/cpu/microcode/intel.c +19 -12
@@ -84,29 +84,36 @@ void intel_collect_cpu_info(struct cpu_signature *sig) }EXPORT_SYMBOL_GPL (intel_collect_cpu_info); -/* -* Returns 1 if update has been found, 0 otherwise. -*/ -int intel_find_matching_signature (void *mc,unsigned int csig,int cpf) +static inline bool cpu_signatures_match (struct cpu_signature *s1,unsigned int sig2, +unsigned int pf2) +{ +if (s1->sig != sig2) +return false; + +/* Processor flags are either both 0 or they intersect. */ +return ((!s1->pf && !pf2) || (s1->pf & pf2)); +} + +bool intel_find_matching_signature (void *mc,struct cpu_signature *sig) {struct microcode_header_intel *mc_hdr = mc; -struct extended_sigtable *ext_hdr;struct extended_signature *ext_sig; +struct extended_sigtable *ext_hdr;int i; -if (intel_cpu_signatures_match (csig, cpf, mc_hdr->sig, mc_hdr->pf)) -return 1 ; +if (cpu_signatures_match (sig, mc_hdr->sig, mc_hdr->pf)) +return true;/* Look for ext. headers: */ if (get_totalsize (mc_hdr) <=intel_microcode_get_datasize (mc_hdr) +MC_HEADER_SIZE ) -return 0 ; +return false; ext_hdr = mc +intel_microcode_get_datasize (mc_hdr) +MC_HEADER_SIZE ; ext_sig = (void *)ext_hdr +EXT_HEADER_SIZE ;for (i =0 ; i < ext_hdr->count; i++) { -if (intel_cpu_signatures_match (csig, cpf, ext_sig->sig, ext_sig->pf)) -return 1 ; +if (cpu_signatures_match (sig, ext_sig->sig, ext_sig->pf)) +return true; ext_sig++; }return 0 ;
@@ -268,7 +275,7 @@ static __init struct microcode_intel *scan_microcode(void *data, size_t size, intel_microcode_sanity_check(data, false, MC_HEADER_TYPE_MICROCODE) < 0)break ; -if (!intel_find_matching_signature (data, uci->cpu_sig.sig, uci->cpu_sig.pf)) +if (!intel_find_matching_signature (data, &uci->cpu_sig))continue ;/*
@@ -512,7 +519,7 @@ static enum ucode_state parse_microcode_blobs(int cpu, struct iov_iter *iter)if (cur_rev >= mc_header.rev)continue ; -if (!intel_find_matching_signature (mc, uci->cpu_sig.sig, uci->cpu_sig.pf)) +if (!intel_find_matching_signature (mc, &uci->cpu_sig))continue ;kvfree (new_mc);
drivers/platform/x86/intel/ifs/load.c +1 -1
@@ -242,7 +242,7 @@ static int image_sanity_check(struct device *dev, const struct microcode_header_intel_collect_cpu_info (&sig); -if (!intel_find_matching_signature ((void *)data, sig.sig, sig.pf)) { +if (!intel_find_matching_signature ((void *)data, &sig)) {dev_err (dev,"cpu signature, processor flags not matching\n" );return -EINVAL ; }
PATCH v4 14/30 x86/microcode/amd: Read revision from hardware in collect_cpu_info_amd()
In reply to Thomas GleixnerPrepare to decrapify the core initialization logic which invokes microcode_ops::apply_microcode() several times just to set cpu_data::microcode.
---
V2: New patch
--- arch/x86/kernel/cpu/microcode/amd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
arch/x86/kernel/cpu/microcode/amd.c +2 -2
@@ -634,12 +634,12 @@ void reload_ucode_amd(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; +u32 dummy__always_unused ;struct ucode_patch *p; csig->sig =cpuid_eax (0x00000001 ); - csig->rev = c->microcode; +rdmsr (MSR_AMD64_PATCH_LEVEL , csig->rev, dummy);/* * a patch could have been loaded early, set uci->mc so that
Borislav Petkov
maintainerRe: [patch V4 14/30] x86/microcode/amd: Read revision from hardware in collect_cpu_info_amd()
Quoted 18 lines · Thomas Gleixner
Prepare to decrapify the core initialization logic which invokes
microcode_ops::apply_microcode()several times just to setcpu_data::microcode.Signed-off-by Thomas Gleixner <[email protected]>---
V2: New patch--- arch/x86/kernel/cpu/microcode/amd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)arch/x86/kernel/cpu/microcode/amd.c +0
-1@@ -634,12 +634,12 @@ void reload_ucode_amd(unsigned int cpu)static int collect_cpu_info_amd (int cpu,struct cpu_signature *csig) { -struct cpuinfo_x86 *c = &cpu_data (cpu);
arch/x86/kernel/cpu/microcode/amd.c void reload_ucode_amd(unsigned int cpu) · line 645
Quoted 10 lines
arch/x86/kernel/cpu/microcode/amd.c +2
-1struct ucode_cpu_info *uci = ucode_cpu_info + cpu; +u32 dummy__always_unused ;struct ucode_patch *p; csig->sig =cpuid_eax (0x00000001 ); - csig->rev = c->microcode; +rdmsr (MSR_AMD64_PATCH_LEVEL , csig->rev, dummy);/* * a patch could have been loaded early, set uci->mc so that
Skipping that one as the rev needs to be read on the respective CPU for mixed silicon steppings on AMD.
Signature · 4 lines
Regards/Gruss,
Boris.
https://people.kernel.org/tglx/notes-about-netiquettePATCH v4 15/30 x86/microcode: Remove pointless apply() invocation
In reply to Thomas GleixnerMicrocode is applied on the APs during early bringup. There is no point in trying to apply the microcode again during the hotplug operations and neither at the point where the microcode device is initialized.
Collect CPU info and microcode revision in setup_online_cpu() for now. This will move to the CPU hotplug callback in the next step.
---
V2: New patch
--- arch/x86/kernel/cpu/microcode/core.c | 34 ++++++---------------------------- include/linux/cpuhotplug.h | 1 - 2 files changed, 6 insertions(+), 29 deletions(-)
arch/x86/kernel/cpu/microcode/core.c +6 -28
@@ -490,17 +490,6 @@ static void microcode_fini_cpu(int cpu) microcode_ops->microcode_fini_cpu (cpu); } -static enum ucode_state microcode_init_cpu (int cpu) -{ -struct ucode_cpu_info *uci = ucode_cpu_info + cpu; - -memset (uci,0 ,sizeof (*uci)); - - microcode_ops->collect_cpu_info (cpu, &uci->cpu_sig); - -return microcode_ops->apply_microcode (cpu); -} -/** * microcode_bsp_resume - Update boot CPU microcode during resume. */
@@ -519,15 +508,6 @@ static struct syscore_ops mc_syscore_ops .resume = microcode_bsp_resume, }; -static int mc_cpu_starting (unsigned int cpu) -{ -enum ucode_state err = microcode_ops->apply_microcode (cpu); - -pr_debug ("%s: CPU%d, err: %d\n" , __func__, cpu, err); - -return err ==UCODE_ERROR ; -} -static int mc_cpu_online (unsigned int cpu) {struct device *dev =get_cpu_device (cpu);
@@ -555,14 +535,14 @@ static int mc_cpu_down_prep(unsigned intstatic void setup_online_cpu (struct work_struct *work) {int cpu =smp_processor_id (); -enum ucode_state err; +struct ucode_cpu_info *uci = ucode_cpu_info + cpu; - err =microcode_init_cpu (cpu); -if (err ==UCODE_ERROR ) { -pr_err ("Error applying microcode on CPU%d\n" , cpu); -return ; - } +memset (uci,0 ,sizeof (*uci)); + microcode_ops->collect_cpu_info (cpu, &uci->cpu_sig); +cpu_data (cpu).microcode = uci->cpu_sig.rev; +if (!cpu) + boot_cpu_data.microcode = uci->cpu_sig.rev;mc_cpu_online (cpu); }
@@ -615,8 +595,6 @@ static int __init microcode_init(void)schedule_on_each_cpu (setup_online_cpu);register_syscore_ops (&mc_syscore_ops); -cpuhp_setup_state_nocalls (CPUHP_AP_MICROCODE_LOADER ,"x86/microcode:starting" , - mc_cpu_starting,NULL );cpuhp_setup_state_nocalls (CPUHP_AP_ONLINE_DYN ,"x86/microcode:online" , mc_cpu_online, mc_cpu_down_prep);
include/linux/cpuhotplug.h +0 -1
@@ -156,7 +156,6 @@ enum cpuhp_state {CPUHP_AP_IRQ_LOONGARCH_STARTING ,CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING ,CPUHP_AP_ARM_MVEBU_COHERENCY , -CPUHP_AP_MICROCODE_LOADER ,CPUHP_AP_PERF_X86_AMD_UNCORE_STARTING ,CPUHP_AP_PERF_X86_STARTING , CPUHP_AP_PERF_X86_AMD_IBS_STARTING,
Borislav Petkov
maintainerRe: [patch V4 15/30] x86/microcode: Remove pointless apply() invocation
Quoted 7 lines · Thomas Gleixner
Microcode is applied on the APs during early bringup. There is no point in trying to apply the microcode again during the hotplug operations and neither at the point where the microcode device is initialized.
Collect CPU info and microcode revision in
setup_online_cpu()for now. This will move to the CPU hotplug callback in the next step.
Quoted 7 lines
Signed-off-by Thomas Gleixner <[email protected]>---
V2: New patch--- arch/x86/kernel/cpu/microcode/core.c | 34 ++++++---------------------------- include/linux/cpuhotplug.h | 1 - 2 files changed, 6 insertions(+), 29 deletions(-)
Partial revert, as discussed. I've left enough breadcrumbs in case we wanna tackle this again in the future:
---
From: Thomas Gleixner <[email protected]>
Date: Mon, 2 Oct 2023 13:59:52 +0200
Subject: [PATCH] x86/microcode: Remove pointless apply() invocation
Microcode is applied on the APs during early bringup. There is no point in trying to apply the microcode again during the hotplug operations and neither at the point where the microcode device is initialized.
Collect CPU info and microcode revision in setup_online_cpu() for now. This will move to the CPU hotplug callback later.
[ bp: Leave the starting notifier for the following scenario:
- boot, late load, suspend to disk, resume
without the starting notifier, only the last core manages to update the microcode upon resume:
# rdmsr -a 0x8b
10000bf
10000bf
10000bf
10000bf
10000bf
10000dc <----
This is on an AMD F10h machine.
For the future, one should check whether potential unification of the CPU init path could cover the resume path too so that this can be simplified even more. ]
--- arch/x86/kernel/cpu/microcode/core.c | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-)
arch/x86/kernel/cpu/microcode/core.c +6 -17
@@ -490,17 +490,6 @@ static void microcode_fini_cpu(int cpu) microcode_ops->microcode_fini_cpu (cpu); } -static enum ucode_state microcode_init_cpu (int cpu) -{ -struct ucode_cpu_info *uci = ucode_cpu_info + cpu; - -memset (uci,0 ,sizeof (*uci)); - - microcode_ops->collect_cpu_info (cpu, &uci->cpu_sig); - -return microcode_ops->apply_microcode (cpu); -} -/** * microcode_bsp_resume - Update boot CPU microcode during resume. */
@@ -555,14 +544,14 @@ static int mc_cpu_down_prep(unsigned int cpu)static void setup_online_cpu (struct work_struct *work) {int cpu =smp_processor_id (); -enum ucode_state err; +struct ucode_cpu_info *uci = ucode_cpu_info + cpu; - err =microcode_init_cpu (cpu); -if (err ==UCODE_ERROR ) { -pr_err ("Error applying microcode on CPU%d\n" , cpu); -return ; - } +memset (uci,0 ,sizeof (*uci)); + microcode_ops->collect_cpu_info (cpu, &uci->cpu_sig); +cpu_data (cpu).microcode = uci->cpu_sig.rev; +if (!cpu) + boot_cpu_data.microcode = uci->cpu_sig.rev;mc_cpu_online (cpu); }
Signature · 7 lines
2.42.0.rc0.25.ga82fb66fed25
--
Regards/Gruss,
Boris.
https://people.kernel.org/tglx/notes-about-netiquette[tip: x86/microcode] x86/microcode: Remove pointless apply() invocation
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 034eaca8e03547ebff0b3974b88d2b772d858533 Gitweb: https://git.kernel.org/tip/034eaca8e03547ebff0b3974b88d2b772d858533 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:52 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Fri, 06 Oct 2023 15:05:18 +02:00
x86/microcode: Remove pointless apply() invocation
Microcode is applied on the APs during early bringup. There is no point in trying to apply the microcode again during the hotplug operations and neither at the point where the microcode device is initialized.
Collect CPU info and microcode revision in setup_online_cpu() for now. This will move to the CPU hotplug callback later.
[ bp: Leave the starting notifier for the following scenario:
- boot, late load, suspend to disk, resume
without the starting notifier, only the last core manages to update the microcode upon resume:
# rdmsr -a 0x8b
10000bf
10000bf
10000bf
10000bf
10000bf
10000dc <----
This is on an AMD F10h machine.
For the future, one should check whether potential unification of the CPU init path could cover the resume path too so that this can be simplified even more. ]
--- arch/x86/kernel/cpu/microcode/core.c | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-)
arch/x86/kernel/cpu/microcode/core.c +6 -17
@@ -490,17 +490,6 @@ static void microcode_fini_cpu(int cpu) microcode_ops->microcode_fini_cpu (cpu); } -static enum ucode_state microcode_init_cpu (int cpu) -{ -struct ucode_cpu_info *uci = ucode_cpu_info + cpu; - -memset (uci,0 ,sizeof (*uci)); - - microcode_ops->collect_cpu_info (cpu, &uci->cpu_sig); - -return microcode_ops->apply_microcode (cpu); -} -/** * microcode_bsp_resume - Update boot CPU microcode during resume. */
@@ -555,14 +544,14 @@ static int mc_cpu_down_prep(unsigned int cpu)static void setup_online_cpu (struct work_struct *work) {int cpu =smp_processor_id (); -enum ucode_state err; +struct ucode_cpu_info *uci = ucode_cpu_info + cpu; - err =microcode_init_cpu (cpu); -if (err ==UCODE_ERROR ) { -pr_err ("Error applying microcode on CPU%d\n" , cpu); -return ; - } +memset (uci,0 ,sizeof (*uci)); + microcode_ops->collect_cpu_info (cpu, &uci->cpu_sig); +cpu_data (cpu).microcode = uci->cpu_sig.rev; +if (!cpu) + boot_cpu_data.microcode = uci->cpu_sig.rev;mc_cpu_online (cpu); }
PATCH v4 16/30 x86/microcode: Get rid of the schedule work indirection
In reply to Thomas GleixnerScheduling work on all CPUs to collect the microcode information is just another extra step for no value. Let the CPU hotplug callback registration do it.
---
V2: New patch
--- arch/x86/kernel/cpu/microcode/core.c | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-)
arch/x86/kernel/cpu/microcode/core.c +10 -19
@@ -510,8 +510,16 @@ static struct syscore_ops mc_syscore_opsstatic int mc_cpu_online (unsigned int cpu) { +struct ucode_cpu_info *uci = ucode_cpu_info + cpu;struct device *dev =get_cpu_device (cpu); +memset (uci,0 ,sizeof (*uci)); + + microcode_ops->collect_cpu_info (cpu, &uci->cpu_sig); +cpu_data (cpu).microcode = uci->cpu_sig.rev; +if (!cpu) + boot_cpu_data.microcode = uci->cpu_sig.rev; +if (sysfs_create_group (&dev->kobj, &mc_attr_group))pr_err ("Failed to create group for CPU%d\n" , cpu);return 0 ;
@@ -532,20 +540,6 @@ static int mc_cpu_down_prep(unsigned intreturn 0 ; } -static void setup_online_cpu (struct work_struct *work) -{ -int cpu =smp_processor_id (); -struct ucode_cpu_info *uci = ucode_cpu_info + cpu; - -memset (uci,0 ,sizeof (*uci)); - - microcode_ops->collect_cpu_info (cpu, &uci->cpu_sig); -cpu_data (cpu).microcode = uci->cpu_sig.rev; -if (!cpu) - boot_cpu_data.microcode = uci->cpu_sig.rev; -mc_cpu_online (cpu); -} -static struct attribute *cpu_root_microcode_attrs[] = { #ifdef CONFIG_MICROCODE_LATE_LOADING &dev_attr_reload.attr,
@@ -591,12 +585,9 @@ static int __init microcode_init(void) } } -/* Do per-CPU setup */ -schedule_on_each_cpu (setup_online_cpu); -register_syscore_ops (&mc_syscore_ops); -cpuhp_setup_state_nocalls (CPUHP_AP_ONLINE_DYN ,"x86/microcode:online" , - mc_cpu_online, mc_cpu_down_prep); +cpuhp_setup_state (CPUHP_AP_ONLINE_DYN ,"x86/microcode:online" , + mc_cpu_online, mc_cpu_down_prep);pr_info ("Microcode Update Driver: v%s." ,DRIVER_VERSION );
[tip: x86/microcode] x86/microcode: Get rid of the schedule work indirection
The following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: f4a21dcbdbfaaa6841d528e5124c581e90c945fe Gitweb: https://git.kernel.org/tip/f4a21dcbdbfaaa6841d528e5124c581e90c945fe Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:54 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Fri, 06 Oct 2023 15:12:19 +02:00
x86/microcode: Get rid of the schedule work indirection
Scheduling work on all CPUs to collect the microcode information is just another extra step for no value. Let the CPU hotplug callback registration do it.
--- arch/x86/kernel/cpu/microcode/core.c | 29 +++++++++------------------ 1 file changed, 10 insertions(+), 19 deletions(-)
arch/x86/kernel/cpu/microcode/core.c +10 -19
@@ -519,8 +519,16 @@ static int mc_cpu_starting(unsigned int cpu)static int mc_cpu_online (unsigned int cpu) { +struct ucode_cpu_info *uci = ucode_cpu_info + cpu;struct device *dev =get_cpu_device (cpu); +memset (uci,0 ,sizeof (*uci)); + + microcode_ops->collect_cpu_info (cpu, &uci->cpu_sig); +cpu_data (cpu).microcode = uci->cpu_sig.rev; +if (!cpu) + boot_cpu_data.microcode = uci->cpu_sig.rev; +if (sysfs_create_group (&dev->kobj, &mc_attr_group))pr_err ("Failed to create group for CPU%d\n" , cpu);return 0 ;
@@ -541,20 +549,6 @@ static int mc_cpu_down_prep(unsigned int cpu)return 0 ; } -static void setup_online_cpu (struct work_struct *work) -{ -int cpu =smp_processor_id (); -struct ucode_cpu_info *uci = ucode_cpu_info + cpu; - -memset (uci,0 ,sizeof (*uci)); - - microcode_ops->collect_cpu_info (cpu, &uci->cpu_sig); -cpu_data (cpu).microcode = uci->cpu_sig.rev; -if (!cpu) - boot_cpu_data.microcode = uci->cpu_sig.rev; -mc_cpu_online (cpu); -} -static struct attribute *cpu_root_microcode_attrs[] = { #ifdef CONFIG_MICROCODE_LATE_LOADING &dev_attr_reload.attr,
@@ -600,14 +594,11 @@ static int __init microcode_init(void) } } -/* Do per-CPU setup */ -schedule_on_each_cpu (setup_online_cpu); -register_syscore_ops (&mc_syscore_ops);cpuhp_setup_state_nocalls (CPUHP_AP_MICROCODE_LOADER ,"x86/microcode:starting" , mc_cpu_starting,NULL ); -cpuhp_setup_state_nocalls (CPUHP_AP_ONLINE_DYN ,"x86/microcode:online" , - mc_cpu_online, mc_cpu_down_prep); +cpuhp_setup_state (CPUHP_AP_ONLINE_DYN ,"x86/microcode:online" , + mc_cpu_online, mc_cpu_down_prep);pr_info ("Microcode Update Driver: v%s." ,DRIVER_VERSION );
PATCH v4 17/30 x86/microcode: Clean up mc_cpu_down_prep()
In reply to Thomas GleixnerThis function has nothing to do with suspend. It's a hotplug callback. Remove the bogus comment.
Drop the pointless debug printk. The hotplug core provides tracepoints which track the invocation of those callbacks.
---
V2: New patch
--- arch/x86/kernel/cpu/microcode/core.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-)
arch/x86/kernel/cpu/microcode/core.c +1 -7
@@ -527,16 +527,10 @@ static int mc_cpu_online(unsigned int cpstatic int mc_cpu_down_prep (unsigned int cpu) { -struct device *dev; - - dev =get_cpu_device (cpu); +struct device *dev =get_cpu_device (cpu);microcode_fini_cpu (cpu); - -/* Suspend is in progress, only remove the interface */ sysfs_remove_group (&dev->kobj, &mc_attr_group); -pr_debug ("%s: CPU%d\n" , __func__, cpu); -return 0 ; }
[tip: x86/microcode] x86/microcode: Clean up mc_cpu_down_prep()
The following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 75d0be07daa201cd283e00d09eb27b30c0b0261e Gitweb: https://git.kernel.org/tip/75d0be07daa201cd283e00d09eb27b30c0b0261e Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:55 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Fri, 06 Oct 2023 15:12:23 +02:00
x86/microcode: Clean up mc_cpu_down_prep()
This function has nothing to do with suspend. It's a hotplug callback. Remove the bogus comment.
Drop the pointless debug printk. The hotplug core provides tracepoints which track the invocation of those callbacks.
--- arch/x86/kernel/cpu/microcode/core.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-)
arch/x86/kernel/cpu/microcode/core.c +1 -7
@@ -536,16 +536,10 @@ static int mc_cpu_online(unsigned int cpu)static int mc_cpu_down_prep (unsigned int cpu) { -struct device *dev; - - dev =get_cpu_device (cpu); +struct device *dev =get_cpu_device (cpu);microcode_fini_cpu (cpu); - -/* Suspend is in progress, only remove the interface */ sysfs_remove_group (&dev->kobj, &mc_attr_group); -pr_debug ("%s: CPU%d\n" , __func__, cpu); -return 0 ; }
[tip: x86/microcode] x86/microcode: Clean up mc_cpu_down_prep()
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: d91172afa89d1bb7ada97625d761bcdefdd4f9f6 Gitweb: https://git.kernel.org/tip/d91172afa89d1bb7ada97625d761bcdefdd4f9f6 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:55 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Thu, 19 Oct 2023 15:53:58 +02:00
x86/microcode: Clean up mc_cpu_down_prep()
This function has nothing to do with suspend. It's a hotplug callback. Remove the bogus comment.
Drop the pointless debug printk. The hotplug core provides tracepoints which track the invocation of those callbacks.
--- arch/x86/kernel/cpu/microcode/core.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-)
arch/x86/kernel/cpu/microcode/core.c +1 -7
@@ -498,16 +498,10 @@ static int mc_cpu_online(unsigned int cpu)static int mc_cpu_down_prep (unsigned int cpu) { -struct device *dev; - - dev =get_cpu_device (cpu); +struct device *dev =get_cpu_device (cpu);microcode_fini_cpu (cpu); - -/* Suspend is in progress, only remove the interface */ sysfs_remove_group (&dev->kobj, &mc_attr_group); -pr_debug ("%s: CPU%d\n" , __func__, cpu); -return 0 ; }
[tip: x86/microcode] x86/microcode: Clean up mc_cpu_down_prep()
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: ba48aa32388ac652256baa8d0a6092d350160da0 Gitweb: https://git.kernel.org/tip/ba48aa32388ac652256baa8d0a6092d350160da0 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:55 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Tue, 24 Oct 2023 15:05:54 +02:00
x86/microcode: Clean up mc_cpu_down_prep()
This function has nothing to do with suspend. It's a hotplug callback. Remove the bogus comment.
Drop the pointless debug printk. The hotplug core provides tracepoints which track the invocation of those callbacks.
--- arch/x86/kernel/cpu/microcode/core.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-)
arch/x86/kernel/cpu/microcode/core.c +1 -7
@@ -498,16 +498,10 @@ static int mc_cpu_online(unsigned int cpu)static int mc_cpu_down_prep (unsigned int cpu) { -struct device *dev; - - dev =get_cpu_device (cpu); +struct device *dev =get_cpu_device (cpu);microcode_fini_cpu (cpu); - -/* Suspend is in progress, only remove the interface */ sysfs_remove_group (&dev->kobj, &mc_attr_group); -pr_debug ("%s: CPU%d\n" , __func__, cpu); -return 0 ; }
PATCH v4 18/30 x86/microcode: Handle "nosmt" correctly
In reply to Thomas GleixnerFrom: Thomas Gleixner <[email protected]>
On CPUs where microcode loading is not NMI safe the SMT siblings which are parked in one of the play_dead() variants still react on NMIs.
So if a NMI hits while the primary thread updates the microcode the resulting behaviour is undefined. The default play_dead() implementation on modern CPUs is using MWAIT, which is not guaranteed to be safe against a microcode update which affects MWAIT.
Take the cpus_booted_once_mask into account to detect this case and refuse to load late if the vendor specific driver does not advertise that late loading is NMI safe.
AMD stated that this is safe, so mark the AMD driver accordingly.
This requirement will be partially lifted in later changes.
--- arch/x86/Kconfig | 2 - arch/x86/kernel/cpu/microcode/amd.c | 9 +++-- arch/x86/kernel/cpu/microcode/core.c | 51 +++++++++++++++++++------------ arch/x86/kernel/cpu/microcode/internal.h | 13 +++---- 4 files changed, 44 insertions(+), 31 deletions(-)
---
arch/x86/Kconfig +1 -1
@@ -1316,7 +1316,7 @@ config MICROCODE config MICROCODE_LATE_LOADING bool "Late microcode loading (DANGEROUS)" default n - depends on MICROCODE + depends on MICROCODE && SMP help Loading microcode late, when the system is up and executing instructions is a tricky business and should be avoided if possible. Just the sequence
arch/x86/kernel/cpu/microcode/amd.c +5 -4
@@ -909,10 +909,11 @@ static void microcode_fini_cpu_amd(int c }static struct microcode_ops microcode_amd_ops = { - .request_microcode_fw = request_microcode_amd, - .collect_cpu_info = collect_cpu_info_amd, - .apply_microcode = apply_microcode_amd, - .microcode_fini_cpu = microcode_fini_cpu_amd, + .request_microcode_fw = request_microcode_amd, + .collect_cpu_info = collect_cpu_info_amd, + .apply_microcode = apply_microcode_amd, + .microcode_fini_cpu = microcode_fini_cpu_amd, + .nmi_safe = true, };struct microcode_ops *__init init_amd_microcode (void )
arch/x86/kernel/cpu/microcode/core.c +32 -19
70 lines not on this page · raw message
arch/x86/kernel/cpu/microcode/internal.h +6 -7
@@ -20,18 +20,17 @@ enum ucode_state {struct microcode_ops {enum ucode_state (*request_microcode_fw)(int cpu,struct device *dev); -void (*microcode_fini_cpu)(int cpu);/* -* The generic 'microcode_core' part guarantees that -* the callbacks below run on a target cpu when they -* are being called. +* The generic 'microcode_core' part guarantees that the callbacks +* below run on a target CPU when they are being called. * See also the "Synchronization" section in microcode_core.c. */ -enum ucode_state (*apply_microcode)(int cpu); -int (*collect_cpu_info)(int cpu,struct cpu_signature *csig); -void (*finalize_late_load)(int result); +enum ucode_state (*apply_microcode)(int cpu); +int (*collect_cpu_info)(int cpu,struct cpu_signature *csig); +void (*finalize_late_load)(int result); +unsigned int nmi_safe :1 ; };extern struct ucode_cpu_info ucode_cpu_info[];
[tip: x86/microcode] x86/microcode: Handle "nosmt" correctly
The following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 10adb827276a944adf06e56b84f45d6ff9ebdd7a Gitweb: https://git.kernel.org/tip/10adb827276a944adf06e56b84f45d6ff9ebdd7a Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:56 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Fri, 06 Oct 2023 15:12:23 +02:00
x86/microcode: Handle "nosmt" correctly
On CPUs where microcode loading is not NMI-safe the SMT siblings which are parked in one of the play_dead() variants still react to NMIs.
So if an NMI hits while the primary thread updates the microcode the resulting behaviour is undefined. The default play_dead() implementation on modern CPUs is using MWAIT which is not guaranteed to be safe against a microcode update which affects MWAIT.
Take the cpus_booted_once_mask into account to detect this case and refuse to load late if the vendor specific driver does not advertise that late loading is NMI safe.
AMD stated that this is safe, so mark the AMD driver accordingly.
This requirement will be partially lifted in later changes.
--- arch/x86/Kconfig | 2 +- arch/x86/kernel/cpu/microcode/amd.c | 9 ++-- arch/x86/kernel/cpu/microcode/core.c | 51 ++++++++++++++--------- arch/x86/kernel/cpu/microcode/internal.h | 13 ++---- 4 files changed, 44 insertions(+), 31 deletions(-)
arch/x86/Kconfig +1 -1
@@ -1316,7 +1316,7 @@ config MICROCODE config MICROCODE_LATE_LOADING bool "Late microcode loading (DANGEROUS)" default n - depends on MICROCODE + depends on MICROCODE && SMP help Loading microcode late, when the system is up and executing instructions is a tricky business and should be avoided if possible. Just the sequence
arch/x86/kernel/cpu/microcode/amd.c +5 -4
@@ -909,10 +909,11 @@ static void microcode_fini_cpu_amd(int cpu) }static struct microcode_ops microcode_amd_ops = { - .request_microcode_fw = request_microcode_amd, - .collect_cpu_info = collect_cpu_info_amd, - .apply_microcode = apply_microcode_amd, - .microcode_fini_cpu = microcode_fini_cpu_amd, + .request_microcode_fw = request_microcode_amd, + .collect_cpu_info = collect_cpu_info_amd, + .apply_microcode = apply_microcode_amd, + .microcode_fini_cpu = microcode_fini_cpu_amd, + .nmi_safe = true, };struct microcode_ops *__init init_amd_microcode (void )
arch/x86/kernel/cpu/microcode/core.c +32 -19
70 lines not on this page · raw message
arch/x86/kernel/cpu/microcode/internal.h +6 -7
@@ -20,18 +20,17 @@ enum ucode_state {struct microcode_ops {enum ucode_state (*request_microcode_fw)(int cpu,struct device *dev); -void (*microcode_fini_cpu)(int cpu);/* -* The generic 'microcode_core' part guarantees that -* the callbacks below run on a target cpu when they -* are being called. +* The generic 'microcode_core' part guarantees that the callbacks +* below run on a target CPU when they are being called. * See also the "Synchronization" section in microcode_core.c. */ -enum ucode_state (*apply_microcode)(int cpu); -int (*collect_cpu_info)(int cpu,struct cpu_signature *csig); -void (*finalize_late_load)(int result); +enum ucode_state (*apply_microcode)(int cpu); +int (*collect_cpu_info)(int cpu,struct cpu_signature *csig); +void (*finalize_late_load)(int result); +unsigned int nmi_safe :1 ; };extern struct ucode_cpu_info ucode_cpu_info[];
[tip: x86/microcode] x86/microcode: Handle "nosmt" correctly
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 00627f647ad560222dd9475c194e5bcfd3c6cd30 Gitweb: https://git.kernel.org/tip/00627f647ad560222dd9475c194e5bcfd3c6cd30 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:56 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Thu, 19 Oct 2023 15:54:42 +02:00
x86/microcode: Handle "nosmt" correctly
On CPUs where microcode loading is not NMI-safe the SMT siblings which are parked in one of the play_dead() variants still react to NMIs.
So if an NMI hits while the primary thread updates the microcode the resulting behaviour is undefined. The default play_dead() implementation on modern CPUs is using MWAIT which is not guaranteed to be safe against a microcode update which affects MWAIT.
Take the cpus_booted_once_mask into account to detect this case and refuse to load late if the vendor specific driver does not advertise that late loading is NMI safe.
AMD stated that this is safe, so mark the AMD driver accordingly.
This requirement will be partially lifted in later changes.
--- arch/x86/Kconfig | 2 +- arch/x86/kernel/cpu/microcode/amd.c | 9 ++-- arch/x86/kernel/cpu/microcode/core.c | 51 ++++++++++++++--------- arch/x86/kernel/cpu/microcode/internal.h | 13 ++---- 4 files changed, 44 insertions(+), 31 deletions(-)
arch/x86/Kconfig +1 -1
@@ -1320,7 +1320,7 @@ config MICROCODE_INITRD32 config MICROCODE_LATE_LOADING bool "Late microcode loading (DANGEROUS)" default n - depends on MICROCODE + depends on MICROCODE && SMP help Loading microcode late, when the system is up and executing instructions is a tricky business and should be avoided if possible. Just the sequence
arch/x86/kernel/cpu/microcode/amd.c +5 -4
@@ -917,10 +917,11 @@ static void microcode_fini_cpu_amd(int cpu) }static struct microcode_ops microcode_amd_ops = { - .request_microcode_fw = request_microcode_amd, - .collect_cpu_info = collect_cpu_info_amd, - .apply_microcode = apply_microcode_amd, - .microcode_fini_cpu = microcode_fini_cpu_amd, + .request_microcode_fw = request_microcode_amd, + .collect_cpu_info = collect_cpu_info_amd, + .apply_microcode = apply_microcode_amd, + .microcode_fini_cpu = microcode_fini_cpu_amd, + .nmi_safe = true, };struct microcode_ops *__init init_amd_microcode (void )
arch/x86/kernel/cpu/microcode/core.c +32 -19
70 lines not on this page · raw message
arch/x86/kernel/cpu/microcode/internal.h +6 -7
@@ -20,18 +20,17 @@ enum ucode_state {struct microcode_ops {enum ucode_state (*request_microcode_fw)(int cpu,struct device *dev); -void (*microcode_fini_cpu)(int cpu);/* -* The generic 'microcode_core' part guarantees that -* the callbacks below run on a target cpu when they -* are being called. +* The generic 'microcode_core' part guarantees that the callbacks +* below run on a target CPU when they are being called. * See also the "Synchronization" section in microcode_core.c. */ -enum ucode_state (*apply_microcode)(int cpu); -int (*collect_cpu_info)(int cpu,struct cpu_signature *csig); -void (*finalize_late_load)(int result); +enum ucode_state (*apply_microcode)(int cpu); +int (*collect_cpu_info)(int cpu,struct cpu_signature *csig); +void (*finalize_late_load)(int result); +unsigned int nmi_safe :1 ; };extern struct ucode_cpu_info ucode_cpu_info[];
[tip: x86/microcode] x86/microcode: Handle "nosmt" correctly
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 634ac23ad609b3ddd9e0e478bd5afbf49d3a2556 Gitweb: https://git.kernel.org/tip/634ac23ad609b3ddd9e0e478bd5afbf49d3a2556 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:56 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Tue, 24 Oct 2023 15:05:54 +02:00
x86/microcode: Handle "nosmt" correctly
On CPUs where microcode loading is not NMI-safe the SMT siblings which are parked in one of the play_dead() variants still react to NMIs.
So if an NMI hits while the primary thread updates the microcode the resulting behaviour is undefined. The default play_dead() implementation on modern CPUs is using MWAIT which is not guaranteed to be safe against a microcode update which affects MWAIT.
Take the cpus_booted_once_mask into account to detect this case and refuse to load late if the vendor specific driver does not advertise that late loading is NMI safe.
AMD stated that this is safe, so mark the AMD driver accordingly.
This requirement will be partially lifted in later changes.
--- arch/x86/Kconfig | 2 +- arch/x86/kernel/cpu/microcode/amd.c | 9 ++-- arch/x86/kernel/cpu/microcode/core.c | 51 ++++++++++++++--------- arch/x86/kernel/cpu/microcode/internal.h | 13 ++---- 4 files changed, 44 insertions(+), 31 deletions(-)
arch/x86/Kconfig +1 -1
@@ -1320,7 +1320,7 @@ config MICROCODE_INITRD32 config MICROCODE_LATE_LOADING bool "Late microcode loading (DANGEROUS)" default n - depends on MICROCODE + depends on MICROCODE && SMP help Loading microcode late, when the system is up and executing instructions is a tricky business and should be avoided if possible. Just the sequence
arch/x86/kernel/cpu/microcode/amd.c +5 -4
@@ -917,10 +917,11 @@ static void microcode_fini_cpu_amd(int cpu) }static struct microcode_ops microcode_amd_ops = { - .request_microcode_fw = request_microcode_amd, - .collect_cpu_info = collect_cpu_info_amd, - .apply_microcode = apply_microcode_amd, - .microcode_fini_cpu = microcode_fini_cpu_amd, + .request_microcode_fw = request_microcode_amd, + .collect_cpu_info = collect_cpu_info_amd, + .apply_microcode = apply_microcode_amd, + .microcode_fini_cpu = microcode_fini_cpu_amd, + .nmi_safe = true, };struct microcode_ops *__init init_amd_microcode (void )
arch/x86/kernel/cpu/microcode/core.c +32 -19
70 lines not on this page · raw message
arch/x86/kernel/cpu/microcode/internal.h +6 -7
@@ -20,18 +20,17 @@ enum ucode_state {struct microcode_ops {enum ucode_state (*request_microcode_fw)(int cpu,struct device *dev); -void (*microcode_fini_cpu)(int cpu);/* -* The generic 'microcode_core' part guarantees that -* the callbacks below run on a target cpu when they -* are being called. +* The generic 'microcode_core' part guarantees that the callbacks +* below run on a target CPU when they are being called. * See also the "Synchronization" section in microcode_core.c. */ -enum ucode_state (*apply_microcode)(int cpu); -int (*collect_cpu_info)(int cpu,struct cpu_signature *csig); -void (*finalize_late_load)(int result); +enum ucode_state (*apply_microcode)(int cpu); +int (*collect_cpu_info)(int cpu,struct cpu_signature *csig); +void (*finalize_late_load)(int result); +unsigned int nmi_safe :1 ; };extern struct ucode_cpu_info ucode_cpu_info[];
PATCH v4 19/30 x86/microcode: Clarify the late load logic
In reply to Thomas GleixnerFrom: Thomas Gleixner <[email protected]>
reload_store() is way too complicated. Split the inner workings out and make the following enhancements:
- Taint the kernel only when the microcode was actually updated. If. e.g. the rendevouz fails, then nothing happened and there is no reason for tainting.
- Return useful error codes
--- arch/x86/kernel/cpu/microcode/core.c | 41 ++++++++++++++++------------------- 1 file changed, 19 insertions(+), 22 deletions(-)
---
arch/x86/kernel/cpu/microcode/core.c +19 -22
@@ -391,11 +391,11 @@ static int microcode_reload_late(void)pr_info ("Reload succeeded, microcode revision: 0x%x -> 0x%x\n" , old, boot_cpu_data.microcode);microcode_check (&prev_info); +add_taint (TAINT_CPU_OUT_OF_SPEC ,LOCKDEP_STILL_OK ); }else {pr_info ("Reload failed, current microcode revision: 0x%x\n" , boot_cpu_data.microcode); } -return ret; }
@@ -428,40 +428,37 @@ static bool ensure_cpus_are_online(void)return true; } +static int ucode_load_late_locked (void ) +{ +if (!ensure_cpus_are_online ()) +return -EBUSY ; + +switch (microcode_ops->request_microcode_fw (0 , µcode_pdev->dev)) { +case UCODE_NEW : +return microcode_reload_late (); +case UCODE_NFOUND : +return -ENOENT ; +default : +return -EBADFD ; + } +} +static ssize_t reload_store (struct device *dev,struct device_attribute *attr,const char *buf,size_t size) { -enum ucode_state tmp_ret =UCODE_OK ; -int bsp = boot_cpu_data.cpu_index;unsigned long val; -ssize_t ret =0 ; +ssize_t ret; ret =kstrtoul (buf,0 , &val);if (ret || val !=1 )return -EINVAL ;cpus_read_lock (); - -if (!ensure_cpus_are_online ()) { - ret = -EBUSY ; - goto put; - } - - tmp_ret = microcode_ops->request_microcode_fw (bsp, µcode_pdev->dev); -if (tmp_ret !=UCODE_NEW ) - goto put; - - ret =microcode_reload_late (); -put: + ret =ucode_load_late_locked ();cpus_read_unlock (); -if (ret ==0 ) - ret = size; - -add_taint (TAINT_CPU_OUT_OF_SPEC ,LOCKDEP_STILL_OK ); - -return ret; +return ret ? : size; }static DEVICE_ATTR_WO (reload);
[tip: x86/microcode] x86/microcode: Clarify the late load logic
The following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: f696f2fbae3dd52a4e79f0a02b8cb31fe0dc69ce Gitweb: https://git.kernel.org/tip/f696f2fbae3dd52a4e79f0a02b8cb31fe0dc69ce Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:57 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Fri, 06 Oct 2023 15:12:23 +02:00
x86/microcode: Clarify the late load logic
reload_store() is way too complicated. Split the inner workings out and make the following enhancements:
- Taint the kernel only when the microcode was actually updated. If. e.g. the rendezvous fails, then nothing happened and there is no reason for tainting.
- Return useful error codes
--- arch/x86/kernel/cpu/microcode/core.c | 41 ++++++++++++--------------- 1 file changed, 19 insertions(+), 22 deletions(-)
arch/x86/kernel/cpu/microcode/core.c +19 -22
@@ -391,11 +391,11 @@ static int microcode_reload_late(void)pr_info ("Reload succeeded, microcode revision: 0x%x -> 0x%x\n" , old, boot_cpu_data.microcode);microcode_check (&prev_info); +add_taint (TAINT_CPU_OUT_OF_SPEC ,LOCKDEP_STILL_OK ); }else {pr_info ("Reload failed, current microcode revision: 0x%x\n" , boot_cpu_data.microcode); } -return ret; }
@@ -428,40 +428,37 @@ static bool ensure_cpus_are_online(void)return true; } +static int ucode_load_late_locked (void ) +{ +if (!ensure_cpus_are_online ()) +return -EBUSY ; + +switch (microcode_ops->request_microcode_fw (0 , µcode_pdev->dev)) { +case UCODE_NEW : +return microcode_reload_late (); +case UCODE_NFOUND : +return -ENOENT ; +default : +return -EBADFD ; + } +} +static ssize_t reload_store (struct device *dev,struct device_attribute *attr,const char *buf,size_t size) { -enum ucode_state tmp_ret =UCODE_OK ; -int bsp = boot_cpu_data.cpu_index;unsigned long val; -ssize_t ret =0 ; +ssize_t ret; ret =kstrtoul (buf,0 , &val);if (ret || val !=1 )return -EINVAL ;cpus_read_lock (); - -if (!ensure_cpus_are_online ()) { - ret = -EBUSY ; - goto put; - } - - tmp_ret = microcode_ops->request_microcode_fw (bsp, µcode_pdev->dev); -if (tmp_ret !=UCODE_NEW ) - goto put; - - ret =microcode_reload_late (); -put: + ret =ucode_load_late_locked ();cpus_read_unlock (); -if (ret ==0 ) - ret = size; - -add_taint (TAINT_CPU_OUT_OF_SPEC ,LOCKDEP_STILL_OK ); - -return ret; +return ret ? : size; }static DEVICE_ATTR_WO (reload);
[tip: x86/microcode] x86/microcode: Clarify the late load logic
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 838c0817392e5d459a4aa90d6fbb4fb571024929 Gitweb: https://git.kernel.org/tip/838c0817392e5d459a4aa90d6fbb4fb571024929 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:57 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Thu, 19 Oct 2023 15:55:26 +02:00
x86/microcode: Clarify the late load logic
reload_store() is way too complicated. Split the inner workings out and make the following enhancements:
- Taint the kernel only when the microcode was actually updated. If. e.g. the rendezvous fails, then nothing happened and there is no reason for tainting.
- Return useful error codes
--- arch/x86/kernel/cpu/microcode/core.c | 41 ++++++++++++--------------- 1 file changed, 19 insertions(+), 22 deletions(-)
arch/x86/kernel/cpu/microcode/core.c +19 -22
@@ -362,11 +362,11 @@ static int microcode_reload_late(void)pr_info ("Reload succeeded, microcode revision: 0x%x -> 0x%x\n" , old, boot_cpu_data.microcode);microcode_check (&prev_info); +add_taint (TAINT_CPU_OUT_OF_SPEC ,LOCKDEP_STILL_OK ); }else {pr_info ("Reload failed, current microcode revision: 0x%x\n" , boot_cpu_data.microcode); } -return ret; }
@@ -399,40 +399,37 @@ static bool ensure_cpus_are_online(void)return true; } +static int ucode_load_late_locked (void ) +{ +if (!ensure_cpus_are_online ()) +return -EBUSY ; + +switch (microcode_ops->request_microcode_fw (0 , µcode_pdev->dev)) { +case UCODE_NEW : +return microcode_reload_late (); +case UCODE_NFOUND : +return -ENOENT ; +default : +return -EBADFD ; + } +} +static ssize_t reload_store (struct device *dev,struct device_attribute *attr,const char *buf,size_t size) { -enum ucode_state tmp_ret =UCODE_OK ; -int bsp = boot_cpu_data.cpu_index;unsigned long val; -ssize_t ret =0 ; +ssize_t ret; ret =kstrtoul (buf,0 , &val);if (ret || val !=1 )return -EINVAL ;cpus_read_lock (); - -if (!ensure_cpus_are_online ()) { - ret = -EBUSY ; - goto put; - } - - tmp_ret = microcode_ops->request_microcode_fw (bsp, µcode_pdev->dev); -if (tmp_ret !=UCODE_NEW ) - goto put; - - ret =microcode_reload_late (); -put: + ret =ucode_load_late_locked ();cpus_read_unlock (); -if (ret ==0 ) - ret = size; - -add_taint (TAINT_CPU_OUT_OF_SPEC ,LOCKDEP_STILL_OK ); - -return ret; +return ret ? : size; }static DEVICE_ATTR_WO (reload);
[tip: x86/microcode] x86/microcode: Clarify the late load logic
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 6f059e634dcd0d725854514c94c114bbdd83950d Gitweb: https://git.kernel.org/tip/6f059e634dcd0d725854514c94c114bbdd83950d Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:57 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Tue, 24 Oct 2023 15:05:54 +02:00
x86/microcode: Clarify the late load logic
reload_store() is way too complicated. Split the inner workings out and make the following enhancements:
- Taint the kernel only when the microcode was actually updated. If. e.g. the rendezvous fails, then nothing happened and there is no reason for tainting.
- Return useful error codes
--- arch/x86/kernel/cpu/microcode/core.c | 41 ++++++++++++--------------- 1 file changed, 19 insertions(+), 22 deletions(-)
arch/x86/kernel/cpu/microcode/core.c +19 -22
@@ -362,11 +362,11 @@ static int microcode_reload_late(void)pr_info ("Reload succeeded, microcode revision: 0x%x -> 0x%x\n" , old, boot_cpu_data.microcode);microcode_check (&prev_info); +add_taint (TAINT_CPU_OUT_OF_SPEC ,LOCKDEP_STILL_OK ); }else {pr_info ("Reload failed, current microcode revision: 0x%x\n" , boot_cpu_data.microcode); } -return ret; }
@@ -399,40 +399,37 @@ static bool ensure_cpus_are_online(void)return true; } +static int ucode_load_late_locked (void ) +{ +if (!ensure_cpus_are_online ()) +return -EBUSY ; + +switch (microcode_ops->request_microcode_fw (0 , µcode_pdev->dev)) { +case UCODE_NEW : +return microcode_reload_late (); +case UCODE_NFOUND : +return -ENOENT ; +default : +return -EBADFD ; + } +} +static ssize_t reload_store (struct device *dev,struct device_attribute *attr,const char *buf,size_t size) { -enum ucode_state tmp_ret =UCODE_OK ; -int bsp = boot_cpu_data.cpu_index;unsigned long val; -ssize_t ret =0 ; +ssize_t ret; ret =kstrtoul (buf,0 , &val);if (ret || val !=1 )return -EINVAL ;cpus_read_lock (); - -if (!ensure_cpus_are_online ()) { - ret = -EBUSY ; - goto put; - } - - tmp_ret = microcode_ops->request_microcode_fw (bsp, µcode_pdev->dev); -if (tmp_ret !=UCODE_NEW ) - goto put; - - ret =microcode_reload_late (); -put: + ret =ucode_load_late_locked ();cpus_read_unlock (); -if (ret ==0 ) - ret = size; - -add_taint (TAINT_CPU_OUT_OF_SPEC ,LOCKDEP_STILL_OK ); - -return ret; +return ret ? : size; }static DEVICE_ATTR_WO (reload);
PATCH v4 20/30 x86/microcode: Sanitize __wait_for_cpus()
In reply to Thomas GleixnerFrom: Thomas Gleixner <[email protected]>
The code is too complicated for no reason:
- The return value is pointless as this is a strict boolean.
- It's way simpler to count down from num_online_cpus() and check for zero.
- The timeout argument is pointless as this is always one second.
- Touching the NMI watchdog every 100ns does not make any sense, neither does checking every 100ns. This is really not a hotpath operation.
Preload the atomic counter with the number of online CPUs and simplify the whole timeout logic. Delay for one microsecond and touch the NMI watchdog once per millisecond.
--- arch/x86/kernel/cpu/microcode/core.c | 39 +++++++++++++++-------------------- 1 file changed, 17 insertions(+), 22 deletions(-)
---
arch/x86/kernel/cpu/microcode/core.c +17 -22
@@ -281,31 +281,26 @@ static struct platform_device *microcode* requirement can be relaxed in the future. Right now, this is conservative * and good. */ -#define SPINUNIT 100/* 100 nsec */ +static atomic_t late_cpus_in, late_cpus_out; - -static atomic_t late_cpus_in; -static atomic_t late_cpus_out; - -static int __wait_for_cpus (atomic_t *t,long long timeout) +static bool wait_for_cpus (atomic_t *cnt) { -int all_cpus =num_online_cpus (); +unsigned int timeout; -atomic_inc (t); +WARN_ON_ONCE (atomic_dec_return (cnt) <0 ); -while (atomic_read (t) < all_cpus) { -if (timeout <SPINUNIT ) { -pr_err ("Timeout while waiting for CPUs rendezvous, remaining: %d\n" , - all_cpus -atomic_read (t)); -return 1 ; - } +for (timeout =0 ; timeout <USEC_PER_SEC ; timeout++) { +if (!atomic_read (cnt)) +return true; -ndelay (SPINUNIT ); - timeout -=SPINUNIT ; +udelay (1 ); -touch_nmi_watchdog (); +if (!(timeout %USEC_PER_MSEC )) +touch_nmi_watchdog (); } -return 0 ; +/* Prevent the late comers from making progress and let them time out */ +atomic_inc (cnt); +return false; }/*
@@ -323,7 +318,7 @@ static int __reload_late(void *info)* Wait for all CPUs to arrive. A load will not be attempted unless all * CPUs show up. * */ -if (__wait_for_cpus (&late_cpus_in,NSEC_PER_SEC )) +if (!wait_for_cpus (&late_cpus_in))return -1 ;/*
@@ -346,7 +341,7 @@ static int __reload_late(void *info) } wait_for_siblings: -if (__wait_for_cpus (&late_cpus_out,NSEC_PER_SEC )) +if (!wait_for_cpus (&late_cpus_out))panic ("Timeout during microcode update!\n" );/*
@@ -373,8 +368,8 @@ static int microcode_reload_late(void)pr_err ("Attempting late microcode loading - it is dangerous and taints the kernel.\n" );pr_err ("You should switch to early loading, if possible.\n" ); -atomic_set (&late_cpus_in,0 ); -atomic_set (&late_cpus_out,0 ); +atomic_set (&late_cpus_in,num_online_cpus ()); +atomic_set (&late_cpus_out,num_online_cpus ());/* * Take a snapshot before the microcode update in order to compare and
[tip: x86/microcode] x86/microcode: Sanitize __wait_for_cpus()
The following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 89d6d026e43bd8b093f6238b569a0efc9485d19f Gitweb: https://git.kernel.org/tip/89d6d026e43bd8b093f6238b569a0efc9485d19f Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:59 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Fri, 06 Oct 2023 15:12:23 +02:00
x86/microcode: Sanitize __wait_for_cpus()
The code is too complicated for no reason:
- The return value is pointless as this is a strict boolean.
- It's way simpler to count down from num_online_cpus() and check for zero.
- The timeout argument is pointless as this is always one second.
- Touching the NMI watchdog every 100ns does not make any sense, neither does checking every 100ns. This is really not a hotpath operation.
Preload the atomic counter with the number of online CPUs and simplify the whole timeout logic. Delay for one microsecond and touch the NMI watchdog once per millisecond.
--- arch/x86/kernel/cpu/microcode/core.c | 39 +++++++++++---------------- 1 file changed, 17 insertions(+), 22 deletions(-)
arch/x86/kernel/cpu/microcode/core.c +17 -22
@@ -281,31 +281,26 @@ static struct platform_device *microcode_pdev;* requirement can be relaxed in the future. Right now, this is conservative * and good. */ -#define SPINUNIT 100/* 100 nsec */ +static atomic_t late_cpus_in, late_cpus_out; - -static atomic_t late_cpus_in; -static atomic_t late_cpus_out; - -static int __wait_for_cpus (atomic_t *t,long long timeout) +static bool wait_for_cpus (atomic_t *cnt) { -int all_cpus =num_online_cpus (); +unsigned int timeout; -atomic_inc (t); +WARN_ON_ONCE (atomic_dec_return (cnt) <0 ); -while (atomic_read (t) < all_cpus) { -if (timeout <SPINUNIT ) { -pr_err ("Timeout while waiting for CPUs rendezvous, remaining: %d\n" , - all_cpus -atomic_read (t)); -return 1 ; - } +for (timeout =0 ; timeout <USEC_PER_SEC ; timeout++) { +if (!atomic_read (cnt)) +return true; -ndelay (SPINUNIT ); - timeout -=SPINUNIT ; +udelay (1 ); -touch_nmi_watchdog (); +if (!(timeout %USEC_PER_MSEC )) +touch_nmi_watchdog (); } -return 0 ; +/* Prevent the late comers from making progress and let them time out */ +atomic_inc (cnt); +return false; }/*
@@ -323,7 +318,7 @@ static int __reload_late(void *info)* Wait for all CPUs to arrive. A load will not be attempted unless all * CPUs show up. * */ -if (__wait_for_cpus (&late_cpus_in,NSEC_PER_SEC )) +if (!wait_for_cpus (&late_cpus_in))return -1 ;/*
@@ -346,7 +341,7 @@ static int __reload_late(void *info) } wait_for_siblings: -if (__wait_for_cpus (&late_cpus_out,NSEC_PER_SEC )) +if (!wait_for_cpus (&late_cpus_out))panic ("Timeout during microcode update!\n" );/*
@@ -373,8 +368,8 @@ static int microcode_reload_late(void)pr_err ("Attempting late microcode loading - it is dangerous and taints the kernel.\n" );pr_err ("You should switch to early loading, if possible.\n" ); -atomic_set (&late_cpus_in,0 ); -atomic_set (&late_cpus_out,0 ); +atomic_set (&late_cpus_in,num_online_cpus ()); +atomic_set (&late_cpus_out,num_online_cpus ());/* * Take a snapshot before the microcode update in order to compare and
[tip: x86/microcode] x86/microcode: Sanitize __wait_for_cpus()
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 7f21c0ed2cfefcbc6e2b383a6001c0c48a135ffe Gitweb: https://git.kernel.org/tip/7f21c0ed2cfefcbc6e2b383a6001c0c48a135ffe Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:59 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Thu, 19 Oct 2023 15:56:17 +02:00
x86/microcode: Sanitize __wait_for_cpus()
The code is too complicated for no reason:
- The return value is pointless as this is a strict boolean.
- It's way simpler to count down from num_online_cpus() and check for zero.
- The timeout argument is pointless as this is always one second.
- Touching the NMI watchdog every 100ns does not make any sense, neither does checking every 100ns. This is really not a hotpath operation.
Preload the atomic counter with the number of online CPUs and simplify the whole timeout logic. Delay for one microsecond and touch the NMI watchdog once per millisecond.
--- arch/x86/kernel/cpu/microcode/core.c | 39 +++++++++++---------------- 1 file changed, 17 insertions(+), 22 deletions(-)
arch/x86/kernel/cpu/microcode/core.c +17 -22
@@ -252,31 +252,26 @@ static struct platform_device *microcode_pdev;* requirement can be relaxed in the future. Right now, this is conservative * and good. */ -#define SPINUNIT 100/* 100 nsec */ +static atomic_t late_cpus_in, late_cpus_out; - -static atomic_t late_cpus_in; -static atomic_t late_cpus_out; - -static int __wait_for_cpus (atomic_t *t,long long timeout) +static bool wait_for_cpus (atomic_t *cnt) { -int all_cpus =num_online_cpus (); +unsigned int timeout; -atomic_inc (t); +WARN_ON_ONCE (atomic_dec_return (cnt) <0 ); -while (atomic_read (t) < all_cpus) { -if (timeout <SPINUNIT ) { -pr_err ("Timeout while waiting for CPUs rendezvous, remaining: %d\n" , - all_cpus -atomic_read (t)); -return 1 ; - } +for (timeout =0 ; timeout <USEC_PER_SEC ; timeout++) { +if (!atomic_read (cnt)) +return true; -ndelay (SPINUNIT ); - timeout -=SPINUNIT ; +udelay (1 ); -touch_nmi_watchdog (); +if (!(timeout %USEC_PER_MSEC )) +touch_nmi_watchdog (); } -return 0 ; +/* Prevent the late comers from making progress and let them time out */ +atomic_inc (cnt); +return false; }/*
@@ -294,7 +289,7 @@ static int __reload_late(void *info)* Wait for all CPUs to arrive. A load will not be attempted unless all * CPUs show up. * */ -if (__wait_for_cpus (&late_cpus_in,NSEC_PER_SEC )) +if (!wait_for_cpus (&late_cpus_in))return -1 ;/*
@@ -317,7 +312,7 @@ static int __reload_late(void *info) } wait_for_siblings: -if (__wait_for_cpus (&late_cpus_out,NSEC_PER_SEC )) +if (!wait_for_cpus (&late_cpus_out))panic ("Timeout during microcode update!\n" );/*
@@ -344,8 +339,8 @@ static int microcode_reload_late(void)pr_err ("Attempting late microcode loading - it is dangerous and taints the kernel.\n" );pr_err ("You should switch to early loading, if possible.\n" ); -atomic_set (&late_cpus_in,0 ); -atomic_set (&late_cpus_out,0 ); +atomic_set (&late_cpus_in,num_online_cpus ()); +atomic_set (&late_cpus_out,num_online_cpus ());/* * Take a snapshot before the microcode update in order to compare and
[tip: x86/microcode] x86/microcode: Sanitize __wait_for_cpus()
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 0772b9aa1a8f7322dce8588c231cff8b57298a53 Gitweb: https://git.kernel.org/tip/0772b9aa1a8f7322dce8588c231cff8b57298a53 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 13:59:59 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Tue, 24 Oct 2023 15:05:54 +02:00
x86/microcode: Sanitize __wait_for_cpus()
The code is too complicated for no reason:
- The return value is pointless as this is a strict boolean.
- It's way simpler to count down from num_online_cpus() and check for zero.
- The timeout argument is pointless as this is always one second.
- Touching the NMI watchdog every 100ns does not make any sense, neither does checking every 100ns. This is really not a hotpath operation.
Preload the atomic counter with the number of online CPUs and simplify the whole timeout logic. Delay for one microsecond and touch the NMI watchdog once per millisecond.
--- arch/x86/kernel/cpu/microcode/core.c | 39 +++++++++++---------------- 1 file changed, 17 insertions(+), 22 deletions(-)
arch/x86/kernel/cpu/microcode/core.c +17 -22
@@ -252,31 +252,26 @@ static struct platform_device *microcode_pdev;* requirement can be relaxed in the future. Right now, this is conservative * and good. */ -#define SPINUNIT 100/* 100 nsec */ +static atomic_t late_cpus_in, late_cpus_out; - -static atomic_t late_cpus_in; -static atomic_t late_cpus_out; - -static int __wait_for_cpus (atomic_t *t,long long timeout) +static bool wait_for_cpus (atomic_t *cnt) { -int all_cpus =num_online_cpus (); +unsigned int timeout; -atomic_inc (t); +WARN_ON_ONCE (atomic_dec_return (cnt) <0 ); -while (atomic_read (t) < all_cpus) { -if (timeout <SPINUNIT ) { -pr_err ("Timeout while waiting for CPUs rendezvous, remaining: %d\n" , - all_cpus -atomic_read (t)); -return 1 ; - } +for (timeout =0 ; timeout <USEC_PER_SEC ; timeout++) { +if (!atomic_read (cnt)) +return true; -ndelay (SPINUNIT ); - timeout -=SPINUNIT ; +udelay (1 ); -touch_nmi_watchdog (); +if (!(timeout %USEC_PER_MSEC )) +touch_nmi_watchdog (); } -return 0 ; +/* Prevent the late comers from making progress and let them time out */ +atomic_inc (cnt); +return false; }/*
@@ -294,7 +289,7 @@ static int __reload_late(void *info)* Wait for all CPUs to arrive. A load will not be attempted unless all * CPUs show up. * */ -if (__wait_for_cpus (&late_cpus_in,NSEC_PER_SEC )) +if (!wait_for_cpus (&late_cpus_in))return -1 ;/*
@@ -317,7 +312,7 @@ static int __reload_late(void *info) } wait_for_siblings: -if (__wait_for_cpus (&late_cpus_out,NSEC_PER_SEC )) +if (!wait_for_cpus (&late_cpus_out))panic ("Timeout during microcode update!\n" );/*
@@ -344,8 +339,8 @@ static int microcode_reload_late(void)pr_err ("Attempting late microcode loading - it is dangerous and taints the kernel.\n" );pr_err ("You should switch to early loading, if possible.\n" ); -atomic_set (&late_cpus_in,0 ); -atomic_set (&late_cpus_out,0 ); +atomic_set (&late_cpus_in,num_online_cpus ()); +atomic_set (&late_cpus_out,num_online_cpus ());/* * Take a snapshot before the microcode update in order to compare and
PATCH v4 21/30 x86/microcode: Add per CPU result state
In reply to Thomas GleixnerFrom: Thomas Gleixner <[email protected]>
The microcode rendevouz is purely acting on global state, which does not allow to analyze fails in a coherent way.
Introduce per CPU state where the results are written into, which allows to analyze the return codes of the individual CPUs.
Initialize the state when walking the cpu_present_mask in the online check to avoid another for_each_cpu() loop.
Enhance the result print out with that.
The structure is intentionally named ucode_ctrl as it will gain control fields in subsequent changes.
--- arch/x86/kernel/cpu/microcode/core.c | 112 ++++++++++++++++++------------- arch/x86/kernel/cpu/microcode/internal.h | 1 2 files changed, 67 insertions(+), 46 deletions(-)
---
arch/x86/kernel/cpu/microcode/core.c +66 -46
@@ -281,6 +281,11 @@ static struct platform_device *microcode* requirement can be relaxed in the future. Right now, this is conservative * and good. */ +struct microcode_ctrl { +enum ucode_state result; +}; + +static DEFINE_PER_CPU(struct microcode_ctrl, ucode_ctrl);static atomic_t late_cpus_in, late_cpus_out;static bool wait_for_cpus (atomic_t *cnt)
@@ -303,23 +308,19 @@ static bool wait_for_cpus(atomic_t *cnt)return false; } -/* -* Returns: -* < 0 - on error -* 0 - success (no update done or microcode was updated) -*/ -static int __reload_late (void *info) +static int load_cpus_stopped (void *unused) {int cpu =smp_processor_id (); -enum ucode_state err; -int ret =0 ; +enum ucode_state ret;/* * Wait for all CPUs to arrive. A load will not be attempted unless all * CPUs show up. * */ -if (!wait_for_cpus (&late_cpus_in)) -return -1 ; +if (!wait_for_cpus (&late_cpus_in)) { +this_cpu_write (ucode_ctrl.result,UCODE_TIMEOUT ); +return 0 ; + }/* * On an SMT system, it suffices to load the microcode on one sibling of
@@ -328,17 +329,11 @@ static int __reload_late(void *info)* loading attempts happen on multiple threads of an SMT core. See * below. */ -if (cpumask_first (topology_sibling_cpumask (cpu)) == cpu) - err = microcode_ops->apply_microcode (cpu); -else +if (cpumask_first (topology_sibling_cpumask (cpu)) != cpu) goto wait_for_siblings; -if (err >=UCODE_NFOUND ) { -if (err ==UCODE_ERROR ) { -pr_warn ("Error reloading microcode on CPU %d\n" , cpu); - ret =-1 ; - } - } + ret = microcode_ops->apply_microcode (cpu); +this_cpu_write (ucode_ctrl.result, ret); wait_for_siblings:if (!wait_for_cpus (&late_cpus_out))
@@ -350,19 +345,18 @@ static int __reload_late(void *info)* per-cpu cpuinfo can be updated with right microcode * revision. */ -if (cpumask_first (topology_sibling_cpumask (cpu)) != cpu) - err = microcode_ops->apply_microcode (cpu); +if (cpumask_first (topology_sibling_cpumask (cpu)) == cpu) +return 0 ; -return ret; + ret = microcode_ops->apply_microcode (cpu); +this_cpu_write (ucode_ctrl.result, ret); +return 0 ; } -/* -* Reload microcode late on all CPUs. Wait for a sec until they -* all gather together. -*/ -static int microcode_reload_late (void ) +static int load_late_stop_cpus (void ) { -int old = boot_cpu_data.microcode, ret; +unsigned int cpu, updated =0 , failed =0 , timedout =0 , siblings =0 ; +int old_rev = boot_cpu_data.microcode;struct cpuinfo_x86 prev_info;pr_err ("Attempting late microcode loading - it is dangerous and taints the kernel.\n" );
@@ -377,26 +371,47 @@ static int microcode_reload_late(void)*/ store_cpu_caps (&prev_info); - ret =stop_machine_cpuslocked (__reload_late,NULL , cpu_online_mask); +stop_machine_cpuslocked (load_cpus_stopped,NULL , cpu_online_mask); + +/* Analyze the results */ +for_each_cpu_and (cpu, cpu_present_mask, &cpus_booted_once_mask) { +switch (per_cpu (ucode_ctrl.result, cpu)) { +case UCODE_UPDATED : updated++;break ; +case UCODE_TIMEOUT : timedout++;break ; +case UCODE_OK : siblings++;break ; +default : failed++;break ; + } + }if (microcode_ops->finalize_late_load) - microcode_ops->finalize_late_load (ret); + microcode_ops->finalize_late_load (!updated); -if (!ret) { -pr_info ("Reload succeeded, microcode revision: 0x%x -> 0x%x\n" , - old, boot_cpu_data.microcode); -microcode_check (&prev_info); -add_taint (TAINT_CPU_OUT_OF_SPEC ,LOCKDEP_STILL_OK ); - }else { -pr_info ("Reload failed, current microcode revision: 0x%x\n" , - boot_cpu_data.microcode); +if (!updated) { +/* Nothing changed. */ +if (!failed && !timedout) +return 0 ; +pr_err ("update failed: %u CPUs failed %u CPUs timed out\n" , + failed, timedout); +return -EIO ; } -return ret; + +add_taint (TAINT_CPU_OUT_OF_SPEC ,LOCKDEP_STILL_OK ); +pr_info ("load: updated on %u primary CPUs with %u siblings\n" , updated, siblings); +if (failed || timedout) { +pr_err ("load incomplete. %u CPUs timed out or failed\n" , +num_online_cpus () - (updated + siblings)); + } +pr_info ("revision: 0x%x -> 0x%x\n" , old_rev, boot_cpu_data.microcode); +microcode_check (&prev_info); + +return updated + siblings ==num_online_cpus () ?0 : -EIO ; }/* -* Ensure that all required CPUs which are present and have been booted -* once are online. +* This function does two things: +* +* 1) Ensure that all required CPUs which are present and have been booted +* once are online. * * To pass this check, all primary threads must be online. *
@@ -407,9 +422,12 @@ static int microcode_reload_late(void)* behaviour is undefined. The default play_dead() implementation on * modern CPUs uses MWAIT, which is also not guaranteed to be safe * against a microcode update which affects MWAIT. +* +* 2) Initialize the per CPU control structure */ -static bool ensure_cpus_are_online (void ) +static bool setup_cpus (void ) { +struct microcode_ctrl ctrl = { .result =-1 , };unsigned int cpu;for_each_cpu_and (cpu, cpu_present_mask, &cpus_booted_once_mask) {
@@ -419,18 +437,20 @@ static bool ensure_cpus_are_online(void)return false; } } +/* Initialize the per CPU state */ +per_cpu (ucode_ctrl, cpu) = ctrl; }return true; } -static int ucode_load_late_locked (void ) +static int load_late_locked (void ) { -if (!ensure_cpus_are_online ()) +if (!setup_cpus ())return -EBUSY ;switch (microcode_ops->request_microcode_fw (0 , µcode_pdev->dev)) {case UCODE_NEW : -return microcode_reload_late (); +return load_late_stop_cpus ();case UCODE_NFOUND :return -ENOENT ;default :
@@ -450,7 +470,7 @@ static ssize_t reload_store(struct devicreturn -EINVAL ;cpus_read_lock (); - ret =ucode_load_late_locked (); + ret =load_late_locked ();cpus_read_unlock ();return ret ? : size;
arch/x86/kernel/cpu/microcode/internal.h +1 -0
@@ -16,6 +16,7 @@ enum ucode_state {UCODE_UPDATED ,UCODE_NFOUND ,UCODE_ERROR , + UCODE_TIMEOUT, }; struct microcode_ops {
[tip: x86/microcode] x86/microcode: Add per CPU result state
The following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: fc27389d6e72cf75f04a49afe851165210f8e9f3 Gitweb: https://git.kernel.org/tip/fc27389d6e72cf75f04a49afe851165210f8e9f3 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 14:00:00 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Fri, 06 Oct 2023 15:12:24 +02:00
x86/microcode: Add per CPU result state
The microcode rendezvous is purely acting on global state, which does not allow to analyze fails in a coherent way.
Introduce per CPU state where the results are written into, which allows to analyze the return codes of the individual CPUs.
Initialize the state when walking the cpu_present_mask in the online check to avoid another for_each_cpu() loop.
Enhance the result print out with that.
The structure is intentionally named ucode_ctrl as it will gain control fields in subsequent changes.
--- arch/x86/kernel/cpu/microcode/core.c | 114 ++++++++++++---------- arch/x86/kernel/cpu/microcode/internal.h | 1 +- 2 files changed, 68 insertions(+), 47 deletions(-)
arch/x86/kernel/cpu/microcode/core.c +67 -47
@@ -281,6 +281,11 @@ static struct platform_device *microcode_pdev;* requirement can be relaxed in the future. Right now, this is conservative * and good. */ +struct microcode_ctrl { +enum ucode_state result; +}; + +static DEFINE_PER_CPU(struct microcode_ctrl, ucode_ctrl);static atomic_t late_cpus_in, late_cpus_out;static bool wait_for_cpus (atomic_t *cnt)
@@ -303,23 +308,19 @@ static bool wait_for_cpus(atomic_t *cnt)return false; } -/* -* Returns: -* < 0 - on error -* 0 - success (no update done or microcode was updated) -*/ -static int __reload_late (void *info) +static int load_cpus_stopped (void *unused) {int cpu =smp_processor_id (); -enum ucode_state err; -int ret =0 ; +enum ucode_state ret;/* * Wait for all CPUs to arrive. A load will not be attempted unless all * CPUs show up. * */ -if (!wait_for_cpus (&late_cpus_in)) -return -1 ; +if (!wait_for_cpus (&late_cpus_in)) { +this_cpu_write (ucode_ctrl.result,UCODE_TIMEOUT ); +return 0 ; + }/* * On an SMT system, it suffices to load the microcode on one sibling of
@@ -328,17 +329,11 @@ static int __reload_late(void *info)* loading attempts happen on multiple threads of an SMT core. See * below. */ -if (cpumask_first (topology_sibling_cpumask (cpu)) == cpu) - err = microcode_ops->apply_microcode (cpu); -else +if (cpumask_first (topology_sibling_cpumask (cpu)) != cpu) goto wait_for_siblings; -if (err >=UCODE_NFOUND ) { -if (err ==UCODE_ERROR ) { -pr_warn ("Error reloading microcode on CPU %d\n" , cpu); - ret =-1 ; - } - } + ret = microcode_ops->apply_microcode (cpu); +this_cpu_write (ucode_ctrl.result, ret); wait_for_siblings:if (!wait_for_cpus (&late_cpus_out))
@@ -350,19 +345,18 @@ wait_for_siblings:* per-cpu cpuinfo can be updated with right microcode * revision. */ -if (cpumask_first (topology_sibling_cpumask (cpu)) != cpu) - err = microcode_ops->apply_microcode (cpu); +if (cpumask_first (topology_sibling_cpumask (cpu)) == cpu) +return 0 ; -return ret; + ret = microcode_ops->apply_microcode (cpu); +this_cpu_write (ucode_ctrl.result, ret); +return 0 ; } -/* -* Reload microcode late on all CPUs. Wait for a sec until they -* all gather together. -*/ -static int microcode_reload_late (void ) +static int load_late_stop_cpus (void ) { -int old = boot_cpu_data.microcode, ret; +unsigned int cpu, updated =0 , failed =0 , timedout =0 , siblings =0 ; +int old_rev = boot_cpu_data.microcode;struct cpuinfo_x86 prev_info;pr_err ("Attempting late microcode loading - it is dangerous and taints the kernel.\n" );
@@ -377,26 +371,47 @@ static int microcode_reload_late(void)*/ store_cpu_caps (&prev_info); - ret =stop_machine_cpuslocked (__reload_late,NULL , cpu_online_mask); +stop_machine_cpuslocked (load_cpus_stopped,NULL , cpu_online_mask); + +/* Analyze the results */ +for_each_cpu_and (cpu, cpu_present_mask, &cpus_booted_once_mask) { +switch (per_cpu (ucode_ctrl.result, cpu)) { +case UCODE_UPDATED : updated++;break ; +case UCODE_TIMEOUT : timedout++;break ; +case UCODE_OK : siblings++;break ; +default : failed++;break ; + } + }if (microcode_ops->finalize_late_load) - microcode_ops->finalize_late_load (ret); - -if (!ret) { -pr_info ("Reload succeeded, microcode revision: 0x%x -> 0x%x\n" , - old, boot_cpu_data.microcode); -microcode_check (&prev_info); -add_taint (TAINT_CPU_OUT_OF_SPEC ,LOCKDEP_STILL_OK ); - }else { -pr_info ("Reload failed, current microcode revision: 0x%x\n" , - boot_cpu_data.microcode); + microcode_ops->finalize_late_load (!updated); + +if (!updated) { +/* Nothing changed. */ +if (!failed && !timedout) +return 0 ; +pr_err ("update failed: %u CPUs failed %u CPUs timed out\n" , + failed, timedout); +return -EIO ; } -return ret; + +add_taint (TAINT_CPU_OUT_OF_SPEC ,LOCKDEP_STILL_OK ); +pr_info ("load: updated on %u primary CPUs with %u siblings\n" , updated, siblings); +if (failed || timedout) { +pr_err ("load incomplete. %u CPUs timed out or failed\n" , +num_online_cpus () - (updated + siblings)); + } +pr_info ("revision: 0x%x -> 0x%x\n" , old_rev, boot_cpu_data.microcode); +microcode_check (&prev_info); + +return updated + siblings ==num_online_cpus () ?0 : -EIO ; }/* -* Ensure that all required CPUs which are present and have been booted -* once are online. +* This function does two things: +* +* 1) Ensure that all required CPUs which are present and have been booted +* once are online. * * To pass this check, all primary threads must be online. *
@@ -407,9 +422,12 @@ static int microcode_reload_late(void)* behaviour is undefined. The default play_dead() implementation on * modern CPUs uses MWAIT, which is also not guaranteed to be safe * against a microcode update which affects MWAIT. +* +* 2) Initialize the per CPU control structure */ -static bool ensure_cpus_are_online (void ) +static bool setup_cpus (void ) { +struct microcode_ctrl ctrl = { .result =-1 , };unsigned int cpu;for_each_cpu_and (cpu, cpu_present_mask, &cpus_booted_once_mask) {
@@ -419,18 +437,20 @@ static bool ensure_cpus_are_online(void)return false; } } +/* Initialize the per CPU state */ +per_cpu (ucode_ctrl, cpu) = ctrl; }return true; } -static int ucode_load_late_locked (void ) +static int load_late_locked (void ) { -if (!ensure_cpus_are_online ()) +if (!setup_cpus ())return -EBUSY ;switch (microcode_ops->request_microcode_fw (0 , µcode_pdev->dev)) {case UCODE_NEW : -return microcode_reload_late (); +return load_late_stop_cpus ();case UCODE_NFOUND :return -ENOENT ;default :
@@ -450,7 +470,7 @@ static ssize_t reload_store(struct device *dev,return -EINVAL ;cpus_read_lock (); - ret =ucode_load_late_locked (); + ret =load_late_locked ();cpus_read_unlock ();return ret ? : size;
arch/x86/kernel/cpu/microcode/internal.h +1 -0
@@ -16,6 +16,7 @@ enum ucode_state {UCODE_UPDATED ,UCODE_NFOUND ,UCODE_ERROR , + UCODE_TIMEOUT, }; struct microcode_ops {
PATCH v4 22/30 x86/microcode: Add per CPU control field
In reply to Thomas GleixnerFrom: Thomas Gleixner <[email protected]>
Add a per CPU control field to ucode_ctrl and define constants for it which are going to be used to control the loading state machine.
In theory this could be a global control field, but a global control does not cover the following case:
15 primary CPUs load microcode successfully
1 primary CPU fails and returns with an error code
With global control the sibling of the failed CPU would either try again or the whole operation would be aborted with the consequence that the 15 siblings do not invoke the apply path and end up with inconsistent software state. The result in dmesg would be inconsistent too.
There are two additional fields added and initialized:
ctrl_cpu and secondaries. ctrl_cpu is the CPU number of the primary thread for now, but with the upcoming uniform loading at package or system scope this will be one CPU per package or just one CPU. Secondaries hands the control CPU a CPU mask which will be required to release the secondary CPUs out of the wait loop.
Preparatory change for implementing a properly split control flow for primary and secondary CPUs.
---
V4: Simplify control CPU selection
--- arch/x86/kernel/cpu/microcode/core.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-)
---
arch/x86/kernel/cpu/microcode/core.c +18 -2
@@ -281,8 +281,19 @@ static struct platform_device *microcode* requirement can be relaxed in the future. Right now, this is conservative * and good. */ +enum sibling_ctrl { +/* Spinwait with timeout */ +SCTRL_WAIT , +/* Invoke the microcode_apply() callback */ +SCTRL_APPLY , +/* Proceed without invoking the microcode_apply() callback */ +SCTRL_DONE , +}; +struct microcode_ctrl { +enum sibling_ctrl ctrl;enum ucode_state result; +unsigned int ctrl_cpu; }; static DEFINE_PER_CPU(struct microcode_ctrl, ucode_ctrl);
@@ -427,7 +438,7 @@ static int load_late_stop_cpus(void)*/ static bool setup_cpus (void ) { -struct microcode_ctrl ctrl = { .result =-1 , }; +struct microcode_ctrl ctrl = { .ctrl =SCTRL_WAIT , .result =-1 , };unsigned int cpu;for_each_cpu_and (cpu, cpu_present_mask, &cpus_booted_once_mask) {
@@ -437,7 +448,12 @@ static bool setup_cpus(void)return false; } } -/* Initialize the per CPU state */ + +/* +* Initialize the per CPU state. This is core scope for now, +* but prepared to take package or system scope into account. +*/ + ctrl.ctrl_cpu =cpumask_first (topology_sibling_cpumask (cpu));per_cpu (ucode_ctrl, cpu) = ctrl; }return true;
[tip: x86/microcode] x86/microcode: Add per CPU control field
The following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 537929fe93b931a13ab1830a9c9f27a95634c797 Gitweb: https://git.kernel.org/tip/537929fe93b931a13ab1830a9c9f27a95634c797 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 14:00:01 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Fri, 06 Oct 2023 15:12:24 +02:00
x86/microcode: Add per CPU control field
Add a per CPU control field to ucode_ctrl and define constants for it which are going to be used to control the loading state machine.
In theory this could be a global control field, but a global control does not cover the following case:
15 primary CPUs load microcode successfully
1 primary CPU fails and returns with an error code
With global control the sibling of the failed CPU would either try again or the whole operation would be aborted with the consequence that the 15 siblings do not invoke the apply path and end up with inconsistent software state. The result in dmesg would be inconsistent too.
There are two additional fields added and initialized:
ctrl_cpu and secondaries. ctrl_cpu is the CPU number of the primary thread for now, but with the upcoming uniform loading at package or system scope this will be one CPU per package or just one CPU. Secondaries hands the control CPU a CPU mask which will be required to release the secondary CPUs out of the wait loop.
Preparatory change for implementing a properly split control flow for primary and secondary CPUs.
--- arch/x86/kernel/cpu/microcode/core.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-)
arch/x86/kernel/cpu/microcode/core.c +18 -2
@@ -281,8 +281,19 @@ static struct platform_device *microcode_pdev;* requirement can be relaxed in the future. Right now, this is conservative * and good. */ +enum sibling_ctrl { +/* Spinwait with timeout */ +SCTRL_WAIT , +/* Invoke the microcode_apply() callback */ +SCTRL_APPLY , +/* Proceed without invoking the microcode_apply() callback */ +SCTRL_DONE , +}; +struct microcode_ctrl { +enum sibling_ctrl ctrl;enum ucode_state result; +unsigned int ctrl_cpu; }; static DEFINE_PER_CPU(struct microcode_ctrl, ucode_ctrl);
@@ -427,7 +438,7 @@ static int load_late_stop_cpus(void)*/ static bool setup_cpus (void ) { -struct microcode_ctrl ctrl = { .result =-1 , }; +struct microcode_ctrl ctrl = { .ctrl =SCTRL_WAIT , .result =-1 , };unsigned int cpu;for_each_cpu_and (cpu, cpu_present_mask, &cpus_booted_once_mask) {
@@ -437,7 +448,12 @@ static bool setup_cpus(void)return false; } } -/* Initialize the per CPU state */ + +/* +* Initialize the per CPU state. This is core scope for now, +* but prepared to take package or system scope into account. +*/ + ctrl.ctrl_cpu =cpumask_first (topology_sibling_cpumask (cpu));per_cpu (ucode_ctrl, cpu) = ctrl; }return true;
[tip: x86/microcode] x86/microcode: Add per CPU control field
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: c91feaf8c8fabfccf32e78cc3576049fdb37ecf4 Gitweb: https://git.kernel.org/tip/c91feaf8c8fabfccf32e78cc3576049fdb37ecf4 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 14:00:01 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Thu, 19 Oct 2023 15:57:56 +02:00
x86/microcode: Add per CPU control field
Add a per CPU control field to ucode_ctrl and define constants for it which are going to be used to control the loading state machine.
In theory this could be a global control field, but a global control does not cover the following case:
15 primary CPUs load microcode successfully
1 primary CPU fails and returns with an error code
With global control the sibling of the failed CPU would either try again or the whole operation would be aborted with the consequence that the 15 siblings do not invoke the apply path and end up with inconsistent software state. The result in dmesg would be inconsistent too.
There are two additional fields added and initialized:
ctrl_cpu and secondaries. ctrl_cpu is the CPU number of the primary thread for now, but with the upcoming uniform loading at package or system scope this will be one CPU per package or just one CPU. Secondaries hands the control CPU a CPU mask which will be required to release the secondary CPUs out of the wait loop.
Preparatory change for implementing a properly split control flow for primary and secondary CPUs.
--- arch/x86/kernel/cpu/microcode/core.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-)
arch/x86/kernel/cpu/microcode/core.c +18 -2
@@ -252,8 +252,19 @@ static struct platform_device *microcode_pdev;* requirement can be relaxed in the future. Right now, this is conservative * and good. */ +enum sibling_ctrl { +/* Spinwait with timeout */ +SCTRL_WAIT , +/* Invoke the microcode_apply() callback */ +SCTRL_APPLY , +/* Proceed without invoking the microcode_apply() callback */ +SCTRL_DONE , +}; +struct microcode_ctrl { +enum sibling_ctrl ctrl;enum ucode_state result; +unsigned int ctrl_cpu; }; static DEFINE_PER_CPU(struct microcode_ctrl, ucode_ctrl);
@@ -398,7 +409,7 @@ static int load_late_stop_cpus(void)*/ static bool setup_cpus (void ) { -struct microcode_ctrl ctrl = { .result =-1 , }; +struct microcode_ctrl ctrl = { .ctrl =SCTRL_WAIT , .result =-1 , };unsigned int cpu;for_each_cpu_and (cpu, cpu_present_mask, &cpus_booted_once_mask) {
@@ -408,7 +419,12 @@ static bool setup_cpus(void)return false; } } -/* Initialize the per CPU state */ + +/* +* Initialize the per CPU state. This is core scope for now, +* but prepared to take package or system scope into account. +*/ + ctrl.ctrl_cpu =cpumask_first (topology_sibling_cpumask (cpu));per_cpu (ucode_ctrl, cpu) = ctrl; }return true;
[tip: x86/microcode] x86/microcode: Add per CPU control field
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: ba3aeb97cb2c53025356f31c5a0a294385194115 Gitweb: https://git.kernel.org/tip/ba3aeb97cb2c53025356f31c5a0a294385194115 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 14:00:01 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Tue, 24 Oct 2023 15:05:54 +02:00
x86/microcode: Add per CPU control field
Add a per CPU control field to ucode_ctrl and define constants for it which are going to be used to control the loading state machine.
In theory this could be a global control field, but a global control does not cover the following case:
15 primary CPUs load microcode successfully
1 primary CPU fails and returns with an error code
With global control the sibling of the failed CPU would either try again or the whole operation would be aborted with the consequence that the 15 siblings do not invoke the apply path and end up with inconsistent software state. The result in dmesg would be inconsistent too.
There are two additional fields added and initialized:
ctrl_cpu and secondaries. ctrl_cpu is the CPU number of the primary thread for now, but with the upcoming uniform loading at package or system scope this will be one CPU per package or just one CPU. Secondaries hands the control CPU a CPU mask which will be required to release the secondary CPUs out of the wait loop.
Preparatory change for implementing a properly split control flow for primary and secondary CPUs.
--- arch/x86/kernel/cpu/microcode/core.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-)
arch/x86/kernel/cpu/microcode/core.c +18 -2
@@ -252,8 +252,19 @@ static struct platform_device *microcode_pdev;* requirement can be relaxed in the future. Right now, this is conservative * and good. */ +enum sibling_ctrl { +/* Spinwait with timeout */ +SCTRL_WAIT , +/* Invoke the microcode_apply() callback */ +SCTRL_APPLY , +/* Proceed without invoking the microcode_apply() callback */ +SCTRL_DONE , +}; +struct microcode_ctrl { +enum sibling_ctrl ctrl;enum ucode_state result; +unsigned int ctrl_cpu; }; static DEFINE_PER_CPU(struct microcode_ctrl, ucode_ctrl);
@@ -398,7 +409,7 @@ static int load_late_stop_cpus(void)*/ static bool setup_cpus (void ) { -struct microcode_ctrl ctrl = { .result =-1 , }; +struct microcode_ctrl ctrl = { .ctrl =SCTRL_WAIT , .result =-1 , };unsigned int cpu;for_each_cpu_and (cpu, cpu_present_mask, &cpus_booted_once_mask) {
@@ -408,7 +419,12 @@ static bool setup_cpus(void)return false; } } -/* Initialize the per CPU state */ + +/* +* Initialize the per CPU state. This is core scope for now, +* but prepared to take package or system scope into account. +*/ + ctrl.ctrl_cpu =cpumask_first (topology_sibling_cpumask (cpu));per_cpu (ucode_ctrl, cpu) = ctrl; }return true;
PATCH v4 23/30 x86/microcode: Provide new control functions
In reply to Thomas GleixnerFrom: Thomas Gleixner <[email protected]>
The current all in one code is unreadable and really not suited for adding future features like uniform loading with package or system scope.
Provide a set of new control functions which split the handling of the primary and secondary CPUs. These will replace the current rendezvouz all in one function in the next step. This is intentionally a separate change because diff makes an complete unreadable mess otherwise.
So the flow separates the primary and the secondary CPUs into their own functions, which use the control field in the per CPU ucode_ctrl struct.
primary() secondary()
wait_for_all() wait_for_all()
apply_ucode() wait_for_release()
release() apply_ucode()--- arch/x86/kernel/cpu/microcode/core.c | 84 +++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+)
---
arch/x86/kernel/cpu/microcode/core.c +84 -0
@@ -319,6 +319,90 @@ static bool wait_for_cpus(atomic_t *cnt)return false; } +static bool wait_for_ctrl (void ) +{ +unsigned int timeout; + +for (timeout =0 ; timeout <USEC_PER_SEC ; timeout++) { +if (this_cpu_read (ucode_ctrl.ctrl) !=SCTRL_WAIT ) +return true; +udelay (1 ); +if (!(timeout %1000 )) +touch_nmi_watchdog (); + } +return false; +} + +static __maybe_unused void load_secondary (unsigned int cpu) +{ +unsigned int ctrl_cpu =this_cpu_read (ucode_ctrl.ctrl_cpu); +enum ucode_state ret; + +/* Initial rendezvouz to ensure that all CPUs have arrived */ +if (!wait_for_cpus (&late_cpus_in)) { +pr_err_once ("load: %d CPUs timed out\n" ,atomic_read (&late_cpus_in) -1 ); +this_cpu_write (ucode_ctrl.result,UCODE_TIMEOUT ); +return ; + } + +/* +* Wait for primary threads to complete. If one of them hangs due +* to the update, there is no way out. This is non-recoverable +* because the CPU might hold locks or resources and confuse the +* scheduler, watchdogs etc. There is no way to safely evacuate the +* machine. +*/ +if (!wait_for_ctrl ()) +panic ("Microcode load: Primary CPU %d timed out\n" , ctrl_cpu); + +/* +* If the primary succeeded then invoke the apply() callback, +* otherwise copy the state from the primary thread. +*/ +if (this_cpu_read (ucode_ctrl.ctrl) ==SCTRL_APPLY ) + ret = microcode_ops->apply_microcode (cpu); +else + ret =per_cpu (ucode_ctrl.result, ctrl_cpu); + +this_cpu_write (ucode_ctrl.result, ret); +this_cpu_write (ucode_ctrl.ctrl,SCTRL_DONE ); +} + +static __maybe_unused void load_primary (unsigned int cpu) +{ +struct cpumask *secondaries =topology_sibling_cpumask (cpu); +enum sibling_ctrl ctrl; +enum ucode_state ret; +unsigned int sibling; + +/* Initial rendezvouz to ensure that all CPUs have arrived */ +if (!wait_for_cpus (&late_cpus_in)) { +this_cpu_write (ucode_ctrl.result,UCODE_TIMEOUT ); +pr_err_once ("load: %d CPUs timed out\n" ,atomic_read (&late_cpus_in) -1 ); +return ; + } + + ret = microcode_ops->apply_microcode (cpu); +this_cpu_write (ucode_ctrl.result, ret); +this_cpu_write (ucode_ctrl.ctrl,SCTRL_DONE ); + +/* +* If the update was successful, let the siblings run the apply() +* callback. If not, tell them it's done. This also covers the +* case where the CPU has uniform loading at package or system +* scope implemented but does not advertise it. +*/ +if (ret ==UCODE_UPDATED || ret ==UCODE_OK ) + ctrl =SCTRL_APPLY ; +else + ctrl =SCTRL_DONE ; + +for_each_cpu (sibling, secondaries) { +if (sibling != cpu) +per_cpu (ucode_ctrl.ctrl, sibling) = ctrl; + } +} +static int load_cpus_stopped (void *unused) {int cpu =smp_processor_id ();
[tip: x86/microcode] x86/microcode: Provide new control functions
The following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 54f1ae8f731a733f45711b71117452c098e73dc4 Gitweb: https://git.kernel.org/tip/54f1ae8f731a733f45711b71117452c098e73dc4 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 14:00:02 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Fri, 06 Oct 2023 15:12:24 +02:00
x86/microcode: Provide new control functions
The current all in one code is unreadable and really not suited for adding future features like uniform loading with package or system scope.
Provide a set of new control functions which split the handling of the primary and secondary CPUs. These will replace the current rendezvous all in one function in the next step. This is intentionally a separate change because diff makes an complete unreadable mess otherwise.
So the flow separates the primary and the secondary CPUs into their own functions which use the control field in the per CPU ucode_ctrl struct.
primary() secondary()
wait_for_all() wait_for_all()
apply_ucode() wait_for_release()
release() apply_ucode()--- arch/x86/kernel/cpu/microcode/core.c | 84 +++++++++++++++++++++++++++- 1 file changed, 84 insertions(+)
arch/x86/kernel/cpu/microcode/core.c +84 -0
@@ -319,6 +319,90 @@ static bool wait_for_cpus(atomic_t *cnt)return false; } +static bool wait_for_ctrl (void ) +{ +unsigned int timeout; + +for (timeout =0 ; timeout <USEC_PER_SEC ; timeout++) { +if (this_cpu_read (ucode_ctrl.ctrl) !=SCTRL_WAIT ) +return true; +udelay (1 ); +if (!(timeout %1000 )) +touch_nmi_watchdog (); + } +return false; +} + +static __maybe_unused void load_secondary (unsigned int cpu) +{ +unsigned int ctrl_cpu =this_cpu_read (ucode_ctrl.ctrl_cpu); +enum ucode_state ret; + +/* Initial rendezvous to ensure that all CPUs have arrived */ +if (!wait_for_cpus (&late_cpus_in)) { +pr_err_once ("load: %d CPUs timed out\n" ,atomic_read (&late_cpus_in) -1 ); +this_cpu_write (ucode_ctrl.result,UCODE_TIMEOUT ); +return ; + } + +/* +* Wait for primary threads to complete. If one of them hangs due +* to the update, there is no way out. This is non-recoverable +* because the CPU might hold locks or resources and confuse the +* scheduler, watchdogs etc. There is no way to safely evacuate the +* machine. +*/ +if (!wait_for_ctrl ()) +panic ("Microcode load: Primary CPU %d timed out\n" , ctrl_cpu); + +/* +* If the primary succeeded then invoke the apply() callback, +* otherwise copy the state from the primary thread. +*/ +if (this_cpu_read (ucode_ctrl.ctrl) ==SCTRL_APPLY ) + ret = microcode_ops->apply_microcode (cpu); +else + ret =per_cpu (ucode_ctrl.result, ctrl_cpu); + +this_cpu_write (ucode_ctrl.result, ret); +this_cpu_write (ucode_ctrl.ctrl,SCTRL_DONE ); +} + +static __maybe_unused void load_primary (unsigned int cpu) +{ +struct cpumask *secondaries =topology_sibling_cpumask (cpu); +enum sibling_ctrl ctrl; +enum ucode_state ret; +unsigned int sibling; + +/* Initial rendezvous to ensure that all CPUs have arrived */ +if (!wait_for_cpus (&late_cpus_in)) { +this_cpu_write (ucode_ctrl.result,UCODE_TIMEOUT ); +pr_err_once ("load: %d CPUs timed out\n" ,atomic_read (&late_cpus_in) -1 ); +return ; + } + + ret = microcode_ops->apply_microcode (cpu); +this_cpu_write (ucode_ctrl.result, ret); +this_cpu_write (ucode_ctrl.ctrl,SCTRL_DONE ); + +/* +* If the update was successful, let the siblings run the apply() +* callback. If not, tell them it's done. This also covers the +* case where the CPU has uniform loading at package or system +* scope implemented but does not advertise it. +*/ +if (ret ==UCODE_UPDATED || ret ==UCODE_OK ) + ctrl =SCTRL_APPLY ; +else + ctrl =SCTRL_DONE ; + +for_each_cpu (sibling, secondaries) { +if (sibling != cpu) +per_cpu (ucode_ctrl.ctrl, sibling) = ctrl; + } +} +static int load_cpus_stopped (void *unused) {int cpu =smp_processor_id ();
[tip: x86/microcode] x86/microcode: Provide new control functions
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 1844a781fdfbcbff64bfa59bd71fc6a62fdcdac0 Gitweb: https://git.kernel.org/tip/1844a781fdfbcbff64bfa59bd71fc6a62fdcdac0 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 14:00:02 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Thu, 19 Oct 2023 15:58:39 +02:00
x86/microcode: Provide new control functions
The current all in one code is unreadable and really not suited for adding future features like uniform loading with package or system scope.
Provide a set of new control functions which split the handling of the primary and secondary CPUs. These will replace the current rendezvous all in one function in the next step. This is intentionally a separate change because diff makes an complete unreadable mess otherwise.
So the flow separates the primary and the secondary CPUs into their own functions which use the control field in the per CPU ucode_ctrl struct.
primary() secondary()
wait_for_all() wait_for_all()
apply_ucode() wait_for_release()
release() apply_ucode()--- arch/x86/kernel/cpu/microcode/core.c | 84 +++++++++++++++++++++++++++- 1 file changed, 84 insertions(+)
arch/x86/kernel/cpu/microcode/core.c +84 -0
@@ -290,6 +290,90 @@ static bool wait_for_cpus(atomic_t *cnt)return false; } +static bool wait_for_ctrl (void ) +{ +unsigned int timeout; + +for (timeout =0 ; timeout <USEC_PER_SEC ; timeout++) { +if (this_cpu_read (ucode_ctrl.ctrl) !=SCTRL_WAIT ) +return true; +udelay (1 ); +if (!(timeout %1000 )) +touch_nmi_watchdog (); + } +return false; +} + +static __maybe_unused void load_secondary (unsigned int cpu) +{ +unsigned int ctrl_cpu =this_cpu_read (ucode_ctrl.ctrl_cpu); +enum ucode_state ret; + +/* Initial rendezvous to ensure that all CPUs have arrived */ +if (!wait_for_cpus (&late_cpus_in)) { +pr_err_once ("load: %d CPUs timed out\n" ,atomic_read (&late_cpus_in) -1 ); +this_cpu_write (ucode_ctrl.result,UCODE_TIMEOUT ); +return ; + } + +/* +* Wait for primary threads to complete. If one of them hangs due +* to the update, there is no way out. This is non-recoverable +* because the CPU might hold locks or resources and confuse the +* scheduler, watchdogs etc. There is no way to safely evacuate the +* machine. +*/ +if (!wait_for_ctrl ()) +panic ("Microcode load: Primary CPU %d timed out\n" , ctrl_cpu); + +/* +* If the primary succeeded then invoke the apply() callback, +* otherwise copy the state from the primary thread. +*/ +if (this_cpu_read (ucode_ctrl.ctrl) ==SCTRL_APPLY ) + ret = microcode_ops->apply_microcode (cpu); +else + ret =per_cpu (ucode_ctrl.result, ctrl_cpu); + +this_cpu_write (ucode_ctrl.result, ret); +this_cpu_write (ucode_ctrl.ctrl,SCTRL_DONE ); +} + +static __maybe_unused void load_primary (unsigned int cpu) +{ +struct cpumask *secondaries =topology_sibling_cpumask (cpu); +enum sibling_ctrl ctrl; +enum ucode_state ret; +unsigned int sibling; + +/* Initial rendezvous to ensure that all CPUs have arrived */ +if (!wait_for_cpus (&late_cpus_in)) { +this_cpu_write (ucode_ctrl.result,UCODE_TIMEOUT ); +pr_err_once ("load: %d CPUs timed out\n" ,atomic_read (&late_cpus_in) -1 ); +return ; + } + + ret = microcode_ops->apply_microcode (cpu); +this_cpu_write (ucode_ctrl.result, ret); +this_cpu_write (ucode_ctrl.ctrl,SCTRL_DONE ); + +/* +* If the update was successful, let the siblings run the apply() +* callback. If not, tell them it's done. This also covers the +* case where the CPU has uniform loading at package or system +* scope implemented but does not advertise it. +*/ +if (ret ==UCODE_UPDATED || ret ==UCODE_OK ) + ctrl =SCTRL_APPLY ; +else + ctrl =SCTRL_DONE ; + +for_each_cpu (sibling, secondaries) { +if (sibling != cpu) +per_cpu (ucode_ctrl.ctrl, sibling) = ctrl; + } +} +static int load_cpus_stopped (void *unused) {int cpu =smp_processor_id ();
[tip: x86/microcode] x86/microcode: Provide new control functions
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 6067788f04b1020b316344fe34746f96d594a042 Gitweb: https://git.kernel.org/tip/6067788f04b1020b316344fe34746f96d594a042 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 14:00:02 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Tue, 24 Oct 2023 15:05:55 +02:00
x86/microcode: Provide new control functions
The current all in one code is unreadable and really not suited for adding future features like uniform loading with package or system scope.
Provide a set of new control functions which split the handling of the primary and secondary CPUs. These will replace the current rendezvous all in one function in the next step. This is intentionally a separate change because diff makes an complete unreadable mess otherwise.
So the flow separates the primary and the secondary CPUs into their own functions which use the control field in the per CPU ucode_ctrl struct.
primary() secondary()
wait_for_all() wait_for_all()
apply_ucode() wait_for_release()
release() apply_ucode()--- arch/x86/kernel/cpu/microcode/core.c | 84 +++++++++++++++++++++++++++- 1 file changed, 84 insertions(+)
arch/x86/kernel/cpu/microcode/core.c +84 -0
@@ -290,6 +290,90 @@ static bool wait_for_cpus(atomic_t *cnt)return false; } +static bool wait_for_ctrl (void ) +{ +unsigned int timeout; + +for (timeout =0 ; timeout <USEC_PER_SEC ; timeout++) { +if (this_cpu_read (ucode_ctrl.ctrl) !=SCTRL_WAIT ) +return true; +udelay (1 ); +if (!(timeout %1000 )) +touch_nmi_watchdog (); + } +return false; +} + +static __maybe_unused void load_secondary (unsigned int cpu) +{ +unsigned int ctrl_cpu =this_cpu_read (ucode_ctrl.ctrl_cpu); +enum ucode_state ret; + +/* Initial rendezvous to ensure that all CPUs have arrived */ +if (!wait_for_cpus (&late_cpus_in)) { +pr_err_once ("load: %d CPUs timed out\n" ,atomic_read (&late_cpus_in) -1 ); +this_cpu_write (ucode_ctrl.result,UCODE_TIMEOUT ); +return ; + } + +/* +* Wait for primary threads to complete. If one of them hangs due +* to the update, there is no way out. This is non-recoverable +* because the CPU might hold locks or resources and confuse the +* scheduler, watchdogs etc. There is no way to safely evacuate the +* machine. +*/ +if (!wait_for_ctrl ()) +panic ("Microcode load: Primary CPU %d timed out\n" , ctrl_cpu); + +/* +* If the primary succeeded then invoke the apply() callback, +* otherwise copy the state from the primary thread. +*/ +if (this_cpu_read (ucode_ctrl.ctrl) ==SCTRL_APPLY ) + ret = microcode_ops->apply_microcode (cpu); +else + ret =per_cpu (ucode_ctrl.result, ctrl_cpu); + +this_cpu_write (ucode_ctrl.result, ret); +this_cpu_write (ucode_ctrl.ctrl,SCTRL_DONE ); +} + +static __maybe_unused void load_primary (unsigned int cpu) +{ +struct cpumask *secondaries =topology_sibling_cpumask (cpu); +enum sibling_ctrl ctrl; +enum ucode_state ret; +unsigned int sibling; + +/* Initial rendezvous to ensure that all CPUs have arrived */ +if (!wait_for_cpus (&late_cpus_in)) { +this_cpu_write (ucode_ctrl.result,UCODE_TIMEOUT ); +pr_err_once ("load: %d CPUs timed out\n" ,atomic_read (&late_cpus_in) -1 ); +return ; + } + + ret = microcode_ops->apply_microcode (cpu); +this_cpu_write (ucode_ctrl.result, ret); +this_cpu_write (ucode_ctrl.ctrl,SCTRL_DONE ); + +/* +* If the update was successful, let the siblings run the apply() +* callback. If not, tell them it's done. This also covers the +* case where the CPU has uniform loading at package or system +* scope implemented but does not advertise it. +*/ +if (ret ==UCODE_UPDATED || ret ==UCODE_OK ) + ctrl =SCTRL_APPLY ; +else + ctrl =SCTRL_DONE ; + +for_each_cpu (sibling, secondaries) { +if (sibling != cpu) +per_cpu (ucode_ctrl.ctrl, sibling) = ctrl; + } +} +static int load_cpus_stopped (void *unused) {int cpu =smp_processor_id ();
PATCH v4 24/30 x86/microcode: Replace the all in one rendevouz handler
In reply to Thomas GleixnerFrom: Thomas Gleixner <[email protected]>
with a new handler which just separates the control flow of primary and secondary CPUs.
--- arch/x86/kernel/cpu/microcode/core.c | 51 ++++++----------------------------- 1 file changed, 9 insertions(+), 42 deletions(-)
---
arch/x86/kernel/cpu/microcode/core.c +9 -42
@@ -297,7 +297,7 @@ struct microcode_ctrl { }; static DEFINE_PER_CPU(struct microcode_ctrl, ucode_ctrl); -static atomic_t late_cpus_in, late_cpus_out; +static atomic_t late_cpus_in;static bool wait_for_cpus (atomic_t *cnt) {
@@ -333,7 +333,7 @@ static bool wait_for_ctrl(void)return false; } -static __maybe_unused void load_secondary (unsigned int cpu) +static void load_secondary (unsigned int cpu) {unsigned int ctrl_cpu =this_cpu_read (ucode_ctrl.ctrl_cpu);enum ucode_state ret;
@@ -368,7 +368,7 @@ static __maybe_unused void load_secondarthis_cpu_write (ucode_ctrl.ctrl,SCTRL_DONE ); } -static __maybe_unused void load_primary (unsigned int cpu) +static void load_primary (unsigned int cpu) {struct cpumask *secondaries =topology_sibling_cpumask (cpu);enum sibling_ctrl ctrl;
@@ -405,46 +405,14 @@ static __maybe_unused void load_primary(static int load_cpus_stopped (void *unused) { -int cpu =smp_processor_id (); -enum ucode_state ret; - -/* -* Wait for all CPUs to arrive. A load will not be attempted unless all -* CPUs show up. -* */ -if (!wait_for_cpus (&late_cpus_in)) { -this_cpu_write (ucode_ctrl.result,UCODE_TIMEOUT ); -return 0 ; - } - -/* -* On an SMT system, it suffices to load the microcode on one sibling of -* the core because the microcode engine is shared between the threads. -* Synchronization still needs to take place so that no concurrent -* loading attempts happen on multiple threads of an SMT core. See -* below. -*/ -if (cpumask_first (topology_sibling_cpumask (cpu)) != cpu) - goto wait_for_siblings; +unsigned int cpu =smp_processor_id (); - ret = microcode_ops->apply_microcode (cpu); -this_cpu_write (ucode_ctrl.result, ret); - -wait_for_siblings: -if (!wait_for_cpus (&late_cpus_out)) -panic ("Timeout during microcode update!\n" ); - -/* -* At least one thread has completed update on each core. -* For others, simply call the update to make sure the -* per-cpu cpuinfo can be updated with right microcode -* revision. -*/ -if (cpumask_first (topology_sibling_cpumask (cpu)) == cpu) -return 0 ; +if (this_cpu_read (ucode_ctrl.ctrl_cpu) == cpu) +load_primary (cpu); +else +load_secondary (cpu); - ret = microcode_ops->apply_microcode (cpu); -this_cpu_write (ucode_ctrl.result, ret); +/* No point to wait here. The CPUs will all wait in stop_machine(). */ return 0 ; }
@@ -458,7 +426,6 @@ static int load_late_stop_cpus(void)pr_err ("You should switch to early loading, if possible.\n" );atomic_set (&late_cpus_in,num_online_cpus ()); -atomic_set (&late_cpus_out,num_online_cpus ());/* * Take a snapshot before the microcode update in order to compare and
[tip: x86/microcode] x86/microcode: Replace the all-in-one rendevous handler
The following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 0eccca7cbc7ff614cb57ba26a8704e842c242c38 Gitweb: https://git.kernel.org/tip/0eccca7cbc7ff614cb57ba26a8704e842c242c38 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 14:00:03 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Fri, 06 Oct 2023 15:12:24 +02:00
x86/microcode: Replace the all-in-one rendevous handler
with a new handler which just separates the control flow of primary and secondary CPUs.
--- arch/x86/kernel/cpu/microcode/core.c | 51 ++++----------------------- 1 file changed, 9 insertions(+), 42 deletions(-)
arch/x86/kernel/cpu/microcode/core.c +9 -42
@@ -297,7 +297,7 @@ struct microcode_ctrl { }; static DEFINE_PER_CPU(struct microcode_ctrl, ucode_ctrl); -static atomic_t late_cpus_in, late_cpus_out; +static atomic_t late_cpus_in;static bool wait_for_cpus (atomic_t *cnt) {
@@ -333,7 +333,7 @@ static bool wait_for_ctrl(void)return false; } -static __maybe_unused void load_secondary (unsigned int cpu) +static void load_secondary (unsigned int cpu) {unsigned int ctrl_cpu =this_cpu_read (ucode_ctrl.ctrl_cpu);enum ucode_state ret;
@@ -368,7 +368,7 @@ static __maybe_unused void load_secondary(unsigned int cpu)this_cpu_write (ucode_ctrl.ctrl,SCTRL_DONE ); } -static __maybe_unused void load_primary (unsigned int cpu) +static void load_primary (unsigned int cpu) {struct cpumask *secondaries =topology_sibling_cpumask (cpu);enum sibling_ctrl ctrl;
@@ -405,46 +405,14 @@ static __maybe_unused void load_primary(unsigned int cpu)static int load_cpus_stopped (void *unused) { -int cpu =smp_processor_id (); -enum ucode_state ret; - -/* -* Wait for all CPUs to arrive. A load will not be attempted unless all -* CPUs show up. -* */ -if (!wait_for_cpus (&late_cpus_in)) { -this_cpu_write (ucode_ctrl.result,UCODE_TIMEOUT ); -return 0 ; - } - -/* -* On an SMT system, it suffices to load the microcode on one sibling of -* the core because the microcode engine is shared between the threads. -* Synchronization still needs to take place so that no concurrent -* loading attempts happen on multiple threads of an SMT core. See -* below. -*/ -if (cpumask_first (topology_sibling_cpumask (cpu)) != cpu) - goto wait_for_siblings; +unsigned int cpu =smp_processor_id (); - ret = microcode_ops->apply_microcode (cpu); -this_cpu_write (ucode_ctrl.result, ret); - -wait_for_siblings: -if (!wait_for_cpus (&late_cpus_out)) -panic ("Timeout during microcode update!\n" ); - -/* -* At least one thread has completed update on each core. -* For others, simply call the update to make sure the -* per-cpu cpuinfo can be updated with right microcode -* revision. -*/ -if (cpumask_first (topology_sibling_cpumask (cpu)) == cpu) -return 0 ; +if (this_cpu_read (ucode_ctrl.ctrl_cpu) == cpu) +load_primary (cpu); +else +load_secondary (cpu); - ret = microcode_ops->apply_microcode (cpu); -this_cpu_write (ucode_ctrl.result, ret); +/* No point to wait here. The CPUs will all wait in stop_machine(). */ return 0 ; }
@@ -458,7 +426,6 @@ static int load_late_stop_cpus(void)pr_err ("You should switch to early loading, if possible.\n" );atomic_set (&late_cpus_in,num_online_cpus ()); -atomic_set (&late_cpus_out,num_online_cpus ());/* * Take a snapshot before the microcode update in order to compare and
[tip: x86/microcode] x86/microcode: Replace the all-in-one rendevous handler
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: e1ce0eb22dc24da625fc59be2a382d133ddcf17f Gitweb: https://git.kernel.org/tip/e1ce0eb22dc24da625fc59be2a382d133ddcf17f Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 14:00:03 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Thu, 19 Oct 2023 15:59:08 +02:00
x86/microcode: Replace the all-in-one rendevous handler
with a new handler which just separates the control flow of primary and secondary CPUs.
--- arch/x86/kernel/cpu/microcode/core.c | 51 ++++----------------------- 1 file changed, 9 insertions(+), 42 deletions(-)
arch/x86/kernel/cpu/microcode/core.c +9 -42
@@ -268,7 +268,7 @@ struct microcode_ctrl { }; static DEFINE_PER_CPU(struct microcode_ctrl, ucode_ctrl); -static atomic_t late_cpus_in, late_cpus_out; +static atomic_t late_cpus_in;static bool wait_for_cpus (atomic_t *cnt) {
@@ -304,7 +304,7 @@ static bool wait_for_ctrl(void)return false; } -static __maybe_unused void load_secondary (unsigned int cpu) +static void load_secondary (unsigned int cpu) {unsigned int ctrl_cpu =this_cpu_read (ucode_ctrl.ctrl_cpu);enum ucode_state ret;
@@ -339,7 +339,7 @@ static __maybe_unused void load_secondary(unsigned int cpu)this_cpu_write (ucode_ctrl.ctrl,SCTRL_DONE ); } -static __maybe_unused void load_primary (unsigned int cpu) +static void load_primary (unsigned int cpu) {struct cpumask *secondaries =topology_sibling_cpumask (cpu);enum sibling_ctrl ctrl;
@@ -376,46 +376,14 @@ static __maybe_unused void load_primary(unsigned int cpu)static int load_cpus_stopped (void *unused) { -int cpu =smp_processor_id (); -enum ucode_state ret; - -/* -* Wait for all CPUs to arrive. A load will not be attempted unless all -* CPUs show up. -* */ -if (!wait_for_cpus (&late_cpus_in)) { -this_cpu_write (ucode_ctrl.result,UCODE_TIMEOUT ); -return 0 ; - } - -/* -* On an SMT system, it suffices to load the microcode on one sibling of -* the core because the microcode engine is shared between the threads. -* Synchronization still needs to take place so that no concurrent -* loading attempts happen on multiple threads of an SMT core. See -* below. -*/ -if (cpumask_first (topology_sibling_cpumask (cpu)) != cpu) - goto wait_for_siblings; +unsigned int cpu =smp_processor_id (); - ret = microcode_ops->apply_microcode (cpu); -this_cpu_write (ucode_ctrl.result, ret); - -wait_for_siblings: -if (!wait_for_cpus (&late_cpus_out)) -panic ("Timeout during microcode update!\n" ); - -/* -* At least one thread has completed update on each core. -* For others, simply call the update to make sure the -* per-cpu cpuinfo can be updated with right microcode -* revision. -*/ -if (cpumask_first (topology_sibling_cpumask (cpu)) == cpu) -return 0 ; +if (this_cpu_read (ucode_ctrl.ctrl_cpu) == cpu) +load_primary (cpu); +else +load_secondary (cpu); - ret = microcode_ops->apply_microcode (cpu); -this_cpu_write (ucode_ctrl.result, ret); +/* No point to wait here. The CPUs will all wait in stop_machine(). */ return 0 ; }
@@ -429,7 +397,6 @@ static int load_late_stop_cpus(void)pr_err ("You should switch to early loading, if possible.\n" );atomic_set (&late_cpus_in,num_online_cpus ()); -atomic_set (&late_cpus_out,num_online_cpus ());/* * Take a snapshot before the microcode update in order to compare and
[tip: x86/microcode] x86/microcode: Replace the all-in-one rendevous handler
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 0bf871651211b58c7b19f40b746b646d5311e2ec Gitweb: https://git.kernel.org/tip/0bf871651211b58c7b19f40b746b646d5311e2ec Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 14:00:03 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Tue, 24 Oct 2023 15:05:55 +02:00
x86/microcode: Replace the all-in-one rendevous handler
with a new handler which just separates the control flow of primary and secondary CPUs.
--- arch/x86/kernel/cpu/microcode/core.c | 51 ++++----------------------- 1 file changed, 9 insertions(+), 42 deletions(-)
arch/x86/kernel/cpu/microcode/core.c +9 -42
@@ -268,7 +268,7 @@ struct microcode_ctrl { }; static DEFINE_PER_CPU(struct microcode_ctrl, ucode_ctrl); -static atomic_t late_cpus_in, late_cpus_out; +static atomic_t late_cpus_in;static bool wait_for_cpus (atomic_t *cnt) {
@@ -304,7 +304,7 @@ static bool wait_for_ctrl(void)return false; } -static __maybe_unused void load_secondary (unsigned int cpu) +static void load_secondary (unsigned int cpu) {unsigned int ctrl_cpu =this_cpu_read (ucode_ctrl.ctrl_cpu);enum ucode_state ret;
@@ -339,7 +339,7 @@ static __maybe_unused void load_secondary(unsigned int cpu)this_cpu_write (ucode_ctrl.ctrl,SCTRL_DONE ); } -static __maybe_unused void load_primary (unsigned int cpu) +static void load_primary (unsigned int cpu) {struct cpumask *secondaries =topology_sibling_cpumask (cpu);enum sibling_ctrl ctrl;
@@ -376,46 +376,14 @@ static __maybe_unused void load_primary(unsigned int cpu)static int load_cpus_stopped (void *unused) { -int cpu =smp_processor_id (); -enum ucode_state ret; - -/* -* Wait for all CPUs to arrive. A load will not be attempted unless all -* CPUs show up. -* */ -if (!wait_for_cpus (&late_cpus_in)) { -this_cpu_write (ucode_ctrl.result,UCODE_TIMEOUT ); -return 0 ; - } - -/* -* On an SMT system, it suffices to load the microcode on one sibling of -* the core because the microcode engine is shared between the threads. -* Synchronization still needs to take place so that no concurrent -* loading attempts happen on multiple threads of an SMT core. See -* below. -*/ -if (cpumask_first (topology_sibling_cpumask (cpu)) != cpu) - goto wait_for_siblings; +unsigned int cpu =smp_processor_id (); - ret = microcode_ops->apply_microcode (cpu); -this_cpu_write (ucode_ctrl.result, ret); - -wait_for_siblings: -if (!wait_for_cpus (&late_cpus_out)) -panic ("Timeout during microcode update!\n" ); - -/* -* At least one thread has completed update on each core. -* For others, simply call the update to make sure the -* per-cpu cpuinfo can be updated with right microcode -* revision. -*/ -if (cpumask_first (topology_sibling_cpumask (cpu)) == cpu) -return 0 ; +if (this_cpu_read (ucode_ctrl.ctrl_cpu) == cpu) +load_primary (cpu); +else +load_secondary (cpu); - ret = microcode_ops->apply_microcode (cpu); -this_cpu_write (ucode_ctrl.result, ret); +/* No point to wait here. The CPUs will all wait in stop_machine(). */ return 0 ; }
@@ -429,7 +397,6 @@ static int load_late_stop_cpus(void)pr_err ("You should switch to early loading, if possible.\n" );atomic_set (&late_cpus_in,num_online_cpus ()); -atomic_set (&late_cpus_out,num_online_cpus ());/* * Take a snapshot before the microcode update in order to compare and
PATCH v4 25/30 x86/microcode: Rendezvous and load in NMI
In reply to Thomas GleixnerFrom: Thomas Gleixner <[email protected]>
stop_machine() does not prevent the spin-waiting sibling from handling an NMI, which is obviously violating the whole concept of rendezvous.
Implement a static branch right in the beginning of the NMI handler which is NOOPed except when enabled by the late loading mechanism.
The late loader enables the static branch before stop_machine() is invoked. Each CPU has an nmi_enable in its control structure which indicates whether the CPU should go into the update routine.
This is required to bridge the gap between enabling the branch and actually being at the point where it is required to enter the loader wait loop.
Each CPU which arrives in the stopper thread function sets that flag and issues a self NMI right after that. If the NMI function sees the flag clear, it returns. If it's set it clears the flag and enters the rendezvous.
This is safe against a real NMI which hits in between setting the flag and sending the NMI to itself. The real NMI will be swallowed by the microcode update and the self NMI will then let stuff continue. Otherwise this would end up with a spurious NMI.
--- arch/x86/include/asm/microcode.h | 12 ++++++++ arch/x86/kernel/cpu/microcode/core.c | 42 ++++++++++++++++++++++++++++--- arch/x86/kernel/cpu/microcode/intel.c | 1 arch/x86/kernel/cpu/microcode/internal.h | 3 +- arch/x86/kernel/nmi.c | 4 ++ 5 files changed, 57 insertions(+), 5 deletions(-)
---
arch/x86/include/asm/microcode.h +12 -0
@@ -70,4 +70,16 @@ static inline u32 intel_get_microcode_re }#endif /* !CONFIG_CPU_SUP_INTEL */ +bool microcode_nmi_handler (void ); + +#ifdef CONFIG_MICROCODE_LATE_LOADING +DECLARE_STATIC_KEY_FALSE (microcode_nmi_handler_enable); +static __always_inline bool microcode_nmi_handler_enabled(void) +{ +return static_branch_unlikely (µcode_nmi_handler_enable); +} +#else +static __always_inline bool microcode_nmi_handler_enabled(void) { return false; } +#endif +#endif /* _ASM_X86_MICROCODE_H */
arch/x86/kernel/cpu/microcode/core.c +38 -4
94 lines not on this page · raw message
arch/x86/kernel/cpu/microcode/intel.c +1 -0
@@ -601,6 +601,7 @@ static struct microcode_ops microcode_in .collect_cpu_info = collect_cpu_info, .apply_microcode = apply_microcode_late, .finalize_late_load = finalize_late_load, + .use_nmi = IS_ENABLED(CONFIG_X86_64), };static __init void calc_llc_size_per_core (struct cpuinfo_x86 *c)
arch/x86/kernel/cpu/microcode/internal.h +2 -1
@@ -31,7 +31,8 @@ struct microcode_ops {enum ucode_state (*apply_microcode)(int cpu);int (*collect_cpu_info)(int cpu,struct cpu_signature *csig);void (*finalize_late_load)(int result); - unsigned int nmi_safe : 1; + unsigned int nmi_safe : 1, + use_nmi : 1; };extern struct ucode_cpu_info ucode_cpu_info[];
arch/x86/kernel/nmi.c +4 -0
@@ -33,6 +33,7 @@#include <asm/reboot.h> #include <asm/cache.h> #include <asm/nospec-branch.h> +#include <asm/microcode.h> #include <asm/sev.h> #define CREATE_TRACE_POINTS
@@ -343,6 +344,9 @@ static noinstr void default_do_nmi(strucinstrumentation_begin (); +if (microcode_nmi_handler_enabled () &µcode_nmi_handler ()) + goto out; + handled =nmi_handle (NMI_LOCAL , regs);__this_cpu_add (nmi_stats.normal, handled);if (handled) {
[tip: x86/microcode] x86/microcode: Rendezvous and load in NMI
The following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 0d834131052b702d74ed93c86678f76cc5ad6a62 Gitweb: https://git.kernel.org/tip/0d834131052b702d74ed93c86678f76cc5ad6a62 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 14:00:05 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Fri, 06 Oct 2023 15:12:24 +02:00
x86/microcode: Rendezvous and load in NMI
stop_machine() does not prevent the spin-waiting sibling from handling an NMI, which is obviously violating the whole concept of rendezvous.
Implement a static branch right in the beginning of the NMI handler which is nopped out except when enabled by the late loading mechanism.
The late loader enables the static branch before stop_machine() is invoked. Each CPU has an nmi_enable in its control structure which indicates whether the CPU should go into the update routine.
This is required to bridge the gap between enabling the branch and actually being at the point where it is required to enter the loader wait loop.
Each CPU which arrives in the stopper thread function sets that flag and issues a self NMI right after that. If the NMI function sees the flag clear, it returns. If it's set it clears the flag and enters the rendezvous.
This is safe against a real NMI which hits in between setting the flag and sending the NMI to itself. The real NMI will be swallowed by the microcode update and the self NMI will then let stuff continue. Otherwise this would end up with a spurious NMI.
--- arch/x86/include/asm/microcode.h | 12 +++++++- arch/x86/kernel/cpu/microcode/core.c | 42 ++++++++++++++++++++--- arch/x86/kernel/cpu/microcode/intel.c | 1 +- arch/x86/kernel/cpu/microcode/internal.h | 3 +- arch/x86/kernel/nmi.c | 4 ++- 5 files changed, 57 insertions(+), 5 deletions(-)
arch/x86/include/asm/microcode.h +12 -0
@@ -70,4 +70,16 @@ static inline u32 intel_get_microcode_revision(void) }#endif /* !CONFIG_CPU_SUP_INTEL */ +bool microcode_nmi_handler (void ); + +#ifdef CONFIG_MICROCODE_LATE_LOADING +DECLARE_STATIC_KEY_FALSE (microcode_nmi_handler_enable); +static __always_inline bool microcode_nmi_handler_enabled(void) +{ +return static_branch_unlikely (µcode_nmi_handler_enable); +} +#else +static __always_inline bool microcode_nmi_handler_enabled(void) { return false; } +#endif +#endif /* _ASM_X86_MICROCODE_H */
arch/x86/kernel/cpu/microcode/core.c +38 -4
94 lines not on this page · raw message
arch/x86/kernel/cpu/microcode/intel.c +1 -0
@@ -602,6 +602,7 @@ static struct microcode_ops microcode_intel_ops = { .collect_cpu_info = collect_cpu_info, .apply_microcode = apply_microcode_late, .finalize_late_load = finalize_late_load, + .use_nmi = IS_ENABLED(CONFIG_X86_64), };static __init void calc_llc_size_per_core (struct cpuinfo_x86 *c)
arch/x86/kernel/cpu/microcode/internal.h +2 -1
@@ -31,7 +31,8 @@ struct microcode_ops {enum ucode_state (*apply_microcode)(int cpu);int (*collect_cpu_info)(int cpu,struct cpu_signature *csig);void (*finalize_late_load)(int result); - unsigned int nmi_safe : 1; + unsigned int nmi_safe : 1, + use_nmi : 1; };extern struct ucode_cpu_info ucode_cpu_info[];
arch/x86/kernel/nmi.c +4 -0
@@ -33,6 +33,7 @@#include <asm/reboot.h> #include <asm/cache.h> #include <asm/nospec-branch.h> +#include <asm/microcode.h> #include <asm/sev.h> #define CREATE_TRACE_POINTS
@@ -343,6 +344,9 @@ static noinstr void default_do_nmi(struct pt_regs *regs)instrumentation_begin (); +if (microcode_nmi_handler_enabled () &µcode_nmi_handler ()) + goto out; + handled =nmi_handle (NMI_LOCAL , regs);__this_cpu_add (nmi_stats.normal, handled);if (handled) {
[tip: x86/microcode] x86/microcode: Rendezvous and load in NMI
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 8858b12182058468542cb5d0a4587895f6ec473c Gitweb: https://git.kernel.org/tip/8858b12182058468542cb5d0a4587895f6ec473c Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 14:00:05 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Thu, 19 Oct 2023 15:59:38 +02:00
x86/microcode: Rendezvous and load in NMI
stop_machine() does not prevent the spin-waiting sibling from handling an NMI, which is obviously violating the whole concept of rendezvous.
Implement a static branch right in the beginning of the NMI handler which is nopped out except when enabled by the late loading mechanism.
The late loader enables the static branch before stop_machine() is invoked. Each CPU has an nmi_enable in its control structure which indicates whether the CPU should go into the update routine.
This is required to bridge the gap between enabling the branch and actually being at the point where it is required to enter the loader wait loop.
Each CPU which arrives in the stopper thread function sets that flag and issues a self NMI right after that. If the NMI function sees the flag clear, it returns. If it's set it clears the flag and enters the rendezvous.
This is safe against a real NMI which hits in between setting the flag and sending the NMI to itself. The real NMI will be swallowed by the microcode update and the self NMI will then let stuff continue. Otherwise this would end up with a spurious NMI.
--- arch/x86/include/asm/microcode.h | 12 +++++++- arch/x86/kernel/cpu/microcode/core.c | 42 ++++++++++++++++++++--- arch/x86/kernel/cpu/microcode/intel.c | 1 +- arch/x86/kernel/cpu/microcode/internal.h | 3 +- arch/x86/kernel/nmi.c | 4 ++- 5 files changed, 57 insertions(+), 5 deletions(-)
arch/x86/include/asm/microcode.h +12 -0
@@ -72,4 +72,16 @@ static inline u32 intel_get_microcode_revision(void) }#endif /* !CONFIG_CPU_SUP_INTEL */ +bool microcode_nmi_handler (void ); + +#ifdef CONFIG_MICROCODE_LATE_LOADING +DECLARE_STATIC_KEY_FALSE (microcode_nmi_handler_enable); +static __always_inline bool microcode_nmi_handler_enabled(void) +{ +return static_branch_unlikely (µcode_nmi_handler_enable); +} +#else +static __always_inline bool microcode_nmi_handler_enabled(void) { return false; } +#endif +#endif /* _ASM_X86_MICROCODE_H */
arch/x86/kernel/cpu/microcode/core.c +38 -4
94 lines not on this page · raw message
arch/x86/kernel/cpu/microcode/intel.c +1 -0
@@ -595,6 +595,7 @@ static struct microcode_ops microcode_intel_ops = { .collect_cpu_info = collect_cpu_info, .apply_microcode = apply_microcode_late, .finalize_late_load = finalize_late_load, + .use_nmi = IS_ENABLED(CONFIG_X86_64), };static __init void calc_llc_size_per_core (struct cpuinfo_x86 *c)
arch/x86/kernel/cpu/microcode/internal.h +2 -1
@@ -31,7 +31,8 @@ struct microcode_ops {enum ucode_state (*apply_microcode)(int cpu);int (*collect_cpu_info)(int cpu,struct cpu_signature *csig);void (*finalize_late_load)(int result); - unsigned int nmi_safe : 1; + unsigned int nmi_safe : 1, + use_nmi : 1; };extern struct ucode_cpu_info ucode_cpu_info[];
arch/x86/kernel/nmi.c +4 -0
@@ -33,6 +33,7 @@#include <asm/reboot.h> #include <asm/cache.h> #include <asm/nospec-branch.h> +#include <asm/microcode.h> #include <asm/sev.h> #define CREATE_TRACE_POINTS
@@ -343,6 +344,9 @@ static noinstr void default_do_nmi(struct pt_regs *regs)instrumentation_begin (); +if (microcode_nmi_handler_enabled () &µcode_nmi_handler ()) + goto out; + handled =nmi_handle (NMI_LOCAL , regs);__this_cpu_add (nmi_stats.normal, handled);if (handled) {
[tip: x86/microcode] x86/microcode: Rendezvous and load in NMI
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 7eb314a22800457396f541c655697dabd71e44a7 Gitweb: https://git.kernel.org/tip/7eb314a22800457396f541c655697dabd71e44a7 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 14:00:05 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Tue, 24 Oct 2023 15:05:55 +02:00
x86/microcode: Rendezvous and load in NMI
stop_machine() does not prevent the spin-waiting sibling from handling an NMI, which is obviously violating the whole concept of rendezvous.
Implement a static branch right in the beginning of the NMI handler which is nopped out except when enabled by the late loading mechanism.
The late loader enables the static branch before stop_machine() is invoked. Each CPU has an nmi_enable in its control structure which indicates whether the CPU should go into the update routine.
This is required to bridge the gap between enabling the branch and actually being at the point where it is required to enter the loader wait loop.
Each CPU which arrives in the stopper thread function sets that flag and issues a self NMI right after that. If the NMI function sees the flag clear, it returns. If it's set it clears the flag and enters the rendezvous.
This is safe against a real NMI which hits in between setting the flag and sending the NMI to itself. The real NMI will be swallowed by the microcode update and the self NMI will then let stuff continue. Otherwise this would end up with a spurious NMI.
--- arch/x86/include/asm/microcode.h | 12 +++++++- arch/x86/kernel/cpu/microcode/core.c | 42 ++++++++++++++++++++--- arch/x86/kernel/cpu/microcode/intel.c | 1 +- arch/x86/kernel/cpu/microcode/internal.h | 3 +- arch/x86/kernel/nmi.c | 4 ++- 5 files changed, 57 insertions(+), 5 deletions(-)
arch/x86/include/asm/microcode.h +12 -0
@@ -72,4 +72,16 @@ static inline u32 intel_get_microcode_revision(void) }#endif /* !CONFIG_CPU_SUP_INTEL */ +bool microcode_nmi_handler (void ); + +#ifdef CONFIG_MICROCODE_LATE_LOADING +DECLARE_STATIC_KEY_FALSE (microcode_nmi_handler_enable); +static __always_inline bool microcode_nmi_handler_enabled(void) +{ +return static_branch_unlikely (µcode_nmi_handler_enable); +} +#else +static __always_inline bool microcode_nmi_handler_enabled(void) { return false; } +#endif +#endif /* _ASM_X86_MICROCODE_H */
arch/x86/kernel/cpu/microcode/core.c +38 -4
94 lines not on this page · raw message
arch/x86/kernel/cpu/microcode/intel.c +1 -0
@@ -611,6 +611,7 @@ static struct microcode_ops microcode_intel_ops = { .collect_cpu_info = collect_cpu_info, .apply_microcode = apply_microcode_late, .finalize_late_load = finalize_late_load, + .use_nmi = IS_ENABLED(CONFIG_X86_64), };static __init void calc_llc_size_per_core (struct cpuinfo_x86 *c)
arch/x86/kernel/cpu/microcode/internal.h +2 -1
@@ -31,7 +31,8 @@ struct microcode_ops {enum ucode_state (*apply_microcode)(int cpu);int (*collect_cpu_info)(int cpu,struct cpu_signature *csig);void (*finalize_late_load)(int result); - unsigned int nmi_safe : 1; + unsigned int nmi_safe : 1, + use_nmi : 1; };extern struct ucode_cpu_info ucode_cpu_info[];
arch/x86/kernel/nmi.c +4 -0
@@ -33,6 +33,7 @@#include <asm/reboot.h> #include <asm/cache.h> #include <asm/nospec-branch.h> +#include <asm/microcode.h> #include <asm/sev.h> #define CREATE_TRACE_POINTS
@@ -343,6 +344,9 @@ static noinstr void default_do_nmi(struct pt_regs *regs)instrumentation_begin (); +if (microcode_nmi_handler_enabled () &µcode_nmi_handler ()) + goto out; + handled =nmi_handle (NMI_LOCAL , regs);__this_cpu_add (nmi_stats.normal, handled);if (handled) {
PATCH v4 26/30 x86/microcode: Protect against instrumentation
In reply to Thomas GleixnerFrom: Thomas Gleixner <[email protected]>
The wait for control loop in which the siblings are waiting for the microcode update on the primary thread must be protected against instrumentation as instrumentation can end up in #INT3, #DB or #PF, which then returns with IRET. That IRET reenables NMI which is the opposite of what the NMI rendezvouz is trying to achieve.
--- arch/x86/kernel/cpu/microcode/core.c | 110 ++++++++++++++++++++++++++--------- 1 file changed, 82 insertions(+), 28 deletions(-)
---
arch/x86/kernel/cpu/microcode/core.c +82 -28
@@ -301,54 +301,65 @@ struct microcode_ctrl {DEFINE_STATIC_KEY_FALSE (microcode_nmi_handler_enable); static DEFINE_PER_CPU(struct microcode_ctrl, ucode_ctrl); +static unsigned int loops_per_usec;static atomic_t late_cpus_in; -static bool wait_for_cpus (atomic_t *cnt) +static noinstr bool wait_for_cpus(atomic_t *cnt) { -unsigned int timeout; +unsigned int timeout, loops; -WARN_ON_ONCE (atomic_dec_return (cnt) <0 ); +WARN_ON_ONCE (raw_atomic_dec_return (cnt) <0 );for (timeout =0 ; timeout <USEC_PER_SEC ; timeout++) { -if (!atomic_read (cnt)) +if (!raw_atomic_read (cnt))return true; -udelay (1 ); +for (loops =0 ; loops < loops_per_usec; loops++) +cpu_relax ();/* If invoked directly, tickle the NMI watchdog */ -if (!microcode_ops->use_nmi && !(timeout %USEC_PER_MSEC )) +if (!microcode_ops->use_nmi && !(timeout %USEC_PER_MSEC )) { +instrumentation_begin ();touch_nmi_watchdog (); +instrumentation_end (); + } }/* Prevent the late comers from making progress and let them time out */ -atomic_inc (cnt); +raw_atomic_inc (cnt);return false; } -static bool wait_for_ctrl (void ) +static noinstr bool wait_for_ctrl(void) { -unsigned int timeout; +unsigned int timeout, loops;for (timeout =0 ; timeout <USEC_PER_SEC ; timeout++) { -if (this_cpu_read (ucode_ctrl.ctrl) !=SCTRL_WAIT ) +if (raw_cpu_read (ucode_ctrl.ctrl) !=SCTRL_WAIT )return true; -udelay (1 ); + +for (loops =0 ; loops < loops_per_usec; loops++) +cpu_relax (); +/* If invoked directly, tickle the NMI watchdog */ -if (!microcode_ops->use_nmi && !(timeout %1000 )) +if (!microcode_ops->use_nmi && !(timeout %USEC_PER_MSEC )) { +instrumentation_begin ();touch_nmi_watchdog (); +instrumentation_end (); + } }return false; } -static void load_secondary (unsigned int cpu) +/* +* Protected against instrumentation up to the point where the primary +* thread completed the update. See microcode_nmi_handler() for details. +*/ +static noinstr bool load_secondary_wait(unsigned int ctrl_cpu) { -unsigned int ctrl_cpu =this_cpu_read (ucode_ctrl.ctrl_cpu); -enum ucode_state ret; -/* Initial rendezvouz to ensure that all CPUs have arrived */ if (!wait_for_cpus (&late_cpus_in)) { -pr_err_once ("load: %d CPUs timed out\n" ,atomic_read (&late_cpus_in) -1 ); -this_cpu_write (ucode_ctrl.result,UCODE_TIMEOUT ); -return ; +raw_cpu_write (ucode_ctrl.result,UCODE_TIMEOUT ); +return false; }/*
@@ -358,9 +369,33 @@ static void load_secondary(unsigned int* scheduler, watchdogs etc. There is no way to safely evacuate the * machine. */ -if (!wait_for_ctrl ()) -panic ("Microcode load: Primary CPU %d timed out\n" , ctrl_cpu); +if (wait_for_ctrl ()) +return true; + +instrumentation_begin (); +panic ("Microcode load: Primary CPU %d timed out\n" , ctrl_cpu); +instrumentation_end (); +} +/* +* Protected against instrumentation up to the point where the primary +* thread completed the update. See microcode_nmi_handler() for details. +*/ +static noinstr void load_secondary(unsigned int cpu) +{ +unsigned int ctrl_cpu =raw_cpu_read (ucode_ctrl.ctrl_cpu); +enum ucode_state ret; + +if (!load_secondary_wait (ctrl_cpu)) { +instrumentation_begin (); +pr_err_once ("Microcode load: %d CPUs timed out\n" , +atomic_read (&late_cpus_in) -1 ); +instrumentation_end (); +return ; + } + +/* Primary thread completed. Allow to invoke instrumentable code */ +instrumentation_begin ();/* * If the primary succeeded then invoke the apply() callback, * otherwise copy the state from the primary thread.
@@ -372,6 +407,7 @@ static void load_secondary(unsigned intthis_cpu_write (ucode_ctrl.result, ret);this_cpu_write (ucode_ctrl.ctrl,SCTRL_DONE ); +instrumentation_end (); }static void load_primary (unsigned int cpu)
@@ -409,25 +445,42 @@ static void load_primary(unsigned int cp } } -static bool microcode_update_handler (void ) +static noinstr bool microcode_update_handler(void) { -unsigned int cpu =smp_processor_id (); +unsigned int cpu =raw_smp_processor_id (); -if (this_cpu_read (ucode_ctrl.ctrl_cpu) == cpu) +if (raw_cpu_read (ucode_ctrl.ctrl_cpu) == cpu) { +instrumentation_begin ();load_primary (cpu); -else +instrumentation_end (); + }else {load_secondary (cpu); + } +instrumentation_begin ();touch_nmi_watchdog (); +instrumentation_end ();return true; } -bool microcode_nmi_handler (void ) +/* +* Protection against instrumentation is required for CPUs which are not +* safe against an NMI which is delivered to the secondary SMT sibling +* while the primary thread updates the microcode. Instrumentation can end +* up in #INT3, #DB and #PF. The IRET from those exceptions reenables NMI +* which is the opposite of what the NMI rendevouz is trying to achieve. +* +* The primary thread is safe versus instrumentation as the actual +* microcode update handles this correctly. It's only the sibling code +* path which must be NMI safe until the primary thread completed the +* update. +*/ +bool noinstr microcode_nmi_handler(void) { -if (!this_cpu_read (ucode_ctrl.nmi_enabled)) +if (!raw_cpu_read (ucode_ctrl.nmi_enabled))return false; -this_cpu_write (ucode_ctrl.nmi_enabled, false); +raw_cpu_write (ucode_ctrl.nmi_enabled, false);return microcode_update_handler (); }
@@ -454,6 +507,7 @@ static int load_late_stop_cpus(void)pr_err ("You should switch to early loading, if possible.\n" );atomic_set (&late_cpus_in,num_online_cpus ()); + loops_per_usec = loops_per_jiffy / (TICK_NSEC /1000 );/* * Take a snapshot before the microcode update in order to compare and
[tip: x86/microcode] x86/microcode: Protect against instrumentation
The following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: f89dd8edd20034c5cc7a3fdf734b3e3c3d808c73 Gitweb: https://git.kernel.org/tip/f89dd8edd20034c5cc7a3fdf734b3e3c3d808c73 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 14:00:06 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Fri, 06 Oct 2023 15:12:25 +02:00
x86/microcode: Protect against instrumentation
The wait for control loop in which the siblings are waiting for the microcode update on the primary thread must be protected against instrumentation as instrumentation can end up in #INT3, #DB or #PF, which then returns with IRET. That IRET reenables NMI which is the opposite of what the NMI rendezvous is trying to achieve.
--- arch/x86/kernel/cpu/microcode/core.c | 111 +++++++++++++++++++------- 1 file changed, 83 insertions(+), 28 deletions(-)
arch/x86/kernel/cpu/microcode/core.c +83 -28
@@ -301,54 +301,65 @@ struct microcode_ctrl {DEFINE_STATIC_KEY_FALSE (microcode_nmi_handler_enable); static DEFINE_PER_CPU(struct microcode_ctrl, ucode_ctrl); +static unsigned int loops_per_usec;static atomic_t late_cpus_in; -static bool wait_for_cpus (atomic_t *cnt) +static noinstr bool wait_for_cpus(atomic_t *cnt) { -unsigned int timeout; +unsigned int timeout, loops; -WARN_ON_ONCE (atomic_dec_return (cnt) <0 ); +WARN_ON_ONCE (raw_atomic_dec_return (cnt) <0 );for (timeout =0 ; timeout <USEC_PER_SEC ; timeout++) { -if (!atomic_read (cnt)) +if (!raw_atomic_read (cnt))return true; -udelay (1 ); +for (loops =0 ; loops < loops_per_usec; loops++) +cpu_relax ();/* If invoked directly, tickle the NMI watchdog */ -if (!microcode_ops->use_nmi && !(timeout %USEC_PER_MSEC )) +if (!microcode_ops->use_nmi && !(timeout %USEC_PER_MSEC )) { +instrumentation_begin ();touch_nmi_watchdog (); +instrumentation_end (); + } }/* Prevent the late comers from making progress and let them time out */ -atomic_inc (cnt); +raw_atomic_inc (cnt);return false; } -static bool wait_for_ctrl (void ) +static noinstr bool wait_for_ctrl(void) { -unsigned int timeout; +unsigned int timeout, loops;for (timeout =0 ; timeout <USEC_PER_SEC ; timeout++) { -if (this_cpu_read (ucode_ctrl.ctrl) !=SCTRL_WAIT ) +if (raw_cpu_read (ucode_ctrl.ctrl) !=SCTRL_WAIT )return true; -udelay (1 ); + +for (loops =0 ; loops < loops_per_usec; loops++) +cpu_relax (); +/* If invoked directly, tickle the NMI watchdog */ -if (!microcode_ops->use_nmi && !(timeout %1000 )) +if (!microcode_ops->use_nmi && !(timeout %USEC_PER_MSEC )) { +instrumentation_begin ();touch_nmi_watchdog (); +instrumentation_end (); + } }return false; } -static void load_secondary (unsigned int cpu) +/* +* Protected against instrumentation up to the point where the primary +* thread completed the update. See microcode_nmi_handler() for details. +*/ +static noinstr bool load_secondary_wait(unsigned int ctrl_cpu) { -unsigned int ctrl_cpu =this_cpu_read (ucode_ctrl.ctrl_cpu); -enum ucode_state ret; -/* Initial rendezvous to ensure that all CPUs have arrived */ if (!wait_for_cpus (&late_cpus_in)) { -pr_err_once ("load: %d CPUs timed out\n" ,atomic_read (&late_cpus_in) -1 ); -this_cpu_write (ucode_ctrl.result,UCODE_TIMEOUT ); -return ; +raw_cpu_write (ucode_ctrl.result,UCODE_TIMEOUT ); +return false; }/*
@@ -358,9 +369,33 @@ static void load_secondary(unsigned int cpu)* scheduler, watchdogs etc. There is no way to safely evacuate the * machine. */ -if (!wait_for_ctrl ()) -panic ("Microcode load: Primary CPU %d timed out\n" , ctrl_cpu); +if (wait_for_ctrl ()) +return true; + +instrumentation_begin (); +panic ("Microcode load: Primary CPU %d timed out\n" , ctrl_cpu); +instrumentation_end (); +} +/* +* Protected against instrumentation up to the point where the primary +* thread completed the update. See microcode_nmi_handler() for details. +*/ +static noinstr void load_secondary(unsigned int cpu) +{ +unsigned int ctrl_cpu =raw_cpu_read (ucode_ctrl.ctrl_cpu); +enum ucode_state ret; + +if (!load_secondary_wait (ctrl_cpu)) { +instrumentation_begin (); +pr_err_once ("load: %d CPUs timed out\n" , +atomic_read (&late_cpus_in) -1 ); +instrumentation_end (); +return ; + } + +/* Primary thread completed. Allow to invoke instrumentable code */ +instrumentation_begin ();/* * If the primary succeeded then invoke the apply() callback, * otherwise copy the state from the primary thread.
@@ -372,6 +407,7 @@ static void load_secondary(unsigned int cpu)this_cpu_write (ucode_ctrl.result, ret);this_cpu_write (ucode_ctrl.ctrl,SCTRL_DONE ); +instrumentation_end (); }static void load_primary (unsigned int cpu)
@@ -409,25 +445,43 @@ static void load_primary(unsigned int cpu) } } -static bool microcode_update_handler (void ) +static noinstr bool microcode_update_handler(void) { -unsigned int cpu =smp_processor_id (); +unsigned int cpu =raw_smp_processor_id (); -if (this_cpu_read (ucode_ctrl.ctrl_cpu) == cpu) +if (raw_cpu_read (ucode_ctrl.ctrl_cpu) == cpu) { +instrumentation_begin ();load_primary (cpu); -else +instrumentation_end (); + }else {load_secondary (cpu); + } +instrumentation_begin ();touch_nmi_watchdog (); +instrumentation_end (); +return true; } -bool microcode_nmi_handler (void ) +/* +* Protection against instrumentation is required for CPUs which are not +* safe against an NMI which is delivered to the secondary SMT sibling +* while the primary thread updates the microcode. Instrumentation can end +* up in #INT3, #DB and #PF. The IRET from those exceptions reenables NMI +* which is the opposite of what the NMI rendezvous is trying to achieve. +* +* The primary thread is safe versus instrumentation as the actual +* microcode update handles this correctly. It's only the sibling code +* path which must be NMI safe until the primary thread completed the +* update. +*/ +bool noinstr microcode_nmi_handler(void) { -if (!this_cpu_read (ucode_ctrl.nmi_enabled)) +if (!raw_cpu_read (ucode_ctrl.nmi_enabled))return false; -this_cpu_write (ucode_ctrl.nmi_enabled, false); +raw_cpu_write (ucode_ctrl.nmi_enabled, false);return microcode_update_handler (); }
@@ -454,6 +508,7 @@ static int load_late_stop_cpus(void)pr_err ("You should switch to early loading, if possible.\n" );atomic_set (&late_cpus_in,num_online_cpus ()); + loops_per_usec = loops_per_jiffy / (TICK_NSEC /1000 );/* * Take a snapshot before the microcode update in order to compare and
[tip: x86/microcode] x86/microcode: Protect against instrumentation
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 388adc4154d5f86b779a5a1e3f2f03f5be8152bf Gitweb: https://git.kernel.org/tip/388adc4154d5f86b779a5a1e3f2f03f5be8152bf Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 14:00:06 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Thu, 19 Oct 2023 16:00:09 +02:00
x86/microcode: Protect against instrumentation
The wait for control loop in which the siblings are waiting for the microcode update on the primary thread must be protected against instrumentation as instrumentation can end up in #INT3, #DB or #PF, which then returns with IRET. That IRET reenables NMI which is the opposite of what the NMI rendezvous is trying to achieve.
--- arch/x86/kernel/cpu/microcode/core.c | 111 +++++++++++++++++++------- 1 file changed, 83 insertions(+), 28 deletions(-)
arch/x86/kernel/cpu/microcode/core.c +83 -28
@@ -272,54 +272,65 @@ struct microcode_ctrl {DEFINE_STATIC_KEY_FALSE (microcode_nmi_handler_enable); static DEFINE_PER_CPU(struct microcode_ctrl, ucode_ctrl); +static unsigned int loops_per_usec;static atomic_t late_cpus_in; -static bool wait_for_cpus (atomic_t *cnt) +static noinstr bool wait_for_cpus(atomic_t *cnt) { -unsigned int timeout; +unsigned int timeout, loops; -WARN_ON_ONCE (atomic_dec_return (cnt) <0 ); +WARN_ON_ONCE (raw_atomic_dec_return (cnt) <0 );for (timeout =0 ; timeout <USEC_PER_SEC ; timeout++) { -if (!atomic_read (cnt)) +if (!raw_atomic_read (cnt))return true; -udelay (1 ); +for (loops =0 ; loops < loops_per_usec; loops++) +cpu_relax ();/* If invoked directly, tickle the NMI watchdog */ -if (!microcode_ops->use_nmi && !(timeout %USEC_PER_MSEC )) +if (!microcode_ops->use_nmi && !(timeout %USEC_PER_MSEC )) { +instrumentation_begin ();touch_nmi_watchdog (); +instrumentation_end (); + } }/* Prevent the late comers from making progress and let them time out */ -atomic_inc (cnt); +raw_atomic_inc (cnt);return false; } -static bool wait_for_ctrl (void ) +static noinstr bool wait_for_ctrl(void) { -unsigned int timeout; +unsigned int timeout, loops;for (timeout =0 ; timeout <USEC_PER_SEC ; timeout++) { -if (this_cpu_read (ucode_ctrl.ctrl) !=SCTRL_WAIT ) +if (raw_cpu_read (ucode_ctrl.ctrl) !=SCTRL_WAIT )return true; -udelay (1 ); + +for (loops =0 ; loops < loops_per_usec; loops++) +cpu_relax (); +/* If invoked directly, tickle the NMI watchdog */ -if (!microcode_ops->use_nmi && !(timeout %1000 )) +if (!microcode_ops->use_nmi && !(timeout %USEC_PER_MSEC )) { +instrumentation_begin ();touch_nmi_watchdog (); +instrumentation_end (); + } }return false; } -static void load_secondary (unsigned int cpu) +/* +* Protected against instrumentation up to the point where the primary +* thread completed the update. See microcode_nmi_handler() for details. +*/ +static noinstr bool load_secondary_wait(unsigned int ctrl_cpu) { -unsigned int ctrl_cpu =this_cpu_read (ucode_ctrl.ctrl_cpu); -enum ucode_state ret; -/* Initial rendezvous to ensure that all CPUs have arrived */ if (!wait_for_cpus (&late_cpus_in)) { -pr_err_once ("load: %d CPUs timed out\n" ,atomic_read (&late_cpus_in) -1 ); -this_cpu_write (ucode_ctrl.result,UCODE_TIMEOUT ); -return ; +raw_cpu_write (ucode_ctrl.result,UCODE_TIMEOUT ); +return false; }/*
@@ -329,9 +340,33 @@ static void load_secondary(unsigned int cpu)* scheduler, watchdogs etc. There is no way to safely evacuate the * machine. */ -if (!wait_for_ctrl ()) -panic ("Microcode load: Primary CPU %d timed out\n" , ctrl_cpu); +if (wait_for_ctrl ()) +return true; + +instrumentation_begin (); +panic ("Microcode load: Primary CPU %d timed out\n" , ctrl_cpu); +instrumentation_end (); +} +/* +* Protected against instrumentation up to the point where the primary +* thread completed the update. See microcode_nmi_handler() for details. +*/ +static noinstr void load_secondary(unsigned int cpu) +{ +unsigned int ctrl_cpu =raw_cpu_read (ucode_ctrl.ctrl_cpu); +enum ucode_state ret; + +if (!load_secondary_wait (ctrl_cpu)) { +instrumentation_begin (); +pr_err_once ("load: %d CPUs timed out\n" , +atomic_read (&late_cpus_in) -1 ); +instrumentation_end (); +return ; + } + +/* Primary thread completed. Allow to invoke instrumentable code */ +instrumentation_begin ();/* * If the primary succeeded then invoke the apply() callback, * otherwise copy the state from the primary thread.
@@ -343,6 +378,7 @@ static void load_secondary(unsigned int cpu)this_cpu_write (ucode_ctrl.result, ret);this_cpu_write (ucode_ctrl.ctrl,SCTRL_DONE ); +instrumentation_end (); }static void load_primary (unsigned int cpu)
@@ -380,25 +416,43 @@ static void load_primary(unsigned int cpu) } } -static bool microcode_update_handler (void ) +static noinstr bool microcode_update_handler(void) { -unsigned int cpu =smp_processor_id (); +unsigned int cpu =raw_smp_processor_id (); -if (this_cpu_read (ucode_ctrl.ctrl_cpu) == cpu) +if (raw_cpu_read (ucode_ctrl.ctrl_cpu) == cpu) { +instrumentation_begin ();load_primary (cpu); -else +instrumentation_end (); + }else {load_secondary (cpu); + } +instrumentation_begin ();touch_nmi_watchdog (); +instrumentation_end (); +return true; } -bool microcode_nmi_handler (void ) +/* +* Protection against instrumentation is required for CPUs which are not +* safe against an NMI which is delivered to the secondary SMT sibling +* while the primary thread updates the microcode. Instrumentation can end +* up in #INT3, #DB and #PF. The IRET from those exceptions reenables NMI +* which is the opposite of what the NMI rendezvous is trying to achieve. +* +* The primary thread is safe versus instrumentation as the actual +* microcode update handles this correctly. It's only the sibling code +* path which must be NMI safe until the primary thread completed the +* update. +*/ +bool noinstr microcode_nmi_handler(void) { -if (!this_cpu_read (ucode_ctrl.nmi_enabled)) +if (!raw_cpu_read (ucode_ctrl.nmi_enabled))return false; -this_cpu_write (ucode_ctrl.nmi_enabled, false); +raw_cpu_write (ucode_ctrl.nmi_enabled, false);return microcode_update_handler (); }
@@ -425,6 +479,7 @@ static int load_late_stop_cpus(void)pr_err ("You should switch to early loading, if possible.\n" );atomic_set (&late_cpus_in,num_online_cpus ()); + loops_per_usec = loops_per_jiffy / (TICK_NSEC /1000 );/* * Take a snapshot before the microcode update in order to compare and
[tip: x86/microcode] x86/microcode: Protect against instrumentation
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 1582c0f4a21303792f523fe2839dd8433ee630c0 Gitweb: https://git.kernel.org/tip/1582c0f4a21303792f523fe2839dd8433ee630c0 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 14:00:06 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Tue, 24 Oct 2023 15:05:55 +02:00
x86/microcode: Protect against instrumentation
The wait for control loop in which the siblings are waiting for the microcode update on the primary thread must be protected against instrumentation as instrumentation can end up in #INT3, #DB or #PF, which then returns with IRET. That IRET reenables NMI which is the opposite of what the NMI rendezvous is trying to achieve.
--- arch/x86/kernel/cpu/microcode/core.c | 111 +++++++++++++++++++------- 1 file changed, 83 insertions(+), 28 deletions(-)
arch/x86/kernel/cpu/microcode/core.c +83 -28
@@ -272,54 +272,65 @@ struct microcode_ctrl {DEFINE_STATIC_KEY_FALSE (microcode_nmi_handler_enable); static DEFINE_PER_CPU(struct microcode_ctrl, ucode_ctrl); +static unsigned int loops_per_usec;static atomic_t late_cpus_in; -static bool wait_for_cpus (atomic_t *cnt) +static noinstr bool wait_for_cpus(atomic_t *cnt) { -unsigned int timeout; +unsigned int timeout, loops; -WARN_ON_ONCE (atomic_dec_return (cnt) <0 ); +WARN_ON_ONCE (raw_atomic_dec_return (cnt) <0 );for (timeout =0 ; timeout <USEC_PER_SEC ; timeout++) { -if (!atomic_read (cnt)) +if (!raw_atomic_read (cnt))return true; -udelay (1 ); +for (loops =0 ; loops < loops_per_usec; loops++) +cpu_relax ();/* If invoked directly, tickle the NMI watchdog */ -if (!microcode_ops->use_nmi && !(timeout %USEC_PER_MSEC )) +if (!microcode_ops->use_nmi && !(timeout %USEC_PER_MSEC )) { +instrumentation_begin ();touch_nmi_watchdog (); +instrumentation_end (); + } }/* Prevent the late comers from making progress and let them time out */ -atomic_inc (cnt); +raw_atomic_inc (cnt);return false; } -static bool wait_for_ctrl (void ) +static noinstr bool wait_for_ctrl(void) { -unsigned int timeout; +unsigned int timeout, loops;for (timeout =0 ; timeout <USEC_PER_SEC ; timeout++) { -if (this_cpu_read (ucode_ctrl.ctrl) !=SCTRL_WAIT ) +if (raw_cpu_read (ucode_ctrl.ctrl) !=SCTRL_WAIT )return true; -udelay (1 ); + +for (loops =0 ; loops < loops_per_usec; loops++) +cpu_relax (); +/* If invoked directly, tickle the NMI watchdog */ -if (!microcode_ops->use_nmi && !(timeout %1000 )) +if (!microcode_ops->use_nmi && !(timeout %USEC_PER_MSEC )) { +instrumentation_begin ();touch_nmi_watchdog (); +instrumentation_end (); + } }return false; } -static void load_secondary (unsigned int cpu) +/* +* Protected against instrumentation up to the point where the primary +* thread completed the update. See microcode_nmi_handler() for details. +*/ +static noinstr bool load_secondary_wait(unsigned int ctrl_cpu) { -unsigned int ctrl_cpu =this_cpu_read (ucode_ctrl.ctrl_cpu); -enum ucode_state ret; -/* Initial rendezvous to ensure that all CPUs have arrived */ if (!wait_for_cpus (&late_cpus_in)) { -pr_err_once ("load: %d CPUs timed out\n" ,atomic_read (&late_cpus_in) -1 ); -this_cpu_write (ucode_ctrl.result,UCODE_TIMEOUT ); -return ; +raw_cpu_write (ucode_ctrl.result,UCODE_TIMEOUT ); +return false; }/*
@@ -329,9 +340,33 @@ static void load_secondary(unsigned int cpu)* scheduler, watchdogs etc. There is no way to safely evacuate the * machine. */ -if (!wait_for_ctrl ()) -panic ("Microcode load: Primary CPU %d timed out\n" , ctrl_cpu); +if (wait_for_ctrl ()) +return true; + +instrumentation_begin (); +panic ("Microcode load: Primary CPU %d timed out\n" , ctrl_cpu); +instrumentation_end (); +} +/* +* Protected against instrumentation up to the point where the primary +* thread completed the update. See microcode_nmi_handler() for details. +*/ +static noinstr void load_secondary(unsigned int cpu) +{ +unsigned int ctrl_cpu =raw_cpu_read (ucode_ctrl.ctrl_cpu); +enum ucode_state ret; + +if (!load_secondary_wait (ctrl_cpu)) { +instrumentation_begin (); +pr_err_once ("load: %d CPUs timed out\n" , +atomic_read (&late_cpus_in) -1 ); +instrumentation_end (); +return ; + } + +/* Primary thread completed. Allow to invoke instrumentable code */ +instrumentation_begin ();/* * If the primary succeeded then invoke the apply() callback, * otherwise copy the state from the primary thread.
@@ -343,6 +378,7 @@ static void load_secondary(unsigned int cpu)this_cpu_write (ucode_ctrl.result, ret);this_cpu_write (ucode_ctrl.ctrl,SCTRL_DONE ); +instrumentation_end (); }static void load_primary (unsigned int cpu)
@@ -380,25 +416,43 @@ static void load_primary(unsigned int cpu) } } -static bool microcode_update_handler (void ) +static noinstr bool microcode_update_handler(void) { -unsigned int cpu =smp_processor_id (); +unsigned int cpu =raw_smp_processor_id (); -if (this_cpu_read (ucode_ctrl.ctrl_cpu) == cpu) +if (raw_cpu_read (ucode_ctrl.ctrl_cpu) == cpu) { +instrumentation_begin ();load_primary (cpu); -else +instrumentation_end (); + }else {load_secondary (cpu); + } +instrumentation_begin ();touch_nmi_watchdog (); +instrumentation_end (); +return true; } -bool microcode_nmi_handler (void ) +/* +* Protection against instrumentation is required for CPUs which are not +* safe against an NMI which is delivered to the secondary SMT sibling +* while the primary thread updates the microcode. Instrumentation can end +* up in #INT3, #DB and #PF. The IRET from those exceptions reenables NMI +* which is the opposite of what the NMI rendezvous is trying to achieve. +* +* The primary thread is safe versus instrumentation as the actual +* microcode update handles this correctly. It's only the sibling code +* path which must be NMI safe until the primary thread completed the +* update. +*/ +bool noinstr microcode_nmi_handler(void) { -if (!this_cpu_read (ucode_ctrl.nmi_enabled)) +if (!raw_cpu_read (ucode_ctrl.nmi_enabled))return false; -this_cpu_write (ucode_ctrl.nmi_enabled, false); +raw_cpu_write (ucode_ctrl.nmi_enabled, false);return microcode_update_handler (); }
@@ -425,6 +479,7 @@ static int load_late_stop_cpus(void)pr_err ("You should switch to early loading, if possible.\n" );atomic_set (&late_cpus_in,num_online_cpus ()); + loops_per_usec = loops_per_jiffy / (TICK_NSEC /1000 );/* * Take a snapshot before the microcode update in order to compare and
PATCH v4 27/30 x86/apic: Provide apic_force_nmi_on_cpu()
In reply to Thomas GleixnerFrom: Thomas Gleixner <[email protected]>
When SMT siblings are soft-offlined and parked in one of the play_dead() variants they still react on NMI, which is problematic on affected Intel CPUs. The default play_dead() variant uses MWAIT on modern CPUs, which is not guaranteed to be safe when updated concurrently.
Right now late loading is prevented when not all SMT siblings are online, but as they still react on NMI, it is possible to bring them out of their park position into a trivial rendevouz handler.
Provide a function which allows to do that. I does sanity checks whether the target is in the cpus_booted_once_mask and whether the APIC driver supports it.
Mark X2APIC and XAPIC as capable, but exclude 32bit and the UV and NUMACHIP variants as that needs feedback from the relevant experts.
--- arch/x86/include/asm/apic.h | 5 ++++- arch/x86/kernel/apic/apic_flat_64.c | 2 ++ arch/x86/kernel/apic/ipi.c | 8 ++++++++ arch/x86/kernel/apic/x2apic_cluster.c | 1 + arch/x86/kernel/apic/x2apic_phys.c | 1 + 5 files changed, 16 insertions(+), 1 deletion(-)
---
arch/x86/include/asm/apic.h +4 -1
@@ -276,7 +276,8 @@ struct apic { u32 disable_esr : 1, dest_mode_logical : 1, - x2apic_set_max_apicid : 1; + x2apic_set_max_apicid : 1, + nmi_to_offline_cpu : 1;u32 (*calc_dest_apicid)(unsigned int cpu);
@@ -542,6 +543,8 @@ extern bool default_check_apicid_used(phextern void default_ioapic_phys_id_map (physid_mask_t *phys_map,physid_mask_t *retmap);extern int default_cpu_present_to_apicid (int mps_cpu); +void apic_send_nmi_to_offline_cpu (unsigned int cpu); +#else /* CONFIG_X86_LOCAL_APIC */ static inline unsigned int read_apic_id (void ) {return 0 ; }
arch/x86/kernel/apic/apic_flat_64.c +2 -0
@@ -103,6 +103,7 @@ static struct apic apic_flat __ro_after_ .send_IPI_allbutself = default_send_IPI_allbutself, .send_IPI_all = default_send_IPI_all, .send_IPI_self = default_send_IPI_self, + .nmi_to_offline_cpu = true, .read = native_apic_mem_read, .write = native_apic_mem_write,
@@ -175,6 +176,7 @@ static struct apic apic_physflat __ro_af .send_IPI_allbutself = default_send_IPI_allbutself, .send_IPI_all = default_send_IPI_all, .send_IPI_self = default_send_IPI_self, + .nmi_to_offline_cpu = true, .read = native_apic_mem_read, .write = native_apic_mem_write,
arch/x86/kernel/apic/ipi.c +8 -0
@@ -97,6 +97,14 @@ void native_send_call_func_ipi(const str __apic_send_IPI_mask(mask, CALL_FUNCTION_VECTOR); } +void apic_send_nmi_to_offline_cpu (unsigned int cpu) +{ +if (WARN_ON_ONCE (!apic->nmi_to_offline_cpu)) +return ; +if (WARN_ON_ONCE (!cpumask_test_cpu (cpu, &cpus_booted_once_mask))) +return ; + apic->send_IPI (cpu,NMI_VECTOR ); +}#endif /* CONFIG_SMP */ static inline int __prepare_ICR2 (unsigned int mask)
arch/x86/kernel/apic/x2apic_cluster.c +1 -0
@@ -251,6 +251,7 @@ static struct apic apic_x2apic_cluster _ .send_IPI_allbutself = x2apic_send_IPI_allbutself, .send_IPI_all = x2apic_send_IPI_all, .send_IPI_self = x2apic_send_IPI_self, + .nmi_to_offline_cpu = true, .read = native_apic_msr_read, .write = native_apic_msr_write,
arch/x86/kernel/apic/x2apic_phys.c +1 -0
@@ -166,6 +166,7 @@ static struct apic apic_x2apic_phys __ro .send_IPI_allbutself = x2apic_send_IPI_allbutself, .send_IPI_all = x2apic_send_IPI_all, .send_IPI_self = x2apic_send_IPI_self, + .nmi_to_offline_cpu = true, .read = native_apic_msr_read, .write = native_apic_msr_write,
[tip: x86/microcode] x86/apic: Provide apic_force_nmi_on_cpu()
The following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 27b28750429ed5ab9c41955976a44c05bd029cfb Gitweb: https://git.kernel.org/tip/27b28750429ed5ab9c41955976a44c05bd029cfb Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 14:00:07 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Fri, 06 Oct 2023 15:12:25 +02:00
x86/apic: Provide apic_force_nmi_on_cpu()
When SMT siblings are soft-offlined and parked in one of the play_dead() variants they still react on NMI, which is problematic on affected Intel CPUs. The default play_dead() variant uses MWAIT on modern CPUs, which is not guaranteed to be safe when updated concurrently.
Right now late loading is prevented when not all SMT siblings are online, but as they still react on NMI, it is possible to bring them out of their park position into a trivial rendezvous handler.
Provide a function which allows to do that. I does sanity checks whether the target is in the cpus_booted_once_mask and whether the APIC driver supports it.
Mark X2APIC and XAPIC as capable, but exclude 32bit and the UV and NUMACHIP variants as that needs feedback from the relevant experts.
--- arch/x86/include/asm/apic.h | 5 ++++- arch/x86/kernel/apic/apic_flat_64.c | 2 ++ arch/x86/kernel/apic/ipi.c | 8 ++++++++ arch/x86/kernel/apic/x2apic_cluster.c | 1 + arch/x86/kernel/apic/x2apic_phys.c | 1 + 5 files changed, 16 insertions(+), 1 deletion(-)
arch/x86/include/asm/apic.h +4 -1
@@ -276,7 +276,8 @@ struct apic { u32 disable_esr : 1, dest_mode_logical : 1, - x2apic_set_max_apicid : 1; + x2apic_set_max_apicid : 1, + nmi_to_offline_cpu : 1;u32 (*calc_dest_apicid)(unsigned int cpu);
@@ -542,6 +543,8 @@ extern bool default_check_apicid_used(physid_mask_t *map, int apicid);extern void default_ioapic_phys_id_map (physid_mask_t *phys_map,physid_mask_t *retmap);extern int default_cpu_present_to_apicid (int mps_cpu); +void apic_send_nmi_to_offline_cpu (unsigned int cpu); +#else /* CONFIG_X86_LOCAL_APIC */ static inline unsigned int read_apic_id (void ) {return 0 ; }
arch/x86/kernel/apic/apic_flat_64.c +2 -0
@@ -103,6 +103,7 @@ static struct apic apic_flat __ro_after_init = { .send_IPI_allbutself = default_send_IPI_allbutself, .send_IPI_all = default_send_IPI_all, .send_IPI_self = default_send_IPI_self, + .nmi_to_offline_cpu = true, .read = native_apic_mem_read, .write = native_apic_mem_write,
@@ -175,6 +176,7 @@ static struct apic apic_physflat __ro_after_init = { .send_IPI_allbutself = default_send_IPI_allbutself, .send_IPI_all = default_send_IPI_all, .send_IPI_self = default_send_IPI_self, + .nmi_to_offline_cpu = true, .read = native_apic_mem_read, .write = native_apic_mem_write,
arch/x86/kernel/apic/ipi.c +8 -0
@@ -97,6 +97,14 @@ sendmask: __apic_send_IPI_mask(mask, CALL_FUNCTION_VECTOR); } +void apic_send_nmi_to_offline_cpu (unsigned int cpu) +{ +if (WARN_ON_ONCE (!apic->nmi_to_offline_cpu)) +return ; +if (WARN_ON_ONCE (!cpumask_test_cpu (cpu, &cpus_booted_once_mask))) +return ; + apic->send_IPI (cpu,NMI_VECTOR ); +}#endif /* CONFIG_SMP */ static inline int __prepare_ICR2 (unsigned int mask)
arch/x86/kernel/apic/x2apic_cluster.c +1 -0
@@ -251,6 +251,7 @@ static struct apic apic_x2apic_cluster __ro_after_init = { .send_IPI_allbutself = x2apic_send_IPI_allbutself, .send_IPI_all = x2apic_send_IPI_all, .send_IPI_self = x2apic_send_IPI_self, + .nmi_to_offline_cpu = true, .read = native_apic_msr_read, .write = native_apic_msr_write,
arch/x86/kernel/apic/x2apic_phys.c +1 -0
@@ -166,6 +166,7 @@ static struct apic apic_x2apic_phys __ro_after_init = { .send_IPI_allbutself = x2apic_send_IPI_allbutself, .send_IPI_all = x2apic_send_IPI_all, .send_IPI_self = x2apic_send_IPI_self, + .nmi_to_offline_cpu = true, .read = native_apic_msr_read, .write = native_apic_msr_write,
[tip: x86/microcode] x86/apic: Provide apic_force_nmi_on_cpu()
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: ea8ef7118d201c0a9d24f25c00f4c36efb2216e3 Gitweb: https://git.kernel.org/tip/ea8ef7118d201c0a9d24f25c00f4c36efb2216e3 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 14:00:07 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Thu, 19 Oct 2023 16:00:37 +02:00
x86/apic: Provide apic_force_nmi_on_cpu()
When SMT siblings are soft-offlined and parked in one of the play_dead() variants they still react on NMI, which is problematic on affected Intel CPUs. The default play_dead() variant uses MWAIT on modern CPUs, which is not guaranteed to be safe when updated concurrently.
Right now late loading is prevented when not all SMT siblings are online, but as they still react on NMI, it is possible to bring them out of their park position into a trivial rendezvous handler.
Provide a function which allows to do that. I does sanity checks whether the target is in the cpus_booted_once_mask and whether the APIC driver supports it.
Mark X2APIC and XAPIC as capable, but exclude 32bit and the UV and NUMACHIP variants as that needs feedback from the relevant experts.
--- arch/x86/include/asm/apic.h | 5 ++++- arch/x86/kernel/apic/apic_flat_64.c | 2 ++ arch/x86/kernel/apic/ipi.c | 8 ++++++++ arch/x86/kernel/apic/x2apic_cluster.c | 1 + arch/x86/kernel/apic/x2apic_phys.c | 1 + 5 files changed, 16 insertions(+), 1 deletion(-)
arch/x86/include/asm/apic.h +4 -1
@@ -276,7 +276,8 @@ struct apic { u32 disable_esr : 1, dest_mode_logical : 1, - x2apic_set_max_apicid : 1; + x2apic_set_max_apicid : 1, + nmi_to_offline_cpu : 1;u32 (*calc_dest_apicid)(unsigned int cpu);
@@ -542,6 +543,8 @@ extern bool default_check_apicid_used(physid_mask_t *map, int apicid);extern void default_ioapic_phys_id_map (physid_mask_t *phys_map,physid_mask_t *retmap);extern int default_cpu_present_to_apicid (int mps_cpu); +void apic_send_nmi_to_offline_cpu (unsigned int cpu); +#else /* CONFIG_X86_LOCAL_APIC */ static inline unsigned int read_apic_id (void ) {return 0 ; }
arch/x86/kernel/apic/apic_flat_64.c +2 -0
@@ -103,6 +103,7 @@ static struct apic apic_flat __ro_after_init = { .send_IPI_allbutself = default_send_IPI_allbutself, .send_IPI_all = default_send_IPI_all, .send_IPI_self = default_send_IPI_self, + .nmi_to_offline_cpu = true, .read = native_apic_mem_read, .write = native_apic_mem_write,
@@ -175,6 +176,7 @@ static struct apic apic_physflat __ro_after_init = { .send_IPI_allbutself = default_send_IPI_allbutself, .send_IPI_all = default_send_IPI_all, .send_IPI_self = default_send_IPI_self, + .nmi_to_offline_cpu = true, .read = native_apic_mem_read, .write = native_apic_mem_write,
arch/x86/kernel/apic/ipi.c +8 -0
@@ -97,6 +97,14 @@ sendmask: __apic_send_IPI_mask(mask, CALL_FUNCTION_VECTOR); } +void apic_send_nmi_to_offline_cpu (unsigned int cpu) +{ +if (WARN_ON_ONCE (!apic->nmi_to_offline_cpu)) +return ; +if (WARN_ON_ONCE (!cpumask_test_cpu (cpu, &cpus_booted_once_mask))) +return ; + apic->send_IPI (cpu,NMI_VECTOR ); +}#endif /* CONFIG_SMP */ static inline int __prepare_ICR2 (unsigned int mask)
arch/x86/kernel/apic/x2apic_cluster.c +1 -0
@@ -251,6 +251,7 @@ static struct apic apic_x2apic_cluster __ro_after_init = { .send_IPI_allbutself = x2apic_send_IPI_allbutself, .send_IPI_all = x2apic_send_IPI_all, .send_IPI_self = x2apic_send_IPI_self, + .nmi_to_offline_cpu = true, .read = native_apic_msr_read, .write = native_apic_msr_write,
arch/x86/kernel/apic/x2apic_phys.c +1 -0
@@ -166,6 +166,7 @@ static struct apic apic_x2apic_phys __ro_after_init = { .send_IPI_allbutself = x2apic_send_IPI_allbutself, .send_IPI_all = x2apic_send_IPI_all, .send_IPI_self = x2apic_send_IPI_self, + .nmi_to_offline_cpu = true, .read = native_apic_msr_read, .write = native_apic_msr_write,
[tip: x86/microcode] x86/apic: Provide apic_force_nmi_on_cpu()
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 9cab5fb776d4367e26950cf759211e948335288e Gitweb: https://git.kernel.org/tip/9cab5fb776d4367e26950cf759211e948335288e Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 14:00:07 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Tue, 24 Oct 2023 15:05:55 +02:00
x86/apic: Provide apic_force_nmi_on_cpu()
When SMT siblings are soft-offlined and parked in one of the play_dead() variants they still react on NMI, which is problematic on affected Intel CPUs. The default play_dead() variant uses MWAIT on modern CPUs, which is not guaranteed to be safe when updated concurrently.
Right now late loading is prevented when not all SMT siblings are online, but as they still react on NMI, it is possible to bring them out of their park position into a trivial rendezvous handler.
Provide a function which allows to do that. I does sanity checks whether the target is in the cpus_booted_once_mask and whether the APIC driver supports it.
Mark X2APIC and XAPIC as capable, but exclude 32bit and the UV and NUMACHIP variants as that needs feedback from the relevant experts.
--- arch/x86/include/asm/apic.h | 5 ++++- arch/x86/kernel/apic/apic_flat_64.c | 2 ++ arch/x86/kernel/apic/ipi.c | 8 ++++++++ arch/x86/kernel/apic/x2apic_cluster.c | 1 + arch/x86/kernel/apic/x2apic_phys.c | 1 + 5 files changed, 16 insertions(+), 1 deletion(-)
arch/x86/include/asm/apic.h +4 -1
@@ -276,7 +276,8 @@ struct apic { u32 disable_esr : 1, dest_mode_logical : 1, - x2apic_set_max_apicid : 1; + x2apic_set_max_apicid : 1, + nmi_to_offline_cpu : 1;u32 (*calc_dest_apicid)(unsigned int cpu);
@@ -542,6 +543,8 @@ extern bool default_check_apicid_used(physid_mask_t *map, int apicid);extern void default_ioapic_phys_id_map (physid_mask_t *phys_map,physid_mask_t *retmap);extern int default_cpu_present_to_apicid (int mps_cpu); +void apic_send_nmi_to_offline_cpu (unsigned int cpu); +#else /* CONFIG_X86_LOCAL_APIC */ static inline unsigned int read_apic_id (void ) {return 0 ; }
arch/x86/kernel/apic/apic_flat_64.c +2 -0
@@ -103,6 +103,7 @@ static struct apic apic_flat __ro_after_init = { .send_IPI_allbutself = default_send_IPI_allbutself, .send_IPI_all = default_send_IPI_all, .send_IPI_self = default_send_IPI_self, + .nmi_to_offline_cpu = true, .read = native_apic_mem_read, .write = native_apic_mem_write,
@@ -175,6 +176,7 @@ static struct apic apic_physflat __ro_after_init = { .send_IPI_allbutself = default_send_IPI_allbutself, .send_IPI_all = default_send_IPI_all, .send_IPI_self = default_send_IPI_self, + .nmi_to_offline_cpu = true, .read = native_apic_mem_read, .write = native_apic_mem_write,
arch/x86/kernel/apic/ipi.c +8 -0
@@ -97,6 +97,14 @@ sendmask: __apic_send_IPI_mask(mask, CALL_FUNCTION_VECTOR); } +void apic_send_nmi_to_offline_cpu (unsigned int cpu) +{ +if (WARN_ON_ONCE (!apic->nmi_to_offline_cpu)) +return ; +if (WARN_ON_ONCE (!cpumask_test_cpu (cpu, &cpus_booted_once_mask))) +return ; + apic->send_IPI (cpu,NMI_VECTOR ); +}#endif /* CONFIG_SMP */ static inline int __prepare_ICR2 (unsigned int mask)
arch/x86/kernel/apic/x2apic_cluster.c +1 -0
@@ -251,6 +251,7 @@ static struct apic apic_x2apic_cluster __ro_after_init = { .send_IPI_allbutself = x2apic_send_IPI_allbutself, .send_IPI_all = x2apic_send_IPI_all, .send_IPI_self = x2apic_send_IPI_self, + .nmi_to_offline_cpu = true, .read = native_apic_msr_read, .write = native_apic_msr_write,
arch/x86/kernel/apic/x2apic_phys.c +1 -0
@@ -166,6 +166,7 @@ static struct apic apic_x2apic_phys __ro_after_init = { .send_IPI_allbutself = x2apic_send_IPI_allbutself, .send_IPI_all = x2apic_send_IPI_all, .send_IPI_self = x2apic_send_IPI_self, + .nmi_to_offline_cpu = true, .read = native_apic_msr_read, .write = native_apic_msr_write,
PATCH v4 28/30 x86/microcode: Handle "offline" CPUs correctly
In reply to Thomas GleixnerFrom: Thomas Gleixner <[email protected]>
Offline CPUs need to be parked in a safe loop when microcode update is in progress on the primary CPU. Currently offline CPUs are parked in 'mwait_play_dead()', and for Intel CPUs, its not a safe instruction, because 'mwait' instruction can be patched in the new microcode update that can cause instability.
- Adds a new microcode state 'UCODE_OFFLINE' to report status on per-cpu basis.
- Force NMI on the offline CPUs.
Wakeup offline CPUs while the update is in progress and then return them back to 'mwait_play_dead()' after microcode update is complete.
--- arch/x86/include/asm/microcode.h | 1 arch/x86/kernel/cpu/microcode/core.c | 112 +++++++++++++++++++++++++++++-- arch/x86/kernel/cpu/microcode/internal.h | 1 arch/x86/kernel/nmi.c | 5 + 4 files changed, 113 insertions(+), 6 deletions(-)
---
arch/x86/include/asm/microcode.h +1 -0
@@ -71,6 +71,7 @@ static inline u32 intel_get_microcode_re#endif /* !CONFIG_CPU_SUP_INTEL */ bool microcode_nmi_handler (void ); +void microcode_offline_nmi_handler (void );#ifdef CONFIG_MICROCODE_LATE_LOADING DECLARE_STATIC_KEY_FALSE (microcode_nmi_handler_enable);
arch/x86/kernel/cpu/microcode/core.c +107 -5
171 lines not on this page · raw message
arch/x86/kernel/cpu/microcode/internal.h +1 -0
@@ -17,6 +17,7 @@ enum ucode_state {UCODE_NFOUND ,UCODE_ERROR ,UCODE_TIMEOUT , + UCODE_OFFLINE, }; struct microcode_ops {
arch/x86/kernel/nmi.c +4 -1
@@ -502,8 +502,11 @@ DEFINE_IDTENTRY_RAW(exc_nmi)if (IS_ENABLED (CONFIG_NMI_CHECK_CPU ))raw_atomic_long_inc (&nsp->idt_calls); -if (IS_ENABLED (CONFIG_SMP ) &&arch_cpu_is_offline (smp_processor_id ())) +if (IS_ENABLED (CONFIG_SMP ) &&arch_cpu_is_offline (smp_processor_id ())) { +if (microcode_nmi_handler_enabled ()) +microcode_offline_nmi_handler ();return ; + }if (this_cpu_read (nmi_state) !=NMI_NOT_RUNNING ) {this_cpu_write (nmi_state,NMI_LATCHED );
[tip: x86/microcode] x86/microcode: Handle "offline" CPUs correctly
The following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: c4228017e7ff3d9b6a774a23fefba85402f161a8 Gitweb: https://git.kernel.org/tip/c4228017e7ff3d9b6a774a23fefba85402f161a8 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 14:00:08 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Fri, 06 Oct 2023 15:12:25 +02:00
x86/microcode: Handle "offline" CPUs correctly
Offline CPUs need to be parked in a safe loop when microcode update is in progress on the primary CPU. Currently, offline CPUs are parked in mwait_play_dead(), and for Intel CPUs, its not a safe instruction, because the MWAIT instruction can be patched in the new microcode update that can cause instability.
- Add a new microcode state 'UCODE_OFFLINE' to report status on per-CPU basis.
- Force NMI on the offline CPUs.
Wake up offline CPUs while the update is in progress and then return them back to mwait_play_dead() after microcode update is complete.
--- arch/x86/include/asm/microcode.h | 1 +- arch/x86/kernel/cpu/microcode/core.c | 112 +++++++++++++++++++++- arch/x86/kernel/cpu/microcode/internal.h | 1 +- arch/x86/kernel/nmi.c | 5 +- 4 files changed, 113 insertions(+), 6 deletions(-)
arch/x86/include/asm/microcode.h +1 -0
@@ -71,6 +71,7 @@ static inline u32 intel_get_microcode_revision(void)#endif /* !CONFIG_CPU_SUP_INTEL */ bool microcode_nmi_handler (void ); +void microcode_offline_nmi_handler (void );#ifdef CONFIG_MICROCODE_LATE_LOADING DECLARE_STATIC_KEY_FALSE (microcode_nmi_handler_enable);
arch/x86/kernel/cpu/microcode/core.c +107 -5
171 lines not on this page · raw message
arch/x86/kernel/cpu/microcode/internal.h +1 -0
@@ -17,6 +17,7 @@ enum ucode_state {UCODE_NFOUND ,UCODE_ERROR ,UCODE_TIMEOUT , + UCODE_OFFLINE, }; struct microcode_ops {
arch/x86/kernel/nmi.c +4 -1
@@ -502,8 +502,11 @@ DEFINE_IDTENTRY_RAW(exc_nmi)if (IS_ENABLED (CONFIG_NMI_CHECK_CPU ))raw_atomic_long_inc (&nsp->idt_calls); -if (IS_ENABLED (CONFIG_SMP ) &&arch_cpu_is_offline (smp_processor_id ())) +if (IS_ENABLED (CONFIG_SMP ) &&arch_cpu_is_offline (smp_processor_id ())) { +if (microcode_nmi_handler_enabled ()) +microcode_offline_nmi_handler ();return ; + }if (this_cpu_read (nmi_state) !=NMI_NOT_RUNNING ) {this_cpu_write (nmi_state,NMI_LATCHED );
[tip: x86/microcode] x86/microcode: Handle "offline" CPUs correctly
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 73f4acb25560825f68f89164f8057742380d36e5 Gitweb: https://git.kernel.org/tip/73f4acb25560825f68f89164f8057742380d36e5 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 14:00:08 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Thu, 19 Oct 2023 16:01:19 +02:00
x86/microcode: Handle "offline" CPUs correctly
Offline CPUs need to be parked in a safe loop when microcode update is in progress on the primary CPU. Currently, offline CPUs are parked in mwait_play_dead(), and for Intel CPUs, its not a safe instruction, because the MWAIT instruction can be patched in the new microcode update that can cause instability.
- Add a new microcode state 'UCODE_OFFLINE' to report status on per-CPU basis.
- Force NMI on the offline CPUs.
Wake up offline CPUs while the update is in progress and then return them back to mwait_play_dead() after microcode update is complete.
--- arch/x86/include/asm/microcode.h | 1 +- arch/x86/kernel/cpu/microcode/core.c | 112 +++++++++++++++++++++- arch/x86/kernel/cpu/microcode/internal.h | 1 +- arch/x86/kernel/nmi.c | 5 +- 4 files changed, 113 insertions(+), 6 deletions(-)
arch/x86/include/asm/microcode.h +1 -0
@@ -73,6 +73,7 @@ static inline u32 intel_get_microcode_revision(void)#endif /* !CONFIG_CPU_SUP_INTEL */ bool microcode_nmi_handler (void ); +void microcode_offline_nmi_handler (void );#ifdef CONFIG_MICROCODE_LATE_LOADING DECLARE_STATIC_KEY_FALSE (microcode_nmi_handler_enable);
arch/x86/kernel/cpu/microcode/core.c +107 -5
171 lines not on this page · raw message
arch/x86/kernel/cpu/microcode/internal.h +1 -0
@@ -17,6 +17,7 @@ enum ucode_state {UCODE_NFOUND ,UCODE_ERROR ,UCODE_TIMEOUT , + UCODE_OFFLINE, }; struct microcode_ops {
arch/x86/kernel/nmi.c +4 -1
@@ -502,8 +502,11 @@ DEFINE_IDTENTRY_RAW(exc_nmi)if (IS_ENABLED (CONFIG_NMI_CHECK_CPU ))raw_atomic_long_inc (&nsp->idt_calls); -if (IS_ENABLED (CONFIG_SMP ) &&arch_cpu_is_offline (smp_processor_id ())) +if (IS_ENABLED (CONFIG_SMP ) &&arch_cpu_is_offline (smp_processor_id ())) { +if (microcode_nmi_handler_enabled ()) +microcode_offline_nmi_handler ();return ; + }if (this_cpu_read (nmi_state) !=NMI_NOT_RUNNING ) {this_cpu_write (nmi_state,NMI_LATCHED );
[tip: x86/microcode] x86/microcode: Handle "offline" CPUs correctly
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 8f849ff63bcbc77670da03cb8f2b78b06257f455 Gitweb: https://git.kernel.org/tip/8f849ff63bcbc77670da03cb8f2b78b06257f455 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 14:00:08 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Tue, 24 Oct 2023 15:05:55 +02:00
x86/microcode: Handle "offline" CPUs correctly
Offline CPUs need to be parked in a safe loop when microcode update is in progress on the primary CPU. Currently, offline CPUs are parked in mwait_play_dead(), and for Intel CPUs, its not a safe instruction, because the MWAIT instruction can be patched in the new microcode update that can cause instability.
- Add a new microcode state 'UCODE_OFFLINE' to report status on per-CPU basis.
- Force NMI on the offline CPUs.
Wake up offline CPUs while the update is in progress and then return them back to mwait_play_dead() after microcode update is complete.
--- arch/x86/include/asm/microcode.h | 1 +- arch/x86/kernel/cpu/microcode/core.c | 112 +++++++++++++++++++++- arch/x86/kernel/cpu/microcode/internal.h | 1 +- arch/x86/kernel/nmi.c | 5 +- 4 files changed, 113 insertions(+), 6 deletions(-)
arch/x86/include/asm/microcode.h +1 -0
@@ -73,6 +73,7 @@ static inline u32 intel_get_microcode_revision(void)#endif /* !CONFIG_CPU_SUP_INTEL */ bool microcode_nmi_handler (void ); +void microcode_offline_nmi_handler (void );#ifdef CONFIG_MICROCODE_LATE_LOADING DECLARE_STATIC_KEY_FALSE (microcode_nmi_handler_enable);
arch/x86/kernel/cpu/microcode/core.c +107 -5
171 lines not on this page · raw message
arch/x86/kernel/cpu/microcode/internal.h +1 -0
@@ -17,6 +17,7 @@ enum ucode_state {UCODE_NFOUND ,UCODE_ERROR ,UCODE_TIMEOUT , + UCODE_OFFLINE, }; struct microcode_ops {
arch/x86/kernel/nmi.c +4 -1
@@ -502,8 +502,11 @@ DEFINE_IDTENTRY_RAW(exc_nmi)if (IS_ENABLED (CONFIG_NMI_CHECK_CPU ))raw_atomic_long_inc (&nsp->idt_calls); -if (IS_ENABLED (CONFIG_SMP ) &&arch_cpu_is_offline (smp_processor_id ())) +if (IS_ENABLED (CONFIG_SMP ) &&arch_cpu_is_offline (smp_processor_id ())) { +if (microcode_nmi_handler_enabled ()) +microcode_offline_nmi_handler ();return ; + }if (this_cpu_read (nmi_state) !=NMI_NOT_RUNNING ) {this_cpu_write (nmi_state,NMI_LATCHED );
PATCH v4 29/30 x86/microcode: Prepare for minimal revision check
In reply to Thomas GleixnerFrom: Thomas Gleixner <[email protected]>
Applying microcode late can be fatal for the running kernel when the update changes functionality which is in use already in a non-compatible way, e.g. by removing a CPUID bit.
There is no way for admins which do not have access to the vendors deep technical support to decide whether late loading of such a microcode is safe or not.
Intel has added a new field to the microcode header which tells the minimal microcode revision which is required to be active in the CPU in order to be safe.
Provide infrastructure for handling this in the core code and a command line switch which allows to enforce it.
If the update is considered safe the kernel is not tainted and the annoying warning message not emitted. If it's enforced and the currently loaded microcode revision is not safe for late loading then the load is aborted.
--- Documentation/admin-guide/kernel-parameters.txt | 5 +++++ arch/x86/Kconfig | 23 ++++++++++++++++++++++- arch/x86/kernel/cpu/microcode/amd.c | 3 +++ arch/x86/kernel/cpu/microcode/core.c | 19 ++++++++++++++----- arch/x86/kernel/cpu/microcode/intel.c | 3 +++ arch/x86/kernel/cpu/microcode/internal.h | 3 +++ 6 files changed, 50 insertions(+), 6 deletions(-)
---
Documentation/admin-guide/kernel-parameters.txt +5 -0
@@ -3275,6 +3275,11 @@ mga= [HW,DRM] + microcode.force_minrev= [X86] + Format: <bool> + Enable or disable the microcode minimal revision + enforcement for the runtime microcode loader. + min_addr=nn[KMG] [KNL,BOOT,IA-64] All physical memory below this physical address is ignored.
arch/x86/Kconfig +22 -1
@@ -1322,7 +1322,28 @@ config MICROCODE_LATE_LOADING is a tricky business and should be avoided if possible. Just the sequence of synchronizing all cores and SMT threads is one fragile dance which does not guarantee that cores might not softlock after the loading. Therefore, - use this at your own risk. Late loading taints the kernel too. + use this at your own risk. Late loading taints the kernel unless the + microcode header indicates that it is safe for late loading via the + minimal revision check. This minimal revision check can be enforced on + the kernel command line with "microcode.minrev=Y". + +config MICROCODE_LATE_FORCE_MINREV + bool "Enforce late microcode loading minimal revision check" + default n + depends on MICROCODE_LATE_LOADING + help + To prevent that users load microcode late which modifies already + in use features, newer microcodes have a minimum revision field + in the microcode header, which tells the kernel which minimum + revision must be active in the CPU to safely load that new microcode + late into the running system. If disabled the check will not + be enforced but the kernel will be tainted when the minimal + revision check fails. + + This minimal revision check can also be controlled via the + "microcode.minrev" parameter on the kernel command line. + + If unsure say Y. config X86_MSR tristate "/dev/cpu/*/msr - Model-specific register support"
arch/x86/kernel/cpu/microcode/amd.c +3 -0
@@ -880,6 +880,9 @@ static enum ucode_state request_microcodenum ucode_state ret =UCODE_NFOUND ;const struct firmware *fw; +if (force_minrev) +return UCODE_NFOUND ; +if (c->x86 >=0x15 )snprintf (fw_name,sizeof (fw_name),"amd-ucode/microcode_amd_fam%.2xh.bin" , c->x86);
arch/x86/kernel/cpu/microcode/core.c +14 -5
@@ -46,6 +46,9 @@static struct microcode_ops *microcode_ops;static bool dis_ucode_ldr = true; +bool force_minrev =IS_ENABLED (CONFIG_MICROCODE_LATE_FORCE_MINREV ); +module_param (force_minrev, bool,S_IRUSR |S_IWUSR ); +bool initrd_gone;/*
@@ -559,15 +562,17 @@ static int ucode_load_cpus_stopped(voidreturn 0 ; } -static int load_late_stop_cpus (void ) +static int load_late_stop_cpus (bool is_safe) {unsigned int cpu, updated =0 , failed =0 , timedout =0 , siblings =0 ;unsigned int nr_offl, offline =0 ;int old_rev = boot_cpu_data.microcode;struct cpuinfo_x86 prev_info; -pr_err ("Attempting late microcode loading - it is dangerous and taints the kernel.\n" ); -pr_err ("You should switch to early loading, if possible.\n" ); +if (!is_safe) { +pr_err ("Late microcode loading without minimal revision check.\n" ); +pr_err ("You should switch to early loading, if possible.\n" ); + }atomic_set (&late_cpus_in,num_online_cpus ());atomic_set (&offline_in_nmi,0 );
@@ -617,7 +622,9 @@ static int load_late_stop_cpus(void)return -EIO ; } -add_taint (TAINT_CPU_OUT_OF_SPEC ,LOCKDEP_STILL_OK ); +if (!is_safe || failed || timedout) +add_taint (TAINT_CPU_OUT_OF_SPEC ,LOCKDEP_STILL_OK ); +pr_info ("load: updated on %u primary CPUs with %u siblings\n" , updated, siblings);if (failed || timedout) { pr_err("load incomplete. %u CPUs timed out or failed\n",
@@ -707,7 +714,9 @@ static int load_late_locked(void)switch (microcode_ops->request_microcode_fw (0 , µcode_pdev->dev)) {case UCODE_NEW : -return load_late_stop_cpus (); +return load_late_stop_cpus (false); +case UCODE_NEW_SAFE : +return load_late_stop_cpus (true);case UCODE_NFOUND :return -ENOENT ;default :
arch/x86/kernel/cpu/microcode/intel.c +3 -0
@@ -470,6 +470,9 @@ static enum ucode_state parse_microcode_unsigned int curr_mc_size =0 ;u8 *new_mc =NULL , *mc =NULL ; +if (force_minrev) +return UCODE_NFOUND ; +while (iov_iter_count (iter)) {struct microcode_header_intel mc_header;unsigned int mc_size, data_size;
arch/x86/kernel/cpu/microcode/internal.h +3 -0
@@ -13,6 +13,7 @@ struct device;enum ucode_state {UCODE_OK =0 ,UCODE_NEW , +UCODE_NEW_SAFE ,UCODE_UPDATED ,UCODE_NFOUND ,UCODE_ERROR ,
@@ -36,6 +37,8 @@ struct microcode_ops { use_nmi :1 ; }; +extern bool force_minrev; +extern struct ucode_cpu_info ucode_cpu_info[];struct cpio_data find_microcode_in_initrd (const char *path);
[tip: x86/microcode] x86/microcode: Prepare for minimal revision check
The following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: bbaea0e7fc4322ce997d6bc218ce3f2df5c64bb8 Gitweb: https://git.kernel.org/tip/bbaea0e7fc4322ce997d6bc218ce3f2df5c64bb8 Author: Thomas Gleixner <[email protected]> AuthorDate: Mon, 02 Oct 2023 14:00:10 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Fri, 06 Oct 2023 15:12:25 +02:00
x86/microcode: Prepare for minimal revision check
Applying microcode late can be fatal for the running kernel when the update changes functionality which is in use already in a non-compatible way, e.g. by removing a CPUID bit.
There is no way for admins which do not have access to the vendors deep technical support to decide whether late loading of such a microcode is safe or not.
Intel has added a new field to the microcode header which tells the minimal microcode revision which is required to be active in the CPU in order to be safe.
Provide infrastructure for handling this in the core code and a command line switch which allows to enforce it.
If the update is considered safe the kernel is not tainted and the annoying warning message not emitted. If it's enforced and the currently loaded microcode revision is not safe for late loading then the load is aborted.
--- Documentation/admin-guide/kernel-parameters.txt | 5 +++- arch/x86/Kconfig | 23 +++++++++++++++- arch/x86/kernel/cpu/microcode/amd.c | 3 ++- arch/x86/kernel/cpu/microcode/core.c | 19 +++++++++---- arch/x86/kernel/cpu/microcode/intel.c | 3 ++- arch/x86/kernel/cpu/microcode/internal.h | 3 ++- 6 files changed, 50 insertions(+), 6 deletions(-)
Documentation/admin-guide/kernel-parameters.txt +5 -0
@@ -3275,6 +3275,11 @@ mga= [HW,DRM] + microcode.force_minrev= [X86] + Format: <bool> + Enable or disable the microcode minimal revision + enforcement for the runtime microcode loader. + min_addr=nn[KMG] [KNL,BOOT,IA-64] All physical memory below this physical address is ignored.
arch/x86/Kconfig +22 -1
@@ -1322,7 +1322,28 @@ config MICROCODE_LATE_LOADING is a tricky business and should be avoided if possible. Just the sequence of synchronizing all cores and SMT threads is one fragile dance which does not guarantee that cores might not softlock after the loading. Therefore, - use this at your own risk. Late loading taints the kernel too. + use this at your own risk. Late loading taints the kernel unless the + microcode header indicates that it is safe for late loading via the + minimal revision check. This minimal revision check can be enforced on + the kernel command line with "microcode.minrev=Y". + +config MICROCODE_LATE_FORCE_MINREV + bool "Enforce late microcode loading minimal revision check" + default n + depends on MICROCODE_LATE_LOADING + help + To prevent that users load microcode late which modifies already + in use features, newer microcode patches have a minimum revision field + in the microcode header, which tells the kernel which minimum + revision must be active in the CPU to safely load that new microcode + late into the running system. If disabled the check will not + be enforced but the kernel will be tainted when the minimal + revision check fails. + + This minimal revision check can also be controlled via the + "microcode.minrev" parameter on the kernel command line. + + If unsure say Y. config X86_MSR tristate "/dev/cpu/*/msr - Model-specific register support"
arch/x86/kernel/cpu/microcode/amd.c +3 -0
@@ -880,6 +880,9 @@ static enum ucode_state request_microcode_amd(int cpu, struct device *device)enum ucode_state ret =UCODE_NFOUND ;const struct firmware *fw; +if (force_minrev) +return UCODE_NFOUND ; +if (c->x86 >=0x15 )snprintf (fw_name,sizeof (fw_name),"amd-ucode/microcode_amd_fam%.2xh.bin" , c->x86);
arch/x86/kernel/cpu/microcode/core.c +14 -5
@@ -46,6 +46,9 @@static struct microcode_ops *microcode_ops;static bool dis_ucode_ldr = true; +bool force_minrev =IS_ENABLED (CONFIG_MICROCODE_LATE_FORCE_MINREV ); +module_param (force_minrev, bool,S_IRUSR |S_IWUSR ); +bool initrd_gone;/*
@@ -560,15 +563,17 @@ static int load_cpus_stopped(void *unused)return 0 ; } -static int load_late_stop_cpus (void ) +static int load_late_stop_cpus (bool is_safe) {unsigned int cpu, updated =0 , failed =0 , timedout =0 , siblings =0 ;unsigned int nr_offl, offline =0 ;int old_rev = boot_cpu_data.microcode;struct cpuinfo_x86 prev_info; -pr_err ("Attempting late microcode loading - it is dangerous and taints the kernel.\n" ); -pr_err ("You should switch to early loading, if possible.\n" ); +if (!is_safe) { +pr_err ("Late microcode loading without minimal revision check.\n" ); +pr_err ("You should switch to early loading, if possible.\n" ); + }atomic_set (&late_cpus_in,num_online_cpus ());atomic_set (&offline_in_nmi,0 );
@@ -618,7 +623,9 @@ static int load_late_stop_cpus(void)return -EIO ; } -add_taint (TAINT_CPU_OUT_OF_SPEC ,LOCKDEP_STILL_OK ); +if (!is_safe || failed || timedout) +add_taint (TAINT_CPU_OUT_OF_SPEC ,LOCKDEP_STILL_OK ); +pr_info ("load: updated on %u primary CPUs with %u siblings\n" , updated, siblings);if (failed || timedout) { pr_err("load incomplete. %u CPUs timed out or failed\n",
@@ -708,7 +715,9 @@ static int load_late_locked(void)switch (microcode_ops->request_microcode_fw (0 , µcode_pdev->dev)) {case UCODE_NEW : -return load_late_stop_cpus (); +return load_late_stop_cpus (false); +case UCODE_NEW_SAFE : +return load_late_stop_cpus (true);case UCODE_NFOUND :return -ENOENT ;default :
arch/x86/kernel/cpu/microcode/intel.c +3 -0
@@ -471,6 +471,9 @@ static enum ucode_state parse_microcode_blobs(int cpu, struct iov_iter *iter)unsigned int curr_mc_size =0 ;u8 *new_mc =NULL , *mc =NULL ; +if (force_minrev) +return UCODE_NFOUND ; +while (iov_iter_count (iter)) {struct microcode_header_intel mc_header;unsigned int mc_size, data_size;
arch/x86/kernel/cpu/microcode/internal.h +3 -0
@@ -13,6 +13,7 @@ struct device;enum ucode_state {UCODE_OK =0 ,UCODE_NEW , +UCODE_NEW_SAFE ,UCODE_UPDATED ,UCODE_NFOUND ,UCODE_ERROR ,
@@ -36,6 +37,8 @@ struct microcode_ops { use_nmi :1 ; }; +extern bool force_minrev; +extern struct ucode_cpu_info ucode_cpu_info[];struct cpio_data find_microcode_in_initrd (const char *path);
PATCH v4 30/30 x86/microcode/intel: Add a minimum required revision for late-loads
In reply to Thomas GleixnerFrom: Ashok Raj <[email protected]>
In general users don't have the necessary information to determine whether late loading of a new microcode version is safe and does not modify anything which the currently running kernel uses already, e.g. removal of CPUID bits or behavioural changes of MSRs.
To address this issue, Intel has added a "minimum required version" field to a previously reserved field in the microcode header. Microcode updates should only be applied if the current microcode version is equal to, or greater than this minimum required version.
Thomas made some suggestions on how meta-data in the microcode file could provide Linux with information to decide if the new microcode is suitable candidate for late loading. But even the "simpler" option requires a lot of metadata and corresponding kernel code to parse it, so the final suggestion was to add the 'minimum required version' field in the header.
When microcode changes visible features, microcode will set the minimum required version to its own revision which prevents late loading.
Old microcode blobs have the minimum revision field always set to 0, which indicates that there is no information and the kernel considers it as unsafe.
This is a pure OS software mechanism. The hardware/firmware ignores this header field.
For early loading there is no restriction because OS visible features are enumerated after the early load and therefor a change has no effect.
The check is always enabled, but by default not enforced. It can be enforced via Kconfig or kernel command line.
If enforced, the kernel refuses to late load microcode with a minium required version field which is zero or when the currently loaded microcode revision is smaller than the minimum required revision.
If not enforced the load happens independent of the revision check to stay compatible with the existing behaviour, but it influences the decision whether the kernel is tainted or not. If the check signals that the late load is safe, then the kernel is not tainted.
Early loading is not affected by this.
[ tglx: Massaged changelog and fixed up the implementation ]
--- arch/x86/include/asm/microcode.h | 3 +- arch/x86/kernel/cpu/microcode/intel.c | 37 ++++++++++++++++++++++++++++++---- 2 files changed, 35 insertions(+), 5 deletions(-)
---
arch/x86/include/asm/microcode.h +2 -1
@@ -36,7 +36,8 @@ struct microcode_header_intel {unsigned int datasize;unsigned int totalsize;unsigned int metasize; -unsigned int reserved[2 ]; +unsigned int min_req_ver; +unsigned int reserved; };struct microcode_intel {
arch/x86/kernel/cpu/microcode/intel.c +33 -4
@@ -463,16 +463,40 @@ static enum ucode_state apply_microcode_return ret; } +static bool ucode_validate_minrev (struct microcode_header_intel *mc_header) +{ +int cur_rev = boot_cpu_data.microcode; + +/* +* When late-loading, ensure the header declares a minimum revision +* required to perform a late-load. The previously reserved field +* is 0 in older microcode blobs. +*/ +if (!mc_header->min_req_ver) { +pr_info ("Unsafe microcode update: Microcode header does not specify a required min version\n" ); +return false; + } + +/* +* Check whether the minimum revision specified in the header is either +* greater or equal to the current revision. +*/ +if (cur_rev < mc_header->min_req_ver) { +pr_info ("Unsafe microcode update: Current revision 0x%x too old\n" , cur_rev); +pr_info ("Current should be at 0x%x or higher. Use early loading instead\n" , mc_header->min_req_ver); +return false; + } +return true; +} +static enum ucode_state parse_microcode_blobs (int cpu,struct iov_iter *iter) {struct ucode_cpu_info *uci = ucode_cpu_info + cpu; +bool is_safe, new_is_safe = false;int cur_rev = uci->cpu_sig.rev;unsigned int curr_mc_size =0 ;u8 *new_mc =NULL , *mc =NULL ; -if (force_minrev) -return UCODE_NFOUND ; -while (iov_iter_count (iter)) {struct microcode_header_intel mc_header;unsigned int mc_size, data_size;
@@ -515,9 +539,14 @@ static enum ucode_state parse_microcode_if (!intel_find_matching_signature (mc, &uci->cpu_sig))continue ; + is_safe =ucode_validate_minrev (&mc_header); +if (force_minrev && !is_safe) +continue ; +kvfree (new_mc); cur_rev = mc_header.rev; new_mc = mc; + new_is_safe = is_safe; mc =NULL ; }
@@ -529,7 +558,7 @@ static enum ucode_state parse_microcode_return UCODE_NFOUND ; ucode_patch_late = (struct microcode_intel *)new_mc; -return UCODE_NEW ; +return new_is_safe ?UCODE_NEW_SAFE :UCODE_NEW ; fail:kvfree (mc);
[tip: x86/microcode] x86/microcode/intel: Add a minimum required revision for late loading
The following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: 9975802d3f7426abc32fd9ac0924a639d11a7c4f Gitweb: https://git.kernel.org/tip/9975802d3f7426abc32fd9ac0924a639d11a7c4f Author: Ashok Raj <[email protected]> AuthorDate: Mon, 02 Oct 2023 14:00:11 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Fri, 06 Oct 2023 15:12:25 +02:00
x86/microcode/intel: Add a minimum required revision for late loading
In general users, don't have the necessary information to determine whether late loading of a new microcode version is safe and does not modify anything which the currently running kernel uses already, e.g. removal of CPUID bits or behavioural changes of MSRs.
To address this issue, Intel has added a "minimum required version" field to a previously reserved field in the microcode header. Microcode updates should only be applied if the current microcode version is equal to, or greater than this minimum required version.
Thomas made some suggestions on how meta-data in the microcode file could provide Linux with information to decide if the new microcode is suitable candidate for late loading. But even the "simpler" option requires a lot of metadata and corresponding kernel code to parse it, so the final suggestion was to add the 'minimum required version' field in the header.
When microcode changes visible features, microcode will set the minimum required version to its own revision which prevents late loading.
Old microcode blobs have the minimum revision field always set to 0, which indicates that there is no information and the kernel considers it unsafe.
This is a pure OS software mechanism. The hardware/firmware ignores this header field.
For early loading there is no restriction because OS visible features are enumerated after the early load and therefore a change has no effect.
The check is always enabled, but by default not enforced. It can be enforced via Kconfig or kernel command line.
If enforced, the kernel refuses to late load microcode with a minimum required version field which is zero or when the currently loaded microcode revision is smaller than the minimum required revision.
If not enforced the load happens independent of the revision check to stay compatible with the existing behaviour, but it influences the decision whether the kernel is tainted or not. If the check signals that the late load is safe, then the kernel is not tainted.
Early loading is not affected by this.
[ tglx: Massaged changelog and fixed up the implementation ]
--- arch/x86/include/asm/microcode.h | 3 +- arch/x86/kernel/cpu/microcode/intel.c | 37 +++++++++++++++++++++++--- 2 files changed, 35 insertions(+), 5 deletions(-)
arch/x86/include/asm/microcode.h +2 -1
@@ -36,7 +36,8 @@ struct microcode_header_intel {unsigned int datasize;unsigned int totalsize;unsigned int metasize; -unsigned int reserved[2 ]; +unsigned int min_req_ver; +unsigned int reserved; };struct microcode_intel {
arch/x86/kernel/cpu/microcode/intel.c +33 -4
@@ -464,16 +464,40 @@ static enum ucode_state apply_microcode_late(int cpu)return ret; } +static bool ucode_validate_minrev (struct microcode_header_intel *mc_header) +{ +int cur_rev = boot_cpu_data.microcode; + +/* +* When late-loading, ensure the header declares a minimum revision +* required to perform a late-load. The previously reserved field +* is 0 in older microcode blobs. +*/ +if (!mc_header->min_req_ver) { +pr_info ("Unsafe microcode update: Microcode header does not specify a required min version\n" ); +return false; + } + +/* +* Check whether the current revision is either greater or equal to +* to the minimum revision specified in the header. +*/ +if (cur_rev < mc_header->min_req_ver) { +pr_info ("Unsafe microcode update: Current revision 0x%x too old\n" , cur_rev); +pr_info ("Current should be at 0x%x or higher. Use early loading instead\n" , mc_header->min_req_ver); +return false; + } +return true; +} +static enum ucode_state parse_microcode_blobs (int cpu,struct iov_iter *iter) {struct ucode_cpu_info *uci = ucode_cpu_info + cpu; +bool is_safe, new_is_safe = false;int cur_rev = uci->cpu_sig.rev;unsigned int curr_mc_size =0 ;u8 *new_mc =NULL , *mc =NULL ; -if (force_minrev) -return UCODE_NFOUND ; -while (iov_iter_count (iter)) {struct microcode_header_intel mc_header;unsigned int mc_size, data_size;
@@ -516,9 +540,14 @@ static enum ucode_state parse_microcode_blobs(int cpu, struct iov_iter *iter)if (!intel_find_matching_signature (mc, &uci->cpu_sig))continue ; + is_safe =ucode_validate_minrev (&mc_header); +if (force_minrev && !is_safe) +continue ; +kvfree (new_mc); cur_rev = mc_header.rev; new_mc = mc; + new_is_safe = is_safe; mc =NULL ; }
@@ -530,7 +559,7 @@ static enum ucode_state parse_microcode_blobs(int cpu, struct iov_iter *iter)return UCODE_NFOUND ; ucode_patch_late = (struct microcode_intel *)new_mc; -return UCODE_NEW ; +return new_is_safe ?UCODE_NEW_SAFE :UCODE_NEW ; fail:kvfree (mc);
[tip: x86/microcode] x86/microcode/intel: Add a minimum required revision for late loading
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: d975d2d3f52eb942df34f9fe3a782a02f0650db2 Gitweb: https://git.kernel.org/tip/d975d2d3f52eb942df34f9fe3a782a02f0650db2 Author: Ashok Raj <[email protected]> AuthorDate: Mon, 02 Oct 2023 14:00:11 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Thu, 19 Oct 2023 16:02:50 +02:00
x86/microcode/intel: Add a minimum required revision for late loading
In general users, don't have the necessary information to determine whether late loading of a new microcode version is safe and does not modify anything which the currently running kernel uses already, e.g. removal of CPUID bits or behavioural changes of MSRs.
To address this issue, Intel has added a "minimum required version" field to a previously reserved field in the microcode header. Microcode updates should only be applied if the current microcode version is equal to, or greater than this minimum required version.
Thomas made some suggestions on how meta-data in the microcode file could provide Linux with information to decide if the new microcode is suitable candidate for late loading. But even the "simpler" option requires a lot of metadata and corresponding kernel code to parse it, so the final suggestion was to add the 'minimum required version' field in the header.
When microcode changes visible features, microcode will set the minimum required version to its own revision which prevents late loading.
Old microcode blobs have the minimum revision field always set to 0, which indicates that there is no information and the kernel considers it unsafe.
This is a pure OS software mechanism. The hardware/firmware ignores this header field.
For early loading there is no restriction because OS visible features are enumerated after the early load and therefore a change has no effect.
The check is always enabled, but by default not enforced. It can be enforced via Kconfig or kernel command line.
If enforced, the kernel refuses to late load microcode with a minimum required version field which is zero or when the currently loaded microcode revision is smaller than the minimum required revision.
If not enforced the load happens independent of the revision check to stay compatible with the existing behaviour, but it influences the decision whether the kernel is tainted or not. If the check signals that the late load is safe, then the kernel is not tainted.
Early loading is not affected by this.
[ tglx: Massaged changelog and fixed up the implementation ]
--- arch/x86/include/asm/microcode.h | 3 +- arch/x86/kernel/cpu/microcode/intel.c | 37 +++++++++++++++++++++++--- 2 files changed, 35 insertions(+), 5 deletions(-)
arch/x86/include/asm/microcode.h +2 -1
@@ -38,7 +38,8 @@ struct microcode_header_intel {unsigned int datasize;unsigned int totalsize;unsigned int metasize; -unsigned int reserved[2 ]; +unsigned int min_req_ver; +unsigned int reserved; };struct microcode_intel {
arch/x86/kernel/cpu/microcode/intel.c +33 -4
@@ -457,16 +457,40 @@ static enum ucode_state apply_microcode_late(int cpu)return ret; } +static bool ucode_validate_minrev (struct microcode_header_intel *mc_header) +{ +int cur_rev = boot_cpu_data.microcode; + +/* +* When late-loading, ensure the header declares a minimum revision +* required to perform a late-load. The previously reserved field +* is 0 in older microcode blobs. +*/ +if (!mc_header->min_req_ver) { +pr_info ("Unsafe microcode update: Microcode header does not specify a required min version\n" ); +return false; + } + +/* +* Check whether the current revision is either greater or equal to +* to the minimum revision specified in the header. +*/ +if (cur_rev < mc_header->min_req_ver) { +pr_info ("Unsafe microcode update: Current revision 0x%x too old\n" , cur_rev); +pr_info ("Current should be at 0x%x or higher. Use early loading instead\n" , mc_header->min_req_ver); +return false; + } +return true; +} +static enum ucode_state parse_microcode_blobs (int cpu,struct iov_iter *iter) {struct ucode_cpu_info *uci = ucode_cpu_info + cpu; +bool is_safe, new_is_safe = false;int cur_rev = uci->cpu_sig.rev;unsigned int curr_mc_size =0 ;u8 *new_mc =NULL , *mc =NULL ; -if (force_minrev) -return UCODE_NFOUND ; -while (iov_iter_count (iter)) {struct microcode_header_intel mc_header;unsigned int mc_size, data_size;
@@ -509,9 +533,14 @@ static enum ucode_state parse_microcode_blobs(int cpu, struct iov_iter *iter)if (!intel_find_matching_signature (mc, &uci->cpu_sig))continue ; + is_safe =ucode_validate_minrev (&mc_header); +if (force_minrev && !is_safe) +continue ; +kvfree (new_mc); cur_rev = mc_header.rev; new_mc = mc; + new_is_safe = is_safe; mc =NULL ; }
@@ -523,7 +552,7 @@ static enum ucode_state parse_microcode_blobs(int cpu, struct iov_iter *iter)return UCODE_NFOUND ; ucode_patch_late = (struct microcode_intel *)new_mc; -return UCODE_NEW ; +return new_is_safe ?UCODE_NEW_SAFE :UCODE_NEW ; fail:kvfree (mc);
[tip: x86/microcode] x86/microcode/intel: Add a minimum required revision for late loading
In reply to Thomas GleixnerThe following commit has been merged into the x86/microcode branch of tip:
The following commit has been merged into the x86/microcode branch of tip:
Commit-ID: cf5ab01c87030a085e211a0a327535932ec6f719 Gitweb: https://git.kernel.org/tip/cf5ab01c87030a085e211a0a327535932ec6f719 Author: Ashok Raj <[email protected]> AuthorDate: Mon, 02 Oct 2023 14:00:11 +02:00 Committer: Borislav Petkov (AMD) <[email protected]> CommitterDate: Tue, 24 Oct 2023 15:05:55 +02:00
x86/microcode/intel: Add a minimum required revision for late loading
In general users, don't have the necessary information to determine whether late loading of a new microcode version is safe and does not modify anything which the currently running kernel uses already, e.g. removal of CPUID bits or behavioural changes of MSRs.
To address this issue, Intel has added a "minimum required version" field to a previously reserved field in the microcode header. Microcode updates should only be applied if the current microcode version is equal to, or greater than this minimum required version.
Thomas made some suggestions on how meta-data in the microcode file could provide Linux with information to decide if the new microcode is suitable candidate for late loading. But even the "simpler" option requires a lot of metadata and corresponding kernel code to parse it, so the final suggestion was to add the 'minimum required version' field in the header.
When microcode changes visible features, microcode will set the minimum required version to its own revision which prevents late loading.
Old microcode blobs have the minimum revision field always set to 0, which indicates that there is no information and the kernel considers it unsafe.
This is a pure OS software mechanism. The hardware/firmware ignores this header field.
For early loading there is no restriction because OS visible features are enumerated after the early load and therefore a change has no effect.
The check is always enabled, but by default not enforced. It can be enforced via Kconfig or kernel command line.
If enforced, the kernel refuses to late load microcode with a minimum required version field which is zero or when the currently loaded microcode revision is smaller than the minimum required revision.
If not enforced the load happens independent of the revision check to stay compatible with the existing behaviour, but it influences the decision whether the kernel is tainted or not. If the check signals that the late load is safe, then the kernel is not tainted.
Early loading is not affected by this.
[ tglx: Massaged changelog and fixed up the implementation ]
--- arch/x86/include/asm/microcode.h | 3 +- arch/x86/kernel/cpu/microcode/intel.c | 37 +++++++++++++++++++++++--- 2 files changed, 35 insertions(+), 5 deletions(-)
arch/x86/include/asm/microcode.h +2 -1
@@ -38,7 +38,8 @@ struct microcode_header_intel {unsigned int datasize;unsigned int totalsize;unsigned int metasize; -unsigned int reserved[2 ]; +unsigned int min_req_ver; +unsigned int reserved; };struct microcode_intel {
arch/x86/kernel/cpu/microcode/intel.c +33 -4
@@ -473,16 +473,40 @@ static enum ucode_state apply_microcode_late(int cpu)return ret; } +static bool ucode_validate_minrev (struct microcode_header_intel *mc_header) +{ +int cur_rev = boot_cpu_data.microcode; + +/* +* When late-loading, ensure the header declares a minimum revision +* required to perform a late-load. The previously reserved field +* is 0 in older microcode blobs. +*/ +if (!mc_header->min_req_ver) { +pr_info ("Unsafe microcode update: Microcode header does not specify a required min version\n" ); +return false; + } + +/* +* Check whether the current revision is either greater or equal to +* to the minimum revision specified in the header. +*/ +if (cur_rev < mc_header->min_req_ver) { +pr_info ("Unsafe microcode update: Current revision 0x%x too old\n" , cur_rev); +pr_info ("Current should be at 0x%x or higher. Use early loading instead\n" , mc_header->min_req_ver); +return false; + } +return true; +} +static enum ucode_state parse_microcode_blobs (int cpu,struct iov_iter *iter) {struct ucode_cpu_info *uci = ucode_cpu_info + cpu; +bool is_safe, new_is_safe = false;int cur_rev = uci->cpu_sig.rev;unsigned int curr_mc_size =0 ;u8 *new_mc =NULL , *mc =NULL ; -if (force_minrev) -return UCODE_NFOUND ; -while (iov_iter_count (iter)) {struct microcode_header_intel mc_header;unsigned int mc_size, data_size;
@@ -525,9 +549,14 @@ static enum ucode_state parse_microcode_blobs(int cpu, struct iov_iter *iter)if (!intel_find_matching_signature (mc, &uci->cpu_sig))continue ; + is_safe =ucode_validate_minrev (&mc_header); +if (force_minrev && !is_safe) +continue ; +kvfree (new_mc); cur_rev = mc_header.rev; new_mc = mc; + new_is_safe = is_safe; mc =NULL ; }
@@ -539,7 +568,7 @@ static enum ucode_state parse_microcode_blobs(int cpu, struct iov_iter *iter)return UCODE_NFOUND ; ucode_patch_late = (struct microcode_intel *)new_mc; -return UCODE_NEW ; +return new_is_safe ?UCODE_NEW_SAFE :UCODE_NEW ; fail:kvfree (mc);
Hi Thomas,
Quoted 6 lines
...
The series is also available from git:
git://git.kernel.org/pub/scm/linux/kernel/git/tglx/devel.gitucode-v4
...
Test Result (same as ucode-v3)
------------------------------
Tested 'ucode-v4' on an Intel Sapphire Rapids server that both early load and late load worked well. For more details, please refer to the test below:
Tested Machine
--------------
Intel Sapphire Rapids server with 2 sockets, each containing 48 cores, resulting in a total of 192 threads.
Microcodes
----------
a) Microcode revisison of CPU : 0xab000130
b) Microcode revision in the initramfs : 0xab000140 // for early load
c) Microcode revision in /lib/firmware/intel-ucode/* : 0xab000160 // for late load
[ Microcode b) & c) headers both contain minirev 0x2b0000a1. ]
Dmesg log
---------
// Early load OK.
[ 0.000000] microcode: updated early: 0xab000130 -> 0xab000140, date = 2022-11-04
...
[ 20.215653] microcode: Microcode Update Driver: v2.2.
...
// Late load OK.
[ 27.596822] microcode: Updated to revision 0xab000160, date = 2022-11-16 [ 27.606848] microcode: load: updated on 96 primary CPUs with 96 siblings [ 27.614789] microcode: revision: 0xab000140 -> 0xab000160
Thanks!
-Qiuxu
Borislav Petkov
maintainerQuoted 4 lines · Qiuxu Zhuo
Test Result (same as ucode-v3)
------------------------------
Tested 'ucode-v4' on an Intel Sapphire Rapids server that both early load and late load worked well. For more details, please refer to the test below:
Thanks.
I've found a couple of issues and once I'm done with my testing, I'll push tip:x86/microcode and you could run it then to make sure it all is still ok.
Signature · 4 lines
Regards/Gruss,
Boris.
https://people.kernel.org/tglx/notes-about-netiquetteQuoted 6 lines
From: Borislav Petkov <[email protected]>
...Quoted 4 lines
Test Result (same as ucode-v3)
------------------------------
Tested 'ucode-v4' on an Intel Sapphire Rapids server that both early load and late load worked well. For more details, please refer to the test
Quoted 6 lines
below:
Thanks.
I've found a couple of issues and once I'm done with my testing, I'll push tip:x86/microcode and you could run it then to make sure it all is still ok.
Hi Boris,
OK. I'll re-run the test once you push the code to tip:x86/microcode.
-Qiuxu
Hi Boris,
Quoted 6 lines
From: Borislav Petkov <[email protected]>
...Quoted 4 lines
Test Result (same as ucode-v3)
------------------------------
Tested 'ucode-v4' on an Intel Sapphire Rapids server that both early load and late load worked well. For more details, please refer to the test
Quoted 6 lines
below:
Thanks.
I've found a couple of issues and once I'm done with my testing, I'll push tip:x86/microcode and you could run it then to make sure it all is still ok.
Test Result (same as ucode-v4)
------------------------------
Tested tip:x86/microcode (top commit 9975802d3f74) on an Intel Sapphire Rapids server that both early load and late load worked well. For more details, please refer to the test below:
Tested Machine
--------------
Intel Sapphire Rapids server with 2 sockets, each containing 48 cores, resulting in a total of 192 threads.
Microcodes
----------
a) Microcode revisison of CPU : 0xab000130
b) Microcode revision in the initramfs : 0xab000140 // for early load
c) Microcode revision in /lib/firmware/intel-ucode/* : 0xab000160 // for late load
[ Microcode b) & c) headers both contain minirev 0x2b0000a1. ]
Dmesg log
---------
// Early load OK.
[ 0.000000] microcode: updated early: 0xab000130 -> 0xab000140, date = 2022-11-04
...
[ 20.261926] microcode: Microcode Update Driver: v2.2. ...
// Late load OK.
[ 27.400858] microcode: Updated to revision 0xab000160, date = 2022-11-16 [ 27.409978] microcode: load: updated on 96 primary CPUs with 96 siblings [ 27.409997] microcode: revision: 0xab000140 -> 0xab000160
cpuinfo
-------
cat /proc/cpuinfo | grep -m1 microcode
microcode : 0xab000160
Thanks!
-Qiuxu
Borislav Petkov
maintainerQuoted 5 lines · Qiuxu
Test Result (same as ucode-v4)
------------------------------
Tested tip:x86/microcode (top commit9975802d3f74) on an Intel Sapphire Rapids server that both early load and late load worked well. For more details, please refer to the test below:
Thanks!
Signature · 4 lines
Regards/Gruss,
Boris.
https://people.kernel.org/tglx/notes-about-netiquette