Skip to content

Commit 7eb314a

Browse files
KAGA-KOKObp3tk0v
authored andcommitted
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. Signed-off-by: Thomas Gleixner <[email protected]> Signed-off-by: Borislav Petkov (AMD) <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 0bf8716 commit 7eb314a

File tree

5 files changed

+57
-5
lines changed

5 files changed

+57
-5
lines changed

arch/x86/include/asm/microcode.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,16 @@ static inline u32 intel_get_microcode_revision(void)
7272
}
7373
#endif /* !CONFIG_CPU_SUP_INTEL */
7474

75+
bool microcode_nmi_handler(void);
76+
77+
#ifdef CONFIG_MICROCODE_LATE_LOADING
78+
DECLARE_STATIC_KEY_FALSE(microcode_nmi_handler_enable);
79+
static __always_inline bool microcode_nmi_handler_enabled(void)
80+
{
81+
return static_branch_unlikely(&microcode_nmi_handler_enable);
82+
}
83+
#else
84+
static __always_inline bool microcode_nmi_handler_enabled(void) { return false; }
85+
#endif
86+
7587
#endif /* _ASM_X86_MICROCODE_H */

arch/x86/kernel/cpu/microcode/core.c

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <linux/miscdevice.h>
2424
#include <linux/capability.h>
2525
#include <linux/firmware.h>
26+
#include <linux/cpumask.h>
2627
#include <linux/kernel.h>
2728
#include <linux/delay.h>
2829
#include <linux/mutex.h>
@@ -31,6 +32,7 @@
3132
#include <linux/fs.h>
3233
#include <linux/mm.h>
3334

35+
#include <asm/apic.h>
3436
#include <asm/cpu_device_id.h>
3537
#include <asm/perf_event.h>
3638
#include <asm/processor.h>
@@ -265,8 +267,10 @@ struct microcode_ctrl {
265267
enum sibling_ctrl ctrl;
266268
enum ucode_state result;
267269
unsigned int ctrl_cpu;
270+
bool nmi_enabled;
268271
};
269272

273+
DEFINE_STATIC_KEY_FALSE(microcode_nmi_handler_enable);
270274
static DEFINE_PER_CPU(struct microcode_ctrl, ucode_ctrl);
271275
static atomic_t late_cpus_in;
272276

@@ -282,7 +286,8 @@ static bool wait_for_cpus(atomic_t *cnt)
282286

283287
udelay(1);
284288

285-
if (!(timeout % USEC_PER_MSEC))
289+
/* If invoked directly, tickle the NMI watchdog */
290+
if (!microcode_ops->use_nmi && !(timeout % USEC_PER_MSEC))
286291
touch_nmi_watchdog();
287292
}
288293
/* Prevent the late comers from making progress and let them time out */
@@ -298,7 +303,8 @@ static bool wait_for_ctrl(void)
298303
if (this_cpu_read(ucode_ctrl.ctrl) != SCTRL_WAIT)
299304
return true;
300305
udelay(1);
301-
if (!(timeout % 1000))
306+
/* If invoked directly, tickle the NMI watchdog */
307+
if (!microcode_ops->use_nmi && !(timeout % 1000))
302308
touch_nmi_watchdog();
303309
}
304310
return false;
@@ -374,7 +380,7 @@ static void load_primary(unsigned int cpu)
374380
}
375381
}
376382

377-
static int load_cpus_stopped(void *unused)
383+
static bool microcode_update_handler(void)
378384
{
379385
unsigned int cpu = smp_processor_id();
380386

@@ -383,7 +389,29 @@ static int load_cpus_stopped(void *unused)
383389
else
384390
load_secondary(cpu);
385391

386-
/* No point to wait here. The CPUs will all wait in stop_machine(). */
392+
touch_nmi_watchdog();
393+
return true;
394+
}
395+
396+
bool microcode_nmi_handler(void)
397+
{
398+
if (!this_cpu_read(ucode_ctrl.nmi_enabled))
399+
return false;
400+
401+
this_cpu_write(ucode_ctrl.nmi_enabled, false);
402+
return microcode_update_handler();
403+
}
404+
405+
static int load_cpus_stopped(void *unused)
406+
{
407+
if (microcode_ops->use_nmi) {
408+
/* Enable the NMI handler and raise NMI */
409+
this_cpu_write(ucode_ctrl.nmi_enabled, true);
410+
apic->send_IPI(smp_processor_id(), NMI_VECTOR);
411+
} else {
412+
/* Just invoke the handler directly */
413+
microcode_update_handler();
414+
}
387415
return 0;
388416
}
389417

@@ -404,8 +432,14 @@ static int load_late_stop_cpus(void)
404432
*/
405433
store_cpu_caps(&prev_info);
406434

435+
if (microcode_ops->use_nmi)
436+
static_branch_enable_cpuslocked(&microcode_nmi_handler_enable);
437+
407438
stop_machine_cpuslocked(load_cpus_stopped, NULL, cpu_online_mask);
408439

440+
if (microcode_ops->use_nmi)
441+
static_branch_disable_cpuslocked(&microcode_nmi_handler_enable);
442+
409443
/* Analyze the results */
410444
for_each_cpu_and(cpu, cpu_present_mask, &cpus_booted_once_mask) {
411445
switch (per_cpu(ucode_ctrl.result, cpu)) {

arch/x86/kernel/cpu/microcode/intel.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,7 @@ static struct microcode_ops microcode_intel_ops = {
611611
.collect_cpu_info = collect_cpu_info,
612612
.apply_microcode = apply_microcode_late,
613613
.finalize_late_load = finalize_late_load,
614+
.use_nmi = IS_ENABLED(CONFIG_X86_64),
614615
};
615616

616617
static __init void calc_llc_size_per_core(struct cpuinfo_x86 *c)

arch/x86/kernel/cpu/microcode/internal.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ struct microcode_ops {
3131
enum ucode_state (*apply_microcode)(int cpu);
3232
int (*collect_cpu_info)(int cpu, struct cpu_signature *csig);
3333
void (*finalize_late_load)(int result);
34-
unsigned int nmi_safe : 1;
34+
unsigned int nmi_safe : 1,
35+
use_nmi : 1;
3536
};
3637

3738
extern struct ucode_cpu_info ucode_cpu_info[];

arch/x86/kernel/nmi.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include <asm/reboot.h>
3434
#include <asm/cache.h>
3535
#include <asm/nospec-branch.h>
36+
#include <asm/microcode.h>
3637
#include <asm/sev.h>
3738

3839
#define CREATE_TRACE_POINTS
@@ -343,6 +344,9 @@ static noinstr void default_do_nmi(struct pt_regs *regs)
343344

344345
instrumentation_begin();
345346

347+
if (microcode_nmi_handler_enabled() && microcode_nmi_handler())
348+
goto out;
349+
346350
handled = nmi_handle(NMI_LOCAL, regs);
347351
__this_cpu_add(nmi_stats.normal, handled);
348352
if (handled) {

0 commit comments

Comments
 (0)