diff --git a/FEXCore/Source/Interface/Core/Core.cpp b/FEXCore/Source/Interface/Core/Core.cpp index 6158a53ca..30f7dae78 100644 --- a/FEXCore/Source/Interface/Core/Core.cpp +++ b/FEXCore/Source/Interface/Core/Core.cpp @@ -347,8 +347,8 @@ bool ContextImpl::InitCore() { #ifndef _WIN32 ThunkHandler = FEXCore::ThunkHandler::Create(); -#else - // WIN32 always needs the interrupt fault check to be enabled. +#elif !defined(_M_ARM64EC) + // WOW64 always needs the interrupt fault check to be enabled. Config.NeedsPendingInterruptFaultCheck = true; #endif diff --git a/FEXCore/Source/Interface/Core/JIT/Arm64/JIT.cpp b/FEXCore/Source/Interface/Core/JIT/Arm64/JIT.cpp index 2e981bde8..ea6e23322 100644 --- a/FEXCore/Source/Interface/Core/JIT/Arm64/JIT.cpp +++ b/FEXCore/Source/Interface/Core/JIT/Arm64/JIT.cpp @@ -718,6 +718,16 @@ CPUBackend::CompiledCode Arm64JITCore::CompileCode(uint64_t Entry, const FEXCore offsetof(FEXCore::Core::InternalThreadState, InterruptFaultPage) - offsetof(FEXCore::Core::InternalThreadState, BaseFrameState)); } +#ifdef _M_ARM_64EC + static constexpr uint16_t SuspendMagic {0xCAFE}; + + ldr(TMP2.W(), STATE_PTR(CpuStateFrame, SuspendDoorbell)); + ARMEmitter::SingleUseForwardLabel l_NoSuspend; + cbz(ARMEmitter::Size::i32Bit, TMP2, &l_NoSuspend); + brk(SuspendMagic); + Bind(&l_NoSuspend); +#endif + SpillSlots = RAData->SpillSlots(); if (SpillSlots) { diff --git a/FEXCore/include/FEXCore/Core/CoreState.h b/FEXCore/include/FEXCore/Core/CoreState.h index ae5bf50ea..c81f93001 100644 --- a/FEXCore/include/FEXCore/Core/CoreState.h +++ b/FEXCore/include/FEXCore/Core/CoreState.h @@ -345,6 +345,11 @@ struct CpuStateFrame { InternalThreadState* Thread; +#ifdef _M_ARM_64EC + // Set by the kernel on ARM64EC whenever the JIT should cooperatively suspend running guest code. + uint32_t SuspendDoorbell {}; +#endif + // Pointers that the JIT needs to load to remove relocations JITPointers Pointers; }; diff --git a/Source/Windows/ARM64EC/Module.S b/Source/Windows/ARM64EC/Module.S index 01bef9a04..fff51f443 100644 --- a/Source/Windows/ARM64EC/Module.S +++ b/Source/Windows/ARM64EC/Module.S @@ -34,6 +34,8 @@ ExitToX64: enter_jit: ldr x17, [x18, #0x1788] // TEB->ChpeV2CpuAreaInfo + mov w16, #1 + strb w16, [x17, #0x0] // ChpeV2CpuAreaInfo->InSimulation ldr x16, [x17, #0x40] // ChpeV2CpuAreaInfo->EmulatorData[2] - DispatcherLoopTopEnterEC br x16 // DispatcherLoopTopEnterEC(RIP:x9, CPUArea:x17) @@ -57,6 +59,8 @@ ExitFunctionEC: mrs x17, fpcr and x17, x17, #~6 // NEP + AH msr fpcr, x17 + ldr x17, [x18, #0x1788] // TEB->ChpeV2CpuAreaInfo + strb wzr, [x17, #0x0] // ChpeV2CpuAreaInfo->InSimulation // Either return to an exit thunk (return to ARM64EC function) or call an entry thunk (call to ARM64EC function). // It is assumed that a 'blr x16' instruction is only ever used to call into x86 code from an exit thunk, and that all diff --git a/Source/Windows/ARM64EC/Module.cpp b/Source/Windows/ARM64EC/Module.cpp index 5974e0d5f..4db8b13eb 100644 --- a/Source/Windows/ARM64EC/Module.cpp +++ b/Source/Windows/ARM64EC/Module.cpp @@ -605,6 +605,15 @@ bool ResetToConsistentStateImpl(EXCEPTION_RECORD* Exception, CONTEXT* GuestConte return true; } + // The JIT (in CompileBlock) emits code to check the suspend doorbell at the start of every block, and run the following instruction if it is set: + static constexpr uint32_t SuspendTrapMagic {0xD4395FC0}; // brk #0xCAFE + if (Exception->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION && *reinterpret_cast(NativeContext->Pc) == SuspendTrapMagic) { + *NativeContext = Exception::ReconstructPackedECContext(*NativeContext); + LogMan::Msg::DFmt("Suspending: RIP: {:X} SP: {:X}", NativeContext->Pc, NativeContext->Sp); + CPUArea.Area->InSimulation = 0; + *CPUArea.Area->SuspendDoorbell = 0; + return true; + } if (IsEmulatorStackAddress(reinterpret_cast(__builtin_frame_address(0)))) { Exception::RethrowGuestException(*Exception, *NativeContext); @@ -764,6 +773,7 @@ NTSTATUS ThreadInit() { } CPUArea.ThreadState() = Thread; + CPUArea.Area->SuspendDoorbell = reinterpret_cast(&Thread->CurrentFrame->SuspendDoorbell); return STATUS_SUCCESS; }