From d283d484a204c215c1924b9c9842d1c1a845bba8 Mon Sep 17 00:00:00 2001 From: xXPBSXx <20614131+xXPBSXx@users.noreply.github.com> Date: Mon, 12 Oct 2026 01:12:12 +0200 Subject: [PATCH] fix(libkernel): answer sceKernelGetPageTableStats PPSA30601 (Resonance) stops at the throwing stub. The four int* arguments follow OpenOrbis' libkernel.h, the Orbital type database and SharpProspero; the values follow KytyPS5 (2048 entries of 2 MiB per side) without its accounting of used entries, listed in TechnicalDebt.md. A null pointer returns EFAULT. Co-Authored-By: Claude Sonnet 5.5 --- core/libs/prx/libkernel/DirectMemory/Export.cpp | 11 ++++++----- core/libs/tests/GuestMemory.cpp | 14 ++++++++++++++ docs/dev/debt/libkernel.md | 1 + 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/core/libs/prx/libkernel/DirectMemory/Export.cpp b/core/libs/prx/libkernel/DirectMemory/Export.cpp index cf0fe12033f..a7d7d28381e 100644 --- a/core/libs/prx/libkernel/DirectMemory/Export.cpp +++ b/core/libs/prx/libkernel/DirectMemory/Export.cpp @@ -388,11 +388,12 @@ int APS5_VABI sceKernelClearVirtualRangeName(const void* addr, uint64_t len) { } int APS5_VABI sceKernelGetPageTableStats(int* cpu_total, int* cpu_available, int* gpu_total, int* gpu_available) { - (void)cpu_total; - (void)cpu_available; - (void)gpu_total; - (void)gpu_available; - NotImplemented_nid_no_patch(__func__); + if (!cpu_total || !cpu_available || !gpu_total || !gpu_available) return SCE_KERNEL_ERROR_EFAULT; + constexpr int pageTableEntries = static_cast((4ull << 30) / (2ull << 20)); + *cpu_total = pageTableEntries; + *cpu_available = pageTableEntries; + *gpu_total = pageTableEntries; + *gpu_available = pageTableEntries; return 0; } diff --git a/core/libs/tests/GuestMemory.cpp b/core/libs/tests/GuestMemory.cpp index fb33dafc1d3..0138185c2b3 100644 --- a/core/libs/tests/GuestMemory.cpp +++ b/core/libs/tests/GuestMemory.cpp @@ -58,6 +58,7 @@ int APS5_VABI sceKernelMemoryPoolBatch(const KernelMemoryPoolBatchEntry*, int, i int APS5_VABI sceKernelVirtualQuery(const void*, int, VirtualQueryInfo*, std::uint64_t); int APS5_VABI sceKernelSetVirtualRangeName(const void*, std::uint64_t, const char*); int APS5_VABI sceKernelClearVirtualRangeName(const void*, std::uint64_t); +int APS5_VABI sceKernelGetPageTableStats(int*, int*, int*, int*); int APS5_VABI sceKernelAllocateDirectMemory(std::int64_t, std::int64_t, std::size_t, std::size_t, int, std::int64_t*); int APS5_VABI sceKernelMapDirectMemory(void**, std::size_t, int, int, std::int64_t, std::size_t); int APS5_VABI sceKernelEnableDmemAliasing(); @@ -113,6 +114,18 @@ static void CheckReleaseFlexibleMemory() { Require(sceKernelMunmap(again, length) == 0); } +static void CheckPageTableStats() { + int cpuTotal = -1, cpuAvailable = -1, gpuTotal = -1, gpuAvailable = -1; + Require(sceKernelGetPageTableStats(&cpuTotal, &cpuAvailable, &gpuTotal, &gpuAvailable) == 0); + Require(cpuTotal > 0 && gpuTotal > 0); + Require(cpuAvailable >= 0 && cpuAvailable <= cpuTotal); + Require(gpuAvailable >= 0 && gpuAvailable <= gpuTotal); + Require(sceKernelGetPageTableStats(nullptr, &cpuAvailable, &gpuTotal, &gpuAvailable) == SCE_KERNEL_ERROR_EFAULT); + Require(sceKernelGetPageTableStats(&cpuTotal, nullptr, &gpuTotal, &gpuAvailable) == SCE_KERNEL_ERROR_EFAULT); + Require(sceKernelGetPageTableStats(&cpuTotal, &cpuAvailable, nullptr, &gpuAvailable) == SCE_KERNEL_ERROR_EFAULT); + Require(sceKernelGetPageTableStats(&cpuTotal, &cpuAvailable, &gpuTotal, nullptr) == SCE_KERNEL_ERROR_EFAULT); +} + static void CheckNamedAndHintedMappings() { constexpr std::size_t length = 0x10000; void* first = nullptr; @@ -1871,6 +1884,7 @@ int main() { CheckReleaseFlexibleMemory(); CheckVirtualQueryPartialMunmap(); CheckMunmapOverUnmappedPages(); + CheckPageTableStats(); CheckNamedAndHintedMappings(); CheckInternalNamedFlexibleMapping(); CheckGuestModuleUsesFlexibleMemory(); diff --git a/docs/dev/debt/libkernel.md b/docs/dev/debt/libkernel.md index e924d67a2e9..63bbc27247e 100644 --- a/docs/dev/debt/libkernel.md +++ b/docs/dev/debt/libkernel.md @@ -155,3 +155,4 @@ - [pthread_spin_init, pthread_spin_destroy, pthread_spin_lock, pthread_spin_trylock, pthread_spin_unlock](../../../core/libs/prx/libkernel/Pthread/Posix/Spinlock.cpp) (libkernel) follow FreeBSD 11.4 libthr [`thr_pspinlock.c`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libthr/thread/thr_pspinlock.c) and the owner checks of its [`thr_umtx.h`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libthr/thread/thr_umtx.h#L80-L175), unverified on the console: `EINVAL` for a null or destroyed lock and a `pshared` other than 0 or 1, `EBUSY` from `trylock` while any thread holds it, `EPERM` from `unlock` by a thread that does not hold it. `PTHREAD_PROCESS_SHARED` allocates a private lock, as guest code runs in one process. libthr spins forever when the holder locks again; here that call throws - [pthread_cond_reltimedwait_np](../../../core/libs/prx/libkernel/Pthread/Posix/Cond.cpp) (libkernel) takes `(cond, mutex, usec)`, a relative timeout in microseconds, and is `scePthreadCondTimedwait` with POSIX error numbers. FreeBSD 11.4 has no such function; the signature comes only from shadPS4, which registers one function for this NID and for `scePthreadCondTimedwait` ([`condvar.cpp`](https://github.com/shadps4-emu/shadPS4/blob/3a9f57265d28975658f1ca941daa5c879c7a74bd/src/core/libraries/kernel/threads/condvar.cpp#L229-L234)). shadPS4 reads a 64-bit count; here it is the 32-bit `SceKernelUseconds` of the SCE twin, as for `sem_reltimedwait_np`. Unverified on the console - [sysctl kern.arandom](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) serves FreeBSD 11's MIB {1, 37} (CTL_KERN, KERN_ARANDOM) and the "kern.arandom" sysctlbyname lookup through std::random_device, returning min(*oldLength, 256) random bytes. Following FreeBSD's sysctl_kern_arnd, a null buffer reports min(*oldLength, 256) without writing, a null length returns 0 without writing, and requests with a new value return EPERM (1) as the node is read-only. Cryptographic quality depends on the host's random device implementation. +- [sceKernelGetPageTableStats](../../../core/libs/prx/libkernel/DirectMemory/Export.cpp) (libkernel) - reports 2048 page-table entries on each side (a 4 GiB pool of 2 MiB entries, the constants of [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/main/src/kernel/memory.cpp)) and all of them available: no entries are counted as used, so a title never sees page-table pressure. KytyPS5 counts the 2 MiB entries of the committed CPU- or GPU-accessible ranges as used, which this port does not do. The four `int*` arguments follow OpenOrbis' `libkernel.h`, the Orbital type database and SharpProspero; a null pointer returns `SCE_KERNEL_ERROR_EFAULT` as in KytyPS5. None of the values is measured on a console. PPSA30601 (Resonance) calls it during start-up