Merge pull request #4153 from hugodearaujo/fix/pthread-attr-errors
fix(libkernel): return the console's errors from the scePthreadAttr calls
This commit is contained in:
commit
5ccf78e4e3
|
|
@ -369,6 +369,16 @@ if(BUILD_TESTING)
|
|||
set_tests_properties(guest_pthread_start PROPERTIES TIMEOUT 60 ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
|
||||
endif()
|
||||
add_test_executable(guest_pthread_attr_errors_tests ${LIBS_INCLUDE_DIR}/tests/GuestPthreadAttrErrors.cpp)
|
||||
target_include_directories(guest_pthread_attr_errors_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_pthread_attr_errors_tests PRIVATE libkernel libc)
|
||||
configure_windows_unwind(guest_pthread_attr_errors_tests)
|
||||
add_test(NAME guest_pthread_attr_errors COMMAND guest_pthread_attr_errors_tests)
|
||||
set_tests_properties(guest_pthread_attr_errors PROPERTIES TIMEOUT 20)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_pthread_attr_errors PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
|
||||
endif()
|
||||
add_test_executable(guest_pthread_stack_tests ${LIBS_INCLUDE_DIR}/tests/GuestPthreadStack.cpp)
|
||||
target_include_directories(guest_pthread_stack_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_pthread_stack_tests PRIVATE libkernel libc)
|
||||
|
|
|
|||
|
|
@ -33,29 +33,31 @@ int APS5_VABI scePthreadAttrInit(PthreadAttr* attr) {
|
|||
}
|
||||
|
||||
int APS5_VABI scePthreadAttrDestroy(PthreadAttr* attr) {
|
||||
if (!attr || !*attr) throw std::runtime_error(std::string(__func__) + ": null attr");
|
||||
if (!attr || !*attr) return SCE_KERNEL_ERROR_EINVAL;
|
||||
delete *attr;
|
||||
*attr = nullptr;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadAttrSetdetachstate(PthreadAttr* attr, int detachstate) {
|
||||
if (!attr || !*attr) throw std::runtime_error(std::string(__func__) + ": null attr");
|
||||
if (detachstate != DETACH_JOINABLE && detachstate != DETACH_DETACHED)
|
||||
throw std::runtime_error(std::string(__func__) + ": invalid state");
|
||||
if (!attr || !*attr) return SCE_KERNEL_ERROR_EINVAL;
|
||||
if (detachstate != DETACH_JOINABLE && detachstate != DETACH_DETACHED) return SCE_KERNEL_ERROR_EINVAL;
|
||||
(*attr)->_detachstate = detachstate;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadAttrSetschedparam(PthreadAttr* attr, const KernelSchedParam* param) {
|
||||
if (!attr || !*attr || !param)
|
||||
throw std::runtime_error(std::string(__func__) + ": null arg");
|
||||
if (!attr || !*attr) return SCE_KERNEL_ERROR_EINVAL;
|
||||
constexpr int HighestPriority = 256;
|
||||
constexpr int LowestPriority = 767;
|
||||
if (!param || param->sched_priority < HighestPriority || param->sched_priority > LowestPriority)
|
||||
return SCE_KERNEL_ERROR_EOPNOTSUPP;
|
||||
(*attr)->_schedpriority = param->sched_priority;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadAttrSetstacksize(PthreadAttr* attr, std::size_t stacksize) {
|
||||
if (!attr || !*attr) throw std::runtime_error(std::string(__func__) + ": null attr");
|
||||
if (!attr || !*attr) return SCE_KERNEL_ERROR_EINVAL;
|
||||
if (stacksize < MIN_STACK_SIZE) return SCE_KERNEL_ERROR_EINVAL;
|
||||
#ifdef _WIN32
|
||||
if (stacksize > std::numeric_limits<unsigned>::max() - 0xffffu)
|
||||
|
|
@ -89,14 +91,14 @@ int APS5_VABI scePthreadAttrGet(Pthread thread, PthreadAttr* attr) {
|
|||
}
|
||||
|
||||
int APS5_VABI scePthreadAttrGetaffinity(const PthreadAttr* attr, KernelCpumask* mask) {
|
||||
if (!attr || !*attr || !mask) throw std::runtime_error("scePthreadAttrGetaffinity: null argument");
|
||||
if (!attr || !*attr) return SCE_KERNEL_ERROR_EINVAL;
|
||||
if (!mask) throw std::runtime_error("scePthreadAttrGetaffinity: null argument");
|
||||
*mask = (*attr)->_affinity;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadAttrGetdetachstate(const PthreadAttr* attr, int* state) {
|
||||
if (!attr || !*attr) throw std::runtime_error(std::string(__func__) + ": null attr");
|
||||
if (!state) throw std::runtime_error(std::string(__func__) + ": null output");
|
||||
if (!attr || !*attr || !state) return SCE_KERNEL_ERROR_EINVAL;
|
||||
*state = (*attr)->_detachstate;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
|
@ -109,8 +111,7 @@ int APS5_VABI scePthreadAttrGetguardsize(const PthreadAttr* attr, size_t* guard_
|
|||
}
|
||||
|
||||
int APS5_VABI scePthreadAttrGetschedparam(const PthreadAttr* attr, KernelSchedParam* param) {
|
||||
if (!attr || !*attr) throw std::runtime_error(std::string(__func__) + ": null attr");
|
||||
if (!param) throw std::runtime_error(std::string(__func__) + ": null output");
|
||||
if (!attr || !*attr || !param) return SCE_KERNEL_ERROR_EINVAL;
|
||||
param->sched_priority = (*attr)->_schedpriority;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
|
@ -130,14 +131,13 @@ int APS5_VABI scePthreadAttrGetstackaddr(const PthreadAttr* attr, void** stack_a
|
|||
}
|
||||
|
||||
int APS5_VABI scePthreadAttrGetstacksize(const PthreadAttr* attr, size_t* stack_size) {
|
||||
if (!attr || !*attr) throw std::runtime_error(std::string(__func__) + ": null attr");
|
||||
if (!stack_size) throw std::runtime_error(std::string(__func__) + ": null output");
|
||||
if (!attr || !*attr || !stack_size) return SCE_KERNEL_ERROR_EINVAL;
|
||||
*stack_size = (*attr)->_stacksize;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadAttrSetaffinity(PthreadAttr* attr, KernelCpumask mask) {
|
||||
if (!attr || !*attr) throw std::runtime_error("scePthreadAttrSetaffinity: null attr");
|
||||
if (!attr || !*attr) return SCE_KERNEL_ERROR_EINVAL;
|
||||
(*attr)->_affinity = mask;
|
||||
(*attr)->_affinityExplicit = true;
|
||||
return SCE_OK;
|
||||
|
|
@ -150,13 +150,18 @@ int APS5_VABI scePthreadAttrSetguardsize(PthreadAttr* attr, size_t guard_size) {
|
|||
}
|
||||
|
||||
int APS5_VABI scePthreadAttrSetinheritsched(PthreadAttr* attr, int inherit_sched) {
|
||||
if (!attr || !*attr) throw std::runtime_error(std::string(__func__) + ": null attr");
|
||||
if (!attr || !*attr) return SCE_KERNEL_ERROR_EINVAL;
|
||||
constexpr int ExplicitSched = 0;
|
||||
constexpr int InheritSched = 4;
|
||||
if (inherit_sched != ExplicitSched && inherit_sched != InheritSched) return SCE_KERNEL_ERROR_EOPNOTSUPP;
|
||||
(*attr)->_inheritsched = inherit_sched;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadAttrSetschedpolicy(PthreadAttr* attr, int policy) {
|
||||
if (!attr || !*attr) throw std::runtime_error(std::string(__func__) + ": null attr");
|
||||
if (!attr || !*attr) return SCE_KERNEL_ERROR_EINVAL;
|
||||
constexpr int SchedRr = 3;
|
||||
if (policy != SCHED_FIFO_PS5 && policy != SchedRr) return SCE_KERNEL_ERROR_EOPNOTSUPP;
|
||||
(*attr)->_schedpolicy = policy;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,85 @@
|
|||
#include "SceTypes.hpp"
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <initializer_list>
|
||||
|
||||
extern "C" {
|
||||
int APS5_VABI scePthreadAttrInit(PthreadAttr* attr);
|
||||
int APS5_VABI scePthreadAttrDestroy(PthreadAttr* attr);
|
||||
int APS5_VABI scePthreadAttrSetstacksize(PthreadAttr* attr, std::size_t stackSize);
|
||||
int APS5_VABI scePthreadAttrGetstacksize(const PthreadAttr* attr, std::size_t* stackSize);
|
||||
int APS5_VABI scePthreadAttrSetdetachstate(PthreadAttr* attr, int state);
|
||||
int APS5_VABI scePthreadAttrGetdetachstate(const PthreadAttr* attr, int* state);
|
||||
int APS5_VABI scePthreadAttrSetschedpolicy(PthreadAttr* attr, int policy);
|
||||
int APS5_VABI scePthreadAttrSetinheritsched(PthreadAttr* attr, int inheritSched);
|
||||
int APS5_VABI scePthreadAttrSetschedparam(PthreadAttr* attr, const KernelSchedParam* param);
|
||||
int APS5_VABI scePthreadAttrGetschedparam(const PthreadAttr* attr, KernelSchedParam* param);
|
||||
int APS5_VABI scePthreadAttrSetaffinity(PthreadAttr* attr, KernelCpumask mask);
|
||||
int APS5_VABI scePthreadAttrGetaffinity(const PthreadAttr* attr, KernelCpumask* mask);
|
||||
}
|
||||
|
||||
static constexpr int SCE_OK = 0;
|
||||
static constexpr int SCE_KERNEL_ERROR_EINVAL = static_cast<int>(0x80020016u);
|
||||
static constexpr int SCE_KERNEL_ERROR_EOPNOTSUPP = static_cast<int>(0x8002002Du);
|
||||
|
||||
static void Require(bool value) { if (!value) std::abort(); }
|
||||
|
||||
int main() {
|
||||
PthreadAttr attr = nullptr;
|
||||
Require(scePthreadAttrInit(&attr) == SCE_OK);
|
||||
|
||||
for (std::size_t size : {std::size_t{0}, std::size_t{1}, std::size_t{16383}})
|
||||
Require(scePthreadAttrSetstacksize(&attr, size) == SCE_KERNEL_ERROR_EINVAL);
|
||||
Require(scePthreadAttrSetstacksize(&attr, 16384) == SCE_OK);
|
||||
|
||||
for (int state : {-1, 2, 3, 100}) Require(scePthreadAttrSetdetachstate(&attr, state) == SCE_KERNEL_ERROR_EINVAL);
|
||||
for (int state : {0, 1}) Require(scePthreadAttrSetdetachstate(&attr, state) == SCE_OK);
|
||||
|
||||
for (int policy : {-1, 0, 2, 4}) Require(scePthreadAttrSetschedpolicy(&attr, policy) == SCE_KERNEL_ERROR_EOPNOTSUPP);
|
||||
for (int policy : {1, 3}) Require(scePthreadAttrSetschedpolicy(&attr, policy) == SCE_OK);
|
||||
|
||||
for (int inherit : {-1, 1, 2, 8}) Require(scePthreadAttrSetinheritsched(&attr, inherit) == SCE_KERNEL_ERROR_EOPNOTSUPP);
|
||||
for (int inherit : {0, 4}) Require(scePthreadAttrSetinheritsched(&attr, inherit) == SCE_OK);
|
||||
|
||||
KernelSchedParam param{};
|
||||
for (int priority : {-1, 0, 255, 768, 900, 960}) {
|
||||
param.sched_priority = priority;
|
||||
Require(scePthreadAttrSetschedparam(&attr, ¶m) == SCE_KERNEL_ERROR_EOPNOTSUPP);
|
||||
}
|
||||
Require(scePthreadAttrSetschedparam(&attr, nullptr) == SCE_KERNEL_ERROR_EOPNOTSUPP);
|
||||
for (int priority : {256, 767}) {
|
||||
param.sched_priority = priority;
|
||||
Require(scePthreadAttrSetschedparam(&attr, ¶m) == SCE_OK);
|
||||
KernelSchedParam read{};
|
||||
Require(scePthreadAttrGetschedparam(&attr, &read) == SCE_OK && read.sched_priority == priority);
|
||||
}
|
||||
for (KernelCpumask mask : {KernelCpumask{0}, KernelCpumask{1}, ~KernelCpumask{0}})
|
||||
Require(scePthreadAttrSetaffinity(&attr, mask) == SCE_OK);
|
||||
|
||||
std::size_t size = 0;
|
||||
int state = 0;
|
||||
KernelCpumask mask = 0;
|
||||
Require(scePthreadAttrGetstacksize(&attr, nullptr) == SCE_KERNEL_ERROR_EINVAL);
|
||||
Require(scePthreadAttrGetdetachstate(&attr, nullptr) == SCE_KERNEL_ERROR_EINVAL);
|
||||
Require(scePthreadAttrGetschedparam(&attr, nullptr) == SCE_KERNEL_ERROR_EINVAL);
|
||||
|
||||
for (PthreadAttr* missing : {static_cast<PthreadAttr*>(nullptr), static_cast<PthreadAttr*>(&attr)}) {
|
||||
PthreadAttr unset = nullptr;
|
||||
PthreadAttr* target = missing ? &unset : nullptr;
|
||||
Require(scePthreadAttrSetstacksize(target, 16384) == SCE_KERNEL_ERROR_EINVAL);
|
||||
Require(scePthreadAttrSetdetachstate(target, 0) == SCE_KERNEL_ERROR_EINVAL);
|
||||
Require(scePthreadAttrSetschedpolicy(target, 1) == SCE_KERNEL_ERROR_EINVAL);
|
||||
Require(scePthreadAttrSetinheritsched(target, 0) == SCE_KERNEL_ERROR_EINVAL);
|
||||
param.sched_priority = 700;
|
||||
Require(scePthreadAttrSetschedparam(target, ¶m) == SCE_KERNEL_ERROR_EINVAL);
|
||||
Require(scePthreadAttrSetaffinity(target, 1) == SCE_KERNEL_ERROR_EINVAL);
|
||||
Require(scePthreadAttrGetstacksize(target, &size) == SCE_KERNEL_ERROR_EINVAL);
|
||||
Require(scePthreadAttrGetdetachstate(target, &state) == SCE_KERNEL_ERROR_EINVAL);
|
||||
Require(scePthreadAttrGetschedparam(target, ¶m) == SCE_KERNEL_ERROR_EINVAL);
|
||||
Require(scePthreadAttrGetaffinity(target, &mask) == SCE_KERNEL_ERROR_EINVAL);
|
||||
Require(scePthreadAttrDestroy(target) == SCE_KERNEL_ERROR_EINVAL);
|
||||
}
|
||||
|
||||
Require(scePthreadAttrDestroy(&attr) == SCE_OK);
|
||||
Require(scePthreadAttrDestroy(&attr) == SCE_KERNEL_ERROR_EINVAL);
|
||||
}
|
||||
|
|
@ -202,6 +202,7 @@ Throughout the project, every function at every stage either **does exactly what
|
|||
- [scePthreadRwlockDestroy, pthread_rwlock_destroy](../../core/libs/prx/libkernel/Pthread/src/Rwlock.cpp) (libkernel) - a rwlock held for reading or writing returns `EBUSY` and keeps its handle, as [KytyPS5](https://github.com/KytyPS5/KytyPS5) does; FreeBSD's [libthr](https://github.com/freebsd/freebsd-src/blob/main/lib/libthr/thread/thr_rwlock.c) and shadPS4 free it without checking, and the console's answer is unverified. Destroying a rwlock while another thread is acquiring it is not guarded
|
||||
- [scePthreadCondWait / scePthreadCondTimedwait](../../core/libs/prx/libkernel/Pthread/src/Cond.cpp) (libkernel) - before waiting, FreeBSD libthr's [`cond_wait_common`](https://github.com/freebsd/freebsd-src/blob/main/lib/libthr/thread/thr_cond.c#L1992-L2029) checks ownership through [`_mutex_owned`](https://github.com/freebsd/freebsd-src/blob/main/lib/libthr/thread/thr_mutex.c#L1166-L1178): an unowned mutex returns `EPERM`, while its destroyed marker returns `EINVAL`. An untouched adaptive static initializer (slot value 1) therefore returns `EPERM`; the test covers this, an uninitialized slot, a mutex owned by another thread, and the destroyed marker for both timed and untimed sce and POSIX waits. A recursive mutex locked several times is released completely for the wait and relocked to the same depth afterwards, as FreeBSD libthr does (`_mutex_cv_unlock`, `_mutex_cv_lock`). These FreeBSD-derived behaviors remain unverified on console hardware
|
||||
- [sceKernelAioSubmitReadCommandsMultiple / sceKernelAioSubmitWriteCommandsMultiple / sceKernelAioWaitRequests / sceKernelAioPollRequests / sceKernelAioCancelRequest / sceKernelAioCancelRequests / sceKernelAioDeleteRequests](../../core/libs/prx/libkernel/Aio/src/Aio.cpp) (libkernel) - behaviour from shadPS4 (one id per request for the Multiple submits, cancel turns any request aborted and id 0 reports processing, wait mode 2 returns once one request completed, poll copies every state without waiting); the 128 requests or ids per batch limit is from SharpProspero, with an unknown error, so larger batches throw; wait modes other than 1 (and) and 2 (or) throw; null pointers, negative counts and invalid ids return the codes the single-request siblings use, checked before any state is written
|
||||
- [scePthreadAttr setters and getters](../../core/libs/prx/libkernel/Pthread/src/Attr.cpp) (libkernel) - measured on a PS5 (firmware 13.60): a null or uninitialized attribute returns `EINVAL`, as does a null output of the stack size, detach state and scheduling parameter getters; a policy other than 1 and 3, an inheritance other than 0 and 4, or a priority outside 256 to 767 (or a null parameter) returns `ENOTSUP` (`0x8002002D`); a detach state other than 0 and 1 and a stack size below 16384 return `EINVAL`; any affinity mask is accepted. A null attribute pointer to `scePthreadAttrInit` and a null output of `scePthreadAttrGetaffinity` crashed the payload, so they still throw. A new attribute holds a 64 KiB stack size on the console and 1 MiB here, which the host code that runs on guest threads needs
|
||||
- [sceKernelGetAvailableCpumask](../../core/libs/prx/libkernel/Pthread/src/Thread.cpp) (libkernel) - returns the default thread affinity, 0x1FFF (CPUs 0 to 12), as KytyPS5 does. Titles pin threads within it (PPSA02664 passes 0x1FFB, Hades II PPSA36082 0x3 and 0x3F); whether CPU 13 is available to a title is not confirmed
|
||||
- [sceKernelGetCurrentCpu](../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) - guest affinities are not applied to host threads, so the host CPU index is mapped into the calling thread's affinity clipped to `sceKernelGetAvailableCpumask` (the available mask when they do not overlap): host CPU n reports the (n modulo the CPU count)-th CPU of that mask, so a thread pinned to one CPU reports that CPU, as a FreeBSD `cpuset` guarantees on the console. Which CPU the console reports for a thread allowed on several is not known; shadPS4 returns 0
|
||||
- [pthread_attr_setsolosched_np](../../core/libs/prx/libkernel/Pthread/Posix/Attr.cpp) (libkernel) takes `(attr, int)` and stores the value through `scePthreadAttrSetsolosched`, returning `EINVAL` (22) for a null or uninitialized attribute like the other POSIX attribute setters. Only the name is public (aerolib `2+pVfgiEd7A`); the signature and behaviour follow the SCE twin, which [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/947627d10d4ef4b68ba3c0189aab6c368e70a8d2/src/kernel/pthread.cpp#L2188-L2199) and [sharpemu](https://github.com/sharpemu/sharpemu/blob/a2983692930564815a77f81e74c151268f5b935f/src/SharpEmu.Libs/Kernel/KernelPthreadExtendedCompatExports.cs#L891-L913) store as an `int` without a range check, and [PS5PCEM](https://github.com/iStark/PS5PCEM/blob/71de7681f06c5db59a1120a3bb554c86780c3f5f/src/hle/libs/kernel_threading.zig#L1396-L1398) implements this alias over it. On a PS5 (firmware 13.60) only 0 and 16 are accepted; every other value returns `ENOTSUP` (45, `0x8002002D` from the SCE call) and keeps the stored one, and the SCE calls return `EINVAL` for a null or uninitialized attribute. PPSA15878 passes 16. Threads created from the attribute are not scheduled differently; what solo scheduling does on the console is unknown. Returnal (PPSA01285 v03.000.005) stopped on it with `what(): pthread_attr_setsolosched_np_nid_postfix not implemented` (#2872, #1155); LEGO Horizon Adventures (PPSA14632, #734) and Palworld (PPSA23126, #2598) import it
|
||||
|
|
|
|||
Loading…
Reference in New Issue