// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include #include #include "core/core.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_system_control.h" #include "core/hle/kernel/svc.h" namespace Kernel::Svc { /// Set the process heap to a given Size. It can both extend and shrink the heap. Result SetHeapSize(Core::System& system, u64* out_address, u64 size) { LOG_TRACE(Kernel_SVC, "called, heap_size={:#x}", size); // Validate size. R_UNLESS(Common::IsAligned(size, HeapSizeAlignment), ResultInvalidSize); // Switch 2 titles size their heap from the total memory, which exceeds Switch 1's 8 GiB. const size_t max_heap_size = (std::max)(MainMemorySizeMax, KSystemControl::Init::GetIntendedMemorySize()); R_UNLESS(size < max_heap_size, ResultInvalidSize); // Set the heap size. KProcessAddress address{}; R_TRY(GetCurrentProcess(system.Kernel()) .GetPageTable() .SetHeapSize(std::addressof(address), size)); // We succeeded. *out_address = GetInteger(address); R_SUCCEED(); } /// Maps memory at a desired address Result MapPhysicalMemory(Core::System& system, u64 addr, u64 size) { LOG_DEBUG(Kernel_SVC, "called, addr={:#016x}, size={:#x}", addr, size); if (!Common::IsAligned(addr, Core::Memory::YUZU_PAGESIZE)) { LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, {:#016X}", addr); R_THROW(ResultInvalidAddress); } if (!Common::IsAligned(size, Core::Memory::YUZU_PAGESIZE)) { LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, {:#X}", size); R_THROW(ResultInvalidSize); } if (size == 0) { LOG_ERROR(Kernel_SVC, "Size is zero"); R_THROW(ResultInvalidSize); } if (!(addr < addr + size)) { LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address"); R_THROW(ResultInvalidMemoryRegion); } KProcess* const current_process{GetCurrentProcessPointer(system.Kernel())}; auto& page_table{current_process->GetPageTable()}; if (current_process->GetTotalSystemResourceSize() == 0) { LOG_ERROR(Kernel_SVC, "System Resource Size is zero"); R_THROW(ResultInvalidState); } if (!page_table.Contains(addr, size)) { LOG_ERROR(Kernel_SVC, "Address is not within the address space, addr={:#016x}, size={:#016x}", addr, size); R_THROW(ResultInvalidMemoryRegion); } if (!page_table.IsInAliasRegion(addr, size)) { LOG_ERROR(Kernel_SVC, "Address is not within the alias region, addr={:#016x}, size={:#016x}", addr, size); R_THROW(ResultInvalidMemoryRegion); } R_RETURN(page_table.MapPhysicalMemory(addr, size)); } /// Unmaps memory previously mapped via MapPhysicalMemory Result UnmapPhysicalMemory(Core::System& system, u64 addr, u64 size) { LOG_DEBUG(Kernel_SVC, "called, addr={:#016x}, size={:#x}", addr, size); if (!Common::IsAligned(addr, Core::Memory::YUZU_PAGESIZE)) { LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, {:#016X}", addr); R_THROW(ResultInvalidAddress); } if (!Common::IsAligned(size, Core::Memory::YUZU_PAGESIZE)) { LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, {:#X}", size); R_THROW(ResultInvalidSize); } if (size == 0) { LOG_ERROR(Kernel_SVC, "Size is zero"); R_THROW(ResultInvalidSize); } if (!(addr < addr + size)) { LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address"); R_THROW(ResultInvalidMemoryRegion); } KProcess* const current_process{GetCurrentProcessPointer(system.Kernel())}; auto& page_table{current_process->GetPageTable()}; if (current_process->GetTotalSystemResourceSize() == 0) { LOG_ERROR(Kernel_SVC, "System Resource Size is zero"); R_THROW(ResultInvalidState); } if (!page_table.Contains(addr, size)) { LOG_ERROR(Kernel_SVC, "Address is not within the address space, addr={:#016x}, size={:#016x}", addr, size); R_THROW(ResultInvalidMemoryRegion); } if (!page_table.IsInAliasRegion(addr, size)) { LOG_ERROR(Kernel_SVC, "Address is not within the alias region, addr={:#016x}, size={:#016x}", addr, size); R_THROW(ResultInvalidMemoryRegion); } R_RETURN(page_table.UnmapPhysicalMemory(addr, size)); } Result MapPhysicalMemoryUnsafe(Core::System& system, uint64_t address, uint64_t size) { UNIMPLEMENTED(); R_THROW(ResultNotImplemented); } Result UnmapPhysicalMemoryUnsafe(Core::System& system, uint64_t address, uint64_t size) { UNIMPLEMENTED(); R_THROW(ResultNotImplemented); } Result SetUnsafeLimit(Core::System& system, uint64_t limit) { UNIMPLEMENTED(); R_THROW(ResultNotImplemented); } // EDEN_NX2_MEM_TRACE: log the heap and physical memory requests of a title (diagnostics). static bool MemTrace() { static const bool trace = std::getenv("EDEN_NX2_MEM_TRACE") != nullptr; return trace; } Result SetHeapSize64(Core::System& system, uint64_t* out_address, uint64_t size) { const Result result = SetHeapSize(system, out_address, size); if (MemTrace()) { LOG_INFO(Kernel_SVC, "mem trace: SetHeapSize {:#x} -> {:#x} at {:#x}", size, result.raw, *out_address); } R_RETURN(result); } Result MapPhysicalMemory64(Core::System& system, uint64_t address, uint64_t size) { const Result result = MapPhysicalMemory(system, address, size); static u64 total{}; if (MemTrace()) { if (result.IsSuccess()) { total += size; } static u32 logs{}; if (result.IsError() || logs++ < 400) { LOG_INFO(Kernel_SVC, "mem trace: MapPhysicalMemory {:#x}+{:#x} -> {:#x} (mapped total {:#x})", address, size, result.raw, total); } } R_RETURN(result); } Result UnmapPhysicalMemory64(Core::System& system, uint64_t address, uint64_t size) { const Result result = UnmapPhysicalMemory(system, address, size); if (MemTrace()) { static u32 logs{}; if (result.IsError() || logs++ < 400) { LOG_INFO(Kernel_SVC, "mem trace: UnmapPhysicalMemory {:#x}+{:#x} -> {:#x}", address, size, result.raw); } } R_RETURN(result); } Result MapPhysicalMemoryUnsafe64(Core::System& system, uint64_t address, uint64_t size) { R_RETURN(MapPhysicalMemoryUnsafe(system, address, size)); } Result UnmapPhysicalMemoryUnsafe64(Core::System& system, uint64_t address, uint64_t size) { R_RETURN(UnmapPhysicalMemoryUnsafe(system, address, size)); } Result SetUnsafeLimit64(Core::System& system, uint64_t limit) { R_RETURN(SetUnsafeLimit(system, limit)); } Result SetHeapSize64From32(Core::System& system, uint64_t* out_address, uint32_t size) { R_RETURN(SetHeapSize(system, out_address, size)); } Result MapPhysicalMemory64From32(Core::System& system, uint32_t address, uint32_t size) { R_RETURN(MapPhysicalMemory(system, address, size)); } Result UnmapPhysicalMemory64From32(Core::System& system, uint32_t address, uint32_t size) { R_RETURN(UnmapPhysicalMemory(system, address, size)); } Result MapPhysicalMemoryUnsafe64From32(Core::System& system, uint32_t address, uint32_t size) { R_RETURN(MapPhysicalMemoryUnsafe(system, address, size)); } Result UnmapPhysicalMemoryUnsafe64From32(Core::System& system, uint32_t address, uint32_t size) { R_RETURN(UnmapPhysicalMemoryUnsafe(system, address, size)); } Result SetUnsafeLimit64From32(Core::System& system, uint32_t limit) { R_RETURN(SetUnsafeLimit(system, limit)); } } // namespace Kernel::Svc