diff options
author | Borislav Petkov <bp@suse.de> | 2013-05-09 06:02:29 -0400 |
---|---|---|
committer | H. Peter Anvin <hpa@linux.intel.com> | 2013-05-13 16:42:44 -0400 |
commit | 4d067d8e056d76a3327f0517c7722db55e7888fc (patch) | |
tree | 995c147a839866f5d1c2c700c27214755054cd7d /arch/x86/kernel/doublefault.c | |
parent | f722406faae2d073cc1d01063d1123c35425939e (diff) |
x86: Extend #DF debugging aid to 64-bit
It is sometimes very helpful to be able to pinpoint the location which
causes a double fault before it turns into a triple fault and the
machine reboots. We have this for 32-bit already so extend it to 64-bit.
On 64-bit we get the register snapshot at #DF time and not from the
first exception which actually causes the #DF. It should be close
enough, though.
[ hpa: and definitely better than nothing, which is what we have now. ]
Signed-off-by: Borislav Petkov <bp@suse.de>
Link: http://lkml.kernel.org/r/1368093749-31296-1-git-send-email-bp@alien8.de
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
Diffstat (limited to 'arch/x86/kernel/doublefault.c')
-rw-r--r-- | arch/x86/kernel/doublefault.c | 84 |
1 files changed, 84 insertions, 0 deletions
diff --git a/arch/x86/kernel/doublefault.c b/arch/x86/kernel/doublefault.c new file mode 100644 index 000000000000..5d3fe8d36e4a --- /dev/null +++ b/arch/x86/kernel/doublefault.c | |||
@@ -0,0 +1,84 @@ | |||
1 | #include <linux/mm.h> | ||
2 | #include <linux/sched.h> | ||
3 | #include <linux/init.h> | ||
4 | #include <linux/init_task.h> | ||
5 | #include <linux/fs.h> | ||
6 | |||
7 | #include <asm/uaccess.h> | ||
8 | #include <asm/pgtable.h> | ||
9 | #include <asm/processor.h> | ||
10 | #include <asm/desc.h> | ||
11 | |||
12 | #ifdef CONFIG_X86_32 | ||
13 | |||
14 | #define DOUBLEFAULT_STACKSIZE (1024) | ||
15 | static unsigned long doublefault_stack[DOUBLEFAULT_STACKSIZE]; | ||
16 | #define STACK_START (unsigned long)(doublefault_stack+DOUBLEFAULT_STACKSIZE) | ||
17 | |||
18 | #define ptr_ok(x) ((x) > PAGE_OFFSET && (x) < PAGE_OFFSET + MAXMEM) | ||
19 | |||
20 | static void doublefault_fn(void) | ||
21 | { | ||
22 | struct desc_ptr gdt_desc = {0, 0}; | ||
23 | unsigned long gdt, tss; | ||
24 | |||
25 | native_store_gdt(&gdt_desc); | ||
26 | gdt = gdt_desc.address; | ||
27 | |||
28 | printk(KERN_EMERG "PANIC: double fault, gdt at %08lx [%d bytes]\n", gdt, gdt_desc.size); | ||
29 | |||
30 | if (ptr_ok(gdt)) { | ||
31 | gdt += GDT_ENTRY_TSS << 3; | ||
32 | tss = get_desc_base((struct desc_struct *)gdt); | ||
33 | printk(KERN_EMERG "double fault, tss at %08lx\n", tss); | ||
34 | |||
35 | if (ptr_ok(tss)) { | ||
36 | struct x86_hw_tss *t = (struct x86_hw_tss *)tss; | ||
37 | |||
38 | printk(KERN_EMERG "eip = %08lx, esp = %08lx\n", | ||
39 | t->ip, t->sp); | ||
40 | |||
41 | printk(KERN_EMERG "eax = %08lx, ebx = %08lx, ecx = %08lx, edx = %08lx\n", | ||
42 | t->ax, t->bx, t->cx, t->dx); | ||
43 | printk(KERN_EMERG "esi = %08lx, edi = %08lx\n", | ||
44 | t->si, t->di); | ||
45 | } | ||
46 | } | ||
47 | |||
48 | for (;;) | ||
49 | cpu_relax(); | ||
50 | } | ||
51 | |||
52 | struct tss_struct doublefault_tss __cacheline_aligned = { | ||
53 | .x86_tss = { | ||
54 | .sp0 = STACK_START, | ||
55 | .ss0 = __KERNEL_DS, | ||
56 | .ldt = 0, | ||
57 | .io_bitmap_base = INVALID_IO_BITMAP_OFFSET, | ||
58 | |||
59 | .ip = (unsigned long) doublefault_fn, | ||
60 | /* 0x2 bit is always set */ | ||
61 | .flags = X86_EFLAGS_SF | 0x2, | ||
62 | .sp = STACK_START, | ||
63 | .es = __USER_DS, | ||
64 | .cs = __KERNEL_CS, | ||
65 | .ss = __KERNEL_DS, | ||
66 | .ds = __USER_DS, | ||
67 | .fs = __KERNEL_PERCPU, | ||
68 | |||
69 | .__cr3 = __pa_nodebug(swapper_pg_dir), | ||
70 | } | ||
71 | }; | ||
72 | |||
73 | /* dummy for do_double_fault() call */ | ||
74 | void df_debug(struct pt_regs *regs, long error_code) {} | ||
75 | |||
76 | #else /* !CONFIG_X86_32 */ | ||
77 | |||
78 | void df_debug(struct pt_regs *regs, long error_code) | ||
79 | { | ||
80 | pr_emerg("PANIC: double fault, error_code: 0x%lx\n", error_code); | ||
81 | show_regs(regs); | ||
82 | panic("Machine halted."); | ||
83 | } | ||
84 | #endif | ||