From eeee6ad0eeeb6ab78ed30320e73a0f37a2b1b0b6 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Wed, 24 Sep 2025 04:40:26 -0500 Subject: [PATCH] Memory: Implement sceKernelMemoryPoolGetBlockStats (#3646) * Implement sceKernelMemoryPoolGetBlockStats Not entirely sure on the logic behind the cached blocks work, but flushed blocks seems to just be based on committed direct memory. * Fix comment --- src/core/libraries/kernel/memory.cpp | 18 ++++++++++++++++++ src/core/libraries/kernel/memory.h | 8 ++++++++ src/core/memory.cpp | 21 +++++++++++++++++++++ src/core/memory.h | 2 ++ 4 files changed, 49 insertions(+) diff --git a/src/core/libraries/kernel/memory.cpp b/src/core/libraries/kernel/memory.cpp index 325ce2d83..7e95fd124 100644 --- a/src/core/libraries/kernel/memory.cpp +++ b/src/core/libraries/kernel/memory.cpp @@ -555,6 +555,23 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolBatch(const OrbisKernelMemoryPoolBatchEntry* return result; } +s32 PS4_SYSV_ABI sceKernelMemoryPoolGetBlockStats(OrbisKernelMemoryPoolBlockStats* stats, + u64 size) { + LOG_WARNING(Kernel_Vmm, "called"); + auto* memory = Core::Memory::Instance(); + OrbisKernelMemoryPoolBlockStats local_stats; + memory->GetMemoryPoolStats(&local_stats); + + u64 size_to_copy = size < sizeof(OrbisKernelMemoryPoolBlockStats) + ? size + : sizeof(OrbisKernelMemoryPoolBlockStats); + // As of firmware 12.02, the kernel does not check if stats is null, + // this can cause crashes on real hardware, so have an assert for this case. + ASSERT_MSG(stats != nullptr || size == 0, "Block stats cannot be null"); + std::memcpy(stats, &local_stats, size_to_copy); + return ORBIS_OK; +} + void* PS4_SYSV_ABI posix_mmap(void* addr, u64 len, s32 prot, s32 flags, s32 fd, s64 phys_addr) { LOG_INFO( Kernel_Vmm, @@ -715,6 +732,7 @@ void RegisterMemory(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("Vzl66WmfLvk", "libkernel", 1, "libkernel", sceKernelMemoryPoolCommit); LIB_FUNCTION("LXo1tpFqJGs", "libkernel", 1, "libkernel", sceKernelMemoryPoolDecommit); LIB_FUNCTION("YN878uKRBbE", "libkernel", 1, "libkernel", sceKernelMemoryPoolBatch); + LIB_FUNCTION("bvD+95Q6asU", "libkernel", 1, "libkernel", sceKernelMemoryPoolGetBlockStats); LIB_FUNCTION("BPE9s9vQQXo", "libkernel", 1, "libkernel", posix_mmap); LIB_FUNCTION("BPE9s9vQQXo", "libScePosix", 1, "libkernel", posix_mmap); diff --git a/src/core/libraries/kernel/memory.h b/src/core/libraries/kernel/memory.h index c60c7992d..2f624d7b1 100644 --- a/src/core/libraries/kernel/memory.h +++ b/src/core/libraries/kernel/memory.h @@ -126,6 +126,13 @@ struct OrbisKernelMemoryPoolBatchEntry { }; }; +struct OrbisKernelMemoryPoolBlockStats { + s32 available_flushed_blocks; + s32 available_cached_blocks; + s32 allocated_flushed_blocks; + s32 allocated_cached_blocks; +}; + u64 PS4_SYSV_ABI sceKernelGetDirectMemorySize(); s32 PS4_SYSV_ABI sceKernelAllocateDirectMemory(s64 searchStart, s64 searchEnd, u64 len, u64 alignment, s32 memoryType, s64* physAddrOut); @@ -176,6 +183,7 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, u64 len, s32 type, s32 pr s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, u64 len, s32 flags); s32 PS4_SYSV_ABI sceKernelMemoryPoolBatch(const OrbisKernelMemoryPoolBatchEntry* entries, s32 count, s32* num_processed, s32 flags); +s32 PS4_SYSV_ABI sceKernelMemoryPoolGetBlockStats(OrbisKernelMemoryPoolBlockStats* stats, u64 size); s32 PS4_SYSV_ABI sceKernelMunmap(void* addr, u64 len); diff --git a/src/core/memory.cpp b/src/core/memory.cpp index cdc5ec52e..e916356cb 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -979,6 +979,27 @@ s32 MemoryManager::IsStack(VAddr addr, void** start, void** end) { return ORBIS_OK; } +s32 MemoryManager::GetMemoryPoolStats(::Libraries::Kernel::OrbisKernelMemoryPoolBlockStats* stats) { + // Run through dmem_map, determine how much physical memory is currently committed + constexpr u64 block_size = 64_KB; + u64 committed_size = 0; + + auto dma_handle = dmem_map.begin(); + while (dma_handle != dmem_map.end()) { + if (dma_handle->second.dma_type == DMAType::Committed) { + committed_size += dma_handle->second.size; + } + dma_handle++; + } + + stats->allocated_flushed_blocks = committed_size / block_size; + stats->available_flushed_blocks = committed_size / block_size; + // TODO: Determine how "cached blocks" work + stats->allocated_cached_blocks = 0; + stats->available_cached_blocks = 0; + return ORBIS_OK; +} + void MemoryManager::InvalidateMemory(const VAddr addr, const u64 size) const { if (rasterizer) { rasterizer->InvalidateMemory(addr, size); diff --git a/src/core/memory.h b/src/core/memory.h index 3d4a8ed69..f729e0207 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -258,6 +258,8 @@ public: void NameVirtualRange(VAddr virtual_addr, u64 size, std::string_view name); + s32 GetMemoryPoolStats(::Libraries::Kernel::OrbisKernelMemoryPoolBlockStats* stats); + void InvalidateMemory(VAddr addr, u64 size) const; private: