From d1631862f5d4d61323ef73bf9716f2c8fd975c2b Mon Sep 17 00:00:00 2001 From: hugodearaujo Date: Sun, 11 Oct 2026 14:20:54 -0300 Subject: [PATCH] fix(libkernel): report the operation mode the console reports sceKernelGetOperationMode returned 0 for both values, which were unknown. A payload on a PS5 (firmware 13.60) reads mode 2 and submode 0; nothing past the two ints is written and the result register holds no status, so the function now returns nothing. PPSA12544 logs both values and ignores the result. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libkernel/CMakeLists.txt | 9 +++++++++ .../libs/prx/libkernel/System/src/Process.cpp | 5 ++--- core/libs/tests/GuestOperationMode.cpp | 20 +++++++++++++++++++ docs/dev/TechnicalDebt.md | 2 +- 4 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 core/libs/tests/GuestOperationMode.cpp diff --git a/core/libs/prx/libkernel/CMakeLists.txt b/core/libs/prx/libkernel/CMakeLists.txt index 41cb24584..feb2f67e2 100644 --- a/core/libs/prx/libkernel/CMakeLists.txt +++ b/core/libs/prx/libkernel/CMakeLists.txt @@ -682,6 +682,15 @@ if(BUILD_TESTING) set_tests_properties(guest_sched_priority PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() + add_test_executable(guest_operation_mode_tests ${LIBS_INCLUDE_DIR}/tests/GuestOperationMode.cpp) + target_include_directories(guest_operation_mode_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_operation_mode_tests PRIVATE libkernel libc) + configure_windows_unwind(guest_operation_mode_tests) + add_test(NAME guest_operation_mode COMMAND guest_operation_mode_tests) + if(WIN32) + set_tests_properties(guest_operation_mode PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() add_test_executable(guest_waitpid_tests ${LIBS_INCLUDE_DIR}/tests/GuestWaitpid.cpp) target_include_directories(guest_waitpid_tests PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(guest_waitpid_tests PRIVATE libkernel libc) diff --git a/core/libs/prx/libkernel/System/src/Process.cpp b/core/libs/prx/libkernel/System/src/Process.cpp index 45528cd7a..95493d47f 100644 --- a/core/libs/prx/libkernel/System/src/Process.cpp +++ b/core/libs/prx/libkernel/System/src/Process.cpp @@ -453,12 +453,11 @@ int APS5_VABI sceKernelIsTrinityMode(void) { return 0; } -int APS5_VABI sceKernelGetOperationMode(int* mode, int* submode) { +void APS5_VABI sceKernelGetOperationMode(int* mode, int* submode) { if (!mode || !submode) throw std::invalid_argument("sceKernelGetOperationMode: null output"); - *mode = 0; + *mode = 2; *submode = 0; - return 0; } int APS5_VABI seteuid_nid_postfix(std::uint32_t euid) { diff --git a/core/libs/tests/GuestOperationMode.cpp b/core/libs/tests/GuestOperationMode.cpp new file mode 100644 index 000000000..248ec491a --- /dev/null +++ b/core/libs/tests/GuestOperationMode.cpp @@ -0,0 +1,20 @@ +#include "prx/libc/include/general/VabiMacros.hpp" +#include +#include +extern "C" { +void APS5_VABI sceKernelGetOperationMode(int* mode, int* submode); +} +static void Require(bool value) { if (!value) std::abort(); } +static bool Throws(int* mode, int* submode) { + try { sceKernelGetOperationMode(mode, submode); } catch (const std::invalid_argument&) { return true; } + return false; +} +int main() { + int out[4] = {-1, -1, -1, -1}; + sceKernelGetOperationMode(&out[0], &out[2]); + Require(out[0] == 2 && out[1] == -1); + Require(out[2] == 0 && out[3] == -1); + int value = -1; + Require(Throws(nullptr, &value) && Throws(&value, nullptr)); + Require(value == -1); +} diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md index 0c72b62a7..1886917fd 100644 --- a/docs/dev/TechnicalDebt.md +++ b/docs/dev/TechnicalDebt.md @@ -224,7 +224,7 @@ Throughout the project, every function at every stage either **does exactly what - [vieBRwlh1Lw](../../core/libs/prx/libSceAgc/Unimplemented.cpp) (libSceAgc) - unknown name, signature - [scePadIsRemoteController](../../core/libs/prx/libScePad/Export.cpp) (libScePad) - the name hashes to NID `fCWdlnmB1Ks`; the signature `(int handle, bool* remote)` comes from PPSA16064, which zeroes a byte, passes its address after `scePadGetControllerInformation` and ignores the result. The error codes follow the other pad calls. Controllers are local devices, so the answer is always false - [scePadSetAngularVelocityBiasCorrectionState](../../core/libs/prx/libScePad/Export.cpp) (libScePad) - inferred signature `(int handle, bool enabled)` follows `scePadSetTiltCorrectionState` and `scePadSetAngularVelocityDeadbandState`; an invalid handle returns `PAD_ERROR_INVALID_HANDLE`, disabling is a no-op, and enabling throws because bias correction is not modelled -- [sceKernelGetOperationMode](../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) - the signature `(int* mode, int* submode)` comes from PPSA12544, which logs both values after the call; the values are unknown and 0 is reported for both, as in the [prosper](https://github.com/mattias800/prosper/pull/4153) reimplementation. Null outputs throw +- [sceKernelGetOperationMode](../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) - the signature `(int* mode, int* submode)` comes from PPSA12544, which logs both values after the call and ignores the result. A homebrew payload on a PS5 (firmware 13.60) reads mode 2 and submode 0, nothing else is written, and the result register holds no status, so the function is taken to return nothing. Whether a title process reads the same values is unverified. Null outputs throw - Font, Http2, Net, Ssl, SystemService and libkernel exports imported by PPSA12544 and added without an implementation have unknown signatures - [sceHttp2GetMemoryPoolStats, sceSslGetMemoryPoolStats, sceNetGetMemoryPoolStats](../../core/libs/prx/libSceNet/Export.cpp) (libSceHttp2, libSceSsl, libSceNet) - the signatures `(int id, stats*)`, the layouts (pool size, peak and current use, and for Http2 and Ssl a 32-bit reserved field) and the error codes were measured on a PS5 (firmware 13.60). The pool size is the one given at init or pool creation; nothing is allocated from these pools, so the peak and current use are 0, where the console already reports use right after initialization (0x15C0 bytes for Ssl, 0x1640 for Http2 and 4 for a Net pool) - [sceNpWebApi2GetMemoryPoolStats](../../core/libs/prx/libSceNpWebApi2/Export.cpp) (libSceNpWebApi2) - imported by PPSA21703's Unity PSN plugin. The signature `(int libCtxId, stats*)` and the layout of the Http2 statistics above (measured on a PS5 for Http2, Ssl and Net) are assumed for this library, not measured. The pool size is the one given to `sceNpWebApi2Initialize`, and the peak and current use are 0. The error codes are unknown, so a null output or an unknown context throws