aboutsummaryrefslogtreecommitdiff
path: root/arch/i386/kernel
diff options
context:
space:
mode:
authorAndi Kleen <ak@suse.de>2007-07-22 11:12:32 +0200
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-22 11:03:37 -0700
commit8f4e956b313dcccbc7be6f10808952345e3b638c (patch)
treecc8c93fa1faf5e0b608e3a21330a32bd82fe6f47 /arch/i386/kernel
parent19d36ccdc34f5ed444f8a6af0cbfdb6790eb1177 (diff)
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: Andi Kleen <ak@suse.de> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/i386/kernel')
-rw-r--r--arch/i386/kernel/alternative.c15
-rw-r--r--arch/i386/kernel/cpu/mcheck/mce.c14
-rw-r--r--arch/i386/kernel/traps.c17
3 files changed, 45 insertions, 1 deletions
diff --git a/arch/i386/kernel/alternative.c b/arch/i386/kernel/alternative.c
index 206ea2ca63c..c3750c2c411 100644
--- a/arch/i386/kernel/alternative.c
+++ b/arch/i386/kernel/alternative.c
@@ -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
}
/*
diff --git a/arch/i386/kernel/cpu/mcheck/mce.c b/arch/i386/kernel/cpu/mcheck/mce.c
index 56cd485b127..34c781eddee 100644
--- a/arch/i386/kernel/cpu/mcheck/mce.c
+++ b/arch/i386/kernel/cpu/mcheck/mce.c
@@ -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;
diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c
index 438949da3b6..cfffe3dd9e8 100644
--- a/arch/i386/kernel/traps.c
+++ b/arch/i386/kernel/traps.c
@@ -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);
- default_do_nmi(regs);
+ 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)
{