Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8f4e956b authored by Andi Kleen's avatar Andi Kleen Committed by Linus Torvalds
Browse files

x86: Stop MCEs and NMIs during code patching



When a machine check or NMI occurs while multiple byte code is patched
the CPU could theoretically see an inconsistent instruction and crash.
Prevent this by temporarily disabling MCEs and returning early in the
NMI handler.

Based on discussion with Mathieu Desnoyers.

Cc: Mathieu Desnoyers <compudj@krystal.dyndns.org>
Cc: Jeremy Fitzhardinge <jeremy@goop.org>
Signed-off-by: default avatarAndi Kleen <ak@suse.de>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 19d36ccd
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -8,6 +8,8 @@
#include <asm/alternative.h>
#include <asm/sections.h>
#include <asm/pgtable.h>
#include <asm/mce.h>
#include <asm/nmi.h>

#ifdef CONFIG_HOTPLUG_CPU
static int smp_alt_once;
@@ -373,6 +375,14 @@ void __init alternative_instructions(void)
{
	unsigned long flags;

	/* The patching is not fully atomic, so try to avoid local interruptions
	   that might execute the to be patched code.
	   Other CPUs are not running. */
	stop_nmi();
#ifdef CONFIG_MCE
	stop_mce();
#endif

	local_irq_save(flags);
	apply_alternatives(__alt_instructions, __alt_instructions_end);

@@ -405,6 +415,11 @@ void __init alternative_instructions(void)
#endif
 	apply_paravirt(__parainstructions, __parainstructions_end);
	local_irq_restore(flags);

	restart_nmi();
#ifdef CONFIG_MCE
	restart_mce();
#endif
}

/*
+14 −0
Original line number Diff line number Diff line
@@ -60,6 +60,20 @@ void mcheck_init(struct cpuinfo_x86 *c)
	}
}

static unsigned long old_cr4 __initdata;

void __init stop_mce(void)
{
	old_cr4 = read_cr4();
	clear_in_cr4(X86_CR4_MCE);
}

void __init restart_mce(void)
{
	if (old_cr4 & X86_CR4_MCE)
		set_in_cr4(X86_CR4_MCE);
}

static int __init mcheck_disable(char *str)
{
	mce_disabled = 1;
+16 −1
Original line number Diff line number Diff line
@@ -775,6 +775,8 @@ static __kprobes void default_do_nmi(struct pt_regs * regs)
	reassert_nmi();
}

static int ignore_nmis;

fastcall __kprobes void do_nmi(struct pt_regs * regs, long error_code)
{
	int cpu;
@@ -785,11 +787,24 @@ fastcall __kprobes void do_nmi(struct pt_regs * regs, long error_code)

	++nmi_count(cpu);

	if (!ignore_nmis)
		default_do_nmi(regs);

	nmi_exit();
}

void stop_nmi(void)
{
	acpi_nmi_disable();
	ignore_nmis++;
}

void restart_nmi(void)
{
	ignore_nmis--;
	acpi_nmi_enable();
}

#ifdef CONFIG_KPROBES
fastcall void __kprobes do_int3(struct pt_regs *regs, long error_code)
{
+14 −0
Original line number Diff line number Diff line
@@ -667,6 +667,20 @@ static struct miscdevice mce_log_device = {
	&mce_chrdev_ops,
};

static unsigned long old_cr4 __initdata;

void __init stop_mce(void)
{
	old_cr4 = read_cr4();
	clear_in_cr4(X86_CR4_MCE);
}

void __init restart_mce(void)
{
	if (old_cr4 & X86_CR4_MCE)
		set_in_cr4(X86_CR4_MCE);
}

/* 
 * Old style boot options parsing. Only for compatibility. 
 */
+16 −1
Original line number Diff line number Diff line
@@ -384,10 +384,13 @@ int __kprobes nmi_watchdog_tick(struct pt_regs * regs, unsigned reason)
	return rc;
}

static unsigned ignore_nmis;

asmlinkage __kprobes void do_nmi(struct pt_regs * regs, long error_code)
{
	nmi_enter();
	add_pda(__nmi_count,1);
	if (!ignore_nmis)
		default_do_nmi(regs);
	nmi_exit();
}
@@ -401,6 +404,18 @@ int do_nmi_callback(struct pt_regs * regs, int cpu)
	return 0;
}

void stop_nmi(void)
{
	acpi_nmi_disable();
	ignore_nmis++;
}

void restart_nmi(void)
{
	ignore_nmis--;
	acpi_nmi_enable();
}

#ifdef CONFIG_SYSCTL

static int unknown_nmi_panic_callback(struct pt_regs *regs, int cpu)
Loading