Commit 94f63ea2 authored by Alexandre Julliard's avatar Alexandre Julliard

ntdll: Add support for user callbacks.

parent b56a4b3e
......@@ -191,7 +191,7 @@ struct syscall_frame
DWORD cpsr; /* 040 */
DWORD restore_flags; /* 044 */
DWORD fpscr; /* 048 */
DWORD align; /* 04c */
struct syscall_frame *prev_frame; /* 04c */
ULONGLONG d[32]; /* 050 */
};
......@@ -575,12 +575,61 @@ NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context
}
struct user_callback_frame
{
struct syscall_frame frame;
void **ret_ptr;
ULONG *ret_len;
__wine_jmp_buf jmpbuf;
NTSTATUS status;
};
/***********************************************************************
* KeUserModeCallback
*/
NTSTATUS WINAPI KeUserModeCallback( ULONG id, const void *args, ULONG len, void **ret_ptr, ULONG *ret_len )
{
struct user_callback_frame callback_frame = { { 0 }, ret_ptr, ret_len };
if ((char *)ntdll_get_thread_data()->kernel_stack + min_kernel_stack > (char *)&callback_frame)
return STATUS_STACK_OVERFLOW;
if (!__wine_setjmpex( &callback_frame.jmpbuf, NULL ))
{
struct syscall_frame *frame = arm_thread_data()->syscall_frame;
void *args_data = (void *)((frame->sp - len) & ~15);
memcpy( args_data, args, len );
callback_frame.frame.r0 = id;
callback_frame.frame.r1 = (ULONG_PTR)args;
callback_frame.frame.r2 = len;
callback_frame.frame.sp = (ULONG_PTR)args_data;
callback_frame.frame.pc = (ULONG_PTR)pKiUserCallbackDispatcher;
callback_frame.frame.restore_flags = CONTEXT_INTEGER;
callback_frame.frame.prev_frame = frame;
arm_thread_data()->syscall_frame = &callback_frame.frame;
__wine_syscall_dispatcher_return( &callback_frame.frame, 0 );
}
return callback_frame.status;
}
/***********************************************************************
* NtCallbackReturn (NTDLL.@)
*/
NTSTATUS WINAPI NtCallbackReturn( void *ret_ptr, ULONG ret_len, NTSTATUS status )
{
return STATUS_NO_CALLBACK_ACTIVE;
struct user_callback_frame *frame = (struct user_callback_frame *)arm_thread_data()->syscall_frame;
if (!frame->frame.prev_frame) return STATUS_NO_CALLBACK_ACTIVE;
*frame->ret_ptr = ret_ptr;
*frame->ret_len = ret_len;
frame->status = status;
arm_thread_data()->syscall_frame = frame->frame.prev_frame;
__wine_longjmp( &frame->jmpbuf, 1 );
}
......@@ -942,6 +991,7 @@ void DECLSPEC_HIDDEN call_init_thunk( LPTHREAD_START_ROUTINE entry, void *arg, B
frame->sp = (DWORD)ctx;
frame->pc = (DWORD)pLdrInitializeThunk;
frame->r0 = (DWORD)ctx;
frame->prev_frame = NULL;
frame->restore_flags |= CONTEXT_INTEGER;
pthread_sigmask( SIG_UNBLOCK, &server_block_set, NULL );
......
......@@ -139,8 +139,9 @@ struct syscall_frame
ULONG64 lr; /* 0f0 */
ULONG64 sp; /* 0f8 */
ULONG64 pc; /* 100 */
ULONG64 cpsr; /* 108 */
ULONG64 restore_flags; /* 110 */
ULONG cpsr; /* 108 */
ULONG restore_flags; /* 10c */
struct syscall_frame *prev_frame; /* 110 */
ULONG fpcr; /* 118 */
ULONG fpsr; /* 11c */
NEON128 v[32]; /* 120 */
......@@ -722,12 +723,62 @@ NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context
}
struct user_callback_frame
{
struct syscall_frame frame;
void **ret_ptr;
ULONG *ret_len;
__wine_jmp_buf jmpbuf;
NTSTATUS status;
};
/***********************************************************************
* KeUserModeCallback
*/
NTSTATUS WINAPI KeUserModeCallback( ULONG id, const void *args, ULONG len, void **ret_ptr, ULONG *ret_len )
{
struct user_callback_frame callback_frame = { {{ 0 }}, ret_ptr, ret_len };
if ((char *)ntdll_get_thread_data()->kernel_stack + min_kernel_stack > (char *)&callback_frame)
return STATUS_STACK_OVERFLOW;
if (!__wine_setjmpex( &callback_frame.jmpbuf, NULL ))
{
struct syscall_frame *frame = arm64_thread_data()->syscall_frame;
void *args_data = (void *)((frame->sp - len) & ~15);
memcpy( args_data, args, len );
callback_frame.frame.x[0] = id;
callback_frame.frame.x[1] = (ULONG_PTR)args;
callback_frame.frame.x[2] = len;
callback_frame.frame.x[18] = frame->x[18];
callback_frame.frame.sp = (ULONG_PTR)args_data;
callback_frame.frame.pc = (ULONG_PTR)pKiUserCallbackDispatcher;
callback_frame.frame.restore_flags = CONTEXT_INTEGER;
callback_frame.frame.prev_frame = frame;
arm64_thread_data()->syscall_frame = &callback_frame.frame;
__wine_syscall_dispatcher_return( &callback_frame.frame, 0 );
}
return callback_frame.status;
}
/***********************************************************************
* NtCallbackReturn (NTDLL.@)
*/
NTSTATUS WINAPI NtCallbackReturn( void *ret_ptr, ULONG ret_len, NTSTATUS status )
{
return STATUS_NO_CALLBACK_ACTIVE;
struct user_callback_frame *frame = (struct user_callback_frame *)arm64_thread_data()->syscall_frame;
if (!frame->frame.prev_frame) return STATUS_NO_CALLBACK_ACTIVE;
*frame->ret_ptr = ret_ptr;
*frame->ret_len = ret_len;
frame->status = status;
arm64_thread_data()->syscall_frame = frame->frame.prev_frame;
__wine_longjmp( &frame->jmpbuf, 1 );
}
......@@ -1133,6 +1184,7 @@ void DECLSPEC_HIDDEN call_init_thunk( LPTHREAD_START_ROUTINE entry, void *arg, B
frame->pc = (ULONG64)pLdrInitializeThunk;
frame->x[0] = (ULONG64)ctx;
frame->x[18] = (ULONG64)teb;
frame->prev_frame = NULL;
frame->restore_flags |= CONTEXT_INTEGER;
pthread_sigmask( SIG_UNBLOCK, &server_block_set, NULL );
......
......@@ -481,7 +481,7 @@ struct syscall_frame
DWORD esi; /* 030 */
DWORD ebp; /* 034 */
DWORD syscall_flags; /* 038 */
DWORD align; /* 03c */
struct syscall_frame *prev_frame; /* 03c */
union /* 040 */
{
XSAVE_FORMAT xsave;
......@@ -490,7 +490,7 @@ struct syscall_frame
/* Leave space for the whole set of YMM registers. They're not used in
* 32-bit mode, but some processors fault if they're not in writable memory.
*/
XSTATE xstate; /* 240 */
DECLSPEC_ALIGN(64) XSTATE xstate; /* 240 */
};
C_ASSERT( sizeof(struct syscall_frame) == 0x380 );
......@@ -1579,12 +1579,65 @@ NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context
}
struct user_callback_frame
{
struct syscall_frame frame;
void **ret_ptr;
ULONG *ret_len;
__wine_jmp_buf jmpbuf;
NTSTATUS status;
};
/***********************************************************************
* KeUserModeCallback
*/
NTSTATUS WINAPI KeUserModeCallback( ULONG id, const void *args, ULONG len, void **ret_ptr, ULONG *ret_len )
{
struct user_callback_frame callback_frame = { { 0 }, ret_ptr, ret_len };
if ((char *)ntdll_get_thread_data()->kernel_stack + min_kernel_stack > (char *)&callback_frame)
return STATUS_STACK_OVERFLOW;
if (!__wine_setjmpex( &callback_frame.jmpbuf, NULL ))
{
struct syscall_frame *frame = x86_thread_data()->syscall_frame;
void *args_data = (void *)((frame->esp - len) & ~15);
ULONG_PTR *stack = args_data;
memcpy( args_data, args, len );
*(--stack) = 0;
*(--stack) = len;
*(--stack) = (ULONG_PTR)args_data;
*(--stack) = id;
*(--stack) = 0xdeadbabe;
callback_frame.frame.esp = (ULONG_PTR)stack;
callback_frame.frame.eip = (ULONG_PTR)pKiUserCallbackDispatcher;
callback_frame.frame.eflags = 0x202;
callback_frame.frame.syscall_flags = __wine_syscall_flags;
callback_frame.frame.prev_frame = frame;
x86_thread_data()->syscall_frame = &callback_frame.frame;
__wine_syscall_dispatcher_return( &callback_frame.frame, 0 );
}
return callback_frame.status;
}
/***********************************************************************
* NtCallbackReturn (NTDLL.@)
*/
NTSTATUS WINAPI NtCallbackReturn( void *ret_ptr, ULONG ret_len, NTSTATUS status )
{
return STATUS_NO_CALLBACK_ACTIVE;
struct user_callback_frame *frame = (struct user_callback_frame *)x86_thread_data()->syscall_frame;
if (!frame->frame.prev_frame) return STATUS_NO_CALLBACK_ACTIVE;
*frame->ret_ptr = ret_ptr;
*frame->ret_len = ret_len;
frame->status = status;
x86_thread_data()->syscall_frame = frame->frame.prev_frame;
__wine_longjmp( &frame->jmpbuf, 1 );
}
......@@ -2364,6 +2417,7 @@ void DECLSPEC_HIDDEN call_init_thunk( LPTHREAD_START_ROUTINE entry, void *arg, B
*(--stack) = 0xdeadbabe;
frame->esp = (DWORD)stack;
frame->eip = (DWORD)pLdrInitializeThunk;
frame->prev_frame = NULL;
frame->syscall_flags = __wine_syscall_flags;
frame->restore_flags |= CONTEXT_INTEGER;
......
......@@ -324,9 +324,10 @@ struct syscall_frame
WORD gs; /* 0092 */
DWORD restore_flags; /* 0094 */
ULONG64 rbp; /* 0098 */
ULONG64 align[4]; /* 00a0 */
struct syscall_frame *prev_frame; /* 00a0 */
ULONG64 align[3]; /* 00a8 */
XMM_SAVE_AREA32 xsave; /* 00c0 */
XSTATE xstate; /* 02c0 */
DECLSPEC_ALIGN(64) XSTATE xstate; /* 02c0 */
};
C_ASSERT( sizeof( struct syscall_frame ) == 0x400);
......@@ -2275,12 +2276,66 @@ NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context
}
struct user_callback_frame
{
struct syscall_frame frame;
void **ret_ptr;
ULONG *ret_len;
__wine_jmp_buf jmpbuf;
NTSTATUS status;
};
/***********************************************************************
* KeUserModeCallback
*/
NTSTATUS WINAPI KeUserModeCallback( ULONG id, const void *args, ULONG len, void **ret_ptr, ULONG *ret_len )
{
struct user_callback_frame callback_frame = { { 0 }, ret_ptr, ret_len };
if ((char *)ntdll_get_thread_data()->kernel_stack + min_kernel_stack > (char *)&callback_frame)
return STATUS_STACK_OVERFLOW;
if (!__wine_setjmpex( &callback_frame.jmpbuf, NULL ))
{
struct syscall_frame *frame = amd64_thread_data()->syscall_frame;
void *args_data = (void *)((frame->rsp - len) & ~15);
memcpy( args_data, args, len );
callback_frame.frame.rcx = id;
callback_frame.frame.rdx = (ULONG_PTR)args;
callback_frame.frame.r8 = len;
callback_frame.frame.cs = cs64_sel;
callback_frame.frame.fs = fs32_sel;
callback_frame.frame.gs = ds64_sel;
callback_frame.frame.ss = ds64_sel;
callback_frame.frame.rsp = (ULONG_PTR)args_data - 0x28;
callback_frame.frame.rip = (ULONG_PTR)pKiUserCallbackDispatcher;
callback_frame.frame.eflags = 0x200;
callback_frame.frame.restore_flags = CONTEXT_CONTROL | CONTEXT_INTEGER;
callback_frame.frame.prev_frame = frame;
amd64_thread_data()->syscall_frame = &callback_frame.frame;
__wine_syscall_dispatcher_return( &callback_frame.frame, 0 );
}
return callback_frame.status;
}
/***********************************************************************
* NtCallbackReturn (NTDLL.@)
*/
NTSTATUS WINAPI NtCallbackReturn( void *ret_ptr, ULONG ret_len, NTSTATUS status )
{
return STATUS_NO_CALLBACK_ACTIVE;
struct user_callback_frame *frame = (struct user_callback_frame *)amd64_thread_data()->syscall_frame;
if (!frame->frame.prev_frame) return STATUS_NO_CALLBACK_ACTIVE;
*frame->ret_ptr = ret_ptr;
*frame->ret_len = ret_len;
frame->status = status;
amd64_thread_data()->syscall_frame = frame->frame.prev_frame;
__wine_longjmp( &frame->jmpbuf, 1 );
}
......@@ -2966,6 +3021,7 @@ void DECLSPEC_HIDDEN call_init_thunk( LPTHREAD_START_ROUTINE entry, void *arg, B
frame->rsp = (ULONG64)ctx - 8;
frame->rip = (ULONG64)pLdrInitializeThunk;
frame->rcx = (ULONG64)ctx;
frame->prev_frame = NULL;
frame->restore_flags |= CONTEXT_INTEGER;
pthread_sigmask( SIG_UNBLOCK, &server_block_set, NULL );
......
......@@ -84,6 +84,7 @@ static const SIZE_T teb_size = 0x3800; /* TEB64 + TEB32 + debug info */
static const SIZE_T signal_stack_mask = 0xffff;
static const SIZE_T signal_stack_size = 0x10000 - 0x3800;
static const SIZE_T kernel_stack_size = 0x20000;
static const SIZE_T min_kernel_stack = 0x2000;
static const LONG teb_offset = 0x2000;
#define FILE_WRITE_TO_END_OF_FILE ((LONGLONG)-1)
......
......@@ -1793,7 +1793,6 @@ static void output_syscall_dispatcher(void)
output( "\tstp x9, x19, [x10, #0xf0]\n" );
output( "\tmrs x9, NZCV\n" );
output( "\tstp x30, x9, [x10, #0x100]\n" );
output( "\tstr xzr, [x10, #0x110]\n" ); /* frame->restore_flags */
output( "\tmrs x9, FPCR\n" );
output( "\tstr w9, [x10, #0x118]\n" );
output( "\tmrs x9, FPSR\n" );
......@@ -1845,7 +1844,7 @@ static void output_syscall_dispatcher(void)
output( "\tldp x24, x25, [sp, #0xc0]\n" );
output( "\tldp x26, x27, [sp, #0xd0]\n" );
output( "\tldp x28, x29, [sp, #0xe0]\n" );
output( "\tldr x16, [sp, #0x110]\n" ); /* frame->restore_flags */
output( "\tldr w16, [sp, #0x10c]\n" ); /* frame->restore_flags */
output( "\ttbz x16, #2, 1f\n" ); /* CONTEXT_FLOATING_POINT */
output( "\tldp q0, q1, [sp, #0x120]\n" );
output( "\tldp q2, q3, [sp, #0x140]\n" );
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment