mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-06-08 19:53:15 +00:00
kernel: Rewrite pthread emulation (#1440)
* libkernel: Cleanup some function places * kernel: Refactor thread functions * kernel: It builds * kernel: Fix a bunch of bugs, kernel thread heap * kernel: File cleanup pt1 * File cleanup pt2 * File cleanup pt3 * File cleanup pt4 * kernel: Add missing funcs * kernel: Add basic exceptions for linux * gnmdriver: Add workload functions * kernel: Fix new pthreads code on macOS. (#1441) * kernel: Downgrade edeadlk to log * gnmdriver: Add sceGnmSubmitCommandBuffersForWorkload * exception: Add context register population for macOS. (#1444) * kernel: Pthread rewrite touchups for Windows * kernel: Multiplatform thread implementation * mutex: Remove spamming log * pthread_spec: Make assert into a log * pthread_spec: Zero initialize array * Attempt to fix non-Windows builds * hotfix: change incorrect NID for scePthreadAttrSetaffinity * scePthreadAttrSetaffinity implementation * Attempt to fix Linux * windows: Address a bunch of address space problems * address_space: Fix unmap of region surrounded by placeholders * libs: Reduce logging * pthread: Implement condvar with waitable atomics and sleepqueue * sleepq: Separate and make faster * time: Remove delay execution * Causes high cpu usage in Tohou Luna Nights * kernel: Cleanup files again * pthread: Add missing include * semaphore: Use binary_semaphore instead of condvar * Seems more reliable * libraries/sysmodule: log module on `sceSysmoduleIsLoaded` * libraries/kernel: implement `scePthreadSetPrio` --------- Co-authored-by: squidbus <175574877+squidbus@users.noreply.github.com> Co-authored-by: Daniel R. <47796739+polybiusproxy@users.noreply.github.com>
This commit is contained in:
parent
6904764aab
commit
c4506da0ae
104 changed files with 5554 additions and 3979 deletions
|
@ -1,15 +0,0 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/kernel/cpu_management.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
int PS4_SYSV_ABI sceKernelIsNeoMode() {
|
||||
LOG_DEBUG(Kernel_Sce, "called");
|
||||
return Config::isNeoMode();
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
|
@ -5,10 +5,143 @@
|
|||
#include "common/debug.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/event_queues.h"
|
||||
#include "core/libraries/kernel/equeue.h"
|
||||
#include "core/libraries/libs.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
bool EqueueInternal::AddEvent(EqueueEvent& event) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
event.time_added = std::chrono::steady_clock::now();
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EqueueInternal::RemoveEvent(u64 id) {
|
||||
bool has_found = false;
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
const auto& it =
|
||||
std::ranges::find_if(m_events, [id](auto& ev) { return ev.event.ident == id; });
|
||||
if (it != m_events.cend()) {
|
||||
m_events.erase(it);
|
||||
has_found = true;
|
||||
}
|
||||
return has_found;
|
||||
}
|
||||
|
||||
int EqueueInternal::WaitForEvents(SceKernelEvent* ev, int num, u32 micros) {
|
||||
int count = 0;
|
||||
|
||||
const auto predicate = [&] {
|
||||
count = GetTriggeredEvents(ev, num);
|
||||
return count > 0;
|
||||
};
|
||||
|
||||
if (micros == 0) {
|
||||
std::unique_lock lock{m_mutex};
|
||||
m_cond.wait(lock, predicate);
|
||||
} else {
|
||||
std::unique_lock lock{m_mutex};
|
||||
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)));
|
||||
}
|
||||
small_timer_event.event.data = 0;
|
||||
}
|
||||
|
||||
if (ev->flags & SceKernelEvent::Flags::OneShot) {
|
||||
for (auto ev_id = 0u; ev_id < count; ++ev_id) {
|
||||
RemoveEvent(ev->ident);
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
bool EqueueInternal::TriggerEvent(u64 ident, s16 filter, void* trigger_data) {
|
||||
bool has_found = false;
|
||||
{
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
for (auto& event : m_events) {
|
||||
if ((event.event.ident == ident) && (event.event.filter == filter)) {
|
||||
event.Trigger(trigger_data);
|
||||
has_found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_cond.notify_one();
|
||||
return has_found;
|
||||
}
|
||||
|
||||
int EqueueInternal::GetTriggeredEvents(SceKernelEvent* ev, int num) {
|
||||
int count = 0;
|
||||
|
||||
for (auto& event : m_events) {
|
||||
if (event.IsTriggered()) {
|
||||
if (event.event.flags & SceKernelEvent::Flags::Clear) {
|
||||
event.Reset();
|
||||
}
|
||||
ev[count++] = event.event;
|
||||
if (count == num) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
bool EqueueInternal::AddSmallTimer(EqueueEvent& ev) {
|
||||
// We assume that only one timer event (with the same ident across calls)
|
||||
// can be posted to the queue, based on observations so far. In the opposite case,
|
||||
// the small timer storage and wait logic should be reworked.
|
||||
ASSERT(!HasSmallTimer() || small_timer_event.event.ident == ev.event.ident);
|
||||
ev.time_added = std::chrono::steady_clock::now();
|
||||
small_timer_event = std::move(ev);
|
||||
return true;
|
||||
}
|
||||
|
||||
int EqueueInternal::WaitForSmallTimer(SceKernelEvent* ev, int num, u32 micros) {
|
||||
int count{};
|
||||
|
||||
ASSERT(num == 1);
|
||||
|
||||
auto curr_clock = std::chrono::steady_clock::now();
|
||||
const auto wait_end_us = curr_clock + std::chrono::microseconds{micros};
|
||||
|
||||
do {
|
||||
curr_clock = std::chrono::steady_clock::now();
|
||||
{
|
||||
std::scoped_lock lock{m_mutex};
|
||||
if ((curr_clock - small_timer_event.time_added) >
|
||||
std::chrono::microseconds{small_timer_event.event.data}) {
|
||||
ev[count++] = small_timer_event.event;
|
||||
small_timer_event.event.data = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::this_thread::yield();
|
||||
} while (curr_clock < wait_end_us);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
extern boost::asio::io_context io_context;
|
||||
extern void KernelSignalRequest();
|
||||
|
||||
|
@ -42,8 +175,7 @@ int PS4_SYSV_ABI sceKernelCreateEqueue(SceKernelEqueue* eq, const char* name) {
|
|||
|
||||
LOG_INFO(Kernel_Event, "name = {}", name);
|
||||
|
||||
*eq = new EqueueInternal;
|
||||
(*eq)->setName(std::string(name));
|
||||
*eq = new EqueueInternal(name);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
|
@ -211,4 +343,19 @@ int PS4_SYSV_ABI sceKernelDeleteUserEvent(SceKernelEqueue eq, int id) {
|
|||
s16 PS4_SYSV_ABI sceKernelGetEventFilter(const SceKernelEvent* ev) {
|
||||
return ev->filter;
|
||||
}
|
||||
|
||||
void RegisterEventQueue(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("D0OdFMjp46I", "libkernel", 1, "libkernel", 1, 1, sceKernelCreateEqueue);
|
||||
LIB_FUNCTION("jpFjmgAC5AE", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteEqueue);
|
||||
LIB_FUNCTION("fzyMKs9kim0", "libkernel", 1, "libkernel", 1, 1, sceKernelWaitEqueue);
|
||||
LIB_FUNCTION("vz+pg2zdopI", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventUserData);
|
||||
LIB_FUNCTION("4R6-OvI2cEA", "libkernel", 1, "libkernel", 1, 1, sceKernelAddUserEvent);
|
||||
LIB_FUNCTION("WDszmSbWuDk", "libkernel", 1, "libkernel", 1, 1, sceKernelAddUserEventEdge);
|
||||
LIB_FUNCTION("R74tt43xP6k", "libkernel", 1, "libkernel", 1, 1, sceKernelAddHRTimerEvent);
|
||||
LIB_FUNCTION("F6e0kwo4cnk", "libkernel", 1, "libkernel", 1, 1, sceKernelTriggerUserEvent);
|
||||
LIB_FUNCTION("LJDwdSNTnDg", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteUserEvent);
|
||||
LIB_FUNCTION("mJ7aghmgvfc", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventId);
|
||||
LIB_FUNCTION("23CPPI1tyBY", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventFilter);
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
|
@ -7,11 +7,14 @@
|
|||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/asio/steady_timer.hpp>
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
class EqueueInternal;
|
||||
|
@ -89,14 +92,12 @@ private:
|
|||
|
||||
class EqueueInternal {
|
||||
public:
|
||||
EqueueInternal() = default;
|
||||
virtual ~EqueueInternal();
|
||||
void setName(const std::string& m_name) {
|
||||
this->m_name = m_name;
|
||||
}
|
||||
const auto& GetName() const {
|
||||
explicit EqueueInternal(std::string_view name) : m_name(name) {}
|
||||
|
||||
std::string_view GetName() const {
|
||||
return m_name;
|
||||
}
|
||||
|
||||
bool AddEvent(EqueueEvent& event);
|
||||
bool RemoveEvent(u64 id);
|
||||
int WaitForEvents(SceKernelEvent* ev, int num, u32 micros);
|
||||
|
@ -107,6 +108,7 @@ public:
|
|||
bool HasSmallTimer() const {
|
||||
return small_timer_event.event.data != 0;
|
||||
}
|
||||
|
||||
int WaitForSmallTimer(SceKernelEvent* ev, int num, u32 micros);
|
||||
|
||||
private:
|
||||
|
@ -117,4 +119,9 @@ private:
|
|||
std::condition_variable m_cond;
|
||||
};
|
||||
|
||||
using SceKernelUseconds = u32;
|
||||
using SceKernelEqueue = EqueueInternal*;
|
||||
|
||||
void RegisterEventQueue(Core::Loader::SymbolsResolver* sym);
|
||||
|
||||
} // namespace Libraries::Kernel
|
|
@ -1,40 +0,0 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
#include "event_flag_codes.h"
|
||||
#include "event_flag_obj.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
using OrbisKernelUseconds = u32;
|
||||
using OrbisKernelEventFlag = EventFlagInternal*;
|
||||
|
||||
struct OrbisKernelEventFlagOptParam {
|
||||
size_t size;
|
||||
};
|
||||
|
||||
int PS4_SYSV_ABI sceKernelCreateEventFlag(OrbisKernelEventFlag* ef, const char* pName, u32 attr,
|
||||
u64 initPattern,
|
||||
const OrbisKernelEventFlagOptParam* pOptParam);
|
||||
int PS4_SYSV_ABI sceKernelDeleteEventFlag(OrbisKernelEventFlag ef);
|
||||
int PS4_SYSV_ABI sceKernelOpenEventFlag();
|
||||
int PS4_SYSV_ABI sceKernelCloseEventFlag();
|
||||
int PS4_SYSV_ABI sceKernelClearEventFlag(OrbisKernelEventFlag ef, u64 bitPattern);
|
||||
int PS4_SYSV_ABI sceKernelCancelEventFlag(OrbisKernelEventFlag ef, u64 setPattern,
|
||||
int* pNumWaitThreads);
|
||||
int PS4_SYSV_ABI sceKernelSetEventFlag(OrbisKernelEventFlag ef, u64 bitPattern);
|
||||
int PS4_SYSV_ABI sceKernelPollEventFlag(OrbisKernelEventFlag ef, u64 bitPattern, u32 waitMode,
|
||||
u64* pResultPat);
|
||||
int PS4_SYSV_ABI sceKernelWaitEventFlag(OrbisKernelEventFlag ef, u64 bitPattern, u32 waitMode,
|
||||
u64* pResultPat, OrbisKernelUseconds* pTimeout);
|
||||
|
||||
void RegisterKernelEventFlag(Core::Loader::SymbolsResolver* sym);
|
||||
|
||||
} // namespace Libraries::Kernel
|
|
@ -1,14 +0,0 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
constexpr int ORBIS_KERNEL_EVF_ATTR_TH_FIFO = 0x01;
|
||||
constexpr int ORBIS_KERNEL_EVF_ATTR_TH_PRIO = 0x02;
|
||||
constexpr int ORBIS_KERNEL_EVF_ATTR_SINGLE = 0x10;
|
||||
constexpr int ORBIS_KERNEL_EVF_ATTR_MULTI = 0x20;
|
||||
|
||||
constexpr int ORBIS_KERNEL_EVF_WAITMODE_AND = 0x01;
|
||||
constexpr int ORBIS_KERNEL_EVF_WAITMODE_OR = 0x02;
|
||||
constexpr int ORBIS_KERNEL_EVF_WAITMODE_CLEAR_ALL = 0x10;
|
||||
constexpr int ORBIS_KERNEL_EVF_WAITMODE_CLEAR_PAT = 0x20;
|
|
@ -1,111 +0,0 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "event_flag_obj.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
int EventFlagInternal::Wait(u64 bits, WaitMode wait_mode, ClearMode clear_mode, u64* result,
|
||||
u32* ptr_micros) {
|
||||
std::unique_lock lock{m_mutex};
|
||||
|
||||
uint32_t micros = 0;
|
||||
bool infinitely = true;
|
||||
if (ptr_micros != nullptr) {
|
||||
micros = *ptr_micros;
|
||||
infinitely = false;
|
||||
}
|
||||
|
||||
if (m_thread_mode == ThreadMode::Single && m_waiting_threads > 0) {
|
||||
return ORBIS_KERNEL_ERROR_EPERM;
|
||||
}
|
||||
|
||||
auto const start = std::chrono::system_clock::now();
|
||||
m_waiting_threads++;
|
||||
auto waitFunc = [this, wait_mode, bits] {
|
||||
return (m_status == Status::Canceled || m_status == Status::Deleted ||
|
||||
(wait_mode == WaitMode::And && (m_bits & bits) == bits) ||
|
||||
(wait_mode == WaitMode::Or && (m_bits & bits) != 0));
|
||||
};
|
||||
|
||||
if (infinitely) {
|
||||
m_cond_var.wait(lock, waitFunc);
|
||||
} else {
|
||||
if (!m_cond_var.wait_for(lock, std::chrono::microseconds(micros), waitFunc)) {
|
||||
if (result != nullptr) {
|
||||
*result = m_bits;
|
||||
}
|
||||
*ptr_micros = 0;
|
||||
--m_waiting_threads;
|
||||
return ORBIS_KERNEL_ERROR_ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
--m_waiting_threads;
|
||||
if (result != nullptr) {
|
||||
*result = m_bits;
|
||||
}
|
||||
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::system_clock::now() - start)
|
||||
.count();
|
||||
if (result != nullptr) {
|
||||
*result = m_bits;
|
||||
}
|
||||
|
||||
if (ptr_micros != nullptr) {
|
||||
*ptr_micros = (elapsed >= micros ? 0 : micros - elapsed);
|
||||
}
|
||||
|
||||
if (m_status == Status::Canceled) {
|
||||
return ORBIS_KERNEL_ERROR_ECANCELED;
|
||||
} else if (m_status == Status::Deleted) {
|
||||
return ORBIS_KERNEL_ERROR_EACCES;
|
||||
}
|
||||
|
||||
if (clear_mode == ClearMode::All) {
|
||||
m_bits = 0;
|
||||
} else if (clear_mode == ClearMode::Bits) {
|
||||
m_bits &= ~bits;
|
||||
}
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int EventFlagInternal::Poll(u64 bits, WaitMode wait_mode, ClearMode clear_mode, u64* result) {
|
||||
u32 micros = 0;
|
||||
auto ret = Wait(bits, wait_mode, clear_mode, result, µs);
|
||||
if (ret == ORBIS_KERNEL_ERROR_ETIMEDOUT) {
|
||||
// Poll returns EBUSY instead.
|
||||
ret = ORBIS_KERNEL_ERROR_EBUSY;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void EventFlagInternal::Set(u64 bits) {
|
||||
std::unique_lock lock{m_mutex};
|
||||
|
||||
while (m_status != Status::Set) {
|
||||
m_mutex.unlock();
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(10));
|
||||
m_mutex.lock();
|
||||
}
|
||||
|
||||
m_bits |= bits;
|
||||
|
||||
m_cond_var.notify_all();
|
||||
}
|
||||
|
||||
void EventFlagInternal::Clear(u64 bits) {
|
||||
std::unique_lock lock{m_mutex};
|
||||
while (m_status != Status::Set) {
|
||||
m_mutex.unlock();
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(10));
|
||||
m_mutex.lock();
|
||||
}
|
||||
|
||||
m_bits &= bits;
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
|
@ -1,44 +0,0 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
class EventFlagInternal {
|
||||
public:
|
||||
enum class ClearMode { None, All, Bits };
|
||||
|
||||
enum class WaitMode { And, Or };
|
||||
|
||||
enum class ThreadMode { Single, Multi };
|
||||
|
||||
enum class QueueMode { Fifo, ThreadPrio };
|
||||
|
||||
EventFlagInternal(const std::string& name, ThreadMode thread_mode, QueueMode queue_mode,
|
||||
uint64_t bits)
|
||||
: m_name(name), m_thread_mode(thread_mode), m_queue_mode(queue_mode), m_bits(bits) {};
|
||||
|
||||
int Wait(u64 bits, WaitMode wait_mode, ClearMode clear_mode, u64* result, u32* ptr_micros);
|
||||
int Poll(u64 bits, WaitMode wait_mode, ClearMode clear_mode, u64* result);
|
||||
void Set(u64 bits);
|
||||
void Clear(u64 bits);
|
||||
|
||||
private:
|
||||
enum class Status { Set, Canceled, Deleted };
|
||||
|
||||
std::mutex m_mutex;
|
||||
std::condition_variable m_cond_var;
|
||||
Status m_status = Status::Set;
|
||||
int m_waiting_threads = 0;
|
||||
std::string m_name;
|
||||
ThreadMode m_thread_mode = ThreadMode::Single;
|
||||
QueueMode m_queue_mode = QueueMode::Fifo;
|
||||
u64 m_bits = 0;
|
||||
};
|
||||
} // namespace Libraries::Kernel
|
|
@ -1,150 +0,0 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/libraries/kernel/event_queue.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
EqueueInternal::~EqueueInternal() = default;
|
||||
|
||||
bool EqueueInternal::AddEvent(EqueueEvent& event) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
event.time_added = std::chrono::steady_clock::now();
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EqueueInternal::RemoveEvent(u64 id) {
|
||||
bool has_found = false;
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
const auto& it =
|
||||
std::ranges::find_if(m_events, [id](auto& ev) { return ev.event.ident == id; });
|
||||
if (it != m_events.cend()) {
|
||||
m_events.erase(it);
|
||||
has_found = true;
|
||||
}
|
||||
return has_found;
|
||||
}
|
||||
|
||||
int EqueueInternal::WaitForEvents(SceKernelEvent* ev, int num, u32 micros) {
|
||||
int count = 0;
|
||||
|
||||
const auto predicate = [&] {
|
||||
count = GetTriggeredEvents(ev, num);
|
||||
return count > 0;
|
||||
};
|
||||
|
||||
if (micros == 0) {
|
||||
std::unique_lock lock{m_mutex};
|
||||
m_cond.wait(lock, predicate);
|
||||
} else {
|
||||
std::unique_lock lock{m_mutex};
|
||||
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)));
|
||||
}
|
||||
small_timer_event.event.data = 0;
|
||||
}
|
||||
|
||||
if (ev->flags & SceKernelEvent::Flags::OneShot) {
|
||||
for (auto ev_id = 0u; ev_id < count; ++ev_id) {
|
||||
RemoveEvent(ev->ident);
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
bool EqueueInternal::TriggerEvent(u64 ident, s16 filter, void* trigger_data) {
|
||||
bool has_found = false;
|
||||
{
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
for (auto& event : m_events) {
|
||||
if ((event.event.ident == ident) && (event.event.filter == filter)) {
|
||||
event.Trigger(trigger_data);
|
||||
has_found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_cond.notify_one();
|
||||
return has_found;
|
||||
}
|
||||
|
||||
int EqueueInternal::GetTriggeredEvents(SceKernelEvent* ev, int num) {
|
||||
int count = 0;
|
||||
|
||||
for (auto& event : m_events) {
|
||||
if (event.IsTriggered()) {
|
||||
if (event.event.flags & SceKernelEvent::Flags::Clear) {
|
||||
event.Reset();
|
||||
}
|
||||
|
||||
ev[count++] = event.event;
|
||||
|
||||
if (count == num) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
bool EqueueInternal::AddSmallTimer(EqueueEvent& ev) {
|
||||
// We assume that only one timer event (with the same ident across calls)
|
||||
// can be posted to the queue, based on observations so far. In the opposite case,
|
||||
// the small timer storage and wait logic should be reworked.
|
||||
ASSERT(!HasSmallTimer() || small_timer_event.event.ident == ev.event.ident);
|
||||
ev.time_added = std::chrono::steady_clock::now();
|
||||
small_timer_event = std::move(ev);
|
||||
return true;
|
||||
}
|
||||
|
||||
int EqueueInternal::WaitForSmallTimer(SceKernelEvent* ev, int num, u32 micros) {
|
||||
int count{};
|
||||
|
||||
ASSERT(num == 1);
|
||||
|
||||
auto curr_clock = std::chrono::steady_clock::now();
|
||||
const auto wait_end_us = curr_clock + std::chrono::microseconds{micros};
|
||||
|
||||
do {
|
||||
curr_clock = std::chrono::steady_clock::now();
|
||||
|
||||
{
|
||||
std::unique_lock lock{m_mutex};
|
||||
if ((curr_clock - small_timer_event.time_added) >
|
||||
std::chrono::microseconds{small_timer_event.event.data}) {
|
||||
ev[count++] = small_timer_event.event;
|
||||
small_timer_event.event.data = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::this_thread::yield();
|
||||
|
||||
} while (curr_clock < wait_end_us);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
|
@ -1,26 +0,0 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/libraries/kernel/event_queue.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
using SceKernelUseconds = u32;
|
||||
using SceKernelEqueue = EqueueInternal*;
|
||||
|
||||
int PS4_SYSV_ABI sceKernelCreateEqueue(SceKernelEqueue* eq, const char* name);
|
||||
int PS4_SYSV_ABI sceKernelDeleteEqueue(SceKernelEqueue eq);
|
||||
int PS4_SYSV_ABI sceKernelWaitEqueue(SceKernelEqueue eq, SceKernelEvent* ev, int num, int* out,
|
||||
SceKernelUseconds* timo);
|
||||
void* PS4_SYSV_ABI sceKernelGetEventUserData(const SceKernelEvent* ev);
|
||||
u64 PS4_SYSV_ABI sceKernelGetEventId(const SceKernelEvent* ev);
|
||||
int PS4_SYSV_ABI sceKernelTriggerUserEvent(SceKernelEqueue eq, int id, void* udata);
|
||||
int PS4_SYSV_ABI sceKernelDeleteUserEvent(SceKernelEqueue eq, int id);
|
||||
int PS4_SYSV_ABI sceKernelAddUserEvent(SceKernelEqueue eq, int id);
|
||||
int PS4_SYSV_ABI sceKernelAddUserEventEdge(SceKernelEqueue eq, int id);
|
||||
s32 PS4_SYSV_ABI sceKernelAddHRTimerEvent(SceKernelEqueue eq, int id, timespec* ts, void* udata);
|
||||
s16 PS4_SYSV_ABI sceKernelGetEventFilter(const SceKernelEvent* ev);
|
||||
|
||||
} // namespace Libraries::Kernel
|
|
@ -9,11 +9,11 @@
|
|||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/file_system.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "libkernel.h"
|
||||
#include "kernel.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
std::vector<Core::FileSys::DirEntry> GetDirectoryEntries(const std::filesystem::path& path) {
|
||||
auto GetDirectoryEntries(const std::filesystem::path& path) {
|
||||
std::vector<Core::FileSys::DirEntry> files;
|
||||
for (const auto& entry : std::filesystem::directory_iterator(path)) {
|
||||
auto& dir_entry = files.emplace_back();
|
||||
|
@ -618,7 +618,7 @@ s32 PS4_SYSV_ABI sceKernelRename(const char* from, const char* to) {
|
|||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void fileSystemSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
|
||||
void RegisterFileSystem(Core::Loader::SymbolsResolver* sym) {
|
||||
std::srand(std::time(nullptr));
|
||||
LIB_FUNCTION("1G3lF1Gg1k8", "libkernel", 1, "libkernel", 1, 1, sceKernelOpen);
|
||||
LIB_FUNCTION("wuCroIGjt2g", "libScePosix", 1, "libkernel", 1, 1, posix_open);
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/kernel/time_management.h"
|
||||
#include "core/libraries/kernel/time.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
|
@ -65,11 +65,6 @@ constexpr int ORBIS_KERNEL_O_DSYNC = 0x1000;
|
|||
constexpr int ORBIS_KERNEL_O_DIRECT = 0x00010000;
|
||||
constexpr int ORBIS_KERNEL_O_DIRECTORY = 0x00020000;
|
||||
|
||||
int PS4_SYSV_ABI sceKernelOpen(const char* path, int flags, /* SceKernelMode*/ u16 mode);
|
||||
|
||||
int PS4_SYSV_ABI posix_open(const char* path, int flags, /* SceKernelMode*/ u16 mode);
|
||||
s64 PS4_SYSV_ABI lseek(int d, s64 offset, int whence);
|
||||
|
||||
void fileSystemSymbolsRegister(Core::Loader::SymbolsResolver* sym);
|
||||
void RegisterFileSystem(Core::Loader::SymbolsResolver* sym);
|
||||
|
||||
} // namespace Libraries::Kernel
|
||||
|
|
250
src/core/libraries/kernel/kernel.cpp
Normal file
250
src/core/libraries/kernel/kernel.cpp
Normal file
|
@ -0,0 +1,250 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/polyfill_thread.h"
|
||||
#include "common/singleton.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/equeue.h"
|
||||
#include "core/libraries/kernel/file_system.h"
|
||||
#include "core/libraries/kernel/kernel.h"
|
||||
#include "core/libraries/kernel/memory.h"
|
||||
#include "core/libraries/kernel/process.h"
|
||||
#include "core/libraries/kernel/threads.h"
|
||||
#include "core/libraries/kernel/threads/exception.h"
|
||||
#include "core/libraries/kernel/time.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/linker.h"
|
||||
|
||||
#ifdef _WIN64
|
||||
#include <io.h>
|
||||
#include <objbase.h>
|
||||
#include <windows.h>
|
||||
#else
|
||||
#ifdef __APPLE__
|
||||
#include <date/tz.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
static u64 g_stack_chk_guard = 0xDEADBEEF54321ABC; // dummy return
|
||||
|
||||
boost::asio::io_context io_context;
|
||||
std::mutex m_asio_req;
|
||||
std::condition_variable_any cv_asio_req;
|
||||
std::atomic<u32> asio_requests;
|
||||
std::jthread service_thread;
|
||||
|
||||
void KernelSignalRequest() {
|
||||
std::unique_lock lock{m_asio_req};
|
||||
++asio_requests;
|
||||
cv_asio_req.notify_one();
|
||||
}
|
||||
|
||||
static void KernelServiceThread(std::stop_token stoken) {
|
||||
Common::SetCurrentThreadName("shadPS4:Kernel_ServiceThread");
|
||||
|
||||
while (!stoken.stop_requested()) {
|
||||
HLE_TRACE;
|
||||
{
|
||||
std::unique_lock lock{m_asio_req};
|
||||
Common::CondvarWait(cv_asio_req, lock, stoken, [] { return asio_requests != 0; });
|
||||
}
|
||||
if (stoken.stop_requested()) {
|
||||
break;
|
||||
}
|
||||
|
||||
io_context.run();
|
||||
io_context.reset();
|
||||
|
||||
asio_requests = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static PS4_SYSV_ABI void stack_chk_fail() {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
struct iovec {
|
||||
void* iov_base; /* Base address. */
|
||||
size_t iov_len; /* Length. */
|
||||
};
|
||||
|
||||
size_t PS4_SYSV_ABI _writev(int fd, const struct iovec* iov, int iovcn) {
|
||||
size_t total_written = 0;
|
||||
for (int i = 0; i < iovcn; i++) {
|
||||
total_written += ::fwrite(iov[i].iov_base, 1, iov[i].iov_len, stdout);
|
||||
}
|
||||
return total_written;
|
||||
}
|
||||
|
||||
static thread_local int g_posix_errno = 0;
|
||||
|
||||
int* PS4_SYSV_ABI __Error() {
|
||||
return &g_posix_errno;
|
||||
}
|
||||
|
||||
void ErrSceToPosix(int result) {
|
||||
const int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP
|
||||
? result + -SCE_KERNEL_ERROR_UNKNOWN
|
||||
: POSIX_EOTHER;
|
||||
g_posix_errno = rt;
|
||||
}
|
||||
|
||||
int ErrnoToSceKernelError(int e) {
|
||||
const auto res = SCE_KERNEL_ERROR_UNKNOWN + e;
|
||||
return res > SCE_KERNEL_ERROR_ESTOP ? SCE_KERNEL_ERROR_UNKNOWN : res;
|
||||
}
|
||||
|
||||
void SetPosixErrno(int e) {
|
||||
// Some error numbers are different between supported OSes or the PS4
|
||||
switch (e) {
|
||||
case EPERM:
|
||||
g_posix_errno = POSIX_EPERM;
|
||||
break;
|
||||
case EAGAIN:
|
||||
g_posix_errno = POSIX_EAGAIN;
|
||||
break;
|
||||
case ENOMEM:
|
||||
g_posix_errno = POSIX_ENOMEM;
|
||||
break;
|
||||
case EINVAL:
|
||||
g_posix_errno = POSIX_EINVAL;
|
||||
break;
|
||||
case ENOSPC:
|
||||
g_posix_errno = POSIX_ENOSPC;
|
||||
break;
|
||||
case ERANGE:
|
||||
g_posix_errno = POSIX_ERANGE;
|
||||
break;
|
||||
case EDEADLK:
|
||||
g_posix_errno = POSIX_EDEADLK;
|
||||
break;
|
||||
case ETIMEDOUT:
|
||||
g_posix_errno = POSIX_ETIMEDOUT;
|
||||
break;
|
||||
default:
|
||||
g_posix_errno = e;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t g_mspace_atomic_id_mask = 0;
|
||||
static uint64_t g_mstate_table[64] = {0};
|
||||
|
||||
struct HeapInfoInfo {
|
||||
uint64_t size = sizeof(HeapInfoInfo);
|
||||
uint32_t flag;
|
||||
uint32_t getSegmentInfo;
|
||||
uint64_t* mspace_atomic_id_mask;
|
||||
uint64_t* mstate_table;
|
||||
};
|
||||
|
||||
void PS4_SYSV_ABI sceLibcHeapGetTraceInfo(HeapInfoInfo* info) {
|
||||
info->mspace_atomic_id_mask = &g_mspace_atomic_id_mask;
|
||||
info->mstate_table = g_mstate_table;
|
||||
info->getSegmentInfo = 0;
|
||||
}
|
||||
|
||||
s64 PS4_SYSV_ABI ps4__write(int d, const char* buf, std::size_t nbytes) {
|
||||
if (d <= 2) { // stdin,stdout,stderr
|
||||
std::string_view str{buf};
|
||||
if (str[nbytes - 1] == '\n') {
|
||||
str = str.substr(0, nbytes - 1);
|
||||
}
|
||||
LOG_INFO(Tty, "{}", str);
|
||||
return nbytes;
|
||||
}
|
||||
LOG_ERROR(Kernel, "(STUBBED) called d = {} nbytes = {} ", d, nbytes);
|
||||
UNREACHABLE();
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s64 PS4_SYSV_ABI ps4__read(int d, void* buf, u64 nbytes) {
|
||||
ASSERT_MSG(d == 0, "d is not 0!");
|
||||
|
||||
return static_cast<s64>(
|
||||
strlen(std::fgets(static_cast<char*>(buf), static_cast<int>(nbytes), stdin)));
|
||||
}
|
||||
|
||||
struct OrbisKernelUuid {
|
||||
u32 timeLow;
|
||||
u16 timeMid;
|
||||
u16 timeHiAndVersion;
|
||||
u8 clockSeqHiAndReserved;
|
||||
u8 clockSeqLow;
|
||||
u8 node[6];
|
||||
};
|
||||
|
||||
int PS4_SYSV_ABI sceKernelUuidCreate(OrbisKernelUuid* orbisUuid) {
|
||||
#ifdef _WIN64
|
||||
UUID uuid;
|
||||
UuidCreate(&uuid);
|
||||
orbisUuid->timeLow = uuid.Data1;
|
||||
orbisUuid->timeMid = uuid.Data2;
|
||||
orbisUuid->timeHiAndVersion = uuid.Data3;
|
||||
orbisUuid->clockSeqHiAndReserved = uuid.Data4[0];
|
||||
orbisUuid->clockSeqLow = uuid.Data4[1];
|
||||
for (int i = 0; i < 6; i++) {
|
||||
orbisUuid->node[i] = uuid.Data4[2 + i];
|
||||
}
|
||||
#else
|
||||
LOG_ERROR(Kernel, "sceKernelUuidCreate: Add linux");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* PS4_SYSV_ABI sceKernelGetFsSandboxRandomWord() {
|
||||
const char* path = "sys";
|
||||
return path;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_connect() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI _sigprocmask() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_getpagesize() {
|
||||
return 4096;
|
||||
}
|
||||
|
||||
void RegisterKernel(Core::Loader::SymbolsResolver* sym) {
|
||||
service_thread = std::jthread{KernelServiceThread};
|
||||
|
||||
Libraries::Kernel::RegisterFileSystem(sym);
|
||||
Libraries::Kernel::RegisterTime(sym);
|
||||
Libraries::Kernel::RegisterThreads(sym);
|
||||
Libraries::Kernel::RegisterKernelEventFlag(sym);
|
||||
Libraries::Kernel::RegisterMemory(sym);
|
||||
Libraries::Kernel::RegisterEventQueue(sym);
|
||||
Libraries::Kernel::RegisterProcess(sym);
|
||||
Libraries::Kernel::RegisterException(sym);
|
||||
|
||||
LIB_OBJ("f7uOxY9mM1U", "libkernel", 1, "libkernel", 1, 1, &g_stack_chk_guard);
|
||||
LIB_FUNCTION("JGfTMBOdUJo", "libkernel", 1, "libkernel", 1, 1, sceKernelGetFsSandboxRandomWord);
|
||||
LIB_FUNCTION("XVL8So3QJUk", "libkernel", 1, "libkernel", 1, 1, posix_connect);
|
||||
LIB_FUNCTION("6xVpy0Fdq+I", "libkernel", 1, "libkernel", 1, 1, _sigprocmask);
|
||||
LIB_FUNCTION("Xjoosiw+XPI", "libkernel", 1, "libkernel", 1, 1, sceKernelUuidCreate);
|
||||
LIB_FUNCTION("Ou3iL1abvng", "libkernel", 1, "libkernel", 1, 1, stack_chk_fail);
|
||||
LIB_FUNCTION("9BcDykPmo1I", "libkernel", 1, "libkernel", 1, 1, __Error);
|
||||
LIB_FUNCTION("YSHRBRLn2pI", "libkernel", 1, "libkernel", 1, 1, _writev);
|
||||
LIB_FUNCTION("DRuBt2pvICk", "libkernel", 1, "libkernel", 1, 1, ps4__read);
|
||||
LIB_FUNCTION("k+AXqu2-eBc", "libkernel", 1, "libkernel", 1, 1, posix_getpagesize);
|
||||
LIB_FUNCTION("k+AXqu2-eBc", "libScePosix", 1, "libkernel", 1, 1, posix_getpagesize);
|
||||
LIB_FUNCTION("NWtTN10cJzE", "libSceLibcInternalExt", 1, "libSceLibcInternal", 1, 1,
|
||||
sceLibcHeapGetTraceInfo);
|
||||
LIB_FUNCTION("FxVZqBAA7ks", "libkernel", 1, "libkernel", 1, 1, ps4__write);
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
56
src/core/libraries/kernel/kernel.h
Normal file
56
src/core/libraries/kernel/kernel.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <fmt/core.h>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
void ErrSceToPosix(int result);
|
||||
int ErrnoToSceKernelError(int e);
|
||||
void SetPosixErrno(int e);
|
||||
|
||||
template <size_t N>
|
||||
struct StringLiteral {
|
||||
constexpr StringLiteral(const char (&str)[N]) {
|
||||
std::copy_n(str, N, value);
|
||||
}
|
||||
|
||||
char value[N];
|
||||
};
|
||||
|
||||
template <StringLiteral name, class F, F f>
|
||||
struct WrapperImpl;
|
||||
|
||||
template <StringLiteral name, class R, class... Args, PS4_SYSV_ABI R (*f)(Args...)>
|
||||
struct WrapperImpl<name, PS4_SYSV_ABI R (*)(Args...), f> {
|
||||
static constexpr StringLiteral Name{name};
|
||||
static R PS4_SYSV_ABI wrap(Args... args) {
|
||||
u32 ret = f(args...);
|
||||
if (ret != 0) {
|
||||
// LOG_ERROR(Lib_Kernel, "Function {} returned {}", std::string_view{name.value}, ret);
|
||||
ret += SCE_KERNEL_ERROR_UNKNOWN;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
template <StringLiteral name, class F, F f>
|
||||
constexpr auto OrbisWrapper = WrapperImpl<name, F, f>::wrap;
|
||||
|
||||
#define ORBIS(func) WrapperImpl<#func, decltype(&func), func>::wrap
|
||||
|
||||
int* PS4_SYSV_ABI __Error();
|
||||
|
||||
void RegisterKernel(Core::Loader::SymbolsResolver* sym);
|
||||
|
||||
} // namespace Libraries::Kernel
|
|
@ -1,509 +0,0 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/elf_info.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/polyfill_thread.h"
|
||||
#include "common/singleton.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/file_format/psf.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/cpu_management.h"
|
||||
#include "core/libraries/kernel/event_flag/event_flag.h"
|
||||
#include "core/libraries/kernel/event_queues.h"
|
||||
#include "core/libraries/kernel/file_system.h"
|
||||
#include "core/libraries/kernel/libkernel.h"
|
||||
#include "core/libraries/kernel/memory_management.h"
|
||||
#include "core/libraries/kernel/thread_management.h"
|
||||
#include "core/libraries/kernel/time_management.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/linker.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
#ifdef _WIN64
|
||||
#include <io.h>
|
||||
#include <objbase.h>
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#ifdef __APPLE__
|
||||
#include <date/tz.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
static u64 g_stack_chk_guard = 0xDEADBEEF54321ABC; // dummy return
|
||||
|
||||
boost::asio::io_context io_context;
|
||||
std::mutex m_asio_req;
|
||||
std::condition_variable_any cv_asio_req;
|
||||
std::atomic<u32> asio_requests;
|
||||
std::jthread service_thread;
|
||||
|
||||
void KernelSignalRequest() {
|
||||
std::unique_lock lock{m_asio_req};
|
||||
++asio_requests;
|
||||
cv_asio_req.notify_one();
|
||||
}
|
||||
|
||||
static void KernelServiceThread(std::stop_token stoken) {
|
||||
Common::SetCurrentThreadName("shadPS4:Kernel_ServiceThread");
|
||||
|
||||
while (!stoken.stop_requested()) {
|
||||
HLE_TRACE;
|
||||
{
|
||||
std::unique_lock lock{m_asio_req};
|
||||
Common::CondvarWait(cv_asio_req, lock, stoken, [] { return asio_requests != 0; });
|
||||
}
|
||||
if (stoken.stop_requested()) {
|
||||
break;
|
||||
}
|
||||
|
||||
io_context.run();
|
||||
io_context.reset();
|
||||
|
||||
asio_requests = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void* PS4_SYSV_ABI sceKernelGetProcParam() {
|
||||
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
return reinterpret_cast<void*>(linker->GetProcParam());
|
||||
}
|
||||
|
||||
static PS4_SYSV_ABI void stack_chk_fail() {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len) {
|
||||
LOG_INFO(Kernel_Vmm, "addr = {}, len = {:#x}", fmt::ptr(addr), len);
|
||||
if (len == 0) {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
auto* memory = Core::Memory::Instance();
|
||||
memory->UnmapMemory(std::bit_cast<VAddr>(addr), len);
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
struct iovec {
|
||||
void* iov_base; /* Base address. */
|
||||
size_t iov_len; /* Length. */
|
||||
};
|
||||
|
||||
size_t PS4_SYSV_ABI _writev(int fd, const struct iovec* iov, int iovcn) {
|
||||
// weird it gives fd ==0 and writes to stdout , i am not sure if it that is valid (found in
|
||||
// openorbis)
|
||||
size_t total_written = 0;
|
||||
for (int i = 0; i < iovcn; i++) {
|
||||
total_written += ::fwrite(iov[i].iov_base, 1, iov[i].iov_len, stdout);
|
||||
}
|
||||
return total_written;
|
||||
}
|
||||
|
||||
static thread_local int g_posix_errno = 0;
|
||||
int* PS4_SYSV_ABI __Error() {
|
||||
return &g_posix_errno;
|
||||
}
|
||||
|
||||
void ErrSceToPosix(int result) {
|
||||
const int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP
|
||||
? result + -SCE_KERNEL_ERROR_UNKNOWN
|
||||
: POSIX_EOTHER;
|
||||
g_posix_errno = rt;
|
||||
}
|
||||
|
||||
int ErrnoToSceKernelError(int e) {
|
||||
const auto res = SCE_KERNEL_ERROR_UNKNOWN + e;
|
||||
return res > SCE_KERNEL_ERROR_ESTOP ? SCE_KERNEL_ERROR_UNKNOWN : res;
|
||||
}
|
||||
|
||||
void SetPosixErrno(int e) {
|
||||
// Some error numbers are different between supported OSes or the PS4
|
||||
switch (e) {
|
||||
case EPERM:
|
||||
g_posix_errno = POSIX_EPERM;
|
||||
break;
|
||||
case EAGAIN:
|
||||
g_posix_errno = POSIX_EAGAIN;
|
||||
break;
|
||||
case ENOMEM:
|
||||
g_posix_errno = POSIX_ENOMEM;
|
||||
break;
|
||||
case EINVAL:
|
||||
g_posix_errno = POSIX_EINVAL;
|
||||
break;
|
||||
case ENOSPC:
|
||||
g_posix_errno = POSIX_ENOSPC;
|
||||
break;
|
||||
case ERANGE:
|
||||
g_posix_errno = POSIX_ERANGE;
|
||||
break;
|
||||
case EDEADLK:
|
||||
g_posix_errno = POSIX_EDEADLK;
|
||||
break;
|
||||
case ETIMEDOUT:
|
||||
g_posix_errno = POSIX_ETIMEDOUT;
|
||||
break;
|
||||
default:
|
||||
g_posix_errno = e;
|
||||
}
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelMmap(void* addr, u64 len, int prot, int flags, int fd, size_t offset,
|
||||
void** res) {
|
||||
LOG_INFO(Kernel_Vmm, "called addr = {}, len = {}, prot = {}, flags = {}, fd = {}, offset = {}",
|
||||
fmt::ptr(addr), len, prot, flags, fd, offset);
|
||||
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
|
||||
auto* memory = Core::Memory::Instance();
|
||||
const auto mem_prot = static_cast<Core::MemoryProt>(prot);
|
||||
const auto mem_flags = static_cast<Core::MemoryMapFlags>(flags);
|
||||
if (fd == -1) {
|
||||
return memory->MapMemory(res, std::bit_cast<VAddr>(addr), len, mem_prot, mem_flags,
|
||||
Core::VMAType::Flexible);
|
||||
} else {
|
||||
const uintptr_t handle = h->GetFile(fd)->f.GetFileMapping();
|
||||
return memory->MapFile(res, std::bit_cast<VAddr>(addr), len, mem_prot, mem_flags, handle,
|
||||
offset);
|
||||
}
|
||||
}
|
||||
|
||||
void* PS4_SYSV_ABI posix_mmap(void* addr, u64 len, int prot, int flags, int fd, u64 offset) {
|
||||
void* ptr;
|
||||
LOG_INFO(Kernel_Vmm, "posix mmap redirect to sceKernelMmap");
|
||||
// posix call the difference is that there is a different behaviour when it doesn't return 0 or
|
||||
// SCE_OK
|
||||
const VAddr ret_addr = (VAddr)__builtin_return_address(0);
|
||||
int result = sceKernelMmap(addr, len, prot, flags, fd, offset, &ptr);
|
||||
ASSERT(result == 0);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelConfiguredFlexibleMemorySize(u64* sizeOut) {
|
||||
if (sizeOut == nullptr) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
auto* memory = Core::Memory::Instance();
|
||||
*sizeOut = memory->GetTotalFlexibleSize();
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
static uint64_t g_mspace_atomic_id_mask = 0;
|
||||
static uint64_t g_mstate_table[64] = {0};
|
||||
|
||||
struct HeapInfoInfo {
|
||||
uint64_t size = sizeof(HeapInfoInfo);
|
||||
uint32_t flag;
|
||||
uint32_t getSegmentInfo;
|
||||
uint64_t* mspace_atomic_id_mask;
|
||||
uint64_t* mstate_table;
|
||||
};
|
||||
|
||||
void PS4_SYSV_ABI sceLibcHeapGetTraceInfo(HeapInfoInfo* info) {
|
||||
info->mspace_atomic_id_mask = &g_mspace_atomic_id_mask;
|
||||
info->mstate_table = g_mstate_table;
|
||||
info->getSegmentInfo = 0;
|
||||
}
|
||||
|
||||
s64 PS4_SYSV_ABI ps4__write(int d, const void* buf, std::size_t nbytes) {
|
||||
if (d <= 2) { // stdin,stdout,stderr
|
||||
char* str = strdup((const char*)buf);
|
||||
if (str[nbytes - 1] == '\n')
|
||||
str[nbytes - 1] = 0;
|
||||
LOG_INFO(Tty, "{}", str);
|
||||
free(str);
|
||||
return nbytes;
|
||||
}
|
||||
LOG_ERROR(Kernel, "(STUBBED) called d = {} nbytes = {} ", d, nbytes);
|
||||
UNREACHABLE(); // normal write , is it a posix call??
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time,
|
||||
struct OrbisTimesec* st, unsigned long* dst_sec) {
|
||||
LOG_TRACE(Kernel, "Called");
|
||||
#ifdef __APPLE__
|
||||
// std::chrono::current_zone() not available yet.
|
||||
const auto* time_zone = date::current_zone();
|
||||
#else
|
||||
const auto* time_zone = std::chrono::current_zone();
|
||||
#endif
|
||||
auto info = time_zone->get_info(std::chrono::system_clock::now());
|
||||
|
||||
*local_time = info.offset.count() + info.save.count() * 60 + time;
|
||||
|
||||
if (st != nullptr) {
|
||||
st->t = time;
|
||||
st->west_sec = info.offset.count() * 60;
|
||||
st->dst_sec = info.save.count() * 60;
|
||||
}
|
||||
|
||||
if (dst_sec != nullptr) {
|
||||
*dst_sec = info.save.count() * 60;
|
||||
}
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(int* ver) {
|
||||
int version = Common::ElfInfo::Instance().RawFirmwareVer();
|
||||
LOG_DEBUG(Kernel, "returned system version = {:#x}", version);
|
||||
*ver = version;
|
||||
return (version > 0) ? ORBIS_OK : ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
s64 PS4_SYSV_ABI ps4__read(int d, void* buf, u64 nbytes) {
|
||||
ASSERT_MSG(d == 0, "d is not 0!");
|
||||
|
||||
return static_cast<s64>(
|
||||
strlen(std::fgets(static_cast<char*>(buf), static_cast<int>(nbytes), stdin)));
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelLoadStartModule(const char* moduleFileName, size_t args, const void* argp,
|
||||
u32 flags, const void* pOpt, int* pRes) {
|
||||
LOG_INFO(Lib_Kernel, "called filename = {}, args = {}", moduleFileName, args);
|
||||
|
||||
if (flags != 0) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
||||
const auto path = mnt->GetHostPath(moduleFileName);
|
||||
|
||||
// Load PRX module and relocate any modules that import it.
|
||||
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
u32 handle = linker->LoadModule(path, true);
|
||||
if (handle == -1) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
auto* module = linker->GetModule(handle);
|
||||
linker->RelocateAnyImports(module);
|
||||
|
||||
// If the new module has a TLS image, trigger its load when TlsGetAddr is called.
|
||||
if (module->tls.image_size != 0) {
|
||||
linker->AdvanceGenerationCounter();
|
||||
}
|
||||
|
||||
// Retrieve and verify proc param according to libkernel.
|
||||
u64* param = module->GetProcParam<u64*>();
|
||||
ASSERT_MSG(!param || param[0] >= 0x18, "Invalid module param size: {}", param[0]);
|
||||
module->Start(args, argp, param);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelDlsym(s32 handle, const char* symbol, void** addrp) {
|
||||
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
auto* module = linker->GetModule(handle);
|
||||
*addrp = module->FindByName(symbol);
|
||||
if (*addrp == nullptr) {
|
||||
return ORBIS_KERNEL_ERROR_ESRCH;
|
||||
}
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
static constexpr size_t ORBIS_DBG_MAX_NAME_LENGTH = 256;
|
||||
|
||||
struct OrbisModuleInfoForUnwind {
|
||||
u64 st_size;
|
||||
std::array<char, ORBIS_DBG_MAX_NAME_LENGTH> name;
|
||||
VAddr eh_frame_hdr_addr;
|
||||
VAddr eh_frame_addr;
|
||||
u64 eh_frame_size;
|
||||
VAddr seg0_addr;
|
||||
u64 seg0_size;
|
||||
};
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelGetModuleInfoForUnwind(VAddr addr, int flags,
|
||||
OrbisModuleInfoForUnwind* info) {
|
||||
if (flags >= 3) {
|
||||
std::memset(info, 0, sizeof(OrbisModuleInfoForUnwind));
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
if (!info) {
|
||||
return ORBIS_KERNEL_ERROR_EFAULT;
|
||||
}
|
||||
if (info->st_size <= sizeof(OrbisModuleInfoForUnwind)) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
// Find module that contains specified address.
|
||||
LOG_INFO(Lib_Kernel, "called addr = {:#x}, flags = {:#x}", addr, flags);
|
||||
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
auto* module = linker->FindByAddress(addr);
|
||||
const auto mod_info = module->GetModuleInfoEx();
|
||||
|
||||
// Fill in module info.
|
||||
info->name = mod_info.name;
|
||||
info->eh_frame_hdr_addr = mod_info.eh_frame_hdr_addr;
|
||||
info->eh_frame_addr = mod_info.eh_frame_addr;
|
||||
info->eh_frame_size = mod_info.eh_frame_size;
|
||||
info->seg0_addr = mod_info.segments[0].address;
|
||||
info->seg0_size = mod_info.segments[0].size;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelGetModuleInfoFromAddr(VAddr addr, int flags,
|
||||
Core::OrbisKernelModuleInfoEx* info) {
|
||||
LOG_INFO(Lib_Kernel, "called addr = {:#x}, flags = {:#x}", addr, flags);
|
||||
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
auto* module = linker->FindByAddress(addr);
|
||||
*info = module->GetModuleInfoEx();
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelDebugRaiseException() {
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelGetCpumode() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PS4_SYSV_ABI sched_yield() {
|
||||
return std::this_thread::yield();
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelUuidCreate(OrbisKernelUuid* orbisUuid) {
|
||||
#ifdef _WIN64
|
||||
UUID uuid;
|
||||
UuidCreate(&uuid);
|
||||
orbisUuid->timeLow = uuid.Data1;
|
||||
orbisUuid->timeMid = uuid.Data2;
|
||||
orbisUuid->timeHiAndVersion = uuid.Data3;
|
||||
orbisUuid->clockSeqHiAndReserved = uuid.Data4[0];
|
||||
orbisUuid->clockSeqLow = uuid.Data4[1];
|
||||
for (int i = 0; i < 6; i++) {
|
||||
orbisUuid->node[i] = uuid.Data4[2 + i];
|
||||
}
|
||||
#else
|
||||
LOG_ERROR(Kernel, "sceKernelUuidCreate: Add linux");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* PS4_SYSV_ABI sceKernelGetFsSandboxRandomWord() {
|
||||
const char* path = "sys";
|
||||
return path;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_connect() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI _sigprocmask() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_getpagesize() {
|
||||
return 4096;
|
||||
}
|
||||
|
||||
void LibKernel_Register(Core::Loader::SymbolsResolver* sym) {
|
||||
service_thread = std::jthread{KernelServiceThread};
|
||||
|
||||
// obj
|
||||
LIB_OBJ("f7uOxY9mM1U", "libkernel", 1, "libkernel", 1, 1, &g_stack_chk_guard);
|
||||
|
||||
// misc
|
||||
LIB_FUNCTION("JGfTMBOdUJo", "libkernel", 1, "libkernel", 1, 1, sceKernelGetFsSandboxRandomWord);
|
||||
LIB_FUNCTION("XVL8So3QJUk", "libkernel", 1, "libkernel", 1, 1, posix_connect);
|
||||
LIB_FUNCTION("6xVpy0Fdq+I", "libkernel", 1, "libkernel", 1, 1, _sigprocmask);
|
||||
|
||||
// memory
|
||||
LIB_FUNCTION("OMDRKKAZ8I4", "libkernel", 1, "libkernel", 1, 1, sceKernelDebugRaiseException);
|
||||
LIB_FUNCTION("rTXw65xmLIA", "libkernel", 1, "libkernel", 1, 1, sceKernelAllocateDirectMemory);
|
||||
LIB_FUNCTION("B+vc2AO2Zrc", "libkernel", 1, "libkernel", 1, 1,
|
||||
sceKernelAllocateMainDirectMemory);
|
||||
LIB_FUNCTION("C0f7TJcbfac", "libkernel", 1, "libkernel", 1, 1,
|
||||
sceKernelAvailableDirectMemorySize);
|
||||
LIB_FUNCTION("hwVSPCmp5tM", "libkernel", 1, "libkernel", 1, 1,
|
||||
sceKernelCheckedReleaseDirectMemory);
|
||||
LIB_FUNCTION("rVjRvHJ0X6c", "libkernel", 1, "libkernel", 1, 1, sceKernelVirtualQuery);
|
||||
LIB_FUNCTION("7oxv3PPCumo", "libkernel", 1, "libkernel", 1, 1, sceKernelReserveVirtualRange);
|
||||
LIB_FUNCTION("BC+OG5m9+bw", "libkernel", 1, "libkernel", 1, 1, sceKernelGetDirectMemoryType);
|
||||
LIB_FUNCTION("pO96TwzOm5E", "libkernel", 1, "libkernel", 1, 1, sceKernelGetDirectMemorySize);
|
||||
LIB_FUNCTION("NcaWUxfMNIQ", "libkernel", 1, "libkernel", 1, 1, sceKernelMapNamedDirectMemory);
|
||||
LIB_FUNCTION("L-Q3LEjIbgA", "libkernel", 1, "libkernel", 1, 1, sceKernelMapDirectMemory);
|
||||
LIB_FUNCTION("WFcfL2lzido", "libkernel", 1, "libkernel", 1, 1, sceKernelQueryMemoryProtection);
|
||||
LIB_FUNCTION("BHouLQzh0X0", "libkernel", 1, "libkernel", 1, 1, sceKernelDirectMemoryQuery);
|
||||
LIB_FUNCTION("MBuItvba6z8", "libkernel", 1, "libkernel", 1, 1, sceKernelReleaseDirectMemory);
|
||||
LIB_FUNCTION("PGhQHd-dzv8", "libkernel", 1, "libkernel", 1, 1, sceKernelMmap);
|
||||
LIB_FUNCTION("cQke9UuBQOk", "libkernel", 1, "libkernel", 1, 1, sceKernelMunmap);
|
||||
LIB_FUNCTION("mL8NDH86iQI", "libkernel", 1, "libkernel", 1, 1, sceKernelMapNamedFlexibleMemory);
|
||||
LIB_FUNCTION("aNz11fnnzi4", "libkernel", 1, "libkernel", 1, 1,
|
||||
sceKernelAvailableFlexibleMemorySize);
|
||||
LIB_FUNCTION("IWIBBdTHit4", "libkernel", 1, "libkernel", 1, 1, sceKernelMapFlexibleMemory);
|
||||
LIB_FUNCTION("p5EcQeEeJAE", "libkernel", 1, "libkernel", 1, 1,
|
||||
_sceKernelRtldSetApplicationHeapAPI);
|
||||
LIB_FUNCTION("wzvqT4UqKX8", "libkernel", 1, "libkernel", 1, 1, sceKernelLoadStartModule);
|
||||
LIB_FUNCTION("LwG8g3niqwA", "libkernel", 1, "libkernel", 1, 1, sceKernelDlsym);
|
||||
LIB_FUNCTION("RpQJJVKTiFM", "libkernel", 1, "libkernel", 1, 1, sceKernelGetModuleInfoForUnwind);
|
||||
LIB_FUNCTION("f7KBOafysXo", "libkernel", 1, "libkernel", 1, 1, sceKernelGetModuleInfoFromAddr);
|
||||
LIB_FUNCTION("VOx8NGmHXTs", "libkernel", 1, "libkernel", 1, 1, sceKernelGetCpumode);
|
||||
LIB_FUNCTION("Xjoosiw+XPI", "libkernel", 1, "libkernel", 1, 1, sceKernelUuidCreate);
|
||||
|
||||
LIB_FUNCTION("2SKEx6bSq-4", "libkernel", 1, "libkernel", 1, 1, sceKernelBatchMap);
|
||||
LIB_FUNCTION("kBJzF8x4SyE", "libkernel", 1, "libkernel", 1, 1, sceKernelBatchMap2);
|
||||
LIB_FUNCTION("DGMG3JshrZU", "libkernel", 1, "libkernel", 1, 1, sceKernelSetVirtualRangeName);
|
||||
LIB_FUNCTION("n1-v6FgU7MQ", "libkernel", 1, "libkernel", 1, 1,
|
||||
sceKernelConfiguredFlexibleMemorySize);
|
||||
|
||||
// Memory pool
|
||||
LIB_FUNCTION("qCSfqDILlns", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolExpand);
|
||||
LIB_FUNCTION("pU-QydtGcGY", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolReserve);
|
||||
LIB_FUNCTION("Vzl66WmfLvk", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolCommit);
|
||||
LIB_FUNCTION("LXo1tpFqJGs", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolDecommit);
|
||||
|
||||
// equeue
|
||||
LIB_FUNCTION("D0OdFMjp46I", "libkernel", 1, "libkernel", 1, 1, sceKernelCreateEqueue);
|
||||
LIB_FUNCTION("jpFjmgAC5AE", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteEqueue);
|
||||
LIB_FUNCTION("fzyMKs9kim0", "libkernel", 1, "libkernel", 1, 1, sceKernelWaitEqueue);
|
||||
LIB_FUNCTION("vz+pg2zdopI", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventUserData);
|
||||
LIB_FUNCTION("4R6-OvI2cEA", "libkernel", 1, "libkernel", 1, 1, sceKernelAddUserEvent);
|
||||
LIB_FUNCTION("WDszmSbWuDk", "libkernel", 1, "libkernel", 1, 1, sceKernelAddUserEventEdge);
|
||||
LIB_FUNCTION("R74tt43xP6k", "libkernel", 1, "libkernel", 1, 1, sceKernelAddHRTimerEvent);
|
||||
LIB_FUNCTION("F6e0kwo4cnk", "libkernel", 1, "libkernel", 1, 1, sceKernelTriggerUserEvent);
|
||||
LIB_FUNCTION("LJDwdSNTnDg", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteUserEvent);
|
||||
LIB_FUNCTION("mJ7aghmgvfc", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventId);
|
||||
LIB_FUNCTION("9bfdLIyuwCY", "libkernel", 1, "libkernel", 1, 1, sceKernelMTypeProtect);
|
||||
LIB_FUNCTION("vSMAm3cxYTY", "libkernel", 1, "libkernel", 1, 1, sceKernelMProtect);
|
||||
LIB_FUNCTION("23CPPI1tyBY", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventFilter);
|
||||
|
||||
// misc
|
||||
LIB_FUNCTION("WslcK1FQcGI", "libkernel", 1, "libkernel", 1, 1, sceKernelIsNeoMode);
|
||||
LIB_FUNCTION("Ou3iL1abvng", "libkernel", 1, "libkernel", 1, 1, stack_chk_fail);
|
||||
LIB_FUNCTION("9BcDykPmo1I", "libkernel", 1, "libkernel", 1, 1, __Error);
|
||||
LIB_FUNCTION("BPE9s9vQQXo", "libkernel", 1, "libkernel", 1, 1, posix_mmap);
|
||||
LIB_FUNCTION("BPE9s9vQQXo", "libScePosix", 1, "libkernel", 1, 1, posix_mmap);
|
||||
LIB_FUNCTION("YSHRBRLn2pI", "libkernel", 1, "libkernel", 1, 1, _writev);
|
||||
LIB_FUNCTION("959qrazPIrg", "libkernel", 1, "libkernel", 1, 1, sceKernelGetProcParam);
|
||||
LIB_FUNCTION("-o5uEDpN+oY", "libkernel", 1, "libkernel", 1, 1, sceKernelConvertUtcToLocaltime);
|
||||
LIB_FUNCTION("WB66evu8bsU", "libkernel", 1, "libkernel", 1, 1, sceKernelGetCompiledSdkVersion);
|
||||
LIB_FUNCTION("DRuBt2pvICk", "libkernel", 1, "libkernel", 1, 1, ps4__read);
|
||||
LIB_FUNCTION("k+AXqu2-eBc", "libkernel", 1, "libkernel", 1, 1, posix_getpagesize);
|
||||
LIB_FUNCTION("k+AXqu2-eBc", "libScePosix", 1, "libkernel", 1, 1, posix_getpagesize);
|
||||
|
||||
Libraries::Kernel::fileSystemSymbolsRegister(sym);
|
||||
Libraries::Kernel::timeSymbolsRegister(sym);
|
||||
Libraries::Kernel::pthreadSymbolsRegister(sym);
|
||||
Libraries::Kernel::RegisterKernelEventFlag(sym);
|
||||
|
||||
// temp
|
||||
LIB_FUNCTION("NWtTN10cJzE", "libSceLibcInternalExt", 1, "libSceLibcInternal", 1, 1,
|
||||
sceLibcHeapGetTraceInfo);
|
||||
LIB_FUNCTION("FxVZqBAA7ks", "libkernel", 1, "libkernel", 1, 1, ps4__write);
|
||||
LIB_FUNCTION("6XG4B33N09g", "libScePosix", 1, "libkernel", 1, 1, sched_yield);
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
|
@ -1,42 +0,0 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
void ErrSceToPosix(int result);
|
||||
int ErrnoToSceKernelError(int e);
|
||||
void SetPosixErrno(int e);
|
||||
|
||||
struct OrbisTimesec {
|
||||
time_t t;
|
||||
u32 west_sec;
|
||||
u32 dst_sec;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint32_t timeLow;
|
||||
uint16_t timeMid;
|
||||
uint16_t timeHiAndVersion;
|
||||
uint8_t clockSeqHiAndReserved;
|
||||
uint8_t clockSeqLow;
|
||||
uint8_t node[6];
|
||||
} OrbisKernelUuid;
|
||||
|
||||
int* PS4_SYSV_ABI __Error();
|
||||
int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time,
|
||||
struct OrbisTimesec* st, unsigned long* dst_sec);
|
||||
int PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(int* ver);
|
||||
|
||||
void LibKernel_Register(Core::Loader::SymbolsResolver* sym);
|
||||
|
||||
} // namespace Libraries::Kernel
|
|
@ -6,10 +6,12 @@
|
|||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/singleton.h"
|
||||
#include "core/address_space.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/memory_management.h"
|
||||
#include "core/libraries/kernel/memory.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/linker.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
|
@ -144,11 +146,6 @@ s32 PS4_SYSV_ABI sceKernelReserveVirtualRange(void** addr, u64 len, int flags, u
|
|||
int PS4_SYSV_ABI sceKernelMapNamedDirectMemory(void** addr, u64 len, int prot, int flags,
|
||||
s64 directMemoryStart, u64 alignment,
|
||||
const char* name) {
|
||||
LOG_INFO(Kernel_Vmm,
|
||||
"addr = {}, len = {:#x}, prot = {:#x}, flags = {:#x}, directMemoryStart = {:#x}, "
|
||||
"alignment = {:#x}",
|
||||
fmt::ptr(*addr), len, prot, flags, directMemoryStart, alignment);
|
||||
|
||||
if (len == 0 || !Common::Is16KBAligned(len)) {
|
||||
LOG_ERROR(Kernel_Vmm, "Map size is either zero or not 16KB aligned!");
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
|
@ -167,6 +164,14 @@ int PS4_SYSV_ABI sceKernelMapNamedDirectMemory(void** addr, u64 len, int prot, i
|
|||
const VAddr in_addr = reinterpret_cast<VAddr>(*addr);
|
||||
const auto mem_prot = static_cast<Core::MemoryProt>(prot);
|
||||
const auto map_flags = static_cast<Core::MemoryMapFlags>(flags);
|
||||
SCOPE_EXIT {
|
||||
LOG_INFO(Kernel_Vmm,
|
||||
"in_addr = {:#x}, out_addr = {}, len = {:#x}, prot = {:#x}, flags = {:#x}, "
|
||||
"directMemoryStart = {:#x}, "
|
||||
"alignment = {:#x}",
|
||||
in_addr, fmt::ptr(*addr), len, prot, flags, directMemoryStart, alignment);
|
||||
};
|
||||
|
||||
auto* memory = Core::Memory::Instance();
|
||||
return memory->MapMemory(addr, in_addr, len, mem_prot, map_flags, Core::VMAType::Direct, "",
|
||||
false, directMemoryStart, alignment);
|
||||
|
@ -200,13 +205,14 @@ s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, std::size_t
|
|||
const VAddr in_addr = reinterpret_cast<VAddr>(*addr_in_out);
|
||||
const auto mem_prot = static_cast<Core::MemoryProt>(prot);
|
||||
const auto map_flags = static_cast<Core::MemoryMapFlags>(flags);
|
||||
SCOPE_EXIT {
|
||||
LOG_INFO(Kernel_Vmm,
|
||||
"in_addr = {:#x}, out_addr = {}, len = {:#x}, prot = {:#x}, flags = {:#x}",
|
||||
in_addr, fmt::ptr(*addr_in_out), len, prot, flags);
|
||||
};
|
||||
auto* memory = Core::Memory::Instance();
|
||||
const int ret = memory->MapMemory(addr_in_out, in_addr, len, mem_prot, map_flags,
|
||||
Core::VMAType::Flexible, name);
|
||||
|
||||
LOG_INFO(Kernel_Vmm, "addr = {}, len = {:#x}, prot = {:#x}, flags = {:#x}",
|
||||
fmt::ptr(*addr_in_out), len, prot, flags);
|
||||
return ret;
|
||||
return memory->MapMemory(addr_in_out, in_addr, len, mem_prot, map_flags,
|
||||
Core::VMAType::Flexible, name);
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, std::size_t len, int prot,
|
||||
|
@ -265,8 +271,6 @@ s32 PS4_SYSV_ABI sceKernelBatchMap(OrbisKernelBatchMapEntry* entries, int numEnt
|
|||
MemoryFlags::SCE_KERNEL_MAP_FIXED); // 0x10, 0x410?
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len);
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEntries,
|
||||
int* numEntriesOut, int flags) {
|
||||
int result = ORBIS_OK;
|
||||
|
@ -445,4 +449,94 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, size_t len, int flags)
|
|||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelMmap(void* addr, u64 len, int prot, int flags, int fd, size_t offset,
|
||||
void** res) {
|
||||
LOG_INFO(Kernel_Vmm, "called addr = {}, len = {}, prot = {}, flags = {}, fd = {}, offset = {}",
|
||||
fmt::ptr(addr), len, prot, flags, fd, offset);
|
||||
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
|
||||
auto* memory = Core::Memory::Instance();
|
||||
const auto mem_prot = static_cast<Core::MemoryProt>(prot);
|
||||
const auto mem_flags = static_cast<Core::MemoryMapFlags>(flags);
|
||||
if (fd == -1) {
|
||||
return memory->MapMemory(res, std::bit_cast<VAddr>(addr), len, mem_prot, mem_flags,
|
||||
Core::VMAType::Flexible);
|
||||
} else {
|
||||
const uintptr_t handle = h->GetFile(fd)->f.GetFileMapping();
|
||||
return memory->MapFile(res, std::bit_cast<VAddr>(addr), len, mem_prot, mem_flags, handle,
|
||||
offset);
|
||||
}
|
||||
}
|
||||
|
||||
void* PS4_SYSV_ABI posix_mmap(void* addr, u64 len, int prot, int flags, int fd, u64 offset) {
|
||||
void* ptr;
|
||||
LOG_INFO(Kernel_Vmm, "posix mmap redirect to sceKernelMmap");
|
||||
int result = sceKernelMmap(addr, len, prot, flags, fd, offset, &ptr);
|
||||
ASSERT(result == 0);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelConfiguredFlexibleMemorySize(u64* sizeOut) {
|
||||
if (sizeOut == nullptr) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
auto* memory = Core::Memory::Instance();
|
||||
*sizeOut = memory->GetTotalFlexibleSize();
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len) {
|
||||
LOG_INFO(Kernel_Vmm, "addr = {}, len = {:#x}", fmt::ptr(addr), len);
|
||||
if (len == 0) {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
auto* memory = Core::Memory::Instance();
|
||||
memory->UnmapMemory(std::bit_cast<VAddr>(addr), len);
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
void RegisterMemory(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("rTXw65xmLIA", "libkernel", 1, "libkernel", 1, 1, sceKernelAllocateDirectMemory);
|
||||
LIB_FUNCTION("B+vc2AO2Zrc", "libkernel", 1, "libkernel", 1, 1,
|
||||
sceKernelAllocateMainDirectMemory);
|
||||
LIB_FUNCTION("C0f7TJcbfac", "libkernel", 1, "libkernel", 1, 1,
|
||||
sceKernelAvailableDirectMemorySize);
|
||||
LIB_FUNCTION("hwVSPCmp5tM", "libkernel", 1, "libkernel", 1, 1,
|
||||
sceKernelCheckedReleaseDirectMemory);
|
||||
LIB_FUNCTION("rVjRvHJ0X6c", "libkernel", 1, "libkernel", 1, 1, sceKernelVirtualQuery);
|
||||
LIB_FUNCTION("7oxv3PPCumo", "libkernel", 1, "libkernel", 1, 1, sceKernelReserveVirtualRange);
|
||||
LIB_FUNCTION("BC+OG5m9+bw", "libkernel", 1, "libkernel", 1, 1, sceKernelGetDirectMemoryType);
|
||||
LIB_FUNCTION("pO96TwzOm5E", "libkernel", 1, "libkernel", 1, 1, sceKernelGetDirectMemorySize);
|
||||
LIB_FUNCTION("NcaWUxfMNIQ", "libkernel", 1, "libkernel", 1, 1, sceKernelMapNamedDirectMemory);
|
||||
LIB_FUNCTION("L-Q3LEjIbgA", "libkernel", 1, "libkernel", 1, 1, sceKernelMapDirectMemory);
|
||||
LIB_FUNCTION("WFcfL2lzido", "libkernel", 1, "libkernel", 1, 1, sceKernelQueryMemoryProtection);
|
||||
LIB_FUNCTION("BHouLQzh0X0", "libkernel", 1, "libkernel", 1, 1, sceKernelDirectMemoryQuery);
|
||||
LIB_FUNCTION("MBuItvba6z8", "libkernel", 1, "libkernel", 1, 1, sceKernelReleaseDirectMemory);
|
||||
LIB_FUNCTION("PGhQHd-dzv8", "libkernel", 1, "libkernel", 1, 1, sceKernelMmap);
|
||||
LIB_FUNCTION("cQke9UuBQOk", "libkernel", 1, "libkernel", 1, 1, sceKernelMunmap);
|
||||
LIB_FUNCTION("mL8NDH86iQI", "libkernel", 1, "libkernel", 1, 1, sceKernelMapNamedFlexibleMemory);
|
||||
LIB_FUNCTION("aNz11fnnzi4", "libkernel", 1, "libkernel", 1, 1,
|
||||
sceKernelAvailableFlexibleMemorySize);
|
||||
LIB_FUNCTION("IWIBBdTHit4", "libkernel", 1, "libkernel", 1, 1, sceKernelMapFlexibleMemory);
|
||||
LIB_FUNCTION("p5EcQeEeJAE", "libkernel", 1, "libkernel", 1, 1,
|
||||
_sceKernelRtldSetApplicationHeapAPI);
|
||||
LIB_FUNCTION("2SKEx6bSq-4", "libkernel", 1, "libkernel", 1, 1, sceKernelBatchMap);
|
||||
LIB_FUNCTION("kBJzF8x4SyE", "libkernel", 1, "libkernel", 1, 1, sceKernelBatchMap2);
|
||||
LIB_FUNCTION("DGMG3JshrZU", "libkernel", 1, "libkernel", 1, 1, sceKernelSetVirtualRangeName);
|
||||
LIB_FUNCTION("n1-v6FgU7MQ", "libkernel", 1, "libkernel", 1, 1,
|
||||
sceKernelConfiguredFlexibleMemorySize);
|
||||
|
||||
LIB_FUNCTION("9bfdLIyuwCY", "libkernel", 1, "libkernel", 1, 1, sceKernelMTypeProtect);
|
||||
LIB_FUNCTION("vSMAm3cxYTY", "libkernel", 1, "libkernel", 1, 1, sceKernelMProtect);
|
||||
|
||||
// Memory pool
|
||||
LIB_FUNCTION("qCSfqDILlns", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolExpand);
|
||||
LIB_FUNCTION("pU-QydtGcGY", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolReserve);
|
||||
LIB_FUNCTION("Vzl66WmfLvk", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolCommit);
|
||||
LIB_FUNCTION("LXo1tpFqJGs", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolDecommit);
|
||||
|
||||
LIB_FUNCTION("BPE9s9vQQXo", "libkernel", 1, "libkernel", 1, 1, posix_mmap);
|
||||
LIB_FUNCTION("BPE9s9vQQXo", "libScePosix", 1, "libkernel", 1, 1, posix_mmap);
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
|
@ -10,6 +10,10 @@ constexpr u64 SCE_KERNEL_MAIN_DMEM_SIZE = 5056_MB; // ~ 5GB
|
|||
// TODO: Confirm this value on hardware.
|
||||
constexpr u64 SCE_KERNEL_MAIN_DMEM_SIZE_PRO = 5568_MB; // ~ 5.5GB
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
enum MemoryTypes : u32 {
|
||||
|
@ -123,4 +127,12 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addrIn, size_t len, size_t ali
|
|||
s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, size_t len, int type, int prot, int flags);
|
||||
s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, size_t len, int flags);
|
||||
|
||||
int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len);
|
||||
|
||||
void* Malloc(size_t size);
|
||||
|
||||
void Free(void* ptr);
|
||||
|
||||
void RegisterMemory(Core::Loader::SymbolsResolver* sym);
|
||||
|
||||
} // namespace Libraries::Kernel
|
140
src/core/libraries/kernel/process.cpp
Normal file
140
src/core/libraries/kernel/process.cpp
Normal file
|
@ -0,0 +1,140 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/elf_info.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/process.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/linker.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
int PS4_SYSV_ABI sceKernelIsNeoMode() {
|
||||
LOG_DEBUG(Kernel_Sce, "called");
|
||||
return Config::isNeoMode();
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(int* ver) {
|
||||
int version = Common::ElfInfo::Instance().RawFirmwareVer();
|
||||
*ver = version;
|
||||
return (version > 0) ? ORBIS_OK : ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelGetCpumode() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* PS4_SYSV_ABI sceKernelGetProcParam() {
|
||||
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
return linker->GetProcParam();
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelLoadStartModule(const char* moduleFileName, size_t args, const void* argp,
|
||||
u32 flags, const void* pOpt, int* pRes) {
|
||||
LOG_INFO(Lib_Kernel, "called filename = {}, args = {}", moduleFileName, args);
|
||||
|
||||
if (flags != 0) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
||||
const auto path = mnt->GetHostPath(moduleFileName);
|
||||
|
||||
// Load PRX module and relocate any modules that import it.
|
||||
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
u32 handle = linker->LoadModule(path, true);
|
||||
if (handle == -1) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
auto* module = linker->GetModule(handle);
|
||||
linker->RelocateAnyImports(module);
|
||||
|
||||
// If the new module has a TLS image, trigger its load when TlsGetAddr is called.
|
||||
if (module->tls.image_size != 0) {
|
||||
linker->AdvanceGenerationCounter();
|
||||
}
|
||||
|
||||
// Retrieve and verify proc param according to libkernel.
|
||||
u64* param = module->GetProcParam<u64*>();
|
||||
ASSERT_MSG(!param || param[0] >= 0x18, "Invalid module param size: {}", param[0]);
|
||||
module->Start(args, argp, param);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelDlsym(s32 handle, const char* symbol, void** addrp) {
|
||||
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
auto* module = linker->GetModule(handle);
|
||||
*addrp = module->FindByName(symbol);
|
||||
if (*addrp == nullptr) {
|
||||
return ORBIS_KERNEL_ERROR_ESRCH;
|
||||
}
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
static constexpr size_t ORBIS_DBG_MAX_NAME_LENGTH = 256;
|
||||
|
||||
struct OrbisModuleInfoForUnwind {
|
||||
u64 st_size;
|
||||
std::array<char, ORBIS_DBG_MAX_NAME_LENGTH> name;
|
||||
VAddr eh_frame_hdr_addr;
|
||||
VAddr eh_frame_addr;
|
||||
u64 eh_frame_size;
|
||||
VAddr seg0_addr;
|
||||
u64 seg0_size;
|
||||
};
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelGetModuleInfoForUnwind(VAddr addr, int flags,
|
||||
OrbisModuleInfoForUnwind* info) {
|
||||
if (flags >= 3) {
|
||||
std::memset(info, 0, sizeof(OrbisModuleInfoForUnwind));
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
if (!info) {
|
||||
return ORBIS_KERNEL_ERROR_EFAULT;
|
||||
}
|
||||
if (info->st_size < sizeof(OrbisModuleInfoForUnwind)) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
// Find module that contains specified address.
|
||||
LOG_INFO(Lib_Kernel, "called addr = {:#x}, flags = {:#x}", addr, flags);
|
||||
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
auto* module = linker->FindByAddress(addr);
|
||||
const auto mod_info = module->GetModuleInfoEx();
|
||||
|
||||
// Fill in module info.
|
||||
std::memset(info, 0, sizeof(OrbisModuleInfoForUnwind));
|
||||
info->name = mod_info.name;
|
||||
info->eh_frame_hdr_addr = mod_info.eh_frame_hdr_addr;
|
||||
info->eh_frame_addr = mod_info.eh_frame_addr;
|
||||
info->eh_frame_size = mod_info.eh_frame_size;
|
||||
info->seg0_addr = mod_info.segments[0].address;
|
||||
info->seg0_size = mod_info.segments[0].size;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelGetModuleInfoFromAddr(VAddr addr, int flags,
|
||||
Core::OrbisKernelModuleInfoEx* info) {
|
||||
LOG_INFO(Lib_Kernel, "called addr = {:#x}, flags = {:#x}", addr, flags);
|
||||
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
auto* module = linker->FindByAddress(addr);
|
||||
*info = module->GetModuleInfoEx();
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void RegisterProcess(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("WB66evu8bsU", "libkernel", 1, "libkernel", 1, 1, sceKernelGetCompiledSdkVersion);
|
||||
LIB_FUNCTION("WslcK1FQcGI", "libkernel", 1, "libkernel", 1, 1, sceKernelIsNeoMode);
|
||||
LIB_FUNCTION("VOx8NGmHXTs", "libkernel", 1, "libkernel", 1, 1, sceKernelGetCpumode);
|
||||
LIB_FUNCTION("959qrazPIrg", "libkernel", 1, "libkernel", 1, 1, sceKernelGetProcParam);
|
||||
LIB_FUNCTION("wzvqT4UqKX8", "libkernel", 1, "libkernel", 1, 1, sceKernelLoadStartModule);
|
||||
LIB_FUNCTION("LwG8g3niqwA", "libkernel", 1, "libkernel", 1, 1, sceKernelDlsym);
|
||||
LIB_FUNCTION("RpQJJVKTiFM", "libkernel", 1, "libkernel", 1, 1, sceKernelGetModuleInfoForUnwind);
|
||||
LIB_FUNCTION("f7KBOafysXo", "libkernel", 1, "libkernel", 1, 1, sceKernelGetModuleInfoFromAddr);
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
|
@ -5,8 +5,16 @@
|
|||
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
int PS4_SYSV_ABI sceKernelIsNeoMode();
|
||||
|
||||
int PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(int* ver);
|
||||
|
||||
void RegisterProcess(Core::Loader::SymbolsResolver* sym);
|
||||
|
||||
} // namespace Libraries::Kernel
|
File diff suppressed because it is too large
Load diff
|
@ -1,225 +0,0 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <semaphore>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
#define ORBIS_PTHREAD_MUTEX_ADAPTIVE_INITIALIZER (reinterpret_cast<ScePthreadMutex>(1))
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
constexpr int ORBIS_KERNEL_PRIO_FIFO_DEFAULT = 700;
|
||||
constexpr int ORBIS_KERNEL_PRIO_FIFO_HIGHEST = 256;
|
||||
constexpr int ORBIS_KERNEL_PRIO_FIFO_LOWEST = 767;
|
||||
constexpr int ORBIS_KERNEL_SEM_VALUE_MAX = 0x7FFFFFFF;
|
||||
|
||||
constexpr int ORBIS_PTHREAD_MUTEX_ERRORCHECK = 1;
|
||||
constexpr int ORBIS_PTHREAD_MUTEX_RECURSIVE = 2;
|
||||
constexpr int ORBIS_PTHREAD_MUTEX_NORMAL = 3;
|
||||
constexpr int ORBIS_PTHREAD_MUTEX_ADAPTIVE = 4;
|
||||
|
||||
struct PthreadInternal;
|
||||
struct PthreadAttrInternal;
|
||||
struct PthreadMutexInternal;
|
||||
struct PthreadMutexattrInternal;
|
||||
struct PthreadCondInternal;
|
||||
struct PthreadCondAttrInternal;
|
||||
struct PthreadRwInternal;
|
||||
struct PthreadRwLockAttrInternal;
|
||||
class PthreadKeys;
|
||||
|
||||
using SceKernelSchedParam = ::sched_param;
|
||||
using ScePthread = PthreadInternal*;
|
||||
using ScePthreadAttr = PthreadAttrInternal*;
|
||||
using ScePthreadMutex = PthreadMutexInternal*;
|
||||
using ScePthreadMutexattr = PthreadMutexattrInternal*;
|
||||
using ScePthreadCond = PthreadCondInternal*;
|
||||
using ScePthreadCondattr = PthreadCondAttrInternal*;
|
||||
using OrbisPthreadRwlock = PthreadRwInternal*;
|
||||
using OrbisPthreadRwlockattr = PthreadRwLockAttrInternal*;
|
||||
using OrbisPthreadKey = u32;
|
||||
|
||||
using PthreadKeyDestructor = PS4_SYSV_ABI void (*)(void*);
|
||||
using PthreadEntryFunc = PS4_SYSV_ABI void* (*)(void*);
|
||||
|
||||
struct PthreadInternal {
|
||||
u8 reserved[4096];
|
||||
std::string name;
|
||||
pthread_t pth;
|
||||
ScePthreadAttr attr;
|
||||
PthreadEntryFunc entry;
|
||||
void* arg;
|
||||
std::atomic_bool is_started;
|
||||
std::atomic_bool is_detached;
|
||||
std::atomic_bool is_almost_done;
|
||||
std::atomic_bool is_free;
|
||||
using Destructor = std::pair<OrbisPthreadKey, PthreadKeyDestructor>;
|
||||
std::vector<Destructor> key_destructors;
|
||||
int prio;
|
||||
};
|
||||
|
||||
struct PthreadAttrInternal {
|
||||
u8 reserved[64];
|
||||
u64 affinity;
|
||||
size_t guard_size;
|
||||
int policy;
|
||||
bool detached;
|
||||
pthread_attr_t pth_attr;
|
||||
};
|
||||
|
||||
struct PthreadMutexInternal {
|
||||
u8 reserved[256];
|
||||
std::string name;
|
||||
pthread_mutex_t pth_mutex;
|
||||
};
|
||||
|
||||
struct PthreadMutexattrInternal {
|
||||
u8 reserved[64];
|
||||
pthread_mutexattr_t pth_mutex_attr;
|
||||
int pprotocol;
|
||||
};
|
||||
|
||||
struct PthreadCondInternal {
|
||||
u8 reserved[256];
|
||||
std::string name;
|
||||
pthread_cond_t cond;
|
||||
};
|
||||
|
||||
struct PthreadCondAttrInternal {
|
||||
u8 reserved[64];
|
||||
pthread_condattr_t cond_attr;
|
||||
clockid_t clock;
|
||||
};
|
||||
|
||||
struct PthreadRwLockAttrInternal {
|
||||
u8 reserved[64];
|
||||
pthread_rwlockattr_t attr_rwlock;
|
||||
int type;
|
||||
};
|
||||
|
||||
struct PthreadRwInternal {
|
||||
pthread_rwlock_t pth_rwlock;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
struct PthreadSemInternal {
|
||||
std::counting_semaphore<ORBIS_KERNEL_SEM_VALUE_MAX> semaphore;
|
||||
std::atomic<s32> value;
|
||||
};
|
||||
|
||||
class PThreadPool {
|
||||
public:
|
||||
ScePthread Create(const char* name);
|
||||
|
||||
private:
|
||||
std::vector<ScePthread> m_threads;
|
||||
std::mutex m_mutex;
|
||||
};
|
||||
|
||||
class PThreadCxt {
|
||||
public:
|
||||
ScePthreadMutexattr* getDefaultMutexattr() {
|
||||
return &m_default_mutexattr;
|
||||
}
|
||||
void setDefaultMutexattr(ScePthreadMutexattr attr) {
|
||||
m_default_mutexattr = attr;
|
||||
}
|
||||
ScePthreadMutexattr* getAdaptiveMutexattr() {
|
||||
return &m_adaptive_mutexattr;
|
||||
}
|
||||
void setAdaptiveMutexattr(ScePthreadMutexattr attr) {
|
||||
m_adaptive_mutexattr = attr;
|
||||
}
|
||||
ScePthreadCondattr* getDefaultCondattr() {
|
||||
return &m_default_condattr;
|
||||
}
|
||||
void setDefaultCondattr(ScePthreadCondattr attr) {
|
||||
m_default_condattr = attr;
|
||||
}
|
||||
ScePthreadAttr* GetDefaultAttr() {
|
||||
return &m_default_attr;
|
||||
}
|
||||
void SetDefaultAttr(ScePthreadAttr attr) {
|
||||
m_default_attr = attr;
|
||||
}
|
||||
PThreadPool* GetPthreadPool() {
|
||||
return m_pthread_pool;
|
||||
}
|
||||
void SetPthreadPool(PThreadPool* pool) {
|
||||
m_pthread_pool = pool;
|
||||
}
|
||||
OrbisPthreadRwlockattr* getDefaultRwattr() {
|
||||
return &m_default_Rwattr;
|
||||
}
|
||||
void setDefaultRwattr(OrbisPthreadRwlockattr attr) {
|
||||
m_default_Rwattr = attr;
|
||||
}
|
||||
|
||||
private:
|
||||
ScePthreadMutexattr m_default_mutexattr = nullptr;
|
||||
ScePthreadMutexattr m_adaptive_mutexattr = nullptr;
|
||||
ScePthreadCondattr m_default_condattr = nullptr;
|
||||
ScePthreadAttr m_default_attr = nullptr;
|
||||
PThreadPool* m_pthread_pool = nullptr;
|
||||
OrbisPthreadRwlockattr m_default_Rwattr = nullptr;
|
||||
};
|
||||
|
||||
void init_pthreads();
|
||||
void pthreadInitSelfMainThread();
|
||||
|
||||
int PS4_SYSV_ABI scePthreadAttrInit(ScePthreadAttr* attr);
|
||||
int PS4_SYSV_ABI scePthreadAttrSetdetachstate(ScePthreadAttr* attr, int detachstate);
|
||||
int PS4_SYSV_ABI scePthreadAttrSetinheritsched(ScePthreadAttr* attr, int inheritSched);
|
||||
int PS4_SYSV_ABI scePthreadAttrSetschedparam(ScePthreadAttr* attr,
|
||||
const SceKernelSchedParam* param);
|
||||
int PS4_SYSV_ABI scePthreadAttrSetschedpolicy(ScePthreadAttr* attr, int policy);
|
||||
ScePthread PS4_SYSV_ABI scePthreadSelf();
|
||||
int PS4_SYSV_ABI scePthreadAttrSetaffinity(ScePthreadAttr* pattr,
|
||||
const /*SceKernelCpumask*/ u64 mask);
|
||||
int PS4_SYSV_ABI scePthreadSetaffinity(ScePthread thread, const /*SceKernelCpumask*/ u64 mask);
|
||||
int PS4_SYSV_ABI scePthreadGetaffinity(ScePthread thread, /*SceKernelCpumask*/ u64* mask);
|
||||
int PS4_SYSV_ABI scePthreadCreate(ScePthread* thread, const ScePthreadAttr* attr,
|
||||
PthreadEntryFunc start_routine, void* arg, const char* name);
|
||||
|
||||
int PS4_SYSV_ABI scePthreadSetprio(ScePthread thread, int prio);
|
||||
|
||||
/***
|
||||
* Mutex calls
|
||||
*/
|
||||
int PS4_SYSV_ABI scePthreadMutexInit(ScePthreadMutex* mutex, const ScePthreadMutexattr* attr,
|
||||
const char* name);
|
||||
int PS4_SYSV_ABI scePthreadMutexattrInit(ScePthreadMutexattr* attr);
|
||||
int PS4_SYSV_ABI scePthreadMutexattrSettype(ScePthreadMutexattr* attr, int type);
|
||||
int PS4_SYSV_ABI scePthreadMutexattrSetprotocol(ScePthreadMutexattr* attr, int protocol);
|
||||
int PS4_SYSV_ABI scePthreadMutexLock(ScePthreadMutex* mutex);
|
||||
int PS4_SYSV_ABI scePthreadMutexUnlock(ScePthreadMutex* mutex);
|
||||
/****
|
||||
* Cond calls
|
||||
*/
|
||||
int PS4_SYSV_ABI scePthreadCondInit(ScePthreadCond* cond, const ScePthreadCondattr* attr,
|
||||
const char* name);
|
||||
int PS4_SYSV_ABI scePthreadCondattrInit(ScePthreadCondattr* attr);
|
||||
int PS4_SYSV_ABI scePthreadCondBroadcast(ScePthreadCond* cond);
|
||||
int PS4_SYSV_ABI scePthreadCondWait(ScePthreadCond* cond, ScePthreadMutex* mutex);
|
||||
/****
|
||||
* Posix calls
|
||||
*/
|
||||
int PS4_SYSV_ABI posix_pthread_mutex_init(ScePthreadMutex* mutex, const ScePthreadMutexattr* attr);
|
||||
int PS4_SYSV_ABI posix_pthread_mutex_lock(ScePthreadMutex* mutex);
|
||||
int PS4_SYSV_ABI posix_pthread_mutex_unlock(ScePthreadMutex* mutex);
|
||||
int PS4_SYSV_ABI posix_pthread_cond_broadcast(ScePthreadCond* cond);
|
||||
|
||||
void pthreadSymbolsRegister(Core::Loader::SymbolsResolver* sym);
|
||||
} // namespace Libraries::Kernel
|
22
src/core/libraries/kernel/threads.cpp
Normal file
22
src/core/libraries/kernel/threads.cpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/libraries/kernel/kernel.h"
|
||||
#include "core/libraries/kernel/threads.h"
|
||||
#include "core/libraries/kernel/threads/pthread.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
void RegisterThreads(Core::Loader::SymbolsResolver* sym) {
|
||||
RegisterMutex(sym);
|
||||
RegisterCond(sym);
|
||||
RegisterRwlock(sym);
|
||||
RegisterSemaphore(sym);
|
||||
RegisterSpec(sym);
|
||||
RegisterThreadAttr(sym);
|
||||
RegisterThread(sym);
|
||||
RegisterRtld(sym);
|
||||
RegisterPthreadClean(sym);
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
72
src/core/libraries/kernel/threads.h
Normal file
72
src/core/libraries/kernel/threads.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include "common/polyfill_thread.h"
|
||||
#include "core/libraries/kernel/threads/pthread.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_attr_init(PthreadAttrT* attr);
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_attr_destroy(PthreadAttrT* attr);
|
||||
|
||||
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);
|
||||
|
||||
void RegisterThreads(Core::Loader::SymbolsResolver* sym);
|
||||
|
||||
class Thread {
|
||||
public:
|
||||
explicit Thread() = default;
|
||||
~Thread() {
|
||||
Stop();
|
||||
}
|
||||
|
||||
void Run(std::function<void(std::stop_token)>&& func) {
|
||||
this->func = std::move(func);
|
||||
PthreadAttrT attr{};
|
||||
posix_pthread_attr_init(&attr);
|
||||
posix_pthread_create(&thread, &attr, RunWrapper, this);
|
||||
posix_pthread_attr_destroy(&attr);
|
||||
}
|
||||
|
||||
void Join() {
|
||||
if (thread) {
|
||||
posix_pthread_join(thread, nullptr);
|
||||
thread = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool Joinable() const {
|
||||
return thread != nullptr;
|
||||
}
|
||||
|
||||
void Stop() {
|
||||
if (Joinable()) {
|
||||
stop.request_stop();
|
||||
Join();
|
||||
}
|
||||
}
|
||||
|
||||
static void* PS4_SYSV_ABI RunWrapper(void* arg) {
|
||||
Thread* thr = (Thread*)arg;
|
||||
thr->func(thr->stop.get_token());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
PthreadT thread{};
|
||||
std::function<void(std::stop_token)> func;
|
||||
std::stop_source stop;
|
||||
};
|
||||
|
||||
} // namespace Libraries::Kernel
|
360
src/core/libraries/kernel/threads/condvar.cpp
Normal file
360
src/core/libraries/kernel/threads/condvar.cpp
Normal file
|
@ -0,0 +1,360 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
#include "common/assert.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/kernel.h"
|
||||
#include "core/libraries/kernel/threads/pthread.h"
|
||||
#include "core/libraries/kernel/threads/sleepq.h"
|
||||
#include "core/libraries/libs.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
static std::mutex CondStaticLock;
|
||||
|
||||
#define THR_COND_INITIALIZER ((PthreadCond*)NULL)
|
||||
#define THR_COND_DESTROYED ((PthreadCond*)1)
|
||||
|
||||
static constexpr PthreadCondAttr PhreadCondattrDefault = {
|
||||
.c_pshared = 0,
|
||||
.c_clockid = ClockId::Realtime,
|
||||
};
|
||||
|
||||
static int CondInit(PthreadCondT* cond, const PthreadCondAttrT* cond_attr, const char* name) {
|
||||
auto* cvp = new PthreadCond{};
|
||||
if (cvp == nullptr) {
|
||||
return POSIX_ENOMEM;
|
||||
}
|
||||
|
||||
if (name) {
|
||||
cvp->name = name;
|
||||
} else {
|
||||
static int CondId = 0;
|
||||
cvp->name = fmt::format("Cond{}", CondId++);
|
||||
}
|
||||
|
||||
if (cond_attr == nullptr || *cond_attr == nullptr) {
|
||||
cvp->clock_id = ClockId::Realtime;
|
||||
} else {
|
||||
// if ((*cond_attr)->c_pshared) {
|
||||
// cvp->flags |= USYNC_PROCESS_SHARED;
|
||||
// }
|
||||
cvp->clock_id = (*cond_attr)->c_clockid;
|
||||
}
|
||||
*cond = cvp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int InitStatic(Pthread* thread, PthreadCondT* cond) {
|
||||
std::scoped_lock lk{CondStaticLock};
|
||||
if (*cond == nullptr) {
|
||||
return CondInit(cond, nullptr, nullptr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define CHECK_AND_INIT_COND \
|
||||
if (cvp = *cond; cvp <= THR_COND_DESTROYED) [[unlikely]] { \
|
||||
if (cvp == THR_COND_INITIALIZER) { \
|
||||
int ret; \
|
||||
ret = InitStatic(g_curthread, cond); \
|
||||
if (ret) \
|
||||
return (ret); \
|
||||
} else if (cvp == THR_COND_DESTROYED) { \
|
||||
return POSIX_EINVAL; \
|
||||
} \
|
||||
cvp = *cond; \
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_cond_init(PthreadCondT* cond, const PthreadCondAttrT* cond_attr) {
|
||||
*cond = nullptr;
|
||||
return CondInit(cond, cond_attr, nullptr);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadCondInit(PthreadCondT* cond, const PthreadCondAttrT* cond_attr,
|
||||
const char* name) {
|
||||
*cond = nullptr;
|
||||
return CondInit(cond, cond_attr, name);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_cond_destroy(PthreadCondT* cond) {
|
||||
PthreadCond* cvp = *cond;
|
||||
if (cvp == THR_COND_INITIALIZER) {
|
||||
return 0;
|
||||
}
|
||||
if (cvp == THR_COND_DESTROYED) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
cvp = *cond;
|
||||
*cond = THR_COND_DESTROYED;
|
||||
delete cvp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PthreadCond::Wait(PthreadMutexT* mutex, const OrbisKernelTimespec* abstime, u64 usec) {
|
||||
PthreadMutex* mp = *mutex;
|
||||
if (int error = mp->IsOwned(g_curthread); error != 0) {
|
||||
return error;
|
||||
}
|
||||
|
||||
Pthread* curthread = g_curthread;
|
||||
ASSERT_MSG(curthread->wchan == nullptr, "Thread was already on queue.");
|
||||
// _thr_testcancel(curthread);
|
||||
SleepqLock(this);
|
||||
|
||||
/*
|
||||
* set __has_user_waiters before unlocking mutex, this allows
|
||||
* us to check it without locking in pthread_cond_signal().
|
||||
*/
|
||||
has_user_waiters = 1;
|
||||
curthread->will_sleep = 1;
|
||||
|
||||
int recurse;
|
||||
mp->CvUnlock(&recurse);
|
||||
|
||||
curthread->mutex_obj = mp;
|
||||
SleepqAdd(this, curthread);
|
||||
|
||||
int error = 0;
|
||||
for (;;) {
|
||||
void(curthread->wake_sema.try_acquire());
|
||||
SleepqUnlock(this);
|
||||
|
||||
//_thr_cancel_enter2(curthread, 0);
|
||||
int error = curthread->Sleep(abstime, usec) ? 0 : POSIX_ETIMEDOUT;
|
||||
//_thr_cancel_leave(curthread, 0);
|
||||
|
||||
SleepqLock(this);
|
||||
if (curthread->wchan == nullptr) {
|
||||
error = 0;
|
||||
break;
|
||||
} else if (curthread->ShouldCancel()) {
|
||||
SleepQueue* sq = SleepqLookup(this);
|
||||
has_user_waiters = SleepqRemove(sq, curthread);
|
||||
SleepqUnlock(this);
|
||||
curthread->mutex_obj = nullptr;
|
||||
mp->CvLock(recurse);
|
||||
return 0;
|
||||
} else if (error == POSIX_ETIMEDOUT) {
|
||||
SleepQueue* sq = SleepqLookup(this);
|
||||
has_user_waiters = SleepqRemove(sq, curthread);
|
||||
break;
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
SleepqUnlock(this);
|
||||
curthread->mutex_obj = nullptr;
|
||||
mp->CvLock(recurse);
|
||||
return error;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_cond_wait(PthreadCondT* cond, PthreadMutexT* mutex) {
|
||||
PthreadCond* cvp{};
|
||||
CHECK_AND_INIT_COND
|
||||
return cvp->Wait(mutex, nullptr);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_cond_timedwait(PthreadCondT* cond, PthreadMutexT* mutex,
|
||||
const OrbisKernelTimespec* abstime) {
|
||||
if (abstime == nullptr || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
|
||||
abstime->tv_nsec >= 1000000000) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
|
||||
PthreadCond* cvp{};
|
||||
CHECK_AND_INIT_COND
|
||||
return cvp->Wait(mutex, abstime);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_cond_reltimedwait_np(PthreadCondT* cond, PthreadMutexT* mutex,
|
||||
u64 usec) {
|
||||
PthreadCond* cvp{};
|
||||
CHECK_AND_INIT_COND
|
||||
return cvp->Wait(mutex, THR_RELTIME, usec);
|
||||
}
|
||||
|
||||
int PthreadCond::Signal() {
|
||||
Pthread* curthread = g_curthread;
|
||||
|
||||
SleepqLock(this);
|
||||
SleepQueue* sq = SleepqLookup(this);
|
||||
if (sq == nullptr) {
|
||||
SleepqUnlock(this);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Pthread* td = sq->sq_blocked.front();
|
||||
PthreadMutex* mp = td->mutex_obj;
|
||||
has_user_waiters = SleepqRemove(sq, td);
|
||||
|
||||
std::binary_semaphore* waddr = nullptr;
|
||||
if (mp->m_owner == curthread) {
|
||||
if (curthread->nwaiter_defer >= Pthread::MaxDeferWaiters) {
|
||||
curthread->WakeAll();
|
||||
}
|
||||
curthread->defer_waiters[curthread->nwaiter_defer++] = &td->wake_sema;
|
||||
mp->m_flags |= PthreadMutexFlags::Defered;
|
||||
} else {
|
||||
waddr = &td->wake_sema;
|
||||
}
|
||||
|
||||
SleepqUnlock(this);
|
||||
if (waddr != nullptr) {
|
||||
waddr->release();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct BroadcastArg {
|
||||
Pthread* curthread;
|
||||
std::binary_semaphore* waddrs[Pthread::MaxDeferWaiters];
|
||||
int count;
|
||||
};
|
||||
|
||||
int PthreadCond::Broadcast() {
|
||||
BroadcastArg ba;
|
||||
ba.curthread = g_curthread;
|
||||
ba.count = 0;
|
||||
|
||||
const auto drop_cb = [](Pthread* td, void* arg) {
|
||||
BroadcastArg* ba = reinterpret_cast<BroadcastArg*>(arg);
|
||||
Pthread* curthread = ba->curthread;
|
||||
PthreadMutex* mp = td->mutex_obj;
|
||||
|
||||
if (mp->m_owner == curthread) {
|
||||
if (curthread->nwaiter_defer >= Pthread::MaxDeferWaiters) {
|
||||
curthread->WakeAll();
|
||||
}
|
||||
curthread->defer_waiters[curthread->nwaiter_defer++] = &td->wake_sema;
|
||||
mp->m_flags |= PthreadMutexFlags::Defered;
|
||||
} else {
|
||||
if (ba->count >= Pthread::MaxDeferWaiters) {
|
||||
for (int i = 0; i < ba->count; i++) {
|
||||
ba->waddrs[i]->release();
|
||||
}
|
||||
ba->count = 0;
|
||||
}
|
||||
ba->waddrs[ba->count++] = &td->wake_sema;
|
||||
}
|
||||
};
|
||||
|
||||
SleepqLock(this);
|
||||
SleepQueue* sq = SleepqLookup(this);
|
||||
if (sq == nullptr) {
|
||||
SleepqUnlock(this);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SleepqDrop(sq, drop_cb, &ba);
|
||||
has_user_waiters = 0;
|
||||
SleepqUnlock(this);
|
||||
|
||||
for (int i = 0; i < ba.count; i++) {
|
||||
ba.waddrs[i]->release();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_cond_signal(PthreadCondT* cond) {
|
||||
PthreadCond* cvp{};
|
||||
CHECK_AND_INIT_COND
|
||||
return cvp->Signal();
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_cond_broadcast(PthreadCondT* cond) {
|
||||
PthreadCond* cvp{};
|
||||
CHECK_AND_INIT_COND
|
||||
cvp->Broadcast();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_condattr_init(PthreadCondAttrT* attr) {
|
||||
PthreadCondAttr* pattr = new PthreadCondAttr{};
|
||||
if (pattr == nullptr) {
|
||||
return POSIX_ENOMEM;
|
||||
}
|
||||
memcpy(pattr, &PhreadCondattrDefault, sizeof(PthreadCondAttr));
|
||||
*attr = pattr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_condattr_destroy(PthreadCondAttrT* attr) {
|
||||
if (attr == nullptr || *attr == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
delete *attr;
|
||||
*attr = nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_condattr_getclock(const PthreadCondAttrT* attr, ClockId* clock_id) {
|
||||
if (attr == nullptr || *attr == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
*clock_id = static_cast<ClockId>((*attr)->c_clockid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_condattr_setclock(PthreadCondAttrT* attr, ClockId clock_id) {
|
||||
if (attr == nullptr || *attr == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
if (clock_id != ClockId::Realtime && clock_id != ClockId::Virtual &&
|
||||
clock_id != ClockId::Prof && clock_id != ClockId::Monotonic) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
(*attr)->c_clockid = clock_id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_condattr_getpshared(const PthreadCondAttrT* attr, int* pshared) {
|
||||
if (attr == nullptr || *attr == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
*pshared = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_condattr_setpshared(PthreadCondAttrT* attr, int pshared) {
|
||||
if (attr == nullptr || *attr == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
if (pshared != 0) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RegisterCond(Core::Loader::SymbolsResolver* sym) {
|
||||
// Posix
|
||||
LIB_FUNCTION("mKoTx03HRWA", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_condattr_init);
|
||||
LIB_FUNCTION("0TyVk4MSLt0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_init);
|
||||
LIB_FUNCTION("2MOy+rUfuhQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_signal);
|
||||
LIB_FUNCTION("RXXqi4CtF8w", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_destroy);
|
||||
LIB_FUNCTION("Op8TBGY5KHg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_wait);
|
||||
LIB_FUNCTION("27bAgiJmOh0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_timedwait);
|
||||
LIB_FUNCTION("mkx2fVhNMsg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_broadcast);
|
||||
|
||||
// Posix-Kernel
|
||||
LIB_FUNCTION("Op8TBGY5KHg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_cond_wait);
|
||||
LIB_FUNCTION("mkx2fVhNMsg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_cond_broadcast);
|
||||
|
||||
// Orbis
|
||||
LIB_FUNCTION("2Tb92quprl0", "libkernel", 1, "libkernel", 1, 1, ORBIS(scePthreadCondInit));
|
||||
LIB_FUNCTION("m5-2bsNfv7s", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_condattr_init));
|
||||
LIB_FUNCTION("JGgj7Uvrl+A", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_cond_broadcast));
|
||||
LIB_FUNCTION("WKAXJ4XBPQ4", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_cond_wait));
|
||||
LIB_FUNCTION("waPcxYiR3WA", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_condattr_destroy));
|
||||
LIB_FUNCTION("kDh-NfxgMtE", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_cond_signal));
|
||||
LIB_FUNCTION("BmMjYxmew1w", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_cond_reltimedwait_np));
|
||||
LIB_FUNCTION("g+PZd2hiacg", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_cond_destroy));
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
|
@ -1,13 +1,158 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "event_flag.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
constexpr int ORBIS_KERNEL_EVF_ATTR_TH_FIFO = 0x01;
|
||||
constexpr int ORBIS_KERNEL_EVF_ATTR_TH_PRIO = 0x02;
|
||||
constexpr int ORBIS_KERNEL_EVF_ATTR_SINGLE = 0x10;
|
||||
constexpr int ORBIS_KERNEL_EVF_ATTR_MULTI = 0x20;
|
||||
|
||||
constexpr int ORBIS_KERNEL_EVF_WAITMODE_AND = 0x01;
|
||||
constexpr int ORBIS_KERNEL_EVF_WAITMODE_OR = 0x02;
|
||||
constexpr int ORBIS_KERNEL_EVF_WAITMODE_CLEAR_ALL = 0x10;
|
||||
constexpr int ORBIS_KERNEL_EVF_WAITMODE_CLEAR_PAT = 0x20;
|
||||
|
||||
class EventFlagInternal {
|
||||
public:
|
||||
enum class ClearMode { None, All, Bits };
|
||||
enum class WaitMode { And, Or };
|
||||
enum class ThreadMode { Single, Multi };
|
||||
enum class QueueMode { Fifo, ThreadPrio };
|
||||
|
||||
EventFlagInternal(const std::string& name, ThreadMode thread_mode, QueueMode queue_mode,
|
||||
uint64_t bits)
|
||||
: m_name(name), m_thread_mode(thread_mode), m_queue_mode(queue_mode), m_bits(bits){};
|
||||
|
||||
int Wait(u64 bits, WaitMode wait_mode, ClearMode clear_mode, u64* result, u32* ptr_micros) {
|
||||
std::unique_lock lock{m_mutex};
|
||||
|
||||
uint32_t micros = 0;
|
||||
bool infinitely = true;
|
||||
if (ptr_micros != nullptr) {
|
||||
micros = *ptr_micros;
|
||||
infinitely = false;
|
||||
}
|
||||
|
||||
if (m_thread_mode == ThreadMode::Single && m_waiting_threads > 0) {
|
||||
return ORBIS_KERNEL_ERROR_EPERM;
|
||||
}
|
||||
|
||||
auto const start = std::chrono::system_clock::now();
|
||||
m_waiting_threads++;
|
||||
auto waitFunc = [this, wait_mode, bits] {
|
||||
return (m_status == Status::Canceled || m_status == Status::Deleted ||
|
||||
(wait_mode == WaitMode::And && (m_bits & bits) == bits) ||
|
||||
(wait_mode == WaitMode::Or && (m_bits & bits) != 0));
|
||||
};
|
||||
|
||||
if (infinitely) {
|
||||
m_cond_var.wait(lock, waitFunc);
|
||||
} else {
|
||||
if (!m_cond_var.wait_for(lock, std::chrono::microseconds(micros), waitFunc)) {
|
||||
if (result != nullptr) {
|
||||
*result = m_bits;
|
||||
}
|
||||
*ptr_micros = 0;
|
||||
--m_waiting_threads;
|
||||
return ORBIS_KERNEL_ERROR_ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
--m_waiting_threads;
|
||||
if (result != nullptr) {
|
||||
*result = m_bits;
|
||||
}
|
||||
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::system_clock::now() - start)
|
||||
.count();
|
||||
if (result != nullptr) {
|
||||
*result = m_bits;
|
||||
}
|
||||
|
||||
if (ptr_micros != nullptr) {
|
||||
*ptr_micros = (elapsed >= micros ? 0 : micros - elapsed);
|
||||
}
|
||||
|
||||
if (m_status == Status::Canceled) {
|
||||
return ORBIS_KERNEL_ERROR_ECANCELED;
|
||||
} else if (m_status == Status::Deleted) {
|
||||
return ORBIS_KERNEL_ERROR_EACCES;
|
||||
}
|
||||
|
||||
if (clear_mode == ClearMode::All) {
|
||||
m_bits = 0;
|
||||
} else if (clear_mode == ClearMode::Bits) {
|
||||
m_bits &= ~bits;
|
||||
}
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int Poll(u64 bits, WaitMode wait_mode, ClearMode clear_mode, u64* result) {
|
||||
u32 micros = 0;
|
||||
auto ret = Wait(bits, wait_mode, clear_mode, result, µs);
|
||||
if (ret == ORBIS_KERNEL_ERROR_ETIMEDOUT) {
|
||||
// Poll returns EBUSY instead.
|
||||
ret = ORBIS_KERNEL_ERROR_EBUSY;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Set(u64 bits) {
|
||||
std::unique_lock lock{m_mutex};
|
||||
|
||||
while (m_status != Status::Set) {
|
||||
m_mutex.unlock();
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(10));
|
||||
m_mutex.lock();
|
||||
}
|
||||
|
||||
m_bits |= bits;
|
||||
|
||||
m_cond_var.notify_all();
|
||||
}
|
||||
|
||||
void Clear(u64 bits) {
|
||||
std::unique_lock lock{m_mutex};
|
||||
while (m_status != Status::Set) {
|
||||
m_mutex.unlock();
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(10));
|
||||
m_mutex.lock();
|
||||
}
|
||||
|
||||
m_bits &= bits;
|
||||
}
|
||||
|
||||
private:
|
||||
enum class Status { Set, Canceled, Deleted };
|
||||
|
||||
std::mutex m_mutex;
|
||||
std::condition_variable m_cond_var;
|
||||
Status m_status = Status::Set;
|
||||
int m_waiting_threads = 0;
|
||||
std::string m_name;
|
||||
ThreadMode m_thread_mode = ThreadMode::Single;
|
||||
QueueMode m_queue_mode = QueueMode::Fifo;
|
||||
u64 m_bits = 0;
|
||||
};
|
||||
|
||||
using OrbisKernelUseconds = u32;
|
||||
using OrbisKernelEventFlag = EventFlagInternal*;
|
||||
|
||||
struct OrbisKernelEventFlagOptParam {
|
||||
size_t size;
|
||||
};
|
||||
|
||||
int PS4_SYSV_ABI sceKernelCreateEventFlag(OrbisKernelEventFlag* ef, const char* pName, u32 attr,
|
||||
u64 initPattern,
|
||||
const OrbisKernelEventFlagOptParam* pOptParam) {
|
||||
|
@ -25,9 +170,8 @@ int PS4_SYSV_ABI sceKernelCreateEventFlag(OrbisKernelEventFlag* ef, const char*
|
|||
return ORBIS_KERNEL_ERROR_ENAMETOOLONG;
|
||||
}
|
||||
|
||||
EventFlagInternal::ThreadMode thread_mode = EventFlagInternal::ThreadMode::Single;
|
||||
EventFlagInternal::QueueMode queue_mode = EventFlagInternal::QueueMode::Fifo;
|
||||
|
||||
auto thread_mode = EventFlagInternal::ThreadMode::Single;
|
||||
auto queue_mode = EventFlagInternal::QueueMode::Fifo;
|
||||
switch (attr & 0xfu) {
|
||||
case 0x01:
|
||||
queue_mode = EventFlagInternal::QueueMode::Fifo;
|
||||
|
@ -61,6 +205,7 @@ int PS4_SYSV_ABI sceKernelCreateEventFlag(OrbisKernelEventFlag* ef, const char*
|
|||
*ef = new EventFlagInternal(std::string(pName), thread_mode, queue_mode, initPattern);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelDeleteEventFlag(OrbisKernelEventFlag ef) {
|
||||
if (ef == nullptr) {
|
||||
return ORBIS_KERNEL_ERROR_ESRCH;
|
||||
|
@ -69,24 +214,29 @@ int PS4_SYSV_ABI sceKernelDeleteEventFlag(OrbisKernelEventFlag ef) {
|
|||
delete ef;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelOpenEventFlag() {
|
||||
LOG_ERROR(Kernel_Event, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelCloseEventFlag() {
|
||||
LOG_ERROR(Kernel_Event, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelClearEventFlag(OrbisKernelEventFlag ef, u64 bitPattern) {
|
||||
LOG_DEBUG(Kernel_Event, "called");
|
||||
ef->Clear(bitPattern);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelCancelEventFlag(OrbisKernelEventFlag ef, u64 setPattern,
|
||||
int* pNumWaitThreads) {
|
||||
LOG_ERROR(Kernel_Event, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelSetEventFlag(OrbisKernelEventFlag ef, u64 bitPattern) {
|
||||
LOG_TRACE(Kernel_Event, "called");
|
||||
if (ef == nullptr) {
|
||||
|
@ -95,6 +245,7 @@ int PS4_SYSV_ABI sceKernelSetEventFlag(OrbisKernelEventFlag ef, u64 bitPattern)
|
|||
ef->Set(bitPattern);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelPollEventFlag(OrbisKernelEventFlag ef, u64 bitPattern, u32 waitMode,
|
||||
u64* pResultPat) {
|
||||
LOG_DEBUG(Kernel_Event, "called bitPattern = {:#x} waitMode = {:#x}", bitPattern, waitMode);
|
||||
|
@ -107,9 +258,8 @@ int PS4_SYSV_ABI sceKernelPollEventFlag(OrbisKernelEventFlag ef, u64 bitPattern,
|
|||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
EventFlagInternal::WaitMode wait = EventFlagInternal::WaitMode::And;
|
||||
EventFlagInternal::ClearMode clear = EventFlagInternal::ClearMode::None;
|
||||
|
||||
auto wait = EventFlagInternal::WaitMode::And;
|
||||
auto clear = EventFlagInternal::ClearMode::None;
|
||||
switch (waitMode & 0xf) {
|
||||
case 0x01:
|
||||
wait = EventFlagInternal::WaitMode::And;
|
||||
|
@ -154,9 +304,8 @@ int PS4_SYSV_ABI sceKernelWaitEventFlag(OrbisKernelEventFlag ef, u64 bitPattern,
|
|||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
EventFlagInternal::WaitMode wait = EventFlagInternal::WaitMode::And;
|
||||
EventFlagInternal::ClearMode clear = EventFlagInternal::ClearMode::None;
|
||||
|
||||
auto wait = EventFlagInternal::WaitMode::And;
|
||||
auto clear = EventFlagInternal::ClearMode::None;
|
||||
switch (waitMode & 0xf) {
|
||||
case 0x01:
|
||||
wait = EventFlagInternal::WaitMode::And;
|
||||
|
@ -190,6 +339,7 @@ int PS4_SYSV_ABI sceKernelWaitEventFlag(OrbisKernelEventFlag ef, u64 bitPattern,
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
void RegisterKernelEventFlag(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("PZku4ZrXJqg", "libkernel", 1, "libkernel", 1, 1, sceKernelCancelEventFlag);
|
||||
LIB_FUNCTION("7uhBFWRAS60", "libkernel", 1, "libkernel", 1, 1, sceKernelClearEventFlag);
|
||||
|
@ -201,4 +351,5 @@ void RegisterKernelEventFlag(Core::Loader::SymbolsResolver* sym) {
|
|||
LIB_FUNCTION("IOnSvHzqu6A", "libkernel", 1, "libkernel", 1, 1, sceKernelSetEventFlag);
|
||||
LIB_FUNCTION("JTvBflhYazQ", "libkernel", 1, "libkernel", 1, 1, sceKernelWaitEventFlag);
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
131
src/core/libraries/kernel/threads/exception.cpp
Normal file
131
src/core/libraries/kernel/threads/exception.cpp
Normal file
|
@ -0,0 +1,131 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/libraries/kernel/threads/exception.h"
|
||||
#include "core/libraries/kernel/threads/pthread.h"
|
||||
#include "core/libraries/libs.h"
|
||||
|
||||
#ifdef _WIN64
|
||||
#else
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
static std::array<SceKernelExceptionHandler, 32> Handlers{};
|
||||
|
||||
#ifndef _WIN64
|
||||
void SigactionHandler(int signum, siginfo_t* inf, ucontext_t* raw_context) {
|
||||
const auto handler = Handlers[POSIX_SIGUSR1];
|
||||
if (handler) {
|
||||
auto ctx = Ucontext{};
|
||||
#ifdef __APPLE__
|
||||
auto& regs = raw_context->uc_mcontext->__ss;
|
||||
ctx.uc_mcontext.mc_r8 = regs.__r8;
|
||||
ctx.uc_mcontext.mc_r9 = regs.__r9;
|
||||
ctx.uc_mcontext.mc_r10 = regs.__r10;
|
||||
ctx.uc_mcontext.mc_r11 = regs.__r11;
|
||||
ctx.uc_mcontext.mc_r12 = regs.__r12;
|
||||
ctx.uc_mcontext.mc_r13 = regs.__r13;
|
||||
ctx.uc_mcontext.mc_r14 = regs.__r14;
|
||||
ctx.uc_mcontext.mc_r15 = regs.__r15;
|
||||
ctx.uc_mcontext.mc_rdi = regs.__rdi;
|
||||
ctx.uc_mcontext.mc_rsi = regs.__rsi;
|
||||
ctx.uc_mcontext.mc_rbp = regs.__rbp;
|
||||
ctx.uc_mcontext.mc_rbx = regs.__rbx;
|
||||
ctx.uc_mcontext.mc_rdx = regs.__rdx;
|
||||
ctx.uc_mcontext.mc_rax = regs.__rax;
|
||||
ctx.uc_mcontext.mc_rcx = regs.__rcx;
|
||||
ctx.uc_mcontext.mc_rsp = regs.__rsp;
|
||||
ctx.uc_mcontext.mc_fs = regs.__fs;
|
||||
ctx.uc_mcontext.mc_gs = regs.__gs;
|
||||
#else
|
||||
auto& regs = raw_context->uc_mcontext.gregs;
|
||||
ctx.uc_mcontext.mc_r8 = regs[REG_R8];
|
||||
ctx.uc_mcontext.mc_r9 = regs[REG_R9];
|
||||
ctx.uc_mcontext.mc_r10 = regs[REG_R10];
|
||||
ctx.uc_mcontext.mc_r11 = regs[REG_R11];
|
||||
ctx.uc_mcontext.mc_r12 = regs[REG_R12];
|
||||
ctx.uc_mcontext.mc_r13 = regs[REG_R13];
|
||||
ctx.uc_mcontext.mc_r14 = regs[REG_R14];
|
||||
ctx.uc_mcontext.mc_r15 = regs[REG_R15];
|
||||
ctx.uc_mcontext.mc_rdi = regs[REG_RDI];
|
||||
ctx.uc_mcontext.mc_rsi = regs[REG_RSI];
|
||||
ctx.uc_mcontext.mc_rbp = regs[REG_RBP];
|
||||
ctx.uc_mcontext.mc_rbx = regs[REG_RBX];
|
||||
ctx.uc_mcontext.mc_rdx = regs[REG_RDX];
|
||||
ctx.uc_mcontext.mc_rax = regs[REG_RAX];
|
||||
ctx.uc_mcontext.mc_rcx = regs[REG_RCX];
|
||||
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;
|
||||
#endif
|
||||
handler(POSIX_SIGUSR1, &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;
|
||||
}
|
||||
ASSERT_MSG(!Handlers[POSIX_SIGUSR1], "Invalid parameters");
|
||||
Handlers[POSIX_SIGUSR1] = handler;
|
||||
#ifdef _WIN64
|
||||
UNREACHABLE_MSG("Missing exception implementation");
|
||||
#else
|
||||
struct sigaction act = {};
|
||||
act.sa_flags = SA_SIGINFO | SA_RESTART;
|
||||
act.sa_sigaction = reinterpret_cast<decltype(act.sa_sigaction)>(SigactionHandler);
|
||||
sigaction(SIGUSR2, &act, nullptr);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
ASSERT_MSG(Handlers[POSIX_SIGUSR1], "Invalid parameters");
|
||||
Handlers[POSIX_SIGUSR1] = nullptr;
|
||||
#ifdef _WIN64
|
||||
UNREACHABLE_MSG("Missing exception implementation");
|
||||
#else
|
||||
struct sigaction act = {};
|
||||
act.sa_flags = SA_SIGINFO | SA_RESTART;
|
||||
act.sa_sigaction = nullptr;
|
||||
sigaction(SIGUSR2, &act, nullptr);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelRaiseException(PthreadT thread, int signum) {
|
||||
LOG_ERROR(Lib_Kernel, "Raising exception");
|
||||
ASSERT_MSG(signum == POSIX_SIGUSR1, "Attempting to raise non user defined signal!");
|
||||
#ifdef _WIN64
|
||||
UNREACHABLE_MSG("Missing exception implementation");
|
||||
#else
|
||||
pthread_t pthr = *reinterpret_cast<pthread_t*>(thread->native_thr.GetHandle());
|
||||
pthread_kill(pthr, SIGUSR2);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelDebugRaiseException() {
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RegisterException(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("il03nluKfMk", "libkernel_unity", 1, "libkernel", 1, 1, sceKernelRaiseException);
|
||||
LIB_FUNCTION("WkwEd3N7w0Y", "libkernel_unity", 1, "libkernel", 1, 1,
|
||||
sceKernelInstallExceptionHandler);
|
||||
LIB_FUNCTION("Qhv5ARAoOEc", "libkernel_unity", 1, "libkernel", 1, 1,
|
||||
sceKernelRemoveExceptionHandler)
|
||||
LIB_FUNCTION("OMDRKKAZ8I4", "libkernel", 1, "libkernel", 1, 1, sceKernelDebugRaiseException);
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
86
src/core/libraries/kernel/threads/exception.h
Normal file
86
src/core/libraries/kernel/threads/exception.h
Normal file
|
@ -0,0 +1,86 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
using SceKernelExceptionHandler = PS4_SYSV_ABI void (*)(int, void*);
|
||||
|
||||
constexpr int POSIX_SIGSEGV = 11;
|
||||
constexpr int POSIX_SIGUSR1 = 30;
|
||||
|
||||
struct Mcontext {
|
||||
u64 mc_onstack;
|
||||
u64 mc_rdi;
|
||||
u64 mc_rsi;
|
||||
u64 mc_rdx;
|
||||
u64 mc_rcx;
|
||||
u64 mc_r8;
|
||||
u64 mc_r9;
|
||||
u64 mc_rax;
|
||||
u64 mc_rbx;
|
||||
u64 mc_rbp;
|
||||
u64 mc_r10;
|
||||
u64 mc_r11;
|
||||
u64 mc_r12;
|
||||
u64 mc_r13;
|
||||
u64 mc_r14;
|
||||
u64 mc_r15;
|
||||
int mc_trapno;
|
||||
u16 mc_fs;
|
||||
u16 mc_gs;
|
||||
u64 mc_addr;
|
||||
int mc_flags;
|
||||
u16 mc_es;
|
||||
u16 mc_ds;
|
||||
u64 mc_err;
|
||||
u64 mc_rip;
|
||||
u64 mc_cs;
|
||||
u64 mc_rflags;
|
||||
u64 mc_rsp;
|
||||
u64 mc_ss;
|
||||
u64 mc_len;
|
||||
u64 mc_fpformat;
|
||||
u64 mc_ownedfp;
|
||||
u64 mc_lbrfrom;
|
||||
u64 mc_lbrto;
|
||||
u64 mc_aux1;
|
||||
u64 mc_aux2;
|
||||
u64 mc_fpstate[104];
|
||||
u64 mc_fsbase;
|
||||
u64 mc_gsbase;
|
||||
u64 mc_spare[6];
|
||||
};
|
||||
|
||||
struct Stack {
|
||||
void* ss_sp;
|
||||
std::size_t ss_size;
|
||||
int ss_flags;
|
||||
int _align;
|
||||
};
|
||||
|
||||
struct Sigset {
|
||||
u64 bits[2];
|
||||
};
|
||||
|
||||
struct Ucontext {
|
||||
struct Sigset uc_sigmask;
|
||||
int field1_0x10[12];
|
||||
struct Mcontext uc_mcontext;
|
||||
struct Ucontext* uc_link;
|
||||
struct Stack uc_stack;
|
||||
int uc_flags;
|
||||
int __spare[4];
|
||||
int field7_0x4f4[3];
|
||||
};
|
||||
|
||||
void RegisterException(Core::Loader::SymbolsResolver* sym);
|
||||
|
||||
} // namespace Libraries::Kernel
|
|
@ -1,50 +0,0 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/thread_management.h"
|
||||
#include "core/libraries/libs.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
int PS4_SYSV_ABI scePthreadKeyCreate(OrbisPthreadKey* key, PthreadKeyDestructor destructor) {
|
||||
if (key == nullptr) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
pthread_key_t thread_key;
|
||||
int result = pthread_key_create(&thread_key, nullptr);
|
||||
*key = static_cast<OrbisPthreadKey>(thread_key);
|
||||
|
||||
if (destructor) {
|
||||
auto thread = scePthreadSelf();
|
||||
thread->key_destructors.emplace_back(*key, destructor);
|
||||
}
|
||||
|
||||
if (result != 0) {
|
||||
LOG_ERROR(Kernel_Pthread, "scePthreadKeyCreate: error = {}", result);
|
||||
result += ORBIS_KERNEL_ERROR_UNKNOWN;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void* PS4_SYSV_ABI scePthreadGetspecific(OrbisPthreadKey key) {
|
||||
return pthread_getspecific(key);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadSetspecific(OrbisPthreadKey key, /* const*/ void* value) {
|
||||
int result = pthread_setspecific(key, value);
|
||||
if (result != 0) {
|
||||
LOG_ERROR(Kernel_Pthread, "scePthreadSetspecific: error = {}", result);
|
||||
result += ORBIS_KERNEL_ERROR_UNKNOWN;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void KeySymbolsRegister(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("geDaqgH9lTg", "libkernel", 1, "libkernel", 1, 1, scePthreadKeyCreate);
|
||||
LIB_FUNCTION("eoht7mQOCmo", "libkernel", 1, "libkernel", 1, 1, scePthreadGetspecific);
|
||||
LIB_FUNCTION("+BzXYkqYeLE", "libkernel", 1, "libkernel", 1, 1, scePthreadSetspecific);
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
468
src/core/libraries/kernel/threads/mutex.cpp
Normal file
468
src/core/libraries/kernel/threads/mutex.cpp
Normal file
|
@ -0,0 +1,468 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <thread>
|
||||
#include "common/assert.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/kernel.h"
|
||||
#include "core/libraries/kernel/threads/pthread.h"
|
||||
#include "core/libraries/libs.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
static constexpr u32 MUTEX_ADAPTIVE_SPINS = 2000;
|
||||
static std::mutex MutxStaticLock;
|
||||
|
||||
#define THR_MUTEX_INITIALIZER ((PthreadMutex*)NULL)
|
||||
#define THR_ADAPTIVE_MUTEX_INITIALIZER ((PthreadMutex*)1)
|
||||
#define THR_MUTEX_DESTROYED ((PthreadMutex*)2)
|
||||
|
||||
#define CPU_SPINWAIT __asm__ volatile("pause")
|
||||
|
||||
#define CHECK_AND_INIT_MUTEX \
|
||||
if (PthreadMutex* m = *mutex; m <= THR_MUTEX_DESTROYED) [[unlikely]] { \
|
||||
if (m == THR_MUTEX_DESTROYED) { \
|
||||
return POSIX_EINVAL; \
|
||||
} \
|
||||
if (s32 ret = InitStatic(g_curthread, mutex); ret) { \
|
||||
return ret; \
|
||||
} \
|
||||
m = *mutex; \
|
||||
}
|
||||
|
||||
static constexpr PthreadMutexAttr PthreadMutexattrDefault = {
|
||||
.m_type = PthreadMutexType::ErrorCheck, .m_protocol = PthreadMutexProt::None, .m_ceiling = 0};
|
||||
|
||||
static constexpr PthreadMutexAttr PthreadMutexattrAdaptiveDefault = {
|
||||
.m_type = PthreadMutexType::AdaptiveNp, .m_protocol = PthreadMutexProt::None, .m_ceiling = 0};
|
||||
|
||||
using CallocFun = void* (*)(size_t, size_t);
|
||||
|
||||
static int MutexInit(PthreadMutexT* mutex, const PthreadMutexAttr* mutex_attr, const char* name) {
|
||||
const PthreadMutexAttr* attr;
|
||||
if (mutex_attr == NULL) {
|
||||
attr = &PthreadMutexattrDefault;
|
||||
} else {
|
||||
attr = mutex_attr;
|
||||
if (attr->m_type < PthreadMutexType::ErrorCheck || attr->m_type >= PthreadMutexType::Max) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
if (attr->m_protocol > PthreadMutexProt::Protect) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
}
|
||||
auto* pmutex = new PthreadMutex{};
|
||||
if (pmutex == nullptr) {
|
||||
return POSIX_ENOMEM;
|
||||
}
|
||||
|
||||
if (name) {
|
||||
pmutex->name = name;
|
||||
} else {
|
||||
static int MutexId = 0;
|
||||
pmutex->name = fmt::format("Mutex{}", MutexId++);
|
||||
}
|
||||
|
||||
pmutex->m_flags = PthreadMutexFlags(attr->m_type);
|
||||
pmutex->m_owner = nullptr;
|
||||
pmutex->m_count = 0;
|
||||
pmutex->m_spinloops = 0;
|
||||
pmutex->m_yieldloops = 0;
|
||||
pmutex->m_protocol = attr->m_protocol;
|
||||
if (attr->m_type == PthreadMutexType::AdaptiveNp) {
|
||||
pmutex->m_spinloops = MUTEX_ADAPTIVE_SPINS;
|
||||
// pmutex->m_yieldloops = _thr_yieldloops;
|
||||
}
|
||||
|
||||
*mutex = pmutex;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int InitStatic(Pthread* thread, PthreadMutexT* mutex) {
|
||||
std::scoped_lock lk{MutxStaticLock};
|
||||
|
||||
if (*mutex == THR_MUTEX_INITIALIZER) {
|
||||
return MutexInit(mutex, &PthreadMutexattrDefault, nullptr);
|
||||
} else if (*mutex == THR_ADAPTIVE_MUTEX_INITIALIZER) {
|
||||
return MutexInit(mutex, &PthreadMutexattrAdaptiveDefault, nullptr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int 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,
|
||||
const char* name) {
|
||||
return MutexInit(mutex, mutex_attr ? *mutex_attr : nullptr, name);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutex_destroy(PthreadMutexT* mutex) {
|
||||
PthreadMutexT m = *mutex;
|
||||
if (m < THR_MUTEX_DESTROYED) {
|
||||
return 0;
|
||||
}
|
||||
if (m == THR_MUTEX_DESTROYED) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
if (m->m_owner != nullptr) {
|
||||
return POSIX_EBUSY;
|
||||
}
|
||||
*mutex = THR_MUTEX_DESTROYED;
|
||||
delete m;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PthreadMutex::SelfTryLock() {
|
||||
switch (Type()) {
|
||||
case PthreadMutexType::ErrorCheck:
|
||||
case PthreadMutexType::Normal:
|
||||
return POSIX_EBUSY;
|
||||
case PthreadMutexType::Recursive: {
|
||||
/* Increment the lock count: */
|
||||
if (m_count + 1 > 0) {
|
||||
m_count++;
|
||||
return 0;
|
||||
}
|
||||
return POSIX_EAGAIN;
|
||||
}
|
||||
default:
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int PthreadMutex::SelfLock(const OrbisKernelTimespec* abstime, u64 usec) {
|
||||
const auto DoSleep = [&] {
|
||||
if (abstime == THR_RELTIME) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(usec));
|
||||
return POSIX_ETIMEDOUT;
|
||||
} else {
|
||||
if (abstime->tv_sec < 0 || abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) {
|
||||
return POSIX_EINVAL;
|
||||
} else {
|
||||
std::this_thread::sleep_until(abstime->TimePoint());
|
||||
return POSIX_ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
};
|
||||
switch (Type()) {
|
||||
case PthreadMutexType::ErrorCheck:
|
||||
case PthreadMutexType::AdaptiveNp: {
|
||||
if (abstime) {
|
||||
return DoSleep();
|
||||
}
|
||||
/*
|
||||
* POSIX specifies that mutexes should return
|
||||
* EDEADLK if a recursive lock is detected.
|
||||
*/
|
||||
return POSIX_EDEADLK;
|
||||
}
|
||||
case PthreadMutexType::Normal: {
|
||||
/*
|
||||
* What SS2 define as a 'normal' mutex. Intentionally
|
||||
* deadlock on attempts to get a lock you already own.
|
||||
*/
|
||||
if (abstime) {
|
||||
return DoSleep();
|
||||
}
|
||||
UNREACHABLE_MSG("Mutex deadlock occured");
|
||||
return 0;
|
||||
}
|
||||
case PthreadMutexType::Recursive: {
|
||||
/* Increment the lock count: */
|
||||
if (m_count + 1 > 0) {
|
||||
m_count++;
|
||||
return 0;
|
||||
}
|
||||
return POSIX_EAGAIN;
|
||||
}
|
||||
default:
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int PthreadMutex::Lock(const OrbisKernelTimespec* abstime, u64 usec) {
|
||||
Pthread* curthread = g_curthread;
|
||||
if (m_owner == curthread) {
|
||||
return SelfLock(abstime, usec);
|
||||
}
|
||||
|
||||
/*
|
||||
* For adaptive mutexes, spin for a bit in the expectation
|
||||
* that if the application requests this mutex type then
|
||||
* the lock is likely to be released quickly and it is
|
||||
* faster than entering the kernel
|
||||
*/
|
||||
if (m_protocol == PthreadMutexProt::None) [[likely]] {
|
||||
int count = m_spinloops;
|
||||
while (count--) {
|
||||
if (m_lock.try_lock()) {
|
||||
m_owner = curthread;
|
||||
return 0;
|
||||
}
|
||||
CPU_SPINWAIT;
|
||||
}
|
||||
|
||||
count = m_yieldloops;
|
||||
while (count--) {
|
||||
std::this_thread::yield();
|
||||
if (m_lock.try_lock()) {
|
||||
m_owner = curthread;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ret = 0;
|
||||
if (abstime == nullptr) {
|
||||
m_lock.lock();
|
||||
} else if (abstime != THR_RELTIME && (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000))
|
||||
[[unlikely]] {
|
||||
ret = POSIX_EINVAL;
|
||||
} else {
|
||||
if (THR_RELTIME) {
|
||||
ret = m_lock.try_lock_for(std::chrono::microseconds(usec)) ? 0 : POSIX_ETIMEDOUT;
|
||||
} else {
|
||||
ret = m_lock.try_lock_until(abstime->TimePoint()) ? 0 : POSIX_ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
if (ret == 0) {
|
||||
m_owner = curthread;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int PthreadMutex::TryLock() {
|
||||
Pthread* curthread = g_curthread;
|
||||
if (m_owner == curthread) {
|
||||
return SelfTryLock();
|
||||
}
|
||||
const int 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) {
|
||||
CHECK_AND_INIT_MUTEX
|
||||
return (*mutex)->TryLock();
|
||||
}
|
||||
|
||||
int 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,
|
||||
const OrbisKernelTimespec* abstime) {
|
||||
CHECK_AND_INIT_MUTEX
|
||||
UNREACHABLE();
|
||||
return (*mutex)->Lock(abstime);
|
||||
}
|
||||
|
||||
int 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() {
|
||||
Pthread* curthread = g_curthread;
|
||||
/*
|
||||
* Check if the running thread is not the owner of the mutex.
|
||||
*/
|
||||
if (m_owner != curthread) [[unlikely]] {
|
||||
return POSIX_EPERM;
|
||||
}
|
||||
|
||||
if (Type() == PthreadMutexType::Recursive && m_count > 0) [[unlikely]] {
|
||||
m_count--;
|
||||
} else {
|
||||
int defered = True(m_flags & PthreadMutexFlags::Defered);
|
||||
m_flags &= ~PthreadMutexFlags::Defered;
|
||||
|
||||
m_owner = nullptr;
|
||||
m_lock.unlock();
|
||||
|
||||
if (curthread->will_sleep == 0 && defered) {
|
||||
curthread->WakeAll();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutex_unlock(PthreadMutexT* mutex) {
|
||||
PthreadMutex* mp = *mutex;
|
||||
if (mp <= THR_MUTEX_DESTROYED) [[unlikely]] {
|
||||
if (mp == THR_MUTEX_DESTROYED) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
return POSIX_EPERM;
|
||||
}
|
||||
return mp->Unlock();
|
||||
}
|
||||
|
||||
int 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) {
|
||||
CHECK_AND_INIT_MUTEX(*mutex)->m_spinloops = count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int 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) {
|
||||
CHECK_AND_INIT_MUTEX(*mutex)->m_yieldloops = count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutex_isowned_np(PthreadMutexT* mutex) {
|
||||
PthreadMutex* m = *mutex;
|
||||
if (m <= THR_MUTEX_DESTROYED) {
|
||||
return 0;
|
||||
}
|
||||
return m->m_owner == g_curthread;
|
||||
}
|
||||
|
||||
bool PthreadMutex::IsOwned(Pthread* curthread) const {
|
||||
if (this <= THR_MUTEX_DESTROYED) [[unlikely]] {
|
||||
if (this == THR_MUTEX_DESTROYED) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
return POSIX_EPERM;
|
||||
}
|
||||
if (m_owner != curthread) {
|
||||
return POSIX_EPERM;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutexattr_init(PthreadMutexAttrT* attr) {
|
||||
PthreadMutexAttrT pattr = new PthreadMutexAttr{};
|
||||
if (pattr == nullptr) {
|
||||
return POSIX_ENOMEM;
|
||||
}
|
||||
memcpy(pattr, &PthreadMutexattrDefault, sizeof(PthreadMutexAttr));
|
||||
*attr = pattr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutexattr_setkind_np(PthreadMutexAttrT* attr,
|
||||
PthreadMutexType kind) {
|
||||
if (attr == nullptr || *attr == nullptr) {
|
||||
*__Error() = POSIX_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
(*attr)->m_type = kind;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutexattr_getkind_np(PthreadMutexAttrT attr) {
|
||||
if (attr == nullptr) {
|
||||
*__Error() = POSIX_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
return static_cast<int>(attr->m_type);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutexattr_settype(PthreadMutexAttrT* attr, PthreadMutexType type) {
|
||||
if (attr == nullptr || *attr == nullptr || type >= PthreadMutexType::Max) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
(*attr)->m_type = type;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutexattr_gettype(PthreadMutexAttrT* attr, PthreadMutexType* type) {
|
||||
if (attr == nullptr || *attr == nullptr || (*attr)->m_type >= PthreadMutexType::Max) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
*type = (*attr)->m_type;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutexattr_destroy(PthreadMutexAttrT* attr) {
|
||||
if (attr == nullptr || *attr == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
delete *attr;
|
||||
*attr = nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutexattr_getprotocol(PthreadMutexAttrT* mattr,
|
||||
PthreadMutexProt* protocol) {
|
||||
if (mattr == nullptr || *mattr == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
*protocol = (*mattr)->m_protocol;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutexattr_setprotocol(PthreadMutexAttrT* mattr,
|
||||
PthreadMutexProt protocol) {
|
||||
if (mattr == nullptr || *mattr == nullptr || (protocol < PthreadMutexProt::None) ||
|
||||
(protocol > PthreadMutexProt::Protect)) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
(*mattr)->m_protocol = protocol;
|
||||
//(*mattr)->m_ceiling = THR_MAX_RR_PRIORITY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RegisterMutex(Core::Loader::SymbolsResolver* sym) {
|
||||
// Posix
|
||||
LIB_FUNCTION("ttHNfU+qDBU", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_init);
|
||||
LIB_FUNCTION("7H0iTOciTLo", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_lock);
|
||||
LIB_FUNCTION("2Z+PpY6CaJg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_unlock);
|
||||
LIB_FUNCTION("ltCfaGr2JGE", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_destroy);
|
||||
LIB_FUNCTION("dQHWEsJtoE4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutexattr_init);
|
||||
LIB_FUNCTION("mDmgMOGVUqg", "libScePosix", 1, "libkernel", 1, 1,
|
||||
posix_pthread_mutexattr_settype);
|
||||
LIB_FUNCTION("5txKfcMUAok", "libScePosix", 1, "libkernel", 1, 1,
|
||||
posix_pthread_mutexattr_setprotocol);
|
||||
LIB_FUNCTION("HF7lK46xzjY", "libScePosix", 1, "libkernel", 1, 1,
|
||||
posix_pthread_mutexattr_destroy);
|
||||
LIB_FUNCTION("K-jXhbt2gn4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_trylock);
|
||||
|
||||
// Posix-Kernel
|
||||
LIB_FUNCTION("7H0iTOciTLo", "libkernel", 1, "libkernel", 1, 1, posix_pthread_mutex_lock);
|
||||
LIB_FUNCTION("2Z+PpY6CaJg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_mutex_unlock);
|
||||
|
||||
// Orbis
|
||||
LIB_FUNCTION("cmo1RIYva9o", "libkernel", 1, "libkernel", 1, 1, ORBIS(scePthreadMutexInit));
|
||||
LIB_FUNCTION("2Of0f+3mhhE", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_mutex_destroy));
|
||||
LIB_FUNCTION("F8bUHwAG284", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_mutexattr_init));
|
||||
LIB_FUNCTION("smWEktiyyG0", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_mutexattr_destroy));
|
||||
LIB_FUNCTION("iMp8QpE+XO4", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_mutexattr_settype));
|
||||
LIB_FUNCTION("1FGvU0i9saQ", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_mutexattr_setprotocol));
|
||||
LIB_FUNCTION("9UK1vLZQft4", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_mutex_lock));
|
||||
LIB_FUNCTION("tn3VlD0hG60", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_mutex_unlock));
|
||||
LIB_FUNCTION("upoVrzMHFeE", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_mutex_trylock));
|
||||
LIB_FUNCTION("IafI2PxcPnQ", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_mutex_reltimedlock_np));
|
||||
LIB_FUNCTION("qH1gXoq71RY", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_mutex_init));
|
||||
LIB_FUNCTION("n2MMpvU8igI", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_mutexattr_init));
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
526
src/core/libraries/kernel/threads/pthread.cpp
Normal file
526
src/core/libraries/kernel/threads/pthread.cpp
Normal file
|
@ -0,0 +1,526 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/debug_state.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/kernel.h"
|
||||
#include "core/libraries/kernel/threads/pthread.h"
|
||||
#include "core/libraries/kernel/threads/thread_state.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
constexpr int PthreadInheritSched = 4;
|
||||
|
||||
constexpr int ORBIS_KERNEL_PRIO_FIFO_DEFAULT = 700;
|
||||
constexpr int ORBIS_KERNEL_PRIO_FIFO_HIGHEST = 256;
|
||||
constexpr int ORBIS_KERNEL_PRIO_FIFO_LOWEST = 767;
|
||||
|
||||
extern PthreadAttr PthreadAttrDefault;
|
||||
|
||||
void _thread_cleanupspecific();
|
||||
|
||||
using ThreadDtor = void (*)();
|
||||
static ThreadDtor* ThreadDtors{};
|
||||
|
||||
void PS4_SYSV_ABI _sceKernelSetThreadDtors(ThreadDtor* dtor) {
|
||||
ThreadDtors = dtor;
|
||||
}
|
||||
|
||||
static void ExitThread() {
|
||||
Pthread* curthread = g_curthread;
|
||||
|
||||
/* Check if there is thread specific data: */
|
||||
if (curthread->specific != nullptr) {
|
||||
/* Run the thread-specific data destructors: */
|
||||
_thread_cleanupspecific();
|
||||
}
|
||||
|
||||
auto* thread_state = ThrState::Instance();
|
||||
ASSERT(thread_state->active_threads.fetch_sub(1) != 1);
|
||||
|
||||
curthread->lock.lock();
|
||||
curthread->state = PthreadState::Dead;
|
||||
ASSERT(False(curthread->flags & ThreadFlags::NeedSuspend));
|
||||
|
||||
/*
|
||||
* Thread was created with initial refcount 1, we drop the
|
||||
* reference count to allow it to be garbage collected.
|
||||
*/
|
||||
curthread->refcount--;
|
||||
thread_state->TryCollect(curthread); /* thread lock released */
|
||||
|
||||
/*
|
||||
* Kernel will do wakeup at the address, so joiner thread
|
||||
* will be resumed if it is sleeping at the address.
|
||||
*/
|
||||
curthread->tid.store(TidTerminated);
|
||||
curthread->tid.notify_all();
|
||||
|
||||
curthread->native_thr.Exit();
|
||||
UNREACHABLE();
|
||||
/* Never reach! */
|
||||
}
|
||||
|
||||
void PS4_SYSV_ABI posix_pthread_exit(void* status) {
|
||||
Pthread* curthread = g_curthread;
|
||||
|
||||
/* Check if this thread is already in the process of exiting: */
|
||||
ASSERT_MSG(!curthread->cancelling, "Thread {} has called pthread_exit from a destructor",
|
||||
fmt::ptr(curthread));
|
||||
|
||||
/* Flag this thread as exiting. */
|
||||
curthread->cancelling = 1;
|
||||
curthread->no_cancel = 1;
|
||||
curthread->cancel_async = 0;
|
||||
curthread->cancel_point = 0;
|
||||
|
||||
/* Save the return value: */
|
||||
curthread->ret = status;
|
||||
while (!curthread->cleanup.empty()) {
|
||||
PthreadCleanup* old = curthread->cleanup.front();
|
||||
curthread->cleanup.pop_front();
|
||||
old->routine(old->routine_arg);
|
||||
if (old->onheap) {
|
||||
delete old;
|
||||
}
|
||||
}
|
||||
/*if (ThreadDtors && *ThreadDtors) {
|
||||
(*ThreadDtors)();
|
||||
}*/
|
||||
ExitThread();
|
||||
}
|
||||
|
||||
static int JoinThread(PthreadT pthread, void** thread_return, const OrbisKernelTimespec* abstime) {
|
||||
Pthread* curthread = g_curthread;
|
||||
|
||||
if (pthread == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
|
||||
if (pthread == curthread) {
|
||||
return POSIX_EDEADLK;
|
||||
}
|
||||
|
||||
auto* thread_state = ThrState::Instance();
|
||||
if (int ret = thread_state->FindThread(pthread, 1); ret != 0) {
|
||||
return POSIX_ESRCH;
|
||||
}
|
||||
|
||||
int ret = 0;
|
||||
if (True(pthread->flags & ThreadFlags::Detached)) {
|
||||
ret = POSIX_EINVAL;
|
||||
} else if (pthread->joiner != nullptr) {
|
||||
/* Multiple joiners are not supported. */
|
||||
ret = POSIX_ENOTSUP;
|
||||
}
|
||||
if (ret) {
|
||||
pthread->lock.unlock();
|
||||
return ret;
|
||||
}
|
||||
/* Set the running thread to be the joiner: */
|
||||
pthread->joiner = curthread;
|
||||
pthread->lock.unlock();
|
||||
|
||||
const auto backout_join = [](void* arg) PS4_SYSV_ABI {
|
||||
Pthread* pthread = (Pthread*)arg;
|
||||
std::scoped_lock lk{pthread->lock};
|
||||
pthread->joiner = nullptr;
|
||||
};
|
||||
|
||||
PthreadCleanup cup{backout_join, pthread, 0};
|
||||
curthread->cleanup.push_front(&cup);
|
||||
|
||||
//_thr_cancel_enter(curthread);
|
||||
|
||||
const int tid = pthread->tid;
|
||||
while (pthread->tid.load() != TidTerminated) {
|
||||
//_thr_testcancel(curthread);
|
||||
ASSERT(abstime == nullptr);
|
||||
pthread->tid.wait(tid);
|
||||
}
|
||||
|
||||
//_thr_cancel_leave(curthread, 0);
|
||||
curthread->cleanup.pop_front();
|
||||
|
||||
if (ret == POSIX_ETIMEDOUT) {
|
||||
backout_join(pthread);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void* tmp = pthread->ret;
|
||||
pthread->lock.lock();
|
||||
pthread->flags |= ThreadFlags::Detached;
|
||||
pthread->joiner = nullptr;
|
||||
thread_state->TryCollect(pthread); /* thread lock released */
|
||||
if (thread_return != nullptr) {
|
||||
*thread_return = tmp;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_join(PthreadT pthread, void** thread_return) {
|
||||
return JoinThread(pthread, thread_return, NULL);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_timedjoin_np(PthreadT pthread, void** thread_return,
|
||||
const OrbisKernelTimespec* abstime) {
|
||||
if (abstime == nullptr || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
|
||||
abstime->tv_nsec >= 1000000000) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
|
||||
return JoinThread(pthread, thread_return, abstime);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_detach(PthreadT pthread) {
|
||||
if (pthread == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
|
||||
auto* thread_state = ThrState::Instance();
|
||||
if (int ret = thread_state->FindThread(pthread, 1); ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Check if the thread is already detached or has a joiner. */
|
||||
if (True(pthread->flags & ThreadFlags::Detached) || (pthread->joiner != NULL)) {
|
||||
pthread->lock.unlock();
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
|
||||
/* Flag the thread as detached. */
|
||||
pthread->flags |= ThreadFlags::Detached;
|
||||
thread_state->TryCollect(pthread); /* thread lock released */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void RunThread(void* arg) {
|
||||
Pthread* curthread = (Pthread*)arg;
|
||||
g_curthread = curthread;
|
||||
Common::SetCurrentThreadName(curthread->name.c_str());
|
||||
DebugState.AddCurrentThreadToGuestList();
|
||||
|
||||
/* Run the current thread's start routine with argument: */
|
||||
void* ret = Core::ExecuteGuest(curthread->start_routine, curthread->arg);
|
||||
|
||||
/* Remove thread from tracking */
|
||||
DebugState.RemoveCurrentThreadFromGuestList();
|
||||
posix_pthread_exit(ret);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_create_name_np(PthreadT* thread, const PthreadAttrT* attr,
|
||||
PthreadEntryFunc start_routine, void* arg,
|
||||
const char* name) {
|
||||
Pthread* curthread = g_curthread;
|
||||
auto* thread_state = ThrState::Instance();
|
||||
Pthread* new_thread = thread_state->Alloc(curthread);
|
||||
if (new_thread == nullptr) {
|
||||
return POSIX_EAGAIN;
|
||||
}
|
||||
|
||||
if (attr == nullptr || *attr == nullptr) {
|
||||
new_thread->attr = PthreadAttrDefault;
|
||||
} else {
|
||||
new_thread->attr = *(*attr);
|
||||
new_thread->attr.cpusetsize = 0;
|
||||
}
|
||||
if (new_thread->attr.sched_inherit == PthreadInheritSched) {
|
||||
if (True(curthread->attr.flags & PthreadAttrFlags::ScopeSystem)) {
|
||||
new_thread->attr.flags |= PthreadAttrFlags::ScopeSystem;
|
||||
} else {
|
||||
new_thread->attr.flags &= ~PthreadAttrFlags::ScopeSystem;
|
||||
}
|
||||
new_thread->attr.prio = curthread->attr.prio;
|
||||
new_thread->attr.sched_policy = curthread->attr.sched_policy;
|
||||
}
|
||||
|
||||
static int TidCounter = 1;
|
||||
new_thread->tid = ++TidCounter;
|
||||
|
||||
if (thread_state->CreateStack(&new_thread->attr) != 0) {
|
||||
/* Insufficient memory to create a stack: */
|
||||
thread_state->Free(curthread, new_thread);
|
||||
return POSIX_EAGAIN;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write a magic value to the thread structure
|
||||
* to help identify valid ones:
|
||||
*/
|
||||
new_thread->magic = Pthread::ThrMagic;
|
||||
new_thread->start_routine = start_routine;
|
||||
new_thread->arg = arg;
|
||||
new_thread->cancel_enable = 1;
|
||||
new_thread->cancel_async = 0;
|
||||
|
||||
auto* memory = Core::Memory::Instance();
|
||||
if (name && memory->IsValidAddress(name)) {
|
||||
new_thread->name = name;
|
||||
} else {
|
||||
new_thread->name = fmt::format("Thread{}", new_thread->tid.load());
|
||||
}
|
||||
|
||||
ASSERT(new_thread->attr.suspend == 0);
|
||||
new_thread->state = PthreadState::Running;
|
||||
|
||||
if (True(new_thread->attr.flags & PthreadAttrFlags::Detached)) {
|
||||
new_thread->flags |= ThreadFlags::Detached;
|
||||
}
|
||||
|
||||
/* Add the new thread. */
|
||||
new_thread->refcount = 1;
|
||||
thread_state->Link(curthread, new_thread);
|
||||
|
||||
/* Return thread pointer eariler so that new thread can use it. */
|
||||
(*thread) = new_thread;
|
||||
|
||||
/* Create thread */
|
||||
new_thread->native_thr = Core::Thread();
|
||||
int ret = new_thread->native_thr.Create(RunThread, new_thread);
|
||||
ASSERT_MSG(ret == 0, "Failed to create thread with error {}", ret);
|
||||
if (ret) {
|
||||
*thread = nullptr;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_create(PthreadT* thread, const PthreadAttrT* attr,
|
||||
PthreadEntryFunc start_routine, void* arg) {
|
||||
return posix_pthread_create_name_np(thread, attr, start_routine, arg, nullptr);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_getthreadid_np() {
|
||||
return g_curthread->tid;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_getname_np(PthreadT thread, char* name) {
|
||||
std::memcpy(name, thread->name.data(), std::min<size_t>(thread->name.size(), 32));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_equal(PthreadT thread1, PthreadT thread2) {
|
||||
return (thread1 == thread2 ? 1 : 0);
|
||||
}
|
||||
|
||||
PthreadT PS4_SYSV_ABI posix_pthread_self() {
|
||||
return g_curthread;
|
||||
}
|
||||
|
||||
void PS4_SYSV_ABI posix_pthread_yield() {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
|
||||
void PS4_SYSV_ABI sched_yield() {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_once(PthreadOnce* once_control, void (*init_routine)()) {
|
||||
for (;;) {
|
||||
auto state = once_control->state.load();
|
||||
if (state == PthreadOnceState::Done) {
|
||||
return 0;
|
||||
}
|
||||
if (state == PthreadOnceState::NeverDone) {
|
||||
if (once_control->state.compare_exchange_strong(state, PthreadOnceState::InProgress,
|
||||
std::memory_order_acquire)) {
|
||||
break;
|
||||
}
|
||||
} else if (state == PthreadOnceState::InProgress) {
|
||||
if (once_control->state.compare_exchange_strong(state, PthreadOnceState::Wait,
|
||||
std::memory_order_acquire)) {
|
||||
once_control->state.wait(PthreadOnceState::Wait);
|
||||
}
|
||||
} else if (state == PthreadOnceState::Wait) {
|
||||
once_control->state.wait(state);
|
||||
} else {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
const auto once_cancel_handler = [](void* arg) PS4_SYSV_ABI {
|
||||
PthreadOnce* once_control = (PthreadOnce*)arg;
|
||||
auto state = PthreadOnceState::InProgress;
|
||||
if (once_control->state.compare_exchange_strong(state, PthreadOnceState::NeverDone,
|
||||
std::memory_order_release)) {
|
||||
return;
|
||||
}
|
||||
|
||||
once_control->state.store(PthreadOnceState::NeverDone, std::memory_order_release);
|
||||
once_control->state.notify_all();
|
||||
};
|
||||
|
||||
PthreadCleanup cup{once_cancel_handler, once_control, 0};
|
||||
g_curthread->cleanup.push_front(&cup);
|
||||
init_routine();
|
||||
g_curthread->cleanup.pop_front();
|
||||
|
||||
auto state = PthreadOnceState::InProgress;
|
||||
if (once_control->state.compare_exchange_strong(state, PthreadOnceState::Done,
|
||||
std::memory_order_release)) {
|
||||
return 0;
|
||||
}
|
||||
once_control->state.store(PthreadOnceState::Done);
|
||||
once_control->state.notify_all();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_sched_get_priority_max() {
|
||||
return ORBIS_KERNEL_PRIO_FIFO_HIGHEST;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_sched_get_priority_min() {
|
||||
return ORBIS_KERNEL_PRIO_FIFO_LOWEST;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_rename_np(PthreadT thread, const char* name) {
|
||||
LOG_INFO(Kernel_Pthread, "name = {}", name);
|
||||
thread->name = name;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_getschedparam(PthreadT pthread, SchedPolicy* policy,
|
||||
SchedParam* param) {
|
||||
if (policy == nullptr || param == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
|
||||
if (pthread == g_curthread) {
|
||||
/*
|
||||
* Avoid searching the thread list when it is the current
|
||||
* thread.
|
||||
*/
|
||||
std::scoped_lock lk{g_curthread->lock};
|
||||
*policy = g_curthread->attr.sched_policy;
|
||||
param->sched_priority = g_curthread->attr.prio;
|
||||
return 0;
|
||||
}
|
||||
auto* thread_state = ThrState::Instance();
|
||||
/* Find the thread in the list of active threads. */
|
||||
if (int ret = thread_state->RefAdd(pthread, /*include dead*/ 0); ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
pthread->lock.lock();
|
||||
*policy = pthread->attr.sched_policy;
|
||||
param->sched_priority = pthread->attr.prio;
|
||||
pthread->lock.unlock();
|
||||
thread_state->RefDelete(pthread);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadGetprio(PthreadT thread, int* priority) {
|
||||
SchedParam param;
|
||||
SchedPolicy policy;
|
||||
|
||||
posix_pthread_getschedparam(thread, &policy, ¶m);
|
||||
*priority = param.sched_priority;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadSetprio(PthreadT thread, int prio) {
|
||||
SchedParam param;
|
||||
int ret;
|
||||
|
||||
param.sched_priority = prio;
|
||||
|
||||
auto* thread_state = ThrState::Instance();
|
||||
if (thread == g_curthread) {
|
||||
g_curthread->lock.lock();
|
||||
} else if (int ret = thread_state->FindThread(thread, /*include dead*/0)) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (thread->attr.sched_policy == SchedPolicy::Other ||
|
||||
thread->attr.prio == prio) {
|
||||
thread->attr.prio = prio;
|
||||
ret = 0;
|
||||
} else {
|
||||
// TODO: _thr_setscheduler
|
||||
thread->attr.prio = prio;
|
||||
}
|
||||
|
||||
thread->lock.unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
enum class PthreadCancelState : u32 {
|
||||
Enable = 0,
|
||||
Disable = 1,
|
||||
};
|
||||
|
||||
#define POSIX_PTHREAD_CANCELED ((void*)1)
|
||||
|
||||
static inline void TestCancel(Pthread* curthread) {
|
||||
if (curthread->ShouldCancel() && !curthread->InCritical()) [[unlikely]] {
|
||||
posix_pthread_exit(POSIX_PTHREAD_CANCELED);
|
||||
}
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_setcancelstate(PthreadCancelState state,
|
||||
PthreadCancelState* oldstate) {
|
||||
Pthread* curthread = g_curthread;
|
||||
int oldval = curthread->cancel_enable;
|
||||
switch (state) {
|
||||
case PthreadCancelState::Disable:
|
||||
curthread->cancel_enable = 0;
|
||||
break;
|
||||
case PthreadCancelState::Enable:
|
||||
curthread->cancel_enable = 1;
|
||||
TestCancel(curthread);
|
||||
break;
|
||||
default:
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
|
||||
if (oldstate) {
|
||||
*oldstate = oldval ? PthreadCancelState::Enable : PthreadCancelState::Disable;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RegisterThread(Core::Loader::SymbolsResolver* sym) {
|
||||
// Posix
|
||||
LIB_FUNCTION("Z4QosVuAsA0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_once);
|
||||
LIB_FUNCTION("7Xl257M4VNI", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_equal);
|
||||
LIB_FUNCTION("CBNtXOoef-E", "libScePosix", 1, "libkernel", 1, 1, posix_sched_get_priority_max);
|
||||
LIB_FUNCTION("m0iS6jNsXds", "libScePosix", 1, "libkernel", 1, 1, posix_sched_get_priority_min);
|
||||
LIB_FUNCTION("EotR8a3ASf4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_self);
|
||||
LIB_FUNCTION("B5GmVDKwpn0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_yield);
|
||||
LIB_FUNCTION("+U1R4WtXvoc", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_detach);
|
||||
LIB_FUNCTION("FJrT5LuUBAU", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_exit);
|
||||
LIB_FUNCTION("h9CcP3J0oVM", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_join);
|
||||
LIB_FUNCTION("OxhIB8LB-PQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create);
|
||||
LIB_FUNCTION("Jmi+9w9u0E4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create_name_np);
|
||||
LIB_FUNCTION("lZzFeSxPl08", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setcancelstate);
|
||||
LIB_FUNCTION("6XG4B33N09g", "libScePosix", 1, "libkernel", 1, 1, sched_yield);
|
||||
|
||||
// Posix-Kernel
|
||||
LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self);
|
||||
LIB_FUNCTION("OxhIB8LB-PQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_create);
|
||||
|
||||
// Orbis
|
||||
LIB_FUNCTION("14bOACANTBo", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_once));
|
||||
LIB_FUNCTION("GBUY7ywdULE", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_rename_np));
|
||||
LIB_FUNCTION("6UgtwV+0zb4", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_create_name_np));
|
||||
LIB_FUNCTION("4qGrR6eoP9Y", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_detach));
|
||||
LIB_FUNCTION("onNY9Byn-W8", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_join));
|
||||
LIB_FUNCTION("P41kTWUS3EI", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_getschedparam));
|
||||
LIB_FUNCTION("How7B8Oet6k", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_getname_np));
|
||||
LIB_FUNCTION("3kg7rT0NQIs", "libkernel", 1, "libkernel", 1, 1, posix_pthread_exit);
|
||||
LIB_FUNCTION("aI+OeCz8xrQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self);
|
||||
LIB_FUNCTION("3PtV6p3QNX4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_equal);
|
||||
LIB_FUNCTION("T72hz6ffq08", "libkernel", 1, "libkernel", 1, 1, posix_pthread_yield);
|
||||
LIB_FUNCTION("EI-5-jlq2dE", "libkernel", 1, "libkernel", 1, 1, posix_pthread_getthreadid_np);
|
||||
LIB_FUNCTION("1tKyG7RlMJo", "libkernel", 1, "libkernel", 1, 1, scePthreadGetprio);
|
||||
LIB_FUNCTION("W0Hpm2X0uPE", "libkernel", 1, "libkernel", 1, 1, scePthreadSetprio);
|
||||
LIB_FUNCTION("rNhWz+lvOMU", "libkernel", 1, "libkernel", 1, 1, _sceKernelSetThreadDtors);
|
||||
LIB_FUNCTION("6XG4B33N09g", "libkernel", 1, "libkernel", 1, 1, sched_yield);
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
349
src/core/libraries/kernel/threads/pthread.h
Normal file
349
src/core/libraries/kernel/threads/pthread.h
Normal file
|
@ -0,0 +1,349 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <forward_list>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <semaphore>
|
||||
#include <shared_mutex>
|
||||
|
||||
#include "common/enum.h"
|
||||
#include "core/libraries/kernel/time.h"
|
||||
#include "core/thread.h"
|
||||
#include "core/tls.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
struct Pthread;
|
||||
|
||||
enum class PthreadMutexFlags : u32 {
|
||||
TypeMask = 0xff,
|
||||
Defered = 0x200,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(PthreadMutexFlags)
|
||||
|
||||
enum class PthreadMutexType : u32 {
|
||||
ErrorCheck = 1,
|
||||
Recursive = 2,
|
||||
Normal = 3,
|
||||
AdaptiveNp = 4,
|
||||
Max
|
||||
};
|
||||
|
||||
enum class PthreadMutexProt : u32 {
|
||||
None = 0,
|
||||
Inherit = 1,
|
||||
Protect = 2,
|
||||
};
|
||||
|
||||
struct PthreadMutex {
|
||||
std::timed_mutex m_lock;
|
||||
PthreadMutexFlags m_flags;
|
||||
Pthread* m_owner;
|
||||
int m_count;
|
||||
int m_spinloops;
|
||||
int m_yieldloops;
|
||||
PthreadMutexProt m_protocol;
|
||||
std::string name;
|
||||
|
||||
PthreadMutexType Type() const noexcept {
|
||||
return static_cast<PthreadMutexType>(m_flags & PthreadMutexFlags::TypeMask);
|
||||
}
|
||||
|
||||
int SelfTryLock();
|
||||
int SelfLock(const OrbisKernelTimespec* abstime, u64 usec);
|
||||
|
||||
int TryLock();
|
||||
int Lock(const OrbisKernelTimespec* abstime, u64 usec = 0);
|
||||
|
||||
int CvLock(int recurse) {
|
||||
const int error = Lock(nullptr);
|
||||
if (error == 0) {
|
||||
m_count = recurse;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
int Unlock();
|
||||
|
||||
int CvUnlock(int* recurse) {
|
||||
*recurse = m_count;
|
||||
m_count = 0;
|
||||
return Unlock();
|
||||
}
|
||||
|
||||
bool IsOwned(Pthread* curthread) const;
|
||||
};
|
||||
using PthreadMutexT = PthreadMutex*;
|
||||
|
||||
struct PthreadMutexAttr {
|
||||
PthreadMutexType m_type;
|
||||
PthreadMutexProt m_protocol;
|
||||
int m_ceiling;
|
||||
};
|
||||
using PthreadMutexAttrT = PthreadMutexAttr*;
|
||||
|
||||
enum class PthreadCondFlags : u32 {
|
||||
Private = 1,
|
||||
Inited = 2,
|
||||
Busy = 4,
|
||||
};
|
||||
|
||||
enum class ClockId : u32 {
|
||||
Realtime = 0,
|
||||
Virtual = 1,
|
||||
Prof = 2,
|
||||
Monotonic = 4,
|
||||
Uptime = 5,
|
||||
UptimePrecise = 7,
|
||||
UptimeFast = 8,
|
||||
RealtimePrecise = 9,
|
||||
RealtimeFast = 10,
|
||||
MonotonicPrecise = 11,
|
||||
MonotonicFast = 12,
|
||||
Second = 13,
|
||||
ThreadCputimeID = 14,
|
||||
};
|
||||
|
||||
struct PthreadCond {
|
||||
u32 has_user_waiters;
|
||||
u32 has_kern_waiters;
|
||||
u32 flags;
|
||||
ClockId clock_id;
|
||||
std::string name;
|
||||
|
||||
int Wait(PthreadMutexT* mutex, const OrbisKernelTimespec* abstime, u64 usec = 0);
|
||||
|
||||
int Signal();
|
||||
int Broadcast();
|
||||
};
|
||||
using PthreadCondT = PthreadCond*;
|
||||
|
||||
struct PthreadCondAttr {
|
||||
int c_pshared;
|
||||
ClockId c_clockid;
|
||||
};
|
||||
using PthreadCondAttrT = PthreadCondAttr*;
|
||||
|
||||
using PthreadCleanupFunc = void PS4_SYSV_ABI (*)(void*);
|
||||
|
||||
struct PthreadCleanup {
|
||||
PthreadCleanupFunc routine;
|
||||
void* routine_arg;
|
||||
int onheap;
|
||||
};
|
||||
|
||||
enum class PthreadAttrFlags : u32 {
|
||||
Detached = 1,
|
||||
ScopeSystem = 2,
|
||||
InheritSched = 4,
|
||||
NoFloat = 8,
|
||||
StackUser = 0x100,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(PthreadAttrFlags)
|
||||
|
||||
enum class SchedPolicy : u32 {
|
||||
Fifo = 0,
|
||||
Other = 2,
|
||||
RoundRobin = 3,
|
||||
};
|
||||
|
||||
struct Cpuset {
|
||||
u64 bits;
|
||||
};
|
||||
|
||||
struct PthreadAttr {
|
||||
SchedPolicy sched_policy;
|
||||
int sched_inherit;
|
||||
int prio;
|
||||
int suspend;
|
||||
PthreadAttrFlags flags;
|
||||
void* stackaddr_attr;
|
||||
size_t stacksize_attr;
|
||||
size_t guardsize_attr;
|
||||
size_t cpusetsize;
|
||||
Cpuset* cpuset;
|
||||
};
|
||||
using PthreadAttrT = PthreadAttr*;
|
||||
|
||||
static constexpr u32 ThrStackDefault = 1_MB;
|
||||
static constexpr u32 ThrStackInitial = 2_MB;
|
||||
static constexpr u32 ThrPageSize = 16_KB;
|
||||
static constexpr u32 ThrGuardDefault = ThrPageSize;
|
||||
|
||||
struct PthreadRwlockAttr {
|
||||
int pshared;
|
||||
};
|
||||
using PthreadRwlockAttrT = PthreadRwlockAttr*;
|
||||
|
||||
struct PthreadRwlock {
|
||||
std::shared_timed_mutex lock;
|
||||
Pthread* owner;
|
||||
|
||||
int Wrlock(const OrbisKernelTimespec* abstime);
|
||||
int Rdlock(const OrbisKernelTimespec* abstime);
|
||||
};
|
||||
using PthreadRwlockT = PthreadRwlock*;
|
||||
|
||||
enum class PthreadState : u32 { Running, Dead };
|
||||
|
||||
struct PthreadSpecificElem {
|
||||
const void* data;
|
||||
int seqno;
|
||||
};
|
||||
|
||||
using PthreadKeyDestructor = void PS4_SYSV_ABI (*)(const void*);
|
||||
|
||||
struct PthreadKey {
|
||||
int allocated;
|
||||
int seqno;
|
||||
PthreadKeyDestructor destructor;
|
||||
};
|
||||
using PthreadKeyT = s32;
|
||||
|
||||
enum class PthreadOnceState : u32 {
|
||||
NeverDone = 0,
|
||||
Done = 1,
|
||||
InProgress = 2,
|
||||
Wait = 3,
|
||||
};
|
||||
|
||||
struct PthreadOnce {
|
||||
std::atomic<PthreadOnceState> state;
|
||||
std::mutex mutex;
|
||||
};
|
||||
|
||||
enum class ThreadFlags : u32 {
|
||||
Private = 1,
|
||||
NeedSuspend = 2,
|
||||
Suspended = 4,
|
||||
Detached = 8,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(ThreadFlags)
|
||||
|
||||
enum class ThreadListFlags : u32 {
|
||||
GcSafe = 1,
|
||||
InTdList = 2,
|
||||
InGcList = 4,
|
||||
};
|
||||
|
||||
using PthreadEntryFunc = void* PS4_SYSV_ABI (*)(void*);
|
||||
|
||||
constexpr u32 TidTerminated = 1;
|
||||
|
||||
struct SleepQueue;
|
||||
|
||||
struct SchedParam {
|
||||
int sched_priority;
|
||||
};
|
||||
|
||||
#define THR_RELTIME (const OrbisKernelTimespec*)-1
|
||||
|
||||
struct Pthread {
|
||||
static constexpr u32 ThrMagic = 0xd09ba115U;
|
||||
static constexpr u32 MaxDeferWaiters = 50;
|
||||
|
||||
std::atomic<long> tid;
|
||||
std::mutex lock;
|
||||
u32 cycle;
|
||||
int locklevel;
|
||||
int critical_count;
|
||||
int sigblock;
|
||||
int refcount;
|
||||
PthreadEntryFunc start_routine;
|
||||
void* arg;
|
||||
Core::Thread native_thr;
|
||||
PthreadAttr attr;
|
||||
bool cancel_enable;
|
||||
bool cancel_pending;
|
||||
bool cancel_point;
|
||||
bool no_cancel;
|
||||
bool cancel_async;
|
||||
bool cancelling;
|
||||
Cpuset sigmask;
|
||||
bool unblock_sigcancel;
|
||||
bool in_sigsuspend;
|
||||
bool force_exit;
|
||||
PthreadState state;
|
||||
int error;
|
||||
Pthread* joiner;
|
||||
ThreadFlags flags;
|
||||
ThreadListFlags tlflags;
|
||||
void* ret;
|
||||
PthreadSpecificElem* specific;
|
||||
int specific_data_count;
|
||||
int rdlock_count;
|
||||
int rtld_bits;
|
||||
Core::Tcb* tcb;
|
||||
std::forward_list<PthreadCleanup*> cleanup;
|
||||
u32 pad[27];
|
||||
u32 magic;
|
||||
int report_events;
|
||||
int event_mask;
|
||||
std::string name;
|
||||
std::binary_semaphore wake_sema{0};
|
||||
SleepQueue* sleepqueue;
|
||||
void* wchan;
|
||||
PthreadMutex* mutex_obj;
|
||||
bool will_sleep;
|
||||
bool has_user_waiters;
|
||||
int nwaiter_defer;
|
||||
std::binary_semaphore* defer_waiters[MaxDeferWaiters];
|
||||
|
||||
bool InCritical() const noexcept {
|
||||
return locklevel > 0 || critical_count > 0;
|
||||
}
|
||||
|
||||
bool ShouldCollect() const noexcept {
|
||||
return refcount == 0 && state == PthreadState::Dead && True(flags & ThreadFlags::Detached);
|
||||
}
|
||||
|
||||
bool ShouldCancel() const noexcept {
|
||||
return cancel_pending && cancel_enable && no_cancel == 0;
|
||||
}
|
||||
|
||||
void WakeAll() {
|
||||
for (int i = 0; i < nwaiter_defer; i++) {
|
||||
defer_waiters[i]->release();
|
||||
}
|
||||
nwaiter_defer = 0;
|
||||
}
|
||||
|
||||
bool Sleep(const OrbisKernelTimespec* abstime, u64 usec) {
|
||||
will_sleep = 0;
|
||||
if (nwaiter_defer > 0) {
|
||||
WakeAll();
|
||||
}
|
||||
if (abstime == THR_RELTIME) {
|
||||
return wake_sema.try_acquire_for(std::chrono::microseconds(usec));
|
||||
} else if (abstime != nullptr) {
|
||||
return wake_sema.try_acquire_until(abstime->TimePoint());
|
||||
} else {
|
||||
wake_sema.acquire();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
using PthreadT = Pthread*;
|
||||
|
||||
extern thread_local Pthread* g_curthread;
|
||||
|
||||
void RegisterMutex(Core::Loader::SymbolsResolver* sym);
|
||||
void RegisterCond(Core::Loader::SymbolsResolver* sym);
|
||||
void RegisterRwlock(Core::Loader::SymbolsResolver* sym);
|
||||
void RegisterSemaphore(Core::Loader::SymbolsResolver* sym);
|
||||
void RegisterSpec(Core::Loader::SymbolsResolver* sym);
|
||||
void RegisterThreadAttr(Core::Loader::SymbolsResolver* sym);
|
||||
void RegisterThread(Core::Loader::SymbolsResolver* sym);
|
||||
void RegisterRtld(Core::Loader::SymbolsResolver* sym);
|
||||
void RegisterKernelEventFlag(Core::Loader::SymbolsResolver* sym);
|
||||
void RegisterPthreadClean(Core::Loader::SymbolsResolver* sym);
|
||||
|
||||
} // namespace Libraries::Kernel
|
345
src/core/libraries/kernel/threads/pthread_attr.cpp
Normal file
345
src/core/libraries/kernel/threads/pthread_attr.cpp
Normal file
|
@ -0,0 +1,345 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/kernel.h"
|
||||
#include "core/libraries/kernel/threads/pthread.h"
|
||||
#include "core/libraries/kernel/threads/thread_state.h"
|
||||
#include "core/libraries/libs.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
static constexpr u32 PthreadStackMin = 16_KB;
|
||||
|
||||
struct PthreadPrio {
|
||||
s32 pri_min;
|
||||
s32 pri_max;
|
||||
s32 pri_default;
|
||||
};
|
||||
|
||||
static constexpr std::array<PthreadPrio, 3> ThrPriorities = {{
|
||||
{0x100, 0x2FF, 0x2BC}, // Fifo
|
||||
{0x300, 0x3BF, 0x384}, // Other
|
||||
{0x100, 0x2FF, 0x2BC}, // Round-Robin
|
||||
}};
|
||||
|
||||
PthreadAttr PthreadAttrDefault = {
|
||||
.sched_policy = SchedPolicy::Fifo,
|
||||
.sched_inherit = 0,
|
||||
.prio = 0,
|
||||
.suspend = false,
|
||||
.flags = PthreadAttrFlags::ScopeSystem,
|
||||
.stackaddr_attr = NULL,
|
||||
.stacksize_attr = ThrStackDefault,
|
||||
.guardsize_attr = 0,
|
||||
.cpusetsize = 0,
|
||||
.cpuset = nullptr,
|
||||
};
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_attr_destroy(PthreadAttrT* attr) {
|
||||
if (attr == nullptr || *attr == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
delete *attr;
|
||||
*attr = nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_attr_getdetachstate(const PthreadAttrT* attr, int* detachstate) {
|
||||
if (attr == nullptr || *attr == nullptr || detachstate == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
*detachstate = True((*attr)->flags & PthreadAttrFlags::Detached);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_attr_getguardsize(const PthreadAttrT* attr, size_t* guardsize) {
|
||||
if (attr == nullptr || *attr == nullptr || guardsize == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
*guardsize = (*attr)->guardsize_attr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_attr_getinheritsched(const PthreadAttrT* attr, int* sched_inherit) {
|
||||
if (attr == nullptr || *attr == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
*sched_inherit = (*attr)->sched_inherit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_attr_getschedparam(const PthreadAttrT* attr, SchedParam* param) {
|
||||
if (attr == nullptr || *attr == nullptr || param == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
param->sched_priority = (*attr)->prio;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_attr_getschedpolicy(const PthreadAttrT* attr, SchedPolicy* policy) {
|
||||
if (attr == nullptr || *attr == nullptr || policy == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
*policy = (*attr)->sched_policy;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_attr_getstack(const PthreadAttrT* attr, void** stackaddr,
|
||||
size_t* stacksize) {
|
||||
if (attr == nullptr || *attr == nullptr || stackaddr == nullptr || stacksize == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
*stackaddr = (*attr)->stackaddr_attr;
|
||||
*stacksize = (*attr)->stacksize_attr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_attr_getstackaddr(const PthreadAttrT* attr, void** stackaddr) {
|
||||
if (attr == nullptr || *attr == nullptr || stackaddr == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
*stackaddr = (*attr)->stackaddr_attr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_attr_getstacksize(const PthreadAttrT* attr, size_t* stacksize) {
|
||||
if (attr == nullptr || *attr == nullptr || stacksize == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
*stacksize = (*attr)->stacksize_attr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_attr_init(PthreadAttrT* attr) {
|
||||
PthreadAttrT pattr = new PthreadAttr{};
|
||||
if (pattr == nullptr) {
|
||||
return POSIX_ENOMEM;
|
||||
}
|
||||
memcpy(pattr, &PthreadAttrDefault, sizeof(PthreadAttr));
|
||||
*attr = pattr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_attr_setschedpolicy(PthreadAttrT* attr, SchedPolicy policy) {
|
||||
if (attr == NULL || *attr == NULL) {
|
||||
return POSIX_EINVAL;
|
||||
} else if ((policy < SchedPolicy::Fifo) || (policy > SchedPolicy::RoundRobin)) {
|
||||
return POSIX_ENOTSUP;
|
||||
}
|
||||
(*attr)->sched_policy = policy;
|
||||
(*attr)->prio = ThrPriorities[u32(policy) - 1].pri_default;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_attr_setstack(PthreadAttrT* attr, void* stackaddr,
|
||||
size_t stacksize) {
|
||||
if (attr == nullptr || *attr == nullptr || stackaddr == nullptr ||
|
||||
stacksize < PthreadStackMin) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
(*attr)->stackaddr_attr = stackaddr;
|
||||
(*attr)->stacksize_attr = stacksize;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_attr_setstackaddr(PthreadAttrT* attr, void* stackaddr) {
|
||||
if (attr == nullptr || *attr == nullptr || stackaddr == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
(*attr)->stackaddr_attr = stackaddr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_attr_setstacksize(PthreadAttrT* attr, size_t stacksize) {
|
||||
if (attr == nullptr || *attr == nullptr || stacksize < PthreadStackMin) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
(*attr)->stacksize_attr = stacksize;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_attr_setdetachstate(PthreadAttrT* attr, int detachstate) {
|
||||
if (attr == nullptr || *attr == nullptr || (detachstate != 1 && detachstate != 0)) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
if (detachstate) {
|
||||
(*attr)->flags |= PthreadAttrFlags::Detached;
|
||||
} else {
|
||||
(*attr)->flags &= ~PthreadAttrFlags::Detached;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_attr_setschedparam(PthreadAttrT* attr, SchedParam* param) {
|
||||
if (attr == nullptr || *attr == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
if (param == nullptr) {
|
||||
return POSIX_ENOTSUP;
|
||||
}
|
||||
|
||||
const auto policy = (*attr)->sched_policy;
|
||||
if (policy == SchedPolicy::RoundRobin) {
|
||||
if (param->sched_priority < ThrPriorities[u32(policy) - 1].pri_min ||
|
||||
param->sched_priority > ThrPriorities[u32(policy) - 1].pri_max) {
|
||||
return POSIX_ENOTSUP;
|
||||
}
|
||||
}
|
||||
(*attr)->prio = param->sched_priority;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_attr_setinheritsched(PthreadAttrT* attr, int sched_inherit) {
|
||||
if (attr == nullptr || *attr == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
if (sched_inherit != 4 && sched_inherit != 0) {
|
||||
return POSIX_ENOTSUP;
|
||||
}
|
||||
|
||||
(*attr)->sched_inherit = sched_inherit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_attr_setguardsize(PthreadAttrT* attr, size_t guardsize) {
|
||||
if (attr == nullptr || *attr == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
(*attr)->guardsize_attr = guardsize;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_attr_get_np(PthreadT pthread, PthreadAttrT* dstattr) {
|
||||
PthreadAttr* dst;
|
||||
if (pthread == nullptr || dstattr == nullptr || (dst = *dstattr) == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
auto* thread_state = ThrState::Instance();
|
||||
int ret = thread_state->FindThread(pthread, /*include dead*/ 0);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
PthreadAttr attr = pthread->attr;
|
||||
if (True(pthread->flags & ThreadFlags::Detached)) {
|
||||
attr.flags |= PthreadAttrFlags::Detached;
|
||||
}
|
||||
pthread->lock.unlock();
|
||||
if (ret == 0) {
|
||||
memcpy(dst, &attr, sizeof(PthreadAttr));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_attr_getaffinity_np(const PthreadAttrT* pattr, size_t cpusetsize,
|
||||
Cpuset* cpusetp) {
|
||||
if (pattr == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
PthreadAttrT attr = *pattr;
|
||||
if (attr == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
if (attr->cpuset != nullptr)
|
||||
memcpy(cpusetp, attr->cpuset, std::min(cpusetsize, attr->cpusetsize));
|
||||
else
|
||||
memset(cpusetp, -1, sizeof(Cpuset));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_attr_setaffinity_np(PthreadAttrT* pattr, size_t cpusetsize,
|
||||
const Cpuset* cpusetp) {
|
||||
if (pattr == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
PthreadAttrT attr = *pattr;
|
||||
if (attr == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
if (cpusetsize == 0 || cpusetp == nullptr) {
|
||||
if (attr->cpuset != nullptr) {
|
||||
free(attr->cpuset);
|
||||
attr->cpuset = NULL;
|
||||
attr->cpusetsize = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (attr->cpuset == nullptr) {
|
||||
attr->cpuset = (Cpuset*)calloc(1, sizeof(Cpuset));
|
||||
attr->cpusetsize = sizeof(Cpuset);
|
||||
}
|
||||
memcpy(attr->cpuset, cpusetp, sizeof(Cpuset));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadAttrGetaffinity(PthreadAttrT* param_1, Cpuset* mask) {
|
||||
Cpuset cpuset;
|
||||
const int ret = posix_pthread_attr_getaffinity_np(param_1, 0x10, &cpuset);
|
||||
if (ret == 0) {
|
||||
*mask = cpuset;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadAttrSetaffinity(PthreadAttrT* attr, const Cpuset mask) {
|
||||
return posix_pthread_attr_setaffinity_np(attr, 0x10, &mask);
|
||||
}
|
||||
|
||||
void RegisterThreadAttr(Core::Loader::SymbolsResolver* sym) {
|
||||
// Posix
|
||||
LIB_FUNCTION("wtkt-teR1so", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_attr_init);
|
||||
LIB_FUNCTION("2Q0z6rnBrTE", "libScePosix", 1, "libkernel", 1, 1,
|
||||
posix_pthread_attr_setstacksize);
|
||||
LIB_FUNCTION("RtLRV-pBTTY", "libScePosix", 1, "libkernel", 1, 1,
|
||||
posix_pthread_attr_getschedpolicy);
|
||||
LIB_FUNCTION("E+tyo3lp5Lw", "libScePosix", 1, "libkernel", 1, 1,
|
||||
posix_pthread_attr_setdetachstate);
|
||||
LIB_FUNCTION("zHchY8ft5pk", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_attr_destroy);
|
||||
LIB_FUNCTION("euKRgm0Vn2M", "libScePosix", 1, "libkernel", 1, 1,
|
||||
posix_pthread_attr_setschedparam);
|
||||
LIB_FUNCTION("7ZlAakEf0Qg", "libScePosix", 1, "libkernel", 1, 1,
|
||||
posix_pthread_attr_setinheritsched);
|
||||
LIB_FUNCTION("0qOtCR-ZHck", "libScePosix", 1, "libkernel", 1, 1,
|
||||
posix_pthread_attr_getstacksize);
|
||||
LIB_FUNCTION("VUT1ZSrHT0I", "libScePosix", 1, "libkernel", 1, 1,
|
||||
posix_pthread_attr_getdetachstate);
|
||||
|
||||
// Orbis
|
||||
LIB_FUNCTION("4+h9EzwKF4I", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_attr_setschedpolicy));
|
||||
LIB_FUNCTION("-Wreprtu0Qs", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_attr_setdetachstate));
|
||||
LIB_FUNCTION("JaRMy+QcpeU", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_attr_getdetachstate));
|
||||
LIB_FUNCTION("eXbUSpEaTsA", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_attr_setinheritsched));
|
||||
LIB_FUNCTION("DzES9hQF4f4", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_attr_setschedparam));
|
||||
LIB_FUNCTION("nsYoNRywwNg", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_attr_init));
|
||||
LIB_FUNCTION("62KCwEMmzcM", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_attr_destroy));
|
||||
LIB_FUNCTION("-quPa4SEJUw", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_attr_getstack));
|
||||
LIB_FUNCTION("Bvn74vj6oLo", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_attr_setstack));
|
||||
LIB_FUNCTION("Ru36fiTtJzA", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_attr_getstackaddr));
|
||||
LIB_FUNCTION("-fA+7ZlGDQs", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_attr_getstacksize));
|
||||
LIB_FUNCTION("x1X76arYMxU", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_attr_get_np));
|
||||
LIB_FUNCTION("FXPWHNk8Of0", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_attr_getschedparam));
|
||||
LIB_FUNCTION("UTXzJbWhhTE", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_attr_setstacksize));
|
||||
LIB_FUNCTION("F+yfmduIBB8", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_attr_setstackaddr));
|
||||
LIB_FUNCTION("El+cQ20DynU", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_attr_setguardsize));
|
||||
LIB_FUNCTION("8+s5BzZjxSg", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(scePthreadAttrGetaffinity));
|
||||
LIB_FUNCTION("3qxgM4ezETA", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(scePthreadAttrSetaffinity));
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
54
src/core/libraries/kernel/threads/pthread_clean.cpp
Normal file
54
src/core/libraries/kernel/threads/pthread_clean.cpp
Normal file
|
@ -0,0 +1,54 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/libraries/kernel/threads/pthread.h"
|
||||
#include "core/libraries/libs.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
void PS4_SYSV_ABI __pthread_cleanup_push_imp(PthreadCleanupFunc routine, void* arg,
|
||||
PthreadCleanup* newbuf) {
|
||||
newbuf->routine = routine;
|
||||
newbuf->routine_arg = arg;
|
||||
newbuf->onheap = 0;
|
||||
g_curthread->cleanup.push_front(newbuf);
|
||||
}
|
||||
|
||||
void PS4_SYSV_ABI posix_pthread_cleanup_push(PthreadCleanupFunc routine, void* arg) {
|
||||
Pthread* curthread = g_curthread;
|
||||
PthreadCleanup* newbuf = new PthreadCleanup{};
|
||||
if (newbuf == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
newbuf->routine = routine;
|
||||
newbuf->routine_arg = arg;
|
||||
newbuf->onheap = 1;
|
||||
curthread->cleanup.push_front(newbuf);
|
||||
}
|
||||
|
||||
void PS4_SYSV_ABI posix_pthread_cleanup_pop(int execute) {
|
||||
Pthread* curthread = g_curthread;
|
||||
if (!curthread->cleanup.empty()) {
|
||||
PthreadCleanup* old = curthread->cleanup.front();
|
||||
curthread->cleanup.pop_front();
|
||||
if (execute) {
|
||||
old->routine(old->routine_arg);
|
||||
}
|
||||
if (old->onheap) {
|
||||
delete old;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterPthreadClean(Core::Loader::SymbolsResolver* sym) {
|
||||
// Posix
|
||||
LIB_FUNCTION("4ZeZWcMsAV0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cleanup_push);
|
||||
LIB_FUNCTION("RVxb0Ssa5t0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cleanup_pop);
|
||||
|
||||
// Posix-Kernel
|
||||
LIB_FUNCTION("1xvtUVx1-Sg", "libkernel", 1, "libkernel", 1, 1, __pthread_cleanup_push_imp);
|
||||
LIB_FUNCTION("iWsFlYMf3Kw", "libkernel", 1, "libkernel", 1, 1, posix_pthread_cleanup_pop);
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
158
src/core/libraries/kernel/threads/pthread_spec.cpp
Normal file
158
src/core/libraries/kernel/threads/pthread_spec.cpp
Normal file
|
@ -0,0 +1,158 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/kernel.h"
|
||||
#include "core/libraries/kernel/threads/pthread.h"
|
||||
#include "core/libraries/libs.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
static constexpr u32 PthreadKeysMax = 256;
|
||||
static constexpr u32 PthreadDestructorIterations = 4;
|
||||
|
||||
static std::array<PthreadKey, PthreadKeysMax> ThreadKeytable{};
|
||||
static std::mutex KeytableLock;
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_key_create(PthreadKeyT* key, PthreadKeyDestructor destructor) {
|
||||
std::scoped_lock lk{KeytableLock};
|
||||
const auto it = std::ranges::find(ThreadKeytable, 0, &PthreadKey::allocated);
|
||||
if (it != ThreadKeytable.end()) {
|
||||
it->allocated = 1;
|
||||
it->destructor = destructor;
|
||||
it->seqno++;
|
||||
*key = std::distance(ThreadKeytable.begin(), it);
|
||||
return 0;
|
||||
}
|
||||
return POSIX_EAGAIN;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_key_delete(PthreadKeyT key) {
|
||||
if (key >= PthreadKeysMax) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
|
||||
std::scoped_lock lk{KeytableLock};
|
||||
if (!ThreadKeytable[key].allocated) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
|
||||
ThreadKeytable[key].allocated = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void _thread_cleanupspecific() {
|
||||
Pthread* curthread = g_curthread;
|
||||
PthreadKeyDestructor destructor;
|
||||
const void* data = NULL;
|
||||
|
||||
if (curthread->specific == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_lock lk{KeytableLock};
|
||||
for (int i = 0; (i < PthreadDestructorIterations) && (curthread->specific_data_count > 0);
|
||||
i++) {
|
||||
for (int key = 0; (key < PthreadKeysMax) && (curthread->specific_data_count > 0); key++) {
|
||||
destructor = nullptr;
|
||||
|
||||
if (ThreadKeytable[key].allocated && (curthread->specific[key].data != nullptr)) {
|
||||
if (curthread->specific[key].seqno == ThreadKeytable[key].seqno) {
|
||||
data = curthread->specific[key].data;
|
||||
destructor = ThreadKeytable[key].destructor;
|
||||
}
|
||||
curthread->specific[key].data = nullptr;
|
||||
curthread->specific_data_count--;
|
||||
} else if (curthread->specific[key].data != NULL) {
|
||||
/*
|
||||
* This can happen if the key is deleted via
|
||||
* pthread_key_delete without first setting the value
|
||||
* to NULL in all threads. POSIX says that the
|
||||
* destructor is not invoked in this case.
|
||||
*/
|
||||
curthread->specific[key].data = nullptr;
|
||||
curthread->specific_data_count--;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there is a destructor, call it
|
||||
* with the key table entry unlocked:
|
||||
*/
|
||||
if (destructor != nullptr) {
|
||||
/*
|
||||
* Don't hold the lock while calling the
|
||||
* destructor:
|
||||
*/
|
||||
lk.unlock();
|
||||
Core::ExecuteGuest(destructor, data);
|
||||
lk.lock();
|
||||
}
|
||||
}
|
||||
}
|
||||
delete[] curthread->specific;
|
||||
curthread->specific = nullptr;
|
||||
if (curthread->specific_data_count > 0) {
|
||||
LOG_WARNING(Lib_Kernel, "Thread has exited with leftover thread-specific data");
|
||||
}
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_setspecific(PthreadKeyT key, const void* value) {
|
||||
int ret = 0;
|
||||
Pthread* pthread = g_curthread;
|
||||
|
||||
if (!pthread->specific) {
|
||||
pthread->specific = new PthreadSpecificElem[PthreadKeysMax]{};
|
||||
if (!pthread->specific) {
|
||||
return POSIX_ENOMEM;
|
||||
}
|
||||
}
|
||||
if (key >= PthreadKeysMax) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
if (!ThreadKeytable[key].allocated) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
|
||||
if (pthread->specific[key].data == nullptr) {
|
||||
if (value != nullptr) {
|
||||
pthread->specific_data_count++;
|
||||
}
|
||||
} else if (value == nullptr) {
|
||||
pthread->specific_data_count--;
|
||||
}
|
||||
pthread->specific[key].data = value;
|
||||
pthread->specific[key].seqno = ThreadKeytable[key].seqno;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const void* PS4_SYSV_ABI posix_pthread_getspecific(PthreadKeyT key) {
|
||||
Pthread* pthread = g_curthread;
|
||||
|
||||
if (!pthread->specific || key >= PthreadKeysMax) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (ThreadKeytable[key].allocated &&
|
||||
(pthread->specific[key].seqno == ThreadKeytable[key].seqno)) {
|
||||
return pthread->specific[key].data;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void RegisterSpec(Core::Loader::SymbolsResolver* sym) {
|
||||
// Posix
|
||||
LIB_FUNCTION("mqULNdimTn0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_key_create);
|
||||
LIB_FUNCTION("6BpEZuDT7YI", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_key_delete);
|
||||
LIB_FUNCTION("0-KXaS70xy4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_getspecific);
|
||||
LIB_FUNCTION("WrOLvHU0yQM", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setspecific);
|
||||
|
||||
// Orbis
|
||||
LIB_FUNCTION("geDaqgH9lTg", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_key_create));
|
||||
LIB_FUNCTION("eoht7mQOCmo", "libkernel", 1, "libkernel", 1, 1, posix_pthread_getspecific);
|
||||
LIB_FUNCTION("+BzXYkqYeLE", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_setspecific));
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
|
@ -1,326 +1,243 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/kernel.h"
|
||||
#include "core/libraries/kernel/threads/pthread.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "threads.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
extern PThreadCxt* g_pthread_cxt;
|
||||
static std::mutex RwlockStaticLock;
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_rwlock_destroy(OrbisPthreadRwlock* rwlock) {
|
||||
int result = pthread_rwlock_destroy(&(*rwlock)->pth_rwlock);
|
||||
delete *rwlock;
|
||||
#define THR_RWLOCK_INITIALIZER ((PthreadRwlock*)NULL)
|
||||
#define THR_RWLOCK_DESTROYED ((PthreadRwlock*)1)
|
||||
|
||||
#define CHECK_AND_INIT_RWLOCK \
|
||||
if (prwlock = (*rwlock); prwlock <= THR_RWLOCK_DESTROYED) [[unlikely]] { \
|
||||
if (prwlock == THR_RWLOCK_INITIALIZER) { \
|
||||
int ret; \
|
||||
ret = InitStatic(g_curthread, rwlock); \
|
||||
if (ret) \
|
||||
return (ret); \
|
||||
} else if (prwlock == THR_RWLOCK_DESTROYED) { \
|
||||
return POSIX_EINVAL; \
|
||||
} \
|
||||
prwlock = *rwlock; \
|
||||
}
|
||||
|
||||
static int RwlockInit(PthreadRwlockT* rwlock, const PthreadRwlockAttrT* attr) {
|
||||
PthreadRwlock* prwlock = new PthreadRwlock{};
|
||||
if (prwlock == nullptr) {
|
||||
return POSIX_ENOMEM;
|
||||
}
|
||||
*rwlock = prwlock;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_rwlock_destroy(PthreadRwlockT* rwlock) {
|
||||
PthreadRwlockT prwlock = *rwlock;
|
||||
if (prwlock == THR_RWLOCK_INITIALIZER) {
|
||||
return 0;
|
||||
}
|
||||
if (prwlock == THR_RWLOCK_DESTROYED) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
*rwlock = THR_RWLOCK_DESTROYED;
|
||||
delete prwlock;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int InitStatic(Pthread* thread, PthreadRwlockT* rwlock) {
|
||||
std::scoped_lock lk{RwlockStaticLock};
|
||||
if (*rwlock == THR_RWLOCK_INITIALIZER) {
|
||||
return RwlockInit(rwlock, nullptr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_rwlock_init(PthreadRwlockT* rwlock, const PthreadRwlockAttrT* attr) {
|
||||
*rwlock = nullptr;
|
||||
if (result != 0) {
|
||||
LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlock_destroy: error = {}", result);
|
||||
result += ORBIS_KERNEL_ERROR_UNKNOWN;
|
||||
return RwlockInit(rwlock, attr);
|
||||
}
|
||||
|
||||
int PthreadRwlock::Rdlock(const OrbisKernelTimespec* abstime) {
|
||||
Pthread* curthread = g_curthread;
|
||||
|
||||
/*
|
||||
* POSIX said the validity of the abstimeout parameter need
|
||||
* not be checked if the lock can be immediately acquired.
|
||||
*/
|
||||
if (lock.try_lock_shared()) {
|
||||
curthread->rdlock_count++;
|
||||
return 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_rwlock_init(OrbisPthreadRwlock* rwlock,
|
||||
const OrbisPthreadRwlockattr* attr, const char* name) {
|
||||
*rwlock = new PthreadRwInternal{};
|
||||
if (attr == nullptr || *attr == nullptr) {
|
||||
attr = g_pthread_cxt->getDefaultRwattr();
|
||||
}
|
||||
int result = pthread_rwlock_init(&(*rwlock)->pth_rwlock, &(*attr)->attr_rwlock);
|
||||
if (result != 0) {
|
||||
LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlock_init: error = {}", result);
|
||||
}
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
OrbisPthreadRwlock* createRwlock(OrbisPthreadRwlock* rwlock) {
|
||||
if (rwlock == nullptr || *rwlock != nullptr) {
|
||||
return rwlock;
|
||||
}
|
||||
static std::mutex mutex;
|
||||
std::scoped_lock lk{mutex};
|
||||
if (*rwlock != nullptr) {
|
||||
return rwlock;
|
||||
}
|
||||
const VAddr addr = std::bit_cast<VAddr>(rwlock);
|
||||
const auto name = fmt::format("rwlock{:#x}", addr);
|
||||
posix_pthread_rwlock_init(rwlock, nullptr, name.c_str());
|
||||
return rwlock;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_rwlock_rdlock(OrbisPthreadRwlock* rwlock) {
|
||||
rwlock = createRwlock(rwlock);
|
||||
int result = pthread_rwlock_rdlock(&(*rwlock)->pth_rwlock);
|
||||
if (result != 0) {
|
||||
LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlock_rdlock: error = {}", result);
|
||||
result += ORBIS_KERNEL_ERROR_UNKNOWN;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_rwlock_reltimedrdlock_np() {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_rwlock_reltimedwrlock_np() {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_rwlock_setname_np() {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_rwlock_timedrdlock() {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_rwlock_timedwrlock() {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_rwlock_tryrdlock(OrbisPthreadRwlock* rwlock) {
|
||||
rwlock = createRwlock(rwlock);
|
||||
if (rwlock == nullptr) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
int result = pthread_rwlock_tryrdlock(&(*rwlock)->pth_rwlock);
|
||||
if (result != 0) {
|
||||
LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlock_tryrdlock: error = {}", result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_rwlock_trywrlock(OrbisPthreadRwlock* rwlock) {
|
||||
rwlock = createRwlock(rwlock);
|
||||
if (rwlock == nullptr) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
int result = pthread_rwlock_trywrlock(&(*rwlock)->pth_rwlock);
|
||||
if (result != 0) {
|
||||
LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlock_trywrlock: error = {}", result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_rwlock_unlock(OrbisPthreadRwlock* rwlock) {
|
||||
rwlock = createRwlock(rwlock);
|
||||
if (rwlock == nullptr) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
int result = pthread_rwlock_unlock(&(*rwlock)->pth_rwlock);
|
||||
if (result != 0) {
|
||||
LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlock_unlock: error = {}", result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_rwlock_wrlock(OrbisPthreadRwlock* rwlock) {
|
||||
rwlock = createRwlock(rwlock);
|
||||
if (rwlock == nullptr) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
int result = pthread_rwlock_wrlock(&(*rwlock)->pth_rwlock);
|
||||
if (result != 0) {
|
||||
LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlock_wrlock: error = {}", result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_rwlockattr_destroy(OrbisPthreadRwlockattr* attr) {
|
||||
int result = pthread_rwlockattr_destroy(&(*attr)->attr_rwlock);
|
||||
delete *attr;
|
||||
*attr = nullptr;
|
||||
if (result != 0) {
|
||||
LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlockattr_destroy: error = {}", result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_rwlockattr_getpshared() {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_rwlockattr_gettype_np() {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_rwlockattr_init(OrbisPthreadRwlockattr* attr) {
|
||||
*attr = new PthreadRwLockAttrInternal{};
|
||||
int result = pthread_rwlockattr_init(&(*attr)->attr_rwlock);
|
||||
if (result != 0) {
|
||||
LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlockattr_init: error = {}", result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_rwlockattr_setpshared() {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_rwlockattr_settype_np() {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadRwlockattrDestroy(OrbisPthreadRwlockattr* attr) {
|
||||
int result = pthread_rwlockattr_destroy(&(*attr)->attr_rwlock);
|
||||
delete *attr;
|
||||
*attr = nullptr;
|
||||
if (result != 0) {
|
||||
LOG_ERROR(Kernel_Pthread, "scePthreadRwlockattrDestroy: error = {}", result);
|
||||
result += ORBIS_KERNEL_ERROR_UNKNOWN;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadRwlockattrGetpshared() {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadRwlockattrGettype() {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadRwlockattrInit(OrbisPthreadRwlockattr* attr) {
|
||||
*attr = new PthreadRwLockAttrInternal{};
|
||||
int result = pthread_rwlockattr_init(&(*attr)->attr_rwlock);
|
||||
if (result != 0) {
|
||||
LOG_ERROR(Kernel_Pthread, "scePthreadRwlockattrInit: error = {}", result);
|
||||
result += ORBIS_KERNEL_ERROR_UNKNOWN;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadRwlockattrSetpshared() {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadRwlockattrSettype() {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadRwlockDestroy(OrbisPthreadRwlock* rwlock) {
|
||||
int result = pthread_rwlock_destroy(&(*rwlock)->pth_rwlock);
|
||||
delete *rwlock;
|
||||
*rwlock = nullptr;
|
||||
if (result != 0) {
|
||||
LOG_ERROR(Kernel_Pthread, "scePthreadRwlockDestroy: error = {}", result);
|
||||
result += ORBIS_KERNEL_ERROR_UNKNOWN;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadRwlockInit(OrbisPthreadRwlock* rwlock,
|
||||
const OrbisPthreadRwlockattr* attr, const char* name) {
|
||||
*rwlock = new PthreadRwInternal{};
|
||||
if (rwlock == nullptr || *rwlock == nullptr) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
if (abstime && (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0)) [[unlikely]] {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
|
||||
if (attr == nullptr || *attr == nullptr) {
|
||||
attr = g_pthread_cxt->getDefaultRwattr();
|
||||
// Note: On interruption an attempt to relock the mutex is made.
|
||||
if (abstime != nullptr) {
|
||||
if (!lock.try_lock_shared_until(abstime->TimePoint())) {
|
||||
return POSIX_ETIMEDOUT;
|
||||
}
|
||||
} else {
|
||||
lock.lock_shared();
|
||||
}
|
||||
if (name != nullptr) {
|
||||
(*rwlock)->name = name;
|
||||
}
|
||||
int result = pthread_rwlock_init(&(*rwlock)->pth_rwlock, &(*attr)->attr_rwlock);
|
||||
if (result != 0) {
|
||||
LOG_ERROR(Kernel_Pthread, "scePthreadRwlockInit: error = {}", result);
|
||||
result += ORBIS_KERNEL_ERROR_UNKNOWN;
|
||||
}
|
||||
return ORBIS_OK;
|
||||
|
||||
curthread->rdlock_count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadRwlockRdlock(OrbisPthreadRwlock* rwlock) {
|
||||
if (rwlock == nullptr || *rwlock == nullptr) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
int PthreadRwlock::Wrlock(const OrbisKernelTimespec* abstime) {
|
||||
Pthread* curthread = g_curthread;
|
||||
|
||||
/*
|
||||
* POSIX said the validity of the abstimeout parameter need
|
||||
* not be checked if the lock can be immediately acquired.
|
||||
*/
|
||||
if (lock.try_lock()) {
|
||||
owner = curthread;
|
||||
return 0;
|
||||
}
|
||||
int result = pthread_rwlock_rdlock(&(*rwlock)->pth_rwlock);
|
||||
if (result != 0) {
|
||||
LOG_ERROR(Kernel_Pthread, "scePthreadRwlockRdlock: error = {}", result);
|
||||
result += ORBIS_KERNEL_ERROR_UNKNOWN;
|
||||
|
||||
if (abstime && (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0)) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
return result;
|
||||
|
||||
// Note: On interruption an attempt to relock the mutex is made.
|
||||
if (abstime != nullptr) {
|
||||
if (!lock.try_lock_until(abstime->TimePoint())) {
|
||||
return POSIX_ETIMEDOUT;
|
||||
}
|
||||
} else {
|
||||
lock.lock();
|
||||
}
|
||||
|
||||
owner = curthread;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadRwlockTimedrdlock() {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
int PS4_SYSV_ABI posix_pthread_rwlock_rdlock(PthreadRwlockT* rwlock) {
|
||||
PthreadRwlockT prwlock{};
|
||||
CHECK_AND_INIT_RWLOCK
|
||||
return prwlock->Rdlock(nullptr);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadRwlockTimedwrlock() {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
int PS4_SYSV_ABI posix_pthread_rwlock_timedrdlock(PthreadRwlockT* rwlock,
|
||||
const OrbisKernelTimespec* abstime) {
|
||||
PthreadRwlockT prwlock{};
|
||||
CHECK_AND_INIT_RWLOCK
|
||||
return prwlock->Rdlock(abstime);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadRwlockTryrdlock(OrbisPthreadRwlock* rwlock) {
|
||||
if (rwlock == nullptr || *rwlock == nullptr) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
int PS4_SYSV_ABI posix_pthread_rwlock_tryrdlock(PthreadRwlockT* rwlock) {
|
||||
Pthread* curthread = g_curthread;
|
||||
PthreadRwlockT prwlock{};
|
||||
CHECK_AND_INIT_RWLOCK
|
||||
|
||||
if (!prwlock->lock.try_lock_shared()) {
|
||||
return POSIX_EBUSY;
|
||||
}
|
||||
int result = pthread_rwlock_tryrdlock(&(*rwlock)->pth_rwlock);
|
||||
if (result != 0) {
|
||||
LOG_ERROR(Kernel_Pthread, "scePthreadRwlockTryrdlock: error = {}", result);
|
||||
result += ORBIS_KERNEL_ERROR_UNKNOWN;
|
||||
}
|
||||
return result;
|
||||
|
||||
curthread->rdlock_count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadRwlockTrywrlock(OrbisPthreadRwlock* rwlock) {
|
||||
if (rwlock == nullptr || *rwlock == nullptr) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
int PS4_SYSV_ABI posix_pthread_rwlock_trywrlock(PthreadRwlockT* rwlock) {
|
||||
Pthread* curthread = g_curthread;
|
||||
PthreadRwlockT prwlock{};
|
||||
CHECK_AND_INIT_RWLOCK
|
||||
|
||||
if (!prwlock->lock.try_lock()) {
|
||||
return POSIX_EBUSY;
|
||||
}
|
||||
int result = pthread_rwlock_trywrlock(&(*rwlock)->pth_rwlock);
|
||||
if (result != 0) {
|
||||
LOG_ERROR(Kernel_Pthread, "scePthreadRwlockTrywrlock: error = {}", result);
|
||||
result += ORBIS_KERNEL_ERROR_UNKNOWN;
|
||||
}
|
||||
return result;
|
||||
prwlock->owner = curthread;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadRwlockUnlock(OrbisPthreadRwlock* rwlock) {
|
||||
if (rwlock == nullptr || *rwlock == nullptr) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
int result = pthread_rwlock_unlock(&(*rwlock)->pth_rwlock);
|
||||
if (result != 0) {
|
||||
LOG_ERROR(Kernel_Pthread, "scePthreadRwlockUnlock: error = {}", result);
|
||||
result += ORBIS_KERNEL_ERROR_UNKNOWN;
|
||||
}
|
||||
return result;
|
||||
int PS4_SYSV_ABI posix_pthread_rwlock_wrlock(PthreadRwlockT* rwlock) {
|
||||
PthreadRwlockT prwlock{};
|
||||
CHECK_AND_INIT_RWLOCK
|
||||
return prwlock->Wrlock(nullptr);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadRwlockWrlock(OrbisPthreadRwlock* rwlock) {
|
||||
rwlock = createRwlock(rwlock);
|
||||
int result = pthread_rwlock_wrlock(&(*rwlock)->pth_rwlock);
|
||||
if (result != 0) {
|
||||
LOG_ERROR(Kernel_Pthread, "scePthreadRwlockWrlock: error = {}", result);
|
||||
result += ORBIS_KERNEL_ERROR_UNKNOWN;
|
||||
}
|
||||
return result;
|
||||
int PS4_SYSV_ABI posix_pthread_rwlock_timedwrlock(PthreadRwlockT* rwlock,
|
||||
const OrbisKernelTimespec* abstime) {
|
||||
PthreadRwlockT prwlock{};
|
||||
CHECK_AND_INIT_RWLOCK
|
||||
return prwlock->Wrlock(abstime);
|
||||
}
|
||||
|
||||
void RwlockSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
|
||||
int PS4_SYSV_ABI posix_pthread_rwlock_unlock(PthreadRwlockT* rwlock) {
|
||||
Pthread* curthread = g_curthread;
|
||||
PthreadRwlockT prwlock = *rwlock;
|
||||
if (prwlock <= THR_RWLOCK_DESTROYED) [[unlikely]] {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
|
||||
if (prwlock->owner == curthread) {
|
||||
prwlock->lock.unlock();
|
||||
prwlock->owner = nullptr;
|
||||
} else {
|
||||
prwlock->lock.unlock_shared();
|
||||
if (prwlock->owner == nullptr) {
|
||||
curthread->rdlock_count--;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_rwlockattr_destroy(PthreadRwlockAttrT* rwlockattr) {
|
||||
if (rwlockattr == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
PthreadRwlockAttrT prwlockattr = *rwlockattr;
|
||||
if (prwlockattr == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
|
||||
delete prwlockattr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_rwlockattr_getpshared(const PthreadRwlockAttrT* rwlockattr,
|
||||
int* pshared) {
|
||||
*pshared = (*rwlockattr)->pshared;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_rwlockattr_init(PthreadRwlockAttrT* rwlockattr) {
|
||||
if (rwlockattr == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
|
||||
PthreadRwlockAttrT prwlockattr = new PthreadRwlockAttr{};
|
||||
if (prwlockattr == nullptr) {
|
||||
return POSIX_ENOMEM;
|
||||
}
|
||||
|
||||
prwlockattr->pshared = 0;
|
||||
*rwlockattr = prwlockattr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_rwlockattr_setpshared(PthreadRwlockAttrT* rwlockattr, int pshared) {
|
||||
/* Only PTHREAD_PROCESS_PRIVATE is supported. */
|
||||
if (pshared != 0) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
|
||||
(*rwlockattr)->pshared = pshared;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RegisterRwlock(Core::Loader::SymbolsResolver* sym) {
|
||||
// Posix-Kernel
|
||||
LIB_FUNCTION("1471ajPzxh0", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlock_destroy);
|
||||
LIB_FUNCTION("ytQULN-nhL4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlock_init);
|
||||
LIB_FUNCTION("iGjsr1WAtI0", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlock_rdlock);
|
||||
LIB_FUNCTION("dYv-+If2GPk", "libkernel", 1, "libkernel", 1, 1,
|
||||
posix_pthread_rwlock_reltimedrdlock_np);
|
||||
LIB_FUNCTION("RRnSj8h8VR4", "libkernel", 1, "libkernel", 1, 1,
|
||||
posix_pthread_rwlock_reltimedwrlock_np);
|
||||
LIB_FUNCTION("Uwxgnsi3xeM", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlock_setname_np);
|
||||
LIB_FUNCTION("lb8lnYo-o7k", "libkernel", 1, "libkernel", 1, 1,
|
||||
posix_pthread_rwlock_timedrdlock);
|
||||
LIB_FUNCTION("9zklzAl9CGM", "libkernel", 1, "libkernel", 1, 1,
|
||||
|
@ -333,13 +250,11 @@ void RwlockSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
|
|||
posix_pthread_rwlockattr_destroy);
|
||||
LIB_FUNCTION("VqEMuCv-qHY", "libkernel", 1, "libkernel", 1, 1,
|
||||
posix_pthread_rwlockattr_getpshared);
|
||||
LIB_FUNCTION("l+bG5fsYkhg", "libkernel", 1, "libkernel", 1, 1,
|
||||
posix_pthread_rwlockattr_gettype_np);
|
||||
LIB_FUNCTION("xFebsA4YsFI", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlockattr_init);
|
||||
LIB_FUNCTION("OuKg+kRDD7U", "libkernel", 1, "libkernel", 1, 1,
|
||||
posix_pthread_rwlockattr_setpshared);
|
||||
LIB_FUNCTION("8NuOHiTr1Vw", "libkernel", 1, "libkernel", 1, 1,
|
||||
posix_pthread_rwlockattr_settype_np);
|
||||
|
||||
// Posix
|
||||
LIB_FUNCTION("1471ajPzxh0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_rwlock_destroy);
|
||||
LIB_FUNCTION("ytQULN-nhL4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_rwlock_init);
|
||||
LIB_FUNCTION("iGjsr1WAtI0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_rwlock_rdlock);
|
||||
|
@ -357,27 +272,37 @@ void RwlockSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
|
|||
posix_pthread_rwlockattr_destroy);
|
||||
LIB_FUNCTION("VqEMuCv-qHY", "libScePosix", 1, "libkernel", 1, 1,
|
||||
posix_pthread_rwlockattr_getpshared);
|
||||
LIB_FUNCTION("l+bG5fsYkhg", "libScePosix", 1, "libkernel", 1, 1,
|
||||
posix_pthread_rwlockattr_gettype_np);
|
||||
LIB_FUNCTION("xFebsA4YsFI", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_rwlockattr_init);
|
||||
LIB_FUNCTION("OuKg+kRDD7U", "libScePosix", 1, "libkernel", 1, 1,
|
||||
posix_pthread_rwlockattr_setpshared);
|
||||
LIB_FUNCTION("8NuOHiTr1Vw", "libScePosix", 1, "libkernel", 1, 1,
|
||||
posix_pthread_rwlockattr_settype_np);
|
||||
LIB_FUNCTION("i2ifZ3fS2fo", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockattrDestroy);
|
||||
LIB_FUNCTION("LcOZBHGqbFk", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockattrGetpshared);
|
||||
LIB_FUNCTION("Kyls1ChFyrc", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockattrGettype);
|
||||
LIB_FUNCTION("yOfGg-I1ZII", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockattrInit);
|
||||
LIB_FUNCTION("-ZvQH18j10c", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockattrSetpshared);
|
||||
LIB_FUNCTION("h-OifiouBd8", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockattrSettype);
|
||||
LIB_FUNCTION("BB+kb08Tl9A", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockDestroy);
|
||||
LIB_FUNCTION("6ULAa0fq4jA", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockInit);
|
||||
LIB_FUNCTION("Ox9i0c7L5w0", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockRdlock);
|
||||
LIB_FUNCTION("iPtZRWICjrM", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockTimedrdlock);
|
||||
LIB_FUNCTION("adh--6nIqTk", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockTimedwrlock);
|
||||
LIB_FUNCTION("XD3mDeybCnk", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockTryrdlock);
|
||||
LIB_FUNCTION("bIHoZCTomsI", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockTrywrlock);
|
||||
LIB_FUNCTION("+L98PIbGttk", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockUnlock);
|
||||
LIB_FUNCTION("mqdNorrB+gI", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockWrlock);
|
||||
|
||||
// Orbis
|
||||
LIB_FUNCTION("i2ifZ3fS2fo", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_rwlockattr_destroy));
|
||||
LIB_FUNCTION("LcOZBHGqbFk", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_rwlockattr_getpshared));
|
||||
LIB_FUNCTION("yOfGg-I1ZII", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_rwlockattr_init));
|
||||
LIB_FUNCTION("-ZvQH18j10c", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_rwlockattr_setpshared));
|
||||
LIB_FUNCTION("BB+kb08Tl9A", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_rwlock_destroy));
|
||||
LIB_FUNCTION("6ULAa0fq4jA", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_rwlock_init));
|
||||
LIB_FUNCTION("Ox9i0c7L5w0", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_rwlock_rdlock));
|
||||
LIB_FUNCTION("iPtZRWICjrM", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_rwlock_timedrdlock));
|
||||
LIB_FUNCTION("adh--6nIqTk", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_rwlock_timedwrlock));
|
||||
LIB_FUNCTION("XD3mDeybCnk", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_rwlock_tryrdlock));
|
||||
LIB_FUNCTION("bIHoZCTomsI", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_rwlock_trywrlock));
|
||||
LIB_FUNCTION("+L98PIbGttk", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_rwlock_unlock));
|
||||
LIB_FUNCTION("mqdNorrB+gI", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_rwlock_wrlock));
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
||||
|
|
|
@ -4,23 +4,31 @@
|
|||
#include <condition_variable>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include <semaphore>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/kernel.h"
|
||||
#include "core/libraries/kernel/threads/pthread.h"
|
||||
#include "core/libraries/kernel/time.h"
|
||||
#include "core/libraries/libs.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
class Semaphore {
|
||||
constexpr int ORBIS_KERNEL_SEM_VALUE_MAX = 0x7FFFFFFF;
|
||||
|
||||
struct PthreadSem {
|
||||
explicit PthreadSem(s32 value_) : semaphore{value_}, value{value_} {}
|
||||
|
||||
std::counting_semaphore<ORBIS_KERNEL_SEM_VALUE_MAX> semaphore;
|
||||
std::atomic<s32> value;
|
||||
};
|
||||
|
||||
class OrbisSem {
|
||||
public:
|
||||
Semaphore(s32 init_count, s32 max_count, std::string_view name, bool is_fifo)
|
||||
OrbisSem(s32 init_count, s32 max_count, std::string_view name, bool is_fifo)
|
||||
: name{name}, token_count{init_count}, max_count{max_count}, init_count{init_count},
|
||||
is_fifo{is_fifo} {}
|
||||
~Semaphore() {
|
||||
ASSERT(wait_list.empty());
|
||||
}
|
||||
~OrbisSem() = default;
|
||||
|
||||
int Wait(bool can_block, s32 need_count, u32* timeout) {
|
||||
std::unique_lock lk{mutex};
|
||||
|
@ -41,7 +49,9 @@ public:
|
|||
const auto it = AddWaiter(&waiter);
|
||||
|
||||
// Perform the wait.
|
||||
const s32 result = waiter.Wait(lk, timeout);
|
||||
lk.unlock();
|
||||
const s32 result = waiter.Wait(timeout);
|
||||
lk.lock();
|
||||
if (result == SCE_KERNEL_ERROR_ETIMEDOUT) {
|
||||
wait_list.erase(it);
|
||||
}
|
||||
|
@ -64,7 +74,7 @@ public:
|
|||
}
|
||||
it = wait_list.erase(it);
|
||||
token_count -= waiter->need_count;
|
||||
waiter->cv.notify_one();
|
||||
waiter->sema.release();
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -77,30 +87,35 @@ public:
|
|||
}
|
||||
for (auto* waiter : wait_list) {
|
||||
waiter->was_cancled = true;
|
||||
waiter->cv.notify_one();
|
||||
waiter->sema.release();
|
||||
}
|
||||
wait_list.clear();
|
||||
token_count = set_count < 0 ? init_count : set_count;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void Delete() {
|
||||
std::scoped_lock lk{mutex};
|
||||
for (auto* waiter : wait_list) {
|
||||
waiter->was_deleted = true;
|
||||
waiter->sema.release();
|
||||
}
|
||||
wait_list.clear();
|
||||
}
|
||||
|
||||
public:
|
||||
struct WaitingThread {
|
||||
std::condition_variable cv;
|
||||
std::binary_semaphore sema;
|
||||
u32 priority;
|
||||
s32 need_count;
|
||||
bool was_deleted{};
|
||||
bool was_cancled{};
|
||||
|
||||
explicit WaitingThread(s32 need_count, bool is_fifo) : need_count{need_count} {
|
||||
if (is_fifo) {
|
||||
return;
|
||||
}
|
||||
explicit WaitingThread(s32 need_count, bool is_fifo) : sema{0}, need_count{need_count} {
|
||||
// Retrieve calling thread priority for sorting into waiting threads list.
|
||||
s32 policy;
|
||||
sched_param param;
|
||||
pthread_getschedparam(pthread_self(), &policy, ¶m);
|
||||
priority = param.sched_priority;
|
||||
if (!is_fifo) {
|
||||
priority = g_curthread->attr.prio;
|
||||
}
|
||||
}
|
||||
|
||||
int GetResult(bool timed_out) {
|
||||
|
@ -116,24 +131,24 @@ public:
|
|||
return SCE_OK;
|
||||
}
|
||||
|
||||
int Wait(std::unique_lock<std::mutex>& lk, u32* timeout) {
|
||||
int Wait(u32* timeout) {
|
||||
if (!timeout) {
|
||||
// Wait indefinitely until we are woken up.
|
||||
cv.wait(lk);
|
||||
sema.acquire();
|
||||
return GetResult(false);
|
||||
}
|
||||
// Wait until timeout runs out, recording how much remaining time there was.
|
||||
const auto start = std::chrono::high_resolution_clock::now();
|
||||
const auto status = cv.wait_for(lk, std::chrono::microseconds(*timeout));
|
||||
const auto sema_timeout = !sema.try_acquire_for(std::chrono::microseconds(*timeout));
|
||||
const auto end = std::chrono::high_resolution_clock::now();
|
||||
const auto time =
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
|
||||
if (status == std::cv_status::timeout) {
|
||||
if (sema_timeout) {
|
||||
*timeout = 0;
|
||||
} else {
|
||||
*timeout -= time;
|
||||
}
|
||||
return GetResult(status == std::cv_status::timeout);
|
||||
return GetResult(sema_timeout);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -163,7 +178,7 @@ public:
|
|||
bool is_fifo;
|
||||
};
|
||||
|
||||
using OrbisKernelSema = Semaphore*;
|
||||
using OrbisKernelSema = OrbisSem*;
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelCreateSema(OrbisKernelSema* sem, const char* pName, u32 attr,
|
||||
s32 initCount, s32 maxCount, const void* pOptParam) {
|
||||
|
@ -171,7 +186,7 @@ s32 PS4_SYSV_ABI sceKernelCreateSema(OrbisKernelSema* sem, const char* pName, u3
|
|||
LOG_ERROR(Lib_Kernel, "Semaphore creation parameters are invalid!");
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
*sem = new Semaphore(initCount, maxCount, pName, attr == 1);
|
||||
*sem = new OrbisSem(initCount, maxCount, pName, attr == 1);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
|
@ -210,17 +225,109 @@ int PS4_SYSV_ABI sceKernelDeleteSema(OrbisKernelSema sem) {
|
|||
if (!sem) {
|
||||
return SCE_KERNEL_ERROR_ESRCH;
|
||||
}
|
||||
delete sem;
|
||||
sem->Delete();
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void SemaphoreSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
|
||||
int PS4_SYSV_ABI posix_sem_init(PthreadSem** sem, int pshared, unsigned int value) {
|
||||
if (value > ORBIS_KERNEL_SEM_VALUE_MAX) {
|
||||
*__Error() = POSIX_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (sem != nullptr) {
|
||||
*sem = new PthreadSem(value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_sem_wait(PthreadSem** sem) {
|
||||
if (sem == nullptr || *sem == nullptr) {
|
||||
*__Error() = POSIX_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
(*sem)->semaphore.acquire();
|
||||
--(*sem)->value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_sem_trywait(PthreadSem** sem) {
|
||||
if (sem == nullptr || *sem == nullptr) {
|
||||
*__Error() = POSIX_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (!(*sem)->semaphore.try_acquire()) {
|
||||
*__Error() = POSIX_EAGAIN;
|
||||
return -1;
|
||||
}
|
||||
--(*sem)->value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_sem_timedwait(PthreadSem** sem, const OrbisKernelTimespec* t) {
|
||||
if (sem == nullptr || *sem == nullptr) {
|
||||
*__Error() = POSIX_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (!(*sem)->semaphore.try_acquire_until(t->TimePoint())) {
|
||||
*__Error() = POSIX_ETIMEDOUT;
|
||||
return -1;
|
||||
}
|
||||
--(*sem)->value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_sem_post(PthreadSem** sem) {
|
||||
if (sem == nullptr || *sem == nullptr) {
|
||||
*__Error() = POSIX_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if ((*sem)->value == ORBIS_KERNEL_SEM_VALUE_MAX) {
|
||||
*__Error() = POSIX_EOVERFLOW;
|
||||
return -1;
|
||||
}
|
||||
++(*sem)->value;
|
||||
(*sem)->semaphore.release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_sem_destroy(PthreadSem** sem) {
|
||||
if (sem == nullptr || *sem == nullptr) {
|
||||
*__Error() = POSIX_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
delete *sem;
|
||||
*sem = nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_sem_getvalue(PthreadSem** sem, int* sval) {
|
||||
if (sem == nullptr || *sem == nullptr) {
|
||||
*__Error() = POSIX_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (sval) {
|
||||
*sval = (*sem)->value;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RegisterSemaphore(Core::Loader::SymbolsResolver* sym) {
|
||||
// Orbis
|
||||
LIB_FUNCTION("188x57JYp0g", "libkernel", 1, "libkernel", 1, 1, sceKernelCreateSema);
|
||||
LIB_FUNCTION("Zxa0VhQVTsk", "libkernel", 1, "libkernel", 1, 1, sceKernelWaitSema);
|
||||
LIB_FUNCTION("4czppHBiriw", "libkernel", 1, "libkernel", 1, 1, sceKernelSignalSema);
|
||||
LIB_FUNCTION("12wOHk8ywb0", "libkernel", 1, "libkernel", 1, 1, sceKernelPollSema);
|
||||
LIB_FUNCTION("4DM06U2BNEY", "libkernel", 1, "libkernel", 1, 1, sceKernelCancelSema);
|
||||
LIB_FUNCTION("R1Jvn8bSCW8", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteSema);
|
||||
|
||||
// Posix
|
||||
LIB_FUNCTION("pDuPEf3m4fI", "libScePosix", 1, "libkernel", 1, 1, posix_sem_init);
|
||||
LIB_FUNCTION("YCV5dGGBcCo", "libScePosix", 1, "libkernel", 1, 1, posix_sem_wait);
|
||||
LIB_FUNCTION("WBWzsRifCEA", "libScePosix", 1, "libkernel", 1, 1, posix_sem_trywait);
|
||||
LIB_FUNCTION("w5IHyvahg-o", "libScePosix", 1, "libkernel", 1, 1, posix_sem_timedwait);
|
||||
LIB_FUNCTION("IKP8typ0QUk", "libScePosix", 1, "libkernel", 1, 1, posix_sem_post);
|
||||
LIB_FUNCTION("cDW233RAwWo", "libScePosix", 1, "libkernel", 1, 1, posix_sem_destroy);
|
||||
LIB_FUNCTION("Bq+LRV-N6Hk", "libScePosix", 1, "libkernel", 1, 1, posix_sem_getvalue);
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
||||
|
|
101
src/core/libraries/kernel/threads/sleepq.cpp
Normal file
101
src/core/libraries/kernel/threads/sleepq.cpp
Normal file
|
@ -0,0 +1,101 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <array>
|
||||
#include "common/spin_lock.h"
|
||||
#include "core/libraries/kernel/threads/pthread.h"
|
||||
#include "core/libraries/kernel/threads/sleepq.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
static constexpr int HASHSHIFT = 9;
|
||||
static constexpr int HASHSIZE = (1 << HASHSHIFT);
|
||||
#define SC_HASH(wchan) \
|
||||
((u32)((((uintptr_t)(wchan) >> 3) ^ ((uintptr_t)(wchan) >> (HASHSHIFT + 3))) & (HASHSIZE - 1)))
|
||||
#define SC_LOOKUP(wc) &sc_table[SC_HASH(wc)]
|
||||
|
||||
struct SleepQueueChain {
|
||||
Common::SpinLock sc_lock;
|
||||
SleepqList sc_queues;
|
||||
int sc_type;
|
||||
};
|
||||
|
||||
static std::array<SleepQueueChain, HASHSIZE> sc_table{};
|
||||
|
||||
void SleepqLock(void* wchan) {
|
||||
SleepQueueChain* sc = SC_LOOKUP(wchan);
|
||||
sc->sc_lock.lock();
|
||||
}
|
||||
|
||||
void SleepqUnlock(void* wchan) {
|
||||
SleepQueueChain* sc = SC_LOOKUP(wchan);
|
||||
sc->sc_lock.unlock();
|
||||
}
|
||||
|
||||
SleepQueue* SleepqLookup(void* wchan) {
|
||||
SleepQueueChain* sc = SC_LOOKUP(wchan);
|
||||
for (auto& sq : sc->sc_queues) {
|
||||
if (sq.sq_wchan == wchan) {
|
||||
return std::addressof(sq);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SleepqAdd(void* wchan, Pthread* td) {
|
||||
SleepQueue* sq = SleepqLookup(wchan);
|
||||
if (sq != nullptr) {
|
||||
sq->sq_freeq.push_front(*td->sleepqueue);
|
||||
} else {
|
||||
SleepQueueChain* sc = SC_LOOKUP(wchan);
|
||||
sq = td->sleepqueue;
|
||||
sc->sc_queues.push_front(*sq);
|
||||
sq->sq_wchan = wchan;
|
||||
/* sq->sq_type = type; */
|
||||
}
|
||||
td->sleepqueue = NULL;
|
||||
td->wchan = wchan;
|
||||
sq->sq_blocked.push_front(td);
|
||||
}
|
||||
|
||||
int SleepqRemove(SleepQueue* sq, Pthread* td) {
|
||||
std::erase(sq->sq_blocked, td);
|
||||
if (sq->sq_blocked.empty()) {
|
||||
td->sleepqueue = sq;
|
||||
sq->unlink();
|
||||
td->wchan = nullptr;
|
||||
return 0;
|
||||
} else {
|
||||
td->sleepqueue = std::addressof(sq->sq_freeq.front());
|
||||
sq->sq_freeq.pop_front();
|
||||
td->wchan = nullptr;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void SleepqDrop(SleepQueue* sq, void (*callback)(Pthread*, void*), void* arg) {
|
||||
if (sq->sq_blocked.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
sq->unlink();
|
||||
Pthread* td = sq->sq_blocked.front();
|
||||
sq->sq_blocked.pop_front();
|
||||
|
||||
callback(td, arg);
|
||||
|
||||
td->sleepqueue = sq;
|
||||
td->wchan = nullptr;
|
||||
|
||||
auto sq2 = sq->sq_freeq.begin();
|
||||
for (Pthread* td : sq->sq_blocked) {
|
||||
callback(td, arg);
|
||||
td->sleepqueue = std::addressof(*sq2);
|
||||
td->wchan = nullptr;
|
||||
++sq2;
|
||||
}
|
||||
sq->sq_blocked.clear();
|
||||
sq->sq_freeq.clear();
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
38
src/core/libraries/kernel/threads/sleepq.h
Normal file
38
src/core/libraries/kernel/threads/sleepq.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <forward_list>
|
||||
#include <list>
|
||||
#include <boost/intrusive/list.hpp>
|
||||
#include <boost/intrusive/list_hook.hpp>
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
struct Pthread;
|
||||
|
||||
using ListBaseHook =
|
||||
boost::intrusive::list_base_hook<boost::intrusive::link_mode<boost::intrusive::auto_unlink>>;
|
||||
|
||||
using SleepqList = boost::intrusive::list<SleepQueue, boost::intrusive::constant_time_size<false>>;
|
||||
|
||||
struct SleepQueue : public ListBaseHook {
|
||||
std::list<Pthread*> sq_blocked;
|
||||
SleepqList sq_freeq;
|
||||
void* sq_wchan;
|
||||
int sq_type;
|
||||
};
|
||||
|
||||
void SleepqLock(void* wchan);
|
||||
|
||||
void SleepqUnlock(void* wchan);
|
||||
|
||||
SleepQueue* SleepqLookup(void* wchan);
|
||||
|
||||
void SleepqAdd(void* wchan, Pthread* td);
|
||||
|
||||
int SleepqRemove(SleepQueue* sq, Pthread* td);
|
||||
|
||||
void SleepqDrop(SleepQueue* sq, void (*callback)(Pthread*, void*), void* arg);
|
||||
|
||||
} // namespace Libraries::Kernel
|
141
src/core/libraries/kernel/threads/stack.cpp
Normal file
141
src/core/libraries/kernel/threads/stack.cpp
Normal file
|
@ -0,0 +1,141 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/libraries/kernel/threads/pthread.h"
|
||||
#include "core/libraries/kernel/threads/thread_state.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
static constexpr size_t RoundUp(size_t size) {
|
||||
if (size % ThrPageSize != 0) {
|
||||
size = ((size / ThrPageSize) + 1) * ThrPageSize;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
int ThreadState::CreateStack(PthreadAttr* attr) {
|
||||
if ((attr->stackaddr_attr) != NULL) {
|
||||
attr->guardsize_attr = 0;
|
||||
attr->flags |= PthreadAttrFlags::StackUser;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Round up stack size to nearest multiple of _thr_page_size so
|
||||
* that mmap() * will work. If the stack size is not an even
|
||||
* multiple, we end up initializing things such that there is
|
||||
* unused space above the beginning of the stack, so the stack
|
||||
* sits snugly against its guard.
|
||||
*/
|
||||
size_t stacksize = RoundUp(attr->stacksize_attr);
|
||||
size_t guardsize = RoundUp(attr->guardsize_attr);
|
||||
|
||||
attr->stackaddr_attr = NULL;
|
||||
attr->flags &= ~PthreadAttrFlags::StackUser;
|
||||
|
||||
/*
|
||||
* Use the garbage collector lock for synchronization of the
|
||||
* spare stack lists and allocations from usrstack.
|
||||
*/
|
||||
thread_list_lock.lock();
|
||||
|
||||
/*
|
||||
* If the stack and guard sizes are default, try to allocate a stack
|
||||
* from the default-size stack cache:
|
||||
*/
|
||||
if (stacksize == ThrStackDefault && guardsize == ThrGuardDefault) {
|
||||
if (!dstackq.empty()) {
|
||||
/* Use the spare stack. */
|
||||
Stack* spare_stack = dstackq.top();
|
||||
dstackq.pop();
|
||||
attr->stackaddr_attr = spare_stack->stackaddr;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* The user specified a non-default stack and/or guard size, so try to
|
||||
* allocate a stack from the non-default size stack cache, using the
|
||||
* rounded up stack size (stack_size) in the search:
|
||||
*/
|
||||
else {
|
||||
const auto it = std::ranges::find_if(mstackq, [&](Stack* stack) {
|
||||
return stack->stacksize == stacksize && stack->guardsize == guardsize;
|
||||
});
|
||||
if (it != mstackq.end()) {
|
||||
attr->stackaddr_attr = (*it)->stackaddr;
|
||||
mstackq.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
/* A cached stack was found. Release the lock. */
|
||||
if (attr->stackaddr_attr != NULL) {
|
||||
thread_list_lock.unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Allocate a stack from usrstack. */
|
||||
if (last_stack == 0) {
|
||||
static constexpr VAddr UsrStack = 0x7EFFF8000ULL;
|
||||
last_stack = UsrStack - ThrStackInitial - ThrGuardDefault;
|
||||
}
|
||||
|
||||
/* Allocate a new stack. */
|
||||
VAddr stackaddr = last_stack - stacksize - guardsize;
|
||||
|
||||
/*
|
||||
* Even if stack allocation fails, we don't want to try to
|
||||
* use this location again, so unconditionally decrement
|
||||
* last_stack. Under normal operating conditions, the most
|
||||
* likely reason for an mmap() error is a stack overflow of
|
||||
* the adjacent thread stack.
|
||||
*/
|
||||
last_stack -= (stacksize + guardsize);
|
||||
|
||||
/* Release the lock before mmap'ing it. */
|
||||
thread_list_lock.unlock();
|
||||
|
||||
/* Map the stack and guard page together, and split guard
|
||||
page from allocated space: */
|
||||
auto* memory = Core::Memory::Instance();
|
||||
int ret = memory->MapMemory(reinterpret_cast<void**>(&stackaddr), stackaddr,
|
||||
stacksize + guardsize, Core::MemoryProt::CpuReadWrite,
|
||||
Core::MemoryMapFlags::NoFlags, Core::VMAType::Stack);
|
||||
ASSERT_MSG(ret == 0, "Unable to map stack memory");
|
||||
|
||||
if (guardsize != 0) {
|
||||
ret = memory->Protect(stackaddr, guardsize, Core::MemoryProt::NoAccess);
|
||||
ASSERT_MSG(ret == 0, "Unable to protect guard page");
|
||||
}
|
||||
|
||||
stackaddr += guardsize;
|
||||
attr->stackaddr_attr = (void*)stackaddr;
|
||||
|
||||
if (attr->stackaddr_attr != nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void ThreadState::FreeStack(PthreadAttr* attr) {
|
||||
if (!attr || True(attr->flags & PthreadAttrFlags::StackUser) || !attr->stackaddr_attr) {
|
||||
return;
|
||||
}
|
||||
|
||||
char* stack_base = (char*)attr->stackaddr_attr;
|
||||
Stack* spare_stack = (Stack*)(stack_base + attr->stacksize_attr - sizeof(Stack));
|
||||
spare_stack->stacksize = RoundUp(attr->stacksize_attr);
|
||||
spare_stack->guardsize = RoundUp(attr->guardsize_attr);
|
||||
spare_stack->stackaddr = attr->stackaddr_attr;
|
||||
|
||||
if (spare_stack->stacksize == ThrStackDefault && spare_stack->guardsize == ThrGuardDefault) {
|
||||
/* Default stack/guard size. */
|
||||
dstackq.push(spare_stack);
|
||||
} else {
|
||||
/* Non-default stack/guard size. */
|
||||
mstackq.push_back(spare_stack);
|
||||
}
|
||||
attr->stackaddr_attr = nullptr;
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
96
src/core/libraries/kernel/threads/tcb.cpp
Normal file
96
src/core/libraries/kernel/threads/tcb.cpp
Normal file
|
@ -0,0 +1,96 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/singleton.h"
|
||||
#include "core/libraries/kernel/threads/pthread.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/linker.h"
|
||||
#include "core/tls.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
static constexpr size_t TlsTcbSize = 0x40;
|
||||
static constexpr size_t TlsTcbAlign = 0x20;
|
||||
|
||||
static std::shared_mutex RtldLock;
|
||||
|
||||
Core::Tcb* TcbCtor(Pthread* thread, int initial) {
|
||||
std::scoped_lock lk{RtldLock};
|
||||
|
||||
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
auto* addr_out = linker->AllocateTlsForThread(initial);
|
||||
ASSERT_MSG(addr_out, "Unable to allocate guest TCB");
|
||||
|
||||
// Initialize allocated memory and allocate DTV table.
|
||||
const u32 num_dtvs = linker->MaxTlsIndex();
|
||||
const auto static_tls_size = linker->StaticTlsSize();
|
||||
auto* dtv_table = new Core::DtvEntry[num_dtvs + 2]{};
|
||||
|
||||
// Initialize thread control block
|
||||
u8* addr = reinterpret_cast<u8*>(addr_out);
|
||||
auto* tcb = reinterpret_cast<Core::Tcb*>(addr + static_tls_size);
|
||||
memset(addr_out, 0, static_tls_size);
|
||||
tcb->tcb_self = tcb;
|
||||
tcb->tcb_dtv = dtv_table;
|
||||
|
||||
// Dtv[0] is the generation counter. libkernel puts their number into dtv[1]
|
||||
dtv_table[0].counter = linker->GenerationCounter();
|
||||
dtv_table[1].counter = num_dtvs;
|
||||
|
||||
// Copy init image of main module.
|
||||
auto* module = linker->GetModule(0);
|
||||
u8* dest = reinterpret_cast<u8*>(addr + static_tls_size - module->tls.offset);
|
||||
|
||||
if (module->tls.image_size != 0) {
|
||||
if (module->tls.image_virtual_addr != 0) {
|
||||
const u8* src = reinterpret_cast<const u8*>(module->tls.image_virtual_addr);
|
||||
memcpy(dest, src, module->tls.init_image_size);
|
||||
}
|
||||
ASSERT_MSG(module->tls.modid > 0 && module->tls.modid <= num_dtvs);
|
||||
tcb->tcb_dtv[module->tls.modid + 1].pointer = dest;
|
||||
}
|
||||
|
||||
if (tcb) {
|
||||
tcb->tcb_thread = thread;
|
||||
}
|
||||
return tcb;
|
||||
}
|
||||
|
||||
void TcbDtor(Core::Tcb* oldtls) {
|
||||
std::scoped_lock lk{RtldLock};
|
||||
auto* dtv_table = oldtls->tcb_dtv;
|
||||
|
||||
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
const u32 max_tls_index = linker->MaxTlsIndex();
|
||||
const u32 num_dtvs = dtv_table[1].counter;
|
||||
ASSERT_MSG(num_dtvs <= max_tls_index, "Out of bounds DTV access");
|
||||
|
||||
const u32 static_tls_size = linker->StaticTlsSize();
|
||||
const u8* tls_base = (const u8*)oldtls - static_tls_size;
|
||||
|
||||
for (int i = 1; i < num_dtvs; i++) {
|
||||
u8* dtv_ptr = dtv_table[i + 1].pointer;
|
||||
if (dtv_ptr && (dtv_ptr < tls_base || (const u8*)oldtls < dtv_ptr)) {
|
||||
linker->FreeTlsForNonPrimaryThread(dtv_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
delete[] dtv_table;
|
||||
}
|
||||
|
||||
struct TlsIndex {
|
||||
u64 ti_module;
|
||||
u64 ti_offset;
|
||||
};
|
||||
|
||||
void* PS4_SYSV_ABI __tls_get_addr(TlsIndex* index) {
|
||||
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
return linker->TlsGetAddr(index->ti_module, index->ti_offset);
|
||||
}
|
||||
|
||||
void RegisterRtld(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("vNe1w4diLCs", "libkernel", 1, "libkernel", 1, 1, __tls_get_addr);
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
172
src/core/libraries/kernel/threads/thread_state.cpp
Normal file
172
src/core/libraries/kernel/threads/thread_state.cpp
Normal file
|
@ -0,0 +1,172 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
#include "common/alignment.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/threads/pthread.h"
|
||||
#include "core/libraries/kernel/threads/sleepq.h"
|
||||
#include "core/libraries/kernel/threads/thread_state.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/tls.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
thread_local Pthread* g_curthread{};
|
||||
|
||||
Core::Tcb* TcbCtor(Pthread* thread, int initial);
|
||||
void TcbDtor(Core::Tcb* oldtls);
|
||||
|
||||
ThreadState::ThreadState() {
|
||||
// Reserve memory for maximum amount of threads allowed.
|
||||
auto* memory = Core::Memory::Instance();
|
||||
static constexpr u32 ThrHeapSize = Common::AlignUp(sizeof(Pthread) * MaxThreads, 16_KB);
|
||||
void* heap_addr{};
|
||||
const int ret = memory->MapMemory(&heap_addr, Core::SYSTEM_RESERVED_MIN, ThrHeapSize,
|
||||
Core::MemoryProt::CpuReadWrite, Core::MemoryMapFlags::NoFlags,
|
||||
Core::VMAType::File, "ThrHeap");
|
||||
ASSERT_MSG(ret == 0, "Unable to allocate thread heap memory {}", ret);
|
||||
thread_heap.Initialize(heap_addr, ThrHeapSize);
|
||||
}
|
||||
|
||||
void ThreadState::Collect(Pthread* curthread) {
|
||||
boost::container::small_vector<Pthread*, 8> work_list;
|
||||
{
|
||||
std::scoped_lock lk{thread_list_lock};
|
||||
for (auto it = gc_list.begin(); it != gc_list.end();) {
|
||||
Pthread* td = *it;
|
||||
if (td->tid != TidTerminated) {
|
||||
it++;
|
||||
continue;
|
||||
}
|
||||
FreeStack(&td->attr);
|
||||
work_list.push_back(td);
|
||||
it = gc_list.erase(it);
|
||||
}
|
||||
}
|
||||
for (Pthread* td : work_list) {
|
||||
Free(curthread, td);
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadState::TryCollect(Pthread* thread) {
|
||||
SCOPE_EXIT {
|
||||
thread->lock.unlock();
|
||||
};
|
||||
if (!thread->ShouldCollect()) {
|
||||
return;
|
||||
}
|
||||
|
||||
thread->refcount++;
|
||||
thread->lock.unlock();
|
||||
std::scoped_lock lk{thread_list_lock};
|
||||
thread->lock.lock();
|
||||
thread->refcount--;
|
||||
if (thread->ShouldCollect()) {
|
||||
threads.erase(thread);
|
||||
gc_list.push_back(thread);
|
||||
}
|
||||
}
|
||||
|
||||
Pthread* ThreadState::Alloc(Pthread* curthread) {
|
||||
Pthread* thread = nullptr;
|
||||
if (curthread != nullptr) {
|
||||
if (GcNeeded()) {
|
||||
Collect(curthread);
|
||||
}
|
||||
if (!free_threads.empty()) {
|
||||
std::scoped_lock lk{free_thread_lock};
|
||||
thread = free_threads.back();
|
||||
free_threads.pop_back();
|
||||
}
|
||||
}
|
||||
if (thread == nullptr) {
|
||||
if (total_threads > MaxThreads) {
|
||||
return nullptr;
|
||||
}
|
||||
total_threads.fetch_add(1);
|
||||
thread = thread_heap.Allocate();
|
||||
if (thread == nullptr) {
|
||||
total_threads.fetch_sub(1);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
Core::Tcb* tcb = nullptr;
|
||||
if (curthread != nullptr) {
|
||||
std::scoped_lock lk{tcb_lock};
|
||||
tcb = TcbCtor(thread, 0 /* not initial tls */);
|
||||
} else {
|
||||
tcb = TcbCtor(thread, 1 /* initial tls */);
|
||||
}
|
||||
if (tcb != nullptr) {
|
||||
memset(thread, 0, sizeof(Pthread));
|
||||
std::construct_at(thread);
|
||||
thread->tcb = tcb;
|
||||
thread->sleepqueue = new SleepQueue{};
|
||||
} else {
|
||||
thread_heap.Free(thread);
|
||||
total_threads.fetch_sub(1);
|
||||
thread = nullptr;
|
||||
}
|
||||
return thread;
|
||||
}
|
||||
|
||||
void ThreadState::Free(Pthread* curthread, Pthread* thread) {
|
||||
if (curthread != nullptr) {
|
||||
std::scoped_lock lk{tcb_lock};
|
||||
TcbDtor(thread->tcb);
|
||||
} else {
|
||||
TcbDtor(thread->tcb);
|
||||
}
|
||||
thread->tcb = nullptr;
|
||||
std::destroy_at(thread);
|
||||
if (free_threads.size() >= MaxCachedThreads) {
|
||||
delete thread->sleepqueue;
|
||||
thread_heap.Free(thread);
|
||||
total_threads.fetch_sub(1);
|
||||
} else {
|
||||
std::scoped_lock lk{free_thread_lock};
|
||||
free_threads.push_back(thread);
|
||||
}
|
||||
}
|
||||
|
||||
int ThreadState::FindThread(Pthread* thread, bool include_dead) {
|
||||
if (thread == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
std::scoped_lock lk{thread_list_lock};
|
||||
const auto it = threads.find(thread);
|
||||
if (it == threads.end()) {
|
||||
return POSIX_ESRCH;
|
||||
}
|
||||
thread->lock.lock();
|
||||
if (!include_dead && thread->state == PthreadState::Dead) {
|
||||
thread->lock.unlock();
|
||||
return POSIX_ESRCH;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ThreadState::RefAdd(Pthread* thread, bool include_dead) {
|
||||
if (thread == nullptr) {
|
||||
/* Invalid thread: */
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
|
||||
if (int ret = FindThread(thread, include_dead); ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
thread->refcount++;
|
||||
thread->lock.unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ThreadState::RefDelete(Pthread* thread) {
|
||||
thread->lock.lock();
|
||||
thread->refcount--;
|
||||
TryCollect(thread);
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
87
src/core/libraries/kernel/threads/thread_state.h
Normal file
87
src/core/libraries/kernel/threads/thread_state.h
Normal file
|
@ -0,0 +1,87 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <stack>
|
||||
#include "common/singleton.h"
|
||||
#include "common/slab_heap.h"
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
struct Pthread;
|
||||
struct PthreadAttr;
|
||||
|
||||
struct Stack {
|
||||
size_t stacksize; /* Stack size (rounded up). */
|
||||
size_t guardsize; /* Guard size. */
|
||||
void* stackaddr; /* Stack address. */
|
||||
};
|
||||
|
||||
struct ThreadState {
|
||||
static constexpr size_t GcThreshold = 5;
|
||||
static constexpr size_t MaxThreads = 100000;
|
||||
static constexpr size_t MaxCachedThreads = 100;
|
||||
|
||||
explicit ThreadState();
|
||||
|
||||
bool GcNeeded() const noexcept {
|
||||
return gc_list.size() >= GcThreshold;
|
||||
}
|
||||
|
||||
void Collect(Pthread* curthread);
|
||||
|
||||
void TryCollect(Pthread* thread);
|
||||
|
||||
Pthread* Alloc(Pthread* curthread);
|
||||
|
||||
void Free(Pthread* curthread, Pthread* thread);
|
||||
|
||||
int FindThread(Pthread* thread, bool include_dead);
|
||||
|
||||
int RefAdd(Pthread* thread, bool include_dead);
|
||||
|
||||
void RefDelete(Pthread* thread);
|
||||
|
||||
int CreateStack(PthreadAttr* attr);
|
||||
|
||||
void FreeStack(PthreadAttr* attr);
|
||||
|
||||
void Link(Pthread* curthread, Pthread* thread) {
|
||||
{
|
||||
std::scoped_lock lk{thread_list_lock};
|
||||
threads.insert(thread);
|
||||
}
|
||||
active_threads.fetch_add(1);
|
||||
}
|
||||
|
||||
void Unlink(Pthread* curthread, Pthread* thread) {
|
||||
{
|
||||
std::scoped_lock lk{thread_list_lock};
|
||||
threads.erase(thread);
|
||||
}
|
||||
active_threads.fetch_sub(1);
|
||||
}
|
||||
|
||||
Common::SlabHeap<Pthread> thread_heap;
|
||||
std::set<Pthread*> threads;
|
||||
std::list<Pthread*> free_threads;
|
||||
std::list<Pthread*> gc_list;
|
||||
std::mutex free_thread_lock;
|
||||
std::mutex tcb_lock;
|
||||
std::mutex thread_list_lock;
|
||||
std::atomic<s32> total_threads{};
|
||||
std::atomic<s32> active_threads{};
|
||||
std::stack<Stack*> dstackq;
|
||||
std::list<Stack*> mstackq;
|
||||
VAddr last_stack = 0;
|
||||
};
|
||||
|
||||
using ThrState = Common::Singleton<ThreadState>;
|
||||
|
||||
} // namespace Libraries::Kernel
|
|
@ -1,20 +0,0 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/libraries/kernel/thread_management.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
int PS4_SYSV_ABI scePthreadRwlockattrInit(OrbisPthreadRwlockattr* attr);
|
||||
|
||||
void SemaphoreSymbolsRegister(Core::Loader::SymbolsResolver* sym);
|
||||
void RwlockSymbolsRegister(Core::Loader::SymbolsResolver* sym);
|
||||
void KeySymbolsRegister(Core::Loader::SymbolsResolver* sym);
|
||||
|
||||
} // namespace Libraries::Kernel
|
|
@ -4,10 +4,9 @@
|
|||
#include <thread>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/native_clock.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/time_management.h"
|
||||
#include "core/libraries/kernel/time.h"
|
||||
#include "core/libraries/libs.h"
|
||||
|
||||
#ifdef _WIN64
|
||||
|
@ -17,6 +16,9 @@
|
|||
#include "common/ntapi.h"
|
||||
|
||||
#else
|
||||
#if __APPLE__
|
||||
#include <date/tz.h>
|
||||
#endif
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
@ -50,14 +52,7 @@ u64 PS4_SYSV_ABI sceKernelReadTsc() {
|
|||
|
||||
int PS4_SYSV_ABI sceKernelUsleep(u32 microseconds) {
|
||||
#ifdef _WIN64
|
||||
if (microseconds < 1000u) {
|
||||
LARGE_INTEGER interval{
|
||||
.QuadPart = -1 * (microseconds * 10u),
|
||||
};
|
||||
NtDelayExecution(FALSE, &interval);
|
||||
} else {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(microseconds));
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(microseconds));
|
||||
return 0;
|
||||
#else
|
||||
timespec start;
|
||||
|
@ -258,7 +253,33 @@ Common::NativeClock* GetClock() {
|
|||
|
||||
} // namespace Dev
|
||||
|
||||
void timeSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
|
||||
int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time,
|
||||
struct OrbisTimesec* st, unsigned long* dst_sec) {
|
||||
LOG_TRACE(Kernel, "Called");
|
||||
#ifdef __APPLE__
|
||||
// std::chrono::current_zone() not available yet.
|
||||
const auto* time_zone = date::current_zone();
|
||||
#else
|
||||
const auto* time_zone = std::chrono::current_zone();
|
||||
#endif
|
||||
auto info = time_zone->get_info(std::chrono::system_clock::now());
|
||||
|
||||
*local_time = info.offset.count() + info.save.count() * 60 + time;
|
||||
|
||||
if (st != nullptr) {
|
||||
st->t = time;
|
||||
st->west_sec = info.offset.count() * 60;
|
||||
st->dst_sec = info.save.count() * 60;
|
||||
}
|
||||
|
||||
if (dst_sec != nullptr) {
|
||||
*dst_sec = info.save.count() * 60;
|
||||
}
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void RegisterTime(Core::Loader::SymbolsResolver* sym) {
|
||||
clock = std::make_unique<Common::NativeClock>();
|
||||
initial_ptc = clock->GetUptime();
|
||||
LIB_FUNCTION("4J2sUJmuHZQ", "libkernel", 1, "libkernel", 1, 1, sceKernelGetProcessTime);
|
||||
|
@ -284,6 +305,7 @@ void timeSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
|
|||
LIB_FUNCTION("lLMT9vJAck0", "libScePosix", 1, "libkernel", 1, 1, posix_clock_gettime);
|
||||
LIB_FUNCTION("smIj7eqzZE8", "libScePosix", 1, "libkernel", 1, 1, posix_clock_getres);
|
||||
LIB_FUNCTION("0NTHN1NKONI", "libkernel", 1, "libkernel", 1, 1, sceKernelConvertLocaltimeToUtc);
|
||||
LIB_FUNCTION("-o5uEDpN+oY", "libkernel", 1, "libkernel", 1, 1, sceKernelConvertUtcToLocaltime);
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Common {
|
||||
|
@ -30,6 +30,19 @@ struct OrbisKernelTimezone {
|
|||
struct OrbisKernelTimespec {
|
||||
s64 tv_sec;
|
||||
s64 tv_nsec;
|
||||
|
||||
std::chrono::system_clock::time_point TimePoint() const noexcept {
|
||||
using namespace std::chrono;
|
||||
const auto duration =
|
||||
duration_cast<system_clock::duration>(seconds{tv_sec} + nanoseconds{tv_nsec});
|
||||
return system_clock::time_point{duration};
|
||||
}
|
||||
};
|
||||
|
||||
struct OrbisTimesec {
|
||||
time_t t;
|
||||
u32 west_sec;
|
||||
u32 dst_sec;
|
||||
};
|
||||
|
||||
constexpr int ORBIS_CLOCK_REALTIME = 0;
|
||||
|
@ -66,6 +79,10 @@ int PS4_SYSV_ABI sceKernelClockGettime(s32 clock_id, OrbisKernelTimespec* tp);
|
|||
s32 PS4_SYSV_ABI sceKernelGettimezone(OrbisKernelTimezone* tz);
|
||||
int PS4_SYSV_ABI sceKernelConvertLocaltimeToUtc(time_t param_1, int64_t param_2, time_t* seconds,
|
||||
OrbisKernelTimezone* timezone, int* dst_seconds);
|
||||
void timeSymbolsRegister(Core::Loader::SymbolsResolver* sym);
|
||||
|
||||
int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time, OrbisTimesec* st,
|
||||
unsigned long* dst_sec);
|
||||
|
||||
void RegisterTime(Core::Loader::SymbolsResolver* sym);
|
||||
|
||||
} // namespace Libraries::Kernel
|
Loading…
Add table
Add a link
Reference in a new issue