Merge remote-tracking branch 'origin/main' into user_and_settings

This commit is contained in:
kalaposfos13 2026-02-24 21:42:22 +01:00
commit 5970ab6ee2
31 changed files with 802 additions and 329 deletions

View File

@ -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
}

View File

@ -85,7 +85,8 @@ enum class FileType {
Device,
Socket,
Epoll,
Resolver
Resolver,
Equeue
};
struct File {

View File

@ -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();

View File

@ -124,21 +124,23 @@ static inline bool IsValidEventType(Platform::InterruptId id) {
static_cast<u32>(id) == static_cast<u32>(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<Platform::InterruptId>(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<GnmEventType>(id), SceKernelEvent::Filter::GraphicsCore,
reinterpret_cast<void*>(id));
equeue->TriggerEvent(static_cast<GnmEventType>(id),
OrbisKernelEvent::Filter::GraphicsCore,
reinterpret_cast<void*>(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<Platform::InterruptId>(id), eq);
Platform::IrqC::Instance()->Unregister(static_cast<Platform::InterruptId>(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);
}

View File

@ -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();

View File

@ -2,12 +2,17 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <thread>
#include <magic_enum/magic_enum.hpp>
#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<s32, EqueueInternal*> 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<OrbisKernelBintime*>(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<boost::asio::steady_timer>(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::microseconds>(
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<OrbisKernelBintime*>(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<Core::FileSys::HandleTable>::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, &micros);
} 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<Core::FileSys::HandleTable>::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<Core::FileSys::HandleTable>::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<u64>(&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<u64>(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<uintptr_t>(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);

View File

@ -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<EqueueEvent> m_events;
@ -182,7 +189,8 @@ private:
std::unordered_map<u64, SmallTimer> 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);

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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;

View File

@ -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<SceKernelExceptionHandler, 32> 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<SceKernelExceptionHandler, 32> 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<uint64_t>(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<uint64_t>(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<uintptr_t>(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<decltype(act.sa_sigaction)>(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<pthread_t>(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<HANDLE>(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) {

View File

@ -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;

View File

@ -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 <thread>
@ -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<int>(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));

View File

@ -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<void*>(buf), 0, size);
buf = nullptr;
}
static void RunThread(void* arg) {
auto* curthread = static_cast<Pthread*>(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 */

View File

@ -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);

View File

@ -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 <ctime>
@ -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);

View File

@ -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
}
}
}

View File

@ -3,6 +3,7 @@
#include "dimensions.h"
#include "core/libraries/kernel/threads.h"
#include "core/tls.h"
#include <mutex>
@ -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<WriteThreadArgs*>(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;
}

View File

@ -103,6 +103,8 @@ protected:
std::queue<std::array<u8, 32>> m_queries;
private:
static void* PS4_SYSV_ABI WriteThread(void* arg);
std::shared_ptr<DimensionsToypad> m_dimensions_toypad = std::make_shared<DimensionsToypad>();
std::array<u8, 9> m_endpoint_out_extra = {0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x1d, 0x00};

View File

@ -194,7 +194,7 @@ void VideoOutDriver::Flip(const Request& req) {
if (event != nullptr) {
event->TriggerEvent(
static_cast<u64>(OrbisVideoOutInternalEventId::Flip),
Kernel::SceKernelEvent::Filter::VideoOut,
Kernel::OrbisKernelEvent::Filter::VideoOut,
reinterpret_cast<void*>(static_cast<u64>(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<u64>(OrbisVideoOutInternalEventId::Vblank),
Kernel::SceKernelEvent::Filter::VideoOut,
Kernel::OrbisKernelEvent::Filter::VideoOut,
reinterpret_cast<void*>(
static_cast<u64>(OrbisVideoOutInternalEventId::Vblank) |
(vblank_status.count << 16)));

View File

@ -25,8 +25,8 @@ struct VideoOutPort {
std::array<BufferAttributeGroup, MaxDisplayBufferGroups> groups;
FlipStatus flip_status;
SceVideoOutVblankStatus vblank_status;
std::vector<Kernel::SceKernelEqueue> flip_events;
std::vector<Kernel::SceKernelEqueue> vblank_events;
std::vector<Kernel::EqueueInternal*> flip_events;
std::vector<Kernel::EqueueInternal*> vblank_events;
std::mutex vo_mutex;
std::mutex port_mutex;
std::condition_variable vo_cv;

View File

@ -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<u64>(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<u64>(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;
}

View File

@ -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);

View File

@ -136,7 +136,7 @@ void Linker::Execute(const std::vector<std::string>& args) {
}
}
params.entry_addr = module->GetEntryAddress();
Core::EnsureThreadInitialized();
Libraries::Kernel::ClearStack();
RunMainEntry(&params);
});
}

View File

@ -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<EntryFunc>(addr)(args, argp, param);
}

View File

@ -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<SceKernelExceptionHandler, 32> 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<ucontext_t*>(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<ucontext_t*>(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
}

View File

@ -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 <set>
#include <signal.h>
#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:

View File

@ -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); });
}

View File

@ -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 <class F, F f>
struct HostCallWrapperImpl;

View File

@ -361,11 +361,8 @@ void Emulator::Run(std::filesystem::path file, std::vector<std::string> 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;