aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kernel
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
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')
-rw-r--r--arch/powerpc/kernel/Makefile2
-rw-r--r--arch/powerpc/kernel/crash.c264
-rw-r--r--arch/powerpc/kernel/crash_dump.c20
-rw-r--r--arch/powerpc/kernel/machine_kexec_64.c13
-rw-r--r--arch/powerpc/kernel/smp.c22
-rw-r--r--arch/powerpc/kernel/traps.c17
6 files changed, 331 insertions, 7 deletions
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index 5719248d344d..5bdc5faac713 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -66,7 +66,7 @@ pci64-$(CONFIG_PPC64) += pci_64.o pci_dn.o pci_iommu.o \
66obj-$(CONFIG_PCI) += $(pci64-y) 66obj-$(CONFIG_PCI) += $(pci64-y)
67kexec-$(CONFIG_PPC64) := machine_kexec_64.o 67kexec-$(CONFIG_PPC64) := machine_kexec_64.o
68kexec-$(CONFIG_PPC32) := machine_kexec_32.o 68kexec-$(CONFIG_PPC32) := machine_kexec_32.o
69obj-$(CONFIG_KEXEC) += machine_kexec.o $(kexec-y) 69obj-$(CONFIG_KEXEC) += machine_kexec.o crash.o $(kexec-y)
70 70
71ifeq ($(CONFIG_PPC_ISERIES),y) 71ifeq ($(CONFIG_PPC_ISERIES),y)
72$(obj)/head_64.o: $(obj)/lparmap.s 72$(obj)/head_64.o: $(obj)/lparmap.s
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}
diff --git a/arch/powerpc/kernel/crash_dump.c b/arch/powerpc/kernel/crash_dump.c
index 63919bcfc9fe..5337ab759780 100644
--- a/arch/powerpc/kernel/crash_dump.c
+++ b/arch/powerpc/kernel/crash_dump.c
@@ -11,6 +11,8 @@
11 11
12#undef DEBUG 12#undef DEBUG
13 13
14#include <linux/crash_dump.h>
15#include <linux/bootmem.h>
14#include <asm/kdump.h> 16#include <asm/kdump.h>
15#include <asm/lmb.h> 17#include <asm/lmb.h>
16#include <asm/firmware.h> 18#include <asm/firmware.h>
@@ -51,3 +53,21 @@ void __init kdump_setup(void)
51 53
52 DBG(" <- kdump_setup()\n"); 54 DBG(" <- kdump_setup()\n");
53} 55}
56
57static int __init parse_elfcorehdr(char *p)
58{
59 if (p)
60 elfcorehdr_addr = memparse(p, &p);
61
62 return 0;
63}
64__setup("elfcorehdr=", parse_elfcorehdr);
65
66static int __init parse_savemaxmem(char *p)
67{
68 if (p)
69 saved_max_pfn = (memparse(p, &p) >> PAGE_SHIFT) - 1;
70
71 return 0;
72}
73__setup("savemaxmem=", parse_savemaxmem);
diff --git a/arch/powerpc/kernel/machine_kexec_64.c b/arch/powerpc/kernel/machine_kexec_64.c
index 0b0fa4768995..d6431440c54f 100644
--- a/arch/powerpc/kernel/machine_kexec_64.c
+++ b/arch/powerpc/kernel/machine_kexec_64.c
@@ -265,11 +265,18 @@ extern NORET_TYPE void kexec_sequence(void *newstack, unsigned long start,
265/* too late to fail here */ 265/* too late to fail here */
266void default_machine_kexec(struct kimage *image) 266void default_machine_kexec(struct kimage *image)
267{ 267{
268
269 /* prepare control code if any */ 268 /* prepare control code if any */
270 269
271 /* shutdown other cpus into our wait loop and quiesce interrupts */ 270 /*
272 kexec_prepare_cpus(); 271 * If the kexec boot is the normal one, need to shutdown other cpus
272 * into our wait loop and quiesce interrupts.
273 * Otherwise, in the case of crashed mode (crashing_cpu >= 0),
274 * stopping other CPUs and collecting their pt_regs is done before
275 * using debugger IPI.
276 */
277
278 if (crashing_cpu == -1)
279 kexec_prepare_cpus();
273 280
274 /* switch to a staticly allocated stack. Based on irq stack code. 281 /* switch to a staticly allocated stack. Based on irq stack code.
275 * XXX: the task struct will likely be invalid once we do the copy! 282 * XXX: the task struct will likely be invalid once we do the copy!
diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c
index a90df6bf0940..8e3ca674d359 100644
--- a/arch/powerpc/kernel/smp.c
+++ b/arch/powerpc/kernel/smp.c
@@ -75,6 +75,8 @@ void smp_call_function_interrupt(void);
75 75
76int smt_enabled_at_boot = 1; 76int smt_enabled_at_boot = 1;
77 77
78static void (*crash_ipi_function_ptr)(struct pt_regs *) = NULL;
79
78#ifdef CONFIG_MPIC 80#ifdef CONFIG_MPIC
79int __init smp_mpic_probe(void) 81int __init smp_mpic_probe(void)
80{ 82{
@@ -123,11 +125,16 @@ void smp_message_recv(int msg, struct pt_regs *regs)
123 /* XXX Do we have to do this? */ 125 /* XXX Do we have to do this? */
124 set_need_resched(); 126 set_need_resched();
125 break; 127 break;
126#ifdef CONFIG_DEBUGGER
127 case PPC_MSG_DEBUGGER_BREAK: 128 case PPC_MSG_DEBUGGER_BREAK:
129 if (crash_ipi_function_ptr) {
130 crash_ipi_function_ptr(regs);
131 break;
132 }
133#ifdef CONFIG_DEBUGGER
128 debugger_ipi(regs); 134 debugger_ipi(regs);
129 break; 135 break;
130#endif 136#endif /* CONFIG_DEBUGGER */
137 /* FALLTHROUGH */
131 default: 138 default:
132 printk("SMP %d: smp_message_recv(): unknown msg %d\n", 139 printk("SMP %d: smp_message_recv(): unknown msg %d\n",
133 smp_processor_id(), msg); 140 smp_processor_id(), msg);
@@ -147,6 +154,17 @@ void smp_send_debugger_break(int cpu)
147} 154}
148#endif 155#endif
149 156
157#ifdef CONFIG_KEXEC
158void crash_send_ipi(void (*crash_ipi_callback)(struct pt_regs *))
159{
160 crash_ipi_function_ptr = crash_ipi_callback;
161 if (crash_ipi_callback) {
162 mb();
163 smp_ops->message_pass(MSG_ALL_BUT_SELF, PPC_MSG_DEBUGGER_BREAK);
164 }
165}
166#endif
167
150static void stop_this_cpu(void *dummy) 168static void stop_this_cpu(void *dummy)
151{ 169{
152 local_irq_disable(); 170 local_irq_disable();
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
index 1511454c4690..76b579ca5230 100644
--- a/arch/powerpc/kernel/traps.c
+++ b/arch/powerpc/kernel/traps.c
@@ -31,6 +31,7 @@
31#include <linux/prctl.h> 31#include <linux/prctl.h>
32#include <linux/delay.h> 32#include <linux/delay.h>
33#include <linux/kprobes.h> 33#include <linux/kprobes.h>
34#include <linux/kexec.h>
34 35
35#include <asm/kdebug.h> 36#include <asm/kdebug.h>
36#include <asm/pgtable.h> 37#include <asm/pgtable.h>
@@ -95,7 +96,7 @@ static DEFINE_SPINLOCK(die_lock);
95 96
96int die(const char *str, struct pt_regs *regs, long err) 97int die(const char *str, struct pt_regs *regs, long err)
97{ 98{
98 static int die_counter; 99 static int die_counter, crash_dump_start = 0;
99 int nl = 0; 100 int nl = 0;
100 101
101 if (debugger(regs)) 102 if (debugger(regs))
@@ -156,7 +157,21 @@ int die(const char *str, struct pt_regs *regs, long err)
156 print_modules(); 157 print_modules();
157 show_regs(regs); 158 show_regs(regs);
158 bust_spinlocks(0); 159 bust_spinlocks(0);
160
161 if (!crash_dump_start && kexec_should_crash(current)) {
162 crash_dump_start = 1;
163 spin_unlock_irq(&die_lock);
164 crash_kexec(regs);
165 /* NOTREACHED */
166 }
159 spin_unlock_irq(&die_lock); 167 spin_unlock_irq(&die_lock);
168 if (crash_dump_start)
169 /*
170 * Only for soft-reset: Other CPUs will be responded to an IPI
171 * sent by first kexec CPU.
172 */
173 for(;;)
174 ;
160 175
161 if (in_interrupt()) 176 if (in_interrupt())
162 panic("Fatal exception in interrupt"); 177 panic("Fatal exception in interrupt");