123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142 |
- /* vim: tabstop=4 shiftwidth=4 noexpandtab
- * This file is part of ToaruOS and is released under the terms
- * of the NCSA / University of Illinois License - see LICENSE.md
- * Copyright (C) 2011-2013 Kevin Lange
- *
- * FPU and SSE context handling.
- *
- * FPU context is kept through context switches,
- * but the FPU is disabled. When an FPU instruction
- * is executed, it will trap here and the context
- * will be saved to its original owner and the context
- * for the current process will be loaded or the FPU
- * will be reset for the new process.
- *
- * FPU states are per kernel thread.
- *
- */
- #include <kernel/system.h>
- #include <kernel/logging.h>
- #define NO_LAZY_FPU
- process_t * fpu_thread = NULL;
- /**
- * Set the FPU control word
- *
- * @param cw What to set the control word to.
- */
- void
- set_fpu_cw(const uint16_t cw) {
- asm volatile("fldcw %0" :: "m"(cw));
- }
- /**
- * Enable the FPU and SSE
- */
- void enable_fpu(void) {
- asm volatile ("clts");
- size_t t;
- asm volatile ("mov %%cr0, %0" : "=r"(t));
- t &= ~(1 << 2);
- t |= (1 << 1);
- asm volatile ("mov %0, %%cr0" :: "r"(t));
- asm volatile ("mov %%cr4, %0" : "=r"(t));
- t |= 3 << 9;
- asm volatile ("mov %0, %%cr4" :: "r"(t));
- }
- /**
- * Disable FPU and SSE so it traps to the kernel
- */
- void disable_fpu(void) {
- size_t t;
- asm volatile ("mov %%cr0, %0" : "=r"(t));
- t |= 1 << 3;
- asm volatile ("mov %0, %%cr0" :: "r"(t));
- }
- /* Temporary aligned buffer for copying around FPU contexts */
- uint8_t saves[512] __attribute__((aligned(16)));
- /**
- * Restore the FPU for a process
- */
- void restore_fpu(process_t * proc) {
- memcpy(&saves,(uint8_t *)&proc->thread.fp_regs,512);
- asm volatile ("fxrstor (%0)" :: "r"(saves));
- }
- /**
- * Save the FPU for a process
- */
- void save_fpu(process_t * proc) {
- asm volatile ("fxsave (%0)" :: "r"(saves));
- memcpy((uint8_t *)&proc->thread.fp_regs,&saves,512);
- }
- /**
- * Initialize the FPU
- */
- void init_fpu(void) {
- asm volatile ("fninit");
- }
- /**
- * Kernel trap for FPU usage when FPU is disabled
- */
- void invalid_op(struct regs * r) {
- /* First, turn the FPU on */
- enable_fpu();
- if (fpu_thread == current_process) {
- /* If this is the thread that last used the FPU, do nothing */
- return;
- }
- if (fpu_thread) {
- /* If there is a thread that was using the FPU, save its state */
- save_fpu(fpu_thread);
- }
- fpu_thread = (process_t *)current_process;
- if (!fpu_thread->thread.fpu_enabled) {
- /*
- * If the FPU has not been used in this thread previously,
- * we need to initialize it.
- */
- init_fpu();
- fpu_thread->thread.fpu_enabled = 1;
- return;
- }
- /* Otherwise we restore the context for this thread. */
- restore_fpu(fpu_thread);
- }
- /* Called during a context switch; disable the FPU */
- void switch_fpu(void) {
- #ifdef NO_LAZY_FPU
- save_fpu((process_t *)current_process);
- #else
- disable_fpu();
- #endif
- }
- void unswitch_fpu(void) {
- #ifdef NO_LAZY_FPU
- restore_fpu((process_t *)current_process);
- #endif
- }
- /* Enable the FPU context handling */
- void fpu_install(void) {
- #ifdef NO_LAZY_FPU
- enable_fpu();
- init_fpu();
- save_fpu((void*)current_process);
- #else
- enable_fpu();
- disable_fpu();
- isrs_install_handler(7, &invalid_op);
- #endif
- }
|