diff --git a/kernel/arch/i386/arch_system.c b/kernel/arch/i386/arch_system.c index 505b50f53..be8503a57 100644 --- a/kernel/arch/i386/arch_system.c +++ b/kernel/arch/i386/arch_system.c @@ -290,20 +290,16 @@ PUBLIC void save_local_fpu(struct proc *pr) PUBLIC void save_fpu(struct proc *pr) { + int r; + #ifdef CONFIG_SMP - if (cpuid == pr->p_cpu) { - if (get_cpulocal_var(fpu_owner) == pr) { - disable_fpu_exception(); - save_local_fpu(pr); - } - } - else { + if (cpuid != pr->p_cpu) { int stopped; /* remember if the process was already stopped */ stopped = RTS_ISSET(pr, RTS_PROC_STOP); - /* stop the remote process and force it's context to be saved */ + /* stop the remote process and force its context to be saved */ smp_schedule_stop_proc_save_ctx(pr); /* @@ -313,13 +309,21 @@ PUBLIC void save_fpu(struct proc *pr) */ if (!stopped) RTS_UNSET(pr, RTS_PROC_STOP); + + return; } -#else +#endif + if (get_cpulocal_var(fpu_owner) == pr) { disable_fpu_exception(); save_local_fpu(pr); + + /* The state may now be reset, and the caller does not expect + * this. Immediately restore the saved state. + */ + r = restore_fpu(pr); + assert(r == OK); } -#endif } PUBLIC int restore_fpu(struct proc *pr) diff --git a/test/test51.c b/test/test51.c index e896c6da2..0a8d83140 100644 --- a/test/test51.c +++ b/test/test51.c @@ -106,6 +106,7 @@ void do_child(void) void do_parent(void) { + ucontext_t dummy; int s; s = 1; @@ -116,11 +117,16 @@ void do_parent(void) between context swaps. */ if (fesetround(FE_UPWARD) != 0) err(10, 2); + /* Quick check to make sure that getcontext does not reset the FPU state. */ + getcontext(&dummy); + + if (fegetround() != FE_UPWARD) err(10, 3); + while(s < SWAPS) { do_calcs(); - if (fegetround() != FE_UPWARD) err(10, 3); + if (fegetround() != FE_UPWARD) err(10, 4); s++; - if (swapcontext(&ctx[1], &ctx[2]) == -1) err(10, 4); + if (swapcontext(&ctx[1], &ctx[2]) == -1) err(10, 5); } /* Returning to main thread through uc_link */ }