Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions core/libs/prx/libkernel/Pthread/Posix/Thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "../include/Pthread.hpp"
#include "Common.hpp"
#include "prx/libkernel/KernelErrors.hpp"
#include <atomic>
#include <chrono>
#include <cstring>
#include <thread>
Expand All @@ -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<int> 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);
Expand Down Expand Up @@ -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<std::int64_t>(lwpid) != static_cast<std::int64_t>(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));
}
Expand Down
27 changes: 27 additions & 0 deletions core/libs/tests/GuestSchedPriority.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "prx/libc/include/general/VabiMacros.hpp"
#include <cstdint>
#include <cstdlib>
#include <stdexcept>
extern "C" int* APS5_VABI __error_nid_postfix();
Expand All @@ -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();
}
Expand Down Expand Up @@ -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);
}
1 change: 1 addition & 0 deletions docs/dev/debt/libkernel.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading