diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h index c4f9cc5f8f05..1af5c3d1d08a 100644 --- a/dlls/ntdll/ntdll_misc.h +++ b/dlls/ntdll/ntdll_misc.h @@ -181,7 +181,6 @@ extern IMAGE_ARM64EC_METADATA *arm64ec_get_module_metadata( HMODULE module ); extern void arm64ec_update_hybrid_metadata( void *module, IMAGE_NT_HEADERS *nt, const IMAGE_ARM64EC_METADATA *metadata ); extern void invoke_arm64ec_syscall(void); -extern void arm64ec_suspend_point(void); extern void *__os_arm64x_check_call; extern void *__os_arm64x_check_icall; diff --git a/dlls/ntdll/signal_arm64ec.c b/dlls/ntdll/signal_arm64ec.c index 3a8576d3e1bf..ce0a16c76516 100644 --- a/dlls/ntdll/signal_arm64ec.c +++ b/dlls/ntdll/signal_arm64ec.c @@ -90,7 +90,6 @@ static inline BOOL enter_syscall_callback(void) static inline void leave_syscall_callback(void) { get_arm64ec_cpu_area()->InSyscallCallback = 0; - if (get_arm64ec_cpu_area()->SuspendDoorbell && *get_arm64ec_cpu_area()->SuspendDoorbell) arm64ec_suspend_point(); } /********************************************************************** diff --git a/dlls/ntdll/signal_x86_64.c b/dlls/ntdll/signal_x86_64.c index 1d93564602b0..d028d2ba13df 100644 --- a/dlls/ntdll/signal_x86_64.c +++ b/dlls/ntdll/signal_x86_64.c @@ -74,14 +74,6 @@ __ASM_GLOBAL_FUNC( invoke_arm64ec_syscall, "pushq %r10\n\t" /* and return to syscall thunk */ "ret" ) -/************************************************************************** - * arm64ec_suspend_point - * - * x64 stub to support cooperative suspend when leaving a syscall callack. - */ -__ASM_GLOBAL_FUNC( arm64ec_suspend_point, - "ret" ) - /******************************************************************* * KiUserExceptionDispatcher (NTDLL.@) */ diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c index 3a3a037f788d..18426bc72bf4 100644 --- a/dlls/ntdll/unix/signal_arm64.c +++ b/dlls/ntdll/unix/signal_arm64.c @@ -213,10 +213,6 @@ struct syscall_frame C_ASSERT( sizeof( struct syscall_frame ) == 0x330 ); -static BOOL is_arm64ec_suspend_doorbell_valid(void) -{ - return is_arm64ec() && NtCurrentTeb()->ChpeV2CpuAreaInfo && NtCurrentTeb()->ChpeV2CpuAreaInfo->SuspendDoorbell; -} /*********************************************************************** * context_init_empty_xstate @@ -241,6 +237,7 @@ void set_process_instrumentation_callback( void *callback ) if (callback) FIXME( "Not supported.\n" ); } + /*********************************************************************** * syscall_frame_fixup_for_fastpath * @@ -347,20 +344,22 @@ static void restore_context( const CONTEXT *context, ucontext_t *sigcontext ) NTSTATUS signal_set_full_context( CONTEXT *context ) { struct syscall_frame *frame = get_syscall_frame(); - NTSTATUS status = NtSetContextThread( GetCurrentThread(), context ); + struct arm64_thread_data *arm64_data = arm64_thread_data(); + NTSTATUS status; - if (is_arm64ec_suspend_doorbell_valid() && arm64_thread_data()->suspend_pending) + if (arm64_data->suspend_pending) { - CONTEXT suspend_context; + sigset_t old_set; + pthread_sigmask( SIG_BLOCK, &server_block_set, &old_set ); *NtCurrentTeb()->ChpeV2CpuAreaInfo->SuspendDoorbell = 0; - arm64_thread_data()->suspend_pending = FALSE; - suspend_context.ContextFlags = CONTEXT_FULL | CONTEXT_EXCEPTION_REPORTING; /* TODO: check */ - NtGetContextThread( GetCurrentThread(), &suspend_context ); - wait_suspend( &suspend_context ); - NtSetContextThread( GetCurrentThread(), &suspend_context ); + arm64_data->suspend_pending = FALSE; + wait_suspend( context ); + status = NtSetContextThread( GetCurrentThread(), context ); + pthread_sigmask( SIG_SETMASK, &old_set, NULL ); } + else status = NtSetContextThread( GetCurrentThread(), context ); - if (!status && (context->ContextFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER) /* TODO: also check with susp */ + if (!status && (context->ContextFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER) frame->restore_flags |= CONTEXT_INTEGER; if (is_arm64ec() && !is_ec_code( frame->pc )) @@ -451,20 +450,12 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) { struct syscall_frame *frame = get_syscall_frame(); DWORD needed_flags = context->ContextFlags & ~CONTEXT_ARM64; - THREAD_BASIC_INFORMATION info; - NTSTATUS ret; - BOOL self; - - NtQueryInformationThread( handle, ThreadBasicInformation, &info, sizeof(info), NULL ); - self = HandleToULong( info.ClientId.UniqueThread ) == GetCurrentThreadId(); + BOOL self = (handle == GetCurrentThread()); if (!self) { - /* Avoid exposing JIT code pointers to other processes on ARM64EC */ - if (is_arm64ec()) NtSuspendThread( handle, NULL ); - ret = get_thread_context( handle, context, &self, IMAGE_FILE_MACHINE_ARM64 ); - if (is_arm64ec()) NtResumeThread( handle, NULL ); - return ret; + NTSTATUS ret = get_thread_context( handle, context, &self, IMAGE_FILE_MACHINE_ARM64 ); + if (ret || !self) return ret; } if (needed_flags & CONTEXT_INTEGER) @@ -1345,17 +1336,20 @@ static void quit_handler( int signal, siginfo_t *siginfo, void *sigcontext ) static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext ) { ucontext_t *ucontext = sigcontext; + CHPE_V2_CPU_AREA_INFO *chpe; CONTEXT context; - if (is_arm64ec_suspend_doorbell_valid() && - (NtCurrentTeb()->ChpeV2CpuAreaInfo->InSimulation || NtCurrentTeb()->ChpeV2CpuAreaInfo->InSyscallCallback)) + if ((chpe = NtCurrentTeb()->ChpeV2CpuAreaInfo) && chpe->InSimulation && chpe->SuspendDoorbell) { - *NtCurrentTeb()->ChpeV2CpuAreaInfo->SuspendDoorbell = 1; - arm64_thread_data()->suspend_pending = TRUE; - return; + NTSTATUS status = server_select( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_COOPERATIVE_SUSPEND, + 0, NULL, NULL ); + if (status == STATUS_THREAD_WAS_SUSPENDED) + { + *chpe->SuspendDoorbell = -1; + arm64_thread_data()->suspend_pending = TRUE; + } } - - if (is_inside_syscall( SP_sig(ucontext) )) + else if (is_inside_syscall( SP_sig(ucontext) )) { context.ContextFlags = CONTEXT_FULL | CONTEXT_EXCEPTION_REQUEST; NtGetContextThread( GetCurrentThread(), &context ); @@ -1510,14 +1504,11 @@ void syscall_dispatcher_return_slowpath(void) */ void init_syscall_frame( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend, TEB *teb ) { - struct ntdll_thread_data *thread_data = (struct ntdll_thread_data *)&teb->GdiTebBatch; - struct syscall_frame *frame = thread_data->syscall_frame; + struct syscall_frame *frame = ((struct ntdll_thread_data *)&teb->GdiTebBatch)->syscall_frame; CONTEXT *ctx, context = { CONTEXT_ALL }; I386_CONTEXT *i386_context; ARM_CONTEXT *arm_context; - ((struct arm64_thread_data *)(thread_data->cpu_data))->suspend_pending = FALSE; - context.X0 = (DWORD64)entry; context.X1 = (DWORD64)arg; context.X18 = (DWORD64)teb; diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index dc1ceeb142e6..d90871790993 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -1614,8 +1614,9 @@ struct select_reply /* VARARG(call,apc_call); */ /* VARARG(contexts,contexts); */ }; -#define SELECT_ALERTABLE 1 -#define SELECT_INTERRUPTIBLE 2 +#define SELECT_ALERTABLE 1 +#define SELECT_INTERRUPTIBLE 2 +#define SELECT_COOPERATIVE_SUSPEND 4 diff --git a/server/protocol.def b/server/protocol.def index 03565fe4d534..ac38076b22cf 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1463,8 +1463,9 @@ struct cpu_topology_override VARARG(call,apc_call); /* APC call arguments */ VARARG(contexts,contexts); /* suspend context(s) */ @END -#define SELECT_ALERTABLE 1 -#define SELECT_INTERRUPTIBLE 2 +#define SELECT_ALERTABLE 1 +#define SELECT_INTERRUPTIBLE 2 +#define SELECT_COOPERATIVE_SUSPEND 4 /* Create an event */ diff --git a/server/request_trace.h b/server/request_trace.h index ec36688c3b39..5e94ddf90efe 100644 --- a/server/request_trace.h +++ b/server/request_trace.h @@ -4577,6 +4577,7 @@ static const struct { "SHUTDOWN_IN_PROGRESS", STATUS_SHUTDOWN_IN_PROGRESS }, { "SUSPEND_COUNT_EXCEEDED", STATUS_SUSPEND_COUNT_EXCEEDED }, { "THREAD_IS_TERMINATING", STATUS_THREAD_IS_TERMINATING }, + { "THREAD_WAS_SUSPENDED", STATUS_THREAD_WAS_SUSPENDED }, { "TIMEOUT", STATUS_TIMEOUT }, { "TOO_MANY_OPENED_FILES", STATUS_TOO_MANY_OPENED_FILES }, { "UNSUCCESSFUL", STATUS_UNSUCCESSFUL }, diff --git a/server/thread.c b/server/thread.c index 7caf39b1d502..5c4a6bd02070 100644 --- a/server/thread.c +++ b/server/thread.c @@ -134,6 +134,7 @@ struct context struct object obj; /* object header */ struct object *sync; /* sync object for wait/signal */ unsigned int status; /* status of the context */ + int cooperative;/* waiting for the cooperative suspend */ struct context_data regs[2]; /* context data */ }; #define CTX_NATIVE 0 /* context for native machine */ @@ -446,6 +447,7 @@ static inline void init_thread_structure( struct thread *thread ) static inline int is_thread_suspended( struct thread *thread ) { + if (thread->context && thread->context->cooperative) return 0; if (thread->suspend) return 1; return !thread->bypass_proc_suspend && thread->process->suspend; } @@ -486,8 +488,9 @@ static struct context *create_thread_context( struct thread *thread ) { struct context *context; if (!(context = alloc_object( &context_ops ))) return NULL; - context->sync = NULL; - context->status = STATUS_PENDING; + context->sync = NULL; + context->status = STATUS_PENDING; + context->cooperative = 0; memset( &context->regs, 0, sizeof(context->regs) ); context->regs[CTX_NATIVE].machine = native_machine; @@ -1193,6 +1196,12 @@ static int check_wait( struct thread *thread ) if ((wait->flags & SELECT_INTERRUPTIBLE) && !list_empty( &thread->system_apc )) return STATUS_KERNEL_APC; + if ((wait->flags & SELECT_COOPERATIVE_SUSPEND) && thread->context) + { + thread->context->cooperative = 1; + return STATUS_THREAD_WAS_SUSPENDED; + } + /* Suspended threads may not acquire locks, but they can run system APCs */ if (is_thread_suspended( thread )) return -1; @@ -2058,6 +2067,7 @@ DECL_HANDLER(select) copy_context( &ctx->regs[CTX_WOW], wow_context, wow_context->flags & ~ctx->regs[CTX_WOW].flags ); } ctx->status = STATUS_SUCCESS; + ctx->cooperative = 0; current->suspend_cookie = req->cookie; signal_sync( ctx->sync ); }