#define CPU_SIZE (TSS_OFFSET + TSS_SIZE + 4)
#define CUR_TASK (TSS_OFFSET + TSS_SIZE)
+
+#define OFFSET_ESP0 0
+#define OFFSET_CR3 4
+#define OFFSET_KSTACK 8
+
.extern _cpu
-.global _task_prepare
-_task_prepare:
- pushl %ebx
- /* eax = task, ecx = eip, ebx = esp */
- movl 8(%esp), %eax
- movl 12(%esp), %ecx
- movl 16(%esp), %ebx
-
- addl $(STACK_OFFSET + STACK_SIZE - STACK_INIT_SIZE), %eax
- movl $USER_DATA, (%eax)
-#if CONFIG_INITIALIZE_USER_REGS == 1
- movl $32, %esi
- xorl %edx, %edx
-
-0: test %esi, %esi
- jz 1f
- movl %edx, (%eax, %esi)
- subl $4, %esi
- jmp 0b
-
-#endif /* CONFIG_INITIALIZE_USER_REGS */
- /* 4(%eax) - 32(%eax) is the location of the general purpose registers */
-1: movl %ecx, 36(%eax)
- movl $USER_CODE, 40(%eax)
- movl $USER_EFLAGS, 44(%eax)
- movl %ebx, 48(%eax)
- movl $USER_DATA, 52(%eax)
-
- popl %ebx
- ret
-
-.global _task_switch
-_task_switch:
- movl 4(%esp), %eax
- movl 4(%eax), %ebx
- movl %cr3, %edx
- cmpl %edx, %ebx
- jz 0f
- movl %ebx, %cr3
-
-0: cpuidx %ecx
- movl $CPU_SIZE, %esi
- shll %cl, %esi
- movl _cpu(%esi), %ebx
- movl CUR_TASK(%ebx), %edi
- cmpl %edi, %eax
-
- jz 0b
- movl %esp, (%edi)
- movl %eax, CUR_TASK(%ebx)
- movl (%eax), %esp
- iret
+.global task_prepare
+.global task_switch
+
+task_prepare:
+ /*
+ * int task_prepare(struct task*, u32_t cr3, u32_t eip,
+ * u32_t esp, u32_t priv)
+ *
+ * priv = 20(%esp)
+ * esp = 16(%esp)
+ * eip = 12(%esp)
+ * cr3 = 8(%esp)
+ * task = 4(%esp)
+ */
+
+ movl 4(%esp), %edi
+
+ /* store cr3 in task structure */
+ movl 8(%esp), %eax
+ movl %eax, 4(%edi)
+
+ /* calculate stack start (since it grows down) */
+ movl $CONFIG_KERNEL_STACK_SIZE, %ecx
+ movl (%esp), %eax
+ leal OFFSET_KSTACK(%eax, %ecx, 1), %eax
+
+ /*
+ * We will use _int_restore (the lower half of the interrupt handler)
+ * to start execution of tasks. In order to do that, we need to set up
+ * the stack properly. The _int_restore assembly function will restore
+ * the data segments and general purpose registers before executing the
+ * iret instruction. This is why we need the following stack layout.
+ *
+ * +--------+
+ * | SS | 60(%eax) \
+ * | ESP | 56(%eax) |
+ * | EFLAGS | 52(%eax) > for iret
+ * | CS | 48(%eax) |
+ * | EIP | 44(%eax) /
+ * +--------+
+ * | error | 40(%eax) \ error pushed by _int_entryNN
+ * | intr | 36(%eax) / int number pushed by _int_entryNN
+ * +--------+
+ * | EDI | 32(%eax) \
+ * | ESI | 28(%eax) |
+ * | EBP | 24(%eax) |
+ * | unused | 20(%eax) |
+ * | EBX | 16(%eax) > for popa
+ * | EDX | 12(%eax) |
+ * | ECX | 8(%eax) |
+ * | EAX | 4(%eax) /
+ * +--------+
+ * | DS | (%eax) > data segment registers
+ * +--------+
+ *
+ * In total, there are 16 dwords to be put on the stack, so we will
+ * start by adding 64 to %eax and then use the addresses above.
+ */
+
+ subl $64, %eax
+
+ /* calculate DS from priv and put it on the new stack */
+ movl 20(%esp), %ecx
+ shll $4, %ecx
+ addl $16, %ecx
+ orl 20(%esp), %ecx
+ movl %ecx, (%eax)
+
+ /* Clear general purpose registers (and error/intr) */
+ movl $0, 4(%eax)
+ movl $0, 8(%eax)
+ movl $0, 12(%eax)
+ movl $0, 16(%eax)
+ movl $0, 20(%eax)
+ movl $0, 24(%eax)
+ movl $0, 28(%eax)
+ movl $0, 32(%eax)
+ movl $0, 36(%eax)
+ movl $0, 40(%eax)
+
+ /* put EIP on the new stack */
+ movl 12(%esp), %ecx
+ movl %ecx, 44(%eax)
+
+ /* calculate CS from priv and put it on the new stack */
+ movl 20(%esp), %ecx
+ shll $4, %ecx
+ addl $8, %ecx
+ orl 20(%esp), %ecx
+ movl %ecx, 48(%eax)
+
+ /* put EFLAGS on the new stack */
+ movl $USER_EFLAGS, 52(%eax)
+
+ /* put the stack pointer on the stack */
+
+ /* put the stack segment on the new stack - use the value from DS */
+ movl (%eax), %ecx
+ movl %ecx, 60(%eax)
+
+ /* put EAX into the task structure */
+ movl 4(%esp), %ecx
+ movl %eax, OFFSET_ESP0(%ecx)
+
+ /* that should be it */
+ ret
+
+task_switch:
+ /*
+ * void task_switch(struct task*)
+ *
+ * task = 4(%esp)
+ */
+
+ /* get a pointer to the cpu structure of the current cpu */
+ cpuidx %ecx
+ movl $_cpu, %eax
+ /* while(--ecx > 0) { eax += CPU_SIZE; } */
+0: subl $1, %ecx
+ js 1f
+ addl $CPU_SIZE, %eax
+ jmp 0b
+
+ movl CUR_TASK(%eax), %esi
+ movl 4(%esp), %edi
+
+ cmpl %esi, %edi
+ jnz 1f
+ /* FIXME: Return -EALREADY instead */
+ movl $-1, %eax
+ ret
+
+1: movl OFFSET_CR3(%edi), %ecx
+
+ /* check if we can avoid writing to cr3, which would clear the TLB */
+ movl %cr3, %edx
+ cmpl %ecx, %edx
+ jz 2f
+
+ /* switch page directory */
+ movl %ecx, %cr3
+
+2: movl %edi, CUR_TASK(%eax)
+
+ /* write ESP to the previous TCB, if any */
+ test %esi, %esi
+ jz 3f
+ movl %esp, OFFSET_ESP0(%esi)
+
+ /* load new ESP */
+3: movl OFFSET_ESP0(%edi), %esp
+
+ /* pretend that we're returning from an interrupt */
+ jmp _int_restore
+++ /dev/null
-/*
- * This file is part of the Corax operating system.
- * Copyright (C) 2016 Matthias Kruk
- *
- * Corax is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Corax is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Corax. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef __TASK_H
-#define __TASK_H
-
-#include <config.h>
-
-/* Definition of an architectural task - think of it
- * as a TCB. It is not a operating system process or
- * thread, but merely the information needed by the
- * CPU to perform context switches, etc.
- *
- * Specifically, we need to remember the task's page
- * directory and the stack pointer. The task's context
- * is on top of its stack, i.e. all we have to do is
- * load the task's stack pointer and execute an iret */
-
-struct task {
- u32_t t_esp;
- u32_t t_cr3;
-
- u8_t t_kstack[CONFIG_USER_STACK_SIZE];
-} __attribute__((packed));
-
-struct task* _task_prepare(struct task *t, u32_t eip, u32_t esp);
-void _task_switch(struct task*);
-
-#endif /* __TASK_H */
/*
* This file is part of the Corax operating system.
- * Copyright (C) 2016 Matthias Kruk
+ * Copyright (C) 2016-2019 Matthias Kruk
*
* Corax is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include <corax/types.h>
+struct task {
+ u32_t t_sp;
+ u32_t t_pgdir;
+
+ u8_t t_kstack[CONFIG_KERNEL_STACK_SIZE];
+} __attribute__((packed));
+
int cpu_get_id(void);
u64_t cpu_get_capabilities(void);
u64_t cpu_timestamp(void);
u32_t cpu_set_pstate(int pstate);
+int task_prepare(struct task*, u32_t cr3, u32_t eip, u32_t esp, u32_t priv);
+int task_switch(struct task*);
+
#endif /* __ARCH_H */