mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 08:33:12 -05:00
Merge pull request #7541 from ionut27001/fix/libkernel-sysctl-absent-names
fix(libkernel): return ENOENT for thirteen sysctl names, as measured on a PS5
This commit is contained in:
3 files changed
+149
-12
No files matched your search
@@ -136,6 +136,30 @@ std::vector<GuestVmEntry> GuestVmEntries() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
bool IsUnansweredMib(const int* name, std::uint32_t nameLength) {
|
||||
if (nameLength != 2) return false;
|
||||
return (name[0] == 1 && (name[1] == 6 || name[1] == 7 || name[1] == 12 || name[1] == 27 || name[1] == 32)) || (name[0] == 6 && (name[1] == 4 || name[1] == 11));
|
||||
}
|
||||
|
||||
bool IsUnansweredName(const char* name) {
|
||||
static constexpr const char* unanswered[] = {
|
||||
"hw.byteorder",
|
||||
"hw.cacheline_size",
|
||||
"hw.machine_arch",
|
||||
"hw.usermem",
|
||||
"kern.clockrate",
|
||||
"kern.maxfiles",
|
||||
"kern.maxfilesperproc",
|
||||
"kern.maxproc",
|
||||
"kern.ps_strings",
|
||||
"kern.sched.name",
|
||||
"kern.smp.maxcpus",
|
||||
"kern.stackprot",
|
||||
"kern.timecounter.hardware",
|
||||
};
|
||||
return std::any_of(std::begin(unanswered), std::end(unanswered), [name](const char* entry) { return std::strcmp(name, entry) == 0; });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
@@ -207,6 +231,11 @@ int APS5_VABI sysctl_nid_postfix(const int* name, std::uint32_t nameLength, void
|
||||
*__error_nid_postfix() = 2;
|
||||
return -1;
|
||||
}
|
||||
if (IsUnansweredMib(name, nameLength)) {
|
||||
if (newValue != nullptr) throw std::runtime_error(std::string(__func__) + ": writing MIB " + std::to_string(name[0]) + "." + std::to_string(name[1]));
|
||||
*__error_nid_postfix() = 2;
|
||||
return -1;
|
||||
}
|
||||
if (nameLength == 2 && name[0] == 6 && name[1] == 7) {
|
||||
if (newValue != nullptr) {
|
||||
*__error_nid_postfix() = 1;
|
||||
@@ -331,6 +360,11 @@ int APS5_VABI sysctlbyname_nid_postfix(const char* name, void* oldValue, std::si
|
||||
for (const auto& entry : SystemStrings) {
|
||||
if (std::strcmp(name, entry.name) == 0) return sysctl_nid_postfix(entry.mib, 2, oldValue, oldLength, newValue, newLength);
|
||||
}
|
||||
if (IsUnansweredName(name)) {
|
||||
if (newValue != nullptr) throw std::runtime_error(std::string(__func__) + ": writing " + name);
|
||||
*__error_nid_postfix() = 2;
|
||||
return -1;
|
||||
}
|
||||
if (std::strcmp(name, "kern.proc.pathname") == 0) {
|
||||
static constexpr int mib[] = {1, 14, 12};
|
||||
return sysctl_nid_postfix(mib, 3, oldValue, oldLength, newValue, newLength);
|
||||
@@ -411,14 +445,21 @@ int APS5_VABI sysctlnametomib_nid_postfix(const char* name, int* mib, std::size_
|
||||
std::size_t length;
|
||||
};
|
||||
static constexpr KnownName known[] = {
|
||||
{"hw.byteorder", {6, 4}, 2},
|
||||
{"hw.machine_arch", {6, 11}, 2},
|
||||
{"hw.model", {6, 2}, 2},
|
||||
{"hw.ncpu", {6, 3}, 2},
|
||||
{"hw.pagesize", {6, 7}, 2},
|
||||
{"hw.physmem", {6, 5}, 2},
|
||||
{"hw.realmem", {6, 12}, 2},
|
||||
{"kern.arandom", {1, 37}, 2},
|
||||
{"kern.rng_pseudo", {1, 50}, 2},
|
||||
{"kern.clockrate", {1, 12}, 2},
|
||||
{"kern.maxfiles", {1, 7}, 2},
|
||||
{"kern.maxfilesperproc", {1, 27}, 2},
|
||||
{"kern.maxproc", {1, 6}, 2},
|
||||
{"kern.proc.pathname", {1, 14, 12}, 3},
|
||||
{"kern.ps_strings", {1, 32}, 2},
|
||||
{"kern.rng_pseudo", {1, 50}, 2},
|
||||
};
|
||||
constexpr std::size_t MaximumPath = 1024;
|
||||
if (name == nullptr || size == nullptr) throw std::invalid_argument(std::string(__func__) + ": null name or size");
|
||||
@@ -427,7 +468,7 @@ int APS5_VABI sysctlnametomib_nid_postfix(const char* name, int* mib, std::size_
|
||||
*__error_nid_postfix() = 63;
|
||||
return -1;
|
||||
}
|
||||
if (nameLength == 0 || std::strcmp(name, "machdep.cacheline_size") == 0) {
|
||||
if (nameLength == 0 || std::strcmp(name, "hw.cacheline_size") == 0 || std::strcmp(name, "hw.usermem") == 0 || std::strcmp(name, "machdep.cacheline_size") == 0) {
|
||||
*__error_nid_postfix() = 2;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -152,13 +152,109 @@ static void CheckTscFrequencySysctl() {
|
||||
length = sizeof(value);
|
||||
Require(sysctlbyname_nid_postfix("machdep.tsc_freq", &value, &length, &value, sizeof(value)) == -1 && *__error_nid_postfix() == 1);
|
||||
}
|
||||
static void CheckMachdepCachelineSizeSysctl() {
|
||||
static bool Untouched(const unsigned char* bytes, std::size_t size) {
|
||||
for (std::size_t i = 0; i < size; ++i)
|
||||
if (bytes[i] != 0xaa) return false;
|
||||
return true;
|
||||
}
|
||||
template <typename TCall>
|
||||
static bool Throws(TCall call) {
|
||||
try {
|
||||
call();
|
||||
} catch (const std::runtime_error&) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static void CheckUnansweredSysctl() {
|
||||
static constexpr const char* names[] = {
|
||||
"hw.byteorder",
|
||||
"hw.cacheline_size",
|
||||
"hw.machine_arch",
|
||||
"hw.usermem",
|
||||
"kern.clockrate",
|
||||
"kern.maxfiles",
|
||||
"kern.maxfilesperproc",
|
||||
"kern.maxproc",
|
||||
"kern.ps_strings",
|
||||
"kern.sched.name",
|
||||
"kern.smp.maxcpus",
|
||||
"kern.stackprot",
|
||||
"kern.timecounter.hardware",
|
||||
"machdep.cacheline_size",
|
||||
};
|
||||
unsigned char buffer[64];
|
||||
for (const char* name : names) {
|
||||
std::memset(buffer, 0xaa, sizeof(buffer));
|
||||
std::size_t length = sizeof(buffer);
|
||||
*__error_nid_postfix() = 13;
|
||||
Require(sysctlbyname_nid_postfix(name, buffer, &length, nullptr, 0) == -1 && *__error_nid_postfix() == 2);
|
||||
Require(length == sizeof(buffer) && Untouched(buffer, sizeof(buffer)));
|
||||
length = 77;
|
||||
*__error_nid_postfix() = 13;
|
||||
Require(sysctlbyname_nid_postfix(name, nullptr, &length, nullptr, 0) == -1 && *__error_nid_postfix() == 2 && length == 77);
|
||||
*__error_nid_postfix() = 13;
|
||||
Require(sysctlbyname_nid_postfix(name, buffer, nullptr, nullptr, 0) == -1 && *__error_nid_postfix() == 2);
|
||||
Require(Untouched(buffer, sizeof(buffer)));
|
||||
*__error_nid_postfix() = 13;
|
||||
Require(sysctlbyname_nid_postfix(name, nullptr, nullptr, nullptr, 0) == -1 && *__error_nid_postfix() == 2);
|
||||
if (std::strcmp(name, "machdep.cacheline_size") == 0) continue;
|
||||
length = sizeof(buffer);
|
||||
Require(Throws([&] { sysctlbyname_nid_postfix(name, buffer, &length, buffer, sizeof(buffer)); }));
|
||||
}
|
||||
struct Numbered {
|
||||
const char* name;
|
||||
int mib[2];
|
||||
};
|
||||
static constexpr Numbered numbered[] = {
|
||||
{"hw.byteorder", {6, 4}},
|
||||
{"hw.machine_arch", {6, 11}},
|
||||
{"kern.clockrate", {1, 12}},
|
||||
{"kern.maxfiles", {1, 7}},
|
||||
{"kern.maxfilesperproc", {1, 27}},
|
||||
{"kern.maxproc", {1, 6}},
|
||||
{"kern.ps_strings", {1, 32}},
|
||||
};
|
||||
for (const auto& entry : numbered) {
|
||||
int mib[4] = {0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a};
|
||||
std::size_t size = 4;
|
||||
*__error_nid_postfix() = 13;
|
||||
Require(sysctlnametomib_nid_postfix(entry.name, mib, &size) == 0 && size == 2);
|
||||
Require(mib[0] == entry.mib[0] && mib[1] == entry.mib[1] && mib[2] == 0x5a5a5a5a);
|
||||
Require(*__error_nid_postfix() == 13);
|
||||
std::memset(buffer, 0xaa, sizeof(buffer));
|
||||
std::size_t length = sizeof(buffer);
|
||||
Require(sysctl_nid_postfix(mib, 2, buffer, &length, nullptr, 0) == -1 && *__error_nid_postfix() == 2);
|
||||
Require(length == sizeof(buffer) && Untouched(buffer, sizeof(buffer)));
|
||||
length = 77;
|
||||
*__error_nid_postfix() = 13;
|
||||
Require(sysctl_nid_postfix(mib, 2, nullptr, &length, nullptr, 0) == -1 && *__error_nid_postfix() == 2 && length == 77);
|
||||
*__error_nid_postfix() = 13;
|
||||
Require(sysctl_nid_postfix(mib, 2, buffer, nullptr, nullptr, 0) == -1 && *__error_nid_postfix() == 2 && Untouched(buffer, sizeof(buffer)));
|
||||
*__error_nid_postfix() = 13;
|
||||
Require(sysctl_nid_postfix(mib, 2, nullptr, nullptr, nullptr, 0) == -1 && *__error_nid_postfix() == 2);
|
||||
length = sizeof(buffer);
|
||||
Require(Throws([&] { sysctl_nid_postfix(mib, 2, buffer, &length, buffer, sizeof(buffer)); }));
|
||||
}
|
||||
int mib[4] = {0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a};
|
||||
std::size_t size = 0;
|
||||
*__error_nid_postfix() = 13;
|
||||
int value = 0x5a5a5a5a;
|
||||
std::size_t length = sizeof(value);
|
||||
Require(sysctlbyname_nid_postfix("machdep.cacheline_size", &value, &length, nullptr, 0) == -1);
|
||||
Require(*__error_nid_postfix() == 2);
|
||||
Require(value == 0x5a5a5a5a);
|
||||
Require(sysctlnametomib_nid_postfix("kern.maxproc", nullptr, &size) == 0 && size == 2 && *__error_nid_postfix() == 13);
|
||||
size = 1;
|
||||
Require(sysctlnametomib_nid_postfix("kern.maxproc", mib, &size) == -1 && *__error_nid_postfix() == 12);
|
||||
Require(size == 1 && mib[0] == 1 && mib[1] == 0x5a5a5a5a);
|
||||
mib[0] = 0x5a5a5a5a;
|
||||
size = 0;
|
||||
Require(sysctlnametomib_nid_postfix("kern.maxproc", mib, &size) == -1 && *__error_nid_postfix() == 12);
|
||||
Require(size == 0 && mib[0] == 0x5a5a5a5a);
|
||||
static constexpr const char* unresolved[] = {"hw.cacheline_size", "hw.usermem", "machdep.cacheline_size"};
|
||||
for (const char* name : unresolved) {
|
||||
int untouched[4] = {0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a};
|
||||
size = 4;
|
||||
*__error_nid_postfix() = 13;
|
||||
Require(sysctlnametomib_nid_postfix(name, untouched, &size) == -1 && *__error_nid_postfix() == 2);
|
||||
Require(size == 4 && untouched[0] == 0x5a5a5a5a && untouched[1] == 0x5a5a5a5a && untouched[2] == 0x5a5a5a5a);
|
||||
}
|
||||
}
|
||||
static void CheckPathnameSysctl() {
|
||||
*__error_nid_postfix() = 13;
|
||||
@@ -663,7 +759,7 @@ int main() {
|
||||
CheckModelSysctl();
|
||||
#endif
|
||||
CheckTscFrequencySysctl();
|
||||
CheckMachdepCachelineSizeSysctl();
|
||||
CheckUnansweredSysctl();
|
||||
CheckPathnameSysctl();
|
||||
CheckSystemStringSysctl();
|
||||
CheckSystemStringNames();
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
- [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.
|
||||
- [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 ([`time.h`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/include/time.h#L48-L54)); the console's value is not checked, and no `times` reports ticks at that rate. CPython's `posix` module stops its import with "cannot read ticks_per_second" when the value is below 1, as the Python in PS5MC (PPSA99420) does. `_SC_ARG_MAX` returns 262144 (FreeBSD's `NCARGS`), `_SC_OPEN_MAX` returns the `RLIMIT_NOFILE` soft limit via `getrlimit` ([`sysconf.c`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libc/gen/sysconf.c#L106-L115)), and `_SC_IOV_MAX` returns 1024 (FreeBSD's `UIO_MAXIOV`). Names other than `_SC_ARG_MAX`, `_SC_CLK_TCK`, `_SC_OPEN_MAX`, `_SC_PAGESIZE`, `_SC_IOV_MAX`, `_SC_NPROCESSORS_CONF`, `_SC_NPROCESSORS_ONLN` and `_SC_PHYS_PAGES` 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`; the console's values are not known. 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)
|
||||
- [vm.overcommit](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) - `sysctlbyname` reports the 4-byte value 0, the default FreeBSD gives its [`vm.overcommit`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/vm/swap_pager.c#L158-L161) `int`, with `ENOMEM` for a short buffer; the console's value is not known and a write throws. mimalloc reads it in [`unix_detect_overcommit`](https://github.com/microsoft/mimalloc/blob/31d034d94cdb8e22f7d7ed55967f581a2d6e831d/src/prim/unix/prim.c#L129-L153) and takes 0 as no overcommit, as PS5MC (PPSA99420) does at start-up
|
||||
- [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)
|
||||
- [vm.overcommit](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) - `sysctlbyname` reports the 4-byte value 0, the default FreeBSD gives its [`vm.overcommit`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/vm/swap_pager.c#L158-L161) `int`, with `ENOMEM` for a short buffer, and a write throws. A payload process on a PS5 (firmware 12.70) gets -1 with `ENOENT` for it, for a read, a size query and a null length; what a title process gets was not measured. mimalloc reads it in [`unix_detect_overcommit`](https://github.com/microsoft/mimalloc/blob/31d034d94cdb8e22f7d7ed55967f581a2d6e831d/src/prim/unix/prim.c#L129-L153) and takes 0 as no overcommit, as PS5MC (PPSA99420) does at start-up
|
||||
- [kern.sdk_version](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) - `sysctlbyname` reports the system software version as the 4-byte value `0x01000000` (1.00), the value `sceKernelGetSystemSwVersion` reports; a write returns -1 with `EPERM`. The major version is byte 3 and the minor version byte 2, in hex digits that read as decimal (13.60 is `0x1360xxxx`), as [Capy Mobile](https://github.com/khalifa007/CapyMobile/blob/2758c016/src/ps5/platform.c#L64-L71) (PPSA92280), [remote_lua_loader](https://github.com/n0llptr/remote_lua_loader/blob/7c2840e475/savedata/misc.lua#L195-L209) and the [lapse payload](https://github.com/NetflixNHack/Netflix-N-Hack/blob/605eb93ba5/PS4/payloads/lapse.js#L436-L450) read it. Capy Mobile only displays it. The console's firmware version is not modeled and the low two bytes are unknown
|
||||
- [kern.ostype, kern.osrelease, kern.version, kern.hostname, hw.machine](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) - `sysctl` (1.1, 1.2, 1.4, 1.10, 6.1), `sysctlbyname` and `sysctlnametomib` serve the names `uname` reads as read-only strings: "FreeBSD", "11.4-RELEASE" and "FreeBSD 11.4-RELEASE\n", the values FreeBSD 11.4-RELEASE builds without source control information ([newvers.sh](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/conf/newvers.sh#L45-L52), [L293-L294](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/conf/newvers.sh#L293-L294)), the host name `gethostname` reports ("AnyPS5") and "amd64"; a short buffer returns `ENOMEM` and a write `EPERM`. The console's strings are not 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
|
||||
@@ -166,7 +166,7 @@
|
||||
- [sysarch](../../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) - `AMD64_GET_FSBASE`, `AMD64_GET_GSBASE` and `AMD64_SET_GSBASE` follow FreeBSD 11's amd64 [`sysarch`](https://github.com/freebsd/freebsd-src/blob/release/11.0.0/sys/amd64/amd64/sys_machdep.c) on Linux x86-64 hosts, through the host's `arch_prctl`. The GS base belongs to the calling thread, a base at or above `0x800000000000` (`VM_MAXUSER_ADDRESS`) returns `EINVAL`, and a null argument returns `EFAULT`. `AMD64_SET_FSBASE` throws, since guest and host code share the thread's FS base for their TLS. In a payload process on a PS5 (firmware 12.70), `I386_GET_FSBASE` (7) and `I386_GET_GSBASE` (9) wrote 4 bytes, the low 32 bits of the FS and GS bases, and failed with `EFAULT` for a null argument; here they do the same on Linux x86-64 hosts. There a GS base set by one thread left the main thread's unchanged, and threads made with `pthread_create` and `scePthreadCreate` started with their creator's GS base; here a guest thread's host thread is created by the creating thread, and Linux's [`copy_thread`](https://github.com/torvalds/linux/blob/v6.6/arch/x86/kernel/process.c#L182-L186) gives a new thread its creator's GS base. The operation numbers FreeBSD 11's amd64 `sysarch` has no case for returned `EINVAL` there for the nine tried, each with a null argument (2, 5, 6, 12, 127, 133, 134, 1000 and -1; 6 is `I386_VM86`, which its header names), as its [default case](https://github.com/freebsd/freebsd-src/blob/release/11.0.0/sys/amd64/amd64/sys_machdep.c#L324-L325) does; here every such number returns `EINVAL` on every host; the numbers other than those nine follow that default case and were not measured. `AMD64_GET_XFPUSTATE` (132) returned 0 there for lengths 0, 64, 128, 256 and 512 (512 was also the largest length a search accepted) and failed with `EINVAL` for 1024, 2048, 4096 and 8192 and with `EFAULT` for a null argument or address, and `I386_GET_XFPUSTATE` (11) failed with `EFAULT` for a null argument; both throw here. The other operations FreeBSD 11's amd64 `sysarch` handles, and every operation it handles on other hosts (Windows keeps its TEB in GS), throw.
|
||||
- [kern.proc.pathname](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) returns `/app0/eboot.bin` for pid -1 and the calling process, following FreeBSD's `sysctl_kern_proc_pathname`; the exact pathname on retail consoles remains unverified.
|
||||
- [kern.proc.vmmap](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) - `sysctl` {`CTL_KERN`, `KERN_PROC`, `KERN_PROC_VMMAP`, pid} returns packed FreeBSD 11 `kinfo_vmentry` records (144 bytes each, an empty path) for the used ranges of the guest address-space arena, in address order, as [`kinfo_getvmmap`](https://github.com/freebsd/freebsd-src/blob/releng/11.0/lib/libutil/kinfo_getvmmap.c) reads them. A range is one record: the type is always `KVME_TYPE_DEFAULT`, the protection is `sceKernelVirtualQuery`'s for the range's first page (0 where it reports none), and offset, object, resident page counts, flags and path are 0. Only the calling process's pid is accepted (`ESRCH` otherwise, -1 included, as FreeBSD's `pget` gives); a write returns `EPERM` and a short buffer `ENOMEM`. Ranges outside the arena, which guest code cannot map, are not reported. The walk takes the arena lock once per range, not for the whole walk as FreeBSD holds the map lock, so an `mmap` or `munmap` on another thread can change the list between two records. Whether a PS5 application may read its own map, and what types and paths the console reports, is unverified
|
||||
- [machdep.cacheline_size](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) returns -1 with `ENOENT`, matching FreeBSD amd64 where this sysctl OID is not defined.
|
||||
- [sysctl names a PS5 payload process gets ENOENT for](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) - `sysctlbyname` returns -1 with `ENOENT` for `hw.byteorder`, `hw.cacheline_size`, `hw.machine_arch`, `hw.usermem`, `kern.clockrate`, `kern.maxfiles`, `kern.maxfilesperproc`, `kern.maxproc`, `kern.ps_strings`, `kern.sched.name`, `kern.smp.maxcpus`, `kern.stackprot`, `kern.timecounter.hardware` and `machdep.cacheline_size`, and `sysctl` does the same for the MIBs {1, 6}, {1, 7}, {1, 12}, {1, 27}, {1, 32}, {6, 4} and {6, 11}. The buffer and the length are left alone. A payload process on a PS5 (firmware 12.70) gets this for a read, a size query, a null length and a null buffer with a null length, by name and by MIB. `sysctlnametomib` gives those seven MIBs, and returns `ENOENT` for `hw.cacheline_size`, `hw.usermem` and `machdep.cacheline_size` without touching the count or the first three words of the MIB, as the console does. A write to these names and MIBs throws, except for `machdep.cacheline_size`, which returns `ENOENT` as FreeBSD amd64 does for an OID it does not define; the console's answer to a write was not tried. The console's `sysctlnametomib` gives `kern.smp.maxcpus`, `kern.stackprot`, `kern.sched.name` and `kern.timecounter.hardware` the MIBs {1, 2147482816, 2147482814}, {1, 2147483151}, {1, 2147483182, 2147483181} and {1, 2147482838, 2147482834}, and its `sysctl` returns `ENOENT` for them; here `sysctlnametomib` throws for them. `hw.physmem`, `hw.realmem` and `vm.overcommit` return `ENOENT` on the console too but report values here, as the `hw.realmem` and `vm.overcommit` entries say. A payload process also gets `ENOENT` for `kern.anyps5_no_such_name`, which its `sysctlnametomib` does not resolve; other names still throw here. A title process was not measured
|
||||
- [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)
|
||||
|
||||
Reference in new issue
Block a user