Files
FEX-Emu--FEX/Source/CommonCore/VMFactory.cpp
T
Ryan Houdek 50349db4c6 Moves our entire execution model over to signal execution
This requires implementing custom ASM dispatchers for the interpreter
side of things so it works correctly.
Allows all of our CPU backends to safely support signaling.
This is a bit of a nightmare change and requires rethinking logic about
debugging in some instances.
2020-09-02 16:00:17 -07:00

291 lines
11 KiB
C++

#include "VM.h"
#include "LogManager.h"
#include <FEXCore/Config/Config.h>
#include <FEXCore/Core/CodeLoader.h>
#include <FEXCore/Core/Context.h>
#include <FEXCore/Core/CoreState.h>
#include <FEXCore/Core/CPUBackend.h>
#include <FEXCore/Core/X86Enums.h>
#include <FEXCore/HLE/SyscallHandler.h>
#include <FEXCore/Debug/InternalThreadState.h>
#include <FEXCore/Memory/SharedMem.h>
namespace VMFactory {
class VMCore final : public FEXCore::CPU::CPUBackend {
public:
explicit VMCore(FEXCore::Context::Context* CTX, FEXCore::Core::ThreadState *Thread, bool Fallback);
~VMCore() override;
std::string GetName() override { return "VM Core"; }
void* CompileCode(FEXCore::IR::IRListView<true> const *IR, FEXCore::Core::DebugData *DebugData) override;
void *MapRegion(void *HostPtr, uint64_t VirtualGuestPtr, uint64_t Size) override {
MemoryRegions.emplace_back(MemoryRegion{HostPtr, VirtualGuestPtr, Size});
LogMan::Throw::A(!IsInitialized, "Tried mapping a new VM region post initialization");
return HostPtr;
}
void Initialize() override;
void ExecuteCode(FEXCore::Core::ThreadState *Thread);
bool NeedsOpDispatch() override { return false; }
private:
FEXCore::Context::Context* CTX;
FEXCore::Core::ThreadState *ThreadState;
bool IsFallback;
void CopyHostStateToGuest();
void CopyGuestCPUToHost();
void CopyHostMemoryToGuest();
void CopyGuestMemoryToHost();
void RecalculatePML4();
struct MemoryRegion {
void *HostPtr;
uint64_t VirtualGuestPtr;
uint64_t Size;
};
std::vector<MemoryRegion> MemoryRegions;
struct PhysicalToVirtual {
uint64_t PhysicalPtr;
uint64_t GuestVirtualPtr;
void *HostPtr;
uint64_t Size;
};
std::vector<PhysicalToVirtual> PhysToVirt;
SU::VM::VMInstance *VM;
bool IsInitialized{false};
};
VMCore::~VMCore() {
delete VM;
}
VMCore::VMCore(FEXCore::Context::Context* CTX, FEXCore::Core::ThreadState *Thread, bool Fallback)
: CTX {CTX}
, ThreadState {Thread}
, IsFallback {Fallback} {
VM = SU::VM::VMInstance::Create();
}
void VMCore::Initialize() {
// Scan the mapped memory regions and see how much memory backing we need
uint64_t PhysicalMemoryNeeded {};
PhysToVirt.reserve(MemoryRegions.size());
for (auto &Region : MemoryRegions) {
PhysToVirt.emplace_back(PhysicalToVirtual{PhysicalMemoryNeeded, Region.VirtualGuestPtr, Region.HostPtr, Region.Size});
PhysicalMemoryNeeded += Region.Size;
}
LogMan::Msg::D("We need 0x%lx physical memory", PhysicalMemoryNeeded);
VM->SetPhysicalMemorySize(PhysicalMemoryNeeded);
// Map these regions in the VM
for (auto &Region : PhysToVirt) {
if (!Region.Size) continue;
VM->AddMemoryMapping(Region.GuestVirtualPtr, Region.PhysicalPtr, Region.Size);
}
// Initialize the VM with the memory mapping
VM->Initialize();
CopyHostMemoryToGuest();
// Set initial register states
CopyHostStateToGuest();
IsInitialized = true;
}
void VMCore::CopyHostMemoryToGuest() {
void *PhysBase = VM->GetPhysicalMemoryPointer();
for (auto &Region : PhysToVirt) {
void *GuestPtr = reinterpret_cast<void*>((reinterpret_cast<uintptr_t>(PhysBase) + Region.PhysicalPtr));
memcpy(GuestPtr, Region.HostPtr, Region.Size);
}
}
void VMCore::CopyGuestMemoryToHost() {
void *PhysBase = VM->GetPhysicalMemoryPointer();
for (auto &Region : PhysToVirt) {
void *GuestPtr = reinterpret_cast<void*>((reinterpret_cast<uintptr_t>(PhysBase) + Region.PhysicalPtr));
memcpy(Region.HostPtr, GuestPtr, Region.Size);
}
}
void VMCore::CopyHostStateToGuest() {
auto CompactRFlags = [](auto Arg) -> uint32_t {
uint32_t Res = 2;
for (int i = 0; i < 32; ++i) {
Res |= Arg->flags[i] << i;
}
return Res;
};
SU::VM::VMInstance::RegisterState State;
SU::VM::VMInstance::SpecialRegisterState SpecialState;
State.rax = ThreadState->State.gregs[FEXCore::X86State::REG_RAX];
State.rbx = ThreadState->State.gregs[FEXCore::X86State::REG_RBX];
State.rcx = ThreadState->State.gregs[FEXCore::X86State::REG_RCX];
State.rdx = ThreadState->State.gregs[FEXCore::X86State::REG_RDX];
State.rsi = ThreadState->State.gregs[FEXCore::X86State::REG_RSI];
State.rdi = ThreadState->State.gregs[FEXCore::X86State::REG_RDI];
State.rsp = ThreadState->State.gregs[FEXCore::X86State::REG_RSP];
State.rbp = ThreadState->State.gregs[FEXCore::X86State::REG_RBP];
State.r8 = ThreadState->State.gregs[FEXCore::X86State::REG_R8];
State.r9 = ThreadState->State.gregs[FEXCore::X86State::REG_R9];
State.r10 = ThreadState->State.gregs[FEXCore::X86State::REG_R10];
State.r11 = ThreadState->State.gregs[FEXCore::X86State::REG_R11];
State.r12 = ThreadState->State.gregs[FEXCore::X86State::REG_R12];
State.r13 = ThreadState->State.gregs[FEXCore::X86State::REG_R13];
State.r14 = ThreadState->State.gregs[FEXCore::X86State::REG_R14];
State.r15 = ThreadState->State.gregs[FEXCore::X86State::REG_R15];
State.rip = ThreadState->State.rip;
State.rflags = CompactRFlags(&ThreadState->State);
VM->SetRegisterState(State);
SpecialState.fs.base = ThreadState->State.fs;
SpecialState.gs.base = ThreadState->State.gs;
VM->SetSpecialRegisterState(SpecialState);
}
void VMCore::CopyGuestCPUToHost() {
auto UnpackRFLAGS = [](auto ThreadState, uint64_t GuestFlags) {
for (int i = 0; i < 32; ++i) {
ThreadState->flags[i] = (GuestFlags >> i) & 1;
}
};
SU::VM::VMInstance::RegisterState State;
SU::VM::VMInstance::SpecialRegisterState SpecialState;
State = VM->GetRegisterState();
SpecialState = VM->GetSpecialRegisterState();
// Copy the VM's register state to our host context
ThreadState->State.gregs[FEXCore::X86State::REG_RAX] = State.rax;
ThreadState->State.gregs[FEXCore::X86State::REG_RBX] = State.rbx;
ThreadState->State.gregs[FEXCore::X86State::REG_RCX] = State.rcx;
ThreadState->State.gregs[FEXCore::X86State::REG_RDX] = State.rdx;
ThreadState->State.gregs[FEXCore::X86State::REG_RSI] = State.rsi;
ThreadState->State.gregs[FEXCore::X86State::REG_RDI] = State.rdi;
ThreadState->State.gregs[FEXCore::X86State::REG_RSP] = State.rsp;
ThreadState->State.gregs[FEXCore::X86State::REG_RBP] = State.rbp;
ThreadState->State.gregs[FEXCore::X86State::REG_R8] = State.r8;
ThreadState->State.gregs[FEXCore::X86State::REG_R9] = State.r9;
ThreadState->State.gregs[FEXCore::X86State::REG_R10] = State.r10;
ThreadState->State.gregs[FEXCore::X86State::REG_R11] = State.r11;
ThreadState->State.gregs[FEXCore::X86State::REG_R12] = State.r12;
ThreadState->State.gregs[FEXCore::X86State::REG_R13] = State.r13;
ThreadState->State.gregs[FEXCore::X86State::REG_R14] = State.r14;
ThreadState->State.gregs[FEXCore::X86State::REG_R15] = State.r15;
ThreadState->State.rip = State.rip;
UnpackRFLAGS(&ThreadState->State, State.rflags);
ThreadState->State.fs = SpecialState.fs.base;
ThreadState->State.gs = SpecialState.gs.base;
}
static void VMExecution(FEXCore::Core::ThreadState *Thread) {
auto InternalThread = reinterpret_cast<FEXCore::Core::InternalThreadState*>(Thread);
VMCore *Core = reinterpret_cast<VMCore*>(InternalThread->CPUBackend.get());
Core->ExecuteCode(Thread);
}
static void VMExecutionFallback(FEXCore::Core::ThreadState *Thread) {
auto InternalThread = reinterpret_cast<FEXCore::Core::InternalThreadState*>(Thread);
VMCore *Core = reinterpret_cast<VMCore*>(InternalThread->FallbackBackend.get());
Core->ExecuteCode(Thread);
}
void* VMCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView<true> const *IR, FEXCore::Core::DebugData *DebugData) {
if (IsFallback)
return reinterpret_cast<void*>(VMExecutionFallback);
else
return reinterpret_cast<void*>(VMExecution);
}
void VMCore::ExecuteCode(FEXCore::Core::ThreadState *Thread) {
#if 0
auto DumpState = [this]() {
LogMan::Msg::D("RIP: 0x%016lx", ThreadState->State.rip);
LogMan::Msg::D("RAX RBX RCX RDX");
LogMan::Msg::D("0x%016lx 0x%016lx 0x%016lx 0x%016lx",
ThreadState->State.gregs[FEXCore::X86State::REG_RAX],
ThreadState->State.gregs[FEXCore::X86State::REG_RBX],
ThreadState->State.gregs[FEXCore::X86State::REG_RCX],
ThreadState->State.gregs[FEXCore::X86State::REG_RDX]);
LogMan::Msg::D("RSI RDI RSP RBP");
LogMan::Msg::D("0x%016lx 0x%016lx 0x%016lx 0x%016lx",
ThreadState->State.gregs[FEXCore::X86State::REG_RSI],
ThreadState->State.gregs[FEXCore::X86State::REG_RDI],
ThreadState->State.gregs[FEXCore::X86State::REG_RSP],
ThreadState->State.gregs[FEXCore::X86State::REG_RBP]);
LogMan::Msg::D("R8 R9 R10 R11");
LogMan::Msg::D("0x%016lx 0x%016lx 0x%016lx 0x%016lx",
ThreadState->State.gregs[FEXCore::X86State::REG_R8],
ThreadState->State.gregs[FEXCore::X86State::REG_R9],
ThreadState->State.gregs[FEXCore::X86State::REG_R10],
ThreadState->State.gregs[FEXCore::X86State::REG_R11]);
LogMan::Msg::D("R12 R13 R14 R15");
LogMan::Msg::D("0x%016lx 0x%016lx 0x%016lx 0x%016lx",
ThreadState->State.gregs[FEXCore::X86State::REG_R12],
ThreadState->State.gregs[FEXCore::X86State::REG_R13],
ThreadState->State.gregs[FEXCore::X86State::REG_R14],
ThreadState->State.gregs[FEXCore::X86State::REG_R15]);
};
#endif
CopyHostMemoryToGuest();
CopyHostStateToGuest();
VM->SetStepping(true);
if (VM->Run()) {
int ExitReason = VM->ExitReason();
// 4 = DEBUG
if (ExitReason == 4 || ExitReason == 5) {
CopyGuestCPUToHost();
CopyGuestMemoryToHost();
}
// 5 = HLT
if (ExitReason == 5) {
}
// 8 = Shutdown. Due to an unhandled error
if (ExitReason == 8) {
LogMan::Msg::E("Unhandled VM Fault");
VM->Debug();
CopyGuestCPUToHost();
}
// 9 = Failed Entry
if (ExitReason == 9) {
LogMan::Msg::E("Failed to enter VM due to: 0x%lx", VM->GetFailEntryReason());
}
}
else {
LogMan::Msg::E("VM failed to run");
}
}
FEXCore::CPU::CPUBackend *CPUCreationFactory(FEXCore::Context::Context* CTX, FEXCore::Core::ThreadState *Thread) {
return new VMCore(CTX, Thread, false);
}
FEXCore::CPU::CPUBackend *CPUCreationFactoryFallback(FEXCore::Context::Context* CTX, FEXCore::Core::ThreadState *Thread) {
return new VMCore(CTX, Thread, true);
}
}