Merge pull request #1992 from LuisBiceps/feat/system-service-kill-app

feat(libSceSystemService): let the running title close itself with sceSystemServiceKillApp
This commit is contained in:
Dean 2026-10-09 22:42:08 +13:00 committed by GitHub
commit 1d7c70a752
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 82 additions and 0 deletions

View File

@ -29,4 +29,17 @@ 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)
add_test(NAME guest_system_kill_app_status 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_status guest_system_kill_app_parameters PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endif()

View File

@ -156,4 +156,18 @@ 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");
}
LibcExit_nid_no_patch(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,52 @@
#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() {
try { LibcAwaitExit_nid_postfix(); }
catch (const ProcessShutdown&) { return; }
catch (const std::runtime_error&) { 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, const char* expected) {
try { sceSystemServiceKillApp(appId, how, reason, coreDump); }
catch (const std::runtime_error& error) { return std::strstr(error.what(), expected) != nullptr; }
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);
std::_Exit(4);
}
LibcRegisterShutdown_nid_postfix(UnexpectedShutdown);
Require(std::atexit(UnexpectedShutdown) == 0);
constexpr char otherApp[] = "sceSystemServiceKillApp: application other than the running title";
constexpr char otherArguments[] = "sceSystemServiceKillApp: arguments other than -1, 0 and 0";
Require(Rejects(appId + 1, -1, 0, 0, otherApp));
Require(Rejects(-1, -1, 0, 0, otherApp));
Require(Rejects(0, -1, 0, 0, otherApp));
Require(Rejects(appId, 0, 0, 0, otherArguments));
Require(Rejects(appId, -2, 0, 0, otherArguments));
Require(Rejects(appId, -1, 1, 0, otherArguments));
Require(Rejects(appId, -1, 0, 1, otherArguments));
finished = true;
}

View File

@ -352,6 +352,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 value is AnyPS5's own: the console's shell assigns app IDs and their values are not known here, so other calls that take an app ID must use the same one. `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 and [ps5-payload-dev's hbldr](https://github.com/ps5-payload-dev/websrv/blob/46374edee6627885abac717de24da7a1d73af6f1/src/ps5/hbldr.c#L425-L435) passes to close the running game, ends the title as its own `exit(0)` would: the guest's shutdown and `atexit` handlers run and the process exits with status 0. On the console the call returns and the shell then ends the title, and whether those handlers run there is unknown; here there is no shell, so the call does not return. 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.