diff options
-rw-r--r-- | arch/x86/kernel/Makefile | 2 | ||||
-rw-r--r-- | arch/x86/kernel/nmi.c | 178 | ||||
-rw-r--r-- | arch/x86/kernel/traps.c | 155 |
3 files changed, 179 insertions, 156 deletions
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 82f2912155a5..8baca3c4871c 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile | |||
@@ -19,7 +19,7 @@ endif | |||
19 | 19 | ||
20 | obj-y := process_$(BITS).o signal.o entry_$(BITS).o | 20 | obj-y := process_$(BITS).o signal.o entry_$(BITS).o |
21 | obj-y += traps.o irq.o irq_$(BITS).o dumpstack_$(BITS).o | 21 | obj-y += traps.o irq.o irq_$(BITS).o dumpstack_$(BITS).o |
22 | obj-y += time.o ioport.o ldt.o dumpstack.o | 22 | obj-y += time.o ioport.o ldt.o dumpstack.o nmi.o |
23 | obj-y += setup.o x86_init.o i8259.o irqinit.o jump_label.o | 23 | obj-y += setup.o x86_init.o i8259.o irqinit.o jump_label.o |
24 | obj-$(CONFIG_IRQ_WORK) += irq_work.o | 24 | obj-$(CONFIG_IRQ_WORK) += irq_work.o |
25 | obj-y += probe_roms.o | 25 | obj-y += probe_roms.o |
diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c new file mode 100644 index 000000000000..68d758aca8cd --- /dev/null +++ b/arch/x86/kernel/nmi.c | |||
@@ -0,0 +1,178 @@ | |||
1 | /* | ||
2 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
3 | * Copyright (C) 2000, 2001, 2002 Andi Kleen, SuSE Labs | ||
4 | * | ||
5 | * Pentium III FXSR, SSE support | ||
6 | * Gareth Hughes <gareth@valinux.com>, May 2000 | ||
7 | */ | ||
8 | |||
9 | /* | ||
10 | * Handle hardware traps and faults. | ||
11 | */ | ||
12 | #include <linux/spinlock.h> | ||
13 | #include <linux/kprobes.h> | ||
14 | #include <linux/kdebug.h> | ||
15 | #include <linux/nmi.h> | ||
16 | |||
17 | #if defined(CONFIG_EDAC) | ||
18 | #include <linux/edac.h> | ||
19 | #endif | ||
20 | |||
21 | #include <linux/atomic.h> | ||
22 | #include <asm/traps.h> | ||
23 | #include <asm/mach_traps.h> | ||
24 | |||
25 | static int ignore_nmis; | ||
26 | |||
27 | int unknown_nmi_panic; | ||
28 | /* | ||
29 | * Prevent NMI reason port (0x61) being accessed simultaneously, can | ||
30 | * only be used in NMI handler. | ||
31 | */ | ||
32 | static DEFINE_RAW_SPINLOCK(nmi_reason_lock); | ||
33 | |||
34 | static int __init setup_unknown_nmi_panic(char *str) | ||
35 | { | ||
36 | unknown_nmi_panic = 1; | ||
37 | return 1; | ||
38 | } | ||
39 | __setup("unknown_nmi_panic", setup_unknown_nmi_panic); | ||
40 | |||
41 | static notrace __kprobes void | ||
42 | pci_serr_error(unsigned char reason, struct pt_regs *regs) | ||
43 | { | ||
44 | pr_emerg("NMI: PCI system error (SERR) for reason %02x on CPU %d.\n", | ||
45 | reason, smp_processor_id()); | ||
46 | |||
47 | /* | ||
48 | * On some machines, PCI SERR line is used to report memory | ||
49 | * errors. EDAC makes use of it. | ||
50 | */ | ||
51 | #if defined(CONFIG_EDAC) | ||
52 | if (edac_handler_set()) { | ||
53 | edac_atomic_assert_error(); | ||
54 | return; | ||
55 | } | ||
56 | #endif | ||
57 | |||
58 | if (panic_on_unrecovered_nmi) | ||
59 | panic("NMI: Not continuing"); | ||
60 | |||
61 | pr_emerg("Dazed and confused, but trying to continue\n"); | ||
62 | |||
63 | /* Clear and disable the PCI SERR error line. */ | ||
64 | reason = (reason & NMI_REASON_CLEAR_MASK) | NMI_REASON_CLEAR_SERR; | ||
65 | outb(reason, NMI_REASON_PORT); | ||
66 | } | ||
67 | |||
68 | static notrace __kprobes void | ||
69 | io_check_error(unsigned char reason, struct pt_regs *regs) | ||
70 | { | ||
71 | unsigned long i; | ||
72 | |||
73 | pr_emerg( | ||
74 | "NMI: IOCK error (debug interrupt?) for reason %02x on CPU %d.\n", | ||
75 | reason, smp_processor_id()); | ||
76 | show_registers(regs); | ||
77 | |||
78 | if (panic_on_io_nmi) | ||
79 | panic("NMI IOCK error: Not continuing"); | ||
80 | |||
81 | /* Re-enable the IOCK line, wait for a few seconds */ | ||
82 | reason = (reason & NMI_REASON_CLEAR_MASK) | NMI_REASON_CLEAR_IOCHK; | ||
83 | outb(reason, NMI_REASON_PORT); | ||
84 | |||
85 | i = 20000; | ||
86 | while (--i) { | ||
87 | touch_nmi_watchdog(); | ||
88 | udelay(100); | ||
89 | } | ||
90 | |||
91 | reason &= ~NMI_REASON_CLEAR_IOCHK; | ||
92 | outb(reason, NMI_REASON_PORT); | ||
93 | } | ||
94 | |||
95 | static notrace __kprobes void | ||
96 | unknown_nmi_error(unsigned char reason, struct pt_regs *regs) | ||
97 | { | ||
98 | if (notify_die(DIE_NMIUNKNOWN, "nmi", regs, reason, 2, SIGINT) == | ||
99 | NOTIFY_STOP) | ||
100 | return; | ||
101 | #ifdef CONFIG_MCA | ||
102 | /* | ||
103 | * Might actually be able to figure out what the guilty party | ||
104 | * is: | ||
105 | */ | ||
106 | if (MCA_bus) { | ||
107 | mca_handle_nmi(); | ||
108 | return; | ||
109 | } | ||
110 | #endif | ||
111 | pr_emerg("Uhhuh. NMI received for unknown reason %02x on CPU %d.\n", | ||
112 | reason, smp_processor_id()); | ||
113 | |||
114 | pr_emerg("Do you have a strange power saving mode enabled?\n"); | ||
115 | if (unknown_nmi_panic || panic_on_unrecovered_nmi) | ||
116 | panic("NMI: Not continuing"); | ||
117 | |||
118 | pr_emerg("Dazed and confused, but trying to continue\n"); | ||
119 | } | ||
120 | |||
121 | static notrace __kprobes void default_do_nmi(struct pt_regs *regs) | ||
122 | { | ||
123 | unsigned char reason = 0; | ||
124 | |||
125 | /* | ||
126 | * CPU-specific NMI must be processed before non-CPU-specific | ||
127 | * NMI, otherwise we may lose it, because the CPU-specific | ||
128 | * NMI can not be detected/processed on other CPUs. | ||
129 | */ | ||
130 | if (notify_die(DIE_NMI, "nmi", regs, 0, 2, SIGINT) == NOTIFY_STOP) | ||
131 | return; | ||
132 | |||
133 | /* Non-CPU-specific NMI: NMI sources can be processed on any CPU */ | ||
134 | raw_spin_lock(&nmi_reason_lock); | ||
135 | reason = get_nmi_reason(); | ||
136 | |||
137 | if (reason & NMI_REASON_MASK) { | ||
138 | if (reason & NMI_REASON_SERR) | ||
139 | pci_serr_error(reason, regs); | ||
140 | else if (reason & NMI_REASON_IOCHK) | ||
141 | io_check_error(reason, regs); | ||
142 | #ifdef CONFIG_X86_32 | ||
143 | /* | ||
144 | * Reassert NMI in case it became active | ||
145 | * meanwhile as it's edge-triggered: | ||
146 | */ | ||
147 | reassert_nmi(); | ||
148 | #endif | ||
149 | raw_spin_unlock(&nmi_reason_lock); | ||
150 | return; | ||
151 | } | ||
152 | raw_spin_unlock(&nmi_reason_lock); | ||
153 | |||
154 | unknown_nmi_error(reason, regs); | ||
155 | } | ||
156 | |||
157 | dotraplinkage notrace __kprobes void | ||
158 | do_nmi(struct pt_regs *regs, long error_code) | ||
159 | { | ||
160 | nmi_enter(); | ||
161 | |||
162 | inc_irq_stat(__nmi_count); | ||
163 | |||
164 | if (!ignore_nmis) | ||
165 | default_do_nmi(regs); | ||
166 | |||
167 | nmi_exit(); | ||
168 | } | ||
169 | |||
170 | void stop_nmi(void) | ||
171 | { | ||
172 | ignore_nmis++; | ||
173 | } | ||
174 | |||
175 | void restart_nmi(void) | ||
176 | { | ||
177 | ignore_nmis--; | ||
178 | } | ||
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 6913369c234c..a8e3eb83466c 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c | |||
@@ -81,15 +81,6 @@ gate_desc idt_table[NR_VECTORS] __page_aligned_data = { { { { 0, 0 } } }, }; | |||
81 | DECLARE_BITMAP(used_vectors, NR_VECTORS); | 81 | DECLARE_BITMAP(used_vectors, NR_VECTORS); |
82 | EXPORT_SYMBOL_GPL(used_vectors); | 82 | EXPORT_SYMBOL_GPL(used_vectors); |
83 | 83 | ||
84 | static int ignore_nmis; | ||
85 | |||
86 | int unknown_nmi_panic; | ||
87 | /* | ||
88 | * Prevent NMI reason port (0x61) being accessed simultaneously, can | ||
89 | * only be used in NMI handler. | ||
90 | */ | ||
91 | static DEFINE_RAW_SPINLOCK(nmi_reason_lock); | ||
92 | |||
93 | static inline void conditional_sti(struct pt_regs *regs) | 84 | static inline void conditional_sti(struct pt_regs *regs) |
94 | { | 85 | { |
95 | if (regs->flags & X86_EFLAGS_IF) | 86 | if (regs->flags & X86_EFLAGS_IF) |
@@ -307,152 +298,6 @@ gp_in_kernel: | |||
307 | die("general protection fault", regs, error_code); | 298 | die("general protection fault", regs, error_code); |
308 | } | 299 | } |
309 | 300 | ||
310 | static int __init setup_unknown_nmi_panic(char *str) | ||
311 | { | ||
312 | unknown_nmi_panic = 1; | ||
313 | return 1; | ||
314 | } | ||
315 | __setup("unknown_nmi_panic", setup_unknown_nmi_panic); | ||
316 | |||
317 | static notrace __kprobes void | ||
318 | pci_serr_error(unsigned char reason, struct pt_regs *regs) | ||
319 | { | ||
320 | pr_emerg("NMI: PCI system error (SERR) for reason %02x on CPU %d.\n", | ||
321 | reason, smp_processor_id()); | ||
322 | |||
323 | /* | ||
324 | * On some machines, PCI SERR line is used to report memory | ||
325 | * errors. EDAC makes use of it. | ||
326 | */ | ||
327 | #if defined(CONFIG_EDAC) | ||
328 | if (edac_handler_set()) { | ||
329 | edac_atomic_assert_error(); | ||
330 | return; | ||
331 | } | ||
332 | #endif | ||
333 | |||
334 | if (panic_on_unrecovered_nmi) | ||
335 | panic("NMI: Not continuing"); | ||
336 | |||
337 | pr_emerg("Dazed and confused, but trying to continue\n"); | ||
338 | |||
339 | /* Clear and disable the PCI SERR error line. */ | ||
340 | reason = (reason & NMI_REASON_CLEAR_MASK) | NMI_REASON_CLEAR_SERR; | ||
341 | outb(reason, NMI_REASON_PORT); | ||
342 | } | ||
343 | |||
344 | static notrace __kprobes void | ||
345 | io_check_error(unsigned char reason, struct pt_regs *regs) | ||
346 | { | ||
347 | unsigned long i; | ||
348 | |||
349 | pr_emerg( | ||
350 | "NMI: IOCK error (debug interrupt?) for reason %02x on CPU %d.\n", | ||
351 | reason, smp_processor_id()); | ||
352 | show_registers(regs); | ||
353 | |||
354 | if (panic_on_io_nmi) | ||
355 | panic("NMI IOCK error: Not continuing"); | ||
356 | |||
357 | /* Re-enable the IOCK line, wait for a few seconds */ | ||
358 | reason = (reason & NMI_REASON_CLEAR_MASK) | NMI_REASON_CLEAR_IOCHK; | ||
359 | outb(reason, NMI_REASON_PORT); | ||
360 | |||
361 | i = 20000; | ||
362 | while (--i) { | ||
363 | touch_nmi_watchdog(); | ||
364 | udelay(100); | ||
365 | } | ||
366 | |||
367 | reason &= ~NMI_REASON_CLEAR_IOCHK; | ||
368 | outb(reason, NMI_REASON_PORT); | ||
369 | } | ||
370 | |||
371 | static notrace __kprobes void | ||
372 | unknown_nmi_error(unsigned char reason, struct pt_regs *regs) | ||
373 | { | ||
374 | if (notify_die(DIE_NMIUNKNOWN, "nmi", regs, reason, 2, SIGINT) == | ||
375 | NOTIFY_STOP) | ||
376 | return; | ||
377 | #ifdef CONFIG_MCA | ||
378 | /* | ||
379 | * Might actually be able to figure out what the guilty party | ||
380 | * is: | ||
381 | */ | ||
382 | if (MCA_bus) { | ||
383 | mca_handle_nmi(); | ||
384 | return; | ||
385 | } | ||
386 | #endif | ||
387 | pr_emerg("Uhhuh. NMI received for unknown reason %02x on CPU %d.\n", | ||
388 | reason, smp_processor_id()); | ||
389 | |||
390 | pr_emerg("Do you have a strange power saving mode enabled?\n"); | ||
391 | if (unknown_nmi_panic || panic_on_unrecovered_nmi) | ||
392 | panic("NMI: Not continuing"); | ||
393 | |||
394 | pr_emerg("Dazed and confused, but trying to continue\n"); | ||
395 | } | ||
396 | |||
397 | static notrace __kprobes void default_do_nmi(struct pt_regs *regs) | ||
398 | { | ||
399 | unsigned char reason = 0; | ||
400 | |||
401 | /* | ||
402 | * CPU-specific NMI must be processed before non-CPU-specific | ||
403 | * NMI, otherwise we may lose it, because the CPU-specific | ||
404 | * NMI can not be detected/processed on other CPUs. | ||
405 | */ | ||
406 | if (notify_die(DIE_NMI, "nmi", regs, 0, 2, SIGINT) == NOTIFY_STOP) | ||
407 | return; | ||
408 | |||
409 | /* Non-CPU-specific NMI: NMI sources can be processed on any CPU */ | ||
410 | raw_spin_lock(&nmi_reason_lock); | ||
411 | reason = get_nmi_reason(); | ||
412 | |||
413 | if (reason & NMI_REASON_MASK) { | ||
414 | if (reason & NMI_REASON_SERR) | ||
415 | pci_serr_error(reason, regs); | ||
416 | else if (reason & NMI_REASON_IOCHK) | ||
417 | io_check_error(reason, regs); | ||
418 | #ifdef CONFIG_X86_32 | ||
419 | /* | ||
420 | * Reassert NMI in case it became active | ||
421 | * meanwhile as it's edge-triggered: | ||
422 | */ | ||
423 | reassert_nmi(); | ||
424 | #endif | ||
425 | raw_spin_unlock(&nmi_reason_lock); | ||
426 | return; | ||
427 | } | ||
428 | raw_spin_unlock(&nmi_reason_lock); | ||
429 | |||
430 | unknown_nmi_error(reason, regs); | ||
431 | } | ||
432 | |||
433 | dotraplinkage notrace __kprobes void | ||
434 | do_nmi(struct pt_regs *regs, long error_code) | ||
435 | { | ||
436 | nmi_enter(); | ||
437 | |||
438 | inc_irq_stat(__nmi_count); | ||
439 | |||
440 | if (!ignore_nmis) | ||
441 | default_do_nmi(regs); | ||
442 | |||
443 | nmi_exit(); | ||
444 | } | ||
445 | |||
446 | void stop_nmi(void) | ||
447 | { | ||
448 | ignore_nmis++; | ||
449 | } | ||
450 | |||
451 | void restart_nmi(void) | ||
452 | { | ||
453 | ignore_nmis--; | ||
454 | } | ||
455 | |||
456 | /* May run on IST stack. */ | 301 | /* May run on IST stack. */ |
457 | dotraplinkage void __kprobes do_int3(struct pt_regs *regs, long error_code) | 302 | dotraplinkage void __kprobes do_int3(struct pt_regs *regs, long error_code) |
458 | { | 303 | { |