]> git.corax.cc Git - corax/commitdiff
Add task_prepare_fork method and modify task_switch to facilitate the implementation...
authorMatthias Kruk <m@m10k.eu>
Mon, 4 Nov 2019 02:03:15 +0000 (11:03 +0900)
committerMatthias Kruk <m@m10k.eu>
Mon, 4 Nov 2019 02:03:15 +0000 (11:03 +0900)
kernel/arch/task.S

index 7ab735417ae899ba36b3fc3639a41ddddba271a3..c7e5e3257eda87aceba3ca670c3a838130efbdcf 100644 (file)
 #define __ASSEMBLY_SOURCE
 
 #include <config.h>
+#include <arch.h>
 #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