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 a021681d21f..8bdb4b6af0f 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) regs->eip = (unsigned long)current_text_addr(); } -static void crash_save_self(void) +/* CPU does not save ss and esp on stack if execution is already + * running in kernel mode at the time of NMI occurrence. This code + * fixes it. + */ +static void crash_setup_regs(struct pt_regs *newregs, struct pt_regs *oldregs) +{ + memcpy(newregs, oldregs, sizeof(*newregs)); + newregs->esp = (unsigned long)&(oldregs->esp); + __asm__ __volatile__("xorl %eax, %eax;"); + __asm__ __volatile__ ("movw %%ss, %%ax;" :"=a"(newregs->xss)); +} + +/* We may have saved_regs from where the error came from + * or it is NULL if via a direct panic(). + */ +static void crash_save_self(struct pt_regs *saved_regs) { struct pt_regs regs; int cpu; cpu = smp_processor_id(); - crash_get_current_regs(®s); + + if (saved_regs) + crash_setup_regs(®s, saved_regs); + else + crash_get_current_regs(®s); crash_save_this_cpu(®s, cpu); } @@ -124,15 +143,8 @@ static int crash_nmi_callback(struct pt_regs *regs, int cpu) return 1; local_irq_disable(); - /* CPU does not save ss and esp on stack if execution is already - * running in kernel mode at the time of NMI occurrence. This code - * fixes it. - */ if (!user_mode(regs)) { - memcpy(&fixed_regs, regs, sizeof(*regs)); - fixed_regs.esp = (unsigned long)&(regs->esp); - __asm__ __volatile__("xorl %eax, %eax;"); - __asm__ __volatile__ ("movw %%ss, %%ax;" :"=a"(fixed_regs.xss)); + crash_setup_regs(&fixed_regs, regs); regs = &fixed_regs; } crash_save_this_cpu(regs, cpu); @@ -184,7 +196,7 @@ static void nmi_shootdown_cpus(void) } #endif -void machine_crash_shutdown(void) +void machine_crash_shutdown(struct pt_regs *regs) { /* This function is only called after the system * has paniced or is otherwise in a critical state. @@ -204,5 +216,5 @@ void machine_crash_shutdown(void) #if defined(CONFIG_X86_IO_APIC) disable_IO_APIC(); #endif - crash_save_self(); + crash_save_self(regs); } diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index 207ea8ba716..e458463ebc0 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c @@ -27,6 +27,7 @@ #include <linux/ptrace.h> #include <linux/utsname.h> #include <linux/kprobes.h> +#include <linux/kexec.h> #ifdef CONFIG_EISA #include <linux/ioport.h> @@ -294,6 +295,9 @@ bug: printk("Kernel BUG\n"); } +/* This is gone through when something in the kernel + * has done something bad and is about to be terminated. +*/ void die(const char * str, struct pt_regs * regs, long err) { static struct { @@ -341,6 +345,10 @@ void die(const char * str, struct pt_regs * regs, long err) bust_spinlocks(0); die.lock_owner = -1; spin_unlock_irq(&die.lock); + + if (kexec_should_crash(current)) + crash_kexec(regs); + if (in_interrupt()) panic("Fatal exception in interrupt"); @@ -570,6 +578,15 @@ void die_nmi (struct pt_regs *regs, const char *msg) console_silent(); spin_unlock(&nmi_print_lock); bust_spinlocks(0); + + /* If we are in kernel we are probably nested up pretty bad + * and might aswell get out now while we still can. + */ + if (!user_mode(regs)) { + current->thread.trap_no = 2; + crash_kexec(regs); + } + do_exit(SIGSEGV); } diff --git a/arch/ppc/kernel/machine_kexec.c b/arch/ppc/kernel/machine_kexec.c index 435ad9ea0a8..b82535357d6 100644 --- a/arch/ppc/kernel/machine_kexec.c +++ b/arch/ppc/kernel/machine_kexec.c @@ -34,7 +34,7 @@ void machine_shutdown(void) } } -void machine_crash_shutdown(void) +void machine_crash_shutdown(struct pt_regs *regs) { if (ppc_md.machine_crash_shutdown) { ppc_md.machine_crash_shutdown(); diff --git a/arch/ppc64/kernel/machine_kexec.c b/arch/ppc64/kernel/machine_kexec.c index 217965d60a4..06b25b59c8a 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]; * and if what it will achieve. Letting it be now to compile the code * in generic kexec environment */ -void machine_crash_shutdown(void) +void machine_crash_shutdown(struct pt_regs *regs) { /* do nothing right now */ /* 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 db38283c1f2..7bd169c58b0 100644 --- a/arch/s390/kernel/crash.c +++ b/arch/s390/kernel/crash.c @@ -12,6 +12,6 @@ note_buf_t crash_notes[NR_CPUS]; -void machine_crash_shutdown(void) +void machine_crash_shutdown(struct pt_regs *regs) { } diff --git a/arch/x86_64/kernel/crash.c b/arch/x86_64/kernel/crash.c index 6183bcb8525..d7fa4248501 100644 --- a/arch/x86_64/kernel/crash.c +++ b/arch/x86_64/kernel/crash.c @@ -22,7 +22,7 @@ note_buf_t crash_notes[NR_CPUS]; -void machine_crash_shutdown(void) +void machine_crash_shutdown(struct pt_regs *regs) { /* This function is only called after the system * has paniced or is otherwise in a critical state. diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c index 53b2c8fab00..af79805b557 100644 --- a/drivers/char/sysrq.c +++ b/drivers/char/sysrq.c @@ -100,7 +100,7 @@ static struct sysrq_key_op sysrq_unraw_op = { static void sysrq_handle_crashdump(int key, struct pt_regs *pt_regs, struct tty_struct *tty) { - crash_kexec(); + crash_kexec(pt_regs); } static struct sysrq_key_op sysrq_crashdump_op = { .handler = sysrq_handle_crashdump, diff --git a/include/linux/kexec.h b/include/linux/kexec.h index 0653a27c3d7..7383173a3a9 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, unsigned long flags); #endif extern struct page *kimage_alloc_control_pages(struct kimage *image, unsigned int order); -extern void crash_kexec(void); +extern void crash_kexec(struct pt_regs *); +int kexec_should_crash(struct task_struct *); extern struct kimage *kexec_image; #define KEXEC_ON_CRASH 0x00000001 @@ -123,6 +124,9 @@ extern struct kimage *kexec_image; extern struct resource crashk_res; #else /* !CONFIG_KEXEC */ -static inline void crash_kexec(void) { } +struct pt_regs; +struct task_struct; +static inline void crash_kexec(struct pt_regs *regs) { } +static inline int kexec_should_crash(struct task_struct *p) { return 0; } #endif /* CONFIG_KEXEC */ #endif /* LINUX_KEXEC_H */ diff --git a/include/linux/reboot.h b/include/linux/reboot.h index c5a05e16edb..2d4dd23168d 100644 --- a/include/linux/reboot.h +++ b/include/linux/reboot.h @@ -52,7 +52,8 @@ extern void machine_halt(void); extern void machine_power_off(void); extern void machine_shutdown(void); -extern void machine_crash_shutdown(void); +struct pt_regs; +extern void machine_crash_shutdown(struct pt_regs *); #endif diff --git a/kernel/kexec.c b/kernel/kexec.c index a0411b3bd54..277f22afe74 100644 --- a/kernel/kexec.c +++ b/kernel/kexec.c @@ -18,6 +18,8 @@ #include <linux/reboot.h> #include <linux/syscalls.h> #include <linux/ioport.h> +#include <linux/hardirq.h> + #include <asm/page.h> #include <asm/uaccess.h> #include <asm/io.h> @@ -32,6 +34,13 @@ struct resource crashk_res = { .flags = IORESOURCE_BUSY | IORESOURCE_MEM }; +int kexec_should_crash(struct task_struct *p) +{ + if (in_interrupt() || !p->pid || p->pid == 1 || panic_on_oops) + return 1; + return 0; +} + /* * When kexec transitions to the new kernel there is a one-to-one * mapping between physical and virtual addresses. On processors @@ -1010,7 +1019,7 @@ asmlinkage long compat_sys_kexec_load(unsigned long entry, } #endif -void crash_kexec(void) +void crash_kexec(struct pt_regs *regs) { struct kimage *image; int locked; @@ -1028,7 +1037,7 @@ void crash_kexec(void) if (!locked) { image = xchg(&kexec_crash_image, NULL); if (image) { - machine_crash_shutdown(); + machine_crash_shutdown(regs); machine_kexec(image); } xchg(&kexec_lock, 0); diff --git a/kernel/panic.c b/kernel/panic.c index 66f43d33cd8..74ba5f3e46c 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -83,7 +83,7 @@ NORET_TYPE void panic(const char * fmt, ...) * everything else. * Do we want to call this before we try to display a message? */ - crash_kexec(); + crash_kexec(NULL); #ifdef CONFIG_SMP /* |