diff --git a/CMakeLists.txt b/CMakeLists.txt index 35fc35f2..e135794e 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/src/common/debug.h b/src/common/debug.h index 596ad7b8..091c6191 100644 --- a/src/common/debug.h +++ b/src/common/debug.h @@ -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); diff --git a/src/common/slab_heap.h b/src/common/slab_heap.h new file mode 100644 index 00000000..7648ebea --- /dev/null +++ b/src/common/slab_heap.h @@ -0,0 +1,163 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#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(obj); + node->next = m_head; + m_head = node; + + m_lock.unlock(); + } + +private: + std::atomic 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(memory); + m_end = m_start + num_obj * obj_size; + m_peak = m_start; + + // Free the objects. + u8* cur = reinterpret_cast(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(obj)); + ASSERT(contained); + SlabHeapImpl::Free(obj); + } + + size_t GetObjectIndex(const void* obj) const { + return (reinterpret_cast(obj) - m_start) / this->GetObjectSize(); + } + + size_t GetPeakIndex() const { + return this->GetObjectIndex(reinterpret_cast(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 +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(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 diff --git a/src/common/slot_vector.h b/src/common/slot_vector.h index 36e64797..d4ac5136 100644 --- a/src/common/slot_vector.h +++ b/src/common/slot_vector.h @@ -3,10 +3,7 @@ #pragma once -#include -#include #include -#include #include #include #include "common/assert.h" diff --git a/src/common/spin_lock.cpp b/src/common/spin_lock.cpp new file mode 100755 index 00000000..9d4cfe36 --- /dev/null +++ b/src/common/spin_lock.cpp @@ -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 +#if _M_AMD64 +#define __x86_64__ 1 +#endif +#if _M_ARM64 +#define __aarch64__ 1 +#endif +#else +#if __x86_64__ +#include +#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 diff --git a/src/common/spin_lock.h b/src/common/spin_lock.h new file mode 100755 index 00000000..3229a8c6 --- /dev/null +++ b/src/common/spin_lock.h @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +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 diff --git a/src/core/address_space.cpp b/src/core/address_space.cpp index 8ba99e32..24f5e9f8 100644 --- a/src/core/address_space.cpp +++ b/src/core/address_space.cpp @@ -1,13 +1,14 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include #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(SYSTEM_RESERVED_MIN); @@ -95,7 +103,8 @@ struct AddressSpace::Impl { const uintptr_t system_managed_addr = reinterpret_cast(system_managed_base); const uintptr_t system_reserved_addr = reinterpret_cast(system_reserved_base); const uintptr_t user_addr = reinterpret_cast(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(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(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(fd) : backing_handle; if (fd && prot == PAGE_READONLY) { DWORD resultvar; - ptr = VirtualAlloc2(process, reinterpret_cast(virtual_addr), aligned_size, + ptr = VirtualAlloc2(process, reinterpret_cast(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(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(virtual_addr), aligned_size, + VirtualAlloc2(process, reinterpret_cast(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 ®ion; + } + + 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 ®ion; + } + + 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(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(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(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 placeholders; + std::map regions; }; #else diff --git a/src/core/debug_state.cpp b/src/core/debug_state.cpp index adcb0cad..1dc4297c 100644 --- a/src/core/debug_state.cpp +++ b/src/core/debug_state.cpp @@ -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" diff --git a/src/core/file_sys/file.cpp b/src/core/file_sys/file.cpp new file mode 100644 index 00000000..be6bc76b --- /dev/null +++ b/src/core/file_sys/file.cpp @@ -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 +#include +#include +#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 \ No newline at end of file diff --git a/src/core/libraries/audio3d/audio3d.cpp b/src/core/libraries/audio3d/audio3d.cpp index 63815a06..2978445e 100644 --- a/src/core/libraries/audio3d/audio3d.cpp +++ b/src/core/libraries/audio3d/audio3d.cpp @@ -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; diff --git a/src/core/libraries/audio3d/audio3d_impl.cpp b/src/core/libraries/audio3d/audio3d_impl.cpp index c267c096..3069e880 100644 --- a/src/core/libraries/audio3d/audio3d_impl.cpp +++ b/src/core/libraries/audio3d/audio3d_impl.cpp @@ -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; diff --git a/src/core/libraries/avplayer/avplayer.cpp b/src/core/libraries/avplayer/avplayer.cpp index 60d68c4f..1257473e 100644 --- a/src/core/libraries/avplayer/avplayer.cpp +++ b/src/core/libraries/avplayer/avplayer.cpp @@ -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); diff --git a/src/core/libraries/avplayer/avplayer.h b/src/core/libraries/avplayer/avplayer.h index 98e93207..9f100bef 100644 --- a/src/core/libraries/avplayer/avplayer.h +++ b/src/core/libraries/avplayer/avplayer.h @@ -5,8 +5,8 @@ #include "common/types.h" -#include // va_list -#include // size_t +#include // va_list +#include // size_t namespace Core::Loader { class SymbolsResolver; diff --git a/src/core/libraries/avplayer/avplayer_common.cpp b/src/core/libraries/avplayer/avplayer_common.cpp index 306603e2..805920a3 100644 --- a/src/core/libraries/avplayer/avplayer_common.cpp +++ b/src/core/libraries/avplayer/avplayer_common.cpp @@ -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 // std::equal +#include // std::tolower -#include // std::equal -#include // std::tolower -#include // 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(a)) == - std::tolower(static_cast(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) { diff --git a/src/core/libraries/avplayer/avplayer_common.h b/src/core/libraries/avplayer/avplayer_common.h index a53696ec..dc3cd787 100644 --- a/src/core/libraries/avplayer/avplayer_common.h +++ b/src/core/libraries/avplayer/avplayer_common.h @@ -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 #include +#include #include #include +#include "core/libraries/avplayer/avplayer.h" + #define AVPLAYER_IS_ERROR(x) ((x) < 0) namespace Libraries::AvPlayer { diff --git a/src/core/libraries/avplayer/avplayer_file_streamer.cpp b/src/core/libraries/avplayer/avplayer_file_streamer.cpp index c7bd5b5d..3323ee9b 100644 --- a/src/core/libraries/avplayer/avplayer_file_streamer.cpp +++ b/src/core/libraries/avplayer/avplayer_file_streamer.cpp @@ -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 // std::max, std::min #include +#include "core/libraries/avplayer/avplayer_file_streamer.h" extern "C" { #include #include } -#include // std::max, std::min - -#define AVPLAYER_AVIO_BUFFER_SIZE 4096 +constexpr u32 AVPLAYER_AVIO_BUFFER_SIZE = 4096; namespace Libraries::AvPlayer { diff --git a/src/core/libraries/avplayer/avplayer_file_streamer.h b/src/core/libraries/avplayer/avplayer_file_streamer.h index 034e40dd..bc096bcc 100644 --- a/src/core/libraries/avplayer/avplayer_file_streamer.h +++ b/src/core/libraries/avplayer/avplayer_file_streamer.h @@ -3,11 +3,9 @@ #pragma once -#include "avplayer.h" -#include "avplayer_data_streamer.h" - #include -#include +#include "core/libraries/avplayer/avplayer.h" +#include "core/libraries/avplayer/avplayer_data_streamer.h" struct AVIOContext; diff --git a/src/core/libraries/avplayer/avplayer_impl.cpp b/src/core/libraries/avplayer/avplayer_impl.cpp index 1c414c96..0f39acfd 100644 --- a/src/core/libraries/avplayer/avplayer_impl.cpp +++ b/src/core/libraries/avplayer/avplayer_impl.cpp @@ -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(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::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(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::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(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::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(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::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::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::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::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::Instance(); - return linker->ExecuteGuest(size, ptr); + return Core::ExecuteGuest(size, ptr); } SceAvPlayerInitData AvPlayer::StubInitData(const SceAvPlayerInitData& data) { diff --git a/src/core/libraries/avplayer/avplayer_impl.h b/src/core/libraries/avplayer/avplayer_impl.h index d7f28094..984d8149 100644 --- a/src/core/libraries/avplayer/avplayer_impl.h +++ b/src/core/libraries/avplayer/avplayer_impl.h @@ -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 @@ -17,7 +14,6 @@ extern "C" { } #include -#include namespace Libraries::AvPlayer { diff --git a/src/core/libraries/avplayer/avplayer_source.cpp b/src/core/libraries/avplayer/avplayer_source.cpp index 19925ba0..8a65377c 100644 --- a/src/core/libraries/avplayer/avplayer_source.cpp +++ b/src/core/libraries/avplayer/avplayer_source.cpp @@ -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 @@ -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 diff --git a/src/core/libraries/avplayer/avplayer_source.h b/src/core/libraries/avplayer/avplayer_source.h index 505d7446..7e199c45 100644 --- a/src/core/libraries/avplayer/avplayer_source.h +++ b/src/core/libraries/avplayer/avplayer_source.h @@ -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 #include #include #include #include -#include +#include + +#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}; diff --git a/src/core/libraries/avplayer/avplayer_state.cpp b/src/core/libraries/avplayer/avplayer_state.cpp index e6610067..3d9840a1 100644 --- a/src/core/libraries/avplayer/avplayer_state.cpp +++ b/src/core/libraries/avplayer/avplayer_state.cpp @@ -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 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(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::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 diff --git a/src/core/libraries/avplayer/avplayer_state.h b/src/core/libraries/avplayer/avplayer_state.h index d106127e..8a0d7f4f 100644 --- a/src/core/libraries/avplayer/avplayer_state.h +++ b/src/core/libraries/avplayer/avplayer_state.h @@ -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 #include #include +#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 m_event_queue{}; }; diff --git a/src/core/libraries/fiber/fiber.cpp b/src/core/libraries/fiber/fiber.cpp index a0bfd685..92bf894b 100644 --- a/src/core/libraries/fiber/fiber.cpp +++ b/src/core/libraries/fiber/fiber.cpp @@ -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 @@ -31,9 +30,7 @@ void FiberEntry(void* param) { argRun = *fiber->pArgRun; } - const auto* linker = Common::Singleton::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 \ No newline at end of file +} // namespace Libraries::Fiber diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index 946622ce..9aede330 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -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() { diff --git a/src/core/libraries/gnmdriver/gnmdriver.h b/src/core/libraries/gnmdriver/gnmdriver.h index 115268ea..5307b3ba 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.h +++ b/src/core/libraries/gnmdriver/gnmdriver.h @@ -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(); diff --git a/src/core/libraries/ime/ime.cpp b/src/core/libraries/ime/ime.cpp index 0310c515..efb988c7 100644 --- a/src/core/libraries/ime/ime.cpp +++ b/src/core/libraries/ime/ime.cpp @@ -70,21 +70,19 @@ public: } void Execute(OrbisImeEventHandler handler, OrbisImeEvent* event, bool use_param_handler) { - const auto* linker = Common::Singleton::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 \ No newline at end of file +} // namespace Libraries::Ime diff --git a/src/core/libraries/ime/ime_dialog_ui.cpp b/src/core/libraries/ime/ime_dialog_ui.cpp index bba45d0f..de64de5f 100644 --- a/src/core/libraries/ime/ime_dialog_ui.cpp +++ b/src/core/libraries/ime/ime_dialog_ui.cpp @@ -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::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::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(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(orbis_text), orbis_text_len, utf8_text, nullptr); @@ -387,4 +382,4 @@ int ImeDialogUi::InputTextCallback(ImGuiInputTextCallbackData* data) { return 0; } -} // namespace Libraries::ImeDialog \ No newline at end of file +} // namespace Libraries::ImeDialog diff --git a/src/core/libraries/kernel/cpu_management.cpp b/src/core/libraries/kernel/cpu_management.cpp deleted file mode 100644 index 3bf609df..00000000 --- a/src/core/libraries/kernel/cpu_management.cpp +++ /dev/null @@ -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 diff --git a/src/core/libraries/kernel/event_queues.cpp b/src/core/libraries/kernel/equeue.cpp similarity index 56% rename from src/core/libraries/kernel/event_queues.cpp rename to src/core/libraries/kernel/equeue.cpp index 540c20c4..d4f7eaba 100644 --- a/src/core/libraries/kernel/event_queues.cpp +++ b/src/core/libraries/kernel/equeue.cpp @@ -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::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 diff --git a/src/core/libraries/kernel/event_queue.h b/src/core/libraries/kernel/equeue.h similarity index 89% rename from src/core/libraries/kernel/event_queue.h rename to src/core/libraries/kernel/equeue.h index 30fdb41e..5a13bdec 100644 --- a/src/core/libraries/kernel/event_queue.h +++ b/src/core/libraries/kernel/equeue.h @@ -7,11 +7,14 @@ #include #include #include - #include #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 diff --git a/src/core/libraries/kernel/event_flag/event_flag.h b/src/core/libraries/kernel/event_flag/event_flag.h deleted file mode 100644 index 2147e3f1..00000000 --- a/src/core/libraries/kernel/event_flag/event_flag.h +++ /dev/null @@ -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 \ No newline at end of file diff --git a/src/core/libraries/kernel/event_flag/event_flag_codes.h b/src/core/libraries/kernel/event_flag/event_flag_codes.h deleted file mode 100644 index 92b265c8..00000000 --- a/src/core/libraries/kernel/event_flag/event_flag_codes.h +++ /dev/null @@ -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; \ No newline at end of file diff --git a/src/core/libraries/kernel/event_flag/event_flag_obj.cpp b/src/core/libraries/kernel/event_flag/event_flag_obj.cpp deleted file mode 100644 index 6d6dcf7a..00000000 --- a/src/core/libraries/kernel/event_flag/event_flag_obj.cpp +++ /dev/null @@ -1,111 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include - -#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::system_clock::now() - start) - .count(); - if (result != nullptr) { - *result = m_bits; - } - - if (ptr_micros != nullptr) { - *ptr_micros = (elapsed >= micros ? 0 : micros - elapsed); - } - - if (m_status == Status::Canceled) { - return ORBIS_KERNEL_ERROR_ECANCELED; - } else if (m_status == Status::Deleted) { - return ORBIS_KERNEL_ERROR_EACCES; - } - - if (clear_mode == ClearMode::All) { - m_bits = 0; - } else if (clear_mode == ClearMode::Bits) { - m_bits &= ~bits; - } - - return ORBIS_OK; -} - -int EventFlagInternal::Poll(u64 bits, WaitMode wait_mode, ClearMode clear_mode, u64* result) { - u32 micros = 0; - auto ret = Wait(bits, wait_mode, clear_mode, result, µs); - if (ret == ORBIS_KERNEL_ERROR_ETIMEDOUT) { - // Poll returns EBUSY instead. - ret = ORBIS_KERNEL_ERROR_EBUSY; - } - return ret; -} - -void EventFlagInternal::Set(u64 bits) { - std::unique_lock lock{m_mutex}; - - while (m_status != Status::Set) { - m_mutex.unlock(); - std::this_thread::sleep_for(std::chrono::microseconds(10)); - m_mutex.lock(); - } - - m_bits |= bits; - - m_cond_var.notify_all(); -} - -void EventFlagInternal::Clear(u64 bits) { - std::unique_lock lock{m_mutex}; - while (m_status != Status::Set) { - m_mutex.unlock(); - std::this_thread::sleep_for(std::chrono::microseconds(10)); - m_mutex.lock(); - } - - m_bits &= bits; -} - -} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/event_flag/event_flag_obj.h b/src/core/libraries/kernel/event_flag/event_flag_obj.h deleted file mode 100644 index 0454b4d7..00000000 --- a/src/core/libraries/kernel/event_flag/event_flag_obj.h +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include - -#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 diff --git a/src/core/libraries/kernel/event_queue.cpp b/src/core/libraries/kernel/event_queue.cpp deleted file mode 100644 index 88918bf5..00000000 --- a/src/core/libraries/kernel/event_queue.cpp +++ /dev/null @@ -1,150 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include - -#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::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 diff --git a/src/core/libraries/kernel/event_queues.h b/src/core/libraries/kernel/event_queues.h deleted file mode 100644 index d400ff18..00000000 --- a/src/core/libraries/kernel/event_queues.h +++ /dev/null @@ -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 diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index e6b657c3..56286eb9 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -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 GetDirectoryEntries(const std::filesystem::path& path) { +auto GetDirectoryEntries(const std::filesystem::path& path) { std::vector 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); diff --git a/src/core/libraries/kernel/file_system.h b/src/core/libraries/kernel/file_system.h index 1174dd86..0bc473ec 100644 --- a/src/core/libraries/kernel/file_system.h +++ b/src/core/libraries/kernel/file_system.h @@ -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 diff --git a/src/core/libraries/kernel/kernel.cpp b/src/core/libraries/kernel/kernel.cpp new file mode 100644 index 00000000..512e77bc --- /dev/null +++ b/src/core/libraries/kernel/kernel.cpp @@ -0,0 +1,250 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +#include + +#include "common/assert.h" +#include "common/debug.h" +#include "common/logging/log.h" +#include "common/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 +#include +#include +#else +#ifdef __APPLE__ +#include +#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 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( + strlen(std::fgets(static_cast(buf), static_cast(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 diff --git a/src/core/libraries/kernel/kernel.h b/src/core/libraries/kernel/kernel.h new file mode 100644 index 00000000..f6865d22 --- /dev/null +++ b/src/core/libraries/kernel/kernel.h @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#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 +struct StringLiteral { + constexpr StringLiteral(const char (&str)[N]) { + std::copy_n(str, N, value); + } + + char value[N]; +}; + +template +struct WrapperImpl; + +template +struct WrapperImpl { + 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 +constexpr auto OrbisWrapper = WrapperImpl::wrap; + +#define ORBIS(func) WrapperImpl<#func, decltype(&func), func>::wrap + +int* PS4_SYSV_ABI __Error(); + +void RegisterKernel(Core::Loader::SymbolsResolver* sym); + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/libkernel.cpp b/src/core/libraries/kernel/libkernel.cpp deleted file mode 100644 index b3eb81ec..00000000 --- a/src/core/libraries/kernel/libkernel.cpp +++ /dev/null @@ -1,509 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include - -#include - -#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 -#include -#include -#else -#include -#ifdef __APPLE__ -#include -#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 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::Instance(); - return reinterpret_cast(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(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::Instance(); - auto* memory = Core::Memory::Instance(); - const auto mem_prot = static_cast(prot); - const auto mem_flags = static_cast(flags); - if (fd == -1) { - return memory->MapMemory(res, std::bit_cast(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(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( - strlen(std::fgets(static_cast(buf), static_cast(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::Instance(); - const auto path = mnt->GetHostPath(moduleFileName); - - // Load PRX module and relocate any modules that import it. - auto* linker = Common::Singleton::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(); - 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::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 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::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::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 diff --git a/src/core/libraries/kernel/libkernel.h b/src/core/libraries/kernel/libkernel.h deleted file mode 100644 index 73705cdc..00000000 --- a/src/core/libraries/kernel/libkernel.h +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include - -#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 diff --git a/src/core/libraries/kernel/memory_management.cpp b/src/core/libraries/kernel/memory.cpp similarity index 75% rename from src/core/libraries/kernel/memory_management.cpp rename to src/core/libraries/kernel/memory.cpp index 5331f47f..13b3a9f4 100644 --- a/src/core/libraries/kernel/memory_management.cpp +++ b/src/core/libraries/kernel/memory.cpp @@ -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(*addr); const auto mem_prot = static_cast(prot); const auto map_flags = static_cast(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(*addr_in_out); const auto mem_prot = static_cast(prot); const auto map_flags = static_cast(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::Instance(); + auto* memory = Core::Memory::Instance(); + const auto mem_prot = static_cast(prot); + const auto mem_flags = static_cast(flags); + if (fd == -1) { + return memory->MapMemory(res, std::bit_cast(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(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(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 diff --git a/src/core/libraries/kernel/memory_management.h b/src/core/libraries/kernel/memory.h similarity index 96% rename from src/core/libraries/kernel/memory_management.h rename to src/core/libraries/kernel/memory.h index 6e90204c..2d19ceb4 100644 --- a/src/core/libraries/kernel/memory_management.h +++ b/src/core/libraries/kernel/memory.h @@ -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 diff --git a/src/core/libraries/kernel/process.cpp b/src/core/libraries/kernel/process.cpp new file mode 100644 index 00000000..fc1d6e13 --- /dev/null +++ b/src/core/libraries/kernel/process.cpp @@ -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::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::Instance(); + const auto path = mnt->GetHostPath(moduleFileName); + + // Load PRX module and relocate any modules that import it. + auto* linker = Common::Singleton::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(); + 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::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 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::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::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 diff --git a/src/core/libraries/kernel/cpu_management.h b/src/core/libraries/kernel/process.h similarity index 60% rename from src/core/libraries/kernel/cpu_management.h rename to src/core/libraries/kernel/process.h index 814ea51d..0340a979 100644 --- a/src/core/libraries/kernel/cpu_management.h +++ b/src/core/libraries/kernel/process.h @@ -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 diff --git a/src/core/libraries/kernel/thread_management.cpp b/src/core/libraries/kernel/thread_management.cpp deleted file mode 100644 index 85f61809..00000000 --- a/src/core/libraries/kernel/thread_management.cpp +++ /dev/null @@ -1,1716 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include - -#include "common/alignment.h" -#include "common/assert.h" -#include "common/error.h" -#include "common/logging/log.h" -#include "common/singleton.h" -#include "common/thread.h" -#include "core/debug_state.h" -#include "core/libraries/error_codes.h" -#include "core/libraries/kernel/libkernel.h" -#include "core/libraries/kernel/thread_management.h" -#include "core/libraries/kernel/threads/threads.h" -#include "core/libraries/libs.h" -#include "core/linker.h" -#include "core/tls.h" -#ifdef _WIN64 -#include -#else -#include -#endif - -namespace Libraries::Kernel { - -thread_local ScePthread g_pthread_self{}; -PThreadCxt* g_pthread_cxt = nullptr; - -void init_pthreads() { - g_pthread_cxt = new PThreadCxt{}; - // default mutex init - ScePthreadMutexattr default_mutexattr = nullptr; - scePthreadMutexattrInit(&default_mutexattr); - g_pthread_cxt->setDefaultMutexattr(default_mutexattr); - ScePthreadMutexattr adaptive_mutexattr = nullptr; - scePthreadMutexattrInit(&adaptive_mutexattr); - scePthreadMutexattrSettype(&adaptive_mutexattr, ORBIS_PTHREAD_MUTEX_ADAPTIVE); - g_pthread_cxt->setAdaptiveMutexattr(adaptive_mutexattr); - // default cond init - ScePthreadCondattr default_condattr = nullptr; - scePthreadCondattrInit(&default_condattr); - g_pthread_cxt->setDefaultCondattr(default_condattr); - // default attr init - ScePthreadAttr default_attr = nullptr; - scePthreadAttrInit(&default_attr); - g_pthread_cxt->SetDefaultAttr(default_attr); - // default rw init - OrbisPthreadRwlockattr default_rwattr = nullptr; - scePthreadRwlockattrInit(&default_rwattr); - g_pthread_cxt->setDefaultRwattr(default_rwattr); - - g_pthread_cxt->SetPthreadPool(new PThreadPool); -} - -void pthreadInitSelfMainThread() { - const char* name = "Main_Thread"; - auto* pthread_pool = g_pthread_cxt->GetPthreadPool(); - g_pthread_self = pthread_pool->Create(name); - scePthreadAttrInit(&g_pthread_self->attr); - g_pthread_self->pth = pthread_self(); - g_pthread_self->name = name; -} - -int PS4_SYSV_ABI scePthreadAttrInit(ScePthreadAttr* attr) { - *attr = new PthreadAttrInternal{}; - - int result = pthread_attr_init(&(*attr)->pth_attr); - - (*attr)->affinity = 0x7f; - (*attr)->guard_size = 0x1000; - - SceKernelSchedParam param{}; - param.sched_priority = 700; - - result = (result == 0 ? scePthreadAttrSetinheritsched(attr, 4) : result); - result = (result == 0 ? scePthreadAttrSetschedparam(attr, ¶m) : result); - result = (result == 0 ? scePthreadAttrSetschedpolicy(attr, SCHED_OTHER) : result); - result = (result == 0 ? scePthreadAttrSetdetachstate(attr, PTHREAD_CREATE_JOINABLE) : result); - - switch (result) { - case 0: - return SCE_OK; - case ENOMEM: - return SCE_KERNEL_ERROR_ENOMEM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadAttrDestroy(ScePthreadAttr* attr) { - - int result = pthread_attr_destroy(&(*attr)->pth_attr); - - delete *attr; - *attr = nullptr; - - if (result == 0) { - return SCE_OK; - } - return SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrSetguardsize(ScePthreadAttr* attr, size_t guard_size) { - if (attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - (*attr)->guard_size = guard_size; - - return SCE_OK; -} - -int PS4_SYSV_ABI scePthreadAttrGetguardsize(const ScePthreadAttr* attr, size_t* guard_size) { - if (guard_size == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - *guard_size = (*attr)->guard_size; - - return SCE_OK; -} - -int PS4_SYSV_ABI scePthreadAttrGetinheritsched(const ScePthreadAttr* attr, int* inherit_sched) { - - if (inherit_sched == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_attr_getinheritsched(&(*attr)->pth_attr, inherit_sched); - - switch (*inherit_sched) { - case PTHREAD_EXPLICIT_SCHED: - *inherit_sched = 0; - break; - case PTHREAD_INHERIT_SCHED: - *inherit_sched = 4; - break; - default: - UNREACHABLE(); - } - - return (result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL); -} - -int PS4_SYSV_ABI scePthreadAttrGetdetachstate(const ScePthreadAttr* attr, int* state) { - if (state == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - // int result = pthread_attr_getdetachstate(&(*attr)->pth_attr, state); - int result = 0; - *state = ((*attr)->detached ? PTHREAD_CREATE_DETACHED : PTHREAD_CREATE_JOINABLE); - - switch (*state) { - case PTHREAD_CREATE_JOINABLE: - *state = 0; - break; - case PTHREAD_CREATE_DETACHED: - *state = 1; - break; - default: - UNREACHABLE(); - } - - return (result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL); -} - -int PS4_SYSV_ABI scePthreadAttrSetdetachstate(ScePthreadAttr* attr, int detachstate) { - if (attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int pstate = PTHREAD_CREATE_JOINABLE; - switch (detachstate) { - case 0: - pstate = PTHREAD_CREATE_JOINABLE; - break; - case 1: - pstate = PTHREAD_CREATE_DETACHED; - break; - default: - UNREACHABLE_MSG("Invalid detachstate: {}", detachstate); - } - - // int result = pthread_attr_setdetachstate(&(*attr)->pth_attr, pstate); - int result = 0; - (*attr)->detached = (pstate == PTHREAD_CREATE_DETACHED); - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrSetinheritsched(ScePthreadAttr* attr, int inheritSched) { - if (attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int pinherit_sched = PTHREAD_INHERIT_SCHED; - switch (inheritSched) { - case 0: - pinherit_sched = PTHREAD_EXPLICIT_SCHED; - break; - case 4: - pinherit_sched = PTHREAD_INHERIT_SCHED; - break; - default: - UNREACHABLE_MSG("Invalid inheritSched: {}", inheritSched); - } - - int result = pthread_attr_setinheritsched(&(*attr)->pth_attr, pinherit_sched); - - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrGetschedparam(const ScePthreadAttr* attr, - SceKernelSchedParam* param) { - - if (param == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_attr_getschedparam(&(*attr)->pth_attr, param); - - if (param->sched_priority <= -2) { - param->sched_priority = 767; - } else if (param->sched_priority >= +2) { - param->sched_priority = 256; - } else { - param->sched_priority = 700; - } - - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrSetschedparam(ScePthreadAttr* attr, - const SceKernelSchedParam* param) { - if (param == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - SceKernelSchedParam pparam{}; - if (param->sched_priority <= 478) { - pparam.sched_priority = +2; - } else if (param->sched_priority >= 733) { - pparam.sched_priority = -2; - } else { - pparam.sched_priority = 0; - } - - // We always use SCHED_OTHER for now, so don't call this for now. - // int result = pthread_attr_setschedparam(&(*attr)->pth_attr, &pparam); - int result = 0; - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrGetschedpolicy(const ScePthreadAttr* attr, int* policy) { - if (policy == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_attr_getschedpolicy(&(*attr)->pth_attr, policy); - - switch (*policy) { - case SCHED_OTHER: - *policy = (*attr)->policy; - break; - case SCHED_FIFO: - *policy = 1; - break; - case SCHED_RR: - *policy = 3; - break; - default: - UNREACHABLE(); - } - - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrSetschedpolicy(ScePthreadAttr* attr, int policy) { - if (attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int ppolicy = SCHED_OTHER; // winpthreads only supports SCHED_OTHER - if (policy != SCHED_OTHER) { - LOG_ERROR(Kernel_Pthread, "policy={} not supported by winpthreads", policy); - } - - (*attr)->policy = policy; - int result = pthread_attr_setschedpolicy(&(*attr)->pth_attr, ppolicy); - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -ScePthread PS4_SYSV_ABI scePthreadSelf() { - return g_pthread_self; -} - -int PS4_SYSV_ABI scePthreadAttrSetaffinity(ScePthreadAttr* pattr, - const /*SceKernelCpumask*/ u64 mask) { - LOG_DEBUG(Kernel_Pthread, "called"); - - if (pattr == nullptr || *pattr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - (*pattr)->affinity = mask; - return SCE_OK; -} - -int PS4_SYSV_ABI scePthreadAttrGetaffinity(const ScePthreadAttr* pattr, - /* SceKernelCpumask*/ u64* mask) { - if (pattr == nullptr || *pattr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - *mask = (*pattr)->affinity; - - return SCE_OK; -} - -int PS4_SYSV_ABI scePthreadAttrGetstackaddr(const ScePthreadAttr* attr, void** stack_addr) { - - if (stack_addr == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - size_t stack_size = 0; - int result = pthread_attr_getstack(&(*attr)->pth_attr, stack_addr, &stack_size); - - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrGetstacksize(const ScePthreadAttr* attr, size_t* stack_size) { - - if (stack_size == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_attr_getstacksize(&(*attr)->pth_attr, stack_size); - - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrSetstackaddr(ScePthreadAttr* attr, void* addr) { - - if (addr == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - size_t stack_size = 0; - pthread_attr_getstacksize(&(*attr)->pth_attr, &stack_size); - - int result = pthread_attr_setstack(&(*attr)->pth_attr, addr, stack_size); - - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrSetstacksize(ScePthreadAttr* attr, size_t stack_size) { - - if (stack_size == 0 || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_attr_setstacksize(&(*attr)->pth_attr, stack_size); - - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI posix_pthread_attr_init(ScePthreadAttr* attr) { - int result = scePthreadAttrInit(attr); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_attr_setstacksize(ScePthreadAttr* attr, size_t stacksize) { - int result = scePthreadAttrSetstacksize(attr, stacksize); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI scePthreadSetaffinity(ScePthread thread, const /*SceKernelCpumask*/ u64 mask) { - LOG_DEBUG(Kernel_Pthread, "called"); - - if (thread == nullptr) { - return SCE_KERNEL_ERROR_ESRCH; - } - - auto result = scePthreadAttrSetaffinity(&thread->attr, mask); - - return result; -} - -int PS4_SYSV_ABI scePthreadGetaffinity(ScePthread thread, /*SceKernelCpumask*/ u64* mask) { - LOG_INFO(Kernel_Pthread, "called"); - - if (thread == nullptr) { - return SCE_KERNEL_ERROR_ESRCH; - } - - auto result = scePthreadAttrGetaffinity(&thread->attr, mask); - - return result; -} - -ScePthreadMutex* createMutex(ScePthreadMutex* addr) { - if (addr == nullptr || - (*addr != nullptr && *addr != ORBIS_PTHREAD_MUTEX_ADAPTIVE_INITIALIZER)) { - return addr; - } - - const VAddr vaddr = reinterpret_cast(addr); - std::string name = fmt::format("mutex{:#x}", vaddr); - scePthreadMutexInit(addr, nullptr, name.c_str()); - return addr; -} - -int PS4_SYSV_ABI scePthreadMutexInit(ScePthreadMutex* mutex, const ScePthreadMutexattr* mutex_attr, - const char* name) { - const ScePthreadMutexattr* attr; - - if (mutex == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - if (mutex_attr == nullptr || *mutex_attr == nullptr) { - if (*mutex == ORBIS_PTHREAD_MUTEX_ADAPTIVE_INITIALIZER) { - attr = g_pthread_cxt->getAdaptiveMutexattr(); - } else { - attr = g_pthread_cxt->getDefaultMutexattr(); - } - } else { - attr = mutex_attr; - } - - *mutex = new PthreadMutexInternal{}; - if (name != nullptr) { - (*mutex)->name = name; - } else { - (*mutex)->name = "nonameMutex"; - } - - int result = pthread_mutex_init(&(*mutex)->pth_mutex, &(*attr)->pth_mutex_attr); - - if (name != nullptr) { - LOG_INFO(Kernel_Pthread, "name={}, result={}", name, result); - } - - switch (result) { - case 0: - return SCE_OK; - case EAGAIN: - return SCE_KERNEL_ERROR_EAGAIN; - case EINVAL: - return SCE_KERNEL_ERROR_EINVAL; - case ENOMEM: - return SCE_KERNEL_ERROR_ENOMEM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadMutexDestroy(ScePthreadMutex* mutex) { - - if (mutex == nullptr || *mutex == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - if (*mutex == ORBIS_PTHREAD_MUTEX_ADAPTIVE_INITIALIZER) { - return ORBIS_OK; - } - - int result = pthread_mutex_destroy(&(*mutex)->pth_mutex); - - LOG_DEBUG(Kernel_Pthread, "name={}, result={}", (*mutex)->name, result); - - delete *mutex; - *mutex = nullptr; - - switch (result) { - case 0: - return SCE_OK; - case EBUSY: - return SCE_KERNEL_ERROR_EBUSY; - case EINVAL: - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} -int PS4_SYSV_ABI scePthreadMutexattrInit(ScePthreadMutexattr* attr) { - *attr = new PthreadMutexattrInternal{}; - - int result = pthread_mutexattr_init(&(*attr)->pth_mutex_attr); - - result = (result == 0 ? scePthreadMutexattrSettype(attr, 1) : result); - result = (result == 0 ? scePthreadMutexattrSetprotocol(attr, 0) : result); - - switch (result) { - case 0: - return SCE_OK; - case ENOMEM: - return SCE_KERNEL_ERROR_ENOMEM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadMutexattrSettype(ScePthreadMutexattr* attr, int type) { - int ptype = PTHREAD_MUTEX_DEFAULT; - switch (type) { - case ORBIS_PTHREAD_MUTEX_ERRORCHECK: - ptype = PTHREAD_MUTEX_ERRORCHECK; - break; - case ORBIS_PTHREAD_MUTEX_RECURSIVE: - ptype = PTHREAD_MUTEX_RECURSIVE; - break; - case ORBIS_PTHREAD_MUTEX_NORMAL: - ptype = PTHREAD_MUTEX_NORMAL; - break; - case ORBIS_PTHREAD_MUTEX_ADAPTIVE: - LOG_ERROR(Kernel_Pthread, "Unimplemented adaptive mutex"); - ptype = PTHREAD_MUTEX_ERRORCHECK; - break; - default: - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_mutexattr_settype(&(*attr)->pth_mutex_attr, ptype); - ASSERT(result == 0); - - return SCE_OK; -} - -int PS4_SYSV_ABI scePthreadMutexattrSetprotocol(ScePthreadMutexattr* attr, int protocol) { - int pprotocol = PTHREAD_PRIO_NONE; - switch (protocol) { - case 0: - pprotocol = PTHREAD_PRIO_NONE; - break; - case 1: - pprotocol = PTHREAD_PRIO_INHERIT; - break; - case 2: - pprotocol = PTHREAD_PRIO_PROTECT; - break; - default: - UNREACHABLE_MSG("Invalid protocol: {}", protocol); - } - -#if _WIN64 - int result = 0; -#else - int result = pthread_mutexattr_setprotocol(&(*attr)->pth_mutex_attr, pprotocol); -#endif - (*attr)->pprotocol = pprotocol; - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadMutexLock(ScePthreadMutex* mutex) { - mutex = createMutex(mutex); - if (mutex == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_mutex_lock(&(*mutex)->pth_mutex); - if (result != 0) { - LOG_TRACE(Kernel_Pthread, "Locked name={}, result={}", (*mutex)->name, result); - } - - switch (result) { - case 0: - return SCE_OK; - case EAGAIN: - return SCE_KERNEL_ERROR_EAGAIN; - case EINVAL: - return SCE_KERNEL_ERROR_EINVAL; - case EDEADLK: - return SCE_KERNEL_ERROR_EDEADLK; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadMutexUnlock(ScePthreadMutex* mutex) { - if (mutex == nullptr || *mutex == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_mutex_unlock(&(*mutex)->pth_mutex); - if (result != 0) { - LOG_TRACE(Kernel_Pthread, "Unlocking name={}, result={}", (*mutex)->name, result); - } - - switch (result) { - case 0: - return SCE_OK; - case EINVAL: - return SCE_KERNEL_ERROR_EINVAL; - case EPERM: - return SCE_KERNEL_ERROR_EPERM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadMutexattrDestroy(ScePthreadMutexattr* attr) { - int result = pthread_mutexattr_destroy(&(*attr)->pth_mutex_attr); - - delete *attr; - *attr = nullptr; - - switch (result) { - case 0: - return SCE_OK; - case ENOMEM: - return SCE_KERNEL_ERROR_ENOMEM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -ScePthreadCond* createCond(ScePthreadCond* addr) { - if (addr == nullptr || *addr != nullptr) { - return addr; - } - static std::mutex mutex; - std::scoped_lock lk{mutex}; - if (*addr != nullptr) { - return addr; - } - const VAddr vaddr = reinterpret_cast(addr); - std::string name = fmt::format("cond{:#x}", vaddr); - scePthreadCondInit(static_cast(addr), nullptr, name.c_str()); - return addr; -} - -int PS4_SYSV_ABI scePthreadCondInit(ScePthreadCond* cond, const ScePthreadCondattr* attr, - const char* name) { - if (cond == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - if (attr == nullptr) { - attr = g_pthread_cxt->getDefaultCondattr(); - } - - *cond = new PthreadCondInternal{}; - - if (name != nullptr) { - (*cond)->name = name; - } else { - (*cond)->name = "nonameCond"; - } - - int result = pthread_cond_init(&(*cond)->cond, &(*attr)->cond_attr); - - if (name != nullptr) { - LOG_TRACE(Kernel_Pthread, "name={}, result={}", (*cond)->name, result); - } - - switch (result) { - case 0: - return SCE_OK; - case EAGAIN: - return SCE_KERNEL_ERROR_EAGAIN; - case EINVAL: - return SCE_KERNEL_ERROR_EINVAL; - case ENOMEM: - return SCE_KERNEL_ERROR_ENOMEM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadCondattrInit(ScePthreadCondattr* attr) { - *attr = new PthreadCondAttrInternal{}; - - int result = pthread_condattr_init(&(*attr)->cond_attr); - - switch (result) { - case 0: - return SCE_OK; - case ENOMEM: - return SCE_KERNEL_ERROR_ENOMEM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadCondBroadcast(ScePthreadCond* cond) { - cond = createCond(cond); - if (cond == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_cond_broadcast(&(*cond)->cond); - - LOG_TRACE(Kernel_Pthread, "called name={}, result={}", (*cond)->name, result); - - return (result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL); -} - -int PS4_SYSV_ABI scePthreadCondTimedwait(ScePthreadCond* cond, ScePthreadMutex* mutex, u64 usec) { - cond = createCond(cond); - if (cond == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - if (mutex == nullptr || *mutex == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - timespec time{}; - time.tv_sec = usec / 1000000; - time.tv_nsec = ((usec % 1000000) * 1000); - int result = pthread_cond_timedwait(&(*cond)->cond, &(*mutex)->pth_mutex, &time); - - // LOG_INFO(Kernel_Pthread, "scePthreadCondTimedwait, result={}", result); - - switch (result) { - case 0: - return SCE_OK; - case ETIMEDOUT: - return SCE_KERNEL_ERROR_ETIMEDOUT; - case EINTR: - return SCE_KERNEL_ERROR_EINTR; - case EAGAIN: - return SCE_KERNEL_ERROR_EAGAIN; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadCondDestroy(ScePthreadCond* cond) { - if (cond == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - int result = pthread_cond_destroy(&(*cond)->cond); - - LOG_DEBUG(Kernel_Pthread, "scePthreadCondDestroy, result={}", result); - - delete *cond; - *cond = nullptr; - - switch (result) { - case 0: - return SCE_OK; - case EBUSY: - return SCE_KERNEL_ERROR_EBUSY; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI posix_pthread_mutex_init(ScePthreadMutex* mutex, const ScePthreadMutexattr* attr) { - // LOG_INFO(Kernel_Pthread, "posix pthread_mutex_init redirect to scePthreadMutexInit"); - int result = scePthreadMutexInit(mutex, attr, nullptr); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_mutex_lock(ScePthreadMutex* mutex) { - // LOG_INFO(Kernel_Pthread, "posix pthread_mutex_lock redirect to scePthreadMutexLock"); - int result = scePthreadMutexLock(mutex); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_mutex_unlock(ScePthreadMutex* mutex) { - // LOG_INFO(Kernel_Pthread, "posix pthread_mutex_unlock redirect to scePthreadMutexUnlock"); - int result = scePthreadMutexUnlock(mutex); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_mutex_destroy(ScePthreadMutex* mutex) { - int result = scePthreadMutexDestroy(mutex); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_cond_wait(ScePthreadCond* cond, ScePthreadMutex* mutex) { - int result = scePthreadCondWait(cond, mutex); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_cond_timedwait(ScePthreadCond* cond, ScePthreadMutex* mutex, - u64 usec) { - int result = scePthreadCondTimedwait(cond, mutex, usec); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_cond_broadcast(ScePthreadCond* cond) { - int result = scePthreadCondBroadcast(cond); - if (result != 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_mutexattr_init(ScePthreadMutexattr* attr) { - int result = scePthreadMutexattrInit(attr); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_mutexattr_settype(ScePthreadMutexattr* attr, int type) { - int result = scePthreadMutexattrSettype(attr, type); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_mutexattr_destroy(ScePthreadMutexattr* attr) { - int result = scePthreadMutexattrDestroy(attr); - if (result < 0) { - UNREACHABLE(); - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_once(pthread_once_t* once_control, void (*init_routine)(void)) { - return pthread_once(once_control, init_routine); -} - -int PS4_SYSV_ABI posix_pthread_mutexattr_setprotocol(ScePthreadMutexattr* attr, int protocol) { - int result = scePthreadMutexattrSetprotocol(attr, protocol); - if (result < 0) { - UNREACHABLE(); - } - return result; -} - -#ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK -static int pthread_mutex_timedlock(pthread_mutex_t* mutex, const struct timespec* abstime) { - int rc; - while ((rc = pthread_mutex_trylock(mutex)) == EBUSY) { - struct timespec curr_time; - clock_gettime(CLOCK_REALTIME, &curr_time); - - s64 remaining_ns = 0; - remaining_ns += - (static_cast(abstime->tv_sec) - static_cast(curr_time.tv_sec)) * 1000000000L; - remaining_ns += static_cast(abstime->tv_nsec) - static_cast(curr_time.tv_nsec); - - if (remaining_ns <= 0) { - return ETIMEDOUT; - } - - struct timespec sleep_time; - sleep_time.tv_sec = 0; - if (remaining_ns < 5000000L) { - sleep_time.tv_nsec = remaining_ns; - } else { - sleep_time.tv_nsec = 5000000; - } - - nanosleep(&sleep_time, nullptr); - } - - return rc; -} -#endif - -int PS4_SYSV_ABI scePthreadMutexTimedlock(ScePthreadMutex* mutex, u64 usec) { - mutex = createMutex(mutex); - if (mutex == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - timespec time{}; - time.tv_sec = usec / 1000000; - time.tv_nsec = ((usec % 1000000) * 1000); - int result = pthread_mutex_timedlock(&(*mutex)->pth_mutex, &time); - - switch (result) { - case 0: - return SCE_OK; - case ETIMEDOUT: - return SCE_KERNEL_ERROR_ETIMEDOUT; - case EINTR: - return SCE_KERNEL_ERROR_EINTR; - case EAGAIN: - return SCE_KERNEL_ERROR_EAGAIN; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -static int pthread_copy_attributes(ScePthreadAttr* dst, const ScePthreadAttr* src) { - if (dst == nullptr || *dst == nullptr || src == nullptr || *src == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - u64 mask = 0; - int state = 0; - size_t guard_size = 0; - int inherit_sched = 0; - SceKernelSchedParam param = {}; - int policy = 0; - void* stack_addr = nullptr; - size_t stack_size = 0; - - int result = 0; - - result = (result == 0 ? scePthreadAttrGetaffinity(src, &mask) : result); - result = (result == 0 ? scePthreadAttrGetdetachstate(src, &state) : result); - result = (result == 0 ? scePthreadAttrGetguardsize(src, &guard_size) : result); - result = (result == 0 ? scePthreadAttrGetinheritsched(src, &inherit_sched) : result); - result = (result == 0 ? scePthreadAttrGetschedparam(src, ¶m) : result); - result = (result == 0 ? scePthreadAttrGetschedpolicy(src, &policy) : result); - result = (result == 0 ? scePthreadAttrGetstackaddr(src, &stack_addr) : result); - result = (result == 0 ? scePthreadAttrGetstacksize(src, &stack_size) : result); - - result = (result == 0 ? scePthreadAttrSetaffinity(dst, mask) : result); - result = (result == 0 ? scePthreadAttrSetdetachstate(dst, state) : result); - result = (result == 0 ? scePthreadAttrSetguardsize(dst, guard_size) : result); - result = (result == 0 ? scePthreadAttrSetinheritsched(dst, inherit_sched) : result); - result = (result == 0 ? scePthreadAttrSetschedparam(dst, ¶m) : result); - result = (result == 0 ? scePthreadAttrSetschedpolicy(dst, policy) : result); - if (stack_addr != nullptr) { - result = (result == 0 ? scePthreadAttrSetstackaddr(dst, stack_addr) : result); - } - if (stack_size != 0) { - result = (result == 0 ? scePthreadAttrSetstacksize(dst, stack_size) : result); - } - - return result; -} - -int PS4_SYSV_ABI scePthreadAttrGet(ScePthread thread, ScePthreadAttr* attr) { - if (thread == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - return pthread_copy_attributes(attr, &thread->attr); -} - -static void cleanup_thread(void* arg) { - auto* thread = static_cast(arg); - for (const auto& [key, destructor] : thread->key_destructors) { - if (void* value = pthread_getspecific(key); value != nullptr) { - destructor(value); - } - } - Core::SetTcbBase(nullptr); - thread->is_almost_done = true; - DebugState.RemoveCurrentThreadFromGuestList(); -} - -static void* run_thread(void* arg) { - auto* thread = static_cast(arg); - Common::SetCurrentThreadName(thread->name.c_str()); - const auto* linker = Common::Singleton::Instance(); - void* ret = nullptr; - g_pthread_self = thread; - pthread_cleanup_push(cleanup_thread, thread); - thread->is_started = true; - DebugState.AddCurrentThreadToGuestList(); - ret = linker->ExecuteGuest(thread->entry, thread->arg); - pthread_cleanup_pop(1); - return ret; -} - -int PS4_SYSV_ABI scePthreadCreate(ScePthread* thread, const ScePthreadAttr* attr, - PthreadEntryFunc start_routine, void* arg, const char* name) { - if (thread == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - auto* pthread_pool = g_pthread_cxt->GetPthreadPool(); - - if (attr == nullptr) { - attr = g_pthread_cxt->GetDefaultAttr(); - } - - *thread = pthread_pool->Create(name); - - if ((*thread)->attr != nullptr) { - scePthreadAttrDestroy(&(*thread)->attr); - } - scePthreadAttrInit(&(*thread)->attr); - - int result = pthread_copy_attributes(&(*thread)->attr, attr); - ASSERT(result == 0); - - if (name != NULL) { - (*thread)->name = name; - } else { - (*thread)->name = "no-name"; - } - (*thread)->entry = start_routine; - (*thread)->arg = arg; - (*thread)->is_almost_done = false; - (*thread)->is_detached = (*attr)->detached; - (*thread)->is_started = false; - - pthread_attr_setstacksize(&(*attr)->pth_attr, 2_MB); - result = pthread_create(&(*thread)->pth, &(*attr)->pth_attr, run_thread, *thread); - - LOG_INFO(Kernel_Pthread, "thread create name = {}", (*thread)->name); - - switch (result) { - case 0: - return SCE_OK; - case ENOMEM: - return SCE_KERNEL_ERROR_ENOMEM; - case EAGAIN: - return SCE_KERNEL_ERROR_EAGAIN; - case EDEADLK: - return SCE_KERNEL_ERROR_EDEADLK; - case EPERM: - return SCE_KERNEL_ERROR_EPERM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -ScePthread PThreadPool::Create(const char* name) { - std::scoped_lock lock{m_mutex}; - - for (auto* p : m_threads) { - if (p->is_free && name != nullptr && p->name == name) { - p->is_free = false; - return p; - } - } - - auto* ret = new PthreadInternal{}; - ret->is_free = false; - ret->is_detached = false; - ret->is_almost_done = false; - ret->attr = nullptr; - - m_threads.push_back(ret); - - return ret; -} - -void PS4_SYSV_ABI scePthreadYield() { - sched_yield(); -} - -void PS4_SYSV_ABI posix_pthread_yield() { - sched_yield(); -} - -int PS4_SYSV_ABI scePthreadAttrGetstack(ScePthreadAttr* attr, void** addr, size_t* size) { - - int result = pthread_attr_getstack(&(*attr)->pth_attr, addr, size); - LOG_INFO(Kernel_Pthread, "scePthreadAttrGetstack: result = {}", result); - - if (result == 0) { - return SCE_OK; - } - return SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrSetstack(ScePthreadAttr* attr, void* addr, size_t size) { - if (attr == nullptr || *attr == nullptr || addr == nullptr || size < 0x4000) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - int result = pthread_attr_setstack(&(*attr)->pth_attr, addr, size); - LOG_INFO(Kernel_Pthread, "scePthreadAttrSetstack: result = {}", result); - - if (result == 0) { - return ORBIS_OK; - } - return ORBIS_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadJoin(ScePthread thread, void** res) { - int result = pthread_join(thread->pth, res); - LOG_INFO(Kernel_Pthread, "scePthreadJoin result = {}", result); - thread->is_detached = false; - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_pthread_join(ScePthread thread, void** res) { - int result = pthread_join(thread->pth, res); - LOG_INFO(Kernel_Pthread, "posix_pthread_join result = {}", result); - thread->is_detached = false; - return ORBIS_OK; -} - -int PS4_SYSV_ABI scePthreadDetach(ScePthread thread) { - thread->is_detached = true; - return ORBIS_OK; -} - -ScePthread PS4_SYSV_ABI posix_pthread_self() { - return g_pthread_self; -} - -int PS4_SYSV_ABI scePthreadCondSignal(ScePthreadCond* cond) { - cond = createCond(cond); - if (cond == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_cond_signal(&(*cond)->cond); - - // LOG_INFO(Kernel_Pthread, "scePthreadCondSignal, result={}", result); - - switch (result) { - case 0: - return SCE_OK; - case EBUSY: - return SCE_KERNEL_ERROR_EBUSY; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadCondWait(ScePthreadCond* cond, ScePthreadMutex* mutex) { - cond = createCond(cond); - if (cond == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - if (mutex == nullptr || *mutex == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - int result = pthread_cond_wait(&(*cond)->cond, &(*mutex)->pth_mutex); - - LOG_DEBUG(Kernel_Pthread, "scePthreadCondWait, result={}", result); - - switch (result) { - case 0: - return SCE_OK; - case EINTR: - return SCE_KERNEL_ERROR_EINTR; - case EAGAIN: - return SCE_KERNEL_ERROR_EAGAIN; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadCondattrDestroy(ScePthreadCondattr* attr) { - if (attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - int result = pthread_condattr_destroy(&(*attr)->cond_attr); - - LOG_DEBUG(Kernel_Pthread, "scePthreadCondattrDestroy: result = {} ", result); - delete *attr; - - switch (result) { - case 0: - return SCE_OK; - case ENOMEM: - return SCE_KERNEL_ERROR_ENOMEM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadMutexTrylock(ScePthreadMutex* mutex) { - mutex = createMutex(mutex); - if (mutex == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - - int result = pthread_mutex_trylock(&(*mutex)->pth_mutex); - if (result != 0) { - LOG_TRACE(Kernel_Pthread, "name={}, result={}", (*mutex)->name, result); - } - - switch (result) { - case 0: - return ORBIS_OK; - case EAGAIN: - return ORBIS_KERNEL_ERROR_EAGAIN; - case EBUSY: - return ORBIS_KERNEL_ERROR_EBUSY; - case EINVAL: - default: - return ORBIS_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadEqual(ScePthread thread1, ScePthread thread2) { - return (thread1 == thread2 ? 1 : 0); -} - -int PS4_SYSV_ABI posix_pthread_equal(ScePthread thread1, ScePthread thread2) { - return (thread1 == thread2 ? 1 : 0); -} - -struct TlsIndex { - u64 ti_module; - u64 ti_offset; -}; - -void* PS4_SYSV_ABI __tls_get_addr(TlsIndex* index) { - auto* linker = Common::Singleton::Instance(); - return linker->TlsGetAddr(index->ti_module, index->ti_offset); -} - -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_mutex_trylock(ScePthreadMutex* mutex) { - int result = scePthreadMutexTrylock(mutex); - if (result < 0) { - // UNREACHABLE(); - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_attr_destroy(ScePthreadAttr* attr) { - int result = scePthreadAttrDestroy(attr); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_attr_setschedparam(ScePthreadAttr* attr, - const SceKernelSchedParam* param) { - int result = scePthreadAttrSetschedparam(attr, param); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_attr_setinheritsched(ScePthreadAttr* attr, int inheritSched) { - int result = scePthreadAttrSetinheritsched(attr, inheritSched); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_setprio(ScePthread thread, int prio) { - int result = scePthreadSetprio(thread, prio); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_attr_setdetachstate(ScePthreadAttr* attr, int detachstate) { - // LOG_INFO(Kernel_Pthread, "posix pthread_mutexattr_init redirect to scePthreadMutexattrInit"); - int result = scePthreadAttrSetdetachstate(attr, detachstate); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_create_name_np(ScePthread* thread, const ScePthreadAttr* attr, - PthreadEntryFunc start_routine, void* arg, - const char* name) { - int result = scePthreadCreate(thread, attr, start_routine, arg, name); - if (result != 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_create(ScePthread* thread, const ScePthreadAttr* attr, - PthreadEntryFunc start_routine, void* arg) { - return posix_pthread_create_name_np(thread, attr, start_routine, arg, "NoName"); -} - -using Destructor = void (*)(void*); - -int PS4_SYSV_ABI posix_pthread_key_create(u32* key, Destructor func) { - pthread_key_t thread_key; - int rc = pthread_key_create(&thread_key, func); - *key = static_cast(thread_key); - return rc; -} - -int PS4_SYSV_ABI posix_pthread_setspecific(int key, const void* value) { - return pthread_setspecific(key, value); -} - -void* PS4_SYSV_ABI posix_pthread_getspecific(int key) { - return pthread_getspecific(key); -} - -int PS4_SYSV_ABI posix_pthread_cond_init(ScePthreadCond* cond, const ScePthreadCondattr* attr) { - // LOG_INFO(Kernel_Pthread, "posix pthread_mutex_init redirect to scePthreadMutexInit"); - int result = scePthreadCondInit(cond, attr, "NoName"); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_cond_signal(ScePthreadCond* cond) { - int result = scePthreadCondSignal(cond); - return result; -} - -int PS4_SYSV_ABI posix_pthread_cond_destroy(ScePthreadCond* cond) { - int result = scePthreadCondDestroy(cond); - return result; -} - -int PS4_SYSV_ABI posix_pthread_setcancelstate(int state, int* oldstate) { - return pthread_setcancelstate(state, oldstate); -} - -int PS4_SYSV_ABI posix_pthread_detach(ScePthread thread) { - return pthread_detach(thread->pth); -} - -int PS4_SYSV_ABI posix_sem_init(PthreadSemInternal** sem, int pshared, unsigned int value) { - if (value > ORBIS_KERNEL_SEM_VALUE_MAX) { - SetPosixErrno(EINVAL); - return -1; - } - if (sem != nullptr) { - *sem = new PthreadSemInternal{ - .semaphore = std::counting_semaphore{value}, - .value = {static_cast(value)}, - }; - } - return 0; -} - -int PS4_SYSV_ABI posix_sem_wait(PthreadSemInternal** sem) { - if (sem == nullptr || *sem == nullptr) { - SetPosixErrno(EINVAL); - return -1; - } - (*sem)->semaphore.acquire(); - --(*sem)->value; - return 0; -} - -int PS4_SYSV_ABI posix_sem_trywait(PthreadSemInternal** sem) { - if (sem == nullptr || *sem == nullptr) { - SetPosixErrno(EINVAL); - return -1; - } - if (!(*sem)->semaphore.try_acquire()) { - SetPosixErrno(EAGAIN); - return -1; - } - --(*sem)->value; - return 0; -} - -int PS4_SYSV_ABI posix_sem_timedwait(PthreadSemInternal** sem, const timespec* t) { - if (sem == nullptr || *sem == nullptr) { - SetPosixErrno(EINVAL); - return -1; - } - - using std::chrono::duration_cast; - using std::chrono::nanoseconds; - using std::chrono::seconds; - using std::chrono::system_clock; - - const system_clock::time_point time{ - duration_cast(seconds{t->tv_sec} + nanoseconds{t->tv_nsec})}; - if (!(*sem)->semaphore.try_acquire_until(time)) { - SetPosixErrno(ETIMEDOUT); - return -1; - } - --(*sem)->value; - return 0; -} - -int PS4_SYSV_ABI posix_sem_post(PthreadSemInternal** sem) { - if (sem == nullptr || *sem == nullptr) { - SetPosixErrno(EINVAL); - return -1; - } - if ((*sem)->value == ORBIS_KERNEL_SEM_VALUE_MAX) { - SetPosixErrno(EOVERFLOW); - return -1; - } - ++(*sem)->value; - (*sem)->semaphore.release(); - return 0; -} - -int PS4_SYSV_ABI posix_sem_destroy(PthreadSemInternal** sem) { - if (sem == nullptr || *sem == nullptr) { - SetPosixErrno(EINVAL); - return -1; - } - delete *sem; - *sem = nullptr; - return 0; -} - -int PS4_SYSV_ABI posix_sem_getvalue(PthreadSemInternal** sem, int* sval) { - if (sem == nullptr || *sem == nullptr) { - SetPosixErrno(EINVAL); - return -1; - } - if (sval) { - *sval = (*sem)->value; - } - return 0; -} - -int PS4_SYSV_ABI posix_pthread_attr_getstacksize(const pthread_attr_t* attr, size_t* size) { - return pthread_attr_getstacksize(attr, size); -} - -int PS4_SYSV_ABI scePthreadGetschedparam(ScePthread thread, int* policy, - SceKernelSchedParam* param) { - return pthread_getschedparam(thread->pth, policy, param); -} - -int PS4_SYSV_ABI scePthreadSetschedparam(ScePthread thread, int policy, - const SceKernelSchedParam* param) { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called policy={}, sched_priority={}", policy, - param->sched_priority); - return ORBIS_OK; -} - -int PS4_SYSV_ABI scePthreadOnce(int* once_control, void (*init_routine)(void)) { - return pthread_once(reinterpret_cast(once_control), init_routine); -} - -[[noreturn]] void PS4_SYSV_ABI scePthreadExit(void* value_ptr) { - g_pthread_self->is_free = true; - - pthread_exit(value_ptr); - UNREACHABLE(); -} - -[[noreturn]] void PS4_SYSV_ABI posix_pthread_exit(void* value_ptr) { - pthread_exit(value_ptr); - UNREACHABLE(); -} - -int PS4_SYSV_ABI scePthreadGetthreadid() { - return (int)(size_t)g_pthread_self; -} - -int PS4_SYSV_ABI scePthreadGetprio(ScePthread thread, int* prio) { - *prio = thread->prio; - return ORBIS_OK; -} -int PS4_SYSV_ABI scePthreadSetprio(ScePthread thread, int prio) { - if (thread == nullptr) { - LOG_ERROR(Kernel_Pthread, "scePthreadSetprio: thread is nullptr"); - return ORBIS_KERNEL_ERROR_EINVAL; - } - thread->prio = prio; - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_pthread_condattr_init(ScePthreadCondattr* attr) { - int result = scePthreadCondattrInit(attr); - LOG_INFO(Kernel_Pthread, - "posix_pthread_condattr_init redirect to scePthreadCondattrInit, result = {}", result); - return result; -} - -int PS4_SYSV_ABI posix_pthread_condattr_destroy(ScePthreadCondattr* attr) { - int result = scePthreadCondattrDestroy(attr); - LOG_INFO(Kernel_Pthread, - "posix_pthread_condattr_destroy redirect to scePthreadCondattrDestroy, result = {}", - result); - return result; -} - -int PS4_SYSV_ABI posix_pthread_condattr_setclock(ScePthreadCondattr* attr, clockid_t clock) { - (*attr)->clock = clock; - return SCE_OK; -} - -int PS4_SYSV_ABI posix_pthread_getschedparam(ScePthread thread, int* policy, - SceKernelSchedParam* param) { - return scePthreadGetschedparam(thread, policy, param); -} - -int PS4_SYSV_ABI posix_pthread_setschedparam(ScePthread thread, int policy, - const SceKernelSchedParam* param) { - return scePthreadSetschedparam(thread, policy, param); -} - -int PS4_SYSV_ABI posix_pthread_attr_getschedpolicy(const ScePthreadAttr* attr, int* policy) { - return scePthreadAttrGetschedpolicy(attr, policy); -} - -int PS4_SYSV_ABI scePthreadRename(ScePthread thread, const char* name) { - thread->name = name; - LOG_INFO(Kernel_Pthread, "scePthreadRename: name = {}", thread->name); - return SCE_OK; -} - -void pthreadSymbolsRegister(Core::Loader::SymbolsResolver* sym) { - LIB_FUNCTION("lZzFeSxPl08", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setcancelstate); - 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("mqULNdimTn0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_key_create); - LIB_FUNCTION("0-KXaS70xy4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_getspecific); - LIB_FUNCTION("WrOLvHU0yQM", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setspecific); - LIB_FUNCTION("4+h9EzwKF4I", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetschedpolicy); - LIB_FUNCTION("-Wreprtu0Qs", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetdetachstate); - LIB_FUNCTION("JaRMy+QcpeU", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGetdetachstate); - LIB_FUNCTION("eXbUSpEaTsA", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetinheritsched); - LIB_FUNCTION("DzES9hQF4f4", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetschedparam); - LIB_FUNCTION("nsYoNRywwNg", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrInit); - LIB_FUNCTION("62KCwEMmzcM", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrDestroy); - LIB_FUNCTION("onNY9Byn-W8", "libkernel", 1, "libkernel", 1, 1, scePthreadJoin); - LIB_FUNCTION("4qGrR6eoP9Y", "libkernel", 1, "libkernel", 1, 1, scePthreadDetach); - LIB_FUNCTION("3PtV6p3QNX4", "libkernel", 1, "libkernel", 1, 1, scePthreadEqual); - LIB_FUNCTION("3kg7rT0NQIs", "libkernel", 1, "libkernel", 1, 1, scePthreadExit); - LIB_FUNCTION("FJrT5LuUBAU", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_exit); - LIB_FUNCTION("7Xl257M4VNI", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_equal); - LIB_FUNCTION("h9CcP3J0oVM", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_join); - LIB_FUNCTION("EI-5-jlq2dE", "libkernel", 1, "libkernel", 1, 1, scePthreadGetthreadid); - LIB_FUNCTION("1tKyG7RlMJo", "libkernel", 1, "libkernel", 1, 1, scePthreadGetprio); - LIB_FUNCTION("W0Hpm2X0uPE", "libkernel", 1, "libkernel", 1, 1, scePthreadSetprio); - LIB_FUNCTION("GBUY7ywdULE", "libkernel", 1, "libkernel", 1, 1, scePthreadRename); - LIB_FUNCTION("F+yfmduIBB8", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetstackaddr); - LIB_FUNCTION("El+cQ20DynU", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetguardsize); - - LIB_FUNCTION("aI+OeCz8xrQ", "libkernel", 1, "libkernel", 1, 1, scePthreadSelf); - LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self); - LIB_FUNCTION("EotR8a3ASf4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_self); - LIB_FUNCTION("3qxgM4ezETA", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetaffinity); - LIB_FUNCTION("8+s5BzZjxSg", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGetaffinity); - LIB_FUNCTION("x1X76arYMxU", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGet); - LIB_FUNCTION("FXPWHNk8Of0", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGetschedparam); - LIB_FUNCTION("P41kTWUS3EI", "libkernel", 1, "libkernel", 1, 1, scePthreadGetschedparam); - LIB_FUNCTION("oIRFTjoILbg", "libkernel", 1, "libkernel", 1, 1, scePthreadSetschedparam); - LIB_FUNCTION("UTXzJbWhhTE", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetstacksize); - LIB_FUNCTION("vNe1w4diLCs", "libkernel", 1, "libkernel", 1, 1, __tls_get_addr); - LIB_FUNCTION("OxhIB8LB-PQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_create); - LIB_FUNCTION("OxhIB8LB-PQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create); - LIB_FUNCTION("bt3CTBKmGyI", "libkernel", 1, "libkernel", 1, 1, scePthreadSetaffinity); - LIB_FUNCTION("rcrVFJsQWRY", "libkernel", 1, "libkernel", 1, 1, scePthreadGetaffinity); - LIB_FUNCTION("6UgtwV+0zb4", "libkernel", 1, "libkernel", 1, 1, scePthreadCreate); - LIB_FUNCTION("T72hz6ffq08", "libkernel", 1, "libkernel", 1, 1, scePthreadYield); - LIB_FUNCTION("B5GmVDKwpn0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_yield); - - LIB_FUNCTION("-quPa4SEJUw", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGetstack); - LIB_FUNCTION("Bvn74vj6oLo", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetstack); - LIB_FUNCTION("Ru36fiTtJzA", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGetstackaddr); - LIB_FUNCTION("-fA+7ZlGDQs", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGetstacksize); - LIB_FUNCTION("14bOACANTBo", "libkernel", 1, "libkernel", 1, 1, scePthreadOnce); - - // mutex calls - LIB_FUNCTION("cmo1RIYva9o", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexInit); - LIB_FUNCTION("2Of0f+3mhhE", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexDestroy); - LIB_FUNCTION("F8bUHwAG284", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexattrInit); - LIB_FUNCTION("smWEktiyyG0", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexattrDestroy); - LIB_FUNCTION("iMp8QpE+XO4", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexattrSettype); - LIB_FUNCTION("1FGvU0i9saQ", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexattrSetprotocol); - LIB_FUNCTION("9UK1vLZQft4", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexLock); - LIB_FUNCTION("tn3VlD0hG60", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexUnlock); - LIB_FUNCTION("upoVrzMHFeE", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexTrylock); - LIB_FUNCTION("IafI2PxcPnQ", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexTimedlock); - - // scePthreadMutexInitForInternalLibc, scePthreadMutexattrInitForInternalLibc - LIB_FUNCTION("qH1gXoq71RY", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexInit); - LIB_FUNCTION("n2MMpvU8igI", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexattrInit); - - // cond calls - LIB_FUNCTION("2Tb92quprl0", "libkernel", 1, "libkernel", 1, 1, scePthreadCondInit); - LIB_FUNCTION("m5-2bsNfv7s", "libkernel", 1, "libkernel", 1, 1, scePthreadCondattrInit); - LIB_FUNCTION("JGgj7Uvrl+A", "libkernel", 1, "libkernel", 1, 1, scePthreadCondBroadcast); - LIB_FUNCTION("WKAXJ4XBPQ4", "libkernel", 1, "libkernel", 1, 1, scePthreadCondWait); - LIB_FUNCTION("waPcxYiR3WA", "libkernel", 1, "libkernel", 1, 1, scePthreadCondattrDestroy); - LIB_FUNCTION("kDh-NfxgMtE", "libkernel", 1, "libkernel", 1, 1, scePthreadCondSignal); - LIB_FUNCTION("BmMjYxmew1w", "libkernel", 1, "libkernel", 1, 1, scePthreadCondTimedwait); - LIB_FUNCTION("g+PZd2hiacg", "libkernel", 1, "libkernel", 1, 1, scePthreadCondDestroy); - - // posix calls - 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("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("Op8TBGY5KHg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_cond_wait); - 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); - 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("mKoTx03HRWA", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_condattr_init); - LIB_FUNCTION("dJcuQVn6-Iw", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_condattr_destroy); - LIB_FUNCTION("EjllaAqAPZo", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_condattr_setclock); - LIB_FUNCTION("Z4QosVuAsA0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_once); - LIB_FUNCTION("RtLRV-pBTTY", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_attr_getschedpolicy); - - // openorbis weird functions - 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); - LIB_FUNCTION("mkx2fVhNMsg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_cond_broadcast); - LIB_FUNCTION("K-jXhbt2gn4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_trylock); - 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("a2P9wYGeZvc", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setprio); - LIB_FUNCTION("Jmi+9w9u0E4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create_name_np); - LIB_FUNCTION("OxhIB8LB-PQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create); - LIB_FUNCTION("+U1R4WtXvoc", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_detach); - 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("FIs3-UQT9sg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_getschedparam); - LIB_FUNCTION("Xs9hdiD7sAA", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setschedparam); - 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); - LIB_FUNCTION("0qOtCR-ZHck", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_attr_getstacksize); - // libs - RwlockSymbolsRegister(sym); - SemaphoreSymbolsRegister(sym); - KeySymbolsRegister(sym); -} - -} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/thread_management.h b/src/core/libraries/kernel/thread_management.h deleted file mode 100644 index 3cdb300f..00000000 --- a/src/core/libraries/kernel/thread_management.h +++ /dev/null @@ -1,225 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include "common/types.h" - -#define ORBIS_PTHREAD_MUTEX_ADAPTIVE_INITIALIZER (reinterpret_cast(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; - std::vector 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 semaphore; - std::atomic value; -}; - -class PThreadPool { -public: - ScePthread Create(const char* name); - -private: - std::vector 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 diff --git a/src/core/libraries/kernel/threads.cpp b/src/core/libraries/kernel/threads.cpp new file mode 100644 index 00000000..082a52b6 --- /dev/null +++ b/src/core/libraries/kernel/threads.cpp @@ -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 diff --git a/src/core/libraries/kernel/threads.h b/src/core/libraries/kernel/threads.h new file mode 100644 index 00000000..ad139359 --- /dev/null +++ b/src/core/libraries/kernel/threads.h @@ -0,0 +1,72 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#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&& 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 func; + std::stop_source stop; +}; + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/condvar.cpp b/src/core/libraries/kernel/threads/condvar.cpp new file mode 100644 index 00000000..5831df9b --- /dev/null +++ b/src/core/libraries/kernel/threads/condvar.cpp @@ -0,0 +1,360 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#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(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((*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 diff --git a/src/core/libraries/kernel/event_flag/event_flag.cpp b/src/core/libraries/kernel/threads/event_flag.cpp similarity index 56% rename from src/core/libraries/kernel/event_flag/event_flag.cpp rename to src/core/libraries/kernel/threads/event_flag.cpp index f83ae0a7..134bd549 100644 --- a/src/core/libraries/kernel/event_flag/event_flag.cpp +++ b/src/core/libraries/kernel/threads/event_flag.cpp @@ -1,13 +1,158 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include + #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::system_clock::now() - start) + .count(); + if (result != nullptr) { + *result = m_bits; + } + + if (ptr_micros != nullptr) { + *ptr_micros = (elapsed >= micros ? 0 : micros - elapsed); + } + + if (m_status == Status::Canceled) { + return ORBIS_KERNEL_ERROR_ECANCELED; + } else if (m_status == Status::Deleted) { + return ORBIS_KERNEL_ERROR_EACCES; + } + + if (clear_mode == ClearMode::All) { + m_bits = 0; + } else if (clear_mode == ClearMode::Bits) { + m_bits &= ~bits; + } + + return ORBIS_OK; + } + + int Poll(u64 bits, WaitMode wait_mode, ClearMode clear_mode, u64* result) { + u32 micros = 0; + auto ret = Wait(bits, wait_mode, clear_mode, result, µs); + if (ret == ORBIS_KERNEL_ERROR_ETIMEDOUT) { + // Poll returns EBUSY instead. + ret = ORBIS_KERNEL_ERROR_EBUSY; + } + return ret; + } + + void Set(u64 bits) { + std::unique_lock lock{m_mutex}; + + while (m_status != Status::Set) { + m_mutex.unlock(); + std::this_thread::sleep_for(std::chrono::microseconds(10)); + m_mutex.lock(); + } + + m_bits |= bits; + + m_cond_var.notify_all(); + } + + void Clear(u64 bits) { + std::unique_lock lock{m_mutex}; + while (m_status != Status::Set) { + m_mutex.unlock(); + std::this_thread::sleep_for(std::chrono::microseconds(10)); + m_mutex.lock(); + } + + m_bits &= bits; + } + +private: + enum class Status { Set, Canceled, Deleted }; + + std::mutex m_mutex; + std::condition_variable m_cond_var; + Status m_status = Status::Set; + int m_waiting_threads = 0; + std::string m_name; + ThreadMode m_thread_mode = ThreadMode::Single; + QueueMode m_queue_mode = QueueMode::Fifo; + u64 m_bits = 0; +}; + +using OrbisKernelUseconds = u32; +using OrbisKernelEventFlag = EventFlagInternal*; + +struct OrbisKernelEventFlagOptParam { + size_t size; +}; + int PS4_SYSV_ABI sceKernelCreateEventFlag(OrbisKernelEventFlag* ef, const char* pName, u32 attr, u64 initPattern, const OrbisKernelEventFlagOptParam* pOptParam) { @@ -25,9 +170,8 @@ int PS4_SYSV_ABI sceKernelCreateEventFlag(OrbisKernelEventFlag* ef, const char* return ORBIS_KERNEL_ERROR_ENAMETOOLONG; } - EventFlagInternal::ThreadMode thread_mode = EventFlagInternal::ThreadMode::Single; - EventFlagInternal::QueueMode queue_mode = EventFlagInternal::QueueMode::Fifo; - + auto thread_mode = EventFlagInternal::ThreadMode::Single; + auto queue_mode = EventFlagInternal::QueueMode::Fifo; switch (attr & 0xfu) { case 0x01: queue_mode = EventFlagInternal::QueueMode::Fifo; @@ -61,6 +205,7 @@ int PS4_SYSV_ABI sceKernelCreateEventFlag(OrbisKernelEventFlag* ef, const char* *ef = new EventFlagInternal(std::string(pName), thread_mode, queue_mode, initPattern); return ORBIS_OK; } + int PS4_SYSV_ABI sceKernelDeleteEventFlag(OrbisKernelEventFlag ef) { if (ef == nullptr) { return ORBIS_KERNEL_ERROR_ESRCH; @@ -69,24 +214,29 @@ int PS4_SYSV_ABI sceKernelDeleteEventFlag(OrbisKernelEventFlag ef) { delete ef; return ORBIS_OK; } + int PS4_SYSV_ABI sceKernelOpenEventFlag() { LOG_ERROR(Kernel_Event, "(STUBBED) called"); return ORBIS_OK; } + int PS4_SYSV_ABI sceKernelCloseEventFlag() { LOG_ERROR(Kernel_Event, "(STUBBED) called"); return ORBIS_OK; } + int PS4_SYSV_ABI sceKernelClearEventFlag(OrbisKernelEventFlag ef, u64 bitPattern) { LOG_DEBUG(Kernel_Event, "called"); ef->Clear(bitPattern); return ORBIS_OK; } + int PS4_SYSV_ABI sceKernelCancelEventFlag(OrbisKernelEventFlag ef, u64 setPattern, int* pNumWaitThreads) { LOG_ERROR(Kernel_Event, "(STUBBED) called"); return ORBIS_OK; } + int PS4_SYSV_ABI sceKernelSetEventFlag(OrbisKernelEventFlag ef, u64 bitPattern) { LOG_TRACE(Kernel_Event, "called"); if (ef == nullptr) { @@ -95,6 +245,7 @@ int PS4_SYSV_ABI sceKernelSetEventFlag(OrbisKernelEventFlag ef, u64 bitPattern) ef->Set(bitPattern); return ORBIS_OK; } + int PS4_SYSV_ABI sceKernelPollEventFlag(OrbisKernelEventFlag ef, u64 bitPattern, u32 waitMode, u64* pResultPat) { LOG_DEBUG(Kernel_Event, "called bitPattern = {:#x} waitMode = {:#x}", bitPattern, waitMode); @@ -107,9 +258,8 @@ int PS4_SYSV_ABI sceKernelPollEventFlag(OrbisKernelEventFlag ef, u64 bitPattern, return ORBIS_KERNEL_ERROR_EINVAL; } - EventFlagInternal::WaitMode wait = EventFlagInternal::WaitMode::And; - EventFlagInternal::ClearMode clear = EventFlagInternal::ClearMode::None; - + auto wait = EventFlagInternal::WaitMode::And; + auto clear = EventFlagInternal::ClearMode::None; switch (waitMode & 0xf) { case 0x01: wait = EventFlagInternal::WaitMode::And; @@ -154,9 +304,8 @@ int PS4_SYSV_ABI sceKernelWaitEventFlag(OrbisKernelEventFlag ef, u64 bitPattern, return ORBIS_KERNEL_ERROR_EINVAL; } - EventFlagInternal::WaitMode wait = EventFlagInternal::WaitMode::And; - EventFlagInternal::ClearMode clear = EventFlagInternal::ClearMode::None; - + auto wait = EventFlagInternal::WaitMode::And; + auto clear = EventFlagInternal::ClearMode::None; switch (waitMode & 0xf) { case 0x01: wait = EventFlagInternal::WaitMode::And; @@ -190,6 +339,7 @@ int PS4_SYSV_ABI sceKernelWaitEventFlag(OrbisKernelEventFlag ef, u64 bitPattern, return result; } + void RegisterKernelEventFlag(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("PZku4ZrXJqg", "libkernel", 1, "libkernel", 1, 1, sceKernelCancelEventFlag); LIB_FUNCTION("7uhBFWRAS60", "libkernel", 1, "libkernel", 1, 1, sceKernelClearEventFlag); @@ -201,4 +351,5 @@ void RegisterKernelEventFlag(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("IOnSvHzqu6A", "libkernel", 1, "libkernel", 1, 1, sceKernelSetEventFlag); LIB_FUNCTION("JTvBflhYazQ", "libkernel", 1, "libkernel", 1, 1, sceKernelWaitEventFlag); } + } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/exception.cpp b/src/core/libraries/kernel/threads/exception.cpp new file mode 100644 index 00000000..b6d89aae --- /dev/null +++ b/src/core/libraries/kernel/threads/exception.cpp @@ -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 +#endif + +namespace Libraries::Kernel { + +static std::array 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(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(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 diff --git a/src/core/libraries/kernel/threads/exception.h b/src/core/libraries/kernel/threads/exception.h new file mode 100644 index 00000000..985a7f56 --- /dev/null +++ b/src/core/libraries/kernel/threads/exception.h @@ -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 diff --git a/src/core/libraries/kernel/threads/keys.cpp b/src/core/libraries/kernel/threads/keys.cpp deleted file mode 100644 index cf5104d2..00000000 --- a/src/core/libraries/kernel/threads/keys.cpp +++ /dev/null @@ -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(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 diff --git a/src/core/libraries/kernel/threads/mutex.cpp b/src/core/libraries/kernel/threads/mutex.cpp new file mode 100644 index 00000000..669eb3bd --- /dev/null +++ b/src/core/libraries/kernel/threads/mutex.cpp @@ -0,0 +1,468 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#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(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 diff --git a/src/core/libraries/kernel/threads/pthread.cpp b/src/core/libraries/kernel/threads/pthread.cpp new file mode 100644 index 00000000..51d436d4 --- /dev/null +++ b/src/core/libraries/kernel/threads/pthread.cpp @@ -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(thread->name.size(), 32)); + return 0; +} + +int PS4_SYSV_ABI posix_pthread_equal(PthreadT thread1, PthreadT thread2) { + return (thread1 == thread2 ? 1 : 0); +} + +PthreadT PS4_SYSV_ABI posix_pthread_self() { + return g_curthread; +} + +void PS4_SYSV_ABI posix_pthread_yield() { + std::this_thread::yield(); +} + +void PS4_SYSV_ABI sched_yield() { + std::this_thread::yield(); +} + +int PS4_SYSV_ABI posix_pthread_once(PthreadOnce* once_control, void (*init_routine)()) { + for (;;) { + auto state = once_control->state.load(); + if (state == PthreadOnceState::Done) { + return 0; + } + if (state == PthreadOnceState::NeverDone) { + if (once_control->state.compare_exchange_strong(state, PthreadOnceState::InProgress, + std::memory_order_acquire)) { + break; + } + } else if (state == PthreadOnceState::InProgress) { + if (once_control->state.compare_exchange_strong(state, PthreadOnceState::Wait, + std::memory_order_acquire)) { + once_control->state.wait(PthreadOnceState::Wait); + } + } else if (state == PthreadOnceState::Wait) { + once_control->state.wait(state); + } else { + return POSIX_EINVAL; + } + } + + const auto once_cancel_handler = [](void* arg) PS4_SYSV_ABI { + PthreadOnce* once_control = (PthreadOnce*)arg; + auto state = PthreadOnceState::InProgress; + if (once_control->state.compare_exchange_strong(state, PthreadOnceState::NeverDone, + std::memory_order_release)) { + return; + } + + once_control->state.store(PthreadOnceState::NeverDone, std::memory_order_release); + once_control->state.notify_all(); + }; + + PthreadCleanup cup{once_cancel_handler, once_control, 0}; + g_curthread->cleanup.push_front(&cup); + init_routine(); + g_curthread->cleanup.pop_front(); + + auto state = PthreadOnceState::InProgress; + if (once_control->state.compare_exchange_strong(state, PthreadOnceState::Done, + std::memory_order_release)) { + return 0; + } + once_control->state.store(PthreadOnceState::Done); + once_control->state.notify_all(); + return 0; +} + +int PS4_SYSV_ABI posix_sched_get_priority_max() { + return ORBIS_KERNEL_PRIO_FIFO_HIGHEST; +} + +int PS4_SYSV_ABI posix_sched_get_priority_min() { + return ORBIS_KERNEL_PRIO_FIFO_LOWEST; +} + +int PS4_SYSV_ABI posix_pthread_rename_np(PthreadT thread, const char* name) { + LOG_INFO(Kernel_Pthread, "name = {}", name); + thread->name = name; + return SCE_OK; +} + +int PS4_SYSV_ABI posix_pthread_getschedparam(PthreadT pthread, SchedPolicy* policy, + SchedParam* param) { + if (policy == nullptr || param == nullptr) { + return POSIX_EINVAL; + } + + if (pthread == g_curthread) { + /* + * Avoid searching the thread list when it is the current + * thread. + */ + std::scoped_lock lk{g_curthread->lock}; + *policy = g_curthread->attr.sched_policy; + param->sched_priority = g_curthread->attr.prio; + return 0; + } + auto* thread_state = ThrState::Instance(); + /* Find the thread in the list of active threads. */ + if (int ret = thread_state->RefAdd(pthread, /*include dead*/ 0); ret != 0) { + return ret; + } + pthread->lock.lock(); + *policy = pthread->attr.sched_policy; + param->sched_priority = pthread->attr.prio; + pthread->lock.unlock(); + thread_state->RefDelete(pthread); + return 0; +} + +int PS4_SYSV_ABI scePthreadGetprio(PthreadT thread, int* priority) { + SchedParam param; + SchedPolicy policy; + + posix_pthread_getschedparam(thread, &policy, ¶m); + *priority = param.sched_priority; + return 0; +} + +int PS4_SYSV_ABI scePthreadSetprio(PthreadT thread, int prio) { + SchedParam param; + int ret; + + param.sched_priority = prio; + + auto* thread_state = ThrState::Instance(); + if (thread == g_curthread) { + g_curthread->lock.lock(); + } else if (int ret = thread_state->FindThread(thread, /*include dead*/0)) { + return ret; + } + + if (thread->attr.sched_policy == SchedPolicy::Other || + thread->attr.prio == prio) { + thread->attr.prio = prio; + ret = 0; + } else { + // TODO: _thr_setscheduler + thread->attr.prio = prio; + } + + thread->lock.unlock(); + return ret; +} + +enum class PthreadCancelState : u32 { + Enable = 0, + Disable = 1, +}; + +#define POSIX_PTHREAD_CANCELED ((void*)1) + +static inline void TestCancel(Pthread* curthread) { + if (curthread->ShouldCancel() && !curthread->InCritical()) [[unlikely]] { + posix_pthread_exit(POSIX_PTHREAD_CANCELED); + } +} + +int PS4_SYSV_ABI posix_pthread_setcancelstate(PthreadCancelState state, + PthreadCancelState* oldstate) { + Pthread* curthread = g_curthread; + int oldval = curthread->cancel_enable; + switch (state) { + case PthreadCancelState::Disable: + curthread->cancel_enable = 0; + break; + case PthreadCancelState::Enable: + curthread->cancel_enable = 1; + TestCancel(curthread); + break; + default: + return POSIX_EINVAL; + } + + if (oldstate) { + *oldstate = oldval ? PthreadCancelState::Enable : PthreadCancelState::Disable; + } + return 0; +} + +void RegisterThread(Core::Loader::SymbolsResolver* sym) { + // Posix + LIB_FUNCTION("Z4QosVuAsA0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_once); + LIB_FUNCTION("7Xl257M4VNI", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_equal); + LIB_FUNCTION("CBNtXOoef-E", "libScePosix", 1, "libkernel", 1, 1, posix_sched_get_priority_max); + LIB_FUNCTION("m0iS6jNsXds", "libScePosix", 1, "libkernel", 1, 1, posix_sched_get_priority_min); + LIB_FUNCTION("EotR8a3ASf4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_self); + LIB_FUNCTION("B5GmVDKwpn0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_yield); + LIB_FUNCTION("+U1R4WtXvoc", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_detach); + LIB_FUNCTION("FJrT5LuUBAU", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_exit); + LIB_FUNCTION("h9CcP3J0oVM", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_join); + LIB_FUNCTION("OxhIB8LB-PQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create); + LIB_FUNCTION("Jmi+9w9u0E4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create_name_np); + LIB_FUNCTION("lZzFeSxPl08", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setcancelstate); + LIB_FUNCTION("6XG4B33N09g", "libScePosix", 1, "libkernel", 1, 1, sched_yield); + + // Posix-Kernel + LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self); + LIB_FUNCTION("OxhIB8LB-PQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_create); + + // Orbis + LIB_FUNCTION("14bOACANTBo", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_once)); + LIB_FUNCTION("GBUY7ywdULE", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_rename_np)); + LIB_FUNCTION("6UgtwV+0zb4", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_create_name_np)); + LIB_FUNCTION("4qGrR6eoP9Y", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_detach)); + LIB_FUNCTION("onNY9Byn-W8", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_join)); + LIB_FUNCTION("P41kTWUS3EI", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_getschedparam)); + LIB_FUNCTION("How7B8Oet6k", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_getname_np)); + LIB_FUNCTION("3kg7rT0NQIs", "libkernel", 1, "libkernel", 1, 1, posix_pthread_exit); + LIB_FUNCTION("aI+OeCz8xrQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self); + LIB_FUNCTION("3PtV6p3QNX4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_equal); + LIB_FUNCTION("T72hz6ffq08", "libkernel", 1, "libkernel", 1, 1, posix_pthread_yield); + LIB_FUNCTION("EI-5-jlq2dE", "libkernel", 1, "libkernel", 1, 1, posix_pthread_getthreadid_np); + LIB_FUNCTION("1tKyG7RlMJo", "libkernel", 1, "libkernel", 1, 1, scePthreadGetprio); + LIB_FUNCTION("W0Hpm2X0uPE", "libkernel", 1, "libkernel", 1, 1, scePthreadSetprio); + LIB_FUNCTION("rNhWz+lvOMU", "libkernel", 1, "libkernel", 1, 1, _sceKernelSetThreadDtors); + LIB_FUNCTION("6XG4B33N09g", "libkernel", 1, "libkernel", 1, 1, sched_yield); +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/pthread.h b/src/core/libraries/kernel/threads/pthread.h new file mode 100644 index 00000000..0a3ab57d --- /dev/null +++ b/src/core/libraries/kernel/threads/pthread.h @@ -0,0 +1,349 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include + +#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(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 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 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 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 diff --git a/src/core/libraries/kernel/threads/pthread_attr.cpp b/src/core/libraries/kernel/threads/pthread_attr.cpp new file mode 100644 index 00000000..db32fa3d --- /dev/null +++ b/src/core/libraries/kernel/threads/pthread_attr.cpp @@ -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 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 diff --git a/src/core/libraries/kernel/threads/pthread_clean.cpp b/src/core/libraries/kernel/threads/pthread_clean.cpp new file mode 100644 index 00000000..4ed15f7a --- /dev/null +++ b/src/core/libraries/kernel/threads/pthread_clean.cpp @@ -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 diff --git a/src/core/libraries/kernel/threads/pthread_spec.cpp b/src/core/libraries/kernel/threads/pthread_spec.cpp new file mode 100644 index 00000000..3d6b0f4d --- /dev/null +++ b/src/core/libraries/kernel/threads/pthread_spec.cpp @@ -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 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 diff --git a/src/core/libraries/kernel/threads/rwlock.cpp b/src/core/libraries/kernel/threads/rwlock.cpp index 87271fe2..2d5a4cdb 100644 --- a/src/core/libraries/kernel/threads/rwlock.cpp +++ b/src/core/libraries/kernel/threads/rwlock.cpp @@ -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(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 diff --git a/src/core/libraries/kernel/threads/semaphore.cpp b/src/core/libraries/kernel/threads/semaphore.cpp index 59099c1b..9c9c1117 100644 --- a/src/core/libraries/kernel/threads/semaphore.cpp +++ b/src/core/libraries/kernel/threads/semaphore.cpp @@ -4,23 +4,31 @@ #include #include #include -#include - -#include "common/assert.h" +#include #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 semaphore; + std::atomic value; +}; + +class OrbisSem { public: - Semaphore(s32 init_count, s32 max_count, std::string_view name, bool is_fifo) + OrbisSem(s32 init_count, s32 max_count, std::string_view name, bool is_fifo) : name{name}, token_count{init_count}, max_count{max_count}, init_count{init_count}, is_fifo{is_fifo} {} - ~Semaphore() { - ASSERT(wait_list.empty()); - } + ~OrbisSem() = default; int Wait(bool can_block, s32 need_count, u32* timeout) { std::unique_lock lk{mutex}; @@ -41,7 +49,9 @@ public: const auto it = AddWaiter(&waiter); // Perform the wait. - const s32 result = waiter.Wait(lk, timeout); + lk.unlock(); + const s32 result = waiter.Wait(timeout); + lk.lock(); if (result == SCE_KERNEL_ERROR_ETIMEDOUT) { wait_list.erase(it); } @@ -64,7 +74,7 @@ public: } it = wait_list.erase(it); token_count -= waiter->need_count; - waiter->cv.notify_one(); + waiter->sema.release(); } return true; @@ -77,30 +87,35 @@ public: } for (auto* waiter : wait_list) { waiter->was_cancled = true; - waiter->cv.notify_one(); + waiter->sema.release(); } wait_list.clear(); token_count = set_count < 0 ? init_count : set_count; return ORBIS_OK; } + void Delete() { + std::scoped_lock lk{mutex}; + for (auto* waiter : wait_list) { + waiter->was_deleted = true; + waiter->sema.release(); + } + wait_list.clear(); + } + public: struct WaitingThread { - std::condition_variable cv; + std::binary_semaphore sema; u32 priority; s32 need_count; bool was_deleted{}; bool was_cancled{}; - explicit WaitingThread(s32 need_count, bool is_fifo) : need_count{need_count} { - if (is_fifo) { - return; - } + explicit WaitingThread(s32 need_count, bool is_fifo) : sema{0}, need_count{need_count} { // Retrieve calling thread priority for sorting into waiting threads list. - s32 policy; - sched_param param; - pthread_getschedparam(pthread_self(), &policy, ¶m); - priority = param.sched_priority; + if (!is_fifo) { + priority = g_curthread->attr.prio; + } } int GetResult(bool timed_out) { @@ -116,24 +131,24 @@ public: return SCE_OK; } - int Wait(std::unique_lock& 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(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 diff --git a/src/core/libraries/kernel/threads/sleepq.cpp b/src/core/libraries/kernel/threads/sleepq.cpp new file mode 100644 index 00000000..d998141d --- /dev/null +++ b/src/core/libraries/kernel/threads/sleepq.cpp @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#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 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 \ No newline at end of file diff --git a/src/core/libraries/kernel/threads/sleepq.h b/src/core/libraries/kernel/threads/sleepq.h new file mode 100644 index 00000000..9274942e --- /dev/null +++ b/src/core/libraries/kernel/threads/sleepq.h @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include "common/types.h" + +namespace Libraries::Kernel { + +struct Pthread; + +using ListBaseHook = + boost::intrusive::list_base_hook>; + +using SleepqList = boost::intrusive::list>; + +struct SleepQueue : public ListBaseHook { + std::list 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 \ No newline at end of file diff --git a/src/core/libraries/kernel/threads/stack.cpp b/src/core/libraries/kernel/threads/stack.cpp new file mode 100644 index 00000000..45715482 --- /dev/null +++ b/src/core/libraries/kernel/threads/stack.cpp @@ -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(&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 diff --git a/src/core/libraries/kernel/threads/tcb.cpp b/src/core/libraries/kernel/threads/tcb.cpp new file mode 100644 index 00000000..e5a15821 --- /dev/null +++ b/src/core/libraries/kernel/threads/tcb.cpp @@ -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::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(addr_out); + auto* tcb = reinterpret_cast(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(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(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::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::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 diff --git a/src/core/libraries/kernel/threads/thread_state.cpp b/src/core/libraries/kernel/threads/thread_state.cpp new file mode 100644 index 00000000..e968c39a --- /dev/null +++ b/src/core/libraries/kernel/threads/thread_state.cpp @@ -0,0 +1,172 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#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 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 diff --git a/src/core/libraries/kernel/threads/thread_state.h b/src/core/libraries/kernel/threads/thread_state.h new file mode 100644 index 00000000..c98f2083 --- /dev/null +++ b/src/core/libraries/kernel/threads/thread_state.h @@ -0,0 +1,87 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#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 thread_heap; + std::set threads; + std::list free_threads; + std::list gc_list; + std::mutex free_thread_lock; + std::mutex tcb_lock; + std::mutex thread_list_lock; + std::atomic total_threads{}; + std::atomic active_threads{}; + std::stack dstackq; + std::list mstackq; + VAddr last_stack = 0; +}; + +using ThrState = Common::Singleton; + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/threads.h b/src/core/libraries/kernel/threads/threads.h deleted file mode 100644 index a3fd354b..00000000 --- a/src/core/libraries/kernel/threads/threads.h +++ /dev/null @@ -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 diff --git a/src/core/libraries/kernel/time_management.cpp b/src/core/libraries/kernel/time.cpp similarity index 88% rename from src/core/libraries/kernel/time_management.cpp rename to src/core/libraries/kernel/time.cpp index 853f8d54..76ea5e35 100644 --- a/src/core/libraries/kernel/time_management.cpp +++ b/src/core/libraries/kernel/time.cpp @@ -4,10 +4,9 @@ #include #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 +#endif #include #include #include @@ -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(); 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 diff --git a/src/core/libraries/kernel/time_management.h b/src/core/libraries/kernel/time.h similarity index 76% rename from src/core/libraries/kernel/time_management.h rename to src/core/libraries/kernel/time.h index f2216f3d..508ef215 100644 --- a/src/core/libraries/kernel/time_management.h +++ b/src/core/libraries/kernel/time.h @@ -3,8 +3,8 @@ #pragma once +#include #include - #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(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 diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index 23d75162..537862a6 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -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); diff --git a/src/core/libraries/libs.h b/src/core/libraries/libs.h index ea928101..aa5ba4a9 100644 --- a/src/core/libraries/libs.h +++ b/src/core/libraries/libs.h @@ -9,41 +9,6 @@ #include "core/loader/elf.h" #include "core/loader/symbols_resolver.h" -template -struct StringLiteral { - constexpr StringLiteral(const char (&str)[N]) { - std::copy_n(str, N, value); - } - - char value[N]; -}; - -template -struct wrapper_impl; - -template -struct wrapper_impl { - 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 || std::is_same_v) { - 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 -constexpr auto wrapper = wrapper_impl::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::wrap; sr.module_version_major = moduleVersionMajor; \ sr.module_version_minor = moduleVersionMinor; \ sr.type = Core::Loader::SymbolType::Function; \ - auto func = reinterpret_cast(W(function)); \ + auto func = reinterpret_cast(function); \ sym->AddSymbol(sr, func); \ } diff --git a/src/core/libraries/network/net_ctl_obj.cpp b/src/core/libraries/network/net_ctl_obj.cpp index 07381d67..ad944cd9 100644 --- a/src/core/libraries/network/net_ctl_obj.cpp +++ b/src/core/libraries/network/net_ctl_obj.cpp @@ -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 +#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::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::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 diff --git a/src/core/libraries/network/net_ctl_obj.h b/src/core/libraries/network/net_ctl_obj.h index 3178677f..cbbb33a4 100644 --- a/src/core/libraries/network/net_ctl_obj.h +++ b/src/core/libraries/network/net_ctl_obj.h @@ -3,9 +3,7 @@ #pragma once -#include #include - #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 nptoolCallbacks; - std::array callbacks; + std::array nptool_callbacks{}; + std::array callbacks{}; std::mutex m_mutex; }; } // namespace Libraries::NetCtl diff --git a/src/core/libraries/network/netctl.cpp b/src/core/libraries/network/netctl.cpp index d3f83c29..b167d278 100644 --- a/src/core/libraries/network/netctl.cpp +++ b/src/core/libraries/network/netctl.cpp @@ -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::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::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::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::Instance(); - s32 result = netctl->registerNpToolkitCallback(func, arg); + s32 result = netctl.RegisterNpToolkitCallback(func, arg); if (result < 0) { return result; } else { diff --git a/src/core/libraries/network/netctl.h b/src/core/libraries/network/netctl.h index 89ba34c3..4482729a 100644 --- a/src/core/libraries/network/netctl.h +++ b/src/core/libraries/network/netctl.h @@ -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; diff --git a/src/core/libraries/ngs2/ngs2_impl.cpp b/src/core/libraries/ngs2/ngs2_impl.cpp index 793435d8..b358a05f 100644 --- a/src/core/libraries/ngs2/ngs2_impl.cpp +++ b/src/core/libraries/ngs2/ngs2_impl.cpp @@ -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; diff --git a/src/core/libraries/np_manager/np_manager.cpp b/src/core/libraries/np_manager/np_manager.cpp index 4fff5900..00070ef8 100644 --- a/src/core/libraries/np_manager/np_manager.cpp +++ b/src/core/libraries/np_manager/np_manager.cpp @@ -1,13 +1,12 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include -#include #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::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; } diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index d786647c..a1897d04 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -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(); } diff --git a/src/core/libraries/rtc/rtc.cpp b/src/core/libraries/rtc/rtc.cpp index 7a46a1e3..1b880297 100644 --- a/src/core/libraries/rtc/rtc.cpp +++ b/src/core/libraries/rtc/rtc.cpp @@ -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 { diff --git a/src/core/libraries/system/sysmodule.cpp b/src/core/libraries/system/sysmodule.cpp index 596efc0a..d37c375b 100644 --- a/src/core/libraries/system/sysmodule.cpp +++ b/src/core/libraries/system/sysmodule.cpp @@ -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; } diff --git a/src/core/libraries/system/sysmodule.h b/src/core/libraries/system/sysmodule.h index d7a0c31b..c9ec97ce 100644 --- a/src/core/libraries/system/sysmodule.h +++ b/src/core/libraries/system/sysmodule.h @@ -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(); diff --git a/src/core/libraries/videoout/driver.cpp b/src/core/libraries/videoout/driver.cpp index 13d67c4e..d8013614 100644 --- a/src/core/libraries/videoout/driver.cpp +++ b/src/core/libraries/videoout/driver.cpp @@ -2,7 +2,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include -#include #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" diff --git a/src/core/libraries/videoout/video_out.h b/src/core/libraries/videoout/video_out.h index 7f8421fb..0680a849 100644 --- a/src/core/libraries/videoout/video_out.h +++ b/src/core/libraries/videoout/video_out.h @@ -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 { diff --git a/src/core/linker.cpp b/src/core/linker.cpp index 2d7865c3..9592bee0 100644 --- a/src/core/linker.cpp +++ b/src/core/linker.cpp @@ -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(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(¶ms); + }); } 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(ExecuteGuest(heap_api->heap_malloc, module->tls.image_size)); + u8* dest = reinterpret_cast(heap_api->heap_malloc(module->tls.image_size)); const u8* src = reinterpret_cast(module->tls.image_virtual_addr); std::memcpy(dest, src, init_image_size); std::memset(dest + init_image_size, 0, module->tls.image_size - init_image_size); @@ -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(addr_out); - Tcb* tcb = reinterpret_cast(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(addr + static_tls_size - module->tls.offset); - const u8* src = reinterpret_cast(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"); } } diff --git a/src/core/linker.h b/src/core/linker.h index fe1278d0..3a1aeb96 100644 --- a/src/core/linker.h +++ b/src/core/linker.h @@ -6,6 +6,7 @@ #include #include #include +#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(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 - 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 - ReturnType ExecuteGuestWithoutTls(PS4_SYSV_ABI ReturnType (*func)(FuncArgs...), - CallArgs&&... args) const { - return func(std::forward(args)...); - } MemoryManager* memory; + Libraries::Kernel::Thread main_thread; std::mutex mutex; u32 dtv_generation_counter{1}; size_t static_tls_size{}; diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 61eb421e..f638e5e1 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -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" diff --git a/src/core/memory.h b/src/core/memory.h index 286f1c97..a9a42e1c 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -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(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); diff --git a/src/core/module.cpp b/src/core/module.cpp index 5d3b4057..ef34f25c 100644 --- a/src/core/module.cpp +++ b/src/core/module.cpp @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include + #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 Salt = {0x51, 0x8D, 0x64, 0xA6, 0x35, 0xDE, 0xD8, 0xC1, + 0xE6, 0xB0, 0x39, 0xB1, 0xC3, 0xE5, 0x52, 0x30}; + std::vector 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 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::Instance(); const VAddr addr = dynamic_info.init_virtual_addr + GetBaseAddress(); - return linker->ExecuteGuest(reinterpret_cast(addr), args, argp, param); + return ExecuteGuest(reinterpret_cast(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(it->virtual_address); + } + return nullptr; +} + } // namespace Core diff --git a/src/core/module.h b/src/core/module.h index 007501f0..630c5d58 100644 --- a/src/core/module.h +++ b/src/core/module.h @@ -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(it->virtual_address); - } - return nullptr; - } - template T GetProcParam() const noexcept { return reinterpret_cast(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); diff --git a/src/core/thread.cpp b/src/core/thread.cpp new file mode 100644 index 00000000..e9c46b52 --- /dev/null +++ b/src/core/thread.cpp @@ -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 +#else +#include +#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(&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 \ No newline at end of file diff --git a/src/core/thread.h b/src/core/thread.h new file mode 100644 index 00000000..8665100a --- /dev/null +++ b/src/core/thread.h @@ -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(native_handle); + } + +private: +#if _WIN64 + void* native_handle; +#else + uintptr_t native_handle; +#endif +}; + +} // namespace Core \ No newline at end of file diff --git a/src/core/tls.cpp b/src/core/tls.cpp index eb07e7a7..9b317817 100644 --- a/src/core/tls.cpp +++ b/src/core/tls.cpp @@ -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 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(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::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(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(addr), .base16 = static_cast(addr >> 16), .base24 = static_cast(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(addr); -} + // Store the TCB base pointer and index in the created LDT area. + ldt_page = reinterpret_cast(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 diff --git a/src/core/tls.h b/src/core/tls.h index f5bf3318..4df9e4ac 100644 --- a/src/core/tls.h +++ b/src/core/tls.h @@ -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 +ReturnType ExecuteGuest(PS4_SYSV_ABI ReturnType (*func)(FuncArgs...), CallArgs&&... args) { + EnsureThreadInitialized(); + return func(std::forward(args)...); +} + } // namespace Core diff --git a/src/emulator.cpp b/src/emulator.cpp index d9d32ec1..24f5907a 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -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()) { diff --git a/src/input/controller.cpp b/src/input/controller.cpp index 2187608e..cd7d3d8a 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -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 diff --git a/src/shader_recompiler/frontend/translate/data_share.cpp b/src/shader_recompiler/frontend/translate/data_share.cpp index a453023f..adc2bbbc 100644 --- a/src/shader_recompiler/frontend/translate/data_share.cpp +++ b/src/shader_recompiler/frontend/translate/data_share.cpp @@ -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)); diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index 3c359b8d..12b5de43 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -570,7 +570,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spansrc_sel == DmaDataSrc::Gds && dma_data->dst_sel == DmaDataDst::Memory) { - LOG_WARNING(Render_Vulkan, "GDS memory read"); + // LOG_WARNING(Render_Vulkan, "GDS memory read"); } else if (dma_data->src_sel == DmaDataSrc::Memory && dma_data->dst_sel == DmaDataDst::Memory) { rasterizer->InlineData(dma_data->DstAddress(), diff --git a/src/video_core/page_manager.cpp b/src/video_core/page_manager.cpp index a49fff43..8c20ee6e 100644 --- a/src/video_core/page_manager.cpp +++ b/src/video_core/page_manager.cpp @@ -7,6 +7,7 @@ #include "common/assert.h" #include "common/error.h" #include "common/signal_context.h" +#include "core/memory.h" #include "core/signals.h" #include "video_core/page_manager.h" #include "video_core/renderer_vulkan/vk_rasterizer.h" @@ -145,15 +146,11 @@ struct PageManager::Impl { ASSERT_MSG(owned_ranges.find(address) != owned_ranges.end(), "Attempted to track non-GPU memory at address {:#x}, size {:#x}.", address, size); -#ifdef _WIN32 - DWORD prot = allow_write ? PAGE_READWRITE : PAGE_READONLY; - DWORD old_prot{}; - BOOL result = VirtualProtect(std::bit_cast(address), size, prot, &old_prot); - ASSERT_MSG(result != 0, "Region protection failed"); -#else - mprotect(reinterpret_cast(address), size, - PROT_READ | (allow_write ? PROT_WRITE : 0)); -#endif + auto* memory = Core::Memory::Instance(); + auto& impl = memory->GetAddressSpace(); + impl.Protect(address, size, + allow_write ? Core::MemoryPermission::ReadWrite + : Core::MemoryPermission::Read); } static bool GuestFaultSignalHandler(void* context, void* fault_address) { diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index b45025d5..0e8dd7cc 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -99,7 +99,7 @@ ImageId TextureCache::ResolveDepthOverlap(const ImageInfo& requested_info, Image if (cache_info.resources == requested_info.resources) { return cache_image_id; } else { - UNREACHABLE(); + // UNREACHABLE(); } } diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index ef9c0efb..96970bfc 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -36,8 +36,8 @@ static constexpr u32 MaxInvalidateDist = 12_MB; class TextureCache { struct Traits { using Entry = boost::container::small_vector; - static constexpr size_t AddressSpaceBits = 39; - static constexpr size_t FirstLevelBits = 9; + static constexpr size_t AddressSpaceBits = 40; + static constexpr size_t FirstLevelBits = 10; static constexpr size_t PageBits = 20; }; using PageTable = MultiLevelPageTable;