// Copyright 2025 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "Common/MemArena.h" #include "Common/Assert.h" #include "Common/Logging/Log.h" namespace Common { MemArena::MemArena() = default; MemArena::~MemArena() = default; void MemArena::GrabSHMSegment(size_t size, std::string_view base_name) { kern_return_t retval = vm_allocate(mach_task_self(), &m_shm_address, size, VM_FLAGS_ANYWHERE); if (retval != KERN_SUCCESS) { ERROR_LOG_FMT(MEMMAP, "GrabSHMSegment failed: vm_allocate returned {0:#x}", retval); m_shm_address = 0; return; } memory_object_size_t entry_size = size; constexpr vm_prot_t prot = VM_PROT_READ | VM_PROT_WRITE; retval = mach_make_memory_entry_64(mach_task_self(), &entry_size, m_shm_address, prot, &m_shm_entry, MACH_PORT_NULL); if (retval != KERN_SUCCESS) { ERROR_LOG_FMT(MEMMAP, "GrabSHMSegment failed: mach_make_memory_entry_64 returned {0:#x}", retval); m_shm_address = 0; m_shm_entry = MACH_PORT_NULL; return; } m_shm_size = size; } void MemArena::ReleaseSHMSegment() { if (m_shm_entry != MACH_PORT_NULL) { mach_port_deallocate(mach_task_self(), m_shm_entry); } if (m_shm_address != 0) { vm_deallocate(mach_task_self(), m_shm_address, m_shm_size); } m_shm_address = 0; m_shm_size = 0; m_shm_entry = MACH_PORT_NULL; } void* MemArena::CreateView(s64 offset, size_t size) { if (m_shm_address == 0) { ERROR_LOG_FMT(MEMMAP, "CreateView failed: no shared memory segment allocated"); return nullptr; } vm_address_t address; constexpr vm_prot_t prot = VM_PROT_READ | VM_PROT_WRITE; kern_return_t retval = vm_map(mach_task_self(), &address, size, 0, VM_FLAGS_ANYWHERE, m_shm_entry, offset, false, prot, prot, VM_INHERIT_DEFAULT); if (retval != KERN_SUCCESS) { ERROR_LOG_FMT(MEMMAP, "CreateView failed: vm_map returned {0:#x}", retval); return nullptr; } return reinterpret_cast(address); } void MemArena::ReleaseView(void* view, size_t size) { vm_deallocate(mach_task_self(), reinterpret_cast(view), size); } u8* MemArena::ReserveMemoryRegion(size_t memory_size) { vm_address_t address; kern_return_t retval = vm_allocate(mach_task_self(), &address, memory_size, VM_FLAGS_ANYWHERE); if (retval != KERN_SUCCESS) { ERROR_LOG_FMT(MEMMAP, "ReserveMemoryRegion: vm_allocate returned {0:#x}", retval); return nullptr; } retval = vm_protect(mach_task_self(), address, memory_size, true, VM_PROT_NONE); if (retval != KERN_SUCCESS) { ERROR_LOG_FMT(MEMMAP, "ReserveMemoryRegion failed: vm_prot returned {0:#x}", retval); return nullptr; } m_region_address = address; m_region_size = memory_size; return reinterpret_cast(m_region_address); } void MemArena::ReleaseMemoryRegion() { if (m_region_address != 0) { vm_deallocate(mach_task_self(), m_region_address, m_region_size); } m_region_address = 0; m_region_size = 0; } void* MemArena::MapInMemoryRegion(s64 offset, size_t size, void* base) { if (m_shm_address == 0) { ERROR_LOG_FMT(MEMMAP, "MapInMemoryRegion failed: no shared memory segment allocated"); return nullptr; } vm_address_t address = reinterpret_cast(base); constexpr vm_prot_t prot = VM_PROT_READ | VM_PROT_WRITE; kern_return_t retval = vm_map(mach_task_self(), &address, size, 0, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE, m_shm_entry, offset, false, prot, prot, VM_INHERIT_DEFAULT); if (retval != KERN_SUCCESS) { ERROR_LOG_FMT(MEMMAP, "MapInMemoryRegion failed: vm_map returned {0:#x}", retval); return nullptr; } return reinterpret_cast(address); } void MemArena::UnmapFromMemoryRegion(void* view, size_t size) { vm_address_t address = reinterpret_cast(view); kern_return_t retval = vm_allocate(mach_task_self(), &address, size, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE); if (retval != KERN_SUCCESS) { ERROR_LOG_FMT(MEMMAP, "UnmapFromMemoryRegion failed: vm_allocate returned {0:#x}", retval); return; } retval = vm_protect(mach_task_self(), address, size, true, VM_PROT_NONE); if (retval != KERN_SUCCESS) { ERROR_LOG_FMT(MEMMAP, "UnmapFromMemoryRegion failed: vm_prot returned {0:#x}", retval); } } LazyMemoryRegion::LazyMemoryRegion() = default; LazyMemoryRegion::~LazyMemoryRegion() { Release(); } void* LazyMemoryRegion::Create(size_t size) { ASSERT(!m_memory); if (size == 0) return nullptr; vm_address_t memory = 0; kern_return_t retval = vm_allocate(mach_task_self(), &memory, size, VM_FLAGS_ANYWHERE); if (retval != KERN_SUCCESS) { ERROR_LOG_FMT(MEMMAP, "Failed to allocate memory space: {0:#x}", retval); return nullptr; } m_memory = reinterpret_cast(memory); m_size = size; return m_memory; } void LazyMemoryRegion::Clear() { ASSERT(m_memory); vm_address_t new_memory = reinterpret_cast(m_memory); kern_return_t retval = vm_allocate(mach_task_self(), &new_memory, m_size, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE); if (retval != KERN_SUCCESS) { ERROR_LOG_FMT(MEMMAP, "Failed to reallocate memory space: {0:#x}", retval); m_memory = nullptr; return; } m_memory = reinterpret_cast(new_memory); } void LazyMemoryRegion::Release() { if (m_memory) { vm_deallocate(mach_task_self(), reinterpret_cast(m_memory), m_size); } m_memory = nullptr; m_size = 0; } } // namespace Common