]>
Commit | Line | Data |
---|---|---|
cc532915 ME |
1 | /* |
2 | * Architecture specific (PPC64) functions for kexec based crash dumps. | |
3 | * | |
4 | * Copyright (C) 2005, IBM Corp. | |
5 | * | |
6 | * Created by: Haren Myneni | |
7 | * | |
8 | * This source code is licensed under the GNU General Public License, | |
9 | * Version 2. See the file COPYING for more details. | |
10 | * | |
11 | */ | |
12 | ||
13 | #undef DEBUG | |
14 | ||
15 | #include <linux/kernel.h> | |
16 | #include <linux/smp.h> | |
17 | #include <linux/reboot.h> | |
18 | #include <linux/kexec.h> | |
19 | #include <linux/bootmem.h> | |
20 | #include <linux/crash_dump.h> | |
cc532915 ME |
21 | #include <linux/delay.h> |
22 | #include <linux/elf.h> | |
23 | #include <linux/elfcore.h> | |
24 | #include <linux/init.h> | |
d6c1a908 | 25 | #include <linux/irq.h> |
cc532915 ME |
26 | #include <linux/types.h> |
27 | ||
28 | #include <asm/processor.h> | |
29 | #include <asm/machdep.h> | |
30 | #include <asm/kdump.h> | |
31 | #include <asm/lmb.h> | |
32 | #include <asm/firmware.h> | |
f6cc82fc | 33 | #include <asm/smp.h> |
cc532915 ME |
34 | |
35 | #ifdef DEBUG | |
36 | #include <asm/udbg.h> | |
37 | #define DBG(fmt...) udbg_printf(fmt) | |
38 | #else | |
39 | #define DBG(fmt...) | |
40 | #endif | |
41 | ||
42 | /* This keeps a track of which one is crashing cpu. */ | |
43 | int crashing_cpu = -1; | |
44 | ||
45 | static u32 *append_elf_note(u32 *buf, char *name, unsigned type, void *data, | |
46 | size_t data_len) | |
47 | { | |
48 | struct elf_note note; | |
49 | ||
50 | note.n_namesz = strlen(name) + 1; | |
51 | note.n_descsz = data_len; | |
52 | note.n_type = type; | |
53 | memcpy(buf, ¬e, sizeof(note)); | |
54 | buf += (sizeof(note) +3)/4; | |
55 | memcpy(buf, name, note.n_namesz); | |
56 | buf += (note.n_namesz + 3)/4; | |
57 | memcpy(buf, data, note.n_descsz); | |
58 | buf += (note.n_descsz + 3)/4; | |
59 | ||
60 | return buf; | |
61 | } | |
62 | ||
63 | static void final_note(u32 *buf) | |
64 | { | |
65 | struct elf_note note; | |
66 | ||
67 | note.n_namesz = 0; | |
68 | note.n_descsz = 0; | |
69 | note.n_type = 0; | |
70 | memcpy(buf, ¬e, sizeof(note)); | |
71 | } | |
72 | ||
73 | static void crash_save_this_cpu(struct pt_regs *regs, int cpu) | |
74 | { | |
75 | struct elf_prstatus prstatus; | |
76 | u32 *buf; | |
77 | ||
78 | if ((cpu < 0) || (cpu >= NR_CPUS)) | |
79 | return; | |
80 | ||
81 | /* Using ELF notes here is opportunistic. | |
82 | * I need a well defined structure format | |
83 | * for the data I pass, and I need tags | |
84 | * on the data to indicate what information I have | |
85 | * squirrelled away. ELF notes happen to provide | |
86 | * all of that that no need to invent something new. | |
87 | */ | |
8385a6a3 HM |
88 | buf = (u32*)per_cpu_ptr(crash_notes, cpu); |
89 | if (!buf) | |
90 | return; | |
91 | ||
cc532915 ME |
92 | memset(&prstatus, 0, sizeof(prstatus)); |
93 | prstatus.pr_pid = current->pid; | |
94 | elf_core_copy_regs(&prstatus.pr_reg, regs); | |
95 | buf = append_elf_note(buf, "CORE", NT_PRSTATUS, &prstatus, | |
96 | sizeof(prstatus)); | |
97 | final_note(buf); | |
98 | } | |
99 | ||
cc532915 ME |
100 | #ifdef CONFIG_SMP |
101 | static atomic_t waiting_for_crash_ipi; | |
102 | ||
103 | void crash_ipi_callback(struct pt_regs *regs) | |
104 | { | |
105 | int cpu = smp_processor_id(); | |
106 | ||
107 | if (cpu == crashing_cpu) | |
108 | return; | |
109 | ||
110 | if (!cpu_online(cpu)) | |
111 | return; | |
112 | ||
113 | if (ppc_md.kexec_cpu_down) | |
114 | ppc_md.kexec_cpu_down(1, 1); | |
115 | ||
116 | local_irq_disable(); | |
117 | ||
118 | crash_save_this_cpu(regs, cpu); | |
119 | atomic_dec(&waiting_for_crash_ipi); | |
120 | kexec_smp_wait(); | |
121 | /* NOTREACHED */ | |
122 | } | |
123 | ||
124 | static void crash_kexec_prepare_cpus(void) | |
125 | { | |
126 | unsigned int msecs; | |
127 | ||
128 | atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1); | |
129 | ||
130 | crash_send_ipi(crash_ipi_callback); | |
131 | smp_wmb(); | |
132 | ||
133 | /* | |
134 | * FIXME: Until we will have the way to stop other CPUSs reliabally, | |
135 | * the crash CPU will send an IPI and wait for other CPUs to | |
136 | * respond. If not, proceed the kexec boot even though we failed to | |
137 | * capture other CPU states. | |
01aaed9d | 138 | * Delay of at least 10 seconds. |
cc532915 | 139 | */ |
01aaed9d HM |
140 | printk(KERN_ALERT "Sending IPI to other cpus...\n"); |
141 | msecs = 10000; | |
cc532915 ME |
142 | while ((atomic_read(&waiting_for_crash_ipi) > 0) && (--msecs > 0)) { |
143 | barrier(); | |
144 | mdelay(1); | |
145 | } | |
146 | ||
147 | /* Would it be better to replace the trap vector here? */ | |
148 | ||
149 | /* | |
150 | * FIXME: In case if we do not get all CPUs, one possibility: ask the | |
151 | * user to do soft reset such that we get all. | |
152 | * IPI handler is already set by the panic cpu initially. Therefore, | |
153 | * all cpus could invoke this handler from die() and the panic CPU | |
154 | * will call machine_kexec() directly from this handler to do | |
155 | * kexec boot. | |
156 | */ | |
157 | if (atomic_read(&waiting_for_crash_ipi)) | |
158 | printk(KERN_ALERT "done waiting: %d cpus not responding\n", | |
159 | atomic_read(&waiting_for_crash_ipi)); | |
160 | /* Leave the IPI callback set */ | |
161 | } | |
162 | #else | |
163 | static void crash_kexec_prepare_cpus(void) | |
164 | { | |
165 | /* | |
166 | * move the secondarys to us so that we can copy | |
167 | * the new kernel 0-0x100 safely | |
168 | * | |
169 | * do this if kexec in setup.c ? | |
170 | */ | |
171 | smp_release_cpus(); | |
172 | } | |
173 | ||
174 | #endif | |
175 | ||
176 | void default_machine_crash_shutdown(struct pt_regs *regs) | |
177 | { | |
d6c1a908 ME |
178 | unsigned int irq; |
179 | ||
cc532915 ME |
180 | /* |
181 | * This function is only called after the system | |
182 | * has paniced or is otherwise in a critical state. | |
183 | * The minimum amount of code to allow a kexec'd kernel | |
184 | * to run successfully needs to happen here. | |
185 | * | |
186 | * In practice this means stopping other cpus in | |
187 | * an SMP system. | |
188 | * The kernel is broken so disable interrupts. | |
189 | */ | |
190 | local_irq_disable(); | |
191 | ||
d6c1a908 ME |
192 | for_each_irq(irq) { |
193 | struct irq_desc *desc = irq_descp(irq); | |
194 | ||
195 | if (desc->status & IRQ_INPROGRESS) | |
196 | desc->handler->end(irq); | |
197 | ||
198 | if (!(desc->status & IRQ_DISABLED)) | |
199 | desc->handler->disable(irq); | |
200 | } | |
201 | ||
cc532915 ME |
202 | if (ppc_md.kexec_cpu_down) |
203 | ppc_md.kexec_cpu_down(1, 0); | |
204 | ||
205 | /* | |
206 | * Make a note of crashing cpu. Will be used in machine_kexec | |
207 | * such that another IPI will not be sent. | |
208 | */ | |
209 | crashing_cpu = smp_processor_id(); | |
210 | crash_kexec_prepare_cpus(); | |
8385a6a3 | 211 | crash_save_this_cpu(regs, crashing_cpu); |
cc532915 | 212 | } |