Separate monolithium exceptions from x86 exceptions; Implement lazy FPU switching
authorcoderain <coderain@sdf.org>
Sat, 14 Oct 2017 16:34:26 +0000 (18:34 +0200)
committercoderain <coderain@sdf.org>
Sat, 14 Oct 2017 16:34:26 +0000 (18:34 +0200)
kernel/include/thread.h
kernel/src/exception.c
kernel/src/object.c
kernel/src/thread.c
sdk/cpu.h
sdk/exception.h

index 35aebb34b9e63dffd369ba2e3707bd86ad2668ee..2cb73c8ac93ed7bae05ae5f4a6bc4083bc896e8d 100644 (file)
@@ -111,6 +111,8 @@ dword_t terminate_thread_internal(thread_t *thread, dword_t return_value);
 void scheduler(registers_t *regs);
 wait_result_t scheduler_wait(wait_condition_t condition, dword_t timeout, uintptr_t *pointer, uintptr_t value);
 dword_t create_system_thread(thread_procedure_t routine, dword_t flags, priority_t priority, dword_t stack_size, void *param, thread_t **new_thread);
+void thread_lazy_fpu(void);
+void thread_cleanup(object_t *thread);
 void thread_init(void);
 
 #endif
index 0d10db9b67e57226e66f5c6ff9a6a49ab53c5f69..f574a30f6606e5cc750c5a7a6ce318774a8a26bc 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * exception.c
  *
- * Copyright (C) 2015 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
+ * Copyright (C) 2017 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
 #include <video.h>
 #include <vm86.h>
 #include <heap.h>
+#include <cpu.h>
 
 static const char *exception_names[] = {
-    "Division By Zero",
-    "Debug Interrupt",
-    "Non-Maskable Interrupt",
     "Breakpoint",
-    "Overflow",
-    "Bound Range Exceeded",
-    "Invalid Opcode",
-    "FPU Not Available",
-    "Double Fault",
-    "FPU Segment Overrun",
-    "Invalid TSS",
-    "Segment Not Present",
-    "Stack Overflow",
-    "General Protection Fault",
-    "Page Fault",
-    "Unknown Exception",
-    "Floating Point Exception",
-    "Alignment Check",
-    "Machine Check",
-    "Unknown"
+    "Arithmetic Error",
+    "Assertion Failure",
+    "Bad Operation",
+    "Memory Access Fault",
 };
 
 static inline dword_t get_cr(int num)
@@ -60,7 +46,7 @@ static inline dword_t get_cr(int num)
 static void raise_exception_internal(registers_t *regs, processor_mode_t mode, exception_info_t *info)
 {
     thread_t *thread = get_current_thread();
-    if (thread == NULL) KERNEL_CRASH_WITH_REGS(exception_names[MIN(info->number, NUM_EXCEPTIONS)], regs);
+    if (thread == NULL) KERNEL_CRASH_WITH_REGS(exception_names[info->number], regs);
 
     if (mode == USER_MODE)
     {
@@ -96,7 +82,7 @@ static void raise_exception_internal(registers_t *regs, processor_mode_t mode, e
         }
         else
         {
-            KERNEL_CRASH_WITH_REGS(exception_names[MIN(info->number, NUM_EXCEPTIONS)], regs);
+            KERNEL_CRASH_WITH_REGS(exception_names[info->number], regs);
         }
     }
 }
