From 4725a1aef148de8c818c41c347b602947dedbfdc Mon Sep 17 00:00:00 2001 From: Matthias Kruk Date: Sat, 7 Sep 2019 02:52:58 +0900 Subject: [PATCH] Move definition of struct task from arch/task.h to include/arch.h; add proper implementations of task_prepare() and task_switch() --- kernel/arch/task.S | 211 +++++++++++++++++++++++++++++++----------- kernel/arch/task.h | 44 --------- kernel/include/arch.h | 12 ++- 3 files changed, 170 insertions(+), 97 deletions(-) delete mode 100644 kernel/arch/task.h diff --git a/kernel/arch/task.S b/kernel/arch/task.S index 6a0360e..31d2f62 100644 --- a/kernel/arch/task.S +++ b/kernel/arch/task.S @@ -23,57 +23,164 @@ #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 diff --git a/kernel/arch/task.h b/kernel/arch/task.h deleted file mode 100644 index 0cd1fd2..0000000 --- a/kernel/arch/task.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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 . - */ - -#ifndef __TASK_H -#define __TASK_H - -#include - -/* 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 */ diff --git a/kernel/include/arch.h b/kernel/include/arch.h index 6b6c65f..06f1932 100644 --- a/kernel/include/arch.h +++ b/kernel/include/arch.h @@ -1,6 +1,6 @@ /* * 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 @@ -21,10 +21,20 @@ #include +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 */ -- 2.47.3