aboutsummaryrefslogtreecommitdiffstats
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
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>
-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
-rw-r--r--arch/x86_64/kernel/mce.c14
-rw-r--r--arch/x86_64/kernel/nmi.c17
-rw-r--r--include/asm-i386/mce.h4
-rw-r--r--include/asm-i386/nmi.h2
-rw-r--r--include/asm-x86_64/mce.h3
-rw-r--r--include/asm-x86_64/nmi.h2
9 files changed, 86 insertions, 2 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{
diff --git a/arch/x86_64/kernel/mce.c b/arch/x86_64/kernel/mce.c
index 4d8450ee3635..a66d607f5b92 100644
--- a/arch/x86_64/kernel/mce.c
+++ b/arch/x86_64/kernel/mce.c
@@ -667,6 +667,20 @@ static struct miscdevice mce_log_device = {
667 &mce_chrdev_ops, 667 &mce_chrdev_ops,
668}; 668};
669 669
670static unsigned long old_cr4 __initdata;
671
672void __init stop_mce(void)
673{
674 old_cr4 = read_cr4();
675 clear_in_cr4(X86_CR4_MCE);
676}
677
678void __init restart_mce(void)
679{
680 if (old_cr4 & X86_CR4_MCE)
681 set_in_cr4(X86_CR4_MCE);
682}
683
670/* 684/*
671 * Old style boot options parsing. Only for compatibility. 685 * Old style boot options parsing. Only for compatibility.
672 */ 686 */
diff --git a/arch/x86_64/kernel/nmi.c b/arch/x86_64/kernel/nmi.c
index edbbc59b7523..cb8ee9d02f86 100644
--- a/arch/x86_64/kernel/nmi.c
+++ b/arch/x86_64/kernel/nmi.c
@@ -384,11 +384,14 @@ int __kprobes nmi_watchdog_tick(struct pt_regs * regs, unsigned reason)
384 return rc; 384 return rc;
385} 385}
386 386
387static unsigned ignore_nmis;
388
387asmlinkage __kprobes void do_nmi(struct pt_regs * regs, long error_code) 389asmlinkage __kprobes void do_nmi(struct pt_regs * regs, long error_code)
388{ 390{
389 nmi_enter(); 391 nmi_enter();
390 add_pda(__nmi_count,1); 392 add_pda(__nmi_count,1);
391 default_do_nmi(regs); 393 if (!ignore_nmis)
394 default_do_nmi(regs);
392 nmi_exit(); 395 nmi_exit();
393} 396}
394 397
@@ -401,6 +404,18 @@ int do_nmi_callback(struct pt_regs * regs, int cpu)
401 return 0; 404 return 0;
402} 405}
403 406
407void stop_nmi(void)
408{
409 acpi_nmi_disable();
410 ignore_nmis++;
411}
412
413void restart_nmi(void)
414{
415 ignore_nmis--;
416 acpi_nmi_enable();
417}
418
404#ifdef CONFIG_SYSCTL 419#ifdef CONFIG_SYSCTL
405 420
406static int unknown_nmi_panic_callback(struct pt_regs *regs, int cpu) 421static int unknown_nmi_panic_callback(struct pt_regs *regs, int cpu)
diff --git a/include/asm-i386/mce.h b/include/asm-i386/mce.h
index b0a02ee34ffd..d56d89742e8f 100644
--- a/include/asm-i386/mce.h
+++ b/include/asm-i386/mce.h
@@ -5,3 +5,7 @@ extern void mcheck_init(struct cpuinfo_x86 *c);
5#endif 5#endif
6 6
7extern int mce_disabled; 7extern int mce_disabled;
8
9extern void stop_mce(void);
10extern void restart_mce(void);
11
diff --git a/include/asm-i386/nmi.h b/include/asm-i386/nmi.h
index fb1e133efd9f..ff30c98f87b0 100644
--- a/include/asm-i386/nmi.h
+++ b/include/asm-i386/nmi.h
@@ -57,5 +57,7 @@ unsigned lapic_adjust_nmi_hz(unsigned hz);
57int lapic_watchdog_ok(void); 57int lapic_watchdog_ok(void);
58void disable_lapic_nmi_watchdog(void); 58void disable_lapic_nmi_watchdog(void);
59void enable_lapic_nmi_watchdog(void); 59void enable_lapic_nmi_watchdog(void);
60void stop_nmi(void);
61void restart_nmi(void);
60 62
61#endif /* ASM_NMI_H */ 63#endif /* ASM_NMI_H */
diff --git a/include/asm-x86_64/mce.h b/include/asm-x86_64/mce.h
index 556be5563e30..7bc030a1996d 100644
--- a/include/asm-x86_64/mce.h
+++ b/include/asm-x86_64/mce.h
@@ -107,6 +107,9 @@ extern void do_machine_check(struct pt_regs *, long);
107 107
108extern int mce_notify_user(void); 108extern int mce_notify_user(void);
109 109
110extern void stop_mce(void);
111extern void restart_mce(void);
112
110#endif 113#endif
111 114
112#endif 115#endif
diff --git a/include/asm-x86_64/nmi.h b/include/asm-x86_64/nmi.h
index d0a7f53b1497..5fb3c0de5ccc 100644
--- a/include/asm-x86_64/nmi.h
+++ b/include/asm-x86_64/nmi.h
@@ -88,5 +88,7 @@ unsigned lapic_adjust_nmi_hz(unsigned hz);
88int lapic_watchdog_ok(void); 88int lapic_watchdog_ok(void);
89void disable_lapic_nmi_watchdog(void); 89void disable_lapic_nmi_watchdog(void);
90void enable_lapic_nmi_watchdog(void); 90void enable_lapic_nmi_watchdog(void);
91void stop_nmi(void);
92void restart_nmi(void);
91 93
92#endif /* ASM_NMI_H */ 94#endif /* ASM_NMI_H */