diff --git a/core/libs/prx/libkernel/Time/Time.cpp b/core/libs/prx/libkernel/Time/Time.cpp index 4513d7604..1432b9f07 100644 --- a/core/libs/prx/libkernel/Time/Time.cpp +++ b/core/libs/prx/libkernel/Time/Time.cpp @@ -31,7 +31,9 @@ extern "C" int* APS5_VABI __error_nid_postfix(); +static constexpr int GUEST_EFAULT = 14; static constexpr int GUEST_EINVAL = 22; +static constexpr int GUEST_CLOCK_REALTIME = 0; static constexpr int GUEST_CLOCK_THREAD_CPUTIME_ID = 14; static bool IsCpuClock(int clockId) { @@ -550,6 +552,20 @@ int APS5_VABI clock_getres_nid_postfix(int clockId, KernelTimespec* res) { #endif } +int APS5_VABI clock_settime_nid_postfix(int clockId, const KernelTimespec* tp) { + if (tp == nullptr) { + *__error_nid_postfix() = GUEST_EFAULT; + return -1; + } + if (clockId != GUEST_CLOCK_REALTIME || tp->tv_nsec < 0 || tp->tv_nsec >= 1000000000 || tp->tv_sec < 0) { + *__error_nid_postfix() = GUEST_EINVAL; + return -1; + } + char requested[48]; + std::snprintf(requested, sizeof(requested), "%lld.%09lld", static_cast(tp->tv_sec), static_cast(tp->tv_nsec)); + throw std::runtime_error(std::string("clock_settime: setting CLOCK_REALTIME to ") + requested + " is not supported, the host clock is not changed"); +} + // --------------------------------------------------------------------------- // Moved as-is (not yet implemented) from the monolithic libkernel/Export.cpp. // --------------------------------------------------------------------------- diff --git a/core/libs/tests/GuestFreebsdClocks.cpp b/core/libs/tests/GuestFreebsdClocks.cpp index 9353846f0..e1f1b5256 100644 --- a/core/libs/tests/GuestFreebsdClocks.cpp +++ b/core/libs/tests/GuestFreebsdClocks.cpp @@ -4,11 +4,13 @@ #include #include #include +#include #include extern "C" { int APS5_VABI clock_gettime_nid_postfix(int clockId, KernelTimespec* tp); int APS5_VABI clock_getres_nid_postfix(int clockId, KernelTimespec* res); +int APS5_VABI clock_settime_nid_postfix(int clockId, const KernelTimespec* tp); int APS5_VABI sceKernelClockGettime(KernelClockid clockId, KernelTimespec* tp); int APS5_VABI sceKernelClockGetres(KernelClockid clockId, KernelTimespec* tp); int* APS5_VABI __error_nid_postfix(); @@ -25,7 +27,9 @@ static constexpr int GUEST_CLOCK_REALTIME = 0; static constexpr int GUEST_CLOCK_VIRTUAL = 1; static constexpr int GUEST_CLOCK_PROF = 2; static constexpr int GUEST_CLOCK_MONOTONIC = 4; +static constexpr int GUEST_CLOCK_REALTIME_PRECISE = 9; static constexpr int GUEST_CLOCK_SECOND = 13; +static constexpr int GUEST_CLOCK_THREAD_CPUTIME_ID = 14; static constexpr int GUEST_CLOCK_PROCESS_CPUTIME_ID = 15; static constexpr std::int64_t NANOS_PER_SECOND = 1000000000LL; @@ -205,9 +209,43 @@ static void CalendarConversionsInitializeTheirState() { Require(sceKernelConvertUtcToLocaltime(0, nullptr, nullptr, nullptr) == SCE_OK); } +static void RequireSettimeError(int clockId, const KernelTimespec* time, int error) { + *__error_nid_postfix() = 0; + Require(clock_settime_nid_postfix(clockId, time) == -1 && *__error_nid_postfix() == error); +} + +static bool SettimeThrows(const KernelTimespec& time, const char* requested) { + try { + clock_settime_nid_postfix(GUEST_CLOCK_REALTIME, &time); + } catch (const std::runtime_error& error) { + return std::strstr(error.what(), requested) != nullptr; + } + return false; +} + +static void SettimeValidatesLikeFreebsd() { + const KernelTimespec valid{1767225600, 5}; + RequireSettimeError(GUEST_CLOCK_REALTIME, nullptr, 14); + RequireSettimeError(GUEST_CLOCK_MONOTONIC, nullptr, 14); + for (const int clockId : {GUEST_CLOCK_VIRTUAL, GUEST_CLOCK_PROF, GUEST_CLOCK_MONOTONIC, GUEST_CLOCK_REALTIME_PRECISE, + GUEST_CLOCK_SECOND, GUEST_CLOCK_THREAD_CPUTIME_ID, GUEST_CLOCK_PROCESS_CPUTIME_ID, -1, 16}) + RequireSettimeError(clockId, &valid, 22); + const KernelTimespec negativeNanoseconds{0, -1}; + const KernelTimespec wholeSecondOfNanoseconds{0, NANOS_PER_SECOND}; + const KernelTimespec negativeSeconds{-1, 0}; + RequireSettimeError(GUEST_CLOCK_REALTIME, &negativeNanoseconds, 22); + RequireSettimeError(GUEST_CLOCK_REALTIME, &wholeSecondOfNanoseconds, 22); + RequireSettimeError(GUEST_CLOCK_REALTIME, &negativeSeconds, 22); + RequireSettimeError(GUEST_CLOCK_MONOTONIC, &negativeSeconds, 22); + Require(SettimeThrows(valid, "1767225600.000000005")); + Require(SettimeThrows(KernelTimespec{0, 0}, "0.000000000")); + Require(SettimeThrows(KernelTimespec{1, NANOS_PER_SECOND - 1}, "1.999999999")); +} + int main() { if (!OmittedResolutionSucceeds()) return 1; UnsupportedResolutionStillThrows(); + SettimeValidatesLikeFreebsd(); CalendarConversionsInitializeTheirState(); SecondClockReportsWholeSeconds(); KernelTimezone zone{-1, -1}; diff --git a/docs/dev/debt/libkernel.md b/docs/dev/debt/libkernel.md index cbc5f9504..e5441d53e 100644 --- a/docs/dev/debt/libkernel.md +++ b/docs/dev/debt/libkernel.md @@ -166,3 +166,4 @@ - [pthread_cond_reltimedwait_np](../../../core/libs/prx/libkernel/Pthread/Posix/Cond.cpp) (libkernel) takes `(cond, mutex, usec)`, a relative timeout in microseconds, and is `scePthreadCondTimedwait` with POSIX error numbers. FreeBSD 11.4 has no such function; the signature comes only from shadPS4, which registers one function for this NID and for `scePthreadCondTimedwait` ([`condvar.cpp`](https://github.com/shadps4-emu/shadPS4/blob/3a9f57265d28975658f1ca941daa5c879c7a74bd/src/core/libraries/kernel/threads/condvar.cpp#L229-L234)). shadPS4 reads a 64-bit count; here it is the 32-bit `SceKernelUseconds` of the SCE twin, as for `sem_reltimedwait_np`. Unverified on the console - [sysctl kern.arandom](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) serves FreeBSD 11's MIB {1, 37} (CTL_KERN, KERN_ARANDOM) and the "kern.arandom" sysctlbyname lookup through std::random_device, returning min(*oldLength, 256) random bytes. Following FreeBSD's sysctl_kern_arnd, a null buffer reports min(*oldLength, 256) without writing, a null length returns 0 without writing, and requests with a new value return EPERM (1) as the node is read-only. Cryptographic quality depends on the host's random device implementation. - [sceKernelGetPageTableStats](../../../core/libs/prx/libkernel/DirectMemory/Export.cpp) (libkernel) - reports 2048 page-table entries on each side (a 4 GiB pool of 2 MiB entries, the constants of [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/main/src/kernel/memory.cpp)) and all of them available: no entries are counted as used, so a title never sees page-table pressure. KytyPS5 counts the 2 MiB entries of the committed CPU- or GPU-accessible ranges as used, which this port does not do. The four `int*` arguments follow OpenOrbis' `libkernel.h`, the Orbital type database and SharpProspero; a null pointer returns `SCE_KERNEL_ERROR_EFAULT` as in KytyPS5. None of the values is measured on a console. PPSA30601 (Resonance) calls it during start-up +- [clock_settime](../../../core/libs/prx/libkernel/Time/Time.cpp) (libkernel) validates like FreeBSD 11.4's [`kern_clock_settime`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/kern_time.c#L396-L412) for a root caller: `EFAULT` for a null time, `EINVAL` for any clock but `CLOCK_REALTIME`, a negative second count or a nanosecond count outside 0 to 999999999. A valid `CLOCK_REALTIME` request throws, naming the requested time, since the host clock is never set and no guest clock offset is kept. FreeBSD checks `PRIV_CLOCK_SETTIME` first, which a jailed process fails with `EPERM`; whether a title holds it on the console is unverified. PS5MC (PPSA99420) imports it; no call site is known