diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2007-12-20 23:39:21 -0500 |
---|---|---|
committer | Josh Boyer <jwboyer@linux.vnet.ibm.com> | 2007-12-23 14:11:59 -0500 |
commit | 47c0bd1ae24c34e851cf0f2b02ef2a6847d7ae15 (patch) | |
tree | 86fab68618a4afa03660cc576c9e7da3e5a0b520 /arch/powerpc/kernel/traps.c | |
parent | c2a7dcad9f0d92d7a96e735abb8bec7b9c621536 (diff) |
[POWERPC] Reworking machine check handling and Fix 440/440A
This adds a cputable function pointer for the CPU-side machine
check handling. The semantic is still the same as the old one,
the one in ppc_md. overrides the one in cputable, though
ultimately we'll want to change that so the CPU gets first.
This removes CONFIG_440A which was a problem for multiplatform
kernels and instead fixes up the IVOR at runtime from a setup_cpu
function. The "A" version of the machine check also tweaks the
regs->trap value to differenciate the 2 versions at the C level.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Josh Boyer <jwboyer@linux.vnet.ibm.com>
Diffstat (limited to 'arch/powerpc/kernel/traps.c')
-rw-r--r-- | arch/powerpc/kernel/traps.c | 62 |
1 files changed, 49 insertions, 13 deletions
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index cad64840fce4..848a20475db8 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c | |||
@@ -334,18 +334,25 @@ static inline int check_io_access(struct pt_regs *regs) | |||
334 | #define clear_single_step(regs) ((regs)->msr &= ~MSR_SE) | 334 | #define clear_single_step(regs) ((regs)->msr &= ~MSR_SE) |
335 | #endif | 335 | #endif |
336 | 336 | ||
337 | static int generic_machine_check_exception(struct pt_regs *regs) | 337 | #if defined(CONFIG_4xx) |
338 | int machine_check_4xx(struct pt_regs *regs) | ||
338 | { | 339 | { |
339 | unsigned long reason = get_mc_reason(regs); | 340 | unsigned long reason = get_mc_reason(regs); |
340 | 341 | ||
341 | #if defined(CONFIG_4xx) && !defined(CONFIG_440A) | ||
342 | if (reason & ESR_IMCP) { | 342 | if (reason & ESR_IMCP) { |
343 | printk("Instruction"); | 343 | printk("Instruction"); |
344 | mtspr(SPRN_ESR, reason & ~ESR_IMCP); | 344 | mtspr(SPRN_ESR, reason & ~ESR_IMCP); |
345 | } else | 345 | } else |
346 | printk("Data"); | 346 | printk("Data"); |
347 | printk(" machine check in kernel mode.\n"); | 347 | printk(" machine check in kernel mode.\n"); |
348 | #elif defined(CONFIG_440A) | 348 | |
349 | return 0; | ||
350 | } | ||
351 | |||
352 | int machine_check_440A(struct pt_regs *regs) | ||
353 | { | ||
354 | unsigned long reason = get_mc_reason(regs); | ||
355 | |||
349 | printk("Machine check in kernel mode.\n"); | 356 | printk("Machine check in kernel mode.\n"); |
350 | if (reason & ESR_IMCP){ | 357 | if (reason & ESR_IMCP){ |
351 | printk("Instruction Synchronous Machine Check exception\n"); | 358 | printk("Instruction Synchronous Machine Check exception\n"); |
@@ -375,7 +382,13 @@ static int generic_machine_check_exception(struct pt_regs *regs) | |||
375 | /* Clear MCSR */ | 382 | /* Clear MCSR */ |
376 | mtspr(SPRN_MCSR, mcsr); | 383 | mtspr(SPRN_MCSR, mcsr); |
377 | } | 384 | } |
378 | #elif defined (CONFIG_E500) | 385 | return 0; |
386 | } | ||
387 | #elif defined(CONFIG_E500) | ||
388 | int machine_check_e500(struct pt_regs *regs) | ||
389 | { | ||
390 | unsigned long reason = get_mc_reason(regs); | ||
391 | |||
379 | printk("Machine check in kernel mode.\n"); | 392 | printk("Machine check in kernel mode.\n"); |
380 | printk("Caused by (from MCSR=%lx): ", reason); | 393 | printk("Caused by (from MCSR=%lx): ", reason); |
381 | 394 | ||
@@ -403,7 +416,14 @@ static int generic_machine_check_exception(struct pt_regs *regs) | |||
403 | printk("Bus - Instruction Parity Error\n"); | 416 | printk("Bus - Instruction Parity Error\n"); |
404 | if (reason & MCSR_BUS_RPERR) | 417 | if (reason & MCSR_BUS_RPERR) |
405 | printk("Bus - Read Parity Error\n"); | 418 | printk("Bus - Read Parity Error\n"); |
406 | #elif defined (CONFIG_E200) | 419 | |
420 | return 0; | ||
421 | } | ||
422 | #elif defined(CONFIG_E200) | ||
423 | int machine_check_e200(struct pt_regs *regs) | ||
424 | { | ||
425 | unsigned long reason = get_mc_reason(regs); | ||
426 | |||
407 | printk("Machine check in kernel mode.\n"); | 427 | printk("Machine check in kernel mode.\n"); |
408 | printk("Caused by (from MCSR=%lx): ", reason); | 428 | printk("Caused by (from MCSR=%lx): ", reason); |
409 | 429 | ||
@@ -421,7 +441,14 @@ static int generic_machine_check_exception(struct pt_regs *regs) | |||
421 | printk("Bus - Read Bus Error on data load\n"); | 441 | printk("Bus - Read Bus Error on data load\n"); |
422 | if (reason & MCSR_BUS_WRERR) | 442 | if (reason & MCSR_BUS_WRERR) |
423 | printk("Bus - Write Bus Error on buffered store or cache line push\n"); | 443 | printk("Bus - Write Bus Error on buffered store or cache line push\n"); |
424 | #else /* !CONFIG_4xx && !CONFIG_E500 && !CONFIG_E200 */ | 444 | |
445 | return 0; | ||
446 | } | ||
447 | #else | ||
448 | int machine_check_generic(struct pt_regs *regs) | ||
449 | { | ||
450 | unsigned long reason = get_mc_reason(regs); | ||
451 | |||
425 | printk("Machine check in kernel mode.\n"); | 452 | printk("Machine check in kernel mode.\n"); |
426 | printk("Caused by (from SRR1=%lx): ", reason); | 453 | printk("Caused by (from SRR1=%lx): ", reason); |
427 | switch (reason & 0x601F0000) { | 454 | switch (reason & 0x601F0000) { |
@@ -451,22 +478,26 @@ static int generic_machine_check_exception(struct pt_regs *regs) | |||
451 | default: | 478 | default: |
452 | printk("Unknown values in msr\n"); | 479 | printk("Unknown values in msr\n"); |
453 | } | 480 | } |
454 | #endif /* CONFIG_4xx */ | ||
455 | |||
456 | return 0; | 481 | return 0; |
457 | } | 482 | } |
483 | #endif /* everything else */ | ||
458 | 484 | ||
459 | void machine_check_exception(struct pt_regs *regs) | 485 | void machine_check_exception(struct pt_regs *regs) |
460 | { | 486 | { |
461 | int recover = 0; | 487 | int recover = 0; |
462 | 488 | ||
463 | /* See if any machine dependent calls */ | 489 | /* See if any machine dependent calls. In theory, we would want |
490 | * to call the CPU first, and call the ppc_md. one if the CPU | ||
491 | * one returns a positive number. However there is existing code | ||
492 | * that assumes the board gets a first chance, so let's keep it | ||
493 | * that way for now and fix things later. --BenH. | ||
494 | */ | ||
464 | if (ppc_md.machine_check_exception) | 495 | if (ppc_md.machine_check_exception) |
465 | recover = ppc_md.machine_check_exception(regs); | 496 | recover = ppc_md.machine_check_exception(regs); |
466 | else | 497 | else if (cur_cpu_spec->machine_check) |
467 | recover = generic_machine_check_exception(regs); | 498 | recover = cur_cpu_spec->machine_check(regs); |
468 | 499 | ||
469 | if (recover) | 500 | if (recover > 0) |
470 | return; | 501 | return; |
471 | 502 | ||
472 | if (user_mode(regs)) { | 503 | if (user_mode(regs)) { |
@@ -476,7 +507,12 @@ void machine_check_exception(struct pt_regs *regs) | |||
476 | } | 507 | } |
477 | 508 | ||
478 | #if defined(CONFIG_8xx) && defined(CONFIG_PCI) | 509 | #if defined(CONFIG_8xx) && defined(CONFIG_PCI) |
479 | /* the qspan pci read routines can cause machine checks -- Cort */ | 510 | /* the qspan pci read routines can cause machine checks -- Cort |
511 | * | ||
512 | * yuck !!! that totally needs to go away ! There are better ways | ||
513 | * to deal with that than having a wart in the mcheck handler. | ||
514 | * -- BenH | ||
515 | */ | ||
480 | bad_page_fault(regs, regs->dar, SIGBUS); | 516 | bad_page_fault(regs, regs->dar, SIGBUS); |
481 | return; | 517 | return; |
482 | #endif | 518 | #endif |