From 561adac0af563dc42ad5672aaf92e8c2001e98cc Mon Sep 17 00:00:00 2001 From: lizzie Date: Mon, 15 Jun 2026 22:05:42 +0200 Subject: [PATCH] [common] fallback to VirtualBuffer if we can't use file mappings (#4005) fixes: - windows 8.1 - windows 7 - managarm - openorbis Signed-off-by: lizzie lizzie@eden-emu.dev Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4005 Reviewed-by: MaranBr Reviewed-by: Maufeat --- src/common/host_memory.cpp | 119 +++++++++++++++++++++++++------------ src/common/host_memory.h | 7 ++- 2 files changed, 86 insertions(+), 40 deletions(-) diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp index 04f3a65778..445d34c444 100644 --- a/src/common/host_memory.cpp +++ b/src/common/host_memory.cpp @@ -32,6 +32,8 @@ #include #elif defined(__FreeBSD__) #include +#elif defined(__OPENORBIS__) +#include #endif // FreeBSD @@ -122,54 +124,55 @@ static void GetFuncAddress(Common::DynamicLibrary& dll, const char* name, T& pfn class HostMemory::Impl { public: explicit Impl(size_t backing_size_, size_t virtual_size_) - : backing_size{backing_size_}, virtual_size{virtual_size_}, process{GetCurrentProcess()}, - kernelbase_dll("Kernelbase") { + : backing_size{backing_size_} + , virtual_size{virtual_size_} + , process{GetCurrentProcess()} + , kernelbase_dll("Kernelbase") + {} + + bool Init() { if (!kernelbase_dll.IsOpen()) { LOG_CRITICAL(HW_Memory, "Failed to load Kernelbase.dll"); - throw std::bad_alloc{}; + return false; } GetFuncAddress(kernelbase_dll, "CreateFileMapping2", pfn_CreateFileMapping2); GetFuncAddress(kernelbase_dll, "VirtualAlloc2", pfn_VirtualAlloc2); GetFuncAddress(kernelbase_dll, "MapViewOfFile3", pfn_MapViewOfFile3); GetFuncAddress(kernelbase_dll, "UnmapViewOfFile2", pfn_UnmapViewOfFile2); + if (!pfn_CreateFileMapping2 || !pfn_VirtualAlloc2 || !pfn_MapViewOfFile3 || !pfn_UnmapViewOfFile2) { + LOG_CRITICAL(HW_Memory, "Failed to find functions for virtual allocs"); + return false; + } + // Allocate backing file map - backing_handle = - pfn_CreateFileMapping2(INVALID_HANDLE_VALUE, nullptr, FILE_MAP_WRITE | FILE_MAP_READ, - PAGE_READWRITE, SEC_COMMIT, backing_size, nullptr, nullptr, 0); + backing_handle = pfn_CreateFileMapping2(INVALID_HANDLE_VALUE, nullptr, FILE_MAP_WRITE | FILE_MAP_READ, PAGE_READWRITE, SEC_COMMIT, backing_size, nullptr, nullptr, 0); if (!backing_handle) { - LOG_CRITICAL(HW_Memory, "Failed to allocate {} MiB of backing memory", - backing_size >> 20); - throw std::bad_alloc{}; + LOG_CRITICAL(HW_Memory, "Failed to allocate {} MiB of backing memory", backing_size >> 20); + return false; } // Allocate a virtual memory for the backing file map as placeholder - backing_base = static_cast(pfn_VirtualAlloc2(process, nullptr, backing_size, - MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, - PAGE_NOACCESS, nullptr, 0)); + backing_base = static_cast(pfn_VirtualAlloc2(process, nullptr, backing_size, MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, PAGE_NOACCESS, nullptr, 0)); if (!backing_base) { Release(); - LOG_CRITICAL(HW_Memory, "Failed to reserve {} MiB of virtual memory", - backing_size >> 20); - throw std::bad_alloc{}; + LOG_CRITICAL(HW_Memory, "Failed to reserve {} MiB of virtual memory", backing_size >> 20); + return false; } // Map backing placeholder - void* const ret = pfn_MapViewOfFile3(backing_handle, process, backing_base, 0, backing_size, - MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0); + void* const ret = pfn_MapViewOfFile3(backing_handle, process, backing_base, 0, backing_size, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0); if (ret != backing_base) { Release(); LOG_CRITICAL(HW_Memory, "Failed to map {} MiB of virtual memory", backing_size >> 20); - throw std::bad_alloc{}; + return false; } // Allocate virtual address placeholder - virtual_base = static_cast(pfn_VirtualAlloc2(process, nullptr, virtual_size, - MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, - PAGE_NOACCESS, nullptr, 0)); + virtual_base = static_cast(pfn_VirtualAlloc2(process, nullptr, virtual_size, MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, PAGE_NOACCESS, nullptr, 0)); if (!virtual_base) { Release(); - LOG_CRITICAL(HW_Memory, "Failed to reserve {} GiB of virtual memory", - virtual_size >> 30); - throw std::bad_alloc{}; + LOG_CRITICAL(HW_Memory, "Failed to reserve {} GiB of virtual memory", virtual_size >> 30); + return false; } + return true; } ~Impl() { @@ -391,6 +394,9 @@ private: ankerl::unordered_dense::map placeholder_host_pointers; ///< Placeholder backing offset }; +#elif defined(__OPENORBIS__) || defined(__managarm__) +// None of the luxuries of POSIX, all of the suffering +// For managarm: see https://github.com/managarm/managarm/issues/1370 #else // ^^^ Windows ^^^ vvv POSIX vvv #ifdef ARCHITECTURE_arm64 @@ -496,10 +502,13 @@ static int shm_open_anon(int flags, mode_t mode) { class HostMemory::Impl { public: explicit Impl(size_t backing_size_, size_t virtual_size_) - : backing_size{backing_size_}, virtual_size{virtual_size_} { + : backing_size{backing_size_} + , virtual_size{virtual_size_} + {} + + bool Init() { long page_size = sysconf(_SC_PAGESIZE); - ASSERT_MSG(page_size == 0x1000, "page size {:#x} is incompatible with 4K paging", - page_size); + ASSERT_MSG(page_size == 0x1000, "page size {:#x} is incompatible with 4K paging", page_size); // Backing memory initialization #if defined(__sun__) || defined(__HAIKU__) || defined(__NetBSD__) || defined(__DragonFly__) fd = shm_open_anon(O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600); @@ -507,7 +516,7 @@ public: fd = shm_open_anon(O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600); #elif defined(__FreeBSD__) fd = shm_open(SHM_ANON, O_RDWR, 0600); -#elif defined(__APPLE__) +#elif defined(__APPLE__) || defined(__managarm__) // macOS doesn't have memfd_create, use anonymous temporary file char template_path[] = "/tmp/eden_mem_XXXXXX"; fd = mkstemp(template_path); @@ -540,15 +549,22 @@ public: } else { backing_base = static_cast(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)); } - ASSERT_MSG(backing_base != MAP_FAILED, "mmap failed: {}", strerror(errno)); + if (backing_base == MAP_FAILED) { + LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno)); + return false; + } // Virtual memory initialization virtual_base = virtual_map_base = static_cast(ChooseVirtualBase(virtual_size)); - ASSERT_MSG(virtual_base != MAP_FAILED, "mmap failed: {}", strerror(errno)); + if (virtual_base == MAP_FAILED) { + LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno)); + return false; + } #if defined(__linux__) madvise(virtual_base, virtual_size, MADV_HUGEPAGE); #endif free_manager.SetAddressSpace(virtual_base, virtual_size); + return true; } ~Impl() { @@ -669,17 +685,35 @@ private: #endif // ^^^ POSIX ^^^ -HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_) : backing_size(backing_size_), virtual_size(virtual_size_) { +HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_) + : backing_size(backing_size_) + , virtual_size(virtual_size_) +{ +#if defined(__OPENORBIS__) || defined(__managarm__) + LOG_WARNING(HW_Memory, "Platform doesn't support fastmem"); + fallback_buffer.emplace(backing_size); + backing_base = fallback_buffer->data(); + virtual_base = nullptr; +#else // Try to allocate a fastmem arena. // The implementation will fail with std::bad_alloc on errors. impl = std::make_unique(AlignUp(backing_size, PageAlignment), AlignUp(virtual_size, PageAlignment) + HugePageSize); - backing_base = impl->backing_base; - virtual_base = impl->virtual_base; - if (virtual_base) { - // Ensure the virtual base is aligned to the L2 block size. - virtual_base = reinterpret_cast(Common::AlignUp(uintptr_t(virtual_base), HugePageSize)); - virtual_base_offset = virtual_base - impl->virtual_base; + if (impl->Init()) { + backing_base = impl->backing_base; + virtual_base = impl->virtual_base; + if (virtual_base) { + // Ensure the virtual base is aligned to the L2 block size. + virtual_base = reinterpret_cast(Common::AlignUp(uintptr_t(virtual_base), HugePageSize)); + virtual_base_offset = virtual_base - impl->virtual_base; + } + } else { + impl.reset(); + LOG_WARNING(HW_Memory, "Platform can support fastmem, but can't create it"); + fallback_buffer.emplace(backing_size); + backing_base = fallback_buffer->data(); + virtual_base = nullptr; } +#endif } HostMemory::~HostMemory() = default; @@ -688,8 +722,8 @@ HostMemory::HostMemory(HostMemory&&) noexcept = default; HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default; -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__)) ASSERT(virtual_offset % PageAlignment == 0); ASSERT(host_offset % PageAlignment == 0); ASSERT(length % PageAlignment == 0); @@ -699,9 +733,11 @@ void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length, return; } impl->Map(virtual_offset + virtual_base_offset, host_offset, length, perms); +#endif } void HostMemory::Unmap(size_t virtual_offset, size_t length, bool separate_heap) { +#if !(defined(__OPENORBIS__) || defined(__managarm__)) ASSERT(virtual_offset % PageAlignment == 0); ASSERT(length % PageAlignment == 0); ASSERT(virtual_offset + length <= virtual_size); @@ -709,9 +745,11 @@ void HostMemory::Unmap(size_t virtual_offset, size_t length, bool separate_heap) return; } impl->Unmap(virtual_offset + virtual_base_offset, length); +#endif } void HostMemory::Protect(size_t virtual_offset, size_t length, MemoryPermission perm) { +#if !(defined(__OPENORBIS__) || defined(__managarm__)) ASSERT(virtual_offset % PageAlignment == 0); ASSERT(length % PageAlignment == 0); ASSERT(virtual_offset + length <= virtual_size); @@ -722,6 +760,7 @@ void HostMemory::Protect(size_t virtual_offset, size_t length, MemoryPermission const bool write = True(perm & MemoryPermission::Write); const bool execute = True(perm & MemoryPermission::Execute); impl->Protect(virtual_offset + virtual_base_offset, length, read, write, execute); +#endif } void HostMemory::ClearBackingRegion(size_t physical_offset, size_t length, u32 fill_value) { @@ -729,10 +768,12 @@ void HostMemory::ClearBackingRegion(size_t physical_offset, size_t length, u32 f } void HostMemory::EnableDirectMappedAddress() { +#if !(defined(__OPENORBIS__) || defined(__managarm__)) if (impl) { impl->EnableDirectMappedAddress(); virtual_size += reinterpret_cast(virtual_base); } +#endif } } // namespace Common diff --git a/src/common/host_memory.h b/src/common/host_memory.h index 8dd30aa9de..24f4670732 100644 --- a/src/common/host_memory.h +++ b/src/common/host_memory.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project @@ -7,6 +7,7 @@ #pragma once #include +#include #include "common/common_funcs.h" #include "common/common_types.h" #include "common/virtual_buffer.h" @@ -76,12 +77,16 @@ private: size_t backing_size{}; size_t virtual_size{}; +#if !(defined(__OPENORBIS__) || defined(__managarm__)) // Low level handler for the platform dependent memory routines class Impl; std::unique_ptr impl; +#endif u8* backing_base{}; u8* virtual_base{}; size_t virtual_base_offset{}; + // Windows requires it for kernels whom lack proper support for some functions! + std::optional> fallback_buffer; }; } // namespace Common