| From 6ce6c629fd8254b3177650de99699682ff7f6707 Mon Sep 17 00:00:00 2001 | 
 | From: Michael Neuling <mikey@neuling.org> | 
 | Date: Sun, 26 May 2013 18:09:39 +0000 | 
 | Subject: powerpc/tm: Abort on emulation and alignment faults | 
 |  | 
 | From: Michael Neuling <mikey@neuling.org> | 
 |  | 
 | commit 6ce6c629fd8254b3177650de99699682ff7f6707 upstream. | 
 |  | 
 | If we are emulating an instruction inside an active user transaction that | 
 | touches memory, the kernel can't emulate it as it operates in transactional | 
 | suspend context.  We need to abort these transactions and send them back to | 
 | userspace for the hardware to rollback. | 
 |  | 
 | We can service these if the user transaction is in suspend mode, since the | 
 | kernel will operate in the same suspend context. | 
 |  | 
 | This adds a check to all alignment faults and to specific instruction | 
 | emulations (only string instructions for now).  If the user process is in an | 
 | active (non-suspended) transaction, we abort the transaction go back to | 
 | userspace allowing the HW to roll back the transaction and tell the user of the | 
 | failure.  This also adds new tm abort cause codes to report the reason of the | 
 | persistent error to the user. | 
 |  | 
 | Crappy test case here http://neuling.org/devel/junkcode/aligntm.c | 
 |  | 
 | Signed-off-by: Michael Neuling <mikey@neuling.org> | 
 | Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> | 
 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 
 |  | 
 | --- | 
 |  Documentation/powerpc/transactional_memory.txt |    7 ++++-- | 
 |  arch/powerpc/include/asm/reg.h                 |    2 + | 
 |  arch/powerpc/kernel/traps.c                    |   29 +++++++++++++++++++++++++ | 
 |  3 files changed, 36 insertions(+), 2 deletions(-) | 
 |  | 
 | --- a/Documentation/powerpc/transactional_memory.txt | 
 | +++ b/Documentation/powerpc/transactional_memory.txt | 
 | @@ -180,9 +180,12 @@ kernel aborted a transaction: | 
 |                          transactions for consistency will use this. | 
 |   TM_CAUSE_SIGNAL        Signal delivered. | 
 |   TM_CAUSE_MISC          Currently unused. | 
 | + TM_CAUSE_ALIGNMENT     Alignment fault. | 
 | + TM_CAUSE_EMULATE       Emulation that touched memory. | 
 |   | 
 | -These can be checked by the user program's abort handler as TEXASR[0:7]. | 
 | - | 
 | +These can be checked by the user program's abort handler as TEXASR[0:7].  If | 
 | +bit 7 is set, it indicates that the error is consider persistent.  For example | 
 | +a TM_CAUSE_ALIGNMENT will be persistent while a TM_CAUSE_RESCHED will not.q | 
 |   | 
 |  GDB | 
 |  === | 
 | --- a/arch/powerpc/include/asm/reg.h | 
 | +++ b/arch/powerpc/include/asm/reg.h | 
 | @@ -122,6 +122,8 @@ | 
 |  #define TM_CAUSE_SYSCALL	0xd8  /* future use */ | 
 |  #define TM_CAUSE_MISC		0xd6  /* future use */ | 
 |  #define TM_CAUSE_SIGNAL		0xd4 | 
 | +#define TM_CAUSE_ALIGNMENT	0xd2 | 
 | +#define TM_CAUSE_EMULATE	0xd0 | 
 |   | 
 |  #if defined(CONFIG_PPC_BOOK3S_64) | 
 |  #define MSR_64BIT	MSR_SF | 
 | --- a/arch/powerpc/kernel/traps.c | 
 | +++ b/arch/powerpc/kernel/traps.c | 
 | @@ -52,6 +52,7 @@ | 
 |  #ifdef CONFIG_PPC64 | 
 |  #include <asm/firmware.h> | 
 |  #include <asm/processor.h> | 
 | +#include <asm/tm.h> | 
 |  #endif | 
 |  #include <asm/kexec.h> | 
 |  #include <asm/ppc-opcode.h> | 
 | @@ -913,6 +914,28 @@ static int emulate_isel(struct pt_regs * | 
 |  	return 0; | 
 |  } | 
 |   | 
 | +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM | 
 | +static inline bool tm_abort_check(struct pt_regs *regs, int cause) | 
 | +{ | 
 | +        /* If we're emulating a load/store in an active transaction, we cannot | 
 | +         * emulate it as the kernel operates in transaction suspended context. | 
 | +         * We need to abort the transaction.  This creates a persistent TM | 
 | +         * abort so tell the user what caused it with a new code. | 
 | +	 */ | 
 | +	if (MSR_TM_TRANSACTIONAL(regs->msr)) { | 
 | +		tm_enable(); | 
 | +		tm_abort(cause); | 
 | +		return true; | 
 | +	} | 
 | +	return false; | 
 | +} | 
 | +#else | 
 | +static inline bool tm_abort_check(struct pt_regs *regs, int reason) | 
 | +{ | 
 | +	return false; | 
 | +} | 
 | +#endif | 
 | + | 
 |  static int emulate_instruction(struct pt_regs *regs) | 
 |  { | 
 |  	u32 instword; | 
 | @@ -952,6 +975,9 @@ static int emulate_instruction(struct pt | 
 |   | 
 |  	/* Emulate load/store string insn. */ | 
 |  	if ((instword & PPC_INST_STRING_GEN_MASK) == PPC_INST_STRING) { | 
 | +		if (tm_abort_check(regs, | 
 | +				   TM_CAUSE_EMULATE | TM_CAUSE_PERSISTENT)) | 
 | +			return -EINVAL; | 
 |  		PPC_WARN_EMULATED(string, regs); | 
 |  		return emulate_string_inst(regs, instword); | 
 |  	} | 
 | @@ -1124,6 +1150,9 @@ void alignment_exception(struct pt_regs | 
 |  	if (!arch_irq_disabled_regs(regs)) | 
 |  		local_irq_enable(); | 
 |   | 
 | +	if (tm_abort_check(regs, TM_CAUSE_ALIGNMENT | TM_CAUSE_PERSISTENT)) | 
 | +		goto bail; | 
 | + | 
 |  	/* we don't implement logging of alignment exceptions */ | 
 |  	if (!(current->thread.align_ctl & PR_UNALIGN_SIGBUS)) | 
 |  		fixed = fix_alignment(regs); |