From 2a0629477b44c5747294353e53c74e09316a8570 Mon Sep 17 00:00:00 2001 From: Marat Idrisov Date: Thu, 21 Nov 2024 20:02:30 +0300 Subject: [PATCH 1/5] [MacOS] Enable game mode when switching to full screen mode (#1541) * GH-1515: enable game mode when switching to full screen mode * GH-1515: minor fix * GH-1515: add MacOSBundleInfo.plist.in to REUSE.toml --- .gitignore | 3 +++ CMakeLists.txt | 5 +++- REUSE.toml | 1 + dist/MacOSBundleInfo.plist.in | 46 +++++++++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 dist/MacOSBundleInfo.plist.in diff --git a/.gitignore b/.gitignore index 61d9e32e1..c331f9e70 100644 --- a/.gitignore +++ b/.gitignore @@ -414,3 +414,6 @@ FodyWeavers.xsd # for macOS **/.DS_Store + +# JetBrains +.idea diff --git a/CMakeLists.txt b/CMakeLists.txt index 47cfd8595..35fc35f23 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -921,7 +921,10 @@ if (ENABLE_QT_GUI) set_target_properties(shadps4 PROPERTIES # WIN32_EXECUTABLE ON MACOSX_BUNDLE ON - MACOSX_BUNDLE_ICON_FILE shadPS4.icns) + MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/dist/MacOSBundleInfo.plist.in" + MACOSX_BUNDLE_ICON_FILE "shadPS4.icns" + MACOSX_BUNDLE_SHORT_VERSION_STRING "0.4.1" + ) set_source_files_properties(src/images/shadPS4.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources) diff --git a/REUSE.toml b/REUSE.toml index 405156231..7b2862e53 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -7,6 +7,7 @@ path = [ ".github/FUNDING.yml", ".github/shadps4.png", ".gitmodules", + "dist/MacOSBundleInfo.plist.in", "dist/net.shadps4.shadPS4.desktop", "dist/net.shadps4.shadPS4_metadata.pot", "dist/net.shadps4.shadPS4.metainfo.xml", diff --git a/dist/MacOSBundleInfo.plist.in b/dist/MacOSBundleInfo.plist.in new file mode 100644 index 000000000..70cbfb4ab --- /dev/null +++ b/dist/MacOSBundleInfo.plist.in @@ -0,0 +1,46 @@ + + + + + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + + CFBundleName + shadps4 + CFBundleIdentifier + com.shadps4-emu.shadps4 + CFBundleExecutable + shadps4 + + CFBundleVersion + 1.0.0 + CFBundleShortVersionString + ${MACOSX_BUNDLE_SHORT_VERSION_STRING} + + LSMinimumSystemVersion + ${CMAKE_OSX_DEPLOYMENT_TARGET} + LSApplicationCategoryType + public.app-category.games + GCSupportsGameMode + + + NSHumanReadableCopyright + + + CFBundleIconFile + ${MACOSX_BUNDLE_ICON_FILE} + + CFBundleDevelopmentRegion + en + CFBundleAllowMixedLocalizations + + + NSPrincipalClass + NSApplication + + NSSupportsAutomaticGraphicsSwitching + + + From e968b1c23f53d72a099423bf9dd3be5b71e83b2e Mon Sep 17 00:00:00 2001 From: "Daniel R." <47796739+polybiusproxy@users.noreply.github.com> Date: Thu, 21 Nov 2024 19:24:13 +0100 Subject: [PATCH 2/5] video_core/amdgpu: heuristic for shader binary info Games can strip the first shader instruction (meant for debugging) which we rely on for obtaining shader information (e.g. LittleBigPlanet 3). For this reason, we start a search through the code start until we arrive at the shader binary info. --- src/shader_recompiler/recompiler.cpp | 4 +- src/video_core/amdgpu/liverpool.h | 42 ++++++++++++++----- .../renderer_vulkan/vk_pipeline_cache.cpp | 4 +- 3 files changed, 37 insertions(+), 13 deletions(-) diff --git a/src/shader_recompiler/recompiler.cpp b/src/shader_recompiler/recompiler.cpp index 19579f665..64f842c42 100644 --- a/src/shader_recompiler/recompiler.cpp +++ b/src/shader_recompiler/recompiler.cpp @@ -32,7 +32,9 @@ IR::Program TranslateProgram(std::span code, Pools& pools, Info& info const RuntimeInfo& runtime_info, const Profile& profile) { // Ensure first instruction is expected. constexpr u32 token_mov_vcchi = 0xBEEB03FF; - ASSERT_MSG(code[0] == token_mov_vcchi, "First instruction is not s_mov_b32 vcc_hi, #imm"); + if (code[0] != token_mov_vcchi) { + LOG_WARNING(Render_Recompiler, "First instruction is not s_mov_b32 vcc_hi, #imm"); + } Gcn::GcnCodeSlice slice(code.data(), code.data() + code.size()); Gcn::GcnDecodeContext decoder; diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index d94e4329a..0595a242c 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -88,6 +88,32 @@ struct Liverpool { } }; + static const BinaryInfo& SearchBinaryInfo(const u32* code, size_t search_limit = 0x1000) { + constexpr u32 token_mov_vcchi = 0xBEEB03FF; + + if (code[0] == token_mov_vcchi) { + const auto* info = std::bit_cast(code + (code[1] + 1) * 2); + if (info->Valid()) { + return *info; + } + } + + // First instruction is not s_mov_b32 vcc_hi, #imm, + // which means we cannot get the binary info via said instruction. + // The easiest solution is to iterate through each dword and break + // on the first instance of the binary info. + constexpr size_t signature_size = sizeof(BinaryInfo::signature_ref) / sizeof(u8); + const u32* end = code + search_limit; + + for (const u32* it = code; it < end; ++it) { + if (const BinaryInfo* info = std::bit_cast(it); info->Valid()) { + return *info; + } + } + + UNREACHABLE_MSG("Shader binary info not found."); + } + struct ShaderProgram { u32 address_lo; BitField<0, 8, u32> address_hi; @@ -113,8 +139,7 @@ struct Liverpool { std::span Code() const { const u32* code = Address(); - BinaryInfo bininfo; - std::memcpy(&bininfo, code + (code[1] + 1) * 2, sizeof(bininfo)); + const BinaryInfo& bininfo = SearchBinaryInfo(code); const u32 num_dwords = bininfo.length / sizeof(u32); return std::span{code, num_dwords}; } @@ -166,27 +191,24 @@ struct Liverpool { std::span Code() const { const u32* code = Address(); - BinaryInfo bininfo; - std::memcpy(&bininfo, code + (code[1] + 1) * 2, sizeof(bininfo)); + const BinaryInfo& bininfo = SearchBinaryInfo(code); const u32 num_dwords = bininfo.length / sizeof(u32); return std::span{code, num_dwords}; } }; template - static constexpr auto* GetBinaryInfo(const Shader& sh) { + static constexpr const BinaryInfo& GetBinaryInfo(const Shader& sh) { const auto* code = sh.template Address(); - const auto* bininfo = std::bit_cast(code + (code[1] + 1) * 2); - // ASSERT_MSG(bininfo->Valid(), "Invalid shader binary header"); - return bininfo; + return SearchBinaryInfo(code); } static constexpr Shader::ShaderParams GetParams(const auto& sh) { - auto* bininfo = GetBinaryInfo(sh); + auto& bininfo = GetBinaryInfo(sh); return { .user_data = sh.user_data, .code = sh.Code(), - .hash = bininfo->shader_hash, + .hash = bininfo.shader_hash, }; } diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 960415d09..b576d4780 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -292,8 +292,8 @@ bool PipelineCache::RefreshGraphicsKey() { return false; } - const auto* bininfo = Liverpool::GetBinaryInfo(*pgm); - if (!bininfo->Valid()) { + const auto& bininfo = Liverpool::GetBinaryInfo(*pgm); + if (!bininfo.Valid()) { LOG_WARNING(Render_Vulkan, "Invalid binary info structure!"); key.stage_hashes[stage_out_idx] = 0; infos[stage_out_idx] = nullptr; From 6904764aab4ef8ce8f17e8bc82a95deb1708d57d Mon Sep 17 00:00:00 2001 From: "Daniel R." <47796739+polybiusproxy@users.noreply.github.com> Date: Thu, 21 Nov 2024 19:52:48 +0100 Subject: [PATCH 3/5] shader_recompiler/frontend: implement `V_MIN3_U32` --- src/shader_recompiler/frontend/translate/translate.h | 1 + src/shader_recompiler/frontend/translate/vector_alu.cpp | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index fd5a58899..f04038909 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -221,6 +221,7 @@ public: void V_FMA_F64(const GcnInst& inst); void V_MIN3_F32(const GcnInst& inst); void V_MIN3_I32(const GcnInst& inst); + void V_MIN3_U32(const GcnInst& inst); void V_MAX3_F32(const GcnInst& inst); void V_MAX3_U32(bool is_signed, const GcnInst& inst); void V_MED3_F32(const GcnInst& inst); diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp index 7ac3131c5..eb90c256e 100644 --- a/src/shader_recompiler/frontend/translate/vector_alu.cpp +++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp @@ -347,6 +347,8 @@ void Translator::EmitVectorAlu(const GcnInst& inst) { return V_MIN3_F32(inst); case Opcode::V_MIN3_I32: return V_MIN3_I32(inst); + case Opcode::V_MIN3_U32: + return V_MIN3_U32(inst); case Opcode::V_MAX3_F32: return V_MAX3_F32(inst); case Opcode::V_MAX3_I32: @@ -1064,6 +1066,13 @@ void Translator::V_MIN3_I32(const GcnInst& inst) { SetDst(inst.dst[0], ir.SMin(src0, ir.SMin(src1, src2))); } +void Translator::V_MIN3_U32(const GcnInst& inst) { + const IR::U32 src0{GetSrc(inst.src[0])}; + const IR::U32 src1{GetSrc(inst.src[1])}; + const IR::U32 src2{GetSrc(inst.src[2])}; + SetDst(inst.dst[0], ir.UMin(src0, ir.UMin(src1, src2))); +} + void Translator::V_MAX3_F32(const GcnInst& inst) { const IR::F32 src0{GetSrc(inst.src[0])}; const IR::F32 src1{GetSrc(inst.src[1])}; From c4506da0ae0fbd218e5ec3d2dc0d94a2ea788ec2 Mon Sep 17 00:00:00 2001 From: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> Date: Thu, 21 Nov 2024 22:59:38 +0200 Subject: [PATCH 4/5] kernel: Rewrite pthread emulation (#1440) * libkernel: Cleanup some function places * kernel: Refactor thread functions * kernel: It builds * kernel: Fix a bunch of bugs, kernel thread heap * kernel: File cleanup pt1 * File cleanup pt2 * File cleanup pt3 * File cleanup pt4 * kernel: Add missing funcs * kernel: Add basic exceptions for linux * gnmdriver: Add workload functions * kernel: Fix new pthreads code on macOS. (#1441) * kernel: Downgrade edeadlk to log * gnmdriver: Add sceGnmSubmitCommandBuffersForWorkload * exception: Add context register population for macOS. (#1444) * kernel: Pthread rewrite touchups for Windows * kernel: Multiplatform thread implementation * mutex: Remove spamming log * pthread_spec: Make assert into a log * pthread_spec: Zero initialize array * Attempt to fix non-Windows builds * hotfix: change incorrect NID for scePthreadAttrSetaffinity * scePthreadAttrSetaffinity implementation * Attempt to fix Linux * windows: Address a bunch of address space problems * address_space: Fix unmap of region surrounded by placeholders * libs: Reduce logging * pthread: Implement condvar with waitable atomics and sleepqueue * sleepq: Separate and make faster * time: Remove delay execution * Causes high cpu usage in Tohou Luna Nights * kernel: Cleanup files again * pthread: Add missing include * semaphore: Use binary_semaphore instead of condvar * Seems more reliable * libraries/sysmodule: log module on `sceSysmoduleIsLoaded` * libraries/kernel: implement `scePthreadSetPrio` --------- Co-authored-by: squidbus <175574877+squidbus@users.noreply.github.com> Co-authored-by: Daniel R. <47796739+polybiusproxy@users.noreply.github.com> --- CMakeLists.txt | 53 +- src/common/debug.h | 2 +- src/common/slab_heap.h | 163 ++ src/common/slot_vector.h | 3 - src/common/spin_lock.cpp | 53 + src/common/spin_lock.h | 33 + src/core/address_space.cpp | 208 +- src/core/debug_state.cpp | 2 +- src/core/file_sys/file.cpp | 114 ++ src/core/libraries/audio3d/audio3d.cpp | 8 +- src/core/libraries/audio3d/audio3d_impl.cpp | 2 +- src/core/libraries/avplayer/avplayer.cpp | 10 +- src/core/libraries/avplayer/avplayer.h | 4 +- .../libraries/avplayer/avplayer_common.cpp | 18 +- src/core/libraries/avplayer/avplayer_common.h | 10 +- .../avplayer/avplayer_file_streamer.cpp | 10 +- .../avplayer/avplayer_file_streamer.h | 6 +- src/core/libraries/avplayer/avplayer_impl.cpp | 37 +- src/core/libraries/avplayer/avplayer_impl.h | 8 +- .../libraries/avplayer/avplayer_source.cpp | 46 +- src/core/libraries/avplayer/avplayer_source.h | 24 +- .../libraries/avplayer/avplayer_state.cpp | 24 +- src/core/libraries/avplayer/avplayer_state.h | 13 +- src/core/libraries/fiber/fiber.cpp | 9 +- src/core/libraries/gnmdriver/gnmdriver.cpp | 58 +- src/core/libraries/gnmdriver/gnmdriver.h | 18 +- src/core/libraries/ime/ime.cpp | 12 +- src/core/libraries/ime/ime_dialog_ui.cpp | 13 +- src/core/libraries/kernel/cpu_management.cpp | 15 - .../kernel/{event_queues.cpp => equeue.cpp} | 153 +- .../kernel/{event_queue.h => equeue.h} | 21 +- .../libraries/kernel/event_flag/event_flag.h | 40 - .../kernel/event_flag/event_flag_codes.h | 14 - .../kernel/event_flag/event_flag_obj.cpp | 111 -- .../kernel/event_flag/event_flag_obj.h | 44 - src/core/libraries/kernel/event_queue.cpp | 150 -- src/core/libraries/kernel/event_queues.h | 26 - src/core/libraries/kernel/file_system.cpp | 6 +- src/core/libraries/kernel/file_system.h | 9 +- src/core/libraries/kernel/kernel.cpp | 250 +++ src/core/libraries/kernel/kernel.h | 56 + src/core/libraries/kernel/libkernel.cpp | 509 ----- src/core/libraries/kernel/libkernel.h | 42 - .../{memory_management.cpp => memory.cpp} | 124 +- .../kernel/{memory_management.h => memory.h} | 12 + src/core/libraries/kernel/process.cpp | 140 ++ .../kernel/{cpu_management.h => process.h} | 8 + .../libraries/kernel/thread_management.cpp | 1716 ----------------- src/core/libraries/kernel/thread_management.h | 225 --- src/core/libraries/kernel/threads.cpp | 22 + src/core/libraries/kernel/threads.h | 72 + src/core/libraries/kernel/threads/condvar.cpp | 360 ++++ .../{event_flag => threads}/event_flag.cpp | 171 +- .../libraries/kernel/threads/exception.cpp | 131 ++ src/core/libraries/kernel/threads/exception.h | 86 + src/core/libraries/kernel/threads/keys.cpp | 50 - src/core/libraries/kernel/threads/mutex.cpp | 468 +++++ src/core/libraries/kernel/threads/pthread.cpp | 526 +++++ src/core/libraries/kernel/threads/pthread.h | 349 ++++ .../libraries/kernel/threads/pthread_attr.cpp | 345 ++++ .../kernel/threads/pthread_clean.cpp | 54 + .../libraries/kernel/threads/pthread_spec.cpp | 158 ++ src/core/libraries/kernel/threads/rwlock.cpp | 543 +++--- .../libraries/kernel/threads/semaphore.cpp | 165 +- src/core/libraries/kernel/threads/sleepq.cpp | 101 + src/core/libraries/kernel/threads/sleepq.h | 38 + src/core/libraries/kernel/threads/stack.cpp | 141 ++ src/core/libraries/kernel/threads/tcb.cpp | 96 + .../libraries/kernel/threads/thread_state.cpp | 172 ++ .../libraries/kernel/threads/thread_state.h | 87 + src/core/libraries/kernel/threads/threads.h | 20 - .../kernel/{time_management.cpp => time.cpp} | 44 +- .../kernel/{time_management.h => time.h} | 21 +- src/core/libraries/libs.cpp | 6 +- src/core/libraries/libs.h | 37 +- src/core/libraries/network/net_ctl_obj.cpp | 81 +- src/core/libraries/network/net_ctl_obj.h | 17 +- src/core/libraries/network/netctl.cpp | 15 +- src/core/libraries/network/netctl.h | 2 +- src/core/libraries/ngs2/ngs2_impl.cpp | 2 +- src/core/libraries/np_manager/np_manager.cpp | 10 +- src/core/libraries/pad/pad.cpp | 1 + src/core/libraries/rtc/rtc.cpp | 8 +- src/core/libraries/system/sysmodule.cpp | 4 +- src/core/libraries/system/sysmodule.h | 2 +- src/core/libraries/videoout/driver.cpp | 3 +- src/core/libraries/videoout/video_out.h | 2 +- src/core/linker.cpp | 170 +- src/core/linker.h | 45 +- src/core/memory.cpp | 2 +- src/core/memory.h | 13 +- src/core/module.cpp | 46 +- src/core/module.h | 11 +- src/core/thread.cpp | 46 + src/core/thread.h | 33 + src/core/tls.cpp | 78 +- src/core/tls.h | 11 +- src/emulator.cpp | 6 +- src/input/controller.cpp | 2 +- .../frontend/translate/data_share.cpp | 5 +- src/video_core/amdgpu/liverpool.cpp | 2 +- src/video_core/page_manager.cpp | 15 +- .../texture_cache/texture_cache.cpp | 2 +- src/video_core/texture_cache/texture_cache.h | 4 +- 104 files changed, 5554 insertions(+), 3979 deletions(-) create mode 100644 src/common/slab_heap.h create mode 100755 src/common/spin_lock.cpp create mode 100755 src/common/spin_lock.h create mode 100644 src/core/file_sys/file.cpp delete mode 100644 src/core/libraries/kernel/cpu_management.cpp rename src/core/libraries/kernel/{event_queues.cpp => equeue.cpp} (56%) rename src/core/libraries/kernel/{event_queue.h => equeue.h} (89%) delete mode 100644 src/core/libraries/kernel/event_flag/event_flag.h delete mode 100644 src/core/libraries/kernel/event_flag/event_flag_codes.h delete mode 100644 src/core/libraries/kernel/event_flag/event_flag_obj.cpp delete mode 100644 src/core/libraries/kernel/event_flag/event_flag_obj.h delete mode 100644 src/core/libraries/kernel/event_queue.cpp delete mode 100644 src/core/libraries/kernel/event_queues.h create mode 100644 src/core/libraries/kernel/kernel.cpp create mode 100644 src/core/libraries/kernel/kernel.h delete mode 100644 src/core/libraries/kernel/libkernel.cpp delete mode 100644 src/core/libraries/kernel/libkernel.h rename src/core/libraries/kernel/{memory_management.cpp => memory.cpp} (75%) rename src/core/libraries/kernel/{memory_management.h => memory.h} (96%) create mode 100644 src/core/libraries/kernel/process.cpp rename src/core/libraries/kernel/{cpu_management.h => process.h} (60%) delete mode 100644 src/core/libraries/kernel/thread_management.cpp delete mode 100644 src/core/libraries/kernel/thread_management.h create mode 100644 src/core/libraries/kernel/threads.cpp create mode 100644 src/core/libraries/kernel/threads.h create mode 100644 src/core/libraries/kernel/threads/condvar.cpp rename src/core/libraries/kernel/{event_flag => threads}/event_flag.cpp (56%) create mode 100644 src/core/libraries/kernel/threads/exception.cpp create mode 100644 src/core/libraries/kernel/threads/exception.h delete mode 100644 src/core/libraries/kernel/threads/keys.cpp create mode 100644 src/core/libraries/kernel/threads/mutex.cpp create mode 100644 src/core/libraries/kernel/threads/pthread.cpp create mode 100644 src/core/libraries/kernel/threads/pthread.h create mode 100644 src/core/libraries/kernel/threads/pthread_attr.cpp create mode 100644 src/core/libraries/kernel/threads/pthread_clean.cpp create mode 100644 src/core/libraries/kernel/threads/pthread_spec.cpp create mode 100644 src/core/libraries/kernel/threads/sleepq.cpp create mode 100644 src/core/libraries/kernel/threads/sleepq.h create mode 100644 src/core/libraries/kernel/threads/stack.cpp create mode 100644 src/core/libraries/kernel/threads/tcb.cpp create mode 100644 src/core/libraries/kernel/threads/thread_state.cpp create mode 100644 src/core/libraries/kernel/threads/thread_state.h delete mode 100644 src/core/libraries/kernel/threads/threads.h rename src/core/libraries/kernel/{time_management.cpp => time.cpp} (88%) rename src/core/libraries/kernel/{time_management.h => time.h} (76%) create mode 100644 src/core/thread.cpp create mode 100644 src/core/thread.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 35fc35f23..e135794ec 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 596ad7b84..091c6191d 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 000000000..7648ebea3 --- /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 36e647971..d4ac51361 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 000000000..9d4cfe36b --- /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 000000000..3229a8c6a --- /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 8ba99e32d..24f5e9f87 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 adcb0cadb..1dc4297c3 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 000000000..be6bc76bb --- /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 63815a068..2978445e8 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 c267c096f..3069e8800 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 60d68c4f7..1257473e1 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 98e932070..9f100bef3 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 306603e29..805920a3d 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 a53696ecf..dc3cd787f 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 c7bd5b5de..3323ee9b6 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 034e40dd4..bc096bccc 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 1c414c961..0f39acfdc 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 d7f28094e..984d81499 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 19925ba0c..8a65377c2 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 505d74465..7e199c457 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 e66100679..3d9840a1b 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 d106127e4..8a0d7f4f8 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 a0bfd6850..92bf894b5 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 946622cef..9aede3304 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 115268ea8..5307b3baa 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 0310c5153..efb988c72 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 bba45d0fe..de64de5fa 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 3bf609dfe..000000000 --- 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 540c20c43..d4f7eabaa 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 30fdb41e3..5a13bdecd 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 2147e3f15..000000000 --- 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 92b265c8d..000000000 --- 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 6d6dcf7a9..000000000 --- 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 0454b4d78..000000000 --- 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 88918bf54..000000000 --- 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 d400ff187..000000000 --- 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 e6b657c3a..56286eb98 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 1174dd86d..0bc473ec0 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 000000000..512e77bc6 --- /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 000000000..f6865d22f --- /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 b3eb81ec8..000000000 --- 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 73705cdc2..000000000 --- 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 5331f47f2..13b3a9f48 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 6e90204cf..2d19ceb49 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 000000000..fc1d6e137 --- /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 814ea51df..0340a9793 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 85f618090..000000000 --- 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 3cdb300f7..000000000 --- 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 000000000..082a52b67 --- /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 000000000..ad1393599 --- /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 000000000..5831df9be --- /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 f83ae0a7f..134bd5491 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 000000000..b6d89aae4 --- /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 000000000..985a7f56b --- /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 cf5104d21..000000000 --- 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 000000000..669eb3bda --- /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 000000000..51d436d44 --- /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 000000000..0a3ab57dd --- /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 000000000..db32fa3d5 --- /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 000000000..4ed15f7a3 --- /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 000000000..3d6b0f4d9 --- /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 87271fe21..2d5a4cdb6 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 59099c1b8..9c9c11178 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 000000000..d998141d0 --- /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 000000000..9274942e3 --- /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 000000000..45715482a --- /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 000000000..e5a158216 --- /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 000000000..e968c39ae --- /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 000000000..c98f2083e --- /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 a3fd354b0..000000000 --- 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 853f8d54c..76ea5e353 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 f2216f3d3..508ef2152 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 23d751622..537862a6c 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 ea928101e..aa5ba4a97 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 07381d676..ad944cd9c 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 3178677f4..cbbb33a4e 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 d3f83c290..b167d2789 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 89ba34c31..4482729a3 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 793435d83..b358a05f7 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 4fff59003..00070ef89 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 d786647c2..a1897d047 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 7a46a1e31..1b8802970 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 596efc0af..d37c375bf 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 d7a0c31b1..c9ec97ce9 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 13d67c4e7..d8013614c 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 7f8421fba..0680a8491 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 2d7865c33..9592bee0f 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 fe1278d00..3a1aeb960 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 61eb421e5..f638e5e1a 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 286f1c979..a9a42e1c2 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 5d3b40577..ef34f25c1 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 007501f08..630c5d583 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 000000000..e9c46b522 --- /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 000000000..8665100af --- /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 eb07e7a72..9b3178171 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 f5bf33184..4df9e4ace 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 d9d32ec1d..24f5907a1 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 2187608e5..cd7d3d8af 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 a453023fc..adc2bbbc2 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 3c359b8df..12b5de436 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 a49fff43a..8c20ee6ed 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 b45025d53..0e8dd7ccc 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 ef9c0efb7..96970bfc8 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; From f9ae945a5575324c4875b2d635a73d2038e29b07 Mon Sep 17 00:00:00 2001 From: psucien Date: Thu, 21 Nov 2024 22:23:09 +0100 Subject: [PATCH 5/5] hot-fix: clang-format --- src/core/libraries/audio3d/audio3d.cpp | 2 +- src/core/libraries/kernel/threads/event_flag.cpp | 2 +- src/core/libraries/kernel/threads/mutex.cpp | 2 +- src/core/libraries/kernel/threads/pthread.cpp | 5 ++--- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/core/libraries/audio3d/audio3d.cpp b/src/core/libraries/audio3d/audio3d.cpp index 2978445e8..8aeb88da8 100644 --- a/src/core/libraries/audio3d/audio3d.cpp +++ b/src/core/libraries/audio3d/audio3d.cpp @@ -111,7 +111,7 @@ int PS4_SYSV_ABI sceAudio3dBedWrite(OrbisAudio3dPortId uiPortId, unsigned int ui OrbisAudio3dFormat eFormat, const void* pBuffer, unsigned int uiNumSamples) { LOG_TRACE(Lib_Audio3d, "uiPortId = {}, uiNumChannels = {}, uiNumSamples = {}", uiPortId, - uiNumChannels, uiNumSamples); + uiNumChannels, uiNumSamples); return ORBIS_OK; } diff --git a/src/core/libraries/kernel/threads/event_flag.cpp b/src/core/libraries/kernel/threads/event_flag.cpp index 134bd5491..b0f08b91c 100644 --- a/src/core/libraries/kernel/threads/event_flag.cpp +++ b/src/core/libraries/kernel/threads/event_flag.cpp @@ -31,7 +31,7 @@ public: 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){}; + : 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}; diff --git a/src/core/libraries/kernel/threads/mutex.cpp b/src/core/libraries/kernel/threads/mutex.cpp index 669eb3bda..2e3e689fa 100644 --- a/src/core/libraries/kernel/threads/mutex.cpp +++ b/src/core/libraries/kernel/threads/mutex.cpp @@ -216,7 +216,7 @@ int PthreadMutex::Lock(const OrbisKernelTimespec* abstime, u64 usec) { } } } - + int ret = 0; if (abstime == nullptr) { m_lock.lock(); diff --git a/src/core/libraries/kernel/threads/pthread.cpp b/src/core/libraries/kernel/threads/pthread.cpp index 51d436d44..28e2be06e 100644 --- a/src/core/libraries/kernel/threads/pthread.cpp +++ b/src/core/libraries/kernel/threads/pthread.cpp @@ -430,12 +430,11 @@ int PS4_SYSV_ABI scePthreadSetprio(PthreadT thread, int 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)) { + } else if (int ret = thread_state->FindThread(thread, /*include dead*/ 0)) { return ret; } - if (thread->attr.sched_policy == SchedPolicy::Other || - thread->attr.prio == prio) { + if (thread->attr.sched_policy == SchedPolicy::Other || thread->attr.prio == prio) { thread->attr.prio = prio; ret = 0; } else {