You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
[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>
This commit is contained in:
committed by
Josh Boyer
parent
c2a7dcad9f
commit
47c0bd1ae2
@@ -23,11 +23,20 @@ _GLOBAL(__setup_cpu_440epx)
|
||||
mflr r4
|
||||
bl __init_fpu_44x
|
||||
bl __plb_disable_wrp
|
||||
bl __fixup_440A_mcheck
|
||||
mtlr r4
|
||||
blr
|
||||
_GLOBAL(__setup_cpu_440grx)
|
||||
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 */
|
||||
_GLOBAL(__init_fpu_44x)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -289,11 +289,8 @@ interrupt_base:
|
||||
CRITICAL_EXCEPTION(0x0100, CriticalInput, unknown_exception)
|
||||
|
||||
/* Machine Check Interrupt */
|
||||
#ifdef CONFIG_440A
|
||||
MCHECK_EXCEPTION(0x0200, MachineCheck, machine_check_exception)
|
||||
#else
|
||||
CRITICAL_EXCEPTION(0x0200, MachineCheck, machine_check_exception)
|
||||
#endif
|
||||
MCHECK_EXCEPTION(0x0210, MachineCheckA, machine_check_exception)
|
||||
|
||||
/* Data Storage Interrupt */
|
||||
START_EXCEPTION(DataStorage)
|
||||
@@ -673,6 +670,15 @@ finish_tlb_load:
|
||||
* 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)
|
||||
*
|
||||
|
||||
@@ -166,7 +166,7 @@ label:
|
||||
mfspr r5,SPRN_ESR; \
|
||||
stw r5,_ESR(r11); \
|
||||
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, \
|
||||
ret_from_mcheck_exc)
|
||||
|
||||
|
||||
+49
-13
@@ -334,18 +334,25 @@ static inline int check_io_access(struct pt_regs *regs)
|
||||
#define clear_single_step(regs) ((regs)->msr &= ~MSR_SE)
|
||||
#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);
|
||||
|
||||
#if defined(CONFIG_4xx) && !defined(CONFIG_440A)
|
||||
if (reason & ESR_IMCP) {
|
||||
printk("Instruction");
|
||||
mtspr(SPRN_ESR, reason & ~ESR_IMCP);
|
||||
} else
|
||||
printk("Data");
|
||||
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");
|
||||
if (reason & ESR_IMCP){
|
||||
printk("Instruction Synchronous Machine Check exception\n");
|
||||
@@ -375,7 +382,13 @@ static int generic_machine_check_exception(struct pt_regs *regs)
|
||||
/* Clear 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("Caused by (from MCSR=%lx): ", reason);
|
||||
|
||||
@@ -403,7 +416,14 @@ static int generic_machine_check_exception(struct pt_regs *regs)
|
||||
printk("Bus - Instruction Parity Error\n");
|
||||
if (reason & MCSR_BUS_RPERR)
|
||||
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("Caused by (from MCSR=%lx): ", reason);
|
||||
|
||||
@@ -421,7 +441,14 @@ static int generic_machine_check_exception(struct pt_regs *regs)
|
||||
printk("Bus - Read Bus Error on data load\n");
|
||||
if (reason & MCSR_BUS_WRERR)
|
||||
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("Caused by (from SRR1=%lx): ", reason);
|
||||
switch (reason & 0x601F0000) {
|
||||
@@ -451,22 +478,26 @@ static int generic_machine_check_exception(struct pt_regs *regs)
|
||||
default:
|
||||
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;
|
||||
|
||||
/* 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)
|
||||
recover = ppc_md.machine_check_exception(regs);
|
||||
else
|
||||
recover = generic_machine_check_exception(regs);
|
||||
else if (cur_cpu_spec->machine_check)
|
||||
recover = cur_cpu_spec->machine_check(regs);
|
||||
|
||||
if (recover)
|
||||
if (recover > 0)
|
||||
return;
|
||||
|
||||
if (user_mode(regs)) {
|
||||
@@ -476,7 +507,12 @@ void machine_check_exception(struct pt_regs *regs)
|
||||
}
|
||||
|
||||
#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);
|
||||
return;
|
||||
#endif
|
||||
|
||||
@@ -62,11 +62,6 @@ config 440GX
|
||||
config 440SP
|
||||
bool
|
||||
|
||||
config 440A
|
||||
bool
|
||||
depends on 440GX || 440EPX
|
||||
default y
|
||||
|
||||
# 44x errata/workaround config symbols, selected by the CPU models above
|
||||
config IBM440EP_ERR42
|
||||
bool
|
||||
|
||||
+67
-31
@@ -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);
|
||||
|
||||
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) {
|
||||
printk("Instruction");
|
||||
mtspr(SPRN_ESR, reason & ~ESR_IMCP);
|
||||
} else
|
||||
printk("Data");
|
||||
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");
|
||||
if (reason & ESR_IMCP){
|
||||
printk("Instruction Synchronous Machine Check exception\n");
|
||||
@@ -293,7 +279,13 @@ void machine_check_exception(struct pt_regs *regs)
|
||||
/* Clear 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("Caused by (from MCSR=%lx): ", reason);
|
||||
|
||||
@@ -305,8 +297,6 @@ void machine_check_exception(struct pt_regs *regs)
|
||||
printk("Data Cache Push Parity Error\n");
|
||||
if (reason & MCSR_DCPERR)
|
||||
printk("Data Cache Parity Error\n");
|
||||
if (reason & MCSR_GL_CI)
|
||||
printk("Guarded Load or Cache-Inhibited stwcx.\n");
|
||||
if (reason & MCSR_BUS_IAERR)
|
||||
printk("Bus - Instruction Address Error\n");
|
||||
if (reason & MCSR_BUS_RAERR)
|
||||
@@ -318,12 +308,19 @@ void machine_check_exception(struct pt_regs *regs)
|
||||
if (reason & MCSR_BUS_RBERR)
|
||||
printk("Bus - Read Data Bus Error\n");
|
||||
if (reason & MCSR_BUS_WBERR)
|
||||
printk("Bus - Write Data Bus Error\n");
|
||||
printk("Bus - Read Data Bus Error\n");
|
||||
if (reason & MCSR_BUS_IPERR)
|
||||
printk("Bus - Instruction Parity Error\n");
|
||||
if (reason & MCSR_BUS_RPERR)
|
||||
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("Caused by (from MCSR=%lx): ", reason);
|
||||
|
||||
@@ -341,7 +338,14 @@ void machine_check_exception(struct pt_regs *regs)
|
||||
printk("Bus - Read Bus Error on data load\n");
|
||||
if (reason & MCSR_BUS_WRERR)
|
||||
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("Caused by (from SRR1=%lx): ", reason);
|
||||
switch (reason & 0x601F0000) {
|
||||
@@ -371,7 +375,39 @@ void machine_check_exception(struct pt_regs *regs)
|
||||
default:
|
||||
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
|
||||
|
||||
@@ -57,6 +57,14 @@ enum powerpc_pmc_type {
|
||||
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! */
|
||||
struct cpu_spec {
|
||||
/* CPU is matched via (PVR & pvr_mask) == pvr_value */
|
||||
@@ -97,6 +105,11 @@ struct cpu_spec {
|
||||
|
||||
/* Name of processor class, for the ELF AT_PLATFORM entry */
|
||||
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;
|
||||
|
||||
@@ -106,7 +106,8 @@ extern int ptrace_put_reg(struct task_struct *task, int regno,
|
||||
*/
|
||||
#define FULL_REGS(regs) (((regs)->trap & 1) == 0)
|
||||
#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__ */
|
||||
#define TRAP(regs) ((regs)->trap & ~0xF)
|
||||
#ifdef __powerpc64__
|
||||
|
||||
@@ -218,7 +218,6 @@
|
||||
#define CCR1_TCS 0x00000080 /* Timer Clock Select */
|
||||
|
||||
/* Bit definitions for the MCSR. */
|
||||
#ifdef CONFIG_440A
|
||||
#define MCSR_MCS 0x80000000 /* Machine Check Summary */
|
||||
#define MCSR_IB 0x40000000 /* Instruction PLB Error */
|
||||
#define MCSR_DRB 0x20000000 /* Data Read PLB Error */
|
||||
@@ -228,7 +227,7 @@
|
||||
#define MCSR_DCSP 0x02000000 /* D-Cache Search Parity Error */
|
||||
#define MCSR_DCFP 0x01000000 /* D-Cache Flush Parity Error */
|
||||
#define MCSR_IMPE 0x00800000 /* Imprecise Machine Check Exception */
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_E500
|
||||
#define MCSR_MCP 0x80000000UL /* Machine Check Input Pin */
|
||||
#define MCSR_ICPERR 0x40000000UL /* I-Cache Parity Error */
|
||||
|
||||
@@ -207,7 +207,7 @@
|
||||
#define CCR1_TCS 0x00000080 /* Timer Clock Select */
|
||||
|
||||
/* Bit definitions for the MCSR. */
|
||||
#ifdef CONFIG_440A
|
||||
#ifdef CONFIG_4xx
|
||||
#define MCSR_MCS 0x80000000 /* Machine Check Summary */
|
||||
#define MCSR_IB 0x40000000 /* Instruction PLB Error */
|
||||
#define MCSR_DRB 0x20000000 /* Data Read PLB Error */
|
||||
|
||||
Reference in New Issue
Block a user