diff options
Diffstat (limited to 'arch/x86_64/kernel/crash.c')
-rw-r--r-- | arch/x86_64/kernel/crash.c | 70 |
1 files changed, 70 insertions, 0 deletions
diff --git a/arch/x86_64/kernel/crash.c b/arch/x86_64/kernel/crash.c index abc601f36b6e..4e6c3b729e39 100644 --- a/arch/x86_64/kernel/crash.c +++ b/arch/x86_64/kernel/crash.c | |||
@@ -11,9 +11,12 @@ | |||
11 | #include <linux/types.h> | 11 | #include <linux/types.h> |
12 | #include <linux/kernel.h> | 12 | #include <linux/kernel.h> |
13 | #include <linux/smp.h> | 13 | #include <linux/smp.h> |
14 | #include <linux/irq.h> | ||
14 | #include <linux/reboot.h> | 15 | #include <linux/reboot.h> |
15 | #include <linux/kexec.h> | 16 | #include <linux/kexec.h> |
16 | #include <linux/delay.h> | 17 | #include <linux/delay.h> |
18 | #include <linux/elf.h> | ||
19 | #include <linux/elfcore.h> | ||
17 | 20 | ||
18 | #include <asm/processor.h> | 21 | #include <asm/processor.h> |
19 | #include <asm/hardirq.h> | 22 | #include <asm/hardirq.h> |
@@ -24,6 +27,71 @@ | |||
24 | /* This keeps a track of which one is crashing cpu. */ | 27 | /* This keeps a track of which one is crashing cpu. */ |
25 | static int crashing_cpu; | 28 | static int crashing_cpu; |
26 | 29 | ||
30 | static u32 *append_elf_note(u32 *buf, char *name, unsigned type, | ||
31 | void *data, size_t data_len) | ||
32 | { | ||
33 | struct elf_note note; | ||
34 | |||
35 | note.n_namesz = strlen(name) + 1; | ||
36 | note.n_descsz = data_len; | ||
37 | note.n_type = type; | ||
38 | memcpy(buf, ¬e, sizeof(note)); | ||
39 | buf += (sizeof(note) +3)/4; | ||
40 | memcpy(buf, name, note.n_namesz); | ||
41 | buf += (note.n_namesz + 3)/4; | ||
42 | memcpy(buf, data, note.n_descsz); | ||
43 | buf += (note.n_descsz + 3)/4; | ||
44 | |||
45 | return buf; | ||
46 | } | ||
47 | |||
48 | static void final_note(u32 *buf) | ||
49 | { | ||
50 | struct elf_note note; | ||
51 | |||
52 | note.n_namesz = 0; | ||
53 | note.n_descsz = 0; | ||
54 | note.n_type = 0; | ||
55 | memcpy(buf, ¬e, sizeof(note)); | ||
56 | } | ||
57 | |||
58 | static void crash_save_this_cpu(struct pt_regs *regs, int cpu) | ||
59 | { | ||
60 | struct elf_prstatus prstatus; | ||
61 | u32 *buf; | ||
62 | |||
63 | if ((cpu < 0) || (cpu >= NR_CPUS)) | ||
64 | return; | ||
65 | |||
66 | /* Using ELF notes here is opportunistic. | ||
67 | * I need a well defined structure format | ||
68 | * for the data I pass, and I need tags | ||
69 | * on the data to indicate what information I have | ||
70 | * squirrelled away. ELF notes happen to provide | ||
71 | * all of that that no need to invent something new. | ||
72 | */ | ||
73 | |||
74 | buf = (u32*)per_cpu_ptr(crash_notes, cpu); | ||
75 | |||
76 | if (!buf) | ||
77 | return; | ||
78 | |||
79 | memset(&prstatus, 0, sizeof(prstatus)); | ||
80 | prstatus.pr_pid = current->pid; | ||
81 | elf_core_copy_regs(&prstatus.pr_reg, regs); | ||
82 | buf = append_elf_note(buf, "CORE", NT_PRSTATUS, &prstatus, | ||
83 | sizeof(prstatus)); | ||
84 | final_note(buf); | ||
85 | } | ||
86 | |||
87 | static void crash_save_self(struct pt_regs *regs) | ||
88 | { | ||
89 | int cpu; | ||
90 | |||
91 | cpu = smp_processor_id(); | ||
92 | crash_save_this_cpu(regs, cpu); | ||
93 | } | ||
94 | |||
27 | #ifdef CONFIG_SMP | 95 | #ifdef CONFIG_SMP |
28 | static atomic_t waiting_for_crash_ipi; | 96 | static atomic_t waiting_for_crash_ipi; |
29 | 97 | ||
@@ -38,6 +106,7 @@ static int crash_nmi_callback(struct pt_regs *regs, int cpu) | |||
38 | return 1; | 106 | return 1; |
39 | local_irq_disable(); | 107 | local_irq_disable(); |
40 | 108 | ||
109 | crash_save_this_cpu(regs, cpu); | ||
41 | disable_local_APIC(); | 110 | disable_local_APIC(); |
42 | atomic_dec(&waiting_for_crash_ipi); | 111 | atomic_dec(&waiting_for_crash_ipi); |
43 | /* Assume hlt works */ | 112 | /* Assume hlt works */ |
@@ -113,4 +182,5 @@ void machine_crash_shutdown(struct pt_regs *regs) | |||
113 | disable_IO_APIC(); | 182 | disable_IO_APIC(); |
114 | #endif | 183 | #endif |
115 | 184 | ||
185 | crash_save_self(regs); | ||
116 | } | 186 | } |