Pause mount hooks while games run

This commit is contained in:
Roman Tarasov committed 2026-09-10 22:15:31 +03:00
1 parent 6b7dc2ced5
commit 87fbe72b46
2 files changed
+134 -13

No files matched your search

+8
View File
@@ -7,6 +7,14 @@
#define SM_KSTUFF_KEKCALL_CHECK UINT64_C(0xffffffff00000027)
#define SM_KSTUFF_KEKCALL_REMOTE_SYSCALL UINT64_C(0x500000027)
#define SM_KSTUFF_KEKCALL_RUNTIME_HOOK_CONTROL UINT64_C(0x800000027)
#define SM_KSTUFF_RUNTIME_HOOK_ENABLE UINT64_C(1)
#define SM_KSTUFF_RUNTIME_HOOK_DISABLE UINT64_C(2)
#define SM_KSTUFF_RUNTIME_HOOK_NMOUNT (UINT64_C(1) << 0)
#define SM_KSTUFF_RUNTIME_HOOK_UNMOUNT (UINT64_C(1) << 1)
#define SM_KSTUFF_RUNTIME_HOOK_MOUNT_UNMOUNT \
(SM_KSTUFF_RUNTIME_HOOK_NMOUNT | SM_KSTUFF_RUNTIME_HOOK_UNMOUNT)
// Resolve firmware-specific kstuff sysentvec addresses and initialize state.
void sm_kstuff_init(void);
+126 -13
View File
@@ -39,6 +39,9 @@ typedef struct {
bool restore_on_empty;
bool sleep_pause_restore;
bool current_app_focus_valid;
bool mount_hooks_restore_needed;
bool mount_hooks_control_unavailable;
pid_t mount_hooks_game_pid;
uint32_t current_app_focus_id;
uint64_t restore_retry_us;
kstuff_game_entry_t game;
@@ -56,6 +59,94 @@ static uint32_t get_pause_delay_seconds_for_title(const char *title_id,
uint32_t *autopause_delay_out,
bool *autopause_delay_valid_out);
static void restore_kstuff_if_needed(const char *reason);
static void restore_game_mount_hooks_if_idle(const char *reason);
static void shutdown_kstuff_game_automation(const char *reason);
static int control_runtime_syscall_hooks(uint64_t command, uint64_t mask,
uint64_t *enabled_mask_out) {
register uint64_t r10 __asm__("r10") = 0;
register uint64_t r8 __asm__("r8") = 0;
register uint64_t r9 __asm__("r9") = 0;
uint64_t result;
unsigned char is_error;
__asm__ __volatile__("syscall"
: "=a"(result), "=@ccc"(is_error), "+r"(r10),
"+r"(r8), "+r"(r9)
: "a"(SM_KSTUFF_KEKCALL_RUNTIME_HOOK_CONTROL),
"D"(command), "S"(mask), "d"(UINT64_C(0))
: "rcx", "r11", "memory");
if (is_error)
return (int)result;
if (enabled_mask_out)
*enabled_mask_out = result;
return 0;
}
static bool set_game_mount_hooks_paused(bool pause, const char *reason) {
if (g_kstuff.mount_hooks_control_unavailable)
return false;
if (!sm_kstuff_is_loaded()) {
log_debug(" [KSTUFF] cannot %s nmount/unmount interception: kstuff is "
"not currently callable",
pause ? "pause" : "resume");
return false;
}
uint64_t enabled_mask = 0;
int rc = control_runtime_syscall_hooks(
pause ? SM_KSTUFF_RUNTIME_HOOK_DISABLE
: SM_KSTUFF_RUNTIME_HOOK_ENABLE,
SM_KSTUFF_RUNTIME_HOOK_MOUNT_UNMOUNT, &enabled_mask);
if (rc != 0) {
if (rc == ENOSYS) {
g_kstuff.mount_hooks_control_unavailable = true;
log_debug(" [KSTUFF] nmount/unmount runtime control is unavailable; "
"update ps5-kstuff");
} else {
log_debug(" [KSTUFF] failed to %s nmount/unmount interception: %s",
pause ? "pause" : "resume", strerror(rc));
}
return false;
}
bool desired_state =
pause ? (enabled_mask & SM_KSTUFF_RUNTIME_HOOK_MOUNT_UNMOUNT) == 0
: (enabled_mask & SM_KSTUFF_RUNTIME_HOOK_MOUNT_UNMOUNT) ==
SM_KSTUFF_RUNTIME_HOOK_MOUNT_UNMOUNT;
if (!desired_state) {
log_debug(" [KSTUFF] nmount/unmount %s returned unexpected status "
"0x%llX",
pause ? "pause" : "resume",
(unsigned long long)enabled_mask);
return false;
}
log_debug(" [KSTUFF] nmount/unmount interception %s (%s)",
pause ? "paused" : "resumed", reason ? reason : "game state");
return true;
}
static void note_game_for_mount_hooks(pid_t pid, const char *reason) {
if (pid <= 0)
return;
g_kstuff.mount_hooks_game_pid = pid;
if (!g_kstuff.mount_hooks_restore_needed &&
set_game_mount_hooks_paused(true, reason)) {
g_kstuff.mount_hooks_restore_needed = true;
}
}
static void restore_game_mount_hooks_if_idle(const char *reason) {
if (g_kstuff.mount_hooks_game_pid > 0 ||
!g_kstuff.mount_hooks_restore_needed) {
return;
}
if (set_game_mount_hooks_paused(false, reason))
g_kstuff.mount_hooks_restore_needed = false;
}
bool sm_kstuff_is_loaded(void) {
register uint64_t r10 __asm__("r10") = 0;
@@ -460,7 +551,7 @@ static void apply_kstuff_config_reload(void) {
sm_mdbg_game_shutdown();
if (!runtime_config().kstuff_game_auto_toggle) {
sm_kstuff_game_shutdown();
shutdown_kstuff_game_automation("config reload");
return;
}
@@ -568,6 +659,7 @@ static void restore_kstuff_if_needed(const char *reason) {
reason ? reason : "no reason",
g_kstuff.game.active ? ", pending delay for another tracked game"
: "");
restore_game_mount_hooks_if_idle(reason);
} else {
log_debug(" [KSTUFF] failed to auto-resume (%s)",
reason ? reason : "no reason");
@@ -776,11 +868,14 @@ bool sm_kstuff_game_feature_enabled(void) {
void sm_kstuff_game_on_exec(pid_t pid, const char *title_id, uint32_t app_id,
uint64_t exec_time_us) {
if (!sm_kstuff_game_feature_enabled())
if (!sm_kstuff_game_feature_enabled()) {
note_game_for_mount_hooks(pid, "game launch");
return;
}
if (g_kstuff.game.active) {
if (g_kstuff.game.pid == pid) {
note_game_for_mount_hooks(pid, "game launch");
log_debug(" [KSTUFF] already tracking pid=%ld for %s", (long)pid,
title_id);
return;
@@ -796,6 +891,10 @@ void sm_kstuff_game_on_exec(pid_t pid, const char *title_id, uint32_t app_id,
finish_tracked_game_clear("tracked game handoff");
}
// A previous tracked title may have disabled the entire sysentvec. Restore
// that state first, then apply the narrower nmount/unmount pause.
note_game_for_mount_hooks(pid, "game launch");
if (is_kstuff_pause_disabled_for_title(title_id)) {
log_debug(" [KSTUFF] auto-pause disabled by config for %s pid=%ld "
"app_id=0x%08X", title_id, (long)pid, app_id);
@@ -836,6 +935,8 @@ bool sm_kstuff_game_handoff(pid_t old_pid, pid_t new_pid,
title_id[0] == '\0') {
return false;
}
note_game_for_mount_hooks(new_pid, "game process handoff");
if (!g_kstuff.game.active || g_kstuff.game.pid != old_pid ||
strcmp(g_kstuff.game.title_id, title_id) != 0) {
// Config may have enabled tracking while the old process was pending, or
@@ -864,16 +965,22 @@ void sm_kstuff_note_app_focus(uint32_t app_id) {
}
void sm_kstuff_game_on_exit(pid_t pid) {
if (!g_kstuff.game.active || g_kstuff.game.pid != pid)
return;
bool mount_hooks_game_exited = g_kstuff.mount_hooks_game_pid == pid;
bool restore_needed = tracked_game_requires_restore();
sm_mdbg_game_on_exit(pid);
log_debug(" [KSTUFF] game stopped: %s pid=%ld",
g_kstuff.game.title_id, (long)pid);
memset(&g_kstuff.game, 0, sizeof(g_kstuff.game));
if (restore_needed)
finish_tracked_game_clear("tracked game exit");
if (g_kstuff.game.active && g_kstuff.game.pid == pid) {
bool restore_needed = tracked_game_requires_restore();
sm_mdbg_game_on_exit(pid);
log_debug(" [KSTUFF] game stopped: %s pid=%ld",
g_kstuff.game.title_id, (long)pid);
memset(&g_kstuff.game, 0, sizeof(g_kstuff.game));
if (restore_needed)
finish_tracked_game_clear("tracked game exit");
}
if (mount_hooks_game_exited) {
g_kstuff.mount_hooks_game_pid = 0;
restore_game_mount_hooks_if_idle("game exit");
}
}
void sm_kstuff_game_poll(bool process_active) {
@@ -891,7 +998,7 @@ void sm_kstuff_game_poll(bool process_active) {
maybe_apply_kstuff_pause_for_slot(&g_kstuff.game);
}
void sm_kstuff_game_shutdown(void) {
static void shutdown_kstuff_game_automation(const char *reason) {
bool restore_needed = tracked_game_requires_restore();
if (restore_needed && !g_kstuff.sleep_pause_restore &&
runtime_sleep_mode_active() && should_stop_requested()) {
@@ -906,10 +1013,16 @@ void sm_kstuff_game_shutdown(void) {
memset(&g_kstuff.game, 0, sizeof(g_kstuff.game));
if (restore_needed) {
mark_restore_needed();
restore_kstuff_if_needed("watcher shutdown");
restore_kstuff_if_needed(reason);
}
}
void sm_kstuff_game_shutdown(void) {
shutdown_kstuff_game_automation("watcher shutdown");
g_kstuff.mount_hooks_game_pid = 0;
restore_game_mount_hooks_if_idle("watcher shutdown");
}
void sm_kstuff_sleep_enter(void) {
bool was_enabled = false;
bool fully_disabled = false;