Browse Source

Kernel: Implement lazy FPU state restore.

Andreas Kling 6 years ago
parent
commit
dfdca9d2a7
8 changed files with 50 additions and 9 deletions
  1. 8 0
      Kernel/Process.cpp
  2. 6 0
      Kernel/Process.h
  3. 2 0
      Kernel/Scheduler.cpp
  4. 1 0
      Kernel/Scheduler.h
  5. 21 5
      Kernel/i386.cpp
  6. 11 0
      Kernel/i386.h
  7. 0 4
      Kernel/init.cpp
  8. 1 0
      LibGUI/GButton.cpp

+ 8 - 0
Kernel/Process.cpp

@@ -258,6 +258,9 @@ Process* Process::fork(RegisterDump& regs)
     child->m_tss.gs = regs.gs;
     child->m_tss.ss = regs.ss_if_crossRing;
 
+    child->m_fpu_state = m_fpu_state;
+    child->m_has_used_fpu = m_has_used_fpu;
+
 #ifdef FORK_DEBUG
     dbgprintf("fork: child will begin executing at %w:%x with stack %w:%x\n", child->m_tss.cs, child->m_tss.eip, child->m_tss.ss, child->m_tss.esp);
 #endif
@@ -588,6 +591,8 @@ Process::Process(String&& name, uid_t uid, gid_t gid, pid_t ppid, RingLevel ring
     , m_tty(tty)
     , m_ppid(ppid)
 {
+    memset(&m_fpu_state, 0, sizeof(FPUState));
+
     m_gids.set(m_gid);
 
     if (fork_parent) {
@@ -698,6 +703,9 @@ Process::~Process()
     ProcFS::the().remove_process(*this);
     system.nprocess--;
 
+    if (g_last_fpu_process == this)
+        g_last_fpu_process = nullptr;
+
     if (selector())
         gdt_free_entry(selector());
 

+ 6 - 0
Kernel/Process.h

@@ -268,6 +268,10 @@ public:
     bool wakeup_requested() { return m_wakeup_requested; }
     void request_wakeup() { m_wakeup_requested = true; }
 
+    FPUState& fpu_state() { return m_fpu_state; }
+    bool has_used_fpu() const { return m_has_used_fpu; }
+    void set_has_used_fpu(bool b) { m_has_used_fpu = b; }
+
 private:
     friend class MemoryManager;
     friend class Scheduler;
@@ -303,6 +307,7 @@ private:
     dword m_wakeupTime { 0 };
     TSS32 m_tss;
     TSS32 m_tss_to_resume_kernel;
+    FPUState m_fpu_state;
     struct FileDescriptorAndFlags {
         operator bool() const { return !!descriptor; }
         void clear() { descriptor = nullptr; flags = 0; }
@@ -372,6 +377,7 @@ private:
     int m_next_window_id { 1 };
 
     dword m_wakeup_requested { false };
+    bool m_has_used_fpu { false };
 };
 
 extern Process* current;

+ 2 - 0
Kernel/Scheduler.cpp

@@ -8,6 +8,7 @@
 static const dword time_slice = 5; // *10 = 50ms
 
 Process* current;
+Process* g_last_fpu_process;
 static Process* s_colonel_process;
 static bool s_in_yield;
 
@@ -299,6 +300,7 @@ void Scheduler::initialize()
     initialize_redirection();
     s_colonel_process = Process::create_kernel_process("colonel", nullptr);
     current = nullptr;
+    g_last_fpu_process = nullptr;
     s_in_yield = false;
     load_task_register(s_redirection.selector);
 }

+ 1 - 0
Kernel/Scheduler.h

@@ -6,6 +6,7 @@ class Process;
 struct RegisterDump;
 
 extern Process* current;
+extern Process* g_last_fpu_process;
 
 class Scheduler {
 public:

+ 21 - 5
Kernel/i386.cpp

@@ -6,6 +6,7 @@
 #include "MemoryManager.h"
 #include "IRQHandler.h"
 #include "PIC.h"
+#include "Scheduler.h"
 
 //#define PAGE_FAULT_DEBUG
 
@@ -150,13 +151,31 @@ void exception_6_handler(RegisterDump& regs)
     current->crash();
 }
 
-// 7: FPU exception
+// 7: FPU not available exception
 EH_ENTRY_NO_CODE(7);
 void exception_7_handler(RegisterDump& regs)
 {
     (void)regs;
+
+    asm volatile("clts");
+    if (g_last_fpu_process == current)
+        return;
+    if (g_last_fpu_process) {
+        asm volatile("fnsave %0":"=m"(g_last_fpu_process->fpu_state()));
+    } else {
+        asm volatile("fnclex");
+    }
+    g_last_fpu_process = current;
+
+    if (current->has_used_fpu()) {
+        asm volatile("frstor %0"::"m"(current->fpu_state()));
+    } else {
+        asm volatile("fninit");
+        current->set_has_used_fpu(true);
+    }
+
 #ifdef FPU_EXCEPTION_DEBUG
-    kprintf("%s FPU exception: %u(%s)\n", current->isRing0() ? "Kernel" : "Process", current->pid(), current->name().characters());
+    kprintf("%s FPU not available exception: %u(%s)\n", current->isRing0() ? "Kernel" : "Process", current->pid(), current->name().characters());
 
     word ss;
     dword esp;
@@ -173,9 +192,6 @@ void exception_7_handler(RegisterDump& regs)
     kprintf("eax=%x ebx=%x ecx=%x edx=%x\n", regs.eax, regs.ebx, regs.ecx, regs.edx);
     kprintf("ebp=%x esp=%x esi=%x edi=%x\n", regs.ebp, esp, regs.esi, regs.edi);
 #endif
-
-    // FIXME: Do stuff.
-    asm volatile("clts");
 }
 
 

+ 11 - 0
Kernel/i386.h

@@ -202,6 +202,17 @@ struct RegisterDumpWithExceptionCode {
     word ss_if_crossRing;
 } PACKED;
 
+struct FPUState {
+    dword cwd;
+    dword swd;
+    dword twd;
+    dword fip;
+    dword fcs;
+    dword foo;
+    dword fos;
+    dword st[20];
+};
+
 inline constexpr dword pageBaseOf(dword address)
 {
     return address & 0xfffff000;

+ 0 - 4
Kernel/init.cpp

@@ -138,10 +138,6 @@ void init()
     gdt_init();
     idt_init();
 
-#ifndef NO_FPU
-    asm volatile("fninit");
-#endif
-
     VFS::initialize_globals();
     vfs = new VFS;
 

+ 1 - 0
LibGUI/GButton.cpp

@@ -42,6 +42,7 @@ void GButton::paint_event(GPaintEvent&)
     } else {
         // Base
         painter.fill_rect({ 3, 3, width() - 5, height() - 5 }, button_color);
+        painter.fill_rect_with_gradient({ 3, 3, width() - 5, height() - 5 }, button_color, Color::White);
 
         // White highlight
         painter.draw_line({ 1, 1 }, { width() - 2, 1 }, highlight_color);