]> git.corax.cc Git - corax/commitdiff
Improve interrupt handling and task switching code:
authorMatthias Kruk <m@m10k.eu>
Tue, 1 Oct 2019 09:13:09 +0000 (18:13 +0900)
committerMatthias Kruk <m@m10k.eu>
Tue, 1 Oct 2019 09:13:09 +0000 (18:13 +0900)
 - 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
kernel/arch/entry.S
kernel/arch/task.S
kernel/core/process.c

index 53a671fe23743308b8327a9139e2e0bc0de88041..f5b134962e38e7c176c0de8eb7ebf3bd51d0777f 100644 (file)
@@ -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 {
index 38ee5301a08ce2b1b8afd1b3be884a54fac53c9b..ab298586c6ced025f1dac102199edc3ab5edcc25 100644 (file)
@@ -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
index 032e2a597af29d7fa8aa95de160b611a03c4d862..32e376b0a9896a42081059dc6ffa85b09fec356c 100644 (file)
@@ -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
index 8a4429d8ea3293fb9e358ecc793d61271fea3e4a..bbad28dff58dc5ec1e161bdfea35e2351706e5cf 100644 (file)
@@ -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;