@@ -104,19 +90,78 @@ static void raise_exception_internal(registers_t *regs, processor_mode_t mode, e
 static void exception_handler(registers_t *regs, byte_t int_num)
 {
     exception_info_t info;
+    processor_mode_t previous_mode = SEGMENT_RPL(regs->cs) == 0 ? KERNEL_MODE : USER_MODE;
+    void *faulting_address;
 
-    if (int_num == EXCEPTION_GPF && (regs->eflags & EFLAGS_VM))
+    info.state = *regs;
+    memset(info.parameters, 0, sizeof(info.parameters));
+
+    switch (int_num)
     {
-        vm86_handler((registers_ext_vm86_t*)regs);
+    case CPU_EXCEPTION_DE:
+    case CPU_EXCEPTION_MF:
+        info.number = EXCEPTION_ARITHMETIC;
+        break;
+
+    case CPU_EXCEPTION_DB:
+    case CPU_EXCEPTION_BP:
+        info.number = EXCEPTION_BREAKPOINT;
+        break;
+
+    case CPU_EXCEPTION_NMI:
+    case CPU_EXCEPTION_MC:
         return;
-    }
 
-    if (int_num == EXCEPTION_PAGE_FAULT && memory_fault_handler((void*)get_cr(2), regs)) return;
+    case CPU_EXCEPTION_OF:
+    case CPU_EXCEPTION_BR:
+    case CPU_EXCEPTION_AC:
+        info.number = EXCEPTION_ASSERTION;
+        break;
 
-    info.number = int_num;
-    memset(info.parameters, 0, sizeof(info.parameters));
+    case CPU_EXCEPTION_NM:
+        if (cpu_features[0] & CPU_FEATURE_FPU)
+        {
+            thread_lazy_fpu();
+            return;
+        }
+        else
+        {
+            info.number = EXCEPTION_BAD_OPERATION;
+        }
+
+        break;
+
+    case CPU_EXCEPTION_DF:
+        KERNEL_CRASH_WITH_REGS("Double Fault", regs);
+        return;
+
+    case CPU_EXCEPTION_GP:
+        if (regs->eflags & EFLAGS_VM)
+        {
+            vm86_handler((registers_ext_vm86_t*)regs);
+            return;
+        }
+
+    case CPU_EXCEPTION_UD:
+    case CPU_EXCEPTION_TS:
+    case CPU_EXCEPTION_NP:
+    case CPU_EXCEPTION_SS:
+        info.number = EXCEPTION_BAD_OPERATION;
+        break;
+
+    case CPU_EXCEPTION_PF:
+        faulting_address = (void*)get_cr(2);
+        if (memory_fault_handler(faulting_address, regs)) return;
+
+        info.number = EXCEPTION_MEMORY_ACCESS;
+        memcpy(info.parameters, &faulting_address, sizeof(faulting_address));
+        break;
+
+    default:
+        KERNEL_CRASH_WITH_REGS("Unexpected CPU exception", regs);
+    }
 
-    raise_exception_internal(regs, SEGMENT_RPL(regs->cs) == 0 ? KERNEL_MODE : USER_MODE, &info);
+    raise_exception_internal(regs, previous_mode, &info);
 }
 
 sysret_t syscall_raise_exception(exception_info_t *info)
@@ -284,5 +329,5 @@ void __attribute__((noreturn)) kernel_crash(const char *message, registers_t *re
 void exceptions_init()
 {
     byte_t i;
-    for (i = 0; i < NUM_EXCEPTIONS; i++) set_int_handler(i, exception_handler, FALSE, FALSE);
+    for (i = 0; i < CPU_EXCEPTION_MAX; i++) set_int_handler(i, exception_handler, FALSE, FALSE);
 }
index 809c86c68d92956ab9f52eee88f57700b7b5fa41..fd85a0b56b984c110ae82784f6cb9fab8f1a2910 100644 (file)
@@ -19,6 +19,7 @@
 
 #include <object.h>
 #include <process.h>
+#include <thread.h>
 #include <heap.h>
 #include <pipe.h>
 
@@ -36,7 +37,7 @@ static object_cleanup_proc_t cleanup_procedures[OBJECT_TYPE_MAX] =
     file_instance_cleanup,
     pipe_cleanup,
     process_cleanup,
-    NULL,
+    thread_cleanup,
     memory_cleanup,
     NULL,
     NULL,
index 893f76e04bd1f1704dc230947475e8cc3eabd450..ca35e6ea206260e80f7d31cc6d5575d34b4c337e 100644 (file)
@@ -31,6 +31,7 @@ extern void reschedule(void);
 bool_t scheduler_enabled = FALSE;
 static list_entry_t thread_queue[THREAD_PRIORITY_MAX];
 static thread_t *current_thread = NULL;
+static thread_t *last_fpu_thread = NULL;
 static dword_t tid_alloc_bitmap[MAX_THREADS / 32];
 static lock_t tid_bitmap_lock = 0;
 
@@ -151,6 +152,11 @@ static void destroy_thread(thread_t *thread)
     dereference(&thread->header);
 }
 
+void thread_cleanup(object_t *obj)
+{
+    if (CONTAINER_OF(obj, thread_t, header) == last_fpu_thread) last_fpu_thread = NULL;
+}
+
 dword_t create_thread_internal(process_t *proc, thread_state_t *initial_state, dword_t flags, priority_t priority, void *kernel_stack, thread_t **new_thread)
 {
     dword_t ret;
@@ -253,6 +259,14 @@ sysret_t syscall_get_thread_id()
     return current_thread->tid;
 }
 
