[PATCH 2/5] ntdll: Support AVX context in fault exceptions on Linux x64.
Paul Gofman
pgofman at codeweavers.com
Thu Aug 20 18:42:12 CDT 2020
Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
---
dlls/ntdll/signal_x86_64.c | 2 +
dlls/ntdll/tests/exception.c | 3 +-
dlls/ntdll/unix/signal_x86_64.c | 114 ++++++++++++++++++++++++++------
dlls/ntdll/unix/unix_private.h | 6 ++
dlls/ntdll/unixlib.h | 30 +++++++++
5 files changed, 134 insertions(+), 21 deletions(-)
diff --git a/dlls/ntdll/signal_x86_64.c b/dlls/ntdll/signal_x86_64.c
index e1cf7f3e013..564b74b7be6 100644
--- a/dlls/ntdll/signal_x86_64.c
+++ b/dlls/ntdll/signal_x86_64.c
@@ -428,6 +428,8 @@ static NTSTATUS call_stack_handlers( EXCEPTION_RECORD *rec, CONTEXT *orig_contex
NTSTATUS status;
context = *orig_context;
+ context.ContextFlags &= ~0x40; /* Clear xstate flag. */
+
dispatch.TargetIp = 0;
dispatch.ContextRecord = &context;
dispatch.HistoryTable = &table;
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c
index 460f0e9a03e..cf704093fce 100644
--- a/dlls/ntdll/tests/exception.c
+++ b/dlls/ntdll/tests/exception.c
@@ -5482,7 +5482,8 @@ static DWORD test_extended_context_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGI
/* Since we got xstates enabled by OS this cpuid level should be supported. */
__cpuidex(regs, 0xd, 1);
compaction = regs[0] & 2;
- todo_wine ok((context->ContextFlags & (CONTEXT_FULL | CONTEXT_XSTATE)) == (CONTEXT_FULL | CONTEXT_XSTATE),
+ todo_wine_if(sizeof(void *) == 4)
+ ok((context->ContextFlags & (CONTEXT_FULL | CONTEXT_XSTATE)) == (CONTEXT_FULL | CONTEXT_XSTATE),
"Got unexpected ContextFlags %#x.\n", context->ContextFlags);
if ((context->ContextFlags & (CONTEXT_FULL | CONTEXT_XSTATE)) != (CONTEXT_FULL | CONTEXT_XSTATE))
diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c
index 638ad5cab76..370f1f36fd7 100644
--- a/dlls/ntdll/unix/signal_x86_64.c
+++ b/dlls/ntdll/unix/signal_x86_64.c
@@ -78,6 +78,7 @@
#define WIN32_NO_STATUS
#include "windef.h"
#include "winternl.h"
+#include "ddk/wdm.h"
#include "wine/exception.h"
#include "wine/list.h"
#include "wine/asm.h"
@@ -94,6 +95,10 @@ WINE_DEFAULT_DEBUG_CHANNEL(seh);
#include <asm/prctl.h>
static inline int arch_prctl( int func, void *ptr ) { return syscall( __NR_arch_prctl, func, ptr ); }
+#ifndef FP_XSTATE_MAGIC1
+#define FP_XSTATE_MAGIC1 0x46505853
+#endif
+
#define RAX_sig(context) ((context)->uc_mcontext.gregs[REG_RAX])
#define RBX_sig(context) ((context)->uc_mcontext.gregs[REG_RBX])
#define RCX_sig(context) ((context)->uc_mcontext.gregs[REG_RCX])
@@ -118,6 +123,7 @@ static inline int arch_prctl( int func, void *ptr ) { return syscall( __NR_arch_
#define TRAP_sig(context) ((context)->uc_mcontext.gregs[REG_TRAPNO])
#define ERROR_sig(context) ((context)->uc_mcontext.gregs[REG_ERR])
#define FPU_sig(context) ((XMM_SAVE_AREA32 *)((context)->uc_mcontext.fpregs))
+#define XState_sig(fpu) (((unsigned int *)fpu->Reserved4)[12] == FP_XSTATE_MAGIC1 ? (XSTATE *)(fpu + 1) : NULL)
#elif defined(__FreeBSD__) || defined (__FreeBSD_kernel__)
@@ -148,6 +154,7 @@ static inline int arch_prctl( int func, void *ptr ) { return syscall( __NR_arch_
#define TRAP_sig(context) ((context)->uc_mcontext.mc_trapno)
#define ERROR_sig(context) ((context)->uc_mcontext.mc_err)
#define FPU_sig(context) ((XMM_SAVE_AREA32 *)((context)->uc_mcontext.mc_fpstate))
+#define XState_sig(context) NULL
#elif defined(__NetBSD__)
@@ -178,6 +185,7 @@ static inline int arch_prctl( int func, void *ptr ) { return syscall( __NR_arch_
#define TRAP_sig(context) ((context)->uc_mcontext.__gregs[_REG_TRAPNO])
#define ERROR_sig(context) ((context)->uc_mcontext.__gregs[_REG_ERR])
#define FPU_sig(context) ((XMM_SAVE_AREA32 *)((context)->uc_mcontext.__fpregs))
+#define XState_sig(context) NULL
#elif defined (__APPLE__)
@@ -205,6 +213,7 @@ static inline int arch_prctl( int func, void *ptr ) { return syscall( __NR_arch_
#define TRAP_sig(context) ((context)->uc_mcontext->__es.__trapno)
#define ERROR_sig(context) ((context)->uc_mcontext->__es.__err)
#define FPU_sig(context) ((XMM_SAVE_AREA32 *)&(context)->uc_mcontext->__fs.__fpu_fcw)
+#define XState_sig(context) NULL
#else
#error You must define the signal context functions for your platform
@@ -237,16 +246,21 @@ enum i386_trap_code
struct stack_layout
{
CONTEXT context;
- ULONG64 unknown[4];
+ CONTEXT_EX context_ex;
EXCEPTION_RECORD rec;
ULONG64 rsi;
ULONG64 rdi;
ULONG64 rbp;
ULONG64 rip;
- ULONG64 red_zone[16];
+ ULONG64 align;
+ char xstate[0]; /* If xstate is present it is allocated
+ * dynamically to provide 64 byte alignment. */
};
-C_ASSERT( sizeof(struct stack_layout) == 0x630 ); /* Should match the size in call_user_exception_dispatcher(). */
+C_ASSERT((offsetof(struct stack_layout, xstate) == sizeof(struct stack_layout)));
+
+C_ASSERT( sizeof(XSTATE) == 0x140 );
+C_ASSERT( sizeof(struct stack_layout) == 0x5b0 ); /* Should match the size in call_user_exception_dispatcher(). */
struct syscall_frame
{
@@ -1408,8 +1422,10 @@ static inline void set_sigcontext( const CONTEXT *context, ucontext_t *sigcontex
*
* Set the register values from a sigcontext.
*/
-static void save_context( CONTEXT *context, const ucontext_t *sigcontext )
+static void save_context( struct xcontext *xcontext, const ucontext_t *sigcontext )
{
+ CONTEXT *context = &xcontext->c;
+
context->ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_DEBUG_REGISTERS;
context->Rax = RAX_sig(sigcontext);
context->Rcx = RCX_sig(sigcontext);
@@ -1458,6 +1474,11 @@ static void save_context( CONTEXT *context, const ucontext_t *sigcontext )
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;
}
}
@@ -1467,8 +1488,10 @@ static void save_context( CONTEXT *context, const ucontext_t *sigcontext )
*
* Build a sigcontext from the register values.
*/
-static void restore_context( const CONTEXT *context, ucontext_t *sigcontext )
+static void restore_context( const struct xcontext *xcontext, ucontext_t *sigcontext )
{
+ const CONTEXT *context = &xcontext->c;
+
amd64_thread_data()->dr0 = context->Dr0;
amd64_thread_data()->dr1 = context->Dr1;
amd64_thread_data()->dr2 = context->Dr2;
@@ -1813,7 +1836,6 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context )
return STATUS_SUCCESS;
}
-
extern void CDECL raise_func_trampoline( void *dispatcher );
__ASM_GLOBAL_FUNC( raise_func_trampoline,
@@ -1822,10 +1844,12 @@ __ASM_GLOBAL_FUNC( raise_func_trampoline,
/***********************************************************************
* setup_raise_exception
*/
-static void setup_raise_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, CONTEXT *context )
+static void setup_raise_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, struct xcontext *xcontext )
{
void *stack_ptr = (void *)(RSP_sig(sigcontext) & ~15);
+ CONTEXT *context = &xcontext->c;
struct stack_layout *stack;
+ size_t stack_size;
NTSTATUS status;
if (rec->ExceptionCode == EXCEPTION_SINGLE_STEP)
@@ -1848,16 +1872,37 @@ static void setup_raise_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec
status = send_debug_event( rec, context, TRUE );
if (status == DBG_CONTINUE || status == DBG_EXCEPTION_HANDLED)
{
- restore_context( context, sigcontext );
+ restore_context( xcontext, sigcontext );
return;
}
/* fix up instruction pointer in context for EXCEPTION_BREAKPOINT */
if (rec->ExceptionCode == EXCEPTION_BREAKPOINT) context->Rip--;
- stack = virtual_setup_exception( stack_ptr, sizeof(*stack), rec );
+ stack_size = sizeof(*stack);
+ if (xcontext->xstate)
+ {
+ stack_size += (ULONG_PTR)stack_ptr - (((ULONG_PTR)stack_ptr
+ - sizeof(XSTATE)) & ~(ULONG_PTR)63);
+ }
+
+ stack = virtual_setup_exception( stack_ptr, stack_size, rec );
stack->rec = *rec;
stack->context = *context;
+ if (xcontext->xstate)
+ {
+ XSTATE *dst_xs = (XSTATE *)stack->xstate;
+
+ assert( !((ULONG_PTR)dst_xs & 63) );
+ context_init_xstate( &stack->context, stack->xstate );
+ dst_xs->CompactionMask = user_shared_data->XState.CompactionEnabled ? 0x8000000000000004 : 0;
+ if (xcontext->xstate->Mask & 4)
+ {
+ dst_xs->Mask = 4;
+ memcpy( &dst_xs->YmmContext, &xcontext->xstate->YmmContext, sizeof(dst_xs->YmmContext) );
+ }
+ }
+
RIP_sig(sigcontext) = (ULONG_PTR)raise_func_trampoline;
R8_sig(sigcontext) = (ULONG_PTR)pKiUserExceptionDispatcher;
RSP_sig(sigcontext) = (ULONG_PTR)stack;
@@ -1875,7 +1920,7 @@ static void setup_raise_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec
*/
static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec )
{
- CONTEXT context;
+ struct xcontext context;
rec->ExceptionAddress = (void *)RIP_sig(sigcontext);
save_context( &context, sigcontext );
@@ -1972,7 +2017,31 @@ void WINAPI do_call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *c
{
struct syscall_frame *frame = amd64_thread_data()->syscall_frame;
- memmove(&stack->context, context, sizeof(*context));
+ if ((context->ContextFlags & CONTEXT_XSTATE) == CONTEXT_XSTATE)
+ {
+ CONTEXT_EX *xctx = (CONTEXT_EX *)context + 1;
+ XSTATE *xs, *src_xs, xs_buf;
+
+ src_xs = xstate_from_context(context);
+ if ((CONTEXT *)src_xs >= &stack->context + 1 || src_xs + 1 <= (XSTATE *)&stack->context)
+ {
+ xs = src_xs;
+ }
+ else
+ {
+ xs = &xs_buf;
+ memcpy(xs, src_xs, sizeof(*xs));
+ }
+
+ memmove(&stack->context, context, sizeof(*context) + sizeof(*xctx));
+ assert(!((ULONG_PTR)stack->xstate & 63));
+ context_init_xstate(&stack->context, stack->xstate);
+ memcpy(stack->xstate, xs, sizeof(*xs));
+ }
+ else
+ {
+ memmove(&stack->context, context, sizeof(*context));
+ }
memcpy(&stack->rec, rec, sizeof(*rec));
/* fix up instruction pointer in context for EXCEPTION_BREAKPOINT */
@@ -1985,7 +2054,11 @@ void WINAPI do_call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *c
__ASM_GLOBAL_FUNC( call_user_exception_dispatcher,
"movq 0x98(%rdx),%r9\n\t" /* context->Rsp */
"andq $~0xf,%r9\n\t"
- "subq $0x630,%r9\n\t" /* sizeof(struct stack_layout) */
+ "bt $6,0x30(%rdx)\n\t" /* context->ContextFlags, CONTEXT_XSTATE bit. */
+ "jnc 1f\n\t"
+ "subq $0x140,%r9\n\t" /* sizeof(XSTATE) */
+ "andq $~63,%r9\n\t"
+ "1:subq $0x5b0,%r9\n\t" /* sizeof(struct stack_layout) */
"cmpq %rsp,%r9\n\t"
"cmovbq %r9,%rsp\n\t"
"jmp " __ASM_NAME("do_call_user_exception_dispatcher") "\n\t")
@@ -2187,8 +2260,10 @@ static void install_bpf(struct sigaction *sig_act)
*
* Handle an interrupt.
*/
-static inline BOOL handle_interrupt( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, CONTEXT *context )
+static inline BOOL handle_interrupt( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, struct xcontext *xcontext )
{
+ CONTEXT *context = &xcontext->c;
+
switch (ERROR_sig(sigcontext) >> 3)
{
case 0x2c:
@@ -2213,11 +2288,10 @@ static inline BOOL handle_interrupt( ucontext_t *sigcontext, EXCEPTION_RECORD *r
default:
return FALSE;
}
- setup_raise_exception( sigcontext, rec, context );
+ setup_raise_exception( sigcontext, rec, xcontext );
return TRUE;
}
-
/**********************************************************************
* segv_handler
*
@@ -2226,7 +2300,7 @@ static inline BOOL handle_interrupt( ucontext_t *sigcontext, EXCEPTION_RECORD *r
static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
{
EXCEPTION_RECORD rec = { 0 };
- CONTEXT context;
+ struct xcontext context;
ucontext_t *ucontext = sigcontext;
rec.ExceptionAddress = (void *)RIP_sig(ucontext);
@@ -2250,7 +2324,7 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
case TRAP_x86_PROTFLT: /* General protection fault */
{
WORD err = ERROR_sig(ucontext);
- if (!err && (rec.ExceptionCode = is_privileged_instr( &context ))) break;
+ if (!err && (rec.ExceptionCode = is_privileged_instr( &context.c ))) break;
if ((err & 7) == 2 && handle_interrupt( ucontext, &rec, &context )) return;
rec.ExceptionCode = EXCEPTION_ACCESS_VIOLATION;
rec.NumberParameters = 2;
@@ -2293,7 +2367,7 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
static void trap_handler( int signal, siginfo_t *siginfo, void *sigcontext )
{
EXCEPTION_RECORD rec = { 0 };
- CONTEXT context;
+ struct xcontext context;
ucontext_t *ucontext = sigcontext;
rec.ExceptionAddress = (void *)RIP_sig(ucontext);
@@ -2412,10 +2486,10 @@ static void quit_handler( int signal, siginfo_t *siginfo, void *ucontext )
*/
static void usr1_handler( int signal, siginfo_t *siginfo, void *ucontext )
{
- CONTEXT context;
+ struct xcontext context;
save_context( &context, ucontext );
- wait_suspend( &context );
+ wait_suspend( &context.c );
restore_context( &context, ucontext );
}
diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h
index c9f8871fd26..68c60f78b7f 100644
--- a/dlls/ntdll/unix/unix_private.h
+++ b/dlls/ntdll/unix/unix_private.h
@@ -400,4 +400,10 @@ static inline int ntdll_wcsnicmp( const WCHAR *str1, const WCHAR *str2, int n )
#define towupper(c) ntdll_towupper(c)
#define towlower(c) ntdll_towlower(c)
+struct xcontext
+{
+ CONTEXT c;
+ XSTATE *xstate; /* Points to xstate in sigcontext. */
+};
+
#endif /* __NTDLL_UNIX_PRIVATE_H */
diff --git a/dlls/ntdll/unixlib.h b/dlls/ntdll/unixlib.h
index 2f57873671c..70b6def530a 100644
--- a/dlls/ntdll/unixlib.h
+++ b/dlls/ntdll/unixlib.h
@@ -129,4 +129,34 @@ struct unix_funcs
const char *function );
};
+static inline XSTATE *xstate_from_context( const CONTEXT *context )
+{
+ CONTEXT_EX *xctx = (CONTEXT_EX *)(context + 1);
+
+ if ((context->ContextFlags & CONTEXT_XSTATE) != CONTEXT_XSTATE)
+ return NULL;
+
+ return (XSTATE *)((char *)(context + 1) + xctx->XState.Offset);
+}
+
+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->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) );
+}
+
#endif /* __NTDLL_UNIXLIB_H */
--
2.26.2
More information about the wine-devel
mailing list