#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
/* 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*)
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