Skip to content

Commit

Permalink
ARM64EC: Implement inline SMC support using context reconstruction
Browse files Browse the repository at this point in the history
When an SMC trap happens: reconstruct the context before the SMC write
then compile the write as a single instruction block to reduce it to
regular SMC. SMC where the writing instruction is the instruction being
patched will hit the signal handler at most twice: the 1st will trigger
the write to be compiled as a single instuction block, the 2nd will
detect inline SMC of a single instruction block and then just take the
usual invalidate+reprotect+continue step, avoiding a potential infinite
loop of recompilation.
  • Loading branch information
bylaws committed Dec 10, 2024
1 parent cff6ab4 commit 8901dba
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 2 deletions.
3 changes: 2 additions & 1 deletion Source/Windows/ARM64EC/Module.S
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ BeginSimulation:
bl "#SyncThreadContext"
ldr x17, [x18, #0x1788] // TEB->ChpeV2CpuAreaInfo
ldr x16, [x17, #0x48] // ChpeV2CpuAreaInfo->EmulatorData[3] - DispatcherLoopTopEnterECFillSRA
br x16 // DispatcherLoopTopEnterECFillSRA(CPUArea:x17)
mov x10, #0 // Zero ENTRY_FILL_SRA_SINGLE_INST_REG to avoid single step
br x16 // DispatcherLoopTopEnterECFillSRA(SingleInst:x10, CPUArea:x17)

// Called into by FEXCore
// Expects the target code address in x9
Expand Down
14 changes: 13 additions & 1 deletion Source/Windows/ARM64EC/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,19 @@ bool ResetToConsistentStateImpl(EXCEPTION_RECORD* Exception, CONTEXT* GuestConte

std::scoped_lock Lock(ThreadCreationMutex);
if (InvalidationTracker->HandleRWXAccessViolation(FaultAddress)) {
LogMan::Msg::DFmt("Handled self-modifying code: pc: {:X} fault: {:X}", NativeContext->Pc, FaultAddress);
if (CTX->IsAddressInCodeBuffer(CPUArea.ThreadState(), NativeContext->Pc) && !CTX->IsCurrentBlockSingleInst(CPUArea.ThreadState()) &&
CTX->IsAddressInCurrentBlock(CPUArea.ThreadState(), FaultAddress, 8)) {
// If we are not patching ourself (single inst block case) and patching the current block, this is inline SMC. Reconstruct the current context (before the SMC write) then single step the write to reduce it to regular SMC.
Exception::ReconstructThreadState(CPUArea.ThreadState(), *NativeContext);
LogMan::Msg::DFmt("Handled inline self-modifying code: pc: {:X} rip: {:X} fault: {:X}", NativeContext->Pc,
CPUArea.ThreadState()->CurrentFrame->State.rip, FaultAddress);
NativeContext->Pc = CPUArea.DispatcherLoopTopEnterECFillSRA();
NativeContext->Sp = CPUArea.EmulatorStackBase();
NativeContext->X10 = 1; // Set ENTRY_FILL_SRA_SINGLE_INST_REG to force a single step
} else {
LogMan::Msg::DFmt("Handled self-modifying code: pc: {:X} fault: {:X}", NativeContext->Pc, FaultAddress);
}

return true;
}
}
Expand Down

0 comments on commit 8901dba

Please sign in to comment.