diff options
Diffstat (limited to 'arch/powerpc/kernel/crash.c')
-rw-r--r-- | arch/powerpc/kernel/crash.c | 101 |
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; | |||
45 | static cpumask_t cpus_in_crash = CPU_MASK_NONE; | 47 | static cpumask_t cpus_in_crash = CPU_MASK_NONE; |
46 | cpumask_t cpus_in_sr = CPU_MASK_NONE; | 48 | cpumask_t cpus_in_sr = CPU_MASK_NONE; |
47 | 49 | ||
50 | #define CRASH_HANDLER_MAX 1 | ||
51 | /* NULL terminated list of shutdown handles */ | ||
52 | static crash_shutdown_t crash_shutdown_handles[CRASH_HANDLER_MAX+1]; | ||
53 | static DEFINE_SPINLOCK(crash_handlers_lock); | ||
54 | |||
48 | #ifdef CONFIG_SMP | 55 | #ifdef CONFIG_SMP |
49 | static atomic_t enter_on_soft_reset = ATOMIC_INIT(0); | 56 | static 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 | */ | ||
299 | int 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 | } | ||
321 | EXPORT_SYMBOL(crash_shutdown_register); | ||
322 | |||
323 | int 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 | } | ||
346 | EXPORT_SYMBOL(crash_shutdown_unregister); | ||
347 | |||
348 | static unsigned long crash_shutdown_buf[JMP_BUF_LEN]; | ||
349 | |||
350 | static int handle_fault(struct pt_regs *regs) | ||
351 | { | ||
352 | longjmp(crash_shutdown_buf, 1); | ||
353 | return 0; | ||
354 | } | ||
355 | |||
288 | void default_machine_crash_shutdown(struct pt_regs *regs) | 356 | void 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 |