From a67d4064f0ca51d46d711589de32b2bc9cc0663d Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 18 Apr 2019 00:18:53 -0700 Subject: [PATCH 01/64] pm: update with new meme command for 8.0.0 --- stratosphere/libstratosphere | 2 +- stratosphere/pm/source/pm_shell.cpp | 12 ++++++++++++ stratosphere/pm/source/pm_shell.hpp | 7 ++++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/stratosphere/libstratosphere b/stratosphere/libstratosphere index b9724cdca..163d9259a 160000 --- a/stratosphere/libstratosphere +++ b/stratosphere/libstratosphere @@ -1 +1 @@ -Subproject commit b9724cdcadd5ea5fbead8f1a9c9b7de11daf6b60 +Subproject commit 163d9259a365c3a89c1a837b28e7609d45795e40 diff --git a/stratosphere/pm/source/pm_shell.cpp b/stratosphere/pm/source/pm_shell.cpp index dc81c4b24..2b0f2a6ab 100644 --- a/stratosphere/pm/source/pm_shell.cpp +++ b/stratosphere/pm/source/pm_shell.cpp @@ -111,3 +111,15 @@ Result ShellService::BoostSystemThreadsResourceLimit() { /* We will simply not reduce the number of system threads available for no reason. */ return ResultSuccess; } + + +Result ShellService::GetUnimplementedEventHandle(Out event) { + /* In 8.0.0, Nintendo added this command which should return an event handle. */ + /* In addition, they also added code to create a new event in the global PM constructor. */ + /* However, nothing signals this event, and this command currently does std::abort();. */ + /* We will oblige. */ + std::abort(); + + /* TODO: Return an event handle, once N makes this command a real thing in the future. */ + /* TODO: return ResultSuccess; */ +} diff --git a/stratosphere/pm/source/pm_shell.hpp b/stratosphere/pm/source/pm_shell.hpp index b36c30454..24aa34030 100644 --- a/stratosphere/pm/source/pm_shell.hpp +++ b/stratosphere/pm/source/pm_shell.hpp @@ -43,7 +43,8 @@ enum ShellCmd_5X { Shell_Cmd_5X_GetApplicationProcessId = 6, Shell_Cmd_5X_BoostSystemMemoryResourceLimit = 7, - Shell_Cmd_BoostSystemThreadsResourceLimit = 8 + Shell_Cmd_BoostSystemThreadsResourceLimit = 8, + Shell_Cmd_GetUnimplementedEventHandle = 9 /* TODO: Rename when Nintendo implements this. */ }; class ShellService final : public IServiceObject { @@ -60,6 +61,7 @@ class ShellService final : public IServiceObject { Result GetApplicationProcessId(Out pid); Result BoostSystemMemoryResourceLimit(u64 sysmem_size); Result BoostSystemThreadsResourceLimit(); + Result GetUnimplementedEventHandle(Out event); public: DEFINE_SERVICE_DISPATCH_TABLE { /* 1.0.0-4.0.0 */ @@ -88,5 +90,8 @@ class ShellService final : public IServiceObject { /* 7.0.0-* */ MakeServiceCommandMeta(), + + /* 8.0.0-* */ + MakeServiceCommandMeta(), }; }; From ae90a9d7a6e9ac87a22501d7d36809637f7b2fbd Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 18 Apr 2019 01:02:12 -0700 Subject: [PATCH 02/64] exo/fusee: implement 8.0.0 support (package2 changes still TODO) --- common/include/atmosphere/target_fw.h | 5 +- common/include/atmosphere/version.h | 4 +- exosphere/src/bootconfig.c | 2 + exosphere/src/configitem.c | 18 +++-- exosphere/src/configitem.h | 5 +- exosphere/src/mc.c | 14 ++-- exosphere/src/package2.c | 4 ++ exosphere/src/sc7.c | 10 +-- exosphere/src/smc_api.c | 77 ++++++++++++---------- exosphere/src/smc_user.c | 1 + exosphere/src/warmboot_init.c | 4 +- exosphere/src/warmboot_main.c | 24 ++++--- fusee/fusee-secondary/src/key_derivation.c | 2 + fusee/fusee-secondary/src/nxboot.c | 6 +- 14 files changed, 107 insertions(+), 69 deletions(-) diff --git a/common/include/atmosphere/target_fw.h b/common/include/atmosphere/target_fw.h index d91578ab5..4f944b293 100644 --- a/common/include/atmosphere/target_fw.h +++ b/common/include/atmosphere/target_fw.h @@ -25,11 +25,12 @@ #define ATMOSPHERE_TARGET_FIRMWARE_600 6 #define ATMOSPHERE_TARGET_FIRMWARE_620 7 #define ATMOSPHERE_TARGET_FIRMWARE_700 8 +#define ATMOSPHERE_TARGET_FIRMWARE_800 9 -#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_700 +#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_800 #define ATMOSPHERE_TARGET_FIRMWARE_MIN ATMOSPHERE_TARGET_FIRMWARE_100 -#define ATMOSPHERE_TARGET_FIRMWARE_MAX ATMOSPHERE_TARGET_FIRMWARE_700 +#define ATMOSPHERE_TARGET_FIRMWARE_MAX ATMOSPHERE_TARGET_FIRMWARE_800 /* TODO: What should this be, for release? */ #define ATMOSPHERE_TARGET_FIRMWARE_DEFAULT_FOR_DEBUG ATMOSPHERE_TARGET_FIRMWARE_CURRENT diff --git a/common/include/atmosphere/version.h b/common/include/atmosphere/version.h index 9323d4913..642a0fe1b 100644 --- a/common/include/atmosphere/version.h +++ b/common/include/atmosphere/version.h @@ -21,8 +21,8 @@ #define ATMOSPHERE_RELEASE_VERSION_MINOR 8 #define ATMOSPHERE_RELEASE_VERSION_MICRO 7 -#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MAJOR 7 +#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MAJOR 8 #define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 0 -#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO 1 +#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO 0 #endif \ No newline at end of file diff --git a/exosphere/src/bootconfig.c b/exosphere/src/bootconfig.c index 599503e83..f1baddb0e 100644 --- a/exosphere/src/bootconfig.c +++ b/exosphere/src/bootconfig.c @@ -100,6 +100,8 @@ uint64_t bootconfig_get_value_for_sysctr0(void) { } uint64_t bootconfig_get_memory_arrangement(void) { + /* TODO: This function has changed pretty significantly since we implemented it. */ + /* Not relevant for retail, but we'll probably want this to be accurate sooner or later. */ if (bootconfig_is_debug_mode()) { if (fuse_get_dram_id() == 4) { if (LOADED_BOOTCONFIG->unsigned_config.data[0x23]) { diff --git a/exosphere/src/configitem.c b/exosphere/src/configitem.c index 2e3c537ae..3db892230 100644 --- a/exosphere/src/configitem.c +++ b/exosphere/src/configitem.c @@ -35,13 +35,13 @@ #undef u8 #undef u32 -static bool g_battery_profile = false; +static bool g_hiz_mode_enabled = false; static bool g_debugmode_override_user = false, g_debugmode_override_priv = false; uint32_t configitem_set(bool privileged, ConfigItem item, uint64_t value) { switch (item) { - case CONFIGITEM_BATTERYPROFILE: - g_battery_profile = (value != 0); + case CONFIGITEM_HIZMODE: + g_hiz_mode_enabled = (value != 0); break; case CONFIGITEM_NEEDS_REBOOT: /* Force a reboot, if requested. */ @@ -133,8 +133,12 @@ bool configitem_is_retail(void) { return is_retail != 0; } -bool configitem_should_profile_battery(void) { - return g_battery_profile; +bool configitem_is_hiz_mode_enabled(void) { + return g_hiz_mode_enabled; +} + +void configitem_set_hiz_mode_enabled(bool enabled) { + g_hiz_mode_enabled = enabled; } bool configitem_is_debugmode_priv(void) { @@ -214,8 +218,8 @@ uint32_t configitem_get(bool privileged, ConfigItem item, uint64_t *p_outvalue) *p_outvalue = config; } break; - case CONFIGITEM_BATTERYPROFILE: - *p_outvalue = (int)g_battery_profile; + case CONFIGITEM_HIZMODE: + *p_outvalue = (int)g_hiz_mode_enabled; break; case CONFIGITEM_ISQUESTUNIT: /* Added on 3.0, used to determine whether console is a kiosk unit. */ diff --git a/exosphere/src/configitem.h b/exosphere/src/configitem.h index 2f2ed5c3e..bcf5ca72c 100644 --- a/exosphere/src/configitem.h +++ b/exosphere/src/configitem.h @@ -33,7 +33,7 @@ typedef enum { CONFIGITEM_MEMORYARRANGE = 10, CONFIGITEM_ISDEBUGMODE = 11, CONFIGITEM_KERNELCONFIGURATION = 12, - CONFIGITEM_BATTERYPROFILE = 13, + CONFIGITEM_HIZMODE = 13, CONFIGITEM_ISQUESTUNIT = 14, CONFIGITEM_NEWHARDWARETYPE_5X = 15, CONFIGITEM_NEWKEYGENERATION_5X = 16, @@ -56,10 +56,11 @@ 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_hiz_mode_enabled(void); bool configitem_is_debugmode_priv(void); void configitem_set_debugmode_override(bool user, bool priv); +void configitem_set_hiz_mode_enabled(bool enabled); uint64_t configitem_get_hardware_type(void); diff --git a/exosphere/src/mc.c b/exosphere/src/mc.c index e1f5c8d08..ccb6b8647 100644 --- a/exosphere/src/mc.c +++ b/exosphere/src/mc.c @@ -140,13 +140,19 @@ void configure_kernel_carveout(unsigned int carveout_id, uint64_t address, uint6 carveout->size_big_pages = (uint32_t)(size >> 17); carveout->client_access_0 = (BIT(CSR_PTCR) | BIT(CSR_DISPLAY0A) | BIT(CSR_DISPLAY0AB) | BIT(CSR_DISPLAY0B) | BIT(CSR_DISPLAY0BB) | BIT(CSR_DISPLAY0C) | BIT(CSR_DISPLAY0CB) | BIT(CSR_AFIR) | BIT(CSR_DISPLAYHC) | BIT(CSR_DISPLAYHCB) | BIT(CSR_HDAR) | BIT(CSR_HOST1XDMAR) | BIT(CSR_HOST1XR) | BIT(CSR_NVENCSRD) | BIT(CSR_PPCSAHBDMAR) | BIT(CSR_PPCSAHBSLVR)); carveout->client_access_1 = (BIT(CSR_MPCORER) | BIT(CSW_NVENCSWR) | BIT(CSW_AFIW) | BIT(CSW_HDAW) | BIT(CSW_HOST1XW) | BIT(CSW_MPCOREW) | BIT(CSW_PPCSAHBDMAW) | BIT(CSW_PPCSAHBSLVW)); - carveout->client_access_2 = (BIT(CSR_XUSB_HOSTR) | BIT(CSW_XUSB_HOSTW) | BIT(CSR_XUSB_DEVR) | BIT(CSW_XUSB_DEVW) | BIT(CSR_TSECSRD) | BIT(CSW_TSECSWR)); - carveout->client_access_3 = (BIT(CSR_SDMMCRA) | BIT(CSR_SDMMCRAA) | BIT(CSR_SDMMCRAB) | BIT(CSW_SDMMCWA) | BIT(CSW_SDMMCWAA) | BIT(CSW_SDMMCWAB) | BIT(CSR_VICSRD) | BIT(CSW_VICSWR) | BIT(CSR_DISPLAYD) | BIT(CSR_NVDECSRD) | BIT(CSW_NVDECSWR) | BIT(CSR_APER) | BIT(CSW_APEW) | BIT(CSR_NVJPGSRD) | BIT(CSW_NVJPGSWR)); - carveout->client_access_4 = (BIT(CSR_SESRD) | BIT(CSW_SESWR)); + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_800) { + carveout->client_access_2 = (BIT(CSR_XUSB_HOSTR) | BIT(CSW_XUSB_HOSTW) | BIT(CSR_XUSB_DEVR) | BIT(CSW_XUSB_DEVW)); + carveout->client_access_3 = (BIT(CSR_SDMMCRA) | BIT(CSR_SDMMCRAA) | BIT(CSR_SDMMCRAB) | BIT(CSW_SDMMCWA) | BIT(CSW_SDMMCWAA) | BIT(CSW_SDMMCWAB) | BIT(CSR_VICSRD) | BIT(CSW_VICSWR) | BIT(CSR_DISPLAYD) | BIT(CSR_NVDECSRD) | BIT(CSW_NVDECSWR) | BIT(CSR_APER) | BIT(CSW_APEW) | BIT(CSR_NVJPGSRD) | BIT(CSW_NVJPGSWR)); + carveout->client_access_4 = (BIT(CSR_SESRD) | BIT(CSW_SESWR) | BIT(CSR_TSECSRDB) | BIT(CSW_TSECSWRB)); + } else { + carveout->client_access_2 = (BIT(CSR_XUSB_HOSTR) | BIT(CSW_XUSB_HOSTW) | BIT(CSR_XUSB_DEVR) | BIT(CSW_XUSB_DEVW) | BIT(CSR_TSECSRD) | BIT(CSW_TSECSWR)); + carveout->client_access_3 = (BIT(CSR_SDMMCRA) | BIT(CSR_SDMMCRAA) | BIT(CSR_SDMMCRAB) | BIT(CSW_SDMMCWA) | BIT(CSW_SDMMCWAA) | BIT(CSW_SDMMCWAB) | BIT(CSR_VICSRD) | BIT(CSW_VICSWR) | BIT(CSR_DISPLAYD) | BIT(CSR_NVDECSRD) | BIT(CSW_NVDECSWR) | BIT(CSR_APER) | BIT(CSW_APEW) | BIT(CSR_NVJPGSRD) | BIT(CSW_NVJPGSWR)); + carveout->client_access_4 = (BIT(CSR_SESRD) | BIT(CSW_SESWR)); + } carveout->client_force_internal_access_0 = ((exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400) && (carveout_id == 4)) ? BIT(CSR_AVPCARM7R) : 0; carveout->client_force_internal_access_1 = ((exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400) && (carveout_id == 4)) ? BIT(CSW_AVPCARM7W) : 0; carveout->client_force_internal_access_2 = 0; carveout->client_force_internal_access_3 = 0; carveout->client_force_internal_access_4 = 0; - carveout->config = 0x8B; + carveout->config = (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_800) ? 0x4CB : 0x8B; } diff --git a/exosphere/src/package2.c b/exosphere/src/package2.c index 1831c1b43..2aedb42f4 100644 --- a/exosphere/src/package2.c +++ b/exosphere/src/package2.c @@ -144,6 +144,7 @@ static void setup_se(void) { master_kek_source_ind = MASTERKEY_REVISION_620 - MASTERKEY_REVISION_620; break; case ATMOSPHERE_TARGET_FIRMWARE_700: + case ATMOSPHERE_TARGET_FIRMWARE_800: master_kek_source_ind = MASTERKEY_REVISION_700_CURRENT - MASTERKEY_REVISION_620; break; default: @@ -179,6 +180,7 @@ static void setup_se(void) { case ATMOSPHERE_TARGET_FIRMWARE_600: case ATMOSPHERE_TARGET_FIRMWARE_620: case ATMOSPHERE_TARGET_FIRMWARE_700: + case ATMOSPHERE_TARGET_FIRMWARE_800: derive_new_device_keys(KEYSLOT_SWITCH_5XNEWDEVICEKEYGENKEY); break; } @@ -487,6 +489,7 @@ static void copy_warmboot_bin_to_dram() { warmboot_src = (uint8_t *)0x4003D800; break; case ATMOSPHERE_TARGET_FIRMWARE_700: + case ATMOSPHERE_TARGET_FIRMWARE_800: warmboot_src = (uint8_t *)0x4003E000; break; } @@ -554,6 +557,7 @@ void load_package2(coldboot_crt0_reloc_list_t *reloc_list) { MAKE_REG32(PMC_BASE + 0x360) = 0xA8; break; case ATMOSPHERE_TARGET_FIRMWARE_700: + case ATMOSPHERE_TARGET_FIRMWARE_800: MAKE_REG32(PMC_BASE + 0x360) = 0x129; break; } diff --git a/exosphere/src/sc7.c b/exosphere/src/sc7.c index 82f1c66f9..feb942f7e 100644 --- a/exosphere/src/sc7.c +++ b/exosphere/src/sc7.c @@ -44,11 +44,11 @@ #undef u8 #undef u32 -static void configure_battery_hi_z_mode(void) { +static void configure_battery_hiz_mode(void) { clkrst_reboot(CARDEVICE_I2C1); - if (configitem_should_profile_battery() && !i2c_query_ti_charger_bit_7()) { - /* Profile the battery. */ + if (configitem_is_hiz_mode_enabled() && !i2c_query_ti_charger_bit_7()) { + /* Configure HiZ mode. */ i2c_set_ti_charger_bit_7(); uint32_t start_time = get_time(); bool should_wait = true; @@ -109,7 +109,7 @@ static void mitigate_jamais_vu(void) { } /* Jamais Vu mitigation #3: Ensure all relevant DMA controllers are held in reset. */ - if ((CLK_RST_CONTROLLER_RST_DEVICES_H_0 & 0x4000004) != 0x4000004) { + if ((CLK_RST_CONTROLLER_RST_DEVICES_H_0 & 0x4000006) != 0x4000006) { generic_panic(); } } @@ -262,7 +262,7 @@ uint32_t cpu_suspend(uint64_t power_state, uint64_t entrypoint, uint64_t argumen } /* Perform I2C comms with TI charger if required. */ - configure_battery_hi_z_mode(); + configure_battery_hiz_mode(); /* Enable LP0 Wake Event Detection. */ enable_lp0_wake_events(); diff --git a/exosphere/src/smc_api.c b/exosphere/src/smc_api.c index adf947f29..ccde9391c 100644 --- a/exosphere/src/smc_api.c +++ b/exosphere/src/smc_api.c @@ -83,8 +83,12 @@ uint32_t smc_read_write_register(smc_args_t *args); /* Atmosphere SMC prototypes */ uint32_t smc_ams_iram_copy(smc_args_t *args); +/* TODO: Provide a way to set this. It's 0 on non-recovery boot anyway... */ +static uint32_t g_smc_blacklist_mask = 0; + typedef struct { uint32_t id; + uint32_t blacklist_mask; uint32_t (*handler)(smc_args_t *args); } smc_table_entry_t; @@ -94,43 +98,43 @@ typedef struct { } smc_table_t; static smc_table_entry_t g_smc_user_table[SMC_USER_HANDLERS] = { - {0, NULL}, - {0xC3000401, smc_set_config_user}, - {0xC3000002, smc_get_config_user}, - {0xC3000003, smc_check_status}, - {0xC3000404, smc_get_result}, - {0xC3000E05, smc_exp_mod}, - {0xC3000006, smc_get_random_bytes_for_user}, - {0xC3000007, smc_generate_aes_kek}, - {0xC3000008, smc_load_aes_key}, - {0xC3000009, smc_crypt_aes}, - {0xC300000A, smc_generate_specific_aes_key}, - {0xC300040B, smc_compute_cmac}, - {0xC300100C, smc_load_rsa_oaep_key}, - {0xC300100D, smc_decrypt_rsa_private_key}, - {0xC300100E, smc_load_secure_exp_mod_key}, - {0xC300060F, smc_secure_exp_mod}, - {0xC3000610, smc_unwrap_rsa_oaep_wrapped_titlekey}, - {0xC3000011, smc_load_titlekey}, - {0xC3000012, smc_unwrap_aes_wrapped_titlekey} + {0, 4, NULL}, + {0xC3000401, 4, smc_set_config_user}, + {0xC3000002, 1, smc_get_config_user}, + {0xC3000003, 1, smc_check_status}, + {0xC3000404, 1, smc_get_result}, + {0xC3000E05, 4, smc_exp_mod}, + {0xC3000006, 1, smc_get_random_bytes_for_user}, + {0xC3000007, 1, smc_generate_aes_kek}, + {0xC3000008, 1, smc_load_aes_key}, + {0xC3000009, 1, smc_crypt_aes}, + {0xC300000A, 1, smc_generate_specific_aes_key}, + {0xC300040B, 1, smc_compute_cmac}, + {0xC300100C, 1, smc_load_rsa_oaep_key}, + {0xC300100D, 2, smc_decrypt_rsa_private_key}, + {0xC300100E, 4, smc_load_secure_exp_mod_key}, + {0xC300060F, 2, smc_secure_exp_mod}, + {0xC3000610, 4, smc_unwrap_rsa_oaep_wrapped_titlekey}, + {0xC3000011, 4, smc_load_titlekey}, + {0xC3000012, 4, smc_unwrap_aes_wrapped_titlekey} }; static smc_table_entry_t g_smc_priv_table[SMC_PRIV_HANDLERS] = { - {0, NULL}, - {0xC4000001, smc_cpu_suspend}, - {0x84000002, smc_cpu_off}, - {0xC4000003, smc_cpu_on}, - {0xC3000004, smc_get_config_priv}, - {0xC3000005, smc_get_random_bytes_for_priv}, - {0xC3000006, smc_panic}, - {0xC3000007, smc_configure_carveout}, - {0xC3000008, smc_read_write_register} + {0, 4, NULL}, + {0xC4000001, 4, smc_cpu_suspend}, + {0x84000002, 4, smc_cpu_off}, + {0xC4000003, 1, smc_cpu_on}, + {0xC3000004, 1, smc_get_config_priv}, + {0xC3000005, 1, smc_get_random_bytes_for_priv}, + {0xC3000006, 1, smc_panic}, + {0xC3000007, 1, smc_configure_carveout}, + {0xC3000008, 1, smc_read_write_register} }; /* This is a table used for atmosphere-specific SMCs. */ static smc_table_entry_t g_smc_ams_table[SMC_AMS_HANDLERS] = { - {0, NULL}, - {0xF0000201, smc_ams_iram_copy}, + {0, 4, NULL}, + {0xF0000201, 0, smc_ams_iram_copy}, }; static smc_table_t g_smc_tables[SMC_HANDLER_COUNT + 1] = { @@ -177,6 +181,7 @@ void set_version_specific_smcs(void) { case ATMOSPHERE_TARGET_FIRMWARE_600: case ATMOSPHERE_TARGET_FIRMWARE_620: case ATMOSPHERE_TARGET_FIRMWARE_700: + case ATMOSPHERE_TARGET_FIRMWARE_800: /* No more LoadSecureExpModKey. */ g_smc_user_table[0xE].handler = NULL; g_smc_user_table[0xC].id = 0xC300D60C; @@ -292,13 +297,17 @@ void call_smc_handler(uint32_t handler_id, smc_args_t *args) { #endif /* Call function. */ - args->X[0] = smc_handler(args); + if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_800 || + (g_smc_tables[handler_id].handlers[smc_id].blacklist_mask & g_smc_blacklist_mask) == 0) { + args->X[0] = smc_handler(args); + } else { + /* Call not allowed due to current boot conditions. */ + args->X[0] = 6; + } + #if DEBUG_LOG_SMCS if (handler_id == SMC_HANDLER_USER) { *(volatile smc_args_t *)(get_iram_address_for_debug() + 0x100 + ((0x80 * num + 0x40) & 0x3FFF)) = *args; - /*if (num >= 0x69) { - panic(PANIC_REBOOT); - }*/ } #endif diff --git a/exosphere/src/smc_user.c b/exosphere/src/smc_user.c index 8a518cb5f..c8d9c12d4 100644 --- a/exosphere/src/smc_user.c +++ b/exosphere/src/smc_user.c @@ -51,6 +51,7 @@ static bool is_user_keyslot_valid(unsigned int keyslot) { case ATMOSPHERE_TARGET_FIRMWARE_600: case ATMOSPHERE_TARGET_FIRMWARE_620: case ATMOSPHERE_TARGET_FIRMWARE_700: + case ATMOSPHERE_TARGET_FIRMWARE_800: default: return keyslot <= 5; } diff --git a/exosphere/src/warmboot_init.c b/exosphere/src/warmboot_init.c index f935682f4..31e382471 100644 --- a/exosphere/src/warmboot_init.c +++ b/exosphere/src/warmboot_init.c @@ -67,8 +67,8 @@ void init_dma_controllers(unsigned int target_firmware) { /* MSELECT_CONFIG_0 |= WRAP_TO_INCR_SLAVE0(APC) | WRAP_TO_INCR_SLAVE1(PCIe) | WRAP_TO_INCR_SLAVE2(GPU) */ MAKE_REG32(0x50060000) |= 0x38000000; - /* AHB_ARBITRATION_DISABLE_0 - Disables USB and USB2 from arbitration */ - MAKE_REG32(0x6000C004) = 0x40040; + /* AHB_ARBITRATION_DISABLE_0 - Disables USB, USB2, and AHB-DMA from arbitration */ + MAKE_REG32(0x6000C004) = 0x40060; /* AHB_ARBITRATION_PRIORITY_CTRL_0 - Select high prio group with prio 7 */ MAKE_REG32(0x6000C008) = 0xE0000001; diff --git a/exosphere/src/warmboot_main.c b/exosphere/src/warmboot_main.c index 46c536138..fbbc55c2e 100644 --- a/exosphere/src/warmboot_main.c +++ b/exosphere/src/warmboot_main.c @@ -39,6 +39,17 @@ uintptr_t get_warmboot_main_stack_address(void) { return TZRAM_GET_SEGMENT_ADDRESS(TZRAM_SEGEMENT_ID_SECMON_EVT) + 0x780; } +static void warmboot_configure_hiz_mode(void) { + /* Enable input to I2C1 */ + PINMUX_AUX_GEN1_I2C_SCL_0 = 0x40; + PINMUX_AUX_GEN1_I2C_SDA_0 = 0x40; + + clkrst_reboot(CARDEVICE_I2C1); + i2c_init(0); + i2c_clear_ti_charger_bit_7(); + clkrst_disable(CARDEVICE_I2C1); +} + void __attribute__((noreturn)) warmboot_main(void) { /* This function and its callers are reached in either of the following events, under normal conditions: @@ -79,15 +90,10 @@ void __attribute__((noreturn)) warmboot_main(void) { /* Make PMC (2.x+), MC (4.x+) registers secure-only */ secure_additional_devices(); - if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_400 || configitem_get_hardware_type() == 0) { - /* Enable input to I2C1 */ - PINMUX_AUX_GEN1_I2C_SCL_0 = 0x40; - PINMUX_AUX_GEN1_I2C_SDA_0 = 0x40; - - clkrst_reboot(CARDEVICE_I2C1); - i2c_init(0); - i2c_clear_ti_charger_bit_7(); - clkrst_disable(CARDEVICE_I2C1); + if ((exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_400) || + ((exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_800) && configitem_get_hardware_type() == 0) || + (configitem_is_hiz_mode_enabled())) { + warmboot_configure_hiz_mode(); } clear_user_smc_in_progress(); diff --git a/fusee/fusee-secondary/src/key_derivation.c b/fusee/fusee-secondary/src/key_derivation.c index 8563f94fd..2a13a7c2c 100644 --- a/fusee/fusee-secondary/src/key_derivation.c +++ b/fusee/fusee-secondary/src/key_derivation.c @@ -149,6 +149,7 @@ int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, ui desired_keyblob = MASTERKEY_REVISION_620; break; case ATMOSPHERE_TARGET_FIRMWARE_700: + case ATMOSPHERE_TARGET_FIRMWARE_800: desired_keyblob = MASTERKEY_REVISION_700_CURRENT; break; default: @@ -223,6 +224,7 @@ int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, ui case ATMOSPHERE_TARGET_FIRMWARE_600: case ATMOSPHERE_TARGET_FIRMWARE_620: case ATMOSPHERE_TARGET_FIRMWARE_700: + case ATMOSPHERE_TARGET_FIRMWARE_800: decrypt_data_into_keyslot(0xA, 0xF, devicekey_4x_seed, 0x10); decrypt_data_into_keyslot(0xF, 0xF, devicekey_seed, 0x10); decrypt_data_into_keyslot(0xE, 0xC, masterkey_4x_seed, 0x10); diff --git a/fusee/fusee-secondary/src/nxboot.c b/fusee/fusee-secondary/src/nxboot.c index 754a0eaeb..7b52157e3 100644 --- a/fusee/fusee-secondary/src/nxboot.c +++ b/fusee/fusee-secondary/src/nxboot.c @@ -170,8 +170,10 @@ static uint32_t nxboot_get_target_firmware(const void *package1loader) { fatal_error("[NXBOOT]: Unable to identify package1!\n"); } } - case 0x0F: + case 0x0F: /* 7.0.0 - 7.0.1 */ return ATMOSPHERE_TARGET_FIRMWARE_700; + case 0x10: /* 8.0.0 */ + return ATMOSPHERE_TARGET_FIRMWARE_800; default: fatal_error("[NXBOOT]: Unable to identify package1!\n"); } @@ -412,7 +414,7 @@ uint32_t nxboot_main(void) { if (!package1_get_tsec_fw(&tsec_fw, package1loader, package1loader_size)) { fatal_error("[NXBOOT]: Failed to read the TSEC firmware from Package1loader!\n"); } - if (target_firmware == ATMOSPHERE_TARGET_FIRMWARE_700) { + if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_700) { tsec_fw_size = 0x3000; } else if (target_firmware == ATMOSPHERE_TARGET_FIRMWARE_620) { tsec_fw_size = 0x2900; From 4e5f033e415a72079029fe9b9eab32d8cdf6178d Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 18 Apr 2019 18:25:39 -0700 Subject: [PATCH 03/64] sm: for 8.0.0, add some first class homebrew support --- stratosphere/sm/source/sm_registration.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/stratosphere/sm/source/sm_registration.cpp b/stratosphere/sm/source/sm_registration.cpp index c9df5b026..390686e26 100644 --- a/stratosphere/sm/source/sm_registration.cpp +++ b/stratosphere/sm/source/sm_registration.cpp @@ -95,6 +95,15 @@ Registration::Service *Registration::GetFreeService() { } bool Registration::IsValidForSac(u8 *sac, size_t sac_size, u64 service, bool is_host) { + /* In 8.0.0, Nintendo removed the service apm:p -- however, all homebrew attempts to get */ + /* a handle to this when calling appletInitialize(). Because hbl has access to all services, */ + /* This would return true, and homebrew would *wait forever* trying to get a handle to a service */ + /* that will never register. Thus, in the interest of not breaking every single piece of homebrew */ + /* we will provide a little first class help. */ + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_800 && service == EncodeNameConstant("apm:p")) { + return false; + } + u8 cur_ctrl; u64 cur_service; u64 service_for_compare; From 908de31a0e890cf30bbd7370bcd5d6461dfd9268 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 19 Apr 2019 03:01:54 -0700 Subject: [PATCH 04/64] pm: on 7.0.0+, npns is launched in maintenance boot (closes #511) --- stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.hpp | 2 +- stratosphere/pm/source/pm_boot2.cpp | 5 +++++ stratosphere/pm/source/pm_main.cpp | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.hpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.hpp index e1bb3c48f..1e818aba2 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.hpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.hpp @@ -61,7 +61,7 @@ class FsMitmService : public IMitmServiceObject { /* TODO: intercepting everything seems to cause issues with sleep mode, for some reason. */ /* Figure out why, and address it. */ - if (tid == TitleId_AppletQlaunch) { + if (tid == TitleId_AppletQlaunch || tid == TitleId_AppletMaintenanceMenu) { has_launched_qlaunch = true; } diff --git a/stratosphere/pm/source/pm_boot2.cpp b/stratosphere/pm/source/pm_boot2.cpp index e04eb1c54..410eab8fc 100644 --- a/stratosphere/pm/source/pm_boot2.cpp +++ b/stratosphere/pm/source/pm_boot2.cpp @@ -247,6 +247,11 @@ void EmbeddedBoot2::Main() { if (!maintenance || std::get(launch_program)) { LaunchTitle(std::get(launch_program), FsStorageId_NandSystem, 0, NULL); } + + /* In 7.0.0, Npns was added to the list of titles to launch during maintenance. */ + if (maintenance && std::get(launch_program) == TitleId_Npns && GetRuntimeFirmwareVersion() >= FirmwareVersion_700) { + LaunchTitle(TitleId_Npns, FsStorageId_NandSystem, 0, NULL); + } } /* Allow for user-customizable programs. */ diff --git a/stratosphere/pm/source/pm_main.cpp b/stratosphere/pm/source/pm_main.cpp index 3aac9a359..5c53aa250 100644 --- a/stratosphere/pm/source/pm_main.cpp +++ b/stratosphere/pm/source/pm_main.cpp @@ -179,7 +179,7 @@ int main(int argc, char **argv) /* TODO: Create services. */ server_manager->AddWaitable(new ServiceServer("pm:shell", 3)); server_manager->AddWaitable(new ServiceServer("pm:dmnt", 2)); - server_manager->AddWaitable(new ServiceServer("pm:bm", 5)); + server_manager->AddWaitable(new ServiceServer("pm:bm", 6)); server_manager->AddWaitable(new ServiceServer("pm:info", 1)); /* Loop forever, servicing our services. */ From a3389e25c94f6f507b0946bf40bfff0ec1faa54c Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 19 Apr 2019 03:17:06 -0700 Subject: [PATCH 05/64] nogc: implement 8.0.0 patches --- ...9B141A23B06AFFF5A98055576D5F337A621C0233CE3.ips | Bin 0 -> 27 bytes ...C84D8E0F78013A3684D8AB5D128096674A8F7755B3D.ips | Bin 0 -> 27 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 common/defaults/kip_patches/default_nogc/B2F5176B3548364D079A29B141A23B06AFFF5A98055576D5F337A621C0233CE3.ips create mode 100644 common/defaults/kip_patches/default_nogc/DBD941C0C53C52CCF7202C84D8E0F78013A3684D8AB5D128096674A8F7755B3D.ips diff --git a/common/defaults/kip_patches/default_nogc/B2F5176B3548364D079A29B141A23B06AFFF5A98055576D5F337A621C0233CE3.ips b/common/defaults/kip_patches/default_nogc/B2F5176B3548364D079A29B141A23B06AFFF5A98055576D5F337A621C0233CE3.ips new file mode 100644 index 0000000000000000000000000000000000000000..430a0314777a6b8ed761c68e26e3ebb4fcca3368 GIT binary patch literal 27 icmWG=3~}}leKnPVu|YVUfq~-zv%J;;=J;!_{%!zmMF<7} literal 0 HcmV?d00001 diff --git a/common/defaults/kip_patches/default_nogc/DBD941C0C53C52CCF7202C84D8E0F78013A3684D8AB5D128096674A8F7755B3D.ips b/common/defaults/kip_patches/default_nogc/DBD941C0C53C52CCF7202C84D8E0F78013A3684D8AB5D128096674A8F7755B3D.ips new file mode 100644 index 0000000000000000000000000000000000000000..5b7451e38ff1eae0b9f82b25470aa5a95bb2d7b5 GIT binary patch literal 27 icmWG=3~}}lTj0vT*dQXafq~-zv%J;;=J;!_{%!zfR0q=l literal 0 HcmV?d00001 From 732a6159f7b01f1ef4146c671e7ffc684e51f424 Mon Sep 17 00:00:00 2001 From: hexkyz Date: Sat, 20 Apr 2019 17:30:03 +0100 Subject: [PATCH 06/64] fusee: Properly finalize SDMMC1 (fixes 8.0.0 issues with PCV) --- fusee/fusee-primary/src/sdmmc/sdmmc_core.c | 3 +++ fusee/fusee-secondary/src/nxfs.c | 26 ++++++++++++++++++-- fusee/fusee-secondary/src/sdmmc/sdmmc_core.c | 3 +++ sept/sept-secondary/src/sdmmc/sdmmc_core.c | 3 +++ 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/fusee/fusee-primary/src/sdmmc/sdmmc_core.c b/fusee/fusee-primary/src/sdmmc/sdmmc_core.c index 3c513de65..6e2093916 100644 --- a/fusee/fusee-primary/src/sdmmc/sdmmc_core.c +++ b/fusee/fusee-primary/src/sdmmc/sdmmc_core.c @@ -1202,6 +1202,9 @@ void sdmmc_finish(sdmmc_t *sdmmc) /* Power cycle for 100ms without power. */ mdelay(100); + + /* Disable the regulator. */ + max77620_regulator_enable(REGULATOR_LDO2, 0); } /* Force a register read to refresh the clock control value. */ diff --git a/fusee/fusee-secondary/src/nxfs.c b/fusee/fusee-secondary/src/nxfs.c index f35d5fe9a..4094b20e0 100644 --- a/fusee/fusee-secondary/src/nxfs.c +++ b/fusee/fusee-secondary/src/nxfs.c @@ -52,7 +52,8 @@ SdmmcPartitionNum g_current_emmc_partition = SDMMC_PARTITION_INVALID; static int mmc_partition_initialize(device_partition_t *devpart) { mmc_partition_info_t *mmcpart = (mmc_partition_info_t *)devpart->device_struct; - if (devpart->read_cipher != NULL || devpart->write_cipher != NULL) { + /* Allocate the crypto work buffer. */ + if ((devpart->read_cipher != NULL) || (devpart->write_cipher != NULL)) { devpart->crypto_work_buffer = memalign(16, devpart->sector_size * 16); if (devpart->crypto_work_buffer == NULL) { return ENOMEM; @@ -70,6 +71,7 @@ static int mmc_partition_initialize(device_partition_t *devpart) { g_ahb_redirect_enabled = true; } + /* Initialize hardware. */ if (mmcpart->device == &g_sd_device) { if (!g_sd_device_initialized) { int rc = sdmmc_device_sd_init(mmcpart->device, &g_sd_sdmmc, SDMMC_BUS_WIDTH_4BIT, SDMMC_SPEED_SDR104) ? 0 : EIO; @@ -94,13 +96,33 @@ static int mmc_partition_initialize(device_partition_t *devpart) { } static void mmc_partition_finalize(device_partition_t *devpart) { - free(devpart->crypto_work_buffer); + mmc_partition_info_t *mmcpart = (mmc_partition_info_t *)devpart->device_struct; + + /* Finalize hardware. */ + if (mmcpart->device == &g_sd_device) { + if (g_sd_device_initialized) { + sdmmc_device_finish(&g_sd_device); + g_sd_device_initialized = false; + } + devpart->initialized = false; + } else if (mmcpart->device == &g_emmc_device) { + if (g_emmc_device_initialized) { + sdmmc_device_finish(&g_emmc_device); + g_emmc_device_initialized = false; + } + devpart->initialized = false; + } /* Disable AHB redirection if necessary. */ if (g_ahb_redirect_enabled) { mc_disable_ahb_redirect(); g_ahb_redirect_enabled = false; } + + /* Free the crypto work buffer. */ + if (devpart->crypto_work_buffer != NULL) { + free(devpart->crypto_work_buffer); + } } static int mmc_partition_read(device_partition_t *devpart, void *dst, uint64_t sector, uint64_t num_sectors) { diff --git a/fusee/fusee-secondary/src/sdmmc/sdmmc_core.c b/fusee/fusee-secondary/src/sdmmc/sdmmc_core.c index 3c513de65..6e2093916 100644 --- a/fusee/fusee-secondary/src/sdmmc/sdmmc_core.c +++ b/fusee/fusee-secondary/src/sdmmc/sdmmc_core.c @@ -1202,6 +1202,9 @@ void sdmmc_finish(sdmmc_t *sdmmc) /* Power cycle for 100ms without power. */ mdelay(100); + + /* Disable the regulator. */ + max77620_regulator_enable(REGULATOR_LDO2, 0); } /* Force a register read to refresh the clock control value. */ diff --git a/sept/sept-secondary/src/sdmmc/sdmmc_core.c b/sept/sept-secondary/src/sdmmc/sdmmc_core.c index 3c513de65..6e2093916 100644 --- a/sept/sept-secondary/src/sdmmc/sdmmc_core.c +++ b/sept/sept-secondary/src/sdmmc/sdmmc_core.c @@ -1202,6 +1202,9 @@ void sdmmc_finish(sdmmc_t *sdmmc) /* Power cycle for 100ms without power. */ mdelay(100); + + /* Disable the regulator. */ + max77620_regulator_enable(REGULATOR_LDO2, 0); } /* Force a register read to refresh the clock control value. */ From 1a396235cd6c18e156a322290767f0391ae02ebf Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 19 Apr 2019 04:20:50 -0700 Subject: [PATCH 07/64] fusee: only partially hash 8.0.0 kernel, add ControlCodeMemory patch --- fusee/fusee-secondary/src/kernel_patches.c | 29 ++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/fusee/fusee-secondary/src/kernel_patches.c b/fusee/fusee-secondary/src/kernel_patches.c index f12cc1fff..46754ccf3 100644 --- a/fusee/fusee-secondary/src/kernel_patches.c +++ b/fusee/fusee-secondary/src/kernel_patches.c @@ -41,6 +41,7 @@ typedef struct { typedef struct { uint8_t hash[0x20]; /* TODO: Come up with a better way to identify kernels, that doesn't rely on hashing them. */ + size_t hash_size; /* Only hash the first N bytes of the kernel, if this is set. */ size_t free_code_space_offset; unsigned int num_patches; const kernel_patch_t *patches; @@ -371,6 +372,7 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(700, proc_id_recv)[] = {0xA9BF static const instruction_t MAKE_KERNEL_PATCH_NAME(500, svc_control_codememory)[] = {MAKE_NOP}; static const instruction_t MAKE_KERNEL_PATCH_NAME(600, svc_control_codememory)[] = {MAKE_NOP}; static const instruction_t MAKE_KERNEL_PATCH_NAME(700, svc_control_codememory)[] = {MAKE_NOP}; +static const instruction_t MAKE_KERNEL_PATCH_NAME(800, svc_control_codememory)[] = {MAKE_NOP}; /* Hook Definitions. */ static const kernel_patch_t g_kernel_patches_100[] = { @@ -532,6 +534,14 @@ static const kernel_patch_t g_kernel_patches_700[] = { .patch_offset = 0x3C6E0, } }; +static const kernel_patch_t g_kernel_patches_800[] = { + /* TODO: Send, Receive. */ + { /* svcControlCodeMemory Patch. */ + .payload_num_instructions = sizeof(MAKE_KERNEL_PATCH_NAME(800, svc_control_codememory))/sizeof(instruction_t), + .payload = MAKE_KERNEL_PATCH_NAME(800, svc_control_codememory), + .patch_offset = 0x3FAD0, + } +}; #define KERNEL_PATCHES(vers) .num_patches = sizeof(g_kernel_patches_##vers)/sizeof(kernel_patch_t), .patches = g_kernel_patches_##vers, @@ -581,6 +591,12 @@ static const kernel_info_t g_kernel_infos[] = { .hash = {0xA2, 0x5E, 0x47, 0x0C, 0x8E, 0x6D, 0x2F, 0xD7, 0x5D, 0xAD, 0x24, 0xD7, 0xD8, 0x24, 0x34, 0xFB, 0xCD, 0x77, 0xBB, 0xE6, 0x66, 0x03, 0xCB, 0xAF, 0xAB, 0x85, 0x45, 0xA0, 0x91, 0xAF, 0x34, 0x25}, .free_code_space_offset = 0x5FEC0, KERNEL_PATCHES(700) + }, + { /* 8.0.0. */ + .hash = {0x8C, 0x2E, 0x2C, 0x0D, 0x89, 0x19, 0x4A, 0x07, 0xF0, 0xE0, 0x7B, 0x44, 0xA7, 0x3D, 0x91, 0x81, 0x24, 0xFB, 0x67, 0x0D, 0x5B, 0xD5, 0x3F, 0x0F, 0x1C, 0xD3, 0x48, 0x06, 0x19, 0xD1, 0x27, 0xE6}, + .hash_size = 0x95000, + .free_code_space_offset = 0x607F0, + KERNEL_PATCHES(800) } }; @@ -607,10 +623,19 @@ uint8_t *search_pattern(void *_mem, size_t mem_size, const void *_pattern, size_ const kernel_info_t *get_kernel_info(void *kernel, size_t size) { uint8_t calculated_hash[0x20]; + uint8_t calculated_partial_hash[0x20]; se_calculate_sha256(calculated_hash, kernel, size); + for (unsigned int i = 0; i < sizeof(g_kernel_infos)/sizeof(kernel_info_t); i++) { - if (memcmp(calculated_hash, g_kernel_infos[i].hash, sizeof(calculated_hash)) == 0) { - return &g_kernel_infos[i]; + if (g_kernel_infos[i].hash_size == 0 || size <= g_kernel_infos[i].hash_size) { + if (memcmp(calculated_hash, g_kernel_infos[i].hash, sizeof(calculated_hash)) == 0) { + return &g_kernel_infos[i]; + } + } else { + se_calculate_sha256(calculated_partial_hash, kernel, g_kernel_infos[i].hash_size); + if (memcmp(calculated_partial_hash, g_kernel_infos[i].hash, sizeof(calculated_partial_hash)) == 0) { + return &g_kernel_infos[i]; + } } } return NULL; From 81895c8019f7c60983a3b03b9e54dfca5b4edb71 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 20 Apr 2019 03:41:03 -0700 Subject: [PATCH 08/64] fusee: update to support booting 8.0.0 --- ...A23B06AFFF5A98055576D5F337A621C0233CE3.ips | Bin 27 -> 27 bytes ...E0F78013A3684D8AB5D128096674A8F7755B3D.ips | Bin 27 -> 27 bytes exosphere/src/package2.c | 15 ++- exosphere/src/utils.c | 8 +- fusee/fusee-secondary/src/kernel_patches.c | 86 +++++++++++++++++- fusee/fusee-secondary/src/kernel_patches.h | 2 +- fusee/fusee-secondary/src/main.c | 4 +- fusee/fusee-secondary/src/package2.c | 32 +++++-- fusee/fusee-secondary/src/utils.c | 4 +- 9 files changed, 128 insertions(+), 23 deletions(-) diff --git a/common/defaults/kip_patches/default_nogc/B2F5176B3548364D079A29B141A23B06AFFF5A98055576D5F337A621C0233CE3.ips b/common/defaults/kip_patches/default_nogc/B2F5176B3548364D079A29B141A23B06AFFF5A98055576D5F337A621C0233CE3.ips index 430a0314777a6b8ed761c68e26e3ebb4fcca3368..05869a6e9febb7fd1dc549376cc45e6725373eb1 100644 GIT binary patch literal 27 icmWG=3~}}leKVDTu|YVKfq~-zv%J;;=J;!_{%!zmjR*<= literal 27 icmWG=3~}}leKnPVu|YVUfq~-zv%J;;=J;!_{%!zmMF<7} diff --git a/common/defaults/kip_patches/default_nogc/DBD941C0C53C52CCF7202C84D8E0F78013A3684D8AB5D128096674A8F7755B3D.ips b/common/defaults/kip_patches/default_nogc/DBD941C0C53C52CCF7202C84D8E0F78013A3684D8AB5D128096674A8F7755B3D.ips index 5b7451e38ff1eae0b9f82b25470aa5a95bb2d7b5..6e66bc3bba73e7a2a407301f094e15b9ca146d31 100644 GIT binary patch literal 27 icmWG=3~}}lTja{X*dQXefq~-zv%J;;=J;!_{%!zfoCntc literal 27 icmWG=3~}}lTj0vT*dQXafq~-zv%J;;=J;!_{%!zfR0q=l diff --git a/exosphere/src/package2.c b/exosphere/src/package2.c index 2aedb42f4..c62781b95 100644 --- a/exosphere/src/package2.c +++ b/exosphere/src/package2.c @@ -336,10 +336,15 @@ static bool validate_package2_metadata(package2_meta_t *metadata) { } /* Ensure no overlap with later sections. */ - for (unsigned int later_section = section + 1; later_section < PACKAGE2_SECTION_MAX; later_section++) { - uint32_t later_section_end = metadata->section_offsets[later_section] + metadata->section_sizes[later_section]; - if (overlaps(metadata->section_offsets[section], section_end, metadata->section_offsets[later_section], later_section_end)) { - return false; + if (metadata->section_sizes[section] != 0) { + for (unsigned int later_section = section + 1; later_section < PACKAGE2_SECTION_MAX; later_section++) { + if (metadata->section_sizes[later_section] == 0) { + continue; + } + uint32_t later_section_end = metadata->section_offsets[later_section] + metadata->section_sizes[later_section]; + if (overlaps(metadata->section_offsets[section], section_end, metadata->section_offsets[later_section], later_section_end)) { + return false; + } } } @@ -392,7 +397,7 @@ static uint32_t decrypt_and_validate_header(package2_header_t *header) { } /* Ensure we successfully decrypted the header. */ - if (mkey_rev > mkey_get_revision()) { + if (mkey_rev > mkey_get_revision()) { panic(0xFAF00003); } } else if (!validate_package2_metadata(&header->metadata)) { diff --git a/exosphere/src/utils.c b/exosphere/src/utils.c index 4de7781c9..df7e6affd 100644 --- a/exosphere/src/utils.c +++ b/exosphere/src/utils.c @@ -37,8 +37,8 @@ __attribute__ ((noreturn)) void panic(uint32_t code) { SAVE_SYSREG64(ELR_EL3, 0x18); SAVE_SYSREG64(FAR_EL3, 0x20); MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x450ull) = 0x2; - MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x400ull) = 0x10; - */ + MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x400ull) = 0x10; */ + /* TODO: Custom Panic Driver, which displays to screen without rebooting. */ /* For now, just use NX BOOTLOADER's panic. */ @@ -68,9 +68,9 @@ __attribute__ ((noreturn)) void panic_predefined(uint32_t which) { __attribute__((noinline)) bool overlaps(uint64_t as, uint64_t ae, uint64_t bs, uint64_t be) { - if(as <= bs && bs <= ae) + if(as <= bs && bs < ae) return true; - if(bs <= as && as <= be) + if(bs <= as && as < be) return true; return false; } diff --git a/fusee/fusee-secondary/src/kernel_patches.c b/fusee/fusee-secondary/src/kernel_patches.c index 46754ccf3..12bf7ab83 100644 --- a/fusee/fusee-secondary/src/kernel_patches.c +++ b/fusee/fusee-secondary/src/kernel_patches.c @@ -42,6 +42,8 @@ typedef struct { typedef struct { uint8_t hash[0x20]; /* TODO: Come up with a better way to identify kernels, that doesn't rely on hashing them. */ size_t hash_size; /* Only hash the first N bytes of the kernel, if this is set. */ + size_t embedded_ini_offset; /* 8.0.0+ embeds the INI in kernel section. */ + size_t embedded_ini_ptr; /* 8.0.0+ embeds the INI in kernel section. */ size_t free_code_space_offset; unsigned int num_patches; const kernel_patch_t *patches; @@ -367,6 +369,61 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(700, proc_id_send)[] = {0xA9BF static const uint8_t MAKE_KERNEL_PATTERN_NAME(700, proc_id_recv)[] = {0x68, 0x03, 0x40, 0xF9, 0x08, 0x1D, 0x40, 0xF9, 0xE0, 0x03, 0x1B, 0xAA, 0x00, 0x01, 0x3F, 0xD6, 0xA9, 0x83, 0x50, 0xF8, 0xE8, 0x03, 0x16, 0x2A, 0xD6, 0x0A, 0x00, 0x11}; static const instruction_t MAKE_KERNEL_PATCH_NAME(700, proc_id_recv)[] = {0xA9BF2FEA, 0xF9404FEB, 0x2A1603EA, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF9400368, 0xF9401D08, 0xAA1B03E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0}; +/* + stp x10, x11, [sp, #-0x10]! + ldr x11, [sp, #0x70] + mov w10, w25 + lsl x10, x10, #2 + ldr x10, [x11, x10] + mov x9, #0x0000ffffffffffff + and x8, x10, x9 + mov x9, #0xffff000000000000 + and x10, x10, x9 + mov x9, #0xfffe000000000000 + cmp x10, x9 + beq #0x20 + + stp x8, x9, [sp, #-0x10]! + ldr x8, [x21] + ldr x8, [x8, #0x38] + mov x0, x21 + blr x8 + ldp x8, x9, [sp],#0x10 + mov x8, x0 + + ldp x10, x11, [sp],#0x10 + mov x0, x8 +*/ +static const uint8_t MAKE_KERNEL_PATTERN_NAME(800, proc_id_send)[] = {0xA8, 0x02, 0x40, 0xF9, 0x08, 0x1D, 0x40, 0xF9, 0xE0, 0x03, 0x15, 0xAA, 0x00, 0x01, 0x3F, 0xD6, 0xE8, 0x03, 0x19, 0x2A, 0x39, 0x0B, 0x00, 0x11, 0x08, 0xF5, 0x7E, 0xD3}; +static const instruction_t MAKE_KERNEL_PATCH_NAME(800, proc_id_send)[] = {0xA9BF2FEA, 0xF9403BEB, 0x2A1903EA, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF94002A8, 0xF9401D08, 0xAA1503E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0}; +/* + stp x10, x11, [sp, #-0x10]! + ldr x11, [sp, #0x98] + mov w10, w22 + lsl x10, x10, #2 + ldr x10, [x11, x10] + mov x9, #0x0000ffffffffffff + and x8, x10, x9 + mov x9, #0xffff000000000000 + and x10, x10, x9 + mov x9, #0xfffe000000000000 + cmp x10, x9 + beq #0x20 + + stp x8, x9, [sp, #-0x10]! + ldr x8, [x27] + ldr x8, [x8, #0x38] + mov x0, x27 + blr x8 + ldp x8, x9, [sp],#0x10 + mov x8, x0 + + ldp x10, x11, [sp],#0x10 + mov x0, x8 +*/ +static const uint8_t MAKE_KERNEL_PATTERN_NAME(800, proc_id_recv)[] = {0x68, 0x03, 0x40, 0xF9, 0x08, 0x1D, 0x40, 0xF9, 0xE0, 0x03, 0x1B, 0xAA, 0x00, 0x01, 0x3F, 0xD6, 0xA9, 0x83, 0x50, 0xF8, 0xE8, 0x03, 0x16, 0x2A, 0xD6, 0x0A, 0x00, 0x11}; +static const instruction_t MAKE_KERNEL_PATCH_NAME(800, proc_id_recv)[] = {0xA9BF2FEA, 0xF9404FEB, 0x2A1603EA, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF9400368, 0xF9401D08, 0xAA1B03E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0}; + /* svcControlCodeMemory Patches */ /* b.eq -> nop */ static const instruction_t MAKE_KERNEL_PATCH_NAME(500, svc_control_codememory)[] = {MAKE_NOP}; @@ -535,7 +592,22 @@ static const kernel_patch_t g_kernel_patches_700[] = { } }; static const kernel_patch_t g_kernel_patches_800[] = { - /* TODO: Send, Receive. */ + { /* Send Message Process ID Patch. */ + .pattern_size = 0x1C, + .pattern = MAKE_KERNEL_PATTERN_NAME(800, proc_id_send), + .pattern_hook_offset = 0x0, + .payload_num_instructions = sizeof(MAKE_KERNEL_PATCH_NAME(800, proc_id_send))/sizeof(instruction_t), + .branch_back_offset = 0x10, + .payload = MAKE_KERNEL_PATCH_NAME(800, proc_id_send) + }, + { /* Receive Message Process ID Patch. */ + .pattern_size = 0x1C, + .pattern = MAKE_KERNEL_PATTERN_NAME(800, proc_id_recv), + .pattern_hook_offset = 0x0, + .payload_num_instructions = sizeof(MAKE_KERNEL_PATCH_NAME(800, proc_id_recv))/sizeof(instruction_t), + .branch_back_offset = 0x10, + .payload = MAKE_KERNEL_PATCH_NAME(800, proc_id_recv) + }, { /* svcControlCodeMemory Patch. */ .payload_num_instructions = sizeof(MAKE_KERNEL_PATCH_NAME(800, svc_control_codememory))/sizeof(instruction_t), .payload = MAKE_KERNEL_PATCH_NAME(800, svc_control_codememory), @@ -593,8 +665,10 @@ static const kernel_info_t g_kernel_infos[] = { KERNEL_PATCHES(700) }, { /* 8.0.0. */ - .hash = {0x8C, 0x2E, 0x2C, 0x0D, 0x89, 0x19, 0x4A, 0x07, 0xF0, 0xE0, 0x7B, 0x44, 0xA7, 0x3D, 0x91, 0x81, 0x24, 0xFB, 0x67, 0x0D, 0x5B, 0xD5, 0x3F, 0x0F, 0x1C, 0xD3, 0x48, 0x06, 0x19, 0xD1, 0x27, 0xE6}, + .hash = {0x24, 0x2A, 0x50, 0x42, 0xFC, 0x6C, 0x0A, 0x64, 0xE7, 0xC2, 0x16, 0x0F, 0xD8, 0x53, 0x1E, 0xFC, 0x5C, 0x25, 0xCA, 0xC0, 0x5A, 0xED, 0x01, 0xA7, 0xE3, 0x11, 0x78, 0x6C, 0x07, 0x10, 0x32, 0xA1}, .hash_size = 0x95000, + .embedded_ini_offset = 0x95000, + .embedded_ini_ptr = 0x168, .free_code_space_offset = 0x607F0, KERNEL_PATCHES(800) } @@ -641,8 +715,9 @@ const kernel_info_t *get_kernel_info(void *kernel, size_t size) { return NULL; } -void package2_patch_kernel(void *_kernel, size_t size, bool is_sd_kernel) { +void package2_patch_kernel(void *_kernel, size_t size, bool is_sd_kernel, void **out_ini1) { const kernel_info_t *kernel_info = get_kernel_info(_kernel, size); + *out_ini1 = NULL; /* Apply IPS patches. */ apply_kernel_ips_patches(_kernel, size); @@ -656,6 +731,11 @@ void package2_patch_kernel(void *_kernel, size_t size, bool is_sd_kernel) { return; } + if (kernel_info->embedded_ini_offset != 0) { + *out_ini1 = (void *)((uintptr_t)_kernel + kernel_info->embedded_ini_offset); + *((volatile uint64_t *)((uintptr_t)_kernel + kernel_info->embedded_ini_ptr)) = (uint64_t)size; + } + /* Apply hooks and patches. */ uint8_t *kernel = (uint8_t *)_kernel; size_t free_space_offset = kernel_info->free_code_space_offset; diff --git a/fusee/fusee-secondary/src/kernel_patches.h b/fusee/fusee-secondary/src/kernel_patches.h index c55c0ed81..60893aed6 100644 --- a/fusee/fusee-secondary/src/kernel_patches.h +++ b/fusee/fusee-secondary/src/kernel_patches.h @@ -19,6 +19,6 @@ #include "utils.h" -void package2_patch_kernel(void *kernel, size_t kernel_size, bool is_sd_kernel); +void package2_patch_kernel(void *kernel, size_t kernel_size, bool is_sd_kernel, void **out_ini1); #endif \ No newline at end of file diff --git a/fusee/fusee-secondary/src/main.c b/fusee/fusee-secondary/src/main.c index c6d53cdf5..44a276940 100644 --- a/fusee/fusee-secondary/src/main.c +++ b/fusee/fusee-secondary/src/main.c @@ -59,10 +59,10 @@ static void setup_env(void) { train_dram(); } + static void cleanup_env(void) { /* Unmount everything (this causes all open files to be flushed and closed) */ nxfs_unmount_all(); - //console_end(); } static void exit_callback(int rc) { @@ -118,6 +118,8 @@ int main(int argc, void **argv) { uint32_t boot_memaddr = nxboot_main(); /* Wait for the splash screen to have been displayed as long as it should be. */ splash_screen_wait_delay(); + /* Cleanup environment. */ + cleanup_env(); /* Finish boot. */ nxboot_finish(boot_memaddr); } else { diff --git a/fusee/fusee-secondary/src/package2.c b/fusee/fusee-secondary/src/package2.c index f8374efc0..ee988cf25 100644 --- a/fusee/fusee-secondary/src/package2.c +++ b/fusee/fusee-secondary/src/package2.c @@ -16,6 +16,7 @@ #include #include +#include #include "utils.h" #include "masterkey.h" #include "stratosphere.h" @@ -86,10 +87,22 @@ void package2_rebuild_and_copy(package2_header_t *package2, uint32_t target_firm } /* Perform any patches we want to the NX kernel. */ - package2_patch_kernel(kernel, kernel_size, is_sd_kernel); - + package2_patch_kernel(kernel, kernel_size, is_sd_kernel, (void *)&orig_ini1); + + /* Ensure we know where embedded INI is if present, and we don't if not. */ + if ((target_firmware < ATMOSPHERE_TARGET_FIRMWARE_800 && orig_ini1 != NULL) || + (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_800 && orig_ini1 == NULL)) { + fatal_error("Error: inappropriate kernel embedded ini context"); + } + print(SCREEN_LOG_LEVEL_DEBUG, "Rebuilding the INI1 section...\n"); - package2_get_src_section((void *)&orig_ini1, package2, PACKAGE2_SECTION_INI1); + if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_800) { + package2_get_src_section((void *)&orig_ini1, package2, PACKAGE2_SECTION_INI1); + } else { + /* On 8.0.0, place INI1 right after kernelldr for our sanity. */ + package2->metadata.section_offsets[PACKAGE2_SECTION_INI1] = package2->metadata.section_offsets[PACKAGE2_SECTION_KERNEL] + package2->metadata.section_sizes[PACKAGE2_SECTION_KERNEL]; + } + /* Perform any patches to the INI1, rebuilding it (This is where our built-in sysmodules will be added.) */ rebuilt_ini1 = package2_rebuild_ini1(orig_ini1, target_firmware); print(SCREEN_LOG_LEVEL_DEBUG, "Rebuilt INI1...\n"); @@ -187,10 +200,15 @@ static bool package2_validate_metadata(package2_meta_t *metadata, uint8_t data[] } /* Ensure no overlap with later sections. */ - for (unsigned int later_section = section + 1; later_section < PACKAGE2_SECTION_MAX; later_section++) { - uint32_t later_section_end = metadata->section_offsets[later_section] + metadata->section_sizes[later_section]; - if (overlaps(metadata->section_offsets[section], section_end, metadata->section_offsets[later_section], later_section_end)) { - return false; + if (metadata->section_sizes[section] != 0) { + for (unsigned int later_section = section + 1; later_section < PACKAGE2_SECTION_MAX; later_section++) { + if (metadata->section_sizes[later_section] == 0) { + continue; + } + uint32_t later_section_end = metadata->section_offsets[later_section] + metadata->section_sizes[later_section]; + if (overlaps(metadata->section_offsets[section], section_end, metadata->section_offsets[later_section], later_section_end)) { + return false; + } } } diff --git a/fusee/fusee-secondary/src/utils.c b/fusee/fusee-secondary/src/utils.c index ccb6835d1..ef9d08eac 100644 --- a/fusee/fusee-secondary/src/utils.c +++ b/fusee/fusee-secondary/src/utils.c @@ -168,9 +168,9 @@ __attribute__((noreturn)) void fatal_error(const char *fmt, ...) { __attribute__((noinline)) bool overlaps(uint64_t as, uint64_t ae, uint64_t bs, uint64_t be) { - if(as <= bs && bs <= ae) + if(as <= bs && bs < ae) return true; - if(bs <= as && as <= be) + if(bs <= as && as < be) return true; return false; } From 9691286d73581cecd95110fc993d3186e52ad75c Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 20 Apr 2019 10:30:57 -0700 Subject: [PATCH 09/64] Bump version to 0.8.8 --- common/include/atmosphere/version.h | 2 +- docs/changelog.md | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/common/include/atmosphere/version.h b/common/include/atmosphere/version.h index 642a0fe1b..61078a1e5 100644 --- a/common/include/atmosphere/version.h +++ b/common/include/atmosphere/version.h @@ -19,7 +19,7 @@ #define ATMOSPHERE_RELEASE_VERSION_MAJOR 0 #define ATMOSPHERE_RELEASE_VERSION_MINOR 8 -#define ATMOSPHERE_RELEASE_VERSION_MICRO 7 +#define ATMOSPHERE_RELEASE_VERSION_MICRO 8 #define ATMOSPHERE_SUPPORTED_HOS_VERSION_MAJOR 8 #define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 0 diff --git a/docs/changelog.md b/docs/changelog.md index 2ada32830..2583fd18b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,4 +1,10 @@ # Changelog +## 0.8.8 ++ Support was added for firmware version 8.0.0. ++ Custom exception handlers were added to stratosphere modules. + + If a crash happens in a core atmosphere module now, instead of silently failing a reboot will occur to log the information to the SD card. ++ A bug was fixed in creport that caused games to hang when crashing under certain circumstances. ++ General system stability improvements to enhance the user's experience. ## 0.8.7 + A few bugs were fixed that could cause fatal to fail to show an error under certain circumstances. + A bug was fixed that caused an error when launching certain games (e.g. Hellblade: Senua's Sacrifice). From 664e5e6b529f73eaf4b73d3950921080befa263e Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 20 Apr 2019 10:37:53 -0700 Subject: [PATCH 10/64] Small 0.8.8 changelog addition --- docs/changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.md b/docs/changelog.md index 2583fd18b..ef1b763c9 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -4,6 +4,7 @@ + Custom exception handlers were added to stratosphere modules. + If a crash happens in a core atmosphere module now, instead of silently failing a reboot will occur to log the information to the SD card. + A bug was fixed in creport that caused games to hang when crashing under certain circumstances. ++ A bug was fixed that prevented maintenance mode from booting on 7.0.0+. + General system stability improvements to enhance the user's experience. ## 0.8.7 + A few bugs were fixed that could cause fatal to fail to show an error under certain circumstances. From 87f7a6ebdc9d004d88cb017247c0f985ef335079 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 20 Apr 2019 11:24:05 -0700 Subject: [PATCH 11/64] fusee: support both exfat and non-exfat --- fusee/fusee-secondary/src/kernel_patches.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/fusee/fusee-secondary/src/kernel_patches.c b/fusee/fusee-secondary/src/kernel_patches.c index 12bf7ab83..0568d8a01 100644 --- a/fusee/fusee-secondary/src/kernel_patches.c +++ b/fusee/fusee-secondary/src/kernel_patches.c @@ -41,7 +41,8 @@ typedef struct { typedef struct { uint8_t hash[0x20]; /* TODO: Come up with a better way to identify kernels, that doesn't rely on hashing them. */ - size_t hash_size; /* Only hash the first N bytes of the kernel, if this is set. */ + size_t hash_offset; /* Start hashing at offset N, if this is set. */ + size_t hash_size; /* Only hash the first N bytes of the kernel, if this is set. */ size_t embedded_ini_offset; /* 8.0.0+ embeds the INI in kernel section. */ size_t embedded_ini_ptr; /* 8.0.0+ embeds the INI in kernel section. */ size_t free_code_space_offset; @@ -665,8 +666,9 @@ static const kernel_info_t g_kernel_infos[] = { KERNEL_PATCHES(700) }, { /* 8.0.0. */ - .hash = {0x24, 0x2A, 0x50, 0x42, 0xFC, 0x6C, 0x0A, 0x64, 0xE7, 0xC2, 0x16, 0x0F, 0xD8, 0x53, 0x1E, 0xFC, 0x5C, 0x25, 0xCA, 0xC0, 0x5A, 0xED, 0x01, 0xA7, 0xE3, 0x11, 0x78, 0x6C, 0x07, 0x10, 0x32, 0xA1}, - .hash_size = 0x95000, + .hash = {0xA6, 0xAD, 0x5D, 0x7F, 0xCF, 0x25, 0x80, 0xAE, 0xE6, 0x57, 0x9F, 0x6F, 0xC5, 0xC5, 0xF6, 0x13, 0x77, 0x23, 0xAC, 0x88, 0x79, 0x76, 0xF7, 0x25, 0x06, 0x16, 0x35, 0x3B, 0x3F, 0xA7, 0x59, 0x49}, + .hash_offset = 0x1A8, + .hash_size = 0x95000 - 0x1A8, .embedded_ini_offset = 0x95000, .embedded_ini_ptr = 0x168, .free_code_space_offset = 0x607F0, @@ -706,7 +708,7 @@ const kernel_info_t *get_kernel_info(void *kernel, size_t size) { return &g_kernel_infos[i]; } } else { - se_calculate_sha256(calculated_partial_hash, kernel, g_kernel_infos[i].hash_size); + se_calculate_sha256(calculated_partial_hash, (void *)((uintptr_t)kernel + g_kernel_infos[i].hash_offset), g_kernel_infos[i].hash_size); if (memcmp(calculated_partial_hash, g_kernel_infos[i].hash, sizeof(calculated_partial_hash)) == 0) { return &g_kernel_infos[i]; } From 5c9d0f05b12ba7276187e3bcbbbf7635e8cac186 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 20 Apr 2019 16:53:56 -0700 Subject: [PATCH 12/64] loader: use libstratosphere randomness --- stratosphere/libstratosphere | 2 +- stratosphere/loader/source/ldr_map.cpp | 5 +- stratosphere/loader/source/ldr_nro.cpp | 1 - stratosphere/loader/source/ldr_nso.cpp | 3 +- stratosphere/loader/source/ldr_random.cpp | 65 ----------------------- stratosphere/loader/source/ldr_random.hpp | 25 --------- 6 files changed, 4 insertions(+), 97 deletions(-) delete mode 100644 stratosphere/loader/source/ldr_random.cpp delete mode 100644 stratosphere/loader/source/ldr_random.hpp diff --git a/stratosphere/libstratosphere b/stratosphere/libstratosphere index 163d9259a..880bce909 160000 --- a/stratosphere/libstratosphere +++ b/stratosphere/libstratosphere @@ -1 +1 @@ -Subproject commit 163d9259a365c3a89c1a837b28e7609d45795e40 +Subproject commit 880bce9092fbef0bcbf101b8ec2e3d2c5af3fb98 diff --git a/stratosphere/loader/source/ldr_map.cpp b/stratosphere/loader/source/ldr_map.cpp index 4e307937e..c05157bca 100644 --- a/stratosphere/loader/source/ldr_map.cpp +++ b/stratosphere/loader/source/ldr_map.cpp @@ -18,7 +18,6 @@ #include #include "ldr_map.hpp" -#include "ldr_random.hpp" Result MapUtils::LocateSpaceForMap(u64 *out, u64 out_size) { if (kernelAbove200()) { @@ -142,7 +141,7 @@ Result MapUtils::MapCodeMemoryForProcessModern(Handle process_h, u64 base_addres u64 try_address; for (unsigned int i = 0; i < 0x200; i++) { while (true) { - try_address = address_space.addspace_base + (RandomUtils::GetRandomU64((u64)(address_space.addspace_size - size) >> 12) << 12); + try_address = address_space.addspace_base + (StratosphereRandomUtils::GetRandomU64((u64)(address_space.addspace_size - size) >> 12) << 12); if (address_space.heap_size && (address_space.heap_base <= try_address + size - 1 && try_address <= address_space.heap_end - 1)) { continue; } @@ -179,7 +178,7 @@ Result MapUtils::MapCodeMemoryForProcessDeprecated(Handle process_h, bool is_64_ u64 try_address; for (unsigned int i = 0; i < 0x200; i++) { - try_address = addspace_base + (RandomUtils::GetRandomU64((u64)(addspace_size - size) >> 12) << 12); + try_address = addspace_base + (StratosphereRandomUtils::GetRandomU64((u64)(addspace_size - size) >> 12) << 12); rc = svcMapProcessCodeMemory(process_h, try_address, base_address, size); if (rc != ResultKernelInvalidMemoryState) { break; diff --git a/stratosphere/loader/source/ldr_nro.cpp b/stratosphere/loader/source/ldr_nro.cpp index 0f2cfbfda..a28da564a 100644 --- a/stratosphere/loader/source/ldr_nro.cpp +++ b/stratosphere/loader/source/ldr_nro.cpp @@ -23,7 +23,6 @@ #include "ldr_nro.hpp" #include "ldr_registration.hpp" #include "ldr_map.hpp" -#include "ldr_random.hpp" Result NroUtils::ValidateNrrHeader(NrrHeader *header, u64 size, u64 title_id_min) { if (header->magic != MAGIC_NRR0) { diff --git a/stratosphere/loader/source/ldr_nso.cpp b/stratosphere/loader/source/ldr_nso.cpp index 3f4045093..8104e9353 100644 --- a/stratosphere/loader/source/ldr_nso.cpp +++ b/stratosphere/loader/source/ldr_nso.cpp @@ -21,7 +21,6 @@ #include "lz4.h" #include "ldr_nso.hpp" #include "ldr_map.hpp" -#include "ldr_random.hpp" #include "ldr_patcher.hpp" #include "ldr_content_management.hpp" @@ -226,7 +225,7 @@ Result NsoUtils::CalculateNsoLoadExtents(u32 addspace_type, u32 args_size, NsoLo u64 aslr_slide = 0; if (addspace_type & 0x20) { - aslr_slide = RandomUtils::GetRandomU64((addspace_size - extents->total_size) >> 21) << 21; + aslr_slide = StratosphereRandomUtils::GetRandomU64((addspace_size - extents->total_size) >> 21) << 21; } extents->base_address = addspace_start + aslr_slide; diff --git a/stratosphere/loader/source/ldr_random.cpp b/stratosphere/loader/source/ldr_random.cpp deleted file mode 100644 index 197c4bdf2..000000000 --- a/stratosphere/loader/source/ldr_random.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include - -#include "ldr_random.hpp" - -/* Official HOS uses TinyMT. This is high effort. Let's just use XorShift. */ -/* https://en.wikipedia.org/wiki/Xorshift */ - -static u32 g_random_state[4] = {0}; -static bool g_has_initialized = false; - -static void EnsureRandomState() { - if (g_has_initialized) { - return; - } - - /* Retrieve process entropy with svcGetInfo. */ - u64 val = 0; - for (unsigned int i = 0; i < 4; i++) { - if (R_FAILED(svcGetInfo(&val, 0xB, 0, i))) { - /* TODO: Panic? */ - } - g_random_state[i] = val & 0xFFFFFFFF; - } - - g_has_initialized = true; -} - -u32 RandomUtils::GetNext() { - EnsureRandomState(); - u32 s, t = g_random_state[3]; - t ^= t << 11; - t ^= t >> 8; - g_random_state[3] = g_random_state[2]; g_random_state[2] = g_random_state[1]; g_random_state[1] = (s = g_random_state[0]); - t ^= s; - t ^= s >> 19; - g_random_state[0] = t; - return t; -} - -/* These are slightly biased, but I think that's totally okay. */ -u32 RandomUtils::GetRandomU32(u32 max) { - return GetNext() % max; -} - -u64 RandomUtils::GetRandomU64(u64 max) { - u64 val = GetNext(); - val |= ((u64)GetNext()) << 32; - return val % max; -} diff --git a/stratosphere/loader/source/ldr_random.hpp b/stratosphere/loader/source/ldr_random.hpp deleted file mode 100644 index 9b74cdd74..000000000 --- a/stratosphere/loader/source/ldr_random.hpp +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once -#include - -class RandomUtils { - public: - static u32 GetNext(); - static u32 GetRandomU32(u32 max); - static u64 GetRandomU64(u64 max); -}; \ No newline at end of file From ed86c44a498ba493c123d91cc95121bf4645ddc5 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 20 Apr 2019 18:15:39 -0700 Subject: [PATCH 13/64] loader: refactor to use LoaderModuleInfo --- .../loader/source/ldr_debug_monitor.cpp | 6 ++-- .../loader/source/ldr_debug_monitor.hpp | 7 ++-- .../loader/source/ldr_process_creation.cpp | 2 +- .../loader/source/ldr_registration.cpp | 32 +++++++++--------- .../loader/source/ldr_registration.hpp | 33 ++++++++----------- 5 files changed, 36 insertions(+), 44 deletions(-) diff --git a/stratosphere/loader/source/ldr_debug_monitor.cpp b/stratosphere/loader/source/ldr_debug_monitor.cpp index 2ab52d4c8..cbb10faa3 100644 --- a/stratosphere/loader/source/ldr_debug_monitor.cpp +++ b/stratosphere/loader/source/ldr_debug_monitor.cpp @@ -31,9 +31,9 @@ void DebugMonitorService::ClearLaunchQueue() { LaunchQueue::Clear(); } -Result DebugMonitorService::GetNsoInfo(Out count, OutPointerWithClientSize out, u64 pid) { +Result DebugMonitorService::GetProcessModuleInfo(Out count, OutPointerWithClientSize out, u64 pid) { /* Zero out the output memory. */ - std::fill(out.pointer, out.pointer + out.num_elements, Registration::NsoInfo{}); + std::memset(out.pointer, 0, out.num_elements * sizeof(LoaderModuleInfo)); /* Actually return the nso infos. */ - return Registration::GetNsoInfosForProcessId(out.pointer, out.num_elements, pid, count.GetPointer()); + return Registration::GetProcessModuleInfo(out.pointer, out.num_elements, pid, count.GetPointer()); } diff --git a/stratosphere/loader/source/ldr_debug_monitor.hpp b/stratosphere/loader/source/ldr_debug_monitor.hpp index d61c419c8..2e87c1521 100644 --- a/stratosphere/loader/source/ldr_debug_monitor.hpp +++ b/stratosphere/loader/source/ldr_debug_monitor.hpp @@ -18,12 +18,11 @@ #include #include -#include "ldr_registration.hpp" enum DebugMonitorServiceCmd { Dmnt_Cmd_AddTitleToLaunchQueue = 0, Dmnt_Cmd_ClearLaunchQueue = 1, - Dmnt_Cmd_GetNsoInfo = 2 + Dmnt_Cmd_GetProcessModuleInfo = 2 }; class DebugMonitorService final : public IServiceObject { @@ -31,11 +30,11 @@ class DebugMonitorService final : public IServiceObject { /* Actual commands. */ Result AddTitleToLaunchQueue(u64 tid, InPointer args, u32 args_size); void ClearLaunchQueue(); - Result GetNsoInfo(Out count, OutPointerWithClientSize out, u64 pid); + Result GetProcessModuleInfo(Out count, OutPointerWithClientSize out, u64 pid); public: DEFINE_SERVICE_DISPATCH_TABLE { MakeServiceCommandMeta(), MakeServiceCommandMeta(), - MakeServiceCommandMeta(), + MakeServiceCommandMeta(), }; }; diff --git a/stratosphere/loader/source/ldr_process_creation.cpp b/stratosphere/loader/source/ldr_process_creation.cpp index 840bd910b..cf86d97fe 100644 --- a/stratosphere/loader/source/ldr_process_creation.cpp +++ b/stratosphere/loader/source/ldr_process_creation.cpp @@ -214,7 +214,7 @@ Result ProcessCreation::CreateProcess(Handle *out_process_h, u64 index, char *nc Registration::SetProcessIdTidAndIs64BitAddressSpace(index, process_id, npdm_info.aci0->title_id, is_64_bit_addspace); for (unsigned int i = 0; i < NSO_NUM_MAX; i++) { if (NsoUtils::IsNsoPresent(i)) { - Registration::AddNsoInfo(index, nso_extents.nso_addresses[i], nso_extents.nso_sizes[i], NsoUtils::GetNsoBuildId(i)); + Registration::AddModuleInfo(index, nso_extents.nso_addresses[i], nso_extents.nso_sizes[i], NsoUtils::GetNsoBuildId(i)); } } diff --git a/stratosphere/loader/source/ldr_registration.cpp b/stratosphere/loader/source/ldr_registration.cpp index db37baae1..3d01cad43 100644 --- a/stratosphere/loader/source/ldr_registration.cpp +++ b/stratosphere/loader/source/ldr_registration.cpp @@ -34,7 +34,7 @@ Registration::Process *Registration::GetFreeProcess() { } Registration::Process *Registration::GetProcess(u64 index) { - for (unsigned int i = 0; i < REGISTRATION_LIST_MAX; i++) { + for (unsigned int i = 0; i < Registration::MaxProcesses; i++) { if (g_registration_list.processes[i].in_use && g_registration_list.processes[i].index == index) { return &g_registration_list.processes[i]; } @@ -43,7 +43,7 @@ Registration::Process *Registration::GetProcess(u64 index) { } Registration::Process *Registration::GetProcessByProcessId(u64 pid) { - for (unsigned int i = 0; i < REGISTRATION_LIST_MAX; i++) { + for (unsigned int i = 0; i < Registration::MaxProcesses; i++) { if (g_registration_list.processes[i].in_use && g_registration_list.processes[i].process_id == pid) { return &g_registration_list.processes[i]; } @@ -52,7 +52,7 @@ Registration::Process *Registration::GetProcessByProcessId(u64 pid) { } Registration::Process *Registration::GetProcessByRoService(void *service) { - for (unsigned int i = 0; i < REGISTRATION_LIST_MAX; i++) { + for (unsigned int i = 0; i < Registration::MaxProcesses; i++) { if (g_registration_list.processes[i].in_use && g_registration_list.processes[i].owner_ro_service == service) { return &g_registration_list.processes[i]; } @@ -109,17 +109,17 @@ void Registration::SetProcessIdTidAndIs64BitAddressSpace(u64 index, u64 process_ target_process->is_64_bit_addspace = is_64_bit_addspace; } -void Registration::AddNsoInfo(u64 index, u64 base_address, u64 size, const unsigned char *build_id) { +void Registration::AddModuleInfo(u64 index, u64 base_address, u64 size, const unsigned char *build_id) { Registration::Process *target_process = GetProcess(index); if (target_process == NULL) { return; } - auto nso_info_it = std::find_if_not(target_process->nso_infos.begin(), target_process->nso_infos.end(), std::mem_fn(&Registration::NsoInfoHolder::in_use)); - if (nso_info_it != target_process->nso_infos.end()) { + auto nso_info_it = std::find_if_not(target_process->module_infos.begin(), target_process->module_infos.end(), std::mem_fn(&Registration::ModuleInfoHolder::in_use)); + if (nso_info_it != target_process->module_infos.end()) { nso_info_it->info.base_address = base_address; nso_info_it->info.size = size; - std::copy(build_id, build_id + sizeof(nso_info_it->info.build_id), nso_info_it->info.build_id); + memcpy(nso_info_it->info.build_id, build_id, sizeof(nso_info_it->info.build_id)); nso_info_it->in_use = true; } } @@ -129,7 +129,7 @@ void Registration::CloseRoService(void *service, Handle process_h) { if (target_process == NULL) { return; } - for (unsigned int i = 0; i < NRR_INFO_MAX; i++) { + for (unsigned int i = 0; i < Registration::MaxNrrInfos; i++) { if (target_process->nrr_infos[i].IsActive() && target_process->nrr_infos[i].process_handle == process_h) { target_process->nrr_infos[i].Close(); } @@ -159,7 +159,7 @@ Result Registration::RemoveNrrInfo(u64 index, u64 base_address) { return ResultLoaderProcessNotRegistered; } - for (unsigned int i = 0; i < NRR_INFO_MAX; i++) { + for (unsigned int i = 0; i < Registration::MaxNrrInfos; i++) { if (target_process->nrr_infos[i].IsActive() && target_process->nrr_infos[i].base_address == base_address) { target_process->nrr_infos[i].Close(); return ResultSuccess; @@ -176,7 +176,7 @@ bool Registration::IsNroHashPresent(u64 index, u8 *nro_hash) { return false; } - for (unsigned int i = 0; i < NRR_INFO_MAX; i++) { + for (unsigned int i = 0; i < Registration::MaxNrrInfos; i++) { if (target_process->nrr_infos[i].IsActive()) { NroUtils::NrrHeader *nrr = (NroUtils::NrrHeader *)target_process->nrr_infos[i].mapped_address; /* Binary search. */ @@ -205,7 +205,7 @@ bool Registration::IsNroAlreadyLoaded(u64 index, u8 *build_id) { return true; } - for (unsigned int i = 0; i < NRO_INFO_MAX; i++) { + for (unsigned int i = 0; i < Registration::MaxNroInfos; i++) { if (target_process->nro_infos[i].in_use && std::equal(build_id, build_id + 0x20, target_process->nro_infos[i].build_id)) { return true; } @@ -241,7 +241,7 @@ Result Registration::RemoveNroInfo(u64 index, Handle process_h, u64 nro_heap_add return ResultLoaderProcessNotRegistered; } - for (unsigned int i = 0; i < NRO_INFO_MAX; i++) { + for (unsigned int i = 0; i < Registration::MaxNroInfos; i++) { if (target_process->nro_infos[i].in_use && target_process->nro_infos[i].nro_heap_address == nro_heap_address) { NroInfo *info = &target_process->nro_infos[i]; Result rc = svcUnmapProcessCodeMemory(process_h, info->base_address + info->text_size + info->ro_size + info->rw_size, info->bss_heap_address, info->bss_heap_size); @@ -258,16 +258,16 @@ Result Registration::RemoveNroInfo(u64 index, Handle process_h, u64 nro_heap_add return ResultLoaderNotLoaded; } -Result Registration::GetNsoInfosForProcessId(Registration::NsoInfo *out, u32 max_out, u64 process_id, u32 *num_written) { +Result Registration::GetProcessModuleInfo(LoaderModuleInfo *out, u32 max_out, u64 process_id, u32 *num_written) { Registration::Process *target_process = GetProcessByProcessId(process_id); if (target_process == NULL) { return ResultLoaderProcessNotRegistered; } u32 cur = 0; - for (unsigned int i = 0; i < NSO_INFO_MAX && cur < max_out; i++) { - if (target_process->nso_infos[i].in_use) { - out[cur++] = target_process->nso_infos[i].info; + for (unsigned int i = 0; i < Registration::MaxModuleInfos && cur < max_out; i++) { + if (target_process->module_infos[i].in_use) { + out[cur++] = target_process->module_infos[i].info; } } diff --git a/stratosphere/loader/source/ldr_registration.hpp b/stratosphere/loader/source/ldr_registration.hpp index a3b6ba318..27f458ee7 100644 --- a/stratosphere/loader/source/ldr_registration.hpp +++ b/stratosphere/loader/source/ldr_registration.hpp @@ -20,23 +20,16 @@ #include "ldr_map.hpp" -#define REGISTRATION_LIST_MAX (0x40) - -#define NSO_INFO_MAX (0x20) -#define NRR_INFO_MAX (0x40) -#define NRO_INFO_MAX (0x40) - class Registration { public: - struct NsoInfo { - u64 base_address; - u64 size; - unsigned char build_id[0x20]; - }; - - struct NsoInfoHolder { + static constexpr size_t MaxProcesses = 0x40; + static constexpr size_t MaxModuleInfos = 0x20; + static constexpr size_t MaxNrrInfos = 0x40; + static constexpr size_t MaxNroInfos = 0x40; + public: + struct ModuleInfoHolder { bool in_use; - NsoInfo info; + LoaderModuleInfo info; }; struct NroInfo { @@ -65,14 +58,14 @@ class Registration { u64 process_id; u64 title_id; Registration::TidSid tid_sid; - std::array nso_infos; - std::array nro_infos; - std::array nrr_infos; + std::array module_infos; + std::array nro_infos; + std::array nrr_infos; void *owner_ro_service; }; struct List { - std::array processes; + std::array processes; u64 num_processes; }; @@ -84,7 +77,7 @@ class Registration { static bool RegisterTidSid(const TidSid *tid_sid, u64 *out_index); static bool UnregisterIndex(u64 index); static void SetProcessIdTidAndIs64BitAddressSpace(u64 index, u64 process_id, u64 tid, bool is_64_bit_addspace); - static void AddNsoInfo(u64 index, u64 base_address, u64 size, const unsigned char *build_id); + static void AddModuleInfo(u64 index, u64 base_address, u64 size, const unsigned char *build_id); static void CloseRoService(void *service, Handle process_h); static Result AddNrrInfo(u64 index, MappedCodeMemory *nrr_info); static Result RemoveNrrInfo(u64 index, u64 base_address); @@ -92,7 +85,7 @@ class Registration { static bool IsNroAlreadyLoaded(u64 index, u8 *build_id); static void AddNroToProcess(u64 index, MappedCodeMemory *nro, MappedCodeMemory *bss, u32 text_size, u32 ro_size, u32 rw_size, u8 *build_id); static Result RemoveNroInfo(u64 index, Handle process_h, u64 base_address); - static Result GetNsoInfosForProcessId(NsoInfo *out, u32 max_out, u64 process_id, u32 *num_written); + static Result GetProcessModuleInfo(LoaderModuleInfo *out, u32 max_out, u64 process_id, u32 *num_written); /* Atmosphere MitM Extension. */ static void AssociatePidTidForMitM(u64 index); From 6004b7479e13450f662d7eaa80a4ead677954589 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 20 Apr 2019 17:43:56 -0700 Subject: [PATCH 14/64] ro: add ro sysmodule skeleton --- Makefile | 2 + stratosphere/Makefile | 2 +- stratosphere/ro/Makefile | 166 +++++++++++++++++++++++++++++ stratosphere/ro/ro.json | 94 ++++++++++++++++ stratosphere/ro/source/ro_main.cpp | 117 ++++++++++++++++++++ 5 files changed, 380 insertions(+), 1 deletion(-) create mode 100644 stratosphere/ro/Makefile create mode 100644 stratosphere/ro/ro.json create mode 100644 stratosphere/ro/source/ro_main.cpp diff --git a/Makefile b/Makefile index e38f766b9..384f76d63 100644 --- a/Makefile +++ b/Makefile @@ -50,6 +50,7 @@ dist: all mkdir atmosphere-$(AMSVER)/atmosphere mkdir atmosphere-$(AMSVER)/sept mkdir atmosphere-$(AMSVER)/switch + mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000037 mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000036 mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000034 mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032 @@ -69,6 +70,7 @@ dist: all cp stratosphere/creport/creport.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000036/exefs.nsp cp stratosphere/fatal/fatal.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000034/exefs.nsp cp stratosphere/eclct.stub/eclct.stub.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/exefs.nsp + cp stratosphere/ro/ro.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000037/exefs.nsp cp troposphere/reboot_to_payload/reboot_to_payload.nro atmosphere-$(AMSVER)/switch/reboot_to_payload.nro mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/flags touch atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/flags/boot2.flag diff --git a/stratosphere/Makefile b/stratosphere/Makefile index 7bbb09435..76ce0d97a 100644 --- a/stratosphere/Makefile +++ b/stratosphere/Makefile @@ -1,4 +1,4 @@ -MODULES := loader pm sm boot ams_mitm eclct.stub creport fatal dmnt +MODULES := loader pm sm boot ams_mitm eclct.stub ro creport fatal dmnt SUBFOLDERS := libstratosphere $(MODULES) diff --git a/stratosphere/ro/Makefile b/stratosphere/ro/Makefile new file mode 100644 index 000000000..2d3bf2862 --- /dev/null +++ b/stratosphere/ro/Makefile @@ -0,0 +1,166 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") +endif + +TOPDIR ?= $(CURDIR) +include $(DEVKITPRO)/libnx/switch_rules + +AMSBRANCH := $(shell git symbolic-ref --short HEAD) +AMSREV := $(AMSBRANCH)-$(shell git rev-parse --short HEAD) + +ifneq (, $(strip $(shell git status --porcelain 2>/dev/null))) + AMSREV := $(AMSREV)-dirty +endif + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +# EXEFS_SRC is the optional input directory containing data copied into exefs, if anything this normally should only contain "main.npdm". +#--------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := source +DATA := data +INCLUDES := include ../../common/include +EXEFS_SRC := exefs_src + +DEFINES := -DDISABLE_IPC -DATMOSPHERE_GIT_BRANCH=\"$(AMSBRANCH)\" -DATMOSPHERE_GIT_REV=\"$(AMSREV)\" -DINI_MAX_LINE=768 + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE + +CFLAGS := -g -Wall -O2 -ffunction-sections \ + $(ARCH) $(DEFINES) + +CFLAGS += $(INCLUDE) -D__SWITCH__ + +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17 + +ASFLAGS := -g $(ARCH) +LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) + +LIBS := -lstratosphere -lnx + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(PORTLIBS) $(LIBNX) $(CURDIR)/../libstratosphere + + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC) + +ifeq ($(strip $(CONFIG_JSON)),) + jsons := $(wildcard *.json) + ifneq (,$(findstring $(TARGET).json,$(jsons))) + export APP_JSON := $(TOPDIR)/$(TARGET).json + else + ifneq (,$(findstring config.json,$(jsons))) + export APP_JSON := $(TOPDIR)/config.json + endif + endif +else + export APP_JSON := $(TOPDIR)/$(CONFIG_JSON) +endif + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).nsp $(TARGET).npdm $(TARGET).nso $(TARGET).elf + + +#--------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +all : $(OUTPUT).nsp + +ifeq ($(strip $(APP_JSON)),) +$(OUTPUT).nsp : $(OUTPUT).nso +else +$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm +endif + +$(OUTPUT).nso : $(OUTPUT).elf + +$(OUTPUT).elf : $(OFILES) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/stratosphere/ro/ro.json b/stratosphere/ro/ro.json new file mode 100644 index 000000000..8b4efa28c --- /dev/null +++ b/stratosphere/ro/ro.json @@ -0,0 +1,94 @@ +{ + "name": "ro", + "title_id": "0x0100000000000037", + "title_id_range_min": "0x0100000000000037", + "title_id_range_max": "0x0100000000000037", + "main_thread_stack_size": "0x00005000", + "main_thread_priority": 49, + "default_cpu_id": 3, + "process_category": 0, + "is_retail": true, + "pool_partition": 2, + "is_64_bit": true, + "address_space_type": 1, + "filesystem_access": { + "permissions": "0xFFFFFFFFFFFFFFFF" + }, + "service_access": ["fatal:u", "spl:", "set:sys", "fsp-srv"], + "service_host": ["ldr:ro", "ro:dmnt", "ro:1"], + "kernel_capabilities": [{ + "type": "kernel_flags", + "value": { + "highest_thread_priority": 59, + "lowest_thread_priority": 28, + "lowest_cpu_id": 3, + "highest_cpu_id": 3 + } + }, { + "type": "syscalls", + "value": { + "svcSetHeapSize": "0x01", + "svcSetMemoryPermission": "0x02", + "svcSetMemoryAttribute": "0x03", + "svcMapMemory": "0x04", + "svcUnmapMemory": "0x05", + "svcQueryMemory": "0x06", + "svcExitProcess": "0x07", + "svcCreateThread": "0x08", + "svcStartThread": "0x09", + "svcExitThread": "0x0a", + "svcSleepThread": "0x0b", + "svcGetThreadPriority": "0x0c", + "svcSetThreadPriority": "0x0d", + "svcGetThreadCoreMask": "0x0e", + "svcSetThreadCoreMask": "0x0f", + "svcGetCurrentProcessorNumber": "0x10", + "svcSignalEvent": "0x11", + "svcClearEvent": "0x12", + "svcMapSharedMemory": "0x13", + "svcUnmapSharedMemory": "0x14", + "svcCreateTransferMemory": "0x15", + "svcCloseHandle": "0x16", + "svcResetSignal": "0x17", + "svcWaitSynchronization": "0x18", + "svcCancelSynchronization": "0x19", + "svcArbitrateLock": "0x1a", + "svcArbitrateUnlock": "0x1b", + "svcWaitProcessWideKeyAtomic": "0x1c", + "svcSignalProcessWideKey": "0x1d", + "svcGetSystemTick": "0x1e", + "svcConnectToNamedPort": "0x1f", + "svcSendSyncRequestLight": "0x20", + "svcSendSyncRequest": "0x21", + "svcSendSyncRequestWithUserBuffer": "0x22", + "svcSendAsyncRequestWithUserBuffer": "0x23", + "svcGetProcessId": "0x24", + "svcGetThreadId": "0x25", + "svcBreak": "0x26", + "svcOutputDebugString": "0x27", + "svcReturnFromException": "0x28", + "svcGetInfo": "0x29", + "svcWaitForAddress": "0x34", + "svcSignalToAddress": "0x35", + "svcCreateSession": "0x40", + "svcAcceptSession": "0x41", + "svcReplyAndReceiveLight": "0x42", + "svcReplyAndReceive": "0x43", + "svcReplyAndReceiveWithUserBuffer": "0x44", + "svcCreateEvent": "0x45", + "svcSetProcessMemoryPermission": "0x73", + "svcMapProcessMemory": "0x74", + "svcUnmapProcessMemory": "0x75", + "svcQueryProcessMemory": "0x76", + "svcMapProcessCodeMemory": "0x77", + "svcUnmapProcessCodeMemory": "0x78", + "svcCallSecureMonitor": "0x7f" + } + }, { + "type": "min_kernel_version", + "value": "0x0030" + }, { + "type": "handle_table_size", + "value": 0 + }] +} \ No newline at end of file diff --git a/stratosphere/ro/source/ro_main.cpp b/stratosphere/ro/source/ro_main.cpp new file mode 100644 index 000000000..0844a1de3 --- /dev/null +++ b/stratosphere/ro/source/ro_main.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include +#include +#include + +extern "C" { + extern u32 __start__; + + u32 __nx_applet_type = AppletType_None; + + #define INNER_HEAP_SIZE 0x30000 + size_t nx_inner_heap_size = INNER_HEAP_SIZE; + char nx_inner_heap[INNER_HEAP_SIZE]; + + void __libnx_initheap(void); + void __appInit(void); + void __appExit(void); + + /* Exception handling. */ + alignas(16) u8 __nx_exception_stack[0x1000]; + u64 __nx_exception_stack_size = sizeof(__nx_exception_stack); + void __libnx_exception_handler(ThreadExceptionDump *ctx); + u64 __stratosphere_title_id = TitleId_Ro; + void __libstratosphere_exception_handler(AtmosphereFatalErrorContext *ctx); +} + +void __libnx_exception_handler(ThreadExceptionDump *ctx) { + StratosphereCrashHandler(ctx); +} + +void __libnx_initheap(void) { + void* addr = nx_inner_heap; + size_t size = nx_inner_heap_size; + + /* Newlib */ + extern char* fake_heap_start; + extern char* fake_heap_end; + + fake_heap_start = (char*)addr; + fake_heap_end = (char*)addr + size; +} + +void __appInit(void) { + Result rc; + + SetFirmwareVersionForLibnx(); + + rc = smInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + rc = setsysInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + rc = splInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + rc = fsInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + rc = fsdevMountSdmc(); + if (R_FAILED(rc)) { + std::abort(); + } + + CheckAtmosphereVersion(CURRENT_ATMOSPHERE_VERSION); +} + +void __appExit(void) { + fsdevUnmountAll(); + fsExit(); + splExit(); + setsysExit(); + smExit(); +} + +int main(int argc, char **argv) +{ + /* Static server manager. */ + static auto g_server_manager = WaitableManager(1); + + /* TODO: Create services. */ + + /* Loop forever, servicing our services. */ + g_server_manager.Process(); + + /* Cleanup */ + return 0; +} + From 4ac8f2745bdc5439139d9575b080c0b09d9215d5 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 20 Apr 2019 18:13:32 -0700 Subject: [PATCH 15/64] ro: skeleton ro:dmnt --- stratosphere/ro/source/ro_debug_monitor.cpp | 26 ++++++++++++++++ stratosphere/ro/source/ro_debug_monitor.hpp | 33 +++++++++++++++++++++ stratosphere/ro/source/ro_main.cpp | 7 +++-- 3 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 stratosphere/ro/source/ro_debug_monitor.cpp create mode 100644 stratosphere/ro/source/ro_debug_monitor.hpp diff --git a/stratosphere/ro/source/ro_debug_monitor.cpp b/stratosphere/ro/source/ro_debug_monitor.cpp new file mode 100644 index 000000000..fb26fa66b --- /dev/null +++ b/stratosphere/ro/source/ro_debug_monitor.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include "ro_debug_monitor.hpp" + +Result DebugMonitorService::GetProcessModuleInfo(Out count, OutBuffer out_infos, u64 pid) { + /* TODO: Implement. */ + return ResultKernelConnectionClosed; +} \ No newline at end of file diff --git a/stratosphere/ro/source/ro_debug_monitor.hpp b/stratosphere/ro/source/ro_debug_monitor.hpp new file mode 100644 index 000000000..d8273e2df --- /dev/null +++ b/stratosphere/ro/source/ro_debug_monitor.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +enum DebugMonitorServiceCmd { + Dmnt_Cmd_GetProcessModuleInfo = 0 +}; + +class DebugMonitorService final : public IServiceObject { + private: + /* Actual commands. */ + Result GetProcessModuleInfo(Out count, OutBuffer out_infos, u64 pid); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MakeServiceCommandMeta(), + }; +}; diff --git a/stratosphere/ro/source/ro_main.cpp b/stratosphere/ro/source/ro_main.cpp index 0844a1de3..4c7264ff0 100644 --- a/stratosphere/ro/source/ro_main.cpp +++ b/stratosphere/ro/source/ro_main.cpp @@ -23,6 +23,8 @@ #include #include +#include "ro_debug_monitor.hpp" + extern "C" { extern u32 __start__; @@ -104,12 +106,13 @@ void __appExit(void) { int main(int argc, char **argv) { /* Static server manager. */ - static auto g_server_manager = WaitableManager(1); + static auto s_server_manager = WaitableManager(1); /* TODO: Create services. */ + s_server_manager.AddWaitable(new ServiceServer("ro:dmnt", 1)); /* Loop forever, servicing our services. */ - g_server_manager.Process(); + s_server_manager.Process(); /* Cleanup */ return 0; From 79c52e2b9144620df9bb0e986ebbab5ba79095e1 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 20 Apr 2019 18:37:01 -0700 Subject: [PATCH 16/64] ro: skeleton ldr:ro --- stratosphere/ro/source/ro_main.cpp | 14 +++++- stratosphere/ro/source/ro_service.cpp | 56 +++++++++++++++++++++++ stratosphere/ro/source/ro_service.hpp | 64 +++++++++++++++++++++++++++ 3 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 stratosphere/ro/source/ro_service.cpp create mode 100644 stratosphere/ro/source/ro_service.hpp diff --git a/stratosphere/ro/source/ro_main.cpp b/stratosphere/ro/source/ro_main.cpp index 4c7264ff0..cff2733f4 100644 --- a/stratosphere/ro/source/ro_main.cpp +++ b/stratosphere/ro/source/ro_main.cpp @@ -24,6 +24,7 @@ #include #include "ro_debug_monitor.hpp" +#include "ro_service.hpp" extern "C" { extern u32 __start__; @@ -103,13 +104,24 @@ void __appExit(void) { smExit(); } +/* Helpers to create RO objects. */ +static const auto MakeRoServiceForSelf = []() { return std::make_shared(RoServiceType_ForSelf); }; +static const auto MakeRoServiceForOthers = []() { return std::make_shared(RoServiceType_ForSelf); }; + int main(int argc, char **argv) { /* Static server manager. */ static auto s_server_manager = WaitableManager(1); - /* TODO: Create services. */ + /* Create services. */ s_server_manager.AddWaitable(new ServiceServer("ro:dmnt", 1)); + { + + s_server_manager.AddWaitable(new ServiceServer("ldr:ro", 32)); + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_700) { + s_server_manager.AddWaitable(new ServiceServer("ro:1", 2)); + } + } /* Loop forever, servicing our services. */ s_server_manager.Process(); diff --git a/stratosphere/ro/source/ro_service.cpp b/stratosphere/ro/source/ro_service.cpp new file mode 100644 index 000000000..0ecbf1551 --- /dev/null +++ b/stratosphere/ro/source/ro_service.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include "ro_service.hpp" + +RelocatableObjectsService::~RelocatableObjectsService() { + /* TODO */ +} + +Result RelocatableObjectsService::LoadNro(Out load_address, PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { + /* TODO */ + return ResultKernelConnectionClosed; +} + +Result RelocatableObjectsService::UnloadNro(PidDescriptor pid_desc, u64 nro_address) { + /* TODO */ + return ResultKernelConnectionClosed; +} + +Result RelocatableObjectsService::LoadNrr(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size) { + /* TODO */ + return ResultKernelConnectionClosed; +} + +Result RelocatableObjectsService::UnloadNrr(PidDescriptor pid_desc, u64 nrr_address) { + /* TODO */ + return ResultKernelConnectionClosed; +} + +Result RelocatableObjectsService::Initialize(PidDescriptor pid_desc, CopiedHandle process_h) { + /* TODO */ + return ResultKernelConnectionClosed; +} + +Result RelocatableObjectsService::LoadNrrEx(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size, CopiedHandle process_h) { + /* TODO */ + return ResultKernelConnectionClosed; +} \ No newline at end of file diff --git a/stratosphere/ro/source/ro_service.hpp b/stratosphere/ro/source/ro_service.hpp new file mode 100644 index 000000000..8066c86e8 --- /dev/null +++ b/stratosphere/ro/source/ro_service.hpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +#include + +enum RoServiceCmd { + Ro_Cmd_LoadNro = 0, + Ro_Cmd_UnloadNro = 1, + Ro_Cmd_LoadNrr = 2, + Ro_Cmd_UnloadNrr = 3, + Ro_Cmd_Initialize = 4, + Ro_Cmd_LoadNrrEx = 10, +}; + +enum RoServiceType : u32 { + RoServiceType_ForSelf = 0, + RoServiceType_ForOthers = 1, +}; + +class RelocatableObjectsService final : public IServiceObject { + Handle process_handle = 0; + u64 process_id = U64_MAX; + bool has_initialized = false; + RoServiceType type; + public: + explicit RelocatableObjectsService(RoServiceType t) : type(t) { + /* ... */ + } + virtual ~RelocatableObjectsService() override; + + private: + /* Actual commands. */ + Result LoadNro(Out load_address, PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size); + Result UnloadNro(PidDescriptor pid_desc, u64 nro_address); + Result LoadNrr(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size); + Result UnloadNrr(PidDescriptor pid_desc, u64 nrr_address); + Result Initialize(PidDescriptor pid_desc, CopiedHandle process_h); + Result LoadNrrEx(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size, CopiedHandle process_h); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + }; +}; From e04fcfff6bc5f08b6cf3a8f14a9ae9c46d41a2ee Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 20 Apr 2019 18:37:50 -0700 Subject: [PATCH 17/64] ro: fix typo --- stratosphere/ro/source/ro_main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stratosphere/ro/source/ro_main.cpp b/stratosphere/ro/source/ro_main.cpp index cff2733f4..4bd9b4893 100644 --- a/stratosphere/ro/source/ro_main.cpp +++ b/stratosphere/ro/source/ro_main.cpp @@ -106,7 +106,7 @@ void __appExit(void) { /* Helpers to create RO objects. */ static const auto MakeRoServiceForSelf = []() { return std::make_shared(RoServiceType_ForSelf); }; -static const auto MakeRoServiceForOthers = []() { return std::make_shared(RoServiceType_ForSelf); }; +static const auto MakeRoServiceForOthers = []() { return std::make_shared(RoServiceType_ForOthers); }; int main(int argc, char **argv) { From d69fc060f4878e551512429b5085b86a26dcb2da Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 21 Apr 2019 02:09:08 -0700 Subject: [PATCH 18/64] ro: Implement ro:dmnt --- stratosphere/ro/source/ro_debug_monitor.cpp | 11 +- stratosphere/ro/source/ro_main.cpp | 13 +-- stratosphere/ro/source/ro_registration.cpp | 95 +++++++++++++++++ stratosphere/ro/source/ro_registration.hpp | 108 ++++++++++++++++++++ stratosphere/ro/source/ro_service.cpp | 5 +- stratosphere/ro/source/ro_service.hpp | 11 +- 6 files changed, 226 insertions(+), 17 deletions(-) create mode 100644 stratosphere/ro/source/ro_registration.cpp create mode 100644 stratosphere/ro/source/ro_registration.hpp diff --git a/stratosphere/ro/source/ro_debug_monitor.cpp b/stratosphere/ro/source/ro_debug_monitor.cpp index fb26fa66b..ef6897c60 100644 --- a/stratosphere/ro/source/ro_debug_monitor.cpp +++ b/stratosphere/ro/source/ro_debug_monitor.cpp @@ -15,12 +15,15 @@ */ #include -#include -#include #include +#include + #include "ro_debug_monitor.hpp" +#include "ro_registration.hpp" Result DebugMonitorService::GetProcessModuleInfo(Out count, OutBuffer out_infos, u64 pid) { - /* TODO: Implement. */ - return ResultKernelConnectionClosed; + if (out_infos.num_elements > INT_MAX) { + return ResultRoInvalidSize; + } + return Registration::GetProcessModuleInfo(count.GetPointer(), out_infos.buffer, out_infos.num_elements, pid); } \ No newline at end of file diff --git a/stratosphere/ro/source/ro_main.cpp b/stratosphere/ro/source/ro_main.cpp index 4bd9b4893..1a3787677 100644 --- a/stratosphere/ro/source/ro_main.cpp +++ b/stratosphere/ro/source/ro_main.cpp @@ -77,7 +77,7 @@ void __appInit(void) { if (R_FAILED(rc)) { std::abort(); } - + rc = splInitialize(); if (R_FAILED(rc)) { std::abort(); @@ -114,13 +114,10 @@ int main(int argc, char **argv) static auto s_server_manager = WaitableManager(1); /* Create services. */ - s_server_manager.AddWaitable(new ServiceServer("ro:dmnt", 1)); - { - - s_server_manager.AddWaitable(new ServiceServer("ldr:ro", 32)); - if (GetRuntimeFirmwareVersion() >= FirmwareVersion_700) { - s_server_manager.AddWaitable(new ServiceServer("ro:1", 2)); - } + s_server_manager.AddWaitable(new ServiceServer("ro:dmnt", 2)); + s_server_manager.AddWaitable(new ServiceServer("ldr:ro", 32)); + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_700) { + s_server_manager.AddWaitable(new ServiceServer("ro:1", 2)); } /* Loop forever, servicing our services. */ diff --git a/stratosphere/ro/source/ro_registration.cpp b/stratosphere/ro/source/ro_registration.cpp new file mode 100644 index 000000000..fe2726e76 --- /dev/null +++ b/stratosphere/ro/source/ro_registration.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include "ro_registration.hpp" + +/* Declare process contexts as static to this function. */ +static Registration::RoProcessContext g_process_contexts[Registration::MaxSessions] = {}; + +Result Registration::RegisterProcess(RoProcessContext **out_context, Handle process_handle, u64 process_id) { + /* Check if a process context already exists. */ + for (size_t i = 0; i < Registration::MaxSessions; i++) { + if (g_process_contexts[i].process_id == process_id) { + return ResultRoInvalidSession; + } + } + + /* Find a free process context. */ + for (size_t i = 0; i < Registration::MaxSessions; i++) { + if (!g_process_contexts[i].in_use) { + g_process_contexts[i].process_id = process_id; + g_process_contexts[i].process_handle = process_handle; + g_process_contexts[i].in_use = true; + *out_context = &g_process_contexts[i]; + return ResultSuccess; + } + } + + /* Failure to find a free context is actually an abort condition. */ + /* TODO: Should this return an unofficial error code? */ + std::abort(); +} + +void Registration::UnregisterProcess(RoProcessContext *context) { + if (context->process_handle != INVALID_HANDLE) { + for (size_t i = 0; i < Registration::MaxNrrInfos; i++) { + if (context->nrr_in_use[i]) { + UnmapNrr(context->process_handle, context->nrr_infos[i].header, context->nrr_infos[i].nrr_heap_address, context->nrr_infos[i].nrr_heap_size, context->nrr_infos[i].mapped_code_address); + } + } + } +} + +Result Registration::GetProcessModuleInfo(u32 *out_count, LoaderModuleInfo *out_infos, size_t max_out_count, u64 process_id) { + size_t count = 0; + for (size_t sess = 0; sess < Registration::MaxSessions; sess++) { + if (g_process_contexts[sess].process_id == process_id) { + /* For convenience, helper. */ + const RoProcessContext *context = &g_process_contexts[sess]; + + for (size_t i = 0; i < Registration::MaxNroInfos && count < max_out_count; i++) { + if (!context->nro_in_use[i]) { + continue; + } + + /* Just copy out the info. */ + LoaderModuleInfo *out_info = &out_infos[count++]; + memcpy(out_info->build_id, &context->nro_infos[i].module_id, sizeof(context->nro_infos[i].module_id)); + out_info->base_address = context->nro_infos[i].base_address; + out_info->size = context->nro_infos[i].nro_heap_size + context->nro_infos[i].bss_heap_size; + } + + break; + } + } + + *out_count = static_cast(count); + return ResultSuccess; +} + +Result Registration::UnmapNrr(Handle process_handle, const NrrHeader *header, u64 nrr_heap_address, u64 nrr_heap_size, u64 mapped_code_address) { + Result rc = svcUnmapProcessMemory((void *)header, process_handle, mapped_code_address, nrr_heap_size); + if (R_FAILED(rc)) { + return rc; + } + + return svcUnmapProcessCodeMemory(process_handle, mapped_code_address, nrr_heap_address, nrr_heap_size); +} diff --git a/stratosphere/ro/source/ro_registration.hpp b/stratosphere/ro/source/ro_registration.hpp new file mode 100644 index 000000000..b869d7e4d --- /dev/null +++ b/stratosphere/ro/source/ro_registration.hpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +#include + +class Registration { + public: + static constexpr size_t MaxSessions = 0x8; + static constexpr size_t MaxNrrInfos = 0x40; + static constexpr size_t MaxNroInfos = 0x40; + public: + struct NrrHeader { + u32 magic; + u32 _0x4; + u32 _0x8; + u32 _0xC; + u64 title_id_mask; + u64 title_id_pattern; + u64 _0x20; + u64 _0x28; + u8 modulus[0x100]; + u8 fixed_key_signature[0x100]; + u8 nrr_signature[0x100]; + u64 title_id_min; + u32 nrr_size; + u8 nrr_type; /* 7.0.0+ */ + u8 _0x33D[3]; + u32 hash_offset; + u32 num_hashes; + u64 _0x348; + }; + static_assert(sizeof(NrrHeader) == 0x350, "NrrHeader definition!"); + + struct NroHeader { + u32 entrypoint_insn; + u32 mod_offset; + u64 padding; + u32 magic; + u32 _0x14; + u32 nro_size; + u32 _0x1C; + u32 text_offset; + u32 text_size; + u32 ro_offset; + u32 ro_size; + u32 rw_offset; + u32 rw_size; + u32 bss_size; + u32 _0x3C; + unsigned char build_id[0x20]; + u8 _0x60[0x20]; + }; + static_assert(sizeof(NroHeader) == 0x80, "NroHeader definition!"); + + struct ModuleId { + u8 build_id[0x20]; + }; + static_assert(sizeof(ModuleId) == sizeof(LoaderModuleInfo::build_id), "ModuleId definition!"); + + struct NroInfo { + u64 base_address; + u64 nro_heap_address; + u64 nro_heap_size; + u64 bss_heap_address; + u64 bss_heap_size; + u64 code_size; + u64 rw_size; + ModuleId module_id; + }; + struct NrrInfo { + NrrHeader *header; + u64 nrr_heap_address; + u64 nrr_heap_size; + u64 mapped_code_address; + }; + struct RoProcessContext { + bool nro_in_use[MaxNroInfos]; + bool nrr_in_use[MaxNrrInfos]; + NroInfo nro_infos[MaxNroInfos]; + NrrInfo nrr_infos[MaxNrrInfos]; + Handle process_handle; + u64 process_id; + bool in_use; + }; + public: + static Result RegisterProcess(RoProcessContext **out_context, Handle process_handle, u64 process_id); + static void UnregisterProcess(RoProcessContext *context); + + static Result GetProcessModuleInfo(u32 *out_count, LoaderModuleInfo *out_infos, size_t max_out_count, u64 process_id); + + static Result UnmapNrr(Handle process_handle, const NrrHeader *header, u64 nrr_heap_address, u64 nrr_heap_size, u64 mapped_code_address); +}; \ No newline at end of file diff --git a/stratosphere/ro/source/ro_service.cpp b/stratosphere/ro/source/ro_service.cpp index 0ecbf1551..8dc2bd504 100644 --- a/stratosphere/ro/source/ro_service.cpp +++ b/stratosphere/ro/source/ro_service.cpp @@ -20,9 +20,12 @@ #include #include "ro_service.hpp" +#include "ro_registration.hpp" RelocatableObjectsService::~RelocatableObjectsService() { - /* TODO */ + if (this->IsInitialized()) { + Registration::UnregisterProcess(this->context); + } } Result RelocatableObjectsService::LoadNro(Out load_address, PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { diff --git a/stratosphere/ro/source/ro_service.hpp b/stratosphere/ro/source/ro_service.hpp index 8066c86e8..2d68f84bd 100644 --- a/stratosphere/ro/source/ro_service.hpp +++ b/stratosphere/ro/source/ro_service.hpp @@ -19,6 +19,8 @@ #include +#include "ro_registration.hpp" + enum RoServiceCmd { Ro_Cmd_LoadNro = 0, Ro_Cmd_UnloadNro = 1, @@ -34,16 +36,17 @@ enum RoServiceType : u32 { }; class RelocatableObjectsService final : public IServiceObject { - Handle process_handle = 0; - u64 process_id = U64_MAX; - bool has_initialized = false; + Registration::RoProcessContext *context = nullptr; RoServiceType type; public: explicit RelocatableObjectsService(RoServiceType t) : type(t) { /* ... */ } virtual ~RelocatableObjectsService() override; - + private: + bool IsInitialized() const { + return this->context != nullptr; + } private: /* Actual commands. */ Result LoadNro(Out load_address, PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size); From cb88fdfd62f79ef4f8e5addabf8566fa2f5b70f2 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 21 Apr 2019 03:39:29 -0700 Subject: [PATCH 19/64] ro: implement UnloadNrr, half of LoadNrr/LoadNrrEx --- stratosphere/pm/source/pm_main.cpp | 2 +- stratosphere/ro/ro.json | 2 +- stratosphere/ro/source/ro_main.cpp | 27 ++++-- stratosphere/ro/source/ro_registration.cpp | 101 ++++++++++++++++++++- stratosphere/ro/source/ro_registration.hpp | 18 +++- stratosphere/ro/source/ro_service.cpp | 58 ++++++++++-- stratosphere/ro/source/ro_service.hpp | 14 ++- 7 files changed, 191 insertions(+), 31 deletions(-) diff --git a/stratosphere/pm/source/pm_main.cpp b/stratosphere/pm/source/pm_main.cpp index 5c53aa250..01ed21f68 100644 --- a/stratosphere/pm/source/pm_main.cpp +++ b/stratosphere/pm/source/pm_main.cpp @@ -180,7 +180,7 @@ int main(int argc, char **argv) server_manager->AddWaitable(new ServiceServer("pm:shell", 3)); server_manager->AddWaitable(new ServiceServer("pm:dmnt", 2)); server_manager->AddWaitable(new ServiceServer("pm:bm", 6)); - server_manager->AddWaitable(new ServiceServer("pm:info", 1)); + server_manager->AddWaitable(new ServiceServer("pm:info", 2)); /* Loop forever, servicing our services. */ server_manager->Process(); diff --git a/stratosphere/ro/ro.json b/stratosphere/ro/ro.json index 8b4efa28c..57d4d3bf3 100644 --- a/stratosphere/ro/ro.json +++ b/stratosphere/ro/ro.json @@ -14,7 +14,7 @@ "filesystem_access": { "permissions": "0xFFFFFFFFFFFFFFFF" }, - "service_access": ["fatal:u", "spl:", "set:sys", "fsp-srv"], + "service_access": ["fatal:u", "spl:", "set:sys", "fsp-srv", "pm:info"], "service_host": ["ldr:ro", "ro:dmnt", "ro:1"], "kernel_capabilities": [{ "type": "kernel_flags", diff --git a/stratosphere/ro/source/ro_main.cpp b/stratosphere/ro/source/ro_main.cpp index 1a3787677..cd5f9e807 100644 --- a/stratosphere/ro/source/ro_main.cpp +++ b/stratosphere/ro/source/ro_main.cpp @@ -25,6 +25,7 @@ #include "ro_debug_monitor.hpp" #include "ro_service.hpp" +#include "ro_registration.hpp" extern "C" { extern u32 __start__; @@ -77,16 +78,18 @@ void __appInit(void) { if (R_FAILED(rc)) { std::abort(); } - - rc = splInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } rc = fsInitialize(); if (R_FAILED(rc)) { std::abort(); } + + if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) { + rc = pminfoInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + } rc = fsdevMountSdmc(); if (R_FAILED(rc)) { @@ -99,23 +102,29 @@ void __appInit(void) { void __appExit(void) { fsdevUnmountAll(); fsExit(); - splExit(); + if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) { + pminfoExit(); + } setsysExit(); smExit(); } /* Helpers to create RO objects. */ -static const auto MakeRoServiceForSelf = []() { return std::make_shared(RoServiceType_ForSelf); }; -static const auto MakeRoServiceForOthers = []() { return std::make_shared(RoServiceType_ForOthers); }; +static const auto MakeRoServiceForSelf = []() { return std::make_shared(RoModuleType_ForSelf); }; +static const auto MakeRoServiceForOthers = []() { return std::make_shared(RoModuleType_ForOthers); }; int main(int argc, char **argv) { + /* Initialize. */ + Registration::Initialize(); + /* Static server manager. */ static auto s_server_manager = WaitableManager(1); /* Create services. */ s_server_manager.AddWaitable(new ServiceServer("ro:dmnt", 2)); - s_server_manager.AddWaitable(new ServiceServer("ldr:ro", 32)); + /* NOTE: Official code passes 32 for ldr:ro max sessions. We will pass 2, because that's the actual limit. */ + s_server_manager.AddWaitable(new ServiceServer("ldr:ro", 2)); if (GetRuntimeFirmwareVersion() >= FirmwareVersion_700) { s_server_manager.AddWaitable(new ServiceServer("ro:1", 2)); } diff --git a/stratosphere/ro/source/ro_registration.cpp b/stratosphere/ro/source/ro_registration.cpp index fe2726e76..03a8c7941 100644 --- a/stratosphere/ro/source/ro_registration.cpp +++ b/stratosphere/ro/source/ro_registration.cpp @@ -21,9 +21,30 @@ #include "ro_registration.hpp" -/* Declare process contexts as static to this function. */ +/* Declare process contexts as global array. */ static Registration::RoProcessContext g_process_contexts[Registration::MaxSessions] = {}; +static bool g_is_development_hardware, g_is_development_function_enabled; + +void Registration::Initialize() { + if (R_FAILED(splInitialize())) { + std::abort(); + } + ON_SCOPE_EXIT { splExit(); }; + + if (R_FAILED(splIsDevelopment(&g_is_development_hardware))) { + std::abort(); + } + + { + u64 out_val = 0; + if (R_FAILED(splGetConfig(SplConfigItem_IsDebugMode, &out_val))) { + std::abort(); + } + g_is_development_function_enabled = out_val != 0; + } +} + Result Registration::RegisterProcess(RoProcessContext **out_context, Handle process_handle, u64 process_id) { /* Check if a process context already exists. */ for (size_t i = 0; i < Registration::MaxSessions; i++) { @@ -56,6 +77,7 @@ void Registration::UnregisterProcess(RoProcessContext *context) { } } } + std::memset(context, 0, sizeof(*context)); } Result Registration::GetProcessModuleInfo(u32 *out_count, LoaderModuleInfo *out_infos, size_t max_out_count, u64 process_id) { @@ -85,6 +107,83 @@ Result Registration::GetProcessModuleInfo(u32 *out_count, LoaderModuleInfo *out_ return ResultSuccess; } +Result Registration::LoadNrr(RoProcessContext *context, u64 title_id, u64 nrr_address, u64 nrr_size, RoModuleType expected_type, bool enforce_type) { + /* Validate address/size. */ + if (nrr_address & 0xFFF) { + return ResultRoInvalidAddress; + } + if (nrr_size == 0 || (nrr_size & 0xFFF) || !(nrr_address < nrr_address + nrr_size)) { + return ResultRoInvalidSize; + } + + /* Check we have space for a new NRR. */ + size_t slot = 0; + for (slot = 0; slot < Registration::MaxNrrInfos; slot++) { + if (!context->nrr_in_use[slot]) { + break; + } + } + if (slot == Registration::MaxNrrInfos) { + return ResultRoTooManyNrr; + } + + NrrInfo *nrr_info = &context->nrr_infos[slot]; + + /* Map. */ + NrrHeader *header = nullptr; + u64 mapped_code_address = 0; + Result rc = MapAndValidateNrr(&header, &mapped_code_address, context->process_handle, title_id, nrr_address, nrr_size); + if (R_FAILED(rc)) { + return rc; + } + + /* Set NRR info. */ + nrr_info->header = header; + nrr_info->nrr_heap_address = nrr_address; + nrr_info->nrr_heap_size = nrr_size; + nrr_info->mapped_code_address = mapped_code_address; + context->nrr_in_use[slot] = true; + + /* TODO. */ + return ResultSuccess; +} + +Result Registration::UnloadNrr(RoProcessContext *context, u64 nrr_address) { + /* Validate address. */ + if (nrr_address & 0xFFF) { + return ResultRoInvalidAddress; + } + + /* Check the NRR is loaded. */ + size_t slot = 0; + for (slot = 0; slot < Registration::MaxNrrInfos; slot++) { + if (!context->nrr_in_use[slot]) { + continue; + } + + if (context->nrr_infos[slot].nrr_heap_address == nrr_address) { + break; + } + } + if (slot == Registration::MaxNrrInfos) { + return ResultRoNotRegistered; + } + + /* Unmap. */ + const NrrInfo nrr_info = context->nrr_infos[slot]; + { + /* Nintendo does this unconditionally, whether or not the actual unmap succeeds. */ + context->nrr_in_use[slot] = false; + std::memset(&context->nrr_infos[slot], 0, sizeof(context->nrr_infos[slot])); + } + return UnmapNrr(context->process_handle, nrr_info.header, nrr_info.nrr_heap_address, nrr_info.nrr_heap_size, nrr_info.mapped_code_address); +} + +Result Registration::MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, Handle process_handle, u64 title_id, u64 nrr_heap_address, u64 nrr_heap_size) { + /* TODO */ + return ResultKernelConnectionClosed; +} + Result Registration::UnmapNrr(Handle process_handle, const NrrHeader *header, u64 nrr_heap_address, u64 nrr_heap_size, u64 mapped_code_address) { Result rc = svcUnmapProcessMemory((void *)header, process_handle, mapped_code_address, nrr_heap_size); if (R_FAILED(rc)) { diff --git a/stratosphere/ro/source/ro_registration.hpp b/stratosphere/ro/source/ro_registration.hpp index b869d7e4d..bc1057c33 100644 --- a/stratosphere/ro/source/ro_registration.hpp +++ b/stratosphere/ro/source/ro_registration.hpp @@ -19,9 +19,15 @@ #include +enum RoModuleType : u32 { + RoModuleType_ForSelf = 0, + RoModuleType_ForOthers = 1, +}; + class Registration { public: - static constexpr size_t MaxSessions = 0x8; + /* NOTE: 2 ldr:ro, 2 ro:1. Nintendo only actually supports 2 total, but we'll be a little more generous. */ + static constexpr size_t MaxSessions = 0x4; static constexpr size_t MaxNrrInfos = 0x40; static constexpr size_t MaxNroInfos = 0x40; public: @@ -98,11 +104,17 @@ class Registration { u64 process_id; bool in_use; }; + private: + static Result MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, Handle process_handle, u64 title_id, u64 nrr_heap_address, u64 nrr_heap_size); + static Result UnmapNrr(Handle process_handle, const NrrHeader *header, u64 nrr_heap_address, u64 nrr_heap_size, u64 mapped_code_address); public: + static void Initialize(); + static Result RegisterProcess(RoProcessContext **out_context, Handle process_handle, u64 process_id); static void UnregisterProcess(RoProcessContext *context); + + static Result LoadNrr(RoProcessContext *context, u64 title_id, u64 nrr_address, u64 nrr_size, RoModuleType expected_type, bool enforce_type); + static Result UnloadNrr(RoProcessContext *context, u64 nrr_address); static Result GetProcessModuleInfo(u32 *out_count, LoaderModuleInfo *out_infos, size_t max_out_count, u64 process_id); - - static Result UnmapNrr(Handle process_handle, const NrrHeader *header, u64 nrr_heap_address, u64 nrr_heap_size, u64 mapped_code_address); }; \ No newline at end of file diff --git a/stratosphere/ro/source/ro_service.cpp b/stratosphere/ro/source/ro_service.cpp index 8dc2bd504..d384fe282 100644 --- a/stratosphere/ro/source/ro_service.cpp +++ b/stratosphere/ro/source/ro_service.cpp @@ -28,6 +28,34 @@ RelocatableObjectsService::~RelocatableObjectsService() { } } +bool RelocatableObjectsService::IsProcessIdValid(u64 process_id) { + if (!this->IsInitialized()) { + return false; + } + + return this->context->process_id == process_id; +} + +u64 RelocatableObjectsService::GetTitleId(Handle process_handle) { + u64 title_id = 0; + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_300) { + /* 3.0.0+: Use svcGetInfo. */ + if (R_FAILED(svcGetInfo(&title_id, 18, process_handle, 0))) { + std::abort(); + } + } else { + /* 1.0.0-2.3.0: We're not inside loader, so ask pm. */ + u64 process_id = 0; + if (R_FAILED(svcGetProcessId(&process_id, process_handle))) { + std::abort(); + } + if (R_FAILED(pminfoGetTitleId(&title_id, process_id))) { + std::abort(); + } + } + return title_id; +} + Result RelocatableObjectsService::LoadNro(Out load_address, PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { /* TODO */ return ResultKernelConnectionClosed; @@ -39,21 +67,35 @@ Result RelocatableObjectsService::UnloadNro(PidDescriptor pid_desc, u64 nro_addr } Result RelocatableObjectsService::LoadNrr(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size) { - /* TODO */ - return ResultKernelConnectionClosed; + if (!this->IsProcessIdValid(pid_desc.pid)) { + return ResultRoInvalidProcess; + } + + return Registration::LoadNrr(this->context, GetTitleId(this->context->process_handle), nrr_address, nrr_size, RoModuleType_ForSelf, true); } Result RelocatableObjectsService::UnloadNrr(PidDescriptor pid_desc, u64 nrr_address) { - /* TODO */ - return ResultKernelConnectionClosed; + if (!this->IsProcessIdValid(pid_desc.pid)) { + return ResultRoInvalidProcess; + } + + return Registration::UnloadNrr(this->context, nrr_address); } Result RelocatableObjectsService::Initialize(PidDescriptor pid_desc, CopiedHandle process_h) { - /* TODO */ - return ResultKernelConnectionClosed; + /* Validate the input pid/process handle. */ + u64 handle_pid = 0; + if (R_FAILED(svcGetProcessId(&handle_pid, process_h.handle)) || handle_pid != pid_desc.pid) { + return ResultRoInvalidProcess; + } + + return Registration::RegisterProcess(&this->context, process_h.handle, pid_desc.pid); } Result RelocatableObjectsService::LoadNrrEx(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size, CopiedHandle process_h) { - /* TODO */ - return ResultKernelConnectionClosed; + if (!this->IsProcessIdValid(pid_desc.pid)) { + return ResultRoInvalidProcess; + } + + return Registration::LoadNrr(this->context, GetTitleId(process_h.handle), nrr_address, nrr_size, this->type, this->type == RoModuleType_ForOthers); } \ No newline at end of file diff --git a/stratosphere/ro/source/ro_service.hpp b/stratosphere/ro/source/ro_service.hpp index 2d68f84bd..6354d0805 100644 --- a/stratosphere/ro/source/ro_service.hpp +++ b/stratosphere/ro/source/ro_service.hpp @@ -30,16 +30,12 @@ enum RoServiceCmd { Ro_Cmd_LoadNrrEx = 10, }; -enum RoServiceType : u32 { - RoServiceType_ForSelf = 0, - RoServiceType_ForOthers = 1, -}; - class RelocatableObjectsService final : public IServiceObject { - Registration::RoProcessContext *context = nullptr; - RoServiceType type; + private: + Registration::RoProcessContext *context = nullptr; + RoModuleType type; public: - explicit RelocatableObjectsService(RoServiceType t) : type(t) { + explicit RelocatableObjectsService(RoModuleType t) : type(t) { /* ... */ } virtual ~RelocatableObjectsService() override; @@ -47,6 +43,8 @@ class RelocatableObjectsService final : public IServiceObject { bool IsInitialized() const { return this->context != nullptr; } + bool IsProcessIdValid(u64 process_id); + static u64 GetTitleId(Handle process_handle); private: /* Actual commands. */ Result LoadNro(Out load_address, PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size); From 4ba6d8b24cb24e866dddd64e247f5042d6530955 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 21 Apr 2019 05:28:57 -0700 Subject: [PATCH 20/64] ro: implement rest of LoadNrr/LoadNrrEx --- stratosphere/loader/source/ldr_map.cpp | 4 +- stratosphere/ro/source/ro_map.cpp | 257 +++++++++++++++++++++ stratosphere/ro/source/ro_map.hpp | 136 +++++++++++ stratosphere/ro/source/ro_nrr.cpp | 81 +++++++ stratosphere/ro/source/ro_nrr.hpp | 56 +++++ stratosphere/ro/source/ro_registration.cpp | 58 ++++- stratosphere/ro/source/ro_registration.hpp | 29 +-- 7 files changed, 589 insertions(+), 32 deletions(-) create mode 100644 stratosphere/ro/source/ro_map.cpp create mode 100644 stratosphere/ro/source/ro_map.hpp create mode 100644 stratosphere/ro/source/ro_nrr.cpp create mode 100644 stratosphere/ro/source/ro_nrr.hpp diff --git a/stratosphere/loader/source/ldr_map.cpp b/stratosphere/loader/source/ldr_map.cpp index c05157bca..3751fd592 100644 --- a/stratosphere/loader/source/ldr_map.cpp +++ b/stratosphere/loader/source/ldr_map.cpp @@ -70,7 +70,7 @@ Result MapUtils::LocateSpaceForMapModern(u64 *out, u64 out_size) { cur_base = address_space.map_end; } else { if (R_FAILED(svcQueryMemory(&mem_info, &page_info, cur_base))) { - /* TODO: panic. */ + std::abort(); } if (mem_info.type == 0 && mem_info.addr - cur_base + mem_info.size >= out_size) { *out = cur_base; @@ -105,7 +105,7 @@ Result MapUtils::LocateSpaceForMapDeprecated(u64 *out, u64 out_size) { rc = ResultKernelOutOfMemory; while (true) { if (mem_info.type == 0x10) { - return rc; + return rc; } if (mem_info.type == 0 && mem_info.addr - cur_base + mem_info.size >= out_size) { *out = cur_base; diff --git a/stratosphere/ro/source/ro_map.cpp b/stratosphere/ro/source/ro_map.cpp new file mode 100644 index 000000000..69046f9ae --- /dev/null +++ b/stratosphere/ro/source/ro_map.cpp @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "ro_map.hpp" + +bool MapUtils::CanAddGuardRegions(Handle process_handle, u64 address, u64 size) { + MemoryInfo mem_info; + u32 page_info; + + /* Nintendo doesn't validate SVC return values at all. */ + /* TODO: Should we allow these to fail? */ + if (R_FAILED(svcQueryProcessMemory(&mem_info, &page_info, process_handle, address - 1))) { + std::abort(); + } + if (mem_info.type == MemType_Unmapped && address - GuardRegionSize >= mem_info.addr) { + if (R_FAILED(svcQueryProcessMemory(&mem_info, &page_info, process_handle, address + size))) { + std::abort(); + } + return mem_info.type == MemType_Unmapped && address + size + GuardRegionSize <= mem_info.addr + mem_info.size; + } + + return false; +} + +Result MapUtils::LocateSpaceForMap(u64 *out, u64 out_size) { + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_200) { + return LocateSpaceForMapModern(out, out_size); + } else { + return LocateSpaceForMapDeprecated(out, out_size); + } +} + + +Result MapUtils::MapCodeMemoryForProcess(MappedCodeMemory &out_mcm, Handle process_handle, bool is_64_bit, u64 base_address, u64 size) { + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_200) { + return MapCodeMemoryForProcessModern(out_mcm, process_handle, base_address, size); + } else { + return MapCodeMemoryForProcessDeprecated(out_mcm, process_handle, is_64_bit, base_address, size); + } +} + +Result MapUtils::LocateSpaceForMapModern(u64 *out, u64 out_size) { + MemoryInfo mem_info = {}; + AddressSpaceInfo address_space = {}; + u32 page_info = 0; + u64 cur_base = 0, cur_end = 0; + Result rc; + + if (R_FAILED((rc = GetAddressSpaceInfo(&address_space, CUR_PROCESS_HANDLE)))) { + return rc; + } + + cur_base = address_space.addspace_base; + + rc = ResultKernelOutOfMemory; + cur_end = cur_base + out_size; + if (cur_end <= cur_base) { + return rc; + } + + while (true) { + if (address_space.heap_size && (address_space.heap_base <= cur_end - 1 && cur_base <= address_space.heap_end - 1)) { + /* If we overlap the heap region, go to the end of the heap region. */ + if (cur_base == address_space.heap_end) { + return rc; + } + cur_base = address_space.heap_end; + } else if (address_space.map_size && (address_space.map_base <= cur_end - 1 && cur_base <= address_space.map_end - 1)) { + /* If we overlap the map region, go to the end of the map region. */ + if (cur_base == address_space.map_end) { + return rc; + } + cur_base = address_space.map_end; + } else { + if (R_FAILED(svcQueryMemory(&mem_info, &page_info, cur_base))) { + std::abort(); + } + if (mem_info.type == 0 && mem_info.addr - cur_base + mem_info.size >= out_size) { + *out = cur_base; + return ResultSuccess; + } + if (mem_info.addr + mem_info.size <= cur_base) { + return rc; + } + cur_base = mem_info.addr + mem_info.size; + if (cur_base >= address_space.addspace_end) { + return rc; + } + } + cur_end = cur_base + out_size; + if (cur_base + out_size <= cur_base) { + return rc; + } + } +} + + +Result MapUtils::LocateSpaceForMapDeprecated(u64 *out, u64 out_size) { + MemoryInfo mem_info = {}; + u32 page_info = 0; + Result rc; + + u64 cur_base = 0x8000000ULL; + if (R_FAILED((rc = svcQueryMemory(&mem_info, &page_info, cur_base)))) { + return rc; + } + + rc = ResultKernelOutOfMemory; + while (true) { + if (mem_info.type == 0x10) { + return rc; + } + if (mem_info.type == 0 && mem_info.addr - cur_base + mem_info.size >= out_size) { + *out = cur_base; + return ResultSuccess; + } + u64 mem_end = mem_info.addr + mem_info.size; + if (mem_end < cur_base) { + return rc; + } + if (mem_end >> 31) { + break; + } + cur_base = mem_end; + if (R_FAILED((rc = svcQueryMemory(&mem_info, &page_info, cur_base)))) { + return rc; + } + } + return rc; +} + +Result MapUtils::MapCodeMemoryForProcessModern(MappedCodeMemory &out_mcm, Handle process_handle, u64 base_address, u64 size) { + AddressSpaceInfo address_space = {}; + Result rc; + + if (R_FAILED((rc = GetAddressSpaceInfo(&address_space, process_handle)))) { + return rc; + } + + if (size > address_space.addspace_size) { + return ResultRoInsufficientAddressSpace; + } + + u64 try_address; + for (unsigned int i = 0; i < LocateRetryCount; i++) { + while (true) { + try_address = address_space.addspace_base + (StratosphereRandomUtils::GetRandomU64((u64)(address_space.addspace_size - size) >> 12) << 12); + if (address_space.heap_size && (address_space.heap_base <= try_address + size - 1 && try_address <= address_space.heap_end - 1)) { + continue; + } + if (address_space.map_size && (address_space.map_base <= try_address + size - 1 && try_address <= address_space.map_end - 1)) { + continue; + } + break; + } + MappedCodeMemory tmp_mcm(process_handle, try_address, base_address, size); + rc = tmp_mcm.GetResult(); + if (rc == ResultKernelInvalidMemoryState) { + continue; + } + if (R_FAILED(rc)) { + return rc; + } + + if (!CanAddGuardRegions(process_handle, try_address, size)) { + continue; + } + + /* We're done searching. */ + out_mcm = std::move(tmp_mcm); + return ResultSuccess; + } + + return ResultRoInsufficientAddressSpace; +} + +Result MapUtils::MapCodeMemoryForProcessDeprecated(MappedCodeMemory &out_mcm, Handle process_handle, bool is_64_bit, u64 base_address, u64 size) { + Result rc; + u64 addspace_base, addspace_size; + if (is_64_bit) { + addspace_base = 0x8000000ULL; + addspace_size = 0x78000000ULL; + } else { + addspace_base = 0x200000ULL; + addspace_size = 0x3FE0000ULL; + } + + if (size > addspace_size) { + return ResultRoInsufficientAddressSpace; + } + + u64 try_address; + for (unsigned int i = 0; i < LocateRetryCount; i++) { + try_address = addspace_base + (StratosphereRandomUtils::GetRandomU64((u64)(addspace_size - size) >> 12) << 12); + + MappedCodeMemory tmp_mcm(process_handle, try_address, base_address, size); + rc = tmp_mcm.GetResult(); + if (rc == ResultKernelInvalidMemoryState) { + continue; + } + if (R_FAILED(rc)) { + return rc; + } + + if (!CanAddGuardRegions(process_handle, try_address, size)) { + continue; + } + + /* We're done searching. */ + out_mcm = std::move(tmp_mcm); + return ResultSuccess; + } + + return ResultRoInsufficientAddressSpace; +} + +Result MapUtils::GetAddressSpaceInfo(AddressSpaceInfo *out, Handle process_h) { + Result rc; + if (R_FAILED((rc = svcGetInfo(&out->heap_base, 4, process_h, 0)))) { + return rc; + } + if (R_FAILED((rc = svcGetInfo(&out->heap_size, 5, process_h, 0)))) { + return rc; + } + if (R_FAILED((rc = svcGetInfo(&out->map_base, 2, process_h, 0)))) { + return rc; + } + if (R_FAILED((rc = svcGetInfo(&out->map_size, 3, process_h, 0)))) { + return rc; + } + if (R_FAILED((rc = svcGetInfo(&out->addspace_base, 12, process_h, 0)))) { + return rc; + } + if (R_FAILED((rc = svcGetInfo(&out->addspace_size, 13, process_h, 0)))) { + return rc; + } + out->heap_end = out->heap_base + out->heap_size; + out->map_end = out->map_base + out->map_size; + out->addspace_end = out->addspace_base + out->addspace_size; + return ResultSuccess; +} \ No newline at end of file diff --git a/stratosphere/ro/source/ro_map.hpp b/stratosphere/ro/source/ro_map.hpp new file mode 100644 index 000000000..bfffee190 --- /dev/null +++ b/stratosphere/ro/source/ro_map.hpp @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +class MappedCodeMemory { + private: + Handle process_handle = INVALID_HANDLE; + Result result = ResultRoInternalError; + u64 dst_address = 0; + u64 src_address = 0; + u64 size = 0; + public: + MappedCodeMemory() : process_handle(INVALID_HANDLE), result(ResultRoInternalError), dst_address(0), src_address(0), size(0) { + /* ... */ + } + + MappedCodeMemory(Handle p_h, u64 dst, u64 src, u64 sz) : process_handle(p_h), dst_address(dst), src_address(src), size(sz) { + this->result = svcMapProcessCodeMemory(this->process_handle, this->dst_address, this->src_address, this->size); + } + + ~MappedCodeMemory() { + if (this->process_handle != INVALID_HANDLE && this->size > 0 && R_SUCCEEDED(this->result)) { + if (R_FAILED((this->result = svcUnmapProcessCodeMemory(this->process_handle, this->dst_address, this->src_address, this->size)))) { + std::abort(); + } + } + } + + u64 GetDstAddress() const { + return this->dst_address; + } + + Result GetResult() const { + return this->result; + } + + bool IsSuccess() const { + return R_SUCCEEDED(this->result); + } + + void Invalidate() { + this->process_handle = INVALID_HANDLE; + } + + MappedCodeMemory &operator=(MappedCodeMemory &&o) { + this->process_handle = o.process_handle; + this->result = o.result; + this->dst_address = o.dst_address; + this->src_address = o.src_address; + this->size = o.size; + o.Invalidate(); + return *this; + } +}; + +class AutoCloseMap { + private: + Handle process_handle; + Result result; + void *mapped_address; + u64 base_address; + u64 size; + public: + AutoCloseMap(void *mp, Handle p_h, u64 ba, u64 sz) : process_handle(p_h), mapped_address(mp), base_address(ba), size(sz) { + this->result = svcMapProcessMemory(this->mapped_address, this->process_handle, this->base_address, this->size); + } + AutoCloseMap(u64 mp, Handle p_h, u64 ba, u64 sz) : process_handle(p_h), mapped_address(reinterpret_cast(mp)), base_address(ba), size(sz) { + this->result = svcMapProcessMemory(this->mapped_address, this->process_handle, this->base_address, this->size); + } + + ~AutoCloseMap() { + if (this->process_handle != INVALID_HANDLE && R_SUCCEEDED(this->result)) { + if (R_FAILED((this->result = svcUnmapProcessMemory(this->mapped_address, this->process_handle, this->base_address, this->size)))) { + std::abort(); + } + } + } + + Result GetResult() const { + return this->result; + } + + bool IsSuccess() const { + return R_SUCCEEDED(this->result); + } + + void Invalidate() { + this->process_handle = INVALID_HANDLE; + } +}; + + +class MapUtils { + public: + static constexpr size_t GuardRegionSize = 0x4000; + static constexpr size_t LocateRetryCount = 0x200; + public: + struct AddressSpaceInfo { + u64 heap_base; + u64 heap_size; + u64 heap_end; + u64 map_base; + u64 map_size; + u64 map_end; + u64 addspace_base; + u64 addspace_size; + u64 addspace_end; + }; + private: + static Result GetAddressSpaceInfo(AddressSpaceInfo *out, Handle process_h); + static Result LocateSpaceForMapDeprecated(u64 *out, u64 out_size); + static Result LocateSpaceForMapModern(u64 *out, u64 out_size); + + static Result MapCodeMemoryForProcessDeprecated(MappedCodeMemory &out_mcm, Handle process_handle, bool is_64_bit, u64 base_address, u64 size); + static Result MapCodeMemoryForProcessModern(MappedCodeMemory &out_mcm, Handle process_handle, u64 base_address, u64 size); + public: + static Result LocateSpaceForMap(u64 *out, u64 out_size); + static Result MapCodeMemoryForProcess(MappedCodeMemory &out_mcm, Handle process_handle, bool is_64_bit, u64 base_address, u64 size); + static bool CanAddGuardRegions(Handle process_handle, u64 address, u64 size); +}; \ No newline at end of file diff --git a/stratosphere/ro/source/ro_nrr.cpp b/stratosphere/ro/source/ro_nrr.cpp new file mode 100644 index 000000000..ee752dc9c --- /dev/null +++ b/stratosphere/ro/source/ro_nrr.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include "ro_nrr.hpp" +#include "ro_registration.hpp" + +Result NrrUtils::ValidateNrrSignature(const NrrHeader *header) { + /* TODO: Implement RSA-2048 PSS..... */ + + /* TODO: Check PSS fixed-key signature. */ + if (false) { + return ResultRoNotAuthorized; + } + + /* Check TitleID pattern is valid. */ + if ((header->title_id & header->title_id_mask) != header->title_id_pattern) { + return ResultRoNotAuthorized; + } + + /* TODO: Check PSS signature over hashes. */ + if (false) { + return ResultRoNotAuthorized; + } + + return ResultSuccess; +} + +Result NrrUtils::ValidateNrr(const NrrHeader *header, u64 size, u64 title_id, RoModuleType expected_type, bool enforce_type) { + if (header->magic != MagicNrr0) { + return ResultRoInvalidNrr; + } + if (header->nrr_size != size) { + return ResultRoInvalidSize; + } + + bool ease_nro_restriction = Registration::ShouldEaseNroRestriction(); + + /* Check signature. */ + Result rc = ValidateNrrSignature(header); + if (R_FAILED(rc)) { + if (!ease_nro_restriction) { + return rc; + } + } + + /* Check title id. */ + if (title_id != header->title_id) { + if (!ease_nro_restriction) { + return ResultRoInvalidNrr; + } + } + + /* Check type. */ + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_700) { + if (!enforce_type || expected_type != static_cast(header->nrr_type)) { + if (!ease_nro_restriction) { + return ResultRoInvalidNrrType; + } + } + } + + return ResultSuccess; +} \ No newline at end of file diff --git a/stratosphere/ro/source/ro_nrr.hpp b/stratosphere/ro/source/ro_nrr.hpp new file mode 100644 index 000000000..08e2204d9 --- /dev/null +++ b/stratosphere/ro/source/ro_nrr.hpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +#include + +enum RoModuleType : u32 { + RoModuleType_ForSelf = 0, + RoModuleType_ForOthers = 1, +}; + +struct NrrHeader { + u32 magic; + u32 _0x4; + u32 _0x8; + u32 _0xC; + u64 title_id_mask; + u64 title_id_pattern; + u64 _0x20; + u64 _0x28; + u8 modulus[0x100]; + u8 fixed_key_signature[0x100]; + u8 nrr_signature[0x100]; + u64 title_id; + u32 nrr_size; + u8 nrr_type; /* 7.0.0+ */ + u8 _0x33D[3]; + u32 hash_offset; + u32 num_hashes; + u64 _0x348; +}; +static_assert(sizeof(NrrHeader) == 0x350, "NrrHeader definition!"); + +class NrrUtils { + public: + static constexpr u32 MagicNrr0 = 0x3052524E; + private: + static Result ValidateNrrSignature(const NrrHeader *header); + public: + static Result ValidateNrr(const NrrHeader *header, u64 size, u64 title_id, RoModuleType expected_type, bool enforce_type); +}; \ No newline at end of file diff --git a/stratosphere/ro/source/ro_registration.cpp b/stratosphere/ro/source/ro_registration.cpp index 03a8c7941..3697c675a 100644 --- a/stratosphere/ro/source/ro_registration.cpp +++ b/stratosphere/ro/source/ro_registration.cpp @@ -20,6 +20,8 @@ #include #include "ro_registration.hpp" +#include "ro_map.hpp" +#include "ro_nrr.hpp" /* Declare process contexts as global array. */ static Registration::RoProcessContext g_process_contexts[Registration::MaxSessions] = {}; @@ -45,6 +47,18 @@ void Registration::Initialize() { } } +bool Registration::ShouldEaseNroRestriction() { + bool should_ease = false; + + if (R_FAILED(setsysGetSettingsItemValue("ro", "ease_nro_restriction", &should_ease, sizeof(should_ease)))) { + return false; + } + + /* Nintendo only allows easing restriction on dev, we will allow on production, as well. */ + /* should_ease &= g_is_development_function_enabled; */ + return should_ease; +} + Result Registration::RegisterProcess(RoProcessContext **out_context, Handle process_handle, u64 process_id) { /* Check if a process context already exists. */ for (size_t i = 0; i < Registration::MaxSessions; i++) { @@ -132,7 +146,7 @@ Result Registration::LoadNrr(RoProcessContext *context, u64 title_id, u64 nrr_ad /* Map. */ NrrHeader *header = nullptr; u64 mapped_code_address = 0; - Result rc = MapAndValidateNrr(&header, &mapped_code_address, context->process_handle, title_id, nrr_address, nrr_size); + Result rc = MapAndValidateNrr(&header, &mapped_code_address, context->process_handle, title_id, nrr_address, nrr_size, expected_type, enforce_type); if (R_FAILED(rc)) { return rc; } @@ -179,9 +193,45 @@ Result Registration::UnloadNrr(RoProcessContext *context, u64 nrr_address) { return UnmapNrr(context->process_handle, nrr_info.header, nrr_info.nrr_heap_address, nrr_info.nrr_heap_size, nrr_info.mapped_code_address); } -Result Registration::MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, Handle process_handle, u64 title_id, u64 nrr_heap_address, u64 nrr_heap_size) { - /* TODO */ - return ResultKernelConnectionClosed; +Result Registration::MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, Handle process_handle, u64 title_id, u64 nrr_heap_address, u64 nrr_heap_size, RoModuleType expected_type, bool enforce_type) { + Result rc; + MappedCodeMemory nrr_mcm; + + /* First, map the NRR. */ + if (R_FAILED((rc = MapUtils::MapCodeMemoryForProcess(nrr_mcm, process_handle, true, nrr_heap_address, nrr_heap_size)))) { + if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) { + /* Try mapping as 32-bit, since we might have guessed wrong on < 3.0.0. */ + rc = MapUtils::MapCodeMemoryForProcess(nrr_mcm, process_handle, false, nrr_heap_address, nrr_heap_size); + } + if (R_FAILED(rc)) { + return rc; + } + } + + const u64 code_address = nrr_mcm.GetDstAddress(); + u64 map_address; + if (R_FAILED(MapUtils::LocateSpaceForMap(&map_address, nrr_heap_size))) { + return ResultRoInsufficientAddressSpace; + } + + /* Nintendo...does not check the return value of this map. We will check, instead of aborting if it fails. */ + AutoCloseMap nrr_map(map_address, process_handle, code_address, nrr_heap_size); + if (!nrr_map.IsSuccess()) { + return nrr_map.GetResult(); + } + + NrrHeader *nrr_header = reinterpret_cast(map_address); + if (R_FAILED((rc = NrrUtils::ValidateNrr(nrr_header, nrr_heap_size, title_id, expected_type, enforce_type)))) { + return rc; + } + + /* Invalidation here actually prevents them from unmapping at scope exit. */ + nrr_map.Invalidate(); + nrr_mcm.Invalidate(); + + *out_header = nrr_header; + *out_mapped_code_address = code_address; + return ResultSuccess; } Result Registration::UnmapNrr(Handle process_handle, const NrrHeader *header, u64 nrr_heap_address, u64 nrr_heap_size, u64 mapped_code_address) { diff --git a/stratosphere/ro/source/ro_registration.hpp b/stratosphere/ro/source/ro_registration.hpp index bc1057c33..30f0910a3 100644 --- a/stratosphere/ro/source/ro_registration.hpp +++ b/stratosphere/ro/source/ro_registration.hpp @@ -19,10 +19,7 @@ #include -enum RoModuleType : u32 { - RoModuleType_ForSelf = 0, - RoModuleType_ForOthers = 1, -}; +#include "ro_nrr.hpp" class Registration { public: @@ -31,27 +28,6 @@ class Registration { static constexpr size_t MaxNrrInfos = 0x40; static constexpr size_t MaxNroInfos = 0x40; public: - struct NrrHeader { - u32 magic; - u32 _0x4; - u32 _0x8; - u32 _0xC; - u64 title_id_mask; - u64 title_id_pattern; - u64 _0x20; - u64 _0x28; - u8 modulus[0x100]; - u8 fixed_key_signature[0x100]; - u8 nrr_signature[0x100]; - u64 title_id_min; - u32 nrr_size; - u8 nrr_type; /* 7.0.0+ */ - u8 _0x33D[3]; - u32 hash_offset; - u32 num_hashes; - u64 _0x348; - }; - static_assert(sizeof(NrrHeader) == 0x350, "NrrHeader definition!"); struct NroHeader { u32 entrypoint_insn; @@ -105,10 +81,11 @@ class Registration { bool in_use; }; private: - static Result MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, Handle process_handle, u64 title_id, u64 nrr_heap_address, u64 nrr_heap_size); + static Result MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, Handle process_handle, u64 title_id, u64 nrr_heap_address, u64 nrr_heap_size, RoModuleType expected_type, bool enforce_type); static Result UnmapNrr(Handle process_handle, const NrrHeader *header, u64 nrr_heap_address, u64 nrr_heap_size, u64 mapped_code_address); public: static void Initialize(); + static bool ShouldEaseNroRestriction(); static Result RegisterProcess(RoProcessContext **out_context, Handle process_handle, u64 process_id); static void UnregisterProcess(RoProcessContext *context); From 13ded6bd1c9d69703d53ff4faef1c1cd9c2389d2 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 21 Apr 2019 07:28:07 -0700 Subject: [PATCH 21/64] ro: implement loadnro/unloadnro --- stratosphere/libstratosphere | 2 +- stratosphere/ro/source/ro_registration.cpp | 310 +++++++++++++++++++-- stratosphere/ro/source/ro_registration.hpp | 28 ++ stratosphere/ro/source/ro_service.cpp | 15 +- 4 files changed, 330 insertions(+), 25 deletions(-) diff --git a/stratosphere/libstratosphere b/stratosphere/libstratosphere index 880bce909..4d6bf21f9 160000 --- a/stratosphere/libstratosphere +++ b/stratosphere/libstratosphere @@ -1 +1 @@ -Subproject commit 880bce9092fbef0bcbf101b8ec2e3d2c5af3fb98 +Subproject commit 4d6bf21f9ce6255086e649073b5e2fa7c26d1d27 diff --git a/stratosphere/ro/source/ro_registration.cpp b/stratosphere/ro/source/ro_registration.cpp index 3697c675a..e7ea8543d 100644 --- a/stratosphere/ro/source/ro_registration.cpp +++ b/stratosphere/ro/source/ro_registration.cpp @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + #include #include #include @@ -33,11 +33,11 @@ void Registration::Initialize() { std::abort(); } ON_SCOPE_EXIT { splExit(); }; - + if (R_FAILED(splIsDevelopment(&g_is_development_hardware))) { std::abort(); } - + { u64 out_val = 0; if (R_FAILED(splGetConfig(SplConfigItem_IsDebugMode, &out_val))) { @@ -49,11 +49,11 @@ void Registration::Initialize() { bool Registration::ShouldEaseNroRestriction() { bool should_ease = false; - + if (R_FAILED(setsysGetSettingsItemValue("ro", "ease_nro_restriction", &should_ease, sizeof(should_ease)))) { return false; } - + /* Nintendo only allows easing restriction on dev, we will allow on production, as well. */ /* should_ease &= g_is_development_function_enabled; */ return should_ease; @@ -66,10 +66,11 @@ Result Registration::RegisterProcess(RoProcessContext **out_context, Handle proc return ResultRoInvalidSession; } } - + /* Find a free process context. */ for (size_t i = 0; i < Registration::MaxSessions; i++) { if (!g_process_contexts[i].in_use) { + std::memset(&g_process_contexts[i], 0, sizeof(g_process_contexts[i])); g_process_contexts[i].process_id = process_id; g_process_contexts[i].process_handle = process_handle; g_process_contexts[i].in_use = true; @@ -77,7 +78,7 @@ Result Registration::RegisterProcess(RoProcessContext **out_context, Handle proc return ResultSuccess; } } - + /* Failure to find a free context is actually an abort condition. */ /* TODO: Should this return an unofficial error code? */ std::abort(); @@ -90,6 +91,7 @@ void Registration::UnregisterProcess(RoProcessContext *context) { UnmapNrr(context->process_handle, context->nrr_infos[i].header, context->nrr_infos[i].nrr_heap_address, context->nrr_infos[i].nrr_heap_size, context->nrr_infos[i].mapped_code_address); } } + svcCloseHandle(context->process_handle); } std::memset(context, 0, sizeof(*context)); } @@ -129,7 +131,7 @@ Result Registration::LoadNrr(RoProcessContext *context, u64 title_id, u64 nrr_ad if (nrr_size == 0 || (nrr_size & 0xFFF) || !(nrr_address < nrr_address + nrr_size)) { return ResultRoInvalidSize; } - + /* Check we have space for a new NRR. */ size_t slot = 0; for (slot = 0; slot < Registration::MaxNrrInfos; slot++) { @@ -140,9 +142,9 @@ Result Registration::LoadNrr(RoProcessContext *context, u64 title_id, u64 nrr_ad if (slot == Registration::MaxNrrInfos) { return ResultRoTooManyNrr; } - + NrrInfo *nrr_info = &context->nrr_infos[slot]; - + /* Map. */ NrrHeader *header = nullptr; u64 mapped_code_address = 0; @@ -150,14 +152,14 @@ Result Registration::LoadNrr(RoProcessContext *context, u64 title_id, u64 nrr_ad if (R_FAILED(rc)) { return rc; } - + /* Set NRR info. */ nrr_info->header = header; nrr_info->nrr_heap_address = nrr_address; nrr_info->nrr_heap_size = nrr_size; nrr_info->mapped_code_address = mapped_code_address; context->nrr_in_use[slot] = true; - + /* TODO. */ return ResultSuccess; } @@ -174,7 +176,7 @@ Result Registration::UnloadNrr(RoProcessContext *context, u64 nrr_address) { if (!context->nrr_in_use[slot]) { continue; } - + if (context->nrr_infos[slot].nrr_heap_address == nrr_address) { break; } @@ -193,10 +195,195 @@ Result Registration::UnloadNrr(RoProcessContext *context, u64 nrr_address) { return UnmapNrr(context->process_handle, nrr_info.header, nrr_info.nrr_heap_address, nrr_info.nrr_heap_size, nrr_info.mapped_code_address); } + +Result Registration::LoadNro(u64 *out_address, RoProcessContext *context, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { + /* Validate address/size. */ + if (nro_address & 0xFFF) { + return ResultRoInvalidAddress; + } + if (nro_size == 0 || (nro_size & 0xFFF) || !(nro_address < nro_address + nro_size)) { + return ResultRoInvalidSize; + } + if (bss_address & 0xFFF) { + return ResultRoInvalidAddress; + } + if ((bss_size & 0xFFF) || (bss_size > 0 && !(bss_address < bss_address + bss_size))) { + return ResultRoInvalidSize; + } + + const u64 total_size = nro_size + bss_size; + if (total_size < nro_size || total_size < bss_size) { + return ResultRoInvalidSize; + } + + /* Check we have space for a new NRO. */ + size_t slot = 0; + for (slot = 0; slot < Registration::MaxNroInfos; slot++) { + if (!context->nro_in_use[slot]) { + break; + } + } + if (slot == Registration::MaxNroInfos) { + return ResultRoTooManyNro; + } + + NroInfo *nro_info = &context->nro_infos[slot]; + nro_info->nro_heap_address = nro_address; + nro_info->nro_heap_size = nro_size; + nro_info->bss_heap_address = bss_address; + nro_info->bss_heap_size = bss_size; + + /* Map the NRO. */ + Result rc = MapNro(&nro_info->base_address, context->process_handle, nro_address, nro_size, bss_address, bss_size); + if (R_FAILED(rc)) { + return rc; + } + + /* Validate the NRO (parsing region extents). */ + u64 rx_size, ro_size, rw_size; + if (R_FAILED((rc = ValidateNro(&nro_info->module_id, &rx_size, &ro_size, &rw_size, context, nro_info->base_address, nro_size, bss_size)))) { + UnmapNro(context->process_handle, nro_info->base_address, nro_address, bss_address, bss_size, nro_size, 0); + return rc; + } + + /* Set NRO perms. */ + if (R_FAILED((rc = SetNroPerms(context->process_handle, nro_info->base_address, rx_size, ro_size, rw_size + bss_size)))) { + UnmapNro(context->process_handle, nro_info->base_address, nro_address, bss_address, bss_size, rx_size + ro_size, rw_size); + return rc; + } + + nro_info->code_size = rx_size + ro_size; + nro_info->rw_size = rw_size; + context->nro_in_use[slot] = true; + *out_address = nro_info->base_address; + return ResultSuccess; +} + +bool Registration::IsNroHashPresent(RoProcessContext *context, const Sha256Hash *hash) { + for (size_t i = 0; i < Registration::MaxNrrInfos; i++) { + if (context->nrr_in_use[i]) { + const Sha256Hash *nro_hashes = reinterpret_cast(reinterpret_cast(context->nrr_infos[i].header) + context->nrr_infos[i].header->hash_offset); + if (std::binary_search(nro_hashes, nro_hashes + context->nrr_infos[i].header->num_hashes, *hash)) { + return true; + } + } + } + return false; +} + +Result Registration::ValidateNro(ModuleId *out_module_id, u64 *out_rx_size, u64 *out_ro_size, u64 *out_rw_size, RoProcessContext *context, u64 base_address, u64 nro_size, u64 bss_size) { + /* Find space to map the NRO. */ + u64 map_address; + if (R_FAILED(MapUtils::LocateSpaceForMap(&map_address, nro_size))) { + return ResultRoInsufficientAddressSpace; + } + + /* Actually map the NRO. */ + AutoCloseMap nro_map(map_address, context->process_handle, base_address, nro_size); + if (!nro_map.IsSuccess()) { + return nro_map.GetResult(); + } + + /* Validate header. */ + const Registration::NroHeader *header = reinterpret_cast(map_address); + if (header->magic != MagicNro0) { + return ResultRoInvalidNro; + } + if (header->nro_size != nro_size || header->bss_size != bss_size) { + return ResultRoInvalidNro; + } + if ((header->text_size & 0xFFF) || (header->ro_size & 0xFFF) || (header->rw_size & 0xFFF) || (header->bss_size & 0xFFF)) { + return ResultRoInvalidNro; + } + if (header->text_offset > header->ro_offset || header->ro_offset > header->rw_offset) { + return ResultRoInvalidNro; + } + if (header->text_offset != 0 || header->text_offset + header->text_size != header->ro_offset || header->ro_offset + header->ro_size != header->rw_offset || header->rw_offset + header->rw_size != header->nro_size) { + return ResultRoInvalidNro; + } + + /* Verify NRO hash. */ + { + Sha256Hash hash; + sha256CalculateHash(&hash, header, nro_size); + if (!IsNroHashPresent(context, &hash)) { + return ResultRoNotAuthorized; + } + } + + ModuleId module_id; + std::memcpy(&module_id, header->build_id, sizeof(module_id)); + + /* Check if NRO has already been loaded. */ + for (size_t i = 0; i < Registration::MaxNroInfos; i++) { + if (context->nro_in_use[i]) { + if (std::memcmp(&context->nro_infos[i].module_id, &module_id, sizeof(module_id)) == 0) { + return ResultRoAlreadyLoaded; + } + } + } + + *out_module_id = module_id; + *out_rx_size = header->text_size; + *out_ro_size = header->ro_size; + *out_rw_size = header->rw_size; + return ResultSuccess; +} + +Result Registration::SetNroPerms(Handle process_handle, u64 base_address, u64 rx_size, u64 ro_size, u64 rw_size) { + Result rc; + const u64 rx_offset = 0; + const u64 ro_offset = rx_offset + rx_size; + const u64 rw_offset = ro_offset + ro_size; + + if (R_FAILED((rc = svcSetProcessMemoryPermission(process_handle, base_address + rx_offset, rx_size, 5)))) { + return rc; + } + if (R_FAILED((rc = svcSetProcessMemoryPermission(process_handle, base_address + ro_offset, ro_size, 1)))) { + return rc; + } + if (R_FAILED((rc = svcSetProcessMemoryPermission(process_handle, base_address + rw_offset, rw_size, 3)))) { + return rc; + } + + return ResultSuccess; +} + +Result Registration::UnloadNro(RoProcessContext *context, u64 nro_address) { + /* Validate address. */ + if (nro_address & 0xFFF) { + return ResultRoInvalidAddress; + } + + /* Check the NRO is loaded. */ + size_t slot = 0; + for (slot = 0; slot < Registration::MaxNroInfos; slot++) { + if (!context->nro_in_use[slot]) { + continue; + } + + if (context->nro_infos[slot].nro_heap_address == nro_address) { + break; + } + } + if (slot == Registration::MaxNrrInfos) { + return ResultRoNotLoaded; + } + + /* Unmap. */ + const NroInfo nro_info = context->nro_infos[slot]; + { + /* Nintendo does this unconditionally, whether or not the actual unmap succeeds. */ + context->nro_in_use[slot] = false; + std::memset(&context->nro_infos[slot], 0, sizeof(context->nro_infos[slot])); + } + return UnmapNro(context->process_handle, nro_info.base_address, nro_info.nro_heap_address, nro_info.bss_heap_address, nro_info.bss_heap_size, nro_info.code_size, nro_info.rw_size); +} + Result Registration::MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, Handle process_handle, u64 title_id, u64 nrr_heap_address, u64 nrr_heap_size, RoModuleType expected_type, bool enforce_type) { Result rc; MappedCodeMemory nrr_mcm; - + /* First, map the NRR. */ if (R_FAILED((rc = MapUtils::MapCodeMemoryForProcess(nrr_mcm, process_handle, true, nrr_heap_address, nrr_heap_size)))) { if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) { @@ -207,28 +394,28 @@ Result Registration::MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_c return rc; } } - + const u64 code_address = nrr_mcm.GetDstAddress(); u64 map_address; if (R_FAILED(MapUtils::LocateSpaceForMap(&map_address, nrr_heap_size))) { return ResultRoInsufficientAddressSpace; } - + /* Nintendo...does not check the return value of this map. We will check, instead of aborting if it fails. */ AutoCloseMap nrr_map(map_address, process_handle, code_address, nrr_heap_size); if (!nrr_map.IsSuccess()) { return nrr_map.GetResult(); } - + NrrHeader *nrr_header = reinterpret_cast(map_address); if (R_FAILED((rc = NrrUtils::ValidateNrr(nrr_header, nrr_heap_size, title_id, expected_type, enforce_type)))) { return rc; } - + /* Invalidation here actually prevents them from unmapping at scope exit. */ nrr_map.Invalidate(); nrr_mcm.Invalidate(); - + *out_header = nrr_header; *out_mapped_code_address = code_address; return ResultSuccess; @@ -239,6 +426,89 @@ Result Registration::UnmapNrr(Handle process_handle, const NrrHeader *header, u6 if (R_FAILED(rc)) { return rc; } - + return svcUnmapProcessCodeMemory(process_handle, mapped_code_address, nrr_heap_address, nrr_heap_size); } + +Result Registration::MapNro(u64 *out_base_address, Handle process_handle, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size) { + Result rc; + MappedCodeMemory nro_mcm; + MappedCodeMemory bss_mcm; + u64 base_address; + + /* Map the NRO, and map the BSS immediately after it. */ + size_t i = 0; + for (i = 0; i < MapUtils::LocateRetryCount; i++) { + MappedCodeMemory tmp_nro_mcm; + bool is_64_bit = true; + if (R_FAILED((rc = MapUtils::MapCodeMemoryForProcess(tmp_nro_mcm, process_handle, is_64_bit, nro_heap_address, nro_heap_size)))) { + if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) { + /* Try mapping as 32-bit, since we might have guessed wrong on < 3.0.0. */ + is_64_bit = false; + rc = MapUtils::MapCodeMemoryForProcess(tmp_nro_mcm, process_handle, is_64_bit, nro_heap_address, nro_heap_size); + } + if (R_FAILED(rc)) { + return rc; + } + } + base_address = tmp_nro_mcm.GetDstAddress(); + + if (bss_heap_size > 0) { + MappedCodeMemory tmp_bss_mcm(process_handle, base_address + nro_heap_size, bss_heap_address, bss_heap_size); + rc = tmp_bss_mcm.GetResult(); + if (rc == ResultKernelInvalidMemoryState) { + continue; + } + if (R_FAILED(rc)) { + return rc; + } + + if (!MapUtils::CanAddGuardRegions(process_handle, base_address, nro_heap_size + bss_heap_size)) { + continue; + } + + bss_mcm = std::move(tmp_bss_mcm); + } else { + if (!MapUtils::CanAddGuardRegions(process_handle, base_address, nro_heap_size)) { + continue; + } + } + nro_mcm = std::move(tmp_nro_mcm); + break; + } + if (i == MapUtils::LocateRetryCount) { + return ResultRoInsufficientAddressSpace; + } + + /* Invalidation here actually prevents them from unmapping at scope exit. */ + nro_mcm.Invalidate(); + bss_mcm.Invalidate(); + + *out_base_address = base_address; + return ResultSuccess; +} + +Result Registration::UnmapNro(Handle process_handle, u64 base_address, u64 nro_heap_address, u64 bss_heap_address, u64 bss_heap_size, u64 code_size, u64 rw_size) { + Result rc; + + /* First, unmap bss. */ + if (bss_heap_size > 0) { + if (R_FAILED((rc = svcUnmapProcessCodeMemory(process_handle, base_address + code_size + rw_size, bss_heap_address, bss_heap_size)))) { + return rc; + } + } + + /* Next, unmap .rwdata */ + if (rw_size > 0) { + if (R_FAILED((rc = svcUnmapProcessCodeMemory(process_handle, base_address + code_size, nro_heap_address + code_size, rw_size)))) { + return rc; + } + } + + /* Finally, unmap .text + .rodata. */ + if (R_FAILED((rc = svcUnmapProcessCodeMemory(process_handle, base_address, nro_heap_address, code_size)))) { + return rc; + } + + return ResultSuccess; +} diff --git a/stratosphere/ro/source/ro_registration.hpp b/stratosphere/ro/source/ro_registration.hpp index 30f0910a3..ca34791ee 100644 --- a/stratosphere/ro/source/ro_registration.hpp +++ b/stratosphere/ro/source/ro_registration.hpp @@ -27,6 +27,8 @@ class Registration { static constexpr size_t MaxSessions = 0x4; static constexpr size_t MaxNrrInfos = 0x40; static constexpr size_t MaxNroInfos = 0x40; + + static constexpr u32 MagicNro0 = 0x304F524E; public: struct NroHeader { @@ -54,6 +56,24 @@ class Registration { u8 build_id[0x20]; }; static_assert(sizeof(ModuleId) == sizeof(LoaderModuleInfo::build_id), "ModuleId definition!"); + + struct Sha256Hash { + u8 hash[0x20]; + + bool operator==(const Sha256Hash &o) const { + return std::memcmp(this, &o, sizeof(*this)) == 0; + } + bool operator!=(const Sha256Hash &o) const { + return std::memcmp(this, &o, sizeof(*this)) != 0; + } + bool operator<(const Sha256Hash &o) const { + return std::memcmp(this, &o, sizeof(*this)) < 0; + } + bool operator>(const Sha256Hash &o) const { + return std::memcmp(this, &o, sizeof(*this)) > 0; + } + }; + static_assert(sizeof(Sha256Hash) == sizeof(Sha256Hash::hash), "Sha256Hash definition!"); struct NroInfo { u64 base_address; @@ -83,6 +103,12 @@ class Registration { private: static Result MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, Handle process_handle, u64 title_id, u64 nrr_heap_address, u64 nrr_heap_size, RoModuleType expected_type, bool enforce_type); static Result UnmapNrr(Handle process_handle, const NrrHeader *header, u64 nrr_heap_address, u64 nrr_heap_size, u64 mapped_code_address); + static bool IsNroHashPresent(RoProcessContext *context, const Sha256Hash *hash); + + static Result MapNro(u64 *out_base_address, Handle process_handle, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size); + static Result ValidateNro(ModuleId *out_module_id, u64 *out_rx_size, u64 *out_ro_size, u64 *out_rw_size, RoProcessContext *context, u64 base_address, u64 nro_size, u64 bss_size); + static Result SetNroPerms(Handle process_handle, u64 base_address, u64 rx_size, u64 ro_size, u64 rw_size); + static Result UnmapNro(Handle process_handle, u64 base_address, u64 nro_heap_address, u64 bss_heap_address, u64 bss_heap_size, u64 code_size, u64 rw_size); public: static void Initialize(); static bool ShouldEaseNroRestriction(); @@ -92,6 +118,8 @@ class Registration { static Result LoadNrr(RoProcessContext *context, u64 title_id, u64 nrr_address, u64 nrr_size, RoModuleType expected_type, bool enforce_type); static Result UnloadNrr(RoProcessContext *context, u64 nrr_address); + static Result LoadNro(u64 *out_address, RoProcessContext *context, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size); + static Result UnloadNro(RoProcessContext *context, u64 nro_address); static Result GetProcessModuleInfo(u32 *out_count, LoaderModuleInfo *out_infos, size_t max_out_count, u64 process_id); }; \ No newline at end of file diff --git a/stratosphere/ro/source/ro_service.cpp b/stratosphere/ro/source/ro_service.cpp index d384fe282..0b0a9e7c6 100644 --- a/stratosphere/ro/source/ro_service.cpp +++ b/stratosphere/ro/source/ro_service.cpp @@ -25,6 +25,7 @@ RelocatableObjectsService::~RelocatableObjectsService() { if (this->IsInitialized()) { Registration::UnregisterProcess(this->context); + this->context = nullptr; } } @@ -57,13 +58,19 @@ u64 RelocatableObjectsService::GetTitleId(Handle process_handle) { } Result RelocatableObjectsService::LoadNro(Out load_address, PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { - /* TODO */ - return ResultKernelConnectionClosed; + if (!this->IsProcessIdValid(pid_desc.pid)) { + return ResultRoInvalidProcess; + } + + return Registration::LoadNro(load_address.GetPointer(), this->context, nro_address, nro_size, bss_address, bss_size); } Result RelocatableObjectsService::UnloadNro(PidDescriptor pid_desc, u64 nro_address) { - /* TODO */ - return ResultKernelConnectionClosed; + if (!this->IsProcessIdValid(pid_desc.pid)) { + return ResultRoInvalidProcess; + } + + return Registration::UnloadNro(this->context, nro_address); } Result RelocatableObjectsService::LoadNrr(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size) { From a09c08994f2b0f56c72b7bc9fb79a7b7009e0b32 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 21 Apr 2019 08:57:21 -0700 Subject: [PATCH 22/64] sm: change location of apm:p check, fixes failure to launch older games --- stratosphere/sm/source/sm_registration.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/stratosphere/sm/source/sm_registration.cpp b/stratosphere/sm/source/sm_registration.cpp index 390686e26..eead4cc7d 100644 --- a/stratosphere/sm/source/sm_registration.cpp +++ b/stratosphere/sm/source/sm_registration.cpp @@ -95,15 +95,6 @@ Registration::Service *Registration::GetFreeService() { } bool Registration::IsValidForSac(u8 *sac, size_t sac_size, u64 service, bool is_host) { - /* In 8.0.0, Nintendo removed the service apm:p -- however, all homebrew attempts to get */ - /* a handle to this when calling appletInitialize(). Because hbl has access to all services, */ - /* This would return true, and homebrew would *wait forever* trying to get a handle to a service */ - /* that will never register. Thus, in the interest of not breaking every single piece of homebrew */ - /* we will provide a little first class help. */ - if (GetRuntimeFirmwareVersion() >= FirmwareVersion_800 && service == EncodeNameConstant("apm:p")) { - return false; - } - u8 cur_ctrl; u64 cur_service; u64 service_for_compare; @@ -310,6 +301,15 @@ Result Registration::GetServiceForPid(u64 pid, u64 service, Handle *out) { if (service_name_len != 8 && (service >> (8 * service_name_len))) { return ResultSmInvalidServiceName; } + + /* In 8.0.0, Nintendo removed the service apm:p -- however, all homebrew attempts to get */ + /* a handle to this when calling appletInitialize(). Because hbl has access to all services, */ + /* This would return true, and homebrew would *wait forever* trying to get a handle to a service */ + /* that will never register. Thus, in the interest of not breaking every single piece of homebrew */ + /* we will provide a little first class help. */ + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_800 && service == EncodeNameConstant("apm:p")) { + return ResultSmNotAllowed; + } if (!IsInitialProcess(pid)) { Registration::Process *proc = GetProcessForPid(pid); From 253afc90a42893c2662e04ce07d92e94b40b5159 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 21 Apr 2019 09:08:08 -0700 Subject: [PATCH 23/64] loader: remove ldr:ro (moved into ro sysmodule) --- stratosphere/loader/source/ldr_main.cpp | 17 +- stratosphere/loader/source/ldr_nro.cpp | 143 ----------------- stratosphere/loader/source/ldr_nro.hpp | 72 --------- .../loader/source/ldr_process_manager.cpp | 4 +- .../loader/source/ldr_registration.cpp | 144 ----------------- .../loader/source/ldr_registration.hpp | 29 +--- stratosphere/loader/source/ldr_ro_service.cpp | 148 ------------------ stratosphere/loader/source/ldr_ro_service.hpp | 60 ------- 8 files changed, 7 insertions(+), 610 deletions(-) delete mode 100644 stratosphere/loader/source/ldr_nro.cpp delete mode 100644 stratosphere/loader/source/ldr_nro.hpp delete mode 100644 stratosphere/loader/source/ldr_ro_service.cpp delete mode 100644 stratosphere/loader/source/ldr_ro_service.hpp diff --git a/stratosphere/loader/source/ldr_main.cpp b/stratosphere/loader/source/ldr_main.cpp index a278f6295..4fd24c72b 100644 --- a/stratosphere/loader/source/ldr_main.cpp +++ b/stratosphere/loader/source/ldr_main.cpp @@ -26,7 +26,6 @@ #include "ldr_process_manager.hpp" #include "ldr_debug_monitor.hpp" #include "ldr_shell.hpp" -#include "ldr_ro_service.hpp" extern "C" { extern u32 __start__; @@ -114,21 +113,15 @@ int main(int argc, char **argv) { consoleDebugInit(debugDevice_SVC); - auto server_manager = new WaitableManager(1); + static auto s_server_manager = WaitableManager(1); /* Add services to manager. */ - server_manager->AddWaitable(new ServiceServer("ldr:pm", 1)); - server_manager->AddWaitable(new ServiceServer("ldr:shel", 3)); - server_manager->AddWaitable(new ServiceServer("ldr:dmnt", 2)); - if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) { - /* On 1.0.0-2.3.0, Loader services ldr:ro instead of ro. */ - server_manager->AddWaitable(new ServiceServer("ldr:ro", 0x20)); - } + s_server_manager.AddWaitable(new ServiceServer("ldr:pm", 1)); + s_server_manager.AddWaitable(new ServiceServer("ldr:shel", 3)); + s_server_manager.AddWaitable(new ServiceServer("ldr:dmnt", 2)); /* Loop forever, servicing our services. */ - server_manager->Process(); - - delete server_manager; + s_server_manager.Process(); return 0; } diff --git a/stratosphere/loader/source/ldr_nro.cpp b/stratosphere/loader/source/ldr_nro.cpp deleted file mode 100644 index a28da564a..000000000 --- a/stratosphere/loader/source/ldr_nro.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include "ldr_nro.hpp" -#include "ldr_registration.hpp" -#include "ldr_map.hpp" - -Result NroUtils::ValidateNrrHeader(NrrHeader *header, u64 size, u64 title_id_min) { - if (header->magic != MAGIC_NRR0) { - return ResultLoaderInvalidNrr; - } - if (header->nrr_size != size) { - return ResultLoaderInvalidSize; - } - - /* TODO: Check NRR signature. */ - if (false) { - return ResultLoaderInvalidSignature; - } - - if (header->title_id_min != title_id_min) { - return ResultLoaderInvalidNrr; - } - - return ResultSuccess; -} - -Result NroUtils::LoadNro(Registration::Process *target_proc, Handle process_h, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size, u64 *out_address) { - NroHeader nro_hdr = {}; - MappedCodeMemory mcm_nro = {}; - MappedCodeMemory mcm_bss = {}; - unsigned int i; - Result rc = ResultSuccess; - u8 nro_hash[0x20]; - - /* Perform cleanup on failure. */ - ON_SCOPE_EXIT { - if (R_FAILED(rc)) { - mcm_nro.Close(); - mcm_bss.Close(); - } - }; - - /* Ensure there is an available NRO slot. */ - if (std::all_of(target_proc->nro_infos.begin(), target_proc->nro_infos.end(), std::mem_fn(&Registration::NroInfo::in_use))) { - rc = ResultLoaderInsufficientNroRegistrations; - return rc; - } - for (i = 0; i < 0x200; i++) { - if (R_SUCCEEDED(mcm_nro.Open(process_h, target_proc->is_64_bit_addspace, nro_heap_address, nro_heap_size))) { - if (R_SUCCEEDED(mcm_bss.OpenAtAddress(process_h, bss_heap_address, bss_heap_size, mcm_nro.code_memory_address + nro_heap_size))) { - break; - } else { - mcm_nro.Close(); - } - } - } - if (i >= 0x200) { - rc = ResultLoaderInsufficientAddressSpace; - return rc; - } - - /* Map the NRO. */ - if (R_FAILED((rc = mcm_nro.Map()))) { - return rc; - } - - /* Read data from NRO while it's mapped. */ - { - nro_hdr = *((NroHeader *)mcm_nro.mapped_address); - - if (nro_hdr.magic != MAGIC_NRO0) { - rc = ResultLoaderInvalidNro; - return rc; - } - if (nro_hdr.nro_size != nro_heap_size || nro_hdr.bss_size != bss_heap_size) { - rc = ResultLoaderInvalidNro; - return rc; - } - if ((nro_hdr.text_size & 0xFFF) || (nro_hdr.ro_size & 0xFFF) || (nro_hdr.rw_size & 0xFFF) || (nro_hdr.bss_size & 0xFFF)) { - rc = ResultLoaderInvalidNro; - return rc; - } - if (nro_hdr.text_offset != 0 || nro_hdr.text_offset + nro_hdr.text_size != nro_hdr.ro_offset || nro_hdr.ro_offset + nro_hdr.ro_size != nro_hdr.rw_offset || nro_hdr.rw_offset + nro_hdr.rw_size != nro_hdr.nro_size) { - rc = ResultLoaderInvalidNro; - return rc; - } - - sha256CalculateHash(nro_hash, mcm_nro.mapped_address, nro_hdr.nro_size); - } - - /* Unmap the NRO. */ - if (R_FAILED((rc = mcm_nro.Unmap()))) { - return rc; - } - - if (!Registration::IsNroHashPresent(target_proc->index, nro_hash)) { - rc = ResultLoaderInvalidSignature; - return rc; - } - - if (Registration::IsNroAlreadyLoaded(target_proc->index, nro_hash)) { - rc = ResultLoaderNroAlreadyLoaded; - return rc; - } - - if (R_FAILED((rc = svcSetProcessMemoryPermission(process_h, mcm_nro.code_memory_address, nro_hdr.text_size, 5)))) { - return rc; - } - - if (R_FAILED((rc = svcSetProcessMemoryPermission(process_h, mcm_nro.code_memory_address + nro_hdr.ro_offset, nro_hdr.ro_size, 1)))) { - return rc; - } - - if (R_FAILED((rc = svcSetProcessMemoryPermission(process_h, mcm_nro.code_memory_address + nro_hdr.rw_offset, nro_hdr.rw_size + nro_hdr.bss_size, 3)))) { - return rc; - } - - Registration::AddNroToProcess(target_proc->index, &mcm_nro, &mcm_bss, nro_hdr.text_size, nro_hdr.ro_size, nro_hdr.rw_size, nro_hdr.build_id); - *out_address = mcm_nro.code_memory_address; - rc = ResultSuccess; - - return rc; -} diff --git a/stratosphere/loader/source/ldr_nro.hpp b/stratosphere/loader/source/ldr_nro.hpp deleted file mode 100644 index a2886d141..000000000 --- a/stratosphere/loader/source/ldr_nro.hpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once -#include -#include - -#include "ldr_registration.hpp" -#define MAGIC_NRO0 0x304F524E -#define MAGIC_NRR0 0x3052524E - -class NroUtils { - public: - struct NrrHeader { - u32 magic; - u32 _0x4; - u32 _0x8; - u32 _0xC; - u64 title_id_mask; - u64 title_id_pattern; - u64 _0x20; - u64 _0x28; - u8 modulus[0x100]; - u8 fixed_key_signature[0x100]; - u8 nrr_signature[0x100]; - u64 title_id_min; - u32 nrr_size; - u32 _0x33C; - u32 hash_offset; - u32 num_hashes; - u64 _0x348; - }; - - struct NroHeader { - u32 entrypoint_insn; - u32 mod_offset; - u64 padding; - u32 magic; - u32 _0x14; - u32 nro_size; - u32 _0x1C; - u32 text_offset; - u32 text_size; - u32 ro_offset; - u32 ro_size; - u32 rw_offset; - u32 rw_size; - u32 bss_size; - u32 _0x3C; - unsigned char build_id[0x20]; - u8 _0x60[0x20]; - }; - - - static_assert(sizeof(NrrHeader) == 0x350, "Incorrectly defined NrrHeader!"); - static_assert(sizeof(NroHeader) == 0x80, "Incorrectly defined NroHeader!"); - static Result ValidateNrrHeader(NrrHeader *header, u64 size, u64 title_id_min); - static Result LoadNro(Registration::Process *target_proc, Handle process_h, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size, u64 *out_address); -}; \ No newline at end of file diff --git a/stratosphere/loader/source/ldr_process_manager.cpp b/stratosphere/loader/source/ldr_process_manager.cpp index ddef59a8c..c6a6d11cf 100644 --- a/stratosphere/loader/source/ldr_process_manager.cpp +++ b/stratosphere/loader/source/ldr_process_manager.cpp @@ -27,9 +27,7 @@ Result ProcessManagerService::CreateProcess(Out proc_h, u64 index, Registration::TidSid tid_sid; LaunchQueue::LaunchItem *launch_item; char nca_path[FS_MAX_PATH] = {0}; - - fprintf(stderr, "CreateProcess(%016lx, %08x, %08x);\n", index, flags, reslimit_h.handle); - + ON_SCOPE_EXIT { /* Loader doesn't persist the copied resource limit handle. */ svcCloseHandle(reslimit_h.handle); diff --git a/stratosphere/loader/source/ldr_registration.cpp b/stratosphere/loader/source/ldr_registration.cpp index 3d01cad43..44c075255 100644 --- a/stratosphere/loader/source/ldr_registration.cpp +++ b/stratosphere/loader/source/ldr_registration.cpp @@ -20,7 +20,6 @@ #include #include #include "ldr_registration.hpp" -#include "ldr_nro.hpp" static Registration::List g_registration_list = {}; static u64 g_num_registered = 1; @@ -51,15 +50,6 @@ Registration::Process *Registration::GetProcessByProcessId(u64 pid) { return NULL; } -Registration::Process *Registration::GetProcessByRoService(void *service) { - for (unsigned int i = 0; i < Registration::MaxProcesses; i++) { - if (g_registration_list.processes[i].in_use && g_registration_list.processes[i].owner_ro_service == service) { - return &g_registration_list.processes[i]; - } - } - return NULL; -} - bool Registration::RegisterTidSid(const TidSid *tid_sid, u64 *out_index) { Registration::Process *free_process = GetFreeProcess(); if (free_process == NULL) { @@ -124,140 +114,6 @@ void Registration::AddModuleInfo(u64 index, u64 base_address, u64 size, const un } } -void Registration::CloseRoService(void *service, Handle process_h) { - Registration::Process *target_process = GetProcessByRoService(service); - if (target_process == NULL) { - return; - } - for (unsigned int i = 0; i < Registration::MaxNrrInfos; i++) { - if (target_process->nrr_infos[i].IsActive() && target_process->nrr_infos[i].process_handle == process_h) { - target_process->nrr_infos[i].Close(); - } - } - target_process->owner_ro_service = NULL; -} - -Result Registration::AddNrrInfo(u64 index, MappedCodeMemory *nrr_info) { - Registration::Process *target_process = GetProcess(index); - if (target_process == NULL) { - /* TODO: std::abort(); */ - return ResultLoaderProcessNotRegistered; - } - - auto nrr_info_it = std::find_if_not(target_process->nrr_infos.begin(), target_process->nrr_infos.end(), std::mem_fn(&MappedCodeMemory::IsActive)); - if (nrr_info_it == target_process->nrr_infos.end()) { - return ResultLoaderInsufficientNrrRegistrations; - } - *nrr_info_it = *nrr_info; - return ResultSuccess; -} - -Result Registration::RemoveNrrInfo(u64 index, u64 base_address) { - Registration::Process *target_process = GetProcess(index); - if (target_process == NULL) { - /* Despite the fact that this should really be a panic condition, Nintendo returns 0x1009 in this case. */ - return ResultLoaderProcessNotRegistered; - } - - for (unsigned int i = 0; i < Registration::MaxNrrInfos; i++) { - if (target_process->nrr_infos[i].IsActive() && target_process->nrr_infos[i].base_address == base_address) { - target_process->nrr_infos[i].Close(); - return ResultSuccess; - } - } - return ResultLoaderNotRegistered; -} - - -bool Registration::IsNroHashPresent(u64 index, u8 *nro_hash) { - Registration::Process *target_process = GetProcess(index); - if (target_process == NULL) { - /* TODO: panic */ - return false; - } - - for (unsigned int i = 0; i < Registration::MaxNrrInfos; i++) { - if (target_process->nrr_infos[i].IsActive()) { - NroUtils::NrrHeader *nrr = (NroUtils::NrrHeader *)target_process->nrr_infos[i].mapped_address; - /* Binary search. */ - int low = 0, high = (int)(nrr->num_hashes - 1); - while (low <= high) { - int mid = (low + high) / 2; - u8 *hash_in_nrr = (u8 *)nrr + nrr->hash_offset + 0x20 * mid; - int ret = std::memcmp(hash_in_nrr, nro_hash, 0x20); - if (ret == 0) { - return true; - } else if (ret > 0) { - high = mid - 1; - } else { - low = mid + 1; - } - } - } - } - return false; -} - -bool Registration::IsNroAlreadyLoaded(u64 index, u8 *build_id) { - Registration::Process *target_process = GetProcess(index); - if (target_process == NULL) { - /* TODO: panic */ - return true; - } - - for (unsigned int i = 0; i < Registration::MaxNroInfos; i++) { - if (target_process->nro_infos[i].in_use && std::equal(build_id, build_id + 0x20, target_process->nro_infos[i].build_id)) { - return true; - } - } - return false; -} - -void Registration::AddNroToProcess(u64 index, MappedCodeMemory *nro, MappedCodeMemory *bss, u32 text_size, u32 ro_size, u32 rw_size, u8 *build_id) { - Registration::Process *target_process = GetProcess(index); - if (target_process == NULL) { - /* TODO: panic */ - return; - } - - auto nro_info_it = std::find_if_not(target_process->nro_infos.begin(), target_process->nro_infos.end(), std::mem_fn(&Registration::NroInfo::in_use)); - if (nro_info_it != target_process->nro_infos.end()) { - nro_info_it->base_address = nro->code_memory_address; - nro_info_it->nro_heap_address = nro->base_address; - nro_info_it->nro_heap_size = nro->size; - nro_info_it->bss_heap_address = bss->base_address; - nro_info_it->bss_heap_size = bss->size; - nro_info_it->text_size = text_size; - nro_info_it->ro_size = ro_size; - nro_info_it->rw_size = rw_size; - std::copy(build_id, build_id + sizeof(nro_info_it->build_id), nro_info_it->build_id); - nro_info_it->in_use = true; - } -} - -Result Registration::RemoveNroInfo(u64 index, Handle process_h, u64 nro_heap_address) { - Registration::Process *target_process = GetProcess(index); - if (target_process == NULL) { - return ResultLoaderProcessNotRegistered; - } - - for (unsigned int i = 0; i < Registration::MaxNroInfos; i++) { - if (target_process->nro_infos[i].in_use && target_process->nro_infos[i].nro_heap_address == nro_heap_address) { - NroInfo *info = &target_process->nro_infos[i]; - Result rc = svcUnmapProcessCodeMemory(process_h, info->base_address + info->text_size + info->ro_size + info->rw_size, info->bss_heap_address, info->bss_heap_size); - if (R_SUCCEEDED(rc)) { - rc = svcUnmapProcessCodeMemory(process_h, info->base_address + info->text_size + info->ro_size, nro_heap_address + info->text_size + info->ro_size, info->rw_size); - if (R_SUCCEEDED(rc)) { - rc = svcUnmapProcessCodeMemory(process_h, info->base_address, nro_heap_address, info->text_size + info->ro_size); - } - } - target_process->nro_infos[i] = {}; - return rc; - } - } - return ResultLoaderNotLoaded; -} - Result Registration::GetProcessModuleInfo(LoaderModuleInfo *out, u32 max_out, u64 process_id, u32 *num_written) { Registration::Process *target_process = GetProcessByProcessId(process_id); if (target_process == NULL) { diff --git a/stratosphere/loader/source/ldr_registration.hpp b/stratosphere/loader/source/ldr_registration.hpp index 27f458ee7..6588a8ae1 100644 --- a/stratosphere/loader/source/ldr_registration.hpp +++ b/stratosphere/loader/source/ldr_registration.hpp @@ -24,28 +24,12 @@ class Registration { public: static constexpr size_t MaxProcesses = 0x40; static constexpr size_t MaxModuleInfos = 0x20; - static constexpr size_t MaxNrrInfos = 0x40; - static constexpr size_t MaxNroInfos = 0x40; public: struct ModuleInfoHolder { bool in_use; LoaderModuleInfo info; }; - - struct NroInfo { - bool in_use; - u64 base_address; - u64 total_mapped_size; - u64 nro_heap_address; - u64 nro_heap_size; - u64 bss_heap_address; - u64 bss_heap_size; - u64 text_size; - u64 ro_size; - u64 rw_size; - unsigned char build_id[0x20]; - }; - + struct TidSid { u64 title_id; FsStorageId storage_id; @@ -59,9 +43,6 @@ class Registration { u64 title_id; Registration::TidSid tid_sid; std::array module_infos; - std::array nro_infos; - std::array nrr_infos; - void *owner_ro_service; }; struct List { @@ -72,19 +53,11 @@ class Registration { static Registration::Process *GetFreeProcess(); static Registration::Process *GetProcess(u64 index); static Registration::Process *GetProcessByProcessId(u64 pid); - static Registration::Process *GetProcessByRoService(void *service); static Result GetRegisteredTidSid(u64 index, Registration::TidSid *out); static bool RegisterTidSid(const TidSid *tid_sid, u64 *out_index); static bool UnregisterIndex(u64 index); static void SetProcessIdTidAndIs64BitAddressSpace(u64 index, u64 process_id, u64 tid, bool is_64_bit_addspace); static void AddModuleInfo(u64 index, u64 base_address, u64 size, const unsigned char *build_id); - static void CloseRoService(void *service, Handle process_h); - static Result AddNrrInfo(u64 index, MappedCodeMemory *nrr_info); - static Result RemoveNrrInfo(u64 index, u64 base_address); - static bool IsNroHashPresent(u64 index, u8 *nro_hash); - static bool IsNroAlreadyLoaded(u64 index, u8 *build_id); - static void AddNroToProcess(u64 index, MappedCodeMemory *nro, MappedCodeMemory *bss, u32 text_size, u32 ro_size, u32 rw_size, u8 *build_id); - static Result RemoveNroInfo(u64 index, Handle process_h, u64 base_address); static Result GetProcessModuleInfo(LoaderModuleInfo *out, u32 max_out, u64 process_id, u32 *num_written); /* Atmosphere MitM Extension. */ diff --git a/stratosphere/loader/source/ldr_ro_service.cpp b/stratosphere/loader/source/ldr_ro_service.cpp deleted file mode 100644 index 5348a2edd..000000000 --- a/stratosphere/loader/source/ldr_ro_service.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include - -#include "ldr_ro_service.hpp" -#include "ldr_registration.hpp" -#include "ldr_map.hpp" -#include "ldr_nro.hpp" - -Result RelocatableObjectsService::LoadNro(Out load_address, PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { - Registration::Process *target_proc = NULL; - if (!this->has_initialized || this->process_id != pid_desc.pid) { - return ResultLoaderInvalidProcess; - } - if (nro_address & 0xFFF) { - return ResultLoaderInvalidAddress; - } - if (nro_address + nro_size <= nro_address || !nro_size || (nro_size & 0xFFF)) { - return ResultLoaderInvalidSize; - } - if (bss_size && bss_address + bss_size <= bss_address) { - return ResultLoaderInvalidSize; - } - /* Ensure no overflow for combined sizes. */ - if (U64_MAX - nro_size < bss_size) { - return ResultLoaderInvalidSize; - } - target_proc = Registration::GetProcessByProcessId(pid_desc.pid); - if (target_proc == NULL || (target_proc->owner_ro_service != NULL && (RelocatableObjectsService *)(target_proc->owner_ro_service) != this)) { - return ResultLoaderInvalidSession; - } - target_proc->owner_ro_service = this; - - return NroUtils::LoadNro(target_proc, this->process_handle, nro_address, nro_size, bss_address, bss_size, load_address.GetPointer()); -} - -Result RelocatableObjectsService::UnloadNro(PidDescriptor pid_desc, u64 nro_address) { - Registration::Process *target_proc = NULL; - if (!this->has_initialized || this->process_id != pid_desc.pid) { - return ResultLoaderInvalidProcess; - } - if (nro_address & 0xFFF) { - return ResultLoaderInvalidAddress; - } - - target_proc = Registration::GetProcessByProcessId(pid_desc.pid); - if (target_proc == NULL || (target_proc->owner_ro_service != NULL && (RelocatableObjectsService *)(target_proc->owner_ro_service) != this)) { - return ResultLoaderInvalidSession; - } - target_proc->owner_ro_service = this; - - return Registration::RemoveNroInfo(target_proc->index, this->process_handle, nro_address); -} - -Result RelocatableObjectsService::LoadNrr(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size) { - Result rc = ResultSuccess; - Registration::Process *target_proc = NULL; - MappedCodeMemory nrr_info = {}; - ON_SCOPE_EXIT { - if (R_FAILED(rc) && nrr_info.IsActive()) { - nrr_info.Close(); - } - }; - - if (!this->has_initialized || this->process_id != pid_desc.pid) { - rc = ResultLoaderInvalidProcess; - return rc; - } - if (nrr_address & 0xFFF) { - rc = ResultLoaderInvalidAddress; - return rc; - } - if (nrr_address + nrr_size <= nrr_address || !nrr_size || (nrr_size & 0xFFF)) { - rc = ResultLoaderInvalidSize; - return rc; - } - - target_proc = Registration::GetProcessByProcessId(pid_desc.pid); - if (target_proc == NULL || (target_proc->owner_ro_service != NULL && (RelocatableObjectsService *)(target_proc->owner_ro_service) != this)) { - rc = ResultLoaderInvalidSession; - return rc; - } - target_proc->owner_ro_service = this; - - if (R_FAILED((rc = nrr_info.Open(this->process_handle, target_proc->is_64_bit_addspace, nrr_address, nrr_size)))) { - return rc; - } - - if (R_FAILED((rc = nrr_info.Map()))) { - return rc; - } - - rc = NroUtils::ValidateNrrHeader((NroUtils::NrrHeader *)nrr_info.mapped_address, nrr_size, target_proc->title_id); - if (R_SUCCEEDED(rc)) { - Registration::AddNrrInfo(target_proc->index, &nrr_info); - } - - return rc; -} - -Result RelocatableObjectsService::UnloadNrr(PidDescriptor pid_desc, u64 nrr_address) { - Registration::Process *target_proc = NULL; - if (!this->has_initialized || this->process_id != pid_desc.pid) { - return ResultLoaderInvalidProcess; - } - if (nrr_address & 0xFFF) { - return ResultLoaderInvalidAddress; - } - - target_proc = Registration::GetProcessByProcessId(pid_desc.pid); - if (target_proc == NULL || (target_proc->owner_ro_service != NULL && (RelocatableObjectsService *)(target_proc->owner_ro_service) != this)) { - return ResultLoaderInvalidSession; - } - target_proc->owner_ro_service = this; - - return Registration::RemoveNrrInfo(target_proc->index, nrr_address); -} - -Result RelocatableObjectsService::Initialize(PidDescriptor pid_desc, CopiedHandle process_h) { - u64 handle_pid; - if (R_SUCCEEDED(svcGetProcessId(&handle_pid, process_h.handle)) && handle_pid == pid_desc.pid) { - if (this->has_initialized) { - svcCloseHandle(this->process_handle); - } - this->process_handle = process_h.handle; - this->process_id = handle_pid; - this->has_initialized = true; - return ResultSuccess; - } - return ResultLoaderInvalidProcess; -} diff --git a/stratosphere/loader/source/ldr_ro_service.hpp b/stratosphere/loader/source/ldr_ro_service.hpp deleted file mode 100644 index f5cc4b62d..000000000 --- a/stratosphere/loader/source/ldr_ro_service.hpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once -#include - -#include -#include "ldr_registration.hpp" - -enum RoServiceCmd { - Ro_Cmd_LoadNro = 0, - Ro_Cmd_UnloadNro = 1, - Ro_Cmd_LoadNrr = 2, - Ro_Cmd_UnloadNrr = 3, - Ro_Cmd_Initialize = 4, - Ro_Cmd_LoadNrrEx = 10, -}; - -class RelocatableObjectsService final : public IServiceObject { - Handle process_handle = 0; - u64 process_id = U64_MAX; - bool has_initialized = false; - public: - virtual ~RelocatableObjectsService() override { - Registration::CloseRoService(this, this->process_handle); - if (this->has_initialized) { - svcCloseHandle(this->process_handle); - } - } - - private: - /* Actual commands. */ - Result LoadNro(Out load_address, PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size); - Result UnloadNro(PidDescriptor pid_desc, u64 nro_address); - Result LoadNrr(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size); - Result UnloadNrr(PidDescriptor pid_desc, u64 nrr_address); - Result Initialize(PidDescriptor pid_desc, CopiedHandle process_h); - Result LoadNrrEx(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size, CopiedHandle process_h); - public: - DEFINE_SERVICE_DISPATCH_TABLE { - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - }; -}; From be4ca7eee505829f092b158c0ea990f0d9746158 Mon Sep 17 00:00:00 2001 From: Sun <22406854+SunTheCourier@users.noreply.github.com> Date: Mon, 22 Apr 2019 00:18:01 -0700 Subject: [PATCH 24/64] Implement Auto Reboot Timer (#518) (#519) * Implement Auto Reboot Timer (#518) * Use > to check for values below -1 * Use TimeoutHelper and accept MS * Add fatal_auto_reboot_interval into config (commented) * Check for 0 --- common/defaults/system_settings.ini | 3 +++ stratosphere/fatal/source/fatal_config.cpp | 5 ++++- stratosphere/fatal/source/fatal_config.hpp | 2 ++ stratosphere/fatal/source/fatal_task_power.cpp | 7 +++++++ 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/common/defaults/system_settings.ini b/common/defaults/system_settings.ini index 075c0d454..07bb5e9e7 100644 --- a/common/defaults/system_settings.ini +++ b/common/defaults/system_settings.ini @@ -6,6 +6,9 @@ upload_enabled = u8!0x0 usb30_force_enabled = u8!0x0 ; Atmosphere custom settings [atmosphere] +; Reboot from fatal automatically after 5 seconds (in milliseconds) +; If field is disabled fatal waits for an input indefinitely +; fatal_auto_reboot_interval = u64!‪5000‬ ; Make the power menu's "reboot" button reboot to payload. ; Set to "normal" for normal reboot, "rcm" for rcm reboot. power_menu_reboot_function = str!payload diff --git a/stratosphere/fatal/source/fatal_config.cpp b/stratosphere/fatal/source/fatal_config.cpp index acd5b1346..0bd7b2930 100644 --- a/stratosphere/fatal/source/fatal_config.cpp +++ b/stratosphere/fatal/source/fatal_config.cpp @@ -18,7 +18,7 @@ #include "fatal_types.hpp" #include "fatal_config.hpp" -static FatalConfig g_fatal_config; +static FatalConfig g_fatal_config = {}; static IEvent *g_fatal_settings_event = nullptr; @@ -84,5 +84,8 @@ void InitializeFatalConfig() { setsysGetFlag(SetSysFlag_Quest, &config->quest_flag); + config->is_auto_reboot_enabled = R_SUCCEEDED(setsysGetSettingsItemValue("atmosphere", "fatal_auto_reboot_interval", &config->fatal_auto_reboot_interval, sizeof(config->fatal_auto_reboot_interval))); + config->is_auto_reboot_enabled &= (config->fatal_auto_reboot_interval != 0); + SetupConfigLanguages(); } diff --git a/stratosphere/fatal/source/fatal_config.hpp b/stratosphere/fatal/source/fatal_config.hpp index 12c495923..6e4c1db7c 100644 --- a/stratosphere/fatal/source/fatal_config.hpp +++ b/stratosphere/fatal/source/fatal_config.hpp @@ -29,6 +29,8 @@ struct FatalConfig { const char *error_msg; const char *error_desc; const char *quest_desc; + u64 fatal_auto_reboot_interval; + bool is_auto_reboot_enabled; }; IEvent *GetFatalSettingsEvent(); diff --git a/stratosphere/fatal/source/fatal_task_power.cpp b/stratosphere/fatal/source/fatal_task_power.cpp index 07ac235d6..4f9117804 100644 --- a/stratosphere/fatal/source/fatal_task_power.cpp +++ b/stratosphere/fatal/source/fatal_task_power.cpp @@ -96,6 +96,8 @@ void PowerButtonObserveTask::WaitForPowerButton() { const FatalConfig *config = GetFatalConfig(); TimeoutHelper reboot_helper(config->quest_reboot_interval_second * 1000000000UL); + TimeoutHelper auto_reboot_helper(config->fatal_auto_reboot_interval * 1000000); + bool check_vol_up = true, check_vol_down = true; GpioPadSession vol_up_btn, vol_down_btn; if (R_FAILED(gpioOpenSession(&vol_up_btn, GpioPadName_ButtonVolUp))) { @@ -121,6 +123,11 @@ void PowerButtonObserveTask::WaitForPowerButton() { GpioValue val; while (true) { Result rc = ResultSuccess; + + if (config->is_auto_reboot_enabled && auto_reboot_helper.TimedOut() ) { + bpcRebootSystem(); + return; + } if (check_vol_up && R_SUCCEEDED((rc = gpioPadGetValue(&vol_up_btn, &val))) && val == GpioValue_Low) { bpcRebootSystem(); From cb74bc6bb8b8fdbc3f08c6ad717181c98c9cacd7 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 22 Apr 2019 00:31:17 -0700 Subject: [PATCH 25/64] ro: fix UnloadNro logic error --- stratosphere/ro/source/ro_registration.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stratosphere/ro/source/ro_registration.cpp b/stratosphere/ro/source/ro_registration.cpp index e7ea8543d..a179a8cff 100644 --- a/stratosphere/ro/source/ro_registration.cpp +++ b/stratosphere/ro/source/ro_registration.cpp @@ -362,11 +362,11 @@ Result Registration::UnloadNro(RoProcessContext *context, u64 nro_address) { continue; } - if (context->nro_infos[slot].nro_heap_address == nro_address) { + if (context->nro_infos[slot].base_address == nro_address) { break; } } - if (slot == Registration::MaxNrrInfos) { + if (slot == Registration::MaxNroInfos) { return ResultRoNotLoaded; } From bfd04cfe921101ff8f7564f62d01a94d1236f714 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 22 Apr 2019 00:40:19 -0700 Subject: [PATCH 26/64] loader: remove more unused ro functionality --- stratosphere/loader/source/ldr_map.cpp | 73 -------------------- stratosphere/loader/source/ldr_map.hpp | 94 -------------------------- 2 files changed, 167 deletions(-) diff --git a/stratosphere/loader/source/ldr_map.cpp b/stratosphere/loader/source/ldr_map.cpp index 3751fd592..468a57a0b 100644 --- a/stratosphere/loader/source/ldr_map.cpp +++ b/stratosphere/loader/source/ldr_map.cpp @@ -27,15 +27,6 @@ Result MapUtils::LocateSpaceForMap(u64 *out, u64 out_size) { } } - -Result MapUtils::MapCodeMemoryForProcess(Handle process_h, bool is_64_bit_address_space, u64 base_address, u64 size, u64 *out_code_memory_address) { - if (kernelAbove200()) { - return MapCodeMemoryForProcessModern(process_h, base_address, size, out_code_memory_address); - } else { - return MapCodeMemoryForProcessDeprecated(process_h, is_64_bit_address_space, base_address, size, out_code_memory_address); - } -} - Result MapUtils::LocateSpaceForMapModern(u64 *out, u64 out_size) { MemoryInfo mem_info = {}; AddressSpaceInfo address_space = {}; @@ -126,70 +117,6 @@ Result MapUtils::LocateSpaceForMapDeprecated(u64 *out, u64 out_size) { return rc; } -Result MapUtils::MapCodeMemoryForProcessModern(Handle process_h, u64 base_address, u64 size, u64 *out_code_memory_address) { - AddressSpaceInfo address_space = {}; - Result rc; - - if (R_FAILED((rc = GetAddressSpaceInfo(&address_space, process_h)))) { - return rc; - } - - if (size > address_space.addspace_size) { - return ResultLoaderInsufficientAddressSpace; - } - - u64 try_address; - for (unsigned int i = 0; i < 0x200; i++) { - while (true) { - try_address = address_space.addspace_base + (StratosphereRandomUtils::GetRandomU64((u64)(address_space.addspace_size - size) >> 12) << 12); - if (address_space.heap_size && (address_space.heap_base <= try_address + size - 1 && try_address <= address_space.heap_end - 1)) { - continue; - } - if (address_space.map_size && (address_space.map_base <= try_address + size - 1 && try_address <= address_space.map_end - 1)) { - continue; - } - break; - } - rc = svcMapProcessCodeMemory(process_h, try_address, base_address, size); - if (rc != ResultKernelInvalidMemoryState) { - break; - } - } - if (R_SUCCEEDED(rc)) { - *out_code_memory_address = try_address; - } - return rc; -} - -Result MapUtils::MapCodeMemoryForProcessDeprecated(Handle process_h, bool is_64_bit_address_space, u64 base_address, u64 size, u64 *out_code_memory_address) { - Result rc; - u64 addspace_base, addspace_size; - if (is_64_bit_address_space) { - addspace_base = 0x8000000ULL; - addspace_size = 0x78000000ULL; - } else { - addspace_base = 0x200000ULL; - addspace_size = 0x3FE0000ULL; - } - - if (size > addspace_size) { - return ResultLoaderInsufficientAddressSpace; - } - - u64 try_address; - for (unsigned int i = 0; i < 0x200; i++) { - try_address = addspace_base + (StratosphereRandomUtils::GetRandomU64((u64)(addspace_size - size) >> 12) << 12); - rc = svcMapProcessCodeMemory(process_h, try_address, base_address, size); - if (rc != ResultKernelInvalidMemoryState) { - break; - } - } - if (R_SUCCEEDED(rc)) { - *out_code_memory_address = try_address; - } - return rc; -} - Result MapUtils::GetAddressSpaceInfo(AddressSpaceInfo *out, Handle process_h) { Result rc; if (R_FAILED((rc = svcGetInfo(&out->heap_base, 4, process_h, 0)))) { diff --git a/stratosphere/loader/source/ldr_map.hpp b/stratosphere/loader/source/ldr_map.hpp index 134d89a2f..c9a83b608 100644 --- a/stratosphere/loader/source/ldr_map.hpp +++ b/stratosphere/loader/source/ldr_map.hpp @@ -35,11 +35,6 @@ class MapUtils { static Result LocateSpaceForMapDeprecated(u64 *out, u64 out_size); static Result LocateSpaceForMapModern(u64 *out, u64 out_size); static Result LocateSpaceForMap(u64 *out, u64 out_size); - - - static Result MapCodeMemoryForProcessDeprecated(Handle process_h, bool is_64_bit_address_space, u64 base_address, u64 size, u64 *out_code_memory_address); - static Result MapCodeMemoryForProcessModern(Handle process_h, u64 base_address, u64 size, u64 *out_code_memory_address); - static Result MapCodeMemoryForProcess(Handle process_h, bool is_64_bit_address_space, u64 base_address, u64 size, u64 *out_code_memory_address); }; class AutoCloseMap { @@ -84,92 +79,3 @@ class AutoCloseMap { } } }; - -struct MappedCodeMemory { - Handle process_handle; - u64 base_address; - u64 size; - u64 code_memory_address; - void *mapped_address; - - bool IsActive() { - return this->code_memory_address != 0; - } - - bool IsMapped() { - return this->mapped_address != NULL; - } - - /* Utility functions. */ - Result Open(Handle process_h, bool is_64_bit_address_space, u64 address, u64 size) { - Result rc; - if (this->IsActive()) { - return ResultLoaderInternalError; - } - - this->process_handle = process_h; - this->base_address = address; - this->size = size; - - if (R_FAILED((rc = MapUtils::MapCodeMemoryForProcess(this->process_handle, is_64_bit_address_space, this->base_address, this->size, &this->code_memory_address)))) { - Close(); - } - return rc; - } - - Result OpenAtAddress(Handle process_h, u64 address, u64 size, u64 target_code_memory_address) { - Result rc; - if (this->IsActive()) { - return ResultLoaderInternalError; - } - this->process_handle = process_h; - this->base_address = address; - this->size = size; - - if (R_SUCCEEDED((rc = svcMapProcessCodeMemory(this->process_handle, target_code_memory_address, this->base_address, this->size)))) { - this->code_memory_address = target_code_memory_address; - } else { - Close(); - } - return rc; - } - - Result Map() { - Result rc; - u64 try_address; - if (this->IsMapped()) { - return ResultLoaderInternalError; - } - if (R_FAILED(rc = MapUtils::LocateSpaceForMap(&try_address, size))) { - return rc; - } - - if (R_FAILED((rc = svcMapProcessMemory((void *)try_address, this->process_handle, this->code_memory_address, size)))) { - return rc; - } - - this->mapped_address = (void *)try_address; - return rc; - } - - Result Unmap() { - Result rc = ResultSuccess; - if (this->IsMapped()) { - if (R_FAILED((rc = svcUnmapProcessMemory(this->mapped_address, this->process_handle, this->code_memory_address, this->size)))) { - /* TODO: panic(). */ - } - } - this->mapped_address = NULL; - return rc; - } - - void Close() { - Unmap(); - if (this->IsActive()) { - if (R_FAILED(svcUnmapProcessCodeMemory(this->process_handle, this->code_memory_address, this->base_address, this->size))) { - /* TODO: panic(). */ - } - } - *this = {}; - } -}; From f50bfaf7d74b643defc27dbff4e82581cf0e0ddb Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 22 Apr 2019 01:54:44 -0700 Subject: [PATCH 27/64] dist: add boot2.flag for ro (to launch on <3.0.0) --- Makefile | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 384f76d63..a380a5d94 100644 --- a/Makefile +++ b/Makefile @@ -50,13 +50,13 @@ dist: all mkdir atmosphere-$(AMSVER)/atmosphere mkdir atmosphere-$(AMSVER)/sept mkdir atmosphere-$(AMSVER)/switch - mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000037 - mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000036 - mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000034 + mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/010000000000000D mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032 + mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000034 + mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000036 + mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000037 mkdir -p atmosphere-$(AMSVER)/atmosphere/fatal_errors cp fusee/fusee-primary/fusee-primary.bin atmosphere-$(AMSVER)/atmosphere/reboot_payload.bin - mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/010000000000000D cp fusee/fusee-secondary/fusee-secondary.bin atmosphere-$(AMSVER)/atmosphere/fusee-secondary.bin cp fusee/fusee-secondary/fusee-secondary.bin atmosphere-$(AMSVER)/sept/payload.bin cp sept/sept-primary/sept-primary.bin atmosphere-$(AMSVER)/sept/sept-primary.bin @@ -67,14 +67,16 @@ dist: all cp common/defaults/system_settings.ini atmosphere-$(AMSVER)/atmosphere/system_settings.ini cp -r common/defaults/kip_patches atmosphere-$(AMSVER)/atmosphere/kip_patches cp -r common/defaults/hbl_html atmosphere-$(AMSVER)/atmosphere/hbl_html - cp stratosphere/creport/creport.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000036/exefs.nsp - cp stratosphere/fatal/fatal.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000034/exefs.nsp + cp stratosphere/dmnt/dmnt.nsp atmosphere-$(AMSVER)/atmosphere/titles/010000000000000D/exefs.nsp cp stratosphere/eclct.stub/eclct.stub.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/exefs.nsp + cp stratosphere/fatal/fatal.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000034/exefs.nsp + cp stratosphere/creport/creport.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000036/exefs.nsp cp stratosphere/ro/ro.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000037/exefs.nsp - cp troposphere/reboot_to_payload/reboot_to_payload.nro atmosphere-$(AMSVER)/switch/reboot_to_payload.nro mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/flags touch atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/flags/boot2.flag - cp stratosphere/dmnt/dmnt.nsp atmosphere-$(AMSVER)/atmosphere/titles/010000000000000D/exefs.nsp + mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000037/flags + touch atmosphere-$(AMSVER)/atmosphere/titles/0100000000000037/flags/boot2.flag + cp troposphere/reboot_to_payload/reboot_to_payload.nro atmosphere-$(AMSVER)/switch/reboot_to_payload.nro cd atmosphere-$(AMSVER); zip -r ../atmosphere-$(AMSVER).zip ./*; cd ../; rm -r atmosphere-$(AMSVER) mkdir out From 9f972831cccb0063ee8ba2b080d8ded0143b88d6 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 22 Apr 2019 03:42:20 -0700 Subject: [PATCH 28/64] fs.mitm: fix data abort --- stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp index 517775854..0421ffb00 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp @@ -270,7 +270,8 @@ Result FsMitmService::OpenBisStorage(Out> out } else { /* Do not allow non-sysmodules to read *or* write CAL0. */ fsStorageClose(&bis_storage); - return ResultFsPermissionDenied; + rc = ResultFsPermissionDenied; + return rc; } } else { if (is_sysmodule || has_bis_write_flag) { From a1d4caa7b4e31896bb4b2db510a7cfbac0083d74 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 22 Apr 2019 04:32:15 -0700 Subject: [PATCH 29/64] ro: add support for applying ips patches to NROs. --- stratosphere/ro/source/ro_patcher.cpp | 188 +++++++++++++++++++++ stratosphere/ro/source/ro_patcher.hpp | 26 +++ stratosphere/ro/source/ro_registration.cpp | 4 + stratosphere/ro/source/ro_registration.hpp | 35 +--- stratosphere/ro/source/ro_types.hpp | 42 +++++ 5 files changed, 266 insertions(+), 29 deletions(-) create mode 100644 stratosphere/ro/source/ro_patcher.cpp create mode 100644 stratosphere/ro/source/ro_patcher.hpp create mode 100644 stratosphere/ro/source/ro_types.hpp diff --git a/stratosphere/ro/source/ro_patcher.cpp b/stratosphere/ro/source/ro_patcher.cpp new file mode 100644 index 000000000..355a7d2ce --- /dev/null +++ b/stratosphere/ro/source/ro_patcher.cpp @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include +#include "ro_patcher.hpp" +#include "ro_registration.hpp" + +/* IPS Patching adapted from Luma3DS (https://github.com/AuroraWright/Luma3DS/blob/master/sysmodules/loader/source/patcher.c) */ + +#define IPS_MAGIC "PATCH" +#define IPS_TAIL "EOF" + +#define IPS32_MAGIC "IPS32" +#define IPS32_TAIL "EEOF" + +static inline u8 HexNybbleToU8(const char nybble) { + if ('0' <= nybble && nybble <= '9') { + return nybble - '0'; + } else if ('a' <= nybble && nybble <= 'f') { + return nybble - 'a' + 0xa; + } else { + return nybble - 'A' + 0xA; + } +} + +static bool MatchesBuildId(const char *name, size_t name_len, const u8 *build_id) { + /* Validate name is hex build id. */ + for (unsigned int i = 0; i < name_len - 4; i++) { + if (isxdigit(name[i]) == 0) { + return false; + } + } + + /* Read build id from name. */ + u8 build_id_from_name[0x20] = {0}; + for (unsigned int name_ofs = 0, id_ofs = 0; name_ofs < name_len - 4; id_ofs++) { + build_id_from_name[id_ofs] |= HexNybbleToU8(name[name_ofs++]) << 4; + build_id_from_name[id_ofs] |= HexNybbleToU8(name[name_ofs++]); + } + + return memcmp(build_id, build_id_from_name, sizeof(build_id_from_name)) == 0; +} + +static void ApplyIpsPatch(u8 *mapped_nro, size_t mapped_size, bool is_ips32, FILE *f_ips) { + u8 buffer[4]; + while (fread(buffer, is_ips32 ? 4 : 3, 1, f_ips) == 1) { + if (is_ips32 && memcmp(buffer, IPS32_TAIL, 4) == 0) { + break; + } else if (!is_ips32 && memcmp(buffer, IPS_TAIL, 3) == 0) { + break; + } + + /* Offset of patch. */ + u32 patch_offset; + if (is_ips32) { + patch_offset = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; + } else { + patch_offset = (buffer[0] << 16) | (buffer[1] << 8) | (buffer[2]); + } + + /* Size of patch. */ + if (fread(buffer, 2, 1, f_ips) != 1) { + break; + } + u32 patch_size = (buffer[0] << 8) | (buffer[1]); + + /* Check for RLE encoding. */ + if (patch_size == 0) { + /* Size of RLE. */ + if (fread(buffer, 2, 1, f_ips) != 1) { + break; + } + + u32 rle_size = (buffer[0] << 8) | (buffer[1]); + + /* Value for RLE. */ + if (fread(buffer, 1, 1, f_ips) != 1) { + break; + } + + if (patch_offset < sizeof(Registration::NroHeader)) { + if (patch_offset + rle_size > sizeof(Registration::NroHeader)) { + u32 diff = sizeof(Registration::NroHeader) - patch_offset; + patch_offset += diff; + rle_size -= diff; + goto IPS_RLE_PATCH_OFFSET_WITHIN_BOUNDS; + } + } else { + IPS_RLE_PATCH_OFFSET_WITHIN_BOUNDS: + patch_offset -= sizeof(Registration::NroHeader); + if (patch_offset + rle_size > mapped_size) { + rle_size = mapped_size - patch_offset; + } + memset(mapped_nro + patch_offset, buffer[0], rle_size); + } + } else { + if (patch_offset < sizeof(Registration::NroHeader)) { + if (patch_offset + patch_size > sizeof(Registration::NroHeader)) { + u32 diff = sizeof(Registration::NroHeader) - patch_offset; + patch_offset += diff; + patch_size -= diff; + fseek(f_ips, diff, SEEK_CUR); + goto IPS_DATA_PATCH_OFFSET_WITHIN_BOUNDS; + } else { + fseek(f_ips, patch_size, SEEK_CUR); + } + } else { + IPS_DATA_PATCH_OFFSET_WITHIN_BOUNDS: + patch_offset -= sizeof(Registration::NroHeader); + u32 read_size = patch_size; + if (patch_offset + read_size > mapped_size) { + read_size = mapped_size - patch_offset; + } + if (fread(mapped_nro + patch_offset, read_size, 1, f_ips) != 1) { + break; + } + if (patch_size > read_size) { + fseek(f_ips, patch_size - read_size, SEEK_CUR); + } + } + } + } +} + +void PatchUtils::ApplyPatches(const ModuleId *module_id, u8 *mapped_nro, size_t mapped_size) { + /* Inspect all patches from /atmosphere/nro_patches/<*>/<*>.ips */ + char path[FS_MAX_PATH+1] = {0}; + snprintf(path, sizeof(path) - 1, "sdmc:/atmosphere/nro_patches"); + DIR *patches_dir = opendir(path); + struct dirent *pdir_ent; + if (patches_dir != NULL) { + /* Iterate over the patches directory to find patch subdirectories. */ + while ((pdir_ent = readdir(patches_dir)) != NULL) { + if (strcmp(pdir_ent->d_name, ".") == 0 || strcmp(pdir_ent->d_name, "..") == 0) { + continue; + } + snprintf(path, sizeof(path) - 1, "sdmc:/atmosphere/nro_patches/%s", pdir_ent->d_name); + DIR *patch_dir = opendir(path); + struct dirent *ent; + if (patch_dir != NULL) { + /* Iterate over the patch subdirectory to find .ips patches. */ + while ((ent = readdir(patch_dir)) != NULL) { + if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) { + continue; + } + size_t name_len = strlen(ent->d_name); + if ((4 < name_len && name_len <= 0x44) && ((name_len & 1) == 0) && strcmp(ent->d_name + name_len - 4, ".ips") == 0 && MatchesBuildId(ent->d_name, name_len, module_id->build_id)) { + snprintf(path, sizeof(path) - 1, "sdmc:/atmosphere/nro_patches/%s/%s", pdir_ent->d_name, ent->d_name); + FILE *f_ips = fopen(path, "rb"); + if (f_ips != NULL) { + u8 header[5]; + if (fread(header, 5, 1, f_ips) == 1) { + if (memcmp(header, IPS_MAGIC, 5) == 0) { + ApplyIpsPatch(mapped_nro, mapped_size, false, f_ips); + } else if (memcmp(header, IPS32_MAGIC, 5) == 0) { + ApplyIpsPatch(mapped_nro, mapped_size, true, f_ips); + } + } + fclose(f_ips); + } + } + } + closedir(patch_dir); + } + } + closedir(patches_dir); + } +} \ No newline at end of file diff --git a/stratosphere/ro/source/ro_patcher.hpp b/stratosphere/ro/source/ro_patcher.hpp new file mode 100644 index 000000000..ed88c7223 --- /dev/null +++ b/stratosphere/ro/source/ro_patcher.hpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +#include "ro_types.hpp" + +class PatchUtils { + public: + static void ApplyPatches(const ModuleId *module_id, u8 *mapped_nro, size_t mapped_size); +}; \ No newline at end of file diff --git a/stratosphere/ro/source/ro_registration.cpp b/stratosphere/ro/source/ro_registration.cpp index a179a8cff..49c090030 100644 --- a/stratosphere/ro/source/ro_registration.cpp +++ b/stratosphere/ro/source/ro_registration.cpp @@ -22,6 +22,7 @@ #include "ro_registration.hpp" #include "ro_map.hpp" #include "ro_nrr.hpp" +#include "ro_patcher.hpp" /* Declare process contexts as global array. */ static Registration::RoProcessContext g_process_contexts[Registration::MaxSessions] = {}; @@ -323,6 +324,9 @@ Result Registration::ValidateNro(ModuleId *out_module_id, u64 *out_rx_size, u64 } } + /* Apply patches to NRO. */ + PatchUtils::ApplyPatches(&module_id, reinterpret_cast(map_address), nro_size); + *out_module_id = module_id; *out_rx_size = header->text_size; *out_ro_size = header->ro_size; diff --git a/stratosphere/ro/source/ro_registration.hpp b/stratosphere/ro/source/ro_registration.hpp index ca34791ee..f416534f1 100644 --- a/stratosphere/ro/source/ro_registration.hpp +++ b/stratosphere/ro/source/ro_registration.hpp @@ -13,24 +13,24 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + #pragma once #include #include #include "ro_nrr.hpp" +#include "ro_types.hpp" class Registration { public: /* NOTE: 2 ldr:ro, 2 ro:1. Nintendo only actually supports 2 total, but we'll be a little more generous. */ - static constexpr size_t MaxSessions = 0x4; + static constexpr size_t MaxSessions = 0x4; static constexpr size_t MaxNrrInfos = 0x40; static constexpr size_t MaxNroInfos = 0x40; - + static constexpr u32 MagicNro0 = 0x304F524E; public: - struct NroHeader { u32 entrypoint_insn; u32 mod_offset; @@ -52,29 +52,6 @@ class Registration { }; static_assert(sizeof(NroHeader) == 0x80, "NroHeader definition!"); - struct ModuleId { - u8 build_id[0x20]; - }; - static_assert(sizeof(ModuleId) == sizeof(LoaderModuleInfo::build_id), "ModuleId definition!"); - - struct Sha256Hash { - u8 hash[0x20]; - - bool operator==(const Sha256Hash &o) const { - return std::memcmp(this, &o, sizeof(*this)) == 0; - } - bool operator!=(const Sha256Hash &o) const { - return std::memcmp(this, &o, sizeof(*this)) != 0; - } - bool operator<(const Sha256Hash &o) const { - return std::memcmp(this, &o, sizeof(*this)) < 0; - } - bool operator>(const Sha256Hash &o) const { - return std::memcmp(this, &o, sizeof(*this)) > 0; - } - }; - static_assert(sizeof(Sha256Hash) == sizeof(Sha256Hash::hash), "Sha256Hash definition!"); - struct NroInfo { u64 base_address; u64 nro_heap_address; @@ -104,7 +81,7 @@ class Registration { static Result MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, Handle process_handle, u64 title_id, u64 nrr_heap_address, u64 nrr_heap_size, RoModuleType expected_type, bool enforce_type); static Result UnmapNrr(Handle process_handle, const NrrHeader *header, u64 nrr_heap_address, u64 nrr_heap_size, u64 mapped_code_address); static bool IsNroHashPresent(RoProcessContext *context, const Sha256Hash *hash); - + static Result MapNro(u64 *out_base_address, Handle process_handle, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size); static Result ValidateNro(ModuleId *out_module_id, u64 *out_rx_size, u64 *out_ro_size, u64 *out_rw_size, RoProcessContext *context, u64 base_address, u64 nro_size, u64 bss_size); static Result SetNroPerms(Handle process_handle, u64 base_address, u64 rx_size, u64 ro_size, u64 rw_size); @@ -115,7 +92,7 @@ class Registration { static Result RegisterProcess(RoProcessContext **out_context, Handle process_handle, u64 process_id); static void UnregisterProcess(RoProcessContext *context); - + static Result LoadNrr(RoProcessContext *context, u64 title_id, u64 nrr_address, u64 nrr_size, RoModuleType expected_type, bool enforce_type); static Result UnloadNrr(RoProcessContext *context, u64 nrr_address); static Result LoadNro(u64 *out_address, RoProcessContext *context, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size); diff --git a/stratosphere/ro/source/ro_types.hpp b/stratosphere/ro/source/ro_types.hpp new file mode 100644 index 000000000..b6473adb1 --- /dev/null +++ b/stratosphere/ro/source/ro_types.hpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +struct ModuleId { + u8 build_id[0x20]; +}; +static_assert(sizeof(ModuleId) == sizeof(LoaderModuleInfo::build_id), "ModuleId definition!"); + +struct Sha256Hash { + u8 hash[0x20]; + + bool operator==(const Sha256Hash &o) const { + return std::memcmp(this, &o, sizeof(*this)) == 0; + } + bool operator!=(const Sha256Hash &o) const { + return std::memcmp(this, &o, sizeof(*this)) != 0; + } + bool operator<(const Sha256Hash &o) const { + return std::memcmp(this, &o, sizeof(*this)) < 0; + } + bool operator>(const Sha256Hash &o) const { + return std::memcmp(this, &o, sizeof(*this)) > 0; + } +}; +static_assert(sizeof(Sha256Hash) == sizeof(Sha256Hash::hash), "Sha256Hash definition!"); From c3875796dfbdca8dcbaeaa0be4005d1badfca5fb Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 22 Apr 2019 09:50:45 -0700 Subject: [PATCH 30/64] dmnt: update for ldr/ro api change --- stratosphere/dmnt/source/dmnt_cheat_manager.cpp | 4 ++-- stratosphere/dmnt/source/dmnt_main.cpp | 12 ++++-------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/stratosphere/dmnt/source/dmnt_cheat_manager.cpp b/stratosphere/dmnt/source/dmnt_cheat_manager.cpp index 0c5ac6f22..42cb5c48f 100644 --- a/stratosphere/dmnt/source/dmnt_cheat_manager.cpp +++ b/stratosphere/dmnt/source/dmnt_cheat_manager.cpp @@ -886,7 +886,7 @@ Result DmntCheatManager::ForceOpenCheatProcess() { { LoaderModuleInfo proc_modules[2]; u32 num_modules; - if (R_FAILED((rc = ldrDmntGetModuleInfos(g_cheat_process_metadata.process_id, proc_modules, sizeof(proc_modules), &num_modules)))) { + if (R_FAILED((rc = ldrDmntGetModuleInfos(g_cheat_process_metadata.process_id, proc_modules, sizeof(proc_modules)/sizeof(proc_modules[0]), &num_modules)))) { return rc; } @@ -981,7 +981,7 @@ void DmntCheatManager::OnNewApplicationLaunch() { { LoaderModuleInfo proc_modules[2]; u32 num_modules; - if (R_FAILED((rc = ldrDmntGetModuleInfos(g_cheat_process_metadata.process_id, proc_modules, sizeof(proc_modules), &num_modules)))) { + if (R_FAILED((rc = ldrDmntGetModuleInfos(g_cheat_process_metadata.process_id, proc_modules, sizeof(proc_modules)/sizeof(proc_modules[0]), &num_modules)))) { fatalSimple(rc); } diff --git a/stratosphere/dmnt/source/dmnt_main.cpp b/stratosphere/dmnt/source/dmnt_main.cpp index 77571d4ed..eb864e8ce 100644 --- a/stratosphere/dmnt/source/dmnt_main.cpp +++ b/stratosphere/dmnt/source/dmnt_main.cpp @@ -86,14 +86,10 @@ void __appInit(void) { fatalSimple(rc); } - /* - if (kernelAbove300()) { - rc = roDmntInitialize(); - if (R_FAILED(rc)) { - fatalSimple(rc); - } + rc = roDmntInitialize(); + if (R_FAILED(rc)) { + fatalSimple(rc); } - */ rc = nsdevInitialize(); if (R_FAILED(rc)) { @@ -142,7 +138,7 @@ void __appExit(void) { setExit(); lrExit(); nsdevExit(); - /* if (kernelAbove300()) { roDmntExit(); } */ + roDmntExit(); ldrDmntExit(); pmdmntExit(); smExit(); From b09adb6a34f41c127e6171027e11aed489dd15a0 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 22 Apr 2019 12:40:53 -0700 Subject: [PATCH 31/64] stratosphere: only hold sm sessions open when needed --- stratosphere/ams_mitm/source/amsmitm_main.cpp | 20 +-- .../ams_mitm/source/ns_mitm/nsmitm_main.cpp | 4 +- stratosphere/ams_mitm/source/utils.cpp | 36 +++-- stratosphere/boot/source/boot_main.cpp | 46 +++--- .../creport/source/creport_crash_report.cpp | 25 ++- stratosphere/creport/source/creport_main.cpp | 26 ++-- stratosphere/dmnt/source/dmnt_main.cpp | 122 ++++++++------- stratosphere/fatal/source/fatal_main.cpp | 142 +++++++++--------- .../fatal/source/fatal_task_error_report.cpp | 25 ++- .../fatal/source/fatal_task_screen.cpp | 93 ++++++------ stratosphere/libstratosphere | 2 +- .../loader/source/ldr_content_management.cpp | 31 ++-- stratosphere/loader/source/ldr_hid.cpp | 18 ++- stratosphere/loader/source/ldr_main.cpp | 51 +++---- stratosphere/pm/source/pm_boot2.cpp | 85 ++++++----- stratosphere/pm/source/pm_main.cpp | 109 +++++++------- 16 files changed, 432 insertions(+), 403 deletions(-) diff --git a/stratosphere/ams_mitm/source/amsmitm_main.cpp b/stratosphere/ams_mitm/source/amsmitm_main.cpp index d244d3246..911c5e561 100644 --- a/stratosphere/ams_mitm/source/amsmitm_main.cpp +++ b/stratosphere/ams_mitm/source/amsmitm_main.cpp @@ -73,15 +73,12 @@ void __appInit(void) { SetFirmwareVersionForLibnx(); - rc = smInitialize(); - if (R_FAILED(rc)) { - fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_SM)); - } - - rc = fsInitialize(); - if (R_FAILED(rc)) { - fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_FS)); - } + DoWithSmSession([&]() { + rc = fsInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + }); CheckAtmosphereVersion(CURRENT_ATMOSPHERE_VERSION); } @@ -89,7 +86,6 @@ void __appInit(void) { void __appExit(void) { /* Cleanup services. */ fsExit(); - smExit(); } int main(int argc, char **argv) @@ -100,10 +96,10 @@ int main(int argc, char **argv) LaunchAllMitmModules(); if (R_FAILED(initializer_thread.Initialize(&Utils::InitializeThreadFunc, NULL, 0x4000, 0x15))) { - /* TODO: Panic. */ + std::abort(); } if (R_FAILED(initializer_thread.Start())) { - /* TODO: Panic. */ + std::abort(); } /* Wait for all mitm modules to end. */ diff --git a/stratosphere/ams_mitm/source/ns_mitm/nsmitm_main.cpp b/stratosphere/ams_mitm/source/ns_mitm/nsmitm_main.cpp index 92a9c632b..256ca76e1 100644 --- a/stratosphere/ams_mitm/source/ns_mitm/nsmitm_main.cpp +++ b/stratosphere/ams_mitm/source/ns_mitm/nsmitm_main.cpp @@ -35,12 +35,12 @@ void NsMitmMain(void *arg) { Utils::WaitSdInitialized(); /* Ensure we can talk to NS. */ - { + DoWithSmSession([&]() { if (R_FAILED(nsInitialize())) { std::abort(); } nsExit(); - } + }); /* Create server manager */ auto server_manager = new WaitableManager(1); diff --git a/stratosphere/ams_mitm/source/utils.cpp b/stratosphere/ams_mitm/source/utils.cpp index 5d50fa178..7c406495a 100644 --- a/stratosphere/ams_mitm/source/utils.cpp +++ b/stratosphere/ams_mitm/source/utils.cpp @@ -78,15 +78,17 @@ static bool IsHexadecimal(const char *str) { void Utils::InitializeThreadFunc(void *args) { /* Get required services. */ - Handle tmp_hnd = 0; - static const char * const required_active_services[] = {"pcv", "gpio", "pinmux", "psc:c"}; - for (unsigned int i = 0; i < sizeof(required_active_services) / sizeof(required_active_services[0]); i++) { - if (R_FAILED(smGetServiceOriginal(&tmp_hnd, smEncodeName(required_active_services[i])))) { - /* TODO: Panic */ - } else { - svcCloseHandle(tmp_hnd); + DoWithSmSession([&]() { + Handle tmp_hnd = 0; + static const char * const required_active_services[] = {"pcv", "gpio", "pinmux", "psc:c"}; + for (unsigned int i = 0; i < sizeof(required_active_services) / sizeof(required_active_services[0]); i++) { + if (R_FAILED(smGetServiceOriginal(&tmp_hnd, smEncodeName(required_active_services[i])))) { + /* TODO: Panic */ + } else { + svcCloseHandle(tmp_hnd); + } } - } + }); /* Mount SD. */ while (R_FAILED(fsMountSdcard(&g_sd_filesystem))) { @@ -197,7 +199,11 @@ void Utils::InitializeThreadFunc(void *args) { Utils::RefreshConfiguration(); /* Initialize set:sys. */ - setsysInitialize(); + DoWithSmSession([&]() { + if (R_FAILED(setsysInitialize())) { + std::abort(); + } + }); /* Signal SD is initialized. */ g_has_initialized = true; @@ -209,13 +215,15 @@ void Utils::InitializeThreadFunc(void *args) { g_sd_signal.Signal(); /* Initialize HID. */ - { - - while (R_FAILED(hidInitialize())) { + while (!g_has_hid_session) { + DoWithSmSession([&]() { + if (R_SUCCEEDED(hidInitialize())) { + g_has_hid_session = true; + } + }); + if (!g_has_hid_session) { svcSleepThread(1000000ULL); } - - g_has_hid_session = true; } } diff --git a/stratosphere/boot/source/boot_main.cpp b/stratosphere/boot/source/boot_main.cpp index 862809d76..a1bd831af 100644 --- a/stratosphere/boot/source/boot_main.cpp +++ b/stratosphere/boot/source/boot_main.cpp @@ -80,30 +80,27 @@ void __appInit(void) { SetFirmwareVersionForLibnx(); /* Initialize services we need (TODO: NCM) */ - rc = smInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } - - rc = fsInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } - - rc = splInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } - - rc = pmshellInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } - - rc = fsdevMountSdmc(); - if (R_FAILED(rc)) { - std::abort(); - } + DoWithSmSession([&]() { + rc = fsInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + rc = splInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + rc = pmshellInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + rc = fsdevMountSdmc(); + if (R_FAILED(rc)) { + std::abort(); + } + }); CheckAtmosphereVersion(CURRENT_ATMOSPHERE_VERSION); } @@ -114,7 +111,6 @@ void __appExit(void) { pmshellExit(); splExit(); fsExit(); - smExit(); } typedef enum { diff --git a/stratosphere/creport/source/creport_crash_report.cpp b/stratosphere/creport/source/creport_crash_report.cpp index 908897161..e13982f4a 100644 --- a/stratosphere/creport/source/creport_crash_report.cpp +++ b/stratosphere/creport/source/creport_crash_report.cpp @@ -240,19 +240,28 @@ bool CrashReport::GetCurrentTime(u64 *out) { /* Verify that pcv isn't dead. */ { - Handle dummy; - if (R_SUCCEEDED(smRegisterService(&dummy, "time:s", false, 0x20))) { - svcCloseHandle(dummy); + bool has_time_service; + DoWithSmSession([&]() { + Handle dummy; + if (R_SUCCEEDED(smRegisterService(&dummy, "time:s", false, 0x20))) { + svcCloseHandle(dummy); + has_time_service = false; + } else { + has_time_service = true; + } + }); + if (!has_time_service) { return false; } } /* Try to get the current time. */ - bool success = false; - if (R_SUCCEEDED(timeInitialize())) { - if (R_SUCCEEDED(timeGetCurrentTime(TimeType_LocalSystemClock, out))) { - success = true; - } + bool success = true; + DoWithSmSession([&]() { + success &= R_SUCCEEDED(timeInitialize()); + }); + if (success) { + success &= R_SUCCEEDED(timeGetCurrentTime(TimeType_LocalSystemClock, out)); timeExit(); } return success; diff --git a/stratosphere/creport/source/creport_main.cpp b/stratosphere/creport/source/creport_main.cpp index 662705fbc..6d3423c18 100644 --- a/stratosphere/creport/source/creport_main.cpp +++ b/stratosphere/creport/source/creport_main.cpp @@ -68,16 +68,13 @@ void __appInit(void) { Result rc; SetFirmwareVersionForLibnx(); - - rc = smInitialize(); - if (R_FAILED(rc)) { - fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_SM)); - } - rc = fsInitialize(); - if (R_FAILED(rc)) { - fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_FS)); - } + DoWithSmSession([&]() { + rc = fsInitialize(); + if (R_FAILED(rc)) { + fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_FS)); + } + }); rc = fsdevMountSdmc(); if (R_FAILED(rc)) { @@ -89,7 +86,6 @@ void __appExit(void) { /* Cleanup services. */ fsdevUnmountAll(); fsExit(); - smExit(); } static u64 creport_parse_u64(char *s) { @@ -127,10 +123,12 @@ int main(int argc, char **argv) { if (g_Creport.WasSuccessful()) { g_Creport.SaveReport(); - if (R_SUCCEEDED(nsdevInitialize())) { - nsdevTerminateProcess(crashed_pid); - nsdevExit(); - } + DoWithSmSession([&]() { + if (R_SUCCEEDED(nsdevInitialize())) { + nsdevTerminateProcess(crashed_pid); + nsdevExit(); + } + }); /* Don't fatal if we have extra info. */ if (kernelAbove500()) { diff --git a/stratosphere/dmnt/source/dmnt_main.cpp b/stratosphere/dmnt/source/dmnt_main.cpp index eb864e8ce..6745a8673 100644 --- a/stratosphere/dmnt/source/dmnt_main.cpp +++ b/stratosphere/dmnt/source/dmnt_main.cpp @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + #include #include #include @@ -36,7 +36,7 @@ extern "C" { #define INNER_HEAP_SIZE 0x80000 size_t nx_inner_heap_size = INNER_HEAP_SIZE; char nx_inner_heap[INNER_HEAP_SIZE]; - + void __libnx_initheap(void); void __appInit(void); void __appExit(void); @@ -68,64 +68,61 @@ void __libnx_initheap(void) { void __appInit(void) { Result rc; - + SetFirmwareVersionForLibnx(); - - rc = smInitialize(); - if (R_FAILED(rc)) { - fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_SM)); - } - - rc = pmdmntInitialize(); - if (R_FAILED(rc)) { - fatalSimple(rc); - } - - rc = ldrDmntInitialize(); - if (R_FAILED(rc)) { - fatalSimple(rc); - } - - rc = roDmntInitialize(); - if (R_FAILED(rc)) { - fatalSimple(rc); - } - - rc = nsdevInitialize(); - if (R_FAILED(rc)) { - fatalSimple(rc); - } - - rc = lrInitialize(); - if (R_FAILED(rc)) { - fatalSimple(rc); - } - - rc = setInitialize(); - if (R_FAILED(rc)) { - fatalSimple(rc); - } - - rc = setsysInitialize(); - if (R_FAILED(rc)) { - fatalSimple(rc); - } - - rc = hidInitialize(); - if (R_FAILED(rc)) { - fatalSimple(rc); - } - - rc = fsInitialize(); - if (R_FAILED(rc)) { - fatalSimple(rc); - } - + + DoWithSmSession([&]() { + rc = pmdmntInitialize(); + if (R_FAILED(rc)) { + fatalSimple(rc); + } + + rc = ldrDmntInitialize(); + if (R_FAILED(rc)) { + fatalSimple(rc); + } + + rc = roDmntInitialize(); + if (R_FAILED(rc)) { + fatalSimple(rc); + } + + rc = nsdevInitialize(); + if (R_FAILED(rc)) { + fatalSimple(rc); + } + + rc = lrInitialize(); + if (R_FAILED(rc)) { + fatalSimple(rc); + } + + rc = setInitialize(); + if (R_FAILED(rc)) { + fatalSimple(rc); + } + + rc = setsysInitialize(); + if (R_FAILED(rc)) { + fatalSimple(rc); + } + + rc = hidInitialize(); + if (R_FAILED(rc)) { + fatalSimple(rc); + } + + rc = fsInitialize(); + if (R_FAILED(rc)) { + fatalSimple(rc); + } + }); + rc = fsdevMountSdmc(); if (R_FAILED(rc)) { fatalSimple(rc); } - + CheckAtmosphereVersion(CURRENT_ATMOSPHERE_VERSION); } @@ -141,28 +138,27 @@ void __appExit(void) { roDmntExit(); ldrDmntExit(); pmdmntExit(); - smExit(); } int main(int argc, char **argv) { consoleDebugInit(debugDevice_SVC); - + /* Initialize configuration manager. */ DmntConfigManager::RefreshConfiguration(); - + /* Start cheat manager. */ DmntCheatManager::InitializeCheatManager(); - + /* Nintendo uses four threads. Add a fifth for our cheat service. */ auto server_manager = new WaitableManager(5); - + /* Create services. */ - + /* TODO: Implement rest of dmnt:- in ams.tma development branch. */ /* server_manager->AddWaitable(new ServiceServer("dmnt:-", 4)); */ - - + + server_manager->AddWaitable(new ServiceServer("dmnt:cht", 1)); /* Loop forever, servicing our services. */ diff --git a/stratosphere/fatal/source/fatal_main.cpp b/stratosphere/fatal/source/fatal_main.cpp index 2f49576f7..4600b951d 100644 --- a/stratosphere/fatal/source/fatal_main.cpp +++ b/stratosphere/fatal/source/fatal_main.cpp @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + #include #include #include @@ -38,10 +38,10 @@ extern "C" { #define INNER_HEAP_SIZE 0x2A0000 size_t nx_inner_heap_size = INNER_HEAP_SIZE; char nx_inner_heap[INNER_HEAP_SIZE]; - + u32 __nx_nv_transfermem_size = 0x40000; ViLayerFlags __nx_vi_stray_layer_flags = (ViLayerFlags)0; - + void __libnx_initheap(void); void __appInit(void); void __appExit(void); @@ -73,79 +73,76 @@ void __libnx_initheap(void) { void __appInit(void) { Result rc; - + SetFirmwareVersionForLibnx(); - - rc = smInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } - - rc = setInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } - - rc = setsysInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } - - rc = pminfoInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } - - rc = i2cInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } - - rc = bpcInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } - - rc = pcvInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } - - rc = lblInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } - - rc = psmInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } - - rc = spsmInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } - - rc = plInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } - - rc = gpioInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } - - rc = fsInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } - + + DoWithSmSession([&]() { + rc = setInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + rc = setsysInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + rc = pminfoInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + rc = i2cInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + rc = bpcInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + rc = pcvInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + rc = lblInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + rc = psmInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + rc = spsmInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + rc = plInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + rc = gpioInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + rc = fsInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + }); + rc = fsdevMountSdmc(); if (R_FAILED(rc)) { std::abort(); } - + /* fatal cannot throw fatal, so don't do: CheckAtmosphereVersion(CURRENT_ATMOSPHERE_VERSION); */ } @@ -164,14 +161,13 @@ void __appExit(void) { pminfoExit(); setsysExit(); setExit(); - smExit(); } int main(int argc, char **argv) { /* Load settings from set:sys. */ InitializeFatalConfig(); - + /* Load shared font. */ if (R_FAILED(FontManager::InitializeSharedFont())) { std::abort(); diff --git a/stratosphere/fatal/source/fatal_task_error_report.cpp b/stratosphere/fatal/source/fatal_task_error_report.cpp index 869adcb43..9efcb3e12 100644 --- a/stratosphere/fatal/source/fatal_task_error_report.cpp +++ b/stratosphere/fatal/source/fatal_task_error_report.cpp @@ -38,19 +38,28 @@ bool ErrorReportTask::GetCurrentTime(u64 *out) { /* Verify that pcv isn't dead. */ { - Handle dummy; - if (R_SUCCEEDED(smRegisterService(&dummy, "time:s", false, 0x20))) { - svcCloseHandle(dummy); + bool has_time_service; + DoWithSmSession([&]() { + Handle dummy; + if (R_SUCCEEDED(smRegisterService(&dummy, "time:s", false, 0x20))) { + svcCloseHandle(dummy); + has_time_service = false; + } else { + has_time_service = true; + } + }); + if (!has_time_service) { return false; } } /* Try to get the current time. */ - bool success = false; - if (R_SUCCEEDED(timeInitialize())) { - if (R_SUCCEEDED(timeGetCurrentTime(TimeType_LocalSystemClock, out))) { - success = true; - } + bool success = true; + DoWithSmSession([&]() { + success &= R_SUCCEEDED(timeInitialize()); + }); + if (success) { + success &= R_SUCCEEDED(timeGetCurrentTime(TimeType_LocalSystemClock, out)); timeExit(); } return success; diff --git a/stratosphere/fatal/source/fatal_task_screen.cpp b/stratosphere/fatal/source/fatal_task_screen.cpp index d9d138f96..ab7937b6a 100644 --- a/stratosphere/fatal/source/fatal_task_screen.cpp +++ b/stratosphere/fatal/source/fatal_task_screen.cpp @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + #include #include @@ -55,17 +55,17 @@ Result ShowFatalTask::SetupDisplayInternal() { } /* Guarantee we close the display. */ ON_SCOPE_EXIT { viCloseDisplay(&display); }; - + /* Turn on the screen. */ if (R_FAILED((rc = viSetDisplayPowerState(&display, ViPowerState_On)))) { return rc; } - + /* Set alpha to 1.0f. */ if (R_FAILED((rc = viSetDisplayAlpha(&display, 1.0f)))) { return rc; } - + return rc; } @@ -82,54 +82,57 @@ Result ShowFatalTask::SetupDisplayExternal() { } /* Guarantee we close the display. */ ON_SCOPE_EXIT { viCloseDisplay(&display); }; - + /* Set alpha to 1.0f. */ if (R_FAILED((rc = viSetDisplayAlpha(&display, 1.0f)))) { return rc; } - + return rc; } Result ShowFatalTask::PrepareScreenForDrawing() { Result rc = ResultSuccess; - + /* Connect to vi. */ - if (R_FAILED((rc = viInitialize(ViServiceType_Manager)))) { + DoWithSmSession([&]() { + rc = viInitialize(ViServiceType_Manager); + }); + if (R_FAILED(rc)) { return rc; } - + /* Close other content. */ viSetContentVisibility(false); - + /* Setup the two displays. */ if (R_FAILED((rc = SetupDisplayInternal())) || R_FAILED((rc = SetupDisplayExternal()))) { return rc; } - + /* Open the default display. */ if (R_FAILED((rc = viOpenDefaultDisplay(&this->display)))) { return rc; } - + /* Reset the display magnification to its default value. */ u32 display_width, display_height; if (R_FAILED((rc = viGetDisplayLogicalResolution(&this->display, &display_width, &display_height)))) { return rc; } - + /* viSetDisplayMagnification was added in 3.0.0. */ if (GetRuntimeFirmwareVersion() >= FirmwareVersion_300) { if (R_FAILED((rc = viSetDisplayMagnification(&this->display, 0, 0, display_width, display_height)))) { return rc; } } - + /* Create layer to draw to. */ if (R_FAILED((rc = viCreateLayer(&this->display, &this->layer)))) { return rc; } - + /* Setup the layer. */ { /* Display a layer of 1280 x 720 at 1.5x magnification */ @@ -139,15 +142,15 @@ Result ShowFatalTask::PrepareScreenForDrawing() { constexpr u32 raw_height = FatalScreenHeight; constexpr u32 layer_width = ((raw_width) * 3) / 2; constexpr u32 layer_height = ((raw_height) * 3) / 2; - + const float layer_x = static_cast((display_width - layer_width) / 2); const float layer_y = static_cast((display_height - layer_height) / 2); u64 layer_z; - + if (R_FAILED((rc = viSetLayerSize(&this->layer, layer_width, layer_height)))) { return rc; } - + /* Set the layer's Z at display maximum, to be above everything else .*/ /* NOTE: Fatal hardcodes 100 here. */ if (R_SUCCEEDED((rc = viGetDisplayMaximumZ(&this->display, &layer_z)))) { @@ -155,12 +158,12 @@ Result ShowFatalTask::PrepareScreenForDrawing() { return rc; } } - + /* Center the layer in the screen. */ if (R_FAILED((rc = viSetLayerPosition(&this->layer, layer_x, layer_y)))) { return rc; } - + /* Create framebuffer. */ if (R_FAILED(rc = nwindowCreateFromLayer(&this->win, &this->layer))) { return rc; @@ -169,7 +172,7 @@ Result ShowFatalTask::PrepareScreenForDrawing() { return rc; } } - + return rc; } @@ -182,29 +185,29 @@ Result ShowFatalTask::ShowFatal() { *(volatile u32 *)(0xCAFEBABE) = rc; return rc; } - + /* Dequeue a buffer. */ u16 *tiled_buf = reinterpret_cast(framebufferBegin(&this->fb, NULL)); if (tiled_buf == nullptr) { return ResultFatalNullGraphicsBuffer; } - + /* Let the font manager know about our framebuffer. */ FontManager::ConfigureFontFramebuffer(tiled_buf, GetPixelOffset); FontManager::SetFontColor(0xFFFF); - + /* Draw a background. */ for (size_t i = 0; i < this->fb.fb_size / sizeof(*tiled_buf); i++) { tiled_buf[i] = 0x39C9; } - + /* Draw the atmosphere logo in the bottom right corner. */ for (size_t y = 0; y < AMS_LOGO_HEIGHT; y++) { for (size_t x = 0; x < AMS_LOGO_WIDTH; x++) { tiled_buf[GetPixelOffset(FatalScreenWidth - AMS_LOGO_WIDTH - 32 + x, 32 + y)] = AMS_LOGO_BIN[y * AMS_LOGO_WIDTH + x]; } } - + /* TODO: Actually draw meaningful shit here. */ FontManager::SetPosition(32, 64); FontManager::SetFontSize(16.0f); @@ -225,18 +228,18 @@ Result ShowFatalTask::ShowFatal() { u8"Please ensure that all Atmosphère components are updated.\n" u8"github.com/Atmosphere-NX/Atmosphere/releases\n"); } - + /* Add a line. */ for (size_t x = 32; x < FatalScreenWidth - 32; x++) { tiled_buf[GetPixelOffset(x, FontManager::GetY())] = 0xFFFF; } - - + + FontManager::AddSpacingLines(1.5f); - + u32 backtrace_y = FontManager::GetY(); u32 backtrace_x = 0; - + /* Print GPRs. */ FontManager::SetFontSize(14.0f); FontManager::Print("General Purpose Registers "); @@ -278,7 +281,7 @@ Result ShowFatalTask::ShowFatal() { FontManager::Print(" "); backtrace_x = FontManager::GetX(); } - + FontManager::PrintLine(""); FontManager::SetPosition(32, FontManager::GetY()); } @@ -306,12 +309,12 @@ Result ShowFatalTask::ShowFatal() { FontManager::Print(" "); backtrace_x = FontManager::GetX(); } - + FontManager::PrintLine(""); FontManager::SetPosition(32, FontManager::GetY()); } } - + /* Print Backtrace. */ u32 bt_size; if (this->ctx->cpu_ctx.is_aarch32) { @@ -319,8 +322,8 @@ Result ShowFatalTask::ShowFatal() { } else { bt_size = this->ctx->cpu_ctx.aarch64_ctx.stack_trace_size; } - - + + FontManager::SetPosition(backtrace_x, backtrace_y); if (bt_size == 0) { if (this->ctx->cpu_ctx.is_aarch32) { @@ -346,7 +349,7 @@ Result ShowFatalTask::ShowFatal() { if (i + Aarch32CpuContext::MaxStackTraceDepth / 2 < this->ctx->cpu_ctx.aarch32_ctx.stack_trace_size) { bt_next = this->ctx->cpu_ctx.aarch32_ctx.stack_trace[i + Aarch32CpuContext::MaxStackTraceDepth / 2]; } - + if (i < this->ctx->cpu_ctx.aarch32_ctx.stack_trace_size) { u32 x = FontManager::GetX(); FontManager::PrintFormat("BT[%02d]: ", i); @@ -354,14 +357,14 @@ Result ShowFatalTask::ShowFatal() { FontManager::PrintMonospaceU32(bt_cur); FontManager::Print(" "); } - + if (i + Aarch32CpuContext::MaxStackTraceDepth / 2 < this->ctx->cpu_ctx.aarch32_ctx.stack_trace_size) { u32 x = FontManager::GetX(); FontManager::PrintFormat("BT[%02d]: ", i + Aarch32CpuContext::MaxStackTraceDepth / 2); FontManager::SetPosition(x + 72, FontManager::GetY()); FontManager::PrintMonospaceU32(bt_next); } - + FontManager::PrintLine(""); FontManager::SetPosition(backtrace_x, FontManager::GetY()); } @@ -378,7 +381,7 @@ Result ShowFatalTask::ShowFatal() { if (i + Aarch64CpuContext::MaxStackTraceDepth / 2 < this->ctx->cpu_ctx.aarch64_ctx.stack_trace_size) { bt_next = this->ctx->cpu_ctx.aarch64_ctx.stack_trace[i + Aarch64CpuContext::MaxStackTraceDepth / 2]; } - + if (i < this->ctx->cpu_ctx.aarch64_ctx.stack_trace_size) { u32 x = FontManager::GetX(); FontManager::PrintFormat("BT[%02d]: ", i); @@ -386,24 +389,24 @@ Result ShowFatalTask::ShowFatal() { FontManager::PrintMonospaceU64(bt_cur); FontManager::Print(" "); } - + if (i + Aarch64CpuContext::MaxStackTraceDepth / 2 < this->ctx->cpu_ctx.aarch64_ctx.stack_trace_size) { u32 x = FontManager::GetX(); FontManager::PrintFormat("BT[%02d]: ", i + Aarch64CpuContext::MaxStackTraceDepth / 2); FontManager::SetPosition(x + 72, FontManager::GetY()); FontManager::PrintMonospaceU64(bt_next); } - + FontManager::PrintLine(""); FontManager::SetPosition(backtrace_x, FontManager::GetY()); } } } - - + + /* Enqueue the buffer. */ framebufferEnd(&fb); - + return rc; } diff --git a/stratosphere/libstratosphere b/stratosphere/libstratosphere index 880bce909..79bc9bf8d 160000 --- a/stratosphere/libstratosphere +++ b/stratosphere/libstratosphere @@ -1 +1 @@ -Subproject commit 880bce9092fbef0bcbf101b8ec2e3d2c5af3fb98 +Subproject commit 79bc9bf8d87dddcfc2d080626eb8c817c7339fd0 diff --git a/stratosphere/loader/source/ldr_content_management.cpp b/stratosphere/loader/source/ldr_content_management.cpp index b47eaf881..f5f660ab2 100644 --- a/stratosphere/loader/source/ldr_content_management.cpp +++ b/stratosphere/loader/source/ldr_content_management.cpp @@ -94,19 +94,20 @@ Result ContentManagement::MountCode(u64 tid, FsStorageId sid) { } /* Always re-initialize fsp-ldr, in case it's closed */ - if (R_FAILED(rc = fsldrInitialize())) { + DoWithSmSession([&]() { + rc = fsldrInitialize(); + }); + if (R_FAILED(rc)) { return rc; } + ON_SCOPE_EXIT { fsldrExit(); }; if (R_FAILED(rc = fsldrOpenCodeFileSystem(tid, path, &g_CodeFileSystem))) { - fsldrExit(); return rc; } fsdevMountDevice("code", g_CodeFileSystem); TryMountHblNspOnSd(); - - fsldrExit(); return rc; } @@ -372,17 +373,21 @@ void ContentManagement::RefreshConfigurationData() { void ContentManagement::TryMountSdCard() { /* Mount SD card, if psc, bus, and pcv have been created. */ if (!g_has_initialized_fs_dev && HasCreatedTitle(TitleId_Psc) && HasCreatedTitle(TitleId_Bus) && HasCreatedTitle(TitleId_Pcv)) { - Handle tmp_hnd = 0; - static const char * const required_active_services[] = {"pcv", "gpio", "pinmux", "psc:c"}; - for (unsigned int i = 0; i < sizeof(required_active_services) / sizeof(required_active_services[0]); i++) { - if (R_FAILED(smGetServiceOriginal(&tmp_hnd, smEncodeName(required_active_services[i])))) { - return; - } else { - svcCloseHandle(tmp_hnd); + bool can_mount = true; + DoWithSmSession([&]() { + Handle tmp_hnd = 0; + static const char * const required_active_services[] = {"pcv", "gpio", "pinmux", "psc:c"}; + for (unsigned int i = 0; i < sizeof(required_active_services) / sizeof(required_active_services[0]); i++) { + if (R_FAILED(smGetServiceOriginal(&tmp_hnd, smEncodeName(required_active_services[i])))) { + can_mount = false; + break; + } else { + svcCloseHandle(tmp_hnd); + } } - } + }); - if (R_SUCCEEDED(fsdevMountSdmc())) { + if (can_mount && R_SUCCEEDED(fsdevMountSdmc())) { g_has_initialized_fs_dev = true; } } diff --git a/stratosphere/loader/source/ldr_hid.cpp b/stratosphere/loader/source/ldr_hid.cpp index 2109539a2..bc103f56c 100644 --- a/stratosphere/loader/source/ldr_hid.cpp +++ b/stratosphere/loader/source/ldr_hid.cpp @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + #include #include @@ -24,13 +24,19 @@ Result HidManagement::GetKeysHeld(u64 *keys) { if (!ContentManagement::HasCreatedTitle(TitleId_Hid)) { return MAKERESULT(Module_Libnx, LibnxError_InitFail_HID); } - - if (!serviceIsActive(hidGetSessionService()) && R_FAILED(hidInitialize())) { - return MAKERESULT(Module_Libnx, LibnxError_InitFail_HID); + + if (!serviceIsActive(hidGetSessionService())) { + Result rc; + DoWithSmSession([&]() { + rc = hidInitialize(); + }); + if (R_FAILED(rc)) { + return MAKERESULT(Module_Libnx, LibnxError_InitFail_HID); + } } - + hidScanInput(); *keys = hidKeysHeld(CONTROLLER_P1_AUTO); - + return ResultSuccess; } diff --git a/stratosphere/loader/source/ldr_main.cpp b/stratosphere/loader/source/ldr_main.cpp index a278f6295..9b248344e 100644 --- a/stratosphere/loader/source/ldr_main.cpp +++ b/stratosphere/loader/source/ldr_main.cpp @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + #include #include #include @@ -36,7 +36,7 @@ extern "C" { #define INNER_HEAP_SIZE 0x30000 size_t nx_inner_heap_size = INNER_HEAP_SIZE; char nx_inner_heap[INNER_HEAP_SIZE]; - + void __libnx_initheap(void); void __appInit(void); void __appExit(void); @@ -68,30 +68,28 @@ void __libnx_initheap(void) { void __appInit(void) { Result rc; - + SetFirmwareVersionForLibnx(); /* Initialize services we need (TODO: SPL) */ - rc = smInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } - - rc = fsInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } - - rc = lrInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } - - rc = fsldrInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } - + DoWithSmSession([&]() { + rc = fsInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + rc = lrInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + rc = fsldrInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + }); + + CheckAtmosphereVersion(CURRENT_ATMOSPHERE_VERSION); } @@ -101,7 +99,6 @@ void __appExit(void) { fsldrExit(); lrExit(); fsExit(); - smExit(); } struct LoaderServerOptions { @@ -124,12 +121,12 @@ int main(int argc, char **argv) /* On 1.0.0-2.3.0, Loader services ldr:ro instead of ro. */ server_manager->AddWaitable(new ServiceServer("ldr:ro", 0x20)); } - + /* Loop forever, servicing our services. */ server_manager->Process(); - + delete server_manager; - + return 0; } diff --git a/stratosphere/pm/source/pm_boot2.cpp b/stratosphere/pm/source/pm_boot2.cpp index 410eab8fc..3fc033db7 100644 --- a/stratosphere/pm/source/pm_boot2.cpp +++ b/stratosphere/pm/source/pm_boot2.cpp @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + #include #include #include @@ -54,12 +54,12 @@ static void ClearLaunchedTitles() { static void LaunchTitle(u64 title_id, FsStorageId storage_id, u32 launch_flags, u64 *pid) { u64 local_pid = 0; - + /* Don't launch a title twice during boot2. */ if (HasLaunchedTitle(title_id)) { return; } - + Result rc = Registration::LaunchProcessByTidSid(Registration::TidSid{title_id, storage_id}, launch_flags, &local_pid); switch (rc) { case ResultKernelResourceExhausted: @@ -81,7 +81,7 @@ static void LaunchTitle(u64 title_id, FsStorageId storage_id, u32 launch_flags, if (pid) { *pid = local_pid; } - + if (R_SUCCEEDED(rc)) { SetLaunchedTitle(title_id); } @@ -92,22 +92,26 @@ static bool GetGpioPadLow(GpioPadName pad) { if (R_FAILED(gpioOpenSession(&button, pad))) { return false; } - + /* Ensure we close even on early return. */ ON_SCOPE_EXIT { gpioPadClose(&button); }; - + /* Set direction input. */ gpioPadSetDirection(&button, GpioDirection_Input); - + GpioValue val; return R_SUCCEEDED(gpioPadGetValue(&button, &val)) && val == GpioValue_Low; } static bool IsMaintenanceMode() { /* Contact set:sys, retrieve boot!force_maintenance. */ - if (R_SUCCEEDED(setsysInitialize())) { + Result rc; + DoWithSmSession([&]() { + rc = setsysInitialize(); + }); + if (R_SUCCEEDED(rc)) { ON_SCOPE_EXIT { setsysExit(); }; - + u8 force_maintenance = 1; setsysGetSettingsItemValue("boot", "force_maintenance", &force_maintenance, sizeof(force_maintenance)); if (force_maintenance != 0) { @@ -116,12 +120,15 @@ static bool IsMaintenanceMode() { } /* Contact GPIO, read plus/minus buttons. */ - if (R_SUCCEEDED(gpioInitialize())) { + DoWithSmSession([&]() { + rc = gpioInitialize(); + }); + if (R_SUCCEEDED(rc)) { ON_SCOPE_EXIT { gpioExit(); }; - + return GetGpioPadLow(GpioPadName_ButtonVolUp) && GetGpioPadLow(GpioPadName_ButtonVolDown); } - + return false; } @@ -168,42 +175,48 @@ static const std::tuple g_additional_launch_programs[] = { }; static void MountSdCard() { - Handle tmp_hnd = 0; - static const char * const required_active_services[] = {"pcv", "gpio", "pinmux", "psc:c"}; - for (unsigned int i = 0; i < sizeof(required_active_services) / sizeof(required_active_services[0]); i++) { - if (R_FAILED(smGetServiceOriginal(&tmp_hnd, smEncodeName(required_active_services[i])))) { - /* TODO: Panic */ - } else { - svcCloseHandle(tmp_hnd); + DoWithSmSession([&]() { + Handle tmp_hnd = 0; + static const char * const required_active_services[] = {"pcv", "gpio", "pinmux", "psc:c"}; + for (unsigned int i = 0; i < sizeof(required_active_services) / sizeof(required_active_services[0]); i++) { + if (R_FAILED(smGetServiceOriginal(&tmp_hnd, smEncodeName(required_active_services[i])))) { + /* TODO: Panic */ + } else { + svcCloseHandle(tmp_hnd); + } } - } + }); fsdevMountSdmc(); } static void WaitForMitm(const char *service) { bool mitm_installed = false; - Result rc = smManagerAmsInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } + Result rc; + DoWithSmSession([&]() { + if (R_FAILED((rc = smManagerAmsInitialize()))) { + std::abort(); + } + }); + while (R_FAILED((rc = smManagerAmsHasMitm(&mitm_installed, service))) || !mitm_installed) { if (R_FAILED(rc)) { std::abort(); } svcSleepThread(1000000ull); } + smManagerAmsExit(); } void EmbeddedBoot2::Main() { /* Wait until fs.mitm has installed itself. We want this to happen as early as possible. */ WaitForMitm("fsp-srv"); - + /* Clear titles. */ ClearLaunchedTitles(); - /* psc, bus, pcv is the minimal set of required titles to get SD card. */ + /* psc, bus, pcv is the minimal set of required titles to get SD card. */ /* bus depends on pcie, and pcv depends on settings. */ /* Launch psc. */ LaunchTitle(TitleId_Psc, FsStorageId_NandSystem, 0, NULL); @@ -215,16 +228,16 @@ void EmbeddedBoot2::Main() { LaunchTitle(TitleId_Settings, FsStorageId_NandSystem, 0, NULL); /* Launch pcv. */ LaunchTitle(TitleId_Pcv, FsStorageId_NandSystem, 0, NULL); - + /* At this point, the SD card can be mounted. */ MountSdCard(); - + /* Find out whether we are maintenance mode. */ bool maintenance = IsMaintenanceMode(); if (maintenance) { BootModeService::SetMaintenanceBootForEmbeddedBoot2(); } - + /* Wait for other atmosphere mitm modules to initialize. */ WaitForMitm("set:sys"); if (GetRuntimeFirmwareVersion() >= FirmwareVersion_200) { @@ -232,16 +245,16 @@ void EmbeddedBoot2::Main() { } else { WaitForMitm("bpc:c"); } - + /* Launch usb. */ LaunchTitle(TitleId_Usb, FsStorageId_NandSystem, 0, NULL); - + /* Launch tma. */ LaunchTitle(TitleId_Tma, FsStorageId_NandSystem, 0, NULL); - + /* Launch Atmosphere dmnt, using FsStorageId_None to force SD card boot. */ LaunchTitle(TitleId_Dmnt, FsStorageId_None, 0, NULL); - + /* Launch default programs. */ for (auto &launch_program : g_additional_launch_programs) { if (!maintenance || std::get(launch_program)) { @@ -253,7 +266,7 @@ void EmbeddedBoot2::Main() { LaunchTitle(TitleId_Npns, FsStorageId_NandSystem, 0, NULL); } } - + /* Allow for user-customizable programs. */ DIR *titles_dir = opendir("sdmc:/atmosphere/titles"); struct dirent *ent; @@ -288,10 +301,10 @@ void EmbeddedBoot2::Main() { } closedir(titles_dir); } - + /* We no longer need the SD card. */ fsdevUnmountAll(); - + /* Free the memory used to track what boot2 launches. */ ClearLaunchedTitles(); } diff --git a/stratosphere/pm/source/pm_main.cpp b/stratosphere/pm/source/pm_main.cpp index 5c53aa250..f380e39cf 100644 --- a/stratosphere/pm/source/pm_main.cpp +++ b/stratosphere/pm/source/pm_main.cpp @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + #include #include #include @@ -38,7 +38,7 @@ extern "C" { #define INNER_HEAP_SIZE 0x30000 size_t nx_inner_heap_size = INNER_HEAP_SIZE; char nx_inner_heap[INNER_HEAP_SIZE]; - + void __libnx_initheap(void); void __appInit(void); void __appExit(void); @@ -72,10 +72,10 @@ void RegisterPrivilegedProcessesWithFs() { /* Ensures that all privileged processes are registered with full FS permissions. */ constexpr u64 PRIVILEGED_PROCESS_MIN = 0; constexpr u64 PRIVILEGED_PROCESS_MAX = 0x4F; - + const u32 PRIVILEGED_FAH[0x1C/sizeof(u32)] = {0x00000001, 0x00000000, 0x80000000, 0x0000001C, 0x00000000, 0x0000001C, 0x00000000}; const u32 PRIVILEGED_FAC[0x2C/sizeof(u32)] = {0x00000001, 0x00000000, 0x80000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF}; - + u32 num_pids; u64 pids[PRIVILEGED_PROCESS_MAX+1]; if (R_SUCCEEDED(svcGetProcessList(&num_pids, pids, sizeof(pids)/sizeof(pids[0])))) { @@ -96,54 +96,52 @@ void RegisterPrivilegedProcessesWithFs() { void __appInit(void) { Result rc; - + SetFirmwareVersionForLibnx(); - rc = smInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } - - rc = fsprInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } - - /* This works around a bug with process permissions on < 4.0.0. */ - RegisterPrivilegedProcessesWithFs(); - - rc = smManagerAmsInitialize(); - if (R_SUCCEEDED(rc)) { - smManagerAmsEndInitialDefers(); - } else { - std::abort(); - } - - rc = smManagerInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } - - rc = lrInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } - - rc = ldrPmInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } - - rc = splInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } - - rc = fsInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } - + DoWithSmSession([&]() { + rc = fsprInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + /* This works around a bug with process permissions on < 4.0.0. */ + RegisterPrivilegedProcessesWithFs(); + + rc = smManagerAmsInitialize(); + if (R_SUCCEEDED(rc)) { + smManagerAmsEndInitialDefers(); + smManagerAmsExit(); + } else { + std::abort(); + } + + rc = smManagerInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + rc = lrInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + rc = ldrPmInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + rc = splInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + rc = fsInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + }); + CheckAtmosphereVersion(CURRENT_ATMOSPHERE_VERSION); } @@ -156,14 +154,13 @@ void __appExit(void) { fsprExit(); lrExit(); fsExit(); - smExit(); } int main(int argc, char **argv) { HosThread process_track_thread; consoleDebugInit(debugDevice_SVC); - + /* Initialize and spawn the Process Tracking thread. */ Registration::InitializeSystemResources(); if (R_FAILED(process_track_thread.Initialize(&ProcessTracking::MainLoop, NULL, 0x4000, 0x15))) { @@ -172,19 +169,19 @@ int main(int argc, char **argv) if (R_FAILED(process_track_thread.Start())) { /* TODO: Panic. */ } - + /* TODO: What's a good timeout value to use here? */ auto server_manager = new WaitableManager(1); - + /* TODO: Create services. */ server_manager->AddWaitable(new ServiceServer("pm:shell", 3)); server_manager->AddWaitable(new ServiceServer("pm:dmnt", 2)); server_manager->AddWaitable(new ServiceServer("pm:bm", 6)); server_manager->AddWaitable(new ServiceServer("pm:info", 1)); - + /* Loop forever, servicing our services. */ server_manager->Process(); - + /* Cleanup. */ delete server_manager; return 0; From 30485f1df9a4fc3a6d3095134b012098895f9dc9 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 22 Apr 2019 13:10:29 -0700 Subject: [PATCH 32/64] pm: fix dangling -> --- stratosphere/pm/source/pm_main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stratosphere/pm/source/pm_main.cpp b/stratosphere/pm/source/pm_main.cpp index 5015a6a91..cab914dd8 100644 --- a/stratosphere/pm/source/pm_main.cpp +++ b/stratosphere/pm/source/pm_main.cpp @@ -180,7 +180,7 @@ int main(int argc, char **argv) s_server_manager.AddWaitable(new ServiceServer("pm:info", 2)); /* Loop forever, servicing our services. */ - server_manager->Process(); + s_server_manager.Process(); return 0; } From 13c825a8bb1e07db255c4d5bb8ab49eda2fef322 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 22 Apr 2019 13:17:57 -0700 Subject: [PATCH 33/64] ro: only hold sm session open when needed --- stratosphere/ro/source/ro_main.cpp | 56 ++++++++++------------ stratosphere/ro/source/ro_registration.cpp | 8 ++-- 2 files changed, 31 insertions(+), 33 deletions(-) diff --git a/stratosphere/ro/source/ro_main.cpp b/stratosphere/ro/source/ro_main.cpp index cd5f9e807..0f260ef73 100644 --- a/stratosphere/ro/source/ro_main.cpp +++ b/stratosphere/ro/source/ro_main.cpp @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + #include #include #include @@ -35,7 +35,7 @@ extern "C" { #define INNER_HEAP_SIZE 0x30000 size_t nx_inner_heap_size = INNER_HEAP_SIZE; char nx_inner_heap[INNER_HEAP_SIZE]; - + void __libnx_initheap(void); void __appInit(void); void __appExit(void); @@ -66,36 +66,33 @@ void __libnx_initheap(void) { void __appInit(void) { Result rc; - + SetFirmwareVersionForLibnx(); - - rc = smInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } - - rc = setsysInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } - - rc = fsInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } - - if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) { - rc = pminfoInitialize(); + + DoWithSmSession([&]() { + rc = setsysInitialize(); if (R_FAILED(rc)) { std::abort(); } - } - - rc = fsdevMountSdmc(); - if (R_FAILED(rc)) { - std::abort(); - } - + + rc = fsInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) { + rc = pminfoInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + } + + rc = fsdevMountSdmc(); + if (R_FAILED(rc)) { + std::abort(); + } + }); + CheckAtmosphereVersion(CURRENT_ATMOSPHERE_VERSION); } @@ -106,7 +103,6 @@ void __appExit(void) { pminfoExit(); } setsysExit(); - smExit(); } /* Helpers to create RO objects. */ @@ -120,7 +116,7 @@ int main(int argc, char **argv) /* Static server manager. */ static auto s_server_manager = WaitableManager(1); - + /* Create services. */ s_server_manager.AddWaitable(new ServiceServer("ro:dmnt", 2)); /* NOTE: Official code passes 32 for ldr:ro max sessions. We will pass 2, because that's the actual limit. */ diff --git a/stratosphere/ro/source/ro_registration.cpp b/stratosphere/ro/source/ro_registration.cpp index 49c090030..74291375b 100644 --- a/stratosphere/ro/source/ro_registration.cpp +++ b/stratosphere/ro/source/ro_registration.cpp @@ -30,9 +30,11 @@ static Registration::RoProcessContext g_process_contexts[Registration::MaxSessio static bool g_is_development_hardware, g_is_development_function_enabled; void Registration::Initialize() { - if (R_FAILED(splInitialize())) { - std::abort(); - } + DoWithSmSession([&]() { + if (R_FAILED(splInitialize())) { + std::abort(); + } + }); ON_SCOPE_EXIT { splExit(); }; if (R_FAILED(splIsDevelopment(&g_is_development_hardware))) { From 83136697168dfabf1f36725fbbf5e08c46b9231e Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 23 Apr 2019 08:31:11 -0700 Subject: [PATCH 34/64] Fix NRO patch offset application --- stratosphere/ro/source/ro_patcher.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/stratosphere/ro/source/ro_patcher.cpp b/stratosphere/ro/source/ro_patcher.cpp index 355a7d2ce..0ac52c98c 100644 --- a/stratosphere/ro/source/ro_patcher.cpp +++ b/stratosphere/ro/source/ro_patcher.cpp @@ -107,7 +107,6 @@ static void ApplyIpsPatch(u8 *mapped_nro, size_t mapped_size, bool is_ips32, FIL } } else { IPS_RLE_PATCH_OFFSET_WITHIN_BOUNDS: - patch_offset -= sizeof(Registration::NroHeader); if (patch_offset + rle_size > mapped_size) { rle_size = mapped_size - patch_offset; } @@ -126,7 +125,6 @@ static void ApplyIpsPatch(u8 *mapped_nro, size_t mapped_size, bool is_ips32, FIL } } else { IPS_DATA_PATCH_OFFSET_WITHIN_BOUNDS: - patch_offset -= sizeof(Registration::NroHeader); u32 read_size = patch_size; if (patch_offset + read_size > mapped_size) { read_size = mapped_size - patch_offset; From df963967f57f6637a75c787ae9af50baf84471f4 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 23 Apr 2019 17:34:21 -0700 Subject: [PATCH 35/64] 8.0.1 seems to work fine. --- common/include/atmosphere/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/include/atmosphere/version.h b/common/include/atmosphere/version.h index 61078a1e5..408d1b551 100644 --- a/common/include/atmosphere/version.h +++ b/common/include/atmosphere/version.h @@ -23,6 +23,6 @@ #define ATMOSPHERE_SUPPORTED_HOS_VERSION_MAJOR 8 #define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 0 -#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO 0 +#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO 1 #endif \ No newline at end of file From f6645387b04f15825fcd63015b2358c3a9f86ac0 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Apr 2019 00:55:14 -0700 Subject: [PATCH 36/64] update libstrat --- stratosphere/libstratosphere | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stratosphere/libstratosphere b/stratosphere/libstratosphere index 79bc9bf8d..9e34dbe7e 160000 --- a/stratosphere/libstratosphere +++ b/stratosphere/libstratosphere @@ -1 +1 @@ -Subproject commit 79bc9bf8d87dddcfc2d080626eb8c817c7339fd0 +Subproject commit 9e34dbe7e2689eadae205f34d494696b5ea6a3f5 From 9858d6fc95b2dff98cc41d52c3b89392964a5312 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Apr 2019 01:18:18 -0700 Subject: [PATCH 37/64] spl: Start skeletoning spl. --- stratosphere/spl/Makefile | 160 ++++++++++++++++++ .../spl/source/spl_general_service.cpp | 48 ++++++ .../spl/source/spl_general_service.hpp | 52 ++++++ stratosphere/spl/source/spl_main.cpp | 110 ++++++++++++ .../spl/source/spl_random_service.cpp | 24 +++ .../spl/source/spl_random_service.hpp | 40 +++++ .../spl/source/spl_secmon_wrapper.cpp | 80 +++++++++ .../spl/source/spl_secmon_wrapper.hpp | 53 ++++++ stratosphere/spl/source/spl_types.hpp | 122 +++++++++++++ stratosphere/spl/spl.json | 74 ++++++++ 10 files changed, 763 insertions(+) create mode 100644 stratosphere/spl/Makefile create mode 100644 stratosphere/spl/source/spl_general_service.cpp create mode 100644 stratosphere/spl/source/spl_general_service.hpp create mode 100644 stratosphere/spl/source/spl_main.cpp create mode 100644 stratosphere/spl/source/spl_random_service.cpp create mode 100644 stratosphere/spl/source/spl_random_service.hpp create mode 100644 stratosphere/spl/source/spl_secmon_wrapper.cpp create mode 100644 stratosphere/spl/source/spl_secmon_wrapper.hpp create mode 100644 stratosphere/spl/source/spl_types.hpp create mode 100644 stratosphere/spl/spl.json diff --git a/stratosphere/spl/Makefile b/stratosphere/spl/Makefile new file mode 100644 index 000000000..4ebc8f6d9 --- /dev/null +++ b/stratosphere/spl/Makefile @@ -0,0 +1,160 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") +endif + +TOPDIR ?= $(CURDIR) +include $(DEVKITPRO)/libnx/switch_rules + +AMSBRANCH := $(shell git symbolic-ref --short HEAD) +AMSREV := $(AMSBRANCH)-$(shell git rev-parse --short HEAD) + +ifneq (, $(strip $(shell git status --porcelain 2>/dev/null))) + AMSREV := $(AMSREV)-dirty +endif + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +# EXEFS_SRC is the optional input directory containing data copied into exefs, if anything this normally should only contain "main.npdm". +#--------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := source +DATA := data +INCLUDES := include ../../common/include +EXEFS_SRC := exefs_src + +DEFINES := -DDISABLE_IPC -DATMOSPHERE_GIT_BRANCH=\"$(AMSBRANCH)\" -DATMOSPHERE_GIT_REV=\"$(AMSREV)\" + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE + +CFLAGS := -g -Wall -O2 -ffunction-sections \ + $(ARCH) $(DEFINES) + +CFLAGS += $(INCLUDE) -D__SWITCH__ + +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17 + +ASFLAGS := -g $(ARCH) +LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) + +LIBS := -lstratosphere -lnx + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(PORTLIBS) $(LIBNX) $(CURDIR)/../libstratosphere + + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC) + +ifeq ($(strip $(CONFIG_JSON)),) + jsons := $(wildcard *.json) + ifneq (,$(findstring $(TARGET).json,$(jsons))) + export APP_JSON := $(TOPDIR)/$(TARGET).json + else + ifneq (,$(findstring config.json,$(jsons))) + export APP_JSON := $(TOPDIR)/config.json + endif + endif +else + export APP_JSON := $(TOPDIR)/$(CONFIG_JSON) +endif + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).kip $(TARGET).elf + + +#--------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +all : $(OUTPUT).kip + +$(OUTPUT).kip : $(OUTPUT).elf + +$(OUTPUT).elf : $(OFILES) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/stratosphere/spl/source/spl_general_service.cpp b/stratosphere/spl/source/spl_general_service.cpp new file mode 100644 index 000000000..ae0c9bf92 --- /dev/null +++ b/stratosphere/spl/source/spl_general_service.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "spl_general_service.hpp" + +Result GeneralService::GetConfig(Out out, u32 which) { + return this->secmon_wrapper->GetConfig(out.GetPointer(), static_cast(which)); +} + +Result GeneralService::ExpMod(OutPointerWithClientSize out, InPointer base, InPointer exp, InPointer mod) { + return this->secmon_wrapper->ExpMod(out.pointer, out.num_elements, base.pointer, base.num_elements, exp.pointer, exp.num_elements, mod.pointer, mod.num_elements); +} + +Result GeneralService::SetConfig(u32 which, u64 value) { + return this->secmon_wrapper->SetConfig(static_cast(which), value); +} + +Result GeneralService::GenerateRandomBytes(OutPointerWithClientSize out) { + return this->secmon_wrapper->GenerateRandomBytes(out.pointer, out.num_elements); +} + +Result GeneralService::IsDevelopment(Out is_dev) { + return this->secmon_wrapper->IsDevelopment(is_dev.GetPointer()); +} + +Result GeneralService::SetBootReason(BootReasonValue boot_reason) { + return this->secmon_wrapper->SetBootReason(boot_reason); +} + +Result GeneralService::GetBootReason(Out out) { + return this->secmon_wrapper->GetBootReason(out.GetPointer()); +} diff --git a/stratosphere/spl/source/spl_general_service.hpp b/stratosphere/spl/source/spl_general_service.hpp new file mode 100644 index 000000000..9f785ee74 --- /dev/null +++ b/stratosphere/spl/source/spl_general_service.hpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +#include "spl_types.hpp" +#include "spl_secmon_wrapper.hpp" + +class GeneralService final : public IServiceObject { + private: + SecureMonitorWrapper *secmon_wrapper; + public: + GeneralService(SecureMonitorWrapper *sw) : secmon_wrapper(sw) { + /* ... */ + } + + virtual ~GeneralService() { /* ... */ } + private: + /* Actual commands. */ + virtual Result GetConfig(Out out, u32 which); + virtual Result ExpMod(OutPointerWithClientSize out, InPointer base, InPointer exp, InPointer mod); + virtual Result SetConfig(u32 which, u64 value); + virtual Result GenerateRandomBytes(OutPointerWithClientSize out); + virtual Result IsDevelopment(Out is_dev); + virtual Result SetBootReason(BootReasonValue boot_reason); + virtual Result GetBootReason(Out out); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + }; +}; diff --git a/stratosphere/spl/source/spl_main.cpp b/stratosphere/spl/source/spl_main.cpp new file mode 100644 index 000000000..2e2f82047 --- /dev/null +++ b/stratosphere/spl/source/spl_main.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include +#include + +#include "spl_random_service.hpp" +#include "spl_general_service.hpp" + +extern "C" { + extern u32 __start__; + + u32 __nx_applet_type = AppletType_None; + + #define INNER_HEAP_SIZE 0x28000 + size_t nx_inner_heap_size = INNER_HEAP_SIZE; + char nx_inner_heap[INNER_HEAP_SIZE]; + + void __libnx_initheap(void); + void __appInit(void); + void __appExit(void); + + /* Exception handling. */ + alignas(16) u8 __nx_exception_stack[0x1000]; + u64 __nx_exception_stack_size = sizeof(__nx_exception_stack); + void __libnx_exception_handler(ThreadExceptionDump *ctx); + u64 __stratosphere_title_id = TitleId_Spl; + void __libstratosphere_exception_handler(AtmosphereFatalErrorContext *ctx); +} + +void __libnx_exception_handler(ThreadExceptionDump *ctx) { + StratosphereCrashHandler(ctx); +} + + +void __libnx_initheap(void) { + void* addr = nx_inner_heap; + size_t size = nx_inner_heap_size; + + /* Newlib */ + extern char* fake_heap_start; + extern char* fake_heap_end; + + fake_heap_start = (char*)addr; + fake_heap_end = (char*)addr + size; +} + +void __appInit(void) { + SetFirmwareVersionForLibnx(); + + /* SPL doesn't really access any services... */ +} + +void __appExit(void) { + /* SPL doesn't really access any services... */ +} + +struct SplServerOptions { + static constexpr size_t PointerBufferSize = 0x800; + static constexpr size_t MaxDomains = 0; + static constexpr size_t MaxDomainObjects = 0; +}; + +/* Single secure monitor wrapper singleton. */ +static SecureMonitorWrapper s_secmon_wrapper; + +/* Helpers for creating services. */ +static const auto MakeRandomService = []() { return std::make_shared(&s_secmon_wrapper); }; +static const auto MakeGeneralService = []() { return std::make_shared(&s_secmon_wrapper); }; + +int main(int argc, char **argv) +{ + consoleDebugInit(debugDevice_SVC); + + /* Create server manager. */ + static auto s_server_manager = WaitableManager(1); + + /* Create services. */ + s_server_manager.AddWaitable(new ServiceServer("csrng", 9)); + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_400) { + s_server_manager.AddWaitable(new ServiceServer("spl:", 9)); + /* TODO: Other services. */ + } else { + /* TODO, DeprecatedGeneralService */ + } + + /* Loop forever, servicing our services. */ + s_server_manager.Process(); + + return 0; +} + diff --git a/stratosphere/spl/source/spl_random_service.cpp b/stratosphere/spl/source/spl_random_service.cpp new file mode 100644 index 000000000..a7b0cd6d3 --- /dev/null +++ b/stratosphere/spl/source/spl_random_service.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "spl_random_service.hpp" + +Result RandomService::GenerateRandomBytes(OutBuffer out) { + return this->secmon_wrapper->GenerateRandomBytes(out.buffer, out.num_elements); +} diff --git a/stratosphere/spl/source/spl_random_service.hpp b/stratosphere/spl/source/spl_random_service.hpp new file mode 100644 index 000000000..0d307fcec --- /dev/null +++ b/stratosphere/spl/source/spl_random_service.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +#include "spl_types.hpp" +#include "spl_secmon_wrapper.hpp" + +class RandomService final : public IServiceObject { + private: + SecureMonitorWrapper *secmon_wrapper; + public: + RandomService(SecureMonitorWrapper *sw) : secmon_wrapper(sw) { + /* ... */ + } + + virtual ~RandomService() { /* ... */ } + private: + /* Actual commands. */ + virtual Result GenerateRandomBytes(OutBuffer out); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MakeServiceCommandMeta(), + }; +}; diff --git a/stratosphere/spl/source/spl_secmon_wrapper.cpp b/stratosphere/spl/source/spl_secmon_wrapper.cpp new file mode 100644 index 000000000..a2421d92f --- /dev/null +++ b/stratosphere/spl/source/spl_secmon_wrapper.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "spl_secmon_wrapper.hpp" + +Result SecureMonitorWrapper::ConvertToSplResult(SmcResult result) { + if (result == SmcResult_Success) { + return ResultSuccess; + } + if (result < SmcResult_Max) { + return MAKERESULT(Module_Spl, static_cast(result)); + } + return ResultSplUnknownSmcResult; +} + +Result SecureMonitorWrapper::GetConfig(u64 *out, SplConfigItem which) { + /* TODO */ + return ResultKernelConnectionClosed; +} + +Result SecureMonitorWrapper::ExpMod(void *out, size_t out_size, const void *base, size_t base_size, const void *exp, size_t exp_size, const void *mod, size_t mod_size) { + /* TODO */ + return ResultKernelConnectionClosed; +} + +Result SecureMonitorWrapper::SetConfig(SplConfigItem which, u64 value) { + /* TODO */ + return ResultKernelConnectionClosed; +} + +Result SecureMonitorWrapper::GenerateRandomBytes(void *out, size_t size) { + /* TODO */ + return ResultKernelConnectionClosed; +} + +Result SecureMonitorWrapper::IsDevelopment(bool *out) { + u64 is_retail; + Result rc = this->GetConfig(&is_retail, SplConfigItem_IsRetail); + if (R_FAILED(rc)) { + return rc; + } + + *out = (is_retail == 0); + return ResultSuccess; +} + +Result SecureMonitorWrapper::SetBootReason(BootReasonValue boot_reason) { + if (this->IsBootReasonSet()) { + return ResultSplBootReasonAlreadySet; + } + + this->boot_reason = boot_reason; + this->boot_reason_set = true; + return ResultSuccess; +} + +Result SecureMonitorWrapper::GetBootReason(BootReasonValue *out) { + if (!this->IsBootReasonSet()) { + return ResultSplBootReasonNotSet; + } + + *out = GetBootReason(); + return ResultSuccess; +} \ No newline at end of file diff --git a/stratosphere/spl/source/spl_secmon_wrapper.hpp b/stratosphere/spl/source/spl_secmon_wrapper.hpp new file mode 100644 index 000000000..5c7104e4d --- /dev/null +++ b/stratosphere/spl/source/spl_secmon_wrapper.hpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +#include "spl_types.hpp" + +class SecureMonitorWrapper { + public: + static constexpr size_t MaxAesKeyslots = 6; + static constexpr size_t MaxAesKeyslotsDeprecated = 4; + private: + uintptr_t keyslot_owners[MaxAesKeyslots] = {}; + BootReasonValue boot_reason = {}; + bool boot_reason_set = false; + private: + static size_t GetMaxKeyslots() { + return (GetRuntimeFirmwareVersion() >= FirmwareVersion_600) ? MaxAesKeyslots : MaxAesKeyslotsDeprecated; + } + private: + BootReasonValue GetBootReason() const { + return this->boot_reason; + } + bool IsBootReasonSet() const { + return this->boot_reason_set; + } + static Result ConvertToSplResult(SmcResult result); + public: + void Initialize(); + public: + Result GetConfig(u64 *out, SplConfigItem which); + Result ExpMod(void *out, size_t out_size, const void *base, size_t base_size, const void *exp, size_t exp_size, const void *mod, size_t mod_size); + Result SetConfig(SplConfigItem which, u64 value); + Result GenerateRandomBytes(void *out, size_t size); + Result IsDevelopment(bool *out); + Result SetBootReason(BootReasonValue boot_reason); + Result GetBootReason(BootReasonValue *out); +}; diff --git a/stratosphere/spl/source/spl_types.hpp b/stratosphere/spl/source/spl_types.hpp new file mode 100644 index 000000000..41745f74c --- /dev/null +++ b/stratosphere/spl/source/spl_types.hpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include +#include +#include + +enum SmcResult : u32 { + SmcResult_Success = 0, + SmcResult_NotImplemented = 1, + SmcResult_InvalidArgument = 2, + SmcResult_InProgress = 3, + SmcResult_NoAsyncOperation = 4, + SmcResult_InvalidAsyncOperation = 5, + SmcResult_Blacklisted = 6, + + SmcResult_Max = 99, +}; + +enum SmcCipherMode : u32 { + SmcCipherMode_CbcEncrypt = 0, + SmcCipherMode_CbcDecrypt = 1, + SmcCipherMode_Ctr = 2, + SmcCipherMode_Cmac = 3, +}; + +enum EsKeyType : u32 { + EsKeyType_TitleKey = 0, + EsKeyType_ElicenseKey = 1, +}; + +struct AsyncOperationKey { + u64 value; +}; + +struct BootReasonValue { + u8 power_intr; + u8 rtc_intr; + u8 _0x3; + u8 boot_reason; +}; +static_assert(sizeof(BootReasonValue) == sizeof(u32), "BootReasonValue definition!"); + +struct AesKey { + u8 data[AES_128_KEY_SIZE]; +}; + +struct IvCtr { + u8 data[AES_128_KEY_SIZE]; +}; + +struct Cmac { + u8 data[AES_128_KEY_SIZE]; +}; + +struct AccessKey { + u8 data[AES_128_KEY_SIZE]; +}; + +struct KeySource { + u8 data[AES_128_KEY_SIZE]; +}; + +enum SplServiceCmd { + /* 1.0.0+ */ + Spl_Cmd_GetConfig = 0, + Spl_Cmd_ExpMod = 1, + Spl_Cmd_GenerateAesKek = 2, + Spl_Cmd_LoadAesKey = 3, + Spl_Cmd_GenerateAesKey = 4, + Spl_Cmd_SetConfig = 5, + Spl_Cmd_GenerateRandomBytes = 7, + Spl_Cmd_ImportLotusKey = 9, + Spl_Cmd_DecryptLotusMessage = 10, + Spl_Cmd_IsDevelopment = 11, + Spl_Cmd_GenerateSpecificAesKey = 12, + Spl_Cmd_DecryptRsaPrivateKey = 13, + Spl_Cmd_DecryptAesKey = 14, + Spl_Cmd_CryptAesCtr = 15, + Spl_Cmd_ComputeCmac = 16, + Spl_Cmd_ImportEsKey = 17, + Spl_Cmd_UnwrapTitleKey = 18, + Spl_Cmd_LoadTitleKey = 19, + + /* 2.0.0+ */ + Spl_Cmd_UnwrapCommonTitleKey = 20, + Spl_Cmd_AllocateAesKeyslot = 21, + Spl_Cmd_FreeAesKeyslot = 22, + Spl_Cmd_GetAesKeyslotEvent = 23, + + /* 3.0.0+ */ + Spl_Cmd_SetBootReason = 24, + Spl_Cmd_GetBootReason = 25, + + /* 5.0.0+ */ + Spl_Cmd_ImportSslKey = 26, + Spl_Cmd_SslExpMod = 27, + Spl_Cmd_ImportDrmKey = 28, + Spl_Cmd_DrmExpMod = 29, + Spl_Cmd_ReEncryptRsaPrivateKey = 30, + Spl_Cmd_GetPackage2Hash = 31, + + /* 6.0.0+ */ + Spl_Cmd_UnwrapElicenseKey = 31, /* re-used command id :( */ + Spl_Cmd_LoadElicenseKey = 32, +}; diff --git a/stratosphere/spl/spl.json b/stratosphere/spl/spl.json new file mode 100644 index 000000000..022b06cc1 --- /dev/null +++ b/stratosphere/spl/spl.json @@ -0,0 +1,74 @@ +{ + "name": "spl", + "title_id": "0x0100000000000028", + "main_thread_stack_size": "0x00002000", + "main_thread_priority": 27, + "default_cpu_id": 3, + "process_category": 1, + "kernel_capabilities": [{ + "type": "handle_table_size", + "value": 128 + }, { + "type": "irq_pair", + "value": [44, null] + }, { + "type": "syscalls", + "value": { + "svcSetHeapSize": "0x01", + "svcSetMemoryPermission": "0x02", + "svcSetMemoryAttribute": "0x03", + "svcMapMemory": "0x04", + "svcUnmapMemory": "0x05", + "svcQueryMemory": "0x06", + "svcExitProcess": "0x07", + "svcCreateThread": "0x08", + "svcStartThread": "0x09", + "svcExitThread": "0x0a", + "svcSleepThread": "0x0b", + "svcGetThreadPriority": "0x0c", + "svcSetThreadPriority": "0x0d", + "svcGetThreadCoreMask": "0x0e", + "svcSetThreadCoreMask": "0x0f", + "svcGetCurrentProcessorNumber": "0x10", + "svcSignalEvent": "0x11", + "svcClearEvent": "0x12", + "svcMapSharedMemory": "0x13", + "svcUnmapSharedMemory": "0x14", + "svcCreateTransferMemory": "0x15", + "svcCloseHandle": "0x16", + "svcResetSignal": "0x17", + "svcWaitSynchronization": "0x18", + "svcCancelSynchronization": "0x19", + "svcArbitrateLock": "0x1a", + "svcArbitrateUnlock": "0x1b", + "svcWaitProcessWideKeyAtomic": "0x1c", + "svcSignalProcessWideKey": "0x1d", + "svcGetSystemTick": "0x1e", + "svcConnectToNamedPort": "0x1f", + "svcSendSyncRequestLight": "0x20", + "svcSendSyncRequest": "0x21", + "svcSendSyncRequestWithUserBuffer": "0x22", + "svcSendAsyncRequestWithUserBuffer": "0x23", + "svcGetProcessId": "0x24", + "svcGetThreadId": "0x25", + "svcBreak": "0x26", + "svcOutputDebugString": "0x27", + "svcReturnFromException": "0x28", + "svcGetInfo": "0x29", + "svcWaitForAddress": "0x34", + "svcSignalToAddress": "0x35", + "svcCreateSession": "0x40", + "svcAcceptSession": "0x41", + "svcReplyAndReceiveLight": "0x42", + "svcReplyAndReceive": "0x43", + "svcReplyAndReceiveWithUserBuffer": "0x44", + "svcCreateEvent": "0x45", + "svcCreateInterruptEvent": "0x53", + "svcCreateDeviceAddressSpace": "0x56", + "svcAttachDeviceAddressSpace": "0x57", + "svcMapDeviceAddressSpaceAligned": "0x5a", + "svcUnmapDeviceAddressSpace": "0x5c", + "svcCallSecureMonitor": "0x7f" + } + }] +} \ No newline at end of file From 3a8f9114fc09b35a26556a972e36811e144ea30f Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Apr 2019 05:18:31 -0700 Subject: [PATCH 38/64] fatal: fix sm session usage --- stratosphere/fatal/source/fatal_task_screen.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/stratosphere/fatal/source/fatal_task_screen.cpp b/stratosphere/fatal/source/fatal_task_screen.cpp index ab7937b6a..7202197ee 100644 --- a/stratosphere/fatal/source/fatal_task_screen.cpp +++ b/stratosphere/fatal/source/fatal_task_screen.cpp @@ -95,10 +95,7 @@ Result ShowFatalTask::PrepareScreenForDrawing() { Result rc = ResultSuccess; /* Connect to vi. */ - DoWithSmSession([&]() { - rc = viInitialize(ViServiceType_Manager); - }); - if (R_FAILED(rc)) { + if (R_FAILED((rc = viInitialize(ViServiceType_Manager)))) { return rc; } @@ -181,7 +178,10 @@ Result ShowFatalTask::ShowFatal() { Result rc = ResultSuccess; const FatalConfig *config = GetFatalConfig(); - if (R_FAILED((rc = PrepareScreenForDrawing()))) { + DoWithSmSession([&]() { + rc = PrepareScreenForDrawing(); + }); + if (R_FAILED(rc)) { *(volatile u32 *)(0xCAFEBABE) = rc; return rc; } From d44b91826dee7ba534df73300278d6e28261c06d Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Apr 2019 05:19:37 -0700 Subject: [PATCH 39/64] dmnt: only init roDmnt when we can --- stratosphere/dmnt/source/dmnt_main.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/stratosphere/dmnt/source/dmnt_main.cpp b/stratosphere/dmnt/source/dmnt_main.cpp index 6745a8673..ce140113e 100644 --- a/stratosphere/dmnt/source/dmnt_main.cpp +++ b/stratosphere/dmnt/source/dmnt_main.cpp @@ -82,9 +82,12 @@ void __appInit(void) { fatalSimple(rc); } - rc = roDmntInitialize(); - if (R_FAILED(rc)) { - fatalSimple(rc); + /* TODO: We provide this on every sysver via ro. Do we need a shim? */ + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_300) { + rc = roDmntInitialize(); + if (R_FAILED(rc)) { + fatalSimple(rc); + } } rc = nsdevInitialize(); From 2dfa1c96d1672d5489d3f07ffdc0cea0da9f45e2 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Apr 2019 05:38:11 -0700 Subject: [PATCH 40/64] spl: continue implementing. --- stratosphere/spl/source/spl_ctr_drbg.cpp | 81 +++++ stratosphere/spl/source/spl_ctr_drbg.hpp | 60 ++++ stratosphere/spl/source/spl_main.cpp | 5 +- .../spl/source/spl_secmon_wrapper.cpp | 92 ++++- .../spl/source/spl_secmon_wrapper.hpp | 7 +- stratosphere/spl/source/spl_smc_wrapper.cpp | 334 ++++++++++++++++++ stratosphere/spl/source/spl_smc_wrapper.hpp | 47 +++ stratosphere/spl/source/spl_types.hpp | 12 +- 8 files changed, 628 insertions(+), 10 deletions(-) create mode 100644 stratosphere/spl/source/spl_ctr_drbg.cpp create mode 100644 stratosphere/spl/source/spl_ctr_drbg.hpp create mode 100644 stratosphere/spl/source/spl_smc_wrapper.cpp create mode 100644 stratosphere/spl/source/spl_smc_wrapper.hpp diff --git a/stratosphere/spl/source/spl_ctr_drbg.cpp b/stratosphere/spl/source/spl_ctr_drbg.cpp new file mode 100644 index 000000000..1eadb7e4e --- /dev/null +++ b/stratosphere/spl/source/spl_ctr_drbg.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "spl_types.hpp" +#include "spl_ctr_drbg.hpp" + +void CtrDrbg::Update(const void *data) { + aes128ContextCreate(&this->aes_ctx, this->key, true); + for (size_t offset = 0; offset < sizeof(this->work[1]); offset += BlockSize) { + IncrementCounter(this->counter); + aes128EncryptBlock(&this->aes_ctx, &this->work[1][offset], this->counter); + } + + Xor(this->work[1], data, sizeof(this->work[1])); + + std::memcpy(this->key, &this->work[1][0], sizeof(this->key)); + std::memcpy(this->counter, &this->work[1][BlockSize], sizeof(this->key)); +} + +void CtrDrbg::Initialize(const void *seed) { + std::memcpy(this->work[0], seed, sizeof(this->work[0])); + std::memset(this->key, 0, sizeof(this->key)); + std::memset(this->counter, 0, sizeof(this->counter)); + this->Update(this->work[0]); + this->reseed_counter = 1; +} + +void CtrDrbg::Reseed(const void *seed) { + std::memcpy(this->work[0], seed, sizeof(this->work[0])); + this->Update(this->work[0]); + this->reseed_counter = 1; +} + +bool CtrDrbg::GenerateRandomBytes(void *out, size_t size) { + if (size > MaxRequestSize) { + return false; + } + + if (this->reseed_counter > ReseedInterval) { + return false; + } + + aes128ContextCreate(&this->aes_ctx, this->key, true); + u8 *cur_dst = reinterpret_cast(out); + + size_t aligned_size = (size & ~(BlockSize - 1)); + for (size_t offset = 0; offset < aligned_size; offset += BlockSize) { + IncrementCounter(this->counter); + aes128EncryptBlock(&this->aes_ctx, cur_dst, this->counter); + cur_dst += BlockSize; + } + + if (size > aligned_size) { + IncrementCounter(this->counter); + aes128EncryptBlock(&this->aes_ctx, this->work[1], this->counter); + std::memcpy(cur_dst, this->work[1], size - aligned_size); + } + + std::memset(this->work[0], 0, sizeof(this->work[0])); + this->Update(this->work[0]); + + this->reseed_counter++; + return true; + +} \ No newline at end of file diff --git a/stratosphere/spl/source/spl_ctr_drbg.hpp b/stratosphere/spl/source/spl_ctr_drbg.hpp new file mode 100644 index 000000000..71564b582 --- /dev/null +++ b/stratosphere/spl/source/spl_ctr_drbg.hpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +#include "spl_types.hpp" + +/* Nintendo implements CTR_DRBG for their csrng. We will do the same. */ +class CtrDrbg { + public: + static constexpr size_t MaxRequestSize = 0x10000; + static constexpr size_t ReseedInterval = 0x7FFFFFF0; + static constexpr size_t BlockSize = AES_BLOCK_SIZE; + static constexpr size_t SeedSize = 2 * AES_BLOCK_SIZE; + private: + Aes128Context aes_ctx; + u8 counter[BlockSize]; + u8 key[BlockSize]; + u8 work[2][SeedSize]; + u32 reseed_counter; + private: + static void Xor(void *dst, const void *src, size_t size) { + const u8 *src_u8 = reinterpret_cast(src); + u8 *dst_u8 = reinterpret_cast(dst); + + for (size_t i = 0; i < size; i++) { + dst_u8[i] = src_u8[i]; + } + } + + static void IncrementCounter(void *ctr) { + u64 *ctr_64 = reinterpret_cast(ctr); + + ctr_64[1] = __builtin_bswap64(__builtin_bswap64(ctr_64[1]) + 1); + if (!ctr_64[1]) { + ctr_64[0] = __builtin_bswap64(__builtin_bswap64(ctr_64[0]) + 1); + } + } + private: + void Update(const void *data); + public: + void Initialize(const void *seed); + void Reseed(const void *seed); + bool GenerateRandomBytes(void *out, size_t size); +}; \ No newline at end of file diff --git a/stratosphere/spl/source/spl_main.cpp b/stratosphere/spl/source/spl_main.cpp index 2e2f82047..a644814fb 100644 --- a/stratosphere/spl/source/spl_main.cpp +++ b/stratosphere/spl/source/spl_main.cpp @@ -89,12 +89,15 @@ static const auto MakeGeneralService = []() { return std::make_shared(1); /* Create services. */ - s_server_manager.AddWaitable(new ServiceServer("csrng", 9)); + s_server_manager.AddWaitable(new ServiceServer("csrng", 3)); if (GetRuntimeFirmwareVersion() >= FirmwareVersion_400) { s_server_manager.AddWaitable(new ServiceServer("spl:", 9)); /* TODO: Other services. */ diff --git a/stratosphere/spl/source/spl_secmon_wrapper.cpp b/stratosphere/spl/source/spl_secmon_wrapper.cpp index a2421d92f..6eeaa0904 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.cpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.cpp @@ -18,6 +18,40 @@ #include #include "spl_secmon_wrapper.hpp" +#include "spl_smc_wrapper.hpp" +#include "spl_ctr_drbg.hpp" + +/* Globals. */ +static CtrDrbg g_drbg; +static Event g_se_event; +static __attribute__((aligned(0x1000))) u8 g_work_buffer[0x1000]; + +void SecureMonitorWrapper::InitializeCtrDrbg() { + u8 seed[CtrDrbg::SeedSize]; + + if (SmcWrapper::GenerateRandomBytes(seed, sizeof(seed)) != SmcResult_Success) { + std::abort(); + } + + g_drbg.Initialize(seed); +} + +void SecureMonitorWrapper::InitializeSeInterruptEvent() { + u64 irq_num; + SmcWrapper::GetConfig(&irq_num, 1, SplConfigItem_SecurityEngineIrqNumber); + Handle hnd; + if (R_FAILED(svcCreateInterruptEvent(&hnd, irq_num, 1))) { + std::abort(); + } + eventLoadRemote(&g_se_event, hnd, true); +} + +void SecureMonitorWrapper::Initialize() { + /* Initialize the Drbg. */ + InitializeCtrDrbg(); + /* Initialize SE interrupt event. */ + InitializeSeInterruptEvent(); +} Result SecureMonitorWrapper::ConvertToSplResult(SmcResult result) { if (result == SmcResult_Success) { @@ -30,8 +64,25 @@ Result SecureMonitorWrapper::ConvertToSplResult(SmcResult result) { } Result SecureMonitorWrapper::GetConfig(u64 *out, SplConfigItem which) { - /* TODO */ - return ResultKernelConnectionClosed; + /* Nintendo explicitly blacklists package2 hash here, amusingly. */ + /* This is not blacklisted in safemode, but we're never in safe mode... */ + if (which == SplConfigItem_Package2Hash) { + return ResultSplInvalidArgument; + } + + SmcResult res = SmcWrapper::GetConfig(out, 1, which); + + /* Nintendo has some special handling here for hardware type/is_retail. */ + if (which == SplConfigItem_HardwareType && res == SmcResult_InvalidArgument) { + *out = 0; + res = SmcResult_Success; + } + if (which == SplConfigItem_IsRetail && res == SmcResult_InvalidArgument) { + *out = 0; + res = SmcResult_Success; + } + + return ConvertToSplResult(res); } Result SecureMonitorWrapper::ExpMod(void *out, size_t out_size, const void *base, size_t base_size, const void *exp, size_t exp_size, const void *mod, size_t mod_size) { @@ -40,13 +91,42 @@ Result SecureMonitorWrapper::ExpMod(void *out, size_t out_size, const void *base } Result SecureMonitorWrapper::SetConfig(SplConfigItem which, u64 value) { - /* TODO */ - return ResultKernelConnectionClosed; + return ConvertToSplResult(SmcWrapper::SetConfig(which, &value, 1)); +} + +Result SecureMonitorWrapper::GenerateRandomBytesInternal(void *out, size_t size) { + if (!g_drbg.GenerateRandomBytes(out, size)) { + /* We need to reseed. */ + { + u8 seed[CtrDrbg::SeedSize]; + + SmcResult res = SmcWrapper::GenerateRandomBytes(seed, sizeof(seed)); + if (res != SmcResult_Success) { + return ConvertToSplResult(res); + } + + g_drbg.Reseed(seed); + g_drbg.GenerateRandomBytes(out, size); + } + } + + return ResultSuccess; } Result SecureMonitorWrapper::GenerateRandomBytes(void *out, size_t size) { - /* TODO */ - return ResultKernelConnectionClosed; + u8 *cur_dst = reinterpret_cast(out); + + for (size_t ofs = 0; ofs < size; ofs += CtrDrbg::MaxRequestSize) { + const size_t cur_size = std::min(size - ofs, CtrDrbg::MaxRequestSize); + + Result rc = GenerateRandomBytesInternal(cur_dst, size); + if (R_FAILED(rc)) { + return rc; + } + cur_dst += cur_size; + } + + return ResultSuccess; } Result SecureMonitorWrapper::IsDevelopment(bool *out) { diff --git a/stratosphere/spl/source/spl_secmon_wrapper.hpp b/stratosphere/spl/source/spl_secmon_wrapper.hpp index 5c7104e4d..820663f99 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.hpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.hpp @@ -40,8 +40,13 @@ class SecureMonitorWrapper { return this->boot_reason_set; } static Result ConvertToSplResult(SmcResult result); + private: + static void InitializeCtrDrbg(); + static void InitializeSeInterruptEvent(); public: - void Initialize(); + static void Initialize(); + private: + Result GenerateRandomBytesInternal(void *out, size_t size); public: Result GetConfig(u64 *out, SplConfigItem which); Result ExpMod(void *out, size_t out_size, const void *base, size_t base_size, const void *exp, size_t exp_size, const void *mod, size_t mod_size); diff --git a/stratosphere/spl/source/spl_smc_wrapper.cpp b/stratosphere/spl/source/spl_smc_wrapper.cpp new file mode 100644 index 000000000..97e39afc5 --- /dev/null +++ b/stratosphere/spl/source/spl_smc_wrapper.cpp @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "spl_smc_wrapper.hpp" + +enum SmcFunctionId : u32 { + SmcFunctionId_SetConfig = 0xC3000401, + SmcFunctionId_GetConfig = 0xC3000002, + SmcFunctionId_CheckStatus = 0xC3000003, + SmcFunctionId_GetResult = 0xC3000404, + SmcFunctionId_ExpMod = 0xC3000E05, + SmcFunctionId_GenerateRandomBytes = 0xC3000006, + SmcFunctionId_GenerateAesKek = 0xC3000007, + SmcFunctionId_LoadAesKey = 0xC3000008, + SmcFunctionId_CryptAes = 0xC3000009, + SmcFunctionId_GenerateSpecificAesKey = 0xC300000A, + SmcFunctionId_ComputeCmac = 0xC300040B, + SmcFunctionId_ReEncryptRsaPrivateKey = 0xC300D60C, + SmcFunctionId_DecryptOrImportRsaPrivateKey = 0xC300100D, + + SmcFunctionId_SecureExpMod = 0xC300060F, + SmcFunctionId_UnwrapTitleKey = 0xC3000610, + SmcFunctionId_LoadTitleKey = 0xC3000011, + SmcFunctionId_UnwrapCommonTitleKey = 0xC3000012, + + /* Deprecated functions. */ + SmcFunctionId_ImportEsKey = 0xC300100C, + SmcFunctionId_DecryptRsaPrivateKey = 0xC300100D, + SmcFunctionId_ImportSecureExpModKey = 0xC300100E, +}; + +SmcResult SmcWrapper::SetConfig(SplConfigItem which, const u64 *value, size_t num_qwords) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_SetConfig; + args.X[1] = which; + args.X[2] = 0; + for (size_t i = 0; i < std::min(size_t(4), num_qwords); i++) { + args.X[3 + i] = value[i]; + } + svcCallSecureMonitor(&args); + + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::GetConfig(u64 *out, size_t num_qwords, SplConfigItem which) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_GetConfig; + args.X[1] = which; + svcCallSecureMonitor(&args); + + for (size_t i = 0; i < std::min(size_t(4), num_qwords); i++) { + out[i] = args.X[1 + i]; + } + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::CheckStatus(SmcResult *out, AsyncOperationKey op) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_CheckStatus; + args.X[1] = op.value; + svcCallSecureMonitor(&args); + + *out = static_cast(args.X[1]); + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::GetResult(SmcResult *out, void *out_buf, size_t out_buf_size, AsyncOperationKey op) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_GetResult; + args.X[1] = op.value; + args.X[2] = reinterpret_cast(out_buf); + args.X[3] = out_buf_size; + svcCallSecureMonitor(&args); + + *out = static_cast(args.X[1]); + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::ExpMod(AsyncOperationKey *out_op, const void *base, const void *exp, size_t exp_size, const void *mod) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_ExpMod; + args.X[1] = reinterpret_cast(base); + args.X[2] = reinterpret_cast(exp); + args.X[3] = reinterpret_cast(mod); + args.X[4] = exp_size; + svcCallSecureMonitor(&args); + + out_op->value = args.X[1]; + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::GenerateRandomBytes(void *out, size_t size) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_GenerateRandomBytes; + args.X[1] = size; + svcCallSecureMonitor(&args); + + if (args.X[0] == SmcResult_Success && (size <= sizeof(args) - sizeof(args.X[0]))) { + std::memcpy(out, &args.X[1], size); + } + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::GenerateAesKek(AccessKey *out, const u64 *source, u32 generation, u32 option) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_GenerateAesKek; + args.X[1] = source[0]; + args.X[2] = source[1]; + args.X[3] = generation; + args.X[4] = option; + svcCallSecureMonitor(&args); + + out->data64[0] = args.X[1]; + out->data64[1] = args.X[2]; + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::LoadAesKey(u32 keyslot, const AccessKey &access_key, const u64 *source) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_LoadAesKey; + args.X[1] = keyslot; + args.X[2] = access_key.data64[0]; + args.X[3] = access_key.data64[1]; + args.X[4] = source[0]; + args.X[5] = source[1]; + svcCallSecureMonitor(&args); + + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::CryptAes(AsyncOperationKey *out_op, u32 mode, const u64 *iv_ctr, u32 dst_addr, u32 src_addr, size_t size) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_CryptAes; + args.X[1] = mode; + args.X[2] = iv_ctr[0]; + args.X[3] = iv_ctr[1]; + args.X[4] = src_addr; + args.X[5] = dst_addr; + args.X[6] = size; + svcCallSecureMonitor(&args); + + out_op->value = args.X[1]; + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::GenerateSpecificAesKey(u64 *out, const u64 *source, u32 generation, u32 which) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_GenerateSpecificAesKey; + args.X[1] = source[0]; + args.X[2] = source[1]; + args.X[3] = generation; + args.X[4] = which; + svcCallSecureMonitor(&args); + + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::ComputeCmac(Cmac &out_mac, u32 keyslot, const void *data, size_t size) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_ComputeCmac; + args.X[1] = keyslot; + args.X[2] = reinterpret_cast(data); + args.X[3] = size; + svcCallSecureMonitor(&args); + + out_mac.data64[0] = args.X[1]; + out_mac.data64[1] = args.X[2]; + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::ReEncryptRsaPrivateKey(void *data, size_t size, const AccessKey &access_key_dec, const u64 *source_dec, const AccessKey &access_key_enc, const u64 *source_enc, u32 option) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_ReEncryptRsaPrivateKey; + args.X[1] = reinterpret_cast(&access_key_dec); + args.X[2] = reinterpret_cast(&access_key_enc); + args.X[3] = option; + args.X[4] = reinterpret_cast(data); + args.X[5] = size; + args.X[6] = reinterpret_cast(source_dec); + args.X[7] = reinterpret_cast(source_enc); + svcCallSecureMonitor(&args); + + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::DecryptOrImportRsaPrivateKey(void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_DecryptOrImportRsaPrivateKey; + args.X[1] = access_key.data64[0]; + args.X[2] = access_key.data64[1]; + args.X[3] = option; + args.X[4] = reinterpret_cast(data); + args.X[5] = size; + args.X[6] = source[0]; + args.X[7] = source[1]; + svcCallSecureMonitor(&args); + + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::SecureExpMod(AsyncOperationKey *out_op, const void *base, const void *mod, u32 option) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_SecureExpMod; + args.X[1] = reinterpret_cast(base); + args.X[2] = reinterpret_cast(mod); + args.X[3] = option; + svcCallSecureMonitor(&args); + + out_op->value = args.X[1]; + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::UnwrapTitleKey(AsyncOperationKey *out_op, const void *base, const void *mod, const void *label_digest, size_t label_digest_size, u32 option) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_UnwrapTitleKey; + args.X[1] = reinterpret_cast(base); + args.X[2] = reinterpret_cast(mod); + std::memset(&args.X[3], 0, 4 * sizeof(args.X[3])); + std::memcpy(&args.X[3], label_digest, std::min(size_t(4 * sizeof(args.X[3])), label_digest_size)); + args.X[7] = option; + svcCallSecureMonitor(&args); + + out_op->value = args.X[1]; + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::LoadTitleKey(u32 keyslot, const AccessKey &access_key) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_LoadTitleKey; + args.X[1] = keyslot; + args.X[2] = access_key.data64[0]; + args.X[3] = access_key.data64[1]; + svcCallSecureMonitor(&args); + + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::UnwrapCommonTitleKey(AccessKey *out, const u64 *source, u32 generation) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_UnwrapCommonTitleKey; + args.X[1] = source[0]; + args.X[2] = source[1]; + args.X[3] = generation; + svcCallSecureMonitor(&args); + + out->data64[0] = args.X[1]; + out->data64[1] = args.X[2]; + return static_cast(args.X[0]); +} + + +/* Deprecated functions. */ +SmcResult SmcWrapper::ImportEsKey(const void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_ImportEsKey; + args.X[1] = access_key.data64[0]; + args.X[2] = access_key.data64[1]; + args.X[3] = option; + args.X[4] = reinterpret_cast(data); + args.X[5] = size; + args.X[6] = source[0]; + args.X[7] = source[1]; + svcCallSecureMonitor(&args); + + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::DecryptRsaPrivateKey(size_t *out_size, void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_DecryptRsaPrivateKey; + args.X[1] = access_key.data64[0]; + args.X[2] = access_key.data64[1]; + args.X[3] = option; + args.X[4] = reinterpret_cast(data); + args.X[5] = size; + args.X[6] = source[0]; + args.X[7] = source[1]; + svcCallSecureMonitor(&args); + + *out_size = static_cast(args.X[1]); + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::ImportSecureExpModKey(const void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_ImportSecureExpModKey; + args.X[1] = access_key.data64[0]; + args.X[2] = access_key.data64[1]; + args.X[3] = option; + args.X[4] = reinterpret_cast(data); + args.X[5] = size; + args.X[6] = source[0]; + args.X[7] = source[1]; + svcCallSecureMonitor(&args); + + return static_cast(args.X[0]); +} + diff --git a/stratosphere/spl/source/spl_smc_wrapper.hpp b/stratosphere/spl/source/spl_smc_wrapper.hpp new file mode 100644 index 000000000..41a97cf40 --- /dev/null +++ b/stratosphere/spl/source/spl_smc_wrapper.hpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +#include "spl_types.hpp" + +class SmcWrapper { + public: + static SmcResult SetConfig(SplConfigItem which, const u64 *value, size_t num_qwords); + static SmcResult GetConfig(u64 *out, size_t num_qwords, SplConfigItem which); + static SmcResult CheckStatus(SmcResult *out, AsyncOperationKey op); + static SmcResult GetResult(SmcResult *out, void *out_buf, size_t out_buf_size, AsyncOperationKey op); + static SmcResult ExpMod(AsyncOperationKey *out_op, const void *base, const void *exp, size_t exp_size, const void *mod); + static SmcResult GenerateRandomBytes(void *out, size_t size); + static SmcResult GenerateAesKek(AccessKey *out, const u64 *source, u32 generation, u32 option); + static SmcResult LoadAesKey(u32 keyslot, const AccessKey &access_key, const u64 *source); + static SmcResult CryptAes(AsyncOperationKey *out_op, u32 mode, const u64 *iv_ctr, u32 dst_addr, u32 src_addr, size_t size); + static SmcResult GenerateSpecificAesKey(u64 *out, const u64 *source, u32 generation, u32 which); + static SmcResult ComputeCmac(Cmac &out_mac, u32 keyslot, const void *data, size_t size); + static SmcResult ReEncryptRsaPrivateKey(void *data, size_t size, const AccessKey &access_key_dec, const u64 *source_dec, const AccessKey &access_key_enc, const u64 *source_enc, u32 option); + static SmcResult DecryptOrImportRsaPrivateKey(void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option); + static SmcResult SecureExpMod(AsyncOperationKey *out_op, const void *base, const void *mod, u32 option); + static SmcResult UnwrapTitleKey(AsyncOperationKey *out_op, const void *base, const void *mod, const void *label_digest, size_t label_digest_size, u32 option); + static SmcResult LoadTitleKey(u32 keyslot, const AccessKey &access_key); + static SmcResult UnwrapCommonTitleKey(AccessKey *out, const u64 *source, u32 generation); + + /* Deprecated functions. */ + static SmcResult ImportEsKey(const void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option); + static SmcResult DecryptRsaPrivateKey(size_t *out_size, void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option); + static SmcResult ImportSecureExpModKey(const void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option); +}; diff --git a/stratosphere/spl/source/spl_types.hpp b/stratosphere/spl/source/spl_types.hpp index 41745f74c..8d8317a48 100644 --- a/stratosphere/spl/source/spl_types.hpp +++ b/stratosphere/spl/source/spl_types.hpp @@ -66,12 +66,20 @@ struct IvCtr { }; struct Cmac { - u8 data[AES_128_KEY_SIZE]; + union { + u8 data[AES_128_KEY_SIZE]; + u8 data64[AES_128_KEY_SIZE / sizeof(u64)]; + }; }; +static_assert(alignof(Cmac) == alignof(u8), "Cmac definition!"); struct AccessKey { - u8 data[AES_128_KEY_SIZE]; + union { + u8 data[AES_128_KEY_SIZE]; + u8 data64[AES_128_KEY_SIZE / sizeof(u64)]; + }; }; +static_assert(alignof(AccessKey) == alignof(u8), "AccessKey definition!"); struct KeySource { u8 data[AES_128_KEY_SIZE]; From ccbab35debb16911cbe36270da0c61b856259d1d Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Apr 2019 06:10:30 -0700 Subject: [PATCH 41/64] spl: finish GeneralService. --- .../spl/source/spl_secmon_wrapper.cpp | 109 +++++++++++++++++- .../spl/source/spl_secmon_wrapper.hpp | 4 + 2 files changed, 111 insertions(+), 2 deletions(-) diff --git a/stratosphere/spl/source/spl_secmon_wrapper.cpp b/stratosphere/spl/source/spl_secmon_wrapper.cpp index 6eeaa0904..a52c49535 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.cpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.cpp @@ -21,11 +21,21 @@ #include "spl_smc_wrapper.hpp" #include "spl_ctr_drbg.hpp" +/* Convenient. */ +constexpr size_t DeviceAddressSpaceAlignSize = 0x400000; +constexpr size_t DeviceAddressSpaceAlignMask = DeviceAddressSpaceAlignSize - 1; +constexpr u32 DeviceMapBase = 0x80000000u; + /* Globals. */ static CtrDrbg g_drbg; static Event g_se_event; + +static Handle g_se_das_hnd; +static u32 g_se_mapped_work_buffer_addr; static __attribute__((aligned(0x1000))) u8 g_work_buffer[0x1000]; +static HosMutex g_se_lock; + void SecureMonitorWrapper::InitializeCtrDrbg() { u8 seed[CtrDrbg::SeedSize]; @@ -46,11 +56,38 @@ void SecureMonitorWrapper::InitializeSeInterruptEvent() { eventLoadRemote(&g_se_event, hnd, true); } +void SecureMonitorWrapper::InitializeDeviceAddressSpace() { + constexpr u64 DeviceName_SE = 29; + + /* Create Address Space. */ + if (R_FAILED(svcCreateDeviceAddressSpace(&g_se_das_hnd, 0, (1ul << 32)))) { + std::abort(); + } + /* Attach it to the SE. */ + if (R_FAILED(svcAttachDeviceAddressSpace(DeviceName_SE, g_se_das_hnd))) { + std::abort(); + } + + const u64 work_buffer_addr = reinterpret_cast(g_work_buffer); + g_se_mapped_work_buffer_addr = 0x80000000u + (work_buffer_addr & DeviceAddressSpaceAlignMask); + + /* Map the work buffer for the SE. */ + if (R_FAILED(svcMapDeviceAddressSpaceAligned(g_se_das_hnd, CUR_PROCESS_HANDLE, work_buffer_addr, sizeof(g_work_buffer), g_se_mapped_work_buffer_addr, 3))) { + std::abort(); + } +} + void SecureMonitorWrapper::Initialize() { /* Initialize the Drbg. */ InitializeCtrDrbg(); /* Initialize SE interrupt event. */ InitializeSeInterruptEvent(); + /* Initialize DAS for the SE. */ + InitializeDeviceAddressSpace(); +} + +void SecureMonitorWrapper::WaitSeOperationComplete() { + eventWait(&g_se_event, U64_MAX); } Result SecureMonitorWrapper::ConvertToSplResult(SmcResult result) { @@ -63,6 +100,30 @@ Result SecureMonitorWrapper::ConvertToSplResult(SmcResult result) { return ResultSplUnknownSmcResult; } +SmcResult SecureMonitorWrapper::WaitCheckStatus(AsyncOperationKey op_key) { + WaitSeOperationComplete(); + + SmcResult op_res; + SmcResult res = SmcWrapper::CheckStatus(&op_res, op_key); + if (res != SmcResult_Success) { + return res; + } + + return op_res; +} + +SmcResult SecureMonitorWrapper::WaitGetResult(void *out_buf, size_t out_buf_size, AsyncOperationKey op_key) { + WaitSeOperationComplete(); + + SmcResult op_res; + SmcResult res = SmcWrapper::GetResult(&op_res, out_buf, out_buf_size, op_key); + if (res != SmcResult_Success) { + return res; + } + + return op_res; +} + Result SecureMonitorWrapper::GetConfig(u64 *out, SplConfigItem which) { /* Nintendo explicitly blacklists package2 hash here, amusingly. */ /* This is not blacklisted in safemode, but we're never in safe mode... */ @@ -86,8 +147,52 @@ Result SecureMonitorWrapper::GetConfig(u64 *out, SplConfigItem which) { } Result SecureMonitorWrapper::ExpMod(void *out, size_t out_size, const void *base, size_t base_size, const void *exp, size_t exp_size, const void *mod, size_t mod_size) { - /* TODO */ - return ResultKernelConnectionClosed; + struct ExpModLayout { + u8 base[0x100]; + u8 exp[0x100]; + u8 mod[0x100]; + }; + ExpModLayout *layout = reinterpret_cast(g_work_buffer); + + /* Validate sizes. */ + if (base_size > sizeof(layout->base)) { + return ResultSplInvalidSize; + } + if (exp_size > sizeof(layout->exp)) { + return ResultSplInvalidSize; + } + if (mod_size > sizeof(layout->mod)) { + return ResultSplInvalidSize; + } + if (out_size > sizeof(g_work_buffer) / 2) { + return ResultSplInvalidSize; + } + + /* Copy data into work buffer. */ + const size_t base_ofs = sizeof(layout->base) - base_size; + const size_t mod_ofs = sizeof(layout->mod) - mod_size; + std::memset(layout, 0, sizeof(*layout)); + std::memcpy(layout->base + base_ofs, base, base_size); + std::memcpy(layout->exp, exp, exp_size); + std::memcpy(layout->mod + mod_ofs, mod, mod_size); + + /* Do exp mod operation. */ + { + std::scoped_lock lk(g_se_lock); + AsyncOperationKey op_key; + + SmcResult res = SmcWrapper::ExpMod(&op_key, layout->base, layout->exp, exp_size, layout->mod); + if (res != SmcResult_Success) { + return ConvertToSplResult(res); + } + + if ((res = WaitGetResult(g_work_buffer, out_size, op_key)) != SmcResult_Success) { + return ConvertToSplResult(res); + } + } + + std::memcpy(out, g_work_buffer, out_size); + return ResultSuccess; } Result SecureMonitorWrapper::SetConfig(SplConfigItem which, u64 value) { diff --git a/stratosphere/spl/source/spl_secmon_wrapper.hpp b/stratosphere/spl/source/spl_secmon_wrapper.hpp index 820663f99..cfaf123ca 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.hpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.hpp @@ -43,10 +43,14 @@ class SecureMonitorWrapper { private: static void InitializeCtrDrbg(); static void InitializeSeInterruptEvent(); + static void InitializeDeviceAddressSpace(); public: static void Initialize(); private: Result GenerateRandomBytesInternal(void *out, size_t size); + void WaitSeOperationComplete(); + SmcResult WaitCheckStatus(AsyncOperationKey op_key); + SmcResult WaitGetResult(void *out_buf, size_t out_buf_size, AsyncOperationKey op_key); public: Result GetConfig(u64 *out, SplConfigItem which); Result ExpMod(void *out, size_t out_size, const void *base, size_t base_size, const void *exp, size_t exp_size, const void *mod, size_t mod_size); From 14683405be233d4860d85f7d7db6da7e9385a0f9 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Apr 2019 16:36:35 -0700 Subject: [PATCH 42/64] fatal: update to use clkrst api on 8.0.0+ --- stratosphere/fatal/source/fatal_main.cpp | 19 +++++++-- .../fatal/source/fatal_task_clock.cpp | 39 +++++++++++++++---- .../fatal/source/fatal_task_clock.hpp | 1 + 3 files changed, 48 insertions(+), 11 deletions(-) diff --git a/stratosphere/fatal/source/fatal_main.cpp b/stratosphere/fatal/source/fatal_main.cpp index 4600b951d..22c1ac6e2 100644 --- a/stratosphere/fatal/source/fatal_main.cpp +++ b/stratosphere/fatal/source/fatal_main.cpp @@ -102,9 +102,16 @@ void __appInit(void) { std::abort(); } - rc = pcvInitialize(); - if (R_FAILED(rc)) { - std::abort(); + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_800) { + rc = clkrstInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + } else { + rc = pcvInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } } rc = lblInitialize(); @@ -155,7 +162,11 @@ void __appExit(void) { spsmExit(); psmExit(); lblExit(); - pcvExit(); + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_800) { + clkrstExit(); + } else { + pcvExit(); + } bpcExit(); i2cExit(); pminfoExit(); diff --git a/stratosphere/fatal/source/fatal_task_clock.cpp b/stratosphere/fatal/source/fatal_task_clock.cpp index 3bd1d296c..6fe9a849f 100644 --- a/stratosphere/fatal/source/fatal_task_clock.cpp +++ b/stratosphere/fatal/source/fatal_task_clock.cpp @@ -17,6 +17,31 @@ #include #include "fatal_task_clock.hpp" +Result AdjustClockTask::AdjustClockForModule(PcvModule module, u32 hz) { + Result rc; + + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_800) { + /* On 8.0.0+, convert to module id + use clkrst API. */ + PcvModuleId module_id; + if (R_FAILED((rc = pcvGetModuleId(&module_id, module)))) { + return rc; + } + + ClkrstSession session; + Result rc = clkrstOpenSession(&session, module_id, 3); + if (R_FAILED(rc)) { + return rc; + } + ON_SCOPE_EXIT { clkrstCloseSession(&session); }; + + rc = clkrstSetClockRate(&session, hz); + } else { + /* On 1.0.0-7.0.1, use pcv API. */ + rc = pcvSetClockRate(module, hz); + } + + return rc; +} Result AdjustClockTask::AdjustClock() { /* Fatal sets the CPU to 1020MHz, the GPU to 307 MHz, and the EMC to 1331MHz. */ @@ -24,19 +49,19 @@ Result AdjustClockTask::AdjustClock() { constexpr u32 GPU_CLOCK_307MHZ = 0x124F8000L; constexpr u32 EMC_CLOCK_1331MHZ = 0x4F588000L; Result rc = ResultSuccess; - - if (R_FAILED((rc = pcvSetClockRate(PcvModule_Cpu, CPU_CLOCK_1020MHZ)))) { + + if (R_FAILED((rc = AdjustClockForModule(PcvModule_CpuBus, CPU_CLOCK_1020MHZ)))) { return rc; } - - if (R_FAILED((rc = pcvSetClockRate(PcvModule_Gpu, GPU_CLOCK_307MHZ)))) { + + if (R_FAILED((rc = AdjustClockForModule(PcvModule_GPU, GPU_CLOCK_307MHZ)))) { return rc; } - - if (R_FAILED((rc = pcvSetClockRate(PcvModule_Emc, EMC_CLOCK_1331MHZ)))) { + + if (R_FAILED((rc = AdjustClockForModule(PcvModule_EMC, EMC_CLOCK_1331MHZ)))) { return rc; } - + return rc; } diff --git a/stratosphere/fatal/source/fatal_task_clock.hpp b/stratosphere/fatal/source/fatal_task_clock.hpp index 9bbbe4d82..942196cad 100644 --- a/stratosphere/fatal/source/fatal_task_clock.hpp +++ b/stratosphere/fatal/source/fatal_task_clock.hpp @@ -21,6 +21,7 @@ class AdjustClockTask : public IFatalTask { private: + Result AdjustClockForModule(PcvModule module, u32 hz); Result AdjustClock(); public: AdjustClockTask(FatalThrowContext *ctx, u64 title_id) : IFatalTask(ctx, title_id) { } From bfa84e27c13da8b1e6f84565bf613b0f0ef649f0 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Apr 2019 21:00:39 -0700 Subject: [PATCH 43/64] spl: implement CryptoService. --- .../spl/source/spl_crypto_service.cpp | 56 +++ .../spl/source/spl_crypto_service.hpp | 64 ++++ stratosphere/spl/source/spl_ctr_drbg.cpp | 18 +- stratosphere/spl/source/spl_ctr_drbg.hpp | 4 +- .../spl/source/spl_general_service.cpp | 14 +- .../spl/source/spl_general_service.hpp | 8 +- stratosphere/spl/source/spl_main.cpp | 5 +- .../spl/source/spl_secmon_wrapper.cpp | 327 ++++++++++++++++-- .../spl/source/spl_secmon_wrapper.hpp | 49 ++- stratosphere/spl/source/spl_smc_wrapper.cpp | 66 ++-- stratosphere/spl/source/spl_smc_wrapper.hpp | 28 +- stratosphere/spl/source/spl_types.hpp | 21 +- 12 files changed, 558 insertions(+), 102 deletions(-) create mode 100644 stratosphere/spl/source/spl_crypto_service.cpp create mode 100644 stratosphere/spl/source/spl_crypto_service.hpp diff --git a/stratosphere/spl/source/spl_crypto_service.cpp b/stratosphere/spl/source/spl_crypto_service.cpp new file mode 100644 index 000000000..53c47242f --- /dev/null +++ b/stratosphere/spl/source/spl_crypto_service.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "spl_crypto_service.hpp" + +Result CryptoService::GenerateAesKek(Out out_access_key, KeySource key_source, u32 generation, u32 option) { + return this->GetSecureMonitorWrapper()->GenerateAesKek(out_access_key.GetPointer(), key_source, generation, option); +} + +Result CryptoService::LoadAesKey(u32 keyslot, AccessKey access_key, KeySource key_source) { + return this->GetSecureMonitorWrapper()->LoadAesKey(keyslot, this, access_key, key_source); +} + +Result CryptoService::GenerateAesKey(Out out_key, AccessKey access_key, KeySource key_source) { + return this->GetSecureMonitorWrapper()->GenerateAesKey(out_key.GetPointer(), access_key, key_source); +} + +Result CryptoService::DecryptAesKey(Out out_key, KeySource key_source, u32 generation, u32 option) { + return this->GetSecureMonitorWrapper()->DecryptAesKey(out_key.GetPointer(), key_source, generation, option); +} + +Result CryptoService::CryptAesCtr(OutBuffer out_buf, u32 keyslot, InBuffer in_buf, IvCtr iv_ctr) { + return this->GetSecureMonitorWrapper()->CryptAesCtr(out_buf.buffer, out_buf.num_elements, keyslot, this, in_buf.buffer, in_buf.num_elements, iv_ctr); +} + +Result CryptoService::ComputeCmac(Out out_cmac, u32 keyslot, InPointer in_buf) { + return this->GetSecureMonitorWrapper()->ComputeCmac(out_cmac.GetPointer(), keyslot, this, in_buf.pointer, in_buf.num_elements); +} + +Result CryptoService::AllocateAesKeyslot(Out out_keyslot) { + return this->GetSecureMonitorWrapper()->AllocateAesKeyslot(out_keyslot.GetPointer(), this); +} + +Result CryptoService::FreeAesKeyslot(u32 keyslot) { + return this->GetSecureMonitorWrapper()->FreeAesKeyslot(keyslot, this); +} + +void CryptoService::GetAesKeyslotAvailableEvent(Out out_hnd) { + out_hnd.SetValue(this->GetSecureMonitorWrapper()->GetAesKeyslotAvailableEventHandle()); +} diff --git a/stratosphere/spl/source/spl_crypto_service.hpp b/stratosphere/spl/source/spl_crypto_service.hpp new file mode 100644 index 000000000..b5e025b17 --- /dev/null +++ b/stratosphere/spl/source/spl_crypto_service.hpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +#include "spl_types.hpp" +#include "spl_general_service.hpp" + +class CryptoService : public GeneralService { + public: + CryptoService(SecureMonitorWrapper *sw) : GeneralService(sw) { + /* ... */ + } + + virtual ~CryptoService() { + this->GetSecureMonitorWrapper()->FreeAesKeyslots(this); + } + protected: + /* Actual commands. */ + virtual Result GenerateAesKek(Out out_access_key, KeySource key_source, u32 generation, u32 option); + virtual Result LoadAesKey(u32 keyslot, AccessKey access_key, KeySource key_source); + virtual Result GenerateAesKey(Out out_key, AccessKey access_key, KeySource key_source); + virtual Result DecryptAesKey(Out out_key, KeySource key_source, u32 generation, u32 option); + virtual Result CryptAesCtr(OutBuffer out_buf, u32 keyslot, InBuffer in_buf, IvCtr iv_ctr); + virtual Result ComputeCmac(Out out_cmac, u32 keyslot, InPointer in_buf); + virtual Result AllocateAesKeyslot(Out out_keyslot); + virtual Result FreeAesKeyslot(u32 keyslot); + virtual void GetAesKeyslotAvailableEvent(Out out_hnd); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + + }; +}; diff --git a/stratosphere/spl/source/spl_ctr_drbg.cpp b/stratosphere/spl/source/spl_ctr_drbg.cpp index 1eadb7e4e..a468d2dcf 100644 --- a/stratosphere/spl/source/spl_ctr_drbg.cpp +++ b/stratosphere/spl/source/spl_ctr_drbg.cpp @@ -26,9 +26,9 @@ void CtrDrbg::Update(const void *data) { IncrementCounter(this->counter); aes128EncryptBlock(&this->aes_ctx, &this->work[1][offset], this->counter); } - + Xor(this->work[1], data, sizeof(this->work[1])); - + std::memcpy(this->key, &this->work[1][0], sizeof(this->key)); std::memcpy(this->counter, &this->work[1][BlockSize], sizeof(this->key)); } @@ -51,31 +51,31 @@ bool CtrDrbg::GenerateRandomBytes(void *out, size_t size) { if (size > MaxRequestSize) { return false; } - + if (this->reseed_counter > ReseedInterval) { return false; } - + aes128ContextCreate(&this->aes_ctx, this->key, true); u8 *cur_dst = reinterpret_cast(out); - + size_t aligned_size = (size & ~(BlockSize - 1)); for (size_t offset = 0; offset < aligned_size; offset += BlockSize) { IncrementCounter(this->counter); aes128EncryptBlock(&this->aes_ctx, cur_dst, this->counter); cur_dst += BlockSize; } - + if (size > aligned_size) { IncrementCounter(this->counter); aes128EncryptBlock(&this->aes_ctx, this->work[1], this->counter); std::memcpy(cur_dst, this->work[1], size - aligned_size); } - + std::memset(this->work[0], 0, sizeof(this->work[0])); this->Update(this->work[0]); - + this->reseed_counter++; return true; - + } \ No newline at end of file diff --git a/stratosphere/spl/source/spl_ctr_drbg.hpp b/stratosphere/spl/source/spl_ctr_drbg.hpp index 71564b582..448a22eab 100644 --- a/stratosphere/spl/source/spl_ctr_drbg.hpp +++ b/stratosphere/spl/source/spl_ctr_drbg.hpp @@ -42,10 +42,10 @@ class CtrDrbg { dst_u8[i] = src_u8[i]; } } - + static void IncrementCounter(void *ctr) { u64 *ctr_64 = reinterpret_cast(ctr); - + ctr_64[1] = __builtin_bswap64(__builtin_bswap64(ctr_64[1]) + 1); if (!ctr_64[1]) { ctr_64[0] = __builtin_bswap64(__builtin_bswap64(ctr_64[0]) + 1); diff --git a/stratosphere/spl/source/spl_general_service.cpp b/stratosphere/spl/source/spl_general_service.cpp index ae0c9bf92..d83a5c5d2 100644 --- a/stratosphere/spl/source/spl_general_service.cpp +++ b/stratosphere/spl/source/spl_general_service.cpp @@ -20,29 +20,29 @@ #include "spl_general_service.hpp" Result GeneralService::GetConfig(Out out, u32 which) { - return this->secmon_wrapper->GetConfig(out.GetPointer(), static_cast(which)); + return this->GetSecureMonitorWrapper()->GetConfig(out.GetPointer(), static_cast(which)); } Result GeneralService::ExpMod(OutPointerWithClientSize out, InPointer base, InPointer exp, InPointer mod) { - return this->secmon_wrapper->ExpMod(out.pointer, out.num_elements, base.pointer, base.num_elements, exp.pointer, exp.num_elements, mod.pointer, mod.num_elements); + return this->GetSecureMonitorWrapper()->ExpMod(out.pointer, out.num_elements, base.pointer, base.num_elements, exp.pointer, exp.num_elements, mod.pointer, mod.num_elements); } Result GeneralService::SetConfig(u32 which, u64 value) { - return this->secmon_wrapper->SetConfig(static_cast(which), value); + return this->GetSecureMonitorWrapper()->SetConfig(static_cast(which), value); } Result GeneralService::GenerateRandomBytes(OutPointerWithClientSize out) { - return this->secmon_wrapper->GenerateRandomBytes(out.pointer, out.num_elements); + return this->GetSecureMonitorWrapper()->GenerateRandomBytes(out.pointer, out.num_elements); } Result GeneralService::IsDevelopment(Out is_dev) { - return this->secmon_wrapper->IsDevelopment(is_dev.GetPointer()); + return this->GetSecureMonitorWrapper()->IsDevelopment(is_dev.GetPointer()); } Result GeneralService::SetBootReason(BootReasonValue boot_reason) { - return this->secmon_wrapper->SetBootReason(boot_reason); + return this->GetSecureMonitorWrapper()->SetBootReason(boot_reason); } Result GeneralService::GetBootReason(Out out) { - return this->secmon_wrapper->GetBootReason(out.GetPointer()); + return this->GetSecureMonitorWrapper()->GetBootReason(out.GetPointer()); } diff --git a/stratosphere/spl/source/spl_general_service.hpp b/stratosphere/spl/source/spl_general_service.hpp index 9f785ee74..6d1879dac 100644 --- a/stratosphere/spl/source/spl_general_service.hpp +++ b/stratosphere/spl/source/spl_general_service.hpp @@ -21,7 +21,7 @@ #include "spl_types.hpp" #include "spl_secmon_wrapper.hpp" -class GeneralService final : public IServiceObject { +class GeneralService : public IServiceObject { private: SecureMonitorWrapper *secmon_wrapper; public: @@ -30,7 +30,11 @@ class GeneralService final : public IServiceObject { } virtual ~GeneralService() { /* ... */ } - private: + protected: + SecureMonitorWrapper *GetSecureMonitorWrapper() const { + return this->secmon_wrapper; + } + protected: /* Actual commands. */ virtual Result GetConfig(Out out, u32 which); virtual Result ExpMod(OutPointerWithClientSize out, InPointer base, InPointer exp, InPointer mod); diff --git a/stratosphere/spl/source/spl_main.cpp b/stratosphere/spl/source/spl_main.cpp index a644814fb..d87872bfc 100644 --- a/stratosphere/spl/source/spl_main.cpp +++ b/stratosphere/spl/source/spl_main.cpp @@ -24,6 +24,7 @@ #include "spl_random_service.hpp" #include "spl_general_service.hpp" +#include "spl_crypto_service.hpp" extern "C" { extern u32 __start__; @@ -83,8 +84,9 @@ struct SplServerOptions { static SecureMonitorWrapper s_secmon_wrapper; /* Helpers for creating services. */ -static const auto MakeRandomService = []() { return std::make_shared(&s_secmon_wrapper); }; +static const auto MakeRandomService = []() { return std::make_shared(&s_secmon_wrapper); }; static const auto MakeGeneralService = []() { return std::make_shared(&s_secmon_wrapper); }; +static const auto MakeCryptoService = []() { return std::make_shared(&s_secmon_wrapper); }; int main(int argc, char **argv) { @@ -100,6 +102,7 @@ int main(int argc, char **argv) s_server_manager.AddWaitable(new ServiceServer("csrng", 3)); if (GetRuntimeFirmwareVersion() >= FirmwareVersion_400) { s_server_manager.AddWaitable(new ServiceServer("spl:", 9)); + s_server_manager.AddWaitable(new ServiceServer("spl:mig", 6)); /* TODO: Other services. */ } else { /* TODO, DeprecatedGeneralService */ diff --git a/stratosphere/spl/source/spl_secmon_wrapper.cpp b/stratosphere/spl/source/spl_secmon_wrapper.cpp index a52c49535..fae5714a1 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.cpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.cpp @@ -24,29 +24,66 @@ /* Convenient. */ constexpr size_t DeviceAddressSpaceAlignSize = 0x400000; constexpr size_t DeviceAddressSpaceAlignMask = DeviceAddressSpaceAlignSize - 1; -constexpr u32 DeviceMapBase = 0x80000000u; +constexpr u32 WorkBufferMapBase = 0x80000000u; +constexpr u32 CryptAesInMapBase = 0x90000000u; +constexpr u32 CryptAesOutMapBase = 0xC0000000u; +constexpr size_t CryptAesSizeMax = static_cast(CryptAesOutMapBase - CryptAesInMapBase); + +/* Types. */ +struct SeLinkedListEntry { + u32 num_entries; + u32 address; + u32 size; +}; + +struct SeCryptContext { + SeLinkedListEntry in; + SeLinkedListEntry out; +}; + +class DeviceAddressSpaceMapHelper { + private: + Handle das_hnd; + u64 dst_addr; + u64 src_addr; + size_t size; + u32 perm; + public: + DeviceAddressSpaceMapHelper(Handle h, u64 dst, u64 src, size_t sz, u32 p) : das_hnd(h), dst_addr(dst), src_addr(src), size(sz), perm(p) { + if (R_FAILED(svcMapDeviceAddressSpaceAligned(this->das_hnd, CUR_PROCESS_HANDLE, this->src_addr, this->size, this->dst_addr, this->perm))) { + std::abort(); + } + } + ~DeviceAddressSpaceMapHelper() { + if (R_FAILED(svcUnmapDeviceAddressSpace(this->das_hnd, CUR_PROCESS_HANDLE, this->src_addr, this->size, this->dst_addr))) { + std::abort(); + } + } +}; /* Globals. */ static CtrDrbg g_drbg; static Event g_se_event; +static IEvent *g_se_keyslot_available_event = nullptr; static Handle g_se_das_hnd; static u32 g_se_mapped_work_buffer_addr; static __attribute__((aligned(0x1000))) u8 g_work_buffer[0x1000]; +constexpr size_t MaxWorkBufferSize = sizeof(g_work_buffer) / 2; -static HosMutex g_se_lock; +static HosMutex g_async_op_lock; void SecureMonitorWrapper::InitializeCtrDrbg() { u8 seed[CtrDrbg::SeedSize]; - + if (SmcWrapper::GenerateRandomBytes(seed, sizeof(seed)) != SmcResult_Success) { std::abort(); } - + g_drbg.Initialize(seed); } -void SecureMonitorWrapper::InitializeSeInterruptEvent() { +void SecureMonitorWrapper::InitializeSeEvents() { u64 irq_num; SmcWrapper::GetConfig(&irq_num, 1, SplConfigItem_SecurityEngineIrqNumber); Handle hnd; @@ -54,6 +91,9 @@ void SecureMonitorWrapper::InitializeSeInterruptEvent() { std::abort(); } eventLoadRemote(&g_se_event, hnd, true); + + g_se_keyslot_available_event = CreateWriteOnlySystemEvent(); + g_se_keyslot_available_event->Signal(); } void SecureMonitorWrapper::InitializeDeviceAddressSpace() { @@ -67,10 +107,10 @@ void SecureMonitorWrapper::InitializeDeviceAddressSpace() { if (R_FAILED(svcAttachDeviceAddressSpace(DeviceName_SE, g_se_das_hnd))) { std::abort(); } - + const u64 work_buffer_addr = reinterpret_cast(g_work_buffer); - g_se_mapped_work_buffer_addr = 0x80000000u + (work_buffer_addr & DeviceAddressSpaceAlignMask); - + g_se_mapped_work_buffer_addr = WorkBufferMapBase + (work_buffer_addr & DeviceAddressSpaceAlignMask); + /* Map the work buffer for the SE. */ if (R_FAILED(svcMapDeviceAddressSpaceAligned(g_se_das_hnd, CUR_PROCESS_HANDLE, work_buffer_addr, sizeof(g_work_buffer), g_se_mapped_work_buffer_addr, 3))) { std::abort(); @@ -80,8 +120,8 @@ void SecureMonitorWrapper::InitializeDeviceAddressSpace() { void SecureMonitorWrapper::Initialize() { /* Initialize the Drbg. */ InitializeCtrDrbg(); - /* Initialize SE interrupt event. */ - InitializeSeInterruptEvent(); + /* Initialize SE interrupt + keyslot events. */ + InitializeSeEvents(); /* Initialize DAS for the SE. */ InitializeDeviceAddressSpace(); } @@ -102,37 +142,78 @@ Result SecureMonitorWrapper::ConvertToSplResult(SmcResult result) { SmcResult SecureMonitorWrapper::WaitCheckStatus(AsyncOperationKey op_key) { WaitSeOperationComplete(); - + SmcResult op_res; SmcResult res = SmcWrapper::CheckStatus(&op_res, op_key); if (res != SmcResult_Success) { return res; } - + return op_res; } SmcResult SecureMonitorWrapper::WaitGetResult(void *out_buf, size_t out_buf_size, AsyncOperationKey op_key) { WaitSeOperationComplete(); - + SmcResult op_res; SmcResult res = SmcWrapper::GetResult(&op_res, out_buf, out_buf_size, op_key); if (res != SmcResult_Success) { return res; } - + return op_res; } +SmcResult SecureMonitorWrapper::DecryptAesBlock(u32 keyslot, void *dst, const void *src) { + struct DecryptAesBlockLayout { + SeCryptContext crypt_ctx; + u8 in_block[AES_BLOCK_SIZE] __attribute__((aligned(AES_BLOCK_SIZE))); + u8 out_block[AES_BLOCK_SIZE] __attribute__((aligned(AES_BLOCK_SIZE))); + }; + DecryptAesBlockLayout *layout = reinterpret_cast(g_work_buffer); + + layout->crypt_ctx.in.num_entries = 0; + layout->crypt_ctx.in.address = g_se_mapped_work_buffer_addr + offsetof(DecryptAesBlockLayout, in_block); + layout->crypt_ctx.in.size = sizeof(layout->in_block); + layout->crypt_ctx.out.num_entries = 0; + layout->crypt_ctx.out.address = g_se_mapped_work_buffer_addr + offsetof(DecryptAesBlockLayout, out_block); + layout->crypt_ctx.out.size = sizeof(layout->out_block); + + std::memcpy(layout->in_block, src, sizeof(layout->in_block)); + + armDCacheFlush(layout, sizeof(*layout)); + { + std::scoped_lock lk(g_async_op_lock); + AsyncOperationKey op_key; + const IvCtr iv_ctr = {}; + const u32 mode = SmcWrapper::GetCryptAesMode(SmcCipherMode_CbcDecrypt, keyslot); + const u32 dst_ll_addr = g_se_mapped_work_buffer_addr + offsetof(DecryptAesBlockLayout, crypt_ctx.out); + const u32 src_ll_addr = g_se_mapped_work_buffer_addr + offsetof(DecryptAesBlockLayout, crypt_ctx.in); + + SmcResult res = SmcWrapper::CryptAes(&op_key, mode, iv_ctr, dst_ll_addr, src_ll_addr, sizeof(layout->in_block)); + if (res != SmcResult_Success) { + return res; + } + + if ((res = WaitCheckStatus(op_key)) != SmcResult_Success) { + return res; + } + } + armDCacheFlush(layout, sizeof(*layout)); + + std::memcpy(dst, layout->out_block, sizeof(layout->out_block)); + return SmcResult_Success; +} + Result SecureMonitorWrapper::GetConfig(u64 *out, SplConfigItem which) { /* Nintendo explicitly blacklists package2 hash here, amusingly. */ /* This is not blacklisted in safemode, but we're never in safe mode... */ if (which == SplConfigItem_Package2Hash) { return ResultSplInvalidArgument; } - + SmcResult res = SmcWrapper::GetConfig(out, 1, which); - + /* Nintendo has some special handling here for hardware type/is_retail. */ if (which == SplConfigItem_HardwareType && res == SmcResult_InvalidArgument) { *out = 0; @@ -142,7 +223,7 @@ Result SecureMonitorWrapper::GetConfig(u64 *out, SplConfigItem which) { *out = 0; res = SmcResult_Success; } - + return ConvertToSplResult(res); } @@ -153,7 +234,7 @@ Result SecureMonitorWrapper::ExpMod(void *out, size_t out_size, const void *base u8 mod[0x100]; }; ExpModLayout *layout = reinterpret_cast(g_work_buffer); - + /* Validate sizes. */ if (base_size > sizeof(layout->base)) { return ResultSplInvalidSize; @@ -164,10 +245,10 @@ Result SecureMonitorWrapper::ExpMod(void *out, size_t out_size, const void *base if (mod_size > sizeof(layout->mod)) { return ResultSplInvalidSize; } - if (out_size > sizeof(g_work_buffer) / 2) { + if (out_size > MaxWorkBufferSize) { return ResultSplInvalidSize; } - + /* Copy data into work buffer. */ const size_t base_ofs = sizeof(layout->base) - base_size; const size_t mod_ofs = sizeof(layout->mod) - mod_size; @@ -175,22 +256,24 @@ Result SecureMonitorWrapper::ExpMod(void *out, size_t out_size, const void *base std::memcpy(layout->base + base_ofs, base, base_size); std::memcpy(layout->exp, exp, exp_size); std::memcpy(layout->mod + mod_ofs, mod, mod_size); - + /* Do exp mod operation. */ + armDCacheFlush(layout, sizeof(*layout)); { - std::scoped_lock lk(g_se_lock); + std::scoped_lock lk(g_async_op_lock); AsyncOperationKey op_key; - + SmcResult res = SmcWrapper::ExpMod(&op_key, layout->base, layout->exp, exp_size, layout->mod); if (res != SmcResult_Success) { return ConvertToSplResult(res); } - + if ((res = WaitGetResult(g_work_buffer, out_size, op_key)) != SmcResult_Success) { return ConvertToSplResult(res); } } - + armDCacheFlush(g_work_buffer, sizeof(out_size)); + std::memcpy(out, g_work_buffer, out_size); return ResultSuccess; } @@ -204,17 +287,17 @@ Result SecureMonitorWrapper::GenerateRandomBytesInternal(void *out, size_t size) /* We need to reseed. */ { u8 seed[CtrDrbg::SeedSize]; - + SmcResult res = SmcWrapper::GenerateRandomBytes(seed, sizeof(seed)); if (res != SmcResult_Success) { return ConvertToSplResult(res); } - + g_drbg.Reseed(seed); g_drbg.GenerateRandomBytes(out, size); } } - + return ResultSuccess; } @@ -223,7 +306,7 @@ Result SecureMonitorWrapper::GenerateRandomBytes(void *out, size_t size) { for (size_t ofs = 0; ofs < size; ofs += CtrDrbg::MaxRequestSize) { const size_t cur_size = std::min(size - ofs, CtrDrbg::MaxRequestSize); - + Result rc = GenerateRandomBytesInternal(cur_dst, size); if (R_FAILED(rc)) { return rc; @@ -262,4 +345,190 @@ Result SecureMonitorWrapper::GetBootReason(BootReasonValue *out) { *out = GetBootReason(); return ResultSuccess; +} + +Result SecureMonitorWrapper::GenerateAesKek(AccessKey *out_access_key, const KeySource &key_source, u32 generation, u32 option) { + return ConvertToSplResult(SmcWrapper::GenerateAesKek(out_access_key, key_source, generation, option)); +} + +Result SecureMonitorWrapper::LoadAesKey(u32 keyslot, const void *owner, const AccessKey &access_key, const KeySource &key_source) { + Result rc = ValidateAesKeyslot(keyslot, owner); + if (R_FAILED(rc)) { + return rc; + } + return ConvertToSplResult(SmcWrapper::LoadAesKey(keyslot, access_key, key_source)); +} + +Result SecureMonitorWrapper::GenerateAesKey(AesKey *out_key, const AccessKey &access_key, const KeySource &key_source) { + Result rc; + SmcResult smc_rc; + + static const KeySource s_generate_aes_key_source = { + .data = {0x89, 0x61, 0x5E, 0xE0, 0x5C, 0x31, 0xB6, 0x80, 0x5F, 0xE5, 0x8F, 0x3D, 0xA2, 0x4F, 0x7A, 0xA8} + }; + + ScopedAesKeyslot keyslot_holder(this); + if (R_FAILED((rc = keyslot_holder.Allocate()))) { + return rc; + } + + smc_rc = SmcWrapper::LoadAesKey(keyslot_holder.GetKeyslot(), access_key, s_generate_aes_key_source); + if (smc_rc == SmcResult_Success) { + smc_rc = DecryptAesBlock(keyslot_holder.GetKeyslot(), out_key, &key_source); + } + + return ConvertToSplResult(smc_rc); +} + +Result SecureMonitorWrapper::DecryptAesKey(AesKey *out_key, const KeySource &key_source, u32 generation, u32 option) { + Result rc; + + static const KeySource s_decrypt_aes_key_source = { + .data = {0x11, 0x70, 0x24, 0x2B, 0x48, 0x69, 0x11, 0xF1, 0x11, 0xB0, 0x0C, 0x47, 0x7C, 0xC3, 0xEF, 0x7E} + }; + + AccessKey access_key; + if (R_FAILED((rc = GenerateAesKek(&access_key, s_decrypt_aes_key_source, generation, option)))) { + return rc; + } + + return GenerateAesKey(out_key, access_key, key_source); +} + +Result SecureMonitorWrapper::CryptAesCtr(void *dst, size_t dst_size, u32 keyslot, const void *owner, const void *src, size_t src_size, const IvCtr &iv_ctr) { + Result rc = ValidateAesKeyslot(keyslot, owner); + if (R_FAILED(rc)) { + return rc; + } + + /* Succeed immediately if there's nothing to crypt. */ + if (src_size == 0) { + return ResultSuccess; + } + + /* Validate sizes. */ + if (src_size > dst_size || src_size % AES_BLOCK_SIZE != 0) { + return ResultSplInvalidSize; + } + + /* We can only map 0x400000 aligned buffers for the SE. With that in mind, we have some math to do. */ + const uintptr_t src_addr = reinterpret_cast(src); + const uintptr_t dst_addr = reinterpret_cast(dst); + const uintptr_t src_addr_page_aligned = src_addr & ~0xFFFul; + const uintptr_t dst_addr_page_aligned = dst_addr & ~0xFFFul; + const size_t src_size_page_aligned = ((src_addr + src_size + 0xFFFul) & ~0xFFFul) - src_addr_page_aligned; + const size_t dst_size_page_aligned = ((dst_addr + dst_size + 0xFFFul) & ~0xFFFul) - dst_addr_page_aligned; + const u32 src_se_map_addr = CryptAesInMapBase + (src_addr_page_aligned & DeviceAddressSpaceAlignMask); + const u32 dst_se_map_addr = CryptAesOutMapBase + (dst_addr_page_aligned & DeviceAddressSpaceAlignMask); + const u32 src_se_addr = CryptAesInMapBase + (src_addr & DeviceAddressSpaceAlignMask); + const u32 dst_se_addr = CryptAesInMapBase + (dst_addr & DeviceAddressSpaceAlignMask); + + /* Validate aligned sizes. */ + if (src_size_page_aligned > CryptAesSizeMax || dst_size_page_aligned > CryptAesSizeMax) { + return ResultSplInvalidSize; + } + + /* Helpers for mapping/unmapping. */ + DeviceAddressSpaceMapHelper in_mapper(g_se_das_hnd, src_se_map_addr, src_addr_page_aligned, src_size_page_aligned, 1); + DeviceAddressSpaceMapHelper out_mapper(g_se_das_hnd, dst_se_map_addr, dst_addr_page_aligned, dst_size_page_aligned, 2); + + /* Setup SE linked list entries. */ + SeCryptContext *crypt_ctx = reinterpret_cast(g_work_buffer); + crypt_ctx->in.num_entries = 0; + crypt_ctx->in.address = src_se_addr; + crypt_ctx->in.size = src_size; + crypt_ctx->out.num_entries = 0; + crypt_ctx->out.address = dst_se_addr; + crypt_ctx->out.size = dst_size; + + armDCacheFlush(crypt_ctx, sizeof(*crypt_ctx)); + armDCacheFlush(const_cast(src), src_size); + armDCacheFlush(dst, dst_size); + { + std::scoped_lock lk(g_async_op_lock); + AsyncOperationKey op_key; + const u32 mode = SmcWrapper::GetCryptAesMode(SmcCipherMode_Ctr, keyslot); + const u32 dst_ll_addr = g_se_mapped_work_buffer_addr + offsetof(SeCryptContext, out); + const u32 src_ll_addr = g_se_mapped_work_buffer_addr + offsetof(SeCryptContext, in); + + SmcResult res = SmcWrapper::CryptAes(&op_key, mode, iv_ctr, dst_ll_addr, src_ll_addr, src_size); + if (res != SmcResult_Success) { + return ConvertToSplResult(res); + } + + if ((res = WaitCheckStatus(op_key)) != SmcResult_Success) { + return ConvertToSplResult(res); + } + } + armDCacheFlush(dst, dst_size); + + return ResultSuccess; +} + +Result SecureMonitorWrapper::ComputeCmac(Cmac *out_cmac, u32 keyslot, const void *owner, const void *data, size_t size) { + Result rc = ValidateAesKeyslot(keyslot, owner); + if (R_FAILED(rc)) { + return rc; + } + + if (size > MaxWorkBufferSize) { + return ResultSplInvalidSize; + } + + std::memcpy(g_work_buffer, data, size); + return ConvertToSplResult(SmcWrapper::ComputeCmac(out_cmac, keyslot, g_work_buffer, size)); +} + +Result SecureMonitorWrapper::AllocateAesKeyslot(u32 *out_keyslot, const void *owner) { + for (size_t i = 0; i < GetMaxKeyslots(); i++) { + if (this->keyslot_owners[i] == 0) { + this->keyslot_owners[i] = owner; + *out_keyslot = static_cast(i); + return ResultSuccess; + } + } + + g_se_keyslot_available_event->Clear(); + return ResultSplOutOfKeyslots; +} + +Result SecureMonitorWrapper::ValidateAesKeyslot(u32 keyslot, const void *owner) { + if (keyslot >= GetMaxKeyslots()) { + return ResultSplInvalidKeyslot; + } + if (this->keyslot_owners[keyslot] != owner) { + return ResultSplInvalidKeyslot; + } + return ResultSuccess; +} + +Result SecureMonitorWrapper::FreeAesKeyslot(u32 keyslot, const void *owner) { + Result rc = ValidateAesKeyslot(keyslot, owner); + if (R_FAILED(rc)) { + return rc; + } + + /* Clear the keyslot. */ + { + AccessKey access_key = {}; + KeySource key_source = {}; + + SmcWrapper::LoadAesKey(keyslot, access_key, key_source); + } + this->keyslot_owners[keyslot] = nullptr; + g_se_keyslot_available_event->Signal(); + return ResultSuccess; +} + +Result SecureMonitorWrapper::FreeAesKeyslots(const void *owner) { + for (size_t i = 0; i < GetMaxKeyslots(); i++) { + if (this->keyslot_owners[i] == owner) { + FreeAesKeyslot(i, owner); + } + } + return ResultSuccess; +} + +Handle SecureMonitorWrapper::GetAesKeyslotAvailableEventHandle() { + return g_se_keyslot_available_event->GetHandle(); } \ No newline at end of file diff --git a/stratosphere/spl/source/spl_secmon_wrapper.hpp b/stratosphere/spl/source/spl_secmon_wrapper.hpp index cfaf123ca..758629485 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.hpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.hpp @@ -25,7 +25,7 @@ class SecureMonitorWrapper { static constexpr size_t MaxAesKeyslots = 6; static constexpr size_t MaxAesKeyslotsDeprecated = 4; private: - uintptr_t keyslot_owners[MaxAesKeyslots] = {}; + const void *keyslot_owners[MaxAesKeyslots] = {}; BootReasonValue boot_reason = {}; bool boot_reason_set = false; private: @@ -42,7 +42,7 @@ class SecureMonitorWrapper { static Result ConvertToSplResult(SmcResult result); private: static void InitializeCtrDrbg(); - static void InitializeSeInterruptEvent(); + static void InitializeSeEvents(); static void InitializeDeviceAddressSpace(); public: static void Initialize(); @@ -51,7 +51,10 @@ class SecureMonitorWrapper { void WaitSeOperationComplete(); SmcResult WaitCheckStatus(AsyncOperationKey op_key); SmcResult WaitGetResult(void *out_buf, size_t out_buf_size, AsyncOperationKey op_key); + Result ValidateAesKeyslot(u32 keyslot, const void *owner); + SmcResult DecryptAesBlock(u32 keyslot, void *dst, const void *src); public: + /* General. */ Result GetConfig(u64 *out, SplConfigItem which); Result ExpMod(void *out, size_t out_size, const void *base, size_t base_size, const void *exp, size_t exp_size, const void *mod, size_t mod_size); Result SetConfig(SplConfigItem which, u64 value); @@ -59,4 +62,46 @@ class SecureMonitorWrapper { Result IsDevelopment(bool *out); Result SetBootReason(BootReasonValue boot_reason); Result GetBootReason(BootReasonValue *out); + + /* Crypto. */ + Result GenerateAesKek(AccessKey *out_access_key, const KeySource &key_source, u32 generation, u32 option); + Result LoadAesKey(u32 keyslot, const void *owner, const AccessKey &access_key, const KeySource &key_source); + Result GenerateAesKey(AesKey *out_key, const AccessKey &access_key, const KeySource &key_source); + Result DecryptAesKey(AesKey *out_key, const KeySource &key_source, u32 generation, u32 option); + Result CryptAesCtr(void *dst, size_t dst_size, u32 keyslot, const void *owner, const void *src, size_t src_size, const IvCtr &iv_ctr); + Result ComputeCmac(Cmac *out_cmac, u32 keyslot, const void *owner, const void *data, size_t size); + Result AllocateAesKeyslot(u32 *out_keyslot, const void *owner); + Result FreeAesKeyslot(u32 keyslot, const void *owner); + + /* Helper. */ + Result FreeAesKeyslots(const void *owner); + Handle GetAesKeyslotAvailableEventHandle(); + private: + class ScopedAesKeyslot { + private: + SecureMonitorWrapper *secmon_wrapper; + u32 slot; + bool has_slot; + public: + ScopedAesKeyslot(SecureMonitorWrapper *sw) : secmon_wrapper(sw), slot(0), has_slot(false) { + /* ... */ + } + ~ScopedAesKeyslot() { + if (has_slot) { + this->secmon_wrapper->FreeAesKeyslot(slot, this); + } + } + + u32 GetKeyslot() const { + return this->slot; + } + + Result Allocate() { + Result rc = this->secmon_wrapper->AllocateAesKeyslot(&this->slot, this); + if (R_SUCCEEDED(rc)) { + this->has_slot = true; + } + return rc; + } + }; }; diff --git a/stratosphere/spl/source/spl_smc_wrapper.cpp b/stratosphere/spl/source/spl_smc_wrapper.cpp index 97e39afc5..8f39e87ec 100644 --- a/stratosphere/spl/source/spl_smc_wrapper.cpp +++ b/stratosphere/spl/source/spl_smc_wrapper.cpp @@ -123,12 +123,12 @@ SmcResult SmcWrapper::GenerateRandomBytes(void *out, size_t size) { return static_cast(args.X[0]); } -SmcResult SmcWrapper::GenerateAesKek(AccessKey *out, const u64 *source, u32 generation, u32 option) { +SmcResult SmcWrapper::GenerateAesKek(AccessKey *out, const KeySource &source, u32 generation, u32 option) { SecmonArgs args; args.X[0] = SmcFunctionId_GenerateAesKek; - args.X[1] = source[0]; - args.X[2] = source[1]; + args.X[1] = source.data64[0]; + args.X[2] = source.data64[1]; args.X[3] = generation; args.X[4] = option; svcCallSecureMonitor(&args); @@ -138,27 +138,27 @@ SmcResult SmcWrapper::GenerateAesKek(AccessKey *out, const u64 *source, u32 gene return static_cast(args.X[0]); } -SmcResult SmcWrapper::LoadAesKey(u32 keyslot, const AccessKey &access_key, const u64 *source) { +SmcResult SmcWrapper::LoadAesKey(u32 keyslot, const AccessKey &access_key, const KeySource &source) { SecmonArgs args; args.X[0] = SmcFunctionId_LoadAesKey; args.X[1] = keyslot; args.X[2] = access_key.data64[0]; args.X[3] = access_key.data64[1]; - args.X[4] = source[0]; - args.X[5] = source[1]; + args.X[4] = source.data64[0]; + args.X[5] = source.data64[1]; svcCallSecureMonitor(&args); return static_cast(args.X[0]); } -SmcResult SmcWrapper::CryptAes(AsyncOperationKey *out_op, u32 mode, const u64 *iv_ctr, u32 dst_addr, u32 src_addr, size_t size) { +SmcResult SmcWrapper::CryptAes(AsyncOperationKey *out_op, u32 mode, const IvCtr &iv_ctr, u32 dst_addr, u32 src_addr, size_t size) { SecmonArgs args; args.X[0] = SmcFunctionId_CryptAes; args.X[1] = mode; - args.X[2] = iv_ctr[0]; - args.X[3] = iv_ctr[1]; + args.X[2] = iv_ctr.data64[0]; + args.X[3] = iv_ctr.data64[1]; args.X[4] = src_addr; args.X[5] = dst_addr; args.X[6] = size; @@ -168,12 +168,12 @@ SmcResult SmcWrapper::CryptAes(AsyncOperationKey *out_op, u32 mode, const u64 *i return static_cast(args.X[0]); } -SmcResult SmcWrapper::GenerateSpecificAesKey(u64 *out, const u64 *source, u32 generation, u32 which) { +SmcResult SmcWrapper::GenerateSpecificAesKey(u64 *out, const KeySource &source, u32 generation, u32 which) { SecmonArgs args; args.X[0] = SmcFunctionId_GenerateSpecificAesKey; - args.X[1] = source[0]; - args.X[2] = source[1]; + args.X[1] = source.data64[0]; + args.X[2] = source.data64[1]; args.X[3] = generation; args.X[4] = which; svcCallSecureMonitor(&args); @@ -181,7 +181,7 @@ SmcResult SmcWrapper::GenerateSpecificAesKey(u64 *out, const u64 *source, u32 ge return static_cast(args.X[0]); } -SmcResult SmcWrapper::ComputeCmac(Cmac &out_mac, u32 keyslot, const void *data, size_t size) { +SmcResult SmcWrapper::ComputeCmac(Cmac *out_mac, u32 keyslot, const void *data, size_t size) { SecmonArgs args; args.X[0] = SmcFunctionId_ComputeCmac; @@ -190,12 +190,12 @@ SmcResult SmcWrapper::ComputeCmac(Cmac &out_mac, u32 keyslot, const void *data, args.X[3] = size; svcCallSecureMonitor(&args); - out_mac.data64[0] = args.X[1]; - out_mac.data64[1] = args.X[2]; + out_mac->data64[0] = args.X[1]; + out_mac->data64[1] = args.X[2]; return static_cast(args.X[0]); } -SmcResult SmcWrapper::ReEncryptRsaPrivateKey(void *data, size_t size, const AccessKey &access_key_dec, const u64 *source_dec, const AccessKey &access_key_enc, const u64 *source_enc, u32 option) { +SmcResult SmcWrapper::ReEncryptRsaPrivateKey(void *data, size_t size, const AccessKey &access_key_dec, const KeySource &source_dec, const AccessKey &access_key_enc, const KeySource &source_enc, u32 option) { SecmonArgs args; args.X[0] = SmcFunctionId_ReEncryptRsaPrivateKey; @@ -204,14 +204,14 @@ SmcResult SmcWrapper::ReEncryptRsaPrivateKey(void *data, size_t size, const Acce args.X[3] = option; args.X[4] = reinterpret_cast(data); args.X[5] = size; - args.X[6] = reinterpret_cast(source_dec); - args.X[7] = reinterpret_cast(source_enc); + args.X[6] = reinterpret_cast(&source_dec); + args.X[7] = reinterpret_cast(&source_enc); svcCallSecureMonitor(&args); return static_cast(args.X[0]); } -SmcResult SmcWrapper::DecryptOrImportRsaPrivateKey(void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option) { +SmcResult SmcWrapper::DecryptOrImportRsaPrivateKey(void *data, size_t size, const AccessKey &access_key, const KeySource &source, u32 option) { SecmonArgs args; args.X[0] = SmcFunctionId_DecryptOrImportRsaPrivateKey; @@ -220,8 +220,8 @@ SmcResult SmcWrapper::DecryptOrImportRsaPrivateKey(void *data, size_t size, cons args.X[3] = option; args.X[4] = reinterpret_cast(data); args.X[5] = size; - args.X[6] = source[0]; - args.X[7] = source[1]; + args.X[6] = source.data64[0]; + args.X[7] = source.data64[1]; svcCallSecureMonitor(&args); return static_cast(args.X[0]); @@ -267,12 +267,12 @@ SmcResult SmcWrapper::LoadTitleKey(u32 keyslot, const AccessKey &access_key) { return static_cast(args.X[0]); } -SmcResult SmcWrapper::UnwrapCommonTitleKey(AccessKey *out, const u64 *source, u32 generation) { +SmcResult SmcWrapper::UnwrapCommonTitleKey(AccessKey *out, const KeySource &source, u32 generation) { SecmonArgs args; args.X[0] = SmcFunctionId_UnwrapCommonTitleKey; - args.X[1] = source[0]; - args.X[2] = source[1]; + args.X[1] = source.data64[0]; + args.X[2] = source.data64[1]; args.X[3] = generation; svcCallSecureMonitor(&args); @@ -283,7 +283,7 @@ SmcResult SmcWrapper::UnwrapCommonTitleKey(AccessKey *out, const u64 *source, u3 /* Deprecated functions. */ -SmcResult SmcWrapper::ImportEsKey(const void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option) { +SmcResult SmcWrapper::ImportEsKey(const void *data, size_t size, const AccessKey &access_key, const KeySource &source, u32 option) { SecmonArgs args; args.X[0] = SmcFunctionId_ImportEsKey; @@ -292,14 +292,14 @@ SmcResult SmcWrapper::ImportEsKey(const void *data, size_t size, const AccessKey args.X[3] = option; args.X[4] = reinterpret_cast(data); args.X[5] = size; - args.X[6] = source[0]; - args.X[7] = source[1]; + args.X[6] = source.data64[0]; + args.X[7] = source.data64[1]; svcCallSecureMonitor(&args); return static_cast(args.X[0]); } -SmcResult SmcWrapper::DecryptRsaPrivateKey(size_t *out_size, void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option) { +SmcResult SmcWrapper::DecryptRsaPrivateKey(size_t *out_size, void *data, size_t size, const AccessKey &access_key, const KeySource &source, u32 option) { SecmonArgs args; args.X[0] = SmcFunctionId_DecryptRsaPrivateKey; @@ -308,15 +308,15 @@ SmcResult SmcWrapper::DecryptRsaPrivateKey(size_t *out_size, void *data, size_t args.X[3] = option; args.X[4] = reinterpret_cast(data); args.X[5] = size; - args.X[6] = source[0]; - args.X[7] = source[1]; + args.X[6] = source.data64[0]; + args.X[7] = source.data64[1]; svcCallSecureMonitor(&args); *out_size = static_cast(args.X[1]); return static_cast(args.X[0]); } -SmcResult SmcWrapper::ImportSecureExpModKey(const void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option) { +SmcResult SmcWrapper::ImportSecureExpModKey(const void *data, size_t size, const AccessKey &access_key, const KeySource &source, u32 option) { SecmonArgs args; args.X[0] = SmcFunctionId_ImportSecureExpModKey; @@ -325,8 +325,8 @@ SmcResult SmcWrapper::ImportSecureExpModKey(const void *data, size_t size, const args.X[3] = option; args.X[4] = reinterpret_cast(data); args.X[5] = size; - args.X[6] = source[0]; - args.X[7] = source[1]; + args.X[6] = source.data64[0]; + args.X[7] = source.data64[1]; svcCallSecureMonitor(&args); return static_cast(args.X[0]); diff --git a/stratosphere/spl/source/spl_smc_wrapper.hpp b/stratosphere/spl/source/spl_smc_wrapper.hpp index 41a97cf40..427b4c7c7 100644 --- a/stratosphere/spl/source/spl_smc_wrapper.hpp +++ b/stratosphere/spl/source/spl_smc_wrapper.hpp @@ -21,6 +21,10 @@ #include "spl_types.hpp" class SmcWrapper { + public: + static inline u32 GetCryptAesMode(SmcCipherMode mode, u32 keyslot) { + return static_cast((mode << 4) | (keyslot & 7)); + } public: static SmcResult SetConfig(SplConfigItem which, const u64 *value, size_t num_qwords); static SmcResult GetConfig(u64 *out, size_t num_qwords, SplConfigItem which); @@ -28,20 +32,20 @@ class SmcWrapper { static SmcResult GetResult(SmcResult *out, void *out_buf, size_t out_buf_size, AsyncOperationKey op); static SmcResult ExpMod(AsyncOperationKey *out_op, const void *base, const void *exp, size_t exp_size, const void *mod); static SmcResult GenerateRandomBytes(void *out, size_t size); - static SmcResult GenerateAesKek(AccessKey *out, const u64 *source, u32 generation, u32 option); - static SmcResult LoadAesKey(u32 keyslot, const AccessKey &access_key, const u64 *source); - static SmcResult CryptAes(AsyncOperationKey *out_op, u32 mode, const u64 *iv_ctr, u32 dst_addr, u32 src_addr, size_t size); - static SmcResult GenerateSpecificAesKey(u64 *out, const u64 *source, u32 generation, u32 which); - static SmcResult ComputeCmac(Cmac &out_mac, u32 keyslot, const void *data, size_t size); - static SmcResult ReEncryptRsaPrivateKey(void *data, size_t size, const AccessKey &access_key_dec, const u64 *source_dec, const AccessKey &access_key_enc, const u64 *source_enc, u32 option); - static SmcResult DecryptOrImportRsaPrivateKey(void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option); + static SmcResult GenerateAesKek(AccessKey *out, const KeySource &source, u32 generation, u32 option); + static SmcResult LoadAesKey(u32 keyslot, const AccessKey &access_key, const KeySource &source); + static SmcResult CryptAes(AsyncOperationKey *out_op, u32 mode, const IvCtr &iv_ctr, u32 dst_addr, u32 src_addr, size_t size); + static SmcResult GenerateSpecificAesKey(u64 *out, const KeySource &source, u32 generation, u32 which); + static SmcResult ComputeCmac(Cmac *out_mac, u32 keyslot, const void *data, size_t size); + static SmcResult ReEncryptRsaPrivateKey(void *data, size_t size, const AccessKey &access_key_dec, const KeySource &source_dec, const AccessKey &access_key_enc, const KeySource &source_enc, u32 option); + static SmcResult DecryptOrImportRsaPrivateKey(void *data, size_t size, const AccessKey &access_key, const KeySource &source, u32 option); static SmcResult SecureExpMod(AsyncOperationKey *out_op, const void *base, const void *mod, u32 option); static SmcResult UnwrapTitleKey(AsyncOperationKey *out_op, const void *base, const void *mod, const void *label_digest, size_t label_digest_size, u32 option); static SmcResult LoadTitleKey(u32 keyslot, const AccessKey &access_key); - static SmcResult UnwrapCommonTitleKey(AccessKey *out, const u64 *source, u32 generation); - + static SmcResult UnwrapCommonTitleKey(AccessKey *out, const KeySource &source, u32 generation); + /* Deprecated functions. */ - static SmcResult ImportEsKey(const void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option); - static SmcResult DecryptRsaPrivateKey(size_t *out_size, void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option); - static SmcResult ImportSecureExpModKey(const void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option); + static SmcResult ImportEsKey(const void *data, size_t size, const AccessKey &access_key, const KeySource &source, u32 option); + static SmcResult DecryptRsaPrivateKey(size_t *out_size, void *data, size_t size, const AccessKey &access_key, const KeySource &source, u32 option); + static SmcResult ImportSecureExpModKey(const void *data, size_t size, const AccessKey &access_key, const KeySource &source, u32 option); }; diff --git a/stratosphere/spl/source/spl_types.hpp b/stratosphere/spl/source/spl_types.hpp index 8d8317a48..d7ee64dbe 100644 --- a/stratosphere/spl/source/spl_types.hpp +++ b/stratosphere/spl/source/spl_types.hpp @@ -37,7 +37,6 @@ enum SmcCipherMode : u32 { SmcCipherMode_CbcEncrypt = 0, SmcCipherMode_CbcDecrypt = 1, SmcCipherMode_Ctr = 2, - SmcCipherMode_Cmac = 3, }; enum EsKeyType : u32 { @@ -58,12 +57,20 @@ struct BootReasonValue { static_assert(sizeof(BootReasonValue) == sizeof(u32), "BootReasonValue definition!"); struct AesKey { - u8 data[AES_128_KEY_SIZE]; + union { + u8 data[AES_128_KEY_SIZE]; + u8 data64[AES_128_KEY_SIZE / sizeof(u64)]; + }; }; +static_assert(alignof(AesKey) == alignof(u8), "AesKey definition!"); struct IvCtr { - u8 data[AES_128_KEY_SIZE]; + union { + u8 data[AES_128_KEY_SIZE]; + u8 data64[AES_128_KEY_SIZE / sizeof(u64)]; + }; }; +static_assert(alignof(IvCtr) == alignof(u8), "IvCtr definition!"); struct Cmac { union { @@ -82,8 +89,12 @@ struct AccessKey { static_assert(alignof(AccessKey) == alignof(u8), "AccessKey definition!"); struct KeySource { - u8 data[AES_128_KEY_SIZE]; + union { + u8 data[AES_128_KEY_SIZE]; + u8 data64[AES_128_KEY_SIZE / sizeof(u64)]; + }; }; +static_assert(alignof(AccessKey) == alignof(u8), "KeySource definition!"); enum SplServiceCmd { /* 1.0.0+ */ @@ -110,7 +121,7 @@ enum SplServiceCmd { Spl_Cmd_UnwrapCommonTitleKey = 20, Spl_Cmd_AllocateAesKeyslot = 21, Spl_Cmd_FreeAesKeyslot = 22, - Spl_Cmd_GetAesKeyslotEvent = 23, + Spl_Cmd_GetAesKeyslotAvailableEvent = 23, /* 3.0.0+ */ Spl_Cmd_SetBootReason = 24, From 9ea1a2a941362cb4f1e092ce51cf0c86dae71588 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Apr 2019 21:42:39 -0700 Subject: [PATCH 44/64] spl: Implement RsaService --- stratosphere/spl/source/spl_rsa_service.cpp | 24 ++++++++ stratosphere/spl/source/spl_rsa_service.hpp | 57 +++++++++++++++++++ .../spl/source/spl_secmon_wrapper.cpp | 35 ++++++++++++ .../spl/source/spl_secmon_wrapper.hpp | 3 + stratosphere/spl/source/spl_types.hpp | 14 +++++ 5 files changed, 133 insertions(+) create mode 100644 stratosphere/spl/source/spl_rsa_service.cpp create mode 100644 stratosphere/spl/source/spl_rsa_service.hpp diff --git a/stratosphere/spl/source/spl_rsa_service.cpp b/stratosphere/spl/source/spl_rsa_service.cpp new file mode 100644 index 000000000..23849b9de --- /dev/null +++ b/stratosphere/spl/source/spl_rsa_service.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "spl_rsa_service.hpp" + +Result RsaService::DecryptRsaPrivateKey(OutPointerWithClientSize dst, InPointer src, AccessKey access_key, KeySource key_source, u32 option) { + return this->GetSecureMonitorWrapper()->DecryptRsaPrivateKey(dst.pointer, dst.num_elements, src.pointer, src.num_elements, access_key, key_source, option); +} \ No newline at end of file diff --git a/stratosphere/spl/source/spl_rsa_service.hpp b/stratosphere/spl/source/spl_rsa_service.hpp new file mode 100644 index 000000000..c6d90185a --- /dev/null +++ b/stratosphere/spl/source/spl_rsa_service.hpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +#include "spl_types.hpp" +#include "spl_crypto_service.hpp" + +class RsaService : public CryptoService { + public: + RsaService(SecureMonitorWrapper *sw) : CryptoService(sw) { + /* ... */ + } + + virtual ~RsaService() { + /* ... */ + } + protected: + /* Actual commands. */ + virtual Result DecryptRsaPrivateKey(OutPointerWithClientSize dst, InPointer src, AccessKey access_key, KeySource key_source, u32 option); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + + }; +}; diff --git a/stratosphere/spl/source/spl_secmon_wrapper.cpp b/stratosphere/spl/source/spl_secmon_wrapper.cpp index fae5714a1..efc9eb6fd 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.cpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.cpp @@ -29,6 +29,9 @@ constexpr u32 CryptAesInMapBase = 0x90000000u; constexpr u32 CryptAesOutMapBase = 0xC0000000u; constexpr size_t CryptAesSizeMax = static_cast(CryptAesOutMapBase - CryptAesInMapBase); +constexpr size_t RsaPrivateKeySize = 0x100; +constexpr size_t RsaPrivateKeyMetaSize = 0x30; + /* Types. */ struct SeLinkedListEntry { u32 num_entries; @@ -520,6 +523,38 @@ Result SecureMonitorWrapper::FreeAesKeyslot(u32 keyslot, const void *owner) { return ResultSuccess; } +Result SecureMonitorWrapper::DecryptRsaPrivateKey(void *dst, size_t dst_size, const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option) { + struct DecryptRsaPrivateKeyLayout { + u8 data[RsaPrivateKeySize + RsaPrivateKeyMetaSize]; + }; + DecryptRsaPrivateKeyLayout *layout = reinterpret_cast(g_work_buffer); + + /* Validate size. */ + if (src_size < RsaPrivateKeyMetaSize || src_size > sizeof(DecryptRsaPrivateKeyLayout)) { + return ResultSplInvalidSize; + } + + std::memcpy(layout->data, src, src_size); + armDCacheFlush(layout, sizeof(*layout)); + + SmcResult smc_res; + size_t copy_size = 0; + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) { + copy_size = std::min(dst_size, src_size - RsaPrivateKeyMetaSize); + smc_res = SmcWrapper::DecryptOrImportRsaPrivateKey(layout->data, src_size, access_key, key_source, SmcDecryptOrImportMode_DecryptRsaPrivateKey); + } else { + smc_res = SmcWrapper::DecryptRsaPrivateKey(©_size, layout->data, src_size, access_key, key_source, option); + copy_size = std::min(dst_size, copy_size); + } + + armDCacheFlush(layout, sizeof(*layout)); + if (smc_res == SmcResult_Success) { + std::memcpy(dst, layout->data, copy_size); + } + + return ConvertToSplResult(smc_res); +} + Result SecureMonitorWrapper::FreeAesKeyslots(const void *owner) { for (size_t i = 0; i < GetMaxKeyslots(); i++) { if (this->keyslot_owners[i] == owner) { diff --git a/stratosphere/spl/source/spl_secmon_wrapper.hpp b/stratosphere/spl/source/spl_secmon_wrapper.hpp index 758629485..d3285195b 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.hpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.hpp @@ -72,6 +72,9 @@ class SecureMonitorWrapper { Result ComputeCmac(Cmac *out_cmac, u32 keyslot, const void *owner, const void *data, size_t size); Result AllocateAesKeyslot(u32 *out_keyslot, const void *owner); Result FreeAesKeyslot(u32 keyslot, const void *owner); + + /* RSA. */ + Result DecryptRsaPrivateKey(void *dst, size_t dst_size, const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option); /* Helper. */ Result FreeAesKeyslots(const void *owner); diff --git a/stratosphere/spl/source/spl_types.hpp b/stratosphere/spl/source/spl_types.hpp index d7ee64dbe..78c984900 100644 --- a/stratosphere/spl/source/spl_types.hpp +++ b/stratosphere/spl/source/spl_types.hpp @@ -39,6 +39,20 @@ enum SmcCipherMode : u32 { SmcCipherMode_Ctr = 2, }; +enum SmcDecryptOrImportMode : u32 { + SmcDecryptOrImportMode_DecryptRsaPrivateKey = 0, + SmcDecryptOrImportMode_ImportLotusKey = 1, + SmcDecryptOrImportMode_ImportEsKey = 2, + SmcDecryptOrImportMode_ImportSslKey = 3, + SmcDecryptOrImportMode_ImportDrmKey = 4, +}; + +enum SmcSecureExpModMode : u32 { + SmcSecureExpModMode_Lotus = 0, + SmcSecureExpModMode_Ssl = 1, + SmcSecureExpModMode_Drm = 2, +}; + enum EsKeyType : u32 { EsKeyType_TitleKey = 0, EsKeyType_ElicenseKey = 1, From f4a8124dc3afcbb3433ac1d603ab41ddce933024 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Apr 2019 23:10:13 -0700 Subject: [PATCH 45/64] spl: implement SslService, some of EsService --- stratosphere/spl/source/spl_es_service.cpp | 52 +++++++++ stratosphere/spl/source/spl_es_service.hpp | 69 +++++++++++ .../spl/source/spl_secmon_wrapper.cpp | 107 ++++++++++++++++++ .../spl/source/spl_secmon_wrapper.hpp | 13 ++- stratosphere/spl/source/spl_ssl_service.cpp | 28 +++++ stratosphere/spl/source/spl_ssl_service.hpp | 60 ++++++++++ 6 files changed, 328 insertions(+), 1 deletion(-) create mode 100644 stratosphere/spl/source/spl_es_service.cpp create mode 100644 stratosphere/spl/source/spl_es_service.hpp create mode 100644 stratosphere/spl/source/spl_ssl_service.cpp create mode 100644 stratosphere/spl/source/spl_ssl_service.hpp diff --git a/stratosphere/spl/source/spl_es_service.cpp b/stratosphere/spl/source/spl_es_service.cpp new file mode 100644 index 000000000..20c2f8d10 --- /dev/null +++ b/stratosphere/spl/source/spl_es_service.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "spl_es_service.hpp" + +Result EsService::ImportEsKey(InPointer src, AccessKey access_key, KeySource key_source, u32 option) { + return this->GetSecureMonitorWrapper()->ImportEsKey(src.pointer, src.num_elements, access_key, key_source, option); +} + +Result EsService::UnwrapTitleKey(Out out_access_key, InPointer base, InPointer mod, InPointer label_digest, u32 generation) { + /* TODO */ + return ResultKernelConnectionClosed; +} + +Result EsService::UnwrapCommonTitleKey(Out out_access_key, KeySource key_source, u32 generation) { + /* TODO */ + return ResultKernelConnectionClosed; +} + +Result EsService::ImportDrmKey(InPointer src, AccessKey access_key, KeySource key_source) { + return this->GetSecureMonitorWrapper()->ImportDrmKey(src.pointer, src.num_elements, access_key, key_source); +} + +Result EsService::DrmExpMod(OutPointerWithClientSize out, InPointer base, InPointer mod) { + return this->GetSecureMonitorWrapper()->DrmExpMod(out.pointer, out.num_elements, base.pointer, base.num_elements, mod.pointer, mod.num_elements); +} + +Result EsService::UnwrapElicenseKey(Out out_access_key, InPointer base, InPointer mod, InPointer label_digest, u32 generation) { + /* TODO */ + return ResultKernelConnectionClosed; +} + +Result EsService::LoadElicenseKey(u32 keyslot, AccessKey access_key) { + /* TODO */ + return ResultKernelConnectionClosed; +} diff --git a/stratosphere/spl/source/spl_es_service.hpp b/stratosphere/spl/source/spl_es_service.hpp new file mode 100644 index 000000000..17f8efa0f --- /dev/null +++ b/stratosphere/spl/source/spl_es_service.hpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +#include "spl_types.hpp" +#include "spl_rsa_service.hpp" + +class EsService : public RsaService { + public: + EsService(SecureMonitorWrapper *sw) : RsaService(sw) { + /* ... */ + } + + virtual ~EsService() { + /* ... */ + } + protected: + /* Actual commands. */ + virtual Result ImportEsKey(InPointer src, AccessKey access_key, KeySource key_source, u32 option); + virtual Result UnwrapTitleKey(Out out_access_key, InPointer base, InPointer mod, InPointer label_digest, u32 generation); + virtual Result UnwrapCommonTitleKey(Out out_access_key, KeySource key_source, u32 generation); + virtual Result ImportDrmKey(InPointer src, AccessKey access_key, KeySource key_source); + virtual Result DrmExpMod(OutPointerWithClientSize out, InPointer base, InPointer mod); + virtual Result UnwrapElicenseKey(Out out_access_key, InPointer base, InPointer mod, InPointer label_digest, u32 generation); + virtual Result LoadElicenseKey(u32 keyslot, AccessKey access_key); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + }; +}; diff --git a/stratosphere/spl/source/spl_secmon_wrapper.cpp b/stratosphere/spl/source/spl_secmon_wrapper.cpp index efc9eb6fd..bcd36e479 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.cpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.cpp @@ -555,6 +555,113 @@ Result SecureMonitorWrapper::DecryptRsaPrivateKey(void *dst, size_t dst_size, co return ConvertToSplResult(smc_res); } +Result SecureMonitorWrapper::ImportSecureExpModKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option) { + struct ImportSecureExpModKeyLayout { + u8 data[RsaPrivateKeyMetaSize + 2 * RsaPrivateKeySize]; + }; + ImportSecureExpModKeyLayout *layout = reinterpret_cast(g_work_buffer); + + /* Validate size. */ + if (src_size > sizeof(ImportSecureExpModKeyLayout)) { + return ResultSplInvalidSize; + } + + std::memcpy(layout, src, src_size); + + armDCacheFlush(layout, sizeof(*layout)); + SmcResult smc_res; + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) { + smc_res = SmcWrapper::DecryptOrImportRsaPrivateKey(layout->data, src_size, access_key, key_source, option); + } else { + smc_res = SmcWrapper::ImportSecureExpModKey(layout->data, src_size, access_key, key_source, option); + } + + return ConvertToSplResult(smc_res); +} + +Result SecureMonitorWrapper::SecureExpMod(void *out, size_t out_size, const void *base, size_t base_size, const void *mod, size_t mod_size, u32 option) { + struct SecureExpModLayout { + u8 base[0x100]; + u8 mod[0x100]; + }; + SecureExpModLayout *layout = reinterpret_cast(g_work_buffer); + + /* Validate sizes. */ + if (base_size > sizeof(layout->base)) { + return ResultSplInvalidSize; + } + if (mod_size > sizeof(layout->mod)) { + return ResultSplInvalidSize; + } + if (out_size > MaxWorkBufferSize) { + return ResultSplInvalidSize; + } + + /* Copy data into work buffer. */ + const size_t base_ofs = sizeof(layout->base) - base_size; + const size_t mod_ofs = sizeof(layout->mod) - mod_size; + std::memset(layout, 0, sizeof(*layout)); + std::memcpy(layout->base + base_ofs, base, base_size); + std::memcpy(layout->mod + mod_ofs, mod, mod_size); + + /* Do exp mod operation. */ + armDCacheFlush(layout, sizeof(*layout)); + { + std::scoped_lock lk(g_async_op_lock); + AsyncOperationKey op_key; + + SmcResult res = SmcWrapper::SecureExpMod(&op_key, layout->base, layout->mod, option); + if (res != SmcResult_Success) { + return ConvertToSplResult(res); + } + + if ((res = WaitGetResult(g_work_buffer, out_size, op_key)) != SmcResult_Success) { + return ConvertToSplResult(res); + } + } + armDCacheFlush(g_work_buffer, sizeof(out_size)); + + std::memcpy(out, g_work_buffer, out_size); + return ResultSuccess; +} + +Result SecureMonitorWrapper::ImportSslKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source) { + return ImportSecureExpModKey(src, src_size, access_key, key_source, SmcDecryptOrImportMode_ImportSslKey); +} + +Result SecureMonitorWrapper::SslExpMod(void *out, size_t out_size, const void *base, size_t base_size, const void *mod, size_t mod_size) { + return SecureExpMod(out, out_size, base, base_size, mod, mod_size, SmcSecureExpModMode_Ssl); +} + +Result SecureMonitorWrapper::ImportEsKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option) { + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) { + return ImportSecureExpModKey(src, src_size, access_key, key_source, SmcDecryptOrImportMode_ImportEsKey); + } else { + struct ImportEsKeyLayout { + u8 data[RsaPrivateKeyMetaSize + 2 * RsaPrivateKeySize]; + }; + ImportEsKeyLayout *layout = reinterpret_cast(g_work_buffer); + + /* Validate size. */ + if (src_size > sizeof(ImportEsKeyLayout)) { + return ResultSplInvalidSize; + } + + std::memcpy(layout, src, src_size); + + armDCacheFlush(layout, sizeof(*layout)); + return ConvertToSplResult(SmcWrapper::ImportEsKey(layout->data, src_size, access_key, key_source, option)); + } +} + +Result SecureMonitorWrapper::ImportDrmKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source) { + return ImportSecureExpModKey(src, src_size, access_key, key_source, SmcDecryptOrImportMode_ImportDrmKey); +} + +Result SecureMonitorWrapper::DrmExpMod(void *out, size_t out_size, const void *base, size_t base_size, const void *mod, size_t mod_size) { + return SecureExpMod(out, out_size, base, base_size, mod, mod_size, SmcSecureExpModMode_Drm); +} + Result SecureMonitorWrapper::FreeAesKeyslots(const void *owner) { for (size_t i = 0; i < GetMaxKeyslots(); i++) { if (this->keyslot_owners[i] == owner) { diff --git a/stratosphere/spl/source/spl_secmon_wrapper.hpp b/stratosphere/spl/source/spl_secmon_wrapper.hpp index d3285195b..4b4106139 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.hpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.hpp @@ -53,6 +53,8 @@ class SecureMonitorWrapper { SmcResult WaitGetResult(void *out_buf, size_t out_buf_size, AsyncOperationKey op_key); Result ValidateAesKeyslot(u32 keyslot, const void *owner); SmcResult DecryptAesBlock(u32 keyslot, void *dst, const void *src); + Result ImportSecureExpModKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option); + Result SecureExpMod(void *out, size_t out_size, const void *base, size_t base_size, const void *mod, size_t mod_size, u32 option); public: /* General. */ Result GetConfig(u64 *out, SplConfigItem which); @@ -72,10 +74,19 @@ class SecureMonitorWrapper { Result ComputeCmac(Cmac *out_cmac, u32 keyslot, const void *owner, const void *data, size_t size); Result AllocateAesKeyslot(u32 *out_keyslot, const void *owner); Result FreeAesKeyslot(u32 keyslot, const void *owner); - + /* RSA. */ Result DecryptRsaPrivateKey(void *dst, size_t dst_size, const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option); + /* SSL */ + Result ImportSslKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source); + Result SslExpMod(void *out, size_t out_size, const void *base, size_t base_size, const void *mod, size_t mod_size); + + /* ES */ + Result ImportEsKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option); + Result ImportDrmKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source); + Result DrmExpMod(void *out, size_t out_size, const void *base, size_t base_size, const void *mod, size_t mod_size); + /* Helper. */ Result FreeAesKeyslots(const void *owner); Handle GetAesKeyslotAvailableEventHandle(); diff --git a/stratosphere/spl/source/spl_ssl_service.cpp b/stratosphere/spl/source/spl_ssl_service.cpp new file mode 100644 index 000000000..2572968f1 --- /dev/null +++ b/stratosphere/spl/source/spl_ssl_service.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "spl_ssl_service.hpp" + +Result SslService::ImportSslKey(InPointer src, AccessKey access_key, KeySource key_source) { + return this->GetSecureMonitorWrapper()->ImportSslKey(src.pointer, src.num_elements, access_key, key_source); +} + +Result SslService::SslExpMod(OutPointerWithClientSize out, InPointer base, InPointer mod) { + return this->GetSecureMonitorWrapper()->SslExpMod(out.pointer, out.num_elements, base.pointer, base.num_elements, mod.pointer, mod.num_elements); +} diff --git a/stratosphere/spl/source/spl_ssl_service.hpp b/stratosphere/spl/source/spl_ssl_service.hpp new file mode 100644 index 000000000..5079ba30d --- /dev/null +++ b/stratosphere/spl/source/spl_ssl_service.hpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +#include "spl_types.hpp" +#include "spl_rsa_service.hpp" + +class SslService : public RsaService { + public: + SslService(SecureMonitorWrapper *sw) : RsaService(sw) { + /* ... */ + } + + virtual ~SslService() { + /* ... */ + } + protected: + /* Actual commands. */ + virtual Result ImportSslKey(InPointer src, AccessKey access_key, KeySource key_source); + virtual Result SslExpMod(OutPointerWithClientSize out, InPointer base, InPointer mod); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + + }; +}; From 0a194cb6a6d9dd1f2d93d8c26f8150bbb354617e Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Apr 2019 23:16:47 -0700 Subject: [PATCH 46/64] spl: add spl:ssl/spl:es to main, fix cmd ids --- stratosphere/spl/source/spl_main.cpp | 6 ++++++ stratosphere/spl/source/spl_ssl_service.hpp | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/stratosphere/spl/source/spl_main.cpp b/stratosphere/spl/source/spl_main.cpp index d87872bfc..6fd128033 100644 --- a/stratosphere/spl/source/spl_main.cpp +++ b/stratosphere/spl/source/spl_main.cpp @@ -25,6 +25,8 @@ #include "spl_random_service.hpp" #include "spl_general_service.hpp" #include "spl_crypto_service.hpp" +#include "spl_ssl_service.hpp" +#include "spl_es_service.hpp" extern "C" { extern u32 __start__; @@ -87,6 +89,8 @@ static SecureMonitorWrapper s_secmon_wrapper; static const auto MakeRandomService = []() { return std::make_shared(&s_secmon_wrapper); }; static const auto MakeGeneralService = []() { return std::make_shared(&s_secmon_wrapper); }; static const auto MakeCryptoService = []() { return std::make_shared(&s_secmon_wrapper); }; +static const auto MakeSslService = []() { return std::make_shared(&s_secmon_wrapper); }; +static const auto MakeEsService = []() { return std::make_shared(&s_secmon_wrapper); }; int main(int argc, char **argv) { @@ -103,6 +107,8 @@ int main(int argc, char **argv) if (GetRuntimeFirmwareVersion() >= FirmwareVersion_400) { s_server_manager.AddWaitable(new ServiceServer("spl:", 9)); s_server_manager.AddWaitable(new ServiceServer("spl:mig", 6)); + s_server_manager.AddWaitable(new ServiceServer("spl:ssl", 2)); + s_server_manager.AddWaitable(new ServiceServer("spl:es", 2)); /* TODO: Other services. */ } else { /* TODO, DeprecatedGeneralService */ diff --git a/stratosphere/spl/source/spl_ssl_service.hpp b/stratosphere/spl/source/spl_ssl_service.hpp index 5079ba30d..575109c6f 100644 --- a/stratosphere/spl/source/spl_ssl_service.hpp +++ b/stratosphere/spl/source/spl_ssl_service.hpp @@ -53,8 +53,8 @@ class SslService : public RsaService { MakeServiceCommandMeta(), MakeServiceCommandMeta(), MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), }; }; From 99106076e6a328eb87091208afa0e431f0d73f23 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Apr 2019 23:42:32 -0700 Subject: [PATCH 47/64] spl: Finish implementing EsService. --- stratosphere/spl/source/spl_es_service.cpp | 12 ++-- stratosphere/spl/source/spl_main.cpp | 4 +- .../spl/source/spl_secmon_wrapper.cpp | 72 +++++++++++++++++++ .../spl/source/spl_secmon_wrapper.hpp | 8 +++ stratosphere/spl/source/spl_smc_wrapper.hpp | 3 + 5 files changed, 89 insertions(+), 10 deletions(-) diff --git a/stratosphere/spl/source/spl_es_service.cpp b/stratosphere/spl/source/spl_es_service.cpp index 20c2f8d10..cb855d05e 100644 --- a/stratosphere/spl/source/spl_es_service.cpp +++ b/stratosphere/spl/source/spl_es_service.cpp @@ -24,13 +24,11 @@ Result EsService::ImportEsKey(InPointer src, AccessKey access_key, KeySource } Result EsService::UnwrapTitleKey(Out out_access_key, InPointer base, InPointer mod, InPointer label_digest, u32 generation) { - /* TODO */ - return ResultKernelConnectionClosed; + return this->GetSecureMonitorWrapper()->UnwrapTitleKey(out_access_key.GetPointer(), base.pointer, base.num_elements, mod.pointer, mod.num_elements, label_digest.pointer, label_digest.num_elements, generation); } Result EsService::UnwrapCommonTitleKey(Out out_access_key, KeySource key_source, u32 generation) { - /* TODO */ - return ResultKernelConnectionClosed; + return this->GetSecureMonitorWrapper()->UnwrapCommonTitleKey(out_access_key.GetPointer(), key_source, generation); } Result EsService::ImportDrmKey(InPointer src, AccessKey access_key, KeySource key_source) { @@ -42,11 +40,9 @@ Result EsService::DrmExpMod(OutPointerWithClientSize out, InPointer base } Result EsService::UnwrapElicenseKey(Out out_access_key, InPointer base, InPointer mod, InPointer label_digest, u32 generation) { - /* TODO */ - return ResultKernelConnectionClosed; + return this->GetSecureMonitorWrapper()->UnwrapElicenseKey(out_access_key.GetPointer(), base.pointer, base.num_elements, mod.pointer, mod.num_elements, label_digest.pointer, label_digest.num_elements, generation); } Result EsService::LoadElicenseKey(u32 keyslot, AccessKey access_key) { - /* TODO */ - return ResultKernelConnectionClosed; + return this->GetSecureMonitorWrapper()->LoadElicenseKey(keyslot, this, access_key); } diff --git a/stratosphere/spl/source/spl_main.cpp b/stratosphere/spl/source/spl_main.cpp index 6fd128033..7bf99b4c4 100644 --- a/stratosphere/spl/source/spl_main.cpp +++ b/stratosphere/spl/source/spl_main.cpp @@ -107,8 +107,8 @@ int main(int argc, char **argv) if (GetRuntimeFirmwareVersion() >= FirmwareVersion_400) { s_server_manager.AddWaitable(new ServiceServer("spl:", 9)); s_server_manager.AddWaitable(new ServiceServer("spl:mig", 6)); - s_server_manager.AddWaitable(new ServiceServer("spl:ssl", 2)); - s_server_manager.AddWaitable(new ServiceServer("spl:es", 2)); + s_server_manager.AddWaitable(new ServiceServer("spl:ssl", 2)); + s_server_manager.AddWaitable(new ServiceServer("spl:es", 2)); /* TODO: Other services. */ } else { /* TODO, DeprecatedGeneralService */ diff --git a/stratosphere/spl/source/spl_secmon_wrapper.cpp b/stratosphere/spl/source/spl_secmon_wrapper.cpp index bcd36e479..f46e5b543 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.cpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.cpp @@ -31,6 +31,7 @@ constexpr size_t CryptAesSizeMax = static_cast(CryptAesOutMapBase - Cryp constexpr size_t RsaPrivateKeySize = 0x100; constexpr size_t RsaPrivateKeyMetaSize = 0x30; +constexpr size_t LabelDigestSizeMax = 0x20; /* Types. */ struct SeLinkedListEntry { @@ -654,6 +655,60 @@ Result SecureMonitorWrapper::ImportEsKey(const void *src, size_t src_size, const } } +Result SecureMonitorWrapper::UnwrapEsRsaOaepWrappedKey(AccessKey *out_access_key, const void *base, size_t base_size, const void *mod, size_t mod_size, const void *label_digest, size_t label_digest_size, u32 generation, EsKeyType type) { + struct UnwrapEsKeyLayout { + u8 base[0x100]; + u8 mod[0x100]; + }; + UnwrapEsKeyLayout *layout = reinterpret_cast(g_work_buffer); + + /* Validate sizes. */ + if (base_size > sizeof(layout->base)) { + return ResultSplInvalidSize; + } + if (mod_size > sizeof(layout->mod)) { + return ResultSplInvalidSize; + } + if (label_digest_size > LabelDigestSizeMax) { + return ResultSplInvalidSize; + } + + /* Copy data into work buffer. */ + const size_t base_ofs = sizeof(layout->base) - base_size; + const size_t mod_ofs = sizeof(layout->mod) - mod_size; + std::memset(layout, 0, sizeof(*layout)); + std::memcpy(layout->base + base_ofs, base, base_size); + std::memcpy(layout->mod + mod_ofs, mod, mod_size); + + /* Do exp mod operation. */ + armDCacheFlush(layout, sizeof(*layout)); + { + std::scoped_lock lk(g_async_op_lock); + AsyncOperationKey op_key; + + SmcResult res = SmcWrapper::UnwrapTitleKey(&op_key, layout->base, layout->mod, label_digest, label_digest_size, SmcWrapper::GetUnwrapEsKeyOption(type, generation)); + if (res != SmcResult_Success) { + return ConvertToSplResult(res); + } + + if ((res = WaitGetResult(g_work_buffer, sizeof(*out_access_key), op_key)) != SmcResult_Success) { + return ConvertToSplResult(res); + } + } + armDCacheFlush(g_work_buffer, sizeof(*out_access_key)); + + std::memcpy(out_access_key, g_work_buffer, sizeof(*out_access_key)); + return ResultSuccess; +} + +Result SecureMonitorWrapper::UnwrapTitleKey(AccessKey *out_access_key, const void *base, size_t base_size, const void *mod, size_t mod_size, const void *label_digest, size_t label_digest_size, u32 generation) { + return UnwrapEsRsaOaepWrappedKey(out_access_key, base, base_size, mod, mod_size, label_digest, label_digest_size, generation, EsKeyType_TitleKey); +} + +Result SecureMonitorWrapper::UnwrapCommonTitleKey(AccessKey *out_access_key, const KeySource &key_source, u32 generation) { + return ConvertToSplResult(SmcWrapper::UnwrapCommonTitleKey(out_access_key, key_source, generation)); +} + Result SecureMonitorWrapper::ImportDrmKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source) { return ImportSecureExpModKey(src, src_size, access_key, key_source, SmcDecryptOrImportMode_ImportDrmKey); } @@ -662,6 +717,23 @@ Result SecureMonitorWrapper::DrmExpMod(void *out, size_t out_size, const void *b return SecureExpMod(out, out_size, base, base_size, mod, mod_size, SmcSecureExpModMode_Drm); } +Result SecureMonitorWrapper::UnwrapElicenseKey(AccessKey *out_access_key, const void *base, size_t base_size, const void *mod, size_t mod_size, const void *label_digest, size_t label_digest_size, u32 generation) { + return UnwrapEsRsaOaepWrappedKey(out_access_key, base, base_size, mod, mod_size, label_digest, label_digest_size, generation, EsKeyType_ElicenseKey); +} + +Result SecureMonitorWrapper::LoadElicenseKey(u32 keyslot, const void *owner, const AccessKey &access_key) { + /* Right now, this is just literally the same function as LoadTitleKey in N's impl. */ + return LoadTitleKey(keyslot, owner, access_key); +} + +Result SecureMonitorWrapper::LoadTitleKey(u32 keyslot, const void *owner, const AccessKey &access_key) { + Result rc = ValidateAesKeyslot(keyslot, owner); + if (R_FAILED(rc)) { + return rc; + } + return ConvertToSplResult(SmcWrapper::LoadTitleKey(keyslot, access_key)); +} + Result SecureMonitorWrapper::FreeAesKeyslots(const void *owner) { for (size_t i = 0; i < GetMaxKeyslots(); i++) { if (this->keyslot_owners[i] == owner) { diff --git a/stratosphere/spl/source/spl_secmon_wrapper.hpp b/stratosphere/spl/source/spl_secmon_wrapper.hpp index 4b4106139..3addf70b9 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.hpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.hpp @@ -55,6 +55,7 @@ class SecureMonitorWrapper { SmcResult DecryptAesBlock(u32 keyslot, void *dst, const void *src); Result ImportSecureExpModKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option); Result SecureExpMod(void *out, size_t out_size, const void *base, size_t base_size, const void *mod, size_t mod_size, u32 option); + Result UnwrapEsRsaOaepWrappedKey(AccessKey *out_access_key, const void *base, size_t base_size, const void *mod, size_t mod_size, const void *label_digest, size_t label_digest_size, u32 generation, EsKeyType type); public: /* General. */ Result GetConfig(u64 *out, SplConfigItem which); @@ -84,8 +85,15 @@ class SecureMonitorWrapper { /* ES */ Result ImportEsKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option); + Result UnwrapTitleKey(AccessKey *out_access_key, const void *base, size_t base_size, const void *mod, size_t mod_size, const void *label_digest, size_t label_digest_size, u32 generation); + Result UnwrapCommonTitleKey(AccessKey *out_access_key, const KeySource &key_source, u32 generation); Result ImportDrmKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source); Result DrmExpMod(void *out, size_t out_size, const void *base, size_t base_size, const void *mod, size_t mod_size); + Result UnwrapElicenseKey(AccessKey *out_access_key, const void *base, size_t base_size, const void *mod, size_t mod_size, const void *label_digest, size_t label_digest_size, u32 generation); + Result LoadElicenseKey(u32 keyslot, const void *owner, const AccessKey &access_key); + + /* FS */ + Result LoadTitleKey(u32 keyslot, const void *owner, const AccessKey &access_key); /* Helper. */ Result FreeAesKeyslots(const void *owner); diff --git a/stratosphere/spl/source/spl_smc_wrapper.hpp b/stratosphere/spl/source/spl_smc_wrapper.hpp index 427b4c7c7..be7b682d7 100644 --- a/stratosphere/spl/source/spl_smc_wrapper.hpp +++ b/stratosphere/spl/source/spl_smc_wrapper.hpp @@ -25,6 +25,9 @@ class SmcWrapper { static inline u32 GetCryptAesMode(SmcCipherMode mode, u32 keyslot) { return static_cast((mode << 4) | (keyslot & 7)); } + static inline u32 GetUnwrapEsKeyOption(EsKeyType type, u32 generation) { + return static_cast((type << 6) | (generation & 0x3F)); + } public: static SmcResult SetConfig(SplConfigItem which, const u64 *value, size_t num_qwords); static SmcResult GetConfig(u64 *out, size_t num_qwords, SplConfigItem which); From 5633444d5edd48e3582bcba2f6f2ff72e4b0609d Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 25 Apr 2019 00:01:47 -0700 Subject: [PATCH 48/64] spl: implement ManuService --- stratosphere/spl/source/spl_main.cpp | 13 +++-- stratosphere/spl/source/spl_manu_service.cpp | 24 ++++++++ stratosphere/spl/source/spl_manu_service.hpp | 58 +++++++++++++++++++ .../spl/source/spl_secmon_wrapper.cpp | 38 +++++++++++- .../spl/source/spl_secmon_wrapper.hpp | 3 + 5 files changed, 130 insertions(+), 6 deletions(-) create mode 100644 stratosphere/spl/source/spl_manu_service.cpp create mode 100644 stratosphere/spl/source/spl_manu_service.hpp diff --git a/stratosphere/spl/source/spl_main.cpp b/stratosphere/spl/source/spl_main.cpp index 7bf99b4c4..1fa653a73 100644 --- a/stratosphere/spl/source/spl_main.cpp +++ b/stratosphere/spl/source/spl_main.cpp @@ -27,6 +27,7 @@ #include "spl_crypto_service.hpp" #include "spl_ssl_service.hpp" #include "spl_es_service.hpp" +#include "spl_manu_service.hpp" extern "C" { extern u32 __start__; @@ -91,6 +92,7 @@ static const auto MakeGeneralService = []() { return std::make_shared(&s_secmon_wrapper); }; static const auto MakeSslService = []() { return std::make_shared(&s_secmon_wrapper); }; static const auto MakeEsService = []() { return std::make_shared(&s_secmon_wrapper); }; +static const auto MakeManuService = []() { return std::make_shared(&s_secmon_wrapper); }; int main(int argc, char **argv) { @@ -106,10 +108,13 @@ int main(int argc, char **argv) s_server_manager.AddWaitable(new ServiceServer("csrng", 3)); if (GetRuntimeFirmwareVersion() >= FirmwareVersion_400) { s_server_manager.AddWaitable(new ServiceServer("spl:", 9)); - s_server_manager.AddWaitable(new ServiceServer("spl:mig", 6)); - s_server_manager.AddWaitable(new ServiceServer("spl:ssl", 2)); - s_server_manager.AddWaitable(new ServiceServer("spl:es", 2)); - /* TODO: Other services. */ + s_server_manager.AddWaitable(new ServiceServer("spl:mig", 6)); + s_server_manager.AddWaitable(new ServiceServer("spl:ssl", 2)); + s_server_manager.AddWaitable(new ServiceServer("spl:es", 2)); + /* TODO: spl:fs. */ + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) { + s_server_manager.AddWaitable(new ServiceServer("spl:manu", 1)); + } } else { /* TODO, DeprecatedGeneralService */ } diff --git a/stratosphere/spl/source/spl_manu_service.cpp b/stratosphere/spl/source/spl_manu_service.cpp new file mode 100644 index 000000000..b78b7e192 --- /dev/null +++ b/stratosphere/spl/source/spl_manu_service.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "spl_manu_service.hpp" + +Result ManuService::ReEncryptRsaPrivateKey(OutPointerWithClientSize out, InPointer src, AccessKey access_key_dec, KeySource source_dec, AccessKey access_key_enc, KeySource source_enc, u32 option) { + return this->GetSecureMonitorWrapper()->ReEncryptRsaPrivateKey(out.pointer, out.num_elements, src.pointer, src.num_elements, access_key_dec, source_dec, access_key_enc, source_enc, option); +} diff --git a/stratosphere/spl/source/spl_manu_service.hpp b/stratosphere/spl/source/spl_manu_service.hpp new file mode 100644 index 000000000..e4e0a87c5 --- /dev/null +++ b/stratosphere/spl/source/spl_manu_service.hpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +#include "spl_types.hpp" +#include "spl_rsa_service.hpp" + +class ManuService : public RsaService { + public: + ManuService(SecureMonitorWrapper *sw) : RsaService(sw) { + /* ... */ + } + + virtual ~ManuService() { + /* ... */ + } + protected: + /* Actual commands. */ + virtual Result ReEncryptRsaPrivateKey(OutPointerWithClientSize out, InPointer src, AccessKey access_key_dec, KeySource source_dec, AccessKey access_key_enc, KeySource source_enc, u32 option); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + + }; +}; diff --git a/stratosphere/spl/source/spl_secmon_wrapper.cpp b/stratosphere/spl/source/spl_secmon_wrapper.cpp index f46e5b543..83c220c00 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.cpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.cpp @@ -558,7 +558,7 @@ Result SecureMonitorWrapper::DecryptRsaPrivateKey(void *dst, size_t dst_size, co Result SecureMonitorWrapper::ImportSecureExpModKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option) { struct ImportSecureExpModKeyLayout { - u8 data[RsaPrivateKeyMetaSize + 2 * RsaPrivateKeySize]; + u8 data[RsaPrivateKeyMetaSize + 2 * RsaPrivateKeySize + 0x10]; }; ImportSecureExpModKeyLayout *layout = reinterpret_cast(g_work_buffer); @@ -639,7 +639,7 @@ Result SecureMonitorWrapper::ImportEsKey(const void *src, size_t src_size, const return ImportSecureExpModKey(src, src_size, access_key, key_source, SmcDecryptOrImportMode_ImportEsKey); } else { struct ImportEsKeyLayout { - u8 data[RsaPrivateKeyMetaSize + 2 * RsaPrivateKeySize]; + u8 data[RsaPrivateKeyMetaSize + 2 * RsaPrivateKeySize + 0x10]; }; ImportEsKeyLayout *layout = reinterpret_cast(g_work_buffer); @@ -734,6 +734,40 @@ Result SecureMonitorWrapper::LoadTitleKey(u32 keyslot, const void *owner, const return ConvertToSplResult(SmcWrapper::LoadTitleKey(keyslot, access_key)); } +Result SecureMonitorWrapper::ReEncryptRsaPrivateKey(void *dst, size_t dst_size, const void *src, size_t src_size, const AccessKey &access_key_dec, const KeySource &source_dec, const AccessKey &access_key_enc, const KeySource &source_enc, u32 option) { + struct ReEncryptRsaPrivateKeyLayout { + u8 data[RsaPrivateKeyMetaSize + 2 * RsaPrivateKeySize + 0x10]; + AccessKey access_key_dec; + KeySource source_dec; + AccessKey access_key_enc; + KeySource source_enc; + }; + ReEncryptRsaPrivateKeyLayout *layout = reinterpret_cast(g_work_buffer); + + /* Validate size. */ + if (src_size < RsaPrivateKeyMetaSize || src_size > sizeof(ReEncryptRsaPrivateKeyLayout)) { + return ResultSplInvalidSize; + } + + std::memcpy(layout, src, src_size); + layout->access_key_dec = access_key_dec; + layout->source_dec = source_dec; + layout->access_key_enc = access_key_enc; + layout->source_enc = source_enc; + + armDCacheFlush(layout, sizeof(*layout)); + + SmcResult smc_res = SmcWrapper::ReEncryptRsaPrivateKey(layout->data, src_size, layout->access_key_dec, layout->source_dec, layout->access_key_enc, layout->source_enc, option); + if (smc_res == SmcResult_Success) { + size_t copy_size = std::min(dst_size, src_size); + armDCacheFlush(layout, copy_size); + std::memcpy(dst, layout->data, copy_size); + } + + return ConvertToSplResult(smc_res); + +} + Result SecureMonitorWrapper::FreeAesKeyslots(const void *owner) { for (size_t i = 0; i < GetMaxKeyslots(); i++) { if (this->keyslot_owners[i] == owner) { diff --git a/stratosphere/spl/source/spl_secmon_wrapper.hpp b/stratosphere/spl/source/spl_secmon_wrapper.hpp index 3addf70b9..d4e93ecb1 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.hpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.hpp @@ -95,6 +95,9 @@ class SecureMonitorWrapper { /* FS */ Result LoadTitleKey(u32 keyslot, const void *owner, const AccessKey &access_key); + /* Manu. */ + Result ReEncryptRsaPrivateKey(void *dst, size_t dst_size, const void *src, size_t src_size, const AccessKey &access_key_dec, const KeySource &source_dec, const AccessKey &access_key_enc, const KeySource &source_enc, u32 option); + /* Helper. */ Result FreeAesKeyslots(const void *owner); Handle GetAesKeyslotAvailableEventHandle(); From 85e8506fa8a1d411d09fb90d62dc0079925deaf3 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 25 Apr 2019 00:33:10 -0700 Subject: [PATCH 49/64] spl: Implement non-Lotus FsService commands. --- stratosphere/spl/source/spl_fs_service.cpp | 44 +++++++++++++ stratosphere/spl/source/spl_fs_service.hpp | 65 +++++++++++++++++++ stratosphere/spl/source/spl_main.cpp | 6 +- .../spl/source/spl_secmon_wrapper.cpp | 20 ++++++ .../spl/source/spl_secmon_wrapper.hpp | 2 + stratosphere/spl/source/spl_smc_wrapper.cpp | 4 +- stratosphere/spl/source/spl_smc_wrapper.hpp | 2 +- 7 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 stratosphere/spl/source/spl_fs_service.cpp create mode 100644 stratosphere/spl/source/spl_fs_service.hpp diff --git a/stratosphere/spl/source/spl_fs_service.cpp b/stratosphere/spl/source/spl_fs_service.cpp new file mode 100644 index 000000000..81dd84f00 --- /dev/null +++ b/stratosphere/spl/source/spl_fs_service.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "spl_fs_service.hpp" + +Result FsService::ImportLotusKey(InPointer src, AccessKey access_key, KeySource key_source, u32 option) { + return ResultSuccess; + /* TODO */ + return ResultKernelConnectionClosed; +} + +Result FsService::DecryptLotusMessage(Out out_size, OutPointerWithClientSize out, InPointer base, InPointer mod, InPointer label_digest) { + return ResultSuccess; + /* TODO */ + return ResultKernelConnectionClosed; +} + +Result FsService::GenerateSpecificAesKey(Out out_key, KeySource key_source, u32 generation, u32 which) { + return this->GetSecureMonitorWrapper()->GenerateSpecificAesKey(out_key.GetPointer(), key_source, generation, which); +} + +Result FsService::LoadTitleKey(u32 keyslot, AccessKey access_key) { + return this->GetSecureMonitorWrapper()->LoadTitleKey(keyslot, this, access_key); +} + +Result FsService::GetPackage2Hash(OutPointerWithClientSize dst) { + return this->GetSecureMonitorWrapper()->GetPackage2Hash(dst.pointer, dst.num_elements); +} diff --git a/stratosphere/spl/source/spl_fs_service.hpp b/stratosphere/spl/source/spl_fs_service.hpp new file mode 100644 index 000000000..4a7bd4872 --- /dev/null +++ b/stratosphere/spl/source/spl_fs_service.hpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +#include "spl_types.hpp" +#include "spl_crypto_service.hpp" + +class FsService : public CryptoService { + public: + FsService(SecureMonitorWrapper *sw) : CryptoService(sw) { + /* ... */ + } + + virtual ~FsService() { + /* ... */ + } + protected: + /* Actual commands. */ + virtual Result ImportLotusKey(InPointer src, AccessKey access_key, KeySource key_source, u32 option); + virtual Result DecryptLotusMessage(Out out_size, OutPointerWithClientSize out, InPointer base, InPointer mod, InPointer label_digest); + virtual Result GenerateSpecificAesKey(Out out_key, KeySource key_source, u32 generation, u32 which); + virtual Result LoadTitleKey(u32 keyslot, AccessKey access_key); + virtual Result GetPackage2Hash(OutPointerWithClientSize dst); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + + }; +}; diff --git a/stratosphere/spl/source/spl_main.cpp b/stratosphere/spl/source/spl_main.cpp index 1fa653a73..47eec447e 100644 --- a/stratosphere/spl/source/spl_main.cpp +++ b/stratosphere/spl/source/spl_main.cpp @@ -27,6 +27,7 @@ #include "spl_crypto_service.hpp" #include "spl_ssl_service.hpp" #include "spl_es_service.hpp" +#include "spl_fs_service.hpp" #include "spl_manu_service.hpp" extern "C" { @@ -92,6 +93,7 @@ static const auto MakeGeneralService = []() { return std::make_shared(&s_secmon_wrapper); }; static const auto MakeSslService = []() { return std::make_shared(&s_secmon_wrapper); }; static const auto MakeEsService = []() { return std::make_shared(&s_secmon_wrapper); }; +static const auto MakeFsService = []() { return std::make_shared(&s_secmon_wrapper); }; static const auto MakeManuService = []() { return std::make_shared(&s_secmon_wrapper); }; int main(int argc, char **argv) @@ -111,13 +113,15 @@ int main(int argc, char **argv) s_server_manager.AddWaitable(new ServiceServer("spl:mig", 6)); s_server_manager.AddWaitable(new ServiceServer("spl:ssl", 2)); s_server_manager.AddWaitable(new ServiceServer("spl:es", 2)); - /* TODO: spl:fs. */ + s_server_manager.AddWaitable(new ServiceServer("spl:fs", 2)); if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) { s_server_manager.AddWaitable(new ServiceServer("spl:manu", 1)); } } else { /* TODO, DeprecatedGeneralService */ } + + RebootToRcm(); /* Loop forever, servicing our services. */ s_server_manager.Process(); diff --git a/stratosphere/spl/source/spl_secmon_wrapper.cpp b/stratosphere/spl/source/spl_secmon_wrapper.cpp index 83c220c00..1105ab896 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.cpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.cpp @@ -726,6 +726,10 @@ Result SecureMonitorWrapper::LoadElicenseKey(u32 keyslot, const void *owner, con return LoadTitleKey(keyslot, owner, access_key); } +Result SecureMonitorWrapper::GenerateSpecificAesKey(AesKey *out_key, const KeySource &key_source, u32 generation, u32 which) { + return ConvertToSplResult(SmcWrapper::GenerateSpecificAesKey(out_key, key_source, generation, which)); +} + Result SecureMonitorWrapper::LoadTitleKey(u32 keyslot, const void *owner, const AccessKey &access_key) { Result rc = ValidateAesKeyslot(keyslot, owner); if (R_FAILED(rc)) { @@ -734,6 +738,22 @@ Result SecureMonitorWrapper::LoadTitleKey(u32 keyslot, const void *owner, const return ConvertToSplResult(SmcWrapper::LoadTitleKey(keyslot, access_key)); } +Result SecureMonitorWrapper::GetPackage2Hash(void *dst, const size_t size) { + u64 hash[4]; + + if (size < sizeof(hash)) { + return ResultSplInvalidSize; + } + + SmcResult smc_res; + if ((smc_res = SmcWrapper::GetConfig(hash, 4, SplConfigItem_Package2Hash)) != SmcResult_Success) { + return ConvertToSplResult(smc_res); + } + + std::memcpy(dst, hash, sizeof(hash)); + return ResultSuccess; +} + Result SecureMonitorWrapper::ReEncryptRsaPrivateKey(void *dst, size_t dst_size, const void *src, size_t src_size, const AccessKey &access_key_dec, const KeySource &source_dec, const AccessKey &access_key_enc, const KeySource &source_enc, u32 option) { struct ReEncryptRsaPrivateKeyLayout { u8 data[RsaPrivateKeyMetaSize + 2 * RsaPrivateKeySize + 0x10]; diff --git a/stratosphere/spl/source/spl_secmon_wrapper.hpp b/stratosphere/spl/source/spl_secmon_wrapper.hpp index d4e93ecb1..4ec4e4222 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.hpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.hpp @@ -93,7 +93,9 @@ class SecureMonitorWrapper { Result LoadElicenseKey(u32 keyslot, const void *owner, const AccessKey &access_key); /* FS */ + Result GenerateSpecificAesKey(AesKey *out_key, const KeySource &key_source, u32 generation, u32 which); Result LoadTitleKey(u32 keyslot, const void *owner, const AccessKey &access_key); + Result GetPackage2Hash(void *dst, const size_t size); /* Manu. */ Result ReEncryptRsaPrivateKey(void *dst, size_t dst_size, const void *src, size_t src_size, const AccessKey &access_key_dec, const KeySource &source_dec, const AccessKey &access_key_enc, const KeySource &source_enc, u32 option); diff --git a/stratosphere/spl/source/spl_smc_wrapper.cpp b/stratosphere/spl/source/spl_smc_wrapper.cpp index 8f39e87ec..5845f37ce 100644 --- a/stratosphere/spl/source/spl_smc_wrapper.cpp +++ b/stratosphere/spl/source/spl_smc_wrapper.cpp @@ -168,7 +168,7 @@ SmcResult SmcWrapper::CryptAes(AsyncOperationKey *out_op, u32 mode, const IvCtr return static_cast(args.X[0]); } -SmcResult SmcWrapper::GenerateSpecificAesKey(u64 *out, const KeySource &source, u32 generation, u32 which) { +SmcResult SmcWrapper::GenerateSpecificAesKey(AesKey *out_key, const KeySource &source, u32 generation, u32 which) { SecmonArgs args; args.X[0] = SmcFunctionId_GenerateSpecificAesKey; @@ -178,6 +178,8 @@ SmcResult SmcWrapper::GenerateSpecificAesKey(u64 *out, const KeySource &source, args.X[4] = which; svcCallSecureMonitor(&args); + out_key->data64[0] = args.X[1]; + out_key->data64[1] = args.X[2]; return static_cast(args.X[0]); } diff --git a/stratosphere/spl/source/spl_smc_wrapper.hpp b/stratosphere/spl/source/spl_smc_wrapper.hpp index be7b682d7..ea8798ee5 100644 --- a/stratosphere/spl/source/spl_smc_wrapper.hpp +++ b/stratosphere/spl/source/spl_smc_wrapper.hpp @@ -38,7 +38,7 @@ class SmcWrapper { static SmcResult GenerateAesKek(AccessKey *out, const KeySource &source, u32 generation, u32 option); static SmcResult LoadAesKey(u32 keyslot, const AccessKey &access_key, const KeySource &source); static SmcResult CryptAes(AsyncOperationKey *out_op, u32 mode, const IvCtr &iv_ctr, u32 dst_addr, u32 src_addr, size_t size); - static SmcResult GenerateSpecificAesKey(u64 *out, const KeySource &source, u32 generation, u32 which); + static SmcResult GenerateSpecificAesKey(AesKey *out_key, const KeySource &source, u32 generation, u32 which); static SmcResult ComputeCmac(Cmac *out_mac, u32 keyslot, const void *data, size_t size); static SmcResult ReEncryptRsaPrivateKey(void *data, size_t size, const AccessKey &access_key_dec, const KeySource &source_dec, const AccessKey &access_key_enc, const KeySource &source_enc, u32 option); static SmcResult DecryptOrImportRsaPrivateKey(void *data, size_t size, const AccessKey &access_key, const KeySource &source, u32 option); From bc44e02aedaef983403b8ebda074e1cfaf6ee453 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 25 Apr 2019 05:27:35 -0700 Subject: [PATCH 50/64] spl: fix vtables/other issues, now boots (not all commands work) --- stratosphere/libstratosphere | 2 +- .../spl/source/spl_crypto_service.cpp | 2 +- .../spl/source/spl_crypto_service.hpp | 34 ++++++------- stratosphere/spl/source/spl_ctr_drbg.hpp | 2 +- stratosphere/spl/source/spl_es_service.hpp | 48 +++++++++---------- stratosphere/spl/source/spl_fs_service.hpp | 42 ++++++++-------- stratosphere/spl/source/spl_main.cpp | 2 - stratosphere/spl/source/spl_manu_service.hpp | 36 +++++++------- .../spl/source/spl_random_service.hpp | 2 +- stratosphere/spl/source/spl_rsa_service.hpp | 34 ++++++------- stratosphere/spl/source/spl_ssl_service.hpp | 38 +++++++-------- stratosphere/spl/source/spl_types.hpp | 16 +++++-- 12 files changed, 131 insertions(+), 127 deletions(-) diff --git a/stratosphere/libstratosphere b/stratosphere/libstratosphere index 9e34dbe7e..63fc847f8 160000 --- a/stratosphere/libstratosphere +++ b/stratosphere/libstratosphere @@ -1 +1 @@ -Subproject commit 9e34dbe7e2689eadae205f34d494696b5ea6a3f5 +Subproject commit 63fc847f8ae43b173a9031071eebb76a1961c41c diff --git a/stratosphere/spl/source/spl_crypto_service.cpp b/stratosphere/spl/source/spl_crypto_service.cpp index 53c47242f..1e3522865 100644 --- a/stratosphere/spl/source/spl_crypto_service.cpp +++ b/stratosphere/spl/source/spl_crypto_service.cpp @@ -35,7 +35,7 @@ Result CryptoService::DecryptAesKey(Out out_key, KeySource key_source, u return this->GetSecureMonitorWrapper()->DecryptAesKey(out_key.GetPointer(), key_source, generation, option); } -Result CryptoService::CryptAesCtr(OutBuffer out_buf, u32 keyslot, InBuffer in_buf, IvCtr iv_ctr) { +Result CryptoService::CryptAesCtr(OutBuffer out_buf, u32 keyslot, InBuffer in_buf, IvCtr iv_ctr) { return this->GetSecureMonitorWrapper()->CryptAesCtr(out_buf.buffer, out_buf.num_elements, keyslot, this, in_buf.buffer, in_buf.num_elements, iv_ctr); } diff --git a/stratosphere/spl/source/spl_crypto_service.hpp b/stratosphere/spl/source/spl_crypto_service.hpp index b5e025b17..2f56df6ec 100644 --- a/stratosphere/spl/source/spl_crypto_service.hpp +++ b/stratosphere/spl/source/spl_crypto_service.hpp @@ -36,29 +36,29 @@ class CryptoService : public GeneralService { virtual Result LoadAesKey(u32 keyslot, AccessKey access_key, KeySource key_source); virtual Result GenerateAesKey(Out out_key, AccessKey access_key, KeySource key_source); virtual Result DecryptAesKey(Out out_key, KeySource key_source, u32 generation, u32 option); - virtual Result CryptAesCtr(OutBuffer out_buf, u32 keyslot, InBuffer in_buf, IvCtr iv_ctr); + virtual Result CryptAesCtr(OutBuffer out_buf, u32 keyslot, InBuffer in_buf, IvCtr iv_ctr); virtual Result ComputeCmac(Out out_cmac, u32 keyslot, InPointer in_buf); virtual Result AllocateAesKeyslot(Out out_keyslot); virtual Result FreeAesKeyslot(u32 keyslot); virtual void GetAesKeyslotAvailableEvent(Out out_hnd); public: DEFINE_SERVICE_DISPATCH_TABLE { - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), }; }; diff --git a/stratosphere/spl/source/spl_ctr_drbg.hpp b/stratosphere/spl/source/spl_ctr_drbg.hpp index 448a22eab..39f82ae0a 100644 --- a/stratosphere/spl/source/spl_ctr_drbg.hpp +++ b/stratosphere/spl/source/spl_ctr_drbg.hpp @@ -39,7 +39,7 @@ class CtrDrbg { u8 *dst_u8 = reinterpret_cast(dst); for (size_t i = 0; i < size; i++) { - dst_u8[i] = src_u8[i]; + dst_u8[i] ^= src_u8[i]; } } diff --git a/stratosphere/spl/source/spl_es_service.hpp b/stratosphere/spl/source/spl_es_service.hpp index 17f8efa0f..2fba15a30 100644 --- a/stratosphere/spl/source/spl_es_service.hpp +++ b/stratosphere/spl/source/spl_es_service.hpp @@ -41,29 +41,29 @@ class EsService : public RsaService { virtual Result LoadElicenseKey(u32 keyslot, AccessKey access_key); public: DEFINE_SERVICE_DISPATCH_TABLE { - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), }; }; diff --git a/stratosphere/spl/source/spl_fs_service.hpp b/stratosphere/spl/source/spl_fs_service.hpp index 4a7bd4872..359ca6401 100644 --- a/stratosphere/spl/source/spl_fs_service.hpp +++ b/stratosphere/spl/source/spl_fs_service.hpp @@ -39,27 +39,27 @@ class FsService : public CryptoService { virtual Result GetPackage2Hash(OutPointerWithClientSize dst); public: DEFINE_SERVICE_DISPATCH_TABLE { - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), }; }; diff --git a/stratosphere/spl/source/spl_main.cpp b/stratosphere/spl/source/spl_main.cpp index 47eec447e..b97c3ee43 100644 --- a/stratosphere/spl/source/spl_main.cpp +++ b/stratosphere/spl/source/spl_main.cpp @@ -121,8 +121,6 @@ int main(int argc, char **argv) /* TODO, DeprecatedGeneralService */ } - RebootToRcm(); - /* Loop forever, servicing our services. */ s_server_manager.Process(); diff --git a/stratosphere/spl/source/spl_manu_service.hpp b/stratosphere/spl/source/spl_manu_service.hpp index e4e0a87c5..e3e911570 100644 --- a/stratosphere/spl/source/spl_manu_service.hpp +++ b/stratosphere/spl/source/spl_manu_service.hpp @@ -35,24 +35,24 @@ class ManuService : public RsaService { virtual Result ReEncryptRsaPrivateKey(OutPointerWithClientSize out, InPointer src, AccessKey access_key_dec, KeySource source_dec, AccessKey access_key_enc, KeySource source_enc, u32 option); public: DEFINE_SERVICE_DISPATCH_TABLE { - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), }; }; diff --git a/stratosphere/spl/source/spl_random_service.hpp b/stratosphere/spl/source/spl_random_service.hpp index 0d307fcec..66261f087 100644 --- a/stratosphere/spl/source/spl_random_service.hpp +++ b/stratosphere/spl/source/spl_random_service.hpp @@ -35,6 +35,6 @@ class RandomService final : public IServiceObject { virtual Result GenerateRandomBytes(OutBuffer out); public: DEFINE_SERVICE_DISPATCH_TABLE { - MakeServiceCommandMeta(), + MakeServiceCommandMeta(), }; }; diff --git a/stratosphere/spl/source/spl_rsa_service.hpp b/stratosphere/spl/source/spl_rsa_service.hpp index c6d90185a..6e9b2471e 100644 --- a/stratosphere/spl/source/spl_rsa_service.hpp +++ b/stratosphere/spl/source/spl_rsa_service.hpp @@ -35,23 +35,23 @@ class RsaService : public CryptoService { virtual Result DecryptRsaPrivateKey(OutPointerWithClientSize dst, InPointer src, AccessKey access_key, KeySource key_source, u32 option); public: DEFINE_SERVICE_DISPATCH_TABLE { - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), }; }; diff --git a/stratosphere/spl/source/spl_ssl_service.hpp b/stratosphere/spl/source/spl_ssl_service.hpp index 575109c6f..cfcdff866 100644 --- a/stratosphere/spl/source/spl_ssl_service.hpp +++ b/stratosphere/spl/source/spl_ssl_service.hpp @@ -36,25 +36,25 @@ class SslService : public RsaService { virtual Result SslExpMod(OutPointerWithClientSize out, InPointer base, InPointer mod); public: DEFINE_SERVICE_DISPATCH_TABLE { - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), }; }; diff --git a/stratosphere/spl/source/spl_types.hpp b/stratosphere/spl/source/spl_types.hpp index 78c984900..c4cebbe2f 100644 --- a/stratosphere/spl/source/spl_types.hpp +++ b/stratosphere/spl/source/spl_types.hpp @@ -70,10 +70,11 @@ struct BootReasonValue { }; static_assert(sizeof(BootReasonValue) == sizeof(u32), "BootReasonValue definition!"); +#pragma pack(push, 1) struct AesKey { union { u8 data[AES_128_KEY_SIZE]; - u8 data64[AES_128_KEY_SIZE / sizeof(u64)]; + u64 data64[AES_128_KEY_SIZE / sizeof(u64)]; }; }; static_assert(alignof(AesKey) == alignof(u8), "AesKey definition!"); @@ -81,7 +82,7 @@ static_assert(alignof(AesKey) == alignof(u8), "AesKey definition!"); struct IvCtr { union { u8 data[AES_128_KEY_SIZE]; - u8 data64[AES_128_KEY_SIZE / sizeof(u64)]; + u64 data64[AES_128_KEY_SIZE / sizeof(u64)]; }; }; static_assert(alignof(IvCtr) == alignof(u8), "IvCtr definition!"); @@ -89,7 +90,7 @@ static_assert(alignof(IvCtr) == alignof(u8), "IvCtr definition!"); struct Cmac { union { u8 data[AES_128_KEY_SIZE]; - u8 data64[AES_128_KEY_SIZE / sizeof(u64)]; + u64 data64[AES_128_KEY_SIZE / sizeof(u64)]; }; }; static_assert(alignof(Cmac) == alignof(u8), "Cmac definition!"); @@ -97,7 +98,7 @@ static_assert(alignof(Cmac) == alignof(u8), "Cmac definition!"); struct AccessKey { union { u8 data[AES_128_KEY_SIZE]; - u8 data64[AES_128_KEY_SIZE / sizeof(u64)]; + u64 data64[AES_128_KEY_SIZE / sizeof(u64)]; }; }; static_assert(alignof(AccessKey) == alignof(u8), "AccessKey definition!"); @@ -105,10 +106,15 @@ static_assert(alignof(AccessKey) == alignof(u8), "AccessKey definition!"); struct KeySource { union { u8 data[AES_128_KEY_SIZE]; - u8 data64[AES_128_KEY_SIZE / sizeof(u64)]; + u64 data64[AES_128_KEY_SIZE / sizeof(u64)]; }; }; static_assert(alignof(AccessKey) == alignof(u8), "KeySource definition!"); +#pragma pack(pop) + +enum CsrngCmd { + Csrng_Cmd_GenerateRandomBytes = 0, +}; enum SplServiceCmd { /* 1.0.0+ */ From 4b8ebfa7c34d0afa393c15c4b1128f4efa0f0696 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 25 Apr 2019 06:00:34 -0700 Subject: [PATCH 51/64] spl: fix CryptAesCtr (eshop games work now) --- stratosphere/spl/source/spl_secmon_wrapper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stratosphere/spl/source/spl_secmon_wrapper.cpp b/stratosphere/spl/source/spl_secmon_wrapper.cpp index 1105ab896..9ef73d5b1 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.cpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.cpp @@ -425,7 +425,7 @@ Result SecureMonitorWrapper::CryptAesCtr(void *dst, size_t dst_size, u32 keyslot const u32 src_se_map_addr = CryptAesInMapBase + (src_addr_page_aligned & DeviceAddressSpaceAlignMask); const u32 dst_se_map_addr = CryptAesOutMapBase + (dst_addr_page_aligned & DeviceAddressSpaceAlignMask); const u32 src_se_addr = CryptAesInMapBase + (src_addr & DeviceAddressSpaceAlignMask); - const u32 dst_se_addr = CryptAesInMapBase + (dst_addr & DeviceAddressSpaceAlignMask); + const u32 dst_se_addr = CryptAesOutMapBase + (dst_addr & DeviceAddressSpaceAlignMask); /* Validate aligned sizes. */ if (src_size_page_aligned > CryptAesSizeMax || dst_size_page_aligned > CryptAesSizeMax) { From d984621150bb00ddba41b984719ba6227c77a1e9 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 25 Apr 2019 07:06:27 -0700 Subject: [PATCH 52/64] spl: Implement FsService lotus commands (gamecards work now) --- stratosphere/spl/source/spl_fs_service.cpp | 14 +-- stratosphere/spl/source/spl_fs_service.hpp | 2 +- .../spl/source/spl_secmon_wrapper.cpp | 110 +++++++++++++++++- .../spl/source/spl_secmon_wrapper.hpp | 5 + 4 files changed, 120 insertions(+), 11 deletions(-) diff --git a/stratosphere/spl/source/spl_fs_service.cpp b/stratosphere/spl/source/spl_fs_service.cpp index 81dd84f00..eaa51d26e 100644 --- a/stratosphere/spl/source/spl_fs_service.cpp +++ b/stratosphere/spl/source/spl_fs_service.cpp @@ -20,15 +20,15 @@ #include "spl_fs_service.hpp" Result FsService::ImportLotusKey(InPointer src, AccessKey access_key, KeySource key_source, u32 option) { - return ResultSuccess; - /* TODO */ - return ResultKernelConnectionClosed; + return this->GetSecureMonitorWrapper()->ImportLotusKey(src.pointer, src.num_elements, access_key, key_source, option); } -Result FsService::DecryptLotusMessage(Out out_size, OutPointerWithClientSize out, InPointer base, InPointer mod, InPointer label_digest) { - return ResultSuccess; - /* TODO */ - return ResultKernelConnectionClosed; +Result FsService::DecryptLotusMessage(Out out_size, OutPointerWithClientSize out, InPointer base, InPointer mod, InPointer label_digest) { + Result rc = this->GetSecureMonitorWrapper()->DecryptLotusMessage(out_size.GetPointer(), out.pointer, out.num_elements, base.pointer, base.num_elements, mod.pointer, mod.num_elements, label_digest.pointer, label_digest.num_elements); + if (R_FAILED(rc)) { + fatalSimple(rc); + } + return rc; } Result FsService::GenerateSpecificAesKey(Out out_key, KeySource key_source, u32 generation, u32 which) { diff --git a/stratosphere/spl/source/spl_fs_service.hpp b/stratosphere/spl/source/spl_fs_service.hpp index 359ca6401..7bac28934 100644 --- a/stratosphere/spl/source/spl_fs_service.hpp +++ b/stratosphere/spl/source/spl_fs_service.hpp @@ -33,7 +33,7 @@ class FsService : public CryptoService { protected: /* Actual commands. */ virtual Result ImportLotusKey(InPointer src, AccessKey access_key, KeySource key_source, u32 option); - virtual Result DecryptLotusMessage(Out out_size, OutPointerWithClientSize out, InPointer base, InPointer mod, InPointer label_digest); + virtual Result DecryptLotusMessage(Out out_size, OutPointerWithClientSize out, InPointer base, InPointer mod, InPointer label_digest); virtual Result GenerateSpecificAesKey(Out out_key, KeySource key_source, u32 generation, u32 which); virtual Result LoadTitleKey(u32 keyslot, AccessKey access_key); virtual Result GetPackage2Hash(OutPointerWithClientSize dst); diff --git a/stratosphere/spl/source/spl_secmon_wrapper.cpp b/stratosphere/spl/source/spl_secmon_wrapper.cpp index 9ef73d5b1..e4ca99a31 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.cpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.cpp @@ -130,6 +130,82 @@ void SecureMonitorWrapper::Initialize() { InitializeDeviceAddressSpace(); } +void SecureMonitorWrapper::CalcMgf1AndXor(void *dst, size_t dst_size, const void *src, size_t src_size) { + uint8_t *dst_u8 = reinterpret_cast(dst); + + u32 ctr = 0; + while (dst_size > 0) { + size_t cur_size = SHA256_HASH_SIZE; + if (cur_size > dst_size) { + cur_size = dst_size; + } + dst_size -= cur_size; + + u32 ctr_be = __builtin_bswap32(ctr++); + u8 hash[SHA256_HASH_SIZE]; + { + Sha256Context ctx; + sha256ContextCreate(&ctx); + sha256ContextUpdate(&ctx, src, src_size); + sha256ContextUpdate(&ctx, &ctr_be, sizeof(ctr_be)); + sha256ContextGetHash(&ctx, hash); + } + + for (size_t i = 0; i < cur_size; i++) { + *(dst_u8++) ^= hash[i]; + } + } +} + +size_t SecureMonitorWrapper::DecodeRsaOaep(void *dst, size_t dst_size, const void *label_digest, size_t label_digest_size, const void *src, size_t src_size) { + /* Very basic validation. */ + if (dst_size == 0 || src_size != 0x100 || label_digest_size != SHA256_HASH_SIZE) { + return 0; + } + + u8 block[0x100]; + std::memcpy(block, src, sizeof(block)); + + /* First, validate byte 0 == 0, and unmask DB. */ + int invalid = block[0]; + u8 *salt = block + 1; + u8 *db = salt + SHA256_HASH_SIZE; + CalcMgf1AndXor(salt, SHA256_HASH_SIZE, db, src_size - (1 + SHA256_HASH_SIZE)); + CalcMgf1AndXor(db, src_size - (1 + SHA256_HASH_SIZE), salt, SHA256_HASH_SIZE); + + /* Validate label digest. */ + for (size_t i = 0; i < SHA256_HASH_SIZE; i++) { + invalid |= db[i] ^ reinterpret_cast(label_digest)[i]; + } + + /* Locate message after 00...0001 padding. */ + const u8 *padded_msg = db + SHA256_HASH_SIZE; + size_t padded_msg_size = src_size - (1 + 2 * SHA256_HASH_SIZE); + size_t msg_ind = 0; + int not_found = 1; + int wrong_padding = 0; + size_t i = 0; + while (i < padded_msg_size) { + int zero = (padded_msg[i] == 0); + int one = (padded_msg[i] == 1); + msg_ind += static_cast(not_found & one) * (++i); + not_found &= ~one; + wrong_padding |= (not_found & ~zero); + } + + if (invalid | not_found | wrong_padding) { + return 0; + } + + /* Copy message out. */ + size_t msg_size = padded_msg_size - msg_ind; + if (msg_size > dst_size) { + return 0; + } + std::memcpy(dst, padded_msg + msg_ind, msg_size); + return msg_size; +} + void SecureMonitorWrapper::WaitSeOperationComplete() { eventWait(&g_se_event, U64_MAX); } @@ -726,6 +802,34 @@ Result SecureMonitorWrapper::LoadElicenseKey(u32 keyslot, const void *owner, con return LoadTitleKey(keyslot, owner, access_key); } +Result SecureMonitorWrapper::ImportLotusKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option) { + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) { + option = SmcDecryptOrImportMode_ImportLotusKey; + } + return ImportSecureExpModKey(src, src_size, access_key, key_source, option); +} + +Result SecureMonitorWrapper::DecryptLotusMessage(u32 *out_size, void *dst, size_t dst_size, const void *base, size_t base_size, const void *mod, size_t mod_size, const void *label_digest, size_t label_digest_size) { + /* Validate sizes. */ + if (dst_size > MaxWorkBufferSize || label_digest_size != LabelDigestSizeMax) { + return ResultSplInvalidSize; + } + + /* Nintendo doesn't check this result code, but we will. */ + Result rc = SecureExpMod(g_work_buffer, 0x100, base, base_size, mod, mod_size, SmcSecureExpModMode_Lotus); + if (R_FAILED(rc)) { + return rc; + } + + size_t data_size = DecodeRsaOaep(dst, dst_size, label_digest, label_digest_size, g_work_buffer, 0x100); + if (data_size == 0) { + return ResultSplDecryptionFailed; + } + + *out_size = static_cast(data_size); + return ResultSuccess; +} + Result SecureMonitorWrapper::GenerateSpecificAesKey(AesKey *out_key, const KeySource &key_source, u32 generation, u32 which) { return ConvertToSplResult(SmcWrapper::GenerateSpecificAesKey(out_key, key_source, generation, which)); } @@ -740,16 +844,16 @@ Result SecureMonitorWrapper::LoadTitleKey(u32 keyslot, const void *owner, const Result SecureMonitorWrapper::GetPackage2Hash(void *dst, const size_t size) { u64 hash[4]; - + if (size < sizeof(hash)) { return ResultSplInvalidSize; } - + SmcResult smc_res; if ((smc_res = SmcWrapper::GetConfig(hash, 4, SplConfigItem_Package2Hash)) != SmcResult_Success) { return ConvertToSplResult(smc_res); } - + std::memcpy(dst, hash, sizeof(hash)); return ResultSuccess; } diff --git a/stratosphere/spl/source/spl_secmon_wrapper.hpp b/stratosphere/spl/source/spl_secmon_wrapper.hpp index 4ec4e4222..83534ee96 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.hpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.hpp @@ -44,6 +44,9 @@ class SecureMonitorWrapper { static void InitializeCtrDrbg(); static void InitializeSeEvents(); static void InitializeDeviceAddressSpace(); + + static void CalcMgf1AndXor(void *dst, size_t dst_size, const void *src, size_t src_size); + static size_t DecodeRsaOaep(void *dst, size_t dst_size, const void *label_digest, size_t label_digest_size, const void *src, size_t src_size); public: static void Initialize(); private: @@ -93,6 +96,8 @@ class SecureMonitorWrapper { Result LoadElicenseKey(u32 keyslot, const void *owner, const AccessKey &access_key); /* FS */ + Result ImportLotusKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option); + Result DecryptLotusMessage(u32 *out_size, void *dst, size_t dst_size, const void *base, size_t base_size, const void *mod, size_t mod_size, const void *label_digest, size_t label_digest_size); Result GenerateSpecificAesKey(AesKey *out_key, const KeySource &key_source, u32 generation, u32 which); Result LoadTitleKey(u32 keyslot, const void *owner, const AccessKey &access_key); Result GetPackage2Hash(void *dst, const size_t size); From edcfbf425405e1b6c644b4141f86755177596688 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 25 Apr 2019 11:12:30 -0700 Subject: [PATCH 53/64] spl: Implement DeprecatedService. --- .../spl/source/spl_deprecated_service.cpp | 120 ++++++++++++++++++ .../spl/source/spl_deprecated_service.hpp | 92 ++++++++++++++ stratosphere/spl/source/spl_fs_service.cpp | 6 +- stratosphere/spl/source/spl_main.cpp | 6 +- 4 files changed, 218 insertions(+), 6 deletions(-) create mode 100644 stratosphere/spl/source/spl_deprecated_service.cpp create mode 100644 stratosphere/spl/source/spl_deprecated_service.hpp diff --git a/stratosphere/spl/source/spl_deprecated_service.cpp b/stratosphere/spl/source/spl_deprecated_service.cpp new file mode 100644 index 000000000..12484385b --- /dev/null +++ b/stratosphere/spl/source/spl_deprecated_service.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "spl_deprecated_service.hpp" + +Result DeprecatedService::GetConfig(Out out, u32 which) { + return this->GetSecureMonitorWrapper()->GetConfig(out.GetPointer(), static_cast(which)); +} + +Result DeprecatedService::ExpMod(OutPointerWithClientSize out, InPointer base, InPointer exp, InPointer mod) { + return this->GetSecureMonitorWrapper()->ExpMod(out.pointer, out.num_elements, base.pointer, base.num_elements, exp.pointer, exp.num_elements, mod.pointer, mod.num_elements); +} + +Result DeprecatedService::GenerateAesKek(Out out_access_key, KeySource key_source, u32 generation, u32 option) { + return this->GetSecureMonitorWrapper()->GenerateAesKek(out_access_key.GetPointer(), key_source, generation, option); +} + +Result DeprecatedService::LoadAesKey(u32 keyslot, AccessKey access_key, KeySource key_source) { + return this->GetSecureMonitorWrapper()->LoadAesKey(keyslot, this, access_key, key_source); +} + +Result DeprecatedService::GenerateAesKey(Out out_key, AccessKey access_key, KeySource key_source) { + return this->GetSecureMonitorWrapper()->GenerateAesKey(out_key.GetPointer(), access_key, key_source); +} + +Result DeprecatedService::SetConfig(u32 which, u64 value) { + return this->GetSecureMonitorWrapper()->SetConfig(static_cast(which), value); +} + +Result DeprecatedService::GenerateRandomBytes(OutPointerWithClientSize out) { + return this->GetSecureMonitorWrapper()->GenerateRandomBytes(out.pointer, out.num_elements); +} + +Result DeprecatedService::ImportLotusKey(InPointer src, AccessKey access_key, KeySource key_source, u32 option) { + return this->GetSecureMonitorWrapper()->ImportLotusKey(src.pointer, src.num_elements, access_key, key_source, option); +} + +Result DeprecatedService::DecryptLotusMessage(Out out_size, OutPointerWithClientSize out, InPointer base, InPointer mod, InPointer label_digest) { + return this->GetSecureMonitorWrapper()->DecryptLotusMessage(out_size.GetPointer(), out.pointer, out.num_elements, base.pointer, base.num_elements, mod.pointer, mod.num_elements, label_digest.pointer, label_digest.num_elements); +} + +Result DeprecatedService::IsDevelopment(Out is_dev) { + return this->GetSecureMonitorWrapper()->IsDevelopment(is_dev.GetPointer()); +} + +Result DeprecatedService::GenerateSpecificAesKey(Out out_key, KeySource key_source, u32 generation, u32 which) { + return this->GetSecureMonitorWrapper()->GenerateSpecificAesKey(out_key.GetPointer(), key_source, generation, which); +} + +Result DeprecatedService::DecryptRsaPrivateKey(OutPointerWithClientSize dst, InPointer src, AccessKey access_key, KeySource key_source, u32 option) { + return this->GetSecureMonitorWrapper()->DecryptRsaPrivateKey(dst.pointer, dst.num_elements, src.pointer, src.num_elements, access_key, key_source, option); +} + +Result DeprecatedService::DecryptAesKey(Out out_key, KeySource key_source, u32 generation, u32 option) { + return this->GetSecureMonitorWrapper()->DecryptAesKey(out_key.GetPointer(), key_source, generation, option); +} + +Result DeprecatedService::CryptAesCtrDeprecated(OutBuffer out_buf, u32 keyslot, InBuffer in_buf, IvCtr iv_ctr) { + return this->GetSecureMonitorWrapper()->CryptAesCtr(out_buf.buffer, out_buf.num_elements, keyslot, this, in_buf.buffer, in_buf.num_elements, iv_ctr); +} + +Result DeprecatedService::CryptAesCtr(OutBuffer out_buf, u32 keyslot, InBuffer in_buf, IvCtr iv_ctr) { + return this->GetSecureMonitorWrapper()->CryptAesCtr(out_buf.buffer, out_buf.num_elements, keyslot, this, in_buf.buffer, in_buf.num_elements, iv_ctr); +} + +Result DeprecatedService::ComputeCmac(Out out_cmac, u32 keyslot, InPointer in_buf) { + return this->GetSecureMonitorWrapper()->ComputeCmac(out_cmac.GetPointer(), keyslot, this, in_buf.pointer, in_buf.num_elements); +} + +Result DeprecatedService::ImportEsKey(InPointer src, AccessKey access_key, KeySource key_source, u32 option) { + return this->GetSecureMonitorWrapper()->ImportEsKey(src.pointer, src.num_elements, access_key, key_source, option); +} + +Result DeprecatedService::UnwrapTitleKey(Out out_access_key, InPointer base, InPointer mod, InPointer label_digest, u32 generation) { + return this->GetSecureMonitorWrapper()->UnwrapTitleKey(out_access_key.GetPointer(), base.pointer, base.num_elements, mod.pointer, mod.num_elements, label_digest.pointer, label_digest.num_elements, generation); +} + +Result DeprecatedService::LoadTitleKey(u32 keyslot, AccessKey access_key) { + return this->GetSecureMonitorWrapper()->LoadTitleKey(keyslot, this, access_key); +} + +Result DeprecatedService::UnwrapCommonTitleKey(Out out_access_key, KeySource key_source, u32 generation) { + return this->GetSecureMonitorWrapper()->UnwrapCommonTitleKey(out_access_key.GetPointer(), key_source, generation); +} + +Result DeprecatedService::AllocateAesKeyslot(Out out_keyslot) { + return this->GetSecureMonitorWrapper()->AllocateAesKeyslot(out_keyslot.GetPointer(), this); +} + +Result DeprecatedService::FreeAesKeyslot(u32 keyslot) { + return this->GetSecureMonitorWrapper()->FreeAesKeyslot(keyslot, this); +} + +void DeprecatedService::GetAesKeyslotAvailableEvent(Out out_hnd) { + out_hnd.SetValue(this->GetSecureMonitorWrapper()->GetAesKeyslotAvailableEventHandle()); +} + +Result DeprecatedService::SetBootReason(BootReasonValue boot_reason) { + return this->GetSecureMonitorWrapper()->SetBootReason(boot_reason); +} + +Result DeprecatedService::GetBootReason(Out out) { + return this->GetSecureMonitorWrapper()->GetBootReason(out.GetPointer()); +} diff --git a/stratosphere/spl/source/spl_deprecated_service.hpp b/stratosphere/spl/source/spl_deprecated_service.hpp new file mode 100644 index 000000000..cd52d2d0d --- /dev/null +++ b/stratosphere/spl/source/spl_deprecated_service.hpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +#include "spl_types.hpp" +#include "spl_secmon_wrapper.hpp" + +class DeprecatedService : public IServiceObject { + private: + SecureMonitorWrapper *secmon_wrapper; + public: + DeprecatedService(SecureMonitorWrapper *sw) : secmon_wrapper(sw) { + /* ... */ + } + + virtual ~DeprecatedService() { /* ... */ } + protected: + SecureMonitorWrapper *GetSecureMonitorWrapper() const { + return this->secmon_wrapper; + } + protected: + /* Actual commands. */ + virtual Result GetConfig(Out out, u32 which); + virtual Result ExpMod(OutPointerWithClientSize out, InPointer base, InPointer exp, InPointer mod); + virtual Result GenerateAesKek(Out out_access_key, KeySource key_source, u32 generation, u32 option); + virtual Result LoadAesKey(u32 keyslot, AccessKey access_key, KeySource key_source); + virtual Result GenerateAesKey(Out out_key, AccessKey access_key, KeySource key_source); + virtual Result SetConfig(u32 which, u64 value); + virtual Result GenerateRandomBytes(OutPointerWithClientSize out); + virtual Result ImportLotusKey(InPointer src, AccessKey access_key, KeySource key_source, u32 option); + virtual Result DecryptLotusMessage(Out out_size, OutPointerWithClientSize out, InPointer base, InPointer mod, InPointer label_digest); + virtual Result IsDevelopment(Out is_dev); + virtual Result GenerateSpecificAesKey(Out out_key, KeySource key_source, u32 generation, u32 which); + virtual Result DecryptRsaPrivateKey(OutPointerWithClientSize dst, InPointer src, AccessKey access_key, KeySource key_source, u32 option); + virtual Result DecryptAesKey(Out out_key, KeySource key_source, u32 generation, u32 option); + virtual Result CryptAesCtrDeprecated(OutBuffer out_buf, u32 keyslot, InBuffer in_buf, IvCtr iv_ctr); + virtual Result CryptAesCtr(OutBuffer out_buf, u32 keyslot, InBuffer in_buf, IvCtr iv_ctr); + virtual Result ComputeCmac(Out out_cmac, u32 keyslot, InPointer in_buf); + virtual Result ImportEsKey(InPointer src, AccessKey access_key, KeySource key_source, u32 option); + virtual Result UnwrapTitleKey(Out out_access_key, InPointer base, InPointer mod, InPointer label_digest, u32 generation); + virtual Result LoadTitleKey(u32 keyslot, AccessKey access_key); + virtual Result UnwrapCommonTitleKey(Out out_access_key, KeySource key_source, u32 generation); + virtual Result AllocateAesKeyslot(Out out_keyslot); + virtual Result FreeAesKeyslot(u32 keyslot); + virtual void GetAesKeyslotAvailableEvent(Out out_hnd); + virtual Result SetBootReason(BootReasonValue boot_reason); + virtual Result GetBootReason(Out out); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + }; +}; diff --git a/stratosphere/spl/source/spl_fs_service.cpp b/stratosphere/spl/source/spl_fs_service.cpp index eaa51d26e..d0808e003 100644 --- a/stratosphere/spl/source/spl_fs_service.cpp +++ b/stratosphere/spl/source/spl_fs_service.cpp @@ -24,11 +24,7 @@ Result FsService::ImportLotusKey(InPointer src, AccessKey access_key, KeySou } Result FsService::DecryptLotusMessage(Out out_size, OutPointerWithClientSize out, InPointer base, InPointer mod, InPointer label_digest) { - Result rc = this->GetSecureMonitorWrapper()->DecryptLotusMessage(out_size.GetPointer(), out.pointer, out.num_elements, base.pointer, base.num_elements, mod.pointer, mod.num_elements, label_digest.pointer, label_digest.num_elements); - if (R_FAILED(rc)) { - fatalSimple(rc); - } - return rc; + return this->GetSecureMonitorWrapper()->DecryptLotusMessage(out_size.GetPointer(), out.pointer, out.num_elements, base.pointer, base.num_elements, mod.pointer, mod.num_elements, label_digest.pointer, label_digest.num_elements); } Result FsService::GenerateSpecificAesKey(Out out_key, KeySource key_source, u32 generation, u32 which) { diff --git a/stratosphere/spl/source/spl_main.cpp b/stratosphere/spl/source/spl_main.cpp index b97c3ee43..7cae77b4a 100644 --- a/stratosphere/spl/source/spl_main.cpp +++ b/stratosphere/spl/source/spl_main.cpp @@ -30,6 +30,8 @@ #include "spl_fs_service.hpp" #include "spl_manu_service.hpp" +#include "spl_deprecated_service.hpp" + extern "C" { extern u32 __start__; @@ -96,6 +98,8 @@ static const auto MakeEsService = []() { return std::make_shared(&s_ static const auto MakeFsService = []() { return std::make_shared(&s_secmon_wrapper); }; static const auto MakeManuService = []() { return std::make_shared(&s_secmon_wrapper); }; +static const auto MakeDeprecatedService = []() { return std::make_shared(&s_secmon_wrapper); }; + int main(int argc, char **argv) { consoleDebugInit(debugDevice_SVC); @@ -118,7 +122,7 @@ int main(int argc, char **argv) s_server_manager.AddWaitable(new ServiceServer("spl:manu", 1)); } } else { - /* TODO, DeprecatedGeneralService */ + s_server_manager.AddWaitable(new ServiceServer("spl:", 12)); } /* Loop forever, servicing our services. */ From 51858e732ac2245588fea417fb9fb4e5d5bf4aac Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 25 Apr 2019 11:24:25 -0700 Subject: [PATCH 54/64] fusee: embed spl. --- fusee/fusee-secondary/Makefile | 4 ++-- fusee/fusee-secondary/linker.ld | 2 ++ fusee/fusee-secondary/src/start.s | 8 ++++++++ fusee/fusee-secondary/src/stratosphere.c | 16 ++++++++++++++-- stratosphere/Makefile | 2 +- 5 files changed, 27 insertions(+), 5 deletions(-) diff --git a/fusee/fusee-secondary/Makefile b/fusee/fusee-secondary/Makefile index d4a0c2c91..8b3157330 100644 --- a/fusee/fusee-secondary/Makefile +++ b/fusee/fusee-secondary/Makefile @@ -84,7 +84,7 @@ ifneq ($(BUILD),$(notdir $(CURDIR))) export OUTPUT := $(CURDIR)/$(TARGET) export TOPDIR := $(CURDIR) -export KIPDIRS := $(AMS)/stratosphere/loader $(AMS)/stratosphere/pm $(AMS)/stratosphere/sm $(AMS)/stratosphere/boot $(AMS)/stratosphere/ams_mitm +export KIPDIRS := $(AMS)/stratosphere/loader $(AMS)/stratosphere/pm $(AMS)/stratosphere/sm $(AMS)/stratosphere/boot $(AMS)/stratosphere/spl $(AMS)/stratosphere/ams_mitm export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ $(AMS)/exosphere $(AMS)/exosphere/lp0fw $(AMS)/exosphere/rebootstub \ @@ -96,7 +96,7 @@ export DEPSDIR := $(CURDIR)/$(BUILD) CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) -KIPFILES := loader.kip pm.kip sm.kip ams_mitm.kip boot_100.kip boot_200.kip +KIPFILES := loader.kip pm.kip sm.kip ams_mitm.kip spl.kip boot_100.kip boot_200.kip BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) fusee-primary.bin \ exosphere.bin lp0fw.bin rebootstub.bin thermosphere.bin splash_screen.bmp \ sept-primary.bin sept-secondary.enc \ diff --git a/fusee/fusee-secondary/linker.ld b/fusee/fusee-secondary/linker.ld index a6328675a..0c8578714 100644 --- a/fusee/fusee-secondary/linker.ld +++ b/fusee/fusee-secondary/linker.ld @@ -242,6 +242,8 @@ SECTIONS PROVIDE(__sept_secondary_enc_size__ = sept_secondary_enc_end - sept_secondary_enc); PROVIDE(__sm_kip_start__ = sm_kip - __start__); PROVIDE(__sm_kip_size__ = sm_kip_end - sm_kip); + PROVIDE(__spl_kip_start__ = spl_kip - __start__); + PROVIDE(__spl_kip_size__ = spl_kip_end - spl_kip); PROVIDE(__splash_screen_bmp_start__ = splash_screen_bmp - __start__); PROVIDE(__splash_screen_bmp_size__ = splash_screen_bmp_end - splash_screen_bmp); PROVIDE(__thermosphere_bin_start__ = thermosphere_bin - __start__); diff --git a/fusee/fusee-secondary/src/start.s b/fusee/fusee-secondary/src/start.s index f0826c8e8..1d3572545 100644 --- a/fusee/fusee-secondary/src/start.s +++ b/fusee/fusee-secondary/src/start.s @@ -191,6 +191,14 @@ _content_headers: .asciz "sm" .align 5 +/* spl content header */ +.word __spl_kip_start__ +.word __spl_kip_size__ +.word CONTENT_TYPE_KIP +.word 0xCCCCCCCC +.asciz "spl" +.align 5 + /* splash_screen content header */ .word __splash_screen_bmp_start__ .word __splash_screen_bmp_size__ diff --git a/fusee/fusee-secondary/src/stratosphere.c b/fusee/fusee-secondary/src/stratosphere.c index e11a2b312..d782e3b09 100644 --- a/fusee/fusee-secondary/src/stratosphere.c +++ b/fusee/fusee-secondary/src/stratosphere.c @@ -35,6 +35,7 @@ #include "ams_mitm_kip.h" #include "boot_100_kip.h" #include "boot_200_kip.h" +#include "spl_kip.h" #undef u8 #undef u32 @@ -45,12 +46,13 @@ static bool g_stratosphere_loader_enabled = true; static bool g_stratosphere_sm_enabled = true; static bool g_stratosphere_pm_enabled = true; static bool g_stratosphere_ams_mitm_enabled = true; +static bool g_stratosphere_spl_enabled = true; static bool g_stratosphere_boot_enabled = false; extern const uint8_t boot_100_kip[], boot_200_kip[]; -extern const uint8_t loader_kip[], pm_kip[], sm_kip[], ams_mitm_kip[]; +extern const uint8_t loader_kip[], pm_kip[], sm_kip[], spl_kip[], ams_mitm_kip[]; extern const uint32_t boot_100_kip_size, boot_200_kip_size; -extern const uint32_t loader_kip_size, pm_kip_size, sm_kip_size, ams_mitm_kip_size; +extern const uint32_t loader_kip_size, pm_kip_size, sm_kip_size, spl_kip_size, ams_mitm_kip_size; /* GCC doesn't consider the size as const... we have to write it ourselves. */ @@ -90,6 +92,11 @@ ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) { num_processes++; } + if (g_stratosphere_spl_enabled) { + size += spl_kip_size; + num_processes++; + } + if (g_stratosphere_ams_mitm_enabled) { size += ams_mitm_kip_size; num_processes++; @@ -129,6 +136,11 @@ ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) { data += sm_kip_size; } + if (g_stratosphere_spl_enabled) { + memcpy(data, spl_kip, spl_kip_size); + data += spl_kip_size; + } + if (g_stratosphere_ams_mitm_enabled) { memcpy(data, ams_mitm_kip, ams_mitm_kip_size); data += ams_mitm_kip_size; diff --git a/stratosphere/Makefile b/stratosphere/Makefile index 76ce0d97a..da374f808 100644 --- a/stratosphere/Makefile +++ b/stratosphere/Makefile @@ -1,4 +1,4 @@ -MODULES := loader pm sm boot ams_mitm eclct.stub ro creport fatal dmnt +MODULES := loader pm sm boot ams_mitm spl eclct.stub ro creport fatal dmnt SUBFOLDERS := libstratosphere $(MODULES) From 0d4a0348b579b39c0c9ff6245ce00705430b941e Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 25 Apr 2019 11:36:23 -0700 Subject: [PATCH 55/64] spl: Loosen keyslot restrictions on 1.0.0 --- stratosphere/spl/source/spl_secmon_wrapper.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/stratosphere/spl/source/spl_secmon_wrapper.cpp b/stratosphere/spl/source/spl_secmon_wrapper.cpp index e4ca99a31..13ab731af 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.cpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.cpp @@ -560,6 +560,12 @@ Result SecureMonitorWrapper::ComputeCmac(Cmac *out_cmac, u32 keyslot, const void } Result SecureMonitorWrapper::AllocateAesKeyslot(u32 *out_keyslot, const void *owner) { + if (GetRuntimeFirmwareVersion() <= FirmwareVersion_100) { + /* On 1.0.0, keyslots were kind of a wild west. */ + *out_keyslot = 0; + return ResultSuccess; + } + for (size_t i = 0; i < GetMaxKeyslots(); i++) { if (this->keyslot_owners[i] == 0) { this->keyslot_owners[i] = owner; @@ -576,13 +582,18 @@ Result SecureMonitorWrapper::ValidateAesKeyslot(u32 keyslot, const void *owner) if (keyslot >= GetMaxKeyslots()) { return ResultSplInvalidKeyslot; } - if (this->keyslot_owners[keyslot] != owner) { + if (this->keyslot_owners[keyslot] != owner && GetRuntimeFirmwareVersion() > FirmwareVersion_100) { return ResultSplInvalidKeyslot; } return ResultSuccess; } Result SecureMonitorWrapper::FreeAesKeyslot(u32 keyslot, const void *owner) { + if (GetRuntimeFirmwareVersion() <= FirmwareVersion_100) { + /* On 1.0.0, keyslots were kind of a wild west. */ + return ResultSuccess; + } + Result rc = ValidateAesKeyslot(keyslot, owner); if (R_FAILED(rc)) { return rc; From c5fa4660c89f8b5f48a6c1aeb2b24e095a7ba859 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 25 Apr 2019 11:42:44 -0700 Subject: [PATCH 56/64] fatal: add clkrst service access. --- stratosphere/fatal/fatal.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stratosphere/fatal/fatal.json b/stratosphere/fatal/fatal.json index b26fcaf34..f2365d850 100644 --- a/stratosphere/fatal/fatal.json +++ b/stratosphere/fatal/fatal.json @@ -14,7 +14,7 @@ "filesystem_access": { "permissions": "0xFFFFFFFFFFFFFFFF" }, - "service_access": ["bpc", "bpc:c", "erpt:c", "fsp-srv", "gpio", "i2c", "lbl", "lm", "nvdrv:s", "pcv", "pl:u", "pm:info", "psm", "set", "set:sys", "spsm", "spl:", "time:*", "vi:m", "vi:s"], + "service_access": ["bpc", "bpc:c", "erpt:c", "fsp-srv", "gpio", "i2c", "lbl", "lm", "nvdrv:s", "clkrst", "pcv", "pl:u", "pm:info", "psm", "set", "set:sys", "spsm", "spl:", "time:*", "vi:m", "vi:s"], "service_host": ["fatal:p", "fatal:u", "time:s"], "kernel_capabilities": [{ "type": "kernel_flags", From bf5a649928801e43184eccfbc2fa5e4882e7f150 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 25 Apr 2019 12:12:36 -0700 Subject: [PATCH 57/64] sept-secondary shouldn't be .PHONY --- sept/sept-secondary/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sept/sept-secondary/Makefile b/sept/sept-secondary/Makefile index 1628eb4f7..d41959363 100644 --- a/sept/sept-secondary/Makefile +++ b/sept/sept-secondary/Makefile @@ -137,7 +137,7 @@ clean: #--------------------------------------------------------------------------------- else -.PHONY: all $(OUTPUT).enc +.PHONY: all DEPENDS := $(OFILES:.o=.d) From 21db90bae99ddc28e76fa7569f0de72856a9336b Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 26 Apr 2019 02:32:23 -0700 Subject: [PATCH 58/64] spl: make ssl key decryption work on >= 5.0.0 --- stratosphere/spl/source/spl_rsa_service.cpp | 6 +++++- stratosphere/spl/source/spl_rsa_service.hpp | 6 ++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/stratosphere/spl/source/spl_rsa_service.cpp b/stratosphere/spl/source/spl_rsa_service.cpp index 23849b9de..348edae79 100644 --- a/stratosphere/spl/source/spl_rsa_service.cpp +++ b/stratosphere/spl/source/spl_rsa_service.cpp @@ -19,6 +19,10 @@ #include "spl_rsa_service.hpp" -Result RsaService::DecryptRsaPrivateKey(OutPointerWithClientSize dst, InPointer src, AccessKey access_key, KeySource key_source, u32 option) { +Result RsaService::DecryptRsaPrivateKeyDeprecated(OutPointerWithClientSize dst, InPointer src, AccessKey access_key, KeySource key_source, u32 option) { return this->GetSecureMonitorWrapper()->DecryptRsaPrivateKey(dst.pointer, dst.num_elements, src.pointer, src.num_elements, access_key, key_source, option); +} + +Result RsaService::DecryptRsaPrivateKey(OutPointerWithClientSize dst, InPointer src, AccessKey access_key, KeySource key_source) { + return this->GetSecureMonitorWrapper()->DecryptRsaPrivateKey(dst.pointer, dst.num_elements, src.pointer, src.num_elements, access_key, key_source, SmcDecryptOrImportMode_DecryptRsaPrivateKey); } \ No newline at end of file diff --git a/stratosphere/spl/source/spl_rsa_service.hpp b/stratosphere/spl/source/spl_rsa_service.hpp index 6e9b2471e..97e467cca 100644 --- a/stratosphere/spl/source/spl_rsa_service.hpp +++ b/stratosphere/spl/source/spl_rsa_service.hpp @@ -32,7 +32,8 @@ class RsaService : public CryptoService { } protected: /* Actual commands. */ - virtual Result DecryptRsaPrivateKey(OutPointerWithClientSize dst, InPointer src, AccessKey access_key, KeySource key_source, u32 option); + virtual Result DecryptRsaPrivateKeyDeprecated(OutPointerWithClientSize dst, InPointer src, AccessKey access_key, KeySource key_source, u32 option); + virtual Result DecryptRsaPrivateKey(OutPointerWithClientSize dst, InPointer src, AccessKey access_key, KeySource key_source); public: DEFINE_SERVICE_DISPATCH_TABLE { MakeServiceCommandMetaEx(), @@ -51,7 +52,8 @@ class RsaService : public CryptoService { MakeServiceCommandMetaEx(), MakeServiceCommandMetaEx(), MakeServiceCommandMetaEx(), - MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), }; }; From ad41d010d34a1b518d05b33a830db2936499d0f5 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 26 Apr 2019 03:10:56 -0700 Subject: [PATCH 59/64] Atmosphere: Add 0.8.9 changelog ahead of release, update system settings. --- common/defaults/system_settings.ini | 10 +++++++--- docs/changelog.md | 25 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/common/defaults/system_settings.ini b/common/defaults/system_settings.ini index 07bb5e9e7..e6987f42c 100644 --- a/common/defaults/system_settings.ini +++ b/common/defaults/system_settings.ini @@ -4,11 +4,15 @@ upload_enabled = u8!0x0 ; Enable USB 3.0 superspeed for homebrew [usb] usb30_force_enabled = u8!0x0 +; Control whether RO should ease its validation of NROs. +; (note: this is normally not necessary, and ips patches can be used.) +[ro] +ease_nro_restriction = u8!0x0 ; Atmosphere custom settings [atmosphere] -; Reboot from fatal automatically after 5 seconds (in milliseconds) -; If field is disabled fatal waits for an input indefinitely -; fatal_auto_reboot_interval = u64!‪5000‬ +; Reboot from fatal automatically after some number of milliseconds. +; If field is not present or 0, fatal will wait indefinitely for user input. +fatal_auto_reboot_interval = u64!0x0 ; Make the power menu's "reboot" button reboot to payload. ; Set to "normal" for normal reboot, "rcm" for rcm reboot. power_menu_reboot_function = str!payload diff --git a/docs/changelog.md b/docs/changelog.md index ef1b763c9..084796078 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,4 +1,29 @@ # Changelog +## 0.8.9 ++ A number of bugs were fixed, including: + + A data abort was fixed when mounting certain partitions on NAND. + + All Stratosphère system modules now only maintain a connection to `sm` when actively using it. + + This helps mitigate the scenario where sm hits the limit of 64 active connections and crashes. + + This sometimes caused crashes when custom non-Atmosphère sysmodules were active and the user played certain games (ex: Smash's Stage Builder). + + fatal now uses the 8.0.0 clkrst API, instead of silently failing to adjust clock rates on that firmware version. + + A wait loop is now performed when trying to get a session to `sm`, in the case where `sm:` is not yet registered. + + This fixes a race condition that could cause a failure to boot under certain circumstances. + + libstratosphere's handling of domain object closing has been improved. + + Previously, this code could cause crashes/extremely odd behavior (misinterpreting what object a service is) under certain circumstances. ++ An optional automatic reboot timer was added to fatal. + + By setting the system setting `atmosphere!fatal_auto_reboot_interval` to a non-zero u64 value, fatal can be made to automatically reboot after a certain number of milliseconds. + + If the setting is zero or not present, fatal will wait for user input as usual. ++ Atmosphère now provides a reimplementation of the `ro` system module. + + `ro` is responsible for loading dynamic libraries (NROs) on 3.0.0+. + + On 1.0.0-2.3.0, this is handled by `loader`. + + Atmosphere's `ro` provides this functionality (`ldr:ro`, `ro:dmnt`) on all firmware versions. + + An extension was implemented to provide support for applying IPS patches to NROs. + + All patches at paths like /atmosphere/nro_patches//.ips will be applied, allowing for easy distribution of patches. + + Both the IPS and IPS32 formats are supported. ++ Atmosphère now provides a reimplementation of the `spl` system module. + + `spl` (Secure Platform Services) is responsible for cryptographic operations, including all communications with the secure monitor (exosphère). + + In the future, this may be used to provide extensions to the API for interacting with exosphère from userland. ++ General system stability improvements to enhance the user's experience. ## 0.8.8 + Support was added for firmware version 8.0.0. + Custom exception handlers were added to stratosphere modules. From 5952ebab54e87823c799893a6ac9ddabce507d7a Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 26 Apr 2019 03:13:15 -0700 Subject: [PATCH 60/64] spl: amend range for DecryptRsaPrivateKeyDeprecated --- stratosphere/spl/source/spl_rsa_service.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stratosphere/spl/source/spl_rsa_service.hpp b/stratosphere/spl/source/spl_rsa_service.hpp index 97e467cca..91518bbe8 100644 --- a/stratosphere/spl/source/spl_rsa_service.hpp +++ b/stratosphere/spl/source/spl_rsa_service.hpp @@ -52,7 +52,7 @@ class RsaService : public CryptoService { MakeServiceCommandMetaEx(), MakeServiceCommandMetaEx(), MakeServiceCommandMetaEx(), - MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), MakeServiceCommandMetaEx(), }; From 3207c38a44c9ba46932ce515d4e8a66885aac7dc Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 26 Apr 2019 03:20:12 -0700 Subject: [PATCH 61/64] spl: use deprecated ssl decrypt command on 4.x in child interfaces --- stratosphere/spl/source/spl_es_service.hpp | 3 ++- stratosphere/spl/source/spl_manu_service.hpp | 3 ++- stratosphere/spl/source/spl_ssl_service.hpp | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/stratosphere/spl/source/spl_es_service.hpp b/stratosphere/spl/source/spl_es_service.hpp index 2fba15a30..47c5b58ae 100644 --- a/stratosphere/spl/source/spl_es_service.hpp +++ b/stratosphere/spl/source/spl_es_service.hpp @@ -57,7 +57,8 @@ class EsService : public RsaService { MakeServiceCommandMetaEx(), MakeServiceCommandMetaEx(), MakeServiceCommandMetaEx(), - MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), MakeServiceCommandMetaEx(), MakeServiceCommandMetaEx(), MakeServiceCommandMetaEx(), diff --git a/stratosphere/spl/source/spl_manu_service.hpp b/stratosphere/spl/source/spl_manu_service.hpp index e3e911570..7d78ff87c 100644 --- a/stratosphere/spl/source/spl_manu_service.hpp +++ b/stratosphere/spl/source/spl_manu_service.hpp @@ -51,7 +51,8 @@ class ManuService : public RsaService { MakeServiceCommandMetaEx(), MakeServiceCommandMetaEx(), MakeServiceCommandMetaEx(), - MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), MakeServiceCommandMetaEx(), }; diff --git a/stratosphere/spl/source/spl_ssl_service.hpp b/stratosphere/spl/source/spl_ssl_service.hpp index cfcdff866..0bf024468 100644 --- a/stratosphere/spl/source/spl_ssl_service.hpp +++ b/stratosphere/spl/source/spl_ssl_service.hpp @@ -52,7 +52,8 @@ class SslService : public RsaService { MakeServiceCommandMetaEx(), MakeServiceCommandMetaEx(), MakeServiceCommandMetaEx(), - MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), MakeServiceCommandMetaEx(), MakeServiceCommandMetaEx(), From 7fef83885f7e233c647429bf5955bbe499856e65 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 26 Apr 2019 07:25:38 -0700 Subject: [PATCH 62/64] ams: improve fatal error context --- fusee/fusee-primary/src/panic.c | 5 ++++- fusee/fusee-primary/src/panic.h | 14 ++++++++++++-- .../source/bpc_mitm/bpcmitm_reboot_manager.cpp | 10 +++++----- stratosphere/libstratosphere | 2 +- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/fusee/fusee-primary/src/panic.c b/fusee/fusee-primary/src/panic.c index 706955ced..be7b8d35d 100644 --- a/fusee/fusee-primary/src/panic.c +++ b/fusee/fusee-primary/src/panic.c @@ -41,6 +41,8 @@ static const char *get_error_desc_str(uint32_t error_desc) { return "SError"; case 0x301: return "Bad SVC"; + case 0xFFE: + return "std::abort() called"; default: return "Unknown"; } @@ -48,7 +50,8 @@ static const char *get_error_desc_str(uint32_t error_desc) { static void _check_and_display_atmosphere_fatal_error(void) { /* Check for valid magic. */ - if (ATMOSPHERE_FATAL_ERROR_CONTEXT->magic != ATMOSPHERE_REBOOT_TO_FATAL_MAGIC) { + if (ATMOSPHERE_FATAL_ERROR_CONTEXT->magic != ATMOSPHERE_REBOOT_TO_FATAL_MAGIC && + ATMOSPHERE_FATAL_ERROR_CONTEXT->magic != ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_0) { return; } diff --git a/fusee/fusee-primary/src/panic.h b/fusee/fusee-primary/src/panic.h index 27d1f4982..1f6654f30 100644 --- a/fusee/fusee-primary/src/panic.h +++ b/fusee/fusee-primary/src/panic.h @@ -28,6 +28,10 @@ #define PANIC_CODE_SAFEMODE 0x00000020 + +#define AMS_FATAL_ERROR_MAX_STACKTRACE 0x20 +#define AMS_FATAL_ERROR_MAX_STACKDUMP 0x100 + /* Atmosphere reboot-to-fatal-error. */ typedef struct { uint32_t magic; @@ -43,17 +47,23 @@ typedef struct { }; }; uint64_t pc; - uint64_t padding; + uint64_t module_base; uint32_t pstate; uint32_t afsr0; uint32_t afsr1; uint32_t esr; uint64_t far; uint64_t report_identifier; /* Normally just system tick. */ + uint64_t stack_trace_size; + uint64_t stack_dump_size; + uint64_t stack_trace[AMS_FATAL_ERROR_MAX_STACKTRACE]; + uint8_t stack_dump[AMS_FATAL_ERROR_MAX_STACKDUMP]; } atmosphere_fatal_error_ctx; +/* "AFE1" */ +#define ATMOSPHERE_REBOOT_TO_FATAL_MAGIC 0x31454641 /* "AFE0" */ -#define ATMOSPHERE_REBOOT_TO_FATAL_MAGIC 0x30454641 +#define ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_0 0x30454641 #define ATMOSPHERE_FATAL_ERROR_CONTEXT ((volatile atmosphere_fatal_error_ctx *)(0x4003E000)) diff --git a/stratosphere/ams_mitm/source/bpc_mitm/bpcmitm_reboot_manager.cpp b/stratosphere/ams_mitm/source/bpc_mitm/bpcmitm_reboot_manager.cpp index 4c6c71d66..98f37bcf1 100644 --- a/stratosphere/ams_mitm/source/bpc_mitm/bpcmitm_reboot_manager.cpp +++ b/stratosphere/ams_mitm/source/bpc_mitm/bpcmitm_reboot_manager.cpp @@ -101,11 +101,6 @@ Result BpcRebootManager::PerformReboot() { } void BpcRebootManager::RebootForFatalError(AtmosphereFatalErrorContext *ctx) { - /* If we don't actually have a payload loaded, just go to RCM. */ - if (!g_payload_loaded) { - RebootToRcm(); - } - /* Ensure clean IRAM state. */ ClearIram(); @@ -118,5 +113,10 @@ void BpcRebootManager::RebootForFatalError(AtmosphereFatalErrorContext *ctx) { memcpy(g_work_page, ctx, sizeof(*ctx)); CopyToIram(IRAM_PAYLOAD_BASE + IRAM_PAYLOAD_MAX_SIZE, g_work_page, sizeof(g_work_page)); + /* If we don't actually have a payload loaded, just go to RCM. */ + if (!g_payload_loaded) { + RebootToRcm(); + } + RebootToIramPayload(); } \ No newline at end of file diff --git a/stratosphere/libstratosphere b/stratosphere/libstratosphere index 63fc847f8..9dfe7709d 160000 --- a/stratosphere/libstratosphere +++ b/stratosphere/libstratosphere @@ -1 +1 @@ -Subproject commit 63fc847f8ae43b173a9031071eebb76a1961c41c +Subproject commit 9dfe7709d950ef440548b123e43ea69ce52684b4 From dcc93ce60e4c4fed9d21aefb310bca64476dd34f Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 26 Apr 2019 07:32:23 -0700 Subject: [PATCH 63/64] strat: disable exception handlers when creport would be better --- stratosphere/dmnt/source/dmnt_main.cpp | 8 -------- stratosphere/ro/source/ro_main.cpp | 4 ---- 2 files changed, 12 deletions(-) diff --git a/stratosphere/dmnt/source/dmnt_main.cpp b/stratosphere/dmnt/source/dmnt_main.cpp index ce140113e..d63c466a2 100644 --- a/stratosphere/dmnt/source/dmnt_main.cpp +++ b/stratosphere/dmnt/source/dmnt_main.cpp @@ -42,15 +42,7 @@ extern "C" { void __appExit(void); /* Exception handling. */ - alignas(16) u8 __nx_exception_stack[0x1000]; - u64 __nx_exception_stack_size = sizeof(__nx_exception_stack); - void __libnx_exception_handler(ThreadExceptionDump *ctx); u64 __stratosphere_title_id = TitleId_Dmnt; - void __libstratosphere_exception_handler(AtmosphereFatalErrorContext *ctx); -} - -void __libnx_exception_handler(ThreadExceptionDump *ctx) { - StratosphereCrashHandler(ctx); } diff --git a/stratosphere/ro/source/ro_main.cpp b/stratosphere/ro/source/ro_main.cpp index 0f260ef73..b77c4b0a1 100644 --- a/stratosphere/ro/source/ro_main.cpp +++ b/stratosphere/ro/source/ro_main.cpp @@ -41,11 +41,7 @@ extern "C" { void __appExit(void); /* Exception handling. */ - alignas(16) u8 __nx_exception_stack[0x1000]; - u64 __nx_exception_stack_size = sizeof(__nx_exception_stack); - void __libnx_exception_handler(ThreadExceptionDump *ctx); u64 __stratosphere_title_id = TitleId_Ro; - void __libstratosphere_exception_handler(AtmosphereFatalErrorContext *ctx); } void __libnx_exception_handler(ThreadExceptionDump *ctx) { From 8c3dae846e1467d962d1092710c6c1642af8d33f Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 26 Apr 2019 07:32:49 -0700 Subject: [PATCH 64/64] Bump version to 0.8.9 --- common/include/atmosphere/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/include/atmosphere/version.h b/common/include/atmosphere/version.h index 408d1b551..5c95d218b 100644 --- a/common/include/atmosphere/version.h +++ b/common/include/atmosphere/version.h @@ -19,7 +19,7 @@ #define ATMOSPHERE_RELEASE_VERSION_MAJOR 0 #define ATMOSPHERE_RELEASE_VERSION_MINOR 8 -#define ATMOSPHERE_RELEASE_VERSION_MICRO 8 +#define ATMOSPHERE_RELEASE_VERSION_MICRO 9 #define ATMOSPHERE_SUPPORTED_HOS_VERSION_MAJOR 8 #define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 0