aboutsummaryrefslogtreecommitdiffstats
path: root/arch/i386
diff options
context:
space:
mode:
authorAndi Kleen <ak@suse.de>2007-07-22 05:12:32 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-22 14:03:37 -0400
commit8f4e956b313dcccbc7be6f10808952345e3b638c (patch)
treecc8c93fa1faf5e0b608e3a21330a32bd82fe6f47 /arch/i386
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')
-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 206ea2ca63cc..c3750c2c4113 100644
--- a/arch/i386/kernel/alternative.c
+++ b/arch/i386/kernel/alternative.c
@@ -8,6 +8,8 @@
8#include <asm/alternative.h> 8#include <asm/alternative.h>
9#include <asm/sections.h> 9#include <asm/sections.h>
10#include <asm/pgtable.h> 10#include <asm/pgtable.h>
11#include <asm/mce.h>
12#include <asm/nmi.h>
11 13
12#ifdef CONFIG_HOTPLUG_CPU 14#ifdef CONFIG_HOTPLUG_CPU
13static int smp_alt_once; 15static int smp_alt_once;
@@ -373,6 +375,14 @@ void __init alternative_instructions(void)
373{ 375{
374 unsigned long flags; 376 unsigned long flags;
375 377
378 /* The patching is not fully atomic, so try to avoid local interruptions
379 that might execute the to be patched code.
380 Other CPUs are not running. */
381 stop_nmi();
382#ifdef CONFIG_MCE
383 stop_mce();
384#endif
385
376 local_irq_save(flags); 386 local_irq_save(flags);
377 apply_alternatives(__alt_instructions, __alt_instructions_end); 387 apply_alternatives(__alt_instructions, __alt_instructions_end);
378 388
@@ -405,6 +415,11 @@ void __init alternative_instructions(void)
405#endif 415#endif
406 apply_paravirt(__parainstructions, __parainstructions_end); 416 apply_paravirt(__parainstructions, __parainstructions_end);
407 local_irq_restore(flags); 417 local_irq_restore(flags);
418
419 restart_nmi();
420#ifdef CONFIG_MCE
421 restart_mce();
422#endif
408} 423}
409 424
410/* 425/*
diff --git a/arch/i386/kernel/cpu/mcheck/mce.c b/arch/i386/kernel/cpu/mcheck/mce.c
index 56cd485b127c..34c781eddee4 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)
60 } 60 }
61} 61}
62 62
63static unsigned long old_cr4 __initdata;
64
65void __init stop_mce(void)
66{
67 old_cr4 = read_cr4();
68 clear_in_cr4(X86_CR4_MCE);
69}
70
71void __init restart_mce(void)
72{
73 if (old_cr4 & X86_CR4_MCE)
74 set_in_cr4(X86_CR4_MCE);
75}
76
63static int __init mcheck_disable(char *str) 77static int __init mcheck_disable(char *str)
64{ 78{
65 mce_disabled = 1; 79 mce_disabled = 1;
diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c
index 438949da3b63..cfffe3dd9e83 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)
775 reassert_nmi(); 775 reassert_nmi();
776} 776}
777 777
778static int ignore_nmis;
779
778fastcall __kprobes void do_nmi(struct pt_regs * regs, long error_code) 780fastcall __kprobes void do_nmi(struct pt_regs * regs, long error_code)
779{ 781{
780 int cpu; 782 int cpu;
@@ -785,11 +787,24 @@ fastcall __kprobes void do_nmi(struct pt_regs * regs, long error_code)
785 787
786 ++nmi_count(cpu); 788 ++nmi_count(cpu);
787 789
788 default_do_nmi(regs); 790 if (!ignore_nmis)
791 default_do_nmi(regs);
789 792
790 nmi_exit(); 793 nmi_exit();
791} 794}
792 795
796void stop_nmi(void)
797{
798 acpi_nmi_disable();
799 ignore_nmis++;
800}
801
802void restart_nmi(void)
803{
804 ignore_nmis--;
805 acpi_nmi_enable();
806}
807
793#ifdef CONFIG_KPROBES 808#ifdef CONFIG_KPROBES
794fastcall void __kprobes do_int3(struct pt_regs *regs, long error_code) 809fastcall void __kprobes do_int3(struct pt_regs *regs, long error_code)
795{ 810{