diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp index fd4ceca79f..0dab026a62 100644 --- a/src/common/host_memory.cpp +++ b/src/common/host_memory.cpp @@ -4,6 +4,7 @@ // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "core/memory.h" #ifdef _WIN32 #include @@ -766,22 +767,46 @@ HostMemory::HostMemory(HostMemory&&) noexcept = default; HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default; +bool HostMemory::IsIrregularlyMappedAddress(size_t addr) noexcept { + auto index = addr / Core::Memory::YUZU_PAGESIZE; + + auto i = irregular_mappings.upper_bound(index); + if (i == irregular_mappings.begin()) + return false; + --i; + + return index < i->first + i->second; +} + void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms, bool separate_heap) { #if !(defined(__OPENORBIS__) || defined(__managarm__)) - size_t aligned_length = length; - ASSERT(virtual_offset % HostPageSize == host_offset % HostPageSize); - // todo: placeholder for now, our best bet is probably using the whole pa page for it if (virtual_offset % HostPageSize != 0) { - // todo: use most permissive protections? or leave to Protect - LOG_WARNING(HW_Memory, "Memory address is unaligned to virtual base, surrounding pages will inherit the same permissions", HostPageSize); - auto aligned = AlignDown(virtual_offset, HostPageSize); - auto diff = virtual_offset - aligned; - assert(virtual_offset > aligned); - virtual_offset = aligned; - aligned_length = AlignUp(length + diff, HostPageSize); - ASSERT(aligned_length >= length); + if (IsIrregularlyMappedAddress(virtual_offset)) { + auto aligned = AlignUp(virtual_offset, HostPageSize); + LOG_WARNING(HW_Memory, "Irregularly mapped virtual addresses {:#x}-{:#x} will not have a valid physical address", + virtual_offset, aligned); + length -= aligned - virtual_offset; + virtual_offset = aligned; + } else { + auto aligned = AlignDown(virtual_offset, HostPageSize); + irregular_mappings.emplace(aligned / Core::Memory::YUZU_PAGESIZE, virtual_offset - aligned); + length += virtual_offset - aligned; + virtual_offset = aligned; + } + } + + if (length % HostPageSize != 0) { + if (IsIrregularlyMappedAddress(virtual_offset + length)) { + auto aligned = AlignDown(length, HostPageSize); + LOG_WARNING(HW_Memory, "Irregularly mapped virtual addresses {:#x}-{:#x} will not have a valid physical address", + virtual_offset + aligned, virtual_offset + length); + length = aligned; + } else { + auto aligned = AlignUp(length, HostPageSize); + irregular_mappings.emplace((virtual_offset + aligned) / Core::Memory::YUZU_PAGESIZE, aligned - length); + length = aligned; + } } - length = aligned_length; ASSERT(virtual_offset % HostPageSize == 0); ASSERT(host_offset % HostPageSize == 0); diff --git a/src/common/host_memory.h b/src/common/host_memory.h index 0908dcd2fb..315f5f0bfe 100644 --- a/src/common/host_memory.h +++ b/src/common/host_memory.h @@ -13,6 +13,8 @@ #include #endif +#include + #include "common/common_funcs.h" #include "common/common_types.h" #include "common/virtual_buffer.h" @@ -24,7 +26,7 @@ const size_t HostPageSize = sysconf(_SC_PAGESIZE); #else constexpr size_t HostPageSize = 0x1000; #endif -const size_t GuestHostAlignment = HostPageSize / 4096; +const size_t GuestHostAlignment = HostPageSize / 0x1000; constexpr size_t HugePageSize = 0x200000; enum class MemoryPermission : u32 { @@ -98,8 +100,12 @@ private: u8* backing_base{}; u8* virtual_base{}; size_t virtual_base_offset{}; + // todo: include actual paddr for host ops? + std::map irregular_mappings{}; // Windows requires it for kernels whom lack proper support for some functions! std::optional> fallback_buffer; + + bool IsIrregularlyMappedAddress(size_t addr) noexcept; }; } // namespace Common