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
This commit is contained in:
Stephen Miller
2025-09-24 04:40:26 -05:00
committed by GitHub
parent 5d8027f0c0
commit eeee6ad0ee
4 changed files with 49 additions and 0 deletions

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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: