From 7d39906acc4d3ad96ac273061d04e5b3dd876dfa Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Sun, 5 Aug 2012 00:06:17 +0200 Subject: [PATCH] Added support for saving FPU registers upon context switch. This code uses the cr0 task switched bit to disable the FPU upon task switch, which allows the kernel to delay copying the registers until another task starts using them. Or better yet, if no other thread actually uses the registers, then it won't need to do any copying at all! --- sortix/Makefile | 1 + sortix/interrupt.cpp | 2 +- sortix/kernel.cpp | 5 +++ sortix/scheduler.cpp | 4 ++ sortix/thread.cpp | 5 +++ sortix/thread.h | 3 ++ sortix/x86-family/float.cpp | 84 +++++++++++++++++++++++++++++++++++++ sortix/x86-family/float.h | 54 ++++++++++++++++++++++++ 8 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 sortix/x86-family/float.cpp create mode 100644 sortix/x86-family/float.h 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