Browse Source

[core] hook DeviceMemory to check for irregular mappings

remotes/1785372757367212240/tmp_refs/heads/variable-page-size
Exverge 1 month ago
parent
commit
2555473c0f
No known key found for this signature in database GPG Key ID: DAD399BCC5FB77E4
  1. 54
      src/common/host_memory.cpp
  2. 27
      src/common/host_memory.h
  3. 42
      src/core/device_memory.h

54
src/common/host_memory.cpp

@ -777,9 +777,9 @@ HostMemory::HostMemory(HostMemory&&) noexcept = default;
HostMemory& HostMemory::operator=(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; 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; return a < b.vaddr;
}; };
auto i = unaligned_mappings.upper_bound(index, cmp); 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; 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; 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; --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) { void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms, bool separate_heap) {
#if !(defined(__OPENORBIS__) || defined(__managarm__)) #if !(defined(__OPENORBIS__) || defined(__managarm__))
// TODO: offset paddr with vaddr to align with page table? // 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 % HostPageSize != 0) {
if (virtual_offset % 0x1000 != 0) [[unlikely]] { 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); 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) { if (auto map = GetUnalignedMappingFromVirtual(virtual_offset); map) {
ASSERT(map->fake_paddr == 0); // sanity check
auto aligned = AlignUp(virtual_offset, HostPageSize); 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; length -= map->size;
virtual_offset = aligned; virtual_offset = aligned;
} else { } else {
auto aligned = AlignDown(virtual_offset, HostPageSize); auto aligned = AlignDown(virtual_offset, HostPageSize);
// TODO: is host_offset right here? // 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); unaligned_mappings.insert(*mapping);
length += mapping->size; 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) { 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); auto aligned = AlignDown(length, HostPageSize);
// TODO: is fake_paddr right here? // 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})", 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; length = aligned;
} else { } else {
auto aligned = AlignUp(length, HostPageSize); auto aligned = AlignUp(length, HostPageSize);
// TODO: is host_offset right here? // 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); unaligned_mappings.insert(*mapping);
length = aligned; length = aligned;

27
src/common/host_memory.h

@ -9,6 +9,7 @@
#include <memory> #include <memory>
#include <optional> #include <optional>
#include <boost/bimap.hpp>
#include <boost/intrusive/set.hpp> #include <boost/intrusive/set.hpp>
namespace bi = boost::intrusive; namespace bi = boost::intrusive;
@ -94,24 +95,22 @@ public:
} }
using by_vaddr = bi::set_base_hook<bi::tag<struct _by_vaddr>>; using by_vaddr = bi::set_base_hook<bi::tag<struct _by_vaddr>>;
using by_fake_paddr = bi::set_base_hook<bi::tag<struct _by_fake_paddr>>;
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), size(size >> Core::Memory::YUZU_PAGEBITS),
real_paddr(paddr >> Core::Memory::YUZU_PAGEBITS) {} real_paddr(paddr >> Core::Memory::YUZU_PAGEBITS) {}
u64 vaddr;
VAddr vaddr;
u64 size; u64 size;
// Real backing memory linked to this mapping // 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: private:
size_t backing_size{}; size_t backing_size{};
@ -128,17 +127,15 @@ private:
// Windows requires it for kernels whom lack proper support for some functions! // Windows requires it for kernels whom lack proper support for some functions!
std::optional<VirtualBuffer<u8>> fallback_buffer; std::optional<VirtualBuffer<u8>> 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; 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 // Mappings that have mapped more memory than needed due to page-size limitations
bi::set<IrregularMapping, bi::base_hook<by_vaddr>, bi::compare<decltype(cmp_vaddr)>> unaligned_mappings;
bi::set<MisalignedMapping, bi::base_hook<by_vaddr>, bi::compare<decltype(unaligned_cmp)>> unaligned_mappings;
// Mappings in `unaligned_mappings` that have a fake physical address due to them being mapped again. // Mappings in `unaligned_mappings` that have a fake physical address due to them being mapped again.
bi::set<IrregularMapping, bi::base_hook<by_fake_paddr>, bi::compare<decltype(cmp_paddr)>> irregular_mappings;
// Each key represents 1 4KiB page.
boost::bimap<PAddr, PAddr> irregular_mappings;
}; };
} // namespace Common } // namespace Common

42
src/core/device_memory.h

@ -26,36 +26,60 @@ public:
template <typename T> template <typename T>
Common::PhysicalAddress GetPhysicalAddr(const T* ptr) const { Common::PhysicalAddress GetPhysicalAddr(const T* ptr) const {
return (reinterpret_cast<uintptr_t>(ptr) -
reinterpret_cast<uintptr_t>(buffer.BackingBasePointer())) +
DramMemoryMap::Base;
auto offset = (reinterpret_cast<uintptr_t>(ptr) -
reinterpret_cast<uintptr_t>(buffer.BackingBasePointer())) +
DramMemoryMap::Base;
if (auto irregular = buffer.GetIrregularAddrFromPhysical(offset); irregular) {
return irregular;
}
return offset;
} }
template <typename T> template <typename T>
PAddr GetRawPhysicalAddr(const T* ptr) const { PAddr GetRawPhysicalAddr(const T* ptr) const {
return static_cast<PAddr>(reinterpret_cast<uintptr_t>(ptr) -
reinterpret_cast<uintptr_t>(buffer.BackingBasePointer()));
auto offset = reinterpret_cast<uintptr_t>(ptr) -
reinterpret_cast<uintptr_t>(buffer.BackingBasePointer());
if (auto irregular = buffer.GetIrregularAddrFromPhysical(offset); irregular) {
return irregular;
}
return offset;
} }
template <typename T> template <typename T>
T* GetPointer(Common::PhysicalAddress addr) { T* GetPointer(Common::PhysicalAddress addr) {
return reinterpret_cast<T*>(buffer.BackingBasePointer() +
(GetInteger(addr) - DramMemoryMap::Base));
auto offset = (GetInteger(addr) - DramMemoryMap::Base);
if (auto real = buffer.GetPhysicalAddrFromIrregular(offset); real) {
offset = real;
}
return reinterpret_cast<T*>(buffer.BackingBasePointer() + offset);
} }
template <typename T> template <typename T>
const T* GetPointer(Common::PhysicalAddress addr) const { const T* GetPointer(Common::PhysicalAddress addr) const {
return reinterpret_cast<T*>(buffer.BackingBasePointer() +
(GetInteger(addr) - DramMemoryMap::Base));
auto offset = (GetInteger(addr) - DramMemoryMap::Base);
if (auto real = buffer.GetPhysicalAddrFromIrregular(offset); real) {
offset = real;
}
return reinterpret_cast<T*>(buffer.BackingBasePointer() + offset);
} }
template <typename T> template <typename T>
T* GetPointerFromRaw(PAddr addr) { T* GetPointerFromRaw(PAddr addr) {
if (auto real = buffer.GetPhysicalAddrFromIrregular(addr); real) {
addr = real;
}
return reinterpret_cast<T*>(buffer.BackingBasePointer() + addr); return reinterpret_cast<T*>(buffer.BackingBasePointer() + addr);
} }
template <typename T> template <typename T>
const T* GetPointerFromRaw(PAddr addr) const { const T* GetPointerFromRaw(PAddr addr) const {
if (auto real = buffer.GetPhysicalAddrFromIrregular(addr); real) {
addr = real;
}
return reinterpret_cast<T*>(buffer.BackingBasePointer() + addr); return reinterpret_cast<T*>(buffer.BackingBasePointer() + addr);
} }

Loading…
Cancel
Save