xtensa: add support for TLS
authorChris Zankel <chris@zankel.net>
Sun, 24 Feb 2013 03:35:57 +0000 (19:35 -0800)
committerChris Zankel <chris@zankel.net>
Sun, 24 Feb 2013 03:35:57 +0000 (19:35 -0800)
The Xtensa architecture provides a global register called THREADPTR
for the purpose of Thread Local Storage (TLS) support. This allows us
to use a fairly simple implementation, keeping the thread pointer in
the regset and simply saving and restoring it upon entering/exiting
the from user space.

Signed-off-by: Chris Zankel <chris@zankel.net>
arch/xtensa/include/asm/elf.h
arch/xtensa/include/asm/ptrace.h
arch/xtensa/kernel/asm-offsets.c
arch/xtensa/kernel/entry.S
arch/xtensa/kernel/process.c
arch/xtensa/kernel/ptrace.c
arch/xtensa/kernel/signal.c

index 264d5fa..eacb25a 100644 (file)
@@ -84,7 +84,8 @@ typedef struct {
        elf_greg_t sar;
        elf_greg_t windowstart;
        elf_greg_t windowbase;
-       elf_greg_t reserved[8+48];
+       elf_greg_t threadptr;
+       elf_greg_t reserved[7+48];
        elf_greg_t a[64];
 } xtensa_gregset_t;
 
index 682b1de..81f31bc 100644 (file)
@@ -38,6 +38,7 @@ struct pt_regs {
        unsigned long syscall;          /*  56 */
        unsigned long icountlevel;      /*  60 */
        unsigned long scompare1;        /*  64 */
+       unsigned long threadptr;        /*  68 */
 
        /* Additional configurable registers that are used by the compiler. */
        xtregs_opt_t xtregs_opt;
@@ -48,7 +49,7 @@ struct pt_regs {
        /* current register frame.
         * Note: The ESF for kernel exceptions ends after 16 registers!
         */
-       unsigned long areg[16];         /* 128 (64) */
+       unsigned long areg[16];
 };
 
 #include <variant/core.h>
index 0701fad..1915c7c 100644 (file)
@@ -42,6 +42,7 @@ int main(void)
        DEFINE(PT_ICOUNTLEVEL, offsetof (struct pt_regs, icountlevel));
        DEFINE(PT_SYSCALL, offsetof (struct pt_regs, syscall));
        DEFINE(PT_SCOMPARE1, offsetof(struct pt_regs, scompare1));
+       DEFINE(PT_THREADPTR, offsetof(struct pt_regs, threadptr));
        DEFINE(PT_AREG, offsetof (struct pt_regs, areg[0]));
        DEFINE(PT_AREG0, offsetof (struct pt_regs, areg[0]));
        DEFINE(PT_AREG1, offsetof (struct pt_regs, areg[1]));
index 70d5a9e..63845f9 100644 (file)
@@ -130,6 +130,11 @@ _user_exception:
        s32i    a3, a1, PT_SAR
        s32i    a2, a1, PT_ICOUNTLEVEL
 
+#if XCHAL_HAVE_THREADPTR
+       rur     a2, threadptr
+       s32i    a2, a1, PT_THREADPTR
+#endif
+
        /* Rotate ws so that the current windowbase is at bit0. */
        /* Assume ws = xxwww1yyyy. Rotate ws right, so that a2 = yyyyxxwww1 */
 
@@ -510,6 +515,11 @@ user_exception_exit:
         *       (if we have restored WSBITS-1 frames).
         */
 
+#if XCHAL_HAVE_THREADPTR
+       l32i    a3, a1, PT_THREADPTR
+       wur     a3, threadptr
+#endif
+
 2:     j       common_exception_exit
 
        /* This is the kernel exception exit.
@@ -1955,7 +1965,7 @@ ENTRY(_switch_to)
        s32i    a6, a3, EXC_TABLE_FIXUP
        s32i    a7, a3, EXC_TABLE_KSTK
 
-       /* restore context of the task that 'next' addresses */
+       /* restore context of the task 'next' */
 
        l32i    a0, a13, THREAD_RA      # restore return address
        l32i    a1, a13, THREAD_SP      # restore stack pointer
index 0dd5784..5cd82e9 100644 (file)
@@ -259,9 +259,10 @@ int copy_thread(unsigned long clone_flags, unsigned long usp_thread_fn,
                        memcpy(&childregs->areg[XCHAL_NUM_AREGS - len/4],
                               &regs->areg[XCHAL_NUM_AREGS - len/4], len);
                }
-// FIXME: we need to set THREADPTR in thread_info...
+
+               /* The thread pointer is passed in the '4th argument' (= a5) */
                if (clone_flags & CLONE_SETTLS)
-                       childregs->areg[2] = childregs->areg[6];
+                       childregs->threadptr = childregs->areg[5];
        } else {
                p->thread.ra = MAKE_RA_FOR_CALL(
                                (unsigned long)ret_from_kernel_thread, 1);
index a8b0d57..562fac6 100644 (file)
@@ -66,6 +66,7 @@ int ptrace_getregs(struct task_struct *child, void __user *uregs)
        __put_user(regs->lcount, &gregset->lcount);
        __put_user(regs->windowstart, &gregset->windowstart);
        __put_user(regs->windowbase, &gregset->windowbase);
+       __put_user(regs->threadptr, &gregset->threadptr);
 
        for (i = 0; i < XCHAL_NUM_AREGS; i++)
                __put_user(regs->areg[i],
@@ -92,6 +93,7 @@ int ptrace_setregs(struct task_struct *child, void __user *uregs)
        __get_user(regs->lcount, &gregset->lcount);
        __get_user(ws, &gregset->windowstart);
        __get_user(wb, &gregset->windowbase);
+       __get_user(regs->threadptr, &gregset->threadptr);
 
        regs->ps = (regs->ps & ~ps_mask) | (ps & ps_mask) | (1 << PS_EXCM_BIT);
 
index de34d6b..6952d89 100644 (file)
@@ -337,7 +337,7 @@ static int setup_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
        struct rt_sigframe *frame;
        int err = 0;
        int signal;
-       unsigned long sp, ra;
+       unsigned long sp, ra, tp;
 
        sp = regs->areg[1];
 
@@ -395,7 +395,8 @@ static int setup_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
         * Return context not modified until this point.
         */
 
-       /* Set up registers for signal handler */
+       /* Set up registers for signal handler; preserve the threadptr */
+       tp = regs->threadptr;
        start_thread(regs, (unsigned long) ka->sa.sa_handler,
                     (unsigned long) frame);
 
@@ -406,6 +407,7 @@ static int setup_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
        regs->areg[6] = (unsigned long) signal;
        regs->areg[7] = (unsigned long) &frame->info;
        regs->areg[8] = (unsigned long) &frame->uc;
+       regs->threadptr = tp;
 
        /* Set access mode to USER_DS.  Nomenclature is outdated, but
         * functionality is used in uaccess.h