| From ae91fb90e0b9558407ec5ba02208b1c2a00aa205 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Mon, 9 Jan 2023 07:35:51 -0800 |
| Subject: x86/microcode: Check CPU capabilities after late microcode update |
| correctly |
| |
| From: Ashok Raj <ashok.raj@intel.com> |
| |
| [ Upstream commit c0dd9245aa9e25a697181f6085692272c9ec61bc ] |
| |
| The kernel caches each CPU's feature bits at boot in an x86_capability[] |
| structure. However, the capabilities in the BSP's copy can be turned off |
| as a result of certain command line parameters or configuration |
| restrictions, for example the SGX bit. This can cause a mismatch when |
| comparing the values before and after the microcode update. |
| |
| Another example is X86_FEATURE_SRBDS_CTRL which gets added only after |
| microcode update: |
| |
| # --- cpuid.before 2023-01-21 14:54:15.652000747 +0100 |
| # +++ cpuid.after 2023-01-21 14:54:26.632001024 +0100 |
| # @@ -10,7 +10,7 @@ CPU: |
| # 0x00000004 0x04: eax=0x00000000 ebx=0x00000000 ecx=0x00000000 edx=0x00000000 |
| # 0x00000005 0x00: eax=0x00000040 ebx=0x00000040 ecx=0x00000003 edx=0x11142120 |
| # 0x00000006 0x00: eax=0x000027f7 ebx=0x00000002 ecx=0x00000001 edx=0x00000000 |
| # - 0x00000007 0x00: eax=0x00000000 ebx=0x029c6fbf ecx=0x40000000 edx=0xbc002400 |
| # + 0x00000007 0x00: eax=0x00000000 ebx=0x029c6fbf ecx=0x40000000 edx=0xbc002e00 |
| ^^^ |
| |
| and which proves for a gazillionth time that late loading is a bad bad |
| idea. |
| |
| microcode_check() is called after an update to report any previously |
| cached CPUID bits which might have changed due to the update. |
| |
| Therefore, store the cached CPU caps before the update and compare them |
| with the CPU caps after the microcode update has succeeded. |
| |
| Thus, the comparison is done between the CPUID *hardware* bits before |
| and after the upgrade instead of using the cached, possibly runtime |
| modified values in BSP's boot_cpu_data copy. |
| |
| As a result, false warnings about CPUID bits changes are avoided. |
| |
| [ bp: |
| - Massage. |
| - Add SRBDS_CTRL example. |
| - Add kernel-doc. |
| - Incorporate forgotten review feedback from dhansen. |
| ] |
| |
| Fixes: 1008c52c09dc ("x86/CPU: Add a microcode loader callback") |
| Signed-off-by: Ashok Raj <ashok.raj@intel.com> |
| Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de> |
| Link: https://lore.kernel.org/r/20230109153555.4986-3-ashok.raj@intel.com |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| arch/x86/include/asm/processor.h | 1 + |
| arch/x86/kernel/cpu/common.c | 36 ++++++++++++++++++---------- |
| arch/x86/kernel/cpu/microcode/core.c | 6 +++++ |
| 3 files changed, 30 insertions(+), 13 deletions(-) |
| |
| diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h |
| index 6d40c409ebc16..60514502ead67 100644 |
| --- a/arch/x86/include/asm/processor.h |
| +++ b/arch/x86/include/asm/processor.h |
| @@ -841,6 +841,7 @@ bool xen_set_default_idle(void); |
| |
| void __noreturn stop_this_cpu(void *dummy); |
| void microcode_check(struct cpuinfo_x86 *prev_info); |
| +void store_cpu_caps(struct cpuinfo_x86 *info); |
| |
| enum l1tf_mitigations { |
| L1TF_MITIGATION_OFF, |
| diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c |
| index f724002adbfc3..e2dee60108460 100644 |
| --- a/arch/x86/kernel/cpu/common.c |
| +++ b/arch/x86/kernel/cpu/common.c |
| @@ -2130,6 +2130,25 @@ void cpu_init_secondary(void) |
| #endif |
| |
| #ifdef CONFIG_MICROCODE_LATE_LOADING |
| +/** |
| + * store_cpu_caps() - Store a snapshot of CPU capabilities |
| + * @curr_info: Pointer where to store it |
| + * |
| + * Returns: None |
| + */ |
| +void store_cpu_caps(struct cpuinfo_x86 *curr_info) |
| +{ |
| + /* Reload CPUID max function as it might've changed. */ |
| + curr_info->cpuid_level = cpuid_eax(0); |
| + |
| + /* Copy all capability leafs and pick up the synthetic ones. */ |
| + memcpy(&curr_info->x86_capability, &boot_cpu_data.x86_capability, |
| + sizeof(curr_info->x86_capability)); |
| + |
| + /* Get the hardware CPUID leafs */ |
| + get_cpu_cap(curr_info); |
| +} |
| + |
| /** |
| * microcode_check() - Check if any CPU capabilities changed after an update. |
| * @prev_info: CPU capabilities stored before an update. |
| @@ -2142,22 +2161,13 @@ void cpu_init_secondary(void) |
| */ |
| void microcode_check(struct cpuinfo_x86 *prev_info) |
| { |
| - perf_check_microcode(); |
| - |
| - /* Reload CPUID max function as it might've changed. */ |
| - prev_info->cpuid_level = cpuid_eax(0); |
| + struct cpuinfo_x86 curr_info; |
| |
| - /* |
| - * Copy all capability leafs to pick up the synthetic ones so that |
| - * memcmp() below doesn't fail on that. The ones coming from CPUID will |
| - * get overwritten in get_cpu_cap(). |
| - */ |
| - memcpy(&prev_info->x86_capability, &boot_cpu_data.x86_capability, |
| - sizeof(prev_info->x86_capability)); |
| + perf_check_microcode(); |
| |
| - get_cpu_cap(prev_info); |
| + store_cpu_caps(&curr_info); |
| |
| - if (!memcmp(&prev_info->x86_capability, &boot_cpu_data.x86_capability, |
| + if (!memcmp(&prev_info->x86_capability, &curr_info.x86_capability, |
| sizeof(prev_info->x86_capability))) |
| return; |
| |
| diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c |
| index 2c70f3cfae68e..122da99bac4e4 100644 |
| --- a/arch/x86/kernel/cpu/microcode/core.c |
| +++ b/arch/x86/kernel/cpu/microcode/core.c |
| @@ -514,6 +514,12 @@ static int microcode_reload_late(void) |
| atomic_set(&late_cpus_in, 0); |
| atomic_set(&late_cpus_out, 0); |
| |
| + /* |
| + * Take a snapshot before the microcode update in order to compare and |
| + * check whether any bits changed after an update. |
| + */ |
| + store_cpu_caps(&prev_info); |
| + |
| ret = stop_machine_cpuslocked(__reload_late, NULL, cpu_online_mask); |
| if (ret == 0) |
| microcode_check(&prev_info); |
| -- |
| 2.39.2 |
| |