kernel fpu context switching: fix race condition
There seems to have been a broken assumption in the fpu context restoring code. It restores the context of the running process, without guarantee that the current process is the one that will be scheduled. This caused fpu saving for a different process to be triggered without fpu hardware being enabled, causing an fpu exception in the kernel. This practically only shows up with DEBUG_RACE on. Fix my thruby+me. The fix . is to only set the fpu-in-use-by-this-process flag in the exception handler, and then take care of fpu restoring when actually returning to userspace And the patch . translates fpu saving and restoring to c in arch_system.c, getting rid of a juicy chunk of assembly . makes osfxsr_feature private to arch_system.c . removes most of the arch dependent code from do_sigsend
This commit is contained in:
parent
332842295a
commit
2f892aca91
@ -11,6 +11,10 @@
|
||||
#include <minix/cpufeature.h>
|
||||
#include <a.out.h>
|
||||
#include <assert.h>
|
||||
#include <signal.h>
|
||||
#include <machine/vm.h>
|
||||
|
||||
#include <sys/sigcontext.h>
|
||||
|
||||
#include "archconst.h"
|
||||
#include "proto.h"
|
||||
@ -23,6 +27,9 @@
|
||||
#include "apic.h"
|
||||
#endif
|
||||
|
||||
PRIVATE int osfxsr_feature; /* FXSAVE/FXRSTOR instructions support (SSEx) */
|
||||
|
||||
|
||||
/* set MP and NE flags to handle FPU exceptions in native mode. */
|
||||
#define CR0_MP_NE 0x0022
|
||||
/* set CR4.OSFXSR[bit 9] if FXSR is supported. */
|
||||
@ -194,6 +201,59 @@ PRIVATE void fpu_init(void)
|
||||
}
|
||||
}
|
||||
|
||||
PUBLIC void save_fpu(struct proc *pr)
|
||||
{
|
||||
if(!fpu_presence)
|
||||
return;
|
||||
|
||||
/* If the process hasn't touched the FPU, there is nothing to do. */
|
||||
|
||||
if(!(pr->p_misc_flags & MF_USED_FPU))
|
||||
return;
|
||||
|
||||
/* Save changed FPU context. */
|
||||
|
||||
if(osfxsr_feature) {
|
||||
fxsave(pr->p_fpu_state.fpu_save_area_p);
|
||||
fninit();
|
||||
} else {
|
||||
fnsave(pr->p_fpu_state.fpu_save_area_p);
|
||||
}
|
||||
|
||||
/* Clear MF_USED_FPU to signal there is no unsaved FPU state. */
|
||||
|
||||
pr->p_misc_flags &= ~MF_USED_FPU;
|
||||
}
|
||||
|
||||
PUBLIC void restore_fpu(struct proc *pr)
|
||||
{
|
||||
/* If the process hasn't touched the FPU, enable the FPU exception
|
||||
* and don't restore anything.
|
||||
*/
|
||||
if(!(pr->p_misc_flags & MF_USED_FPU)) {
|
||||
write_cr0(read_cr0() | I386_CR0_TS);
|
||||
return;
|
||||
}
|
||||
|
||||
/* If the process has touched the FPU, disable the FPU
|
||||
* exception (both for the kernel and for the process once
|
||||
* it's scheduled), and initialize or restore the FPU state.
|
||||
*/
|
||||
|
||||
clts();
|
||||
|
||||
if(!(pr->p_misc_flags & MF_FPU_INITIALIZED)) {
|
||||
fninit();
|
||||
pr->p_misc_flags |= MF_FPU_INITIALIZED;
|
||||
} else {
|
||||
if(osfxsr_feature) {
|
||||
fxrstor(pr->p_fpu_state.fpu_save_area_p);
|
||||
} else {
|
||||
frstor(pr->p_fpu_state.fpu_save_area_p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PUBLIC void arch_init(void)
|
||||
{
|
||||
#ifdef CONFIG_APIC
|
||||
@ -438,3 +498,36 @@ PUBLIC struct proc * arch_finish_switch_to_user(void)
|
||||
*((reg_t *)stk) = (reg_t) proc_ptr;
|
||||
return proc_ptr;
|
||||
}
|
||||
|
||||
PUBLIC void fpu_sigcontext(struct proc *pr, struct sigframe *fr, struct sigcontext *sc)
|
||||
{
|
||||
int fp_error;
|
||||
|
||||
if (osfxsr_feature) {
|
||||
fp_error = sc->sc_fpu_state.xfp_regs.fp_status &
|
||||
~sc->sc_fpu_state.xfp_regs.fp_control;
|
||||
} else {
|
||||
fp_error = sc->sc_fpu_state.fpu_regs.fp_status &
|
||||
~sc->sc_fpu_state.fpu_regs.fp_control;
|
||||
}
|
||||
|
||||
if (fp_error & 0x001) { /* Invalid op */
|
||||
/*
|
||||
* swd & 0x240 == 0x040: Stack Underflow
|
||||
* swd & 0x240 == 0x240: Stack Overflow
|
||||
* User must clear the SF bit (0x40) if set
|
||||
*/
|
||||
fr->sf_code = FPE_FLTINV;
|
||||
} else if (fp_error & 0x004) {
|
||||
fr->sf_code = FPE_FLTDIV; /* Divide by Zero */
|
||||
} else if (fp_error & 0x008) {
|
||||
fr->sf_code = FPE_FLTOVF; /* Overflow */
|
||||
} else if (fp_error & 0x012) {
|
||||
fr->sf_code = FPE_FLTUND; /* Denormal, Underflow */
|
||||
} else if (fp_error & 0x020) {
|
||||
fr->sf_code = FPE_FLTRES; /* Precision */
|
||||
} else {
|
||||
fr->sf_code = 0; /* XXX - probably should be used for FPE_INTOVF or
|
||||
* FPE_INTDIV */
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +50,11 @@
|
||||
.globl _fninit /* non-waiting FPU initialization */
|
||||
.globl _fnstsw /* store status word (non-waiting) */
|
||||
.globl _fnstcw /* store control word (non-waiting) */
|
||||
.globl _fxsave
|
||||
.globl _fnsave
|
||||
.globl _fxrstor
|
||||
.globl _frstor
|
||||
.globl _clts
|
||||
|
||||
/*
|
||||
* The routines only guarantee to preserve the registers the C compiler
|
||||
@ -655,6 +660,10 @@ _fninit:
|
||||
fninit
|
||||
ret
|
||||
|
||||
_clts:
|
||||
clts
|
||||
ret
|
||||
|
||||
_fnstsw:
|
||||
xor %eax, %eax
|
||||
|
||||
@ -671,6 +680,40 @@ _fnstcw:
|
||||
pop %eax
|
||||
ret
|
||||
|
||||
/*===========================================================================*/
|
||||
/* fxsave */
|
||||
/*===========================================================================*/
|
||||
_fxsave:
|
||||
mov 4(%esp), %eax
|
||||
fxsave (%eax) /* Do not change the operand! (gas2ack) */
|
||||
ret
|
||||
|
||||
/*===========================================================================*/
|
||||
/* fnsave */
|
||||
/*===========================================================================*/
|
||||
_fnsave:
|
||||
mov 4(%esp), %eax
|
||||
fnsave (%eax) /* Do not change the operand! (gas2ack) */
|
||||
fwait /* required for compatibility with processors prior pentium */
|
||||
ret
|
||||
|
||||
/*===========================================================================*/
|
||||
/* fxrstor */
|
||||
/*===========================================================================*/
|
||||
_fxrstor:
|
||||
mov 4(%esp), %eax
|
||||
fxrstor (%eax) /* Do not change the operand! (gas2ack) */
|
||||
ret
|
||||
|
||||
/*===========================================================================*/
|
||||
/* frstor */
|
||||
/*===========================================================================*/
|
||||
_frstor:
|
||||
mov 4(%esp), %eax
|
||||
frstor (%eax) /* Do not change the operand! (gas2ack) */
|
||||
ret
|
||||
|
||||
|
||||
/*===========================================================================*/
|
||||
/* read_cr0 */
|
||||
/*===========================================================================*/
|
||||
@ -847,3 +890,4 @@ _switch_address_space:
|
||||
mov %edx, _ptproc
|
||||
0:
|
||||
ret
|
||||
|
||||
|
@ -90,7 +90,7 @@ begbss:
|
||||
.globl _params_offset
|
||||
.globl _mon_ds
|
||||
.globl _switch_to_user
|
||||
.globl _lazy_fpu
|
||||
.globl _save_fpu
|
||||
|
||||
.globl _hwint00 /* handlers for hardware interrupts */
|
||||
.globl _hwint01
|
||||
@ -589,33 +589,16 @@ _inval_opcode:
|
||||
|
||||
_copr_not_available:
|
||||
TEST_INT_IN_KERNEL(4, copr_not_available_in_kernel)
|
||||
clts
|
||||
cld /* set direction flag to a known value */
|
||||
cld /* set direction flag to a known value */
|
||||
SAVE_PROCESS_CTX_NON_LAZY(0)
|
||||
/* stop user process cycles */
|
||||
push %ebp
|
||||
mov $0, %ebp
|
||||
call _context_stop
|
||||
pop %ebp
|
||||
lea P_MISC_FLAGS(%ebp), %ebx
|
||||
movw (%ebx), %cx
|
||||
and $MF_FPU_INITIALIZED, %cx
|
||||
jnz 0f /* jump if FPU is already initialized */
|
||||
orw $MF_FPU_INITIALIZED, (%ebx)
|
||||
fninit
|
||||
jmp copr_return
|
||||
0: /* load FPU context for current process */
|
||||
mov %ss:FP_SAVE_AREA_P(%ebp), %eax
|
||||
cmp $0, _osfxsr_feature
|
||||
jz fp_l_no_fxsr /* FXSR is not avaible. */
|
||||
|
||||
/* DO NOT CHANGE THE OPERAND!!! gas2ack does not handle it yet */
|
||||
fxrstor (%eax)
|
||||
jmp copr_return
|
||||
fp_l_no_fxsr:
|
||||
/* DO NOT CHANGE THE OPERAND!!! gas2ack does not handle it yet */
|
||||
frstor (%eax)
|
||||
copr_return:
|
||||
orw $MF_USED_FPU, (%ebx) /* fpu was used during last execution */
|
||||
orw $MF_USED_FPU, (%ebx)
|
||||
mov $0, %ebp
|
||||
jmp _switch_to_user
|
||||
|
||||
copr_not_available_in_kernel:
|
||||
@ -656,51 +639,6 @@ _machine_check:
|
||||
_simd_exception:
|
||||
EXCEPTION_NO_ERR_CODE(SIMD_EXCEPTION_VECTOR)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* lazy_fpu */
|
||||
/*===========================================================================*/
|
||||
/* void lazy_fpu(struct proc *pptr)
|
||||
* It's called, when we are on kernel stack.
|
||||
* Actualy lazy code is just few lines, which check MF_USED_FPU,
|
||||
* another part is save_init_fpu().
|
||||
*/
|
||||
_lazy_fpu:
|
||||
push %ebp
|
||||
mov %esp, %ebp
|
||||
push %eax
|
||||
push %ebx
|
||||
push %ecx
|
||||
cmp $0, _fpu_presence /* Do we have FPU? */
|
||||
jz no_fpu_available
|
||||
mov 8(%ebp), %eax /* Get pptr */
|
||||
lea P_MISC_FLAGS(%eax), %ebx
|
||||
movw (%ebx), %cx
|
||||
and $MF_USED_FPU, %cx
|
||||
jz 0f /* Don't save FPU */
|
||||
mov %ss:FP_SAVE_AREA_P(%eax), %eax
|
||||
cmp $0, _osfxsr_feature
|
||||
jz fp_s_no_fxsr /* FXSR is not avaible. */
|
||||
|
||||
/* DO NOT CHANGE THE OPERAND!!! gas2ack does not handle it yet */
|
||||
fxsave (%eax)
|
||||
fninit
|
||||
jmp fp_saved
|
||||
fp_s_no_fxsr:
|
||||
/* DO NOT CHANGE THE OPERAND!!! gas2ack does not handle it yet */
|
||||
fnsave (%eax)
|
||||
fwait /* required for compatibility with processors prior pentium */
|
||||
fp_saved:
|
||||
andw $~MF_USED_FPU, (%ebx)
|
||||
0: mov %cr0, %eax
|
||||
or $0x00000008, %eax /* Set TS flag */
|
||||
mov %eax, %cr0
|
||||
no_fpu_available:
|
||||
pop %ecx
|
||||
pop %ebx
|
||||
pop %eax
|
||||
pop %ebp
|
||||
ret
|
||||
|
||||
/*===========================================================================*/
|
||||
/* reload_cr3 */
|
||||
/*===========================================================================*/
|
||||
|
@ -86,6 +86,11 @@ _PROTOTYPE( void reload_ds, (void) );
|
||||
_PROTOTYPE( void ia32_msr_read, (u32_t reg, u32_t * hi, u32_t * lo) );
|
||||
_PROTOTYPE( void ia32_msr_write, (u32_t reg, u32_t hi, u32_t lo) );
|
||||
_PROTOTYPE( void fninit, (void));
|
||||
_PROTOTYPE( void clts, (void));
|
||||
_PROTOTYPE( void fxsave, (void *));
|
||||
_PROTOTYPE( void fnsave, (void *));
|
||||
_PROTOTYPE( void fxrstor, (void *));
|
||||
_PROTOTYPE( void frstor, (void *));
|
||||
_PROTOTYPE( unsigned short fnstsw, (void));
|
||||
_PROTOTYPE( void fnstcw, (unsigned short* cw));
|
||||
|
||||
|
@ -140,10 +140,18 @@
|
||||
SAVE_TRAP_CTX(displ, %ebp, %esi) ;
|
||||
|
||||
#define SAVE_PROCESS_CTX(displ) \
|
||||
SAVE_PROCESS_CTX_NON_LAZY(displ) ;\
|
||||
SAVE_PROCESS_CTX_NON_LAZY(displ) ;\
|
||||
push %eax ;\
|
||||
push %ebx ;\
|
||||
push %ecx ;\
|
||||
push %edx ;\
|
||||
push %ebp ;\
|
||||
call _lazy_fpu ;\
|
||||
add $4, %esp ;
|
||||
call _save_fpu ;\
|
||||
pop %ebp ;\
|
||||
pop %edx ;\
|
||||
pop %ecx ;\
|
||||
pop %ebx ;\
|
||||
pop %eax ;
|
||||
|
||||
/*
|
||||
* clear the IF flag in eflags which are stored somewhere in memory, e.g. on
|
||||
|
@ -45,7 +45,6 @@ EXTERN time_t boottime;
|
||||
EXTERN char params_buffer[512]; /* boot monitor parameters */
|
||||
EXTERN int minix_panicing;
|
||||
EXTERN char fpu_presence;
|
||||
EXTERN char osfxsr_feature; /* FXSAVE/FXRSTOR instructions support (SSEx) */
|
||||
EXTERN int verboseboot; /* verbose boot, init'ed in cstart */
|
||||
#define MAGICTEST 0xC0FFEE23
|
||||
EXTERN u32_t magictest; /* global magic number */
|
||||
|
@ -241,6 +241,7 @@ check_misc_flags:
|
||||
* restore_user_context() carries out the actual mode switch from kernel
|
||||
* to userspace. This function does not return
|
||||
*/
|
||||
restore_fpu(proc_ptr);
|
||||
restore_user_context(proc_ptr);
|
||||
NOT_REACHABLE;
|
||||
}
|
||||
|
@ -146,6 +146,7 @@ struct proc {
|
||||
#define proc_is_preempted(p) ((p)->p_rts_flags & RTS_PREEMPTED)
|
||||
#define proc_no_quantum(p) ((p)->p_rts_flags & RTS_NO_QUANTUM)
|
||||
#define proc_ptr_ok(p) ((p)->p_magic == PMAGIC)
|
||||
#define proc_used_fpu(p) ((p)->p_misc_flags & (MF_FPU_INITIALIZED|MF_USED_FPU))
|
||||
|
||||
/* test whether the process is scheduled by the kernel's default policy */
|
||||
#define proc_kernel_scheduler(p) ((p)->p_scheduler == NULL || \
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include <minix/safecopies.h>
|
||||
#include <machine/archtypes.h>
|
||||
#include <sys/sigcontext.h>
|
||||
#include <a.out.h>
|
||||
|
||||
/* Struct declarations. */
|
||||
@ -28,6 +29,9 @@ _PROTOTYPE( void cycles_accounting_init, (void) );
|
||||
_PROTOTYPE( void context_stop, (struct proc * p) );
|
||||
/* this is a wrapper to make calling it from assembly easier */
|
||||
_PROTOTYPE( void context_stop_idle, (void) );
|
||||
_PROTOTYPE( void restore_fpu, (struct proc *) );
|
||||
_PROTOTYPE( void save_fpu, (struct proc *) );
|
||||
_PROTOTYPE( void fpu_sigcontext, (struct proc *, struct sigframe *fr, struct sigcontext *sc) );
|
||||
|
||||
/* main.c */
|
||||
_PROTOTYPE( void main, (void) );
|
||||
|
@ -27,9 +27,6 @@ PUBLIC int do_sigsend(struct proc * caller, message * m_ptr)
|
||||
struct sigcontext sc, *scp;
|
||||
struct sigframe fr, *frp;
|
||||
int proc_nr, r;
|
||||
#if (_MINIX_CHIP == _CHIP_INTEL)
|
||||
unsigned short int fp_error;
|
||||
#endif
|
||||
|
||||
if (!isokendpt(m_ptr->SIG_ENDPT, &proc_nr)) return(EINVAL);
|
||||
if (iskerneln(proc_nr)) return(EPERM);
|
||||
@ -69,38 +66,7 @@ PUBLIC int do_sigsend(struct proc * caller, message * m_ptr)
|
||||
rp->p_reg.fp = (reg_t) &frp->sf_fp;
|
||||
fr.sf_scp = scp;
|
||||
|
||||
#if (_MINIX_CHIP == _CHIP_INTEL)
|
||||
if (osfxsr_feature == 1) {
|
||||
fp_error = sc.sc_fpu_state.xfp_regs.fp_status &
|
||||
~sc.sc_fpu_state.xfp_regs.fp_control;
|
||||
} else {
|
||||
fp_error = sc.sc_fpu_state.fpu_regs.fp_status &
|
||||
~sc.sc_fpu_state.fpu_regs.fp_control;
|
||||
}
|
||||
|
||||
if (fp_error & 0x001) { /* Invalid op */
|
||||
/*
|
||||
* swd & 0x240 == 0x040: Stack Underflow
|
||||
* swd & 0x240 == 0x240: Stack Overflow
|
||||
* User must clear the SF bit (0x40) if set
|
||||
*/
|
||||
fr.sf_code = FPE_FLTINV;
|
||||
} else if (fp_error & 0x004) {
|
||||
fr.sf_code = FPE_FLTDIV; /* Divide by Zero */
|
||||
} else if (fp_error & 0x008) {
|
||||
fr.sf_code = FPE_FLTOVF; /* Overflow */
|
||||
} else if (fp_error & 0x012) {
|
||||
fr.sf_code = FPE_FLTUND; /* Denormal, Underflow */
|
||||
} else if (fp_error & 0x020) {
|
||||
fr.sf_code = FPE_FLTRES; /* Precision */
|
||||
} else {
|
||||
fr.sf_code = 0; /* XXX - probably should be used for FPE_INTOVF or
|
||||
* FPE_INTDIV */
|
||||
}
|
||||
|
||||
#else
|
||||
fr.sf_code = 0;
|
||||
#endif
|
||||
fpu_sigcontext(rp, &fr, &sc);
|
||||
|
||||
fr.sf_signo = smsg.sm_signo;
|
||||
fr.sf_retadr = (void (*)()) smsg.sm_sigreturn;
|
||||
|
Loading…
x
Reference in New Issue
Block a user