Commit 6e274d14 authored by Alexander Nyberg's avatar Alexander Nyberg Committed by Linus Torvalds

[PATCH] kdump: Use real pt_regs from exception

Makes kexec_crashdump() take a pt_regs * as an argument.  This allows to
get exact register state at the point of the crash.  If we come from direct
panic assertion NULL will be passed and the current registers saved before
crashdump.

This hooks into two places:
die(): check the conditions under which we will panic when calling
do_exit and go there directly with the pt_regs that caused the fatal
fault.

die_nmi(): If we receive an NMI lockup while in the kernel use the
pt_regs and go directly to crash_kexec(). We're probably nested up badly
at this point so this might be the only chance to escape with proper
information.
Signed-off-by: default avatarAlexander Nyberg <alexn@telia.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 86b1ae38
...@@ -100,12 +100,31 @@ static void crash_get_current_regs(struct pt_regs *regs) ...@@ -100,12 +100,31 @@ static void crash_get_current_regs(struct pt_regs *regs)
regs->eip = (unsigned long)current_text_addr(); 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; struct pt_regs regs;
int cpu; int cpu;
cpu = smp_processor_id(); cpu = smp_processor_id();
crash_get_current_regs(&regs);
if (saved_regs)
crash_setup_regs(&regs, saved_regs);
else
crash_get_current_regs(&regs);
crash_save_this_cpu(&regs, cpu); crash_save_this_cpu(&regs, cpu);
} }
...@@ -124,15 +143,8 @@ static int crash_nmi_callback(struct pt_regs *regs, int cpu) ...@@ -124,15 +143,8 @@ static int crash_nmi_callback(struct pt_regs *regs, int cpu)
return 1; return 1;
local_irq_disable(); 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)) { if (!user_mode(regs)) {
memcpy(&fixed_regs, regs, sizeof(*regs)); crash_setup_regs(&fixed_regs, regs);
fixed_regs.esp = (unsigned long)&(regs->esp);
__asm__ __volatile__("xorl %eax, %eax;");
__asm__ __volatile__ ("movw %%ss, %%ax;" :"=a"(fixed_regs.xss));
regs = &fixed_regs; regs = &fixed_regs;
} }
crash_save_this_cpu(regs, cpu); crash_save_this_cpu(regs, cpu);
...@@ -184,7 +196,7 @@ static void nmi_shootdown_cpus(void) ...@@ -184,7 +196,7 @@ static void nmi_shootdown_cpus(void)
} }
#endif #endif
void machine_crash_shutdown(void) void machine_crash_shutdown(struct pt_regs *regs)
{ {
/* This function is only called after the system /* This function is only called after the system
* has paniced or is otherwise in a critical state. * has paniced or is otherwise in a critical state.
...@@ -204,5 +216,5 @@ void machine_crash_shutdown(void) ...@@ -204,5 +216,5 @@ void machine_crash_shutdown(void)
#if defined(CONFIG_X86_IO_APIC) #if defined(CONFIG_X86_IO_APIC)
disable_IO_APIC(); disable_IO_APIC();
#endif #endif
crash_save_self(); crash_save_self(regs);
} }
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <linux/ptrace.h> #include <linux/ptrace.h>
#include <linux/utsname.h> #include <linux/utsname.h>
#include <linux/kprobes.h> #include <linux/kprobes.h>
#include <linux/kexec.h>
#ifdef CONFIG_EISA #ifdef CONFIG_EISA
#include <linux/ioport.h> #include <linux/ioport.h>
...@@ -294,6 +295,9 @@ static void handle_BUG(struct pt_regs *regs) ...@@ -294,6 +295,9 @@ static void handle_BUG(struct pt_regs *regs)
printk("Kernel BUG\n"); 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) void die(const char * str, struct pt_regs * regs, long err)
{ {
static struct { static struct {
...@@ -341,6 +345,10 @@ void die(const char * str, struct pt_regs * regs, long err) ...@@ -341,6 +345,10 @@ void die(const char * str, struct pt_regs * regs, long err)
bust_spinlocks(0); bust_spinlocks(0);
die.lock_owner = -1; die.lock_owner = -1;
spin_unlock_irq(&die.lock); spin_unlock_irq(&die.lock);
if (kexec_should_crash(current))
crash_kexec(regs);
if (in_interrupt()) if (in_interrupt())
panic("Fatal exception in interrupt"); panic("Fatal exception in interrupt");
...@@ -570,6 +578,15 @@ void die_nmi (struct pt_regs *regs, const char *msg) ...@@ -570,6 +578,15 @@ void die_nmi (struct pt_regs *regs, const char *msg)
console_silent(); console_silent();
spin_unlock(&nmi_print_lock); spin_unlock(&nmi_print_lock);
bust_spinlocks(0); 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); do_exit(SIGSEGV);
} }
......
...@@ -34,7 +34,7 @@ void machine_shutdown(void) ...@@ -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) { if (ppc_md.machine_crash_shutdown) {
ppc_md.machine_crash_shutdown(); ppc_md.machine_crash_shutdown();
......
...@@ -34,7 +34,7 @@ note_buf_t crash_notes[NR_CPUS]; ...@@ -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 * and if what it will achieve. Letting it be now to compile the code
* in generic kexec environment * in generic kexec environment
*/ */
void machine_crash_shutdown(void) void machine_crash_shutdown(struct pt_regs *regs)
{ {
/* do nothing right now */ /* do nothing right now */
/* smp_relase_cpus() if we want smp on panic kernel */ /* smp_relase_cpus() if we want smp on panic kernel */
......
...@@ -12,6 +12,6 @@ ...@@ -12,6 +12,6 @@
note_buf_t crash_notes[NR_CPUS]; note_buf_t crash_notes[NR_CPUS];
void machine_crash_shutdown(void) void machine_crash_shutdown(struct pt_regs *regs)
{ {
} }
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
note_buf_t crash_notes[NR_CPUS]; 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 /* This function is only called after the system
* has paniced or is otherwise in a critical state. * has paniced or is otherwise in a critical state.
......
...@@ -100,7 +100,7 @@ static struct sysrq_key_op sysrq_unraw_op = { ...@@ -100,7 +100,7 @@ static struct sysrq_key_op sysrq_unraw_op = {
static void sysrq_handle_crashdump(int key, struct pt_regs *pt_regs, static void sysrq_handle_crashdump(int key, struct pt_regs *pt_regs,
struct tty_struct *tty) struct tty_struct *tty)
{ {
crash_kexec(); crash_kexec(pt_regs);
} }
static struct sysrq_key_op sysrq_crashdump_op = { static struct sysrq_key_op sysrq_crashdump_op = {
.handler = sysrq_handle_crashdump, .handler = sysrq_handle_crashdump,
......
...@@ -99,7 +99,8 @@ extern asmlinkage long compat_sys_kexec_load(unsigned long entry, ...@@ -99,7 +99,8 @@ extern asmlinkage long compat_sys_kexec_load(unsigned long entry,
unsigned long flags); unsigned long flags);
#endif #endif
extern struct page *kimage_alloc_control_pages(struct kimage *image, unsigned int order); 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; extern struct kimage *kexec_image;
#define KEXEC_ON_CRASH 0x00000001 #define KEXEC_ON_CRASH 0x00000001
...@@ -123,6 +124,9 @@ extern struct kimage *kexec_image; ...@@ -123,6 +124,9 @@ extern struct kimage *kexec_image;
extern struct resource crashk_res; extern struct resource crashk_res;
#else /* !CONFIG_KEXEC */ #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 /* CONFIG_KEXEC */
#endif /* LINUX_KEXEC_H */ #endif /* LINUX_KEXEC_H */
...@@ -52,7 +52,8 @@ extern void machine_halt(void); ...@@ -52,7 +52,8 @@ extern void machine_halt(void);
extern void machine_power_off(void); extern void machine_power_off(void);
extern void machine_shutdown(void); extern void machine_shutdown(void);
extern void machine_crash_shutdown(void); struct pt_regs;
extern void machine_crash_shutdown(struct pt_regs *);
#endif #endif
......
...@@ -18,6 +18,8 @@ ...@@ -18,6 +18,8 @@
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/syscalls.h> #include <linux/syscalls.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/hardirq.h>
#include <asm/page.h> #include <asm/page.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/io.h> #include <asm/io.h>
...@@ -32,6 +34,13 @@ struct resource crashk_res = { ...@@ -32,6 +34,13 @@ struct resource crashk_res = {
.flags = IORESOURCE_BUSY | IORESOURCE_MEM .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 * When kexec transitions to the new kernel there is a one-to-one
* mapping between physical and virtual addresses. On processors * mapping between physical and virtual addresses. On processors
...@@ -1010,7 +1019,7 @@ asmlinkage long compat_sys_kexec_load(unsigned long entry, ...@@ -1010,7 +1019,7 @@ asmlinkage long compat_sys_kexec_load(unsigned long entry,
} }
#endif #endif
void crash_kexec(void) void crash_kexec(struct pt_regs *regs)
{ {
struct kimage *image; struct kimage *image;
int locked; int locked;
...@@ -1028,7 +1037,7 @@ void crash_kexec(void) ...@@ -1028,7 +1037,7 @@ void crash_kexec(void)
if (!locked) { if (!locked) {
image = xchg(&kexec_crash_image, NULL); image = xchg(&kexec_crash_image, NULL);
if (image) { if (image) {
machine_crash_shutdown(); machine_crash_shutdown(regs);
machine_kexec(image); machine_kexec(image);
} }
xchg(&kexec_lock, 0); xchg(&kexec_lock, 0);
......
...@@ -83,7 +83,7 @@ NORET_TYPE void panic(const char * fmt, ...) ...@@ -83,7 +83,7 @@ NORET_TYPE void panic(const char * fmt, ...)
* everything else. * everything else.
* Do we want to call this before we try to display a message? * Do we want to call this before we try to display a message?
*/ */
crash_kexec(); crash_kexec(NULL);
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment