diff --git a/core/libs/prx/libkernel/System/src/SoftwareVersion.cpp b/core/libs/prx/libkernel/System/src/SoftwareVersion.cpp index 9c6bf6029c..7bba44ee84 100644 --- a/core/libs/prx/libkernel/System/src/SoftwareVersion.cpp +++ b/core/libs/prx/libkernel/System/src/SoftwareVersion.cpp @@ -21,8 +21,7 @@ constexpr std::uint32_t SdkPatchMask = 0xfff; extern "C" { int APS5_VABI sceKernelGetProsperoSystemSwVersion_nid_postfix(SceKernelProsperoSystemSwVersion* version) { - if (version == nullptr) return SCE_KERNEL_ERROR_EFAULT; - if (version->size != sizeof(SceKernelProsperoSystemSwVersion)) return SCE_KERNEL_ERROR_EINVAL; + if (version == nullptr) return 0; std::memset(version->versionString, 0, sizeof(version->versionString)); std::memcpy(version->versionString, SystemSoftwareVersionString, sizeof(SystemSoftwareVersionString)); version->version = SystemSoftwareVersion; diff --git a/core/libs/prx/libkernel/System/src/SystemConfiguration.cpp b/core/libs/prx/libkernel/System/src/SystemConfiguration.cpp index aec744cc05..b6c40105c9 100644 --- a/core/libs/prx/libkernel/System/src/SystemConfiguration.cpp +++ b/core/libs/prx/libkernel/System/src/SystemConfiguration.cpp @@ -599,7 +599,7 @@ int APS5_VABI sysctlbyname_nid_postfix(const char* name, void* oldValue, std::si int APS5_VABI sceKernelGetSystemSwVersion_nid_postfix(SceKernelSystemSwVersion* version) { if (version == nullptr) { - return SCE_KERNEL_ERROR_EFAULT; + return 0; } std::memset(version->versionString, 0, sizeof(version->versionString)); std::memcpy(version->versionString, "01.000.000", sizeof("01.000.000")); diff --git a/core/libs/tests/GuestSoftwareVersion.cpp b/core/libs/tests/GuestSoftwareVersion.cpp index 1343316e32..0dd66f019f 100644 --- a/core/libs/tests/GuestSoftwareVersion.cpp +++ b/core/libs/tests/GuestSoftwareVersion.cpp @@ -15,23 +15,22 @@ static void Require(bool value) { if (!value) std::abort(); } static void CheckProsperoSystemSwVersion() { *__error_nid_postfix() = 13; - Require(sceKernelGetProsperoSystemSwVersion_nid_postfix(nullptr) == SCE_KERNEL_ERROR_EFAULT); - const std::uint64_t sizes[] = {0, 0x18, 0x27, 0x30}; + Require(sceKernelGetProsperoSystemSwVersion_nid_postfix(nullptr) == 0); + const std::uint64_t sizes[] = {0, 0x18, 0x27, 0x28, 0x29, 0x30, 0x100, ~0ull}; for (const std::uint64_t size : sizes) { - SceKernelProsperoSystemSwVersion version{}; - std::memset(&version, 0xaa, sizeof(version)); - version.size = size; - Require(sceKernelGetProsperoSystemSwVersion_nid_postfix(&version) == SCE_KERNEL_ERROR_EINVAL); - Require(version.size == size && version.version == 0xaaaaaaaa && static_cast(version.versionString[0]) == 0xaa); + struct { + SceKernelProsperoSystemSwVersion version; + unsigned char suffix[0x40]; + } storage; + std::memset(&storage, 0xaa, sizeof(storage)); + storage.version.size = size; + Require(sceKernelGetProsperoSystemSwVersion_nid_postfix(&storage.version) == 0); + Require(storage.version.size == size); + Require(std::strcmp(storage.version.versionString, "01.000.000") == 0); + for (std::size_t i = sizeof("01.000.000"); i < sizeof(storage.version.versionString); ++i) Require(storage.version.versionString[i] == 0); + Require(storage.version.version == 0x01000000); + for (const unsigned char byte : storage.suffix) Require(byte == 0xaa); } - SceKernelProsperoSystemSwVersion version{}; - std::memset(&version, 0xaa, sizeof(version)); - version.size = sizeof(version); - Require(sceKernelGetProsperoSystemSwVersion_nid_postfix(&version) == 0); - Require(version.size == sizeof(version)); - Require(std::strcmp(version.versionString, "01.000.000") == 0); - for (std::size_t i = sizeof("01.000.000"); i < sizeof(version.versionString); ++i) Require(version.versionString[i] == 0); - Require(version.version == 0x01000000); Require(*__error_nid_postfix() == 13); } diff --git a/core/libs/tests/GuestSystemConfiguration.cpp b/core/libs/tests/GuestSystemConfiguration.cpp index 1b0355447d..9c19aa6e91 100644 --- a/core/libs/tests/GuestSystemConfiguration.cpp +++ b/core/libs/tests/GuestSystemConfiguration.cpp @@ -604,8 +604,8 @@ static void CheckVersionLimits() { } static void CheckSystemSwVersion() { *__error_nid_postfix() = 13; - Require(sceKernelGetSystemSwVersion_nid_postfix(nullptr) == SCE_KERNEL_ERROR_EFAULT && *__error_nid_postfix() == 13); - const std::uint64_t sizes[] = {0, 24, 39, 40, 41, 48, 64}; + Require(sceKernelGetSystemSwVersion_nid_postfix(nullptr) == 0 && *__error_nid_postfix() == 13); + const std::uint64_t sizes[] = {0, 1, 24, 39, 40, 41, 48, 64, 0x1000, ~0ull}; for (const std::uint64_t size : sizes) { struct { std::uint64_t prefix; diff --git a/docs/dev/debt/libkernel.md b/docs/dev/debt/libkernel.md index 67e5d0e246..4c6e3367f8 100644 --- a/docs/dev/debt/libkernel.md +++ b/docs/dev/debt/libkernel.md @@ -24,7 +24,7 @@ - [getlogin_r](../../../core/libs/prx/libkernel/System/src/Login.cpp) (libkernel) returns the host effective account name: `GetUserNameW` as UTF-8 on Windows and `getpwuid_r(geteuid())` on POSIX. A valid account returns a name even without a login session; host `getlogin_r`, `USER` and `LOGNAME` are not consulted. The console's login name and behaviour without a session are unknown. Account lookup or encoding failures, a missing or empty account name, and POSIX account records exceeding the 16 KiB lookup buffer throw; there is no replacement name or retry. A short guest buffer returns `ERANGE` without modifying it, and a null output returns `EFAULT`. - [getlogin](../../../core/libs/prx/libkernel/System/src/Getlogin.cpp) (libkernel) follows FreeBSD 11 `lib/libc/gen/getlogin.c` and returns a pointer to one static 33-byte buffer filled by `getlogin_r`; on an error it sets `errno` to `getlogin_r`'s code and returns NULL. These semantics are not checked against a PS5. It exports the test seam `LoginNameForTest_nid_no_patch`, which replaces the host account name for tests. - [setlogin](../../../core/libs/prx/libkernel/System/src/Login.cpp) (libkernel_sys) keeps one name for the process, where FreeBSD 11.4's [`sys_setlogin`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/kern/kern_prot.c#L2138-L2161) sets it for the session, and does not check `PRIV_PROC_SETLOGIN`, which a jailed process fails with `EPERM`; the console's answer is not known. As in FreeBSD, a null name fails with `EFAULT` and a name of 33 bytes or more (`MAXLOGNAME` with the NUL) with `EINVAL`, and nothing is stored. Once a name is stored, `getlogin_r` returns it instead of the host account name described for `getlogin_r`, and ahead of the `LoginNameForTest_nid_no_patch` seam. The name is copied from the guest with the same 33-byte bound as `copyinstr`, so a name with no NUL in its first 33 bytes fails with `EINVAL` without reading past them. -- [sceKernelGetSystemSwVersion](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) reports baseline system software version `0x01000000` ("01.000.000") for ProsperoStore (PPSA99000); the console's firmware version is not modeled. Measured on PS5 firmware 13.42: record values 0, 24, 39, 40, 41, 48 and 64 in the leading 64-bit field are accepted and preserved, the 28-byte string at offset 8 and packed version at 0x24 are filled, and bytes from 0x28 onward remain untouched. This API reports "13.590.001" / `0x13590001` in that environment, while the Prospero variant reports "13.420.000" / `0x13420000`; their values must not be assumed identical. A null pointer still returns `SCE_KERNEL_ERROR_EFAULT`, which was not measured; other firmware versions and record values remain unverified. +- [sceKernelGetSystemSwVersion](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) reports baseline system software version `0x01000000` ("01.000.000") for ProsperoStore (PPSA99000); the console's firmware version is not modeled. Measured on PS5 firmware 13.42: record values 0, 24, 39, 40, 41, 48 and 64 in the leading 64-bit field are accepted and preserved, the 28-byte string at offset 8 and packed version at 0x24 are filled, and bytes from 0x28 onward remain untouched. This API reports "13.590.001" / `0x13590001` in that environment, while the Prospero variant reports "13.420.000" / `0x13420000`; their values must not be assumed identical. Measured on 13.60: the same holds for values 1, 0x1000 and 2^64-1, this API still reports "13.590.001" while the Prospero variant reports "13.600.007", and a null pointer returns 0 without a fault or an `errno` change. Other firmware versions remain unverified. - [kern.rng_pseudo](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) - `sysctlbyname` draws a new block of 64 bytes from the host's `std::random_device` for each read (no new value) that passes a buffer and a length, and sizes its answers as a payload process on a PS5 (firmware 12.70) sees it: a size query reports 64; a buffer of 64 bytes or more gets 64 bytes and a length of 64 (tried up to 65536); a smaller buffer gets as many bytes as its length, which is left as given, and a null length gets none, both with -1 and `ENOMEM`; a null buffer with a null length returns 0. `errno` is kept when the call succeeds. A write returns -1 with `EPERM`, which was not tried on the console. The only caller seen is mbedTLS's `mbedtls_hardware_poll` in the catalog's ProsperoLight boilerplate, which asks for 64-byte blocks and fails if fewer come back. The console's generator is unknown, and a title process was not measured. On the console `sysctlnametomib` gives MIB {1, 50} for the name, and `sysctl` with it answers reads of 0, 1, 63, 64, 65 and 128 bytes, a size query, a null length and a null buffer with a null length as the name does; here `sysctlnametomib` maps the name to {1, 50} and `sysctl` {1, 50} answers as the name does; a write through the MIB was not measured and throws - [sysconf](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) - `_SC_CLK_TCK` returns 128, FreeBSD's `CLK_TCK` as its [`sysconf`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libc/gen/sysconf.c#L100-L101) returns it; the console's value is unverified and no `times` reports ticks at that rate. CPython in PS5MC (PPSA99420) requires it >= 1. `_SC_ARG_MAX` returns 262144, `_SC_CHILD_MAX` the `RLIMIT_NPROC` limit via `getrlimit`, `_SC_NGROUPS_MAX` 1023 (`KERN_NGROUPS`), `_SC_OPEN_MAX` the `RLIMIT_NOFILE` limit via `getrlimit`, `_SC_STREAM_MAX` that limit bounded by `SHRT_MAX`, `_SC_HOST_NAME_MAX` 255, `_SC_LOGIN_NAME_MAX` 33, and `_SC_IOV_MAX` 1024 (`UIO_MAXIOV`). Other names fail with `EINVAL` - [hw.realmem](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) - `sysctl` {`CTL_HW`, 12} and `sysctlbyname` report the same 8-byte value as `hw.physmem` (the `_SC_PHYS_PAGES` pages of 16 KiB), read-only with `EPERM` for a write and `ENOMEM` for a short buffer, as FreeBSD's [`sysctl_hw_realmem`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/kern_mib.c#L191-L204) does. FreeBSD reports there the memory the firmware describes, which is at least `hw.physmem`. A payload process on a PS5 (firmware 12.70) gets -1 with `ENOENT` for `hw.physmem` and `hw.realmem` (a read, a size query and a null length) and for {`CTL_HW`, 5} and {`CTL_HW`, 12} (a read and a size query); what a title process gets was not measured. Dolphin reads it as the machine's memory size in [`MemPhysical`](https://github.com/dolphin-emu/dolphin/blob/8d2ad1a662472955497e5bb79d7b9ec6999e1f0d/Source/Core/Common/MemoryUtil.cpp#L253-L273) (RetroArch PPSA99169's `dolphin_libretro` core) @@ -33,7 +33,7 @@ - [kern.ostype, kern.osrelease, kern.version, kern.hostname, hw.machine, hw.model](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) - `sysctl` (1.1, 1.2, 1.4, 1.10, 6.1, 6.2), `sysctlbyname` and `sysctlnametomib` serve read-only strings. A payload process on a PS5 (firmware 13.60) read "FreeBSD", "0.0-prototype", an empty host name, "amd64" and, for `hw.model`, "100-000001020" padded with spaces to 47 characters, which are returned here. Its `kern.version` was "r229358/releases/13.60 Jul 17 2026 02:16:29", which names the firmware, so the FreeBSD 11.4 text "FreeBSD 11.4-RELEASE\n" stays here. Whether another PS5 model reports another `hw.model` is not known. A short buffer gets as many bytes as fit and `ENOMEM`, as on the console, and a write `EPERM` - [kern.osreldate, kern.osrevision, kern.argmax, kern.ngroups](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) - `sysctl` (1.24, 1.3, 1.8, 1.18) and `sysctlbyname` report 900000, 199506, 262144 and 1023, the 4-byte values a payload process on a PS5 (firmware 13.60) read; a short buffer returns `ENOMEM` and a write `EPERM` - [kern.boottime](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) - `sysctl` {`CTL_KERN`, `KERN_BOOTTIME`} and `sysctlbyname` report a read-only 16-byte `struct timeval`, as FreeBSD 11.4's [`sysctl_kern_boottime`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/kern/kern_tc.c#L144-L163) does: the guest's `CLOCK_REALTIME` minus its `CLOCK_UPTIME`, taken at the first read and kept, so a later step of the host clock does not move it as FreeBSD's `tc_setclock` would. The value a PS5 title gets has not been measured. -- [sceKernelGetProsperoSystemSwVersion](../../../core/libs/prx/libkernel/System/src/SoftwareVersion.cpp) (libkernel) reports system software version `0x01000000` ("01.000.000"); the console's firmware version is not modeled. The 0x28-byte block (a 64-bit size at 0, the version string at 8 and the packed version at 0x24) follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Kernel/KernelSystem.cs), which Prospero Multi Tools (PPSA99110) and Prospero Explorer (PPSA99109) are built with and which sets the size to 40, and the same fields in [etaHEN](https://github.com/etaHEN/etaHEN/blob/main/Source%20Code/daemon/include/ipc.hpp) and [shsrv](https://github.com/ps5-payload-dev/shsrv/blob/master/sh.c). A size other than 0x28 returns `SCE_KERNEL_ERROR_EINVAL` without writing; whether the console checks the size is unverified, since etaHEN and shsrv declare a 0x30-byte block and do not set it +- [sceKernelGetProsperoSystemSwVersion](../../../core/libs/prx/libkernel/System/src/SoftwareVersion.cpp) (libkernel) reports system software version `0x01000000` ("01.000.000"); the console's firmware version is not modeled. The 0x28-byte block (a 64-bit size at 0, the version string at 8 and the packed version at 0x24) follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Kernel/KernelSystem.cs), which Prospero Multi Tools (PPSA99110) and Prospero Explorer (PPSA99109) are built with and which sets the size to 40, and the same fields in [etaHEN](https://github.com/etaHEN/etaHEN/blob/main/Source%20Code/daemon/include/ipc.hpp) and [shsrv](https://github.com/ps5-payload-dev/shsrv/blob/master/sh.c). Measured on PS5 firmware 13.60: every size (0, 0x27, 0x28, 0x29, 0x30, 0x100 and 2^64-1) returns 0 and is preserved, bytes 8 to 0x27 are written and nothing from 0x28 onward, and a null pointer returns 0 without a fault or an `errno` change - [sceKernelGetAllowedSdkVersionOnSystem](../../../core/libs/prx/libkernel/System/src/SoftwareVersion.cpp) (libkernel) reports the system software version with the 12 patch bits set, `0x01000fff`, as [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/98fd0689/src/core/libraries/kernel/kernel.cpp) does on the PS4 and as [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Kernel/KernelSystem.cs) describes it, which Prospero Explorer (PPSA99109) reads it through; a null pointer returns `SCE_KERNEL_ERROR_EINVAL`. The console's value and its errors are unverified - [sceKernelConvertUtcToLocaltime, sceKernelConvertLocaltimeToUtc](../../../core/libs/prx/libkernel/Time/Time.cpp) (libkernel) convert with the host time zone at the given instant, as libc `localtime` does, instead of the console's time zone setting. `west_sec` holds the standard-time offset in seconds east of UTC, despite its name, and `dst_sec` the daylight saving seconds in effect. This follows PPSA99203 ([PS5SX2](https://github.com/Swordpdf/PS5SX2/blob/b4cf55e87c34e8a2a1cd9302df4373e5a94d28f4/ps5/frontend/fe_ps5.cpp#L54-L63)), whose author saw a console write 7200 and 3600 into the 16-byte block; which zone that console was set to is not recorded, so whether `west_sec` includes daylight saving is not settled. [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/23c41333/src/core/libraries/kernel/time.cpp) also stores seconds east. PPSA99203 declares the last argument of `sceKernelConvertUtcToLocaltime` as `uint64_t*`; 32 bits are written. On Windows the standard offset and the daylight saving amount are the C runtime's current `_get_timezone` and `_get_dstbias` for every instant, as libc `localtime_s` uses them, so instants under a zone's earlier rules get today's offsets; a `TZ` variable does not set `_dstbias`, which keeps the value an earlier `_tzset` read from the system (0 when the system zone has no daylight saving), and summer time then adds nothing. In a repeated or skipped local hour, `sceKernelConvertLocaltimeToUtc` takes the offset in effect one standard offset earlier. Its last argument receives the zone's daylight saving amount whether or not it is in effect, found by sampling the host zone over the year around the result, while `dst_sec` is the amount in effect: the libc `mktime` that PPSA99109 bundles (text 0x70ebb-0x70ee5) subtracts that amount when `tm_isdst` is set outside summer time. That `mktime` passes `tm_isdst` as the second argument, which is ignored, so it does not choose the offset in a repeated hour. The `gettimeofday` time zone still reports no offset and no daylight saving. A host time the C runtime cannot convert throws - [sceKernelConvertUtcToLocaltime and sceKernelConvertLocaltimeToUtc, measured](../../../core/libs/prx/libkernel/Time/Time.cpp) (libkernel) - a payload process on a PS5 (firmware 13.60) set to UTC-3 with Brazil's history differs from AnyPS5 in two ways. The 16-byte block's first 8 bytes hold the zone's last transition at or before the instant (`1550368800`, the end of summer time in February 2019, for every 2026 and 2038 date; `-57967200` for 1970), not the instant. The last argument is 3600 in every case but `sceKernelConvertLocaltimeToUtc` at 1970-01-01 (0), even in 2026, when the zone has had no summer time since 2019. The offsets, `dst_sec` and the converted times match the zone's history, and both calls return 0 with null outputs. Reproducing the transition needs the zone's full history, which the Windows C runtime does not have