From afc0599d49cefe67be188edb85c7de201360cb0e Mon Sep 17 00:00:00 2001 From: Bahadir Balban Date: Mon, 1 Sep 2008 16:19:03 +0300 Subject: [PATCH] Saving all registers in syscall so that return from fork is easier. Child needs rewound function stack in order to reach registers r9-r12 that have original userspace values. But we jump to return_from_syscall without rewinding the stack. Therefore to ease context restore, we save r9-r12 on the stack as well upon syscall entry. --- include/l4/glue/arm/syscall.h | 4 ++++ src/api/thread.c | 19 +++++++++++-------- src/arch/arm/vectors.S | 17 +++++++++++++---- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/include/l4/glue/arm/syscall.h b/include/l4/glue/arm/syscall.h index c0c7071..54b1eed 100644 --- a/include/l4/glue/arm/syscall.h +++ b/include/l4/glue/arm/syscall.h @@ -33,6 +33,10 @@ typedef struct syscall_context { u32 r6; /* MR3 */ u32 r7; /* MR4 */ u32 r8; /* MR5 */ + u32 r9; + u32 r10; + u32 r11; + u32 r12; u32 sp_usr; u32 lr_usr; } __attribute__((__packed__)) syscall_context_t; diff --git a/src/api/thread.c b/src/api/thread.c index 70f0635..bba153f 100644 --- a/src/api/thread.c +++ b/src/api/thread.c @@ -106,17 +106,17 @@ int arch_setup_new_thread(struct ktcb *new, struct ktcb *orig) * A cleaner but slower way would be the pager setting child registers * via exchanges_registers() and start the child thread afterwards. */ - new->syscall_regs->r0 = 0; + KTCB_REF_MR0(new)[MR_RETURN] = 0; /* - * Set up the stack pointer, saved program status register and program - * counter so that next time the new thread schedules, it executes the - * end part of the system call exception where the previous context is - * restored. + * Set up the stack pointer, saved program status register and the + * program counter so that next time the new thread schedules, it + * executes the end part of the system call exception where the + * previous context is restored. */ new->context.sp = (unsigned long)new->syscall_regs; new->context.pc = (unsigned long)&return_from_syscall; - new->context.spsr = (unsigned long)orig->context.spsr; + new->context.spsr = (unsigned long)orig->context.spsr; /* Copy other relevant fields from original ktcb */ new->pagerid = orig->pagerid; @@ -139,6 +139,7 @@ int thread_create(struct task_ids *ids, unsigned int flags) { struct ktcb *task, *new = (struct ktcb *)zalloc_page(); flags &= THREAD_FLAGS_MASK; + int ret = 0; if (flags == THREAD_CREATE_NEWSPC) { /* Allocate new pgd and copy all kernel areas */ @@ -192,13 +193,15 @@ out: * system call return environment so that it can safely * return as a copy of its original thread. */ - if (flags == THREAD_CREATE_COPYSPC) + if (flags == THREAD_CREATE_COPYSPC) { arch_setup_new_thread(new, task); + ret = new->tid; + } /* Add task to global hlist of tasks */ add_task_global(new); - return 0; + return ret; } /* diff --git a/src/arch/arm/vectors.S b/src/arch/arm/vectors.S index ac053ac..ed8f4af 100644 --- a/src/arch/arm/vectors.S +++ b/src/arch/arm/vectors.S @@ -122,9 +122,18 @@ END_PROC(arm_undef_exception) */ BEGIN_PROC(arm_swi_exception) sub lr, lr, #4 @ Get address of swi instruction user executed. - stmfd sp, {r0-r8,sp,lr}^ @ Push arguments, LR_USR and SP_USR to stack. + stmfd sp, {r0-r12,sp,lr}^ @ Push arguments, LR_USR and SP_USR to stack. nop - @ NOTE: SP_USR MUST be pushed here, otherwise a kernel preemption could + + @ Future optimisation 1: + @ For all syscalls we need not push any more than r8 but we push up to + @ r12 because upon a fork, a child's easiest way to restore user + @ registers is to pop it from stack during return_from_syscall. In future + @ fork function could return back to here, save all context into child + @ from actual registers instead of reading from stack, and then return. + + @ Future optimisation 2: + @ SP_USR MUST be pushed here, otherwise a kernel preemption could @ cause user mode of another process to overwrite SP_USR. The reason we @ save it here is because the preemption path does not currently save it @ if it is a kernel preemption. User SP can also be used here, as the @@ -140,7 +149,7 @@ BEGIN_PROC(arm_swi_exception) * LR_svc). */ - sub sp, sp, #44 @ stmfd on user registers can't writeback the SP. We do it manually. + sub sp, sp, #60 @ stmfd on user registers can't writeback the SP. We do it manually. mrs r0, spsr_fc @ psr also need saving in case this context is interrupted. stmfd sp!, {r0} enable_irqs r0 @@ -158,7 +167,7 @@ return_from_syscall: @ if they duplicated another thread's address space. msr spsr, r1 add sp, sp, #4 @ Skip, r0's location, since r0 already has returned result. @ Note we're obliged to preserve at least r3-r8 because they're MRs. - ldmfd sp!, {r1-r8} @ Restore r1-r8 pushed to stack earlier. r0 already has return result. + ldmfd sp!, {r1-r12} @ Restore r1-r8 pushed to stack earlier. r0 already has return result. ldmfd sp, {sp}^ @ Restore user stack pointer, which might have been corrupt on preemption nop add sp, sp, #4 @ Update sp.