From ab9c30fc484017a3e37e420462d0838ad50b5091 Mon Sep 17 00:00:00 2001 From: Matthias Kruk Date: Tue, 1 Oct 2019 18:13:09 +0900 Subject: [PATCH] Improve interrupt handling and task switching code: - Introduce definitions to make task_prepare and task_switch easier to understand - Make interrupt handlers push cr3 onto the stack and restore it before an iret - Make interrupt handlers adjust the stack pointers upon entry/exit since the tasks' kernel-mode stacks are mapped at their linear addresses in the kernel's page directory --- kernel/arch/cpu.h | 33 +++++---- kernel/arch/entry.S | 42 +++++++++-- kernel/arch/task.S | 167 +++++++++++++++++++++++++----------------- kernel/core/process.c | 3 - 4 files changed, 152 insertions(+), 93 deletions(-) diff --git a/kernel/arch/cpu.h b/kernel/arch/cpu.h index 53a671f..f5b1349 100644 --- a/kernel/arch/cpu.h +++ b/kernel/arch/cpu.h @@ -114,22 +114,23 @@ struct tss64 { typedef struct stack_frame stack_frame_t; struct stack_frame { - u32_t ds; /* + 0x00 / 00 */ - u32_t edi; /* + 0x04 / 04 */ - u32_t esi; /* + 0x08 / 08 */ - u32_t ebp; /* + 0x0c / 12 */ - u32_t esp; /* + 0x10 / 16 */ - u32_t ebx; /* + 0x14 / 20 */ - u32_t edx; /* + 0x18 / 24 */ - u32_t ecx; /* + 0x1c / 28 */ - u32_t eax; /* + 0x20 / 32 */ - u32_t intn; /* + 0x24 / 36 */ - u32_t error; - u32_t eip; - u32_t cs; - u32_t eflags; - u32_t prevesp; - u32_t ss; + u32_t cr3; + u32_t ds; + u32_t edi; + u32_t esi; + u32_t ebp; + u32_t esp; + u32_t ebx; + u32_t edx; + u32_t ecx; + u32_t eax; + u32_t intn; + u32_t error; + u32_t eip; + u32_t cs; + u32_t eflags; + u32_t prevesp; + u32_t ss; } __attribute__((packed)); struct cpu { diff --git a/kernel/arch/entry.S b/kernel/arch/entry.S index 38ee530..ab29858 100644 --- a/kernel/arch/entry.S +++ b/kernel/arch/entry.S @@ -334,12 +334,14 @@ _int_entry_common: movw %ax, %fs movw %ax, %gs - /* switch to the kernel page directory, if we're not already using it */ - movl (_kernel_cr3), %eax + /* push the current page directory onto the stack */ movl %cr3, %ebx + pushl %ebx + + /* switch to the kernel pagedir, if necessary */ + movl (_kernel_cr3), %eax cmpl %eax, %ebx je _already_in_kernel - movl %eax, %cr3 /* @@ -372,10 +374,10 @@ _int_entry_common: /* we can now use the stack */ _already_in_kernel: - cmp $EXC_MAX, 36(%esp) + cmp $EXC_MAX, 40(%esp) jl 0f - cmp $INT_MAX, 36(%esp) + cmp $INT_MAX, 40(%esp) jl 1f call _sys_handle @@ -389,6 +391,36 @@ _already_in_kernel: call _int_handle _int_restore: + /* pop the page directory from the stack */ + popl %eax + + /* + * Check if the destination of the iret is in the kernel or user-space. + * In the former case, we can skip rebasing the stack and restoring the + * page directory. + */ + cmpl %eax, (_kernel_cr3) + je _return_to_kernel + + /* switch page directory first since we'll need the register */ + movl %eax, %cr3 + + cpuidx %ecx + movl $_cpu, %eax +0: subl $1, %ecx + js 1f + addl $CPU_SIZE, %eax + jmp 0b + + /* rebase the stack pointers to the task's page directory */ +1: movl OFFSET_CPU_CTASK(%eax), %eax + + subl OFFSET_TASK_KSTACK(%eax), %esp + addl $CONFIG_KERNEL_STACK_BASE, %esp + subl OFFSET_TASK_KSTACK(%eax), %ebp + addl $CONFIG_KERNEL_STACK_BASE, %ebp + +_return_to_kernel: popl %eax movw %ax, %ds movw %ax, %es diff --git a/kernel/arch/task.S b/kernel/arch/task.S index 032e2a5..32e376b 100644 --- a/kernel/arch/task.S +++ b/kernel/arch/task.S @@ -41,18 +41,25 @@ task_prepare: * task = 4(%esp) */ - movl 4(%esp), %edi +#define ARG_PRIV 24 +#define ARG_ESP 20 +#define ARG_ESP0 16 +#define ARG_EIP 12 +#define ARG_CR3 8 +#define ARG_TASK 4 + + movl ARG_TASK(%esp), %edi /* store cr3 in task structure */ - movl 8(%esp), %eax + movl ARG_CR3(%esp), %eax movl %eax, OFFSET_TASK_CR3(%edi) /* store privilege level in task structure */ - movl 24(%esp), %eax + movl ARG_PRIV(%esp), %eax movl %eax, OFFSET_TASK_PRIVL(%edi) /* calculate stack start (since it grows down) */ - movl 16(%esp), %eax + movl ARG_ESP0(%esp), %eax /* * We will use _int_restore (the lower half of the interrupt handler) @@ -62,73 +69,96 @@ task_prepare: * 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) / + * | SS | 64(%eax) \ + * | ESP | 60(%eax) | + * | EFLAGS | 56(%eax) > for iret + * | CS | 52(%eax) | + * | EIP | 48(%eax) / * +--------+ - * | error | 40(%eax) \ error pushed by _int_entryNN - * | intr | 36(%eax) / int number pushed by _int_entryNN + * | error | 44(%eax) \ error pushed by _int_entryNN + * | intr | 40(%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) / + * | EDI | 36(%eax) \ + * | ESI | 32(%eax) | + * | EBP | 28(%eax) | + * | unused | 24(%eax) | + * | EBX | 20(%eax) > for popa + * | EDX | 16(%eax) | + * | ECX | 12(%eax) | + * | EAX | 8(%eax) / * +--------+ - * | DS | (%eax) > data segment registers + * | DS | 4(%eax) > data segment registers + * | CR3 | (%eax) > page directory base * +--------+ * - * 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. + * In total, there are 17 dwords to be put on the stack, so we will + * start by adding 68 to %eax and then use the addresses above. */ - subl $64, %eax +#define STK_SS 64 +#define STK_ESP0 60 +#define STK_EFLAGS 56 +#define STK_CS 52 +#define STK_EIP 48 +#define STK_ERROR 44 +#define STK_INTR 40 +#define STK_EDI 36 +#define STK_ESI 32 +#define STK_EBP 28 +#define STK_ESP 24 +#define STK_EBX 20 +#define STK_EDX 16 +#define STK_ECX 12 +#define STK_EAX 8 +#define STK_DS 4 +#define STK_CR3 0 + + subl $68, %eax /* calculate DS from priv and put it on the new stack */ - movl 24(%esp), %ecx + movl ARG_PRIV(%esp), %ecx shll $4, %ecx addl $16, %ecx - orl 24(%esp), %ecx - movl %ecx, (%eax) + orl ARG_PRIV(%esp), %ecx + movl %ecx, STK_DS(%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) + movl $0, STK_EAX(%eax) + movl $0, STK_ECX(%eax) + movl $0, STK_EDX(%eax) + movl $0, STK_EBX(%eax) + movl $0, STK_ESP(%eax) + movl $0, STK_EBP(%eax) + movl $0, STK_ESI(%eax) + movl $0, STK_EDI(%eax) + movl $0, STK_INTR(%eax) + movl $0, STK_ERROR(%eax) /* put EIP on the new stack */ - movl 12(%esp), %ecx - movl %ecx, 44(%eax) + movl ARG_EIP(%esp), %ecx + movl %ecx, STK_EIP(%eax) /* calculate CS from priv and put it on the new stack */ - movl 24(%esp), %ecx + movl ARG_PRIV(%esp), %ecx shll $4, %ecx addl $8, %ecx - orl 24(%esp), %ecx - movl %ecx, 48(%eax) + orl ARG_PRIV(%esp), %ecx + movl %ecx, STK_CS(%eax) /* put EFLAGS on the new stack */ - movl $USER_EFLAGS, 52(%eax) + movl $USER_EFLAGS, STK_EFLAGS(%eax) /* put the stack pointer on the stack */ - movl 20(%esp), %ecx - movl %ecx, 56(%eax) + movl ARG_ESP(%esp), %ecx + movl %ecx, STK_ESP0(%eax) /* put the stack segment on the new stack - use the value from DS */ - movl (%eax), %ecx - movl %ecx, 60(%eax) + movl STK_DS(%eax), %ecx + movl %ecx, STK_SS(%eax) + + /* put cr3 onto the stack */ + movl ARG_CR3(%esp), %ecx + movl %ecx, STK_CR3(%eax) /* put EAX into the task structure - task pointer is still in EDI */ movl %eax, OFFSET_TASK_ESP0(%edi) @@ -155,14 +185,17 @@ task_switch: 1: movl OFFSET_CPU_CTASK(%eax), %esi movl 4(%esp), %edi - cmpl %esi, %edi - jne 2f - /* FIXME: Return -EALREADY instead */ - movl $-1, %eax - ret + /* update the task pointer */ + movl %edi, OFFSET_CPU_CTASK(%eax) + + /* + * Get the address of the task's ESP0 for its own page directory + * and store it in the current CPU's TSS + */ + movl OFFSET_TASK_ESP0(%edi), %ecx + subl OFFSET_TASK_KSTACK(%edi), %ecx + addl $CONFIG_KERNEL_STACK_BASE, %ecx - /* put the task's ESP0 into the TSS */ -2: movl OFFSET_TASK_ESP0(%edi), %ecx movl %ecx, CPU_ESP0(%eax) movl $KERNEL_DATA, CPU_SS0(%eax) @@ -184,26 +217,22 @@ task_switch: movl %ecx, CPU_GS(%eax) movl %ecx, CPU_SS(%eax) - movl OFFSET_TASK_CR3(%edi), %ecx + /* write ESP to the previous TCB, if any */ + test %esi, %esi + jz 2f + movl %esp, OFFSET_TASK_ESP0(%esi) /* * Clear interrupts, since we won't have a valid stack until the * iret instruction in _int_restore is executed */ - cli +2: cli - /* switch to the task's page directory since the kernel uses a separate one */ - movl %ecx, %cr3 - - movl %edi, OFFSET_CPU_CTASK(%eax) - - /* write ESP to the previous TCB, if any */ - test %esi, %esi - jz 3f - movl %esp, OFFSET_TASK_ESP0(%esi) - - /* load new ESP */ -3: movl OFFSET_TASK_ESP0(%edi), %esp + /* + * Load ESP0 from the task structure, but don't adjust it since we need the + * linear address while we're in the context of the kernel + */ + movl OFFSET_TASK_ESP0(%edi), %esp /* * If this is a new task, we have to use _int_restore to return, @@ -213,7 +242,7 @@ task_switch: movl OFFSET_TASK_STATE(%edi), %eax test %eax, %eax - jz 4f + jz 3f xorl %eax, %eax /* @@ -224,7 +253,7 @@ task_switch: */ ret -4: movl $1, OFFSET_TASK_STATE(%edi) +3: movl $1, 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 diff --git a/kernel/core/process.c b/kernel/core/process.c index 8a4429d..bbad28d 100644 --- a/kernel/core/process.c +++ b/kernel/core/process.c @@ -51,9 +51,6 @@ int process_create(process_t **dst, u32_t ppl, void *entry) (u32_t)kstack + CONFIG_KERNEL_STACK_SIZE, (u32_t)ustack + CONFIG_USER_STACK_SIZE, ppl); - proc->p_tasks->t_sp = (u32_t)(CONFIG_KERNEL_STACK_BASE + - ((u32_t)proc->p_tasks->t_sp - (u32_t)kstack)); - proc->p_tasks->t_kstack = kstack; proc->p_tasks->t_tslice = 50; proc->p_tasks->t_rslice = 50; -- 2.47.3