From: André Hentschel <[email protected]>

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 <[email protected]>
Cc: Will Deacon <[email protected]>
Cc: Jonathan Austin <[email protected]> 

---
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-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to