+void thread_lazy_fpu(void)
+{
+    if (last_fpu_thread) fpu_save(last_fpu_thread->state.fpu_state);
+    fpu_restore(current_thread->state.fpu_state);
+    last_fpu_thread = current_thread;
+    asm volatile ("clts");
+}
+
 void scheduler(registers_t *regs)
 {
     int i;
@@ -287,13 +301,17 @@ found:
         if (current_thread != next_thread)
         {
             memcpy(&current_thread->state.regs, regs, sizeof(registers_t));
-            if (cpu_features[0] & CPU_FEATURE_FPU) fpu_save(current_thread->state.fpu_state);
 
             current_thread->kernel_esp = regs->esp;
             current_thread->state.regs.esp = ((registers_ext_t*)regs)->esp3;
 
             set_kernel_esp(next_thread->kernel_esp);
-            if (cpu_features[0] & CPU_FEATURE_FPU) fpu_restore(next_thread->state.fpu_state);
+
+            asm volatile ("pushl %eax\n"
+                          "movl %cr4, %eax\n"
+                          "orb $0x08, %al\n"
+                          "movl %eax, %cr4\n"
+                          "popl %eax\n");
 
             if (SEGMENT_RPL(next_thread->state.regs.cs) != 0)
             {
index 463fd29baf84d1c73b6214c87076700c679d2613..1231d42fe6a97425b2d167bcbc584449328e748d 100644 (file)
--- a/sdk/cpu.h
+++ b/sdk/cpu.h
@@ -44,4 +44,30 @@ typedef struct
     dword_t eip, cs, eflags, esp3, ss, es, ds, fs, gs;
 } registers_ext_vm86_t;
 
+typedef enum
+{
+    CPU_EXCEPTION_DE  = 0x00,
+    CPU_EXCEPTION_DB  = 0x01,
+    CPU_EXCEPTION_NMI = 0x02,
+    CPU_EXCEPTION_BP  = 0x03,
+    CPU_EXCEPTION_OF  = 0x04,
+    CPU_EXCEPTION_BR  = 0x05,
+    CPU_EXCEPTION_UD  = 0x06,
+    CPU_EXCEPTION_NM  = 0x07,
+    CPU_EXCEPTION_DF  = 0x08,
+    CPU_EXCEPTION_TS  = 0x0A,
+    CPU_EXCEPTION_NP  = 0x0B,
+    CPU_EXCEPTION_SS  = 0x0C,
+    CPU_EXCEPTION_GP  = 0x0D,
+    CPU_EXCEPTION_PF  = 0x0E,
+    CPU_EXCEPTION_MF  = 0x10,
+    CPU_EXCEPTION_AC  = 0x11,
+    CPU_EXCEPTION_MC  = 0x12,
+    CPU_EXCEPTION_XM  = 0x13,
+    CPU_EXCEPTION_VE  = 0x14,
+    CPU_EXCEPTION_SX  = 0x1E,
+
+    CPU_EXCEPTION_MAX
+} cpu_exception_t;
+
 #endif
index 49c906f3cd4e129e5d58ed54437714cf997b72b9..1b4a35c7065b2557a8c8d54be68fa71276826cb7 100644 (file)
@@ -37,30 +37,18 @@ typedef registers_t exception_handler_t;
 
 typedef enum
 {
-    EXCEPTION_DIVISION_BY_ZERO,
-    EXCEPTION_DEBUG_INT,
-    EXCEPTION_NMI,
     EXCEPTION_BREAKPOINT,
-    EXCEPTION_OVERFLOW,
-    EXCEPTION_BOUND_RANGE,
-    EXCEPTION_INVALID_OPCODE,
-    EXCEPTION_NO_FPU,
-    EXCEPTION_DOUBLE_FAULT,
-    EXCEPTION_FPU_SEGMENT,
-    EXCEPTION_BAD_TSS,
-    EXCEPTION_NO_SEGMENT,
-    EXCEPTION_STACK_OVERFLOW,
-    EXCEPTION_GPF,
-    EXCEPTION_PAGE_FAULT,
-    EXCEPTION_ALIGNMENT,
-    EXCEPTION_HARDWARE,
-    NUM_EXCEPTIONS
+    EXCEPTION_ARITHMETIC,
+    EXCEPTION_ASSERTION,
+    EXCEPTION_BAD_OPERATION,
+    EXCEPTION_MEMORY_ACCESS,
 } exception_t;
 
 typedef struct
 {
     exception_t number;
-    dword_t parameters[15];
+    registers_t state;
+    dword_t parameters[4];
 } exception_info_t;
 
 sysret_t syscall_get_exception_info(exception_info_t *info);