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 1/2] 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 2/2] 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++) {