mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-06 10:00:16 +02:00
156 lines
5.6 KiB
ArmAsm
156 lines
5.6 KiB
ArmAsm
.text
|
|
.balign 16
|
|
|
|
// __os_arm64x_x64_jump in ARM64EC docs
|
|
// Expects target code address in x9
|
|
.globl DispatchJump
|
|
DispatchJump:
|
|
str lr, [sp, #-8]! // Push return address to stack, this will be popped by the x86 RET instr.
|
|
b check_target_ec
|
|
|
|
// __os_arm64x_dispatch_ret in ARM64EC docs
|
|
// Expects target code address in lr
|
|
.globl RetToEntryThunk
|
|
RetToEntryThunk:
|
|
mov x9, lr
|
|
|
|
check_target_ec:
|
|
// Check if target is in fact x86 code
|
|
ldr x16, [x18, #0x60] // TEB->PEB
|
|
ldr x16, [x16, #0x368] // PEB->EcCodeBitMap
|
|
lsr x17, x9, #18
|
|
ldr x16, [x16, x17, lsl #3]
|
|
lsr x17, x9, #12
|
|
lsr x16, x16, x17
|
|
tbnz x16, #0, ExitFunctionEC
|
|
b enter_jit
|
|
|
|
// __os_arm64x_dispatch_call_no_redirect in ARM64EC docs
|
|
// Expects target code address in x9, and to be called using a 'blr x16' instruction.
|
|
.globl ExitToX64
|
|
ExitToX64:
|
|
str lr, [sp, #-8]! // Push return address to stack, this will be popped by the x86 RET instr.
|
|
|
|
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)
|
|
|
|
// Invoked by KiUserEmulationDispatcher after e.g. an NtContinue to x86 code
|
|
.global BeginSimulation
|
|
BeginSimulation:
|
|
ldr x17, [x18, #0x1788] // TEB->ChpeV2CpuAreaInfo
|
|
ldr x16, [x17, #0x8] // ChpeV2CpuAreaInfo->EmulatorStackBase
|
|
mov sp, x16
|
|
ldr x0, [x17, #0x18] // ChpeV2CpuAreaInfo->ContextAmd64
|
|
bl "#SyncThreadContext"
|
|
ldr x17, [x18, #0x1788] // TEB->ChpeV2CpuAreaInfo
|
|
ldr x16, [x17, #0x48] // ChpeV2CpuAreaInfo->EmulatorData[3] - DispatcherLoopTopEnterECFillSRA
|
|
mov x11, #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
|
|
.global ExitFunctionEC
|
|
ExitFunctionEC:
|
|
// Clear any the AFP NEP and AH bits in FPCR as native code won't expect their behaviour.
|
|
mrs x17, fpcr
|
|
and x17, x17, #~6 // NEP + AH
|
|
msr fpcr, x17
|
|
ldr x17, [x18, #0x1788] // TEB->ChpeV2CpuAreaInfo
|
|
strb wzr, [x17, #0x0] // ChpeV2CpuAreaInfo->InSimulation
|
|
ldr x17, [x17, #0x20] // ChpeV2CpuAreaInfo->SuspendDoorbell
|
|
ldr w17, [x17]
|
|
cbnz w17, ExitFunctionSuspendPoint
|
|
|
|
.global ExitFunctionSuspendResumePoint
|
|
ExitFunctionSuspendResumePoint:
|
|
// 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
|
|
// exported ARM64EC functions have a 4-byte offset to their entry thunk immediately before their first instruction.
|
|
mov x17, x9
|
|
mov w16, #0x200
|
|
movk w16, #0xd63f, lsl 16 // blr x16
|
|
ldursw x23, [x17, #-0x4] // Load either the entry thunk offset or the calling instruction.
|
|
cmp w23, w16
|
|
beq ret_sp_aligned
|
|
|
|
and x23, x23, #-0x4
|
|
add x17, x17, x23 // Resolve entry thunk address.
|
|
|
|
mov x4, sp
|
|
tbz x4, #3, ret_sp_misaligned
|
|
ldr lr, [x4], #0x8 // Pop the return address into lr.
|
|
mov sp, x4
|
|
|
|
ret_sp_aligned:
|
|
br x17
|
|
|
|
ret_sp_misaligned:
|
|
// In the case of the x64 caller leaving sp only 8-byte aligned, leave the return address on the stack to keep 16-byte
|
|
// alignment and have the callee return to an x86 ret instruction. FEX can then return to the actual caller keeping
|
|
// the misaligned RSP.
|
|
adrp lr, X64ReturnInstr
|
|
ldr lr, [lr, #:lo12:X64ReturnInstr]
|
|
br x17
|
|
|
|
.global ExitFunctionSuspendPoint
|
|
ExitFunctionSuspendPoint:
|
|
brk #0xCAFE
|
|
// Resume will jump back to `ExitFunctionSuspendResumePoint`
|
|
|
|
// Makes a wrapper for calling a system call directly, skipping the usual ntdll thunks
|
|
#define HASH #
|
|
#define DIRECT_SYSCALL_WRAPPER(Name, WineIdName, WindowsId) \
|
|
.global Name; \
|
|
Name:; \
|
|
adrp x16, WineSyscallDispatcher; \
|
|
ldr x16, [x16, HASH:lo12:WineSyscallDispatcher]; \
|
|
cbz x16, 1f; \
|
|
mov x9, x30; \
|
|
adrp x8, WineIdName; \
|
|
ldr x8, [x8, HASH:lo12:WineIdName]; \
|
|
blr x16; \
|
|
ret; \
|
|
1:; \
|
|
svc HASH WindowsId; \
|
|
ret
|
|
|
|
// Allows for continuing from a full native context, as the NTDLL NtContinue export takes in an x64 context with EC and
|
|
// the conversion to that loses the ARM64EC ABI-disallowed registers that FEX uses.
|
|
DIRECT_SYSCALL_WRAPPER("#NtContinueNative", WineNtContinueSyscallId, 0x43)
|
|
|
|
// Both of these are wrapped as FEX needs them to setup its call checker at startup time and their NTDLL thunks could
|
|
// already be patched by then (and because the call checker isn't installed, their patched x86 versions would be invoked
|
|
// when called by FEX).
|
|
DIRECT_SYSCALL_WRAPPER("#NtAllocateVirtualMemoryNative", WineNtAllocateVirtualMemorySyscallId, 0x18)
|
|
DIRECT_SYSCALL_WRAPPER("#NtProtectVirtualMemoryNative", WineNtProtectVirtualMemorySyscallId, 0x50)
|
|
|
|
// A replacement for the standard ARM64EC call checker that ignores any FFS patches and always redirects to a function's
|
|
// native implementation. As the only library FEX calls into is NTDLL, this is done using a LUT generated at init time.
|
|
// Expects the FFS address in x11, exit thunk address in x10 (unused) and it's own address in x9. Return address is in x11.
|
|
.global "CheckCall"
|
|
"CheckCall":
|
|
adrp x9, NtDllBase
|
|
ldr x9, [x9, #:lo12:NtDllBase]
|
|
subs x16, x11, x9
|
|
b.lo end
|
|
adrp x17, NtDllRedirectionLUTSize
|
|
ldr x17, [x17, #:lo12:NtDllRedirectionLUTSize]
|
|
cmp x16, x17
|
|
b.hi end
|
|
adrp x17, NtDllRedirectionLUT
|
|
ldr x17, [x17, #:lo12:NtDllRedirectionLUT]
|
|
ldr w11, [x17, x16, lsl #2]
|
|
add x11, x11, x9
|
|
end:
|
|
ret
|
|
|
|
// Expects target address in x0, and the SP to set in x1
|
|
.global "#JumpSetStack"
|
|
"#JumpSetStack":
|
|
mov sp, x1
|
|
br x0
|