// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include #include #ifndef _WIN32 #include #endif #include "common/common_funcs.h" #include "common/common_types.h" namespace Common { #if !defined(ARCHITECTURE_x86_64) && !defined(__WIN32) const u64 HostPageSize = sysconf(_SC_PAGESIZE); const u64 HostPageBits = std::countr_zero(HostPageSize); const u64 HostPageMask = ~(HostPageSize - 1); const u64 GuestHostAlignment = HostPageSize / 0x1000; #else constexpr u64 HostPageSize = 0x1000; constexpr u64 HostPageBits = 12; constexpr u64 HostPageMask = ~(HostPageSize - 1); constexpr u64 GuestHostAlignment = 1; #endif constexpr u64 HugePageSize = 0x200000; enum class MemoryPermission : u32 { Read = 1 << 0, Write = 1 << 1, ReadWrite = Read | Write, Execute = 1 << 2, }; DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission) /** * A low level linear memory buffer, which supports multiple mappings * Its purpose is to rebuild a given sparse memory layout, including mirrors. */ class HostMemory { public: explicit HostMemory(size_t backing_size_, size_t virtual_size_); ~HostMemory(); /** * Copy constructors. They shall return a copy of the buffer without the mappings. * TODO: Implement them with COW if needed. */ HostMemory(const HostMemory& other) = delete; HostMemory& operator=(const HostMemory& other) = delete; /** * Move constructors. They will move the buffer and the mappings to the new object. */ HostMemory(HostMemory&& other) noexcept; HostMemory& operator=(HostMemory&& other) noexcept; void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms, bool separate_heap); void Unmap(size_t virtual_offset, size_t length, bool separate_heap); void Protect(size_t virtual_offset, size_t length, MemoryPermission perms); void EnableDirectMappedAddress(); void ClearBackingRegion(size_t physical_offset, size_t length, u32 fill_value); [[nodiscard]] u8* BackingBasePointer() noexcept { return backing_base; } [[nodiscard]] const u8* BackingBasePointer() const noexcept { return backing_base; } [[nodiscard]] u8* VirtualBasePointer() noexcept { return virtual_base; } [[nodiscard]] const u8* VirtualBasePointer() const noexcept { return virtual_base; } bool IsInVirtualRange(const void* address) const noexcept { return address >= virtual_base && address < virtual_base + virtual_size; } static void AdjustMap(u8* virtual_map_base, size_t virtual_size, size_t* virtual_offset, size_t* length) { // If we are direct mapped, we want to make sure we are operating on a region // that is in range of our virtual mapping. size_t intended_start = *virtual_offset; size_t intended_end = intended_start + *length; size_t address_space_start = reinterpret_cast(virtual_map_base); size_t address_space_end = address_space_start + virtual_size; if (address_space_start > intended_end || intended_start > address_space_end) { *virtual_offset = 0; *length = 0; } else { *virtual_offset = (std::max)(intended_start, address_space_start); *length = (std::min)(intended_end, address_space_end) - *virtual_offset; } } 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! bool fallback_buffer{false}; }; } // namespace Common