Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

[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
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -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)
+105 −0

File changed.

Preview size limit exceeded, changes collapsed.

+10 −4
Original line number Diff line number Diff line
@@ -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)
 *
+1 −1
Original line number Diff line number Diff line
@@ -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
Original line number Diff line number Diff line
@@ -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);
	}
	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");

	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
Loading