From 9241ebd4dd48ace4f41d78dcba5519e08e84c5d5 Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Sat, 21 Feb 2026 14:10:25 +0300 Subject: [PATCH 1/9] Revert "threads: initialize TLS on thread creation (#4048)" (#4062) This reverts commit f6d71646c0a6e450e9edab472ccbbb435bdc35c1. --- src/core/libraries/avplayer/avplayer_impl.cpp | 16 ++++++------- .../libraries/avplayer/avplayer_state.cpp | 2 +- src/core/libraries/ime/ime.cpp | 8 +++---- src/core/libraries/ime/ime_dialog_ui.cpp | 5 ++-- src/core/libraries/kernel/threads/pthread.cpp | 22 +----------------- .../libraries/kernel/threads/pthread_spec.cpp | 2 +- src/core/libraries/network/net_ctl_obj.cpp | 4 ++-- src/core/libraries/ngs2/ngs2.cpp | 4 ++-- .../libraries/usbd/emulated/dimensions.cpp | 4 ---- src/core/linker.cpp | 10 ++++---- src/core/module.cpp | 3 +-- src/core/tls.h | 23 +++++++++++++++++++ 12 files changed, 51 insertions(+), 52 deletions(-) diff --git a/src/core/libraries/avplayer/avplayer_impl.cpp b/src/core/libraries/avplayer/avplayer_impl.cpp index db32862ad..138747da4 100644 --- a/src/core/libraries/avplayer/avplayer_impl.cpp +++ b/src/core/libraries/avplayer/avplayer_impl.cpp @@ -12,28 +12,28 @@ void* PS4_SYSV_ABI AvPlayer::Allocate(void* handle, u32 alignment, u32 size) { const auto* const self = reinterpret_cast(handle); const auto allocate = self->m_init_data_original.memory_replacement.allocate; const auto ptr = self->m_init_data_original.memory_replacement.object_ptr; - return allocate(ptr, alignment, size); + return Core::ExecuteGuest(allocate, ptr, alignment, size); } void PS4_SYSV_ABI AvPlayer::Deallocate(void* handle, void* memory) { const auto* const self = reinterpret_cast(handle); const auto deallocate = self->m_init_data_original.memory_replacement.deallocate; const auto ptr = self->m_init_data_original.memory_replacement.object_ptr; - return deallocate(ptr, memory); + return Core::ExecuteGuest(deallocate, ptr, memory); } void* PS4_SYSV_ABI AvPlayer::AllocateTexture(void* handle, u32 alignment, u32 size) { const auto* const self = reinterpret_cast(handle); const auto allocate = self->m_init_data_original.memory_replacement.allocate_texture; const auto ptr = self->m_init_data_original.memory_replacement.object_ptr; - return allocate(ptr, alignment, size); + return Core::ExecuteGuest(allocate, ptr, alignment, size); } void PS4_SYSV_ABI AvPlayer::DeallocateTexture(void* handle, void* memory) { const auto* const self = reinterpret_cast(handle); const auto deallocate = self->m_init_data_original.memory_replacement.deallocate_texture; const auto ptr = self->m_init_data_original.memory_replacement.object_ptr; - return deallocate(ptr, memory); + return Core::ExecuteGuest(deallocate, ptr, memory); } int PS4_SYSV_ABI AvPlayer::OpenFile(void* handle, const char* filename) { @@ -42,7 +42,7 @@ int PS4_SYSV_ABI AvPlayer::OpenFile(void* handle, const char* filename) { const auto open = self->m_init_data_original.file_replacement.open; const auto ptr = self->m_init_data_original.file_replacement.object_ptr; - return open(ptr, filename); + return Core::ExecuteGuest(open, ptr, filename); } int PS4_SYSV_ABI AvPlayer::CloseFile(void* handle) { @@ -51,7 +51,7 @@ int PS4_SYSV_ABI AvPlayer::CloseFile(void* handle) { const auto close = self->m_init_data_original.file_replacement.close; const auto ptr = self->m_init_data_original.file_replacement.object_ptr; - return close(ptr); + return Core::ExecuteGuest(close, ptr); } int PS4_SYSV_ABI AvPlayer::ReadOffsetFile(void* handle, u8* buffer, u64 position, u32 length) { @@ -60,7 +60,7 @@ int PS4_SYSV_ABI AvPlayer::ReadOffsetFile(void* handle, u8* buffer, u64 position const auto read_offset = self->m_init_data_original.file_replacement.read_offset; const auto ptr = self->m_init_data_original.file_replacement.object_ptr; - return read_offset(ptr, buffer, position, length); + return Core::ExecuteGuest(read_offset, ptr, buffer, position, length); } u64 PS4_SYSV_ABI AvPlayer::SizeFile(void* handle) { @@ -69,7 +69,7 @@ u64 PS4_SYSV_ABI AvPlayer::SizeFile(void* handle) { const auto size = self->m_init_data_original.file_replacement.size; const auto ptr = self->m_init_data_original.file_replacement.object_ptr; - return size(ptr); + return Core::ExecuteGuest(size, ptr); } AvPlayerInitData AvPlayer::StubInitData(const AvPlayerInitData& data) { diff --git a/src/core/libraries/avplayer/avplayer_state.cpp b/src/core/libraries/avplayer/avplayer_state.cpp index dbaa36d18..e1b11840e 100644 --- a/src/core/libraries/avplayer/avplayer_state.cpp +++ b/src/core/libraries/avplayer/avplayer_state.cpp @@ -92,7 +92,7 @@ void AvPlayerState::DefaultEventCallback(void* opaque, AvPlayerEvents event_id, const auto callback = self->m_event_replacement.event_callback; const auto ptr = self->m_event_replacement.object_ptr; if (callback != nullptr) { - callback(ptr, event_id, 0, event_data); + Core::ExecuteGuest(callback, ptr, event_id, 0, event_data); } } diff --git a/src/core/libraries/ime/ime.cpp b/src/core/libraries/ime/ime.cpp index 96ae446fa..258cc61e1 100644 --- a/src/core/libraries/ime/ime.cpp +++ b/src/core/libraries/ime/ime.cpp @@ -99,16 +99,16 @@ public: if (m_ime_mode) { OrbisImeParam param = m_param.ime; if (use_param_handler) { - param.handler(param.arg, event); + Core::ExecuteGuest(param.handler, param.arg, event); } else { - handler(param.arg, event); + Core::ExecuteGuest(handler, param.arg, event); } } else { OrbisImeKeyboardParam param = m_param.key; if (use_param_handler) { - param.handler(param.arg, event); + Core::ExecuteGuest(param.handler, param.arg, event); } else { - handler(param.arg, event); + Core::ExecuteGuest(handler, param.arg, event); } } } diff --git a/src/core/libraries/ime/ime_dialog_ui.cpp b/src/core/libraries/ime/ime_dialog_ui.cpp index 9611e7c49..4a95c60c9 100644 --- a/src/core/libraries/ime/ime_dialog_ui.cpp +++ b/src/core/libraries/ime/ime_dialog_ui.cpp @@ -131,7 +131,8 @@ bool ImeDialogState::CallTextFilter() { return false; } - int ret = text_filter(out_text, &out_text_length, src_text, src_text_length); + int ret = + Core::ExecuteGuest(text_filter, out_text, &out_text_length, src_text, src_text_length); if (ret != 0) { return false; @@ -152,7 +153,7 @@ bool ImeDialogState::CallKeyboardFilter(const OrbisImeKeycode* src_keycode, u16* return true; } - int ret = keyboard_filter(src_keycode, out_keycode, out_status, nullptr); + int ret = Core::ExecuteGuest(keyboard_filter, src_keycode, out_keycode, out_status, nullptr); return ret == 0; } diff --git a/src/core/libraries/kernel/threads/pthread.cpp b/src/core/libraries/kernel/threads/pthread.cpp index 0218285f7..20bd20f4b 100644 --- a/src/core/libraries/kernel/threads/pthread.cpp +++ b/src/core/libraries/kernel/threads/pthread.cpp @@ -194,21 +194,6 @@ int PS4_SYSV_ABI posix_pthread_detach(PthreadT pthread) { return 0; } -#ifdef __clang__ -__attribute__((optnone)) -#else -__attribute__((optimize("O0"))) -#endif -void ClearStack(const PthreadAttr& attr) { - void* sp; - asm("mov %%rsp, %0" : "=rm"(sp)); - // leave a safety net of 128 bytes for memset - const u64 size = (u64)sp - (u64)attr.stackaddr_attr - 128; - volatile void* buf = alloca(size); - memset(const_cast(buf), 0, size); - buf = nullptr; -} - static void RunThread(void* arg) { auto* curthread = static_cast(arg); g_curthread = curthread; @@ -217,12 +202,7 @@ static void RunThread(void* arg) { /* Run the current thread's start routine with argument: */ curthread->native_thr.Initialize(); - Core::EnsureThreadInitialized(); - - // Clear the stack before running the guest thread - ClearStack(curthread->attr); - - void* ret = curthread->start_routine(curthread->arg); + void* ret = Core::ExecuteGuest(curthread->start_routine, curthread->arg); /* Remove thread from tracking */ DebugState.RemoveCurrentThreadFromGuestList(); diff --git a/src/core/libraries/kernel/threads/pthread_spec.cpp b/src/core/libraries/kernel/threads/pthread_spec.cpp index 38032f174..094866a5a 100644 --- a/src/core/libraries/kernel/threads/pthread_spec.cpp +++ b/src/core/libraries/kernel/threads/pthread_spec.cpp @@ -84,7 +84,7 @@ void _thread_cleanupspecific() { * destructor: */ lk.unlock(); - destructor(data); + Core::ExecuteGuest(destructor, data); lk.lock(); } } diff --git a/src/core/libraries/network/net_ctl_obj.cpp b/src/core/libraries/network/net_ctl_obj.cpp index a4081cd11..a295477b6 100644 --- a/src/core/libraries/network/net_ctl_obj.cpp +++ b/src/core/libraries/network/net_ctl_obj.cpp @@ -50,7 +50,7 @@ void NetCtlInternal::CheckCallback() { : ORBIS_NET_CTL_EVENT_TYPE_DISCONNECTED; for (const auto [func, arg] : callbacks) { if (func != nullptr) { - func(event, arg); + Core::ExecuteGuest(func, event, arg); } } } @@ -61,7 +61,7 @@ void NetCtlInternal::CheckNpToolkitCallback() { : ORBIS_NET_CTL_EVENT_TYPE_DISCONNECTED; for (const auto [func, arg] : nptool_callbacks) { if (func != nullptr) { - func(event, arg); + Core::ExecuteGuest(func, event, arg); } } } diff --git a/src/core/libraries/ngs2/ngs2.cpp b/src/core/libraries/ngs2/ngs2.cpp index 97d19c352..2f785f9a0 100644 --- a/src/core/libraries/ngs2/ngs2.cpp +++ b/src/core/libraries/ngs2/ngs2.cpp @@ -160,13 +160,13 @@ s32 PS4_SYSV_ABI sceNgs2SystemCreateWithAllocator(const OrbisNgs2SystemOption* o result = SystemSetup(option, &bufferInfo, 0, 0); if (result >= 0) { uintptr_t sysUserData = allocator->userData; - result = hostAlloc(&bufferInfo); + result = Core::ExecuteGuest(hostAlloc, &bufferInfo); if (result >= 0) { OrbisNgs2Handle* handleCopy = outHandle; result = SystemSetup(option, &bufferInfo, hostFree, handleCopy); if (result < 0) { if (hostFree) { - hostFree(&bufferInfo); + Core::ExecuteGuest(hostFree, &bufferInfo); } } } diff --git a/src/core/libraries/usbd/emulated/dimensions.cpp b/src/core/libraries/usbd/emulated/dimensions.cpp index 4d38c66fa..272f2f649 100644 --- a/src/core/libraries/usbd/emulated/dimensions.cpp +++ b/src/core/libraries/usbd/emulated/dimensions.cpp @@ -3,8 +3,6 @@ #include "dimensions.h" -#include "core/tls.h" - #include #include @@ -624,8 +622,6 @@ libusb_transfer_status DimensionsBackend::HandleAsyncTransfer(libusb_transfer* t s32 DimensionsBackend::SubmitTransfer(libusb_transfer* transfer) { if (transfer->endpoint == 0x01) { std::thread write_thread([this, transfer] { - Core::EnsureThreadInitialized(); - HandleAsyncTransfer(transfer); const u8 flags = transfer->flags; diff --git a/src/core/linker.cpp b/src/core/linker.cpp index 20d81409e..7a0653e9f 100644 --- a/src/core/linker.cpp +++ b/src/core/linker.cpp @@ -135,8 +135,7 @@ void Linker::Execute(const std::vector& args) { } } params.entry_addr = module->GetEntryAddress(); - Core::EnsureThreadInitialized(); - RunMainEntry(¶ms); + ExecuteGuest(RunMainEntry, ¶ms); }); } @@ -380,7 +379,8 @@ void* Linker::TlsGetAddr(u64 module_index, u64 offset) { if (!addr) { // Module was just loaded by above code. Allocate TLS block for it. const u32 init_image_size = module->tls.init_image_size; - u8* dest = reinterpret_cast(heap_api->heap_malloc(module->tls.image_size)); + u8* dest = reinterpret_cast( + Core::ExecuteGuest(heap_api->heap_malloc, module->tls.image_size)); const u8* src = reinterpret_cast(module->tls.image_virtual_addr); std::memcpy(dest, src, init_image_size); std::memset(dest + init_image_size, 0, module->tls.image_size - init_image_size); @@ -412,7 +412,7 @@ void* Linker::AllocateTlsForThread(bool is_primary) { ASSERT_MSG(ret == 0, "Unable to allocate TLS+TCB for the primary thread"); } else { if (heap_api) { - addr_out = heap_api->heap_malloc(total_tls_size); + addr_out = Core::ExecuteGuest(heap_api->heap_malloc, total_tls_size); } else { addr_out = std::malloc(total_tls_size); } @@ -422,7 +422,7 @@ void* Linker::AllocateTlsForThread(bool is_primary) { void Linker::FreeTlsForNonPrimaryThread(void* pointer) { if (heap_api) { - heap_api->heap_free(pointer); + Core::ExecuteGuest(heap_api->heap_free, pointer); } else { std::free(pointer); } diff --git a/src/core/module.cpp b/src/core/module.cpp index d0fae3a9f..127e74293 100644 --- a/src/core/module.cpp +++ b/src/core/module.cpp @@ -97,8 +97,7 @@ Module::~Module() = default; s32 Module::Start(u64 args, const void* argp, void* param) { LOG_INFO(Core_Linker, "Module started : {}", name); const VAddr addr = dynamic_info.init_virtual_addr + GetBaseAddress(); - Core::EnsureThreadInitialized(); - return reinterpret_cast(addr)(args, argp, param); + return ExecuteGuest(reinterpret_cast(addr), args, argp, param); } void Module::LoadModuleToMemory(u32& max_tls_index) { diff --git a/src/core/tls.h b/src/core/tls.h index 00eba188e..27de518ea 100644 --- a/src/core/tls.h +++ b/src/core/tls.h @@ -45,6 +45,29 @@ Tcb* GetTcbBase(); /// Makes sure TLS is initialized for the thread before entering guest. void EnsureThreadInitialized(); +template +#ifdef __clang__ +__attribute__((optnone)) +#else +__attribute__((optimize("O0"))) +#endif +void ClearStack() { + volatile void* buf = alloca(size); + memset(const_cast(buf), 0, size); + buf = nullptr; +} + +template +ReturnType ExecuteGuest(PS4_SYSV_ABI ReturnType (*func)(FuncArgs...), CallArgs&&... args) { + EnsureThreadInitialized(); + // clear stack to avoid trash from EnsureThreadInitialized + auto* tcb = GetTcbBase(); + if (tcb != nullptr && tcb->tcb_fiber == nullptr) { + ClearStack<12_KB>(); + } + return func(std::forward(args)...); +} + template struct HostCallWrapperImpl; From b37e0a6ea605e6e00e42345b02d5e5d10c82cc25 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Sun, 22 Feb 2026 16:08:06 +0100 Subject: [PATCH 2/9] Miscallenous function exports and implementations (#4068) * _nanosleep * pthread_mutexattr_setpshared * pthread_attr_setschedpolicy * getuid * copyright 2026 --- src/core/libraries/kernel/process.cpp | 7 +- src/core/libraries/kernel/threads/mutex.cpp | 79 +++++++++++-------- .../libraries/kernel/threads/pthread_attr.cpp | 4 +- src/core/libraries/kernel/time.cpp | 4 +- 4 files changed, 56 insertions(+), 38 deletions(-) diff --git a/src/core/libraries/kernel/process.cpp b/src/core/libraries/kernel/process.cpp index 31cc9a81b..a79da62ee 100644 --- a/src/core/libraries/kernel/process.cpp +++ b/src/core/libraries/kernel/process.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "common/config.h" @@ -275,6 +275,10 @@ s32 PS4_SYSV_ABI sceKernelGetModuleList2(s32* handles, u64 num_array, u64* out_c return ORBIS_OK; } +u32 PS4_SYSV_ABI posix_getuid() { + return 1; +} + s32 PS4_SYSV_ABI exit(s32 status) { UNREACHABLE_MSG("Exiting with status code {}", status); return 0; @@ -299,6 +303,7 @@ void RegisterProcess(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("HZO7xOos4xc", "libkernel", 1, "libkernel", sceKernelGetModuleInfoInternal); LIB_FUNCTION("IuxnUuXk6Bg", "libkernel", 1, "libkernel", sceKernelGetModuleList); LIB_FUNCTION("ZzzC3ZGVAkc", "libkernel", 1, "libkernel", sceKernelGetModuleList2); + LIB_FUNCTION("kg4x8Prhfxw", "libkernel", 1, "libkernel", posix_getuid); LIB_FUNCTION("6Z83sYWFlA8", "libkernel", 1, "libkernel", exit); } diff --git a/src/core/libraries/kernel/threads/mutex.cpp b/src/core/libraries/kernel/threads/mutex.cpp index 31e8b900b..51d2d3bcd 100644 --- a/src/core/libraries/kernel/threads/mutex.cpp +++ b/src/core/libraries/kernel/threads/mutex.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include @@ -39,7 +39,7 @@ static constexpr PthreadMutexAttr PthreadMutexattrAdaptiveDefault = { using CallocFun = void* (*)(size_t, size_t); -static int MutexInit(PthreadMutexT* mutex, const PthreadMutexAttr* mutex_attr, const char* name) { +static s32 MutexInit(PthreadMutexT* mutex, const PthreadMutexAttr* mutex_attr, const char* name) { const PthreadMutexAttr* attr; if (mutex_attr == nullptr) { attr = &PthreadMutexattrDefault; @@ -60,7 +60,7 @@ static int MutexInit(PthreadMutexT* mutex, const PthreadMutexAttr* mutex_attr, c if (name) { pmutex->name = name; } else { - static int MutexId = 0; + static s32 MutexId = 0; pmutex->name = fmt::format("Mutex{}", MutexId++); } @@ -79,7 +79,7 @@ static int MutexInit(PthreadMutexT* mutex, const PthreadMutexAttr* mutex_attr, c return 0; } -static int InitStatic(Pthread* thread, PthreadMutexT* mutex) { +static s32 InitStatic(Pthread* thread, PthreadMutexT* mutex) { std::scoped_lock lk{MutxStaticLock}; if (*mutex == THR_MUTEX_INITIALIZER) { @@ -90,17 +90,17 @@ static int InitStatic(Pthread* thread, PthreadMutexT* mutex) { return 0; } -int PS4_SYSV_ABI posix_pthread_mutex_init(PthreadMutexT* mutex, +s32 PS4_SYSV_ABI posix_pthread_mutex_init(PthreadMutexT* mutex, const PthreadMutexAttrT* mutex_attr) { return MutexInit(mutex, mutex_attr ? *mutex_attr : nullptr, nullptr); } -int PS4_SYSV_ABI scePthreadMutexInit(PthreadMutexT* mutex, const PthreadMutexAttrT* mutex_attr, +s32 PS4_SYSV_ABI scePthreadMutexInit(PthreadMutexT* mutex, const PthreadMutexAttrT* mutex_attr, const char* name) { return MutexInit(mutex, mutex_attr ? *mutex_attr : nullptr, name); } -int PS4_SYSV_ABI posix_pthread_mutex_destroy(PthreadMutexT* mutex) { +s32 PS4_SYSV_ABI posix_pthread_mutex_destroy(PthreadMutexT* mutex) { PthreadMutexT m = *mutex; if (m < THR_MUTEX_DESTROYED) { return 0; @@ -116,7 +116,7 @@ int PS4_SYSV_ABI posix_pthread_mutex_destroy(PthreadMutexT* mutex) { return 0; } -int PthreadMutex::SelfTryLock() { +s32 PthreadMutex::SelfTryLock() { switch (Type()) { case PthreadMutexType::ErrorCheck: case PthreadMutexType::Normal: @@ -135,7 +135,7 @@ int PthreadMutex::SelfTryLock() { } } -int PthreadMutex::SelfLock(const OrbisKernelTimespec* abstime, u64 usec) { +s32 PthreadMutex::SelfLock(const OrbisKernelTimespec* abstime, u64 usec) { const auto DoSleep = [&] { if (abstime == THR_RELTIME) { std::this_thread::sleep_for(std::chrono::microseconds(usec)); @@ -185,7 +185,7 @@ int PthreadMutex::SelfLock(const OrbisKernelTimespec* abstime, u64 usec) { } } -int PthreadMutex::Lock(const OrbisKernelTimespec* abstime, u64 usec) { +s32 PthreadMutex::Lock(const OrbisKernelTimespec* abstime, u64 usec) { Pthread* curthread = g_curthread; if (m_owner == curthread) { return SelfLock(abstime, usec); @@ -198,7 +198,7 @@ int PthreadMutex::Lock(const OrbisKernelTimespec* abstime, u64 usec) { * faster than entering the kernel */ if (m_protocol == PthreadMutexProt::None) [[likely]] { - int count = m_spinloops; + s32 count = m_spinloops; while (count--) { if (m_lock.try_lock()) { m_owner = curthread; @@ -217,7 +217,7 @@ int PthreadMutex::Lock(const OrbisKernelTimespec* abstime, u64 usec) { } } - int ret = 0; + s32 ret = 0; if (abstime == nullptr) { m_lock.lock(); } else if (abstime != THR_RELTIME && (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)) @@ -236,40 +236,40 @@ int PthreadMutex::Lock(const OrbisKernelTimespec* abstime, u64 usec) { return ret; } -int PthreadMutex::TryLock() { +s32 PthreadMutex::TryLock() { Pthread* curthread = g_curthread; if (m_owner == curthread) { return SelfTryLock(); } - const int ret = m_lock.try_lock() ? 0 : POSIX_EBUSY; + const s32 ret = m_lock.try_lock() ? 0 : POSIX_EBUSY; if (ret == 0) { m_owner = curthread; } return ret; } -int PS4_SYSV_ABI posix_pthread_mutex_trylock(PthreadMutexT* mutex) { +s32 PS4_SYSV_ABI posix_pthread_mutex_trylock(PthreadMutexT* mutex) { CHECK_AND_INIT_MUTEX return (*mutex)->TryLock(); } -int PS4_SYSV_ABI posix_pthread_mutex_lock(PthreadMutexT* mutex) { +s32 PS4_SYSV_ABI posix_pthread_mutex_lock(PthreadMutexT* mutex) { CHECK_AND_INIT_MUTEX return (*mutex)->Lock(nullptr); } -int PS4_SYSV_ABI posix_pthread_mutex_timedlock(PthreadMutexT* mutex, +s32 PS4_SYSV_ABI posix_pthread_mutex_timedlock(PthreadMutexT* mutex, const OrbisKernelTimespec* abstime) { CHECK_AND_INIT_MUTEX return (*mutex)->Lock(abstime); } -int PS4_SYSV_ABI posix_pthread_mutex_reltimedlock_np(PthreadMutexT* mutex, u64 usec) { +s32 PS4_SYSV_ABI posix_pthread_mutex_reltimedlock_np(PthreadMutexT* mutex, u64 usec) { CHECK_AND_INIT_MUTEX return (*mutex)->Lock(THR_RELTIME, usec); } -int PthreadMutex::Unlock() { +s32 PthreadMutex::Unlock() { Pthread* curthread = g_curthread; /* * Check if the running thread is not the owner of the mutex. @@ -294,7 +294,7 @@ int PthreadMutex::Unlock() { return 0; } -int PS4_SYSV_ABI posix_pthread_mutex_unlock(PthreadMutexT* mutex) { +s32 PS4_SYSV_ABI posix_pthread_mutex_unlock(PthreadMutexT* mutex) { PthreadMutex* mp = *mutex; if (mp <= THR_MUTEX_DESTROYED) [[unlikely]] { if (mp == THR_MUTEX_DESTROYED) { @@ -305,29 +305,29 @@ int PS4_SYSV_ABI posix_pthread_mutex_unlock(PthreadMutexT* mutex) { return mp->Unlock(); } -int PS4_SYSV_ABI posix_pthread_mutex_getspinloops_np(PthreadMutexT* mutex, int* count) { +s32 PS4_SYSV_ABI posix_pthread_mutex_getspinloops_np(PthreadMutexT* mutex, int* count) { CHECK_AND_INIT_MUTEX *count = (*mutex)->m_spinloops; return 0; } -int PS4_SYSV_ABI posix_pthread_mutex_setspinloops_np(PthreadMutexT* mutex, int count) { +s32 PS4_SYSV_ABI posix_pthread_mutex_setspinloops_np(PthreadMutexT* mutex, s32 count) { CHECK_AND_INIT_MUTEX(*mutex)->m_spinloops = count; return 0; } -int PS4_SYSV_ABI posix_pthread_mutex_getyieldloops_np(PthreadMutexT* mutex, int* count) { +s32 PS4_SYSV_ABI posix_pthread_mutex_getyieldloops_np(PthreadMutexT* mutex, int* count) { CHECK_AND_INIT_MUTEX *count = (*mutex)->m_yieldloops; return 0; } -int PS4_SYSV_ABI posix_pthread_mutex_setyieldloops_np(PthreadMutexT* mutex, int count) { +s32 PS4_SYSV_ABI posix_pthread_mutex_setyieldloops_np(PthreadMutexT* mutex, s32 count) { CHECK_AND_INIT_MUTEX(*mutex)->m_yieldloops = count; return 0; } -int PS4_SYSV_ABI posix_pthread_mutex_isowned_np(PthreadMutexT* mutex) { +s32 PS4_SYSV_ABI posix_pthread_mutex_isowned_np(PthreadMutexT* mutex) { PthreadMutex* m = *mutex; if (m <= THR_MUTEX_DESTROYED) { return 0; @@ -335,7 +335,7 @@ int PS4_SYSV_ABI posix_pthread_mutex_isowned_np(PthreadMutexT* mutex) { return m->m_owner == g_curthread; } -int PthreadMutex::IsOwned(Pthread* curthread) const { +s32 PthreadMutex::IsOwned(Pthread* curthread) const { if (this <= THR_MUTEX_DESTROYED) [[unlikely]] { if (this == THR_MUTEX_DESTROYED) { return POSIX_EINVAL; @@ -348,7 +348,7 @@ int PthreadMutex::IsOwned(Pthread* curthread) const { return 0; } -int PS4_SYSV_ABI posix_pthread_mutexattr_init(PthreadMutexAttrT* attr) { +s32 PS4_SYSV_ABI posix_pthread_mutexattr_init(PthreadMutexAttrT* attr) { auto pattr = new (std::nothrow) PthreadMutexAttr{}; if (pattr == nullptr) { return POSIX_ENOMEM; @@ -358,7 +358,7 @@ int PS4_SYSV_ABI posix_pthread_mutexattr_init(PthreadMutexAttrT* attr) { return 0; } -int PS4_SYSV_ABI posix_pthread_mutexattr_setkind_np(PthreadMutexAttrT* attr, +s32 PS4_SYSV_ABI posix_pthread_mutexattr_setkind_np(PthreadMutexAttrT* attr, PthreadMutexType kind) { if (attr == nullptr || *attr == nullptr) { *__Error() = POSIX_EINVAL; @@ -368,7 +368,7 @@ int PS4_SYSV_ABI posix_pthread_mutexattr_setkind_np(PthreadMutexAttrT* attr, return 0; } -int PS4_SYSV_ABI posix_pthread_mutexattr_getkind_np(PthreadMutexAttrT attr) { +s32 PS4_SYSV_ABI posix_pthread_mutexattr_getkind_np(PthreadMutexAttrT attr) { if (attr == nullptr) { *__Error() = POSIX_EINVAL; return -1; @@ -376,7 +376,7 @@ int PS4_SYSV_ABI posix_pthread_mutexattr_getkind_np(PthreadMutexAttrT attr) { return static_cast(attr->m_type); } -int PS4_SYSV_ABI posix_pthread_mutexattr_settype(PthreadMutexAttrT* attr, PthreadMutexType type) { +s32 PS4_SYSV_ABI posix_pthread_mutexattr_settype(PthreadMutexAttrT* attr, PthreadMutexType type) { if (attr == nullptr || *attr == nullptr || type < PthreadMutexType::ErrorCheck || type >= PthreadMutexType::Max) { return POSIX_EINVAL; @@ -385,7 +385,7 @@ int PS4_SYSV_ABI posix_pthread_mutexattr_settype(PthreadMutexAttrT* attr, Pthrea return 0; } -int PS4_SYSV_ABI posix_pthread_mutexattr_gettype(PthreadMutexAttrT* attr, PthreadMutexType* type) { +s32 PS4_SYSV_ABI posix_pthread_mutexattr_gettype(PthreadMutexAttrT* attr, PthreadMutexType* type) { if (attr == nullptr || *attr == nullptr || (*attr)->m_type >= PthreadMutexType::Max) { return POSIX_EINVAL; } @@ -393,7 +393,7 @@ int PS4_SYSV_ABI posix_pthread_mutexattr_gettype(PthreadMutexAttrT* attr, Pthrea return 0; } -int PS4_SYSV_ABI posix_pthread_mutexattr_destroy(PthreadMutexAttrT* attr) { +s32 PS4_SYSV_ABI posix_pthread_mutexattr_destroy(PthreadMutexAttrT* attr) { if (attr == nullptr || *attr == nullptr) { return POSIX_EINVAL; } @@ -402,7 +402,7 @@ int PS4_SYSV_ABI posix_pthread_mutexattr_destroy(PthreadMutexAttrT* attr) { return 0; } -int PS4_SYSV_ABI posix_pthread_mutexattr_getprotocol(PthreadMutexAttrT* mattr, +s32 PS4_SYSV_ABI posix_pthread_mutexattr_getprotocol(PthreadMutexAttrT* mattr, PthreadMutexProt* protocol) { if (mattr == nullptr || *mattr == nullptr) { return POSIX_EINVAL; @@ -411,7 +411,7 @@ int PS4_SYSV_ABI posix_pthread_mutexattr_getprotocol(PthreadMutexAttrT* mattr, return 0; } -int PS4_SYSV_ABI posix_pthread_mutexattr_setprotocol(PthreadMutexAttrT* mattr, +s32 PS4_SYSV_ABI posix_pthread_mutexattr_setprotocol(PthreadMutexAttrT* mattr, PthreadMutexProt protocol) { if (mattr == nullptr || *mattr == nullptr || (protocol < PthreadMutexProt::None) || (protocol > PthreadMutexProt::Protect)) { @@ -422,6 +422,15 @@ int PS4_SYSV_ABI posix_pthread_mutexattr_setprotocol(PthreadMutexAttrT* mattr, return 0; } +s32 PS4_SYSV_ABI posix_pthread_mutexattr_setpshared(PthreadMutexAttrT* attr, s32 pshared) { + constexpr s32 POSIX_PTHREAD_PROCESS_PRIVATE = 0; + constexpr s32 POSIX_PTHREAD_PROCESS_SHARED = 1; + if (!attr || !*attr || pshared != POSIX_PTHREAD_PROCESS_PRIVATE) { + return POSIX_EINVAL; + } + return ORBIS_OK; +} + void RegisterMutex(Core::Loader::SymbolsResolver* sym) { // Posix LIB_FUNCTION("ttHNfU+qDBU", "libScePosix", 1, "libkernel", posix_pthread_mutex_init); @@ -434,6 +443,7 @@ void RegisterMutex(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("5txKfcMUAok", "libScePosix", 1, "libkernel", posix_pthread_mutexattr_setprotocol); LIB_FUNCTION("HF7lK46xzjY", "libScePosix", 1, "libkernel", posix_pthread_mutexattr_destroy); LIB_FUNCTION("K-jXhbt2gn4", "libScePosix", 1, "libkernel", posix_pthread_mutex_trylock); + LIB_FUNCTION("EXv3ztGqtDM", "libScePosix", 1, "libkernel", posix_pthread_mutexattr_setpshared); // Posix-Kernel LIB_FUNCTION("ttHNfU+qDBU", "libkernel", 1, "libkernel", posix_pthread_mutex_init); @@ -444,6 +454,7 @@ void RegisterMutex(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("mDmgMOGVUqg", "libkernel", 1, "libkernel", posix_pthread_mutexattr_settype); LIB_FUNCTION("HF7lK46xzjY", "libkernel", 1, "libkernel", posix_pthread_mutexattr_destroy); LIB_FUNCTION("K-jXhbt2gn4", "libkernel", 1, "libkernel", posix_pthread_mutex_trylock); + LIB_FUNCTION("EXv3ztGqtDM", "libkernel", 1, "libkernel", posix_pthread_mutexattr_setpshared); // Orbis LIB_FUNCTION("cmo1RIYva9o", "libkernel", 1, "libkernel", ORBIS(scePthreadMutexInit)); diff --git a/src/core/libraries/kernel/threads/pthread_attr.cpp b/src/core/libraries/kernel/threads/pthread_attr.cpp index aa5781f4f..2f3f26049 100644 --- a/src/core/libraries/kernel/threads/pthread_attr.cpp +++ b/src/core/libraries/kernel/threads/pthread_attr.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "core/libraries/kernel/kernel.h" @@ -291,7 +291,7 @@ void RegisterThreadAttr(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("2Q0z6rnBrTE", "libScePosix", 1, "libkernel", posix_pthread_attr_setstacksize); LIB_FUNCTION("Ucsu-OK+els", "libScePosix", 1, "libkernel", posix_pthread_attr_get_np); LIB_FUNCTION("RtLRV-pBTTY", "libScePosix", 1, "libkernel", posix_pthread_attr_getschedpolicy); - LIB_FUNCTION("JarMIy8kKEY", "libkernel", 1, "libkernel", posix_pthread_attr_setschedpolicy); + LIB_FUNCTION("JarMIy8kKEY", "libScePosix", 1, "libkernel", posix_pthread_attr_setschedpolicy); LIB_FUNCTION("E+tyo3lp5Lw", "libScePosix", 1, "libkernel", posix_pthread_attr_setdetachstate); LIB_FUNCTION("zHchY8ft5pk", "libScePosix", 1, "libkernel", posix_pthread_attr_destroy); LIB_FUNCTION("euKRgm0Vn2M", "libScePosix", 1, "libkernel", posix_pthread_attr_setschedparam); diff --git a/src/core/libraries/kernel/time.cpp b/src/core/libraries/kernel/time.cpp index 51f86e2c7..3e1648b98 100644 --- a/src/core/libraries/kernel/time.cpp +++ b/src/core/libraries/kernel/time.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include @@ -548,6 +548,8 @@ void RegisterTime(Core::Loader::SymbolsResolver* sym) { initial_ptc = clock->GetUptime(); // POSIX + LIB_FUNCTION("NhpspxdjEKU", "libkernel", 1, "libkernel", posix_nanosleep); + LIB_FUNCTION("NhpspxdjEKU", "libScePosix", 1, "libkernel", posix_nanosleep); LIB_FUNCTION("yS8U2TGCe1A", "libkernel", 1, "libkernel", posix_nanosleep); LIB_FUNCTION("yS8U2TGCe1A", "libScePosix", 1, "libkernel", posix_nanosleep); LIB_FUNCTION("QcteRwbsnV0", "libkernel", 1, "libkernel", posix_usleep); From fb5c36fa115ab3127a56c7f2577b48b0fa1a4d78 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Sun, 22 Feb 2026 17:56:54 +0100 Subject: [PATCH 3/9] Implement guest signal handlers (#4064) * Change thread pausing to use SIGTRMIN on UNIX * Allow handling of the rest of the signals * Add orbis-native signal number conversion and fix a few bugs * ifdefing away the issues * add check for mac for the signal that's used for thread pausing there * Add a few more registers * Don't break HLE memory tracking Now, if a guest app installs a handler for SIGSEGV/SIGBUS/SIGILL, that'll be handled by keeping the original signal handler, and if we can't handle the signal ourselves (as in it didn't come from HLE memory tracking), we pass it on to the guest * copyright 2026 * + --- src/core/debug_state.cpp | 5 +- .../libraries/kernel/threads/exception.cpp | 234 +++++++++++++++--- src/core/libraries/kernel/threads/exception.h | 37 ++- src/core/signals.cpp | 37 ++- src/core/signals.h | 14 +- 5 files changed, 283 insertions(+), 44 deletions(-) diff --git a/src/core/debug_state.cpp b/src/core/debug_state.cpp index f898117ec..3cc6010b5 100644 --- a/src/core/debug_state.cpp +++ b/src/core/debug_state.cpp @@ -6,6 +6,7 @@ #include "common/assert.h" #include "common/native_clock.h" #include "common/singleton.h" +#include "core/signals.h" #include "debug_state.h" #include "devtools/widget/common.h" #include "libraries/kernel/time.h" @@ -33,7 +34,7 @@ static void PauseThread(ThreadID id) { SuspendThread(handle); CloseHandle(handle); #else - pthread_kill(id, SIGUSR1); + pthread_kill(id, SIGSLEEP); #endif } @@ -43,7 +44,7 @@ static void ResumeThread(ThreadID id) { ResumeThread(handle); CloseHandle(handle); #else - pthread_kill(id, SIGUSR1); + pthread_kill(id, SIGSLEEP); #endif } diff --git a/src/core/libraries/kernel/threads/exception.cpp b/src/core/libraries/kernel/threads/exception.cpp index 95ced79c0..dfb3b05f4 100644 --- a/src/core/libraries/kernel/threads/exception.cpp +++ b/src/core/libraries/kernel/threads/exception.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" @@ -6,6 +6,7 @@ #include "core/libraries/kernel/threads/exception.h" #include "core/libraries/kernel/threads/pthread.h" #include "core/libraries/libs.h" +#include "core/signals.h" #ifdef _WIN64 #include "common/ntapi.h" @@ -15,11 +16,156 @@ namespace Libraries::Kernel { -static std::array Handlers{}; +#ifdef _WIN32 + +// Windows doesn't have native versions of these, and we don't need to use them either. +static s32 NativeToOrbisSignal(s32 s) { + return s; +} + +static s32 OrbisToNativeSignal(s32 s) { + return s; +} + +#else + +static s32 NativeToOrbisSignal(s32 s) { + switch (s) { + case SIGHUP: + return POSIX_SIGHUP; + case SIGINT: + return POSIX_SIGINT; + case SIGQUIT: + return POSIX_SIGQUIT; + case SIGILL: + return POSIX_SIGILL; + case SIGTRAP: + return POSIX_SIGTRAP; + case SIGABRT: + return POSIX_SIGABRT; + case SIGFPE: + return POSIX_SIGFPE; + case SIGKILL: + return POSIX_SIGKILL; + case SIGBUS: + return POSIX_SIGBUS; + case SIGSEGV: + return POSIX_SIGSEGV; + case SIGSYS: + return POSIX_SIGSYS; + case SIGPIPE: + return POSIX_SIGPIPE; + case SIGALRM: + return POSIX_SIGALRM; + case SIGTERM: + return POSIX_SIGTERM; + case SIGURG: + return POSIX_SIGURG; + case SIGSTOP: + return POSIX_SIGSTOP; + case SIGTSTP: + return POSIX_SIGTSTP; + case SIGCONT: + return POSIX_SIGCONT; + case SIGCHLD: + return POSIX_SIGCHLD; + case SIGTTIN: + return POSIX_SIGTTIN; + case SIGTTOU: + return POSIX_SIGTTOU; + case SIGIO: + return POSIX_SIGIO; + case SIGXCPU: + return POSIX_SIGXCPU; + case SIGXFSZ: + return POSIX_SIGXFSZ; + case SIGVTALRM: + return POSIX_SIGVTALRM; + case SIGPROF: + return POSIX_SIGPROF; + case SIGWINCH: + return POSIX_SIGWINCH; + case SIGUSR1: + return POSIX_SIGUSR1; + case SIGUSR2: + return POSIX_SIGUSR2; + default: + UNREACHABLE_MSG("Unknown signal {}", s); + } +} + +static s32 OrbisToNativeSignal(s32 s) { + switch (s) { + case POSIX_SIGHUP: + return SIGHUP; + case POSIX_SIGINT: + return SIGINT; + case POSIX_SIGQUIT: + return SIGQUIT; + case POSIX_SIGILL: + return SIGILL; + case POSIX_SIGTRAP: + return SIGTRAP; + case POSIX_SIGABRT: + return SIGABRT; + case POSIX_SIGFPE: + return SIGFPE; + case POSIX_SIGKILL: + return SIGKILL; + case POSIX_SIGBUS: + return SIGBUS; + case POSIX_SIGSEGV: + return SIGSEGV; + case POSIX_SIGSYS: + return SIGSYS; + case POSIX_SIGPIPE: + return SIGPIPE; + case POSIX_SIGALRM: + return SIGALRM; + case POSIX_SIGTERM: + return SIGTERM; + case POSIX_SIGURG: + return SIGURG; + case POSIX_SIGSTOP: + return SIGSTOP; + case POSIX_SIGTSTP: + return SIGTSTP; + case POSIX_SIGCONT: + return SIGCONT; + case POSIX_SIGCHLD: + return SIGCHLD; + case POSIX_SIGTTIN: + return SIGTTIN; + case POSIX_SIGTTOU: + return SIGTTOU; + case POSIX_SIGIO: + return SIGIO; + case POSIX_SIGXCPU: + return SIGXCPU; + case POSIX_SIGXFSZ: + return SIGXFSZ; + case POSIX_SIGVTALRM: + return SIGVTALRM; + case POSIX_SIGPROF: + return SIGPROF; + case POSIX_SIGWINCH: + return SIGWINCH; + case POSIX_SIGUSR1: + return SIGUSR1; + case POSIX_SIGUSR2: + return SIGUSR2; + default: + UNREACHABLE_MSG("Unknown signal {}", s); + } +} + +#endif + +std::array Handlers{}; #ifndef _WIN64 -void SigactionHandler(int signum, siginfo_t* inf, ucontext_t* raw_context) { - const auto handler = Handlers[POSIX_SIGUSR1]; +void SigactionHandler(int native_signum, siginfo_t* inf, ucontext_t* raw_context) { + const auto handler = Handlers[native_signum]; if (handler) { auto ctx = Ucontext{}; #ifdef __APPLE__ @@ -42,6 +188,8 @@ void SigactionHandler(int signum, siginfo_t* inf, ucontext_t* raw_context) { ctx.uc_mcontext.mc_rsp = regs.__rsp; ctx.uc_mcontext.mc_fs = regs.__fs; ctx.uc_mcontext.mc_gs = regs.__gs; + ctx.uc_mcontext.mc_gs = regs.__rip; + ctx.uc_mcontext.mc_addr = reinterpret_cast(inf->si_addr); #else const auto& regs = raw_context->uc_mcontext.gregs; ctx.uc_mcontext.mc_r8 = regs[REG_R8]; @@ -62,15 +210,18 @@ void SigactionHandler(int signum, siginfo_t* inf, ucontext_t* raw_context) { ctx.uc_mcontext.mc_rsp = regs[REG_RSP]; ctx.uc_mcontext.mc_fs = (regs[REG_CSGSFS] >> 32) & 0xFFFF; ctx.uc_mcontext.mc_gs = (regs[REG_CSGSFS] >> 16) & 0xFFFF; + ctx.uc_mcontext.mc_rip = (regs[REG_RIP]); + ctx.uc_mcontext.mc_addr = reinterpret_cast(inf->si_addr); #endif - handler(POSIX_SIGUSR1, &ctx); + handler(NativeToOrbisSignal(native_signum), &ctx); } } #else void ExceptionHandler(void* arg1, void* arg2, void* arg3, PCONTEXT context) { const char* thrName = (char*)arg1; + int native_signum = reinterpret_cast(arg2); LOG_INFO(Lib_Kernel, "Exception raised successfully on thread '{}'", thrName); - const auto handler = Handlers[POSIX_SIGUSR1]; + const auto handler = Handlers[native_signum]; if (handler) { auto ctx = Ucontext{}; ctx.uc_mcontext.mc_r8 = context->R8; @@ -91,49 +242,71 @@ void ExceptionHandler(void* arg1, void* arg2, void* arg3, PCONTEXT context) { ctx.uc_mcontext.mc_rsp = context->Rsp; ctx.uc_mcontext.mc_fs = context->SegFs; ctx.uc_mcontext.mc_gs = context->SegGs; - handler(POSIX_SIGUSR1, &ctx); + handler(NativeToOrbisSignal(native_signum), &ctx); } } #endif int PS4_SYSV_ABI sceKernelInstallExceptionHandler(s32 signum, SceKernelExceptionHandler handler) { - if (signum != POSIX_SIGUSR1) { - LOG_ERROR(Lib_Kernel, "Installing non-supported exception handler for signal {}", signum); - return 0; + if (signum > POSIX_SIGUSR2) { + return ORBIS_KERNEL_ERROR_EINVAL; } - ASSERT_MSG(!Handlers[POSIX_SIGUSR1], "Invalid parameters"); - Handlers[POSIX_SIGUSR1] = handler; + LOG_INFO(Lib_Kernel, "Installing signal handler for {}", signum); + int const native_signum = OrbisToNativeSignal(signum); +#ifdef __APPLE__ + ASSERT_MSG(native_signum != SIGVTALRM, "SIGVTALRM is HLE-reserved on macOS!"); +#endif + ASSERT_MSG(!Handlers[native_signum], "Invalid parameters"); + Handlers[native_signum] = handler; #ifndef _WIN64 + if (native_signum == SIGSEGV || native_signum == SIGBUS || native_signum == SIGILL) { + return ORBIS_OK; // These are handled in Core::SignalHandler + } struct sigaction act = {}; act.sa_flags = SA_SIGINFO | SA_RESTART; act.sa_sigaction = reinterpret_cast(SigactionHandler); - sigaction(SIGUSR2, &act, nullptr); + sigemptyset(&act.sa_mask); + sigaction(native_signum, &act, nullptr); #endif - return 0; + return ORBIS_OK; } int PS4_SYSV_ABI sceKernelRemoveExceptionHandler(s32 signum) { - if (signum != POSIX_SIGUSR1) { - LOG_ERROR(Lib_Kernel, "Installing non-supported exception handler for signal {}", signum); - return 0; + if (signum > POSIX_SIGUSR2) { + return ORBIS_KERNEL_ERROR_EINVAL; } - ASSERT_MSG(Handlers[POSIX_SIGUSR1], "Invalid parameters"); - Handlers[POSIX_SIGUSR1] = nullptr; + int const native_signum = OrbisToNativeSignal(signum); + ASSERT_MSG(Handlers[native_signum], "Invalid parameters"); + Handlers[native_signum] = nullptr; #ifndef _WIN64 - struct sigaction act = {}; - act.sa_flags = SA_SIGINFO | SA_RESTART; - act.sa_sigaction = nullptr; - sigaction(SIGUSR2, &act, nullptr); + if (native_signum == SIGSEGV || native_signum == SIGBUS || native_signum == SIGILL) { + struct sigaction action{}; + action.sa_sigaction = Core::SignalHandler; + action.sa_flags = SA_SIGINFO | SA_ONSTACK; + sigemptyset(&action.sa_mask); + + ASSERT_MSG(sigaction(native_signum, &action, nullptr) == 0, + "Failed to reinstate original signal handler for signal {}", native_signum); + } else { + struct sigaction act = {}; + act.sa_flags = SA_SIGINFO | SA_RESTART; + act.sa_sigaction = nullptr; + sigemptyset(&act.sa_mask); + sigaction(native_signum, &act, nullptr); + } #endif - return 0; + return ORBIS_OK; } int PS4_SYSV_ABI sceKernelRaiseException(PthreadT thread, int signum) { + if (signum != POSIX_SIGUSR1) { + return ORBIS_KERNEL_ERROR_EINVAL; + } LOG_WARNING(Lib_Kernel, "Raising exception on thread '{}'", thread->name); - ASSERT_MSG(signum == POSIX_SIGUSR1, "Attempting to raise non user defined signal!"); + int const native_signum = OrbisToNativeSignal(signum); #ifndef _WIN64 const auto pthr = reinterpret_cast(thread->native_thr.GetHandle()); - const auto ret = pthread_kill(pthr, SIGUSR2); + const auto ret = pthread_kill(pthr, native_signum); if (ret != 0) { LOG_ERROR(Kernel, "Failed to send exception signal to thread '{}': {}", thread->name, strerror(ret)); @@ -143,10 +316,11 @@ int PS4_SYSV_ABI sceKernelRaiseException(PthreadT thread, int signum) { option.UserApcFlags = QueueUserApcFlagsSpecialUserApc; u64 res = NtQueueApcThreadEx(reinterpret_cast(thread->native_thr.GetHandle()), option, - ExceptionHandler, (void*)thread->name.c_str(), nullptr, nullptr); + ExceptionHandler, (void*)thread->name.c_str(), + (void*)native_signum, nullptr); ASSERT(res == 0); #endif - return 0; + return ORBIS_OK; } s32 PS4_SYSV_ABI sceKernelDebugRaiseException(s32 error, s64 unk) { @@ -154,7 +328,7 @@ s32 PS4_SYSV_ABI sceKernelDebugRaiseException(s32 error, s64 unk) { return ORBIS_KERNEL_ERROR_EINVAL; } UNREACHABLE_MSG("error {:#x}", error); - return 0; + return ORBIS_OK; } s32 PS4_SYSV_ABI sceKernelDebugRaiseExceptionOnReleaseMode(s32 error, s64 unk) { @@ -162,7 +336,7 @@ s32 PS4_SYSV_ABI sceKernelDebugRaiseExceptionOnReleaseMode(s32 error, s64 unk) { return ORBIS_KERNEL_ERROR_EINVAL; } UNREACHABLE_MSG("error {:#x}", error); - return 0; + return ORBIS_OK; } void RegisterException(Core::Loader::SymbolsResolver* sym) { diff --git a/src/core/libraries/kernel/threads/exception.h b/src/core/libraries/kernel/threads/exception.h index 6a851e9d8..42c92ab2e 100644 --- a/src/core/libraries/kernel/threads/exception.h +++ b/src/core/libraries/kernel/threads/exception.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -13,8 +13,39 @@ namespace Libraries::Kernel { using SceKernelExceptionHandler = PS4_SYSV_ABI void (*)(int, void*); -constexpr int POSIX_SIGSEGV = 11; -constexpr int POSIX_SIGUSR1 = 30; +constexpr s32 POSIX_SIGHUP = 1; +constexpr s32 POSIX_SIGINT = 2; +constexpr s32 POSIX_SIGQUIT = 3; +constexpr s32 POSIX_SIGILL = 4; +constexpr s32 POSIX_SIGTRAP = 5; +constexpr s32 POSIX_SIGABRT = 6; +constexpr s32 POSIX_SIGEMT = 7; +constexpr s32 POSIX_SIGFPE = 8; +constexpr s32 POSIX_SIGKILL = 9; +constexpr s32 POSIX_SIGBUS = 10; +constexpr s32 POSIX_SIGSEGV = 11; +constexpr s32 POSIX_SIGSYS = 12; +constexpr s32 POSIX_SIGPIPE = 13; +constexpr s32 POSIX_SIGALRM = 14; +constexpr s32 POSIX_SIGTERM = 15; +constexpr s32 POSIX_SIGURG = 16; +constexpr s32 POSIX_SIGSTOP = 17; +constexpr s32 POSIX_SIGTSTP = 18; +constexpr s32 POSIX_SIGCONT = 19; +constexpr s32 POSIX_SIGCHLD = 20; +constexpr s32 POSIX_SIGTTIN = 21; +constexpr s32 POSIX_SIGTTOU = 22; +constexpr s32 POSIX_SIGIO = 23; +constexpr s32 POSIX_SIGXCPU = 24; +constexpr s32 POSIX_SIGXFSZ = 25; +constexpr s32 POSIX_SIGVTALRM = 26; +constexpr s32 POSIX_SIGPROF = 27; +constexpr s32 POSIX_SIGWINCH = 28; +constexpr s32 POSIX_SIGINFO = 29; +constexpr s32 POSIX_SIGUSR1 = 30; +constexpr s32 POSIX_SIGUSR2 = 31; +constexpr s32 POSIX_SIGTHR = 32; +constexpr s32 POSIX_SIGLIBRT = 33; struct Mcontext { u64 mc_onstack; diff --git a/src/core/signals.cpp b/src/core/signals.cpp index 8df4edea8..70b431d39 100644 --- a/src/core/signals.cpp +++ b/src/core/signals.cpp @@ -5,6 +5,7 @@ #include "common/assert.h" #include "common/decoder.h" #include "common/signal_context.h" +#include "core/libraries/kernel/threads/exception.h" #include "core/signals.h" #ifdef _WIN32 @@ -17,6 +18,13 @@ #endif #endif +#ifndef _WIN32 +namespace Libraries::Kernel { +void SigactionHandler(int native_signum, siginfo_t* inf, ucontext_t* raw_context); +extern std::array Handlers; +} // namespace Libraries::Kernel +#endif + namespace Core { #if defined(_WIN32) @@ -66,7 +74,7 @@ static std::string DisassembleInstruction(void* code_address) { return buffer; } -static void SignalHandler(int sig, siginfo_t* info, void* raw_context) { +void SignalHandler(int sig, siginfo_t* info, void* raw_context) { const auto* signals = Signals::Instance(); auto* code_address = Common::GetRip(raw_context); @@ -76,6 +84,13 @@ static void SignalHandler(int sig, siginfo_t* info, void* raw_context) { case SIGBUS: { const bool is_write = Common::IsWriteError(raw_context); if (!signals->DispatchAccessViolation(raw_context, info->si_addr)) { + // If the guest has installed a custom signal handler, and the access violation didn't + // come from HLE memory tracking, pass the signal on + if (Libraries::Kernel::Handlers[sig]) { + Libraries::Kernel::SigactionHandler(sig, info, + reinterpret_cast(raw_context)); + return; + } UNREACHABLE_MSG("Unhandled access violation at code address {}: {} address {}", fmt::ptr(code_address), is_write ? "Write to" : "Read from", fmt::ptr(info->si_addr)); @@ -84,17 +99,23 @@ static void SignalHandler(int sig, siginfo_t* info, void* raw_context) { } case SIGILL: if (!signals->DispatchIllegalInstruction(raw_context)) { + if (Libraries::Kernel::Handlers[sig]) { + Libraries::Kernel::SigactionHandler(sig, info, + reinterpret_cast(raw_context)); + return; + } UNREACHABLE_MSG("Unhandled illegal instruction at code address {}: {}", fmt::ptr(code_address), DisassembleInstruction(code_address)); } break; - case SIGUSR1: { // Sleep thread until signal is received - sigset_t sigset; - sigemptyset(&sigset); - sigaddset(&sigset, SIGUSR1); - sigwait(&sigset, &sig); - } break; default: + if (sig == SIGSLEEP) { + // Sleep thread until signal is received again + sigset_t sigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGSLEEP); + sigwait(&sigset, &sig); + } break; } } @@ -116,7 +137,7 @@ SignalDispatch::SignalDispatch() { "Failed to register access violation signal handler."); ASSERT_MSG(sigaction(SIGILL, &action, nullptr) == 0, "Failed to register illegal instruction signal handler."); - ASSERT_MSG(sigaction(SIGUSR1, &action, nullptr) == 0, + ASSERT_MSG(sigaction(SIGSLEEP, &action, nullptr) == 0, "Failed to register sleep signal handler."); #endif } diff --git a/src/core/signals.h b/src/core/signals.h index 0409b73ae..90801debb 100644 --- a/src/core/signals.h +++ b/src/core/signals.h @@ -1,17 +1,29 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include +#include #include "common/singleton.h" #include "common/types.h" +#ifdef _WIN32 +#define SIGSLEEP -1 +#elif defined(__APPLE__) +#define SIGSLEEP SIGVTALRM +#else +#define SIGSLEEP SIGRTMAX +#endif namespace Core { using AccessViolationHandler = bool (*)(void* context, void* fault_address); using IllegalInstructionHandler = bool (*)(void* context); +#ifndef _WIN32 +void SignalHandler(int sig, siginfo_t* info, void* raw_context); +#endif + /// Receives OS signals and dispatches to the appropriate handlers. class SignalDispatch { public: From 607d704707799abbe38cf22368c6a63399069348 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Sun, 22 Feb 2026 18:10:55 +0100 Subject: [PATCH 4/9] Mount /data to /data instead of /data/gameid (#4066) --- src/emulator.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/emulator.cpp b/src/emulator.cpp index 5d3a652b8..27def3565 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -341,11 +341,8 @@ void Emulator::Run(std::filesystem::path file, std::vector args, g_window = window.get(); - const auto& mount_data_dir = Common::FS::GetUserPath(Common::FS::PathType::GameDataDir) / id; - if (!std::filesystem::exists(mount_data_dir)) { - std::filesystem::create_directory(mount_data_dir); - } - mnt->Mount(mount_data_dir, "/data"); // should just exist, manually create with game serial + const auto& mount_data_dir = Common::FS::GetUserPath(Common::FS::PathType::GameDataDir); + mnt->Mount(mount_data_dir, "/data"); // Mounting temp folders const auto& mount_temp_dir = Common::FS::GetUserPath(Common::FS::PathType::TempDataDir) / id; From 6cbab8774532a2a4b0b0bf967e5417f7809f6b3e Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Sun, 22 Feb 2026 15:41:05 -0600 Subject: [PATCH 5/9] Kernel.Event: Implement kqueue and kevent (#4065) * Remove dead code from EqueueInternal::WaitForEvents No longer necessary now that we avoid using small timers when falling back on equeue logic. * Refactor type names Might as well * Properly define OrbisKernelEqueue as a handle Most of the functions using an "OrbisKernelEqueue" call directly into kevent. Therefore, OrbisKernelEqueue should be a equeue handle. * Clang * Widen OrbisKernelEqueue type On real hardware, it's some value that contains the handle, as opposed to just the handle itself. * kqueue implementation The easy part * Hardware-accurate timer data Needed to make kevent simpler for these uses. * Move callback scheduling to EqueueInternal::AddEvent kevent would become excessively bloated if I needed to deal with that in there. * posix_kevent kevent is a bit of a pain, for now I've implemented as much as libkernel actually uses for it's wrappers, and left error logs to skip behavior when necessary. * Log calls * Apple, why are you calling fstat on an equeue? --- src/core/file_sys/fs.h | 3 +- src/core/libraries/gnmdriver/gnmdriver.cpp | 30 +- src/core/libraries/gnmdriver/gnmdriver.h | 6 +- src/core/libraries/kernel/equeue.cpp | 427 ++++++++++++++------- src/core/libraries/kernel/equeue.h | 30 +- src/core/libraries/kernel/file_system.cpp | 3 +- src/core/libraries/videoout/driver.cpp | 4 +- src/core/libraries/videoout/driver.h | 4 +- src/core/libraries/videoout/video_out.cpp | 56 +-- src/core/libraries/videoout/video_out.h | 8 +- 10 files changed, 370 insertions(+), 201 deletions(-) diff --git a/src/core/file_sys/fs.h b/src/core/file_sys/fs.h index 6fc6c570f..0522c3d8a 100644 --- a/src/core/file_sys/fs.h +++ b/src/core/file_sys/fs.h @@ -85,7 +85,8 @@ enum class FileType { Device, Socket, Epoll, - Resolver + Resolver, + Equeue }; struct File { diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index 25682ada0..326dc2418 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -123,21 +123,23 @@ static inline bool IsValidEventType(Platform::InterruptId id) { static_cast(id) == static_cast(Platform::InterruptId::GfxEop); } -s32 PS4_SYSV_ABI sceGnmAddEqEvent(SceKernelEqueue eq, u64 id, void* udata) { +s32 PS4_SYSV_ABI sceGnmAddEqEvent(OrbisKernelEqueue eq, u64 id, void* udata) { LOG_TRACE(Lib_GnmDriver, "called"); - if (!eq) { + auto equeue = GetEqueue(eq); + if (!equeue) { return ORBIS_KERNEL_ERROR_EBADF; } EqueueEvent kernel_event{}; kernel_event.event.ident = id; - kernel_event.event.filter = SceKernelEvent::Filter::GraphicsCore; - kernel_event.event.flags = SceKernelEvent::Flags::Add; + kernel_event.event.filter = OrbisKernelEvent::Filter::GraphicsCore; + kernel_event.event.flags = OrbisKernelEvent::Flags::Add; kernel_event.event.fflags = 0; kernel_event.event.data = id; kernel_event.event.udata = udata; - eq->AddEvent(kernel_event); + + equeue->AddEvent(kernel_event); Platform::IrqC::Instance()->Register( static_cast(id), @@ -149,10 +151,11 @@ s32 PS4_SYSV_ABI sceGnmAddEqEvent(SceKernelEqueue eq, u64 id, void* udata) { return; // Event data is expected to be an event type as per sceGnmGetEqEventType. - eq->TriggerEvent(static_cast(id), SceKernelEvent::Filter::GraphicsCore, - reinterpret_cast(id)); + equeue->TriggerEvent(static_cast(id), + OrbisKernelEvent::Filter::GraphicsCore, + reinterpret_cast(id)); }, - eq); + equeue); return ORBIS_OK; } @@ -267,16 +270,17 @@ int PS4_SYSV_ABI sceGnmDebugHardwareStatus() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceGnmDeleteEqEvent(SceKernelEqueue eq, u64 id) { +s32 PS4_SYSV_ABI sceGnmDeleteEqEvent(OrbisKernelEqueue eq, u64 id) { LOG_TRACE(Lib_GnmDriver, "called"); - if (!eq) { + auto equeue = GetEqueue(eq); + if (!equeue) { return ORBIS_KERNEL_ERROR_EBADF; } - eq->RemoveEvent(id, SceKernelEvent::Filter::GraphicsCore); + equeue->RemoveEvent(id, OrbisKernelEvent::Filter::GraphicsCore); - Platform::IrqC::Instance()->Unregister(static_cast(id), eq); + Platform::IrqC::Instance()->Unregister(static_cast(id), equeue); return ORBIS_OK; } @@ -895,7 +899,7 @@ int PS4_SYSV_ABI sceGnmGetDebugTimestamp() { return ORBIS_OK; } -int PS4_SYSV_ABI sceGnmGetEqEventType(const SceKernelEvent* ev) { +int PS4_SYSV_ABI sceGnmGetEqEventType(const OrbisKernelEvent* ev) { LOG_TRACE(Lib_GnmDriver, "called"); return sceKernelGetEventData(ev); } diff --git a/src/core/libraries/gnmdriver/gnmdriver.h b/src/core/libraries/gnmdriver/gnmdriver.h index 5f3462dd9..9f5fde628 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.h +++ b/src/core/libraries/gnmdriver/gnmdriver.h @@ -14,7 +14,7 @@ namespace Libraries::GnmDriver { using namespace Kernel; -s32 PS4_SYSV_ABI sceGnmAddEqEvent(SceKernelEqueue eq, u64 id, void* udata); +s32 PS4_SYSV_ABI sceGnmAddEqEvent(OrbisKernelEqueue eq, u64 id, void* udata); int PS4_SYSV_ABI sceGnmAreSubmitsAllowed(); int PS4_SYSV_ABI sceGnmBeginWorkload(u32 workload_stream, u64* workload); s32 PS4_SYSV_ABI sceGnmComputeWaitOnAddress(u32* cmdbuf, u32 size, uintptr_t addr, u32 mask, @@ -31,7 +31,7 @@ int PS4_SYSV_ABI sceGnmDebuggerSetAddressWatch(); int PS4_SYSV_ABI sceGnmDebuggerWriteGds(); int PS4_SYSV_ABI sceGnmDebuggerWriteSqIndirectRegister(); int PS4_SYSV_ABI sceGnmDebugHardwareStatus(); -s32 PS4_SYSV_ABI sceGnmDeleteEqEvent(SceKernelEqueue eq, u64 id); +s32 PS4_SYSV_ABI sceGnmDeleteEqEvent(OrbisKernelEqueue eq, u64 id); int PS4_SYSV_ABI sceGnmDestroyWorkloadStream(); void PS4_SYSV_ABI sceGnmDingDong(u32 gnm_vqid, u32 next_offs_dw); void PS4_SYSV_ABI sceGnmDingDongForWorkload(u32 gnm_vqid, u32 next_offs_dw, u64 workload_id); @@ -87,7 +87,7 @@ int PS4_SYSV_ABI sceGnmGetCoredumpMode(); int PS4_SYSV_ABI sceGnmGetCoredumpProtectionFaultTimestamp(); int PS4_SYSV_ABI sceGnmGetDbgGcHandle(); int PS4_SYSV_ABI sceGnmGetDebugTimestamp(); -int PS4_SYSV_ABI sceGnmGetEqEventType(const SceKernelEvent* ev); +int PS4_SYSV_ABI sceGnmGetEqEventType(const OrbisKernelEvent* ev); int PS4_SYSV_ABI sceGnmGetEqTimeStamp(); int PS4_SYSV_ABI sceGnmGetGpuBlockStatus(); u32 PS4_SYSV_ABI sceGnmGetGpuCoreClockFrequency(); diff --git a/src/core/libraries/kernel/equeue.cpp b/src/core/libraries/kernel/equeue.cpp index 9291c0a1a..68a6dae24 100644 --- a/src/core/libraries/kernel/equeue.cpp +++ b/src/core/libraries/kernel/equeue.cpp @@ -2,12 +2,17 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include "common/assert.h" #include "common/debug.h" #include "common/logging/log.h" +#include "common/singleton.h" +#include "core/file_sys/fs.h" #include "core/libraries/kernel/equeue.h" +#include "core/libraries/kernel/kernel.h" #include "core/libraries/kernel/orbis_error.h" +#include "core/libraries/kernel/posix_error.h" #include "core/libraries/kernel/time.h" #include "core/libraries/libs.h" @@ -16,59 +21,104 @@ namespace Libraries::Kernel { extern boost::asio::io_context io_context; extern void KernelSignalRequest(); +static std::unordered_map kqueues; static constexpr auto HrTimerSpinlockThresholdNs = 1200000u; +EqueueInternal* GetEqueue(OrbisKernelEqueue eq) { + if (!kqueues.contains(eq)) { + return nullptr; + } + return kqueues[eq]; +} + +static void HrTimerCallback(OrbisKernelEqueue eq, const OrbisKernelEvent& kevent) { + if (kqueues.contains(eq)) { + kqueues[eq]->TriggerEvent(kevent.ident, OrbisKernelEvent::Filter::HrTimer, kevent.udata); + } +} + +static void TimerCallback(OrbisKernelEqueue eq, const OrbisKernelEvent& kevent) { + if (kqueues.contains(eq) && kqueues[eq]->EventExists(kevent.ident, kevent.filter)) { + kqueues[eq]->TriggerEvent(kevent.ident, OrbisKernelEvent::Filter::Timer, kevent.udata); + if (!(kevent.flags & OrbisKernelEvent::Flags::OneShot)) { + // Reschedule the event for its next period. + kqueues[eq]->ScheduleEvent(kevent.ident, kevent.filter, TimerCallback); + } + } +} + // Events are uniquely identified by id and filter. bool EqueueInternal::AddEvent(EqueueEvent& event) { - std::scoped_lock lock{m_mutex}; + { + std::scoped_lock lock{m_mutex}; - // Calculate timer interval - event.time_added = std::chrono::steady_clock::now(); - if (event.event.filter == SceKernelEvent::Filter::Timer || - event.event.filter == SceKernelEvent::Filter::HrTimer) { - // Set timer interval - event.timer_interval = std::chrono::nanoseconds(event.event.data); + // Calculate timer interval + event.time_added = std::chrono::steady_clock::now(); + if (event.event.filter == OrbisKernelEvent::Filter::Timer) { + // Set timer interval, this is stored in milliseconds for timers. + event.timer_interval = std::chrono::milliseconds(event.event.data); + } else if (event.event.filter == OrbisKernelEvent::Filter::HrTimer) { + // Retrieve inputted time, this is stored in the bintime format. + OrbisKernelBintime* time = reinterpret_cast(event.event.data); + + // Convert the bintime format to a timespec. + OrbisKernelTimespec ts; + ts.tv_sec = time->sec; + ts.tv_nsec = (1000000000 * (time->frac >> 32)) >> 32; + + // Then use the timespec to set the timer interval. + event.timer_interval = std::chrono::nanoseconds(ts.tv_nsec + ts.tv_sec * 1000000000); + } + + // First, check if there's already an event with the same id and filter. + u64 id = event.event.ident; + OrbisKernelEvent::Filter filter = event.event.filter; + const auto& find_it = std::ranges::find_if(m_events, [id, filter](auto& ev) { + return ev.event.ident == id && ev.event.filter == filter; + }); + // If there is a duplicate event, we need to update that instead. + if (find_it != m_events.cend()) { + // Specifically, update user data and timer_interval. + // Trigger status and event data should remain intact. + auto& old_event = *find_it; + old_event.timer_interval = event.timer_interval; + old_event.event.udata = event.event.udata; + return true; + } + + // Clear input data from event. + event.event.data = 0; + + // Remove add flag from event + event.event.flags &= ~OrbisKernelEvent::Flags::Add; + + // Clear flag is appended to most event types internally. + if (event.event.filter != OrbisKernelEvent::Filter::User) { + event.event.flags |= OrbisKernelEvent::Flags::Clear; + } + + const auto& it = std::ranges::find(m_events, event); + if (it != m_events.cend()) { + *it = std::move(event); + } else { + m_events.emplace_back(std::move(event)); + } } - // First, check if there's already an event with the same id and filter. - u64 id = event.event.ident; - SceKernelEvent::Filter filter = event.event.filter; - const auto& find_it = std::ranges::find_if(m_events, [id, filter](auto& ev) { - return ev.event.ident == id && ev.event.filter == filter; - }); - // If there is a duplicate event, we need to update that instead. - if (find_it != m_events.cend()) { - // Specifically, update user data and timer_interval. - // Trigger status and event data should remain intact. - auto& old_event = *find_it; - old_event.timer_interval = event.timer_interval; - old_event.event.udata = event.event.udata; - return true; - } - - // Clear input data from event. - event.event.data = 0; - - // Remove add flag from event - event.event.flags &= ~SceKernelEvent::Flags::Add; - - // Clear flag is appended to most event types internally. - if (event.event.filter != SceKernelEvent::Filter::User) { - event.event.flags |= SceKernelEvent::Flags::Clear; - } - - const auto& it = std::ranges::find(m_events, event); - if (it != m_events.cend()) { - *it = std::move(event); - } else { - m_events.emplace_back(std::move(event)); + // Schedule callbacks for timer events + if (event.event.filter == OrbisKernelEvent::Timer) { + return this->ScheduleEvent(event.event.ident, OrbisKernelEvent::Filter::Timer, + TimerCallback); + } else if (event.event.filter == OrbisKernelEvent::HrTimer) { + return this->ScheduleEvent(event.event.ident, OrbisKernelEvent::Filter::HrTimer, + HrTimerCallback); } return true; } bool EqueueInternal::ScheduleEvent(u64 id, s16 filter, - void (*callback)(SceKernelEqueue, const SceKernelEvent&)) { + void (*callback)(OrbisKernelEqueue, const OrbisKernelEvent&)) { std::scoped_lock lock{m_mutex}; const auto& it = std::ranges::find_if(m_events, [id, filter](auto& ev) { @@ -79,8 +129,8 @@ bool EqueueInternal::ScheduleEvent(u64 id, s16 filter, } const auto& event = *it; - ASSERT(event.event.filter == SceKernelEvent::Filter::Timer || - event.event.filter == SceKernelEvent::Filter::HrTimer); + ASSERT(event.event.filter == OrbisKernelEvent::Filter::Timer || + event.event.filter == OrbisKernelEvent::Filter::HrTimer); if (!it->timer) { it->timer = std::make_unique(io_context, event.timer_interval); @@ -101,7 +151,7 @@ bool EqueueInternal::ScheduleEvent(u64 id, s16 filter, } return; } - callback(this, event_data); + callback(this->m_handle, event_data); }); KernelSignalRequest(); @@ -122,7 +172,7 @@ bool EqueueInternal::RemoveEvent(u64 id, s16 filter) { return has_found; } -int EqueueInternal::WaitForEvents(SceKernelEvent* ev, int num, const SceKernelUseconds* timo) { +int EqueueInternal::WaitForEvents(OrbisKernelEvent* ev, int num, const OrbisKernelUseconds* timo) { if (timo != nullptr && *timo == 0) { // Effectively acts as a poll; only events that have already // arrived at the time of this function call can be received @@ -152,15 +202,6 @@ int EqueueInternal::WaitForEvents(SceKernelEvent* ev, int num, const SceKernelUs m_cond.wait_for(lock, std::chrono::microseconds(micros), predicate); } - if (HasSmallTimer()) { - if (count > 0) { - const auto time_waited = std::chrono::duration_cast( - std::chrono::steady_clock::now() - m_events[0].time_added) - .count(); - count = WaitForSmallTimer(ev, num, std::max(0l, long(micros - time_waited))); - } - } - return count; } @@ -170,12 +211,12 @@ bool EqueueInternal::TriggerEvent(u64 ident, s16 filter, void* trigger_data) { std::scoped_lock lock{m_mutex}; for (auto& event : m_events) { if (event.event.ident == ident && event.event.filter == filter) { - if (filter == SceKernelEvent::Filter::VideoOut) { + if (filter == OrbisKernelEvent::Filter::VideoOut) { event.TriggerDisplay(trigger_data); - } else if (filter == SceKernelEvent::Filter::User) { + } else if (filter == OrbisKernelEvent::Filter::User) { event.TriggerUser(trigger_data); - } else if (filter == SceKernelEvent::Filter::Timer || - filter == SceKernelEvent::Filter::HrTimer) { + } else if (filter == OrbisKernelEvent::Filter::Timer || + filter == OrbisKernelEvent::Filter::HrTimer) { event.TriggerTimer(); } else { event.Trigger(trigger_data); @@ -188,15 +229,15 @@ bool EqueueInternal::TriggerEvent(u64 ident, s16 filter, void* trigger_data) { return has_found; } -int EqueueInternal::GetTriggeredEvents(SceKernelEvent* ev, int num) { +int EqueueInternal::GetTriggeredEvents(OrbisKernelEvent* ev, int num) { int count = 0; for (auto it = m_events.begin(); it != m_events.end();) { if (it->IsTriggered()) { ev[count++] = it->event; - if (it->event.flags & SceKernelEvent::Flags::Clear) { + if (it->event.flags & OrbisKernelEvent::Flags::Clear) { it->Clear(); } - if (it->event.flags & SceKernelEvent::Flags::OneShot) { + if (it->event.flags & OrbisKernelEvent::Flags::OneShot) { it = m_events.erase(it); } else { ++it; @@ -214,10 +255,17 @@ int EqueueInternal::GetTriggeredEvents(SceKernelEvent* ev, int num) { } bool EqueueInternal::AddSmallTimer(EqueueEvent& ev) { + // Retrieve inputted time, this is stored in the bintime format + OrbisKernelBintime* time = reinterpret_cast(ev.event.data); + OrbisKernelTimespec ts; + ts.tv_sec = time->sec; + ts.tv_nsec = ((1000000000 * (time->frac >> 32)) >> 32); + + // Create the small timer SmallTimer st; st.event = ev.event; st.added = std::chrono::steady_clock::now(); - st.interval = std::chrono::nanoseconds{ev.event.data}; + st.interval = std::chrono::nanoseconds(ts.tv_nsec + ts.tv_sec * 1000000000); { std::scoped_lock lock{m_mutex}; m_small_timers[st.event.ident] = std::move(st); @@ -225,7 +273,7 @@ bool EqueueInternal::AddSmallTimer(EqueueEvent& ev) { return true; } -int EqueueInternal::WaitForSmallTimer(SceKernelEvent* ev, int num, u32 micros) { +int EqueueInternal::WaitForSmallTimer(OrbisKernelEvent* ev, int num, u32 micros) { ASSERT(num >= 1); auto curr_clock = std::chrono::steady_clock::now(); @@ -266,18 +314,119 @@ bool EqueueInternal::EventExists(u64 id, s16 filter) { return it != m_events.cend(); } -int PS4_SYSV_ABI sceKernelCreateEqueue(SceKernelEqueue* eq, const char* name) { +s32 PS4_SYSV_ABI posix_kqueue() { + // Reserve a file handle for the kqueue + auto* handles = Common::Singleton::Instance(); + s32 kqueue_handle = handles->CreateHandle(); + auto* kqueue_file = handles->GetFile(kqueue_handle); + kqueue_file->type = Core::FileSys::FileType::Equeue; + + // Plenty of equeue logic uses names to identify queues. + // Create a unique name for the queue we create. + char name[32]; + memset(name, 0, sizeof(name)); + snprintf(name, sizeof(name), "kqueue%i", kqueue_handle); + + // Create the queue + kqueues[kqueue_handle] = new EqueueInternal(kqueue_handle, name); + LOG_INFO(Kernel_Event, "kqueue created with name {}", name); + + // Return handle. + return kqueue_handle; +} + +// Helper method to detect supported filters. +// We don't want to allow adding events we don't handle properly. +bool SupportedEqueueFilter(OrbisKernelEvent::Filter filter) { + return filter == OrbisKernelEvent::Filter::GraphicsCore || + filter == OrbisKernelEvent::Filter::HrTimer || + filter == OrbisKernelEvent::Filter::Timer || filter == OrbisKernelEvent::Filter::User || + filter == OrbisKernelEvent::Filter::VideoOut; +} + +s32 PS4_SYSV_ABI posix_kevent(s32 handle, OrbisKernelEvent* changelist, u64 nchanges, + OrbisKernelEvent* eventlist, u64 nevents, + OrbisKernelTimespec* timeout) { + LOG_INFO(Kernel_Event, "called, eq = {}, nchanges = {}, nevents = {}", handle, nchanges, + nevents); + + // Get the equeue + if (!kqueues.contains(handle)) { + *__Error() = POSIX_EBADF; + return ORBIS_FAIL; + } + auto equeue = kqueues[handle]; + + // First step is to apply all changes in changelist. + for (u64 i = 0; i < nchanges; i++) { + auto event = changelist[i]; + if (!SupportedEqueueFilter(event.filter)) { + LOG_ERROR(Kernel_Event, "Unsupported event filter {}", + magic_enum::enum_name(event.filter)); + continue; + } + + // Check the event flags to determine the appropriate action + if (event.flags & OrbisKernelEvent::Flags::Add) { + // The caller is requesting to add an event. + EqueueEvent internal_event{}; + internal_event.event = event; + if (!equeue->AddEvent(internal_event)) { + // Failed to add event, return error. + *__Error() = POSIX_ENOMEM; + return ORBIS_FAIL; + } + } + + if (event.flags & OrbisKernelEvent::Flags::Delete) { + // The caller is requesting to remove an event. + if (!equeue->RemoveEvent(event.ident, event.filter)) { + // Failed to remove event, return error. + *__Error() = POSIX_ENOENT; + return ORBIS_FAIL; + } + } + + if (event.filter == OrbisKernelEvent::Filter::User && event.fflags == 0x1000000) { + // For user events, this fflags value indicates we need to trigger the event. + if (!equeue->TriggerEvent(event.ident, OrbisKernelEvent::Filter::User, event.udata)) { + *__Error() = POSIX_ENOENT; + return ORBIS_FAIL; + } + } else if (event.fflags != 0) { + // The title is using filter-specific flags. Right now, these are unhandled. + LOG_ERROR(Kernel_Event, "Unhandled fflags {:#x} for event filter {}", event.fflags, + magic_enum::enum_name(event.filter)); + continue; + } + } + + // Now we need to wait on the event list. + s32 count = 0; + if (nevents > 0) { + if (timeout != nullptr) { + OrbisKernelUseconds micros = (timeout->tv_sec * 1000000) + (timeout->tv_nsec / 1000); + count = equeue->WaitForEvents(eventlist, nevents, µs); + } else { + count = equeue->WaitForEvents(eventlist, nevents, nullptr); + } + } + return count; +} + +int PS4_SYSV_ABI sceKernelCreateEqueue(OrbisKernelEqueue* eq, const char* name) { if (eq == nullptr) { LOG_ERROR(Kernel_Event, "Event queue is null!"); return ORBIS_KERNEL_ERROR_EINVAL; } + if (name == nullptr) { LOG_ERROR(Kernel_Event, "Event queue name is null!"); return ORBIS_KERNEL_ERROR_EINVAL; } // Maximum is 32 including null terminator - static constexpr size_t MaxEventQueueNameSize = 32; + static constexpr u64 MaxEventQueueNameSize = 32; if (std::strlen(name) > MaxEventQueueNameSize) { LOG_ERROR(Kernel_Event, "Event queue name exceeds 32 bytes!"); return ORBIS_KERNEL_ERROR_ENAMETOOLONG; @@ -285,29 +434,42 @@ int PS4_SYSV_ABI sceKernelCreateEqueue(SceKernelEqueue* eq, const char* name) { LOG_INFO(Kernel_Event, "name = {}", name); - *eq = new EqueueInternal(name); + // Reserve a file handle for the kqueue + auto* handles = Common::Singleton::Instance(); + OrbisKernelEqueue kqueue_handle = handles->CreateHandle(); + auto* kqueue_file = handles->GetFile(kqueue_handle); + kqueue_file->type = Core::FileSys::FileType::Equeue; + + // Create the equeue + kqueues[kqueue_handle] = new EqueueInternal(kqueue_handle, name); + *eq = kqueue_handle; + return ORBIS_OK; } -int PS4_SYSV_ABI sceKernelDeleteEqueue(SceKernelEqueue eq) { - if (eq == nullptr) { +int PS4_SYSV_ABI sceKernelDeleteEqueue(OrbisKernelEqueue eq) { + if (!kqueues.contains(eq)) { return ORBIS_KERNEL_ERROR_EBADF; } - delete eq; + auto* handles = Common::Singleton::Instance(); + handles->DeleteHandle(eq); + kqueues.erase(eq); return ORBIS_OK; } -int PS4_SYSV_ABI sceKernelWaitEqueue(SceKernelEqueue eq, SceKernelEvent* ev, int num, int* out, - SceKernelUseconds* timo) { +int PS4_SYSV_ABI sceKernelWaitEqueue(OrbisKernelEqueue eq, OrbisKernelEvent* ev, int num, int* out, + OrbisKernelUseconds* timo) { HLE_TRACE; - TRACE_HINT(eq->GetName()); - LOG_TRACE(Kernel_Event, "equeue = {} num = {}", eq->GetName(), num); - - if (eq == nullptr) { + if (!kqueues.contains(eq)) { return ORBIS_KERNEL_ERROR_EBADF; } + auto& equeue = kqueues[eq]; + + TRACE_HINT(equeue->GetName()); + LOG_TRACE(Kernel_Event, "equeue = {} num = {}", equeue->GetName(), num); + if (ev == nullptr) { return ORBIS_KERNEL_ERROR_EFAULT; } @@ -317,7 +479,7 @@ int PS4_SYSV_ABI sceKernelWaitEqueue(SceKernelEqueue eq, SceKernelEvent* ev, int return ORBIS_KERNEL_ERROR_EINVAL; } - *out = eq->WaitForEvents(ev, num, timo); + *out = equeue->WaitForEvents(ev, num, timo); if (*out == 0) { return ORBIS_KERNEL_ERROR_ETIMEDOUT; @@ -326,13 +488,9 @@ int PS4_SYSV_ABI sceKernelWaitEqueue(SceKernelEqueue eq, SceKernelEvent* ev, int return ORBIS_OK; } -static void HrTimerCallback(SceKernelEqueue eq, const SceKernelEvent& kevent) { - eq->TriggerEvent(kevent.ident, SceKernelEvent::Filter::HrTimer, kevent.udata); -} - -s32 PS4_SYSV_ABI sceKernelAddHRTimerEvent(SceKernelEqueue eq, int id, OrbisKernelTimespec* ts, +s32 PS4_SYSV_ABI sceKernelAddHRTimerEvent(OrbisKernelEqueue eq, int id, OrbisKernelTimespec* ts, void* udata) { - if (eq == nullptr) { + if (!kqueues.contains(eq)) { return ORBIS_KERNEL_ERROR_EBADF; } @@ -340,10 +498,12 @@ s32 PS4_SYSV_ABI sceKernelAddHRTimerEvent(SceKernelEqueue eq, int id, OrbisKerne EqueueEvent event{}; event.event.ident = id; - event.event.filter = SceKernelEvent::Filter::HrTimer; - event.event.flags = SceKernelEvent::Flags::Add | SceKernelEvent::Flags::OneShot; + event.event.filter = OrbisKernelEvent::Filter::HrTimer; + event.event.flags = OrbisKernelEvent::Flags::Add | OrbisKernelEvent::Flags::OneShot; event.event.fflags = 0; - event.event.data = total_ns; + // Data is stored as the address of a OrbisKernelBintime struct. + OrbisKernelBintime time{ts->tv_sec, ts->tv_nsec * 0x44b82fa09}; + event.event.data = reinterpret_cast(&time); event.event.udata = udata; // HR timers cannot be implemented within the existing event queue architecture due to the @@ -353,146 +513,137 @@ s32 PS4_SYSV_ABI sceKernelAddHRTimerEvent(SceKernelEqueue eq, int id, OrbisKerne // `HrTimerSpinlockThresholdUs`) and fall back to boost asio timers if the time to tick is // large. Even for large delays, we truncate a small portion to complete the wait // using the spinlock, prioritizing precision. + auto& equeue = kqueues[eq]; if (total_ns < HrTimerSpinlockThresholdNs) { - return eq->AddSmallTimer(event) ? ORBIS_OK : ORBIS_KERNEL_ERROR_ENOMEM; + return equeue->AddSmallTimer(event) ? ORBIS_OK : ORBIS_KERNEL_ERROR_ENOMEM; } - if (!eq->AddEvent(event) || - !eq->ScheduleEvent(id, SceKernelEvent::Filter::HrTimer, HrTimerCallback)) { + if (!equeue->AddEvent(event)) { return ORBIS_KERNEL_ERROR_ENOMEM; } return ORBIS_OK; } -int PS4_SYSV_ABI sceKernelDeleteHRTimerEvent(SceKernelEqueue eq, int id) { - if (eq == nullptr) { +int PS4_SYSV_ABI sceKernelDeleteHRTimerEvent(OrbisKernelEqueue eq, int id) { + if (!kqueues.contains(eq)) { return ORBIS_KERNEL_ERROR_EBADF; } - if (eq->HasSmallTimer()) { - return eq->RemoveSmallTimer(id) ? ORBIS_OK : ORBIS_KERNEL_ERROR_ENOENT; + auto& equeue = kqueues[eq]; + if (equeue->HasSmallTimer()) { + return equeue->RemoveSmallTimer(id) ? ORBIS_OK : ORBIS_KERNEL_ERROR_ENOENT; } else { - return eq->RemoveEvent(id, SceKernelEvent::Filter::HrTimer) ? ORBIS_OK - : ORBIS_KERNEL_ERROR_ENOENT; + return equeue->RemoveEvent(id, OrbisKernelEvent::Filter::HrTimer) + ? ORBIS_OK + : ORBIS_KERNEL_ERROR_ENOENT; } } -static void TimerCallback(SceKernelEqueue eq, const SceKernelEvent& kevent) { - if (eq->EventExists(kevent.ident, kevent.filter)) { - eq->TriggerEvent(kevent.ident, SceKernelEvent::Filter::Timer, kevent.udata); - - if (!(kevent.flags & SceKernelEvent::Flags::OneShot)) { - // Reschedule the event for its next period. - eq->ScheduleEvent(kevent.ident, kevent.filter, TimerCallback); - } - } -} - -int PS4_SYSV_ABI sceKernelAddTimerEvent(SceKernelEqueue eq, int id, SceKernelUseconds usec, +int PS4_SYSV_ABI sceKernelAddTimerEvent(OrbisKernelEqueue eq, int id, OrbisKernelUseconds usec, void* udata) { - if (eq == nullptr) { + if (!kqueues.contains(eq)) { return ORBIS_KERNEL_ERROR_EBADF; } EqueueEvent event{}; event.event.ident = static_cast(id); - event.event.filter = SceKernelEvent::Filter::Timer; - event.event.flags = SceKernelEvent::Flags::Add; + event.event.filter = OrbisKernelEvent::Filter::Timer; + event.event.flags = OrbisKernelEvent::Flags::Add; event.event.fflags = 0; - event.event.data = usec * 1000; + event.event.data = usec / 1000; event.event.udata = udata; - LOG_DEBUG(Kernel_Event, "Added timing event: queue name={}, queue id={}, usec={}, pointer={:x}", - eq->GetName(), event.event.ident, usec, reinterpret_cast(udata)); - - if (!eq->AddEvent(event) || - !eq->ScheduleEvent(id, SceKernelEvent::Filter::Timer, TimerCallback)) { + auto& equeue = kqueues[eq]; + if (!equeue->AddEvent(event)) { return ORBIS_KERNEL_ERROR_ENOMEM; } return ORBIS_OK; } -int PS4_SYSV_ABI sceKernelDeleteTimerEvent(SceKernelEqueue eq, int id) { - if (eq == nullptr) { +int PS4_SYSV_ABI sceKernelDeleteTimerEvent(OrbisKernelEqueue eq, int id) { + if (!kqueues.contains(eq)) { return ORBIS_KERNEL_ERROR_EBADF; } - return eq->RemoveEvent(id, SceKernelEvent::Filter::Timer) ? ORBIS_OK - : ORBIS_KERNEL_ERROR_ENOENT; + return kqueues[eq]->RemoveEvent(id, OrbisKernelEvent::Filter::Timer) + ? ORBIS_OK + : ORBIS_KERNEL_ERROR_ENOENT; } -int PS4_SYSV_ABI sceKernelAddUserEvent(SceKernelEqueue eq, int id) { - if (eq == nullptr) { +int PS4_SYSV_ABI sceKernelAddUserEvent(OrbisKernelEqueue eq, int id) { + if (!kqueues.contains(eq)) { return ORBIS_KERNEL_ERROR_EBADF; } EqueueEvent event{}; event.event.ident = id; - event.event.filter = SceKernelEvent::Filter::User; + event.event.filter = OrbisKernelEvent::Filter::User; event.event.udata = 0; - event.event.flags = SceKernelEvent::Flags::Add; + event.event.flags = OrbisKernelEvent::Flags::Add; event.event.fflags = 0; event.event.data = 0; - return eq->AddEvent(event) ? ORBIS_OK : ORBIS_KERNEL_ERROR_ENOMEM; + return kqueues[eq]->AddEvent(event) ? ORBIS_OK : ORBIS_KERNEL_ERROR_ENOMEM; } -int PS4_SYSV_ABI sceKernelAddUserEventEdge(SceKernelEqueue eq, int id) { - if (eq == nullptr) { +int PS4_SYSV_ABI sceKernelAddUserEventEdge(OrbisKernelEqueue eq, int id) { + if (!kqueues.contains(eq)) { return ORBIS_KERNEL_ERROR_EBADF; } EqueueEvent event{}; event.event.ident = id; - event.event.filter = SceKernelEvent::Filter::User; + event.event.filter = OrbisKernelEvent::Filter::User; event.event.udata = 0; - event.event.flags = SceKernelEvent::Flags::Add | SceKernelEvent::Flags::Clear; + event.event.flags = OrbisKernelEvent::Flags::Add | OrbisKernelEvent::Flags::Clear; event.event.fflags = 0; event.event.data = 0; - return eq->AddEvent(event) ? ORBIS_OK : ORBIS_KERNEL_ERROR_ENOMEM; + return kqueues[eq]->AddEvent(event) ? ORBIS_OK : ORBIS_KERNEL_ERROR_ENOMEM; } -void* PS4_SYSV_ABI sceKernelGetEventUserData(const SceKernelEvent* ev) { +void* PS4_SYSV_ABI sceKernelGetEventUserData(const OrbisKernelEvent* ev) { ASSERT(ev); return ev->udata; } -u64 PS4_SYSV_ABI sceKernelGetEventId(const SceKernelEvent* ev) { +u64 PS4_SYSV_ABI sceKernelGetEventId(const OrbisKernelEvent* ev) { return ev->ident; } -int PS4_SYSV_ABI sceKernelTriggerUserEvent(SceKernelEqueue eq, int id, void* udata) { - if (eq == nullptr) { +int PS4_SYSV_ABI sceKernelTriggerUserEvent(OrbisKernelEqueue eq, int id, void* udata) { + if (!kqueues.contains(eq)) { return ORBIS_KERNEL_ERROR_EBADF; } - if (!eq->TriggerEvent(id, SceKernelEvent::Filter::User, udata)) { + if (!kqueues[eq]->TriggerEvent(id, OrbisKernelEvent::Filter::User, udata)) { return ORBIS_KERNEL_ERROR_ENOENT; } return ORBIS_OK; } -int PS4_SYSV_ABI sceKernelDeleteUserEvent(SceKernelEqueue eq, int id) { - if (eq == nullptr) { +int PS4_SYSV_ABI sceKernelDeleteUserEvent(OrbisKernelEqueue eq, int id) { + if (!kqueues.contains(eq)) { return ORBIS_KERNEL_ERROR_EBADF; } - if (!eq->RemoveEvent(id, SceKernelEvent::Filter::User)) { + if (!kqueues[eq]->RemoveEvent(id, OrbisKernelEvent::Filter::User)) { return ORBIS_KERNEL_ERROR_ENOENT; } return ORBIS_OK; } -int PS4_SYSV_ABI sceKernelGetEventFilter(const SceKernelEvent* ev) { +int PS4_SYSV_ABI sceKernelGetEventFilter(const OrbisKernelEvent* ev) { return ev->filter; } -u64 PS4_SYSV_ABI sceKernelGetEventData(const SceKernelEvent* ev) { +u64 PS4_SYSV_ABI sceKernelGetEventData(const OrbisKernelEvent* ev) { return ev->data; } void RegisterEventQueue(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("nh2IFMgKTv8", "libScePosix", 1, "libkernel", posix_kqueue); + LIB_FUNCTION("RW-GEfpnsqg", "libScePosix", 1, "libkernel", posix_kevent); LIB_FUNCTION("D0OdFMjp46I", "libkernel", 1, "libkernel", sceKernelCreateEqueue); LIB_FUNCTION("jpFjmgAC5AE", "libkernel", 1, "libkernel", sceKernelDeleteEqueue); LIB_FUNCTION("fzyMKs9kim0", "libkernel", 1, "libkernel", sceKernelWaitEqueue); diff --git a/src/core/libraries/kernel/equeue.h b/src/core/libraries/kernel/equeue.h index 83b4b8689..9b3c73c19 100644 --- a/src/core/libraries/kernel/equeue.h +++ b/src/core/libraries/kernel/equeue.h @@ -22,10 +22,15 @@ namespace Libraries::Kernel { class EqueueInternal; struct EqueueEvent; -using SceKernelUseconds = u32; -using SceKernelEqueue = EqueueInternal*; +struct OrbisKernelBintime { + s64 sec; + s64 frac; +}; -struct SceKernelEvent { +using OrbisKernelUseconds = u32; +using OrbisKernelEqueue = s64; + +struct OrbisKernelEvent { enum Filter : s16 { None = 0, Read = -1, @@ -78,7 +83,7 @@ struct OrbisVideoOutEventData { }; struct EqueueEvent { - SceKernelEvent event; + OrbisKernelEvent event; void* data = nullptr; std::chrono::steady_clock::time_point time_added; std::chrono::nanoseconds timer_interval; @@ -137,13 +142,14 @@ private: class EqueueInternal { struct SmallTimer { - SceKernelEvent event; + OrbisKernelEvent event; std::chrono::steady_clock::time_point added; std::chrono::nanoseconds interval; }; public: - explicit EqueueInternal(std::string_view name) : m_name(name) {} + explicit EqueueInternal(OrbisKernelEqueue handle, std::string_view name) + : m_handle(handle), m_name(name) {} std::string_view GetName() const { return m_name; @@ -151,11 +157,11 @@ public: bool AddEvent(EqueueEvent& event); bool ScheduleEvent(u64 id, s16 filter, - void (*callback)(SceKernelEqueue, const SceKernelEvent&)); + void (*callback)(OrbisKernelEqueue, const OrbisKernelEvent&)); bool RemoveEvent(u64 id, s16 filter); - int WaitForEvents(SceKernelEvent* ev, int num, const SceKernelUseconds* timo); + int WaitForEvents(OrbisKernelEvent* ev, int num, const OrbisKernelUseconds* timo); bool TriggerEvent(u64 ident, s16 filter, void* trigger_data); - int GetTriggeredEvents(SceKernelEvent* ev, int num); + int GetTriggeredEvents(OrbisKernelEvent* ev, int num); bool AddSmallTimer(EqueueEvent& event); bool HasSmallTimer() { @@ -170,11 +176,12 @@ public: return false; } - int WaitForSmallTimer(SceKernelEvent* ev, int num, u32 micros); + int WaitForSmallTimer(OrbisKernelEvent* ev, int num, u32 micros); bool EventExists(u64 id, s16 filter); private: + OrbisKernelEqueue m_handle; std::string m_name; std::mutex m_mutex; std::vector m_events; @@ -182,7 +189,8 @@ private: std::unordered_map m_small_timers; }; -u64 PS4_SYSV_ABI sceKernelGetEventData(const SceKernelEvent* ev); +EqueueInternal* GetEqueue(OrbisKernelEqueue eq); +u64 PS4_SYSV_ABI sceKernelGetEventData(const OrbisKernelEvent* ev); void RegisterEventQueue(Core::Loader::SymbolsResolver* sym); diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index bc4e2def6..184343801 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -792,7 +792,8 @@ s32 PS4_SYSV_ABI fstat(s32 fd, OrbisKernelStat* sb) { return file->socket->fstat(sb); } case Core::FileSys::FileType::Epoll: - case Core::FileSys::FileType::Resolver: { + case Core::FileSys::FileType::Resolver: + case Core::FileSys::FileType::Equeue: { LOG_ERROR(Kernel_Fs, "(STUBBED) file type {}", magic_enum::enum_name(file->type.load())); break; } diff --git a/src/core/libraries/videoout/driver.cpp b/src/core/libraries/videoout/driver.cpp index 2f44b1c99..bebbf9602 100644 --- a/src/core/libraries/videoout/driver.cpp +++ b/src/core/libraries/videoout/driver.cpp @@ -193,7 +193,7 @@ void VideoOutDriver::Flip(const Request& req) { if (event != nullptr) { event->TriggerEvent( static_cast(OrbisVideoOutInternalEventId::Flip), - Kernel::SceKernelEvent::Filter::VideoOut, + Kernel::OrbisKernelEvent::Filter::VideoOut, reinterpret_cast(static_cast(OrbisVideoOutInternalEventId::Flip) | (req.flip_arg << 16))); } @@ -320,7 +320,7 @@ void VideoOutDriver::PresentThread(std::stop_token token) { for (auto& event : main_port.vblank_events) { if (event != nullptr) { event->TriggerEvent(static_cast(OrbisVideoOutInternalEventId::Vblank), - Kernel::SceKernelEvent::Filter::VideoOut, + Kernel::OrbisKernelEvent::Filter::VideoOut, reinterpret_cast( static_cast(OrbisVideoOutInternalEventId::Vblank) | (vblank_status.count << 16))); diff --git a/src/core/libraries/videoout/driver.h b/src/core/libraries/videoout/driver.h index e0eace791..96bd58500 100644 --- a/src/core/libraries/videoout/driver.h +++ b/src/core/libraries/videoout/driver.h @@ -25,8 +25,8 @@ struct VideoOutPort { std::array groups; FlipStatus flip_status; SceVideoOutVblankStatus vblank_status; - std::vector flip_events; - std::vector vblank_events; + std::vector flip_events; + std::vector vblank_events; std::mutex vo_mutex; std::mutex port_mutex; std::condition_variable vo_cv; diff --git a/src/core/libraries/videoout/video_out.cpp b/src/core/libraries/videoout/video_out.cpp index da58772a0..1b8a6b59d 100644 --- a/src/core/libraries/videoout/video_out.cpp +++ b/src/core/libraries/videoout/video_out.cpp @@ -38,7 +38,7 @@ void PS4_SYSV_ABI sceVideoOutSetBufferAttribute(BufferAttribute* attribute, Pixe attribute->option = SCE_VIDEO_OUT_BUFFER_ATTRIBUTE_OPTION_NONE; } -s32 PS4_SYSV_ABI sceVideoOutAddFlipEvent(Kernel::SceKernelEqueue eq, s32 handle, void* udata) { +s32 PS4_SYSV_ABI sceVideoOutAddFlipEvent(Kernel::OrbisKernelEqueue eq, s32 handle, void* udata) { LOG_INFO(Lib_VideoOut, "handle = {}", handle); auto* port = driver->GetPort(handle); @@ -46,39 +46,41 @@ s32 PS4_SYSV_ABI sceVideoOutAddFlipEvent(Kernel::SceKernelEqueue eq, s32 handle, return ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE; } - if (eq == nullptr) { + auto equeue = Kernel::GetEqueue(eq); + if (equeue == nullptr) { return ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE; } Kernel::EqueueEvent event{}; event.event.ident = static_cast(OrbisVideoOutInternalEventId::Flip); - event.event.filter = Kernel::SceKernelEvent::Filter::VideoOut; - event.event.flags = Kernel::SceKernelEvent::Flags::Add; + event.event.filter = Kernel::OrbisKernelEvent::Filter::VideoOut; + event.event.flags = Kernel::OrbisKernelEvent::Flags::Add; event.event.udata = udata; event.event.fflags = 0; event.event.data = 0; event.data = port; - eq->AddEvent(event); + equeue->AddEvent(event); - port->flip_events.push_back(eq); + port->flip_events.push_back(equeue); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceVideoOutDeleteFlipEvent(Kernel::SceKernelEqueue eq, s32 handle) { +s32 PS4_SYSV_ABI sceVideoOutDeleteFlipEvent(Kernel::OrbisKernelEqueue eq, s32 handle) { auto* port = driver->GetPort(handle); if (port == nullptr) { return ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE; } - if (eq == nullptr) { + auto equeue = Kernel::GetEqueue(eq); + if (equeue == nullptr) { return ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE; } - eq->RemoveEvent(handle, Kernel::SceKernelEvent::Filter::VideoOut); - port->flip_events.erase(find(port->flip_events.begin(), port->flip_events.end(), eq)); + equeue->RemoveEvent(handle, Kernel::OrbisKernelEvent::Filter::VideoOut); + port->flip_events.erase(find(port->flip_events.begin(), port->flip_events.end(), equeue)); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceVideoOutAddVblankEvent(Kernel::SceKernelEqueue eq, s32 handle, void* udata) { +s32 PS4_SYSV_ABI sceVideoOutAddVblankEvent(Kernel::OrbisKernelEqueue eq, s32 handle, void* udata) { LOG_INFO(Lib_VideoOut, "handle = {}", handle); auto* port = driver->GetPort(handle); @@ -86,35 +88,37 @@ s32 PS4_SYSV_ABI sceVideoOutAddVblankEvent(Kernel::SceKernelEqueue eq, s32 handl return ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE; } - if (eq == nullptr) { + auto equeue = Kernel::GetEqueue(eq); + if (equeue == nullptr) { return ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE; } Kernel::EqueueEvent event{}; event.event.ident = static_cast(OrbisVideoOutInternalEventId::Vblank); - event.event.filter = Kernel::SceKernelEvent::Filter::VideoOut; - event.event.flags = Kernel::SceKernelEvent::Flags::Add; + event.event.filter = Kernel::OrbisKernelEvent::Filter::VideoOut; + event.event.flags = Kernel::OrbisKernelEvent::Flags::Add; event.event.udata = udata; event.event.fflags = 0; event.event.data = 0; event.data = port; - eq->AddEvent(event); + equeue->AddEvent(event); - port->vblank_events.push_back(eq); + port->vblank_events.push_back(equeue); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceVideoOutDeleteVblankEvent(Kernel::SceKernelEqueue eq, s32 handle) { +s32 PS4_SYSV_ABI sceVideoOutDeleteVblankEvent(Kernel::OrbisKernelEqueue eq, s32 handle) { auto* port = driver->GetPort(handle); if (port == nullptr) { return ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE; } - if (eq == nullptr) { + auto equeue = Kernel::GetEqueue(eq); + if (equeue == nullptr) { return ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE; } - eq->RemoveEvent(handle, Kernel::SceKernelEvent::Filter::VideoOut); - port->vblank_events.erase(find(port->vblank_events.begin(), port->vblank_events.end(), eq)); + equeue->RemoveEvent(handle, Kernel::OrbisKernelEvent::Filter::VideoOut); + port->vblank_events.erase(find(port->vblank_events.begin(), port->vblank_events.end(), equeue)); return ORBIS_OK; } @@ -180,11 +184,11 @@ s32 PS4_SYSV_ABI sceVideoOutSubmitFlip(s32 handle, s32 bufferIndex, s32 flipMode return ORBIS_OK; } -s32 PS4_SYSV_ABI sceVideoOutGetEventId(const Kernel::SceKernelEvent* ev) { +s32 PS4_SYSV_ABI sceVideoOutGetEventId(const Kernel::OrbisKernelEvent* ev) { if (ev == nullptr) { return ORBIS_VIDEO_OUT_ERROR_INVALID_ADDRESS; } - if (ev->filter != Kernel::SceKernelEvent::Filter::VideoOut) { + if (ev->filter != Kernel::OrbisKernelEvent::Filter::VideoOut) { return ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT; } @@ -208,11 +212,11 @@ s32 PS4_SYSV_ABI sceVideoOutGetEventId(const Kernel::SceKernelEvent* ev) { } } -s32 PS4_SYSV_ABI sceVideoOutGetEventData(const Kernel::SceKernelEvent* ev, s64* data) { +s32 PS4_SYSV_ABI sceVideoOutGetEventData(const Kernel::OrbisKernelEvent* ev, s64* data) { if (ev == nullptr || data == nullptr) { return ORBIS_VIDEO_OUT_ERROR_INVALID_ADDRESS; } - if (ev->filter != Kernel::SceKernelEvent::Filter::VideoOut) { + if (ev->filter != Kernel::OrbisKernelEvent::Filter::VideoOut) { return ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT; } @@ -225,11 +229,11 @@ s32 PS4_SYSV_ABI sceVideoOutGetEventData(const Kernel::SceKernelEvent* ev, s64* return ORBIS_OK; } -s32 PS4_SYSV_ABI sceVideoOutGetEventCount(const Kernel::SceKernelEvent* ev) { +s32 PS4_SYSV_ABI sceVideoOutGetEventCount(const Kernel::OrbisKernelEvent* ev) { if (ev == nullptr) { return ORBIS_VIDEO_OUT_ERROR_INVALID_ADDRESS; } - if (ev->filter != Kernel::SceKernelEvent::Filter::VideoOut) { + if (ev->filter != Kernel::OrbisKernelEvent::Filter::VideoOut) { return ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT; } diff --git a/src/core/libraries/videoout/video_out.h b/src/core/libraries/videoout/video_out.h index 2c99a4d1c..09b79e85d 100644 --- a/src/core/libraries/videoout/video_out.h +++ b/src/core/libraries/videoout/video_out.h @@ -119,8 +119,8 @@ struct OrbisVideoOutEventData { void PS4_SYSV_ABI sceVideoOutSetBufferAttribute(BufferAttribute* attribute, PixelFormat pixelFormat, u32 tilingMode, u32 aspectRatio, u32 width, u32 height, u32 pitchInPixel); -s32 PS4_SYSV_ABI sceVideoOutAddFlipEvent(Kernel::SceKernelEqueue eq, s32 handle, void* udata); -s32 PS4_SYSV_ABI sceVideoOutAddVblankEvent(Kernel::SceKernelEqueue eq, s32 handle, void* udata); +s32 PS4_SYSV_ABI sceVideoOutAddFlipEvent(Kernel::OrbisKernelEqueue eq, s32 handle, void* udata); +s32 PS4_SYSV_ABI sceVideoOutAddVblankEvent(Kernel::OrbisKernelEqueue eq, s32 handle, void* udata); s32 PS4_SYSV_ABI sceVideoOutRegisterBuffers(s32 handle, s32 startIndex, void* const* addresses, s32 bufferNum, const BufferAttribute* attribute); s32 PS4_SYSV_ABI sceVideoOutGetBufferLabelAddress(s32 handle, uintptr_t* label_addr); @@ -133,8 +133,8 @@ s32 PS4_SYSV_ABI sceVideoOutGetResolutionStatus(s32 handle, SceVideoOutResolutio s32 PS4_SYSV_ABI sceVideoOutOpen(Libraries::UserService::OrbisUserServiceUserId userId, s32 busType, s32 index, const void* param); s32 PS4_SYSV_ABI sceVideoOutClose(s32 handle); -s32 PS4_SYSV_ABI sceVideoOutGetEventId(const Kernel::SceKernelEvent* ev); -s32 PS4_SYSV_ABI sceVideoOutGetEventData(const Kernel::SceKernelEvent* ev, s64* data); +s32 PS4_SYSV_ABI sceVideoOutGetEventId(const Kernel::OrbisKernelEvent* ev); +s32 PS4_SYSV_ABI sceVideoOutGetEventData(const Kernel::OrbisKernelEvent* ev, s64* data); s32 PS4_SYSV_ABI sceVideoOutColorSettingsSetGamma(SceVideoOutColorSettings* settings, float gamma); s32 PS4_SYSV_ABI sceVideoOutAdjustColor(s32 handle, const SceVideoOutColorSettings* settings); From c265a39227c4a654592b5c91fd21568ece816c77 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Mon, 23 Feb 2026 09:08:39 +0100 Subject: [PATCH 6/9] fix typo --- src/core/libraries/kernel/threads/exception.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/libraries/kernel/threads/exception.cpp b/src/core/libraries/kernel/threads/exception.cpp index dfb3b05f4..0b27f2bd8 100644 --- a/src/core/libraries/kernel/threads/exception.cpp +++ b/src/core/libraries/kernel/threads/exception.cpp @@ -188,7 +188,7 @@ void SigactionHandler(int native_signum, siginfo_t* inf, ucontext_t* raw_context ctx.uc_mcontext.mc_rsp = regs.__rsp; ctx.uc_mcontext.mc_fs = regs.__fs; ctx.uc_mcontext.mc_gs = regs.__gs; - ctx.uc_mcontext.mc_gs = regs.__rip; + ctx.uc_mcontext.mc_rip = regs.__rip; ctx.uc_mcontext.mc_addr = reinterpret_cast(inf->si_addr); #else const auto& regs = raw_context->uc_mcontext.gregs; From 407d287fb1c34cfe7134ec8e500826798f2a21fa Mon Sep 17 00:00:00 2001 From: ElBread3 <92335081+ElBread3@users.noreply.github.com> Date: Mon, 23 Feb 2026 19:30:57 +0000 Subject: [PATCH 7/9] tweak LoadFigure and RemoveFigure (#4071) --- src/core/ipc/ipc.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/core/ipc/ipc.cpp b/src/core/ipc/ipc.cpp index ea7cd38b4..aab3e7de5 100644 --- a/src/core/ipc/ipc.cpp +++ b/src/core/ipc/ipc.cpp @@ -174,12 +174,18 @@ void IPC::InputLoop() { } else if (cmd == "USB_LOAD_FIGURE") { const auto ref = Libraries::Usbd::usb_backend->GetImplRef(); if (ref) { - ref->LoadFigure(next_str(), next_u64(), next_u64()); + std::string file_name = next_str(); + const u8 pad = next_u64(); + const u8 slot = next_u64(); + ref->LoadFigure(file_name, pad, slot); } } else if (cmd == "USB_REMOVE_FIGURE") { const auto ref = Libraries::Usbd::usb_backend->GetImplRef(); if (ref) { - ref->RemoveFigure(next_u64(), next_u64(), next_u64() != 0); + const u8 pad = next_u64(); + const u8 slot = next_u64(); + bool full_remove = next_u64() != 0; + ref->RemoveFigure(pad, slot, full_remove); } } else if (cmd == "USB_MOVE_FIGURE") { const auto ref = Libraries::Usbd::usb_backend->GetImplRef(); From af9cbb8e8aeb5c5cae59779ccc4ae767d8eee7ba Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Tue, 24 Feb 2026 08:00:17 +0100 Subject: [PATCH 8/9] Fix some logic bugs in sceHttpUriParse (#4067) --- src/core/libraries/network/http.cpp | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/core/libraries/network/http.cpp b/src/core/libraries/network/http.cpp index 8bc9b51f0..4d6886908 100644 --- a/src/core/libraries/network/http.cpp +++ b/src/core/libraries/network/http.cpp @@ -935,18 +935,24 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v pathLength++; } - // Ensure the path starts with '/' - if (pathLength > 0 && pathStart[0] != '/') { + if (pathLength > 0) { // Prepend '/' to the path requiredSize += pathLength + 2; // Include '/' and null terminator if (pool && prepare < requiredSize) { - LOG_ERROR(Lib_Http, "out of memory"); + LOG_ERROR(Lib_Http, "out of memory, provided size: {}, required size: {}", + prepare, requiredSize); return ORBIS_HTTP_ERROR_OUT_OF_MEMORY; } if (out && pool) { out->path = (char*)pool + (requiredSize - pathLength - 2); + out->username = (char*)pool + (requiredSize - pathLength - 3); + out->password = (char*)pool + (requiredSize - pathLength - 3); + out->hostname = (char*)pool + (requiredSize - pathLength - 3); + out->query = (char*)pool + (requiredSize - pathLength - 3); + out->fragment = (char*)pool + (requiredSize - pathLength - 3); + out->username[0] = '\0'; out->path[0] = '/'; // Add leading '/' memcpy(out->path + 1, pathStart, pathLength); out->path[pathLength + 1] = '\0'; @@ -969,6 +975,19 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v // Move past the path offset += pathLength; + } else { + // Parse the path (everything after the slashes) + char* pathStart = (char*)srcUri + offset; + u64 pathLength = 0; + while (pathStart[pathLength] && pathStart[pathLength] != '?' && + pathStart[pathLength] != '#') { + pathLength++; + } + + if (pathLength > 0) { + requiredSize += pathLength + 3; // Add '/' and null terminator, and the dummy + // null character for the other fields + } } } From e1ecd8e98c12128c6dca9044f6688dcec1b9cfc2 Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Tue, 24 Feb 2026 20:35:05 +0300 Subject: [PATCH 9/9] threads: initialize tls on thread creation (take 2) (#4070) --- src/core/libraries/avplayer/avplayer_impl.cpp | 16 +++--- .../libraries/avplayer/avplayer_state.cpp | 2 +- src/core/libraries/ime/ime.cpp | 8 +-- src/core/libraries/ime/ime_dialog_ui.cpp | 5 +- src/core/libraries/kernel/threads.cpp | 11 ++++ src/core/libraries/kernel/threads.h | 3 ++ .../libraries/kernel/threads/exception.cpp | 2 +- src/core/libraries/kernel/threads/pthread.cpp | 11 +++- .../libraries/kernel/threads/pthread_spec.cpp | 2 +- src/core/libraries/network/net_ctl_obj.cpp | 4 +- src/core/libraries/ngs2/ngs2.cpp | 4 +- .../libraries/usbd/emulated/dimensions.cpp | 53 ++++++++++++++----- src/core/libraries/usbd/emulated/dimensions.h | 2 + src/core/linker.cpp | 10 ++-- src/core/module.cpp | 2 +- src/core/tls.cpp | 2 +- src/core/tls.h | 25 +-------- 17 files changed, 94 insertions(+), 68 deletions(-) diff --git a/src/core/libraries/avplayer/avplayer_impl.cpp b/src/core/libraries/avplayer/avplayer_impl.cpp index 138747da4..db32862ad 100644 --- a/src/core/libraries/avplayer/avplayer_impl.cpp +++ b/src/core/libraries/avplayer/avplayer_impl.cpp @@ -12,28 +12,28 @@ void* PS4_SYSV_ABI AvPlayer::Allocate(void* handle, u32 alignment, u32 size) { const auto* const self = reinterpret_cast(handle); const auto allocate = self->m_init_data_original.memory_replacement.allocate; const auto ptr = self->m_init_data_original.memory_replacement.object_ptr; - return Core::ExecuteGuest(allocate, ptr, alignment, size); + return allocate(ptr, alignment, size); } void PS4_SYSV_ABI AvPlayer::Deallocate(void* handle, void* memory) { const auto* const self = reinterpret_cast(handle); const auto deallocate = self->m_init_data_original.memory_replacement.deallocate; const auto ptr = self->m_init_data_original.memory_replacement.object_ptr; - return Core::ExecuteGuest(deallocate, ptr, memory); + return deallocate(ptr, memory); } void* PS4_SYSV_ABI AvPlayer::AllocateTexture(void* handle, u32 alignment, u32 size) { const auto* const self = reinterpret_cast(handle); const auto allocate = self->m_init_data_original.memory_replacement.allocate_texture; const auto ptr = self->m_init_data_original.memory_replacement.object_ptr; - return Core::ExecuteGuest(allocate, ptr, alignment, size); + return allocate(ptr, alignment, size); } void PS4_SYSV_ABI AvPlayer::DeallocateTexture(void* handle, void* memory) { const auto* const self = reinterpret_cast(handle); const auto deallocate = self->m_init_data_original.memory_replacement.deallocate_texture; const auto ptr = self->m_init_data_original.memory_replacement.object_ptr; - return Core::ExecuteGuest(deallocate, ptr, memory); + return deallocate(ptr, memory); } int PS4_SYSV_ABI AvPlayer::OpenFile(void* handle, const char* filename) { @@ -42,7 +42,7 @@ int PS4_SYSV_ABI AvPlayer::OpenFile(void* handle, const char* filename) { const auto open = self->m_init_data_original.file_replacement.open; const auto ptr = self->m_init_data_original.file_replacement.object_ptr; - return Core::ExecuteGuest(open, ptr, filename); + return open(ptr, filename); } int PS4_SYSV_ABI AvPlayer::CloseFile(void* handle) { @@ -51,7 +51,7 @@ int PS4_SYSV_ABI AvPlayer::CloseFile(void* handle) { const auto close = self->m_init_data_original.file_replacement.close; const auto ptr = self->m_init_data_original.file_replacement.object_ptr; - return Core::ExecuteGuest(close, ptr); + return close(ptr); } int PS4_SYSV_ABI AvPlayer::ReadOffsetFile(void* handle, u8* buffer, u64 position, u32 length) { @@ -60,7 +60,7 @@ int PS4_SYSV_ABI AvPlayer::ReadOffsetFile(void* handle, u8* buffer, u64 position const auto read_offset = self->m_init_data_original.file_replacement.read_offset; const auto ptr = self->m_init_data_original.file_replacement.object_ptr; - return Core::ExecuteGuest(read_offset, ptr, buffer, position, length); + return read_offset(ptr, buffer, position, length); } u64 PS4_SYSV_ABI AvPlayer::SizeFile(void* handle) { @@ -69,7 +69,7 @@ u64 PS4_SYSV_ABI AvPlayer::SizeFile(void* handle) { const auto size = self->m_init_data_original.file_replacement.size; const auto ptr = self->m_init_data_original.file_replacement.object_ptr; - return Core::ExecuteGuest(size, ptr); + return size(ptr); } AvPlayerInitData AvPlayer::StubInitData(const AvPlayerInitData& data) { diff --git a/src/core/libraries/avplayer/avplayer_state.cpp b/src/core/libraries/avplayer/avplayer_state.cpp index e1b11840e..dbaa36d18 100644 --- a/src/core/libraries/avplayer/avplayer_state.cpp +++ b/src/core/libraries/avplayer/avplayer_state.cpp @@ -92,7 +92,7 @@ void AvPlayerState::DefaultEventCallback(void* opaque, AvPlayerEvents event_id, const auto callback = self->m_event_replacement.event_callback; const auto ptr = self->m_event_replacement.object_ptr; if (callback != nullptr) { - Core::ExecuteGuest(callback, ptr, event_id, 0, event_data); + callback(ptr, event_id, 0, event_data); } } diff --git a/src/core/libraries/ime/ime.cpp b/src/core/libraries/ime/ime.cpp index 258cc61e1..96ae446fa 100644 --- a/src/core/libraries/ime/ime.cpp +++ b/src/core/libraries/ime/ime.cpp @@ -99,16 +99,16 @@ public: if (m_ime_mode) { OrbisImeParam param = m_param.ime; if (use_param_handler) { - Core::ExecuteGuest(param.handler, param.arg, event); + param.handler(param.arg, event); } else { - Core::ExecuteGuest(handler, param.arg, event); + handler(param.arg, event); } } else { OrbisImeKeyboardParam param = m_param.key; if (use_param_handler) { - Core::ExecuteGuest(param.handler, param.arg, event); + param.handler(param.arg, event); } else { - Core::ExecuteGuest(handler, param.arg, event); + handler(param.arg, event); } } } diff --git a/src/core/libraries/ime/ime_dialog_ui.cpp b/src/core/libraries/ime/ime_dialog_ui.cpp index 4a95c60c9..9611e7c49 100644 --- a/src/core/libraries/ime/ime_dialog_ui.cpp +++ b/src/core/libraries/ime/ime_dialog_ui.cpp @@ -131,8 +131,7 @@ bool ImeDialogState::CallTextFilter() { return false; } - int ret = - Core::ExecuteGuest(text_filter, out_text, &out_text_length, src_text, src_text_length); + int ret = text_filter(out_text, &out_text_length, src_text, src_text_length); if (ret != 0) { return false; @@ -153,7 +152,7 @@ bool ImeDialogState::CallKeyboardFilter(const OrbisImeKeycode* src_keycode, u16* return true; } - int ret = Core::ExecuteGuest(keyboard_filter, src_keycode, out_keycode, out_status, nullptr); + int ret = keyboard_filter(src_keycode, out_keycode, out_status, nullptr); return ret == 0; } diff --git a/src/core/libraries/kernel/threads.cpp b/src/core/libraries/kernel/threads.cpp index 082a52b67..083dc8ee1 100644 --- a/src/core/libraries/kernel/threads.cpp +++ b/src/core/libraries/kernel/threads.cpp @@ -7,6 +7,17 @@ namespace Libraries::Kernel { +void PS4_SYSV_ABI ClearStack() { + void* const stackaddr_attr = Libraries::Kernel::g_curthread->attr.stackaddr_attr; + void* volatile sp; + asm("mov %%rsp, %0" : "=rm"(sp)); + // leave a safety net of 64 bytes for memset + const size_t size = ((uintptr_t)sp - (uintptr_t)stackaddr_attr) - 64; + void* volatile buf = alloca(size); + memset(buf, 0, size); + sp = nullptr; +} + void RegisterThreads(Core::Loader::SymbolsResolver* sym) { RegisterMutex(sym); RegisterCond(sym); diff --git a/src/core/libraries/kernel/threads.h b/src/core/libraries/kernel/threads.h index 42ab0b13f..81535352c 100644 --- a/src/core/libraries/kernel/threads.h +++ b/src/core/libraries/kernel/threads.h @@ -27,6 +27,7 @@ int PS4_SYSV_ABI posix_pthread_create(PthreadT* thread, const PthreadAttrT* attr PthreadEntryFunc start_routine, void* arg); int PS4_SYSV_ABI posix_pthread_join(PthreadT pthread, void** thread_return); +int PS4_SYSV_ABI posix_pthread_detach(PthreadT pthread); int PS4_SYSV_ABI posix_pthread_mutexattr_init(PthreadMutexAttrT* attr); int PS4_SYSV_ABI posix_pthread_mutexattr_settype(PthreadMutexAttrT* attr, PthreadMutexType type); @@ -40,6 +41,8 @@ int PS4_SYSV_ABI posix_pthread_mutex_destroy(PthreadMutexT* mutex); void RegisterThreads(Core::Loader::SymbolsResolver* sym); +void PS4_SYSV_ABI ClearStack(); + class Thread { public: explicit Thread() = default; diff --git a/src/core/libraries/kernel/threads/exception.cpp b/src/core/libraries/kernel/threads/exception.cpp index 0b27f2bd8..cf36da0cc 100644 --- a/src/core/libraries/kernel/threads/exception.cpp +++ b/src/core/libraries/kernel/threads/exception.cpp @@ -317,7 +317,7 @@ int PS4_SYSV_ABI sceKernelRaiseException(PthreadT thread, int signum) { u64 res = NtQueueApcThreadEx(reinterpret_cast(thread->native_thr.GetHandle()), option, ExceptionHandler, (void*)thread->name.c_str(), - (void*)native_signum, nullptr); + (void*)(s64)native_signum, nullptr); ASSERT(res == 0); #endif return ORBIS_OK; diff --git a/src/core/libraries/kernel/threads/pthread.cpp b/src/core/libraries/kernel/threads/pthread.cpp index 20bd20f4b..da9e1600f 100644 --- a/src/core/libraries/kernel/threads/pthread.cpp +++ b/src/core/libraries/kernel/threads/pthread.cpp @@ -199,10 +199,17 @@ static void RunThread(void* arg) { g_curthread = curthread; Common::SetCurrentThreadName(curthread->name.c_str()); DebugState.AddCurrentThreadToGuestList(); + Core::InitializeTLS(); + + curthread->native_thr.Initialize(); + + // Clear the stack before running the guest thread + if (False(g_curthread->attr.flags & PthreadAttrFlags::StackUser)) { + ClearStack(); + } /* Run the current thread's start routine with argument: */ - curthread->native_thr.Initialize(); - void* ret = Core::ExecuteGuest(curthread->start_routine, curthread->arg); + void* ret = curthread->start_routine(curthread->arg); /* Remove thread from tracking */ DebugState.RemoveCurrentThreadFromGuestList(); diff --git a/src/core/libraries/kernel/threads/pthread_spec.cpp b/src/core/libraries/kernel/threads/pthread_spec.cpp index 094866a5a..38032f174 100644 --- a/src/core/libraries/kernel/threads/pthread_spec.cpp +++ b/src/core/libraries/kernel/threads/pthread_spec.cpp @@ -84,7 +84,7 @@ void _thread_cleanupspecific() { * destructor: */ lk.unlock(); - Core::ExecuteGuest(destructor, data); + destructor(data); lk.lock(); } } diff --git a/src/core/libraries/network/net_ctl_obj.cpp b/src/core/libraries/network/net_ctl_obj.cpp index a295477b6..a4081cd11 100644 --- a/src/core/libraries/network/net_ctl_obj.cpp +++ b/src/core/libraries/network/net_ctl_obj.cpp @@ -50,7 +50,7 @@ void NetCtlInternal::CheckCallback() { : ORBIS_NET_CTL_EVENT_TYPE_DISCONNECTED; for (const auto [func, arg] : callbacks) { if (func != nullptr) { - Core::ExecuteGuest(func, event, arg); + func(event, arg); } } } @@ -61,7 +61,7 @@ void NetCtlInternal::CheckNpToolkitCallback() { : ORBIS_NET_CTL_EVENT_TYPE_DISCONNECTED; for (const auto [func, arg] : nptool_callbacks) { if (func != nullptr) { - Core::ExecuteGuest(func, event, arg); + func(event, arg); } } } diff --git a/src/core/libraries/ngs2/ngs2.cpp b/src/core/libraries/ngs2/ngs2.cpp index 2f785f9a0..97d19c352 100644 --- a/src/core/libraries/ngs2/ngs2.cpp +++ b/src/core/libraries/ngs2/ngs2.cpp @@ -160,13 +160,13 @@ s32 PS4_SYSV_ABI sceNgs2SystemCreateWithAllocator(const OrbisNgs2SystemOption* o result = SystemSetup(option, &bufferInfo, 0, 0); if (result >= 0) { uintptr_t sysUserData = allocator->userData; - result = Core::ExecuteGuest(hostAlloc, &bufferInfo); + result = hostAlloc(&bufferInfo); if (result >= 0) { OrbisNgs2Handle* handleCopy = outHandle; result = SystemSetup(option, &bufferInfo, hostFree, handleCopy); if (result < 0) { if (hostFree) { - Core::ExecuteGuest(hostFree, &bufferInfo); + hostFree(&bufferInfo); } } } diff --git a/src/core/libraries/usbd/emulated/dimensions.cpp b/src/core/libraries/usbd/emulated/dimensions.cpp index 272f2f649..452968840 100644 --- a/src/core/libraries/usbd/emulated/dimensions.cpp +++ b/src/core/libraries/usbd/emulated/dimensions.cpp @@ -3,6 +3,9 @@ #include "dimensions.h" +#include "core/libraries/kernel/threads.h" +#include "core/tls.h" + #include #include @@ -619,22 +622,46 @@ libusb_transfer_status DimensionsBackend::HandleAsyncTransfer(libusb_transfer* t return LIBUSB_TRANSFER_COMPLETED; } +struct WriteThreadArgs { + DimensionsBackend* self; + libusb_transfer* transfer; +}; + +void* PS4_SYSV_ABI DimensionsBackend::WriteThread(void* arg) { + auto* args = reinterpret_cast(arg); + + auto* self = args->self; + auto* transfer = args->transfer; + + self->HandleAsyncTransfer(transfer); + + const u8 flags = transfer->flags; + transfer->status = LIBUSB_TRANSFER_COMPLETED; + transfer->actual_length = transfer->length; + if (transfer->callback) { + transfer->callback(transfer); + } + if (flags & LIBUSB_TRANSFER_FREE_TRANSFER) { + libusb_free_transfer(transfer); + } + delete args; + return nullptr; +} + s32 DimensionsBackend::SubmitTransfer(libusb_transfer* transfer) { if (transfer->endpoint == 0x01) { - std::thread write_thread([this, transfer] { - HandleAsyncTransfer(transfer); + using namespace Libraries::Kernel; + + PthreadAttrT attr{}; + posix_pthread_attr_init(&attr); + PthreadT thread{}; + auto* args = new WriteThreadArgs(); + args->self = this; + args->transfer = transfer; + posix_pthread_create(&thread, &attr, HOST_CALL(DimensionsBackend::WriteThread), args); + posix_pthread_attr_destroy(&attr); + posix_pthread_detach(thread); - const u8 flags = transfer->flags; - transfer->status = LIBUSB_TRANSFER_COMPLETED; - transfer->actual_length = transfer->length; - if (transfer->callback) { - transfer->callback(transfer); - } - if (flags & LIBUSB_TRANSFER_FREE_TRANSFER) { - libusb_free_transfer(transfer); - } - }); - write_thread.detach(); return LIBUSB_SUCCESS; } diff --git a/src/core/libraries/usbd/emulated/dimensions.h b/src/core/libraries/usbd/emulated/dimensions.h index d9573b5f4..bc409f7c3 100644 --- a/src/core/libraries/usbd/emulated/dimensions.h +++ b/src/core/libraries/usbd/emulated/dimensions.h @@ -103,6 +103,8 @@ protected: std::queue> m_queries; private: + static void* PS4_SYSV_ABI WriteThread(void* arg); + std::shared_ptr m_dimensions_toypad = std::make_shared(); std::array m_endpoint_out_extra = {0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x1d, 0x00}; diff --git a/src/core/linker.cpp b/src/core/linker.cpp index 7a0653e9f..0b80ecacc 100644 --- a/src/core/linker.cpp +++ b/src/core/linker.cpp @@ -135,7 +135,8 @@ void Linker::Execute(const std::vector& args) { } } params.entry_addr = module->GetEntryAddress(); - ExecuteGuest(RunMainEntry, ¶ms); + Libraries::Kernel::ClearStack(); + RunMainEntry(¶ms); }); } @@ -379,8 +380,7 @@ void* Linker::TlsGetAddr(u64 module_index, u64 offset) { if (!addr) { // Module was just loaded by above code. Allocate TLS block for it. const u32 init_image_size = module->tls.init_image_size; - u8* dest = reinterpret_cast( - Core::ExecuteGuest(heap_api->heap_malloc, module->tls.image_size)); + u8* dest = reinterpret_cast(heap_api->heap_malloc(module->tls.image_size)); const u8* src = reinterpret_cast(module->tls.image_virtual_addr); std::memcpy(dest, src, init_image_size); std::memset(dest + init_image_size, 0, module->tls.image_size - init_image_size); @@ -412,7 +412,7 @@ void* Linker::AllocateTlsForThread(bool is_primary) { ASSERT_MSG(ret == 0, "Unable to allocate TLS+TCB for the primary thread"); } else { if (heap_api) { - addr_out = Core::ExecuteGuest(heap_api->heap_malloc, total_tls_size); + addr_out = heap_api->heap_malloc(total_tls_size); } else { addr_out = std::malloc(total_tls_size); } @@ -422,7 +422,7 @@ void* Linker::AllocateTlsForThread(bool is_primary) { void Linker::FreeTlsForNonPrimaryThread(void* pointer) { if (heap_api) { - Core::ExecuteGuest(heap_api->heap_free, pointer); + heap_api->heap_free(pointer); } else { std::free(pointer); } diff --git a/src/core/module.cpp b/src/core/module.cpp index 127e74293..7e9d74a09 100644 --- a/src/core/module.cpp +++ b/src/core/module.cpp @@ -97,7 +97,7 @@ Module::~Module() = default; s32 Module::Start(u64 args, const void* argp, void* param) { LOG_INFO(Core_Linker, "Module started : {}", name); const VAddr addr = dynamic_info.init_virtual_addr + GetBaseAddress(); - return ExecuteGuest(reinterpret_cast(addr), args, argp, param); + return reinterpret_cast(addr)(args, argp, param); } void Module::LoadModuleToMemory(u32& max_tls_index) { diff --git a/src/core/tls.cpp b/src/core/tls.cpp index 57ed20f38..8b926cb39 100644 --- a/src/core/tls.cpp +++ b/src/core/tls.cpp @@ -198,7 +198,7 @@ Tcb* GetTcbBase() { thread_local std::once_flag init_tls_flag; -void EnsureThreadInitialized() { +void InitializeTLS() { std::call_once(init_tls_flag, [] { SetTcbBase(Libraries::Kernel::g_curthread->tcb); }); } diff --git a/src/core/tls.h b/src/core/tls.h index 27de518ea..2d94488f7 100644 --- a/src/core/tls.h +++ b/src/core/tls.h @@ -43,30 +43,7 @@ void SetTcbBase(void* image_address); Tcb* GetTcbBase(); /// Makes sure TLS is initialized for the thread before entering guest. -void EnsureThreadInitialized(); - -template -#ifdef __clang__ -__attribute__((optnone)) -#else -__attribute__((optimize("O0"))) -#endif -void ClearStack() { - volatile void* buf = alloca(size); - memset(const_cast(buf), 0, size); - buf = nullptr; -} - -template -ReturnType ExecuteGuest(PS4_SYSV_ABI ReturnType (*func)(FuncArgs...), CallArgs&&... args) { - EnsureThreadInitialized(); - // clear stack to avoid trash from EnsureThreadInitialized - auto* tcb = GetTcbBase(); - if (tcb != nullptr && tcb->tcb_fiber == nullptr) { - ClearStack<12_KB>(); - } - return func(std::forward(args)...); -} +void InitializeTLS(); template struct HostCallWrapperImpl;