diff --git a/core/libs/prx/libkernel/DirectMemory/Export.cpp b/core/libs/prx/libkernel/DirectMemory/Export.cpp index 4ea6af06be..fa42c29e9d 100644 --- a/core/libs/prx/libkernel/DirectMemory/Export.cpp +++ b/core/libs/prx/libkernel/DirectMemory/Export.cpp @@ -504,6 +504,12 @@ int APS5_VABI sceKernelGetPageTableStats(int* cpu_total, int* cpu_available, int return 0; } +int APS5_VABI sceKernelGetTraceMemoryStats(std::uint64_t* first, std::uint64_t* second) { + if (first) *first = 0; + if (second) *second = 0; + return 0; +} + int APS5_VABI sceKernelGetPrtAperture(int index, void** addr, size_t* len) { if (index < 0 || index >= PRT_APERTURE_COUNT || !addr || !len) return SCE_KERNEL_ERROR_EINVAL; std::lock_guard lock(g_prtLock); diff --git a/core/libs/prx/libkernel/System/src/SystemConfiguration.cpp b/core/libs/prx/libkernel/System/src/SystemConfiguration.cpp index ff20f5f917..28ac2786d8 100644 --- a/core/libs/prx/libkernel/System/src/SystemConfiguration.cpp +++ b/core/libs/prx/libkernel/System/src/SystemConfiguration.cpp @@ -481,6 +481,31 @@ int APS5_VABI sceKernelGetSystemSwVersion_nid_postfix(SceKernelSystemSwVersion* return 0; } +int APS5_VABI sceKernelGetSystemExVersion(char* first, char* second) { + if (first == nullptr || second == nullptr) { + return 22; + } + *first = '\0'; + *second = '\0'; + return 0; +} + +int APS5_VABI sysKernelGetLowerLimitSysexVersion_nid_postfix(char* version) { + if (version == nullptr) { + return SCE_KERNEL_ERROR_EINVAL; + } + *version = '\0'; + return 0; +} + +int APS5_VABI sysKernelGetLowerLimitUpdVersion_nid_postfix(std::uint32_t* version) { + if (version == nullptr) { + return SCE_KERNEL_ERROR_EINVAL; + } + *version = 0; + return 0; +} + int APS5_VABI sysctlnametomib_nid_postfix(const char* name, int* mib, std::size_t* size) { struct KnownName { const char* name; diff --git a/core/libs/prx/libkernel_sys/Export.Discovery.cpp b/core/libs/prx/libkernel_sys/Export.Discovery.cpp index c19074087c..122ab52e24 100644 --- a/core/libs/prx/libkernel_sys/Export.Discovery.cpp +++ b/core/libs/prx/libkernel_sys/Export.Discovery.cpp @@ -1151,11 +1151,6 @@ int APS5_VABI sceKernelGetSubsysSubId(void) { return 0; } -int APS5_VABI sceKernelGetSystemExVersion(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - int APS5_VABI sceKernelGetSystemLevelDebuggerModeForRcmgr(void) { NotImplemented_nid_no_patch(__func__); return 0; @@ -1171,11 +1166,6 @@ int APS5_VABI sceKernelGetThreadName(void) { return 0; } -int APS5_VABI sceKernelGetTraceMemoryStats(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - int APS5_VABI sceKernelGetUniversalMode(void) { NotImplemented_nid_no_patch(__func__); return 0; @@ -2394,16 +2384,6 @@ int APS5_VABI sysKernelGetIntdevModeForRcmgr_nid_postfix(void) { return 0; } -int APS5_VABI sysKernelGetLowerLimitSysexVersion_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sysKernelGetLowerLimitUpdVersion_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - int APS5_VABI sysKernelGetManufacturingMode_nid_postfix(void) { NotImplemented_nid_no_patch(__func__); return 0; diff --git a/core/libs/tests/GuestPageTableStats.cpp b/core/libs/tests/GuestPageTableStats.cpp index eba38dd12c..2ef3d599be 100644 --- a/core/libs/tests/GuestPageTableStats.cpp +++ b/core/libs/tests/GuestPageTableStats.cpp @@ -9,6 +9,7 @@ extern "C" { int APS5_VABI sceKernelGetPageTableStats(int*, int*, int*, int*); +int APS5_VABI sceKernelGetTraceMemoryStats(std::uint64_t*, std::uint64_t*); int APS5_VABI sceKernelMapFlexibleMemory(void**, std::size_t, int, int); int APS5_VABI sceKernelMapDirectMemory(void**, std::size_t, int, int, std::int64_t, std::size_t); int APS5_VABI sceKernelAllocateDirectMemory(std::int64_t, std::int64_t, std::size_t, std::size_t, int, std::int64_t*); @@ -111,8 +112,21 @@ static void CheckGpuDirectMapping() { RequireTaken(before, 0, 0); } +static void CheckTraceMemoryStats() { + std::uint64_t first[2] = {0xaaaaaaaaaaaaaaaaull, 0xaaaaaaaaaaaaaaaaull}; + std::uint64_t second[2] = {0xaaaaaaaaaaaaaaaaull, 0xaaaaaaaaaaaaaaaaull}; + Require(sceKernelGetTraceMemoryStats(first, second) == 0); + Require(first[0] == 0 && first[1] == 0xaaaaaaaaaaaaaaaaull && second[0] == 0 && second[1] == 0xaaaaaaaaaaaaaaaaull); + first[0] = 1; + Require(sceKernelGetTraceMemoryStats(first, nullptr) == 0 && first[0] == 0); + second[0] = 1; + Require(sceKernelGetTraceMemoryStats(nullptr, second) == 0 && second[0] == 0); + Require(sceKernelGetTraceMemoryStats(nullptr, nullptr) == 0); +} + int main() { CheckRejectedArguments(); + CheckTraceMemoryStats(); CheckCpuGranules(); CheckGpuEntries(); CheckGpuDirectMapping(); diff --git a/core/libs/tests/GuestSystemConfiguration.cpp b/core/libs/tests/GuestSystemConfiguration.cpp index a4d441e2e0..eb45e6e603 100644 --- a/core/libs/tests/GuestSystemConfiguration.cpp +++ b/core/libs/tests/GuestSystemConfiguration.cpp @@ -24,6 +24,9 @@ int APS5_VABI sysctl_nid_postfix(const int*, std::uint32_t, void*, std::size_t*, int APS5_VABI sysctlbyname_nid_postfix(const char*, void*, std::size_t*, const void*, std::size_t); int APS5_VABI sysctlnametomib_nid_postfix(const char*, int*, std::size_t*); int APS5_VABI sceKernelGetSystemSwVersion_nid_postfix(SceKernelSystemSwVersion*); +int APS5_VABI sceKernelGetSystemExVersion(char*, char*); +int APS5_VABI sysKernelGetLowerLimitSysexVersion_nid_postfix(char*); +int APS5_VABI sysKernelGetLowerLimitUpdVersion_nid_postfix(std::uint32_t*); int APS5_VABI getpid_nid_postfix(); void* APS5_VABI mmap_nid_postfix(void*, std::size_t, int, int, int, std::int64_t); int APS5_VABI munmap_nid_postfix(void*, std::size_t); @@ -461,6 +464,31 @@ static void CheckMeasuredAbsentSysctlMibs() { Require(threw); } } +static void CheckVersionLimits() { + unsigned char first[4]; + unsigned char second[4]; + std::memset(first, 0xaa, sizeof(first)); + std::memset(second, 0xaa, sizeof(second)); + *__error_nid_postfix() = 13; + Require(sceKernelGetSystemExVersion(reinterpret_cast(first + 1), reinterpret_cast(second + 1)) == 0 && *__error_nid_postfix() == 13); + Require(first[0] == 0xaa && first[1] == 0 && first[2] == 0xaa && second[0] == 0xaa && second[1] == 0 && second[2] == 0xaa); + std::memset(first, 0xaa, sizeof(first)); + Require(sceKernelGetSystemExVersion(reinterpret_cast(first), nullptr) == 22 && first[0] == 0xaa); + Require(sceKernelGetSystemExVersion(nullptr, reinterpret_cast(first)) == 22 && first[0] == 0xaa); + Require(sceKernelGetSystemExVersion(nullptr, nullptr) == 22 && *__error_nid_postfix() == 13); + + std::memset(first, 0xaa, sizeof(first)); + Require(sysKernelGetLowerLimitSysexVersion_nid_postfix(reinterpret_cast(first + 1)) == 0); + Require(first[0] == 0xaa && first[1] == 0 && first[2] == 0xaa); + Require(sysKernelGetLowerLimitSysexVersion_nid_postfix(nullptr) == SCE_KERNEL_ERROR_EINVAL); + + struct { + std::uint32_t version; + std::uint32_t after; + } update{0xaaaaaaaau, 0xaaaaaaaau}; + Require(sysKernelGetLowerLimitUpdVersion_nid_postfix(&update.version) == 0 && update.version == 0 && update.after == 0xaaaaaaaau); + Require(sysKernelGetLowerLimitUpdVersion_nid_postfix(nullptr) == SCE_KERNEL_ERROR_EINVAL && *__error_nid_postfix() == 13); +} static void CheckSystemSwVersion() { *__error_nid_postfix() = 13; Require(sceKernelGetSystemSwVersion_nid_postfix(nullptr) == SCE_KERNEL_ERROR_EFAULT && *__error_nid_postfix() == 13); @@ -836,6 +864,7 @@ int main() { CheckLeafWithTrailingComponent(); CheckMeasuredAbsentSysctlMibs(); CheckSystemSwVersion(); + CheckVersionLimits(); CheckPseudoRandomSysctl(); CheckSdkVersionSysctl(); CheckOvercommitSysctl(); diff --git a/docs/dev/debt/libkernel.md b/docs/dev/debt/libkernel.md index 85652b04ae..ec65a1d81d 100644 --- a/docs/dev/debt/libkernel.md +++ b/docs/dev/debt/libkernel.md @@ -197,6 +197,7 @@ - [hw.pagesize](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) reports the system page size (16384 bytes, PS5_PAGE_SIZE) as an integer through sysctl MIB {6, 7} (CTL_HW, HW_PAGESIZE) and the "hw.pagesize" sysctlbyname lookup, following FreeBSD's [`hw.pagesize`](https://github.com/freebsd/freebsd-src/blob/main/sys/kern/kern_mib.c#L177), a read-only `SYSCTL_INT` of `PAGE_SIZE`. The value on a PS5 follows the 16 KiB page size the rest of libkernel uses and is unmeasured. A write returns -1 with EPERM. - [hw.model](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) reports the [host CPU's name](../../../core/libs/prx/libkernel/System/src/HostCpu.cpp), built as FreeBSD 11 builds `hw.model`: the CPUID brand string (leaves 0x80000002 to 0x80000004) without leading spaces ([identcpu.c](https://github.com/freebsd/freebsd-src/blob/release/11.0.0/sys/x86/x86/identcpu.c#L683-L691)). Trailing padding stays, as it does there, so an AMD host's name ends in spaces. The name a PS5 reports is unknown. A write returns -1 with `EPERM`. A host without an x86 CPUID brand string, such as an Apple Silicon Mac, throws. PPSA99203 only prints it (`[console] hw.model: %s`) - [Host CPU check](../../../core/libs/prx/libkernel/System/src/HostCpu.cpp) (libkernel) - when libkernel loads on a Linux or Windows x86-64 host, it logs which of the PS5 CPU's user-mode extensions CPUID reports and throws, naming the host CPU, if any extension is missing that LLVM selects from plain C/C++ for the PS5's Zen 2 (`-march=znver2`, [feature list](https://github.com/llvm/llvm-project/blob/llvmorg-19.1.7/llvm/lib/Target/X86/X86.td#L1459-L1513)): SSE to SSE4.2, SSSE3, POPCNT, LZCNT, BMI1, BMI2, MOVBE, CMPXCHG16B, LAHF/SAHF, AVX, AVX2, FMA, F16C, and the operating system's AVX state in XCR0 (#3719). SSE4A, CLZERO and SHA, which `--to-intel` converts, and AES, PCLMULQDQ, RDRAND, RDSEED, ADX, CLFLUSHOPT, CLWB, RDPID, XSAVEOPT, XSAVEC and PREFETCHW, which compilers emit only for intrinsics or explicit prefetches, are logged but not required: a title that executes one of them on a host without it still stops with an illegal instruction. macOS hosts are not checked: some Rosetta versions execute extensions without reporting them through CPUID, but support differs by host. ANDN and RDRAND fault on an M1 with macOS 15.3.1; see the [Rosetta conversion limits](relinker.md) +- [sceKernelGetSystemExVersion, sysKernelGetLowerLimitSysexVersion, sysKernelGetLowerLimitUpdVersion](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) and [sceKernelGetTraceMemoryStats](../../../core/libs/prx/libkernel/DirectMemory/Export.cpp) (libkernel) - follow libkernel.sprx, libkernel_sys.sprx and libkernel_web.sprx of firmware 13.42, read from disassembly and not measured on a console. None of them asks the kernel: the first writes an empty string to both outputs and returns a plain 22, not `SCE_KERNEL_ERROR_EINVAL`, when either is null; the lower limits write an empty string and a 32-bit 0; the trace memory statistics write 0 to each output that is not null. The meaning of the outputs is not known - [machdep.tsc_freq](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) reports the host TSC frequency that `sceKernelGetTscFrequency` measures, as a 64-bit value like FreeBSD 11's [`sysctl_machdep_tsc_freq`](https://github.com/freebsd/freebsd-src/blob/release/11.0.0/sys/x86/x86/tsc.c#L658-L676). FreeBSD lets root write it to change the TSC timecounter's frequency; a write here returns -1 with `EPERM`. PPSA99203 reads it at start-up and measures the TSC itself when the value is 100 MHz or less - [getrlimit](../../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) - `RLIMIT_CPU`, `RLIMIT_FSIZE`, `RLIMIT_CORE`, `RLIMIT_SBSIZE`, `RLIMIT_VMEM`/`AS`, `RLIMIT_NPTS`, `RLIMIT_SWAP`, `RLIMIT_KQUEUES` and `RLIMIT_UMTXP` report infinity as in FreeBSD's process-0 defaults, and `RLIMIT_DATA` mirrors the host limit; a payload process on a PS5 (firmware 12.70) reads 0x800000000 for both limits. `RLIMIT_NOFILE` reports, as both soft and hard limit, the number of descriptors after which AnyPS5 itself fails with `EMFILE`: on Windows the C runtime's 8192 low-level descriptors, which guest files, pipes and sockets all take, and as many stdio streams, which libkernel raises from 512 at load with [`_setmaxstdio`](https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/setmaxstdio), on Linux the host's `RLIMIT_NOFILE`, whose soft limit libkernel raises to the hard limit at load, and on macOS the smaller of the host's hard limit and `kern.maxfilesperproc`, which libkernel sets as both limits at load, since macOS fails with `EMFILE` at `kern.maxfilesperproc` whatever the soft limit. A payload process on a PS5 (firmware 12.70) reads 13952 for both limits, while `getdtablesize` returns 12555 and the process can open descriptors up to 12554. XPSemu (PPSA97358) reads `RLIMIT_NOFILE` in QEMU's `os_setup_limits` and raises the soft limit to the hard one when they differ. `RLIMIT_STACK` (0x200000), `RLIMIT_RSS` and `RLIMIT_MEMLOCK` (infinity) and `RLIMIT_NPROC` (1604) report the soft and hard limits a payload process reads on a PS5 (firmware 12.70). Guest locking still follows the host: `mlock` and `sceKernelMlock` fail at the host's hard `RLIMIT_MEMLOCK` on Linux, and on Windows when the process working set cannot grow, although `RLIMIT_MEMLOCK` reports infinity. There the main thread's stack is 0x200000 as well (`pthread_attr_get_np`); here the main thread runs on the host thread's stack, whose size can differ. Whether a title process reads the same limits is unverified. - [setrlimit](../../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) validates the resource and input in FreeBSD order, maps negative limits to infinity, and lowers a soft limit above the hard limit. A request other than `RLIMIT_NPROC` succeeds when its normalized values match `getrlimit`, or, for `RLIMIT_NOFILE`, when `rlim_max` does not exceed the current hard limit and `rlim_cur` does not exceed `rlim_max`, storing the updated limits for subsequent `getrlimit` queries; descriptor allocation enforcement of the lowered soft limit is not implemented, and changing other limits throws because guest limit state is not stored. In a payload process on a PS5 (firmware 12.70), `setrlimit(RLIMIT_NOFILE)` lowers a requested 13952 to 12555, as the hard and as the soft limit; here a request within the current hard limit is stored as given and one above it throws; setting `RLIMIT_STACK`, `RLIMIT_RSS` and `RLIMIT_MEMLOCK` to their current values succeeds and changes nothing, as here; setting `RLIMIT_NPROC` to its current 1604 also succeeds but leaves 1443. `RLIMIT_NPROC` follows FreeBSD 11 by clamping both values to 1 through `maxprocperuid`, stores the result and returns it from `getrlimit`. The initial 1604 and `maxprocperuid` 1443 follow that measurement: process 0's limit starts at `maxproc` ([`init_main.c`](https://github.com/freebsd/freebsd-src/blob/release/11.0.0/sys/kern/init_main.c#L550-L551)), `maxprocperuid` starts at nine tenths of it ([`subr_param.c`](https://github.com/freebsd/freebsd-src/blob/release/11.0.0/sys/kern/subr_param.c#L258)), and `setrlimit` applies that bound ([`kern_resource.c`](https://github.com/freebsd/freebsd-src/blob/release/11.0.0/sys/kern/kern_resource.c#L714-L718)). `kern.maxproc` fails with `ENOENT` in the payload process, so the values cannot be queried independently, and process creation does not enforce the stored limit. Java 25 LAN / DNS (PPSA99869) sets `rlim_cur = MIN(0x100000, rlim_max)` during VM initialization