diff options
author | Chris Zankel <chris@zankel.net> | 2013-02-23 22:35:57 -0500 |
---|---|---|
committer | Chris Zankel <chris@zankel.net> | 2013-02-23 22:35:57 -0500 |
commit | c50842df47970eab459f13490c152aac85fc02f2 (patch) | |
tree | d2933b4d9641067002ba6d662b0abf6ee3011375 /arch/xtensa | |
parent | b0c438e642699f8c47b84de957f30cf586cdbebd (diff) |
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.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 { | |||
84 | elf_greg_t sar; | 84 | elf_greg_t sar; |
85 | elf_greg_t windowstart; | 85 | elf_greg_t windowstart; |
86 | elf_greg_t windowbase; | 86 | elf_greg_t windowbase; |
87 | elf_greg_t reserved[8+48]; | 87 | elf_greg_t threadptr; |
88 | elf_greg_t reserved[7+48]; | ||
88 | elf_greg_t a[64]; | 89 | elf_greg_t a[64]; |
89 | } xtensa_gregset_t; | 90 | } xtensa_gregset_t; |
90 | 91 | ||
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 { | |||
38 | unsigned long syscall; /* 56 */ | 38 | unsigned long syscall; /* 56 */ |
39 | unsigned long icountlevel; /* 60 */ | 39 | unsigned long icountlevel; /* 60 */ |
40 | unsigned long scompare1; /* 64 */ | 40 | unsigned long scompare1; /* 64 */ |
41 | unsigned long threadptr; /* 68 */ | ||
41 | 42 | ||
42 | /* Additional configurable registers that are used by the compiler. */ | 43 | /* Additional configurable registers that are used by the compiler. */ |
43 | xtregs_opt_t xtregs_opt; | 44 | xtregs_opt_t xtregs_opt; |
@@ -48,7 +49,7 @@ struct pt_regs { | |||
48 | /* current register frame. | 49 | /* current register frame. |
49 | * Note: The ESF for kernel exceptions ends after 16 registers! | 50 | * Note: The ESF for kernel exceptions ends after 16 registers! |
50 | */ | 51 | */ |
51 | unsigned long areg[16]; /* 128 (64) */ | 52 | unsigned long areg[16]; |
52 | }; | 53 | }; |
53 | 54 | ||
54 | #include <variant/core.h> | 55 | #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) | |||
42 | DEFINE(PT_ICOUNTLEVEL, offsetof (struct pt_regs, icountlevel)); | 42 | DEFINE(PT_ICOUNTLEVEL, offsetof (struct pt_regs, icountlevel)); |
43 | DEFINE(PT_SYSCALL, offsetof (struct pt_regs, syscall)); | 43 | DEFINE(PT_SYSCALL, offsetof (struct pt_regs, syscall)); |
44 | DEFINE(PT_SCOMPARE1, offsetof(struct pt_regs, scompare1)); | 44 | DEFINE(PT_SCOMPARE1, offsetof(struct pt_regs, scompare1)); |
45 | DEFINE(PT_THREADPTR, offsetof(struct pt_regs, threadptr)); | ||
45 | DEFINE(PT_AREG, offsetof (struct pt_regs, areg[0])); | 46 | DEFINE(PT_AREG, offsetof (struct pt_regs, areg[0])); |
46 | DEFINE(PT_AREG0, offsetof (struct pt_regs, areg[0])); | 47 | DEFINE(PT_AREG0, offsetof (struct pt_regs, areg[0])); |
47 | DEFINE(PT_AREG1, offsetof (struct pt_regs, areg[1])); | 48 | 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: | |||
130 | s32i a3, a1, PT_SAR | 130 | s32i a3, a1, PT_SAR |
131 | s32i a2, a1, PT_ICOUNTLEVEL | 131 | s32i a2, a1, PT_ICOUNTLEVEL |
132 | 132 | ||
133 | #if XCHAL_HAVE_THREADPTR | ||
134 | rur a2, threadptr | ||
135 | s32i a2, a1, PT_THREADPTR | ||
136 | #endif | ||
137 | |||
133 | /* Rotate ws so that the current windowbase is at bit0. */ | 138 | /* Rotate ws so that the current windowbase is at bit0. */ |
134 | /* Assume ws = xxwww1yyyy. Rotate ws right, so that a2 = yyyyxxwww1 */ | 139 | /* Assume ws = xxwww1yyyy. Rotate ws right, so that a2 = yyyyxxwww1 */ |
135 | 140 | ||
@@ -510,6 +515,11 @@ user_exception_exit: | |||
510 | * (if we have restored WSBITS-1 frames). | 515 | * (if we have restored WSBITS-1 frames). |
511 | */ | 516 | */ |
512 | 517 | ||
518 | #if XCHAL_HAVE_THREADPTR | ||
519 | l32i a3, a1, PT_THREADPTR | ||
520 | wur a3, threadptr | ||
521 | #endif | ||
522 | |||
513 | 2: j common_exception_exit | 523 | 2: j common_exception_exit |
514 | 524 | ||
515 | /* This is the kernel exception exit. | 525 | /* This is the kernel exception exit. |
@@ -1955,7 +1965,7 @@ ENTRY(_switch_to) | |||
1955 | s32i a6, a3, EXC_TABLE_FIXUP | 1965 | s32i a6, a3, EXC_TABLE_FIXUP |
1956 | s32i a7, a3, EXC_TABLE_KSTK | 1966 | s32i a7, a3, EXC_TABLE_KSTK |
1957 | 1967 | ||
1958 | /* restore context of the task that 'next' addresses */ | 1968 | /* restore context of the task 'next' */ |
1959 | 1969 | ||
1960 | l32i a0, a13, THREAD_RA # restore return address | 1970 | l32i a0, a13, THREAD_RA # restore return address |
1961 | l32i a1, a13, THREAD_SP # restore stack pointer | 1971 | 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, | |||
259 | memcpy(&childregs->areg[XCHAL_NUM_AREGS - len/4], | 259 | memcpy(&childregs->areg[XCHAL_NUM_AREGS - len/4], |
260 | ®s->areg[XCHAL_NUM_AREGS - len/4], len); | 260 | ®s->areg[XCHAL_NUM_AREGS - len/4], len); |
261 | } | 261 | } |
262 | // FIXME: we need to set THREADPTR in thread_info... | 262 | |
263 | /* The thread pointer is passed in the '4th argument' (= a5) */ | ||
263 | if (clone_flags & CLONE_SETTLS) | 264 | if (clone_flags & CLONE_SETTLS) |
264 | childregs->areg[2] = childregs->areg[6]; | 265 | childregs->threadptr = childregs->areg[5]; |
265 | } else { | 266 | } else { |
266 | p->thread.ra = MAKE_RA_FOR_CALL( | 267 | p->thread.ra = MAKE_RA_FOR_CALL( |
267 | (unsigned long)ret_from_kernel_thread, 1); | 268 | (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) | |||
66 | __put_user(regs->lcount, &gregset->lcount); | 66 | __put_user(regs->lcount, &gregset->lcount); |
67 | __put_user(regs->windowstart, &gregset->windowstart); | 67 | __put_user(regs->windowstart, &gregset->windowstart); |
68 | __put_user(regs->windowbase, &gregset->windowbase); | 68 | __put_user(regs->windowbase, &gregset->windowbase); |
69 | __put_user(regs->threadptr, &gregset->threadptr); | ||
69 | 70 | ||
70 | for (i = 0; i < XCHAL_NUM_AREGS; i++) | 71 | for (i = 0; i < XCHAL_NUM_AREGS; i++) |
71 | __put_user(regs->areg[i], | 72 | __put_user(regs->areg[i], |
@@ -92,6 +93,7 @@ int ptrace_setregs(struct task_struct *child, void __user *uregs) | |||
92 | __get_user(regs->lcount, &gregset->lcount); | 93 | __get_user(regs->lcount, &gregset->lcount); |
93 | __get_user(ws, &gregset->windowstart); | 94 | __get_user(ws, &gregset->windowstart); |
94 | __get_user(wb, &gregset->windowbase); | 95 | __get_user(wb, &gregset->windowbase); |
96 | __get_user(regs->threadptr, &gregset->threadptr); | ||
95 | 97 | ||
96 | regs->ps = (regs->ps & ~ps_mask) | (ps & ps_mask) | (1 << PS_EXCM_BIT); | 98 | regs->ps = (regs->ps & ~ps_mask) | (ps & ps_mask) | (1 << PS_EXCM_BIT); |
97 | 99 | ||
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, | |||
337 | struct rt_sigframe *frame; | 337 | struct rt_sigframe *frame; |
338 | int err = 0; | 338 | int err = 0; |
339 | int signal; | 339 | int signal; |
340 | unsigned long sp, ra; | 340 | unsigned long sp, ra, tp; |
341 | 341 | ||
342 | sp = regs->areg[1]; | 342 | sp = regs->areg[1]; |
343 | 343 | ||
@@ -395,7 +395,8 @@ static int setup_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | |||
395 | * Return context not modified until this point. | 395 | * Return context not modified until this point. |
396 | */ | 396 | */ |
397 | 397 | ||
398 | /* Set up registers for signal handler */ | 398 | /* Set up registers for signal handler; preserve the threadptr */ |
399 | tp = regs->threadptr; | ||
399 | start_thread(regs, (unsigned long) ka->sa.sa_handler, | 400 | start_thread(regs, (unsigned long) ka->sa.sa_handler, |
400 | (unsigned long) frame); | 401 | (unsigned long) frame); |
401 | 402 | ||
@@ -406,6 +407,7 @@ static int setup_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | |||
406 | regs->areg[6] = (unsigned long) signal; | 407 | regs->areg[6] = (unsigned long) signal; |
407 | regs->areg[7] = (unsigned long) &frame->info; | 408 | regs->areg[7] = (unsigned long) &frame->info; |
408 | regs->areg[8] = (unsigned long) &frame->uc; | 409 | regs->areg[8] = (unsigned long) &frame->uc; |
410 | regs->threadptr = tp; | ||
409 | 411 | ||
410 | /* Set access mode to USER_DS. Nomenclature is outdated, but | 412 | /* Set access mode to USER_DS. Nomenclature is outdated, but |
411 | * functionality is used in uaccess.h | 413 | * functionality is used in uaccess.h |