diff options
Diffstat (limited to 'arch/powerpc/kernel/traps.c')
| -rw-r--r-- | arch/powerpc/kernel/traps.c | 56 | 
1 files changed, 36 insertions, 20 deletions
| diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index eeff136b83d9..64ff37721fd0 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -1512,23 +1512,11 @@ static void do_program_check(struct pt_regs *regs)  			return;  		} -		if (cpu_has_feature(CPU_FTR_DEXCR_NPHIE) && user_mode(regs)) { -			ppc_inst_t insn; - -			if (get_user_instr(insn, (void __user *)regs->nip)) { -				_exception(SIGSEGV, regs, SEGV_MAPERR, regs->nip); -				return; -			} - -			if (ppc_inst_primary_opcode(insn) == 31 && -			    get_xop(ppc_inst_val(insn)) == OP_31_XOP_HASHCHK) { -				_exception(SIGILL, regs, ILL_ILLOPN, regs->nip); -				return; -			} +		/* User mode considers other cases after enabling IRQs */ +		if (!user_mode(regs)) { +			_exception(SIGTRAP, regs, TRAP_BRKPT, regs->nip); +			return;  		} - -		_exception(SIGTRAP, regs, TRAP_BRKPT, regs->nip); -		return;  	}  #ifdef CONFIG_PPC_TRANSACTIONAL_MEM  	if (reason & REASON_TM) { @@ -1561,16 +1549,44 @@ static void do_program_check(struct pt_regs *regs)  	/*  	 * If we took the program check in the kernel skip down to sending a -	 * SIGILL. The subsequent cases all relate to emulating instructions -	 * which we should only do for userspace. We also do not want to enable -	 * interrupts for kernel faults because that might lead to further -	 * faults, and loose the context of the original exception. +	 * SIGILL. The subsequent cases all relate to user space, such as +	 * emulating instructions which we should only do for user space. We +	 * also do not want to enable interrupts for kernel faults because that +	 * might lead to further faults, and loose the context of the original +	 * exception.  	 */  	if (!user_mode(regs))  		goto sigill;  	interrupt_cond_local_irq_enable(regs); +	/* +	 * (reason & REASON_TRAP) is mostly handled before enabling IRQs, +	 * except get_user_instr() can sleep so we cannot reliably inspect the +	 * current instruction in that context. Now that we know we are +	 * handling a user space trap and can sleep, we can check if the trap +	 * was a hashchk failure. +	 */ +	if (reason & REASON_TRAP) { +		if (cpu_has_feature(CPU_FTR_DEXCR_NPHIE)) { +			ppc_inst_t insn; + +			if (get_user_instr(insn, (void __user *)regs->nip)) { +				_exception(SIGSEGV, regs, SEGV_MAPERR, regs->nip); +				return; +			} + +			if (ppc_inst_primary_opcode(insn) == 31 && +			    get_xop(ppc_inst_val(insn)) == OP_31_XOP_HASHCHK) { +				_exception(SIGILL, regs, ILL_ILLOPN, regs->nip); +				return; +			} +		} + +		_exception(SIGTRAP, regs, TRAP_BRKPT, regs->nip); +		return; +	} +  	/* (reason & REASON_ILLEGAL) would be the obvious thing here,  	 * but there seems to be a hardware bug on the 405GP (RevD)  	 * that means ESR is sometimes set incorrectly - either to | 
