diff options
Diffstat (limited to 'arch/powerpc/kernel/interrupt.c')
| -rw-r--r-- | arch/powerpc/kernel/interrupt.c | 43 | 
1 files changed, 43 insertions, 0 deletions
diff --git a/arch/powerpc/kernel/interrupt.c b/arch/powerpc/kernel/interrupt.c index a73f3f70a657..de10a2697258 100644 --- a/arch/powerpc/kernel/interrupt.c +++ b/arch/powerpc/kernel/interrupt.c @@ -18,6 +18,7 @@  #include <asm/switch_to.h>  #include <asm/syscall.h>  #include <asm/time.h> +#include <asm/tm.h>  #include <asm/unistd.h>  #if defined(CONFIG_PPC_ADV_DEBUG_REGS) && defined(CONFIG_PPC32) @@ -136,6 +137,48 @@ notrace long system_call_exception(long r3, long r4, long r5,  	 */  	irq_soft_mask_regs_set_state(regs, IRQS_ENABLED); +	/* +	 * If system call is called with TM active, set _TIF_RESTOREALL to +	 * prevent RFSCV being used to return to userspace, because POWER9 +	 * TM implementation has problems with this instruction returning to +	 * transactional state. Final register values are not relevant because +	 * the transaction will be aborted upon return anyway. Or in the case +	 * of unsupported_scv SIGILL fault, the return state does not much +	 * matter because it's an edge case. +	 */ +	if (IS_ENABLED(CONFIG_PPC_TRANSACTIONAL_MEM) && +			unlikely(MSR_TM_TRANSACTIONAL(regs->msr))) +		current_thread_info()->flags |= _TIF_RESTOREALL; + +	/* +	 * If the system call was made with a transaction active, doom it and +	 * return without performing the system call. Unless it was an +	 * unsupported scv vector, in which case it's treated like an illegal +	 * instruction. +	 */ +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM +	if (unlikely(MSR_TM_TRANSACTIONAL(regs->msr)) && +	    !trap_is_unsupported_scv(regs)) { +		/* Enable TM in the kernel, and disable EE (for scv) */ +		hard_irq_disable(); +		mtmsr(mfmsr() | MSR_TM); + +		/* tabort, this dooms the transaction, nothing else */ +		asm volatile(".long 0x7c00071d | ((%0) << 16)" +				:: "r"(TM_CAUSE_SYSCALL|TM_CAUSE_PERSISTENT)); + +		/* +		 * Userspace will never see the return value. Execution will +		 * resume after the tbegin. of the aborted transaction with the +		 * checkpointed register state. A context switch could occur +		 * or signal delivered to the process before resuming the +		 * doomed transaction context, but that should all be handled +		 * as expected. +		 */ +		return -ENOSYS; +	} +#endif // CONFIG_PPC_TRANSACTIONAL_MEM +  	local_irq_enable();  	if (unlikely(current_thread_info()->flags & _TIF_SYSCALL_DOTRACE)) {  |