ntdll: Support AVX registers for other thread in Nt{Get|Set}ContextThread().

Signed-off-by: Paul Gofman <pgofman@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Paul Gofman 2020-10-09 16:03:54 +03:00 committed by Alexandre Julliard
parent ec1ea1ea1b
commit 419abd49a1
9 changed files with 148 additions and 34 deletions

View File

@ -631,8 +631,15 @@ unsigned int server_select( const select_op_t *select_op, data_size_t size, UINT
if (wine_server_reply_size( reply ))
{
DWORD context_flags = context->ContextFlags; /* unchanged registers are still available */
XSTATE *xs = xstate_from_context( context );
ULONG64 mask;
if (xs)
mask = xs->Mask;
context_from_server( context, &server_context );
context->ContextFlags |= context_flags;
if (xs)
xs->Mask |= mask;
}
}
SERVER_END_REQ;

View File

@ -886,14 +886,16 @@ static inline void save_context( struct xcontext *xcontext, const ucontext_t *si
}
if (fpux)
{
XSTATE *xs;
context->ContextFlags |= CONTEXT_FLOATING_POINT | CONTEXT_EXTENDED_REGISTERS;
memcpy( context->ExtendedRegisters, fpux, sizeof(*fpux) );
if (!fpu) fpux_to_fpu( &context->FloatSave, fpux );
xcontext->xstate = XState_sig(fpux);
}
else
{
xcontext->xstate = NULL;
if ((xs = XState_sig(fpux)))
{
context_init_xstate( context, xs );
xcontext->host_compaction_mask = xs->CompactionMask;
}
}
if (!fpu && !fpux) save_fpu( context );
}
@ -944,6 +946,7 @@ static inline void restore_context( const struct xcontext *xcontext, ucontext_t
{
memcpy( &dst_xs->YmmContext, &src_xs->YmmContext, sizeof(dst_xs->YmmContext) );
dst_xs->Mask |= src_xs->Mask;
dst_xs->CompactionMask = xcontext->host_compaction_mask;
}
}
if (!fpu && !fpux) restore_fpu( context );
@ -1023,6 +1026,7 @@ static unsigned int get_server_context_flags( DWORD flags )
if (flags & CONTEXT_FLOATING_POINT) ret |= SERVER_CTX_FLOATING_POINT;
if (flags & CONTEXT_DEBUG_REGISTERS) ret |= SERVER_CTX_DEBUG_REGISTERS;
if (flags & CONTEXT_EXTENDED_REGISTERS) ret |= SERVER_CTX_EXTENDED_REGISTERS;
if (flags & CONTEXT_XSTATE) ret |= SERVER_CTX_YMM_REGISTERS;
return ret;
}
@ -1095,6 +1099,7 @@ NTSTATUS context_to_server( context_t *to, const CONTEXT *from )
to->flags |= SERVER_CTX_EXTENDED_REGISTERS;
memcpy( to->ext.i386_regs, from->ExtendedRegisters, sizeof(to->ext.i386_regs) );
}
xstate_to_server( to, xstate_from_context( from ) );
return STATUS_SUCCESS;
}
@ -1108,7 +1113,7 @@ NTSTATUS context_from_server( CONTEXT *to, const context_t *from )
{
if (from->cpu != CPU_x86) return STATUS_INVALID_PARAMETER;
to->ContextFlags = CONTEXT_i386;
to->ContextFlags = CONTEXT_i386 | (to->ContextFlags & 0x40);
if (from->flags & SERVER_CTX_CONTROL)
{
to->ContextFlags |= CONTEXT_CONTROL;
@ -1165,6 +1170,7 @@ NTSTATUS context_from_server( CONTEXT *to, const context_t *from )
to->ContextFlags |= CONTEXT_EXTENDED_REGISTERS;
memcpy( to->ExtendedRegisters, from->ext.i386_regs, sizeof(to->ExtendedRegisters) );
}
xstate_from_server( xstate_from_context( to ), from );
return STATUS_SUCCESS;
}
@ -1246,7 +1252,7 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context )
/* Save xstate before any calls which can potentially change volatile ymm registers.
* E. g., debug output will clobber ymm registers. */
xsave_status = self ? save_xstate( context ) : STATUS_SUCCESS; /* FIXME: other thread. */
xsave_status = self ? save_xstate( context ) : STATUS_SUCCESS;
/* debug registers require a server call */
if (needed_flags & CONTEXT_DEBUG_REGISTERS) self = FALSE;
@ -1293,7 +1299,6 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context )
}
if (needed_flags & CONTEXT_FLOATING_POINT) save_fpu( context );
if (needed_flags & CONTEXT_EXTENDED_REGISTERS) save_fpux( context );
/* FIXME: xstate */
/* update the cached version of the debug registers */
if (context->ContextFlags & (CONTEXT_DEBUG_REGISTERS & ~CONTEXT_i386))
{
@ -1579,6 +1584,7 @@ static void setup_raise_exception( ucontext_t *sigcontext, void *stack_ptr,
{
CONTEXT *context = &xcontext->c;
size_t stack_size;
XSTATE *src_xs;
struct stack_layout
{
@ -1606,7 +1612,7 @@ C_ASSERT( (offsetof(struct stack_layout, xstate) == sizeof(struct stack_layout))
if (rec->ExceptionCode == EXCEPTION_BREAKPOINT) context->Eip--;
stack_size = sizeof(*stack);
if (xcontext->xstate)
if ((src_xs = xstate_from_context( context )))
{
stack_size += (ULONG_PTR)stack_ptr - (((ULONG_PTR)stack_ptr
- sizeof(XSTATE)) & ~(ULONG_PTR)63);
@ -1616,17 +1622,18 @@ C_ASSERT( (offsetof(struct stack_layout, xstate) == sizeof(struct stack_layout))
stack->rec = *rec;
stack->context = *context;
if (xcontext->xstate)
if (src_xs)
{
XSTATE *dst_xs = (XSTATE *)stack->xstate;
assert(!((ULONG_PTR)dst_xs & 63));
context_init_xstate( &stack->context, stack->xstate );
memset( dst_xs, 0, offsetof(XSTATE, YmmContext) );
dst_xs->CompactionMask = user_shared_data->XState.CompactionEnabled ? 0x8000000000000004 : 0;
if (xcontext->xstate->Mask & 4)
if (src_xs->Mask & 4)
{
dst_xs->Mask = 4;
memcpy( &dst_xs->YmmContext, &xcontext->xstate->YmmContext, sizeof(dst_xs->YmmContext) );
memcpy( &dst_xs->YmmContext, &src_xs->YmmContext, sizeof(dst_xs->YmmContext) );
}
}

View File

@ -1461,14 +1461,19 @@ static void save_context( struct xcontext *xcontext, const ucontext_t *sigcontex
context->Dr7 = amd64_thread_data()->dr7;
if (FPU_sig(sigcontext))
{
XSTATE *xs;
context->ContextFlags |= CONTEXT_FLOATING_POINT;
context->u.FltSave = *FPU_sig(sigcontext);
context->MxCsr = context->u.FltSave.MxCsr;
xcontext->xstate = XState_sig(FPU_sig(sigcontext));
}
else
{
xcontext->xstate = NULL;
if ((xs = XState_sig(FPU_sig(sigcontext))))
{
/* xcontext and sigcontext are both on the signal stack, so we can
* just reference sigcontext without overflowing 32 bit XState.Offset */
context_init_xstate( context, xs );
assert( xcontext->c_ex.XState.Offset == (BYTE *)xs - (BYTE *)&xcontext->c_ex );
xcontext->host_compaction_mask = xs->CompactionMask;
}
}
}
@ -1531,6 +1536,7 @@ static inline NTSTATUS save_xstate( CONTEXT *context )
static void restore_context( const struct xcontext *xcontext, ucontext_t *sigcontext )
{
const CONTEXT *context = &xcontext->c;
XSTATE *xs;
amd64_thread_data()->dr0 = context->Dr0;
amd64_thread_data()->dr1 = context->Dr1;
@ -1540,6 +1546,8 @@ static void restore_context( const struct xcontext *xcontext, ucontext_t *sigcon
amd64_thread_data()->dr7 = context->Dr7;
set_sigcontext( context, sigcontext );
if (FPU_sig(sigcontext)) *FPU_sig(sigcontext) = context->u.FltSave;
if ((xs = XState_sig(FPU_sig(sigcontext))))
xs->CompactionMask = xcontext->host_compaction_mask;
}
@ -1628,6 +1636,7 @@ static unsigned int get_server_context_flags( DWORD flags )
if (flags & CONTEXT_SEGMENTS) ret |= SERVER_CTX_SEGMENTS;
if (flags & CONTEXT_FLOATING_POINT) ret |= SERVER_CTX_FLOATING_POINT;
if (flags & CONTEXT_DEBUG_REGISTERS) ret |= SERVER_CTX_DEBUG_REGISTERS;
if (flags & CONTEXT_XSTATE) ret |= SERVER_CTX_YMM_REGISTERS;
return ret;
}
@ -1695,6 +1704,7 @@ NTSTATUS context_to_server( context_t *to, const CONTEXT *from )
to->debug.x86_64_regs.dr6 = from->Dr6;
to->debug.x86_64_regs.dr7 = from->Dr7;
}
xstate_to_server( to, xstate_from_context( from ) );
return STATUS_SUCCESS;
}
@ -1708,7 +1718,7 @@ NTSTATUS context_from_server( CONTEXT *to, const context_t *from )
{
if (from->cpu != CPU_x86_64) return STATUS_INVALID_PARAMETER;
to->ContextFlags = CONTEXT_AMD64;
to->ContextFlags = CONTEXT_AMD64 | (to->ContextFlags & 0x40);
if (from->flags & SERVER_CTX_CONTROL)
{
to->ContextFlags |= CONTEXT_CONTROL;
@ -1762,6 +1772,7 @@ NTSTATUS context_from_server( CONTEXT *to, const context_t *from )
to->Dr6 = from->debug.x86_64_regs.dr6;
to->Dr7 = from->debug.x86_64_regs.dr7;
}
xstate_from_server( xstate_from_context( to ), from );
return STATUS_SUCCESS;
}
@ -1831,7 +1842,7 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context )
/* Save xstate before any calls which can potentially change volatile ymm registers.
* E. g., debug output will clobber ymm registers. */
xsave_status = self ? save_xstate( context ) : STATUS_SUCCESS; /* FIXME: other thread. */
xsave_status = self ? save_xstate( context ) : STATUS_SUCCESS;
needed_flags = context->ContextFlags & ~CONTEXT_AMD64;
@ -1924,6 +1935,7 @@ static void setup_raise_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec
struct stack_layout *stack;
size_t stack_size;
NTSTATUS status;
XSTATE *src_xs;
if (rec->ExceptionCode == EXCEPTION_SINGLE_STEP)
{
@ -1953,7 +1965,7 @@ static void setup_raise_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec
if (rec->ExceptionCode == EXCEPTION_BREAKPOINT) context->Rip--;
stack_size = sizeof(*stack);
if (xcontext->xstate)
if ((src_xs = xstate_from_context( context )))
{
stack_size += (ULONG_PTR)stack_ptr - (((ULONG_PTR)stack_ptr
- sizeof(XSTATE)) & ~(ULONG_PTR)63);
@ -1962,17 +1974,18 @@ static void setup_raise_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec
stack = virtual_setup_exception( stack_ptr, stack_size, rec );
stack->rec = *rec;
stack->context = *context;
if (xcontext->xstate)
if (src_xs)
{
XSTATE *dst_xs = (XSTATE *)stack->xstate;
assert( !((ULONG_PTR)dst_xs & 63) );
context_init_xstate( &stack->context, stack->xstate );
memset( dst_xs, 0, offsetof(XSTATE, YmmContext) );
dst_xs->CompactionMask = user_shared_data->XState.CompactionEnabled ? 0x8000000000000004 : 0;
if (xcontext->xstate->Mask & 4)
if (src_xs->Mask & 4)
{
dst_xs->Mask = 4;
memcpy( &dst_xs->YmmContext, &xcontext->xstate->YmmContext, sizeof(dst_xs->YmmContext) );
memcpy( &dst_xs->YmmContext, &src_xs->YmmContext, sizeof(dst_xs->YmmContext) );
}
}

View File

@ -344,7 +344,6 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c
DWORD i;
obj_handle_t handle = 0;
client_ptr_t params[EXCEPTION_MAXIMUM_PARAMETERS];
CONTEXT exception_context = *context;
select_op_t select_op;
sigset_t old_set;
@ -370,10 +369,22 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c
if (handle)
{
struct xcontext exception_context;
DECLSPEC_ALIGN(64) XSTATE xs;
XSTATE *src_xs;
select_op.wait.op = SELECT_WAIT;
select_op.wait.handles[0] = handle;
exception_context.c = *context;
if ((src_xs = xstate_from_context( context )))
{
context_init_xstate( &exception_context.c, &xs );
memcpy( &xs, src_xs, sizeof(xs) );
}
server_select( &select_op, offsetof( select_op_t, wait.handles[1] ), SELECT_INTERRUPTIBLE,
TIMEOUT_INFINITE, &exception_context, NULL, NULL );
TIMEOUT_INFINITE, &exception_context.c, NULL, NULL );
SERVER_START_REQ( get_exception_status )
{
@ -381,7 +392,12 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c
ret = wine_server_call( req );
}
SERVER_END_REQ;
if (ret >= 0) *context = exception_context;
if (ret >= 0)
{
*context = exception_context.c;
if (src_xs)
memcpy( src_xs, &xs, sizeof(xs) );
}
}
pthread_sigmask( SIG_SETMASK, &old_set, NULL );
@ -632,7 +648,7 @@ static NTSTATUS wow64_context_from_server( WOW64_CONTEXT *to, const context_t *f
{
if (from->cpu != CPU_x86) return STATUS_INVALID_PARAMETER;
to->ContextFlags = WOW64_CONTEXT_i386;
to->ContextFlags = WOW64_CONTEXT_i386 | (to->ContextFlags & 0x40);
if (from->flags & SERVER_CTX_CONTROL)
{
to->ContextFlags |= WOW64_CONTEXT_CONTROL;
@ -689,6 +705,12 @@ static NTSTATUS wow64_context_from_server( WOW64_CONTEXT *to, const context_t *f
to->ContextFlags |= WOW64_CONTEXT_EXTENDED_REGISTERS;
memcpy( to->ExtendedRegisters, from->ext.i386_regs, sizeof(to->ExtendedRegisters) );
}
if ((to->ContextFlags & WOW64_CONTEXT_XSTATE) == WOW64_CONTEXT_XSTATE)
{
CONTEXT_EX *c_ex = (CONTEXT_EX *)(to + 1);
xstate_from_server( (XSTATE *)((BYTE *)c_ex + c_ex->XState.Offset), from );
}
return STATUS_SUCCESS;
}
@ -758,6 +780,12 @@ static void wow64_context_to_server( context_t *to, const WOW64_CONTEXT *from )
to->flags |= SERVER_CTX_EXTENDED_REGISTERS;
memcpy( to->ext.i386_regs, from->ExtendedRegisters, sizeof(to->ext.i386_regs) );
}
if (flags & WOW64_CONTEXT_XSTATE)
{
CONTEXT_EX *c_ex = (CONTEXT_EX *)(from + 1);
xstate_to_server( to, (XSTATE *)((BYTE *)c_ex + c_ex->XState.Offset) );
}
}
#endif /* __x86_64__ */

View File

@ -288,13 +288,14 @@ static inline void mutex_unlock( pthread_mutex_t *mutex )
static inline TEB64 *NtCurrentTeb64(void) { return (TEB64 *)NtCurrentTeb()->GdiBatchCount; }
#endif
#if defined(__i386__) || defined(__x86_64__)
struct xcontext
{
CONTEXT c;
XSTATE *xstate; /* points to xstate in sigcontext */
CONTEXT_EX c_ex;
ULONG64 host_compaction_mask;
};
#if defined(__i386__) || defined(__x86_64__)
static inline XSTATE *xstate_from_context( const CONTEXT *context )
{
CONTEXT_EX *xctx = (CONTEXT_EX *)(context + 1);
@ -308,21 +309,62 @@ static inline XSTATE *xstate_from_context( const CONTEXT *context )
static inline void context_init_xstate( CONTEXT *context, void *xstate_buffer )
{
CONTEXT_EX *xctx;
XSTATE *xs;
xctx = (CONTEXT_EX *)(context + 1);
xctx->Legacy.Length = sizeof(CONTEXT);
xctx->Legacy.Offset = -(LONG)sizeof(CONTEXT);
xctx->XState.Length = sizeof(XSTATE);
xctx->XState.Offset = xstate_buffer ? (((ULONG_PTR)xstate_buffer + 63) & ~63) - (ULONG_PTR)xctx
: (((ULONG_PTR)context + sizeof(CONTEXT) + sizeof(CONTEXT_EX) + 63) & ~63) - (ULONG_PTR)xctx;
xctx->XState.Offset = (BYTE *)xstate_buffer - (BYTE *)xctx;
xctx->All.Length = sizeof(CONTEXT) + xctx->XState.Offset + xctx->XState.Length;
xctx->All.Offset = -(LONG)sizeof(CONTEXT);
context->ContextFlags |= 0x40;
}
xs = xstate_from_context(context);
memset( xs, 0, offsetof(XSTATE, YmmContext) );
static inline void xstate_to_server( context_t *to, const XSTATE *xs )
{
if (!xs)
return;
to->flags |= SERVER_CTX_YMM_REGISTERS;
if (xs->Mask & 4)
memcpy(&to->ymm.ymm_high_regs.ymm_high, &xs->YmmContext, sizeof(xs->YmmContext));
else
memset(&to->ymm.ymm_high_regs.ymm_high, 0, sizeof(xs->YmmContext));
}
static inline void xstate_from_server_( XSTATE *xs, const context_t *from, BOOL compaction_enabled)
{
if (!xs)
return;
xs->Mask = 0;
xs->CompactionMask = compaction_enabled ? 0x8000000000000004 : 0;
if (from->flags & SERVER_CTX_YMM_REGISTERS)
{
unsigned long *src = (unsigned long *)&from->ymm.ymm_high_regs.ymm_high;
unsigned int i;
for (i = 0; i < sizeof(xs->YmmContext) / sizeof(unsigned long); ++i)
if (src[i])
{
memcpy( &xs->YmmContext, &from->ymm.ymm_high_regs.ymm_high, sizeof(xs->YmmContext) );
xs->Mask = 4;
break;
}
}
}
#define xstate_from_server( xs, from ) xstate_from_server_( xs, from, user_shared_data->XState.CompactionEnabled )
#else
static inline XSTATE *xstate_from_context( const CONTEXT *context )
{
return NULL;
}
static inline void context_init_xstate( CONTEXT *context, void *xstate_buffer )
{
}
#endif

View File

@ -170,6 +170,10 @@ typedef struct
{
unsigned char i386_regs[512];
} ext;
union
{
struct { struct { unsigned __int64 low, high; } ymm_high[16]; } ymm_high_regs;
} ymm;
} context_t;
#define SERVER_CTX_CONTROL 0x01
@ -178,6 +182,7 @@ typedef struct
#define SERVER_CTX_FLOATING_POINT 0x08
#define SERVER_CTX_DEBUG_REGISTERS 0x10
#define SERVER_CTX_EXTENDED_REGISTERS 0x20
#define SERVER_CTX_YMM_REGISTERS 0x40
struct send_fd

View File

@ -186,6 +186,10 @@ typedef struct
{
unsigned char i386_regs[512];
} ext; /* selected by SERVER_CTX_EXTENDED_REGISTERS */
union
{
struct { struct { unsigned __int64 low, high; } ymm_high[16]; } ymm_high_regs;
} ymm; /* selected by SERVER_CTX_YMM_REGISTERS */
} context_t;
#define SERVER_CTX_CONTROL 0x01
@ -194,6 +198,7 @@ typedef struct
#define SERVER_CTX_FLOATING_POINT 0x08
#define SERVER_CTX_DEBUG_REGISTERS 0x10
#define SERVER_CTX_EXTENDED_REGISTERS 0x20
#define SERVER_CTX_YMM_REGISTERS 0x40
/* structure used in sending an fd from client to server */
struct send_fd

View File

@ -1285,6 +1285,7 @@ static void copy_context( context_t *to, const context_t *from, unsigned int fla
if (flags & SERVER_CTX_FLOATING_POINT) to->fp = from->fp;
if (flags & SERVER_CTX_DEBUG_REGISTERS) to->debug = from->debug;
if (flags & SERVER_CTX_EXTENDED_REGISTERS) to->ext = from->ext;
if (flags & SERVER_CTX_YMM_REGISTERS) to->ymm = from->ymm;
}
/* return the context flags that correspond to system regs */

View File

@ -620,6 +620,9 @@ static void dump_varargs_context( const char *prefix, data_size_t size )
if (ctx.flags & SERVER_CTX_EXTENDED_REGISTERS)
dump_uints( ",extended=", (const unsigned int *)ctx.ext.i386_regs,
sizeof(ctx.ext.i386_regs) / sizeof(int) );
if (ctx.flags & SERVER_CTX_YMM_REGISTERS)
dump_uints( ",ymm_high=", (const unsigned int *)ctx.ymm.ymm_high_regs.ymm_high,
sizeof(ctx.ymm.ymm_high_regs) / sizeof(int) );
break;
case CPU_x86_64:
if (ctx.flags & SERVER_CTX_CONTROL)
@ -669,6 +672,9 @@ static void dump_varargs_context( const char *prefix, data_size_t size )
(unsigned int)(ctx.fp.x86_64_regs.fpregs[i].low >> 32),
(unsigned int)ctx.fp.x86_64_regs.fpregs[i].low );
}
if (ctx.flags & SERVER_CTX_YMM_REGISTERS)
dump_uints( ",ymm_high=", (const unsigned int *)ctx.ymm.ymm_high_regs.ymm_high,
sizeof(ctx.ymm.ymm_high_regs) / sizeof(int) );
break;
case CPU_POWERPC:
if (ctx.flags & SERVER_CTX_CONTROL)