#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Xbyak; namespace HostFactory { #ifdef _M_X86_64 class HostCore final : public FEXCore::CPU::CPUBackend, public Xbyak::CodeGenerator { public: explicit HostCore(FEXCore::Context::Context* CTX, FEXCore::Core::ThreadState *Thread, bool Fallback); ~HostCore() override; std::string GetName() override { return "Host Core"; } void* CompileCode(FEXCore::IR::IRListView const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) override; void *MapRegion(void *HostPtr, uint64_t VirtualGuestPtr, uint64_t Size) override { return HostPtr; } void Initialize() override; bool NeedsOpDispatch() override { return false; } bool HandleSIGSEGV(FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext); private: FEXCore::Context::Context* CTX; FEXCore::Core::ThreadState *ThreadState; bool IsFallback{}; uint64_t ReturningStackLocation; uint64_t ThreadStopHandlerAddress; }; HostCore::~HostCore() { } HostCore::HostCore(FEXCore::Context::Context* CTX, FEXCore::Core::ThreadState *Thread, bool Fallback) : CodeGenerator(4096) , CTX {CTX} , ThreadState {Thread} , IsFallback {Fallback} { FEXCore::Context::RegisterHostSignalHandler(CTX, SIGSEGV, [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) -> bool { auto InternalThread = reinterpret_cast(Thread); HostCore *Core = reinterpret_cast(InternalThread->CPUBackend.get()); return Core->HandleSIGSEGV(Thread, Signal, info, ucontext); } ); FEXCore::Context::RegisterHostSignalHandler(CTX, 63, [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) -> bool { return true; }); } bool HostCore::HandleSIGSEGV(FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) { ucontext_t* _context = (ucontext_t*)ucontext; mcontext_t* _mcontext = &_context->uc_mcontext; // Check our current instruction that we just executed to ensure it was an HLT uint8_t *Inst = reinterpret_cast(_mcontext->gregs[REG_RIP]); constexpr uint8_t HLT = 0xF4; if (Inst[0] != HLT) { return false; } // Store our host state in to the guest for testing against Thread->State.State.gregs[FEXCore::X86State::REG_RAX] = _mcontext->gregs[REG_RAX]; Thread->State.State.gregs[FEXCore::X86State::REG_RBX] = _mcontext->gregs[REG_RBX]; Thread->State.State.gregs[FEXCore::X86State::REG_RCX] = _mcontext->gregs[REG_RCX]; Thread->State.State.gregs[FEXCore::X86State::REG_RDX] = _mcontext->gregs[REG_RDX]; Thread->State.State.gregs[FEXCore::X86State::REG_RBP] = _mcontext->gregs[REG_RBP]; Thread->State.State.gregs[FEXCore::X86State::REG_RSI] = _mcontext->gregs[REG_RSI]; Thread->State.State.gregs[FEXCore::X86State::REG_RDI] = _mcontext->gregs[REG_RDI]; Thread->State.State.gregs[FEXCore::X86State::REG_RSP] = _mcontext->gregs[REG_RSP]; Thread->State.State.gregs[FEXCore::X86State::REG_R8] = _mcontext->gregs[REG_R8]; Thread->State.State.gregs[FEXCore::X86State::REG_R9] = _mcontext->gregs[REG_R9]; Thread->State.State.gregs[FEXCore::X86State::REG_R10] = _mcontext->gregs[REG_R10]; Thread->State.State.gregs[FEXCore::X86State::REG_R11] = _mcontext->gregs[REG_R11]; Thread->State.State.gregs[FEXCore::X86State::REG_R12] = _mcontext->gregs[REG_R12]; Thread->State.State.gregs[FEXCore::X86State::REG_R13] = _mcontext->gregs[REG_R13]; Thread->State.State.gregs[FEXCore::X86State::REG_R14] = _mcontext->gregs[REG_R14]; Thread->State.State.gregs[FEXCore::X86State::REG_R15] = _mcontext->gregs[REG_R15]; Thread->State.State.rip = _mcontext->gregs[REG_RIP]; for (size_t i = 0; i < 16; ++i) { memcpy(&Thread->State.State.xmm[i], &_mcontext->fpregs->_xmm[i], sizeof(_mcontext->fpregs->_xmm[0])); } uint16_t CurrentOffset = (_mcontext->fpregs->swd >> 11) & 7; for (size_t i = 0; i < 8; ++i) { memcpy(&Thread->State.State.mm[(i + CurrentOffset) % 8], &_mcontext->fpregs->_st[i], sizeof(_mcontext->fpregs->_st[0])); } // Our thread is stopping // We don't care about anything at this point // Set the stack to our starting location when we entered the JIT and get out safely _mcontext->gregs[REG_RSP] = ReturningStackLocation; // Set the new PC _mcontext->gregs[REG_RIP] = ThreadStopHandlerAddress; return true; } void HostCore::Initialize() { DispatchPtr = getCurr(); // x86-64 ABI has the stack aligned when /call/ happens // Which means the destination has a misaligned stack at that point push(rbx); push(rbp); push(r12); push(r13); push(r14); push(r15); sub(rsp, 8); // Save this stack pointer so we can cleanly shutdown the emulation with a long jump // regardless of where we were in the stack mov(rax, (uint64_t)&ReturningStackLocation); mov(qword [rax], rsp); push(qword [rdi + offsetof(FEXCore::Core::CPUState, rip)]); mov (rax, 0); mov (rbx, 0); mov (rcx, 0); mov (rdx, 0); mov (rbp, 0); mov (rsi, 0); mov (r8, 0); mov (r9, 0); mov (r10, 0); mov (r11, 0); mov (r12, 0); mov (r13, 0); mov (r14, 0); mov (r15, 0); finit(); { // Load our RIP // RSP won't be set to zero here but should be fine ret(); } ThreadStopHandlerAddress = getCurr(); add(rsp, 8); pop(r15); pop(r14); pop(r13); pop(r12); pop(rbp); pop(rbx); ret(); ready(); } void* HostCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) { return nullptr; } FEXCore::CPU::CPUBackend *CPUCreationFactory(FEXCore::Context::Context* CTX, FEXCore::Core::ThreadState *Thread) { return new HostCore(CTX, Thread, false); } #else FEXCore::CPU::CPUBackend *CPUCreationFactory(FEXCore::Context::Context* CTX, FEXCore::Core::ThreadState *Thread) { LogMan::Msg::A("HostCPU factory doesn't exist for this host"); return nullptr; } #endif }