diff --git a/core/libs/prx/libkernel/Pthread/Posix/Thread.cpp b/core/libs/prx/libkernel/Pthread/Posix/Thread.cpp index 90ad038f818..76609154565 100644 --- a/core/libs/prx/libkernel/Pthread/Posix/Thread.cpp +++ b/core/libs/prx/libkernel/Pthread/Posix/Thread.cpp @@ -8,6 +8,7 @@ #include "../include/Pthread.hpp" #include "Common.hpp" #include "prx/libkernel/KernelErrors.hpp" +#include #include #include #include @@ -31,6 +32,10 @@ int APS5_VABI scePthreadGetprio(Pthread thread, int* prio); static constexpr int GUEST_SCHED_FIFO = 1; static constexpr int GUEST_SCHED_RR = 3; +namespace { +std::atomic guestConcurrency{0}; +} + static bool AcceptsSchedParam(int policy, int priority) { if (policy != GUEST_SCHED_FIFO && policy != GUEST_SCHED_RR) return false; return (priority >= 256 && priority <= 767) || (priority >= 896 && priority <= 959); @@ -116,6 +121,48 @@ int APS5_VABI cpuset_setaffinity_nid_postfix(int level, int which, std::int64_t return 0; } +struct GuestRtPrio { + std::uint16_t type; + std::uint16_t prio; +}; + +int APS5_VABI rtprio_thread_nid_postfix(int function, int lwpid, GuestRtPrio* priority) { + constexpr int Lookup = 0, Set = 1; + constexpr int GuestEsrch = 3, GuestEfault = 14, GuestEinval = 22; + constexpr std::uint16_t Normal = 3; + const auto fail = [](int error) { + *__error_nid_postfix() = error; + return -1; + }; + if (function != Lookup && function != Set) return fail(GuestEinval); + if (priority == nullptr) return fail(GuestEfault); + auto* self = scePthreadSelf(); + if (lwpid != 0 && static_cast(lwpid) != static_cast(self->tid)) { + if (!LiveGuestThreadExists(lwpid)) return fail(GuestEsrch); + NotImplemented_nid_no_patch("rtprio_thread: a thread other than the calling one"); + } + const int policy = self->schedPolicy.load(std::memory_order_relaxed); + if (policy == 1 || policy == 3) NotImplemented_nid_no_patch("rtprio_thread: a guest thread's real-time class"); + if (function == Lookup) { + priority->type = Normal; + priority->prio = 0; + return 0; + } + if (priority->type == Normal && priority->prio == 0) return 0; + NotImplemented_nid_no_patch("rtprio_thread: a real-time priority change"); +} + +int APS5_VABI pthread_setconcurrency_nid_postfix(int newLevel) { + constexpr int GuestEinval = 22; + if (newLevel < 0) return GuestEinval; + guestConcurrency.store(newLevel, std::memory_order_relaxed); + return 0; +} + +int APS5_VABI pthread_getconcurrency_nid_postfix() { + return guestConcurrency.load(std::memory_order_relaxed); +} + int APS5_VABI pthread_create_nid_postfix(Pthread* thread, const PthreadAttr* attr, PthreadEntry entry, void* arg) { return PosixThread::ToErrno(scePthreadCreate(thread, attr, entry, arg, nullptr)); } diff --git a/core/libs/tests/GuestSchedPriority.cpp b/core/libs/tests/GuestSchedPriority.cpp index a83bebd2310..544422b9cd7 100644 --- a/core/libs/tests/GuestSchedPriority.cpp +++ b/core/libs/tests/GuestSchedPriority.cpp @@ -1,4 +1,5 @@ #include "prx/libc/include/general/VabiMacros.hpp" +#include #include #include extern "C" int* APS5_VABI __error_nid_postfix(); @@ -7,6 +8,13 @@ int APS5_VABI sched_get_priority_max_nid_postfix(int policy); int APS5_VABI sched_get_priority_min_nid_postfix(int policy); int APS5_VABI getpriority_nid_postfix(int which, int who); int APS5_VABI setpriority_nid_postfix(int which, int who, int niceval); +struct GuestRtPrio { + std::uint16_t type; + std::uint16_t prio; +}; +int APS5_VABI rtprio_thread_nid_postfix(int function, int lwpid, GuestRtPrio* priority); +int APS5_VABI pthread_setconcurrency_nid_postfix(int newLevel); +int APS5_VABI pthread_getconcurrency_nid_postfix(void); int APS5_VABI getpid_nid_postfix(void); int* APS5_VABI __error_nid_postfix(); } @@ -70,4 +78,23 @@ int main() { Require(setpriority_nid_postfix(PrioProcess, 0, 99) == 0 && getpriority_nid_postfix(PrioProcess, 0) == 20); Require(setpriority_nid_postfix(PrioProcess, 0, -99) == 0 && getpriority_nid_postfix(PrioProcess, 0) == -20); Require(setpriority_nid_postfix(PrioProcess, 0, 0) == 0 && getpriority_nid_postfix(PrioProcess, 0) == 0); + GuestRtPrio rtprio{7, 7}; + bool rtprioThrew = false; + try { rtprio_thread_nid_postfix(0, 0, &rtprio); } catch (const std::exception&) { rtprioThrew = true; } + Require(rtprioThrew); + rtprioThrew = false; + try { rtprio_thread_nid_postfix(1, 0, &rtprio); } catch (const std::exception&) { rtprioThrew = true; } + Require(rtprioThrew); + *__error_nid_postfix() = 0; + Require(rtprio_thread_nid_postfix(2, 0, &rtprio) == -1 && *__error_nid_postfix() == 22); + *__error_nid_postfix() = 0; + Require(rtprio_thread_nid_postfix(0, 0, nullptr) == -1 && *__error_nid_postfix() == 14); + *__error_nid_postfix() = 0; + Require(rtprio_thread_nid_postfix(0, self + 1, &rtprio) == -1 && *__error_nid_postfix() == 3); + Require(pthread_getconcurrency_nid_postfix() == 0); + *__error_nid_postfix() = 13; + Require(pthread_setconcurrency_nid_postfix(4) == 0 && pthread_getconcurrency_nid_postfix() == 4 && *__error_nid_postfix() == 13); + *__error_nid_postfix() = 13; + Require(pthread_setconcurrency_nid_postfix(-1) == 22 && *__error_nid_postfix() == 13); + Require(pthread_setconcurrency_nid_postfix(0) == 0 && pthread_getconcurrency_nid_postfix() == 0); } diff --git a/docs/dev/debt/libkernel.md b/docs/dev/debt/libkernel.md index 0cd44fdc108..64a20ba4d3d 100644 --- a/docs/dev/debt/libkernel.md +++ b/docs/dev/debt/libkernel.md @@ -188,3 +188,4 @@ - [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 - [chflags](../../../core/libs/prx/libkernel/File/src/Stdio.cpp) (libkernel) follows FreeBSD 11.4's [`kern_chflagsat`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/vfs_syscalls.c#L2366-L2383) and UFS's [`ufs_setattr`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/ufs/ufs/ufs_vnops.c#L509-L556) for a root caller: the path lookup reports `EFAULT`, `ENOENT` and `ENOTDIR`, a flag outside the 17 UFS stores (`VNOVAL` included) `EOPNOTSUPP`, and `SF_SNAPSHOT` `EPERM`. Neither host keeps BSD file flags, so every file is taken to have none (`stat` reports `st_flags` 0): flags 0 succeed without touching the file, and any other flag throws, naming the flags. `UF_READONLY`, `UF_HIDDEN`, `UF_SYSTEM` and `UF_ARCHIVE` are FreeBSD's names for the Windows attributes but are not mapped to them: UFS stores them without enforcing them, `FILE_ATTRIBUTE_READONLY` already carries the owner write bit of `chmod` on Windows, and Linux has no equivalent a process can set without `CAP_LINUX_IMMUTABLE`. The console's filesystem types, `EROFS` on read-only mounts and the change-time update are not modelled - [getpriority](../../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) follows FreeBSD 11.4's [`sys_getpriority`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/kern_resource.c#L93-L165) for the only guest process, which is taken to run at nice 0: `PRIO_PROCESS` and `PRIO_PGRP` with 0 or its own pid (the guest is taken to lead its own process group, see `kill`) and `PRIO_USER` with 0 or its user id 0 return 0, any other id fails with `ESRCH` and any other `which` with `EINVAL`. As in FreeBSD's libc, where `getpriority` is the bare system call, errno is left unchanged on success. The nice value of a title process on the console is unverified, and the host nice of the process or of the threads `APS5_THREAD_PRIORITY=high` raises is not reported. `setpriority` accepts the same target ids (any other id fails with `ESRCH`, any other `which` with `EINVAL`), clamps the value to FreeBSD's `PRIO_MIN`-`PRIO_MAX` (-20 to 20) and stores it as the guest process's nice value, which `getpriority` returns; the host scheduling class is not changed, and whether a title process may lower the value without privilege is unverified +- [rtprio_thread, pthread_setconcurrency, pthread_getconcurrency](../../../core/libs/prx/libkernel/Pthread/Posix/Thread.cpp) (libkernel) - `rtprio_thread` follows FreeBSD 11.4's `rtprio_thread(2)`. Every guest thread here runs under `SCHED_FIFO` or `SCHED_RR` (priority 256-767), whose rtprio class is real-time; the console's mapping from that priority to the rtprio `prio` range (0-31) is unmeasured, so `RTP_LOOKUP` and `RTP_SET` throw for such a thread rather than report an impossible priority. A thread that is not real-time reports `RTP_PRIO_NORMAL` (3) with 0, and `RTP_SET` accepts only that value. Another function fails with `EINVAL`, a null `rtprio` with `EFAULT`, a thread other than the calling one throws, and an id that is not a live guest thread with `ESRCH`. `pthread_setconcurrency` stores 0 or more and returns `EINVAL` for a negative level (the error itself, as FreeBSD's libthr does, with errno untouched); `pthread_getconcurrency` returns the level, which is a hint and changes nothing about the host's concurrency. Morrowind (`mshivam019/morrowind-ps5`) imports all three at start-up