summaryrefslogtreecommitdiff
path: root/arch/xtensa
diff options
context:
space:
mode:
authorChris Zankel <chris@zankel.net>2013-02-23 19:35:57 -0800
committerChris Zankel <chris@zankel.net>2013-02-23 19:35:57 -0800
commitc50842df47970eab459f13490c152aac85fc02f2 (patch)
treed2933b4d9641067002ba6d662b0abf6ee3011375 /arch/xtensa
parentb0c438e642699f8c47b84de957f30cf586cdbebd (diff)
downloadlinux-3.10-c50842df47970eab459f13490c152aac85fc02f2.tar.gz
linux-3.10-c50842df47970eab459f13490c152aac85fc02f2.tar.bz2
linux-3.10-c50842df47970eab459f13490c152aac85fc02f2.zip
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>
Diffstat (limited to 'arch/xtensa')
-rw-r--r--arch/xtensa/include/asm/elf.h3
-rw-r--r--arch/xtensa/include/asm/ptrace.h3
-rw-r--r--arch/xtensa/kernel/asm-offsets.c1
-rw-r--r--arch/xtensa/kernel/entry.S12
-rw-r--r--arch/xtensa/kernel/process.c5
-rw-r--r--arch/xtensa/kernel/ptrace.c2
-rw-r--r--arch/xtensa/kernel/signal.c6
7 files changed, 25 insertions, 7 deletions
diff --git a/arch/xtensa/include/asm/elf.h b/arch/xtensa/include/asm/elf.h
index 264d5fa450d..eacb25a4171 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 682b1deac1f..81f31bc9dde 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 0701fad170d..1915c7c889b 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 70d5a9e3357..63845f95079 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 0dd5784416d..5cd82e9f601 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],
&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);
diff --git a/arch/xtensa/kernel/ptrace.c b/arch/xtensa/kernel/ptrace.c
index a8b0d579557..562fac66475 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 de34d6be91c..6952d895923 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