diff options
author | Chris Zankel <chris@zankel.net> | 2013-02-24 07:35:57 +0400 |
---|---|---|
committer | Chris Zankel <chris@zankel.net> | 2013-02-24 07:35:57 +0400 |
commit | c50842df47970eab459f13490c152aac85fc02f2 (patch) | |
tree | d2933b4d9641067002ba6d662b0abf6ee3011375 | |
parent | b0c438e642699f8c47b84de957f30cf586cdbebd (diff) | |
download | linux-c50842df47970eab459f13490c152aac85fc02f2.tar.xz |
xtensa: add support for TLS
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>
-rw-r--r-- | arch/xtensa/include/asm/elf.h | 3 | ||||
-rw-r--r-- | arch/xtensa/include/asm/ptrace.h | 3 | ||||
-rw-r--r-- | arch/xtensa/kernel/asm-offsets.c | 1 | ||||
-rw-r--r-- | arch/xtensa/kernel/entry.S | 12 | ||||
-rw-r--r-- | arch/xtensa/kernel/process.c | 5 | ||||
-rw-r--r-- | arch/xtensa/kernel/ptrace.c | 2 | ||||
-rw-r--r-- | arch/xtensa/kernel/signal.c | 6 |
7 files changed, 25 insertions, 7 deletions
diff --git a/arch/xtensa/include/asm/elf.h b/arch/xtensa/include/asm/elf.h index 264d5fa450d8..eacb25a41718 100644 --- a/arch/xtensa/include/asm/elf.h +++ b/arch/xtensa/include/asm/elf.h @@ -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; diff --git a/arch/xtensa/include/asm/ptrace.h b/arch/xtensa/include/asm/ptrace.h index 682b1deac1f2..81f31bc9dde0 100644 --- a/arch/xtensa/include/asm/ptrace.h +++ b/arch/xtensa/include/asm/ptrace.h @@ -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> diff --git a/arch/xtensa/kernel/asm-offsets.c b/arch/xtensa/kernel/asm-offsets.c index 0701fad170db..1915c7c889ba 100644 --- a/arch/xtensa/kernel/asm-offsets.c +++ b/arch/xtensa/kernel/asm-offsets.c @@ -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])); diff --git a/arch/xtensa/kernel/entry.S b/arch/xtensa/kernel/entry.S index 70d5a9e33573..63845f950792 100644 --- a/arch/xtensa/kernel/entry.S +++ b/arch/xtensa/kernel/entry.S @@ -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 diff --git a/arch/xtensa/kernel/process.c b/arch/xtensa/kernel/process.c index 0dd5784416d3..5cd82e9f601c 100644 --- a/arch/xtensa/kernel/process.c +++ b/arch/xtensa/kernel/process.c @@ -259,9 +259,10 @@ int copy_thread(unsigned long clone_flags, unsigned long usp_thread_fn, memcpy(&childregs->areg[XCHAL_NUM_AREGS - len/4], ®s->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); diff --git a/arch/xtensa/kernel/ptrace.c b/arch/xtensa/kernel/ptrace.c index a8b0d5795571..562fac664751 100644 --- a/arch/xtensa/kernel/ptrace.c +++ b/arch/xtensa/kernel/ptrace.c @@ -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); diff --git a/arch/xtensa/kernel/signal.c b/arch/xtensa/kernel/signal.c index de34d6be91cd..6952d8959236 100644 --- a/arch/xtensa/kernel/signal.c +++ b/arch/xtensa/kernel/signal.c @@ -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 |