feat(libSceSystemService): let the running title close itself with sceSystemServiceKillApp

Report an app ID for the running title from sceSystemServiceGetAppIdOfRunningBigApp, and shut the guest down and exit when sceSystemServiceKillApp receives that ID with the arguments -1, 0 and 0. SwanStationPS5 (PPSA98510) 1.0.6 closes itself this way and stops on both imports on main.
This commit is contained in:
LuisBiceps 2026-10-09 12:10:19 +08:00
parent f145139676
commit cb43a4c306
5 changed files with 74 additions and 0 deletions

View File

@ -29,4 +29,16 @@ if(BUILD_TESTING)
set_tests_properties(guest_system_service PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test_executable(guest_system_kill_app_tests ${LIBS_INCLUDE_DIR}/tests/GuestSystemKillApp.cpp)
target_include_directories(guest_system_kill_app_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_system_kill_app_tests PRIVATE libSceSystemService libc)
configure_windows_unwind(guest_system_kill_app_tests)
add_test(NAME guest_system_kill_app_parameters COMMAND guest_system_kill_app_tests)
add_test(NAME guest_system_kill_app COMMAND guest_system_kill_app_tests --kill)
set_tests_properties(guest_system_kill_app PROPERTIES
PASS_REGULAR_EXPRESSION "Guest shutdown and atexit completed")
if(WIN32)
set_tests_properties(guest_system_kill_app guest_system_kill_app_parameters PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endif()

View File

@ -158,4 +158,19 @@ int APS5_VABI sceSystemServiceShowControllerSettings(void) {
return 0;
}
int APS5_VABI sceSystemServiceGetAppIdOfRunningBigApp(void) {
return SYSTEM_SERVICE_RUNNING_APP_ID;
}
int APS5_VABI sceSystemServiceKillApp(int appId, int how, int reason, int coreDump) {
if (appId != SYSTEM_SERVICE_RUNNING_APP_ID) {
NotImplemented_nid_no_patch("sceSystemServiceKillApp: application other than the running title");
}
if (how != -1 || reason != 0 || coreDump != 0) {
NotImplemented_nid_no_patch("sceSystemServiceKillApp: arguments other than -1, 0 and 0");
}
LibcRunShutdown_nid_postfix();
std::exit(0);
}
}

View File

@ -8,6 +8,8 @@ constexpr int SYSTEM_SERVICE_OK = 0;
constexpr int SYSTEM_SERVICE_ERROR_PARAMETER = -2136932349;
constexpr int SYSTEM_SERVICE_ERROR_NO_EVENT = -2136932348;
constexpr int SYSTEM_SERVICE_RUNNING_APP_ID = 1;
constexpr int SYSTEM_SERVICE_PARAM_ID_LANG = 1;
constexpr int SYSTEM_SERVICE_PARAM_ID_DATE_FORMAT = 2;
constexpr int SYSTEM_SERVICE_PARAM_ID_TIME_FORMAT = 3;

View File

@ -0,0 +1,44 @@
#include "prx/libc/include/general/VabiMacros.hpp"
#include "prx/libc/include/Shutdown.hpp"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <stdexcept>
extern "C" int APS5_VABI sceSystemServiceGetAppIdOfRunningBigApp(void);
extern "C" int APS5_VABI sceSystemServiceKillApp(int, int, int, int);
namespace {
bool cleaned = false;
bool finished = false;
void Cleanup() { cleaned = true; }
void VerifyExit() {
if (!cleaned) std::_Exit(1);
std::puts("Guest shutdown and atexit completed");
}
void UnexpectedShutdown() {
if (!finished) std::_Exit(3);
}
void Require(bool value) { if (!value) std::abort(); }
bool Rejects(int appId, int how, int reason, int coreDump) {
try { sceSystemServiceKillApp(appId, how, reason, coreDump); }
catch (const std::runtime_error&) { return true; }
return false;
}
}
int main(int argc, char** argv) {
const int appId = sceSystemServiceGetAppIdOfRunningBigApp();
Require(appId > 0 && sceSystemServiceGetAppIdOfRunningBigApp() == appId);
if (argc > 1 && std::strcmp(argv[1], "--kill") == 0) {
LibcRegisterShutdown_nid_postfix(Cleanup);
Require(std::atexit(VerifyExit) == 0);
sceSystemServiceKillApp(appId, -1, 0, 0);
return 2;
}
LibcRegisterShutdown_nid_postfix(UnexpectedShutdown);
Require(std::atexit(UnexpectedShutdown) == 0);
Require(Rejects(appId + 1, -1, 0, 0));
Require(Rejects(-1, -1, 0, 0));
Require(Rejects(appId, 0, 0, 0));
Require(Rejects(appId, -1, 1, 0));
Require(Rejects(appId, -1, 0, 1));
finished = true;
}

View File

@ -348,6 +348,7 @@ Throughout the project, every function at every stage either **does exactly what
- [sceImeKeyboardGetResourceId](../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) reports that no USB keyboard is connected (`0x80bc0004`, as [shadPS4](https://github.com/shadps4-emu/shadPS4) does) because IME keyboard events are not delivered to the handler, although [libSceKeyboard](../../core/libs/prx/libSceKeyboard/src/keyboard_impl.cpp) exposes the host keyboard
- [Player review dialog](../../core/libs/prx/libSceCdlgPlayerReview/Export.cpp) (libSceCdlgPlayerReview) shows nothing: `scePlayerReviewDialogOpen` finishes at once and the result reports that the user canceled, as `sceWebBrowserDialogOpen` does
- [NP reachability callbacks](../../core/libs/prx/libSceNpManager/Export.cpp) retain one callback and its user data; duplicate registration and unregistering an absent callback return the errors used by [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/945dbc3cc3eee80ac3e053b438502ed936fa6bb2/src/core/libraries/np/np_manager.cpp). No callback is delivered because the existing NP model remains signed out with reachability unavailable.
- [Running application ID](../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - `sceSystemServiceGetAppIdOfRunningBigApp` reports 1 as the running title's app ID; the console's shell assigns app IDs, and their values are not known here. `sceSystemServiceKillApp` with that ID and the arguments -1, 0 and 0, which [SwanStationPS5](https://github.com/darkxex/SwanStationPS5/blob/4cdc981d1a64ffb7f2af73f7a27a9e91a3cb5eb4/src/main.c#L1050-L1058) passes to close itself, shuts the guest down and exits the process with status 0, as `sceSystemServiceLoadExec("exit")` does. Another app ID or other arguments throw.
- [Temporary app-content storage](../../core/libs/prx/libSceAppContent/Export.cpp) tracks one `/temp0` mount, preserves its files on unmount, and rejects new path resolutions until remount. Mount, format, space-query and unmount errors follow [fpPS4](https://github.com/red-prig/fpPS4/blob/04cefd43e6fddd1ab033e7980cd356d14c964905/sys/sys_path.pas); the pointer ABI is also declared by [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/AppContent/AppContent.cs). Existing open host descriptors remain usable after unmount; console behaviour with open files is unverified. Host filesystem failures still throw.
- [Guest path resolution](../../core/libs/prx/libc/src/General.cpp) falls back to ASCII case-insensitive component lookup, following [shadPS4's host filesystem](https://github.com/shadps4-emu/shadPS4/blob/945dbc3cc3eee80ac3e053b438502ed936fa6bb2/src/core/file_sys/backends/host_fs.cpp). PPSA03747 requests `Skyrim.INI`, `Data/Video` and lowercase archive/plugin names that differ from the dump's filenames. Exact host names take precedence; multiple fallback matches throw instead of selecting an arbitrary file. Existing parent names are resolved when creating a new file, and guest mount aliases use the same ASCII comparison. Results are not cached, so renames and newly created files remain visible. Unicode case folding and PS5 behaviour for case-colliding host entries are unverified; host roots are used as supplied.
- [Static scalar calls](../../core/shader/recompiler/ControlFlow/src/GraphBuilder.cpp) (`s_call_b64` and `s_swappc_b64`) require one call per ordinary even-aligned SGPR link pair (s0:s1 through s104:s105) and one later `s_setpc_b64` return reading that exact pair. Regions must nest or be disjoint; recursion, shared links, escaping link values (including overlapping tuples and resource descriptors), invalid targets, interleaved regions, cross-region branches or fall-through, relative SGPR reads and dynamic `s_setpc_b64` in a program with calls throw. The analysis checks all decoded instructions, including unreachable code. Arbitrary indirect calls and general call frames are not implemented.