From 125a612f05f3c1a21e6b90892d6ffbe0eab4f951 Mon Sep 17 00:00:00 2001 From: Matthias Kruk Date: Mon, 4 Nov 2019 11:03:15 +0900 Subject: [PATCH] Add task_prepare_fork method and modify task_switch to facilitate the implementation of sys_fork() --- kernel/arch/task.S | 117 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 113 insertions(+), 4 deletions(-) diff --git a/kernel/arch/task.S b/kernel/arch/task.S index 7ab7354..c7e5e32 100644 --- a/kernel/arch/task.S +++ b/kernel/arch/task.S @@ -19,11 +19,13 @@ #define __ASSEMBLY_SOURCE #include +#include #include "defs.h" .extern _cpu .global task_prepare +.global task_prepare_fork .global task_switch .global task_get_current .global task_set_current @@ -167,6 +169,100 @@ task_prepare: /* that should be it */ ret +copy_and_fix_stack: + /* + * This function dword-wise copies the kernel-mode stack and adjusts + * pointers that are pointing into the stack (such as base pointers + * that have been pushed onto the stack). + * + * esi = source stack + * edi = destination stack + * CONFIG_KERNEL_STACK_SIZE = stack size in bytes + */ + + /* edx is the upper boundary of the source stack */ + xorl %ecx, %ecx + movl %esi, %edx + addl $CONFIG_KERNEL_STACK_SIZE, %edx + +0: cmpl $CONFIG_KERNEL_STACK_SIZE, %ecx + jz 2f + + movl (%esi, %ecx, 1), %eax + + /* eax < stack_start ? */ + cmpl %esi, %eax + jl 1f + /* eax >= stack_end ? */ + cmpl %edx, %eax + jge 1f + + /* + * eax >= stack_start && eax < stack_end + * + * The value appears to be a pointer into the old + * stack and needs to be rebased to the new stack + */ + subl %esi, %eax + addl %edi, %eax + +1: movl %eax, (%edi, %ecx, 1) + addl $4, %ecx + jmp 0b + +2: ret + +task_prepare_fork: + /* + * int task_prepare_fork(task_t *new) + * + * new = 4(%esp) + * EIP = 0(%esp) + */ + + call task_get_current + movl %eax, %esi + + /* first of all, adjust the stack pointers */ + movl %esp, %eax + movl %ebp, %ecx + + /* get the offsets into the original stack */ + movl %esi, %edx + subl OFFSET_TASK_KSTACK(%edx), %eax + subl OFFSET_TASK_KSTACK(%edx), %ecx + /* add the base of the new stack */ + movl 4(%esp), %edx + addl OFFSET_TASK_KSTACK(%edx), %eax + addl OFFSET_TASK_KSTACK(%edx), %ecx + + /* store stack pointers in the new context */ + movl %eax, OFFSET_TASK_ESP0(%edx) + movl %ecx, OFFSET_TASK_EBP0(%edx) + + /* + * The forked process (or rather, its task) will begin execution like a task + * that had been interrupted, i.e. in task_switch. Here, we're making use of + * the fact that neither function adjusts the stack pointers, and effectively + * make task_switch reuse the stack frame of task_prepare_fork. + */ + + movl 4(%esp), %edx + movl %esi, %eax + movl OFFSET_TASK_KSTACK(%edx), %edi + movl OFFSET_TASK_KSTACK(%eax), %esi + call copy_and_fix_stack + + /* + * In the case of the parent, zero will be returned. The child process + * will return with 0xf00f00ff, since that is the value that will be set + * in task_switch, when it starts executing the child process, reusing + * the stack frame of this function. + */ + xorl %eax, %eax + + ret + task_switch: /* * void task_switch(struct task*) @@ -248,17 +344,30 @@ task_switch: movl OFFSET_TASK_STATE(%edi), %eax test %eax, %eax jz 3f - xorl %eax, %eax /* * If the context has previously executed, it was interrupted in this * very same function, so by switching back to its stack and page directory * we have done enough to restore it's context. Everything else will happen - * when the task returns "naturally" to user-space + * when the task returns "naturally" to user-space. + * However: if this is a newly forked process, we need to set eax in a way + * that we can distinguish the forked and the parent process. */ - ret -3: movl $1, OFFSET_TASK_STATE(%edi) + cmpl $TASK_STATE_FORKED, %eax + jne 4f + + movl $TASK_STATE_RUNNING, OFFSET_TASK_STATE(%edi) + movl $0xf00f00ff, %eax + + /* + * The forked process will appear to be returning from task_prepare_fork with + * a return value of 0xf00f00ff, since we are reusing the stack frame from that + * function when we're coming from task_switch. + */ +4: ret + +3: movl $TASK_STATE_RUNNING, OFFSET_TASK_STATE(%edi) /* * In the previous line, the task state is set to TASK_STATE_RUNNING. * This is the only place where a transition from NEW to any other state -- 2.47.3