mirror of
https://github.com/drasko/codezero.git
synced 2026-03-06 12:33:14 +01:00
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.
This commit is contained in:
@@ -33,6 +33,10 @@ typedef struct syscall_context {
|
|||||||
u32 r6; /* MR3 */
|
u32 r6; /* MR3 */
|
||||||
u32 r7; /* MR4 */
|
u32 r7; /* MR4 */
|
||||||
u32 r8; /* MR5 */
|
u32 r8; /* MR5 */
|
||||||
|
u32 r9;
|
||||||
|
u32 r10;
|
||||||
|
u32 r11;
|
||||||
|
u32 r12;
|
||||||
u32 sp_usr;
|
u32 sp_usr;
|
||||||
u32 lr_usr;
|
u32 lr_usr;
|
||||||
} __attribute__((__packed__)) syscall_context_t;
|
} __attribute__((__packed__)) syscall_context_t;
|
||||||
|
|||||||
@@ -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
|
* A cleaner but slower way would be the pager setting child registers
|
||||||
* via exchanges_registers() and start the child thread afterwards.
|
* 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
|
* Set up the stack pointer, saved program status register and the
|
||||||
* counter so that next time the new thread schedules, it executes the
|
* program counter so that next time the new thread schedules, it
|
||||||
* end part of the system call exception where the previous context is
|
* executes the end part of the system call exception where the
|
||||||
* restored.
|
* previous context is restored.
|
||||||
*/
|
*/
|
||||||
new->context.sp = (unsigned long)new->syscall_regs;
|
new->context.sp = (unsigned long)new->syscall_regs;
|
||||||
new->context.pc = (unsigned long)&return_from_syscall;
|
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 */
|
/* Copy other relevant fields from original ktcb */
|
||||||
new->pagerid = orig->pagerid;
|
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();
|
struct ktcb *task, *new = (struct ktcb *)zalloc_page();
|
||||||
flags &= THREAD_FLAGS_MASK;
|
flags &= THREAD_FLAGS_MASK;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
if (flags == THREAD_CREATE_NEWSPC) {
|
if (flags == THREAD_CREATE_NEWSPC) {
|
||||||
/* Allocate new pgd and copy all kernel areas */
|
/* Allocate new pgd and copy all kernel areas */
|
||||||
@@ -192,13 +193,15 @@ out:
|
|||||||
* system call return environment so that it can safely
|
* system call return environment so that it can safely
|
||||||
* return as a copy of its original thread.
|
* return as a copy of its original thread.
|
||||||
*/
|
*/
|
||||||
if (flags == THREAD_CREATE_COPYSPC)
|
if (flags == THREAD_CREATE_COPYSPC) {
|
||||||
arch_setup_new_thread(new, task);
|
arch_setup_new_thread(new, task);
|
||||||
|
ret = new->tid;
|
||||||
|
}
|
||||||
|
|
||||||
/* Add task to global hlist of tasks */
|
/* Add task to global hlist of tasks */
|
||||||
add_task_global(new);
|
add_task_global(new);
|
||||||
|
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -122,9 +122,18 @@ END_PROC(arm_undef_exception)
|
|||||||
*/
|
*/
|
||||||
BEGIN_PROC(arm_swi_exception)
|
BEGIN_PROC(arm_swi_exception)
|
||||||
sub lr, lr, #4 @ Get address of swi instruction user executed.
|
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
|
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
|
@ 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
|
@ 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
|
@ 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).
|
* 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.
|
mrs r0, spsr_fc @ psr also need saving in case this context is interrupted.
|
||||||
stmfd sp!, {r0}
|
stmfd sp!, {r0}
|
||||||
enable_irqs r0
|
enable_irqs r0
|
||||||
@@ -158,7 +167,7 @@ return_from_syscall: @ if they duplicated another thread's address space.
|
|||||||
msr spsr, r1
|
msr spsr, r1
|
||||||
add sp, sp, #4 @ Skip, r0's location, since r0 already has returned result.
|
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.
|
@ 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
|
ldmfd sp, {sp}^ @ Restore user stack pointer, which might have been corrupt on preemption
|
||||||
nop
|
nop
|
||||||
add sp, sp, #4 @ Update sp.
|
add sp, sp, #4 @ Update sp.
|
||||||
|
|||||||
Reference in New Issue
Block a user