arm64/task/pthread_start: Fix rare issue with context register location

There is a tiny possibility that when a process is started a trap is
taken which causes a context switch. This moves the kernel stack
unexpectedly and the task start logic no longer works.

Fix this by recording the initial context location, and use that to
trampoline into the user process with interrupts disabled. This ensures
the context stays intact AND the kernel stack is fully unwound before
the user process starts.
This commit is contained in:
Ville Juven 2024-09-10 15:01:24 +03:00 committed by Alan C. Assis
parent 87d9dac817
commit ca4bd482a0
4 changed files with 29 additions and 18 deletions

View file

@ -280,6 +280,9 @@ struct xcptcontext
/* task stack reg context */
uint64_t *regs;
#ifndef CONFIG_BUILD_FLAT
uint64_t *initregs;
#endif
/* task context, for signal process */

View file

@ -108,6 +108,10 @@ void arm64_new_task(struct tcb_s * tcb)
pinitctx->tpidr_el1 = (uint64_t)tcb;
tcb->xcp.regs = (uint64_t *)pinitctx;
#ifndef CONFIG_BUILD_FLAT
tcb->xcp.initregs = tcb->xcp.regs;
#endif
}
/****************************************************************************

View file

@ -68,8 +68,11 @@
void up_pthread_start(pthread_trampoline_t startup,
pthread_startroutine_t entrypt, pthread_addr_t arg)
{
struct tcb_s *tcb = this_task();
uint64_t spsr;
uint64_t *regs = this_task()->xcp.initregs;
/* This must be performed atomically, the C-section ends upon user entry */
enter_critical_section();
/* Set up to enter the user-space pthread start-up function in
* unprivileged mode. We need:
@ -80,11 +83,10 @@ void up_pthread_start(pthread_trampoline_t startup,
* SPSR = user mode
*/
tcb->xcp.regs[REG_ELR] = (uint64_t)startup;
tcb->xcp.regs[REG_X0] = (uint64_t)entrypt;
tcb->xcp.regs[REG_X1] = (uint64_t)arg;
spsr = tcb->xcp.regs[REG_SPSR] & ~SPSR_MODE_MASK;
tcb->xcp.regs[REG_SPSR] = spsr | SPSR_MODE_EL0T;
regs[REG_ELR] = (uint64_t)startup;
regs[REG_X0] = (uint64_t)entrypt;
regs[REG_X1] = (uint64_t)arg;
regs[REG_SPSR] = (regs[REG_SPSR] & ~SPSR_MODE_MASK) | SPSR_MODE_EL0T;
/* Fully unwind the kernel stack and drop to user space */
@ -94,8 +96,8 @@ void up_pthread_start(pthread_trampoline_t startup,
"mov sp, x0\n" /* Stack pointer = context */
"b arm64_exit_exception\n"
:
: "r" (tcb->xcp.regs)
: "memory"
: "r" (regs)
: "x0", "memory"
);
PANIC();

View file

@ -65,8 +65,11 @@
void up_task_start(main_t taskentry, int argc, char *argv[])
{
struct tcb_s *tcb = this_task();
uint64_t spsr;
uint64_t *regs = this_task()->xcp.initregs;
/* This must be performed atomically, the C-section ends upon user entry */
enter_critical_section();
/* Set up to return to the user-space _start function in
* unprivileged mode. We need:
@ -77,11 +80,10 @@ void up_task_start(main_t taskentry, int argc, char *argv[])
* SPSR = user mode
*/
tcb->xcp.regs[REG_ELR] = (uint64_t)taskentry;
tcb->xcp.regs[REG_X0] = (uint64_t)argc;
tcb->xcp.regs[REG_X1] = (uint64_t)argv;
spsr = tcb->xcp.regs[REG_SPSR] & ~SPSR_MODE_MASK;
tcb->xcp.regs[REG_SPSR] = spsr | SPSR_MODE_EL0T;
regs[REG_ELR] = (uint64_t)taskentry;
regs[REG_X0] = (uint64_t)argc;
regs[REG_X1] = (uint64_t)argv;
regs[REG_SPSR] = (regs[REG_SPSR] & ~SPSR_MODE_MASK) | SPSR_MODE_EL0T;
/* Fully unwind the kernel stack and drop to user space */
@ -91,8 +93,8 @@ void up_task_start(main_t taskentry, int argc, char *argv[])
"mov sp, x0\n" /* Stack pointer = context */
"b arm64_exit_exception\n"
:
: "r" (tcb->xcp.regs)
: "memory"
: "r" (regs)
: "x0", "memory"
);
PANIC();