diff options
-rw-r--r-- | arch/x86_64/Kconfig | 7 | ||||
-rw-r--r-- | arch/x86_64/kernel/crash.c | 70 | ||||
-rw-r--r-- | include/asm-x86_64/kexec.h | 36 |
3 files changed, 113 insertions, 0 deletions
diff --git a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig index 4f3e925962c3..dd2d116b9ab8 100644 --- a/arch/x86_64/Kconfig +++ b/arch/x86_64/Kconfig | |||
@@ -427,6 +427,13 @@ config KEXEC | |||
427 | support. As of this writing the exact hardware interface is | 427 | support. As of this writing the exact hardware interface is |
428 | strongly in flux, so no good recommendation can be made. | 428 | strongly in flux, so no good recommendation can be made. |
429 | 429 | ||
430 | config CRASH_DUMP | ||
431 | bool "kernel crash dumps (EXPERIMENTAL)" | ||
432 | depends on EMBEDDED | ||
433 | depends on EXPERIMENTAL | ||
434 | help | ||
435 | Generate crash dump after being started by kexec. | ||
436 | |||
430 | config SECCOMP | 437 | config SECCOMP |
431 | bool "Enable seccomp to safely compute untrusted bytecode" | 438 | bool "Enable seccomp to safely compute untrusted bytecode" |
432 | depends on PROC_FS | 439 | depends on PROC_FS |
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 | } |
diff --git a/include/asm-x86_64/kexec.h b/include/asm-x86_64/kexec.h index cea78543a574..ae28cd44bcd3 100644 --- a/include/asm-x86_64/kexec.h +++ b/include/asm-x86_64/kexec.h | |||
@@ -3,6 +3,7 @@ | |||
3 | 3 | ||
4 | #include <asm/page.h> | 4 | #include <asm/page.h> |
5 | #include <asm/proto.h> | 5 | #include <asm/proto.h> |
6 | #include <asm/ptrace.h> | ||
6 | 7 | ||
7 | /* | 8 | /* |
8 | * KEXEC_SOURCE_MEMORY_LIMIT maximum page get_free_page can return. | 9 | * KEXEC_SOURCE_MEMORY_LIMIT maximum page get_free_page can return. |
@@ -27,4 +28,39 @@ | |||
27 | 28 | ||
28 | #define MAX_NOTE_BYTES 1024 | 29 | #define MAX_NOTE_BYTES 1024 |
29 | 30 | ||
31 | /* | ||
32 | * Saving the registers of the cpu on which panic occured in | ||
33 | * crash_kexec to save a valid sp. The registers of other cpus | ||
34 | * will be saved in machine_crash_shutdown while shooting down them. | ||
35 | */ | ||
36 | |||
37 | static inline void crash_setup_regs(struct pt_regs *newregs, | ||
38 | struct pt_regs *oldregs) | ||
39 | { | ||
40 | if (oldregs) | ||
41 | memcpy(newregs, oldregs, sizeof(*newregs)); | ||
42 | else { | ||
43 | __asm__ __volatile__("movq %%rbx,%0" : "=m"(newregs->rbx)); | ||
44 | __asm__ __volatile__("movq %%rcx,%0" : "=m"(newregs->rcx)); | ||
45 | __asm__ __volatile__("movq %%rdx,%0" : "=m"(newregs->rdx)); | ||
46 | __asm__ __volatile__("movq %%rsi,%0" : "=m"(newregs->rsi)); | ||
47 | __asm__ __volatile__("movq %%rdi,%0" : "=m"(newregs->rdi)); | ||
48 | __asm__ __volatile__("movq %%rbp,%0" : "=m"(newregs->rbp)); | ||
49 | __asm__ __volatile__("movq %%rax,%0" : "=m"(newregs->rax)); | ||
50 | __asm__ __volatile__("movq %%rsp,%0" : "=m"(newregs->rsp)); | ||
51 | __asm__ __volatile__("movq %%r8,%0" : "=m"(newregs->r8)); | ||
52 | __asm__ __volatile__("movq %%r9,%0" : "=m"(newregs->r9)); | ||
53 | __asm__ __volatile__("movq %%r10,%0" : "=m"(newregs->r10)); | ||
54 | __asm__ __volatile__("movq %%r11,%0" : "=m"(newregs->r11)); | ||
55 | __asm__ __volatile__("movq %%r12,%0" : "=m"(newregs->r12)); | ||
56 | __asm__ __volatile__("movq %%r13,%0" : "=m"(newregs->r13)); | ||
57 | __asm__ __volatile__("movq %%r14,%0" : "=m"(newregs->r14)); | ||
58 | __asm__ __volatile__("movq %%r15,%0" : "=m"(newregs->r15)); | ||
59 | __asm__ __volatile__("movl %%ss, %%eax;" :"=a"(newregs->ss)); | ||
60 | __asm__ __volatile__("movl %%cs, %%eax;" :"=a"(newregs->cs)); | ||
61 | __asm__ __volatile__("pushfq; popq %0" :"=m"(newregs->eflags)); | ||
62 | |||
63 | newregs->rip = (unsigned long)current_text_addr(); | ||
64 | } | ||
65 | } | ||
30 | #endif /* _X86_64_KEXEC_H */ | 66 | #endif /* _X86_64_KEXEC_H */ |