aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorMichael Neuling <mikey@neuling.org>2008-01-17 23:50:30 -0500
committerPaul Mackerras <paulus@samba.org>2008-01-25 06:52:50 -0500
commit496b010e1e70a9b4286fa34f19523f24a194f119 (patch)
tree82304fcb3342e1204fe2b2b69c0f396068ad409c /arch
parentc3b75bd7bbf4a0438dc140033b80657995fd30ed (diff)
[POWERPC] kdump shutdown hook support
This adds hooks into the default_machine_crash_shutdown so drivers can register a function to be run in the first kernel before we hand off to the second kernel. This should only be used in exceptional circumstances, like where the device can't be reset in the second kernel alone (as is the case with eHEA). To emphasize this, the number of handles allowed to be registered is currently #def to 1. This uses the setjmp/longjmp code around the call out to the registered hooks, so any bogus exceptions we encounter will hopefully be recoverable. Tested with bogus data and instruction exceptions. Signed-off-by: Michael Neuling <mikey@neuling.org> Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/powerpc/kernel/crash.c101
1 files changed, 96 insertions, 5 deletions
diff --git a/arch/powerpc/kernel/crash.c b/arch/powerpc/kernel/crash.c
index 77c749a13378..571132ed12c1 100644
--- a/arch/powerpc/kernel/crash.c
+++ b/arch/powerpc/kernel/crash.c
@@ -32,6 +32,8 @@
32#include <asm/lmb.h> 32#include <asm/lmb.h>
33#include <asm/firmware.h> 33#include <asm/firmware.h>
34#include <asm/smp.h> 34#include <asm/smp.h>
35#include <asm/system.h>
36#include <asm/setjmp.h>
35 37
36#ifdef DEBUG 38#ifdef DEBUG
37#include <asm/udbg.h> 39#include <asm/udbg.h>
@@ -45,6 +47,11 @@ int crashing_cpu = -1;
45static cpumask_t cpus_in_crash = CPU_MASK_NONE; 47static cpumask_t cpus_in_crash = CPU_MASK_NONE;
46cpumask_t cpus_in_sr = CPU_MASK_NONE; 48cpumask_t cpus_in_sr = CPU_MASK_NONE;
47 49
50#define CRASH_HANDLER_MAX 1
51/* NULL terminated list of shutdown handles */
52static crash_shutdown_t crash_shutdown_handles[CRASH_HANDLER_MAX+1];
53static DEFINE_SPINLOCK(crash_handlers_lock);
54
48#ifdef CONFIG_SMP 55#ifdef CONFIG_SMP
49static atomic_t enter_on_soft_reset = ATOMIC_INIT(0); 56static atomic_t enter_on_soft_reset = ATOMIC_INIT(0);
50 57
@@ -285,9 +292,72 @@ static inline void crash_kexec_stop_spus(void)
285} 292}
286#endif /* CONFIG_SPU_BASE */ 293#endif /* CONFIG_SPU_BASE */
287 294
295/*
296 * Register a function to be called on shutdown. Only use this if you
297 * can't reset your device in the second kernel.
298 */
299int crash_shutdown_register(crash_shutdown_t handler)
300{
301 unsigned int i, rc;
302
303 spin_lock(&crash_handlers_lock);
304 for (i = 0 ; i < CRASH_HANDLER_MAX; i++)
305 if (!crash_shutdown_handles[i]) {
306 /* Insert handle at first empty entry */
307 crash_shutdown_handles[i] = handler;
308 rc = 0;
309 break;
310 }
311
312 if (i == CRASH_HANDLER_MAX) {
313 printk(KERN_ERR "Crash shutdown handles full, "
314 "not registered.\n");
315 rc = 1;
316 }
317
318 spin_unlock(&crash_handlers_lock);
319 return rc;
320}
321EXPORT_SYMBOL(crash_shutdown_register);
322
323int crash_shutdown_unregister(crash_shutdown_t handler)
324{
325 unsigned int i, rc;
326
327 spin_lock(&crash_handlers_lock);
328 for (i = 0 ; i < CRASH_HANDLER_MAX; i++)
329 if (crash_shutdown_handles[i] == handler)
330 break;
331
332 if (i == CRASH_HANDLER_MAX) {
333 printk(KERN_ERR "Crash shutdown handle not found\n");
334 rc = 1;
335 } else {
336 /* Shift handles down */
337 for (; crash_shutdown_handles[i]; i++)
338 crash_shutdown_handles[i] =
339 crash_shutdown_handles[i+1];
340 rc = 0;
341 }
342
343 spin_unlock(&crash_handlers_lock);
344 return rc;
345}
346EXPORT_SYMBOL(crash_shutdown_unregister);
347
348static unsigned long crash_shutdown_buf[JMP_BUF_LEN];
349
350static int handle_fault(struct pt_regs *regs)
351{
352 longjmp(crash_shutdown_buf, 1);
353 return 0;
354}
355
288void default_machine_crash_shutdown(struct pt_regs *regs) 356void default_machine_crash_shutdown(struct pt_regs *regs)
289{ 357{
290 unsigned int irq; 358 unsigned int i;
359 int (*old_handler)(struct pt_regs *regs);
360
291 361
292 /* 362 /*
293 * This function is only called after the system 363 * This function is only called after the system
@@ -301,15 +371,36 @@ void default_machine_crash_shutdown(struct pt_regs *regs)
301 */ 371 */
302 hard_irq_disable(); 372 hard_irq_disable();
303 373
304 for_each_irq(irq) { 374 for_each_irq(i) {
305 struct irq_desc *desc = irq_desc + irq; 375 struct irq_desc *desc = irq_desc + i;
306 376
307 if (desc->status & IRQ_INPROGRESS) 377 if (desc->status & IRQ_INPROGRESS)
308 desc->chip->eoi(irq); 378 desc->chip->eoi(i);
309 379
310 if (!(desc->status & IRQ_DISABLED)) 380 if (!(desc->status & IRQ_DISABLED))
311 desc->chip->disable(irq); 381 desc->chip->disable(i);
382 }
383
384 /*
385 * Call registered shutdown routines savely. Swap out
386 * __debugger_fault_handler, and replace on exit.
387 */
388 old_handler = __debugger_fault_handler;
389 __debugger_fault_handler = handle_fault;
390 for (i = 0; crash_shutdown_handles[i]; i++) {
391 if (setjmp(crash_shutdown_buf) == 0) {
392 /*
393 * Insert syncs and delay to ensure
394 * instructions in the dangerous region don't
395 * leak away from this protected region.
396 */
397 asm volatile("sync; isync");
398 /* dangerous region */
399 crash_shutdown_handles[i]();
400 asm volatile("sync; isync");
401 }
312 } 402 }
403 __debugger_fault_handler = old_handler;
313 404
314 /* 405 /*
315 * Make a note of crashing cpu. Will be used in machine_kexec 406 * Make a note of crashing cpu. Will be used in machine_kexec