diff --git a/sortix/Makefile b/sortix/Makefile index c22e2ab9..d21dc521 100644 --- a/sortix/Makefile +++ b/sortix/Makefile @@ -42,6 +42,7 @@ ifdef X86FAMILY $(CPU)/scheduler.o \ $(CPU)/process.o \ x86-family/msr.o \ + x86-family/float.o \ x86-family/x86-family.o CPUFLAGS:=$(CPUFLAGS) -mno-mmx -mno-sse -mno-sse2 -mno-sse3 -mno-3dnow endif diff --git a/sortix/interrupt.cpp b/sortix/interrupt.cpp index ecd51cc7..6e1e44a7 100644 --- a/sortix/interrupt.cpp +++ b/sortix/interrupt.cpp @@ -255,7 +255,7 @@ void ISRHandler(Sortix::CPU::InterruptRegisters* regs) Log::Print("\n"); } - if ( regs->int_no < 32 ) + if ( regs->int_no < 32 && regs->int_no != 7 ) { CrashHandler(regs); return; diff --git a/sortix/kernel.cpp b/sortix/kernel.cpp index 7a082cf0..0de92529 100644 --- a/sortix/kernel.cpp +++ b/sortix/kernel.cpp @@ -39,6 +39,7 @@ #include #include "kernelinfo.h" #include "x86-family/gdt.h" +#include "x86-family/float.h" #include "time.h" #include "keyboard.h" #include "multiboot.h" @@ -265,6 +266,7 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo) idlethread->kernelstackpos = (addr_t) stack; idlethread->kernelstacksize = STACK_SIZE; idlethread->kernelstackmalloced = false; + idlethread->fpuinitialized = true; system->firstthread = idlethread; Scheduler::SetIdleThread(idlethread); @@ -273,6 +275,9 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo) // nothing to run. Therefore we must become the system idle thread. RunKernelThread(BootThread, NULL); + // Set up such that floating point registers are lazily switched. + Float::Init(); + // The time driver will run the scheduler on the next timer interrupt. Time::Init(); diff --git a/sortix/scheduler.cpp b/sortix/scheduler.cpp index 1074db12..652abc76 100644 --- a/sortix/scheduler.cpp +++ b/sortix/scheduler.cpp @@ -27,6 +27,7 @@ #include #include #include "x86-family/gdt.h" +#include "x86-family/float.h" #include "syscall.h" #include "interrupt.h" #include "time.h" @@ -104,6 +105,8 @@ static void DoActualSwitch(CPU::InterruptRegisters* regs) current->SaveRegisters(regs); next->LoadRegisters(regs); + Float::NotityTaskSwitch(); + addr_t newaddrspace = next->addrspace; Memory::SwitchAddressSpace(newaddrspace); SetCurrentThread(next); @@ -174,6 +177,7 @@ static void InterruptYieldCPU(CPU::InterruptRegisters* regs, void* /*user*/) static void ThreadExitCPU(CPU::InterruptRegisters* regs, void* /*user*/) { + Float::NofityTaskExit(); // Can't use floating point instructions from now. SetThreadState(currentthread, Thread::State::DEAD); InterruptYieldCPU(regs, NULL); } diff --git a/sortix/thread.cpp b/sortix/thread.cpp index a1733d4b..4289385e 100644 --- a/sortix/thread.cpp +++ b/sortix/thread.cpp @@ -59,6 +59,11 @@ namespace Sortix siglevel = 0; sighandler = NULL; terminated = false; + fpuinitialized = false; + // If malloc isn't 16-byte aligned, then we can't rely on offsets in + // our own class, so we'll just fix ourselves nicely up. + unsigned long fpuaddr = ((unsigned long) fpuenv+16UL) & ~(16UL-1UL); + fpuenvaligned = (uint8_t*) fpuaddr; } Thread::~Thread() diff --git a/sortix/thread.h b/sortix/thread.h index ebe3327a..bcebb3d4 100644 --- a/sortix/thread.h +++ b/sortix/thread.h @@ -100,6 +100,9 @@ namespace Sortix Thread* schedulerlistprev; Thread* schedulerlistnext; volatile State state; + uint8_t fpuenv[512UL + 16UL]; + uint8_t* fpuenvaligned; + bool fpuinitialized; public: addr_t addrspace; diff --git a/sortix/x86-family/float.cpp b/sortix/x86-family/float.cpp new file mode 100644 index 00000000..b8e137c3 --- /dev/null +++ b/sortix/x86-family/float.cpp @@ -0,0 +1,84 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. + + This file is part of Sortix. + + Sortix 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. + + Sortix 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 + Sortix. If not, see . + + float.cpp + Handles saving and restoration of floating point numbers. + +*******************************************************************************/ + +#include +#include "../interrupt.h" +#include "../thread.h" +#include "float.h" + +namespace Sortix { +namespace Float { + +static Thread* fputhread; + +static inline void InitFPU() +{ + asm volatile ("fninit"); +} + +static inline void SaveState(uint8_t* dest) +{ + ASSERT( (((unsigned long) dest) & (16UL-1UL)) == 0 ); + asm volatile ("fxsave (%0)" : : "r"(dest)); +} + +static inline void LoadState(const uint8_t* src) +{ + ASSERT( (((unsigned long) src) & (16UL-1UL)) == 0 ); + asm volatile ("fxrstor (%0)" : : "r"(src)); +} + +static void OnFPUAccess(CPU::InterruptRegisters* /*regs*/, void* /*user*/) +{ + EnableFPU(); + Thread* thread = CurrentThread(); + if ( thread == fputhread ) + return; + if ( fputhread ) + SaveState(fputhread->fpuenvaligned); + fputhread = thread; + if ( !thread->fpuinitialized ) + { + InitFPU(); + thread->fpuinitialized = true; + return; + } + LoadState(thread->fpuenvaligned); +} + +void Init() +{ + fputhread = CurrentThread(); + ASSERT(fputhread); + Interrupt::RegisterHandler(7, OnFPUAccess, NULL); +} + +void NofityTaskExit() +{ + fputhread = NULL; + DisableFPU(); +} + +} // namespace Float +} // namespace Sortix diff --git a/sortix/x86-family/float.h b/sortix/x86-family/float.h new file mode 100644 index 00000000..94b6e19f --- /dev/null +++ b/sortix/x86-family/float.h @@ -0,0 +1,54 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. + + This file is part of Sortix. + + Sortix 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. + + Sortix 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 + Sortix. If not, see . + + float.cpp + Handles saving and restoration of floating point numbers. + +*******************************************************************************/ + +#ifndef SORTIX_FLOAT_H +#define SORTIX_FLOAT_H + +namespace Sortix { +namespace Float { + +void Init(); +void NofityTaskExit(); + +static inline void EnableFPU() +{ + asm volatile ("clts"); +} + +static inline void DisableFPU() +{ + unsigned long cr0; + asm volatile ("mov %%cr0, %0" : "=r"(cr0)); + cr0 |= 1UL<<3UL; + asm volatile ("mov %0, %%cr0" : : "r"(cr0)); +} + +static inline void NotityTaskSwitch() +{ + DisableFPU(); +} + +} // namespace Float +} // namespace Sortix +#endif