diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2011-09-19 17:45:04 (GMT) |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2011-09-20 06:10:03 (GMT) |
commit | ed79ba9e15f84cef05aba5cbfe6e93f9b43c31f4 (patch) | |
tree | 7df9a2dc61e5c43e426562adc4367cb7e32380a0 /arch/powerpc/platforms/powernv | |
parent | a125e0928c736bc50cdd9a13151d4f4ee7821266 (diff) | |
download | linux-ed79ba9e15f84cef05aba5cbfe6e93f9b43c31f4.tar.xz |
powerpc/powernv: Machine check and other system interrupts
OPAL can handle various interrupt for us such as Machine Checks (it
performs all sorts of recovery tasks and passes back control to us with
informations about the error), Hardware Management Interrupts and Softpatch
interrupts.
This wires up the mechanisms and prints out specific informations returned
by HAL when a machine check occurs.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/platforms/powernv')
-rw-r--r-- | arch/powerpc/platforms/powernv/opal.c | 130 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/setup.c | 1 |
2 files changed, 131 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c index 5a598ca..aaa0dba 100644 --- a/arch/powerpc/platforms/powernv/opal.c +++ b/arch/powerpc/platforms/powernv/opal.c @@ -27,12 +27,14 @@ struct opal { static struct device_node *opal_node; static DEFINE_SPINLOCK(opal_write_lock); +extern u64 opal_mc_secondary_handler[]; int __init early_init_dt_scan_opal(unsigned long node, const char *uname, int depth, void *data) { const void *basep, *entryp; unsigned long basesz, entrysz; + u64 glue; if (depth != 1 || strcmp(uname, "ibm,opal") != 0) return 0; @@ -59,6 +61,19 @@ int __init early_init_dt_scan_opal(unsigned long node, printk("OPAL V1 detected !\n"); } + /* Hookup some exception handlers. We use the fwnmi area at 0x7000 + * to provide the glue space to OPAL + */ + glue = 0x7000; + opal_register_exception_handler(OPAL_MACHINE_CHECK_HANDLER, + __pa(opal_mc_secondary_handler[0]), + glue); + glue += 128; + opal_register_exception_handler(OPAL_HYPERVISOR_MAINTENANCE_HANDLER, + 0, glue); + glue += 128; + opal_register_exception_handler(OPAL_SOFTPATCH_HANDLER, 0, glue); + return 1; } @@ -136,6 +151,121 @@ int opal_put_chars(uint32_t vtermno, const char *data, int total_len) return written; } +int opal_machine_check(struct pt_regs *regs) +{ + struct opal_machine_check_event *opal_evt = get_paca()->opal_mc_evt; + struct opal_machine_check_event evt; + const char *level, *sevstr, *subtype; + static const char *opal_mc_ue_types[] = { + "Indeterminate", + "Instruction fetch", + "Page table walk ifetch", + "Load/Store", + "Page table walk Load/Store", + }; + static const char *opal_mc_slb_types[] = { + "Indeterminate", + "Parity", + "Multihit", + }; + static const char *opal_mc_erat_types[] = { + "Indeterminate", + "Parity", + "Multihit", + }; + static const char *opal_mc_tlb_types[] = { + "Indeterminate", + "Parity", + "Multihit", + }; + + /* Copy the event structure and release the original */ + evt = *opal_evt; + opal_evt->in_use = 0; + + /* Print things out */ + if (evt.version != OpalMCE_V1) { + pr_err("Machine Check Exception, Unknown event version %d !\n", + evt.version); + return 0; + } + switch(evt.severity) { + case OpalMCE_SEV_NO_ERROR: + level = KERN_INFO; + sevstr = "Harmless"; + break; + case OpalMCE_SEV_WARNING: + level = KERN_WARNING; + sevstr = ""; + break; + case OpalMCE_SEV_ERROR_SYNC: + level = KERN_ERR; + sevstr = "Severe"; + break; + case OpalMCE_SEV_FATAL: + default: + level = KERN_ERR; + sevstr = "Fatal"; + break; + } + + printk("%s%s Machine check interrupt [%s]\n", level, sevstr, + evt.disposition == OpalMCE_DISPOSITION_RECOVERED ? + "Recovered" : "[Not recovered"); + printk("%s Initiator: %s\n", level, + evt.initiator == OpalMCE_INITIATOR_CPU ? "CPU" : "Unknown"); + switch(evt.error_type) { + case OpalMCE_ERROR_TYPE_UE: + subtype = evt.u.ue_error.ue_error_type < + ARRAY_SIZE(opal_mc_ue_types) ? + opal_mc_ue_types[evt.u.ue_error.ue_error_type] + : "Unknown"; + printk("%s Error type: UE [%s]\n", level, subtype); + if (evt.u.ue_error.effective_address_provided) + printk("%s Effective address: %016llx\n", + level, evt.u.ue_error.effective_address); + if (evt.u.ue_error.physical_address_provided) + printk("%s Physial address: %016llx\n", + level, evt.u.ue_error.physical_address); + break; + case OpalMCE_ERROR_TYPE_SLB: + subtype = evt.u.slb_error.slb_error_type < + ARRAY_SIZE(opal_mc_slb_types) ? + opal_mc_slb_types[evt.u.slb_error.slb_error_type] + : "Unknown"; + printk("%s Error type: SLB [%s]\n", level, subtype); + if (evt.u.slb_error.effective_address_provided) + printk("%s Effective address: %016llx\n", + level, evt.u.slb_error.effective_address); + break; + case OpalMCE_ERROR_TYPE_ERAT: + subtype = evt.u.erat_error.erat_error_type < + ARRAY_SIZE(opal_mc_erat_types) ? + opal_mc_erat_types[evt.u.erat_error.erat_error_type] + : "Unknown"; + printk("%s Error type: ERAT [%s]\n", level, subtype); + if (evt.u.erat_error.effective_address_provided) + printk("%s Effective address: %016llx\n", + level, evt.u.erat_error.effective_address); + break; + case OpalMCE_ERROR_TYPE_TLB: + subtype = evt.u.tlb_error.tlb_error_type < + ARRAY_SIZE(opal_mc_tlb_types) ? + opal_mc_tlb_types[evt.u.tlb_error.tlb_error_type] + : "Unknown"; + printk("%s Error type: TLB [%s]\n", level, subtype); + if (evt.u.tlb_error.effective_address_provided) + printk("%s Effective address: %016llx\n", + level, evt.u.tlb_error.effective_address); + break; + default: + case OpalMCE_ERROR_TYPE_UNKNOWN: + printk("%s Error type: Unknown\n", level); + break; + } + return evt.severity == OpalMCE_SEV_FATAL ? 0 : 1; +} + static irqreturn_t opal_interrupt(int irq, void *data) { uint64_t events; diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c index 4a2b2e2..f0242f3 100644 --- a/arch/powerpc/platforms/powernv/setup.c +++ b/arch/powerpc/platforms/powernv/setup.c @@ -141,6 +141,7 @@ static void __init pnv_setup_machdep_opal(void) ppc_md.restart = pnv_restart; ppc_md.power_off = pnv_power_off; ppc_md.halt = pnv_halt; + ppc_md.machine_check_exception = opal_machine_check; } #ifdef CONFIG_PPC_POWERNV_RTAS |