From 26cad4e8c826f17be921ad0ae1708cf0d658a100 Mon Sep 17 00:00:00 2001 From: overlordxrz-source Date: Sun, 11 Oct 2026 02:38:19 +0000 Subject: [PATCH] feat(libkernel): implement sceKernelGetProsperoSystemSwVersion AI-assisted: yes --- core/libs/prx/libkernel/CMakeLists.txt | 10 ++++ .../libkernel/System/src/SoftwareVersion.cpp | 36 +++++++++++++ core/libs/tests/GuestSoftwareVersion.cpp | 50 +++++++++++++++++++ docs/dev/TechnicalDebt.md | 1 + 4 files changed, 97 insertions(+) create mode 100644 core/libs/prx/libkernel/System/src/SoftwareVersion.cpp create mode 100644 core/libs/tests/GuestSoftwareVersion.cpp diff --git a/core/libs/prx/libkernel/CMakeLists.txt b/core/libs/prx/libkernel/CMakeLists.txt index 0cfc3d427..deaa24180 100644 --- a/core/libs/prx/libkernel/CMakeLists.txt +++ b/core/libs/prx/libkernel/CMakeLists.txt @@ -53,6 +53,7 @@ add_library(libkernel SHARED EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/System/src/Process.cpp ${CMAKE_CURRENT_SOURCE_DIR}/System/src/Sandbox.cpp ${CMAKE_CURRENT_SOURCE_DIR}/System/src/SystemConfiguration.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/System/src/SoftwareVersion.cpp ${CMAKE_CURRENT_SOURCE_DIR}/System/src/Signals.cpp ${CMAKE_CURRENT_SOURCE_DIR}/System/src/SignalSets.cpp ${CMAKE_CURRENT_SOURCE_DIR}/System/src/Notification.cpp @@ -677,6 +678,15 @@ if(BUILD_TESTING) set_tests_properties(guest_system_configuration PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() + add_test_executable(guest_software_version_tests ${LIBS_INCLUDE_DIR}/tests/GuestSoftwareVersion.cpp) + target_include_directories(guest_software_version_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_software_version_tests PRIVATE libkernel libc) + configure_windows_unwind(guest_software_version_tests) + add_test(NAME guest_software_version COMMAND guest_software_version_tests) + if(WIN32) + set_tests_properties(guest_software_version PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() add_test_executable(guest_system_command_tests ${LIBS_INCLUDE_DIR}/tests/GuestSystemCommand.cpp) target_include_directories(guest_system_command_tests PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(guest_system_command_tests PRIVATE libkernel libc) diff --git a/core/libs/prx/libkernel/System/src/SoftwareVersion.cpp b/core/libs/prx/libkernel/System/src/SoftwareVersion.cpp new file mode 100644 index 000000000..0987eb6fa --- /dev/null +++ b/core/libs/prx/libkernel/System/src/SoftwareVersion.cpp @@ -0,0 +1,36 @@ +#include "prx/libc/include/general/VabiMacros.hpp" +#include "prx/libkernel/KernelErrors.hpp" +#include +#include +#include + +struct SceKernelProsperoSystemSwVersion { + std::uint64_t size; + char versionString[0x1C]; + std::uint32_t version; +}; + +static_assert(offsetof(SceKernelProsperoSystemSwVersion, size) == 0x00); +static_assert(offsetof(SceKernelProsperoSystemSwVersion, versionString) == 0x08); +static_assert(offsetof(SceKernelProsperoSystemSwVersion, version) == 0x24); +static_assert(sizeof(SceKernelProsperoSystemSwVersion) == 0x28); + +namespace { + +constexpr std::uint32_t SystemSoftwareVersion = 0x01000000; +constexpr char SystemSoftwareVersionString[] = "01.000.000"; + +} + +extern "C" { + +int APS5_VABI sceKernelGetProsperoSystemSwVersion_nid_postfix(SceKernelProsperoSystemSwVersion* version) { + if (version == nullptr) return SCE_KERNEL_ERROR_EFAULT; + if (version->size != sizeof(SceKernelProsperoSystemSwVersion)) return SCE_KERNEL_ERROR_EINVAL; + std::memset(version->versionString, 0, sizeof(version->versionString)); + std::memcpy(version->versionString, SystemSoftwareVersionString, sizeof(SystemSoftwareVersionString)); + version->version = SystemSoftwareVersion; + return 0; +} + +} diff --git a/core/libs/tests/GuestSoftwareVersion.cpp b/core/libs/tests/GuestSoftwareVersion.cpp new file mode 100644 index 000000000..16a704685 --- /dev/null +++ b/core/libs/tests/GuestSoftwareVersion.cpp @@ -0,0 +1,50 @@ +#include "prx/libc/include/general/VabiMacros.hpp" +#include "prx/libkernel/KernelErrors.hpp" +#include +#include +#include +#include + +struct SceKernelProsperoSystemSwVersion { + std::uint64_t size; + char versionString[0x1C]; + std::uint32_t version; +}; + +static_assert(offsetof(SceKernelProsperoSystemSwVersion, versionString) == 0x08); +static_assert(offsetof(SceKernelProsperoSystemSwVersion, version) == 0x24); +static_assert(sizeof(SceKernelProsperoSystemSwVersion) == 0x28); + +extern "C" { +int* APS5_VABI __error_nid_postfix(); +int APS5_VABI sceKernelGetProsperoSystemSwVersion_nid_postfix(SceKernelProsperoSystemSwVersion*); +} + +static void Require(bool value) { if (!value) std::abort(); } + +static void CheckProsperoSystemSwVersion() { + *__error_nid_postfix() = 13; + Require(sceKernelGetProsperoSystemSwVersion_nid_postfix(nullptr) == SCE_KERNEL_ERROR_EFAULT); + const std::uint64_t sizes[] = {0, 0x18, 0x27, 0x30}; + for (const std::uint64_t size : sizes) { + SceKernelProsperoSystemSwVersion version{}; + std::memset(&version, 0xaa, sizeof(version)); + version.size = size; + Require(sceKernelGetProsperoSystemSwVersion_nid_postfix(&version) == SCE_KERNEL_ERROR_EINVAL); + Require(version.size == size && version.version == 0xaaaaaaaa && static_cast(version.versionString[0]) == 0xaa); + } + SceKernelProsperoSystemSwVersion version{}; + std::memset(&version, 0xaa, sizeof(version)); + version.size = sizeof(version); + Require(sceKernelGetProsperoSystemSwVersion_nid_postfix(&version) == 0); + Require(version.size == sizeof(version)); + Require(std::strcmp(version.versionString, "01.000.000") == 0); + for (std::size_t i = sizeof("01.000.000"); i < sizeof(version.versionString); ++i) Require(version.versionString[i] == 0); + Require(version.version == 0x01000000); + Require(*__error_nid_postfix() == 13); +} + +int main() { + CheckProsperoSystemSwVersion(); + return 0; +} diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md index 8211cd700..aaa484ecd 100644 --- a/docs/dev/TechnicalDebt.md +++ b/docs/dev/TechnicalDebt.md @@ -48,6 +48,7 @@ Throughout the project, every function at every stage either **does exactly what - [sceNpGetSandboxInfo](../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager, NID `ZVZ++68nNI8`) - the name hashes to the imported NID (found with nidhunt, confirmed with `tools/nid_names.py`). STAR WARS: Galactic Racer (PPSA28416) imports it; its OnlineSubsystemPS5 calls it with one pointer to a 0x50-byte stack buffer and reads the buffer as a string (discussions/3086). The returned string, the exact parameter struct and console errors are unknown, so every call throws without reading or writing caller memory. A working implementation belongs with that title's bring-up. - [sceVideoOutGetOutputStatusWithVrr](../../core/libs/prx/libSceVideoOut/Unimplemented.cpp) (libSceVideoOut, NID `gWT7X8H0bYs`) - the name hashes to the imported NID. PPSA26344 version 02.002.000 passes a VideoOut handle and an output pointer at three call sites. It compares a 32-bit value at byte 4 with 2, tests bit 0x10 at byte 16, and, when set, compares low 16-bit values at bytes 48, 50 and 52 with 48, 60 and 120. The full output layout, size, field meanings and console errors are unknown. Every call throws without reading or writing caller memory. +- [sceKernelGetProsperoSystemSwVersion](../../core/libs/prx/libkernel/System/src/SoftwareVersion.cpp) (libkernel) reports system software version `0x01000000` ("01.000.000"); the console's firmware version is not modeled. The 0x28-byte block (a 64-bit size at 0, the version string at 8 and the packed version at 0x24) follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Kernel/KernelSystem.cs), which Prospero Multi Tools (PPSA99110) and Prospero Explorer (PPSA99109) are built with and which sets the size to 40, and the same fields in [etaHEN](https://github.com/etaHEN/etaHEN/blob/main/Source%20Code/daemon/include/ipc.hpp) and [shsrv](https://github.com/ps5-payload-dev/shsrv/blob/master/sh.c). A size other than 0x28 returns `SCE_KERNEL_ERROR_EINVAL` without writing; whether the console checks the size is unverified, since etaHEN and shsrv declare a 0x30-byte block and do not set it - [getuid, geteuid, getgid, getegid and issetugid](../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) report the root identity (0) and a process that never changed credentials (`issetugid` 0), as a jailbroken process has; the host user and group are not exposed to the guest. The values a title sees on a console are unverified - [sceKernelGetAppInfo](../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) - only `app_id` and the title ID (offset 0x10, up to 9 characters plus a terminator) are written; callers declare the structure with different sizes, so the rest is left alone. `app_id` is 1, the running title's id in libSceSystemService, which is a guess, not something measured. The error codes follow shadPS4: `EPERM` for another process, and 0 without writing anything for a null pointer. - [sceFontSetScriptLanguage, sceFontGetScriptLanguage, sceFontSetTypographicDesign, sceFontGetTypographicDesign](../../core/libs/prx/libSceFont/src/Style.cpp) (libSceFont) - signatures follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Font/SceFont.cs), not confirmed for the PS5. The handle keeps the language set for each script and the feature set for each typographic design and returns them, with 0 for one never set; the console default and its valid ranges are unknown, so any value is accepted. Neither setting changes shaping or rendering yet