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>
This commit is contained in:
parent
b0c438e642
commit
c50842df47
7 changed files with 25 additions and 7 deletions
|
@ -84,7 +84,8 @@ typedef struct {
|
||||||
elf_greg_t sar;
|
elf_greg_t sar;
|
||||||
elf_greg_t windowstart;
|
elf_greg_t windowstart;
|
||||||
elf_greg_t windowbase;
|
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];
|
elf_greg_t a[64];
|
||||||
} xtensa_gregset_t;
|
} xtensa_gregset_t;
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ struct pt_regs {
|
||||||
unsigned long syscall; /* 56 */
|
unsigned long syscall; /* 56 */
|
||||||
unsigned long icountlevel; /* 60 */
|
unsigned long icountlevel; /* 60 */
|
||||||
unsigned long scompare1; /* 64 */
|
unsigned long scompare1; /* 64 */
|
||||||
|
unsigned long threadptr; /* 68 */
|
||||||
|
|
||||||
/* Additional configurable registers that are used by the compiler. */
|
/* Additional configurable registers that are used by the compiler. */
|
||||||
xtregs_opt_t xtregs_opt;
|
xtregs_opt_t xtregs_opt;
|
||||||
|
@ -48,7 +49,7 @@ struct pt_regs {
|
||||||
/* current register frame.
|
/* current register frame.
|
||||||
* Note: The ESF for kernel exceptions ends after 16 registers!
|
* Note: The ESF for kernel exceptions ends after 16 registers!
|
||||||
*/
|
*/
|
||||||
unsigned long areg[16]; /* 128 (64) */
|
unsigned long areg[16];
|
||||||
};
|
};
|
||||||
|
|
||||||
#include <variant/core.h>
|
#include <variant/core.h>
|
||||||
|
|
|
@ -42,6 +42,7 @@ int main(void)
|
||||||
DEFINE(PT_ICOUNTLEVEL, offsetof (struct pt_regs, icountlevel));
|
DEFINE(PT_ICOUNTLEVEL, offsetof (struct pt_regs, icountlevel));
|
||||||
DEFINE(PT_SYSCALL, offsetof (struct pt_regs, syscall));
|
DEFINE(PT_SYSCALL, offsetof (struct pt_regs, syscall));
|
||||||
DEFINE(PT_SCOMPARE1, offsetof(struct pt_regs, scompare1));
|
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_AREG, offsetof (struct pt_regs, areg[0]));
|
||||||
DEFINE(PT_AREG0, offsetof (struct pt_regs, areg[0]));
|
DEFINE(PT_AREG0, offsetof (struct pt_regs, areg[0]));
|
||||||
DEFINE(PT_AREG1, offsetof (struct pt_regs, areg[1]));
|
DEFINE(PT_AREG1, offsetof (struct pt_regs, areg[1]));
|
||||||
|
|
|
@ -130,6 +130,11 @@ _user_exception:
|
||||||
s32i a3, a1, PT_SAR
|
s32i a3, a1, PT_SAR
|
||||||
s32i a2, a1, PT_ICOUNTLEVEL
|
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. */
|
/* Rotate ws so that the current windowbase is at bit0. */
|
||||||
/* Assume ws = xxwww1yyyy. Rotate ws right, so that a2 = yyyyxxwww1 */
|
/* 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 we have restored WSBITS-1 frames).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#if XCHAL_HAVE_THREADPTR
|
||||||
|
l32i a3, a1, PT_THREADPTR
|
||||||
|
wur a3, threadptr
|
||||||
|
#endif
|
||||||
|
|
||||||
2: j common_exception_exit
|
2: j common_exception_exit
|
||||||
|
|
||||||
/* This is the kernel exception exit.
|
/* This is the kernel exception exit.
|
||||||
|
@ -1955,7 +1965,7 @@ ENTRY(_switch_to)
|
||||||
s32i a6, a3, EXC_TABLE_FIXUP
|
s32i a6, a3, EXC_TABLE_FIXUP
|
||||||
s32i a7, a3, EXC_TABLE_KSTK
|
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 a0, a13, THREAD_RA # restore return address
|
||||||
l32i a1, a13, THREAD_SP # restore stack pointer
|
l32i a1, a13, THREAD_SP # restore stack pointer
|
||||||
|
|
|
@ -259,9 +259,10 @@ int copy_thread(unsigned long clone_flags, unsigned long usp_thread_fn,
|
||||||
memcpy(&childregs->areg[XCHAL_NUM_AREGS - len/4],
|
memcpy(&childregs->areg[XCHAL_NUM_AREGS - len/4],
|
||||||
®s->areg[XCHAL_NUM_AREGS - len/4], len);
|
®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)
|
if (clone_flags & CLONE_SETTLS)
|
||||||
childregs->areg[2] = childregs->areg[6];
|
childregs->threadptr = childregs->areg[5];
|
||||||
} else {
|
} else {
|
||||||
p->thread.ra = MAKE_RA_FOR_CALL(
|
p->thread.ra = MAKE_RA_FOR_CALL(
|
||||||
(unsigned long)ret_from_kernel_thread, 1);
|
(unsigned long)ret_from_kernel_thread, 1);
|
||||||
|
|
|
@ -66,6 +66,7 @@ int ptrace_getregs(struct task_struct *child, void __user *uregs)
|
||||||
__put_user(regs->lcount, &gregset->lcount);
|
__put_user(regs->lcount, &gregset->lcount);
|
||||||
__put_user(regs->windowstart, &gregset->windowstart);
|
__put_user(regs->windowstart, &gregset->windowstart);
|
||||||
__put_user(regs->windowbase, &gregset->windowbase);
|
__put_user(regs->windowbase, &gregset->windowbase);
|
||||||
|
__put_user(regs->threadptr, &gregset->threadptr);
|
||||||
|
|
||||||
for (i = 0; i < XCHAL_NUM_AREGS; i++)
|
for (i = 0; i < XCHAL_NUM_AREGS; i++)
|
||||||
__put_user(regs->areg[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(regs->lcount, &gregset->lcount);
|
||||||
__get_user(ws, &gregset->windowstart);
|
__get_user(ws, &gregset->windowstart);
|
||||||
__get_user(wb, &gregset->windowbase);
|
__get_user(wb, &gregset->windowbase);
|
||||||
|
__get_user(regs->threadptr, &gregset->threadptr);
|
||||||
|
|
||||||
regs->ps = (regs->ps & ~ps_mask) | (ps & ps_mask) | (1 << PS_EXCM_BIT);
|
regs->ps = (regs->ps & ~ps_mask) | (ps & ps_mask) | (1 << PS_EXCM_BIT);
|
||||||
|
|
||||||
|
|
|
@ -337,7 +337,7 @@ static int setup_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
|
||||||
struct rt_sigframe *frame;
|
struct rt_sigframe *frame;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
int signal;
|
int signal;
|
||||||
unsigned long sp, ra;
|
unsigned long sp, ra, tp;
|
||||||
|
|
||||||
sp = regs->areg[1];
|
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.
|
* 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,
|
start_thread(regs, (unsigned long) ka->sa.sa_handler,
|
||||||
(unsigned long) frame);
|
(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[6] = (unsigned long) signal;
|
||||||
regs->areg[7] = (unsigned long) &frame->info;
|
regs->areg[7] = (unsigned long) &frame->info;
|
||||||
regs->areg[8] = (unsigned long) &frame->uc;
|
regs->areg[8] = (unsigned long) &frame->uc;
|
||||||
|
regs->threadptr = tp;
|
||||||
|
|
||||||
/* Set access mode to USER_DS. Nomenclature is outdated, but
|
/* Set access mode to USER_DS. Nomenclature is outdated, but
|
||||||
* functionality is used in uaccess.h
|
* functionality is used in uaccess.h
|
||||||
|
|
Loading…
Add table
Reference in a new issue