From 5efe93fac9eee24d72a150c66907cb14321da41f Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Sat, 7 Sep 2019 19:23:03 -0700 Subject: [PATCH] Some changes --- ELFLoader.cpp | 111 +++++++++++++++++---------------- ELFLoader.h | 19 ++---- LogManager.cpp | 14 ++--- LogManager.h | 5 +- VM.cpp | 166 +++++++++++++++++++++++++++++++++++++++++-------- 5 files changed, 214 insertions(+), 101 deletions(-) diff --git a/ELFLoader.cpp b/ELFLoader.cpp index d51783c64..3f600dcad 100644 --- a/ELFLoader.cpp +++ b/ELFLoader.cpp @@ -26,7 +26,6 @@ ELFContainer::ELFContainer(std::string const &Filename) { memcpy(&Header, reinterpret_cast(&RawFile.at(0)), sizeof(Elf64_Ehdr)); - LogMan::Throw::A(Header.e_type == ET_EXEC, "ELF wasn't EXEC"); LogMan::Throw::A(Header.e_phentsize == 56, "PH Entry size wasn't 56"); LogMan::Throw::A(Header.e_shentsize == 64, "PH Entry size wasn't 64"); @@ -39,11 +38,11 @@ ELFContainer::ELFContainer(std::string const &Filename) { reinterpret_cast(&RawFile.at(Header.e_phoff)); for (uint32_t i = 0; i < Header.e_shnum; ++i) { - memcpy(&SectionHeaders.at(i), &RawShdrs[i], Header.e_shentsize); + SectionHeaders[i] = &RawShdrs[i]; } for (uint32_t i = 0; i < Header.e_phnum; ++i) { - memcpy(&ProgramHeaders.at(i), &RawPhdrs[i], Header.e_phentsize); + ProgramHeaders[i] = &RawPhdrs[i]; } CalculateMemoryLayouts(); @@ -54,10 +53,11 @@ ELFContainer::ELFContainer(std::string const &Filename) { //PrintSectionHeaders(); //PrintProgramHeaders(); //PrintSymbolTable(); + + LogMan::Throw::A(IsStatic, "Can only handle static programs"); } ELFContainer::~ELFContainer() { - SymbolMapByAddressRange.clear(); SymbolMapByAddress.clear(); SymbolMap.clear(); Symbols.clear(); @@ -68,14 +68,14 @@ ELFContainer::~ELFContainer() { void ELFContainer::WriteLoadableSections(MemoryWriter Writer) { for (uint32_t i = 0; i < ProgramHeaders.size(); ++i) { - Elf64_Phdr const &hdr = ProgramHeaders.at(i); - if (hdr.p_type != PT_LOAD) + Elf64_Phdr const *hdr = ProgramHeaders.at(i); + if (hdr->p_type != PT_LOAD) continue; - Writer(&RawFile.at(hdr.p_offset), hdr.p_paddr, hdr.p_filesz); + Writer(&RawFile.at(hdr->p_offset), hdr->p_paddr, hdr->p_filesz); } } -ELFSymbol const *ELFContainer::GetSymbol(std::string const &Name) { +ELFSymbol const *ELFContainer::GetSymbol(char const *Name) { auto Sym = SymbolMap.find(Name); if (Sym == SymbolMap.end()) return nullptr; @@ -88,9 +88,15 @@ ELFSymbol const *ELFContainer::GetSymbol(uint64_t Address) { return Sym->second; } ELFSymbol const *ELFContainer::GetSymbolInRange(RangeType Address) { - auto Sym = SymbolMapByAddress.lower_bound(Address.first); + auto Sym = SymbolMapByAddress.upper_bound(Address.first); + if (Sym != SymbolMapByAddress.begin()) + --Sym; if (Sym == SymbolMapByAddress.end()) return nullptr; + + if ((Sym->second->Address + Sym->second->Size) < Address.first) + return nullptr; + return Sym->second; } @@ -99,9 +105,13 @@ void ELFContainer::CalculateMemoryLayouts() { uint64_t MaxPhysAddr = 0; uint64_t PhysMemSize = 0; for (uint32_t i = 0; i < ProgramHeaders.size(); ++i) { - Elf64_Phdr const &hdr = ProgramHeaders.at(i); - MinPhysAddr = std::min(MinPhysAddr, hdr.p_paddr); - MaxPhysAddr = std::max(MaxPhysAddr, hdr.p_paddr + hdr.p_memsz); + Elf64_Phdr const *hdr = ProgramHeaders.at(i); + MinPhysAddr = std::min(MinPhysAddr, hdr->p_paddr); + MaxPhysAddr = std::max(MaxPhysAddr, hdr->p_paddr + hdr->p_memsz); + + if (hdr->p_type == PT_INTERP) { + IsStatic = false; + } } PhysMemSize = MaxPhysAddr - MinPhysAddr; @@ -120,7 +130,7 @@ void ELFContainer::CalculateSymbols() { Elf64_Shdr const *StringTableHeader{nullptr}; char const *StrTab{nullptr}; for (uint32_t i = 0; i < SectionHeaders.size(); ++i) { - Elf64_Shdr const *hdr = &SectionHeaders.at(i); + Elf64_Shdr const *hdr = SectionHeaders.at(i); if (hdr->sh_type == SHT_SYMTAB) { SymTabHeader = hdr; break; @@ -136,7 +146,7 @@ void ELFContainer::CalculateSymbols() { LogMan::Throw::A(SymTabHeader->sh_entsize == sizeof(Elf64_Sym), "Entry size doesn't match symbol entry"); - StringTableHeader = &SectionHeaders.at(SymTabHeader->sh_link); + StringTableHeader = SectionHeaders.at(SymTabHeader->sh_link); StrTab = &RawFile.at(StringTableHeader->sh_offset); uint64_t NumSymbols = SymTabHeader->sh_size / SymTabHeader->sh_entsize; @@ -146,19 +156,16 @@ void ELFContainer::CalculateSymbols() { Elf64_Sym const *Symbol = reinterpret_cast(&RawFile.at(offset)); if (ELF64_ST_VISIBILITY(Symbol->st_other) != STV_HIDDEN) { - ELFSymbol DefinedSymbol{}; - DefinedSymbol.FileOffset = offset; - DefinedSymbol.Address = Symbol->st_value; - DefinedSymbol.Size = Symbol->st_size; - DefinedSymbol.Type = ELF64_ST_TYPE(Symbol->st_info); - DefinedSymbol.Bind = ELF64_ST_BIND(Symbol->st_info); - DefinedSymbol.Name = &StrTab[Symbol->st_name]; + ELFSymbol *DefinedSymbol = &Symbols.at(i); + DefinedSymbol->FileOffset = offset; + DefinedSymbol->Address = Symbol->st_value; + DefinedSymbol->Size = Symbol->st_size; + DefinedSymbol->Type = ELF64_ST_TYPE(Symbol->st_info); + DefinedSymbol->Bind = ELF64_ST_BIND(Symbol->st_info); + DefinedSymbol->Name = &StrTab[Symbol->st_name]; - Symbols.emplace_back(DefinedSymbol); - auto Sym = &Symbols.back(); - SymbolMap[DefinedSymbol.Name] = Sym; - SymbolMapByAddress[DefinedSymbol.Address] = Sym; - SymbolMapByAddressRange[std::make_pair(DefinedSymbol.Address, DefinedSymbol.Address + Symbol->st_size)] = Sym; + SymbolMap[DefinedSymbol->Name] = DefinedSymbol; + SymbolMapByAddress[DefinedSymbol->Address] = DefinedSymbol; } } } @@ -182,39 +189,39 @@ void ELFContainer::PrintHeader() const { void ELFContainer::PrintSectionHeaders() const { LogMan::Throw::A(Header.e_shstrndx < SectionHeaders.size(), "String index section is wrong index!"); - Elf64_Shdr const &StrHeader = SectionHeaders.at(Header.e_shstrndx); - char const *SHStrings = &RawFile.at(StrHeader.sh_offset); + Elf64_Shdr const *StrHeader = SectionHeaders.at(Header.e_shstrndx); + char const *SHStrings = &RawFile.at(StrHeader->sh_offset); for (uint32_t i = 0; i < SectionHeaders.size(); ++i) { - Elf64_Shdr const &hdr = SectionHeaders.at(i); + Elf64_Shdr const *hdr = SectionHeaders.at(i); LogMan::Msg::I("Index: %d", i); - LogMan::Msg::I("Name: %s", &SHStrings[hdr.sh_name]); - LogMan::Msg::I("Type: %d", hdr.sh_type); - LogMan::Msg::I("Flags: %d", hdr.sh_flags); - LogMan::Msg::I("Addr: 0x%lx", hdr.sh_addr); - LogMan::Msg::I("Offset: 0x%lx", hdr.sh_offset); - LogMan::Msg::I("Size: %d", hdr.sh_size); - LogMan::Msg::I("Link: %d", hdr.sh_link); - LogMan::Msg::I("Info: %d", hdr.sh_info); - LogMan::Msg::I("AddrAlign: %d", hdr.sh_addralign); - LogMan::Msg::I("Entry Size: %d", hdr.sh_entsize); + LogMan::Msg::I("Name: %s", &SHStrings[hdr->sh_name]); + LogMan::Msg::I("Type: %d", hdr->sh_type); + LogMan::Msg::I("Flags: %d", hdr->sh_flags); + LogMan::Msg::I("Addr: 0x%lx", hdr->sh_addr); + LogMan::Msg::I("Offset: 0x%lx", hdr->sh_offset); + LogMan::Msg::I("Size: %d", hdr->sh_size); + LogMan::Msg::I("Link: %d", hdr->sh_link); + LogMan::Msg::I("Info: %d", hdr->sh_info); + LogMan::Msg::I("AddrAlign: %d", hdr->sh_addralign); + LogMan::Msg::I("Entry Size: %d", hdr->sh_entsize); } } void ELFContainer::PrintProgramHeaders() const { LogMan::Throw::A(Header.e_shstrndx < SectionHeaders.size(), "String index section is wrong index!"); - Elf64_Shdr const &StrHeader = SectionHeaders.at(Header.e_shstrndx); - char const *SHStrings = &RawFile.at(StrHeader.sh_offset); + Elf64_Shdr const *StrHeader = SectionHeaders.at(Header.e_shstrndx); + char const *SHStrings = &RawFile.at(StrHeader->sh_offset); for (uint32_t i = 0; i < ProgramHeaders.size(); ++i) { - Elf64_Phdr const &hdr = ProgramHeaders.at(i); - LogMan::Msg::I("Type: %d", hdr.p_type); - LogMan::Msg::I("Flags: %d", hdr.p_flags); - LogMan::Msg::I("Offset: %d", hdr.p_offset); - LogMan::Msg::I("VAddr: 0x%lx", hdr.p_vaddr); - LogMan::Msg::I("PAddr: 0x%lx", hdr.p_paddr); - LogMan::Msg::I("FSize: %d", hdr.p_filesz); - LogMan::Msg::I("MemSize: %d", hdr.p_memsz); - LogMan::Msg::I("Align: %d", hdr.p_align); + Elf64_Phdr const *hdr = ProgramHeaders.at(i); + LogMan::Msg::I("Type: %d", hdr->p_type); + LogMan::Msg::I("Flags: %d", hdr->p_flags); + LogMan::Msg::I("Offset: %d", hdr->p_offset); + LogMan::Msg::I("VAddr: 0x%lx", hdr->p_vaddr); + LogMan::Msg::I("PAddr: 0x%lx", hdr->p_paddr); + LogMan::Msg::I("FSize: %d", hdr->p_filesz); + LogMan::Msg::I("MemSize: %d", hdr->p_memsz); + LogMan::Msg::I("Align: %d", hdr->p_align); } } @@ -224,7 +231,7 @@ void ELFContainer::PrintSymbolTable() const { Elf64_Shdr const *StringTableHeader{nullptr}; char const *StrTab{nullptr}; for (uint32_t i = 0; i < SectionHeaders.size(); ++i) { - Elf64_Shdr const *hdr = &SectionHeaders.at(i); + Elf64_Shdr const *hdr = SectionHeaders.at(i); if (hdr->sh_type == SHT_SYMTAB) { SymTabHeader = hdr; break; @@ -240,7 +247,7 @@ void ELFContainer::PrintSymbolTable() const { LogMan::Throw::A(SymTabHeader->sh_entsize == sizeof(Elf64_Sym), "Entry size doesn't match symbol entry"); - StringTableHeader = &SectionHeaders.at(SymTabHeader->sh_link); + StringTableHeader = SectionHeaders.at(SymTabHeader->sh_link); StrTab = &RawFile.at(StringTableHeader->sh_offset); uint64_t NumSymbols = SymTabHeader->sh_size / SymTabHeader->sh_entsize; diff --git a/ELFLoader.h b/ELFLoader.h index b7b14c567..530cca9ad 100644 --- a/ELFLoader.h +++ b/ELFLoader.h @@ -15,7 +15,7 @@ struct ELFSymbol { uint64_t Size; uint8_t Type; uint8_t Bind; - std::string Name; + char const *Name; }; class ELFContainer { @@ -36,7 +36,7 @@ public: using MemoryWriter = std::function; void WriteLoadableSections(MemoryWriter Writer); - ELFSymbol const *GetSymbol(std::string const &Name); + ELFSymbol const *GetSymbol(char const *Name); ELFSymbol const *GetSymbol(uint64_t Address); using RangeType = std::pair; @@ -54,23 +54,16 @@ private: std::vector RawFile; Elf64_Ehdr Header; - std::vector SectionHeaders; - std::vector ProgramHeaders; + std::vector SectionHeaders; + std::vector ProgramHeaders; std::vector Symbols; - std::unordered_map SymbolMap; + std::unordered_map SymbolMap; std::map SymbolMapByAddress; - struct RangeCompare { - bool operator()(const RangeType& a, const RangeType& b) const { - return a.first == b.first || - (a.first <= b.first && a.second >= b.second); - } - }; - std::map SymbolMapByAddressRange; - uint64_t MinPhysicalMemoryLocation{0}; uint64_t MaxPhysicalMemoryLocation{0}; uint64_t PhysicalMemorySize{0}; + bool IsStatic{true}; }; } // namespace ELFLoader diff --git a/LogManager.cpp b/LogManager.cpp index 0915132d1..6abbab9d7 100644 --- a/LogManager.cpp +++ b/LogManager.cpp @@ -10,10 +10,9 @@ void InstallHandler(ThrowHandler Handler) { Handlers.emplace_back(Handler); } [[noreturn]] void M(const char *fmt, va_list args) { char Buffer[1024]; - vsprintf(Buffer, fmt, args); - std::string Message(Buffer); - for (auto Handler : Handlers) { - Handler(Message); + vsnprintf(Buffer, 1024, fmt, args); + for (auto &Handler : Handlers) { + Handler(Buffer); } __builtin_trap(); @@ -26,10 +25,9 @@ void InstallHandler(MsgHandler Handler) { Handlers.emplace_back(Handler); } void M(DebugLevels Level, const char *fmt, va_list args) { char Buffer[1024]; - vsprintf(Buffer, fmt, args); - std::string Message(Buffer); - for (auto Handler : Handlers) { - Handler(Level, Message); + vsnprintf(Buffer, 1024, fmt, args); + for (auto &Handler : Handlers) { + Handler(Level, Buffer); } } diff --git a/LogManager.h b/LogManager.h index a8e21e4ac..09563aba5 100644 --- a/LogManager.h +++ b/LogManager.h @@ -17,7 +17,7 @@ enum DebugLevels { constexpr DebugLevels MSG_LEVEL = INFO; namespace Throw { -using ThrowHandler = std::function; +using ThrowHandler = void(*)(char const *Message); void InstallHandler(ThrowHandler Handler); [[noreturn]] void M(const char *fmt, va_list args); @@ -38,8 +38,7 @@ static inline void A(bool Value, const char *fmt, ...) { } // namespace Throw namespace Msg { -using MsgHandler = - std::function; +using MsgHandler = void(*)(DebugLevels Level, char const *Message); void InstallHandler(MsgHandler Handler); void M(DebugLevels Level, const char *fmt, va_list args); diff --git a/VM.cpp b/VM.cpp index 10d69a044..39bf65daa 100644 --- a/VM.cpp +++ b/VM.cpp @@ -25,6 +25,9 @@ public: void *MapRegionFlags(size_t Offset, size_t Size, uint32_t flags, bool Fixed); void *MapRegion(size_t Offset, size_t Size, bool Fixed); + void *MapRegionMirror(size_t BasePtr, size_t Offset, size_t Size, uint32_t flags, bool Fixed); + void *FakeMapRegion(size_t Offset); + private: int SHMfd; size_t SHMSize; @@ -35,6 +38,7 @@ void *Memmap::MapRegion(size_t Offset, size_t Size, bool Fixed) { } void *Memmap::MapRegionFlags(size_t Offset, size_t Size, uint32_t flags, bool Fixed) { + return mmap(0, Size, flags, MAP_ANONYMOUS | MAP_SHARED, -1, 0); uintptr_t PtrOffset = reinterpret_cast(Base) + Offset; void *Ptr = mmap(reinterpret_cast(PtrOffset), Size, flags, MAP_SHARED | (Fixed ? MAP_FIXED : 0), SHMfd, Offset); @@ -43,10 +47,30 @@ void *Memmap::MapRegionFlags(size_t Offset, size_t Size, uint32_t flags, bool Fi LogMan::Msg::A("Failed to map memory region [0x%lx, 0x%lx) - %d", Offset, Size, Fixed); return nullptr; } + LogMan::Msg::D("Mapped [0x%0lx, 0x%0lx) to 0x%0lx on host", Offset, Offset + Size, Ptr); return Ptr; } +void *Memmap::MapRegionMirror(size_t BasePtr, size_t Offset, size_t Size, uint32_t flags, bool Fixed) { + uintptr_t PtrOffset = reinterpret_cast(Base) + BasePtr; + 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; + } + LogMan::Msg::D("Mapped [0x%0lx, 0x%0lx) to 0x%0lx on host", Offset, Offset + Size, Ptr); + + return Ptr; +} + +void *Memmap::FakeMapRegion(size_t Offset) { + uintptr_t PtrOffset = reinterpret_cast(Base) + Offset; + return reinterpret_cast(PtrOffset); +} + 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); @@ -202,6 +226,7 @@ namespace SU::VM { int VM_FD{-1}; int VCPU_FD{-1}; + constexpr static uint64_t VirtualBase = 0x0000'0001'0000'0000; // This is the size of the user's memory region uint64_t PhysicalUserMemorySize; @@ -224,8 +249,12 @@ namespace SU::VM { MemoryRegion APICRegion { .RegionSize = 4096, }; + MemoryRegion LongQuirkRegion { + .RegionSize = 4096, + }; bool SetupCPUInLongMode(); bool SetupMemory(); + bool HandleKVMLongModeQuirk(); Memmap Mapper; }; @@ -251,6 +280,7 @@ namespace SU::VM { // [IDTRegion, IDTRegion + Size) // [GDTRegion, GDTRegion + Size) // [APICRegion, APICRegion + Size) + // [LongQuirkRegion, LongQuirkRegion + Size) // [PML4, PML4 + Size) // // ============================ @@ -258,7 +288,6 @@ namespace SU::VM { // ============================ // 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; @@ -275,6 +304,10 @@ namespace SU::VM { APICRegion.VirtualBase = VirtualBase + APICRegion.PhysicalBase; // Mapped to the same place PhysicalMemorySize += APICRegion.RegionSize; + LongQuirkRegion.PhysicalBase = PhysicalMemorySize; + LongQuirkRegion.VirtualBase = VirtualBase + LongQuirkRegion.PhysicalBase; // Mapped to the same place + PhysicalMemorySize += LongQuirkRegion.RegionSize; + // Physical and virtual map directly atm PML4.SetPhysicalAddress(PhysicalMemorySize); PML4.SetVirtualAddress(VirtualBase + PhysicalMemorySize); @@ -284,7 +317,7 @@ namespace SU::VM { // Align up just to make sure we are page aligned PhysicalMemorySize = AlignUp(PhysicalMemorySize, 4096); - Mapper.AllocateSHMRegion(1ULL << 36); + Mapper.AllocateSHMRegion(1ULL << 37); // Physical memory is just [0, PhysicalMemorySize) // Just map physical zero to virtual zero @@ -560,18 +593,46 @@ namespace SU::VM { return false; } - uint64_t *APICRegisters = (uint64_t*)APICRegion.MappedLocation; for (unsigned i = 0; i < 0x54; ++i) { APICRegisters[i] = ~0ULL; } } + // Set up our memory region for working around a KVM quirk + { + LongQuirkRegion.MappedLocation = Mapper.MapRegion(LongQuirkRegion.PhysicalBase, LongQuirkRegion.RegionSize, true); + LogMan::Throw::A(LongQuirkRegion.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 = LongQuirkRegion.PhysicalBase, + .memory_size = LongQuirkRegion.RegionSize, + .userspace_addr = (uint64_t)LongQuirkRegion.MappedLocation, + }; + + if (ioctl(VM_FD, KVM_SET_USER_MEMORY_REGION, &Region) == -1) { + printf("Couldn't set guestPD region\n"); + return false; + } + + uint8_t *LongQuirkMemory = (uint8_t*)LongQuirkRegion.MappedLocation; + uint8_t Code[] = { + 0xEB, + 0x00, // JMP +0 + 0xF4, + 0xF4, + 0xF4, // HLT + }; + memcpy(LongQuirkMemory, Code, sizeof(Code)); + } + return true; } bool KVMInstance::SetupCPUInLongMode() { - kvm_sregs SpecialRegisters = { .cs = { .base = 0, // Ignored in 64bit mode @@ -599,14 +660,22 @@ namespace SU::VM { }, .cr0 = (1U << 0) | // Protected Mode enable. Required for 64bit mode +// (1U << 1) | // Monitor coprocessor (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.GetPhysicalAddress(), // PDBR in PAE mode. Required for 64bit mode + .cr3 = PML4.GetPhysicalAddress() | // PDBR in PAE mode. Required for 64bit mode +// (1U << 4) | // Page cache disable + 0, .cr4 = (1U << 5) | // PAE. Required for 64bit mode +// (1U << 0) | // (VME) VM86 extensions +// (1U << 1) | // (PVI) Virtual interrupt flags +// (1U << 3) | // (DE) Debug extensions +// (1U << 6) | // (MCE) Machine check enable +// (1U << 7) | // (PGE) Global page enable (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 + // (1U << 18) | // XXX: OSXSave. Required to enable AVX. Requires setting the guest CPUID to say it supports xsave 0, .efer = (1U << 10) | // (LMA) Long Mode Active. (1U << 8) | // (LME) Long Mode Enable @@ -620,10 +689,10 @@ namespace SU::VM { .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 + // (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 = { + .tr = { .base = 0, .limit = 0, }, @@ -632,14 +701,16 @@ namespace SU::VM { .base = 0, .limit = 0, }, - }; + }; kvm_regs Registers = { .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 + (1U << 9) | // By default enable hardware interrupts + (1U << 19) | // By default enable virtual hardware interrupts + (1U << 21) | // CPU ID detection + 0, }; if (ioctl(VCPU_FD, KVM_SET_SREGS, &SpecialRegisters) == -1) { @@ -654,6 +725,60 @@ namespace SU::VM { return true; } + bool KVMInstance::HandleKVMLongModeQuirk() { + uint64_t TestRIP = LongQuirkRegion.VirtualBase + 3; + uint64_t MAXRIP = LongQuirkRegion.VirtualBase + 0x10; + + for (uint32_t tries = 0; tries < 4; ++tries) { + fprintf(stderr, "Trie: %d\n", tries); + kvm_regs Registers = { + .rip = LongQuirkRegion.VirtualBase, + .rsp = 0xFF0, + .rflags = 0x2 | // 0x2 is the default state of rflags + (1U << 9) | // By default enable hardware interrupts + (1U << 19) | // By default enable virtual hardware interrupts + (1U << 21) | // CPU ID detection + 0, + }; + + if (ioctl(VCPU_FD, KVM_SET_REGS, &Registers) == -1) { + fprintf(stderr, "[Quirk] Couldn't set kvm_regs\n"); + return false; + } + + if (!SetStepping(true)) { + fprintf(stderr, "[Quirk] Couldn't set stepping\n"); + return false; + } + + do { + if (!Run()) { + fprintf(stderr, "[Quirk] Couldn't step\n"); + return false; + } + auto Regs = GetRegisterState(); + if (Regs.rip > MAXRIP) { + break; + } + + } while (ExitReason() == 4); + + auto Regs = GetRegisterState(); + if (Regs.rip == TestRIP) { + break; + } + } + + auto Regs = GetRegisterState(); + + if (Regs.rip != TestRIP) { + fprintf(stderr, "[Quirk] Expected RIP: 0x%lx but got 0x%lx\n", TestRIP, Regs.rip); + return false; + } + + return true; + } + bool KVMInstance::Initialize() { int APIVersion; int VCPUSize; @@ -722,6 +847,11 @@ namespace SU::VM { goto err; } + if (!HandleKVMLongModeQuirk()) { + fprintf(stderr, "Couldn't work around KVM long mode enable quirk"); + goto err; + } + return true; err: if (VCPU_FD != -1) { @@ -898,20 +1028,6 @@ err: 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]); - } } }