mirror of
https://github.com/drakmor/ShadowMountPlus.git
synced 2026-10-21 22:22:28 -05:00
Fix ShellCore runtime release after app exit
This commit is contained in:
1 parent
eb3c611ebf
commit
4085fcd8ca
14 files changed
+333
-216
No files matched your search
@@ -42,6 +42,8 @@ bool mount_title_nullfs(const char *title_id, const char *src_path);
|
||||
bool rollback_title_nullfs_mount(const char *title_id, const char *src_path);
|
||||
// Drop runtime layers for a title while preserving its metadata/link files.
|
||||
bool unmount_title_runtime_layers(const char *title_id);
|
||||
// Same operation without expected EBUSY diagnostics during a bounded retry.
|
||||
bool unmount_title_runtime_layers_quiet(const char *title_id);
|
||||
// Release every managed title layer while preserving link metadata.
|
||||
bool unmount_all_title_runtime_layers(void);
|
||||
// Reconcile the title mount stack against the expected source/backport state.
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
// Install/remove the fail-open SceShellCore lifecycle bridge.
|
||||
bool sm_shellcore_hooks_start(void);
|
||||
void sm_shellcore_hooks_stop(void);
|
||||
// Return whether the post-unmountWorkspace lifecycle hook is installed.
|
||||
bool sm_shellcore_workspace_hook_active(void);
|
||||
// Return whether the post-onAppExit lifecycle hook is installed.
|
||||
bool sm_shellcore_app_exit_hook_active(void);
|
||||
|
||||
// Return whether this firmware uses the internal TitleDir bridge.
|
||||
bool sm_shellcore_install_bridge_enabled(void);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
typedef enum {
|
||||
SM_SHELLCORE_TARGET_LAUNCH_APP = 0,
|
||||
SM_SHELLCORE_TARGET_UNMOUNT_WORKSPACE,
|
||||
SM_SHELLCORE_TARGET_APP_EXIT,
|
||||
SM_SHELLCORE_TARGET_INSTALL_TITLE_DIR,
|
||||
SM_SHELLCORE_TARGET_INSTALL_ALL,
|
||||
SM_SHELLCORE_TARGET_COUNT
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
typedef enum {
|
||||
SM_SHELLCORE_REQUEST_LAUNCH = 1,
|
||||
SM_SHELLCORE_REQUEST_LAUNCH_FAILED = 2,
|
||||
SM_SHELLCORE_REQUEST_WORKSPACE_UNMOUNTED = 3
|
||||
SM_SHELLCORE_REQUEST_APP_EXITED = 3
|
||||
} sm_shellcore_request_op_t;
|
||||
|
||||
typedef struct {
|
||||
@@ -24,7 +24,7 @@ typedef struct {
|
||||
struct {
|
||||
uint32_t app_id;
|
||||
uint8_t reserved[MAX_TITLE_ID - sizeof(uint32_t)];
|
||||
} workspace;
|
||||
} app_exit;
|
||||
} payload;
|
||||
} sm_shellcore_request_t;
|
||||
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
bool sm_shellcore_service_start(void);
|
||||
void sm_shellcore_service_stop(void);
|
||||
|
||||
// Release transient title/image mounts after launch failure or ShellCore
|
||||
// workspace teardown.
|
||||
// Release transient title/image mounts after launch failure or app exit.
|
||||
bool sm_shellcore_release_title_runtime(const char *title_id);
|
||||
// Public API entry points. Return 0 or a positive errno value.
|
||||
int sm_shellcore_mount_title_runtime(const char *title_id);
|
||||
@@ -17,7 +16,7 @@ int sm_shellcore_unmount_title_runtime(const char *title_id);
|
||||
// Bind the prepared managed title to the app id stored in LncApplication.
|
||||
void sm_shellcore_service_bind_prepared_app(const char *title_id,
|
||||
uint32_t app_id);
|
||||
// Publish process exit without tearing down the still-live ShellCore workspace.
|
||||
// Publish process exit while ShellCore finishes its own app-exit cleanup.
|
||||
// Returns true when a managed prepared title transitioned to exit-pending.
|
||||
bool sm_shellcore_service_note_game_exit(const char *title_id);
|
||||
// Ensure that a managed title has its image/nullfs/backport runtime stack.
|
||||
|
||||
+31
-8
@@ -11,6 +11,8 @@
|
||||
#include "sm_paths.h"
|
||||
|
||||
static bool unmount_controlled_mount_stack(const char *path);
|
||||
static bool unmount_controlled_mount_stack_impl(const char *path,
|
||||
bool diagnose_busy);
|
||||
|
||||
// --- FILESYSTEM ---
|
||||
bool is_installed(const char *title_id) {
|
||||
@@ -593,7 +595,8 @@ static bool inspect_title_stack(const char *title_id, const char *source_path,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool unmount_top_controlled_layer(const char *path) {
|
||||
static bool unmount_top_controlled_layer_impl(const char *path,
|
||||
bool diagnose_busy) {
|
||||
struct statfs mount_st;
|
||||
if (!get_top_mount(path, &mount_st))
|
||||
return true;
|
||||
@@ -611,15 +614,21 @@ static bool unmount_top_controlled_layer(const char *path) {
|
||||
if (unmount(path, 0) == 0 || errno == ENOENT || errno == EINVAL)
|
||||
return true;
|
||||
int unmount_errno = errno;
|
||||
if (unmount_errno == EBUSY)
|
||||
if (unmount_errno == EBUSY && diagnose_busy)
|
||||
sm_mount_diag_log_busy(path);
|
||||
|
||||
log_debug(" [LINK] unmount deferred for %s: %s", path,
|
||||
strerror(unmount_errno));
|
||||
if (unmount_errno != EBUSY || diagnose_busy) {
|
||||
log_debug(" [LINK] unmount deferred for %s: %s", path,
|
||||
strerror(unmount_errno));
|
||||
}
|
||||
errno = unmount_errno;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool unmount_top_controlled_layer(const char *path) {
|
||||
return unmount_top_controlled_layer_impl(path, true);
|
||||
}
|
||||
|
||||
static bool title_stack_top_is_managed(const title_mount_state_t *state) {
|
||||
return state->top_is_our_nullfs || state->top_kind == TITLE_STACK_TOP_BACKPORT;
|
||||
}
|
||||
@@ -638,7 +647,8 @@ bool rollback_title_nullfs_mount(const char *title_id, const char *src_path) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool unmount_title_runtime_layers(const char *title_id) {
|
||||
static bool unmount_title_runtime_layers_impl(const char *title_id,
|
||||
bool diagnose_busy) {
|
||||
if (!title_id || title_id[0] == '\0')
|
||||
return false;
|
||||
|
||||
@@ -647,12 +657,20 @@ bool unmount_title_runtime_layers(const char *title_id) {
|
||||
if (written < 0 || (size_t)written >= sizeof(path))
|
||||
return false;
|
||||
|
||||
if (!unmount_controlled_mount_stack(path))
|
||||
if (!unmount_controlled_mount_stack_impl(path, diagnose_busy))
|
||||
return false;
|
||||
log_debug(" [LINK] runtime layers released: %s", title_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool unmount_title_runtime_layers(const char *title_id) {
|
||||
return unmount_title_runtime_layers_impl(title_id, true);
|
||||
}
|
||||
|
||||
bool unmount_title_runtime_layers_quiet(const char *title_id) {
|
||||
return unmount_title_runtime_layers_impl(title_id, false);
|
||||
}
|
||||
|
||||
static bool unmount_managed_title_entry(
|
||||
const char *title_id, const title_link_paths_t *paths, void *ctx) {
|
||||
bool *all_released = (bool *)ctx;
|
||||
@@ -763,12 +781,13 @@ bool mount_backport_overlay(const char *mount_point,
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool unmount_controlled_mount_stack(const char *path) {
|
||||
static bool unmount_controlled_mount_stack_impl(const char *path,
|
||||
bool diagnose_busy) {
|
||||
for (int i = 0; i < MAX_LAYERED_UNMOUNT_ATTEMPTS * 4; i++) {
|
||||
struct statfs mount_st;
|
||||
if (!get_top_mount(path, &mount_st))
|
||||
return true;
|
||||
if (!unmount_top_controlled_layer(path))
|
||||
if (!unmount_top_controlled_layer_impl(path, diagnose_busy))
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -776,6 +795,10 @@ static bool unmount_controlled_mount_stack(const char *path) {
|
||||
return !get_top_mount(path, &mount_st);
|
||||
}
|
||||
|
||||
static bool unmount_controlled_mount_stack(const char *path) {
|
||||
return unmount_controlled_mount_stack_impl(path, true);
|
||||
}
|
||||
|
||||
static void unmount_mount_point_for_recovery(const char *path) {
|
||||
if (!unmount_controlled_mount_stack(path)) {
|
||||
log_debug(" [LINK] failed to reset mount stack for recovery: %s", path);
|
||||
|
||||
@@ -476,11 +476,11 @@ static void handle_game_exit(pid_t pid) {
|
||||
sm_kstuff_game_on_exit(pid);
|
||||
if (had_active_title) {
|
||||
if (sm_shellcore_service_note_game_exit(title_id)) {
|
||||
if (sm_shellcore_workspace_hook_active()) {
|
||||
log_debug(" [SHELLCORE] runtime release awaiting unmountWorkspace: %s",
|
||||
if (sm_shellcore_app_exit_hook_active()) {
|
||||
log_debug(" [SHELLCORE] runtime release awaiting onAppExit: %s",
|
||||
title_id);
|
||||
} else if (!sm_shellcore_release_title_runtime(title_id)) {
|
||||
log_debug(" [SHELLCORE] no workspace hook; runtime release deferred: %s",
|
||||
log_debug(" [SHELLCORE] no app-exit hook; runtime release deferred: %s",
|
||||
title_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
.global sm_shellcore_bridge_blob_start
|
||||
.global sm_shellcore_bridge_blob_end
|
||||
.global sm_shellcore_bridge_launch_hook
|
||||
.global sm_shellcore_bridge_unmount_hook
|
||||
.global sm_shellcore_bridge_app_exit_hook
|
||||
.global sm_shellcore_bridge_install_all_hook
|
||||
.global sm_shellcore_bridge_launch_trampoline
|
||||
.global sm_shellcore_bridge_unmount_trampoline
|
||||
.global sm_shellcore_bridge_app_exit_trampoline
|
||||
.global sm_shellcore_bridge_install_all_trampoline
|
||||
.global sm_shellcore_bridge_install_title_dir
|
||||
.global sm_shellcore_bridge_install_armed
|
||||
@@ -70,15 +70,14 @@ sm_shellcore_bridge_launch_hook:
|
||||
|
||||
.p2align 4
|
||||
/*
|
||||
* LncApplication::unmountWorkspace() first removes FsSandbox mappings and
|
||||
* tears down m_workspace. Notify ShadowMount only after the original method
|
||||
* returns. The first LncApplication dword is m_appId (also passed by callers
|
||||
* to endAppMount), which lets the service reject unrelated application exits.
|
||||
* LncManager::onAppExit(uint32_t app_id) owns the SysCore application-exit
|
||||
* event. Notify ShadowMount only after the original method returns, after its
|
||||
* firmware-specific exit cleanup. The app id is the second argument.
|
||||
*/
|
||||
sm_shellcore_bridge_unmount_hook:
|
||||
sm_shellcore_bridge_app_exit_hook:
|
||||
pushq %rbx
|
||||
movl (%rdi), %ebx
|
||||
call *sm_shellcore_bridge_unmount_trampoline(%rip)
|
||||
movl %esi, %ebx
|
||||
call *sm_shellcore_bridge_app_exit_trampoline(%rip)
|
||||
pushq %rax
|
||||
subq $8, %rsp
|
||||
movl $3, %edi
|
||||
@@ -196,7 +195,7 @@ sm_shellcore_bridge_exchange:
|
||||
.p2align 3
|
||||
sm_shellcore_bridge_launch_trampoline:
|
||||
.quad 0
|
||||
sm_shellcore_bridge_unmount_trampoline:
|
||||
sm_shellcore_bridge_app_exit_trampoline:
|
||||
.quad 0
|
||||
sm_shellcore_bridge_install_all_trampoline:
|
||||
.quad 0
|
||||
|
||||
+13
-13
@@ -71,10 +71,10 @@ int kernel_dynlib_obj(pid_t pid, uint32_t handle, shellcore_dynlib_obj_t *obj);
|
||||
extern const uint8_t sm_shellcore_bridge_blob_start[];
|
||||
extern const uint8_t sm_shellcore_bridge_blob_end[];
|
||||
extern const uint8_t sm_shellcore_bridge_launch_hook[];
|
||||
extern const uint8_t sm_shellcore_bridge_unmount_hook[];
|
||||
extern const uint8_t sm_shellcore_bridge_app_exit_hook[];
|
||||
extern const uint8_t sm_shellcore_bridge_install_all_hook[];
|
||||
extern const uint8_t sm_shellcore_bridge_launch_trampoline[];
|
||||
extern const uint8_t sm_shellcore_bridge_unmount_trampoline[];
|
||||
extern const uint8_t sm_shellcore_bridge_app_exit_trampoline[];
|
||||
extern const uint8_t sm_shellcore_bridge_install_all_trampoline[];
|
||||
extern const uint8_t sm_shellcore_bridge_install_title_dir[];
|
||||
extern const uint8_t sm_shellcore_bridge_install_armed[];
|
||||
@@ -260,12 +260,12 @@ static bool install_hooks_for_pid(pid_t pid) {
|
||||
: SHELLCORE_BASE_HOOK_COUNT;
|
||||
uintptr_t targets[SHELLCORE_MAX_HOOK_COUNT] = {
|
||||
remote.targets[SM_SHELLCORE_TARGET_LAUNCH_APP],
|
||||
remote.targets[SM_SHELLCORE_TARGET_UNMOUNT_WORKSPACE],
|
||||
remote.targets[SM_SHELLCORE_TARGET_APP_EXIT],
|
||||
remote.targets[SM_SHELLCORE_TARGET_INSTALL_ALL],
|
||||
};
|
||||
sm_shellcore_target_t target_ids[SHELLCORE_MAX_HOOK_COUNT] = {
|
||||
SM_SHELLCORE_TARGET_LAUNCH_APP,
|
||||
SM_SHELLCORE_TARGET_UNMOUNT_WORKSPACE,
|
||||
SM_SHELLCORE_TARGET_APP_EXIT,
|
||||
SM_SHELLCORE_TARGET_INSTALL_ALL,
|
||||
};
|
||||
|
||||
@@ -290,7 +290,7 @@ static bool install_hooks_for_pid(pid_t pid) {
|
||||
|
||||
size_t cursor = blob_size;
|
||||
uintptr_t launch_trampoline = 0;
|
||||
uintptr_t unmount_trampoline = 0;
|
||||
uintptr_t app_exit_trampoline = 0;
|
||||
uintptr_t install_all_trampoline = 0;
|
||||
if (!append_trampoline(bridge, bridge_capacity, &cursor, g_hooks.original[0],
|
||||
g_hooks.original_size[0],
|
||||
@@ -303,11 +303,11 @@ static bool install_hooks_for_pid(pid_t pid) {
|
||||
if (!append_trampoline(bridge, bridge_capacity, &cursor, g_hooks.original[1],
|
||||
g_hooks.original_size[1],
|
||||
targets[1] + g_hooks.original_size[1], object.eh_frame,
|
||||
&unmount_trampoline)) {
|
||||
&app_exit_trampoline)) {
|
||||
goto done;
|
||||
}
|
||||
set_bridge_pointer(bridge, sm_shellcore_bridge_unmount_trampoline,
|
||||
unmount_trampoline);
|
||||
set_bridge_pointer(bridge, sm_shellcore_bridge_app_exit_trampoline,
|
||||
app_exit_trampoline);
|
||||
if (install_hook_enabled) {
|
||||
if (!append_trampoline(bridge, bridge_capacity, &cursor,
|
||||
g_hooks.original[2], g_hooks.original_size[2],
|
||||
@@ -335,8 +335,8 @@ static bool install_hooks_for_pid(pid_t pid) {
|
||||
uintptr_t launch_hook =
|
||||
object.eh_frame + (uintptr_t)(sm_shellcore_bridge_launch_hook -
|
||||
sm_shellcore_bridge_blob_start);
|
||||
uintptr_t unmount_hook =
|
||||
object.eh_frame + (uintptr_t)(sm_shellcore_bridge_unmount_hook -
|
||||
uintptr_t app_exit_hook =
|
||||
object.eh_frame + (uintptr_t)(sm_shellcore_bridge_app_exit_hook -
|
||||
sm_shellcore_bridge_blob_start);
|
||||
uintptr_t install_all_hook =
|
||||
object.eh_frame +
|
||||
@@ -344,7 +344,7 @@ static bool install_hooks_for_pid(pid_t pid) {
|
||||
sm_shellcore_bridge_blob_start);
|
||||
if (!patch_remote_jump(pid, targets[0], launch_hook,
|
||||
g_hooks.original_size[0]) ||
|
||||
!patch_remote_jump(pid, targets[1], unmount_hook,
|
||||
!patch_remote_jump(pid, targets[1], app_exit_hook,
|
||||
g_hooks.original_size[1]) ||
|
||||
(install_hook_enabled &&
|
||||
!patch_remote_jump(pid, targets[2], install_all_hook,
|
||||
@@ -360,7 +360,7 @@ static bool install_hooks_for_pid(pid_t pid) {
|
||||
g_hooks.installed = true;
|
||||
ok = true;
|
||||
log_debug(" [SHELLCORE] hooks installed: fw=%s pid=%ld lifecycle=1 "
|
||||
"workspace=1 install=%d",
|
||||
"app_exit=1 install=%d",
|
||||
remote.offsets->name, (long)pid, install_hook_enabled ? 1 : 0);
|
||||
goto done;
|
||||
|
||||
@@ -417,7 +417,7 @@ void sm_shellcore_hooks_stop(void) {
|
||||
pthread_mutex_unlock(&g_install_mutex);
|
||||
}
|
||||
|
||||
bool sm_shellcore_workspace_hook_active(void) {
|
||||
bool sm_shellcore_app_exit_hook_active(void) {
|
||||
int saved_errno = errno;
|
||||
pthread_mutex_lock(&g_install_mutex);
|
||||
bool active = g_hooks.installed && g_hooks.remote.pid > 0;
|
||||
|
||||
@@ -21,8 +21,8 @@ const char *sm_shellcore_target_name(sm_shellcore_target_t target) {
|
||||
switch (target) {
|
||||
case SM_SHELLCORE_TARGET_LAUNCH_APP:
|
||||
return "launchApp";
|
||||
case SM_SHELLCORE_TARGET_UNMOUNT_WORKSPACE:
|
||||
return "unmountWorkspace";
|
||||
case SM_SHELLCORE_TARGET_APP_EXIT:
|
||||
return "onAppExit";
|
||||
case SM_SHELLCORE_TARGET_INSTALL_TITLE_DIR:
|
||||
return "AppInstallTitleDirMain";
|
||||
case SM_SHELLCORE_TARGET_INSTALL_ALL:
|
||||
|
||||
+118
-117
@@ -1,285 +1,286 @@
|
||||
/* Generated by tools/generate_shellcore_offsets_from_files.py. */
|
||||
{
|
||||
.firmware = 0x0100u,
|
||||
.name = "1.00",
|
||||
.targets = {{.offset = 0x3465a0u, .patch_size = 12u}, {.offset = 0x3479b0u, .patch_size = 12u}, {.offset = 0x1adc50u, .patch_size = 12u}, {.offset = 0xa05c40u, .patch_size = 14u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x1000u,
|
||||
.name = "10.00",
|
||||
.targets = {{.offset = 0x5ca1a0u, .patch_size = 12u}, {.offset = 0x605140u, .patch_size = 13u}, {.offset = 0x2b6700u, .patch_size = 12u}, {.offset = 0x17d1f00u, .patch_size = 16u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x1001u,
|
||||
.name = "10.01",
|
||||
.targets = {{.offset = 0x5ca1a0u, .patch_size = 12u}, {.offset = 0x605140u, .patch_size = 13u}, {.offset = 0x2b6700u, .patch_size = 12u}, {.offset = 0x17d1f00u, .patch_size = 16u}},
|
||||
.targets = {{.offset = 0x3465a0u, .patch_size = 12u}, {.offset = 0x3498e0u, .patch_size = 12u}, {.offset = 0x1adc50u, .patch_size = 12u}, {.offset = 0xa05c40u, .patch_size = 14u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0101u,
|
||||
.name = "1.01",
|
||||
.targets = {{.offset = 0x3465a0u, .patch_size = 12u}, {.offset = 0x3479b0u, .patch_size = 12u}, {.offset = 0x1adc50u, .patch_size = 12u}, {.offset = 0xa05c40u, .patch_size = 14u}},
|
||||
.targets = {{.offset = 0x3465a0u, .patch_size = 12u}, {.offset = 0x3498e0u, .patch_size = 12u}, {.offset = 0x1adc50u, .patch_size = 12u}, {.offset = 0xa05c40u, .patch_size = 14u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0102u,
|
||||
.name = "1.02",
|
||||
.targets = {{.offset = 0x3465a0u, .patch_size = 12u}, {.offset = 0x3479b0u, .patch_size = 12u}, {.offset = 0x1adc50u, .patch_size = 12u}, {.offset = 0xa05c40u, .patch_size = 14u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x1020u,
|
||||
.name = "10.20",
|
||||
.targets = {{.offset = 0x5ca1a0u, .patch_size = 12u}, {.offset = 0x605140u, .patch_size = 13u}, {.offset = 0x2b6700u, .patch_size = 12u}, {.offset = 0x17d61c0u, .patch_size = 16u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x1040u,
|
||||
.name = "10.40",
|
||||
.targets = {{.offset = 0x5ca0f0u, .patch_size = 12u}, {.offset = 0x605090u, .patch_size = 13u}, {.offset = 0x2b6760u, .patch_size = 12u}, {.offset = 0x17d61e0u, .patch_size = 16u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x1060u,
|
||||
.name = "10.60",
|
||||
.targets = {{.offset = 0x5cb980u, .patch_size = 12u}, {.offset = 0x606920u, .patch_size = 13u}, {.offset = 0x2b80c0u, .patch_size = 12u}, {.offset = 0x17d7a60u, .patch_size = 16u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x1100u,
|
||||
.name = "11.00",
|
||||
.targets = {{.offset = 0x62a940u, .patch_size = 12u}, {.offset = 0x6668d0u, .patch_size = 15u}, {.offset = 0x2bd880u, .patch_size = 12u}, {.offset = 0x2c4e60u, .patch_size = 15u}},
|
||||
.targets = {{.offset = 0x3465a0u, .patch_size = 12u}, {.offset = 0x3498e0u, .patch_size = 12u}, {.offset = 0x1adc50u, .patch_size = 12u}, {.offset = 0xa05c40u, .patch_size = 14u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0112u,
|
||||
.name = "1.12",
|
||||
.targets = {{.offset = 0x3469b0u, .patch_size = 12u}, {.offset = 0x347dc0u, .patch_size = 12u}, {.offset = 0x1adc30u, .patch_size = 12u}, {.offset = 0xa063a0u, .patch_size = 14u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x1120u,
|
||||
.name = "11.20",
|
||||
.targets = {{.offset = 0x62aab0u, .patch_size = 12u}, {.offset = 0x666a40u, .patch_size = 15u}, {.offset = 0x2bd930u, .patch_size = 12u}, {.offset = 0x2c4f10u, .patch_size = 15u}},
|
||||
.targets = {{.offset = 0x3469b0u, .patch_size = 12u}, {.offset = 0x349cf0u, .patch_size = 12u}, {.offset = 0x1adc30u, .patch_size = 12u}, {.offset = 0xa063a0u, .patch_size = 14u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0114u,
|
||||
.name = "1.14",
|
||||
.targets = {{.offset = 0x3469b0u, .patch_size = 12u}, {.offset = 0x347dc0u, .patch_size = 12u}, {.offset = 0x1adc30u, .patch_size = 12u}, {.offset = 0xa06660u, .patch_size = 14u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x1140u,
|
||||
.name = "11.40",
|
||||
.targets = {{.offset = 0x62ba90u, .patch_size = 12u}, {.offset = 0x667a20u, .patch_size = 15u}, {.offset = 0x2be910u, .patch_size = 12u}, {.offset = 0x2c5ef0u, .patch_size = 15u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x1160u,
|
||||
.name = "11.60",
|
||||
.targets = {{.offset = 0x633500u, .patch_size = 12u}, {.offset = 0x66f490u, .patch_size = 15u}, {.offset = 0x2c2c10u, .patch_size = 12u}, {.offset = 0x2ca1f0u, .patch_size = 15u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x1200u,
|
||||
.name = "12.00",
|
||||
.targets = {{.offset = 0x647410u, .patch_size = 12u}, {.offset = 0x683720u, .patch_size = 15u}, {.offset = 0x2e0050u, .patch_size = 12u}, {.offset = 0x2dd100u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x1202u,
|
||||
.name = "12.02",
|
||||
.targets = {{.offset = 0x647410u, .patch_size = 12u}, {.offset = 0x683720u, .patch_size = 15u}, {.offset = 0x2e0050u, .patch_size = 12u}, {.offset = 0x2dd100u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x1220u,
|
||||
.name = "12.20",
|
||||
.targets = {{.offset = 0x647410u, .patch_size = 12u}, {.offset = 0x683720u, .patch_size = 15u}, {.offset = 0x2e0050u, .patch_size = 12u}, {.offset = 0x2dd100u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x1240u,
|
||||
.name = "12.40",
|
||||
.targets = {{.offset = 0x647410u, .patch_size = 12u}, {.offset = 0x683720u, .patch_size = 15u}, {.offset = 0x2e0050u, .patch_size = 12u}, {.offset = 0x2dd100u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x1260u,
|
||||
.name = "12.60",
|
||||
.targets = {{.offset = 0x648660u, .patch_size = 12u}, {.offset = 0x684970u, .patch_size = 15u}, {.offset = 0x2e0320u, .patch_size = 12u}, {.offset = 0x2dd3d0u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x1270u,
|
||||
.name = "12.70",
|
||||
.targets = {{.offset = 0x648660u, .patch_size = 12u}, {.offset = 0x684970u, .patch_size = 15u}, {.offset = 0x2e0320u, .patch_size = 12u}, {.offset = 0x2dd3d0u, .patch_size = 12u}},
|
||||
.targets = {{.offset = 0x3469b0u, .patch_size = 12u}, {.offset = 0x349cf0u, .patch_size = 12u}, {.offset = 0x1adc30u, .patch_size = 12u}, {.offset = 0xa06660u, .patch_size = 14u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0200u,
|
||||
.name = "2.00",
|
||||
.targets = {{.offset = 0x3a4660u, .patch_size = 12u}, {.offset = 0x3a5aa0u, .patch_size = 12u}, {.offset = 0x1df8f0u, .patch_size = 12u}, {.offset = 0xaf3740u, .patch_size = 14u}},
|
||||
.targets = {{.offset = 0x3a4660u, .patch_size = 12u}, {.offset = 0x3a7cf0u, .patch_size = 12u}, {.offset = 0x1df8f0u, .patch_size = 12u}, {.offset = 0xaf3740u, .patch_size = 14u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0220u,
|
||||
.name = "2.20",
|
||||
.targets = {{.offset = 0x3a4930u, .patch_size = 12u}, {.offset = 0x3a5d70u, .patch_size = 12u}, {.offset = 0x1dfb80u, .patch_size = 12u}, {.offset = 0xaf3aa0u, .patch_size = 14u}},
|
||||
.targets = {{.offset = 0x3a4930u, .patch_size = 12u}, {.offset = 0x3a7fc0u, .patch_size = 12u}, {.offset = 0x1dfb80u, .patch_size = 12u}, {.offset = 0xaf3aa0u, .patch_size = 14u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0225u,
|
||||
.name = "2.25",
|
||||
.targets = {{.offset = 0x3a4e80u, .patch_size = 12u}, {.offset = 0x3a62c0u, .patch_size = 12u}, {.offset = 0x1dfb80u, .patch_size = 12u}, {.offset = 0xaf3fe0u, .patch_size = 14u}},
|
||||
.targets = {{.offset = 0x3a4e80u, .patch_size = 12u}, {.offset = 0x3a8510u, .patch_size = 12u}, {.offset = 0x1dfb80u, .patch_size = 12u}, {.offset = 0xaf3fe0u, .patch_size = 14u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0226u,
|
||||
.name = "2.26",
|
||||
.targets = {{.offset = 0x3a65f0u, .patch_size = 12u}, {.offset = 0x3a7a30u, .patch_size = 12u}, {.offset = 0x1e0d50u, .patch_size = 12u}, {.offset = 0xaf57a0u, .patch_size = 14u}},
|
||||
.targets = {{.offset = 0x3a65f0u, .patch_size = 12u}, {.offset = 0x3a9c80u, .patch_size = 12u}, {.offset = 0x1e0d50u, .patch_size = 12u}, {.offset = 0xaf57a0u, .patch_size = 14u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0230u,
|
||||
.name = "2.30",
|
||||
.targets = {{.offset = 0x3a6d90u, .patch_size = 12u}, {.offset = 0x3a81d0u, .patch_size = 12u}, {.offset = 0x1e0f00u, .patch_size = 12u}, {.offset = 0xaf6360u, .patch_size = 14u}},
|
||||
.targets = {{.offset = 0x3a6d90u, .patch_size = 12u}, {.offset = 0x3aa420u, .patch_size = 12u}, {.offset = 0x1e0f00u, .patch_size = 12u}, {.offset = 0xaf6360u, .patch_size = 14u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0250u,
|
||||
.name = "2.50",
|
||||
.targets = {{.offset = 0x3a8140u, .patch_size = 12u}, {.offset = 0x3a9580u, .patch_size = 12u}, {.offset = 0x1e0ca0u, .patch_size = 12u}, {.offset = 0xaf7700u, .patch_size = 14u}},
|
||||
.targets = {{.offset = 0x3a8140u, .patch_size = 12u}, {.offset = 0x3ab7d0u, .patch_size = 12u}, {.offset = 0x1e0ca0u, .patch_size = 12u}, {.offset = 0xaf7700u, .patch_size = 14u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0270u,
|
||||
.name = "2.70",
|
||||
.targets = {{.offset = 0x3a8140u, .patch_size = 12u}, {.offset = 0x3a9580u, .patch_size = 12u}, {.offset = 0x1e0ca0u, .patch_size = 12u}, {.offset = 0xaf7700u, .patch_size = 14u}},
|
||||
.targets = {{.offset = 0x3a8140u, .patch_size = 12u}, {.offset = 0x3ab7d0u, .patch_size = 12u}, {.offset = 0x1e0ca0u, .patch_size = 12u}, {.offset = 0xaf7700u, .patch_size = 14u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0300u,
|
||||
.name = "3.00",
|
||||
.targets = {{.offset = 0x3f1960u, .patch_size = 12u}, {.offset = 0x3f2ef0u, .patch_size = 12u}, {.offset = 0x2138e0u, .patch_size = 12u}, {.offset = 0x212ab0u, .patch_size = 12u}},
|
||||
.targets = {{.offset = 0x3f1960u, .patch_size = 12u}, {.offset = 0x3f51e0u, .patch_size = 12u}, {.offset = 0x2138e0u, .patch_size = 12u}, {.offset = 0x212ab0u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0310u,
|
||||
.name = "3.10",
|
||||
.targets = {{.offset = 0x3f19a0u, .patch_size = 12u}, {.offset = 0x3f2f30u, .patch_size = 12u}, {.offset = 0x213920u, .patch_size = 12u}, {.offset = 0x212af0u, .patch_size = 12u}},
|
||||
.targets = {{.offset = 0x3f19a0u, .patch_size = 12u}, {.offset = 0x3f5220u, .patch_size = 12u}, {.offset = 0x213920u, .patch_size = 12u}, {.offset = 0x212af0u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0320u,
|
||||
.name = "3.20",
|
||||
.targets = {{.offset = 0x3f1a50u, .patch_size = 12u}, {.offset = 0x3f2fe0u, .patch_size = 12u}, {.offset = 0x2139d0u, .patch_size = 12u}, {.offset = 0x212ba0u, .patch_size = 12u}},
|
||||
.targets = {{.offset = 0x3f1a50u, .patch_size = 12u}, {.offset = 0x3f52d0u, .patch_size = 12u}, {.offset = 0x2139d0u, .patch_size = 12u}, {.offset = 0x212ba0u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0321u,
|
||||
.name = "3.21",
|
||||
.targets = {{.offset = 0x3f1a50u, .patch_size = 12u}, {.offset = 0x3f2fe0u, .patch_size = 12u}, {.offset = 0x2139d0u, .patch_size = 12u}, {.offset = 0x212ba0u, .patch_size = 12u}},
|
||||
.targets = {{.offset = 0x3f1a50u, .patch_size = 12u}, {.offset = 0x3f52d0u, .patch_size = 12u}, {.offset = 0x2139d0u, .patch_size = 12u}, {.offset = 0x212ba0u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0400u,
|
||||
.name = "4.00",
|
||||
.targets = {{.offset = 0x432630u, .patch_size = 12u}, {.offset = 0x433d90u, .patch_size = 12u}, {.offset = 0x226c20u, .patch_size = 12u}, {.offset = 0x225e40u, .patch_size = 12u}},
|
||||
.targets = {{.offset = 0x432630u, .patch_size = 12u}, {.offset = 0x436460u, .patch_size = 12u}, {.offset = 0x226c20u, .patch_size = 12u}, {.offset = 0x225e40u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0403u,
|
||||
.name = "4.03",
|
||||
.targets = {{.offset = 0x432630u, .patch_size = 12u}, {.offset = 0x433d90u, .patch_size = 12u}, {.offset = 0x226c20u, .patch_size = 12u}, {.offset = 0x225e40u, .patch_size = 12u}},
|
||||
.targets = {{.offset = 0x432630u, .patch_size = 12u}, {.offset = 0x436460u, .patch_size = 12u}, {.offset = 0x226c20u, .patch_size = 12u}, {.offset = 0x225e40u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0450u,
|
||||
.name = "4.50",
|
||||
.targets = {{.offset = 0x432d80u, .patch_size = 12u}, {.offset = 0x4344e0u, .patch_size = 12u}, {.offset = 0x227350u, .patch_size = 12u}, {.offset = 0x226570u, .patch_size = 12u}},
|
||||
.targets = {{.offset = 0x432d80u, .patch_size = 12u}, {.offset = 0x436bb0u, .patch_size = 12u}, {.offset = 0x227350u, .patch_size = 12u}, {.offset = 0x226570u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0451u,
|
||||
.name = "4.51",
|
||||
.targets = {{.offset = 0x432d80u, .patch_size = 12u}, {.offset = 0x4344e0u, .patch_size = 12u}, {.offset = 0x227350u, .patch_size = 12u}, {.offset = 0x226570u, .patch_size = 12u}},
|
||||
.targets = {{.offset = 0x432d80u, .patch_size = 12u}, {.offset = 0x436bb0u, .patch_size = 12u}, {.offset = 0x227350u, .patch_size = 12u}, {.offset = 0x226570u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0500u,
|
||||
.name = "5.00",
|
||||
.targets = {{.offset = 0x498950u, .patch_size = 12u}, {.offset = 0x49a0a0u, .patch_size = 12u}, {.offset = 0x2556a0u, .patch_size = 12u}, {.offset = 0x254900u, .patch_size = 12u}},
|
||||
.targets = {{.offset = 0x498950u, .patch_size = 12u}, {.offset = 0x49c640u, .patch_size = 12u}, {.offset = 0x2556a0u, .patch_size = 12u}, {.offset = 0x254900u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0502u,
|
||||
.name = "5.02",
|
||||
.targets = {{.offset = 0x498940u, .patch_size = 12u}, {.offset = 0x49a090u, .patch_size = 12u}, {.offset = 0x2556a0u, .patch_size = 12u}, {.offset = 0x254900u, .patch_size = 12u}},
|
||||
.targets = {{.offset = 0x498940u, .patch_size = 12u}, {.offset = 0x49c630u, .patch_size = 12u}, {.offset = 0x2556a0u, .patch_size = 12u}, {.offset = 0x254900u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0510u,
|
||||
.name = "5.10",
|
||||
.targets = {{.offset = 0x4998f0u, .patch_size = 12u}, {.offset = 0x49b650u, .patch_size = 12u}, {.offset = 0x256570u, .patch_size = 12u}, {.offset = 0x2557d0u, .patch_size = 12u}},
|
||||
.targets = {{.offset = 0x4998f0u, .patch_size = 12u}, {.offset = 0x49dfc0u, .patch_size = 12u}, {.offset = 0x256570u, .patch_size = 12u}, {.offset = 0x2557d0u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0550u,
|
||||
.name = "5.50",
|
||||
.targets = {{.offset = 0x4998f0u, .patch_size = 12u}, {.offset = 0x49b650u, .patch_size = 12u}, {.offset = 0x256570u, .patch_size = 12u}, {.offset = 0x2557d0u, .patch_size = 12u}},
|
||||
.targets = {{.offset = 0x4998f0u, .patch_size = 12u}, {.offset = 0x49dfc0u, .patch_size = 12u}, {.offset = 0x256570u, .patch_size = 12u}, {.offset = 0x2557d0u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0600u,
|
||||
.name = "6.00",
|
||||
.targets = {{.offset = 0x4c7380u, .patch_size = 12u}, {.offset = 0x4fdf70u, .patch_size = 15u}, {.offset = 0x26b9b0u, .patch_size = 12u}, {.offset = 0x271bb0u, .patch_size = 12u}},
|
||||
.targets = {{.offset = 0x4c7380u, .patch_size = 12u}, {.offset = 0x4cb8c0u, .patch_size = 12u}, {.offset = 0x26b9b0u, .patch_size = 12u}, {.offset = 0x271bb0u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0602u,
|
||||
.name = "6.02",
|
||||
.targets = {{.offset = 0x4c7380u, .patch_size = 12u}, {.offset = 0x4fdf70u, .patch_size = 15u}, {.offset = 0x26b9b0u, .patch_size = 12u}, {.offset = 0x271bb0u, .patch_size = 12u}},
|
||||
.targets = {{.offset = 0x4c7380u, .patch_size = 12u}, {.offset = 0x4cb8c0u, .patch_size = 12u}, {.offset = 0x26b9b0u, .patch_size = 12u}, {.offset = 0x271bb0u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0650u,
|
||||
.name = "6.50",
|
||||
.targets = {{.offset = 0x4c73f0u, .patch_size = 12u}, {.offset = 0x4fdfe0u, .patch_size = 15u}, {.offset = 0x26ba20u, .patch_size = 12u}, {.offset = 0x271c20u, .patch_size = 12u}},
|
||||
.targets = {{.offset = 0x4c73f0u, .patch_size = 12u}, {.offset = 0x4cb930u, .patch_size = 12u}, {.offset = 0x26ba20u, .patch_size = 12u}, {.offset = 0x271c20u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0700u,
|
||||
.name = "7.00",
|
||||
.targets = {{.offset = 0x568920u, .patch_size = 12u}, {.offset = 0x5a85d0u, .patch_size = 15u}, {.offset = 0x2852e0u, .patch_size = 12u}, {.offset = 0x28b690u, .patch_size = 12u}},
|
||||
.targets = {{.offset = 0x568920u, .patch_size = 12u}, {.offset = 0x56e9b0u, .patch_size = 12u}, {.offset = 0x2852e0u, .patch_size = 12u}, {.offset = 0x28b690u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0701u,
|
||||
.name = "7.01",
|
||||
.targets = {{.offset = 0x568920u, .patch_size = 12u}, {.offset = 0x5a85d0u, .patch_size = 15u}, {.offset = 0x2852e0u, .patch_size = 12u}, {.offset = 0x28b690u, .patch_size = 12u}},
|
||||
.targets = {{.offset = 0x568920u, .patch_size = 12u}, {.offset = 0x56e9b0u, .patch_size = 12u}, {.offset = 0x2852e0u, .patch_size = 12u}, {.offset = 0x28b690u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0720u,
|
||||
.name = "7.20",
|
||||
.targets = {{.offset = 0x568940u, .patch_size = 12u}, {.offset = 0x5a85f0u, .patch_size = 15u}, {.offset = 0x2852e0u, .patch_size = 12u}, {.offset = 0x28b690u, .patch_size = 12u}},
|
||||
.targets = {{.offset = 0x568940u, .patch_size = 12u}, {.offset = 0x56e9d0u, .patch_size = 12u}, {.offset = 0x2852e0u, .patch_size = 12u}, {.offset = 0x28b690u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0740u,
|
||||
.name = "7.40",
|
||||
.targets = {{.offset = 0x56d430u, .patch_size = 12u}, {.offset = 0x5ad0e0u, .patch_size = 15u}, {.offset = 0x2896b0u, .patch_size = 12u}, {.offset = 0x28fa60u, .patch_size = 12u}},
|
||||
.targets = {{.offset = 0x56d430u, .patch_size = 12u}, {.offset = 0x5734c0u, .patch_size = 12u}, {.offset = 0x2896b0u, .patch_size = 12u}, {.offset = 0x28fa60u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0760u,
|
||||
.name = "7.60",
|
||||
.targets = {{.offset = 0x56d430u, .patch_size = 12u}, {.offset = 0x5ad0e0u, .patch_size = 15u}, {.offset = 0x2896b0u, .patch_size = 12u}, {.offset = 0x28fa60u, .patch_size = 12u}},
|
||||
.targets = {{.offset = 0x56d430u, .patch_size = 12u}, {.offset = 0x5734c0u, .patch_size = 12u}, {.offset = 0x2896b0u, .patch_size = 12u}, {.offset = 0x28fa60u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0761u,
|
||||
.name = "7.61",
|
||||
.targets = {{.offset = 0x56d430u, .patch_size = 12u}, {.offset = 0x5ad0e0u, .patch_size = 15u}, {.offset = 0x2896b0u, .patch_size = 12u}, {.offset = 0x28fa60u, .patch_size = 12u}},
|
||||
.targets = {{.offset = 0x56d430u, .patch_size = 12u}, {.offset = 0x5734c0u, .patch_size = 12u}, {.offset = 0x2896b0u, .patch_size = 12u}, {.offset = 0x28fa60u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0800u,
|
||||
.name = "8.00",
|
||||
.targets = {{.offset = 0x597eb0u, .patch_size = 12u}, {.offset = 0x5d3060u, .patch_size = 13u}, {.offset = 0x29a730u, .patch_size = 12u}, {.offset = 0x2a12f0u, .patch_size = 12u}},
|
||||
.targets = {{.offset = 0x597eb0u, .patch_size = 12u}, {.offset = 0x59c640u, .patch_size = 12u}, {.offset = 0x29a730u, .patch_size = 12u}, {.offset = 0x2a12f0u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0820u,
|
||||
.name = "8.20",
|
||||
.targets = {{.offset = 0x598f80u, .patch_size = 12u}, {.offset = 0x5d4130u, .patch_size = 13u}, {.offset = 0x29a9a0u, .patch_size = 12u}, {.offset = 0x2a1560u, .patch_size = 12u}},
|
||||
.targets = {{.offset = 0x598f80u, .patch_size = 12u}, {.offset = 0x59d710u, .patch_size = 12u}, {.offset = 0x29a9a0u, .patch_size = 12u}, {.offset = 0x2a1560u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0840u,
|
||||
.name = "8.40",
|
||||
.targets = {{.offset = 0x598f80u, .patch_size = 12u}, {.offset = 0x5d4130u, .patch_size = 13u}, {.offset = 0x29a9a0u, .patch_size = 12u}, {.offset = 0x2a1560u, .patch_size = 12u}},
|
||||
.targets = {{.offset = 0x598f80u, .patch_size = 12u}, {.offset = 0x59d710u, .patch_size = 12u}, {.offset = 0x29a9a0u, .patch_size = 12u}, {.offset = 0x2a1560u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0860u,
|
||||
.name = "8.60",
|
||||
.targets = {{.offset = 0x598d60u, .patch_size = 12u}, {.offset = 0x5d3f10u, .patch_size = 13u}, {.offset = 0x29a750u, .patch_size = 12u}, {.offset = 0x2a1310u, .patch_size = 12u}},
|
||||
.targets = {{.offset = 0x598d60u, .patch_size = 12u}, {.offset = 0x59d4f0u, .patch_size = 12u}, {.offset = 0x29a750u, .patch_size = 12u}, {.offset = 0x2a1310u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0900u,
|
||||
.name = "9.00",
|
||||
.targets = {{.offset = 0x5cc0a0u, .patch_size = 12u}, {.offset = 0x6088e0u, .patch_size = 13u}, {.offset = 0x2b74d0u, .patch_size = 12u}, {.offset = 0x2be0e0u, .patch_size = 12u}},
|
||||
.targets = {{.offset = 0x5cc0a0u, .patch_size = 12u}, {.offset = 0x5d0a80u, .patch_size = 12u}, {.offset = 0x2b74d0u, .patch_size = 12u}, {.offset = 0x2be0e0u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0905u,
|
||||
.name = "9.05",
|
||||
.targets = {{.offset = 0x5cc0a0u, .patch_size = 12u}, {.offset = 0x6088e0u, .patch_size = 13u}, {.offset = 0x2b74d0u, .patch_size = 12u}, {.offset = 0x2be0e0u, .patch_size = 12u}},
|
||||
.targets = {{.offset = 0x5cc0a0u, .patch_size = 12u}, {.offset = 0x5d0a80u, .patch_size = 12u}, {.offset = 0x2b74d0u, .patch_size = 12u}, {.offset = 0x2be0e0u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0920u,
|
||||
.name = "9.20",
|
||||
.targets = {{.offset = 0x5cbdc0u, .patch_size = 12u}, {.offset = 0x608600u, .patch_size = 13u}, {.offset = 0x2b74d0u, .patch_size = 12u}, {.offset = 0x2be0e0u, .patch_size = 12u}},
|
||||
.targets = {{.offset = 0x5cbdc0u, .patch_size = 12u}, {.offset = 0x5d07a0u, .patch_size = 12u}, {.offset = 0x2b74d0u, .patch_size = 12u}, {.offset = 0x2be0e0u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0940u,
|
||||
.name = "9.40",
|
||||
.targets = {{.offset = 0x5cc490u, .patch_size = 12u}, {.offset = 0x608cd0u, .patch_size = 13u}, {.offset = 0x2b7ba0u, .patch_size = 12u}, {.offset = 0x2be7b0u, .patch_size = 12u}},
|
||||
.targets = {{.offset = 0x5cc490u, .patch_size = 12u}, {.offset = 0x5d0e70u, .patch_size = 12u}, {.offset = 0x2b7ba0u, .patch_size = 12u}, {.offset = 0x2be7b0u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x0960u,
|
||||
.name = "9.60",
|
||||
.targets = {{.offset = 0x5cc500u, .patch_size = 12u}, {.offset = 0x608d40u, .patch_size = 13u}, {.offset = 0x2b7c10u, .patch_size = 12u}, {.offset = 0x2be820u, .patch_size = 12u}},
|
||||
.targets = {{.offset = 0x5cc500u, .patch_size = 12u}, {.offset = 0x5d0ee0u, .patch_size = 12u}, {.offset = 0x2b7c10u, .patch_size = 12u}, {.offset = 0x2be820u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x1000u,
|
||||
.name = "10.00",
|
||||
.targets = {{.offset = 0x5ca1a0u, .patch_size = 12u}, {.offset = 0x5ce820u, .patch_size = 12u}, {.offset = 0x2b6700u, .patch_size = 12u}, {.offset = 0x17d1f00u, .patch_size = 16u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x1001u,
|
||||
.name = "10.01",
|
||||
.targets = {{.offset = 0x5ca1a0u, .patch_size = 12u}, {.offset = 0x5ce820u, .patch_size = 12u}, {.offset = 0x2b6700u, .patch_size = 12u}, {.offset = 0x17d1f00u, .patch_size = 16u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x1020u,
|
||||
.name = "10.20",
|
||||
.targets = {{.offset = 0x5ca1a0u, .patch_size = 12u}, {.offset = 0x5ce820u, .patch_size = 12u}, {.offset = 0x2b6700u, .patch_size = 12u}, {.offset = 0x17d61c0u, .patch_size = 16u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x1040u,
|
||||
.name = "10.40",
|
||||
.targets = {{.offset = 0x5ca0f0u, .patch_size = 12u}, {.offset = 0x5ce770u, .patch_size = 12u}, {.offset = 0x2b6760u, .patch_size = 12u}, {.offset = 0x17d61e0u, .patch_size = 16u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x1060u,
|
||||
.name = "10.60",
|
||||
.targets = {{.offset = 0x5cb980u, .patch_size = 12u}, {.offset = 0x5d0000u, .patch_size = 12u}, {.offset = 0x2b80c0u, .patch_size = 12u}, {.offset = 0x17d7a60u, .patch_size = 16u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x1100u,
|
||||
.name = "11.00",
|
||||
.targets = {{.offset = 0x62a940u, .patch_size = 12u}, {.offset = 0x62efe0u, .patch_size = 12u}, {.offset = 0x2bd880u, .patch_size = 12u}, {.offset = 0x2c4e60u, .patch_size = 15u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x1120u,
|
||||
.name = "11.20",
|
||||
.targets = {{.offset = 0x62aab0u, .patch_size = 12u}, {.offset = 0x62f150u, .patch_size = 12u}, {.offset = 0x2bd930u, .patch_size = 12u}, {.offset = 0x2c4f10u, .patch_size = 15u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x1140u,
|
||||
.name = "11.40",
|
||||
.targets = {{.offset = 0x62ba90u, .patch_size = 12u}, {.offset = 0x630130u, .patch_size = 12u}, {.offset = 0x2be910u, .patch_size = 12u}, {.offset = 0x2c5ef0u, .patch_size = 15u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x1160u,
|
||||
.name = "11.60",
|
||||
.targets = {{.offset = 0x633500u, .patch_size = 12u}, {.offset = 0x637ba0u, .patch_size = 12u}, {.offset = 0x2c2c10u, .patch_size = 12u}, {.offset = 0x2ca1f0u, .patch_size = 15u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x1200u,
|
||||
.name = "12.00",
|
||||
.targets = {{.offset = 0x647410u, .patch_size = 12u}, {.offset = 0x64bac0u, .patch_size = 12u}, {.offset = 0x2e0050u, .patch_size = 12u}, {.offset = 0x2dd100u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x1202u,
|
||||
.name = "12.02",
|
||||
.targets = {{.offset = 0x647410u, .patch_size = 12u}, {.offset = 0x64bac0u, .patch_size = 12u}, {.offset = 0x2e0050u, .patch_size = 12u}, {.offset = 0x2dd100u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x1220u,
|
||||
.name = "12.20",
|
||||
.targets = {{.offset = 0x647410u, .patch_size = 12u}, {.offset = 0x64bac0u, .patch_size = 12u}, {.offset = 0x2e0050u, .patch_size = 12u}, {.offset = 0x2dd100u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x1240u,
|
||||
.name = "12.40",
|
||||
.targets = {{.offset = 0x647410u, .patch_size = 12u}, {.offset = 0x64bac0u, .patch_size = 12u}, {.offset = 0x2e0050u, .patch_size = 12u}, {.offset = 0x2dd100u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x1260u,
|
||||
.name = "12.60",
|
||||
.targets = {{.offset = 0x648660u, .patch_size = 12u}, {.offset = 0x64cd10u, .patch_size = 12u}, {.offset = 0x2e0320u, .patch_size = 12u}, {.offset = 0x2dd3d0u, .patch_size = 12u}},
|
||||
},
|
||||
{
|
||||
.firmware = 0x1270u,
|
||||
.name = "12.70",
|
||||
.targets = {{.offset = 0x648660u, .patch_size = 12u}, {.offset = 0x64cd10u, .patch_size = 12u}, {.offset = 0x2e0320u, .patch_size = 12u}, {.offset = 0x2dd3d0u, .patch_size = 12u}},
|
||||
}
|
||||
+61
-18
@@ -22,8 +22,8 @@ _Static_assert(sizeof(sm_shellcore_request_t) == 40,
|
||||
"unexpected ShellCore request size");
|
||||
_Static_assert(offsetof(sm_shellcore_request_t, payload) == 8,
|
||||
"unexpected ShellCore request payload offset");
|
||||
_Static_assert(offsetof(sm_shellcore_request_t, payload.workspace.app_id) == 8,
|
||||
"unexpected ShellCore workspace app id offset");
|
||||
_Static_assert(offsetof(sm_shellcore_request_t, payload.app_exit.app_id) == 8,
|
||||
"unexpected ShellCore app-exit app id offset");
|
||||
_Static_assert(sizeof(sm_shellcore_response_t) == 4,
|
||||
"unexpected ShellCore response size");
|
||||
|
||||
@@ -49,6 +49,9 @@ static shellcore_service_state_t g_service = {
|
||||
.client_fd = -1,
|
||||
};
|
||||
|
||||
#define SHELLCORE_RELEASE_MAX_ATTEMPTS 5u
|
||||
#define SHELLCORE_RELEASE_RETRY_DELAY_US 150000u
|
||||
|
||||
static int claim_prepared_title(const char *title_id, bool launch_request,
|
||||
bool *claimed_out) {
|
||||
bool changed = false;
|
||||
@@ -134,6 +137,13 @@ static void end_runtime_release(void) {
|
||||
pthread_mutex_unlock(&g_service.mutex);
|
||||
}
|
||||
|
||||
static bool shellcore_service_stopping(void) {
|
||||
pthread_mutex_lock(&g_service.mutex);
|
||||
bool stopping = g_service.stop_requested;
|
||||
pthread_mutex_unlock(&g_service.mutex);
|
||||
return stopping;
|
||||
}
|
||||
|
||||
static bool find_required_image_layer(const char *root,
|
||||
const char *runtime_source,
|
||||
unsigned int depth,
|
||||
@@ -361,18 +371,47 @@ static bool release_title_runtime(const char *title_id,
|
||||
errno = begin_status;
|
||||
return false;
|
||||
}
|
||||
runtime_mount_state_lock();
|
||||
log_debug(" [SHELLCORE] runtime release start: %s", title_id);
|
||||
bool released = unmount_title_runtime_layers(title_id);
|
||||
if (released)
|
||||
released = release_backing_image(title_id);
|
||||
runtime_mount_state_unlock();
|
||||
bool released = false;
|
||||
int release_errno = 0;
|
||||
unsigned int attempts = public_request ? 1u : SHELLCORE_RELEASE_MAX_ATTEMPTS;
|
||||
unsigned int attempt = 0;
|
||||
for (; attempt < attempts; ++attempt) {
|
||||
bool diagnose_busy = attempt + 1u == attempts;
|
||||
runtime_mount_state_lock();
|
||||
released = diagnose_busy
|
||||
? unmount_title_runtime_layers(title_id)
|
||||
: unmount_title_runtime_layers_quiet(title_id);
|
||||
if (released)
|
||||
released = release_backing_image(title_id);
|
||||
release_errno = released ? 0 : errno;
|
||||
runtime_mount_state_unlock();
|
||||
|
||||
if (released || release_errno != EBUSY || attempt + 1u == attempts ||
|
||||
shellcore_service_stopping()) {
|
||||
break;
|
||||
}
|
||||
if (attempt == 0) {
|
||||
log_debug(" [SHELLCORE] runtime release busy; fast retry armed: %s "
|
||||
"attempts=%u delay_ms=%u",
|
||||
title_id, attempts - 1u,
|
||||
SHELLCORE_RELEASE_RETRY_DELAY_US / 1000u);
|
||||
}
|
||||
(void)sceKernelUsleep(SHELLCORE_RELEASE_RETRY_DELAY_US);
|
||||
}
|
||||
if (released && attempt > 0) {
|
||||
log_debug(" [SHELLCORE] runtime release completed after retry: %s "
|
||||
"attempt=%u",
|
||||
title_id, attempt + 1u);
|
||||
}
|
||||
// The lifecycle owner is gone even when a short-lived ShellCore sandbox
|
||||
// nullfs still pins the title stack. Leaving the title marked as prepared
|
||||
// would block scanner cleanup forever. The intact stack remains discoverable
|
||||
// through mount.lnk and is retried by the normal full-scan cleanup cycle.
|
||||
clear_prepared_title(title_id);
|
||||
end_runtime_release();
|
||||
if (!released && release_errno != 0)
|
||||
errno = release_errno;
|
||||
return released;
|
||||
}
|
||||
|
||||
@@ -413,7 +452,7 @@ bool sm_shellcore_service_note_game_exit(const char *title_id) {
|
||||
bool sm_shellcore_service_has_prepared_mount(void) {
|
||||
pthread_mutex_lock(&g_service.mutex);
|
||||
// Keep scanner cleanup blocked after process exit as well. Only the
|
||||
// post-unmountWorkspace event (or the explicit no-hook fallback) owns the
|
||||
// post-onAppExit event (or the explicit no-hook fallback) owns the
|
||||
// transition that clears this state and releases the runtime stack.
|
||||
bool prepared = g_service.prepared_title_id[0] != '\0';
|
||||
pthread_mutex_unlock(&g_service.mutex);
|
||||
@@ -442,28 +481,29 @@ static int handle_launch_failed_request(const char *title_id) {
|
||||
return sm_shellcore_release_title_runtime(title_id) ? 0 : EBUSY;
|
||||
}
|
||||
|
||||
static int handle_workspace_unmounted(uint32_t app_id) {
|
||||
static bool resolve_app_exit_title(uint32_t app_id,
|
||||
char title_id[MAX_TITLE_ID]) {
|
||||
if (app_id == 0)
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
char title_id[MAX_TITLE_ID] = {0};
|
||||
pthread_mutex_lock(&g_service.mutex);
|
||||
if (g_service.prepared_app_id == app_id &&
|
||||
g_service.prepared_title_id[0] != '\0') {
|
||||
(void)strlcpy(title_id, g_service.prepared_title_id, sizeof(title_id));
|
||||
(void)strlcpy(title_id, g_service.prepared_title_id, MAX_TITLE_ID);
|
||||
}
|
||||
pthread_mutex_unlock(&g_service.mutex);
|
||||
if (title_id[0] == '\0')
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
log_debug(" [SHELLCORE] unmountWorkspace complete: %s app_id=0x%08X",
|
||||
log_debug(" [SHELLCORE] onAppExit cleanup complete: %s app_id=0x%08X",
|
||||
title_id, app_id);
|
||||
return sm_shellcore_release_title_runtime(title_id) ? 0 : EBUSY;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void handle_client(int fd) {
|
||||
sm_shellcore_request_t request;
|
||||
sm_shellcore_response_t response;
|
||||
char release_title_id[MAX_TITLE_ID] = {0};
|
||||
if (!sm_socket_read_full(fd, &request, sizeof(request)))
|
||||
return;
|
||||
if (request.magic != SM_SHELLCORE_PROTOCOL_MAGIC ||
|
||||
@@ -477,13 +517,16 @@ static void handle_client(int fd) {
|
||||
response.status = handle_launch_request(request.payload.title_id);
|
||||
} else if (request.operation == SM_SHELLCORE_REQUEST_LAUNCH_FAILED) {
|
||||
response.status = handle_launch_failed_request(request.payload.title_id);
|
||||
} else if (request.operation == SM_SHELLCORE_REQUEST_WORKSPACE_UNMOUNTED) {
|
||||
response.status =
|
||||
handle_workspace_unmounted(request.payload.workspace.app_id);
|
||||
} else if (request.operation == SM_SHELLCORE_REQUEST_APP_EXITED) {
|
||||
(void)resolve_app_exit_title(request.payload.app_exit.app_id,
|
||||
release_title_id);
|
||||
response.status = 0;
|
||||
} else {
|
||||
response.status = ENOTSUP;
|
||||
}
|
||||
(void)sm_socket_write_full(fd, &response, sizeof(response));
|
||||
if (release_title_id[0] != '\0')
|
||||
(void)sm_shellcore_release_title_runtime(release_title_id);
|
||||
}
|
||||
|
||||
static void *service_thread_main(void *arg) {
|
||||
|
||||
@@ -5,6 +5,13 @@ The generator identifies functions through semantic log strings, finds the
|
||||
containing function prologue and emits a compact firmware offset table. It is
|
||||
intentionally independent from IDA databases so the checked-in table can be
|
||||
reproduced from unpacked firmware files.
|
||||
|
||||
The runtime-release hook targets ``LncManager::onAppExit`` rather than an
|
||||
application/workspace destructor. ShellCore can destroy a launch-time
|
||||
``LncApplication`` object with the live app id, so destructor hooks produce a
|
||||
false exit event while the game is still running. ``onAppExit`` is the
|
||||
SysCore exit-event owner and returns after the firmware-specific exit cleanup
|
||||
path has run.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -19,7 +26,7 @@ from capstone import CS_ARCH_X86, CS_MODE_64, Cs
|
||||
PROLOGUE = b"\x55\x48\x89\xe5"
|
||||
TARGET_NAMES = (
|
||||
"launch_app",
|
||||
"unmount_workspace",
|
||||
"app_exit",
|
||||
"install_title_dir",
|
||||
"install_all",
|
||||
)
|
||||
@@ -54,6 +61,7 @@ class ShellCore:
|
||||
self.executable = segment
|
||||
if self.executable is None:
|
||||
raise ValueError(f"{path}: executable PT_LOAD not found")
|
||||
self._rip_references: list[tuple[int, int]] | None = None
|
||||
|
||||
def file_to_virtual(self, offset: int) -> int:
|
||||
for segment in self.loads:
|
||||
@@ -85,25 +93,42 @@ class ShellCore:
|
||||
offset += 1
|
||||
|
||||
def rip_xrefs(self, targets: list[int], slop: int = 0) -> list[int]:
|
||||
segment = self.executable
|
||||
assert segment is not None
|
||||
code = self.data[
|
||||
segment.file_offset : segment.file_offset + segment.file_size
|
||||
if self._rip_references is None:
|
||||
segment = self.executable
|
||||
assert segment is not None
|
||||
code = self.data[
|
||||
segment.file_offset : segment.file_offset + segment.file_size
|
||||
]
|
||||
references: list[tuple[int, int]] = []
|
||||
for index in range(len(code) - 7):
|
||||
if code[index] not in (0x48, 0x4C):
|
||||
continue
|
||||
if code[index + 1] not in (0x8D, 0x8B):
|
||||
continue
|
||||
if code[index + 2] & 0xC7 != 0x05:
|
||||
continue
|
||||
displacement = struct.unpack_from("<i", code, index + 3)[0]
|
||||
instruction_address = segment.virtual_address + index
|
||||
references.append(
|
||||
(instruction_address, instruction_address + 7 + displacement)
|
||||
)
|
||||
self._rip_references = references
|
||||
|
||||
if slop == 0:
|
||||
exact_targets = set(targets)
|
||||
return [
|
||||
address
|
||||
for address, destination in self._rip_references
|
||||
if destination in exact_targets
|
||||
]
|
||||
return [
|
||||
address
|
||||
for address, destination in self._rip_references
|
||||
if any(
|
||||
target - slop <= destination <= target + slop
|
||||
for target in targets
|
||||
)
|
||||
]
|
||||
result: list[int] = []
|
||||
for index in range(len(code) - 7):
|
||||
if code[index] not in (0x48, 0x4C):
|
||||
continue
|
||||
if code[index + 1] not in (0x8D, 0x8B):
|
||||
continue
|
||||
if code[index + 2] & 0xC7 != 0x05:
|
||||
continue
|
||||
displacement = struct.unpack_from("<i", code, index + 3)[0]
|
||||
instruction_address = segment.virtual_address + index
|
||||
destination = instruction_address + 7 + displacement
|
||||
if any(target - slop <= destination <= target + slop for target in targets):
|
||||
result.append(instruction_address)
|
||||
return result
|
||||
|
||||
def preceding_prologue(self, address: int, window: int = 0x40000) -> int:
|
||||
end = self.virtual_to_file(address)
|
||||
@@ -120,17 +145,32 @@ class ShellCore:
|
||||
raise ValueError(f"launchApp xrefs: {launch_xrefs!r}")
|
||||
launch = self.preceding_prologue(launch_xrefs[0])
|
||||
|
||||
unmount_strings = self.string_addresses(
|
||||
b"m_workspace.unmountWorkspace()", False
|
||||
app_exit_strings = self.string_addresses(
|
||||
b"[SceLncService] onAppExit() appId={0x%08x}", False
|
||||
)
|
||||
unmount_groups = {
|
||||
app_exit_groups = {
|
||||
self.preceding_prologue(xref)
|
||||
for xref in self.rip_xrefs(unmount_strings, slop=8)
|
||||
for xref in self.rip_xrefs(app_exit_strings, slop=8)
|
||||
}
|
||||
unmount_groups.discard(launch)
|
||||
if len(unmount_groups) != 1:
|
||||
raise ValueError(f"unmountWorkspace candidates: {unmount_groups!r}")
|
||||
unmount = next(iter(unmount_groups))
|
||||
if len(app_exit_groups) != 1:
|
||||
raise ValueError(f"onAppExit candidates: {app_exit_groups!r}")
|
||||
app_exit = next(iter(app_exit_groups))
|
||||
|
||||
# Validate the candidate with an independent source-expression log.
|
||||
# This prevents a changed format string from silently selecting an
|
||||
# unrelated function that also logs an app id.
|
||||
app_remove_strings = self.string_addresses(
|
||||
b"m_app_list.remove(app);", False
|
||||
)
|
||||
app_remove_groups = {
|
||||
self.preceding_prologue(xref)
|
||||
for xref in self.rip_xrefs(app_remove_strings, slop=8)
|
||||
}
|
||||
if app_exit not in app_remove_groups:
|
||||
raise ValueError(
|
||||
"onAppExit candidate does not remove the application: "
|
||||
f"0x{app_exit:x}, remove candidates {app_remove_groups!r}"
|
||||
)
|
||||
|
||||
install_strings = self.string_addresses(b"AppInstallTitleDirMain", False)
|
||||
install_groups: dict[int, int] = {}
|
||||
@@ -162,7 +202,7 @@ class ShellCore:
|
||||
|
||||
return {
|
||||
"launch_app": launch,
|
||||
"unmount_workspace": unmount,
|
||||
"app_exit": app_exit,
|
||||
"install_title_dir": best[0],
|
||||
"install_all": app_install_all,
|
||||
}
|
||||
|
||||
@@ -9,21 +9,18 @@ from pathlib import Path
|
||||
from capstone import CS_ARCH_X86, CS_GRP_CALL, CS_GRP_JUMP, CS_MODE_64, Cs
|
||||
from capstone.x86 import X86_OP_MEM, X86_REG_RIP
|
||||
|
||||
from generate_shellcore_offsets import ShellCore
|
||||
from generate_shellcore_offsets import ShellCore, firmware_key
|
||||
from generate_shellcore_offsets_from_files import firmware_files
|
||||
|
||||
|
||||
def verify_firmware(path: Path) -> None:
|
||||
def verify_firmware(path: Path, firmware: int) -> None:
|
||||
shellcore = ShellCore(path)
|
||||
targets = shellcore.locate_targets()
|
||||
decoder = Cs(CS_ARCH_X86, CS_MODE_64)
|
||||
decoder.detail = True
|
||||
|
||||
names = ["launch_app", "unmount_workspace"]
|
||||
names = ["launch_app", "app_exit"]
|
||||
# AppInstallAll is hooked after the public TitleDir RPC disappeared.
|
||||
try:
|
||||
firmware = int(path.parents[2].name.replace(".", ""), 16)
|
||||
except ValueError:
|
||||
firmware = 0
|
||||
if firmware >= 0x1200:
|
||||
names.append("install_all")
|
||||
|
||||
@@ -53,14 +50,27 @@ def main() -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("root", type=Path)
|
||||
args = parser.parse_args()
|
||||
firmware_inputs: list[tuple[int, Path]] = []
|
||||
firmware_dirs = sorted(
|
||||
path
|
||||
for path in args.root.iterdir()
|
||||
if (path / "system/vsh/SceShellCore.elf").is_file()
|
||||
)
|
||||
for firmware_dir in firmware_dirs:
|
||||
verify_firmware(firmware_dir / "system/vsh/SceShellCore.elf")
|
||||
print(f"verified {len(firmware_dirs)} firmware trampoline layouts")
|
||||
firmware_inputs.append(
|
||||
(
|
||||
firmware_key(firmware_dir.name),
|
||||
firmware_dir / "system/vsh/SceShellCore.elf",
|
||||
)
|
||||
)
|
||||
for major, minor, path in firmware_files(args.root):
|
||||
firmware_inputs.append((firmware_key(f"{major}.{minor:02d}"), path))
|
||||
if not firmware_inputs:
|
||||
raise ValueError(f"no SceShellCore firmware files under {args.root}")
|
||||
|
||||
for firmware, path in firmware_inputs:
|
||||
verify_firmware(path, firmware)
|
||||
print(f"verified {len(firmware_inputs)} firmware trampoline layouts")
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user