From 632ed99ee77de8e1c7513eed0bd2319fffca9ff8 Mon Sep 17 00:00:00 2001 From: MajorP93 Date: Sat, 26 Apr 2025 00:06:51 +0200 Subject: [PATCH 1/4] ci: Bump Clang to 19 for Linux builds, align LLVM repository with runner version (#2844) * ci: Bump Clang to 19 for Linux builds * PR #2434 was intended to bump Clang to 19. In reality it only made sure that clang-format-19 is being used and that the shadPS4 codebase can be compiled with Clang 19. This PR makes sure that Clang 19 is actually being used for Linux builds which makes sense since we use Clang 19 for Windows builds already (Since Visual Studio 17.13 Clang 19 is being shipped). * ci: Use noble variant of LLVM repository * shadPS4 has been using Ubuntu 24.04 runners for some time now. This commit makes sure the correct LLVM repository is being used. --- .github/workflows/build.yml | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 55549ab4c..787aba251 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,7 +30,7 @@ jobs: - name: Install run: | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - - sudo add-apt-repository 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-19 main' + sudo add-apt-repository 'deb http://apt.llvm.org/noble/ llvm-toolchain-noble-19 main' sudo apt update sudo apt install clang-format-19 - name: Build @@ -281,8 +281,13 @@ jobs: with: submodules: recursive + - name: Add LLVM repository + run: | + wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - + sudo add-apt-repository 'deb http://apt.llvm.org/noble/ llvm-toolchain-noble-19 main' + - name: Install dependencies - run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev + run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang-19 build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev - name: Cache CMake Configuration uses: actions/cache@v4 @@ -304,7 +309,7 @@ jobs: key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }} - name: Configure CMake - run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang-19 -DCMAKE_CXX_COMPILER=clang++-19 -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc) @@ -337,8 +342,13 @@ jobs: with: submodules: recursive + - name: Add LLVM repository + run: | + wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - + sudo add-apt-repository 'deb http://apt.llvm.org/noble/ llvm-toolchain-noble-19 main' + - name: Install dependencies - run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev libudev-dev + run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang-19 build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev libudev-dev - name: Cache CMake Configuration uses: actions/cache@v4 @@ -360,7 +370,7 @@ jobs: key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }} - name: Configure CMake - run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang-19 -DCMAKE_CXX_COMPILER=clang++-19 -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc) From e816bc4b99ec1859467f7e69fce68b46009d821b Mon Sep 17 00:00:00 2001 From: baggins183 Date: Fri, 25 Apr 2025 19:44:03 -0700 Subject: [PATCH 2/4] Use GetSrc in VALU insts instead of assuming vector reg (was vcc_lo) (#2845) * Use GetSrc in v_add_i32 instead of assuming vector reg (was vcc_lo) * some other cases --- src/shader_recompiler/frontend/translate/vector_alu.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp index 787cf6ad3..3ce86c131 100644 --- a/src/shader_recompiler/frontend/translate/vector_alu.cpp +++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp @@ -513,13 +513,13 @@ void Translator::V_LSHLREV_B32(const GcnInst& inst) { void Translator::V_AND_B32(const GcnInst& inst) { const IR::U32 src0{GetSrc(inst.src[0])}; - const IR::U32 src1{ir.GetVectorReg(IR::VectorReg(inst.src[1].code))}; + const IR::U32 src1{GetSrc(inst.src[1])}; SetDst(inst.dst[0], ir.BitwiseAnd(src0, src1)); } void Translator::V_OR_B32(bool is_xor, const GcnInst& inst) { const IR::U32 src0{GetSrc(inst.src[0])}; - const IR::U32 src1{ir.GetVectorReg(IR::VectorReg(inst.src[1].code))}; + const IR::U32 src1{GetSrc(inst.src[1])}; SetDst(inst.dst[0], is_xor ? ir.BitwiseXor(src0, src1) : IR::U32(ir.BitwiseOr(src0, src1))); } @@ -579,7 +579,7 @@ void Translator::V_MBCNT_U32_B32(bool is_low, const GcnInst& inst) { void Translator::V_ADD_I32(const GcnInst& inst) { // Signed or unsigned components const IR::U32 src0{GetSrc(inst.src[0])}; - const IR::U32 src1{ir.GetVectorReg(IR::VectorReg(inst.src[1].code))}; + const IR::U32 src1{GetSrc(inst.src[1])}; const IR::U32 result{ir.IAdd(src0, src1)}; SetDst(inst.dst[0], result); From c09fff2da6f4a879711d915f51797eebe315a60a Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Sun, 27 Apr 2025 01:04:17 -0500 Subject: [PATCH 3/4] VideoOut event cleanup (#2849) * Readable VideoOutEvent data packing Inspired by the work of former shadPS4 devs and mostly based on red_prig's current code. * Apply DceData struct to sceVideoOutGetEventCount Makes the code easier to read * Update equeue.h * Update main.cpp * Update equeue.h * Proper struct names * Fix hint mask Thanks to red_prig for catching my mistake here. * Clang * Fix header discrepancy --- src/core/libraries/kernel/equeue.h | 33 +++++++++++++++-------- src/core/libraries/videoout/video_out.cpp | 5 ++-- src/core/libraries/videoout/video_out.h | 10 +++++-- src/main.cpp | 2 +- 4 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/core/libraries/kernel/equeue.h b/src/core/libraries/kernel/equeue.h index 11c09bb37..2bd7ef510 100644 --- a/src/core/libraries/kernel/equeue.h +++ b/src/core/libraries/kernel/equeue.h @@ -61,6 +61,18 @@ struct SceKernelEvent { void* udata = nullptr; /* opaque user data identifier */ }; +struct OrbisVideoOutEventHint { + u64 event_id : 8; + u64 video_id : 8; + u64 flip_arg : 48; +}; + +struct OrbisVideoOutEventData { + u64 time : 12; + u64 count : 4; + u64 flip_arg : 48; +}; + struct EqueueEvent { SceKernelEvent event; void* data = nullptr; @@ -84,19 +96,18 @@ struct EqueueEvent { void TriggerDisplay(void* data) { is_triggered = true; - auto hint = reinterpret_cast(data); - if (hint != 0) { - auto hint_h = static_cast(hint >> 8) & 0xFFFFFF; - auto ident_h = static_cast(event.ident >> 40); - if ((static_cast(hint) & 0xFF) == event.ident && event.ident != 0xFE && - ((hint_h ^ ident_h) & 0xFF) == 0) { + if (data != nullptr) { + auto event_data = static_cast(event.data); + auto event_hint_raw = reinterpret_cast(data); + auto event_hint = static_cast(event_hint_raw); + if (event_hint.event_id == event.ident && event.ident != 0xfe) { auto time = Common::FencedRDTSC(); - auto mask = 0xF000; - if ((static_cast(event.data) & 0xF000) != 0xF000) { - mask = (static_cast(event.data) + 0x1000) & 0xF000; + auto counter = event_data.count; + if (counter != 0xf) { + counter++; } - event.data = (mask | static_cast(static_cast(time) & 0xFFF) | - (hint & 0xFFFFFFFFFFFF0000)); + event.data = + (time & 0xfff) | (counter << 0xc) | (event_hint_raw & 0xffffffffffff0000); } } } diff --git a/src/core/libraries/videoout/video_out.cpp b/src/core/libraries/videoout/video_out.cpp index 3c839dadd..c5208b6dd 100644 --- a/src/core/libraries/videoout/video_out.cpp +++ b/src/core/libraries/videoout/video_out.cpp @@ -220,7 +220,7 @@ s32 PS4_SYSV_ABI sceVideoOutGetEventData(const Kernel::SceKernelEvent* ev, s64* if (ev->ident != static_cast(OrbisVideoOutInternalEventId::Flip) || ev->data == 0) { *data = event_data; } else { - *data = event_data | 0xFFFF000000000000; + *data = event_data | 0xffff000000000000; } return ORBIS_OK; } @@ -233,7 +233,8 @@ s32 PS4_SYSV_ABI sceVideoOutGetEventCount(const Kernel::SceKernelEvent* ev) { return ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT; } - return (ev->data >> 0xc) & 0xf; + auto event_data = static_cast(ev->data); + return event_data.count; } s32 PS4_SYSV_ABI sceVideoOutGetFlipStatus(s32 handle, FlipStatus* status) { diff --git a/src/core/libraries/videoout/video_out.h b/src/core/libraries/videoout/video_out.h index f3e661de4..7db09530b 100644 --- a/src/core/libraries/videoout/video_out.h +++ b/src/core/libraries/videoout/video_out.h @@ -111,6 +111,12 @@ struct SceVideoOutColorSettings { u32 reserved[3]; }; +struct OrbisVideoOutEventData { + u64 time : 12; + u64 count : 4; + u64 flip_arg : 48; +}; + void PS4_SYSV_ABI sceVideoOutSetBufferAttribute(BufferAttribute* attribute, PixelFormat pixelFormat, u32 tilingMode, u32 aspectRatio, u32 width, u32 height, u32 pitchInPixel); @@ -128,8 +134,8 @@ s32 PS4_SYSV_ABI sceVideoOutGetResolutionStatus(s32 handle, SceVideoOutResolutio s32 PS4_SYSV_ABI sceVideoOutOpen(SceUserServiceUserId userId, s32 busType, s32 index, const void* param); s32 PS4_SYSV_ABI sceVideoOutClose(s32 handle); -int PS4_SYSV_ABI sceVideoOutGetEventId(const Kernel::SceKernelEvent* ev); -int PS4_SYSV_ABI sceVideoOutGetEventData(const Kernel::SceKernelEvent* ev, int64_t* data); +s32 PS4_SYSV_ABI sceVideoOutGetEventId(const Kernel::SceKernelEvent* ev); +s32 PS4_SYSV_ABI sceVideoOutGetEventData(const Kernel::SceKernelEvent* ev, s64* data); s32 PS4_SYSV_ABI sceVideoOutColorSettingsSetGamma(SceVideoOutColorSettings* settings, float gamma); s32 PS4_SYSV_ABI sceVideoOutAdjustColor(s32 handle, const SceVideoOutColorSettings* settings); diff --git a/src/main.cpp b/src/main.cpp index 6b334e446..85581774b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -154,7 +154,7 @@ int main(int argc, char* argv[]) { // If no game directory is set and no command line argument, prompt for it if (Config::getGameInstallDirs().empty()) { std::cout << "Warning: No game folder set, please set it by calling shadps4" - " with the --add-game-folder argument"; + " with the --add-game-folder argument\n"; } if (!has_game_argument) { From 410313ca87840de8b8bd06a18e74863863b60db6 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Sun, 27 Apr 2025 01:32:01 -0500 Subject: [PATCH 4/4] Implement sceKernelGetModuleInfo, sceKernelGetModuleInfoInternal, and sceKernelGetModuleList (#2850) * Fix GetModule exception Simple mistake * Prevent OOB writes in add_segment Due to mistakes in our linker logic, OpenOrbis' libSceFios2 causes OOB writes here. While the ideal solution would be to fix the erroneous behavior, the best I'm capable of right now is just preventing the OOB writes. * Implement sceKernelGetModuleInfo, sceKernelGetModuleInfoInternal, sceKernelGetModuleList These are implemented based on hardware observations and a homebrew sample made by red_prig. I've yet to test what error cases can show up. * Clang * Accurate error returns If there are more modules than provided space, then return kernel ENOMEM. If either handles or out_count are null, return kernel EFAULT. * Accurate error checks in ModuleInfo functions * Clang --- src/core/libraries/kernel/process.cpp | 59 +++++++++++++++++++++++++++ src/core/linker.h | 2 +- src/core/module.cpp | 12 ++++-- 3 files changed, 68 insertions(+), 5 deletions(-) diff --git a/src/core/libraries/kernel/process.cpp b/src/core/libraries/kernel/process.cpp index 02f8a538d..8a37e78d5 100644 --- a/src/core/libraries/kernel/process.cpp +++ b/src/core/libraries/kernel/process.cpp @@ -127,6 +127,62 @@ int PS4_SYSV_ABI sceKernelGetModuleInfoFromAddr(VAddr addr, int flags, return ORBIS_OK; } +s32 PS4_SYSV_ABI sceKernelGetModuleInfo(s32 handle, Core::OrbisKernelModuleInfo* info) { + if (info == nullptr) { + return ORBIS_KERNEL_ERROR_EFAULT; + } + if (info->st_size != sizeof(Core::OrbisKernelModuleInfo)) { + return ORBIS_KERNEL_ERROR_EINVAL; + } + + auto* linker = Common::Singleton::Instance(); + auto* module = linker->GetModule(handle); + if (module == nullptr) { + return ORBIS_KERNEL_ERROR_ESRCH; + } + *info = module->GetModuleInfo(); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceKernelGetModuleInfoInternal(s32 handle, Core::OrbisKernelModuleInfoEx* info) { + if (info == nullptr) { + return ORBIS_KERNEL_ERROR_EFAULT; + } + if (info->st_size != sizeof(Core::OrbisKernelModuleInfoEx)) { + return ORBIS_KERNEL_ERROR_EINVAL; + } + + auto* linker = Common::Singleton::Instance(); + auto* module = linker->GetModule(handle); + if (module == nullptr) { + return ORBIS_KERNEL_ERROR_ESRCH; + } + *info = module->GetModuleInfoEx(); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceKernelGetModuleList(s32* handles, u64 num_array, u64* out_count) { + if (handles == nullptr || out_count == nullptr) { + return ORBIS_KERNEL_ERROR_EFAULT; + } + + auto* linker = Common::Singleton::Instance(); + u64 count = 0; + auto* module = linker->GetModule(count); + while (module != nullptr && count < num_array) { + handles[count] = count; + count++; + module = linker->GetModule(count); + } + + if (count == num_array && module != nullptr) { + return ORBIS_KERNEL_ERROR_ENOMEM; + } + + *out_count = count; + return ORBIS_OK; +} + s32 PS4_SYSV_ABI exit(s32 status) { UNREACHABLE_MSG("Exiting with status code {}", status); return 0; @@ -141,6 +197,9 @@ void RegisterProcess(Core::Loader::SymbolsResolver* sym) { 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("kUpgrXIrz7Q", "libkernel", 1, "libkernel", 1, 1, sceKernelGetModuleInfo); + LIB_FUNCTION("HZO7xOos4xc", "libkernel", 1, "libkernel", 1, 1, sceKernelGetModuleInfoInternal); + LIB_FUNCTION("IuxnUuXk6Bg", "libkernel", 1, "libkernel", 1, 1, sceKernelGetModuleList); LIB_FUNCTION("6Z83sYWFlA8", "libkernel", 1, "libkernel", 1, 1, exit); } diff --git a/src/core/linker.h b/src/core/linker.h index 63dfc37e8..028e18ead 100644 --- a/src/core/linker.h +++ b/src/core/linker.h @@ -83,7 +83,7 @@ public: } Module* GetModule(s32 index) const { - if (index >= 0 || index < m_modules.size()) { + if (index >= 0 && index < m_modules.size()) { return m_modules.at(index).get(); } return nullptr; diff --git a/src/core/module.cpp b/src/core/module.cpp index 1004f4404..cbe44457c 100644 --- a/src/core/module.cpp +++ b/src/core/module.cpp @@ -135,10 +135,14 @@ void Module::LoadModuleToMemory(u32& max_tls_index) { if (do_map) { elf.LoadSegment(segment_addr, phdr.p_offset, phdr.p_filesz); } - auto& segment = info.segments[info.num_segments++]; - segment.address = segment_addr; - segment.prot = phdr.p_flags; - segment.size = GetAlignedSize(phdr); + if (info.num_segments < 4) { + auto& segment = info.segments[info.num_segments++]; + segment.address = segment_addr; + segment.prot = phdr.p_flags; + segment.size = GetAlignedSize(phdr); + } else { + LOG_ERROR(Core_Linker, "Attempting to add too many segments!"); + } }; for (u16 i = 0; i < elf_header.e_phnum; i++) {