Merge pull request #10110 from BunnyFl/feat/libkernel-version-trace-queries

feat(libkernel): implement the system-ex version, lower-limit and trace memory queries
This commit is contained in:
Dean authored and GitHub committed 2026-10-25 01:53:38 +13:00
commit 9a1ea5de1b
6 files changed
+75 -20

No files matched your search

@@ -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);
@@ -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;
@@ -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;
+14
View File
@@ -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();
@@ -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<char*>(first + 1), reinterpret_cast<char*>(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<char*>(first), nullptr) == 22 && first[0] == 0xaa);
Require(sceKernelGetSystemExVersion(nullptr, reinterpret_cast<char*>(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<char*>(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();
+1
View File
@@ -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