x86/microcode: Add a microcode loader callback
Subsystems (2)
Borislav Petkov
maintainercover x86/microcode: Add a microcode loader callback
From: Borislav Petkov <[email protected]>
Add a callback which the microcode loader calls after microcode has been upgraded late to let the user know that any CPUID changes potentially won't take effect.
One possible example for this is that late loading happens after alternatives have run and thus patching which relies on those new CPUID features won't happen.
@Ashok: I haven't added your tags yet because I did a small change to run the callback only when no error has been encountered during loading. Please run them on your setup to confirm everything's still fine.
Thanks.
Borislav Petkov (3):
x86/microcode: Propagate return value from updating functions x86/CPU: Add a microcode loader callback
x86/CPU: Check CPU feature bits after microcode upgrade
arch/x86/include/asm/microcode.h | 9 ++++++-- arch/x86/include/asm/processor.h | 1 + arch/x86/kernel/cpu/common.c | 30 +++++++++++++++++++++++++++ arch/x86/kernel/cpu/microcode/amd.c | 10 ++++----- arch/x86/kernel/cpu/microcode/core.c | 39 ++++++++++++++++++++--------------- arch/x86/kernel/cpu/microcode/intel.c | 10 ++++----- 6 files changed, 70 insertions(+), 29 deletions(-)
Signature · 1 line
2.13.0
Borislav Petkov
maintainerPATCH 1/3 x86/microcode: Propagate return value from updating functions
From: Borislav Petkov <[email protected]>
... so that callers can know when microcode was updated and act accordingly.
--- arch/x86/include/asm/microcode.h | 9 +++++++-- arch/x86/kernel/cpu/microcode/amd.c | 10 +++++----- arch/x86/kernel/cpu/microcode/core.c | 33 +++++++++++++++++---------------- arch/x86/kernel/cpu/microcode/intel.c | 10 +++++----- 4 files changed, 34 insertions(+), 28 deletions(-)
arch/x86/include/asm/microcode.h +7 -2
@@ -37,7 +37,12 @@ struct cpu_signature {struct device ; -enum ucode_state {UCODE_ERROR ,UCODE_OK ,UCODE_NFOUND }; +enum ucode_state { +UCODE_OK =0 , +UCODE_UPDATED , +UCODE_NFOUND , +UCODE_ERROR , +};struct microcode_ops { enum ucode_state (*request_microcode_user) (int cpu,
@@ -54,7 +59,7 @@ struct microcode_ops {* are being called. * See also the "Synchronization" section in microcode_core.c. */ -int (*apply_microcode) (int cpu); +enum ucode_state (*apply_microcode) (int cpu);int (*collect_cpu_info) (int cpu,struct cpu_signature *csig); };
arch/x86/kernel/cpu/microcode/amd.c +5 -5
@@ -498,7 +498,7 @@ static unsigned int verify_patch_size(u8 family, u32 patch_size,return patch_size; } -static int apply_microcode_amd (int cpu) +static enum ucode_state apply_microcode_amd (int cpu) {struct cpuinfo_x86 *c = &cpu_data (cpu);struct microcode_amd *mc_amd;
@@ -512,7 +512,7 @@ static int apply_microcode_amd(int cpu) p =find_patch (cpu);if (!p) -return 0 ; +return UCODE_NFOUND ; mc_amd = p->data; uci->mc = p->data;
@@ -523,13 +523,13 @@ static int apply_microcode_amd(int cpu)if (rev >= mc_amd->hdr.patch_id) { c->microcode = rev; uci->cpu_sig.rev = rev; -return 0 ; +return UCODE_OK ; }if (__apply_microcode_amd (mc_amd)) {pr_err ("CPU%d: update failed for patch_level=0x%08x\n" , cpu, mc_amd->hdr.patch_id); -return -1 ; +return UCODE_ERROR ; }pr_info ("CPU%d: new patch_level=0x%08x\n" , cpu, mc_amd->hdr.patch_id);
@@ -537,7 +537,7 @@ static int apply_microcode_amd(int cpu) uci->cpu_sig.rev = mc_amd->hdr.patch_id; c->microcode = mc_amd->hdr.patch_id; -return 0 ; +return UCODE_UPDATED ; }static int install_equiv_cpu_table (const u8 *buf)
arch/x86/kernel/cpu/microcode/core.c +17 -16
@@ -374,7 +374,7 @@ static int collect_cpu_info(int cpu) }struct apply_microcode_ctx { -int err; +enum ucode_state err; };static void apply_microcode_local (void *arg)
@@ -489,31 +489,29 @@ static void __exit microcode_dev_exit(void)/* fake device for request_firmware */ static struct platform_device *microcode_pdev; -static int reload_for_cpu (int cpu) +static enum ucode_state reload_for_cpu (int cpu) {struct ucode_cpu_info *uci = ucode_cpu_info + cpu;enum ucode_state ustate; -int err =0 ;if (!uci->valid) -return err; +return UCODE_OK ; ustate = microcode_ops->request_microcode_fw (cpu, µcode_pdev->dev, true); -if (ustate ==UCODE_OK ) -apply_microcode_on_target (cpu); -else -if (ustate ==UCODE_ERROR ) - err = -EINVAL ; -return err; +if (ustate !=UCODE_OK ) +return ustate; + +return apply_microcode_on_target (cpu); }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 ;unsigned long val; +ssize_t ret =0 ;int cpu; -ssize_t ret =0 , tmp_ret; ret =kstrtoul (buf,0 , &val);if (ret)
@@ -526,15 +524,18 @@ static ssize_t reload_store(struct device *dev,mutex_lock (µcode_mutex);for_each_online_cpu (cpu) { tmp_ret =reload_for_cpu (cpu); -if (tmp_ret !=0 ) +if (tmp_ret >UCODE_NFOUND ) {pr_warn ("Error reloading microcode on CPU %d\n" , cpu); -/* save retval of the first encountered reload error */ -if (!ret) - ret = tmp_ret; +/* set retval for the first encountered reload error */ +if (!ret) + ret = -EINVAL ; + } } -if (!ret) + +if (!ret && tmp_ret ==UCODE_UPDATED )perf_check_microcode (); +mutex_unlock (µcode_mutex);put_online_cpus ();
arch/x86/kernel/cpu/microcode/intel.c +5 -5
@@ -772,7 +772,7 @@ static int collect_cpu_info(int cpu_num, struct cpu_signature *csig)return 0 ; } -static int apply_microcode_intel (int cpu) +static enum ucode_state apply_microcode_intel (int cpu) {struct microcode_intel *mc;struct ucode_cpu_info *uci;
@@ -782,7 +782,7 @@ static int apply_microcode_intel(int cpu)/* We should bind the task to the CPU */ if (WARN_ON (raw_smp_processor_id () != cpu)) -return -1 ; +return UCODE_ERROR ; uci = ucode_cpu_info + cpu; mc = uci->mc;
@@ -790,7 +790,7 @@ static int apply_microcode_intel(int cpu)/* Look for a newer patch in our cache: */ mc =find_patch (uci);if (!mc) -return 0 ; +return UCODE_NFOUND ; }/* write microcode via MSR 0x79 */
@@ -801,7 +801,7 @@ static int apply_microcode_intel(int cpu)if (rev != mc->hdr.rev) {pr_err ("CPU%d update to revision 0x%x failed\n" , cpu, mc->hdr.rev); -return -1 ; +return UCODE_ERROR ; }if (rev != prev_rev) {
@@ -818,7 +818,7 @@ static int apply_microcode_intel(int cpu) uci->cpu_sig.rev = rev; c->microcode = rev; -return 0 ; +return UCODE_UPDATED ; } static enum ucode_state generic_load_microcode(int cpu, void *data, size_t size,
Signature · 1 line
2.13.0
tip-bot for Borislav Petkov
merge bot[tip:x86/pti] x86/microcode: Propagate return value from updating functions
x86/microcode: Propagate return value from updating functions
Commit-ID: 3f1f576a195aa266813cbd4ca70291deb61e0129 Gitweb: https://git.kernel.org/tip/3f1f576a195aa266813cbd4ca70291deb61e0129 Author: Borislav Petkov <[email protected]> AuthorDate: Fri, 16 Feb 2018 12:26:38 +0100 Committer: Ingo Molnar <[email protected]> CommitDate: Sat, 17 Feb 2018 08:43:55 +0100
x86/microcode: Propagate return value from updating functions
... so that callers can know when microcode was updated and act accordingly.
--- arch/x86/include/asm/microcode.h | 9 +++++++-- arch/x86/kernel/cpu/microcode/amd.c | 10 +++++----- arch/x86/kernel/cpu/microcode/core.c | 33 +++++++++++++++++---------------- arch/x86/kernel/cpu/microcode/intel.c | 10 +++++----- 4 files changed, 34 insertions(+), 28 deletions(-)
arch/x86/include/asm/microcode.h +7 -2
@@ -37,7 +37,12 @@ struct cpu_signature {struct device ; -enum ucode_state {UCODE_ERROR ,UCODE_OK ,UCODE_NFOUND }; +enum ucode_state { +UCODE_OK =0 , +UCODE_UPDATED , +UCODE_NFOUND , +UCODE_ERROR , +};struct microcode_ops { enum ucode_state (*request_microcode_user) (int cpu,
@@ -54,7 +59,7 @@ struct microcode_ops {* are being called. * See also the "Synchronization" section in microcode_core.c. */ -int (*apply_microcode) (int cpu); +enum ucode_state (*apply_microcode) (int cpu);int (*collect_cpu_info) (int cpu,struct cpu_signature *csig); };
arch/x86/kernel/cpu/microcode/amd.c +5 -5
@@ -498,7 +498,7 @@ static unsigned int verify_patch_size(u8 family, u32 patch_size,return patch_size; } -static int apply_microcode_amd (int cpu) +static enum ucode_state apply_microcode_amd (int cpu) {struct cpuinfo_x86 *c = &cpu_data (cpu);struct microcode_amd *mc_amd;
@@ -512,7 +512,7 @@ static int apply_microcode_amd(int cpu) p =find_patch (cpu);if (!p) -return 0 ; +return UCODE_NFOUND ; mc_amd = p->data; uci->mc = p->data;
@@ -523,13 +523,13 @@ static int apply_microcode_amd(int cpu)if (rev >= mc_amd->hdr.patch_id) { c->microcode = rev; uci->cpu_sig.rev = rev; -return 0 ; +return UCODE_OK ; }if (__apply_microcode_amd (mc_amd)) {pr_err ("CPU%d: update failed for patch_level=0x%08x\n" , cpu, mc_amd->hdr.patch_id); -return -1 ; +return UCODE_ERROR ; }pr_info ("CPU%d: new patch_level=0x%08x\n" , cpu, mc_amd->hdr.patch_id);
@@ -537,7 +537,7 @@ static int apply_microcode_amd(int cpu) uci->cpu_sig.rev = mc_amd->hdr.patch_id; c->microcode = mc_amd->hdr.patch_id; -return 0 ; +return UCODE_UPDATED ; }static int install_equiv_cpu_table (const u8 *buf)
arch/x86/kernel/cpu/microcode/core.c +17 -16
@@ -374,7 +374,7 @@ static int collect_cpu_info(int cpu) }struct apply_microcode_ctx { -int err; +enum ucode_state err; };static void apply_microcode_local (void *arg)
@@ -489,31 +489,29 @@ static void __exit microcode_dev_exit(void)/* fake device for request_firmware */ static struct platform_device *microcode_pdev; -static int reload_for_cpu (int cpu) +static enum ucode_state reload_for_cpu (int cpu) {struct ucode_cpu_info *uci = ucode_cpu_info + cpu;enum ucode_state ustate; -int err =0 ;if (!uci->valid) -return err; +return UCODE_OK ; ustate = microcode_ops->request_microcode_fw (cpu, µcode_pdev->dev, true); -if (ustate ==UCODE_OK ) -apply_microcode_on_target (cpu); -else -if (ustate ==UCODE_ERROR ) - err = -EINVAL ; -return err; +if (ustate !=UCODE_OK ) +return ustate; + +return apply_microcode_on_target (cpu); }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 ;unsigned long val; +ssize_t ret =0 ;int cpu; -ssize_t ret =0 , tmp_ret; ret =kstrtoul (buf,0 , &val);if (ret)
@@ -526,15 +524,18 @@ static ssize_t reload_store(struct device *dev,mutex_lock (µcode_mutex);for_each_online_cpu (cpu) { tmp_ret =reload_for_cpu (cpu); -if (tmp_ret !=0 ) +if (tmp_ret >UCODE_NFOUND ) {pr_warn ("Error reloading microcode on CPU %d\n" , cpu); -/* save retval of the first encountered reload error */ -if (!ret) - ret = tmp_ret; +/* set retval for the first encountered reload error */ +if (!ret) + ret = -EINVAL ; + } } -if (!ret) + +if (!ret && tmp_ret ==UCODE_UPDATED )perf_check_microcode (); +mutex_unlock (µcode_mutex);put_online_cpus ();
arch/x86/kernel/cpu/microcode/intel.c +5 -5
@@ -772,7 +772,7 @@ static int collect_cpu_info(int cpu_num, struct cpu_signature *csig)return 0 ; } -static int apply_microcode_intel (int cpu) +static enum ucode_state apply_microcode_intel (int cpu) {struct microcode_intel *mc;struct ucode_cpu_info *uci;
@@ -782,7 +782,7 @@ static int apply_microcode_intel(int cpu)/* We should bind the task to the CPU */ if (WARN_ON (raw_smp_processor_id () != cpu)) -return -1 ; +return UCODE_ERROR ; uci = ucode_cpu_info + cpu; mc = uci->mc;
@@ -790,7 +790,7 @@ static int apply_microcode_intel(int cpu)/* Look for a newer patch in our cache: */ mc =find_patch (uci);if (!mc) -return 0 ; +return UCODE_NFOUND ; }/* write microcode via MSR 0x79 */
@@ -801,7 +801,7 @@ static int apply_microcode_intel(int cpu)if (rev != mc->hdr.rev) {pr_err ("CPU%d update to revision 0x%x failed\n" , cpu, mc->hdr.rev); -return -1 ; +return UCODE_ERROR ; }if (rev != prev_rev) {
@@ -818,7 +818,7 @@ static int apply_microcode_intel(int cpu) uci->cpu_sig.rev = rev; c->microcode = rev; -return 0 ; +return UCODE_UPDATED ; } static enum ucode_state generic_load_microcode(int cpu, void *data, size_t size,
Borislav Petkov
maintainerPATCH 2/3 x86/CPU: Add a microcode loader callback
In reply to Borislav PetkovFrom: Borislav Petkov <[email protected]>
Add a callback function which the microcode loader calls when microcode has been updated to a newer revision. Do the callback only when no error was encountered during loading.
--- arch/x86/include/asm/processor.h | 1 + arch/x86/kernel/cpu/common.c | 10 ++++++++++ arch/x86/kernel/cpu/microcode/core.c | 8 ++++++-- 3 files changed, 17 insertions(+), 2 deletions(-)
arch/x86/include/asm/processor.h +1 -0
@@ -977,4 +977,5 @@ bool xen_set_default_idle(void);void stop_this_cpu (void *dummy);void df_debug (struct pt_regs *regs,long error_code); +void microcode_check (void );#endif /* _ASM_X86_PROCESSOR_H */
arch/x86/kernel/cpu/common.c +10 -0
@@ -1749,3 +1749,13 @@ static int __init init_cpu_syscore(void)return 0 ; }core_initcall (init_cpu_syscore); + +/* +* The microcode loader calls this upon late microcode load to recheck features, +* only when microcode has been updated. Caller holds microcode_mutex and CPU +* hotplug lock. +*/ +void microcode_check (void ) +{ +perf_check_microcode (); +}
arch/x86/kernel/cpu/microcode/core.c +6 -2
@@ -509,6 +509,7 @@ static ssize_t reload_store(struct device *dev, const char *buf, size_t size) {enum ucode_state tmp_ret =UCODE_OK ; +bool do_callback = false;unsigned long val;ssize_t ret =0 ;int cpu;
@@ -531,10 +532,13 @@ static ssize_t reload_store(struct device *dev,if (!ret) ret = -EINVAL ; } + +if (tmp_ret ==UCODE_UPDATED ) + do_callback = true; } -if (!ret && tmp_ret ==UCODE_UPDATED ) -perf_check_microcode (); +if (!ret && do_callback) +microcode_check ();mutex_unlock (µcode_mutex);put_online_cpus ();
Signature · 1 line
2.13.0
tip-bot for Borislav Petkov
merge bot[tip:x86/pti] x86/CPU: Add a microcode loader callback
x86/CPU: Add a microcode loader callback
Commit-ID: 1008c52c09dcb23d93f8e0ea83a6246265d2cce0 Gitweb: https://git.kernel.org/tip/1008c52c09dcb23d93f8e0ea83a6246265d2cce0 Author: Borislav Petkov <[email protected]> AuthorDate: Fri, 16 Feb 2018 12:26:39 +0100 Committer: Ingo Molnar <[email protected]> CommitDate: Sat, 17 Feb 2018 08:43:55 +0100
x86/CPU: Add a microcode loader callback
Add a callback function which the microcode loader calls when microcode has been updated to a newer revision. Do the callback only when no error was encountered during loading.
--- arch/x86/include/asm/processor.h | 1 + arch/x86/kernel/cpu/common.c | 10 ++++++++++ arch/x86/kernel/cpu/microcode/core.c | 8 ++++++-- 3 files changed, 17 insertions(+), 2 deletions(-)
arch/x86/include/asm/processor.h +1 -0
@@ -977,4 +977,5 @@ bool xen_set_default_idle(void);void stop_this_cpu (void *dummy);void df_debug (struct pt_regs *regs,long error_code); +void microcode_check (void );#endif /* _ASM_X86_PROCESSOR_H */
arch/x86/kernel/cpu/common.c +10 -0
@@ -1749,3 +1749,13 @@ static int __init init_cpu_syscore(void)return 0 ; }core_initcall (init_cpu_syscore); + +/* +* The microcode loader calls this upon late microcode load to recheck features, +* only when microcode has been updated. Caller holds microcode_mutex and CPU +* hotplug lock. +*/ +void microcode_check (void ) +{ +perf_check_microcode (); +}
arch/x86/kernel/cpu/microcode/core.c +6 -2
@@ -509,6 +509,7 @@ static ssize_t reload_store(struct device *dev, const char *buf, size_t size) {enum ucode_state tmp_ret =UCODE_OK ; +bool do_callback = false;unsigned long val;ssize_t ret =0 ;int cpu;
@@ -531,10 +532,13 @@ static ssize_t reload_store(struct device *dev,if (!ret) ret = -EINVAL ; } + +if (tmp_ret ==UCODE_UPDATED ) + do_callback = true; } -if (!ret && tmp_ret ==UCODE_UPDATED ) -perf_check_microcode (); +if (!ret && do_callback) +microcode_check ();mutex_unlock (µcode_mutex);put_online_cpus ();
Re: [PATCH 2/3] x86/CPU: Add a microcode loader callback
In reply to Borislav PetkovHi Boris
Sorry this is coming late... Thought i mentioned it earlier.
arch/x86/include/asm/processor.h bool xen_set_default_idle(void); · line 980
Quoted 11 lines · Borislav Petkov
3 files changed, 17 insertions(+), 2 deletions(-)arch/x86/include/asm/processor.h +1
-0@@ -977,4 +977,5 @@ bool xen_set_default_idle(void);void stop_this_cpu (void *dummy);void df_debug (struct pt_regs *regs,long error_code); +void microcode_check (void );
microcode_check() is only called from microcode/core.c
Could you only list it there instead? common.c has no other microcode related functions other than this.?
Cheers,
Ashok
Borislav Petkov
maintainerRe: [PATCH 2/3] x86/CPU: Add a microcode loader callback
Quoted 2 lines · Ashok
Could you only list it there instead?
common.chas no other microcode related functions other than this.?
I already explained that to you: this function is called by the loader but it uses facilities which are closer to cpu/common.c - get_cpu_cap(), etc.
Also, this file will be split eventually after the CPUID cleanup.
So it could just as well be called recheck_cpu_caps(), without the "microcode" prefix.
So no need to do anything now.
Signature · 4 lines
Regards/Gruss,
Boris.
Good mailing practices for 400: avoid top-posting and trim the reply.Re: [PATCH 2/3] x86/CPU: Add a microcode loader callback
Quoted 4 lines · Borislav Petkov
Quoted 2 lines · Ashok
Could you only list it there instead?
common.chas no other microcode related functions other than this.?
Quoted 8 lines
I already explained that to you: this function is called by the loader but it uses facilities which are closer to
cpu/common.c-get_cpu_cap(), etc.Also, this file will be split eventually after the CPUID cleanup.
So it could just as well be called
recheck_cpu_caps(), without the "microcode" prefix.
Never mind.. now that rings a bell :-)
Quoted 3 lines
So no need to do anything now.
Borislav Petkov
maintainerPATCH 3/3 x86/CPU: Check CPU feature bits after microcode upgrade
In reply to Borislav PetkovFrom: Borislav Petkov <[email protected]>
With some microcode upgrades, new CPUID features can become visible on the CPU. Check what the kernel has mirrored now and issue a warning hinting at possible things the user/admin can do to make use of the newly visible features.
--- arch/x86/kernel/cpu/common.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)
arch/x86/kernel/cpu/common.c +20 -0
@@ -1757,5 +1757,25 @@ core_initcall(init_cpu_syscore);*/ void microcode_check (void ) { +struct cpuinfo_x86 info; +perf_check_microcode (); + +/* Reload CPUID max function as it might've changed. */ + info.cpuid_level =cpuid_eax (0 ); + +/* +* 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 (&info.x86_capability, &boot_cpu_data.x86_capability,sizeof (info.x86_capability)); + +get_cpu_cap (&info); + +if (!memcmp (&info.x86_capability, &boot_cpu_data.x86_capability,sizeof (info.x86_capability))) +return ; + +pr_warn ("x86/CPU: CPU features have changed after loading microcode, but might not take effect.\n" ); +pr_warn ("x86/CPU: Please consider either early loading through initrd/built-in or a potential BIOS update.\n" ); }
Signature · 1 line
2.13.0
tip-bot for Borislav Petkov
merge bot[tip:x86/pti] x86/CPU: Check CPU feature bits after microcode upgrade
x86/CPU: Check CPU feature bits after microcode upgrade
Commit-ID: 42ca8082e260dcfd8afa2afa6ec1940b9d41724c Gitweb: https://git.kernel.org/tip/42ca8082e260dcfd8afa2afa6ec1940b9d41724c Author: Borislav Petkov <[email protected]> AuthorDate: Fri, 16 Feb 2018 12:26:40 +0100 Committer: Ingo Molnar <[email protected]> CommitDate: Sat, 17 Feb 2018 08:43:55 +0100
x86/CPU: Check CPU feature bits after microcode upgrade
With some microcode upgrades, new CPUID features can become visible on the CPU. Check what the kernel has mirrored now and issue a warning hinting at possible things the user/admin can do to make use of the newly visible features.
--- arch/x86/kernel/cpu/common.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)
arch/x86/kernel/cpu/common.c +20 -0
@@ -1757,5 +1757,25 @@ core_initcall(init_cpu_syscore);*/ void microcode_check (void ) { +struct cpuinfo_x86 info; +perf_check_microcode (); + +/* Reload CPUID max function as it might've changed. */ + info.cpuid_level =cpuid_eax (0 ); + +/* +* 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 (&info.x86_capability, &boot_cpu_data.x86_capability,sizeof (info.x86_capability)); + +get_cpu_cap (&info); + +if (!memcmp (&info.x86_capability, &boot_cpu_data.x86_capability,sizeof (info.x86_capability))) +return ; + +pr_warn ("x86/CPU: CPU features have changed after loading microcode, but might not take effect.\n" ); +pr_warn ("x86/CPU: Please consider either early loading through initrd/built-in or a potential BIOS update.\n" ); }