ntdll: Linux support for saving and restoring the extended FPU context on exceptions.

This commit is contained in:
Alexandre Julliard 2008-01-15 18:08:29 +01:00
parent f45b7078bf
commit bd352bcd1c
1 changed files with 101 additions and 18 deletions

View File

@ -68,6 +68,33 @@
#undef ERR /* Solaris needs to define this */ #undef ERR /* Solaris needs to define this */
/* not defined for x86, so copy the x86_64 definition */
typedef struct DECLSPEC_ALIGN(16) _M128A
{
ULONGLONG Low;
LONGLONG High;
} M128A;
typedef struct
{
WORD ControlWord;
WORD StatusWord;
BYTE TagWord;
BYTE Reserved1;
WORD ErrorOpcode;
DWORD ErrorOffset;
WORD ErrorSelector;
WORD Reserved2;
DWORD DataOffset;
WORD DataSelector;
WORD Reserved3;
DWORD MxCsr;
DWORD MxCsr_Mask;
M128A FloatRegisters[8];
M128A XmmRegisters[16];
BYTE Reserved4[96];
} XMM_SAVE_AREA32;
/*********************************************************************** /***********************************************************************
* signal context platform-specific definitions * signal context platform-specific definitions
*/ */
@ -98,6 +125,7 @@ typedef ucontext_t SIGCONTEXT;
#define ERROR_sig(context) ((context)->uc_mcontext.gregs[REG_ERR]) #define ERROR_sig(context) ((context)->uc_mcontext.gregs[REG_ERR])
#define FPU_sig(context) ((FLOATING_SAVE_AREA*)((context)->uc_mcontext.fpregs)) #define FPU_sig(context) ((FLOATING_SAVE_AREA*)((context)->uc_mcontext.fpregs))
#define FPUX_sig(context) ((context)->uc_mcontext.fpregs->status >> 16 ? NULL : (XMM_SAVE_AREA32 *)(FPU_sig(context) + 1))
#define VM86_EAX 0 /* the %eax value while vm86_enter is executing */ #define VM86_EAX 0 /* the %eax value while vm86_enter is executing */
@ -165,6 +193,9 @@ typedef struct trapframe SIGCONTEXT;
#define EIP_sig(context) (*((unsigned long*)&(context)->tf_eip)) #define EIP_sig(context) (*((unsigned long*)&(context)->tf_eip))
#define ESP_sig(context) (*((unsigned long*)&(context)->tf_esp)) #define ESP_sig(context) (*((unsigned long*)&(context)->tf_esp))
#define FPU_sig(context) NULL /* FIXME */
#define FPUX_sig(context) NULL /* FIXME */
#endif /* bsdi */ #endif /* bsdi */
#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__)
@ -193,6 +224,9 @@ typedef struct sigcontext SIGCONTEXT;
#define EIP_sig(context) ((context)->sc_eip) #define EIP_sig(context) ((context)->sc_eip)
#define ESP_sig(context) ((context)->sc_esp) #define ESP_sig(context) ((context)->sc_esp)
#define FPU_sig(context) NULL /* FIXME */
#define FPUX_sig(context) NULL /* FIXME */
#endif /* *BSD */ #endif /* *BSD */
#if defined(__svr4__) || defined(_SCO_DS) || defined(__sun) #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
@ -240,6 +274,9 @@ typedef struct ucontext SIGCONTEXT;
#define TRAP_sig(context) ((context)->uc_mcontext.gregs[TRAPNO]) #define TRAP_sig(context) ((context)->uc_mcontext.gregs[TRAPNO])
#endif #endif
#define FPU_sig(context) NULL /* FIXME */
#define FPUX_sig(context) NULL /* FIXME */
#endif /* svr4 || SCO_DS */ #endif /* svr4 || SCO_DS */
#ifdef __APPLE__ #ifdef __APPLE__
@ -288,6 +325,9 @@ typedef ucontext_t SIGCONTEXT;
#define ERROR_sig(context) ((context)->uc_mcontext->es.err) #define ERROR_sig(context) ((context)->uc_mcontext->es.err)
#endif #endif
#define FPU_sig(context) NULL /* FIXME */
#define FPUX_sig(context) NULL /* FIXME */
#endif /* __APPLE__ */ #endif /* __APPLE__ */
WINE_DEFAULT_DEBUG_CHANNEL(seh); WINE_DEFAULT_DEBUG_CHANNEL(seh);
@ -299,6 +339,8 @@ static size_t signal_stack_size;
static wine_signal_handler handlers[256]; static wine_signal_handler handlers[256];
static int fpux_support; /* whether the CPU support extended fpu context */
extern void DECLSPEC_NORETURN __wine_call_from_32_restore_regs( const CONTEXT *context ); extern void DECLSPEC_NORETURN __wine_call_from_32_restore_regs( const CONTEXT *context );
enum i386_trap_code enum i386_trap_code
@ -605,6 +647,25 @@ static inline void save_fpu( CONTEXT *context )
} }
/***********************************************************************
* save_fpux
*
* Save the thread FPU extended context.
*/
static inline void save_fpux( CONTEXT *context )
{
#ifdef __GNUC__
/* we have to enforce alignment by hand */
char buffer[sizeof(XMM_SAVE_AREA32) + 16];
XMM_SAVE_AREA32 *state = (XMM_SAVE_AREA32 *)(((ULONG_PTR)buffer + 15) & ~15);
__asm__ __volatile__( "fxsave %0" : "=m" (*state) );
context->ContextFlags |= CONTEXT_EXTENDED_REGISTERS;
memcpy( context->ExtendedRegisters, state, sizeof(*state) );
#endif
}
/*********************************************************************** /***********************************************************************
* restore_fpu * restore_fpu
* *
@ -621,6 +682,26 @@ static inline void restore_fpu( const CONTEXT *context )
} }
/***********************************************************************
* restore_fpux
*
* Restore the FPU extended context to a sigcontext.
*/
static inline void restore_fpux( const CONTEXT *context )
{
#ifdef __GNUC__
/* we have to enforce alignment by hand */
char buffer[sizeof(XMM_SAVE_AREA32) + 16];
XMM_SAVE_AREA32 *state = (XMM_SAVE_AREA32 *)(((ULONG_PTR)buffer + 15) & ~15);
memcpy( state, context->ExtendedRegisters, sizeof(*state) );
/* reset the current interrupt status */
state->StatusWord &= state->ControlWord | 0xff80;
__asm__ __volatile__( "fxrstor %0" : : "m" (*state) );
#endif
}
/*********************************************************************** /***********************************************************************
* save_context * save_context
* *
@ -629,6 +710,8 @@ static inline void restore_fpu( const CONTEXT *context )
static inline void save_context( CONTEXT *context, const SIGCONTEXT *sigcontext, WORD fs, WORD gs ) static inline void save_context( CONTEXT *context, const SIGCONTEXT *sigcontext, WORD fs, WORD gs )
{ {
struct ntdll_thread_regs * const regs = ntdll_get_thread_regs(); struct ntdll_thread_regs * const regs = ntdll_get_thread_regs();
FLOATING_SAVE_AREA *fpu = FPU_sig(sigcontext);
XMM_SAVE_AREA32 *fpux = FPUX_sig(sigcontext);
memset(context, 0, sizeof(*context)); memset(context, 0, sizeof(*context));
context->ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS; context->ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
@ -655,17 +738,20 @@ static inline void save_context( CONTEXT *context, const SIGCONTEXT *sigcontext,
context->Dr6 = regs->dr6; context->Dr6 = regs->dr6;
context->Dr7 = regs->dr7; context->Dr7 = regs->dr7;
#ifdef FPU_sig if (fpu)
if (FPU_sig(sigcontext))
{ {
context->ContextFlags |= CONTEXT_FLOATING_POINT; context->ContextFlags |= CONTEXT_FLOATING_POINT;
context->FloatSave = *FPU_sig(sigcontext); context->FloatSave = *fpu;
} }
else if (fpux)
#endif
{ {
save_fpu( context ); save_fpux( context );
context->ContextFlags |= CONTEXT_EXTENDED_REGISTERS;
memcpy( context->ExtendedRegisters, fpux, sizeof(*fpux) );
fpux_support = 1;
/* FIXME: convert fpux to fpu */
} }
if (!fpu && !fpux) save_fpu( context );
} }
@ -677,6 +763,8 @@ static inline void save_context( CONTEXT *context, const SIGCONTEXT *sigcontext,
static inline void restore_context( const CONTEXT *context, SIGCONTEXT *sigcontext ) static inline void restore_context( const CONTEXT *context, SIGCONTEXT *sigcontext )
{ {
struct ntdll_thread_regs * const regs = ntdll_get_thread_regs(); struct ntdll_thread_regs * const regs = ntdll_get_thread_regs();
FLOATING_SAVE_AREA *fpu = FPU_sig(sigcontext);
XMM_SAVE_AREA32 *fpux = FPUX_sig(sigcontext);
regs->dr0 = context->Dr0; regs->dr0 = context->Dr0;
regs->dr1 = context->Dr1; regs->dr1 = context->Dr1;
@ -709,16 +797,9 @@ static inline void restore_context( const CONTEXT *context, SIGCONTEXT *sigconte
wine_set_fs( context->SegFs ); wine_set_fs( context->SegFs );
#endif #endif
#ifdef FPU_sig if (fpu) *fpu = context->FloatSave;
if (FPU_sig(sigcontext)) if (fpux) memcpy( fpux, context->ExtendedRegisters, sizeof(*fpux) );
{ if (!fpu && !fpux) restore_fpu( context );
*FPU_sig(sigcontext) = context->FloatSave;
}
else
#endif
{
restore_fpu( context );
}
} }
@ -730,7 +811,8 @@ static inline void restore_context( const CONTEXT *context, SIGCONTEXT *sigconte
void WINAPI __regs_get_cpu_context( CONTEXT *context, CONTEXT *regs ) void WINAPI __regs_get_cpu_context( CONTEXT *context, CONTEXT *regs )
{ {
*context = *regs; *context = *regs;
save_fpu( context ); if (fpux_support) save_fpux( context );
else save_fpu( context );
} }
DEFINE_REGS_ENTRYPOINT( get_cpu_context, 4, 4 ) DEFINE_REGS_ENTRYPOINT( get_cpu_context, 4, 4 )
@ -744,7 +826,8 @@ void set_cpu_context( const CONTEXT *context )
{ {
DWORD flags = context->ContextFlags & ~CONTEXT_i386; DWORD flags = context->ContextFlags & ~CONTEXT_i386;
if (flags & CONTEXT_FLOATING_POINT) restore_fpu( context ); if (flags & CONTEXT_EXTENDED_REGISTERS) restore_fpux( context );
else if (flags & CONTEXT_FLOATING_POINT) restore_fpu( context );
if (flags & CONTEXT_DEBUG_REGISTERS) if (flags & CONTEXT_DEBUG_REGISTERS)
{ {