mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-07 13:39:55 +00:00
feat(libkernel_sys): implement the scePthreadBarrier, cpuclockid, concurrency and msync forms
Forward scePthreadBarrier*, scePthreadBarrierattr*, scePthreadGetcpuclockid, scePthreadGetconcurrency and sceKernelMsync to libkernel's POSIX implementations and return POSIX errors as SCE kernel errors. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
d6eafa54d0
commit
106e73669d
4 files changed
+160
-30
No files matched your search
@@ -315,12 +315,18 @@ if(BUILD_TESTING)
|
||||
configure_windows_unwind(guest_kernel_sys_thread_defaults_tests)
|
||||
add_test(NAME guest_kernel_sys_thread_defaults COMMAND guest_kernel_sys_thread_defaults_tests)
|
||||
set_tests_properties(guest_kernel_sys_thread_defaults PROPERTIES TIMEOUT 20)
|
||||
add_test_executable(guest_kernel_sys_barrier_tests tests/GuestKernelSysBarrier.cpp)
|
||||
target_include_directories(guest_kernel_sys_barrier_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(guest_kernel_sys_barrier_tests PRIVATE libkernel_sys libkernel libc)
|
||||
configure_windows_unwind(guest_kernel_sys_barrier_tests)
|
||||
add_test(NAME guest_kernel_sys_barrier COMMAND guest_kernel_sys_barrier_tests)
|
||||
set_tests_properties(guest_kernel_sys_barrier PROPERTIES TIMEOUT 20)
|
||||
if(Python3_Interpreter_FOUND)
|
||||
add_test(NAME nid_verneed COMMAND ${Python3_EXECUTABLE}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/nid/tests/test_verneed.py $<TARGET_FILE:nid_patcher>)
|
||||
endif()
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_kernel_sys guest_kernel_sys_thread_defaults PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
set_tests_properties(guest_kernel_sys guest_kernel_sys_thread_defaults guest_kernel_sys_barrier PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel_sys>")
|
||||
endif()
|
||||
if(APPLE)
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libkernel/KernelErrors.hpp"
|
||||
#include <cstddef>
|
||||
|
||||
extern "C" {
|
||||
int* APS5_VABI __error_nid_postfix();
|
||||
int APS5_VABI pthread_barrier_init_nid_postfix(void** barrier, void* const* attr, unsigned count);
|
||||
int APS5_VABI pthread_barrier_wait_nid_postfix(void** barrier);
|
||||
int APS5_VABI pthread_barrier_destroy_nid_postfix(void** barrier);
|
||||
int APS5_VABI pthread_barrierattr_init_nid_postfix(void** attr);
|
||||
int APS5_VABI pthread_barrierattr_destroy_nid_postfix(void** attr);
|
||||
int APS5_VABI pthread_barrierattr_getpshared_nid_postfix(void* const* attr, int* pshared);
|
||||
int APS5_VABI pthread_barrierattr_setpshared_nid_postfix(void** attr, int pshared);
|
||||
int APS5_VABI pthread_getcpuclockid_nid_postfix(void* thread, int* clockId);
|
||||
int APS5_VABI pthread_getconcurrency_nid_postfix();
|
||||
int APS5_VABI msync_nid_postfix(void* address, std::size_t length, int flags);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
int SceResult(int error) {
|
||||
return error == 0 ? 0 : SceKernelError(error);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
@@ -1733,9 +1757,8 @@ int APS5_VABI sceKernelMlockall(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceKernelMsync(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
int APS5_VABI sceKernelMsync(void* address, std::size_t length, int flags) {
|
||||
return msync_nid_postfix(address, length, flags) == 0 ? 0 : SceKernelError(*__error_nid_postfix());
|
||||
}
|
||||
|
||||
int APS5_VABI sceKernelNormalizePath(void) {
|
||||
@@ -2303,39 +2326,34 @@ int APS5_VABI scePthreadAttrSetscope(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadBarrierDestroy(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
int APS5_VABI scePthreadBarrierDestroy(void** barrier) {
|
||||
return SceResult(pthread_barrier_destroy_nid_postfix(barrier));
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadBarrierInit(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
int APS5_VABI scePthreadBarrierInit(void** barrier, void* const* attr, unsigned count, const char* name) {
|
||||
(void)name;
|
||||
return SceResult(pthread_barrier_init_nid_postfix(barrier, attr, count));
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadBarrierWait(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
int APS5_VABI scePthreadBarrierWait(void** barrier) {
|
||||
const int result = pthread_barrier_wait_nid_postfix(barrier);
|
||||
return result > 0 ? SceKernelError(result) : result;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadBarrierattrDestroy(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
int APS5_VABI scePthreadBarrierattrDestroy(void** attr) {
|
||||
return SceResult(pthread_barrierattr_destroy_nid_postfix(attr));
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadBarrierattrGetpshared(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
int APS5_VABI scePthreadBarrierattrGetpshared(void* const* attr, int* pshared) {
|
||||
return SceResult(pthread_barrierattr_getpshared_nid_postfix(attr, pshared));
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadBarrierattrInit(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
int APS5_VABI scePthreadBarrierattrInit(void** attr) {
|
||||
return SceResult(pthread_barrierattr_init_nid_postfix(attr));
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadBarrierattrSetpshared(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
int APS5_VABI scePthreadBarrierattrSetpshared(void** attr, int pshared) {
|
||||
return SceResult(pthread_barrierattr_setpshared_nid_postfix(attr, pshared));
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadCondattrGetclock(void) {
|
||||
@@ -2344,13 +2362,11 @@ int APS5_VABI scePthreadCondattrGetclock(void) {
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadGetconcurrency(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
return pthread_getconcurrency_nid_postfix();
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadGetcpuclockid(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
int APS5_VABI scePthreadGetcpuclockid(void* thread, int* clockId) {
|
||||
return SceResult(pthread_getcpuclockid_nid_postfix(thread, clockId));
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadMain(void) {
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
#include "SceTypes.hpp"
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
|
||||
struct GuestBarrier;
|
||||
struct GuestBarrierattr;
|
||||
|
||||
extern "C" {
|
||||
int APS5_VABI scePthreadCreate(Pthread* thread, const PthreadAttr* attr, PthreadEntry entry, void* arg, const char* name);
|
||||
int APS5_VABI scePthreadJoin(Pthread thread, void** retval);
|
||||
Pthread APS5_VABI scePthreadSelf();
|
||||
int APS5_VABI scePthreadBarrierattrInit(GuestBarrierattr** attr);
|
||||
int APS5_VABI scePthreadBarrierattrDestroy(GuestBarrierattr** attr);
|
||||
int APS5_VABI scePthreadBarrierattrGetpshared(GuestBarrierattr* const* attr, int* pshared);
|
||||
int APS5_VABI scePthreadBarrierattrSetpshared(GuestBarrierattr** attr, int pshared);
|
||||
int APS5_VABI scePthreadBarrierInit(GuestBarrier** barrier, GuestBarrierattr* const* attr, unsigned count, const char* name);
|
||||
int APS5_VABI scePthreadBarrierWait(GuestBarrier** barrier);
|
||||
int APS5_VABI scePthreadBarrierDestroy(GuestBarrier** barrier);
|
||||
int APS5_VABI scePthreadGetcpuclockid(Pthread thread, int* clockId);
|
||||
int APS5_VABI pthread_getcpuclockid_nid_postfix(Pthread thread, int* clockId);
|
||||
int APS5_VABI scePthreadGetconcurrency();
|
||||
int APS5_VABI pthread_setconcurrency_nid_postfix(int level);
|
||||
int APS5_VABI sceKernelMsync(void* address, std::size_t length, int flags);
|
||||
}
|
||||
|
||||
static constexpr int SCE_KERNEL_ERROR_EINVAL = static_cast<int>(0x80020016);
|
||||
static constexpr int SCE_KERNEL_ERROR_EFAULT = static_cast<int>(0x8002000E);
|
||||
static constexpr int SCE_KERNEL_ERROR_ENOMEM = static_cast<int>(0x8002000C);
|
||||
static constexpr int PROCESS_PRIVATE = 0;
|
||||
static constexpr int PROCESS_SHARED = 1;
|
||||
static constexpr int BARRIER_SERIAL_THREAD = -1;
|
||||
static constexpr int MS_ASYNC = 0x1;
|
||||
static constexpr int MS_INVALIDATE = 0x2;
|
||||
static constexpr unsigned WAITERS = 4;
|
||||
|
||||
static void Require(bool value) { if (!value) std::abort(); }
|
||||
|
||||
struct Context {
|
||||
GuestBarrier* barrier = nullptr;
|
||||
std::atomic<int> serial{0};
|
||||
std::atomic<int> released{0};
|
||||
};
|
||||
|
||||
static void* APS5_VABI Waiter(void* arg) {
|
||||
auto& context = *static_cast<Context*>(arg);
|
||||
const int result = scePthreadBarrierWait(&context.barrier);
|
||||
if (result == BARRIER_SERIAL_THREAD) ++context.serial;
|
||||
else if (result == 0) ++context.released;
|
||||
else std::abort();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int main() {
|
||||
GuestBarrierattr* attr = nullptr;
|
||||
Require(scePthreadBarrierattrInit(nullptr) == SCE_KERNEL_ERROR_EINVAL);
|
||||
Require(scePthreadBarrierattrInit(&attr) == 0 && attr != nullptr);
|
||||
int pshared = -1;
|
||||
Require(scePthreadBarrierattrGetpshared(&attr, &pshared) == 0 && pshared == PROCESS_PRIVATE);
|
||||
Require(scePthreadBarrierattrSetpshared(&attr, PROCESS_SHARED) == SCE_KERNEL_ERROR_EINVAL);
|
||||
Require(scePthreadBarrierattrSetpshared(&attr, PROCESS_PRIVATE) == 0);
|
||||
|
||||
Context context;
|
||||
Require(scePthreadBarrierInit(&context.barrier, &attr, 0, "zero") == SCE_KERNEL_ERROR_EINVAL);
|
||||
Require(scePthreadBarrierInit(&context.barrier, &attr, 0x80000000u, "over") == SCE_KERNEL_ERROR_EINVAL && context.barrier == nullptr);
|
||||
Require(scePthreadBarrierInit(nullptr, &attr, WAITERS, "null") == SCE_KERNEL_ERROR_EINVAL);
|
||||
Require(scePthreadBarrierInit(&context.barrier, &attr, WAITERS, "barrier") == 0 && context.barrier != nullptr);
|
||||
Require(scePthreadBarrierattrDestroy(&attr) == 0 && attr == nullptr);
|
||||
Require(scePthreadBarrierattrDestroy(&attr) == SCE_KERNEL_ERROR_EINVAL);
|
||||
|
||||
for (int round = 0; round < 2; ++round) {
|
||||
context.serial = 0;
|
||||
context.released = 0;
|
||||
std::array<Pthread, WAITERS> threads{};
|
||||
for (auto& thread : threads) Require(scePthreadCreate(&thread, nullptr, Waiter, &context, nullptr) == 0);
|
||||
for (auto& thread : threads) Require(scePthreadJoin(thread, nullptr) == 0);
|
||||
Require(context.serial == 1 && context.released == static_cast<int>(WAITERS) - 1);
|
||||
}
|
||||
Require(scePthreadBarrierDestroy(&context.barrier) == 0 && context.barrier == nullptr);
|
||||
Require(scePthreadBarrierDestroy(&context.barrier) == SCE_KERNEL_ERROR_EINVAL);
|
||||
Require(scePthreadBarrierWait(nullptr) == SCE_KERNEL_ERROR_EINVAL);
|
||||
|
||||
GuestBarrier* single = nullptr;
|
||||
Require(scePthreadBarrierInit(&single, nullptr, 1, nullptr) == 0);
|
||||
Require(scePthreadBarrierWait(&single) == BARRIER_SERIAL_THREAD);
|
||||
Require(scePthreadBarrierDestroy(&single) == 0);
|
||||
|
||||
int clock = 0;
|
||||
int posixClock = 0;
|
||||
Require(scePthreadGetcpuclockid(scePthreadSelf(), &clock) == 0);
|
||||
Require(pthread_getcpuclockid_nid_postfix(scePthreadSelf(), &posixClock) == 0 && clock == posixClock);
|
||||
Require(scePthreadGetcpuclockid(scePthreadSelf(), nullptr) == SCE_KERNEL_ERROR_EFAULT);
|
||||
Require(scePthreadGetcpuclockid(nullptr, &clock) == SCE_KERNEL_ERROR_EINVAL);
|
||||
|
||||
Require(pthread_setconcurrency_nid_postfix(3) == 0 && scePthreadGetconcurrency() == 3);
|
||||
Require(pthread_setconcurrency_nid_postfix(0) == 0 && scePthreadGetconcurrency() == 0);
|
||||
|
||||
auto* unmapped = reinterpret_cast<void*>(static_cast<std::uintptr_t>(0x4000));
|
||||
Require(sceKernelMsync(unmapped, 0x4000, 0) == SCE_KERNEL_ERROR_ENOMEM);
|
||||
Require(sceKernelMsync(unmapped, 0x4000, MS_ASYNC | MS_INVALIDATE) == SCE_KERNEL_ERROR_EINVAL);
|
||||
return 0;
|
||||
}
|
||||
@@ -19,3 +19,7 @@
|
||||
- [chroot](../../../core/libs/prx/libkernel_sys/Export.cpp) (libkernel_sys) follows FreeBSD 11.4's [`sys_chroot`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/vfs_syscalls.c#L776-L807) and [`pwd_chroot`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/kern_descrip.c#L3059-L3085) for a root caller: `EFAULT` for a null path, `ENOENT` for an empty or missing one, `ENAMETOOLONG` for a path of 1024 bytes or more or a component over 255, and `ENOTDIR` for a path that is not a directory or passes through a file. A directory that is the guest root (`/`, `/..`, or `.` after `fchdir` to a descriptor of `/`) succeeds and changes nothing, as the root stays the same directory; the guest is taken never to have changed its root, so open directory descriptors do not make it fail with `EPERM` (`kern.chroot_allow_open_directories` 1). Any other directory throws, naming the path: the guest path resolver has a fixed root and mount aliases (`/app0`, `/savedata0`) that do not move with it. On the console a title starts in a sandbox root, where `pwd_chroot` refuses with `EPERM` while a directory is open; whether that holds for titles, and whether a title holds `PRIV_VFS_CHROOT`, are unverified. Porpoise (PPSA99764) weak-imports it and calls it only as `fchdir` to its saved descriptor of `/` then `chroot(".")`, to put its sandbox root back after a jailbreak daemon replaced it ([`put_root_back`](https://github.com/elripalda/Porpoise-Dolphin-Emulator-for-PS5/blob/2ed1ab5a06995aa198f7bccd4da0f90c3fc29d39/src/porpoise_jailbreak.cpp#L303-L331))
|
||||
- [fchown](../../../core/libs/prx/libkernel_sys/Export.cpp) (libkernel_sys) - ownership is not modelled: the guest's own owner and group (0) and the unchanged value (-1) are accepted, a descriptor that is not open fails with `EBADF`, and any other owner or group throws, naming it. The check reads the descriptor with `fstat`, ignoring the CRT's invalid-parameter handler on Windows as `DescriptorControl` does. The console's ownership rules are unverified. Morrowind (`mshivam019/morrowind-ps5`) imports it
|
||||
- [pthread_set_defaultstacksize_np and scePthreadSetDefaultstacksize](../../../core/libs/prx/libkernel_sys/Export.cpp) (libkernel_sys) set the stack size that `scePthreadAttrInit` and a thread created without attributes use, as [fpPS4's `pthread_set_defaultstacksize_np`](https://github.com/red-prig/fpPS4/blob/04cefd4/kernel/ps4_pthread_attr.pas#L89-L102) does: a size of 16384 bytes or less fails with `EINVAL` (`0x80020016` from the sce form) and there is no upper limit, so a default the host cannot allocate fails only when a thread is created. Attributes initialised earlier keep their size. Not checked on a PS5
|
||||
|
||||
### Unknown function info
|
||||
|
||||
- [scePthreadBarrier*, scePthreadBarrierattr*, scePthreadGetcpuclockid, scePthreadGetconcurrency and sceKernelMsync](../../../core/libs/prx/libkernel_sys/Export.Discovery.cpp) (libkernel_sys) call libkernel's `pthread_barrier*`, `pthread_getcpuclockid`, `pthread_getconcurrency` and `msync` and turn a POSIX error into `0x80020000 | errno`; that the console's `sce` forms do the same is not measured. Signatures follow the [Orbital type database](https://github.com/AlexAltea/orbital/blob/master/tools/ida/db_types.json); `scePthreadBarrierInit` ignores its name, and `scePthreadBarrierWait` returns -1 to the serial thread, both as in [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/kernel/threads/barrier.cpp) (PS4). `scePthreadGetconcurrency` returns the level unchanged
|
||||
Reference in new issue
Block a user