aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kernel
diff options
context:
space:
mode:
authorMichael Neuling <mikey@neuling.org>2013-05-26 14:09:39 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2013-05-31 18:29:22 -0400
commit6ce6c629fd8254b3177650de99699682ff7f6707 (patch)
tree08a371c158cbf22868e71d36c0430640d9daf8cc /arch/powerpc/kernel
parent24b92375dc4ec8a15262e8aaaab60b7404d4b1e7 (diff)
powerpc/tm: Abort on emulation and alignment faults
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> Cc: <stable@vger.kernel.org> # v3.9 Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/kernel')
-rw-r--r--arch/powerpc/kernel/traps.c29
1 files changed, 29 insertions, 0 deletions
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
index a7a648f6b750..f18c79c324ef 100644
--- a/arch/powerpc/kernel/traps.c
+++ b/arch/powerpc/kernel/traps.c
@@ -53,6 +53,7 @@
53#ifdef CONFIG_PPC64 53#ifdef CONFIG_PPC64
54#include <asm/firmware.h> 54#include <asm/firmware.h>
55#include <asm/processor.h> 55#include <asm/processor.h>
56#include <asm/tm.h>
56#endif 57#endif
57#include <asm/kexec.h> 58#include <asm/kexec.h>
58#include <asm/ppc-opcode.h> 59#include <asm/ppc-opcode.h>
@@ -932,6 +933,28 @@ static int emulate_isel(struct pt_regs *regs, u32 instword)
932 return 0; 933 return 0;
933} 934}
934 935
936#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
937static inline bool tm_abort_check(struct pt_regs *regs, int cause)
938{
939 /* If we're emulating a load/store in an active transaction, we cannot
940 * emulate it as the kernel operates in transaction suspended context.
941 * We need to abort the transaction. This creates a persistent TM
942 * abort so tell the user what caused it with a new code.
943 */
944 if (MSR_TM_TRANSACTIONAL(regs->msr)) {
945 tm_enable();
946 tm_abort(cause);
947 return true;
948 }
949 return false;
950}
951#else
952static inline bool tm_abort_check(struct pt_regs *regs, int reason)
953{
954 return false;
955}
956#endif
957
935static int emulate_instruction(struct pt_regs *regs) 958static int emulate_instruction(struct pt_regs *regs)
936{ 959{
937 u32 instword; 960 u32 instword;
@@ -971,6 +994,9 @@ static int emulate_instruction(struct pt_regs *regs)
971 994
972 /* Emulate load/store string insn. */ 995 /* Emulate load/store string insn. */
973 if ((instword & PPC_INST_STRING_GEN_MASK) == PPC_INST_STRING) { 996 if ((instword & PPC_INST_STRING_GEN_MASK) == PPC_INST_STRING) {
997 if (tm_abort_check(regs,
998 TM_CAUSE_EMULATE | TM_CAUSE_PERSISTENT))
999 return -EINVAL;
974 PPC_WARN_EMULATED(string, regs); 1000 PPC_WARN_EMULATED(string, regs);
975 return emulate_string_inst(regs, instword); 1001 return emulate_string_inst(regs, instword);
976 } 1002 }
@@ -1148,6 +1174,9 @@ void alignment_exception(struct pt_regs *regs)
1148 if (!arch_irq_disabled_regs(regs)) 1174 if (!arch_irq_disabled_regs(regs))
1149 local_irq_enable(); 1175 local_irq_enable();
1150 1176
1177 if (tm_abort_check(regs, TM_CAUSE_ALIGNMENT | TM_CAUSE_PERSISTENT))
1178 goto bail;
1179
1151 /* we don't implement logging of alignment exceptions */ 1180 /* we don't implement logging of alignment exceptions */
1152 if (!(current->thread.align_ctl & PR_UNALIGN_SIGBUS)) 1181 if (!(current->thread.align_ctl & PR_UNALIGN_SIGBUS))
1153 fixed = fix_alignment(regs); 1182 fixed = fix_alignment(regs);