From fc426a06b229f08327e26dbb9956d4b74d4285b1 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 1 Dec 2018 13:56:13 -0800 Subject: [PATCH 01/10] exo: fix vaddr/paddr confusion in rcm reboot code --- exosphere/src/configitem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exosphere/src/configitem.c b/exosphere/src/configitem.c index 9aa1e6fa8..a0309ec2b 100644 --- a/exosphere/src/configitem.c +++ b/exosphere/src/configitem.c @@ -38,8 +38,8 @@ uint32_t configitem_set(bool privileged, ConfigItem item, uint64_t value) { case CONFIGITEM_NEEDS_REBOOT_TO_RCM: /* Force a reboot to RCM, if requested. */ if (value != 0) { - MAKE_REG32(0x7000E450) = 0x2; - MAKE_REG32(0x7000E400) = 0x10; + MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x450ull) = 0x2; + MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x400ull) = 0x10; while (1) { } } break; From bd76e73b253fa14327425e3952815e720dbf8343 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 1 Dec 2018 13:58:09 -0800 Subject: [PATCH 02/10] fusee: fix configuration typo --- fusee/fusee-secondary/src/exocfg.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fusee/fusee-secondary/src/exocfg.h b/fusee/fusee-secondary/src/exocfg.h index 133ebffd3..d21abb4f9 100644 --- a/fusee/fusee-secondary/src/exocfg.h +++ b/fusee/fusee-secondary/src/exocfg.h @@ -49,7 +49,7 @@ typedef struct { #define MAILBOX_EXOSPHERE_CONFIGURATION ((volatile exosphere_config_t *)(0x40002E40)) #define EXOSPHERE_TARGETFW_KEY "target_firmware" -#define EXOSPHERE_DEBUGMODE_PRIV_KEY "debug_mode" -#define EXOSPHERE_DEBUGMODE_USER_KEY "debug_mode_user" +#define EXOSPHERE_DEBUGMODE_PRIV_KEY "debugmode" +#define EXOSPHERE_DEBUGMODE_USER_KEY "debugmode_user" #endif \ No newline at end of file From 3fa973f430196c36756be8f533b34cd2eaeaf767 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 1 Dec 2018 19:07:05 -0800 Subject: [PATCH 03/10] fatal/set_mitm: Support 1.0.0 kernel. --- stratosphere/fatal/fatal.json | 6 +++--- stratosphere/set_mitm/set_mitm.json | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/stratosphere/fatal/fatal.json b/stratosphere/fatal/fatal.json index 201875895..0bdbeeeb2 100644 --- a/stratosphere/fatal/fatal.json +++ b/stratosphere/fatal/fatal.json @@ -4,13 +4,13 @@ "title_id_range_min": "0x0100000000000034", "title_id_range_max": "0x0100000000000034", "main_thread_stack_size": "0x00010000", - "main_thread_priority": 37, + "main_thread_priority": 15, "default_cpu_id": 3, "process_category": 0, "is_retail": true, "pool_partition": 2, "is_64_bit": true, - "address_space_type": 3, + "address_space_type": 1, "filesystem_access": { "permissions": "0xFFFFFFFFFFFFFFFF" }, @@ -88,7 +88,7 @@ } }, { "type": "min_kernel_version", - "value": "0x0060" + "value": "0x0030" }, { "type": "handle_table_size", "value": 128 diff --git a/stratosphere/set_mitm/set_mitm.json b/stratosphere/set_mitm/set_mitm.json index 055c6af6b..1f71cf7c1 100644 --- a/stratosphere/set_mitm/set_mitm.json +++ b/stratosphere/set_mitm/set_mitm.json @@ -10,7 +10,7 @@ "is_retail": true, "pool_partition": 2, "is_64_bit": true, - "address_space_type": 3, + "address_space_type": 1, "filesystem_access": { "permissions": "0xFFFFFFFFFFFFFFFF" }, @@ -99,7 +99,7 @@ }, { "type": "min_kernel_version", - "value": "0x0060" + "value": "0x0030" } ] } \ No newline at end of file From bbed78149c7a176a3687dc081f6a34063a603f8e Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 1 Dec 2018 19:07:25 -0800 Subject: [PATCH 04/10] fs.mitm: Hog less CPU time if SD card not inserted --- stratosphere/fs_mitm/source/fsmitm_utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stratosphere/fs_mitm/source/fsmitm_utils.cpp b/stratosphere/fs_mitm/source/fsmitm_utils.cpp index c1683eda9..2151e22ff 100644 --- a/stratosphere/fs_mitm/source/fsmitm_utils.cpp +++ b/stratosphere/fs_mitm/source/fsmitm_utils.cpp @@ -69,7 +69,7 @@ void Utils::InitializeSdThreadFunc(void *args) { /* Mount SD. */ while (R_FAILED(fsMountSdcard(&g_sd_filesystem))) { - svcSleepThread(1000ULL); + svcSleepThread(1000000ULL); } /* Back up CAL0, if it's not backed up already. */ From 8ecf68cb655f3e102c42e9d08d361f668f74bc02 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 1 Dec 2018 21:38:28 -0800 Subject: [PATCH 05/10] fusee: Add support for 1.0.0-7. --- fusee/fusee-secondary/src/kernel_patches.c | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/fusee/fusee-secondary/src/kernel_patches.c b/fusee/fusee-secondary/src/kernel_patches.c index d5ae8df7b..03c5bbf4d 100644 --- a/fusee/fusee-secondary/src/kernel_patches.c +++ b/fusee/fusee-secondary/src/kernel_patches.c @@ -310,6 +310,24 @@ static const uint8_t MAKE_KERNEL_PATTERN_NAME(600, proc_id_recv)[] = {0x08, 0x03 static const instruction_t MAKE_KERNEL_HOOK_NAME(600, proc_id_recv)[] = {0xA9BF2FEA, 0xF94043EB, 0x2A1503EA, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF9400308, 0xF9401D08, 0xAA1803E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0}; /* Hook Definitions. */ +static const kernel_hook_t g_kernel_hooks_100_7[] = { + { /* Send Message Process ID Patch. */ + .pattern_size = 0x10, + .pattern = MAKE_KERNEL_PATTERN_NAME(100, proc_id_send), + .pattern_hook_offset = 0x0, + .payload_num_instructions = sizeof(MAKE_KERNEL_HOOK_NAME(100, proc_id_send))/sizeof(instruction_t), + .branch_back_offset = 0x4, + .payload = MAKE_KERNEL_HOOK_NAME(100, proc_id_send) + }, + { /* Receive Message Process ID Patch. */ + .pattern_size = 0x10, + .pattern = MAKE_KERNEL_PATTERN_NAME(100, proc_id_recv), + .pattern_hook_offset = 0x0, + .payload_num_instructions = sizeof(MAKE_KERNEL_HOOK_NAME(100, proc_id_recv))/sizeof(instruction_t), + .branch_back_offset = 0x4, + .payload = MAKE_KERNEL_HOOK_NAME(100, proc_id_recv) + } +}; static const kernel_hook_t g_kernel_hooks_100[] = { { /* Send Message Process ID Patch. */ .pattern_size = 0x10, @@ -442,6 +460,11 @@ static const kernel_hook_t g_kernel_hooks_600[] = { /* Kernel Infos. */ static const kernel_info_t g_kernel_infos[] = { + { /* 1.0.0-7. */ + .hash = {0x64, 0x44, 0x07, 0x2F, 0x56, 0x44, 0x73, 0xDD, 0xD5, 0x46, 0x1B, 0x8C, 0xDC, 0xEF, 0x54, 0x98, 0x16, 0xDA, 0x81, 0xDE, 0x5B, 0x1C, 0x9D, 0xD7, 0x5A, 0x13, 0x91, 0xD9, 0x53, 0xAB, 0x8D, 0x8D}, + .free_code_space_offset = 0x4797C, + KERNEL_HOOKS(100_7) + }, { /* 1.0.0. */ .hash = {0xB8, 0xC5, 0x0C, 0x68, 0x25, 0xA9, 0xB9, 0x5B, 0xD2, 0x4D, 0x2C, 0x7C, 0x81, 0x7F, 0xE6, 0x96, 0xF2, 0x42, 0x4E, 0x1D, 0x78, 0xDF, 0x3B, 0xCA, 0x3D, 0x6B, 0x68, 0x12, 0xDD, 0xA9, 0xCB, 0x9C}, .free_code_space_offset = 0x4797C, From 021d84ff04d35ec905f733b505b94f9e8fe0bec6 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 1 Dec 2018 21:39:29 -0800 Subject: [PATCH 06/10] fusee: remove duplicate hook --- fusee/fusee-secondary/src/kernel_patches.c | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/fusee/fusee-secondary/src/kernel_patches.c b/fusee/fusee-secondary/src/kernel_patches.c index 03c5bbf4d..5b83d8ef9 100644 --- a/fusee/fusee-secondary/src/kernel_patches.c +++ b/fusee/fusee-secondary/src/kernel_patches.c @@ -310,24 +310,6 @@ static const uint8_t MAKE_KERNEL_PATTERN_NAME(600, proc_id_recv)[] = {0x08, 0x03 static const instruction_t MAKE_KERNEL_HOOK_NAME(600, proc_id_recv)[] = {0xA9BF2FEA, 0xF94043EB, 0x2A1503EA, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF9400308, 0xF9401D08, 0xAA1803E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0}; /* Hook Definitions. */ -static const kernel_hook_t g_kernel_hooks_100_7[] = { - { /* Send Message Process ID Patch. */ - .pattern_size = 0x10, - .pattern = MAKE_KERNEL_PATTERN_NAME(100, proc_id_send), - .pattern_hook_offset = 0x0, - .payload_num_instructions = sizeof(MAKE_KERNEL_HOOK_NAME(100, proc_id_send))/sizeof(instruction_t), - .branch_back_offset = 0x4, - .payload = MAKE_KERNEL_HOOK_NAME(100, proc_id_send) - }, - { /* Receive Message Process ID Patch. */ - .pattern_size = 0x10, - .pattern = MAKE_KERNEL_PATTERN_NAME(100, proc_id_recv), - .pattern_hook_offset = 0x0, - .payload_num_instructions = sizeof(MAKE_KERNEL_HOOK_NAME(100, proc_id_recv))/sizeof(instruction_t), - .branch_back_offset = 0x4, - .payload = MAKE_KERNEL_HOOK_NAME(100, proc_id_recv) - } -}; static const kernel_hook_t g_kernel_hooks_100[] = { { /* Send Message Process ID Patch. */ .pattern_size = 0x10, @@ -463,7 +445,7 @@ static const kernel_info_t g_kernel_infos[] = { { /* 1.0.0-7. */ .hash = {0x64, 0x44, 0x07, 0x2F, 0x56, 0x44, 0x73, 0xDD, 0xD5, 0x46, 0x1B, 0x8C, 0xDC, 0xEF, 0x54, 0x98, 0x16, 0xDA, 0x81, 0xDE, 0x5B, 0x1C, 0x9D, 0xD7, 0x5A, 0x13, 0x91, 0xD9, 0x53, 0xAB, 0x8D, 0x8D}, .free_code_space_offset = 0x4797C, - KERNEL_HOOKS(100_7) + KERNEL_HOOKS(100) }, { /* 1.0.0. */ .hash = {0xB8, 0xC5, 0x0C, 0x68, 0x25, 0xA9, 0xB9, 0x5B, 0xD2, 0x4D, 0x2C, 0x7C, 0x81, 0x7F, 0xE6, 0x96, 0xF2, 0x42, 0x4E, 0x1D, 0x78, 0xDF, 0x3B, 0xCA, 0x3D, 0x6B, 0x68, 0x12, 0xDD, 0xA9, 0xCB, 0x9C}, From 49ba3a86e2f574234030130c9c3564f4c5d57b76 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 4 Dec 2018 03:54:25 -0800 Subject: [PATCH 07/10] fusee: move BCT.ini/secondary into atmosphere/ --- Makefile | 4 ++-- common/defaults/BCT.ini | 2 +- fusee/fusee-primary/src/main.c | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 9d8364e89..62fdd0f64 100644 --- a/Makefile +++ b/Makefile @@ -42,8 +42,8 @@ dist: all mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000036 mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000034 mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032 - cp fusee/fusee-secondary/fusee-secondary.bin atmosphere-$(AMSVER)/fusee-secondary.bin - cp common/defaults/BCT.ini atmosphere-$(AMSVER)/BCT.ini + cp fusee/fusee-secondary/fusee-secondary.bin atmosphere-$(AMSVER)/atmosphere/fusee-secondary.bin + cp common/defaults/BCT.ini atmosphere-$(AMSVER)/atmosphere/BCT.ini cp common/defaults/loader.ini atmosphere-$(AMSVER)/atmosphere/loader.ini cp -r common/defaults/kip_patches atmosphere-$(AMSVER)/atmosphere/kip_patches cp stratosphere/creport/creport.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000036/exefs.nsp diff --git a/common/defaults/BCT.ini b/common/defaults/BCT.ini index 81d69e48e..660b456b7 100644 --- a/common/defaults/BCT.ini +++ b/common/defaults/BCT.ini @@ -1,6 +1,6 @@ BCT0 [stage1] -stage2_path = fusee-secondary.bin +stage2_path = atmosphere/fusee-secondary.bin stage2_addr = 0xF0000000 stage2_entrypoint = 0xF0000000 diff --git a/fusee/fusee-primary/src/main.c b/fusee/fusee-primary/src/main.c index 130362e0a..9c5e81def 100644 --- a/fusee/fusee-primary/src/main.c +++ b/fusee/fusee-primary/src/main.c @@ -40,12 +40,12 @@ static char g_bct0_buffer[BCTO_MAX_SIZE]; #define DEFAULT_BCT0_FOR_DEBUG \ "BCT0\n"\ "[stage1]\n"\ -"stage2_path = fusee-secondary.bin\n"\ +"stage2_path = atmosphere/fusee-secondary.bin\n"\ "stage2_addr = 0xF0000000\n"\ "stage2_entrypoint = 0xF0000000\n" static const char *load_config(void) { - if (!read_from_file(g_bct0_buffer, BCTO_MAX_SIZE, "BCT.ini")) { + if (!read_from_file(g_bct0_buffer, BCTO_MAX_SIZE, "atmosphere/BCT.ini")) { print(SCREEN_LOG_LEVEL_DEBUG, "Failed to read BCT0 from SD!\n"); print(SCREEN_LOG_LEVEL_DEBUG, "Using default BCT0!\n"); memcpy(g_bct0_buffer, DEFAULT_BCT0_FOR_DEBUG, sizeof(DEFAULT_BCT0_FOR_DEBUG)); @@ -159,7 +159,7 @@ int main(void) { strcpy(g_chainloader_arg_data, stage2_path); stage2_args = (stage2_args_t *)(g_chainloader_arg_data + strlen(stage2_path) + 1); /* May be unaligned. */ memcpy(&stage2_args->version, &stage2_version, 4); - stage2_args->log_level = log_level; + memcpy(&stage2_args->log_level, &log_level, sizeof(log_level)); stage2_args->display_initialized = false; strcpy(stage2_args->bct0, bct0); g_chainloader_argc = 2; From 903789cf6e4c3b5b0e2eab9540ce0854ef679b44 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 4 Dec 2018 04:01:22 -0800 Subject: [PATCH 08/10] fusee: fix error printing pre-SD card init (closes #289). --- fusee/fusee-primary/src/main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fusee/fusee-primary/src/main.c b/fusee/fusee-primary/src/main.c index 9c5e81def..c54862ba8 100644 --- a/fusee/fusee-primary/src/main.c +++ b/fusee/fusee-primary/src/main.c @@ -108,7 +108,7 @@ static void setup_env(void) { /* Set up the exception handlers. */ setup_exception_handlers(); - + /* Mount the SD card. */ mount_sd(); } @@ -133,6 +133,9 @@ int main(void) { uint32_t stage2_version = 0; ScreenLogLevel log_level = SCREEN_LOG_LEVEL_MANDATORY; + /* Override the global logging level. */ + log_set_log_level(log_level); + /* Initialize the display, console, etc. */ setup_env(); @@ -144,9 +147,6 @@ int main(void) { fatal_error("Failed to parse BCT.ini!\n"); } - /* Override the global logging level. */ - log_set_log_level(log_level); - /* Say hello. */ print(SCREEN_LOG_LEVEL_MANDATORY, "Welcome to Atmosph\xe8re Fus\xe9" "e!\n"); print(SCREEN_LOG_LEVEL_DEBUG, "Using color linear framebuffer at 0x%p!\n", g_framebuffer); From a79f4cf6f6ecef2f17da43ca9cca2225e0949e7f Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 4 Dec 2018 15:59:30 -0800 Subject: [PATCH 09/10] exosphere: fix sleep mode when debugmode is enabled --- exosphere/src/bootup.c | 8 ++++++++ exosphere/src/configitem.c | 9 +++++++++ exosphere/src/configitem.h | 1 + exosphere/src/lp0.c | 3 ++- exosphere/src/warmboot_main.c | 11 ++++++++++- 5 files changed, 30 insertions(+), 2 deletions(-) diff --git a/exosphere/src/bootup.c b/exosphere/src/bootup.c index 962ef8e75..4836f4610 100644 --- a/exosphere/src/bootup.c +++ b/exosphere/src/bootup.c @@ -31,6 +31,7 @@ #include "configitem.h" #include "timers.h" #include "misc.h" +#include "uart.h" #include "bpmp.h" #include "sysreg.h" #include "interrupt.h" @@ -213,6 +214,13 @@ void bootup_misc_mmio(void) { } if (!g_has_booted_up) { + /* N doesn't do this, but we should for compatibility. */ + if (configitem_is_debugmode_priv()) { + uart_select(UART_A); + clkrst_reboot(CARDEVICE_UARTA); + uart_init(UART_A, 115200); + } + intr_register_handler(INTERRUPT_ID_SECURITY_ENGINE, se_operation_completed); if (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_400) { intr_register_handler(INTERRUPT_ID_ACTIVITY_MONITOR_4X, actmon_interrupt_handler); diff --git a/exosphere/src/configitem.c b/exosphere/src/configitem.c index a0309ec2b..cac02b835 100644 --- a/exosphere/src/configitem.c +++ b/exosphere/src/configitem.c @@ -72,6 +72,15 @@ bool configitem_should_profile_battery(void) { return g_battery_profile; } +bool configitem_is_debugmode_priv(void) { + uint64_t debugmode = 0; + if (configitem_get(true, CONFIGITEM_ISDEBUGMODE, &debugmode) != 0) { + generic_panic(); + } + + return debugmode != 0; +} + uint64_t configitem_get_hardware_type(void) { uint64_t hardware_type; if (configitem_get(true, CONFIGITEM_HARDWARETYPE, &hardware_type) != 0) { diff --git a/exosphere/src/configitem.h b/exosphere/src/configitem.h index 8aef4e975..df54c5740 100644 --- a/exosphere/src/configitem.h +++ b/exosphere/src/configitem.h @@ -50,6 +50,7 @@ uint32_t configitem_get(bool privileged, ConfigItem item, uint64_t *p_outvalue); bool configitem_is_recovery_boot(void); bool configitem_is_retail(void); bool configitem_should_profile_battery(void); +bool configitem_is_debugmode_priv(void); void configitem_set_debugmode_override(bool user, bool priv); diff --git a/exosphere/src/lp0.c b/exosphere/src/lp0.c index e6e19d3bf..e38693cf0 100644 --- a/exosphere/src/lp0.c +++ b/exosphere/src/lp0.c @@ -35,6 +35,7 @@ #include "smc_api.h" #include "timers.h" #include "misc.h" +#include "uart.h" #include "exocfg.h" #define u8 uint8_t @@ -241,7 +242,7 @@ void save_se_and_power_down_cpu(void) { save_se_state(); if (!configitem_is_retail()) { - /* TODO: uart_log("OYASUMI"); */ + uart_send(UART_A, "OYASUMI", 8); } finalize_powerdown(); diff --git a/exosphere/src/warmboot_main.c b/exosphere/src/warmboot_main.c index 8ee15c4fd..d6f66aeb3 100644 --- a/exosphere/src/warmboot_main.c +++ b/exosphere/src/warmboot_main.c @@ -30,6 +30,7 @@ #include "car.h" #include "i2c.h" #include "misc.h" +#include "uart.h" #include "interrupt.h" #include "pmc.h" @@ -55,8 +56,16 @@ void __attribute__((noreturn)) warmboot_main(void) { /* On warmboot (not cpu_on) only */ if (VIRT_MC_SECURITY_CFG3 == 0) { + /* N only does this on dev units, but we will do it unconditionally. */ + { + uart_select(UART_A); + clkrst_reboot(CARDEVICE_UARTA); + uart_init(UART_A, 115200); + } + if (!configitem_is_retail()) { - /* TODO: uart_log("OHAYO"); */ + uart_send(UART_A, "OHAYO", 6); + uart_wait_idle(UART_A, UART_VENDOR_STATE_TX_IDLE); } /* Sanity check the Security Engine. */ From a51d355707cb70f90021fde9226a64ad3e9e2f16 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 4 Dec 2018 16:01:26 -0800 Subject: [PATCH 10/10] exo: there's no reason to not always init uart to be safe, actually --- exosphere/src/bootup.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/exosphere/src/bootup.c b/exosphere/src/bootup.c index 4836f4610..9d01ae08a 100644 --- a/exosphere/src/bootup.c +++ b/exosphere/src/bootup.c @@ -215,11 +215,9 @@ void bootup_misc_mmio(void) { if (!g_has_booted_up) { /* N doesn't do this, but we should for compatibility. */ - if (configitem_is_debugmode_priv()) { - uart_select(UART_A); - clkrst_reboot(CARDEVICE_UARTA); - uart_init(UART_A, 115200); - } + uart_select(UART_A); + clkrst_reboot(CARDEVICE_UARTA); + uart_init(UART_A, 115200); intr_register_handler(INTERRUPT_ID_SECURITY_ENGINE, se_operation_completed); if (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_400) {