From c69eb7027d6d4f8bd01e77681f8d97835f3cbc0f Mon Sep 17 00:00:00 2001 From: babyhuey <7751607+babyhuey@users.noreply.github.com> Date: Mon, 12 Oct 2026 12:36:26 -0400 Subject: [PATCH] feat(libkernel): report hw.model from sysctl --- .../System/src/SystemConfiguration.cpp | 30 +++++++++++++++++++ core/libs/tests/GuestSystemConfiguration.cpp | 23 ++++++++++++++ docs/dev/debt/libkernel.md | 1 + 3 files changed, 54 insertions(+) diff --git a/core/libs/prx/libkernel/System/src/SystemConfiguration.cpp b/core/libs/prx/libkernel/System/src/SystemConfiguration.cpp index c5c0f2220..672c9d291 100644 --- a/core/libs/prx/libkernel/System/src/SystemConfiguration.cpp +++ b/core/libs/prx/libkernel/System/src/SystemConfiguration.cpp @@ -2,6 +2,9 @@ #include "prx/libkernel/DirectMemory/DirectMemory.hpp" #include "prx/libkernel/KernelErrors.hpp" #include +#if defined(__x86_64__) || defined(__i386__) +#include +#endif #include #include #include @@ -41,6 +44,20 @@ int CopyOut(const void* value, std::size_t size, void* oldValue, std::size_t* ol return 0; } +std::string HostCpuModel() { +#if defined(__x86_64__) || defined(__i386__) + if (__get_cpuid_max(0x80000000, nullptr) < 0x80000004) return {}; + unsigned int brand[12] = {}; + for (unsigned int leaf = 0; leaf < 3; ++leaf) + __get_cpuid(0x80000002 + leaf, &brand[leaf * 4], &brand[leaf * 4 + 1], &brand[leaf * 4 + 2], &brand[leaf * 4 + 3]); + std::string model(reinterpret_cast(brand), strnlen(reinterpret_cast(brand), sizeof(brand))); + model.erase(0, model.find_first_not_of(' ')); + return model; +#else + return {}; +#endif +} + } extern "C" { @@ -101,6 +118,15 @@ int APS5_VABI sysctl_nid_postfix(const int* name, std::uint32_t nameLength, void const int value = static_cast(processors); return CopyOut(&value, sizeof(value), oldValue, oldLength); } + if (nameLength == 2 && name[0] == 6 && name[1] == 2) { + if (newValue != nullptr) { + *__error_nid_postfix() = 1; + return -1; + } + static const std::string model = HostCpuModel(); + if (model.empty()) throw std::runtime_error(std::string(__func__) + ": the host CPU has no CPUID brand string for hw.model"); + return CopyOut(model.c_str(), model.size() + 1, oldValue, oldLength); + } if (nameLength == 2 && name[0] == 6 && name[1] == 5) { if (newValue != nullptr) { *__error_nid_postfix() = 1; @@ -188,6 +214,10 @@ int APS5_VABI sysctlbyname_nid_postfix(const char* name, void* oldValue, std::si static constexpr int mib[] = {6, 3}; return sysctl_nid_postfix(mib, 2, oldValue, oldLength, newValue, newLength); } + if (std::strcmp(name, "hw.model") == 0) { + static constexpr int mib[] = {6, 2}; + return sysctl_nid_postfix(mib, 2, oldValue, oldLength, newValue, newLength); + } if (std::strcmp(name, "hw.physmem") == 0) { static constexpr int mib[] = {6, 5}; return sysctl_nid_postfix(mib, 2, oldValue, oldLength, newValue, newLength); diff --git a/core/libs/tests/GuestSystemConfiguration.cpp b/core/libs/tests/GuestSystemConfiguration.cpp index 069ef2767..1ad0f5c6f 100644 --- a/core/libs/tests/GuestSystemConfiguration.cpp +++ b/core/libs/tests/GuestSystemConfiguration.cpp @@ -86,6 +86,26 @@ static void CheckPhysicalMemorySysctl() { Require(sysctl_nid_postfix(mib, 2, &value, &length, &value, sizeof(value)) == -1 && *__error_nid_postfix() == 1); Require(sysctlbyname_nid_postfix("hw.physmem", &value, &length, &value, sizeof(value)) == -1 && *__error_nid_postfix() == 1); } +#if defined(__x86_64__) || defined(__i386__) +static void CheckModelSysctl() { + const int mib[] = {6, 2}; + *__error_nid_postfix() = 13; + std::size_t length = 0; + Require(sysctl_nid_postfix(mib, 2, nullptr, &length, nullptr, 0) == 0 && length > 1); + const std::size_t size = length; + char model[128] = {}; + length = sizeof(model); + Require(sysctlbyname_nid_postfix("hw.model", model, &length, nullptr, 0) == 0 && length == size); + Require(std::strlen(model) + 1 == size && model[0] != ' '); + Require(*__error_nid_postfix() == 13); + char truncated[4] = {}; + length = sizeof(truncated); + Require(sysctl_nid_postfix(mib, 2, truncated, &length, nullptr, 0) == -1 && *__error_nid_postfix() == 12); + Require(length == sizeof(truncated) && std::memcmp(truncated, model, sizeof(truncated)) == 0); + length = sizeof(model); + Require(sysctlbyname_nid_postfix("hw.model", model, &length, model, size) == -1 && *__error_nid_postfix() == 1); +} +#endif static void CheckMachdepCachelineSizeSysctl() { *__error_nid_postfix() = 13; int value = 0x5a5a5a5a; @@ -232,6 +252,9 @@ static void CheckArandomSysctl() { int main() { CheckProcessorCountSysctl(); CheckPhysicalMemorySysctl(); +#if defined(__x86_64__) || defined(__i386__) + CheckModelSysctl(); +#endif CheckMachdepCachelineSizeSysctl(); CheckPathnameSysctl(); CheckSystemSwVersion(); diff --git a/docs/dev/debt/libkernel.md b/docs/dev/debt/libkernel.md index 8781325f3..da792219e 100644 --- a/docs/dev/debt/libkernel.md +++ b/docs/dev/debt/libkernel.md @@ -128,6 +128,7 @@ - [execv](../../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) throws: replacing the process image is not supported, the same as `execvp`. PPSA97358 imports it and fails to load without it. - [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. - [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. +- [hw.model](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) reports the host CPU's name, 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`) - [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. `RLIMIT_STACK`, `RLIMIT_RSS`, `RLIMIT_MEMLOCK`, `RLIMIT_NPROC` and `RLIMIT_NOFILE` throw because no console values are established. - [execvp](../../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) throws: replacing the process image is not supported. PPSA85300 imports it and fails to load without it; its `fork` and `vfork` fail with `ENOSYS` ([libc_missing.c](https://github.com/ZiZc3/VLC/blob/f3253020a64368153233ede29183014c0b6c8979/ps5/compat/libc_missing.c#L290-L292)), so the child process that would call it is never created. - [socketpair](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) - the pair is the host's `AF_UNIX` pair on Linux and, on Windows, a loopback TCP connection for `SOCK_STREAM` or two connected UDP sockets for `SOCK_DGRAM`; `SOCK_SEQPACKET` throws on Windows. A datagram sent to a full receiver gets `ENOBUFS` on the console, waits or gets `EAGAIN` on Linux and is dropped on Windows, and on Windows a datagram `recv` after the peer closed reports `ECONNRESET` instead of the console's `ENOTCONN`, and a stream `send` after the peer closed reports `ECONNABORTED` instead of the console's `EPIPE`. `read` and `write` on a pair end go to the host like on every guest socket. `getpeername` on a pair end succeeds after the peer closed, where the console returns `ENOTCONN`, and `connect`, `bind` and `sendto` with an address on a pair end report an address-family error instead of the console's `EISCONN`. On Windows `poll` on a stream end reports `POLLHUP` without `POLLIN` after the peer's `shutdown(SHUT_WR)` or `close`, where the console reports `POLLIN`, with `POLLHUP` only after `close`