diff options
-rw-r--r-- | arch/i386/kernel/crash.c | 36 | ||||
-rw-r--r-- | arch/i386/kernel/traps.c | 17 | ||||
-rw-r--r-- | arch/ppc/kernel/machine_kexec.c | 2 | ||||
-rw-r--r-- | arch/ppc64/kernel/machine_kexec.c | 2 | ||||
-rw-r--r-- | arch/s390/kernel/crash.c | 2 | ||||
-rw-r--r-- | arch/x86_64/kernel/crash.c | 2 | ||||
-rw-r--r-- | drivers/char/sysrq.c | 2 | ||||
-rw-r--r-- | include/linux/kexec.h | 8 | ||||
-rw-r--r-- | include/linux/reboot.h | 3 | ||||
-rw-r--r-- | kernel/kexec.c | 13 | ||||
-rw-r--r-- | kernel/panic.c | 2 |
11 files changed, 66 insertions, 23 deletions
diff --git a/arch/i386/kernel/crash.c b/arch/i386/kernel/crash.c index a021681d21f8..8bdb4b6af0ff 100644 --- a/arch/i386/kernel/crash.c +++ b/arch/i386/kernel/crash.c | |||
@@ -100,12 +100,31 @@ static void crash_get_current_regs(struct pt_regs *regs) | |||
100 | regs->eip = (unsigned long)current_text_addr(); | 100 | regs->eip = (unsigned long)current_text_addr(); |
101 | } | 101 | } |
102 | 102 | ||
103 | static void crash_save_self(void) | 103 | /* CPU does not save ss and esp on stack if execution is already |
104 | * running in kernel mode at the time of NMI occurrence. This code | ||
105 | * fixes it. | ||
106 | */ | ||
107 | static void crash_setup_regs(struct pt_regs *newregs, struct pt_regs *oldregs) | ||
108 | { | ||
109 | memcpy(newregs, oldregs, sizeof(*newregs)); | ||
110 | newregs->esp = (unsigned long)&(oldregs->esp); | ||
111 | __asm__ __volatile__("xorl %eax, %eax;"); | ||
112 | __asm__ __volatile__ ("movw %%ss, %%ax;" :"=a"(newregs->xss)); | ||
113 | } | ||
114 | |||
115 | /* We may have saved_regs from where the error came from | ||
116 | * or it is NULL if via a direct panic(). | ||
117 | */ | ||
118 | static void crash_save_self(struct pt_regs *saved_regs) | ||
104 | { | 119 | { |
105 | struct pt_regs regs; | 120 | struct pt_regs regs; |
106 | int cpu; | 121 | int cpu; |
107 | cpu = smp_processor_id(); | 122 | cpu = smp_processor_id(); |
108 | crash_get_current_regs(®s); | 123 | |
124 | if (saved_regs) | ||
125 | crash_setup_regs(®s, saved_regs); | ||
126 | else | ||
127 | crash_get_current_regs(®s); | ||
109 | crash_save_this_cpu(®s, cpu); | 128 | crash_save_this_cpu(®s, cpu); |
110 | } | 129 | } |
111 | 130 | ||
@@ -124,15 +143,8 @@ static int crash_nmi_callback(struct pt_regs *regs, int cpu) | |||
124 | return 1; | 143 | return 1; |
125 | local_irq_disable(); | 144 | local_irq_disable(); |
126 | 145 | ||
127 | /* CPU does not save ss and esp on stack if execution is already | ||
128 | * running in kernel mode at the time of NMI occurrence. This code | ||
129 | * fixes it. | ||
130 | */ | ||
131 | if (!user_mode(regs)) { | 146 | if (!user_mode(regs)) { |
132 | memcpy(&fixed_regs, regs, sizeof(*regs)); | 147 | crash_setup_regs(&fixed_regs, regs); |
133 | fixed_regs.esp = (unsigned long)&(regs->esp); | ||
134 | __asm__ __volatile__("xorl %eax, %eax;"); | ||
135 | __asm__ __volatile__ ("movw %%ss, %%ax;" :"=a"(fixed_regs.xss)); | ||
136 | regs = &fixed_regs; | 148 | regs = &fixed_regs; |
137 | } | 149 | } |
138 | crash_save_this_cpu(regs, cpu); | 150 | crash_save_this_cpu(regs, cpu); |
@@ -184,7 +196,7 @@ static void nmi_shootdown_cpus(void) | |||
184 | } | 196 | } |
185 | #endif | 197 | #endif |
186 | 198 | ||
187 | void machine_crash_shutdown(void) | 199 | void machine_crash_shutdown(struct pt_regs *regs) |
188 | { | 200 | { |
189 | /* This function is only called after the system | 201 | /* This function is only called after the system |
190 | * has paniced or is otherwise in a critical state. | 202 | * has paniced or is otherwise in a critical state. |
@@ -204,5 +216,5 @@ void machine_crash_shutdown(void) | |||
204 | #if defined(CONFIG_X86_IO_APIC) | 216 | #if defined(CONFIG_X86_IO_APIC) |
205 | disable_IO_APIC(); | 217 | disable_IO_APIC(); |
206 | #endif | 218 | #endif |
207 | crash_save_self(); | 219 | crash_save_self(regs); |
208 | } | 220 | } |
diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index 207ea8ba7169..e458463ebc05 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <linux/ptrace.h> | 27 | #include <linux/ptrace.h> |
28 | #include <linux/utsname.h> | 28 | #include <linux/utsname.h> |
29 | #include <linux/kprobes.h> | 29 | #include <linux/kprobes.h> |
30 | #include <linux/kexec.h> | ||
30 | 31 | ||
31 | #ifdef CONFIG_EISA | 32 | #ifdef CONFIG_EISA |
32 | #include <linux/ioport.h> | 33 | #include <linux/ioport.h> |
@@ -294,6 +295,9 @@ bug: | |||
294 | printk("Kernel BUG\n"); | 295 | printk("Kernel BUG\n"); |
295 | } | 296 | } |
296 | 297 | ||
298 | /* This is gone through when something in the kernel | ||
299 | * has done something bad and is about to be terminated. | ||
300 | */ | ||
297 | void die(const char * str, struct pt_regs * regs, long err) | 301 | void die(const char * str, struct pt_regs * regs, long err) |
298 | { | 302 | { |
299 | static struct { | 303 | static struct { |
@@ -341,6 +345,10 @@ void die(const char * str, struct pt_regs * regs, long err) | |||
341 | bust_spinlocks(0); | 345 | bust_spinlocks(0); |
342 | die.lock_owner = -1; | 346 | die.lock_owner = -1; |
343 | spin_unlock_irq(&die.lock); | 347 | spin_unlock_irq(&die.lock); |
348 | |||
349 | if (kexec_should_crash(current)) | ||
350 | crash_kexec(regs); | ||
351 | |||
344 | if (in_interrupt()) | 352 | if (in_interrupt()) |
345 | panic("Fatal exception in interrupt"); | 353 | panic("Fatal exception in interrupt"); |
346 | 354 | ||
@@ -570,6 +578,15 @@ void die_nmi (struct pt_regs *regs, const char *msg) | |||
570 | console_silent(); | 578 | console_silent(); |
571 | spin_unlock(&nmi_print_lock); | 579 | spin_unlock(&nmi_print_lock); |
572 | bust_spinlocks(0); | 580 | bust_spinlocks(0); |
581 | |||
582 | /* If we are in kernel we are probably nested up pretty bad | ||
583 | * and might aswell get out now while we still can. | ||
584 | */ | ||
585 | if (!user_mode(regs)) { | ||
586 | current->thread.trap_no = 2; | ||
587 | crash_kexec(regs); | ||
588 | } | ||
589 | |||
573 | do_exit(SIGSEGV); | 590 | do_exit(SIGSEGV); |
574 | } | 591 | } |
575 | 592 | ||
diff --git a/arch/ppc/kernel/machine_kexec.c b/arch/ppc/kernel/machine_kexec.c index 435ad9ea0a83..b82535357d6d 100644 --- a/arch/ppc/kernel/machine_kexec.c +++ b/arch/ppc/kernel/machine_kexec.c | |||
@@ -34,7 +34,7 @@ void machine_shutdown(void) | |||
34 | } | 34 | } |
35 | } | 35 | } |
36 | 36 | ||
37 | void machine_crash_shutdown(void) | 37 | void machine_crash_shutdown(struct pt_regs *regs) |
38 | { | 38 | { |
39 | if (ppc_md.machine_crash_shutdown) { | 39 | if (ppc_md.machine_crash_shutdown) { |
40 | ppc_md.machine_crash_shutdown(); | 40 | ppc_md.machine_crash_shutdown(); |
diff --git a/arch/ppc64/kernel/machine_kexec.c b/arch/ppc64/kernel/machine_kexec.c index 217965d60a45..06b25b59c8a8 100644 --- a/arch/ppc64/kernel/machine_kexec.c +++ b/arch/ppc64/kernel/machine_kexec.c | |||
@@ -34,7 +34,7 @@ note_buf_t crash_notes[NR_CPUS]; | |||
34 | * and if what it will achieve. Letting it be now to compile the code | 34 | * and if what it will achieve. Letting it be now to compile the code |
35 | * in generic kexec environment | 35 | * in generic kexec environment |
36 | */ | 36 | */ |
37 | void machine_crash_shutdown(void) | 37 | void machine_crash_shutdown(struct pt_regs *regs) |
38 | { | 38 | { |
39 | /* do nothing right now */ | 39 | /* do nothing right now */ |
40 | /* smp_relase_cpus() if we want smp on panic kernel */ | 40 | /* smp_relase_cpus() if we want smp on panic kernel */ |
diff --git a/arch/s390/kernel/crash.c b/arch/s390/kernel/crash.c index db38283c1f27..7bd169c58b0c 100644 --- a/arch/s390/kernel/crash.c +++ b/arch/s390/kernel/crash.c | |||
@@ -12,6 +12,6 @@ | |||
12 | 12 | ||
13 | note_buf_t crash_notes[NR_CPUS]; | 13 | note_buf_t crash_notes[NR_CPUS]; |
14 | 14 | ||
15 | void machine_crash_shutdown(void) | 15 | void machine_crash_shutdown(struct pt_regs *regs) |
16 | { | 16 | { |
17 | } | 17 | } |
diff --git a/arch/x86_64/kernel/crash.c b/arch/x86_64/kernel/crash.c index 6183bcb85257..d7fa4248501c 100644 --- a/arch/x86_64/kernel/crash.c +++ b/arch/x86_64/kernel/crash.c | |||
@@ -22,7 +22,7 @@ | |||
22 | 22 | ||
23 | note_buf_t crash_notes[NR_CPUS]; | 23 | note_buf_t crash_notes[NR_CPUS]; |
24 | 24 | ||
25 | void machine_crash_shutdown(void) | 25 | void machine_crash_shutdown(struct pt_regs *regs) |
26 | { | 26 | { |
27 | /* This function is only called after the system | 27 | /* This function is only called after the system |
28 | * has paniced or is otherwise in a critical state. | 28 | * has paniced or is otherwise in a critical state. |
diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c index 53b2c8fab00e..af79805b5576 100644 --- a/drivers/char/sysrq.c +++ b/drivers/char/sysrq.c | |||
@@ -100,7 +100,7 @@ static struct sysrq_key_op sysrq_unraw_op = { | |||
100 | static void sysrq_handle_crashdump(int key, struct pt_regs *pt_regs, | 100 | static void sysrq_handle_crashdump(int key, struct pt_regs *pt_regs, |
101 | struct tty_struct *tty) | 101 | struct tty_struct *tty) |
102 | { | 102 | { |
103 | crash_kexec(); | 103 | crash_kexec(pt_regs); |
104 | } | 104 | } |
105 | static struct sysrq_key_op sysrq_crashdump_op = { | 105 | static struct sysrq_key_op sysrq_crashdump_op = { |
106 | .handler = sysrq_handle_crashdump, | 106 | .handler = sysrq_handle_crashdump, |
diff --git a/include/linux/kexec.h b/include/linux/kexec.h index 0653a27c3d72..7383173a3a9c 100644 --- a/include/linux/kexec.h +++ b/include/linux/kexec.h | |||
@@ -99,7 +99,8 @@ extern asmlinkage long compat_sys_kexec_load(unsigned long entry, | |||
99 | unsigned long flags); | 99 | unsigned long flags); |
100 | #endif | 100 | #endif |
101 | extern struct page *kimage_alloc_control_pages(struct kimage *image, unsigned int order); | 101 | extern struct page *kimage_alloc_control_pages(struct kimage *image, unsigned int order); |
102 | extern void crash_kexec(void); | 102 | extern void crash_kexec(struct pt_regs *); |
103 | int kexec_should_crash(struct task_struct *); | ||
103 | extern struct kimage *kexec_image; | 104 | extern struct kimage *kexec_image; |
104 | 105 | ||
105 | #define KEXEC_ON_CRASH 0x00000001 | 106 | #define KEXEC_ON_CRASH 0x00000001 |
@@ -123,6 +124,9 @@ extern struct kimage *kexec_image; | |||
123 | extern struct resource crashk_res; | 124 | extern struct resource crashk_res; |
124 | 125 | ||
125 | #else /* !CONFIG_KEXEC */ | 126 | #else /* !CONFIG_KEXEC */ |
126 | static inline void crash_kexec(void) { } | 127 | struct pt_regs; |
128 | struct task_struct; | ||
129 | static inline void crash_kexec(struct pt_regs *regs) { } | ||
130 | static inline int kexec_should_crash(struct task_struct *p) { return 0; } | ||
127 | #endif /* CONFIG_KEXEC */ | 131 | #endif /* CONFIG_KEXEC */ |
128 | #endif /* LINUX_KEXEC_H */ | 132 | #endif /* LINUX_KEXEC_H */ |
diff --git a/include/linux/reboot.h b/include/linux/reboot.h index c5a05e16edb2..2d4dd23168dd 100644 --- a/include/linux/reboot.h +++ b/include/linux/reboot.h | |||
@@ -52,7 +52,8 @@ extern void machine_halt(void); | |||
52 | extern void machine_power_off(void); | 52 | extern void machine_power_off(void); |
53 | 53 | ||
54 | extern void machine_shutdown(void); | 54 | extern void machine_shutdown(void); |
55 | extern void machine_crash_shutdown(void); | 55 | struct pt_regs; |
56 | extern void machine_crash_shutdown(struct pt_regs *); | ||
56 | 57 | ||
57 | #endif | 58 | #endif |
58 | 59 | ||
diff --git a/kernel/kexec.c b/kernel/kexec.c index a0411b3bd54a..277f22afe74b 100644 --- a/kernel/kexec.c +++ b/kernel/kexec.c | |||
@@ -18,6 +18,8 @@ | |||
18 | #include <linux/reboot.h> | 18 | #include <linux/reboot.h> |
19 | #include <linux/syscalls.h> | 19 | #include <linux/syscalls.h> |
20 | #include <linux/ioport.h> | 20 | #include <linux/ioport.h> |
21 | #include <linux/hardirq.h> | ||
22 | |||
21 | #include <asm/page.h> | 23 | #include <asm/page.h> |
22 | #include <asm/uaccess.h> | 24 | #include <asm/uaccess.h> |
23 | #include <asm/io.h> | 25 | #include <asm/io.h> |
@@ -32,6 +34,13 @@ struct resource crashk_res = { | |||
32 | .flags = IORESOURCE_BUSY | IORESOURCE_MEM | 34 | .flags = IORESOURCE_BUSY | IORESOURCE_MEM |
33 | }; | 35 | }; |
34 | 36 | ||
37 | int kexec_should_crash(struct task_struct *p) | ||
38 | { | ||
39 | if (in_interrupt() || !p->pid || p->pid == 1 || panic_on_oops) | ||
40 | return 1; | ||
41 | return 0; | ||
42 | } | ||
43 | |||
35 | /* | 44 | /* |
36 | * When kexec transitions to the new kernel there is a one-to-one | 45 | * When kexec transitions to the new kernel there is a one-to-one |
37 | * mapping between physical and virtual addresses. On processors | 46 | * mapping between physical and virtual addresses. On processors |
@@ -1010,7 +1019,7 @@ asmlinkage long compat_sys_kexec_load(unsigned long entry, | |||
1010 | } | 1019 | } |
1011 | #endif | 1020 | #endif |
1012 | 1021 | ||
1013 | void crash_kexec(void) | 1022 | void crash_kexec(struct pt_regs *regs) |
1014 | { | 1023 | { |
1015 | struct kimage *image; | 1024 | struct kimage *image; |
1016 | int locked; | 1025 | int locked; |
@@ -1028,7 +1037,7 @@ void crash_kexec(void) | |||
1028 | if (!locked) { | 1037 | if (!locked) { |
1029 | image = xchg(&kexec_crash_image, NULL); | 1038 | image = xchg(&kexec_crash_image, NULL); |
1030 | if (image) { | 1039 | if (image) { |
1031 | machine_crash_shutdown(); | 1040 | machine_crash_shutdown(regs); |
1032 | machine_kexec(image); | 1041 | machine_kexec(image); |
1033 | } | 1042 | } |
1034 | xchg(&kexec_lock, 0); | 1043 | xchg(&kexec_lock, 0); |
diff --git a/kernel/panic.c b/kernel/panic.c index 66f43d33cd80..74ba5f3e46c7 100644 --- a/kernel/panic.c +++ b/kernel/panic.c | |||
@@ -83,7 +83,7 @@ NORET_TYPE void panic(const char * fmt, ...) | |||
83 | * everything else. | 83 | * everything else. |
84 | * Do we want to call this before we try to display a message? | 84 | * Do we want to call this before we try to display a message? |
85 | */ | 85 | */ |
86 | crash_kexec(); | 86 | crash_kexec(NULL); |
87 | 87 | ||
88 | #ifdef CONFIG_SMP | 88 | #ifdef CONFIG_SMP |
89 | /* | 89 | /* |