From: André Hentschel <nerv@xxxxxxxxxxx> Since commit a4780adeefd042482f624f5e0d577bf9cdcbb760 the user writeable TLS register on ARM is preserved per thread. This patch does it analogous to the ARM patch, but for compat mode on ARM64. Signed-off-by: André Hentschel <nerv@xxxxxxxxxxx> Cc: Will Deacon <will.deacon@xxxxxxx> Cc: Jonathan Austin <jonathan.austin@xxxxxxx> --- This patch is against Linux 4.1-rc1 (b787f68c36d49bb1d9236f403813641efa74a031) diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h index 20e9591..cd7b8c9 100644 --- a/arch/arm64/include/asm/processor.h +++ b/arch/arm64/include/asm/processor.h @@ -78,7 +78,7 @@ struct cpu_context { struct thread_struct { struct cpu_context cpu_context; /* cpu context */ - unsigned long tp_value; + unsigned long tp_value[2]; /* TLS registers */ struct fpsimd_state fpsimd_state; unsigned long fault_address; /* fault info */ unsigned long fault_code; /* ESR_EL1 value */ diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index c6b1f3b..fc7cc6bc 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -218,7 +218,8 @@ static void tls_thread_flush(void) asm ("msr tpidr_el0, xzr"); if (is_compat_task()) { - current->thread.tp_value = 0; + current->thread.tp_value[0] = 0; + current->thread.tp_value[1] = 0; /* * We need to ensure ordering between the shadow state and the @@ -254,7 +255,7 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start, unsigned long stk_sz, struct task_struct *p) { struct pt_regs *childregs = task_pt_regs(p); - unsigned long tls = p->thread.tp_value; + unsigned long tls = p->thread.tp_value[0]; memset(&p->thread.cpu_context, 0, sizeof(struct cpu_context)); @@ -283,6 +284,11 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start, */ if (clone_flags & CLONE_SETTLS) tls = childregs->regs[3]; + if (is_compat_thread(task_thread_info(p))) { + unsigned long tpuser; + asm("mrs %0, tpidr_el0" : "=r" (tpuser)); + p->thread.tp_value[1] = tpuser; + } } else { memset(childregs, 0, sizeof(struct pt_regs)); childregs->pstate = PSR_MODE_EL1h; @@ -291,7 +297,7 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start, } p->thread.cpu_context.pc = (unsigned long)ret_from_fork; p->thread.cpu_context.sp = (unsigned long)childregs; - p->thread.tp_value = tls; + p->thread.tp_value[0] = tls; ptrace_hw_copy_thread(p); @@ -302,16 +308,17 @@ static void tls_thread_switch(struct task_struct *next) { unsigned long tpidr, tpidrro; - if (!is_compat_task()) { - asm("mrs %0, tpidr_el0" : "=r" (tpidr)); - current->thread.tp_value = tpidr; - } + asm("mrs %0, tpidr_el0" : "=r" (tpidr)); + if (is_compat_task()) + current->thread.tp_value[1] = tpidr; + else + current->thread.tp_value[0] = tpidr; if (is_compat_thread(task_thread_info(next))) { - tpidr = 0; - tpidrro = next->thread.tp_value; + tpidr = next->thread.tp_value[1]; + tpidrro = next->thread.tp_value[0]; } else { - tpidr = next->thread.tp_value; + tpidr = next->thread.tp_value[0]; tpidrro = 0; } diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index d882b83..1eec962 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -533,7 +533,7 @@ static int tls_get(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) { - unsigned long *tls = &target->thread.tp_value; + unsigned long *tls = &target->thread.tp_value[0]; return user_regset_copyout(&pos, &count, &kbuf, &ubuf, tls, 0, -1); } @@ -548,7 +548,7 @@ static int tls_set(struct task_struct *target, const struct user_regset *regset, if (ret) return ret; - target->thread.tp_value = tls; + target->thread.tp_value[0] = tls; return ret; } @@ -1061,7 +1061,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, break; case COMPAT_PTRACE_GET_THREAD_AREA: - ret = put_user((compat_ulong_t)child->thread.tp_value, + ret = put_user((compat_ulong_t)child->thread.tp_value[0], (compat_ulong_t __user *)datap); break; diff --git a/arch/arm64/kernel/sys_compat.c b/arch/arm64/kernel/sys_compat.c index 28c511b..fd4330c 100644 --- a/arch/arm64/kernel/sys_compat.c +++ b/arch/arm64/kernel/sys_compat.c @@ -87,7 +87,7 @@ long compat_arm_syscall(struct pt_regs *regs) return do_compat_cache_op(regs->regs[0], regs->regs[1], regs->regs[2]); case __ARM_NR_compat_set_tls: - current->thread.tp_value = regs->regs[0]; + current->thread.tp_value[0] = regs->regs[0]; /* * Protect against register corruption from context switch. -- To unsubscribe from this list: send the line "unsubscribe linux-arch" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html