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/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/ipc/ipc.cpp b/src/core/ipc/ipc.cpp index cf0c1d81e..3f2fd5374 100644 --- a/src/core/ipc/ipc.cpp +++ b/src/core/ipc/ipc.cpp @@ -175,12 +175,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(); diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index c5b18fd90..5ed9af24e 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -124,21 +124,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), @@ -150,10 +152,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; } @@ -268,16 +271,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; } @@ -896,7 +900,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/kernel/process.cpp b/src/core/libraries/kernel/process.cpp index bbabe204d..43bda87e2 100644 --- a/src/core/libraries/kernel/process.cpp +++ b/src/core/libraries/kernel/process.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "common/config.h" @@ -276,6 +276,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; @@ -300,6 +304,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.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 95ced79c0..cf36da0cc 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_rip = 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*)(s64)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/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.cpp b/src/core/libraries/kernel/threads/pthread.cpp index 0218285f7..da9e1600f 100644 --- a/src/core/libraries/kernel/threads/pthread.cpp +++ b/src/core/libraries/kernel/threads/pthread.cpp @@ -194,34 +194,21 @@ 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; Common::SetCurrentThreadName(curthread->name.c_str()); DebugState.AddCurrentThreadToGuestList(); + Core::InitializeTLS(); - /* 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); + if (False(g_curthread->attr.flags & PthreadAttrFlags::StackUser)) { + ClearStack(); + } + /* Run the current thread's start routine with argument: */ void* ret = curthread->start_routine(curthread->arg); /* Remove thread from tracking */ 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); 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 + } } } diff --git a/src/core/libraries/usbd/emulated/dimensions.cpp b/src/core/libraries/usbd/emulated/dimensions.cpp index 4d38c66fa..452968840 100644 --- a/src/core/libraries/usbd/emulated/dimensions.cpp +++ b/src/core/libraries/usbd/emulated/dimensions.cpp @@ -3,6 +3,7 @@ #include "dimensions.h" +#include "core/libraries/kernel/threads.h" #include "core/tls.h" #include @@ -621,24 +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] { - Core::EnsureThreadInitialized(); + using namespace Libraries::Kernel; - HandleAsyncTransfer(transfer); + 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/libraries/videoout/driver.cpp b/src/core/libraries/videoout/driver.cpp index 4672aadb4..7d17a1364 100644 --- a/src/core/libraries/videoout/driver.cpp +++ b/src/core/libraries/videoout/driver.cpp @@ -194,7 +194,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))); } @@ -322,7 +322,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 7817c7770..1705aa63f 100644 --- a/src/core/libraries/videoout/video_out.cpp +++ b/src/core/libraries/videoout/video_out.cpp @@ -39,7 +39,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); @@ -47,39 +47,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); @@ -87,35 +89,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; } @@ -181,11 +185,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; } @@ -209,11 +213,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; } @@ -226,11 +230,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); diff --git a/src/core/linker.cpp b/src/core/linker.cpp index 41e787efd..a0047ae14 100644 --- a/src/core/linker.cpp +++ b/src/core/linker.cpp @@ -136,7 +136,7 @@ void Linker::Execute(const std::vector& args) { } } params.entry_addr = module->GetEntryAddress(); - Core::EnsureThreadInitialized(); + Libraries::Kernel::ClearStack(); RunMainEntry(¶ms); }); } diff --git a/src/core/module.cpp b/src/core/module.cpp index d0fae3a9f..7e9d74a09 100644 --- a/src/core/module.cpp +++ b/src/core/module.cpp @@ -97,7 +97,6 @@ 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); } 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: 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 00eba188e..2d94488f7 100644 --- a/src/core/tls.h +++ b/src/core/tls.h @@ -43,7 +43,7 @@ void SetTcbBase(void* image_address); Tcb* GetTcbBase(); /// Makes sure TLS is initialized for the thread before entering guest. -void EnsureThreadInitialized(); +void InitializeTLS(); template struct HostCallWrapperImpl; diff --git a/src/emulator.cpp b/src/emulator.cpp index 55e1b2f2f..d10311727 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -361,11 +361,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;