aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kernel/crash.c
diff options
context:
space:
mode:
authorMichael Ellerman <michael@ellerman.id.au>2005-12-04 02:39:43 -0500
committerPaul Mackerras <paulus@samba.org>2006-01-08 22:52:28 -0500
commitcc53291521701f9c7c7265bbb3c140563174d8b2 (patch)
tree9fa71dbb7d853f983c9033fc629edcd118c67858 /arch/powerpc/kernel/crash.c
parent758438a7b8da593c9116e95cc7fdff6e9e0b0c40 (diff)
[PATCH] powerpc: Add arch dependent basic infrastructure for Kdump.
Implementing the machine_crash_shutdown which will be called by crash_kexec (called in case of a panic, sysrq etc.). Disable the interrupts, shootdown cpus using debugger IPI and collect regs for all CPUs. elfcorehdr= specifies the location of elf core header stored by the crashed kernel. This command line option will be passed by the kexec-tools to capture kernel. savemaxmem= specifies the actual memory size that the first kernel has and this value will be used for dumping in the capture kernel. This command line option will be passed by the kexec-tools to capture kernel. Signed-off-by: Haren Myneni <haren@us.ibm.com> Signed-off-by: Michael Ellerman <michael@ellerman.id.au> Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc/kernel/crash.c')
-rw-r--r--arch/powerpc/kernel/crash.c264
1 files changed, 264 insertions, 0 deletions
diff --git a/arch/powerpc/kernel/crash.c b/arch/powerpc/kernel/crash.c
new file mode 100644
index 000000000000..4681155121ef
--- /dev/null
+++ b/arch/powerpc/kernel/crash.c
@@ -0,0 +1,264 @@
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>
21#include <linux/irq.h>
22#include <linux/delay.h>
23#include <linux/elf.h>
24#include <linux/elfcore.h>
25#include <linux/init.h>
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>
33
34#ifdef DEBUG
35#include <asm/udbg.h>
36#define DBG(fmt...) udbg_printf(fmt)
37#else
38#define DBG(fmt...)
39#endif
40
41/* This keeps a track of which one is crashing cpu. */
42int crashing_cpu = -1;
43
44static u32 *append_elf_note(u32 *buf, char *name, unsigned type, void *data,
45 size_t data_len)
46{
47 struct elf_note note;
48
49 note.n_namesz = strlen(name) + 1;
50 note.n_descsz = data_len;
51 note.n_type = type;
52 memcpy(buf, &note, sizeof(note));
53 buf += (sizeof(note) +3)/4;
54 memcpy(buf, name, note.n_namesz);
55 buf += (note.n_namesz + 3)/4;
56 memcpy(buf, data, note.n_descsz);
57 buf += (note.n_descsz + 3)/4;
58
59 return buf;
60}
61
62static void final_note(u32 *buf)
63{
64 struct elf_note note;
65
66 note.n_namesz = 0;
67 note.n_descsz = 0;
68 note.n_type = 0;
69 memcpy(buf, &note, sizeof(note));
70}
71
72static void crash_save_this_cpu(struct pt_regs *regs, int cpu)
73{
74 struct elf_prstatus prstatus;
75 u32 *buf;
76
77 if ((cpu < 0) || (cpu >= NR_CPUS))
78 return;
79
80 /* Using ELF notes here is opportunistic.
81 * I need a well defined structure format
82 * for the data I pass, and I need tags
83 * on the data to indicate what information I have
84 * squirrelled away. ELF notes happen to provide
85 * all of that that no need to invent something new.
86 */
87 buf = &crash_notes[cpu][0];
88 memset(&prstatus, 0, sizeof(prstatus));
89 prstatus.pr_pid = current->pid;
90 elf_core_copy_regs(&prstatus.pr_reg, regs);
91 buf = append_elf_note(buf, "CORE", NT_PRSTATUS, &prstatus,
92 sizeof(prstatus));
93 final_note(buf);
94}
95
96/* FIXME Merge this with xmon_save_regs ?? */
97static inline void crash_get_current_regs(struct pt_regs *regs)
98{
99 unsigned long tmp1, tmp2;
100
101 __asm__ __volatile__ (
102 "std 0,0(%2)\n"
103 "std 1,8(%2)\n"
104 "std 2,16(%2)\n"
105 "std 3,24(%2)\n"
106 "std 4,32(%2)\n"
107 "std 5,40(%2)\n"
108 "std 6,48(%2)\n"
109 "std 7,56(%2)\n"
110 "std 8,64(%2)\n"
111 "std 9,72(%2)\n"
112 "std 10,80(%2)\n"
113 "std 11,88(%2)\n"
114 "std 12,96(%2)\n"
115 "std 13,104(%2)\n"
116 "std 14,112(%2)\n"
117 "std 15,120(%2)\n"
118 "std 16,128(%2)\n"
119 "std 17,136(%2)\n"
120 "std 18,144(%2)\n"
121 "std 19,152(%2)\n"
122 "std 20,160(%2)\n"
123 "std 21,168(%2)\n"
124 "std 22,176(%2)\n"
125 "std 23,184(%2)\n"
126 "std 24,192(%2)\n"
127 "std 25,200(%2)\n"
128 "std 26,208(%2)\n"
129 "std 27,216(%2)\n"
130 "std 28,224(%2)\n"
131 "std 29,232(%2)\n"
132 "std 30,240(%2)\n"
133 "std 31,248(%2)\n"
134 "mfmsr %0\n"
135 "std %0, 264(%2)\n"
136 "mfctr %0\n"
137 "std %0, 280(%2)\n"
138 "mflr %0\n"
139 "std %0, 288(%2)\n"
140 "bl 1f\n"
141 "1: mflr %1\n"
142 "std %1, 256(%2)\n"
143 "mtlr %0\n"
144 "mfxer %0\n"
145 "std %0, 296(%2)\n"
146 : "=&r" (tmp1), "=&r" (tmp2)
147 : "b" (regs));
148}
149
150/* We may have saved_regs from where the error came from
151 * or it is NULL if via a direct panic().
152 */
153static void crash_save_self(struct pt_regs *saved_regs)
154{
155 struct pt_regs regs;
156 int cpu;
157
158 cpu = smp_processor_id();
159 if (saved_regs)
160 memcpy(&regs, saved_regs, sizeof(regs));
161 else
162 crash_get_current_regs(&regs);
163 crash_save_this_cpu(&regs, cpu);
164}
165
166#ifdef CONFIG_SMP
167static atomic_t waiting_for_crash_ipi;
168
169void crash_ipi_callback(struct pt_regs *regs)
170{
171 int cpu = smp_processor_id();
172
173 if (cpu == crashing_cpu)
174 return;
175
176 if (!cpu_online(cpu))
177 return;
178
179 if (ppc_md.kexec_cpu_down)
180 ppc_md.kexec_cpu_down(1, 1);
181
182 local_irq_disable();
183
184 crash_save_this_cpu(regs, cpu);
185 atomic_dec(&waiting_for_crash_ipi);
186 kexec_smp_wait();
187 /* NOTREACHED */
188}
189
190static void crash_kexec_prepare_cpus(void)
191{
192 unsigned int msecs;
193
194 atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1);
195
196 crash_send_ipi(crash_ipi_callback);
197 smp_wmb();
198
199 /*
200 * FIXME: Until we will have the way to stop other CPUSs reliabally,
201 * the crash CPU will send an IPI and wait for other CPUs to
202 * respond. If not, proceed the kexec boot even though we failed to
203 * capture other CPU states.
204 */
205 msecs = 1000000;
206 while ((atomic_read(&waiting_for_crash_ipi) > 0) && (--msecs > 0)) {
207 barrier();
208 mdelay(1);
209 }
210
211 /* Would it be better to replace the trap vector here? */
212
213 /*
214 * FIXME: In case if we do not get all CPUs, one possibility: ask the
215 * user to do soft reset such that we get all.
216 * IPI handler is already set by the panic cpu initially. Therefore,
217 * all cpus could invoke this handler from die() and the panic CPU
218 * will call machine_kexec() directly from this handler to do
219 * kexec boot.
220 */
221 if (atomic_read(&waiting_for_crash_ipi))
222 printk(KERN_ALERT "done waiting: %d cpus not responding\n",
223 atomic_read(&waiting_for_crash_ipi));
224 /* Leave the IPI callback set */
225}
226#else
227static void crash_kexec_prepare_cpus(void)
228{
229 /*
230 * move the secondarys to us so that we can copy
231 * the new kernel 0-0x100 safely
232 *
233 * do this if kexec in setup.c ?
234 */
235 smp_release_cpus();
236}
237
238#endif
239
240void default_machine_crash_shutdown(struct pt_regs *regs)
241{
242 /*
243 * This function is only called after the system
244 * has paniced or is otherwise in a critical state.
245 * The minimum amount of code to allow a kexec'd kernel
246 * to run successfully needs to happen here.
247 *
248 * In practice this means stopping other cpus in
249 * an SMP system.
250 * The kernel is broken so disable interrupts.
251 */
252 local_irq_disable();
253
254 if (ppc_md.kexec_cpu_down)
255 ppc_md.kexec_cpu_down(1, 0);
256
257 /*
258 * Make a note of crashing cpu. Will be used in machine_kexec
259 * such that another IPI will not be sent.
260 */
261 crashing_cpu = smp_processor_id();
262 crash_kexec_prepare_cpus();
263 crash_save_self(regs);
264}