From 2555473c0f1b583c55922bec0acbe8d69e617591 Mon Sep 17 00:00:00 2001 From: Exverge Date: Sun, 12 Jul 2026 22:48:53 -0400 Subject: [PATCH] [core] hook DeviceMemory to check for irregular mappings --- src/common/host_memory.cpp | 54 ++++++++++++++++++++++---------------- src/common/host_memory.h | 27 +++++++++---------- src/core/device_memory.h | 42 ++++++++++++++++++++++------- 3 files changed, 77 insertions(+), 46 deletions(-) diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp index 5c30d34519..7674235dca 100644 --- a/src/common/host_memory.cpp +++ b/src/common/host_memory.cpp @@ -777,9 +777,9 @@ HostMemory::HostMemory(HostMemory&&) noexcept = default; HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default; -HostMemory::IrregularMapping* HostMemory::GetUnalignedMappingFromVirtual(u64 offset) { +const HostMemory::MisalignedMapping* HostMemory::GetUnalignedMappingFromVirtual(u64 offset) const { auto index = offset >> Core::Memory::YUZU_PAGEBITS; - constexpr auto cmp = [](const u64 a, const IrregularMapping b) { + constexpr auto cmp = [](const u64 a, const MisalignedMapping b) { return a < b.vaddr; }; auto i = unaligned_mappings.upper_bound(index, cmp); @@ -791,45 +791,54 @@ HostMemory::IrregularMapping* HostMemory::GetUnalignedMappingFromVirtual(u64 off return index < i->vaddr + i->size ? &*i : nullptr; } -const HostMemory::IrregularMapping* HostMemory::GetIrregularMappingFromFakePhysical(u64 offset) { +PAddr HostMemory::GetPhysicalAddrFromIrregular(PAddr offset) const { auto index = offset >> Core::Memory::YUZU_PAGEBITS; - constexpr auto cmp = [](const u64 a, const IrregularMapping b) { - return a < b.vaddr; - }; - auto i = irregular_mappings.upper_bound(index, cmp); + auto i = irregular_mappings.left.upper_bound(index); - if (i == irregular_mappings.begin()) - return nullptr; + if (i == irregular_mappings.left.begin()) + return 0; --i; - return index < i->fake_paddr + i->size ? &*i : nullptr; + return index == i->first ? i->second + (offset % Core::Memory::YUZU_PAGESIZE) : 0; +} + +PAddr HostMemory::GetIrregularAddrFromPhysical(PAddr offset) const { + auto index = offset >> Core::Memory::YUZU_PAGEBITS; + auto i = irregular_mappings.right.upper_bound(index); + + if (i == irregular_mappings.right.begin()) + return 0; + --i; + + return index == i->first ? i->second + (offset % Core::Memory::YUZU_PAGESIZE) : 0; } void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms, bool separate_heap) { #if !(defined(__OPENORBIS__) || defined(__managarm__)) // TODO: offset paddr with vaddr to align with page table? + // ASSERT(virtual_offset % HostPageSize == host_offset % HostPageSize) if (virtual_offset % HostPageSize != 0) { if (virtual_offset % 0x1000 != 0) [[unlikely]] { UNREACHABLE_MSG("Attempted to map virtual addresses {:#x}-{:#x} which is unaligned to guest page size", virtual_offset, virtual_offset + length); } if (auto map = GetUnalignedMappingFromVirtual(virtual_offset); map) { - ASSERT(map->fake_paddr == 0); // sanity check auto aligned = AlignUp(virtual_offset, HostPageSize); - // TODO: is fake_paddr right here? - map->fake_paddr = host_offset; - irregular_mappings.insert(*map); + for (size_t i = 0; i < ((aligned - virtual_offset) / Core::Memory::YUZU_PAGESIZE); ++i) { + auto fake = i + (host_offset >> Core::Memory::YUZU_PAGEBITS); + irregular_mappings.insert({fake, i + map->real_paddr}); + } - LOG_WARNING(HW_Memory, "Irregularly mapped virtual addresses {:#x}-{:#x} will not have a valid physical address (fake: {:#x}, real: {:#x})", - virtual_offset, aligned, map->fake_paddr, map->real_paddr); + LOG_WARNING(HW_Memory, "Irregularly mapped virtual addresses {:#x}-{:#x} have an incorrect physical address (fake: {:#x}, real: {:#x})", + virtual_offset, aligned, host_offset, map->real_paddr); length -= map->size; virtual_offset = aligned; } else { auto aligned = AlignDown(virtual_offset, HostPageSize); // TODO: is host_offset right here? - auto* mapping = new IrregularMapping {aligned, host_offset, virtual_offset - aligned}; + auto* mapping = new MisalignedMapping {aligned, host_offset, virtual_offset - aligned}; unaligned_mappings.insert(*mapping); length += mapping->size; @@ -845,20 +854,21 @@ void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length, M } if (auto map = GetUnalignedMappingFromVirtual(virtual_offset + length); map) { - ASSERT(map->fake_paddr == 0); // sanity check TODO: handle multiple misaligned mappings in one page boundary auto aligned = AlignDown(length, HostPageSize); // TODO: is fake_paddr right here? - map->fake_paddr = host_offset + length; - irregular_mappings.insert(*map); + for (size_t i = 0; i < ((length - aligned) / Core::Memory::YUZU_PAGESIZE); ++i) { + auto fake = i + ((host_offset + aligned) >> Core::Memory::YUZU_PAGEBITS); + irregular_mappings.insert({fake, i + map->real_paddr}); + } LOG_WARNING(HW_Memory, "Irregularly mapped virtual addresses {:#x}-{:#x} will not have a valid physical address (fake: {:#x}, real: {:#x})", - virtual_offset + aligned, virtual_offset + length, map->fake_paddr, map->real_paddr); + virtual_offset + aligned, virtual_offset + length, host_offset + aligned, map->real_paddr); length = aligned; } else { auto aligned = AlignUp(length, HostPageSize); // TODO: is host_offset right here? - auto mapping = new IrregularMapping { virtual_offset + length, host_offset + length, aligned - length }; + auto mapping = new MisalignedMapping { virtual_offset + length, host_offset + length, aligned - length }; unaligned_mappings.insert(*mapping); length = aligned; diff --git a/src/common/host_memory.h b/src/common/host_memory.h index 596060a7ed..567507e710 100644 --- a/src/common/host_memory.h +++ b/src/common/host_memory.h @@ -9,6 +9,7 @@ #include #include +#include #include namespace bi = boost::intrusive; @@ -94,24 +95,22 @@ public: } using by_vaddr = bi::set_base_hook>; - using by_fake_paddr = bi::set_base_hook>; - struct IrregularMapping : by_vaddr, by_fake_paddr { + struct MisalignedMapping : by_vaddr { - IrregularMapping(u64 vaddr, u64 paddr, u64 size) : vaddr(vaddr >> Core::Memory::YUZU_PAGEBITS), + MisalignedMapping(u64 vaddr, u64 paddr, u64 size) : vaddr(vaddr >> Core::Memory::YUZU_PAGEBITS), size(size >> Core::Memory::YUZU_PAGEBITS), real_paddr(paddr >> Core::Memory::YUZU_PAGEBITS) {} - u64 vaddr; + VAddr vaddr; u64 size; // Real backing memory linked to this mapping - u64 real_paddr; - // Memory address stored by the page table that mapped this mapping - u64 fake_paddr {0}; + PAddr real_paddr; }; - IrregularMapping* GetUnalignedMappingFromVirtual(u64 offset); - const IrregularMapping* GetIrregularMappingFromFakePhysical(u64 offset); + const MisalignedMapping* GetUnalignedMappingFromVirtual(VAddr offset) const; + PAddr GetPhysicalAddrFromIrregular(PAddr offset) const; + PAddr GetIrregularAddrFromPhysical(PAddr offset) const; private: size_t backing_size{}; @@ -128,17 +127,15 @@ private: // Windows requires it for kernels whom lack proper support for some functions! std::optional> fallback_buffer; - static inline auto cmp_vaddr = [](const IrregularMapping& a, const IrregularMapping& b) { + static inline auto unaligned_cmp = [](const MisalignedMapping& a, const MisalignedMapping& b) { return a.vaddr < b.vaddr; }; - static inline auto cmp_paddr = [](const IrregularMapping& a, const IrregularMapping& b) { - return a.fake_paddr < b.fake_paddr; - }; // Mappings that have mapped more memory than needed due to page-size limitations - bi::set, bi::compare> unaligned_mappings; + bi::set, bi::compare> unaligned_mappings; // Mappings in `unaligned_mappings` that have a fake physical address due to them being mapped again. - bi::set, bi::compare> irregular_mappings; + // Each key represents 1 4KiB page. + boost::bimap irregular_mappings; }; } // namespace Common diff --git a/src/core/device_memory.h b/src/core/device_memory.h index 11bf0e3268..47c85e7007 100644 --- a/src/core/device_memory.h +++ b/src/core/device_memory.h @@ -26,36 +26,60 @@ public: template Common::PhysicalAddress GetPhysicalAddr(const T* ptr) const { - return (reinterpret_cast(ptr) - - reinterpret_cast(buffer.BackingBasePointer())) + - DramMemoryMap::Base; + auto offset = (reinterpret_cast(ptr) - + reinterpret_cast(buffer.BackingBasePointer())) + + DramMemoryMap::Base; + if (auto irregular = buffer.GetIrregularAddrFromPhysical(offset); irregular) { + return irregular; + } + + return offset; } template PAddr GetRawPhysicalAddr(const T* ptr) const { - return static_cast(reinterpret_cast(ptr) - - reinterpret_cast(buffer.BackingBasePointer())); + auto offset = reinterpret_cast(ptr) - + reinterpret_cast(buffer.BackingBasePointer()); + if (auto irregular = buffer.GetIrregularAddrFromPhysical(offset); irregular) { + return irregular; + } + + return offset; } template T* GetPointer(Common::PhysicalAddress addr) { - return reinterpret_cast(buffer.BackingBasePointer() + - (GetInteger(addr) - DramMemoryMap::Base)); + auto offset = (GetInteger(addr) - DramMemoryMap::Base); + if (auto real = buffer.GetPhysicalAddrFromIrregular(offset); real) { + offset = real; + } + + return reinterpret_cast(buffer.BackingBasePointer() + offset); } template const T* GetPointer(Common::PhysicalAddress addr) const { - return reinterpret_cast(buffer.BackingBasePointer() + - (GetInteger(addr) - DramMemoryMap::Base)); + auto offset = (GetInteger(addr) - DramMemoryMap::Base); + if (auto real = buffer.GetPhysicalAddrFromIrregular(offset); real) { + offset = real; + } + + return reinterpret_cast(buffer.BackingBasePointer() + offset); } template T* GetPointerFromRaw(PAddr addr) { + if (auto real = buffer.GetPhysicalAddrFromIrregular(addr); real) { + addr = real; + } return reinterpret_cast(buffer.BackingBasePointer() + addr); } template const T* GetPointerFromRaw(PAddr addr) const { + if (auto real = buffer.GetPhysicalAddrFromIrregular(addr); real) { + addr = real; + } return reinterpret_cast(buffer.BackingBasePointer() + addr); }