diff --git a/VM.cpp b/VM.cpp index 7d59f3927..10d69a044 100644 --- a/VM.cpp +++ b/VM.cpp @@ -1,8 +1,10 @@ +#include "LogManager.h" #include "VM.h" #include #include #include +#include #include #include #include @@ -11,9 +13,77 @@ #include #include +inline uint64_t AlignUp(uint64_t value, uint64_t size) { + return value + (size - value % size) % size; +}; + +class Memmap { +public: + bool AllocateSHMRegion(size_t Size); + void DeallocateSHMRegion(); + int GetFD() { return SHMfd; } + void *MapRegionFlags(size_t Offset, size_t Size, uint32_t flags, bool Fixed); + void *MapRegion(size_t Offset, size_t Size, bool Fixed); + +private: + int SHMfd; + size_t SHMSize; + void *Base; +}; +void *Memmap::MapRegion(size_t Offset, size_t Size, bool Fixed) { + return MapRegionFlags(Offset, Size, PROT_READ | PROT_WRITE, Fixed); +} + +void *Memmap::MapRegionFlags(size_t Offset, size_t Size, uint32_t flags, bool Fixed) { + uintptr_t PtrOffset = reinterpret_cast(Base) + Offset; + void *Ptr = mmap(reinterpret_cast(PtrOffset), Size, flags, + MAP_SHARED | (Fixed ? MAP_FIXED : 0), SHMfd, Offset); + + if (Ptr == MAP_FAILED) { + LogMan::Msg::A("Failed to map memory region [0x%lx, 0x%lx) - %d", Offset, Size, Fixed); + return nullptr; + } + + return Ptr; +} + +bool Memmap::AllocateSHMRegion(size_t Size) { + const std::string SHMName = "VM" + std::to_string(getpid()); + SHMfd = shm_open(SHMName.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600); + if (SHMfd == -1) { + LogMan::Msg::E("Couldn't open SHM"); + return false; + } + + // Unlink the shm file immediately to not leave it around + shm_unlink(SHMName.c_str()); + + // Extend the SHM to the size we requested + if (ftruncate(SHMfd, Size) != 0) { + LogMan::Msg::E("Couldn't set SHM size"); + return false; + } + + SHMSize = Size; + Base = MapRegion(0, SHMSize, false); + + return true; +} + +void Memmap::DeallocateSHMRegion() { + close(SHMfd); +} + namespace SU::VM { namespace KVM { + struct MemoryRegion { + uint64_t VirtualBase; + uint64_t PhysicalBase; + uint64_t RegionSize; + void *MappedLocation; + }; + // This only supports 4k pages class PML4Control final { public: @@ -41,11 +111,6 @@ namespace SU::VM { void SetupTables(); private: - struct MemoryRegion { - uint64_t VirtualBase; - uint64_t PhysicalBase; - uint64_t RegionSize; - }; std::vector MemoryMappings; uint64_t PML4VirtualAddress{}; uint64_t PML4PhysicalAddress{}; @@ -85,22 +150,25 @@ namespace SU::VM { uint64_t NumPages = Region.RegionSize / PAGE_SIZE; - uint64_t PML4_Offset = (VirtualAddress >> 39) & 0x1FF; - uint64_t PDPE_Offset = (VirtualAddress >> 30) & 0x1FF; - uint64_t PDE_Offset = (VirtualAddress >> 21) & 0x1FF; - uint64_t PTE_Offset = (VirtualAddress >> 12) & 0x1FF; - uint64_t Physical_Offset = (VirtualAddress ) & 0xFFF; - // Now walk the tables for all the pages and map the table for (uint64_t i = 0; i < NumPages; ++i) { + uint64_t OffsetVirtual = VirtualAddress + (i * PAGE_SIZE); + uint64_t OffsetPhysical = PhysicalAddress + (i * PAGE_SIZE); + + uint64_t PML4_Offset = (OffsetVirtual >> 39) & 0x1FF; + uint64_t PDPE_Offset = (OffsetVirtual >> 30) & 0x1FF; + uint64_t PDE_Offset = (OffsetVirtual >> 21) & 0x1FF; + uint64_t PTE_Offset = (OffsetVirtual >> 12) & 0x1FF; + uint64_t Physical_Offset = (OffsetVirtual ) & 0xFFF; + // Set the PML4_table to point to Base of the PDPE table offset - PML4_Table[PML4_Offset] = (uint64_t)Physical_PDPE_Table | 0b11; + PML4_Table[PML4_Offset] = (uint64_t)Physical_PDPE_Table | 0b111; // Continue mapping - PDPE_Table[PDPE_Offset] = (uint64_t)Physical_PDE_Table | 0b11; + PDPE_Table[PDPE_Offset] = (uint64_t)Physical_PDE_Table | 0b111; // Continue mapping - PDE_Table[PDE_Offset] = (uint64_t)Physical_PTE_Table | 0b11; + PDE_Table[PDE_Offset] = (uint64_t)Physical_PTE_Table | 0b111; // This now maps directly to the region's physical address - PTE_Table[PTE_Offset] = PhysicalAddress | 0b11; + PTE_Table[PTE_Offset] = OffsetPhysical | 0b000'100000011; } } } @@ -120,6 +188,13 @@ namespace SU::VM { bool SetStepping(bool Step) override; bool Run() override; int ExitReason() override { return VCPUMemory->exit_reason; } + uint64_t GetFailEntryReason() override { return VCPUMemory->fail_entry.hardware_entry_failure_reason; } + VMInstance::RegisterState GetRegisterState() override; + VMInstance::SpecialRegisterState GetSpecialRegisterState() override; + + void SetRegisterState(VMInstance::RegisterState &State) override; + void SetSpecialRegisterState(VMInstance::SpecialRegisterState &State) override; + void Debug() override; private: // FDs @@ -140,8 +215,18 @@ namespace SU::VM { // Allocated memory void *PML4_Addr; void *VM_Addr; + MemoryRegion IDTRegion { + .RegionSize = 4096, + }; + MemoryRegion GDTRegion { + .RegionSize = 16 * 4096, + }; + MemoryRegion APICRegion { + .RegionSize = 4096, + }; bool SetupCPUInLongMode(); bool SetupMemory(); + Memmap Mapper; }; KVMInstance::~KVMInstance() { @@ -153,27 +238,88 @@ namespace SU::VM { // unmap all of our pointers munmap(VM_Addr, PhysicalMemorySize); munmap(PML4_Addr, PML4.GetPML4Size()); + munmap(IDTRegion.MappedLocation, IDTRegion.RegionSize); } bool KVMInstance::SetupMemory() { + // ============================ + // Physical Memory map currently + // ============================ + + // Everything is mapped linearly from [0, end) + // [UserMemory, UserMemory + Size) + // [IDTRegion, IDTRegion + Size) + // [GDTRegion, GDTRegion + Size) + // [APICRegion, APICRegion + Size) + // [PML4, PML4 + Size) + // + // ============================ + // Virtual Memory map currently + // ============================ + // Virtual memory map directly mirrors the physical mapping + + uint64_t VirtualBase = 0x0000'00F0'0000'0000; uint64_t PhysicalMemoryBase = 0; // Calculate the amount of physical memory backing we need PhysicalMemorySize = PhysicalUserMemorySize; + IDTRegion.PhysicalBase = PhysicalMemorySize; + IDTRegion.VirtualBase = VirtualBase + IDTRegion.PhysicalBase; // Mapped to the same place + PhysicalMemorySize += IDTRegion.RegionSize; + + GDTRegion.PhysicalBase = PhysicalMemorySize; + GDTRegion.VirtualBase = VirtualBase + GDTRegion.PhysicalBase; // Mapped to the same place + PhysicalMemorySize += GDTRegion.RegionSize; + + APICRegion.PhysicalBase = PhysicalMemorySize; + APICRegion.VirtualBase = VirtualBase + APICRegion.PhysicalBase; // Mapped to the same place + PhysicalMemorySize += APICRegion.RegionSize; + // Physical and virtual map directly atm PML4.SetPhysicalAddress(PhysicalMemorySize); - PML4.SetVirtualAddress(PhysicalMemorySize); + PML4.SetVirtualAddress(VirtualBase + PhysicalMemorySize); PhysicalMemorySize += PML4.GetPML4Size(); + + // Align up just to make sure we are page aligned + PhysicalMemorySize = AlignUp(PhysicalMemorySize, 4096); + + Mapper.AllocateSHMRegion(1ULL << 36); + // Physical memory is just [0, PhysicalMemorySize) // Just map physical zero to virtual zero // This maps both our user set memory region and our PML4 region to be accessable - PML4.AddMemoryMapping(0, 0, PhysicalMemorySize); + PML4.AddMemoryMapping(VirtualBase, 0, PhysicalMemorySize); + + // Set up user memory + { + // Allocate the memory for the user physical memory + VM_Addr = Mapper.MapRegion(0, PhysicalUserMemorySize, true); + LogMan::Throw::A(VM_Addr != (void*)-1ULL, "Couldn't Allocate"); + + // Map this memory to the guest VM + kvm_userspace_memory_region Region = { + .slot = MemorySlot++, + .flags = 0, + .guest_phys_addr = PhysicalMemoryBase, + .memory_size = PhysicalUserMemorySize, + .userspace_addr = (uint64_t)VM_Addr, + }; + + if (ioctl(VM_FD, KVM_SET_USER_MEMORY_REGION, &Region) == -1) { + printf("Couldn't set guestPD region\n"); + return false; + } + + // The PM4L setup with handle the page mapping + } // Set up PML4 { // Allocate the host facing PML4 memory - PML4_Addr = mmap(nullptr, PML4.GetPML4Size(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0); + PML4_Addr = Mapper.MapRegion(PML4.GetPhysicalAddress(), PML4.GetPML4Size(), true); + LogMan::Throw::A(PML4_Addr != (void*)-1ULL, "Couldn't Allocate"); + if (PML4_Addr == reinterpret_cast(-1ULL)) { fprintf(stderr, "Couldn't allocate PML4 table memory"); return false; @@ -183,7 +329,7 @@ namespace SU::VM { // Map this address space in to the VM kvm_userspace_memory_region Region = { .slot = MemorySlot++, - .flags = 0, // Read-only? + .flags = 0, .guest_phys_addr = PML4.GetPhysicalAddress(), .memory_size = PML4.GetPML4Size(), .userspace_addr = (uint64_t)PML4_Addr, @@ -198,18 +344,19 @@ namespace SU::VM { PML4.SetupTables(); } - // Set up user memory + // Set up GDT + // GDT contains SEGMENTS { - // Allocate the memory for the user physical memory - VM_Addr = mmap(nullptr, PhysicalUserMemorySize, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0); + GDTRegion.MappedLocation = Mapper.MapRegion(GDTRegion.PhysicalBase, GDTRegion.RegionSize, true); + LogMan::Throw::A(GDTRegion.MappedLocation != (void*)-1ULL, "Couldn't Allocate"); // Map this memory to the guest VM kvm_userspace_memory_region Region = { .slot = MemorySlot++, - .flags = 0, // Read-only? - .guest_phys_addr = PhysicalMemoryBase, - .memory_size = PhysicalUserMemorySize, - .userspace_addr = (uint64_t)VM_Addr, + .flags = 0, + .guest_phys_addr = GDTRegion.PhysicalBase, + .memory_size = GDTRegion.RegionSize, + .userspace_addr = (uint64_t)GDTRegion.MappedLocation, }; if (ioctl(VM_FD, KVM_SET_USER_MEMORY_REGION, &Region) == -1) { @@ -217,13 +364,214 @@ namespace SU::VM { return false; } - // The PM4L setup with handle the page mapping + // Set up the table entries + union GDTSegment { + struct NullSegment { + uint32_t Unused_0; + uint32_t Unused_1; + } Null; + struct CodeSegment { + uint32_t Unused; + unsigned Unused_0 : 8; + unsigned Unused_1 : 1; + unsigned Unused_2 : 1; + unsigned C : 1; + unsigned MustBeSet: 2; + unsigned DPL : 2; + unsigned P : 1; + unsigned Unused_3 : 4; + unsigned Unused_4 : 1; + unsigned L : 1; + unsigned D : 1; + unsigned Unused_5 : 1; + unsigned Unused_6 : 8; + } Code; + struct DataSegment { + uint32_t Unused_0; + unsigned Unused_1 : 11; + unsigned DataType : 2; + unsigned Unused_2 : 2; + unsigned P : 1; + unsigned Unused_3 : 16; + } Data; + } __attribute__((packed)); + + // Even though this is still a GDT segment + // its size has increased to 64 bits which means it takes two slots + struct SystemSegment { + uint16_t SegmentLimitLow; + uint16_t BaseLow; + unsigned BaseLowMid : 8; + unsigned Type : 4; + unsigned MustBeZero_0 : 1; + unsigned DPL : 2; + unsigned P : 1; + unsigned SegmentLimitHigh : 4; + unsigned Unused_0 : 2; + unsigned G : 1; + unsigned BaseLowHigh : 8; + uint32_t BaseHigh; + unsigned Unused_1 : 8; + unsigned MustBeZero_1 : 5; + unsigned Unused_2 : 19; + } __attribute__((packed)); + + GDTSegment DefaultCodeSegment = { + .Code = { + .MustBeSet = 0b11, + }, + }; + GDTSegment DefaultDataSegment = { + .Data = { + .DataType = 0b10, + }, + }; + SystemSegment DefaultSystemSegment = {}; + + unsigned Entries = GDTRegion.RegionSize / sizeof(GDTSegment); + + GDTSegment *GuestEntries = (GDTSegment*)GDTRegion.MappedLocation; + + // Null Segment + GDTSegment NullSegment{}; + + // Data segment + GDTSegment DataSegment = DefaultDataSegment; + DataSegment.Data.P = 1; + + // Code Segment + GDTSegment CodeSegment = DefaultCodeSegment; + CodeSegment.Code.P = 1; + CodeSegment.Code.L = 1; // 64bit Code + CodeSegment.Code.D = 0; // MUST be 0 in 64bit mode. CS.D = 0 and CS.L = 1 + + // 64bit TSS segment + SystemSegment Enable64BitTSS = DefaultSystemSegment; + Enable64BitTSS.Type = 0b1001; + Enable64BitTSS.P = 1; + + unsigned CurrentOffset = 0; + + // Null segment is first + GuestEntries[CurrentOffset++] = NullSegment; + GuestEntries[CurrentOffset++] = CodeSegment; + GuestEntries[CurrentOffset++] = DataSegment; + + void *Ptr = &GuestEntries[CurrentOffset]; + CurrentOffset += 2; + memcpy(Ptr, &Enable64BitTSS, sizeof(SystemSegment)); + } + + // Set up IDT + // When an exception or interrupt occurs, the processor uses the interrupt vector number as an index in to the IDT + // IDT is used in all operating modes + // IDT contains GATES + { + IDTRegion.MappedLocation = Mapper.MapRegion(IDTRegion.PhysicalBase, IDTRegion.RegionSize, true); + LogMan::Throw::A(IDTRegion.MappedLocation != (void*)-1ULL, "Couldn't Allocate"); + + // Map this memory to the guest VM + kvm_userspace_memory_region Region = { + .slot = MemorySlot++, + .flags = 0, + .guest_phys_addr = IDTRegion.PhysicalBase, + .memory_size = IDTRegion.RegionSize, + .userspace_addr = (uint64_t)IDTRegion.MappedLocation, + }; + + if (ioctl(VM_FD, KVM_SET_USER_MEMORY_REGION, &Region) == -1) { + printf("Couldn't set guestPD region\n"); + return false; + } + + union GateDesc { + struct CallGateType { + uint16_t TargetLow; + uint16_t TargetSelector; + unsigned Unused_0 : 8; + unsigned Type : 4; + unsigned MustBeZero : 1; + unsigned DPL : 2; + unsigned P : 1; + uint16_t TargetMid; + uint32_t TargetHigh; + uint32_t Pad; + } Call; + + struct InterruptTrapType { + uint16_t TargetLow; + uint16_t TargetSelector; + unsigned IST : 2; + unsigned Unused_0 : 6; + unsigned Type : 4; + unsigned MustBeZero : 1; + unsigned DPL : 2; + unsigned P : 1; + uint16_t TargetMid; + uint32_t TargetHigh; + uint32_t Pad; + } IntTrap; + } __attribute__((packed)); + + GateDesc Test; + + // Types 0x04-0x07 are illegal in 64bit mode + uint8_t TypeCall = 0x0C; + uint8_t TypeIDT = 0x0E; + uint8_t TypeTrap = 0x0F; + Test.IntTrap.TargetLow = 0; + Test.IntTrap.TargetSelector = 0; + Test.IntTrap.P = 1; // If P == 0 then `Segment not present` fault occurs + Test.IntTrap.IST = 0; // Must be zero otherwise we need a TSS register set up + Test.IntTrap.Type = TypeTrap; + Test.IntTrap.TargetMid = 0; + Test.IntTrap.TargetHigh = 0; + + unsigned Entries = IDTRegion.RegionSize / sizeof(GateDesc); + printf("%d IDT entries\n", Entries); + GateDesc *GuestEntries = (GateDesc*)IDTRegion.MappedLocation; + + // Section 8.2 Vectors of the AMD64 system programmer's manual + // Claims that the first 32 vectors are reserved for predefined exception and interrupt conditions + // The IDT can fit 256 interrupts + + //Back to IDT + for (unsigned i = 0; i < Entries; ++i) { + GuestEntries[i] = Test; + } + } + + // Set up our initial APIC region + { + APICRegion.MappedLocation = Mapper.MapRegion(APICRegion.PhysicalBase, APICRegion.RegionSize, true); + LogMan::Throw::A(APICRegion.MappedLocation != (void*)-1ULL, "Couldn't Allocate"); + + // Map this memory to the guest VM + kvm_userspace_memory_region Region = { + .slot = MemorySlot++, + .flags = 0, + .guest_phys_addr = APICRegion.PhysicalBase, + .memory_size = APICRegion.RegionSize, + .userspace_addr = (uint64_t)APICRegion.MappedLocation, + }; + + if (ioctl(VM_FD, KVM_SET_USER_MEMORY_REGION, &Region) == -1) { + printf("Couldn't set guestPD region\n"); + return false; + } + + + uint64_t *APICRegisters = (uint64_t*)APICRegion.MappedLocation; + for (unsigned i = 0; i < 0x54; ++i) { + APICRegisters[i] = ~0ULL; + } } return true; } bool KVMInstance::SetupCPUInLongMode() { + kvm_sregs SpecialRegisters = { .cs = { .base = 0, // Ignored in 64bit mode @@ -232,31 +580,73 @@ namespace SU::VM { .db = 0, // Ignored in 64bit mode .dpl = 0, // Data-pivilege-level Still used in 64bit mode to allow accesses .l = 1, // 1 for 64bit execution mode. 0 if you want 32bit compatibility + .present = 1, }, + .ds = { + .present = 1, + }, + .es = { + .present = 1, + }, + .fs = { + .present = 1, + }, + .gs = { + .present = 1, + }, + .ss = { + .present = 1, + }, + .cr0 = (1U << 0) | // Protected Mode enable. Required for 64bit mode + (0U << 2) | // EM bit. 0 means State x87 FPU is present + (0U << 3) | // TS bit. If set causes a device not available exception (1U << 31) | // (PG) Enable paging bit. Required for 64bit mode 0, - .cr3 = PML4.GetVirtualAddress(), // PDBR in PAE mode. Required for 64bit mode - .cr4 = (1 << 5) | // PAE. Required for 64bit mode + .cr3 = PML4.GetPhysicalAddress(), // PDBR in PAE mode. Required for 64bit mode + .cr4 = (1U << 5) | // PAE. Required for 64bit mode + (1U << 9) | // OSFXSR. Required to enable SSE +// (1U << 18) | // XXX: OSXSave. Required to enable AVX. Requires setting the guest CPUID to say it supports xsave 0, - .efer = (1 << 10) | // Long Mode Active - (1 << 8) | // Long Mode Enable + .efer = (1U << 10) | // (LMA) Long Mode Active. + (1U << 8) | // (LME) Long Mode Enable 0, - .gdt = { // Is GDT actually necessary? + .gdt = { + .base = GDTRegion.VirtualBase, + .limit = (uint16_t)GDTRegion.RegionSize, + }, + .idt = { + .base = IDTRegion.VirtualBase, + .limit = (uint16_t)IDTRegion.RegionSize, + }, + .apic_base = (1 << 11) | // (AE) APIC Enable +// (APICRegion.PhysicalBase >> 12 << 32), // ABA, APIC Base Address. ABA is zext by 12 bits to form a 52bit physical address + 0, + // Do we need the LDT(Local Descriptor Table) and TR (Task Register)? + .tr = { .base = 0, .limit = 0, - } - }; + }, + + .ldt = { + .base = 0, + .limit = 0, + }, + }; kvm_regs Registers = { - .rip = ~0ULL, // Make sure the user sets the RIP - .rflags = 0x2, // 0x2 is the default state of rflags + .rip = ~0ULL, + .rsp = 0xFF0, + .rflags = 0x2 | // 0x2 is the default state of rflags + (1 << 9) | // By default enable hardware interrupts + (1 << 19), // By default enable virtual hardware interrupts }; if (ioctl(VCPU_FD, KVM_SET_SREGS, &SpecialRegisters) == -1) { fprintf(stderr, "Couldn't set kvm_sregs\n"); return false; } + if (ioctl(VCPU_FD, KVM_SET_REGS, &Registers) == -1) { fprintf(stderr, "Couldn't set kvm_regs\n"); return false; @@ -311,6 +701,7 @@ namespace SU::VM { // Map in the kvm_run structure that is mapped to VCPU_FD at offset 0 VCPUMemory = reinterpret_cast(mmap(nullptr, VCPUSize, PROT_READ | PROT_WRITE, MAP_SHARED, VCPU_FD, 0)); + LogMan::Throw::A(VCPUMemory != (void*)-1ULL, "Couldn't Allocate"); if (!SetupMemory()) { fprintf(stderr, "Couldn't set up the CPU's memory\n"); @@ -322,6 +713,15 @@ namespace SU::VM { goto err; } + // Set a default TSS + // AMD SVM literally doesn't care but Intel VMX mandates it + // Needs to fit under 4GB and also have a minimum of three pages + Result = ioctl(VM_FD, KVM_SET_TSS_ADDR, -1U - (4096 * 3)); + if (Result == -1) { + fprintf(stderr, "Couldn't set TSS ADDR\n"); + goto err; + } + return true; err: if (VCPU_FD != -1) { @@ -378,8 +778,141 @@ err: fprintf(stderr, "KVM_RUN failed\n"); return false; } + kvm_regs Registers; + ioctl(VCPU_FD, KVM_GET_REGS, &Registers); + printf("RIP is now: 0x%llx\n", Registers.rip); + return true; } + + VMInstance::RegisterState KVMInstance::GetRegisterState() { + kvm_regs Registers; + if (ioctl(VCPU_FD, KVM_GET_REGS, &Registers) == -1) { + fprintf(stderr, "Couldn't get KVM register state\n"); + return {}; + } + VMInstance::RegisterState State; + // This map 1:1 + memcpy(&State, &Registers, sizeof(kvm_regs)); + return State; + } + + VMInstance::SpecialRegisterState KVMInstance::GetSpecialRegisterState() { + kvm_sregs Registers; + if (ioctl(VCPU_FD, KVM_GET_SREGS, &Registers) == -1) { + fprintf(stderr, "Couldn't get KVM special register state\n"); + return {}; + } + VMInstance::SpecialRegisterState State; + State.fs.base = Registers.fs.base; + State.gs.base = Registers.gs.base; + return State; + } + + void KVMInstance::SetRegisterState(VMInstance::RegisterState &State) { + kvm_regs Registers; + // This map 1:1 + memcpy(&Registers, &State, sizeof(kvm_regs)); + + if (ioctl(VCPU_FD, KVM_SET_REGS, &Registers) == -1) { + fprintf(stderr, "Couldn't set kvm_regs\n"); + return; + } + } + + void KVMInstance::SetSpecialRegisterState(VMInstance::SpecialRegisterState &State) { + kvm_sregs Registers; + + // First read the state so we don't destroy a bunch of data + if (ioctl(VCPU_FD, KVM_GET_SREGS, &Registers) == -1) { + fprintf(stderr, "Couldn't get KVM special register state\n"); + return; + } + Registers.fs.base = State.fs.base; + Registers.gs.base = State.gs.base; + + if (ioctl(VCPU_FD, KVM_SET_SREGS, &Registers) == -1) { + fprintf(stderr, "Couldn't set kvm_sregs\n"); + return; + } + } + + void KVMInstance::Debug() { + kvm_sregs Registers; + if (ioctl(VCPU_FD, KVM_GET_SREGS, &Registers) == -1) { + fprintf(stderr, "Couldn't get KVM special register state\n"); + return; + } + + auto PrintDTable = [](const char *Name, kvm_dtable &Table) { + LogMan::Msg::D("%s 0x%016llx 0x%04x", Name, Table.base, Table.limit); + }; + + auto PrintSegment = [](const char *Name, kvm_segment &Segment) { + LogMan::Msg::D("%s 0x%016llx 0x%08x 0x%04x 0x%02x %d %d %d %d %d %d %d", + Name, Segment.base, Segment.limit, Segment.selector, Segment.type, + Segment.present, Segment.dpl, Segment.db, Segment.s, Segment.l, Segment.g, Segment.avl); + }; + + SU::VM::VMInstance::RegisterState State; + State = GetRegisterState(); + LogMan::Msg::D("================"); + LogMan::Msg::D(" RAX RBX RCX RDX"); + LogMan::Msg::D("%016lx %016lx %016lx %016lx", State.rax, State.rbx, State.rcx, State.rdx); + LogMan::Msg::D(" RSI RDI RSP RBP"); + LogMan::Msg::D("%016lx %016lx %016lx %016lx", State.rsi, State.rdi, State.rsp, State.rbp); + LogMan::Msg::D(" R8 R9 R10 R11"); + LogMan::Msg::D("%016lx %016lx %016lx %016lx", State.r8, State.r9, State.r10, State.r11); + LogMan::Msg::D(" R12 R13 R14 R15"); + LogMan::Msg::D("%016lx %016lx %016lx %016lx", State.r12, State.r13, State.r14, State.r15); + LogMan::Msg::D(" RIP RFLAGS"); + LogMan::Msg::D("%016lx %016lx", State.rip, State.rflags); + + LogMan::Msg::D("================"); + LogMan::Msg::D("cr0: cr2: cr3:"); + LogMan::Msg::D("%016llx %016llx %016llx", Registers.cr0, Registers.cr2, Registers.cr3); + LogMan::Msg::D("cr4: cr6:"); + LogMan::Msg::D("%016llx %016llx", Registers.cr4, Registers.cr8); + + LogMan::Msg::D("================"); + LogMan::Msg::D("Reg Base Limit Select Type p dpl db s l g avl"); + + PrintSegment("CS ", Registers.cs); + PrintSegment("DS ", Registers.ds); + PrintSegment("ES ", Registers.es); + PrintSegment("FS ", Registers.fs); + PrintSegment("GS ", Registers.gs); + PrintSegment("SS ", Registers.ss); + + PrintSegment("TR ", Registers.tr); + PrintSegment("LDT", Registers.ldt); + + LogMan::Msg::D("================"); + PrintDTable("GDT", Registers.gdt); + PrintDTable("IDT", Registers.idt); + + LogMan::Msg::D("================"); + LogMan::Msg::D("EFER: 0x%016llx APIC: 0x%016llx", Registers.efer, Registers.apic_base); + LogMan::Msg::D("================"); + LogMan::Msg::D("Interrupt Bitmap"); + for (int i = 0; i < (KVM_NR_INTERRUPTS + 63) / 64; ++i) { + LogMan::Msg::D("Interrupt: %d: 0x%016llx", Registers.interrupt_bitmap[i]); + } + + void *PhysBase = VM_Addr; + uint8_t *GuestData = reinterpret_cast(PhysBase); + + uint64_t Offset = State.rip - 0x1000'0000ULL; + uint64_t Size = 16; + LogMan::Msg::D("Mem: [%016llx, %016llx)", Offset, Offset + Size); + for (uint64_t i = 0; i < 16 * 3; i += 16) { + LogMan::Msg::D("%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", + GuestData[Offset + i + 0], GuestData[Offset + i + 1], GuestData[Offset + i + 2], GuestData[Offset + i + 3], + GuestData[Offset + i + 4], GuestData[Offset + i + 5], GuestData[Offset + i + 6], GuestData[Offset + i + 7], + GuestData[Offset + i + 8], GuestData[Offset + i + 9], GuestData[Offset + i + 10], GuestData[Offset + i + 11], + GuestData[Offset + i + 12], GuestData[Offset + i + 13], GuestData[Offset + i + 14], GuestData[Offset + i + 15]); + } + } } VMInstance* VMInstance::Create() { diff --git a/VM.h b/VM.h index 1231fc6ba..835a84166 100644 --- a/VM.h +++ b/VM.h @@ -4,6 +4,18 @@ namespace SU::VM { class VMInstance { public: + struct RegisterState { + uint64_t rax, rbx, rcx, rdx, rsi, rdi, rsp, rbp; + uint64_t r8, r9, r10, r11, r12, r13, r14, r15; + uint64_t rip, rflags; + }; + + struct SpecialRegisterState { + struct { + uint64_t base; + } fs, gs; + }; + static VMInstance* Create(); virtual ~VMInstance() {} @@ -15,5 +27,14 @@ namespace SU::VM { virtual bool Run() = 0; virtual bool Initialize() = 0; virtual int ExitReason() = 0; + virtual uint64_t GetFailEntryReason() = 0; + virtual RegisterState GetRegisterState() = 0; + virtual SpecialRegisterState GetSpecialRegisterState() = 0; + + virtual void SetRegisterState(RegisterState &State) = 0; + virtual void SetSpecialRegisterState(SpecialRegisterState &State) = 0; + + virtual void Debug() = 0; + }; }