diff --git a/arch/i386/kernel/alternative.c b/arch/i386/kernel/alternative.c index 206ea2ca63cc5beb221c238ff81172244de262b9..c3750c2c41137a694be9570443f59b379cc4cc4d 100644 --- a/arch/i386/kernel/alternative.c +++ b/arch/i386/kernel/alternative.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include #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 56cd485b127ce59b2da500e9f176b83bb755b7b7..34c781eddee4cdc6fce3419366b63329965655a5 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 438949da3b634d63e40503da9baba34fe00e130b..cfffe3dd9e838315089c9461a49b0fa2b6aef12b 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) { diff --git a/arch/x86_64/kernel/mce.c b/arch/x86_64/kernel/mce.c index 4d8450ee3635e8ee587f5f639f7862b5a5155a74..a66d607f5b924e09d864120d1d656c3e1a1c2da0 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 = { &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. */ diff --git a/arch/x86_64/kernel/nmi.c b/arch/x86_64/kernel/nmi.c index edbbc59b752334d570cb939c70ece3f43ca8ae42..cb8ee9d02f8682d4e52374aae1847a4daf03292a 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) 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); - default_do_nmi(regs); + 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) diff --git a/include/asm-i386/mce.h b/include/asm-i386/mce.h index b0a02ee34ffd74720747c295077720e86604933c..d56d89742e8fdcd0bfd96ccd1ff83292eea0bc99 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); #endif extern int mce_disabled; + +extern void stop_mce(void); +extern void restart_mce(void); + diff --git a/include/asm-i386/nmi.h b/include/asm-i386/nmi.h index fb1e133efd9fecdc182d827ddd8c75841b8c6660..ff30c98f87b023290c8d1355f3873540e7d6c2ae 100644 --- a/include/asm-i386/nmi.h +++ b/include/asm-i386/nmi.h @@ -57,5 +57,7 @@ unsigned lapic_adjust_nmi_hz(unsigned hz); int lapic_watchdog_ok(void); void disable_lapic_nmi_watchdog(void); void enable_lapic_nmi_watchdog(void); +void stop_nmi(void); +void restart_nmi(void); #endif /* ASM_NMI_H */ diff --git a/include/asm-x86_64/mce.h b/include/asm-x86_64/mce.h index 556be5563e308f2d80e7e67f65b4ab19b67b3b49..7bc030a1996de9c2ad53763eac202643e459a54b 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); extern int mce_notify_user(void); +extern void stop_mce(void); +extern void restart_mce(void); + #endif #endif diff --git a/include/asm-x86_64/nmi.h b/include/asm-x86_64/nmi.h index d0a7f53b1497cf65442c2162716608378589c467..5fb3c0de5cccba5a421a3820bb551619d489fad9 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); int lapic_watchdog_ok(void); void disable_lapic_nmi_watchdog(void); void enable_lapic_nmi_watchdog(void); +void stop_nmi(void); +void restart_nmi(void); #endif /* ASM_NMI_H */