From: Thomas Gleixner Date: Fri, 29 Jun 2018 14:05:48 +0000 (+0200) Subject: cpu/hotplug: Boot HT siblings at least once X-Git-Tag: v4.1.12-124.31.3~643 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=b89fa9ef34bd313e786ffca20819cf1f1e8a9104;p=users%2Fjedix%2Flinux-maple.git cpu/hotplug: Boot HT siblings at least once Due to the way Machine Check Exceptions work on X86 hyperthreads it's required to boot up _all_ logical cores at least once in order to set the CR4.MCE bit. So instead of ignoring the sibling threads right away, let them boot up once so they can configure themselves. After they came out of the initial boot stage check whether its a "secondary" sibling and cancel the operation which puts the CPU back into offline state. Reported-by: Dave Hansen Signed-off-by: Thomas Gleixner Tested-by: Tony Luck Orabug: 28220674 CVE: CVE-2018-3620 (cherry picked from commit 0cc3cd21657be04cb0559fe8063f2130493f92cf) Signed-off-by: Mihai Carabas Reviewed-by: Darren Kenny Reviewed-by: Boris Ostrovsky Conflicts: kernel/cpu.c Contextual: The following changes were applied to the initial commit: - used a per-cpu variable booted_once because struct cpuhp_cpu_state doesn't exist in the current version - cpu_smt_allowed check from bringup_wait_for_ap is done in _cpu_up - taking into account that boot_cpu_state_init doesn't exist in the current version (it was added by cff7d378: cpu/hotplug: Convert to a state machine for the control processor), the per-cpu variable booted_once isn't made static and it is set on true in start_kernel --- diff --git a/include/linux/cpu.h b/include/linux/cpu.h index b7c865c997ad..3c58810123e4 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -298,6 +298,7 @@ void arch_cpu_idle_exit(void); void arch_cpu_idle_dead(void); DECLARE_PER_CPU(bool, cpu_dead_idle); +DECLARE_PER_CPU(bool, booted_once); int cpu_report_state(int cpu); int cpu_check_up_prepare(int cpu); diff --git a/init/main.c b/init/main.c index 1b124c04e157..1b9ee932507d 100644 --- a/init/main.c +++ b/init/main.c @@ -533,6 +533,7 @@ asmlinkage __visible void __init start_kernel(void) setup_command_line(command_line); setup_nr_cpu_ids(); setup_per_cpu_areas(); + this_cpu_write(booted_once, true); smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */ build_all_zonelists(NULL, NULL); diff --git a/kernel/cpu.c b/kernel/cpu.c index 217e6501c0f6..0383a1eee12e 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -29,6 +29,7 @@ #ifdef CONFIG_SMP /* Serializes the updates to cpu_online_mask, cpu_present_mask */ static DEFINE_MUTEX(cpu_add_remove_lock); +DEFINE_PER_CPU(bool, booted_once); /* * The following two APIs (cpu_maps_update_begin/done) must be used when @@ -510,8 +511,19 @@ early_param("nosmt", smt_cmdline_disable); static inline bool cpu_smt_allowed(unsigned int cpu) { - return cpu_smt_control == CPU_SMT_ENABLED || - topology_is_primary_thread(cpu); + if (cpu_smt_control == CPU_SMT_ENABLED) + return true; + + if (topology_is_primary_thread(cpu)) + return true; + + /* + * On x86 it's required to boot all logical CPUs at least once so + * that the init code can get a chance to set CR4.MCE on each + * CPU. Otherwise, a broadacasted MCE observing CR4.MCE=0b on any + * core will shutdown the machine. + */ + return !per_cpu(booted_once, cpu); } #else static inline bool cpu_smt_allowed(unsigned int cpu) { return true; } @@ -566,6 +578,18 @@ out_notify: out: cpu_hotplug_done(); + /* + * SMT soft disabling on X86 requires to bring the CPU out of the + * BIOS 'wait for SIPI' state in order to set the CR4.MCE bit. The + * CPU marked itself as booted_once in cpu_notify_starting() so the + * cpu_smt_allowed() check will now return false if this is not the + * primary sibling. + */ + if (!cpu_smt_allowed(cpu)) { + _cpu_down(cpu, 0); + ret = -ECANCELED; + } + return ret; } @@ -760,6 +784,7 @@ void notify_cpu_starting(unsigned int cpu) if (frozen_cpus != NULL && cpumask_test_cpu(cpu, frozen_cpus)) val = CPU_STARTING_FROZEN; #endif /* CONFIG_PM_SLEEP_SMP */ + this_cpu_write(booted_once, true); cpu_notify(val, (void *)(long)cpu); }