diff --git a/arch/x86/include/asm/tasks_types.h b/arch/x86/include/asm/tasks_types.h index f0095bf3..138fbd1c 100644 --- a/arch/x86/include/asm/tasks_types.h +++ b/arch/x86/include/asm/tasks_types.h @@ -55,11 +55,15 @@ typedef struct i387_fxsave_struct { long padding[56]; } i387_fxsave_t __attribute__ ((aligned (16))); -union fpu_union { +union fpu_state { i387_fsave_t fsave; i387_fxsave_t fxsave; }; +static inline void save_fpu_state(union fpu_state* state) { + asm volatile ("fsave %0; fwait" : "=m"((*state).fsave)); +} + #ifdef __cplusplus } #endif diff --git a/arch/x86/kernel/entry.asm b/arch/x86/kernel/entry.asm index 1a325dba..ca3afaa1 100644 --- a/arch/x86/kernel/entry.asm +++ b/arch/x86/kernel/entry.asm @@ -64,16 +64,19 @@ ALIGN 4 stublet: ; initialize stack pointer. mov esp, default_stack_pointer -; enable cache and turn on FPU exceptions mov eax, cr0 - ; enable cache - and eax, 0x9fffffff - ; ...and turn on FPU exceptions - or eax, 0x20 +; enable cache, disable paging and fpu emulation + and eax, 0x3ffffffb +; ...monitor coprocessor and turn on FPU exceptions + or eax, 0x22 mov cr0, eax ; clears the current pgd entry xor eax, eax mov cr3, eax +; disable SSE support (TODO) + mov eax, cr4 + and eax, 0xfffbf9ff + mov cr4, eax ; interpret multiboot information extern multiboot_init push ebx diff --git a/arch/x86/kernel/isrs.c b/arch/x86/kernel/isrs.c index 63075d00..6df9526e 100644 --- a/arch/x86/kernel/isrs.c +++ b/arch/x86/kernel/isrs.c @@ -74,6 +74,7 @@ extern void isr30(void); extern void isr31(void); static void fault_handler(struct state *s); +static void fpu_handler(struct state *s); /* * This is a very repetitive function... it's not hard, it's @@ -158,6 +159,23 @@ void isrs_install(void) // install the default handler for(i=0; i<32; i++) irq_install_handler(i, fault_handler); + + // set hanlder for fpu exceptions + irq_uninstall_handler(7); + irq_install_handler(7, fpu_handler); +} + +static void fpu_handler(struct state *s) +{ + task_t* task = per_core(current_task); + + kputs("got FPU exception\n"); + asm volatile ("clts"); // clear the TS flag of cr0 + if (!task->fpu_used) { + task->fpu_used = 1; + asm volatile ("finit"); + } else + asm volatile ("frstor %0" :: "m"(task->fpu.fsave)); // restore fpu state } /** @brief Exception messages @@ -189,7 +207,7 @@ static void fault_handler(struct state *s) { if (s->int_no < 32) { kputs(exception_messages[s->int_no]); - kputs(" Exception.\n"); + kprintf(" Exception. (%d)\n", s->int_no); /* Now, we signalize that we have handled the interrupt */ if (apic_is_enabled()) diff --git a/include/metalsvm/tasks_types.h b/include/metalsvm/tasks_types.h index 29fa5337..c46ec4b4 100644 --- a/include/metalsvm/tasks_types.h +++ b/include/metalsvm/tasks_types.h @@ -66,12 +66,14 @@ typedef struct task { spinlock_t vma_lock; /// List of VMAs vma_t* vma_list; + /// Is set, when the FPU is used + uint32_t fpu_used; /// Mail inbox mailbox_wait_msg_t inbox; /// Mail outbox array mailbox_wait_msg_t* outbox[MAX_TASKS]; /// FPU state - union fpu_union fpu_state; + union fpu_state fpu; } __attribute__((packed)) task_t; #ifdef __cplusplus diff --git a/kernel/tasks.c b/kernel/tasks.c index 6be430ce..3ffb49b9 100644 --- a/kernel/tasks.c +++ b/kernel/tasks.c @@ -48,7 +48,7 @@ DEFINE_PER_CORE(task_t*, current_task, NULL); * A task's id will be its position in this array. */ static task_t task_table[MAX_TASKS] = {[0 ... MAX_TASKS-1] = {0, TASK_INVALID, ATOMIC_INIT(0), \ - SPINLOCK_INIT, NULL, SPINLOCK_INIT, NULL}}; + SPINLOCK_INIT, NULL, SPINLOCK_INIT, NULL, 0}}; static spinlock_irqsave_t table_lock = SPINLOCK_IRQSAVE_INIT; /** @brief helper function for the assembly code to determine the current task @@ -67,6 +67,7 @@ int multitasking_init(void) { memset(task_table[0].outbox, 0x00, sizeof(mailbox_wait_msg_t*)*MAX_TASKS); per_core(current_task) = task_table+0; per_core(current_task)->pgd = get_boot_pgd(); + task_table[0].fpu_used = 0; return 0; } @@ -189,6 +190,7 @@ static int create_task(tid_t* id, entry_point_t ep, void* arg) ret = create_default_frame(task_table+i, ep, arg); + task_table[i].fpu_used = 0; task_table[i].status = TASK_READY; break; } @@ -250,6 +252,7 @@ int sys_fork(void) mailbox_wait_msg_init(&task_table[i].inbox); memset(task_table[i].outbox, 0x00, sizeof(mailbox_wait_msg_t*)*MAX_TASKS); task_table[i].outbox[per_core(current_task)->id] = &per_core(current_task)->inbox; + task_table[i].fpu_used = 0x00; ret = arch_fork(task_table+i); @@ -707,7 +710,9 @@ void scheduler(void) if (per_core(current_task)->status == TASK_RUNNING) per_core(current_task)->status = TASK_READY; task_table[new_id].status = TASK_RUNNING; - + + if (per_core(current_task)->fpu_used) + save_fpu_state(&(per_core(current_task)->fpu)); per_core(current_task) = task_table+new_id; goto get_task_out; }