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:
TheTurtle 2024-11-21 22:59:38 +02:00 committed by GitHub
parent 6904764aab
commit c4506da0ae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
104 changed files with 5554 additions and 3979 deletions

View file

@ -208,30 +208,38 @@ set(GNM_LIB src/core/libraries/gnmdriver/gnmdriver.cpp
src/core/libraries/gnmdriver/gnm_error.h
)
set(KERNEL_LIB src/core/libraries/kernel/event_flag/event_flag.cpp
src/core/libraries/kernel/event_flag/event_flag.h
src/core/libraries/kernel/event_flag/event_flag_obj.cpp
src/core/libraries/kernel/event_flag/event_flag_obj.h
set(KERNEL_LIB src/core/libraries/kernel/threads/condvar.cpp
src/core/libraries/kernel/threads/event_flag.cpp
src/core/libraries/kernel/threads/exception.cpp
src/core/libraries/kernel/threads/exception.h
src/core/libraries/kernel/threads/mutex.cpp
src/core/libraries/kernel/threads/pthread_attr.cpp
src/core/libraries/kernel/threads/pthread_clean.cpp
src/core/libraries/kernel/threads/pthread.cpp
src/core/libraries/kernel/threads/pthread_spec.cpp
src/core/libraries/kernel/threads/rwlock.cpp
src/core/libraries/kernel/threads/semaphore.cpp
src/core/libraries/kernel/threads/keys.cpp
src/core/libraries/kernel/threads/threads.h
src/core/libraries/kernel/cpu_management.cpp
src/core/libraries/kernel/cpu_management.h
src/core/libraries/kernel/event_queue.cpp
src/core/libraries/kernel/event_queue.h
src/core/libraries/kernel/event_queues.cpp
src/core/libraries/kernel/event_queues.h
src/core/libraries/kernel/threads/sleepq.cpp
src/core/libraries/kernel/threads/sleepq.h
src/core/libraries/kernel/threads/stack.cpp
src/core/libraries/kernel/threads/tcb.cpp
src/core/libraries/kernel/threads/pthread.h
src/core/libraries/kernel/threads/thread_state.cpp
src/core/libraries/kernel/threads/thread_state.h
src/core/libraries/kernel/process.cpp
src/core/libraries/kernel/process.h
src/core/libraries/kernel/equeue.cpp
src/core/libraries/kernel/equeue.h
src/core/libraries/kernel/file_system.cpp
src/core/libraries/kernel/file_system.h
src/core/libraries/kernel/libkernel.cpp
src/core/libraries/kernel/libkernel.h
src/core/libraries/kernel/memory_management.cpp
src/core/libraries/kernel/memory_management.h
src/core/libraries/kernel/thread_management.cpp
src/core/libraries/kernel/thread_management.h
src/core/libraries/kernel/time_management.cpp
src/core/libraries/kernel/time_management.h
src/core/libraries/kernel/kernel.cpp
src/core/libraries/kernel/kernel.h
src/core/libraries/kernel/memory.cpp
src/core/libraries/kernel/memory.h
src/core/libraries/kernel/threads.cpp
src/core/libraries/kernel/threads.h
src/core/libraries/kernel/time.cpp
src/core/libraries/kernel/time.h
)
set(NETWORK_LIBS src/core/libraries/network/http.cpp
@ -453,7 +461,10 @@ set(COMMON src/common/logging/backend.cpp
src/common/signal_context.h
src/common/signal_context.cpp
src/common/singleton.h
src/common/slab_heap.h
src/common/slot_vector.h
src/common/spin_lock.cpp
src/common/spin_lock.h
src/common/string_util.cpp
src/common/string_util.h
src/common/thread.cpp
@ -541,6 +552,8 @@ set(CORE src/core/aerolib/stubs.cpp
src/core/platform.h
src/core/signals.cpp
src/core/signals.h
src/core/thread.cpp
src/core/thread.h
src/core/tls.cpp
src/core/tls.h
src/core/virtual_memory.cpp

View file

@ -41,7 +41,7 @@ enum MarkersPalette : int {
#define RENDERER_TRACE ZoneScopedC(RendererMarkerColor)
#define HLE_TRACE ZoneScopedC(HleMarkerColor)
#define TRACE_HINT(str) ZoneText(str.c_str(), str.size())
#define TRACE_HINT(str) ZoneText(str.data(), str.size())
#define TRACE_WARN(msg) \
[](const auto& msg) { TracyMessageC(msg.c_str(), msg.size(), tracy::Color::DarkOrange); }(msg);

163
src/common/slab_heap.h Normal file
View file

@ -0,0 +1,163 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include "common/assert.h"
#include "common/spin_lock.h"
namespace Common {
class SlabHeapImpl {
public:
struct Node {
Node* next{};
};
public:
constexpr SlabHeapImpl() = default;
void Initialize() {
ASSERT(m_head == nullptr);
}
Node* GetHead() const {
return m_head;
}
void* Allocate() {
m_lock.lock();
Node* ret = m_head;
if (ret != nullptr) {
m_head = ret->next;
}
m_lock.unlock();
return ret;
}
void Free(void* obj) {
m_lock.lock();
Node* node = static_cast<Node*>(obj);
node->next = m_head;
m_head = node;
m_lock.unlock();
}
private:
std::atomic<Node*> m_head{};
Common::SpinLock m_lock;
};
class SlabHeapBase : protected SlabHeapImpl {
private:
size_t m_obj_size{};
uintptr_t m_peak{};
uintptr_t m_start{};
uintptr_t m_end{};
public:
constexpr SlabHeapBase() = default;
bool Contains(uintptr_t address) const {
return m_start <= address && address < m_end;
}
void Initialize(size_t obj_size, void* memory, size_t memory_size) {
// Ensure we don't initialize a slab using null memory.
ASSERT(memory != nullptr);
// Set our object size.
m_obj_size = obj_size;
// Initialize the base allocator.
SlabHeapImpl::Initialize();
// Set our tracking variables.
const size_t num_obj = (memory_size / obj_size);
m_start = reinterpret_cast<uintptr_t>(memory);
m_end = m_start + num_obj * obj_size;
m_peak = m_start;
// Free the objects.
u8* cur = reinterpret_cast<u8*>(m_end);
for (size_t i = 0; i < num_obj; i++) {
cur -= obj_size;
SlabHeapImpl::Free(cur);
}
}
size_t GetSlabHeapSize() const {
return (m_end - m_start) / this->GetObjectSize();
}
size_t GetObjectSize() const {
return m_obj_size;
}
void* Allocate() {
void* obj = SlabHeapImpl::Allocate();
return obj;
}
void Free(void* obj) {
// Don't allow freeing an object that wasn't allocated from this heap.
const bool contained = this->Contains(reinterpret_cast<uintptr_t>(obj));
ASSERT(contained);
SlabHeapImpl::Free(obj);
}
size_t GetObjectIndex(const void* obj) const {
return (reinterpret_cast<uintptr_t>(obj) - m_start) / this->GetObjectSize();
}
size_t GetPeakIndex() const {
return this->GetObjectIndex(reinterpret_cast<const void*>(m_peak));
}
uintptr_t GetSlabHeapAddress() const {
return m_start;
}
size_t GetNumRemaining() const {
// Only calculate the number of remaining objects under debug configuration.
return 0;
}
};
template <typename T>
class SlabHeap final : public SlabHeapBase {
private:
using BaseHeap = SlabHeapBase;
public:
constexpr SlabHeap() = default;
void Initialize(void* memory, size_t memory_size) {
BaseHeap::Initialize(sizeof(T), memory, memory_size);
}
T* Allocate() {
T* obj = static_cast<T*>(BaseHeap::Allocate());
if (obj != nullptr) [[likely]] {
std::construct_at(obj);
}
return obj;
}
void Free(T* obj) {
BaseHeap::Free(obj);
}
size_t GetObjectIndex(const T* obj) const {
return BaseHeap::GetObjectIndex(obj);
}
};
} // namespace Common

View file

@ -3,10 +3,7 @@
#pragma once
#include <bit>
#include <compare>
#include <numeric>
#include <type_traits>
#include <utility>
#include <vector>
#include "common/assert.h"

53
src/common/spin_lock.cpp Executable file
View file

@ -0,0 +1,53 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/spin_lock.h"
#if _MSC_VER
#include <intrin.h>
#if _M_AMD64
#define __x86_64__ 1
#endif
#if _M_ARM64
#define __aarch64__ 1
#endif
#else
#if __x86_64__
#include <xmmintrin.h>
#endif
#endif
namespace {
void ThreadPause() {
#if __x86_64__
_mm_pause();
#elif __aarch64__ && _MSC_VER
__yield();
#elif __aarch64__
asm("yield");
#endif
}
} // Anonymous namespace
namespace Common {
void SpinLock::lock() {
while (lck.test_and_set(std::memory_order_acquire)) {
ThreadPause();
}
}
void SpinLock::unlock() {
lck.clear(std::memory_order_release);
}
bool SpinLock::try_lock() {
if (lck.test_and_set(std::memory_order_acquire)) {
return false;
}
return true;
}
} // namespace Common

33
src/common/spin_lock.h Executable file
View file

@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
namespace Common {
/**
* SpinLock class
* a lock similar to mutex that forces a thread to spin wait instead calling the
* supervisor. Should be used on short sequences of code.
*/
class SpinLock {
public:
SpinLock() = default;
SpinLock(const SpinLock&) = delete;
SpinLock& operator=(const SpinLock&) = delete;
SpinLock(SpinLock&&) = delete;
SpinLock& operator=(SpinLock&&) = delete;
void lock();
void unlock();
[[nodiscard]] bool try_lock();
private:
std::atomic_flag lck = ATOMIC_FLAG_INIT;
};
} // namespace Common

View file

@ -1,13 +1,14 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <map>
#include <boost/icl/separate_interval_set.hpp>
#include "common/alignment.h"
#include "common/arch.h"
#include "common/assert.h"
#include "common/error.h"
#include "core/address_space.h"
#include "core/libraries/kernel/memory_management.h"
#include "core/libraries/kernel/memory.h"
#include "core/memory.h"
#include "libraries/error_codes.h"
@ -40,6 +41,12 @@ static constexpr size_t BackingSize = SCE_KERNEL_MAIN_DMEM_SIZE_PRO;
}
}
struct MemoryRegion {
VAddr base;
size_t size;
bool is_mapped;
};
struct AddressSpace::Impl {
Impl() : process{GetCurrentProcess()} {
// Allocate virtual address placeholder for our address space.
@ -75,6 +82,7 @@ struct AddressSpace::Impl {
Common::GetLastErrorMsg());
// Take the reduction off of the system managed area, and leave the others unchanged.
reduction = size_t(virtual_base - SYSTEM_MANAGED_MIN);
system_managed_base = virtual_base;
system_managed_size = SystemManagedSize - reduction;
system_reserved_base = reinterpret_cast<u8*>(SYSTEM_RESERVED_MIN);
@ -95,7 +103,8 @@ struct AddressSpace::Impl {
const uintptr_t system_managed_addr = reinterpret_cast<uintptr_t>(system_managed_base);
const uintptr_t system_reserved_addr = reinterpret_cast<uintptr_t>(system_reserved_base);
const uintptr_t user_addr = reinterpret_cast<uintptr_t>(user_base);
placeholders.insert({system_managed_addr, virtual_size - reduction});
regions.emplace(system_managed_addr,
MemoryRegion{system_managed_addr, virtual_size - reduction, false});
// Allocate backing file that represents the total physical memory.
backing_handle =
@ -132,42 +141,15 @@ struct AddressSpace::Impl {
}
void* Map(VAddr virtual_addr, PAddr phys_addr, size_t size, ULONG prot, uintptr_t fd = 0) {
const size_t aligned_size = Common::AlignUp(size, 16_KB);
const auto it = placeholders.find(virtual_addr);
ASSERT_MSG(it != placeholders.end(), "Cannot map already mapped region");
ASSERT_MSG(virtual_addr >= it->lower() && virtual_addr + aligned_size <= it->upper(),
"Map range must be fully contained in a placeholder");
// Windows only allows splitting a placeholder into two.
// This means that if the map range is fully
// contained the the placeholder we need to perform two split operations,
// one at the start and at the end.
const VAddr placeholder_start = it->lower();
const VAddr placeholder_end = it->upper();
const VAddr virtual_end = virtual_addr + aligned_size;
// If the placeholder doesn't exactly start at virtual_addr, split it at the start.
if (placeholder_start != virtual_addr) {
VirtualFreeEx(process, reinterpret_cast<LPVOID>(placeholder_start),
virtual_addr - placeholder_start, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER);
}
// If the placeholder doesn't exactly end at virtual_end, split it at the end.
if (placeholder_end != virtual_end) {
VirtualFreeEx(process, reinterpret_cast<LPVOID>(virtual_end),
placeholder_end - virtual_end, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER);
}
// Remove the placeholder.
placeholders.erase({virtual_addr, virtual_end});
// Perform the map.
// Before mapping we must carve a placeholder with the exact properties of our mapping.
auto* region = EnsureSplitRegionForMapping(virtual_addr, size);
region->is_mapped = true;
void* ptr = nullptr;
if (phys_addr != -1) {
HANDLE backing = fd ? reinterpret_cast<HANDLE>(fd) : backing_handle;
if (fd && prot == PAGE_READONLY) {
DWORD resultvar;
ptr = VirtualAlloc2(process, reinterpret_cast<PVOID>(virtual_addr), aligned_size,
ptr = VirtualAlloc2(process, reinterpret_cast<PVOID>(virtual_addr), size,
MEM_RESERVE | MEM_COMMIT | MEM_REPLACE_PLACEHOLDER,
PAGE_READWRITE, nullptr, 0);
bool ret = ReadFile(backing, ptr, size, &resultvar, NULL);
@ -176,12 +158,11 @@ struct AddressSpace::Impl {
ASSERT_MSG(ret, "VirtualProtect failed. {}", Common::GetLastErrorMsg());
} else {
ptr = MapViewOfFile3(backing, process, reinterpret_cast<PVOID>(virtual_addr),
phys_addr, aligned_size, MEM_REPLACE_PLACEHOLDER, prot,
nullptr, 0);
phys_addr, size, MEM_REPLACE_PLACEHOLDER, prot, nullptr, 0);
}
} else {
ptr =
VirtualAlloc2(process, reinterpret_cast<PVOID>(virtual_addr), aligned_size,
VirtualAlloc2(process, reinterpret_cast<PVOID>(virtual_addr), size,
MEM_RESERVE | MEM_COMMIT | MEM_REPLACE_PLACEHOLDER, prot, nullptr, 0);
}
ASSERT_MSG(ptr, "{}", Common::GetLastErrorMsg());
@ -202,33 +183,118 @@ struct AddressSpace::Impl {
// The unmap call will create a new placeholder region. We need to see if we can coalesce it
// with neighbors.
VAddr placeholder_start = virtual_addr;
VAddr placeholder_end = virtual_addr + size;
JoinRegionsAfterUnmap(virtual_addr, size);
}
// The following code is inspired from Dolphin's MemArena
// https://github.com/dolphin-emu/dolphin/blob/deee3ee4/Source/Core/Common/MemArenaWin.cpp#L212
MemoryRegion* EnsureSplitRegionForMapping(VAddr address, size_t size) {
// Find closest region that is <= the given address by using upper bound and decrementing
auto it = regions.upper_bound(address);
ASSERT_MSG(it != regions.begin(), "Invalid address {:#x}", address);
--it;
ASSERT_MSG(!it->second.is_mapped,
"Attempt to map {:#x} with size {:#x} which overlaps with {:#x} mapping",
address, size, it->second.base);
auto& [base, region] = *it;
const VAddr mapping_address = region.base;
const size_t region_size = region.size;
if (mapping_address == address) {
// If this region is already split up correctly we don't have to do anything
if (region_size == size) {
return &region;
}
ASSERT_MSG(region_size >= size,
"Region with address {:#x} and size {:#x} can't fit {:#x}", mapping_address,
region_size, size);
// Split the placeholder.
if (!VirtualFreeEx(process, LPVOID(address), size,
MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER)) {
UNREACHABLE_MSG("Region splitting failed: {}", Common::GetLastErrorMsg());
return nullptr;
}
// Update tracked mappings and return the first of the two
region.size = size;
const VAddr new_mapping_start = address + size;
regions.emplace_hint(std::next(it), new_mapping_start,
MemoryRegion(new_mapping_start, region_size - size, false));
return &region;
}
ASSERT(mapping_address < address);
// Is there enough space to map this?
const size_t offset_in_region = address - mapping_address;
const size_t minimum_size = size + offset_in_region;
ASSERT(region_size >= minimum_size);
// Split the placeholder.
if (!VirtualFreeEx(process, LPVOID(address), size,
MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER)) {
UNREACHABLE_MSG("Region splitting failed: {}", Common::GetLastErrorMsg());
return nullptr;
}
// Do we now have two regions or three regions?
if (region_size == minimum_size) {
// Split into two; update tracked mappings and return the second one
region.size = offset_in_region;
it = regions.emplace_hint(std::next(it), address, MemoryRegion(address, size, false));
return &it->second;
} else {
// Split into three; update tracked mappings and return the middle one
region.size = offset_in_region;
const VAddr middle_mapping_start = address;
const size_t middle_mapping_size = size;
const VAddr after_mapping_start = address + size;
const size_t after_mapping_size = region_size - minimum_size;
it = regions.emplace_hint(std::next(it), after_mapping_start,
MemoryRegion(after_mapping_start, after_mapping_size, false));
it = regions.emplace_hint(
it, middle_mapping_start,
MemoryRegion(middle_mapping_start, middle_mapping_size, false));
return &it->second;
}
}
void JoinRegionsAfterUnmap(VAddr address, size_t size) {
// There should be a mapping that matches the request exactly, find it
auto it = regions.find(address);
ASSERT_MSG(it != regions.end() && it->second.size == size,
"Invalid address/size given to unmap.");
auto& [base, region] = *it;
region.is_mapped = false;
// Check if a placeholder exists right before us.
const auto left_it = placeholders.find(virtual_addr - 1);
if (left_it != placeholders.end()) {
ASSERT_MSG(left_it->upper() == virtual_addr,
"Left placeholder does not end at virtual_addr!");
placeholder_start = left_it->lower();
VirtualFreeEx(process, reinterpret_cast<LPVOID>(placeholder_start),
placeholder_end - placeholder_start,
MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS);
auto it_prev = it != regions.begin() ? std::prev(it) : regions.end();
if (it_prev != regions.end() && !it_prev->second.is_mapped) {
const size_t total_size = it_prev->second.size + size;
if (!VirtualFreeEx(process, LPVOID(it_prev->first), total_size,
MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS)) {
UNREACHABLE_MSG("Region coalescing failed: {}", Common::GetLastErrorMsg());
}
it_prev->second.size = total_size;
regions.erase(it);
it = it_prev;
}
// Check if a placeholder exists right after us.
const auto right_it = placeholders.find(placeholder_end + 1);
if (right_it != placeholders.end()) {
ASSERT_MSG(right_it->lower() == placeholder_end,
"Right placeholder does not start at virtual_end!");
placeholder_end = right_it->upper();
VirtualFreeEx(process, reinterpret_cast<LPVOID>(placeholder_start),
placeholder_end - placeholder_start,
MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS);
}
auto it_next = std::next(it);
if (it_next != regions.end() && !it_next->second.is_mapped) {
const size_t total_size = it->second.size + it_next->second.size;
if (!VirtualFreeEx(process, LPVOID(it->first), total_size,
MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS)) {
UNREACHABLE_MSG("Region coalescing failed: {}", Common::GetLastErrorMsg());
}
// Insert the new placeholder.
placeholders.insert({placeholder_start, placeholder_end});
it->second.size = total_size;
regions.erase(it_next);
}
}
void Protect(VAddr virtual_addr, size_t size, bool read, bool write, bool execute) {
@ -251,18 +317,22 @@ struct AddressSpace::Impl {
return;
}
DWORD old_flags{};
bool success =
VirtualProtect(reinterpret_cast<void*>(virtual_addr), size, new_flags, &old_flags);
if (!success) {
LOG_ERROR(Common_Memory,
"Failed to change virtual memory protection for address {:#x}, size {}",
virtual_addr, size);
const VAddr virtual_end = virtual_addr + size;
auto it = --regions.upper_bound(virtual_addr);
for (; it->first < virtual_end; it++) {
if (!it->second.is_mapped) {
continue;
}
const auto& region = it->second;
const size_t range_addr = std::max(region.base, virtual_addr);
const size_t range_size = std::min(region.base + region.size, virtual_end) - range_addr;
DWORD old_flags{};
if (!VirtualProtectEx(process, LPVOID(range_addr), range_size, new_flags, &old_flags)) {
UNREACHABLE_MSG(
"Failed to change virtual memory protection for address {:#x}, size {}",
range_addr, range_size);
}
}
// Use assert to ensure success in debug builds
DEBUG_ASSERT(success && "Failed to change virtual memory protection");
}
HANDLE process{};
@ -275,7 +345,7 @@ struct AddressSpace::Impl {
size_t system_reserved_size{};
u8* user_base{};
size_t user_size{};
boost::icl::separate_interval_set<uintptr_t> placeholders;
std::map<VAddr, MemoryRegion> regions;
};
#else

View file

@ -8,7 +8,7 @@
#include "common/singleton.h"
#include "debug_state.h"
#include "devtools/widget/common.h"
#include "libraries/kernel/time_management.h"
#include "libraries/kernel/time.h"
#include "libraries/system/msgdialog.h"
#include "video_core/amdgpu/pm4_cmds.h"

114
src/core/file_sys/file.cpp Normal file
View file

@ -0,0 +1,114 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/error.h"
#include "core/file_sys/file.h"
#ifdef _WIN64
#include <io.h>
#include <share.h>
#include <windows.h>
#include "common/ntapi.h"
#endif
namespace Core::FileSys {
#ifdef _WIN64
int File::Open(const std::filesystem::path& path, Common::FS::FileAccessMode f_access) {
DWORD access{};
if (f_access == Common::FS::FileAccessMode::Read) {
access = GENERIC_READ;
} else if (f_access == Common::FS::FileAccessMode::Write) {
access = GENERIC_WRITE;
} else if (f_access == Common::FS::FileAccessMode::ReadWrite) {
access = GENERIC_READ | GENERIC_WRITE;
} else {
UNREACHABLE();
}
handle = CreateFileW(path.native().c_str(), access, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if (handle == INVALID_HANDLE_VALUE) {
return ENOENT;
}
}
s64 File::Read(void* buf, size_t nbytes) {
DWORD bytes_read;
if (!ReadFile(handle, buf, nbytes, &bytes_read, nullptr)) {
UNREACHABLE_MSG("ReadFile failed: {}", Common::GetLastErrorMsg());
}
return bytes_read;
}
s64 File::Pread(void* buf, size_t nbytes, s64 offset) {
OVERLAPPED ol{};
ol.Offset = offset;
ol.OffsetHigh = offset >> 32;
DWORD bytes_read;
if (!ReadFile(handle, buf, nbytes, &bytes_read, &ol)) {
UNREACHABLE_MSG("ReadFile failed: {}", Common::GetLastErrorMsg());
}
return bytes_read;
}
s64 File::Write(const void* buf, size_t nbytes) {
DWORD bytes_written;
if (!WriteFile(handle, buf, nbytes, &bytes_written, nullptr)) {
UNREACHABLE_MSG("WriteFile failed: {}", Common::GetLastErrorMsg());
}
return bytes_written;
}
s64 File::Pwrite(const void* buf, size_t nbytes, s64 offset) {
OVERLAPPED ol{};
ol.Offset = offset;
ol.OffsetHigh = offset >> 32;
DWORD bytes_written;
if (!WriteFile(handle, buf, nbytes, &bytes_written, &ol)) {
UNREACHABLE_MSG("WriteFile failed: {}", Common::GetLastErrorMsg());
}
return bytes_written;
}
void File::SetSize(s64 size) {
Lseek(size, 0);
if (!SetEndOfFile(handle)) {
UNREACHABLE_MSG("SetEndOfFile failed: {}", Common::GetLastErrorMsg());
}
}
void File::Flush() {
FlushFileBuffers(handle);
}
s64 File::Lseek(s64 offset, int whence) {
LARGE_INTEGER new_file_pointer;
DWORD origin{};
if (whence == 0) {
origin = FILE_BEGIN;
} else if (whence == 1) {
origin = FILE_CURRENT;
} else if (whence == 2) {
origin = FILE_END;
}
if (!SetFilePointerEx(handle, LARGE_INTEGER{.QuadPart = offset}, &new_file_pointer, origin)) {
UNREACHABLE_MSG("SetFilePointerEx failed: {}", Common::GetLastErrorMsg());
}
return new_file_pointer.QuadPart;
}
void File::Unlink() {
FILE_DISPOSITION_INFORMATION disposition;
IO_STATUS_BLOCK iosb;
disposition.DeleteFile = TRUE;
NtSetInformationFile(handle, &iosb, &disposition, sizeof(disposition),
FileDispositionInformation);
}
#else
#endif
} // namespace Core::FileSys

View file

@ -65,12 +65,12 @@ int PS4_SYSV_ABI sceAudio3dPortFlush(OrbisAudio3dPortId uiPortId) {
}
int PS4_SYSV_ABI sceAudio3dPortAdvance(OrbisAudio3dPortId uiPortId) {
LOG_INFO(Lib_Audio3d, "uiPortId = {}", uiPortId);
LOG_TRACE(Lib_Audio3d, "uiPortId = {}", uiPortId);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceAudio3dPortPush(OrbisAudio3dPortId uiPortId, OrbisAudio3dBlocking eBlocking) {
LOG_INFO(Lib_Audio3d, "uiPortId = {}", uiPortId);
LOG_TRACE(Lib_Audio3d, "uiPortId = {}", uiPortId);
return ORBIS_OK;
}
@ -110,7 +110,7 @@ int PS4_SYSV_ABI sceAudio3dObjectSetAttributes(OrbisAudio3dPortId uiPortId,
int PS4_SYSV_ABI sceAudio3dBedWrite(OrbisAudio3dPortId uiPortId, unsigned int uiNumChannels,
OrbisAudio3dFormat eFormat, const void* pBuffer,
unsigned int uiNumSamples) {
LOG_INFO(Lib_Audio3d, "uiPortId = {}, uiNumChannels = {}, uiNumSamples = {}", uiPortId,
LOG_TRACE(Lib_Audio3d, "uiPortId = {}, uiNumChannels = {}, uiNumSamples = {}", uiPortId,
uiNumChannels, uiNumSamples);
return ORBIS_OK;
}
@ -191,7 +191,7 @@ s32 PS4_SYSV_ABI sceAudio3dAudioOutClose(s32 handle) {
}
s32 PS4_SYSV_ABI sceAudio3dAudioOutOutput(s32 handle, const void* ptr) {
LOG_INFO(Lib_Audio3d, "handle = {}", handle);
LOG_TRACE(Lib_Audio3d, "handle = {}", handle);
if (ptr == nullptr) {
LOG_ERROR(Lib_Audio3d, "invalid Output ptr");
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;

View file

@ -6,7 +6,7 @@
#include "common/logging/log.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/kernel/libkernel.h"
#include "core/libraries/kernel/kernel.h"
using namespace Libraries::Kernel;

View file

@ -1,18 +1,14 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "avplayer.h"
#include "avplayer_impl.h"
#include "common/logging/log.h"
#include "core/libraries/avplayer/avplayer.h"
#include "core/libraries/avplayer/avplayer_impl.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/kernel/thread_management.h"
#include "core/libraries/libs.h"
namespace Libraries::AvPlayer {
using namespace Kernel;
s32 PS4_SYSV_ABI sceAvPlayerAddSource(SceAvPlayerHandle handle, const char* filename) {
LOG_TRACE(Lib_AvPlayer, "filename = {}", filename);
if (handle == nullptr) {
@ -309,7 +305,7 @@ void RegisterlibSceAvPlayer(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("XC9wM+xULz8", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerJumpToTime);
LIB_FUNCTION("9y5v+fGN4Wk", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerPause);
LIB_FUNCTION("HD1YKVU26-M", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerPostInit);
LIB_FUNCTION("agig-iDRrTE", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerPrintf);
// LIB_FUNCTION("agig-iDRrTE", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerPrintf);
LIB_FUNCTION("w5moABNwnRY", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerResume);
LIB_FUNCTION("k-q+xOxdc3E", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0,
sceAvPlayerSetAvSyncMode);

View file

@ -5,8 +5,8 @@
#include "common/types.h"
#include <stdarg.h> // va_list
#include <stddef.h> // size_t
#include <cstdarg> // va_list
#include <cstddef> // size_t
namespace Core::Loader {
class SymbolsResolver;

View file

@ -1,24 +1,16 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "avplayer.h"
#include "avplayer_common.h"
#include <algorithm> // std::equal
#include <cctype> // std::tolower
#include <algorithm> // std::equal
#include <cctype> // std::tolower
#include <string_view> // std::string_view
#include "core/libraries/avplayer/avplayer.h"
#include "core/libraries/avplayer/avplayer_common.h"
namespace Libraries::AvPlayer {
using namespace Kernel;
static bool ichar_equals(char a, char b) {
return std::tolower(static_cast<unsigned char>(a)) ==
std::tolower(static_cast<unsigned char>(b));
}
static bool iequals(std::string_view l, std::string_view r) {
return std::ranges::equal(l, r, ichar_equals);
return std::ranges::equal(l, r, [](u8 a, u8 b) { return std::tolower(a) == std::tolower(b); });
}
SceAvPlayerSourceType GetSourceType(std::string_view path) {

View file

@ -3,16 +3,14 @@
#pragma once
#include "avplayer.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/libraries/kernel/thread_management.h"
#include <mutex>
#include <optional>
#include <string_view>
#include <utility>
#include <queue>
#include "core/libraries/avplayer/avplayer.h"
#define AVPLAYER_IS_ERROR(x) ((x) < 0)
namespace Libraries::AvPlayer {

View file

@ -1,20 +1,16 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "avplayer_file_streamer.h"
#include "avplayer_common.h"
#include <algorithm> // std::max, std::min
#include <magic_enum.hpp>
#include "core/libraries/avplayer/avplayer_file_streamer.h"
extern "C" {
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
}
#include <algorithm> // std::max, std::min
#define AVPLAYER_AVIO_BUFFER_SIZE 4096
constexpr u32 AVPLAYER_AVIO_BUFFER_SIZE = 4096;
namespace Libraries::AvPlayer {

View file

@ -3,11 +3,9 @@
#pragma once
#include "avplayer.h"
#include "avplayer_data_streamer.h"
#include <string_view>
#include <vector>
#include "core/libraries/avplayer/avplayer.h"
#include "core/libraries/avplayer/avplayer_data_streamer.h"
struct AVIOContext;

View file

@ -1,17 +1,10 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "avplayer_common.h"
#include "avplayer_file_streamer.h"
#include "avplayer_impl.h"
#include "common/logging/log.h"
#include "common/singleton.h"
#include "core/libraries/avplayer/avplayer_common.h"
#include "core/libraries/avplayer/avplayer_impl.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/kernel/libkernel.h"
#include "core/linker.h"
using namespace Libraries::Kernel;
#include "core/tls.h"
namespace Libraries::AvPlayer {
@ -19,32 +12,28 @@ void* PS4_SYSV_ABI AvPlayer::Allocate(void* handle, u32 alignment, u32 size) {
const auto* const self = reinterpret_cast<AvPlayer*>(handle);
const auto allocate = self->m_init_data_original.memory_replacement.allocate;
const auto ptr = self->m_init_data_original.memory_replacement.object_ptr;
const auto* linker = Common::Singleton<Core::Linker>::Instance();
return linker->ExecuteGuest(allocate, ptr, alignment, size);
return Core::ExecuteGuest(allocate, ptr, alignment, size);
}
void PS4_SYSV_ABI AvPlayer::Deallocate(void* handle, void* memory) {
const auto* const self = reinterpret_cast<AvPlayer*>(handle);
const auto deallocate = self->m_init_data_original.memory_replacement.deallocate;
const auto ptr = self->m_init_data_original.memory_replacement.object_ptr;
const auto* linker = Common::Singleton<Core::Linker>::Instance();
return linker->ExecuteGuest(deallocate, ptr, memory);
return Core::ExecuteGuest(deallocate, ptr, memory);
}
void* PS4_SYSV_ABI AvPlayer::AllocateTexture(void* handle, u32 alignment, u32 size) {
const auto* const self = reinterpret_cast<AvPlayer*>(handle);
const auto allocate = self->m_init_data_original.memory_replacement.allocate_texture;
const auto ptr = self->m_init_data_original.memory_replacement.object_ptr;
const auto* linker = Common::Singleton<Core::Linker>::Instance();
return linker->ExecuteGuest(allocate, ptr, alignment, size);
return Core::ExecuteGuest(allocate, ptr, alignment, size);
}
void PS4_SYSV_ABI AvPlayer::DeallocateTexture(void* handle, void* memory) {
const auto* const self = reinterpret_cast<AvPlayer*>(handle);
const auto deallocate = self->m_init_data_original.memory_replacement.deallocate_texture;
const auto ptr = self->m_init_data_original.memory_replacement.object_ptr;
const auto* linker = Common::Singleton<Core::Linker>::Instance();
return linker->ExecuteGuest(deallocate, ptr, memory);
return Core::ExecuteGuest(deallocate, ptr, memory);
}
int PS4_SYSV_ABI AvPlayer::OpenFile(void* handle, const char* filename) {
@ -53,8 +42,7 @@ int PS4_SYSV_ABI AvPlayer::OpenFile(void* handle, const char* filename) {
const auto open = self->m_init_data_original.file_replacement.open;
const auto ptr = self->m_init_data_original.file_replacement.object_ptr;
const auto* linker = Common::Singleton<Core::Linker>::Instance();
return linker->ExecuteGuest(open, ptr, filename);
return Core::ExecuteGuest(open, ptr, filename);
}
int PS4_SYSV_ABI AvPlayer::CloseFile(void* handle) {
@ -63,8 +51,7 @@ int PS4_SYSV_ABI AvPlayer::CloseFile(void* handle) {
const auto close = self->m_init_data_original.file_replacement.close;
const auto ptr = self->m_init_data_original.file_replacement.object_ptr;
const auto* linker = Common::Singleton<Core::Linker>::Instance();
return linker->ExecuteGuest(close, ptr);
return Core::ExecuteGuest(close, ptr);
}
int PS4_SYSV_ABI AvPlayer::ReadOffsetFile(void* handle, u8* buffer, u64 position, u32 length) {
@ -73,8 +60,7 @@ int PS4_SYSV_ABI AvPlayer::ReadOffsetFile(void* handle, u8* buffer, u64 position
const auto read_offset = self->m_init_data_original.file_replacement.readOffset;
const auto ptr = self->m_init_data_original.file_replacement.object_ptr;
const auto* linker = Common::Singleton<Core::Linker>::Instance();
return linker->ExecuteGuest(read_offset, ptr, buffer, position, length);
return Core::ExecuteGuest(read_offset, ptr, buffer, position, length);
}
u64 PS4_SYSV_ABI AvPlayer::SizeFile(void* handle) {
@ -83,8 +69,7 @@ u64 PS4_SYSV_ABI AvPlayer::SizeFile(void* handle) {
const auto size = self->m_init_data_original.file_replacement.size;
const auto ptr = self->m_init_data_original.file_replacement.object_ptr;
const auto* linker = Common::Singleton<Core::Linker>::Instance();
return linker->ExecuteGuest(size, ptr);
return Core::ExecuteGuest(size, ptr);
}
SceAvPlayerInitData AvPlayer::StubInitData(const SceAvPlayerInitData& data) {

View file

@ -3,11 +3,8 @@
#pragma once
#include "avplayer.h"
#include "avplayer_data_streamer.h"
#include "avplayer_state.h"
#include "core/libraries/kernel/thread_management.h"
#include "core/libraries/avplayer/avplayer.h"
#include "core/libraries/avplayer/avplayer_state.h"
#include <mutex>
@ -17,7 +14,6 @@ extern "C" {
}
#include <memory>
#include <vector>
namespace Libraries::AvPlayer {

View file

@ -1,16 +1,12 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "avplayer_source.h"
#include "avplayer_file_streamer.h"
#include "common/alignment.h"
#include "common/singleton.h"
#include "common/thread.h"
#include "core/file_sys/fs.h"
#include "core/libraries/kernel/time_management.h"
#include "core/libraries/avplayer/avplayer_file_streamer.h"
#include "core/libraries/avplayer/avplayer_source.h"
#include <magic_enum.hpp>
@ -35,8 +31,6 @@ av_always_inline std::string av_err2string(int errnum) {
namespace Libraries::AvPlayer {
using namespace Kernel;
AvPlayerSource::AvPlayerSource(AvPlayerStateCallback& state, bool use_vdec2)
: m_state(state), m_use_vdec2(use_vdec2) {}
@ -258,11 +252,9 @@ bool AvPlayerSource::Start() {
LOG_ERROR(Lib_AvPlayer, "Could not start playback. NULL context.");
return false;
}
m_demuxer_thread = std::jthread([this](std::stop_token stop) { this->DemuxerThread(stop); });
m_video_decoder_thread =
std::jthread([this](std::stop_token stop) { this->VideoDecoderThread(stop); });
m_audio_decoder_thread =
std::jthread([this](std::stop_token stop) { this->AudioDecoderThread(stop); });
m_demuxer_thread.Run([this](std::stop_token stop) { this->DemuxerThread(stop); });
m_video_decoder_thread.Run([this](std::stop_token stop) { this->VideoDecoderThread(stop); });
m_audio_decoder_thread.Run([this](std::stop_token stop) { this->AudioDecoderThread(stop); });
m_start_time = std::chrono::high_resolution_clock::now();
return true;
}
@ -275,18 +267,10 @@ bool AvPlayerSource::Stop() {
return false;
}
m_video_decoder_thread.request_stop();
m_audio_decoder_thread.request_stop();
m_demuxer_thread.request_stop();
if (m_demuxer_thread.joinable()) {
m_demuxer_thread.join();
}
if (m_video_decoder_thread.joinable()) {
m_video_decoder_thread.join();
}
if (m_audio_decoder_thread.joinable()) {
m_audio_decoder_thread.join();
}
m_video_decoder_thread.Stop();
m_audio_decoder_thread.Stop();
m_demuxer_thread.Stop();
if (m_current_audio_frame.has_value()) {
m_audio_buffers.Push(std::move(m_current_audio_frame.value()));
m_current_audio_frame.reset();
@ -510,12 +494,8 @@ void AvPlayerSource::DemuxerThread(std::stop_token stop) {
m_video_frames_cv.Notify();
m_audio_frames_cv.Notify();
if (m_video_decoder_thread.joinable()) {
m_video_decoder_thread.join();
}
if (m_audio_decoder_thread.joinable()) {
m_audio_decoder_thread.join();
}
m_video_decoder_thread.Join();
m_audio_decoder_thread.Join();
m_state.OnEOF();
LOG_INFO(Lib_AvPlayer, "Demuxer Thread exited normally");
@ -808,8 +788,8 @@ void AvPlayerSource::AudioDecoderThread(std::stop_token stop) {
}
bool AvPlayerSource::HasRunningThreads() const {
return m_demuxer_thread.joinable() || m_video_decoder_thread.joinable() ||
m_audio_decoder_thread.joinable();
return m_demuxer_thread.Joinable() || m_video_decoder_thread.Joinable() ||
m_audio_decoder_thread.Joinable();
}
} // namespace Libraries::AvPlayer

View file

@ -3,20 +3,18 @@
#pragma once
#include "avplayer.h"
#include "avplayer_common.h"
#include "avplayer_data_streamer.h"
#include "common/polyfill_thread.h"
#include "common/types.h"
#include "core/libraries/kernel/thread_management.h"
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <optional>
#include <string>
#include <string_view>
#include "common/assert.h"
#include "core/libraries/avplayer/avplayer.h"
#include "core/libraries/avplayer/avplayer_common.h"
#include "core/libraries/avplayer/avplayer_data_streamer.h"
#include "core/libraries/kernel/threads.h"
struct AVCodecContext;
struct AVFormatContext;
@ -139,8 +137,6 @@ public:
bool IsActive();
private:
using ScePthread = Kernel::ScePthread;
static void ReleaseAVPacket(AVPacket* packet);
static void ReleaseAVFrame(AVFrame* frame);
static void ReleaseAVCodecContext(AVCodecContext* context);
@ -204,9 +200,9 @@ private:
EventCV m_stop_cv{};
std::mutex m_state_mutex{};
std::jthread m_demuxer_thread{};
std::jthread m_video_decoder_thread{};
std::jthread m_audio_decoder_thread{};
Kernel::Thread m_demuxer_thread{};
Kernel::Thread m_video_decoder_thread{};
Kernel::Thread m_audio_decoder_thread{};
AVFormatContextPtr m_avformat_context{nullptr, &ReleaseAVFormatContext};
AVCodecContextPtr m_video_codec_context{nullptr, &ReleaseAVCodecContext};

View file

@ -1,22 +1,17 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "avplayer_file_streamer.h"
#include "avplayer_source.h"
#include "avplayer_state.h"
#include "common/singleton.h"
#include "common/logging/log.h"
#include "common/thread.h"
#include "core/libraries/avplayer/avplayer_source.h"
#include "core/libraries/avplayer/avplayer_state.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/kernel/time_management.h"
#include "core/linker.h"
#include "core/tls.h"
#include <magic_enum.hpp>
namespace Libraries::AvPlayer {
using namespace Kernel;
void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, SceAvPlayerEvents event_id,
s32 source_id, void* event_data) {
auto const self = reinterpret_cast<AvPlayerState*>(opaque);
@ -96,8 +91,7 @@ void AvPlayerState::DefaultEventCallback(void* opaque, SceAvPlayerEvents event_i
const auto callback = self->m_event_replacement.event_callback;
const auto ptr = self->m_event_replacement.object_ptr;
if (callback != nullptr) {
const auto* linker = Common::Singleton<Core::Linker>::Instance();
linker->ExecuteGuest(callback, ptr, event_id, 0, event_data);
Core::ExecuteGuest(callback, ptr, event_id, 0, event_data);
}
}
@ -123,10 +117,7 @@ AvPlayerState::~AvPlayerState() {
std::unique_lock lock(m_source_mutex);
m_up_source.reset();
}
if (m_controller_thread.joinable()) {
m_controller_thread.request_stop();
m_controller_thread.join();
}
m_controller_thread.Stop();
m_event_queue.Clear();
}
@ -227,8 +218,7 @@ void AvPlayerState::WarningEvent(s32 id) {
// Called inside GAME thread
void AvPlayerState::StartControllerThread() {
m_controller_thread =
std::jthread([this](std::stop_token stop) { this->AvControllerThread(stop); });
m_controller_thread.Run([this](std::stop_token stop) { this->AvControllerThread(stop); });
}
// Called inside GAME thread

View file

@ -3,17 +3,14 @@
#pragma once
#include "avplayer.h"
#include "avplayer_data_streamer.h"
#include "avplayer_source.h"
#include "common/polyfill_thread.h"
#include "core/libraries/kernel/thread_management.h"
#include <memory>
#include <mutex>
#include <shared_mutex>
#include "core/libraries/avplayer/avplayer.h"
#include "core/libraries/avplayer/avplayer_source.h"
#include "core/libraries/kernel/threads.h"
namespace Libraries::AvPlayer {
class Stream;
@ -83,7 +80,7 @@ private:
std::shared_mutex m_source_mutex{};
std::mutex m_state_machine_mutex{};
std::mutex m_event_handler_mutex{};
std::jthread m_controller_thread{};
Kernel::Thread m_controller_thread{};
AvPlayerQueue<AvPlayerEvent> m_event_queue{};
};

View file

@ -4,10 +4,9 @@
#include "fiber.h"
#include "common/logging/log.h"
#include "common/singleton.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/libs.h"
#include "core/linker.h"
#include "core/tls.h"
#ifdef _WIN64
#include <windows.h>
@ -31,9 +30,7 @@ void FiberEntry(void* param) {
argRun = *fiber->pArgRun;
}
const auto* linker = Common::Singleton<Core::Linker>::Instance();
linker->ExecuteGuest(fiber->entry, fiber->argOnInitialize, argRun);
Core::ExecuteGuest(fiber->entry, fiber->argOnInitialize, argRun);
UNREACHABLE();
}
@ -281,4 +278,4 @@ void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("JzyT91ucGDc", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberRename);
}
} // namespace Libraries::Fiber
} // namespace Libraries::Fiber

View file

@ -8,12 +8,11 @@
#include "common/config.h"
#include "common/debug.h"
#include "common/logging/log.h"
#include "common/path_util.h"
#include "common/slot_vector.h"
#include "core/address_space.h"
#include "core/debug_state.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/kernel/libkernel.h"
#include "core/libraries/kernel/process.h"
#include "core/libraries/libs.h"
#include "core/libraries/videoout/video_out.h"
#include "core/platform.h"
@ -377,9 +376,12 @@ int PS4_SYSV_ABI sceGnmAreSubmitsAllowed() {
return submission_lock == 0;
}
int PS4_SYSV_ABI sceGnmBeginWorkload() {
LOG_ERROR(Lib_GnmDriver, "(STUBBED) called");
return ORBIS_OK;
int PS4_SYSV_ABI sceGnmBeginWorkload(u32 workload_stream, u64* workload) {
if (workload) {
*workload = (-(u32)(workload_stream < 0x10) & 1);
return 0xf < workload_stream;
}
return 3;
}
s32 PS4_SYSV_ABI sceGnmComputeWaitOnAddress(u32* cmdbuf, u32 size, uintptr_t addr, u32 mask,
@ -413,9 +415,12 @@ int PS4_SYSV_ABI sceGnmComputeWaitSemaphore() {
return ORBIS_OK;
}
int PS4_SYSV_ABI sceGnmCreateWorkloadStream() {
LOG_ERROR(Lib_GnmDriver, "(STUBBED) called");
return ORBIS_OK;
int PS4_SYSV_ABI sceGnmCreateWorkloadStream(u64 param1, u32* workload_stream) {
if (param1 != 0 && workload_stream) {
*workload_stream = 1;
return 0;
}
return 3;
}
int PS4_SYSV_ABI sceGnmDebuggerGetAddressWatch() {
@ -952,9 +957,11 @@ int PS4_SYSV_ABI sceGnmDriverTriggerCapture() {
return ORBIS_OK;
}
int PS4_SYSV_ABI sceGnmEndWorkload() {
LOG_ERROR(Lib_GnmDriver, "(STUBBED) called");
return ORBIS_OK;
int PS4_SYSV_ABI sceGnmEndWorkload(u64 workload) {
if (workload != 0) {
return (0xf < ((workload >> 0x38) & 0xff)) * 2;
}
return 2;
}
s32 PS4_SYSV_ABI sceGnmFindResourcesPublic() {
@ -2124,6 +2131,14 @@ s32 PS4_SYSV_ABI sceGnmSubmitAndFlipCommandBuffers(u32 count, u32* dcb_gpu_addrs
u32* dcb_sizes_in_bytes, u32* ccb_gpu_addrs[],
u32* ccb_sizes_in_bytes, u32 vo_handle,
u32 buf_idx, u32 flip_mode, u32 flip_arg) {
return sceGnmSubmitAndFlipCommandBuffersForWorkload(
count, count, dcb_gpu_addrs, dcb_sizes_in_bytes, ccb_gpu_addrs, ccb_sizes_in_bytes,
vo_handle, buf_idx, flip_mode, flip_arg);
}
s32 PS4_SYSV_ABI sceGnmSubmitAndFlipCommandBuffersForWorkload(
u32 workload, u32 count, u32* dcb_gpu_addrs[], u32* dcb_sizes_in_bytes, u32* ccb_gpu_addrs[],
u32* ccb_sizes_in_bytes, u32 vo_handle, u32 buf_idx, u32 flip_mode, u32 flip_arg) {
LOG_DEBUG(Lib_GnmDriver, "called [buf = {}]", buf_idx);
auto* cmdbuf = dcb_gpu_addrs[count - 1];
@ -2140,14 +2155,11 @@ s32 PS4_SYSV_ABI sceGnmSubmitAndFlipCommandBuffers(u32 count, u32* dcb_gpu_addrs
ccb_sizes_in_bytes);
}
int PS4_SYSV_ABI sceGnmSubmitAndFlipCommandBuffersForWorkload() {
LOG_ERROR(Lib_GnmDriver, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceGnmSubmitCommandBuffers(u32 count, const u32* dcb_gpu_addrs[],
u32* dcb_sizes_in_bytes, const u32* ccb_gpu_addrs[],
u32* ccb_sizes_in_bytes) {
int PS4_SYSV_ABI sceGnmSubmitCommandBuffersForWorkload(u32 workload, u32 count,
const u32* dcb_gpu_addrs[],
u32* dcb_sizes_in_bytes,
const u32* ccb_gpu_addrs[],
u32* ccb_sizes_in_bytes) {
LOG_DEBUG(Lib_GnmDriver, "called");
if (!dcb_gpu_addrs || !dcb_sizes_in_bytes) {
@ -2232,9 +2244,11 @@ s32 PS4_SYSV_ABI sceGnmSubmitCommandBuffers(u32 count, const u32* dcb_gpu_addrs[
return ORBIS_OK;
}
int PS4_SYSV_ABI sceGnmSubmitCommandBuffersForWorkload() {
LOG_ERROR(Lib_GnmDriver, "(STUBBED) called");
return ORBIS_OK;
s32 PS4_SYSV_ABI sceGnmSubmitCommandBuffers(u32 count, const u32* dcb_gpu_addrs[],
u32* dcb_sizes_in_bytes, const u32* ccb_gpu_addrs[],
u32* ccb_sizes_in_bytes) {
return sceGnmSubmitCommandBuffersForWorkload(count, count, dcb_gpu_addrs, dcb_sizes_in_bytes,
ccb_gpu_addrs, ccb_sizes_in_bytes);
}
int PS4_SYSV_ABI sceGnmSubmitDone() {

View file

@ -4,7 +4,7 @@
#pragma once
#include "common/types.h"
#include "core/libraries/kernel/event_queues.h"
#include "core/libraries/kernel/equeue.h"
namespace Core::Loader {
class SymbolsResolver;
@ -16,11 +16,11 @@ using namespace Kernel;
s32 PS4_SYSV_ABI sceGnmAddEqEvent(SceKernelEqueue eq, u64 id, void* udata);
int PS4_SYSV_ABI sceGnmAreSubmitsAllowed();
int PS4_SYSV_ABI sceGnmBeginWorkload();
int PS4_SYSV_ABI sceGnmBeginWorkload(u32 workload_stream, u64* workload);
s32 PS4_SYSV_ABI sceGnmComputeWaitOnAddress(u32* cmdbuf, u32 size, uintptr_t addr, u32 mask,
u32 cmp_func, u32 ref);
int PS4_SYSV_ABI sceGnmComputeWaitSemaphore();
int PS4_SYSV_ABI sceGnmCreateWorkloadStream();
int PS4_SYSV_ABI sceGnmCreateWorkloadStream(u64 param1, u32* workload_stream);
int PS4_SYSV_ABI sceGnmDebuggerGetAddressWatch();
int PS4_SYSV_ABI sceGnmDebuggerHaltWavefront();
int PS4_SYSV_ABI sceGnmDebuggerReadGds();
@ -77,7 +77,7 @@ int PS4_SYSV_ABI sceGnmDriverInternalRetrieveGnmInterfaceForValidation();
int PS4_SYSV_ABI sceGnmDriverInternalVirtualQuery();
int PS4_SYSV_ABI sceGnmDriverTraceInProgress();
int PS4_SYSV_ABI sceGnmDriverTriggerCapture();
int PS4_SYSV_ABI sceGnmEndWorkload();
int PS4_SYSV_ABI sceGnmEndWorkload(u64 workload);
s32 PS4_SYSV_ABI sceGnmFindResourcesPublic();
void PS4_SYSV_ABI sceGnmFlushGarlic();
int PS4_SYSV_ABI sceGnmGetCoredumpAddress();
@ -210,11 +210,17 @@ s32 PS4_SYSV_ABI sceGnmSubmitAndFlipCommandBuffers(u32 count, u32* dcb_gpu_addrs
u32* dcb_sizes_in_bytes, u32* ccb_gpu_addrs[],
u32* ccb_sizes_in_bytes, u32 vo_handle,
u32 buf_idx, u32 flip_mode, u32 flip_arg);
int PS4_SYSV_ABI sceGnmSubmitAndFlipCommandBuffersForWorkload();
int PS4_SYSV_ABI sceGnmSubmitAndFlipCommandBuffersForWorkload(
u32 workload, u32 count, u32* dcb_gpu_addrs[], u32* dcb_sizes_in_bytes, u32* ccb_gpu_addrs[],
u32* ccb_sizes_in_bytes, u32 vo_handle, u32 buf_idx, u32 flip_mode, u32 flip_arg);
s32 PS4_SYSV_ABI sceGnmSubmitCommandBuffers(u32 count, const u32* dcb_gpu_addrs[],
u32* dcb_sizes_in_bytes, const u32* ccb_gpu_addrs[],
u32* ccb_sizes_in_bytes);
int PS4_SYSV_ABI sceGnmSubmitCommandBuffersForWorkload();
int PS4_SYSV_ABI sceGnmSubmitCommandBuffersForWorkload(u32 workload, u32 count,
const u32* dcb_gpu_addrs[],
u32* dcb_sizes_in_bytes,
const u32* ccb_gpu_addrs[],
u32* ccb_sizes_in_bytes);
int PS4_SYSV_ABI sceGnmSubmitDone();
int PS4_SYSV_ABI sceGnmUnmapComputeQueue();
int PS4_SYSV_ABI sceGnmUnregisterAllResourcesForOwner();

View file

@ -70,21 +70,19 @@ public:
}
void Execute(OrbisImeEventHandler handler, OrbisImeEvent* event, bool use_param_handler) {
const auto* linker = Common::Singleton<Core::Linker>::Instance();
if (m_ime_mode) {
OrbisImeParam param = m_param.ime;
if (use_param_handler) {
linker->ExecuteGuest(param.handler, param.arg, event);
Core::ExecuteGuest(param.handler, param.arg, event);
} else {
linker->ExecuteGuest(handler, param.arg, event);
Core::ExecuteGuest(handler, param.arg, event);
}
} else {
OrbisImeKeyboardParam param = m_param.key;
if (use_param_handler) {
linker->ExecuteGuest(param.handler, param.arg, event);
Core::ExecuteGuest(param.handler, param.arg, event);
} else {
linker->ExecuteGuest(handler, param.arg, event);
Core::ExecuteGuest(handler, param.arg, event);
}
}
}
@ -503,4 +501,4 @@ void RegisterlibSceIme(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("fwcPR7+7Rks", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshUpdateContext2);
};
} // namespace Libraries::Ime
} // namespace Libraries::Ime

View file

@ -11,7 +11,7 @@
#include "common/singleton.h"
#include "core/libraries/ime/ime_dialog.h"
#include "core/libraries/ime/ime_dialog_ui.h"
#include "core/linker.h"
#include "core/tls.h"
#include "imgui/imgui_std.h"
using namespace ImGui;
@ -124,9 +124,8 @@ bool ImeDialogState::CallTextFilter() {
return false;
}
auto* linker = Common::Singleton<Core::Linker>::Instance();
int ret =
linker->ExecuteGuest(text_filter, out_text, &out_text_length, src_text, src_text_length);
Core::ExecuteGuest(text_filter, out_text, &out_text_length, src_text, src_text_length);
if (ret != 0) {
return false;
@ -147,15 +146,12 @@ bool ImeDialogState::CallKeyboardFilter(const OrbisImeKeycode* src_keycode, u16*
return true;
}
auto* linker = Common::Singleton<Core::Linker>::Instance();
int ret = linker->ExecuteGuest(keyboard_filter, src_keycode, out_keycode, out_status, nullptr);
int ret = Core::ExecuteGuest(keyboard_filter, src_keycode, out_keycode, out_status, nullptr);
return ret == 0;
}
bool ImeDialogState::ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t orbis_text_len,
char* utf8_text, std::size_t utf8_text_len) {
std::fill(utf8_text, utf8_text + utf8_text_len, '\0');
const ImWchar* orbis_text_ptr = reinterpret_cast<const ImWchar*>(orbis_text);
ImTextStrToUtf8(utf8_text, utf8_text_len, orbis_text_ptr, orbis_text_ptr + orbis_text_len);
@ -165,7 +161,6 @@ bool ImeDialogState::ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t
bool ImeDialogState::ConvertUTF8ToOrbis(const char* utf8_text, std::size_t utf8_text_len,
char16_t* orbis_text, std::size_t orbis_text_len) {
std::fill(orbis_text, orbis_text + orbis_text_len, u'\0');
ImTextStrFromUtf8(reinterpret_cast<ImWchar*>(orbis_text), orbis_text_len, utf8_text, nullptr);
@ -387,4 +382,4 @@ int ImeDialogUi::InputTextCallback(ImGuiInputTextCallbackData* data) {
return 0;
}
} // namespace Libraries::ImeDialog
} // namespace Libraries::ImeDialog

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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, &micros);
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

View file

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

View file

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

View file

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

View file

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

View file

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

View 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

View 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

View file

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

View file

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

View file

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

View file

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

View 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

View file

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

View file

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

View 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

View 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

View 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

View file

@ -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, &micros);
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

View 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

View 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

View file

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

View 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

View 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, &param);
*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

View 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

View 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

View 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

View 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

View file

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

View file

@ -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, &param);
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

View 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

View 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

View 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

View 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

View 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

View 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

View file

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

View file

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

View file

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

View file

@ -14,7 +14,7 @@
#include "core/libraries/ime/error_dialog.h"
#include "core/libraries/ime/ime.h"
#include "core/libraries/ime/ime_dialog.h"
#include "core/libraries/kernel/libkernel.h"
#include "core/libraries/kernel/kernel.h"
#include "core/libraries/libc_internal/libc_internal.h"
#include "core/libraries/libpng/pngdec.h"
#include "core/libraries/libs.h"
@ -49,11 +49,9 @@ namespace Libraries {
void InitHLELibs(Core::Loader::SymbolsResolver* sym) {
LOG_INFO(Lib_Kernel, "Initializing HLE libraries");
Libraries::Kernel::LibKernel_Register(sym);
Libraries::Kernel::RegisterKernel(sym);
Libraries::GnmDriver::RegisterlibSceGnmDriver(sym);
Libraries::VideoOut::RegisterLib(sym);
// New libraries folder from autogen
Libraries::UserService::RegisterlibSceUserService(sym);
Libraries::SystemService::RegisterlibSceSystemService(sym);
Libraries::CommonDialog::RegisterlibSceCommonDialog(sym);

View file

@ -9,41 +9,6 @@
#include "core/loader/elf.h"
#include "core/loader/symbols_resolver.h"
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 wrapper_impl;
template <StringLiteral name, class R, class... Args, PS4_SYSV_ABI R (*f)(Args...)>
struct wrapper_impl<name, PS4_SYSV_ABI R (*)(Args...), f> {
static R PS4_SYSV_ABI wrap(Args... args) {
if (std::string_view(name.value) != "scePthreadEqual" &&
std::string_view(name.value) != "sceUserServiceGetEvent") {
// LOG_WARNING(Core_Linker, "Function {} called", name.value);
}
if constexpr (std::is_same_v<R, s32> || std::is_same_v<R, u32>) {
const u32 ret = f(args...);
if (ret != 0 && std::string_view(name.value) != "scePthreadEqual") {
LOG_WARNING(Core_Linker, "Function {} returned {:#x}", name.value, ret);
}
return ret;
}
// stuff
return f(args...);
}
};
template <StringLiteral name, class F, F f>
constexpr auto wrapper = wrapper_impl<name, F, f>::wrap;
// #define W(foo) wrapper<#foo, decltype(&foo), foo>
#define W(foo) foo
#define LIB_FUNCTION(nid, lib, libversion, mod, moduleVersionMajor, moduleVersionMinor, function) \
@ -56,7 +21,7 @@ constexpr auto wrapper = wrapper_impl<name, F, f>::wrap;
sr.module_version_major = moduleVersionMajor; \
sr.module_version_minor = moduleVersionMinor; \
sr.type = Core::Loader::SymbolType::Function; \
auto func = reinterpret_cast<u64>(W(function)); \
auto func = reinterpret_cast<u64>(function); \
sym->AddSymbol(sr, func); \
}

View file

@ -1,80 +1,63 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/singleton.h"
#include "core/linker.h"
#include "net_ctl_codes.h"
#include "net_ctl_obj.h"
#include <algorithm>
#include "core/libraries/network/net_ctl_codes.h"
#include "core/libraries/network/net_ctl_obj.h"
#include "core/tls.h"
Libraries::NetCtl::NetCtlInternal::NetCtlInternal() {
callbacks.fill({nullptr, nullptr});
nptoolCallbacks.fill({nullptr, nullptr});
}
namespace Libraries::NetCtl {
Libraries::NetCtl::NetCtlInternal::~NetCtlInternal() {}
NetCtlInternal::NetCtlInternal() = default;
s32 Libraries::NetCtl::NetCtlInternal::registerCallback(OrbisNetCtlCallback func, void* arg) {
std::unique_lock lock{m_mutex};
NetCtlInternal::~NetCtlInternal() = default;
s32 NetCtlInternal::RegisterCallback(OrbisNetCtlCallback func, void* arg) {
std::scoped_lock lock{m_mutex};
// Find the next available slot
int next_id = 0;
for (const auto& callback : callbacks) {
if (callback.func == nullptr) {
break;
}
next_id++;
}
if (next_id == 8) {
const auto it = std::ranges::find(callbacks, nullptr, &NetCtlCallback::func);
if (it == callbacks.end()) {
return ORBIS_NET_CTL_ERROR_CALLBACK_MAX;
}
const int next_id = std::distance(callbacks.begin(), it);
callbacks[next_id].func = func;
callbacks[next_id].arg = arg;
return next_id;
}
s32 Libraries::NetCtl::NetCtlInternal::registerNpToolkitCallback(
OrbisNetCtlCallbackForNpToolkit func, void* arg) {
std::unique_lock lock{m_mutex};
s32 NetCtlInternal::RegisterNpToolkitCallback(OrbisNetCtlCallbackForNpToolkit func, void* arg) {
std::scoped_lock lock{m_mutex};
// Find the next available slot
int next_id = 0;
for (const auto& callback : nptoolCallbacks) {
if (callback.func == nullptr) {
break;
}
next_id++;
}
if (next_id == 8) {
const auto it = std::ranges::find(nptool_callbacks, nullptr, &NetCtlCallbackForNpToolkit::func);
if (it == nptool_callbacks.end()) {
return ORBIS_NET_CTL_ERROR_CALLBACK_MAX;
}
nptoolCallbacks[next_id].func = func;
nptoolCallbacks[next_id].arg = arg;
const int next_id = std::distance(nptool_callbacks.begin(), it);
nptool_callbacks[next_id].func = func;
nptool_callbacks[next_id].arg = arg;
return next_id;
}
void Libraries::NetCtl::NetCtlInternal::checkCallback() {
std::unique_lock lock{m_mutex};
const auto* linker = Common::Singleton<Core::Linker>::Instance();
for (auto& callback : callbacks) {
if (callback.func != nullptr) {
linker->ExecuteGuest(callback.func, ORBIS_NET_CTL_EVENT_TYPE_DISCONNECTED,
callback.arg);
void NetCtlInternal::CheckCallback() {
std::scoped_lock lock{m_mutex};
for (const auto [func, arg] : callbacks) {
if (func != nullptr) {
Core::ExecuteGuest(func, ORBIS_NET_CTL_EVENT_TYPE_DISCONNECTED, arg);
}
}
}
void Libraries::NetCtl::NetCtlInternal::checkNpToolkitCallback() {
std::unique_lock lock{m_mutex};
const auto* linker = Common::Singleton<Core::Linker>::Instance();
for (auto& callback : nptoolCallbacks) {
if (callback.func != nullptr) {
linker->ExecuteGuest(callback.func, ORBIS_NET_CTL_EVENT_TYPE_DISCONNECTED,
callback.arg);
void NetCtlInternal::CheckNpToolkitCallback() {
std::scoped_lock lock{m_mutex};
for (const auto [func, arg] : nptool_callbacks) {
if (func != nullptr) {
Core::ExecuteGuest(func, ORBIS_NET_CTL_EVENT_TYPE_DISCONNECTED, arg);
}
}
}
} // namespace Libraries::NetCtl

View file

@ -3,9 +3,7 @@
#pragma once
#include <condition_variable>
#include <mutex>
#include "common/types.h"
namespace Libraries::NetCtl {
@ -25,16 +23,17 @@ struct NetCtlCallbackForNpToolkit {
class NetCtlInternal {
public:
NetCtlInternal();
explicit NetCtlInternal();
~NetCtlInternal();
s32 registerCallback(OrbisNetCtlCallback func, void* arg);
s32 registerNpToolkitCallback(OrbisNetCtlCallbackForNpToolkit func, void* arg);
void checkCallback();
void checkNpToolkitCallback();
s32 RegisterCallback(OrbisNetCtlCallback func, void* arg);
s32 RegisterNpToolkitCallback(OrbisNetCtlCallbackForNpToolkit func, void* arg);
void CheckCallback();
void CheckNpToolkitCallback();
public:
std::array<NetCtlCallback, 8> nptoolCallbacks;
std::array<NetCtlCallbackForNpToolkit, 8> callbacks;
std::array<NetCtlCallbackForNpToolkit, 8> nptool_callbacks{};
std::array<NetCtlCallback, 8> callbacks{};
std::mutex m_mutex;
};
} // namespace Libraries::NetCtl

View file

@ -13,7 +13,6 @@
#endif
#include "common/logging/log.h"
#include "common/singleton.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/libs.h"
#include "core/libraries/network/net_ctl_codes.h"
@ -21,6 +20,8 @@
namespace Libraries::NetCtl {
static NetCtlInternal netctl;
int PS4_SYSV_ABI sceNetBweCheckCallbackIpcInt() {
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
return ORBIS_OK;
@ -92,8 +93,7 @@ int PS4_SYSV_ABI sceNetCtlUnregisterCallbackV6() {
}
int PS4_SYSV_ABI sceNetCtlCheckCallback() {
auto* netctl = Common::Singleton<Libraries::NetCtl::NetCtlInternal>::Instance();
netctl->checkCallback();
netctl.CheckCallback();
return ORBIS_OK;
}
@ -298,8 +298,7 @@ int PS4_SYSV_ABI sceNetCtlRegisterCallback(OrbisNetCtlCallback func, void* arg,
if (!func || !cid) {
return ORBIS_NET_CTL_ERROR_INVALID_ADDR;
}
auto* netctl = Common::Singleton<Libraries::NetCtl::NetCtlInternal>::Instance();
s32 result = netctl->registerCallback(func, arg);
s32 result = netctl.RegisterCallback(func, arg);
if (result < 0) {
return result;
} else {
@ -374,8 +373,7 @@ int PS4_SYSV_ABI Func_D8DCB6973537A3DC() {
}
int PS4_SYSV_ABI sceNetCtlCheckCallbackForNpToolkit() {
auto* netctl = Common::Singleton<Libraries::NetCtl::NetCtlInternal>::Instance();
netctl->checkNpToolkitCallback();
netctl.CheckNpToolkitCallback();
return ORBIS_OK;
}
@ -389,8 +387,7 @@ int PS4_SYSV_ABI sceNetCtlRegisterCallbackForNpToolkit(OrbisNetCtlCallbackForNpT
if (!func || !cid) {
return ORBIS_NET_CTL_ERROR_INVALID_ADDR;
}
auto* netctl = Common::Singleton<Libraries::NetCtl::NetCtlInternal>::Instance();
s32 result = netctl->registerNpToolkitCallback(func, arg);
s32 result = netctl.RegisterNpToolkitCallback(func, arg);
if (result < 0) {
return result;
} else {

View file

@ -4,7 +4,7 @@
#pragma once
#include "common/types.h"
#include "net_ctl_obj.h"
#include "core/libraries/network/net_ctl_obj.h"
namespace Core::Loader {
class SymbolsResolver;

View file

@ -6,7 +6,7 @@
#include "common/logging/log.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/kernel/libkernel.h"
#include "core/libraries/kernel/kernel.h"
using namespace Libraries::Kernel;

View file

@ -1,13 +1,12 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <common/singleton.h>
#include <core/linker.h>
#include "common/config.h"
#include "common/logging/log.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/libs.h"
#include "np_manager.h"
#include "core/libraries/np_manager/np_manager.h"
#include "core/tls.h"
namespace Libraries::NpManager {
@ -2519,10 +2518,7 @@ struct NpStateCallbackForNpToolkit {
NpStateCallbackForNpToolkit NpStateCbForNp;
int PS4_SYSV_ABI sceNpCheckCallbackForLib() {
// LOG_ERROR(Lib_NpManager, "(STUBBED) called");
const auto* linker = Common::Singleton<Core::Linker>::Instance();
linker->ExecuteGuest(NpStateCbForNp.func, 1, ORBIS_NP_STATE_SIGNED_OUT,
NpStateCbForNp.userdata);
Core::ExecuteGuest(NpStateCbForNp.func, 1, ORBIS_NP_STATE_SIGNED_OUT, NpStateCbForNp.userdata);
return ORBIS_OK;
}

View file

@ -25,6 +25,7 @@ int PS4_SYSV_ABI scePadConnectPort() {
int PS4_SYSV_ABI scePadDeviceClassGetExtendedInformation(
s32 handle, OrbisPadDeviceClassExtendedInformation* pExtInfo) {
LOG_ERROR(Lib_Pad, "(STUBBED) called");
std::memset(pExtInfo, 0, sizeof(OrbisPadDeviceClassExtendedInformation));
if (Config::getUseSpecialPad()) {
pExtInfo->deviceClass = (OrbisPadDeviceClass)Config::getSpecialPadClass();
}

View file

@ -5,11 +5,11 @@
#include "common/logging/log.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/kernel/libkernel.h"
#include "core/libraries/kernel/time_management.h"
#include "core/libraries/kernel/process.h"
#include "core/libraries/kernel/time.h"
#include "core/libraries/libs.h"
#include "rtc.h"
#include "rtc_error.h"
#include "core/libraries/rtc/rtc.h"
#include "core/libraries/rtc/rtc_error.h"
namespace Libraries::Rtc {

View file

@ -32,8 +32,8 @@ int PS4_SYSV_ABI sceSysmoduleIsCameraPreloaded() {
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSysmoduleIsLoaded() {
LOG_ERROR(Lib_SysModule, "(STUBBED) called");
int PS4_SYSV_ABI sceSysmoduleIsLoaded(OrbisSysModule id) {
LOG_ERROR(Lib_SysModule, "(DUMMY) called module = {}", magic_enum::enum_name(id));
return ORBIS_OK;
}

View file

@ -151,7 +151,7 @@ int PS4_SYSV_ABI sceSysmoduleGetModuleHandleInternal();
int PS4_SYSV_ABI sceSysmoduleGetModuleInfoForUnwind();
int PS4_SYSV_ABI sceSysmoduleIsCalledFromSysModule();
int PS4_SYSV_ABI sceSysmoduleIsCameraPreloaded();
int PS4_SYSV_ABI sceSysmoduleIsLoaded();
int PS4_SYSV_ABI sceSysmoduleIsLoaded(OrbisSysModule id);
int PS4_SYSV_ABI sceSysmoduleIsLoadedInternal();
int PS4_SYSV_ABI sceSysmoduleLoadModule(OrbisSysModule id);
int PS4_SYSV_ABI sceSysmoduleLoadModuleByNameInternal();

View file

@ -2,7 +2,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <imgui.h>
#include <pthread.h>
#include "common/assert.h"
#include "common/config.h"
@ -10,7 +9,7 @@
#include "common/thread.h"
#include "core/debug_state.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/kernel/time_management.h"
#include "core/libraries/kernel/time.h"
#include "core/libraries/videoout/driver.h"
#include "core/platform.h"
#include "video_core/renderer_vulkan/vk_presenter.h"

View file

@ -3,7 +3,7 @@
#pragma once
#include "core/libraries/kernel/event_queues.h"
#include "core/libraries/kernel/equeue.h"
#include "core/libraries/videoout/buffer.h"
namespace Core::Loader {

View file

@ -11,27 +11,22 @@
#include "common/thread.h"
#include "core/aerolib/aerolib.h"
#include "core/aerolib/stubs.h"
#include "core/cpu_patches.h"
#include "core/libraries/kernel/memory_management.h"
#include "core/libraries/kernel/thread_management.h"
#include "core/libraries/kernel/memory.h"
#include "core/libraries/kernel/threads.h"
#include "core/linker.h"
#include "core/memory.h"
#include "core/tls.h"
#include "core/virtual_memory.h"
#include "debug_state.h"
namespace Core {
using ExitFunc = PS4_SYSV_ABI void (*)();
static PS4_SYSV_ABI void ProgramExitFunc() {
fmt::print("exit function called\n");
LOG_ERROR(Core_Linker, "Exit function called");
}
#ifdef ARCH_X86_64
static PS4_SYSV_ABI void RunMainEntry(VAddr addr, EntryParams* params, ExitFunc exit_func) {
// reinterpret_cast<entry_func_t>(addr)(params, exit_func); // can't be used, stack has to have
// a specific layout
static PS4_SYSV_ABI void* RunMainEntry [[noreturn]] (EntryParams* params) {
// Start shared library modules
asm volatile("andq $-16, %%rsp\n" // Align to 16 bytes
"subq $8, %%rsp\n" // videoout_basic expects the stack to be misaligned
@ -47,8 +42,9 @@ static PS4_SYSV_ABI void RunMainEntry(VAddr addr, EntryParams* params, ExitFunc
"jmp *%0\n" // can't use call here, as that would mangle the prepared stack.
// there's no coming back
:
: "r"(addr), "r"(params), "r"(exit_func)
: "r"(params->entry_addr), "r"(params), "r"(ProgramExitFunc)
: "rax", "rsi", "rdi");
UNREACHABLE();
}
#endif
@ -62,10 +58,8 @@ void Linker::Execute() {
}
// Calculate static TLS size.
for (const auto& module : m_modules) {
static_tls_size += module->tls.image_size;
module->tls.offset = static_tls_size;
}
Module* module = m_modules[0].get();
static_tls_size = module->tls.offset = module->tls.image_size;
// Relocate all modules
for (const auto& m : m_modules) {
@ -87,36 +81,17 @@ void Linker::Execute() {
}
}
// Init primary thread.
Common::SetCurrentThreadName("GAME_MainThread");
DebugState.AddCurrentThreadToGuestList();
Libraries::Kernel::pthreadInitSelfMainThread();
EnsureThreadInitialized(true);
main_thread.Run([this, module](std::stop_token) {
Common::SetCurrentThreadName("GAME_MainThread");
LoadSharedLibraries();
// Start shared library modules
for (auto& m : m_modules) {
if (m->IsSharedLib()) {
m->Start(0, nullptr, nullptr);
}
}
// Start main module.
EntryParams p{};
p.argc = 1;
p.argv[0] = "eboot.bin";
for (auto& m : m_modules) {
if (!m->IsSharedLib()) {
#ifdef ARCH_X86_64
ExecuteGuest(RunMainEntry, m->GetEntryAddress(), &p, ProgramExitFunc);
#else
UNIMPLEMENTED_MSG(
"Missing guest entrypoint implementation for target CPU architecture.");
#endif
}
}
SetTcbBase(nullptr);
// Start main module.
EntryParams params{};
params.argc = 1;
params.argv[0] = "eboot.bin";
params.entry_addr = module->GetEntryAddress();
RunMainEntry(&params);
});
}
s32 Linker::LoadModule(const std::filesystem::path& elf_name, bool is_dynamic) {
@ -149,10 +124,9 @@ Module* Linker::FindByAddress(VAddr address) {
}
void Linker::Relocate(Module* module) {
module->ForEachRelocation([&](elf_relocation* rel, u32 i, bool isJmpRel) {
const u32 bit_idx =
(isJmpRel ? module->dynamic_info.relocation_table_size / sizeof(elf_relocation) : 0) +
i;
module->ForEachRelocation([&](elf_relocation* rel, u32 i, bool is_jmp_rel) {
const u32 num_relocs = module->dynamic_info.relocation_table_size / sizeof(elf_relocation);
const u32 bit_idx = (is_jmp_rel ? num_relocs : 0) + i;
if (module->TestRelaBit(bit_idx)) {
return;
}
@ -160,7 +134,7 @@ void Linker::Relocate(Module* module) {
auto symbol = rel->GetSymbol();
auto addend = rel->rel_addend;
auto* symbol_table = module->dynamic_info.symbol_table;
auto* namesTlb = module->dynamic_info.str_table;
auto* names_tlb = module->dynamic_info.str_table;
const VAddr rel_base_virtual_addr = module->GetBaseAddress();
const VAddr rel_virtual_addr = rel_base_virtual_addr + rel->rel_offset;
@ -216,7 +190,7 @@ void Linker::Relocate(Module* module) {
break;
case STB_GLOBAL:
case STB_WEAK: {
rel_name = namesTlb + sym.st_name;
rel_name = names_tlb + sym.st_name;
if (Resolve(rel_name, rel_sym_type, module, &symrec)) {
// Only set the rela bit if the symbol was actually resolved and not stubbed.
module->SetRelaBit(bit_idx);
@ -225,7 +199,7 @@ void Linker::Relocate(Module* module) {
break;
}
default:
ASSERT_MSG(0, "unknown bind type {}", sym_bind);
UNREACHABLE_MSG("Unknown bind type {}", sym_bind);
}
rel_is_resolved = (symbol_virtual_addr != 0);
rel_value = (rel_is_resolved ? symbol_virtual_addr + addend : 0);
@ -239,7 +213,7 @@ void Linker::Relocate(Module* module) {
if (rel_is_resolved) {
VirtualMemory::memory_patch(rel_virtual_addr, rel_value);
} else {
LOG_INFO(Core_Linker, "function not patched! {}", rel_name);
LOG_INFO(Core_Linker, "Function not patched! {}", rel_name);
}
});
}
@ -310,7 +284,7 @@ void* Linker::TlsGetAddr(u64 module_index, u64 offset) {
const u32 old_num_dtvs = dtv_table[1].counter;
ASSERT_MSG(max_tls_index > old_num_dtvs, "Module unloading unsupported");
// Module was loaded, increase DTV table size.
DtvEntry* new_dtv_table = new DtvEntry[max_tls_index + 2];
DtvEntry* new_dtv_table = new DtvEntry[max_tls_index + 2]{};
std::memcpy(new_dtv_table + 2, dtv_table + 2, old_num_dtvs * sizeof(DtvEntry));
new_dtv_table[0].counter = dtv_generation_counter;
new_dtv_table[1].counter = max_tls_index;
@ -322,13 +296,13 @@ void* Linker::TlsGetAddr(u64 module_index, u64 offset) {
}
u8* addr = dtv_table[module_index + 1].pointer;
Module* module = m_modules[module_index - 1].get();
// LOG_INFO(Core_Linker, "Got DTV addr {} from module index {} with name {}",
// fmt::ptr(addr), module_index, module->file.filename().string());
if (!addr) {
// Module was just loaded by above code. Allocate TLS block for it.
Module* module = m_modules[module_index - 1].get();
const u32 init_image_size = module->tls.init_image_size;
// TODO: Determine if Windows will crash from this
u8* dest =
reinterpret_cast<u8*>(ExecuteGuest(heap_api->heap_malloc, module->tls.image_size));
u8* dest = reinterpret_cast<u8*>(heap_api->heap_malloc(module->tls.image_size));
const u8* src = reinterpret_cast<const u8*>(module->tls.image_virtual_addr);
std::memcpy(dest, src, init_image_size);
std::memset(dest + init_image_size, 0, module->tls.image_size - init_image_size);
@ -338,18 +312,7 @@ void* Linker::TlsGetAddr(u64 module_index, u64 offset) {
return addr + offset;
}
thread_local std::once_flag init_tls_flag;
void Linker::EnsureThreadInitialized(bool is_primary) const {
std::call_once(init_tls_flag, [this, is_primary] {
#ifdef ARCH_X86_64
InitializeThreadPatchStack();
#endif
InitTlsForThread(is_primary);
});
}
void Linker::InitTlsForThread(bool is_primary) const {
void* Linker::AllocateTlsForThread(bool is_primary) {
static constexpr size_t TcbSize = 0x40;
static constexpr size_t TlsAllocAlign = 0x20;
const size_t total_tls_size = Common::AlignUp(static_tls_size, TlsAllocAlign) + TcbSize;
@ -370,54 +333,20 @@ void Linker::InitTlsForThread(bool is_primary) const {
ASSERT_MSG(ret == 0, "Unable to allocate TLS+TCB for the primary thread");
} else {
if (heap_api) {
#ifndef WIN32
addr_out = ExecuteGuestWithoutTls(heap_api->heap_malloc, total_tls_size);
addr_out = Core::ExecuteGuest(heap_api->heap_malloc, total_tls_size);
} else {
addr_out = std::malloc(total_tls_size);
#else
// TODO: Windows tls malloc replacement, refer to rtld_tls_block_malloc
LOG_ERROR(Core_Linker, "TLS user malloc called, using std::malloc");
addr_out = std::malloc(total_tls_size);
if (!addr_out) {
auto pth_id = pthread_self();
auto handle = pthread_gethandle(pth_id);
ASSERT_MSG(addr_out,
"Cannot allocate TLS block defined for handle=%x, index=%d size=%d",
handle, pth_id, total_tls_size);
}
#endif
}
}
return addr_out;
}
// Initialize allocated memory and allocate DTV table.
const u32 num_dtvs = max_tls_index;
std::memset(addr_out, 0, total_tls_size);
DtvEntry* dtv_table = new DtvEntry[num_dtvs + 2];
// Initialize thread control block
u8* addr = reinterpret_cast<u8*>(addr_out);
Tcb* tcb = reinterpret_cast<Tcb*>(addr + 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] (why?)
dtv_table[0].counter = dtv_generation_counter;
dtv_table[1].counter = num_dtvs;
// Copy init images to TLS thread blocks and map them to DTV slots.
for (u32 i = 0; i < num_static_modules; i++) {
auto* module = m_modules[i].get();
if (module->tls.image_size == 0) {
continue;
}
u8* dest = reinterpret_cast<u8*>(addr + static_tls_size - module->tls.offset);
const u8* src = reinterpret_cast<const u8*>(module->tls.image_virtual_addr);
std::memcpy(dest, src, module->tls.init_image_size);
tcb->tcb_dtv[module->tls.modid + 1].pointer = dest;
void Linker::FreeTlsForNonPrimaryThread(void* pointer) {
if (heap_api) {
Core::ExecuteGuest(heap_api->heap_free, pointer);
} else {
std::free(pointer);
}
// Set pointer to FS base
SetTcbBase(tcb);
}
void Linker::DebugDump() {
@ -425,17 +354,18 @@ void Linker::DebugDump() {
const std::filesystem::path debug(log_dir / "debugdump");
std::filesystem::create_directory(debug);
for (const auto& m : m_modules) {
// TODO make a folder with game id for being more unique?
const std::filesystem::path filepath(debug / m.get()->file.stem());
Module* module = m.get();
auto& elf = module->elf;
const std::filesystem::path filepath(debug / module->file.stem());
std::filesystem::create_directory(filepath);
m.get()->import_sym.DebugDump(filepath / "imports.txt");
m.get()->export_sym.DebugDump(filepath / "exports.txt");
if (m.get()->elf.IsSelfFile()) {
m.get()->elf.SelfHeaderDebugDump(filepath / "selfHeader.txt");
m.get()->elf.SelfSegHeaderDebugDump(filepath / "selfSegHeaders.txt");
module->import_sym.DebugDump(filepath / "imports.txt");
module->export_sym.DebugDump(filepath / "exports.txt");
if (elf.IsSelfFile()) {
elf.SelfHeaderDebugDump(filepath / "selfHeader.txt");
elf.SelfSegHeaderDebugDump(filepath / "selfSegHeaders.txt");
}
m.get()->elf.ElfHeaderDebugDump(filepath / "elfHeader.txt");
m.get()->elf.PHeaderDebugDump(filepath / "elfPHeaders.txt");
elf.ElfHeaderDebugDump(filepath / "elfHeader.txt");
elf.PHeaderDebugDump(filepath / "elfPHeaders.txt");
}
}

View file

@ -6,6 +6,7 @@
#include <algorithm>
#include <mutex>
#include <vector>
#include "core/libraries/kernel/threads.h"
#include "core/module.h"
namespace Core {
@ -40,10 +41,15 @@ struct OrbisProcParam {
u64 unknown1;
};
using ExitFunc = PS4_SYSV_ABI void (*)();
class Linker;
struct EntryParams {
int argc;
u32 padding;
const char* argv[3];
VAddr entry_addr;
};
struct HeapAPI {
@ -79,6 +85,18 @@ public:
return m_modules.at(index).get();
}
u32 MaxTlsIndex() const {
return max_tls_index;
}
u32 GenerationCounter() const {
return dtv_generation_counter;
}
size_t StaticTlsSize() const noexcept {
return static_tls_size;
}
void RelocateAnyImports(Module* m) {
Relocate(m);
for (auto& module : m_modules) {
@ -89,6 +107,14 @@ public:
}
}
void LoadSharedLibraries() {
for (auto& module : m_modules) {
if (module->IsSharedLib()) {
module->Start(0, nullptr, nullptr);
}
}
}
void SetHeapAPI(void* func[]) {
heap_api = reinterpret_cast<AppHeapAPI>(func);
}
@ -98,6 +124,8 @@ public:
}
void* TlsGetAddr(u64 module_index, u64 offset);
void* AllocateTlsForThread(bool is_primary);
void FreeTlsForNonPrimaryThread(void* pointer);
s32 LoadModule(const std::filesystem::path& elf_name, bool is_dynamic = false);
Module* FindByAddress(VAddr address);
@ -108,26 +136,11 @@ public:
void Execute();
void DebugDump();
template <class ReturnType, class... FuncArgs, class... CallArgs>
ReturnType ExecuteGuest(PS4_SYSV_ABI ReturnType (*func)(FuncArgs...),
CallArgs&&... args) const {
// Make sure TLS is initialized for the thread before entering guest.
EnsureThreadInitialized();
return ExecuteGuestWithoutTls(func, args...);
}
private:
const Module* FindExportedModule(const ModuleInfo& m, const LibraryInfo& l);
void EnsureThreadInitialized(bool is_primary = false) const;
void InitTlsForThread(bool is_primary) const;
template <class ReturnType, class... FuncArgs, class... CallArgs>
ReturnType ExecuteGuestWithoutTls(PS4_SYSV_ABI ReturnType (*func)(FuncArgs...),
CallArgs&&... args) const {
return func(std::forward<CallArgs>(args)...);
}
MemoryManager* memory;
Libraries::Kernel::Thread main_thread;
std::mutex mutex;
u32 dtv_generation_counter{1};
size_t static_tls_size{};

View file

@ -6,7 +6,7 @@
#include "common/config.h"
#include "common/debug.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/kernel/memory_management.h"
#include "core/libraries/kernel/memory.h"
#include "core/memory.h"
#include "video_core/renderer_vulkan/vk_instance.h"
#include "video_core/renderer_vulkan/vk_rasterizer.h"

View file

@ -10,7 +10,7 @@
#include "common/singleton.h"
#include "common/types.h"
#include "core/address_space.h"
#include "core/libraries/kernel/memory_management.h"
#include "core/libraries/kernel/memory.h"
namespace Vulkan {
class Rasterizer;
@ -133,6 +133,10 @@ public:
rasterizer = rasterizer_;
}
AddressSpace& GetAddressSpace() {
return impl;
}
u64 GetTotalDirectSize() const {
return total_direct_size;
}
@ -149,6 +153,13 @@ public:
return impl.SystemReservedVirtualBase();
}
bool IsValidAddress(const void* addr) const noexcept {
const VAddr virtual_addr = reinterpret_cast<VAddr>(addr);
const auto end_it = std::prev(vma_map.end());
const VAddr end_addr = end_it->first + end_it->second.size;
return virtual_addr >= vma_map.begin()->first && virtual_addr < end_addr;
}
bool TryWriteBacking(void* address, const void* data, u32 num_bytes);
void SetupMemoryRegions(u64 flexible_size);

View file

@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cryptopp/sha.h>
#include "common/alignment.h"
#include "common/arch.h"
#include "common/assert.h"
@ -9,10 +11,10 @@
#include "common/string_util.h"
#include "core/aerolib/aerolib.h"
#include "core/cpu_patches.h"
#include "core/linker.h"
#include "core/loader/dwarf.h"
#include "core/memory.h"
#include "core/module.h"
#include "core/tls.h"
namespace Core {
@ -56,6 +58,30 @@ static std::string EncodeId(u64 nVal) {
return enc;
}
static std::string StringToNid(std::string_view symbol) {
static constexpr std::array<u8, 16> Salt = {0x51, 0x8D, 0x64, 0xA6, 0x35, 0xDE, 0xD8, 0xC1,
0xE6, 0xB0, 0x39, 0xB1, 0xC3, 0xE5, 0x52, 0x30};
std::vector<u8> input(symbol.size() + Salt.size());
std::memcpy(input.data(), symbol.data(), symbol.size());
std::memcpy(input.data() + symbol.size(), Salt.data(), Salt.size());
std::array<u8, CryptoPP::SHA1::DIGESTSIZE> hash;
CryptoPP::SHA1().CalculateDigest(hash.data(), input.data(), input.size());
u64 digest;
std::memcpy(&digest, hash.data(), sizeof(digest));
static constexpr std::string_view codes =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-";
std::string dst(11, '\0');
for (int i = 0; i < 10; i++) {
dst[i] = codes[(digest >> (58 - i * 6)) & 0x3f];
}
dst[10] = codes[(digest & 0xf) * 4];
return dst;
}
Module::Module(Core::MemoryManager* memory_, const std::filesystem::path& file_, u32& max_tls_index)
: memory{memory_}, file{file_}, name{file.stem().string()} {
elf.Open(file);
@ -70,9 +96,8 @@ Module::~Module() = default;
s32 Module::Start(size_t args, const void* argp, void* param) {
LOG_INFO(Core_Linker, "Module started : {}", name);
const auto* linker = Common::Singleton<Core::Linker>::Instance();
const VAddr addr = dynamic_info.init_virtual_addr + GetBaseAddress();
return linker->ExecuteGuest(reinterpret_cast<EntryFunc>(addr), args, argp, param);
return ExecuteGuest(reinterpret_cast<EntryFunc>(addr), args, argp, param);
}
void Module::LoadModuleToMemory(u32& max_tls_index) {
@ -167,9 +192,7 @@ void Module::LoadModuleToMemory(u32& max_tls_index) {
tls.align = elf_pheader[i].p_align;
tls.image_virtual_addr = elf_pheader[i].p_vaddr + base_virtual_addr;
tls.image_size = GetAlignedSize(elf_pheader[i]);
if (tls.image_size != 0) {
tls.modid = ++max_tls_index;
}
tls.modid = ++max_tls_index;
LOG_INFO(Core_Linker, "TLS virtual address = {:#x}", tls.image_virtual_addr);
LOG_INFO(Core_Linker, "TLS image size = {}", tls.image_size);
break;
@ -492,4 +515,15 @@ const LibraryInfo* Module::FindLibrary(std::string_view id) {
return nullptr;
}
void* Module::FindByName(std::string_view name) {
const auto nid_str = StringToNid(name);
const auto symbols = export_sym.GetSymbols();
const auto it = std::ranges::find_if(
symbols, [&](const Loader::SymbolRecord& record) { return record.name.contains(nid_str); });
if (it != symbols.end()) {
return reinterpret_cast<void*>(it->virtual_address);
}
return nullptr;
}
} // namespace Core

View file

@ -165,15 +165,6 @@ public:
return elf.IsSharedLib();
}
void* FindByName(std::string_view name) {
const auto symbols = export_sym.GetSymbols();
const auto it = std::ranges::find(symbols, name, &Loader::SymbolRecord::nid_name);
if (it != symbols.end()) {
return reinterpret_cast<void*>(it->virtual_address);
}
return nullptr;
}
template <typename T = VAddr>
T GetProcParam() const noexcept {
return reinterpret_cast<T>(proc_param_virtual_addr);
@ -217,6 +208,8 @@ public:
void LoadDynamicInfo();
void LoadSymbols();
void* FindByName(std::string_view name);
OrbisKernelModuleInfoEx GetModuleInfoEx() const;
const ModuleInfo* FindModule(std::string_view id);
const LibraryInfo* FindLibrary(std::string_view id);

46
src/core/thread.cpp Normal file
View file

@ -0,0 +1,46 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "thread.h"
#ifdef _WIN64
#include <windows.h>
#else
#include <pthread.h>
#endif
namespace Core {
Thread::Thread() : native_handle{0} {}
Thread::~Thread() {}
int Thread::Create(ThreadFunc func, void* arg) {
#ifdef _WIN64
native_handle = CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)func, arg, 0, nullptr);
return native_handle ? 0 : -1;
#else
pthread_t* pthr = reinterpret_cast<pthread_t*>(&native_handle);
pthread_attr_t pattr;
pthread_attr_init(&pattr);
return pthread_create(pthr, &pattr, (PthreadFunc)func, arg);
#endif
}
void Thread::Exit() {
if (!native_handle) {
return;
}
#ifdef _WIN64
CloseHandle(native_handle);
native_handle = nullptr;
// We call this assuming the thread has finished execution.
ExitThread(0);
#else
pthread_exit(nullptr);
#endif
}
} // namespace Core

33
src/core/thread.h Normal file
View file

@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/types.h"
namespace Core {
class Thread {
public:
using ThreadFunc = void (*)(void*);
using PthreadFunc = void* (*)(void*);
Thread();
~Thread();
int Create(ThreadFunc func, void* arg);
void Exit();
uintptr_t GetHandle() {
return reinterpret_cast<uintptr_t>(native_handle);
}
private:
#if _WIN64
void* native_handle;
#else
uintptr_t native_handle;
#endif
};
} // namespace Core

View file

@ -5,6 +5,8 @@
#include "common/arch.h"
#include "common/assert.h"
#include "common/types.h"
#include "core/cpu_patches.h"
#include "core/libraries/kernel/threads/pthread.h"
#include "core/tls.h"
#ifdef _WIN32
@ -52,8 +54,13 @@ Tcb* GetTcbBase() {
// Reserve space in the 32-bit address range for allocating TCB pages.
asm(".zerofill TCB_SPACE,TCB_SPACE,__guest_system,0x3FC000");
static constexpr u64 ldt_region_base = 0x4000;
static constexpr u64 ldt_region_size = 0x3FC000;
struct LdtPage {
void* tcb;
u16 index;
};
static constexpr uintptr_t ldt_region_base = 0x4000;
static constexpr size_t ldt_region_size = 0x3FC000;
static constexpr u16 ldt_block_size = 0x1000;
static constexpr u16 ldt_index_base = 8;
static constexpr u16 ldt_index_total = (ldt_region_size - ldt_region_base) / ldt_block_size;
@ -61,11 +68,13 @@ static constexpr u16 ldt_index_total = (ldt_region_size - ldt_region_base) / ldt
static boost::icl::interval_set<u16> free_ldts{};
static std::mutex free_ldts_lock;
static std::once_flag ldt_region_init_flag;
static pthread_key_t ldt_page_slot = 0;
static u16 GetLdtIndex() {
sel_t selector;
asm volatile("mov %%fs, %0" : "=r"(selector));
return selector.index;
static void FreeLdtPage(void* raw) {
const auto* ldt_page = static_cast<LdtPage*>(raw);
std::unique_lock lock{free_ldts_lock};
free_ldts += ldt_page->index;
}
static void InitLdtRegion() {
@ -76,11 +85,20 @@ static void InitLdtRegion() {
free_ldts +=
boost::icl::interval<u16>::right_open(ldt_index_base, ldt_index_base + ldt_index_total);
ASSERT_MSG(pthread_key_create(&ldt_page_slot, FreeLdtPage) == 0,
"Failed to create thread LDT page key: {}", errno);
}
static void** SetupThreadLdt() {
void SetTcbBase(void* image_address) {
std::call_once(ldt_region_init_flag, InitLdtRegion);
auto* ldt_page = static_cast<LdtPage*>(pthread_getspecific(ldt_page_slot));
if (ldt_page != nullptr) {
// Update TCB pointer in existing page.
ldt_page->tcb = image_address;
return;
}
// Allocate a new LDT index for the current thread.
u16 ldt_index;
{
@ -89,10 +107,12 @@ static void** SetupThreadLdt() {
ldt_index = first(*free_ldts.begin());
free_ldts -= ldt_index;
}
const u64 addr = ldt_region_base + (ldt_index - ldt_index_base) * ldt_block_size;
const uintptr_t addr = ldt_region_base + (ldt_index - ldt_index_base) * ldt_block_size;
// Create an LDT entry for the TCB.
const ldt_entry ldt{.data{
ldt_entry ldt{};
ldt.data = {
.base00 = static_cast<u16>(addr),
.base16 = static_cast<u8>(addr >> 16),
.base24 = static_cast<u8>(addr >> 24),
@ -103,34 +123,27 @@ static void** SetupThreadLdt() {
.present = 1, // Segment present
.stksz = DESC_DATA_32B,
.granular = DESC_GRAN_BYTE,
}};
};
int ret = i386_set_ldt(ldt_index, &ldt, 1);
ASSERT_MSG(ret == ldt_index,
"Failed to set LDT for TLS area: expected {}, but syscall returned {}", ldt_index,
ret);
"Failed to set LDT {} at {:#x} for TLS area: syscall returned {}, errno {}",
ldt_index, addr, ret, errno);
// Set the FS segment to the created LDT.
const sel_t sel{
const sel_t new_selector{
.rpl = USER_PRIV,
.ti = SEL_LDT,
.index = ldt_index,
};
asm volatile("mov %0, %%fs" ::"r"(sel));
asm volatile("mov %0, %%fs" ::"r"(new_selector));
return reinterpret_cast<void**>(addr);
}
// Store the TCB base pointer and index in the created LDT area.
ldt_page = reinterpret_cast<LdtPage*>(addr);
ldt_page->tcb = image_address;
ldt_page->index = ldt_index;
static void FreeThreadLdt() {
std::unique_lock lock{free_ldts_lock};
free_ldts += GetLdtIndex();
}
void SetTcbBase(void* image_address) {
if (image_address != nullptr) {
*SetupThreadLdt() = image_address;
} else {
FreeThreadLdt();
}
ASSERT_MSG(pthread_setspecific(ldt_page_slot, ldt_page) == 0,
"Failed to store thread LDT page pointer: {}", errno);
}
Tcb* GetTcbBase() {
@ -181,4 +194,15 @@ Tcb* GetTcbBase() {
#endif
thread_local std::once_flag init_tls_flag;
void EnsureThreadInitialized() {
std::call_once(init_tls_flag, [] {
#ifdef ARCH_X86_64
InitializeThreadPatchStack();
#endif
SetTcbBase(Libraries::Kernel::g_curthread->tcb);
});
}
} // namespace Core

View file

@ -12,7 +12,7 @@ class CodeGenerator;
namespace Core {
union DtvEntry {
size_t counter;
std::size_t counter;
u8* pointer;
};
@ -33,4 +33,13 @@ void SetTcbBase(void* image_address);
/// Retrieves Tcb structure for the calling thread.
Tcb* GetTcbBase();
/// Makes sure TLS is initialized for the thread before entering guest.
void EnsureThreadInitialized();
template <class ReturnType, class... FuncArgs, class... CallArgs>
ReturnType ExecuteGuest(PS4_SYSV_ABI ReturnType (*func)(FuncArgs...), CallArgs&&... args) {
EnsureThreadInitialized();
return func(std::forward<CallArgs>(args)...);
}
} // namespace Core

View file

@ -29,7 +29,6 @@
#include "core/file_sys/fs.h"
#include "core/libraries/disc_map/disc_map.h"
#include "core/libraries/fiber/fiber.h"
#include "core/libraries/kernel/thread_management.h"
#include "core/libraries/libc_internal/libc_internal.h"
#include "core/libraries/libs.h"
#include "core/libraries/ngs2/ngs2.h"
@ -222,7 +221,6 @@ void Emulator::Run(const std::filesystem::path& file) {
VideoCore::SetOutputDir(mount_captures_dir, id);
// Initialize kernel and library facilities.
Libraries::Kernel::init_pthreads();
Libraries::InitHLELibs(&linker->GetHLESymbols());
// Load the module with the linker
@ -257,9 +255,7 @@ void Emulator::Run(const std::filesystem::path& file) {
}
#endif
// start execution
std::jthread mainthread =
std::jthread([this](std::stop_token stop_token) { linker->Execute(); });
linker->Execute();
window->initTimers();
while (window->isOpen()) {

View file

@ -4,7 +4,7 @@
#include "controller.h"
#include "common/assert.h"
#include "core/libraries/kernel/time_management.h"
#include "core/libraries/kernel/time.h"
#include "core/libraries/pad/pad.h"
#include <SDL3/SDL.h>

View file

@ -68,10 +68,9 @@ void Translator::V_READFIRSTLANE_B32(const GcnInst& inst) {
}
void Translator::V_READLANE_B32(const GcnInst& inst) {
const IR::ScalarReg dst{inst.dst[0].code};
const IR::U32 value{GetSrc(inst.src[0])};
const IR::U32 lane{GetSrc(inst.src[1])};
ir.SetScalarReg(dst, ir.ReadLane(value, lane));
SetDst(inst.dst[0], ir.ReadLane(value, lane));
}
void Translator::V_WRITELANE_B32(const GcnInst& inst) {
@ -155,7 +154,7 @@ void Translator::DS_SWIZZLE_B32(const GcnInst& inst) {
const u8 offset0 = inst.control.ds.offset0;
const u8 offset1 = inst.control.ds.offset1;
const IR::U32 src{GetSrc(inst.src[1])};
ASSERT(offset1 & 0x80);
// ASSERT(offset1 & 0x80);
const IR::U32 lane_id = ir.LaneId();
const IR::U32 id_in_group = ir.BitwiseAnd(lane_id, ir.Imm32(0b11));
const IR::U32 base = ir.ShiftLeftLogical(id_in_group, ir.Imm32(1));

Some files were not shown because too many files have changed in this diff Show more