Commit 47c0bd1a authored by Benjamin Herrenschmidt's avatar Benjamin Herrenschmidt Committed by Josh Boyer

[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: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: default avatarJosh Boyer <jwboyer@linux.vnet.ibm.com>
parent c2a7dcad
...@@ -23,11 +23,20 @@ _GLOBAL(__setup_cpu_440epx) ...@@ -23,11 +23,20 @@ _GLOBAL(__setup_cpu_440epx)
mflr r4 mflr r4
bl __init_fpu_44x bl __init_fpu_44x
bl __plb_disable_wrp bl __plb_disable_wrp
bl __fixup_440A_mcheck
mtlr r4 mtlr r4
blr blr
_GLOBAL(__setup_cpu_440grx) _GLOBAL(__setup_cpu_440grx)
b __plb_disable_wrp b __plb_disable_wrp
_GLOBAL(__setup_cpu_440gx)
_GLOBAL(__setup_cpu_440spe)
b __fixup_440A_mcheck
/* Temporary fixup for arch/ppc until we kill the whole thing */
#ifndef CONFIG_PPC_MERGE
_GLOBAL(__fixup_440A_mcheck)
blr
#endif
/* enable APU between CPU and FPU */ /* enable APU between CPU and FPU */
_GLOBAL(__init_fpu_44x) _GLOBAL(__init_fpu_44x)
......
This diff is collapsed.
...@@ -289,11 +289,8 @@ interrupt_base: ...@@ -289,11 +289,8 @@ interrupt_base:
CRITICAL_EXCEPTION(0x0100, CriticalInput, unknown_exception) CRITICAL_EXCEPTION(0x0100, CriticalInput, unknown_exception)
/* Machine Check Interrupt */ /* Machine Check Interrupt */
#ifdef CONFIG_440A
MCHECK_EXCEPTION(0x0200, MachineCheck, machine_check_exception)
#else
CRITICAL_EXCEPTION(0x0200, MachineCheck, machine_check_exception) CRITICAL_EXCEPTION(0x0200, MachineCheck, machine_check_exception)
#endif MCHECK_EXCEPTION(0x0210, MachineCheckA, machine_check_exception)
/* Data Storage Interrupt */ /* Data Storage Interrupt */
START_EXCEPTION(DataStorage) START_EXCEPTION(DataStorage)
...@@ -673,6 +670,15 @@ finish_tlb_load: ...@@ -673,6 +670,15 @@ finish_tlb_load:
* Global functions * Global functions
*/ */
/*
* Adjust the machine check IVOR on 440A cores
*/
_GLOBAL(__fixup_440A_mcheck)
li r3,MachineCheckA@l
mtspr SPRN_IVOR1,r3
sync
blr
/* /*
* extern void giveup_altivec(struct task_struct *prev) * extern void giveup_altivec(struct task_struct *prev)
* *
......
...@@ -166,7 +166,7 @@ ...@@ -166,7 +166,7 @@
mfspr r5,SPRN_ESR; \ mfspr r5,SPRN_ESR; \
stw r5,_ESR(r11); \ stw r5,_ESR(r11); \
addi r3,r1,STACK_FRAME_OVERHEAD; \ addi r3,r1,STACK_FRAME_OVERHEAD; \
EXC_XFER_TEMPLATE(hdlr, n+2, (MSR_KERNEL & ~(MSR_ME|MSR_DE|MSR_CE)), \ EXC_XFER_TEMPLATE(hdlr, n+4, (MSR_KERNEL & ~(MSR_ME|MSR_DE|MSR_CE)), \
NOCOPY, mcheck_transfer_to_handler, \ NOCOPY, mcheck_transfer_to_handler, \
ret_from_mcheck_exc) ret_from_mcheck_exc)
......
...@@ -334,18 +334,25 @@ static inline int check_io_access(struct pt_regs *regs) ...@@ -334,18 +334,25 @@ static inline int check_io_access(struct pt_regs *regs)
#define clear_single_step(regs) ((regs)->msr &= ~MSR_SE) #define clear_single_step(regs) ((regs)->msr &= ~MSR_SE)
#endif #endif
static int generic_machine_check_exception(struct pt_regs *regs) #if defined(CONFIG_4xx)
int machine_check_4xx(struct pt_regs *regs)
{ {
unsigned long reason = get_mc_reason(regs); unsigned long reason = get_mc_reason(regs);
#if defined(CONFIG_4xx) && !defined(CONFIG_440A)
if (reason & ESR_IMCP) { if (reason & ESR_IMCP) {
printk("Instruction"); printk("Instruction");
mtspr(SPRN_ESR, reason & ~ESR_IMCP); mtspr(SPRN_ESR, reason & ~ESR_IMCP);
} else } else
printk("Data"); printk("Data");
printk(" machine check in kernel mode.\n"); printk(" machine check in kernel mode.\n");
#elif defined(CONFIG_440A)
return 0;
}
int machine_check_440A(struct pt_regs *regs)
{
unsigned long reason = get_mc_reason(regs);
printk("Machine check in kernel mode.\n"); printk("Machine check in kernel mode.\n");
if (reason & ESR_IMCP){ if (reason & ESR_IMCP){
printk("Instruction Synchronous Machine Check exception\n"); printk("Instruction Synchronous Machine Check exception\n");
...@@ -375,7 +382,13 @@ static int generic_machine_check_exception(struct pt_regs *regs) ...@@ -375,7 +382,13 @@ static int generic_machine_check_exception(struct pt_regs *regs)
/* Clear MCSR */ /* Clear MCSR */
mtspr(SPRN_MCSR, mcsr); mtspr(SPRN_MCSR, mcsr);
} }
#elif defined (CONFIG_E500) return 0;
}
#elif defined(CONFIG_E500)
int machine_check_e500(struct pt_regs *regs)
{
unsigned long reason = get_mc_reason(regs);
printk("Machine check in kernel mode.\n"); printk("Machine check in kernel mode.\n");
printk("Caused by (from MCSR=%lx): ", reason); printk("Caused by (from MCSR=%lx): ", reason);
...@@ -403,7 +416,14 @@ static int generic_machine_check_exception(struct pt_regs *regs) ...@@ -403,7 +416,14 @@ static int generic_machine_check_exception(struct pt_regs *regs)
printk("Bus - Instruction Parity Error\n"); printk("Bus - Instruction Parity Error\n");
if (reason & MCSR_BUS_RPERR) if (reason & MCSR_BUS_RPERR)
printk("Bus - Read Parity Error\n"); printk("Bus - Read Parity Error\n");
#elif defined (CONFIG_E200)
return 0;
}
#elif defined(CONFIG_E200)
int machine_check_e200(struct pt_regs *regs)
{
unsigned long reason = get_mc_reason(regs);
printk("Machine check in kernel mode.\n"); printk("Machine check in kernel mode.\n");
printk("Caused by (from MCSR=%lx): ", reason); printk("Caused by (from MCSR=%lx): ", reason);
...@@ -421,7 +441,14 @@ static int generic_machine_check_exception(struct pt_regs *regs) ...@@ -421,7 +441,14 @@ static int generic_machine_check_exception(struct pt_regs *regs)
printk("Bus - Read Bus Error on data load\n"); printk("Bus - Read Bus Error on data load\n");
if (reason & MCSR_BUS_WRERR) if (reason & MCSR_BUS_WRERR)
printk("Bus - Write Bus Error on buffered store or cache line push\n"); printk("Bus - Write Bus Error on buffered store or cache line push\n");
#else /* !CONFIG_4xx && !CONFIG_E500 && !CONFIG_E200 */
return 0;
}
#else
int machine_check_generic(struct pt_regs *regs)
{
unsigned long reason = get_mc_reason(regs);
printk("Machine check in kernel mode.\n"); printk("Machine check in kernel mode.\n");
printk("Caused by (from SRR1=%lx): ", reason); printk("Caused by (from SRR1=%lx): ", reason);
switch (reason & 0x601F0000) { switch (reason & 0x601F0000) {
...@@ -451,22 +478,26 @@ static int generic_machine_check_exception(struct pt_regs *regs) ...@@ -451,22 +478,26 @@ static int generic_machine_check_exception(struct pt_regs *regs)
default: default:
printk("Unknown values in msr\n"); printk("Unknown values in msr\n");
} }
#endif /* CONFIG_4xx */
return 0; return 0;
} }
#endif /* everything else */
void machine_check_exception(struct pt_regs *regs) void machine_check_exception(struct pt_regs *regs)
{ {
int recover = 0; int recover = 0;
/* See if any machine dependent calls */ /* See if any machine dependent calls. In theory, we would want
* to call the CPU first, and call the ppc_md. one if the CPU
* one returns a positive number. However there is existing code
* that assumes the board gets a first chance, so let's keep it
* that way for now and fix things later. --BenH.
*/
if (ppc_md.machine_check_exception) if (ppc_md.machine_check_exception)
recover = ppc_md.machine_check_exception(regs); recover = ppc_md.machine_check_exception(regs);
else else if (cur_cpu_spec->machine_check)
recover = generic_machine_check_exception(regs); recover = cur_cpu_spec->machine_check(regs);
if (recover) if (recover > 0)
return; return;
if (user_mode(regs)) { if (user_mode(regs)) {
...@@ -476,7 +507,12 @@ void machine_check_exception(struct pt_regs *regs) ...@@ -476,7 +507,12 @@ void machine_check_exception(struct pt_regs *regs)
} }
#if defined(CONFIG_8xx) && defined(CONFIG_PCI) #if defined(CONFIG_8xx) && defined(CONFIG_PCI)
/* the qspan pci read routines can cause machine checks -- Cort */ /* the qspan pci read routines can cause machine checks -- Cort
*
* yuck !!! that totally needs to go away ! There are better ways
* to deal with that than having a wart in the mcheck handler.
* -- BenH
*/
bad_page_fault(regs, regs->dar, SIGBUS); bad_page_fault(regs, regs->dar, SIGBUS);
return; return;
#endif #endif
......
...@@ -62,11 +62,6 @@ config 440GX ...@@ -62,11 +62,6 @@ config 440GX
config 440SP config 440SP
bool bool
config 440A
bool
depends on 440GX || 440EPX
default y
# 44x errata/workaround config symbols, selected by the CPU models above # 44x errata/workaround config symbols, selected by the CPU models above
config IBM440EP_ERR42 config IBM440EP_ERR42
bool bool
...@@ -231,39 +231,25 @@ platform_machine_check(struct pt_regs *regs) ...@@ -231,39 +231,25 @@ platform_machine_check(struct pt_regs *regs)
{ {
} }
void machine_check_exception(struct pt_regs *regs) #if defined(CONFIG_4xx)
int machine_check_4xx(struct pt_regs *regs)
{ {
unsigned long reason = get_mc_reason(regs); unsigned long reason = get_mc_reason(regs);
if (user_mode(regs)) {
regs->msr |= MSR_RI;
_exception(SIGBUS, regs, BUS_ADRERR, regs->nip);
return;
}
#if defined(CONFIG_8xx) && defined(CONFIG_PCI)
/* the qspan pci read routines can cause machine checks -- Cort */
bad_page_fault(regs, regs->dar, SIGBUS);
return;
#endif
if (debugger_fault_handler) {
debugger_fault_handler(regs);
regs->msr |= MSR_RI;
return;
}
if (check_io_access(regs))
return;
#if defined(CONFIG_4xx) && !defined(CONFIG_440A)
if (reason & ESR_IMCP) { if (reason & ESR_IMCP) {
printk("Instruction"); printk("Instruction");
mtspr(SPRN_ESR, reason & ~ESR_IMCP); mtspr(SPRN_ESR, reason & ~ESR_IMCP);
} else } else
printk("Data"); printk("Data");
printk(" machine check in kernel mode.\n"); printk(" machine check in kernel mode.\n");
#elif defined(CONFIG_440A)
return 0;
}
int machine_check_440A(struct pt_regs *regs)
{
unsigned long reason = get_mc_reason(regs);
printk("Machine check in kernel mode.\n"); printk("Machine check in kernel mode.\n");
if (reason & ESR_IMCP){ if (reason & ESR_IMCP){
printk("Instruction Synchronous Machine Check exception\n"); printk("Instruction Synchronous Machine Check exception\n");
...@@ -293,7 +279,13 @@ void machine_check_exception(struct pt_regs *regs) ...@@ -293,7 +279,13 @@ void machine_check_exception(struct pt_regs *regs)
/* Clear MCSR */ /* Clear MCSR */
mtspr(SPRN_MCSR, mcsr); mtspr(SPRN_MCSR, mcsr);
} }
#elif defined (CONFIG_E500) return 0;
}
#elif defined(CONFIG_E500)
int machine_check_e500(struct pt_regs *regs)
{
unsigned long reason = get_mc_reason(regs);
printk("Machine check in kernel mode.\n"); printk("Machine check in kernel mode.\n");
printk("Caused by (from MCSR=%lx): ", reason); printk("Caused by (from MCSR=%lx): ", reason);
...@@ -305,8 +297,6 @@ void machine_check_exception(struct pt_regs *regs) ...@@ -305,8 +297,6 @@ void machine_check_exception(struct pt_regs *regs)
printk("Data Cache Push Parity Error\n"); printk("Data Cache Push Parity Error\n");
if (reason & MCSR_DCPERR) if (reason & MCSR_DCPERR)
printk("Data Cache Parity Error\n"); printk("Data Cache Parity Error\n");
if (reason & MCSR_GL_CI)
printk("Guarded Load or Cache-Inhibited stwcx.\n");
if (reason & MCSR_BUS_IAERR) if (reason & MCSR_BUS_IAERR)
printk("Bus - Instruction Address Error\n"); printk("Bus - Instruction Address Error\n");
if (reason & MCSR_BUS_RAERR) if (reason & MCSR_BUS_RAERR)
...@@ -318,12 +308,19 @@ void machine_check_exception(struct pt_regs *regs) ...@@ -318,12 +308,19 @@ void machine_check_exception(struct pt_regs *regs)
if (reason & MCSR_BUS_RBERR) if (reason & MCSR_BUS_RBERR)
printk("Bus - Read Data Bus Error\n"); printk("Bus - Read Data Bus Error\n");
if (reason & MCSR_BUS_WBERR) if (reason & MCSR_BUS_WBERR)
printk("Bus - Write Data Bus Error\n"); printk("Bus - Read Data Bus Error\n");
if (reason & MCSR_BUS_IPERR) if (reason & MCSR_BUS_IPERR)
printk("Bus - Instruction Parity Error\n"); printk("Bus - Instruction Parity Error\n");
if (reason & MCSR_BUS_RPERR) if (reason & MCSR_BUS_RPERR)
printk("Bus - Read Parity Error\n"); printk("Bus - Read Parity Error\n");
#elif defined (CONFIG_E200)
return 0;
}
#elif defined(CONFIG_E200)
int machine_check_e200(struct pt_regs *regs)
{
unsigned long reason = get_mc_reason(regs);
printk("Machine check in kernel mode.\n"); printk("Machine check in kernel mode.\n");
printk("Caused by (from MCSR=%lx): ", reason); printk("Caused by (from MCSR=%lx): ", reason);
...@@ -341,7 +338,14 @@ void machine_check_exception(struct pt_regs *regs) ...@@ -341,7 +338,14 @@ void machine_check_exception(struct pt_regs *regs)
printk("Bus - Read Bus Error on data load\n"); printk("Bus - Read Bus Error on data load\n");
if (reason & MCSR_BUS_WRERR) if (reason & MCSR_BUS_WRERR)
printk("Bus - Write Bus Error on buffered store or cache line push\n"); printk("Bus - Write Bus Error on buffered store or cache line push\n");
#else /* !CONFIG_4xx && !CONFIG_E500 && !CONFIG_E200 */
return 0;
}
#else
int machine_check_generic(struct pt_regs *regs)
{
unsigned long reason = get_mc_reason(regs);
printk("Machine check in kernel mode.\n"); printk("Machine check in kernel mode.\n");
printk("Caused by (from SRR1=%lx): ", reason); printk("Caused by (from SRR1=%lx): ", reason);
switch (reason & 0x601F0000) { switch (reason & 0x601F0000) {
...@@ -371,7 +375,39 @@ void machine_check_exception(struct pt_regs *regs) ...@@ -371,7 +375,39 @@ void machine_check_exception(struct pt_regs *regs)
default: default:
printk("Unknown values in msr\n"); printk("Unknown values in msr\n");
} }
#endif /* CONFIG_4xx */ return 0;
}
#endif /* everything else */
void machine_check_exception(struct pt_regs *regs)
{
int recover = 0;
if (cur_cpu_spec->machine_check)
recover = cur_cpu_spec->machine_check(regs);
if (recover > 0)
return;
if (user_mode(regs)) {
regs->msr |= MSR_RI;
_exception(SIGBUS, regs, BUS_ADRERR, regs->nip);
return;
}
#if defined(CONFIG_8xx) && defined(CONFIG_PCI)
/* the qspan pci read routines can cause machine checks -- Cort */
bad_page_fault(regs, regs->dar, SIGBUS);
return;
#endif
if (debugger_fault_handler) {
debugger_fault_handler(regs);
regs->msr |= MSR_RI;
return;
}
if (check_io_access(regs))
return;
/* /*
* Optional platform-provided routine to print out * Optional platform-provided routine to print out
......
...@@ -57,6 +57,14 @@ enum powerpc_pmc_type { ...@@ -57,6 +57,14 @@ enum powerpc_pmc_type {
PPC_PMC_PA6T = 2, PPC_PMC_PA6T = 2,
}; };
struct pt_regs;
extern int machine_check_generic(struct pt_regs *regs);
extern int machine_check_4xx(struct pt_regs *regs);
extern int machine_check_440A(struct pt_regs *regs);
extern int machine_check_e500(struct pt_regs *regs);
extern int machine_check_e200(struct pt_regs *regs);
/* NOTE WELL: Update identify_cpu() if fields are added or removed! */ /* NOTE WELL: Update identify_cpu() if fields are added or removed! */
struct cpu_spec { struct cpu_spec {
/* CPU is matched via (PVR & pvr_mask) == pvr_value */ /* CPU is matched via (PVR & pvr_mask) == pvr_value */
...@@ -97,6 +105,11 @@ struct cpu_spec { ...@@ -97,6 +105,11 @@ struct cpu_spec {
/* Name of processor class, for the ELF AT_PLATFORM entry */ /* Name of processor class, for the ELF AT_PLATFORM entry */
char *platform; char *platform;
/* Processor specific machine check handling. Return negative
* if the error is fatal, 1 if it was fully recovered and 0 to
* pass up (not CPU originated) */
int (*machine_check)(struct pt_regs *regs);
}; };
extern struct cpu_spec *cur_cpu_spec; extern struct cpu_spec *cur_cpu_spec;
......
...@@ -106,7 +106,8 @@ extern int ptrace_put_reg(struct task_struct *task, int regno, ...@@ -106,7 +106,8 @@ extern int ptrace_put_reg(struct task_struct *task, int regno,
*/ */
#define FULL_REGS(regs) (((regs)->trap & 1) == 0) #define FULL_REGS(regs) (((regs)->trap & 1) == 0)
#ifndef __powerpc64__ #ifndef __powerpc64__
#define IS_CRITICAL_EXC(regs) (((regs)->trap & 2) == 0) #define IS_CRITICAL_EXC(regs) (((regs)->trap & 2) != 0)
#define IS_MCHECK_EXC(regs) (((regs)->trap & 4) != 0)
#endif /* ! __powerpc64__ */ #endif /* ! __powerpc64__ */
#define TRAP(regs) ((regs)->trap & ~0xF) #define TRAP(regs) ((regs)->trap & ~0xF)
#ifdef __powerpc64__ #ifdef __powerpc64__
......
...@@ -218,7 +218,6 @@ ...@@ -218,7 +218,6 @@
#define CCR1_TCS 0x00000080 /* Timer Clock Select */ #define CCR1_TCS 0x00000080 /* Timer Clock Select */
/* Bit definitions for the MCSR. */ /* Bit definitions for the MCSR. */
#ifdef CONFIG_440A
#define MCSR_MCS 0x80000000 /* Machine Check Summary */ #define MCSR_MCS 0x80000000 /* Machine Check Summary */
#define MCSR_IB 0x40000000 /* Instruction PLB Error */ #define MCSR_IB 0x40000000 /* Instruction PLB Error */
#define MCSR_DRB 0x20000000 /* Data Read PLB Error */ #define MCSR_DRB 0x20000000 /* Data Read PLB Error */
...@@ -228,7 +227,7 @@ ...@@ -228,7 +227,7 @@
#define MCSR_DCSP 0x02000000 /* D-Cache Search Parity Error */ #define MCSR_DCSP 0x02000000 /* D-Cache Search Parity Error */
#define MCSR_DCFP 0x01000000 /* D-Cache Flush Parity Error */ #define MCSR_DCFP 0x01000000 /* D-Cache Flush Parity Error */
#define MCSR_IMPE 0x00800000 /* Imprecise Machine Check Exception */ #define MCSR_IMPE 0x00800000 /* Imprecise Machine Check Exception */
#endif
#ifdef CONFIG_E500 #ifdef CONFIG_E500
#define MCSR_MCP 0x80000000UL /* Machine Check Input Pin */ #define MCSR_MCP 0x80000000UL /* Machine Check Input Pin */
#define MCSR_ICPERR 0x40000000UL /* I-Cache Parity Error */ #define MCSR_ICPERR 0x40000000UL /* I-Cache Parity Error */
......
...@@ -207,7 +207,7 @@ ...@@ -207,7 +207,7 @@
#define CCR1_TCS 0x00000080 /* Timer Clock Select */ #define CCR1_TCS 0x00000080 /* Timer Clock Select */
/* Bit definitions for the MCSR. */ /* Bit definitions for the MCSR. */
#ifdef CONFIG_440A #ifdef CONFIG_4xx
#define MCSR_MCS 0x80000000 /* Machine Check Summary */ #define MCSR_MCS 0x80000000 /* Machine Check Summary */
#define MCSR_IB 0x40000000 /* Instruction PLB Error */ #define MCSR_IB 0x40000000 /* Instruction PLB Error */
#define MCSR_DRB 0x20000000 /* Data Read PLB Error */ #define MCSR_DRB 0x20000000 /* Data Read PLB Error */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment