diff --git a/Makefile b/Makefile index e38f766b9..a380a5d94 100644 --- a/Makefile +++ b/Makefile @@ -50,12 +50,13 @@ dist: all mkdir atmosphere-$(AMSVER)/atmosphere mkdir atmosphere-$(AMSVER)/sept mkdir atmosphere-$(AMSVER)/switch - 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 @@ -66,13 +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 troposphere/reboot_to_payload/reboot_to_payload.nro atmosphere-$(AMSVER)/switch/reboot_to_payload.nro + 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 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 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 000000000..05869a6e9 Binary files /dev/null and b/common/defaults/kip_patches/default_nogc/B2F5176B3548364D079A29B141A23B06AFFF5A98055576D5F337A621C0233CE3.ips differ 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 000000000..6e66bc3bb Binary files /dev/null and b/common/defaults/kip_patches/default_nogc/DBD941C0C53C52CCF7202C84D8E0F78013A3684D8AB5D128096674A8F7755B3D.ips differ diff --git a/common/defaults/system_settings.ini b/common/defaults/system_settings.ini index 075c0d454..e6987f42c 100644 --- a/common/defaults/system_settings.ini +++ b/common/defaults/system_settings.ini @@ -4,8 +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 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/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..5c95d218b 100644 --- a/common/include/atmosphere/version.h +++ b/common/include/atmosphere/version.h @@ -19,9 +19,9 @@ #define ATMOSPHERE_RELEASE_VERSION_MAJOR 0 #define ATMOSPHERE_RELEASE_VERSION_MINOR 8 -#define ATMOSPHERE_RELEASE_VERSION_MICRO 7 +#define ATMOSPHERE_RELEASE_VERSION_MICRO 9 -#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 diff --git a/docs/changelog.md b/docs/changelog.md index 2ada32830..084796078 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,4 +1,36 @@ # 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. + + 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. + A bug was fixed that caused an error when launching certain games (e.g. Hellblade: Senua's Sacrifice). 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..c62781b95 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; } @@ -334,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; + } } } @@ -390,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)) { @@ -487,6 +494,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 +562,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/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/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-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/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/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/kernel_patches.c b/fusee/fusee-secondary/src/kernel_patches.c index f12cc1fff..0568d8a01 100644 --- a/fusee/fusee-secondary/src/kernel_patches.c +++ b/fusee/fusee-secondary/src/kernel_patches.c @@ -41,6 +41,10 @@ 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_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; unsigned int num_patches; const kernel_patch_t *patches; @@ -366,11 +370,67 @@ 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}; 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 +592,29 @@ static const kernel_patch_t g_kernel_patches_700[] = { .patch_offset = 0x3C6E0, } }; +static const kernel_patch_t g_kernel_patches_800[] = { + { /* 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), + .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 +664,15 @@ 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 = {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, + KERNEL_PATCHES(800) } }; @@ -607,17 +699,27 @@ 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, (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]; + } } } 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); @@ -631,6 +733,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/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/main.c b/fusee/fusee-secondary/src/main.c index d5d3e551a..e7b8747a9 100644 --- a/fusee/fusee-secondary/src/main.c +++ b/fusee/fusee-secondary/src/main.c @@ -60,6 +60,7 @@ 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_end(); @@ -119,6 +120,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/nxboot.c b/fusee/fusee-secondary/src/nxboot.c index 1dcb8c81b..6593bc2eb 100644 --- a/fusee/fusee-secondary/src/nxboot.c +++ b/fusee/fusee-secondary/src/nxboot.c @@ -190,8 +190,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"); } @@ -514,7 +516,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; diff --git a/fusee/fusee-secondary/src/nxfs.c b/fusee/fusee-secondary/src/nxfs.c index c993133ea..701523fd2 100644 --- a/fusee/fusee-secondary/src/nxfs.c +++ b/fusee/fusee-secondary/src/nxfs.c @@ -56,7 +56,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; @@ -74,6 +75,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; @@ -98,13 +100,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/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/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/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/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; } 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) 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. */ diff --git a/stratosphere/Makefile b/stratosphere/Makefile index 7bbb09435..da374f808 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 spl eclct.stub ro creport fatal dmnt SUBFOLDERS := libstratosphere $(MODULES) 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/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/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) { 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/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_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..d63c466a2 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,21 +36,13 @@ 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); /* 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); } @@ -68,68 +60,64 @@ 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); - } - - /* - if (kernelAbove300()) { - rc = roDmntInitialize(); + + DoWithSmSession([&]() { + rc = pmdmntInitialize(); 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 = ldrDmntInitialize(); + 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(); + 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); } @@ -142,31 +130,30 @@ void __appExit(void) { setExit(); lrExit(); nsdevExit(); - /* if (kernelAbove300()) { roDmntExit(); } */ + 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/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", 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_main.cpp b/stratosphere/fatal/source/fatal_main.cpp index 2f49576f7..22c1ac6e2 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,83 @@ 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(); + } + + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_800) { + rc = clkrstInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + } else { + 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); */ } @@ -158,20 +162,23 @@ void __appExit(void) { spsmExit(); psmExit(); lblExit(); - pcvExit(); + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_800) { + clkrstExit(); + } else { + pcvExit(); + } bpcExit(); i2cExit(); 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_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) { } 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_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(); diff --git a/stratosphere/fatal/source/fatal_task_screen.cpp b/stratosphere/fatal/source/fatal_task_screen.cpp index d9d138f96..7202197ee 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,54 @@ 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)))) { 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 +139,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 +155,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 +169,7 @@ Result ShowFatalTask::PrepareScreenForDrawing() { return rc; } } - + return rc; } @@ -178,33 +178,36 @@ 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; } - + /* 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 b9724cdca..9dfe7709d 160000 --- a/stratosphere/libstratosphere +++ b/stratosphere/libstratosphere @@ -1 +1 @@ -Subproject commit b9724cdcadd5ea5fbead8f1a9c9b7de11daf6b60 +Subproject commit 9dfe7709d950ef440548b123e43ea69ce52684b4 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_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_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..5c7249989 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 @@ -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__; @@ -36,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); @@ -68,30 +67,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 +98,6 @@ void __appExit(void) { fsldrExit(); lrExit(); fsExit(); - smExit(); } struct LoaderServerOptions { @@ -114,21 +110,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_map.cpp b/stratosphere/loader/source/ldr_map.cpp index 4e307937e..468a57a0b 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()) { @@ -28,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 = {}; @@ -71,7 +61,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; @@ -106,7 +96,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; @@ -127,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 + (RandomUtils::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 + (RandomUtils::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 = {}; - } -}; diff --git a/stratosphere/loader/source/ldr_nro.cpp b/stratosphere/loader/source/ldr_nro.cpp deleted file mode 100644 index 0f2cfbfda..000000000 --- a/stratosphere/loader/source/ldr_nro.cpp +++ /dev/null @@ -1,144 +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" -#include "ldr_random.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_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_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_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_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_registration.cpp b/stratosphere/loader/source/ldr_registration.cpp index db37baae1..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; @@ -34,7 +33,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 +42,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]; } @@ -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_LIST_MAX; 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) { @@ -109,165 +99,31 @@ 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; } } -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 < NRR_INFO_MAX; 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 < NRR_INFO_MAX; 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 < NRR_INFO_MAX; 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 < NRO_INFO_MAX; 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 < NRO_INFO_MAX; 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::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..6588a8ae1 100644 --- a/stratosphere/loader/source/ldr_registration.hpp +++ b/stratosphere/loader/source/ldr_registration.hpp @@ -20,39 +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; + public: + struct ModuleInfoHolder { bool in_use; - NsoInfo info; + 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; @@ -65,34 +42,23 @@ class Registration { u64 process_id; u64 title_id; Registration::TidSid tid_sid; - std::array nso_infos; - std::array nro_infos; - std::array nrr_infos; - void *owner_ro_service; + std::array module_infos; }; struct List { - std::array processes; + std::array processes; u64 num_processes; }; 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 AddNsoInfo(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 GetNsoInfosForProcessId(NsoInfo *out, u32 max_out, u64 process_id, u32 *num_written); + static void AddModuleInfo(u64 index, u64 base_address, u64 size, const unsigned char *build_id); + static Result GetProcessModuleInfo(LoaderModuleInfo *out, u32 max_out, u64 process_id, u32 *num_written); /* Atmosphere MitM Extension. */ static void AssociatePidTidForMitM(u64 index); 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/pm/source/pm_boot2.cpp b/stratosphere/pm/source/pm_boot2.cpp index e04eb1c54..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,23 +245,28 @@ 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)) { 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. */ DIR *titles_dir = opendir("sdmc:/atmosphere/titles"); struct dirent *ent; @@ -283,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 3aac9a359..cab914dd8 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,37 +154,34 @@ 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))) { - /* TODO: Panic. */ + std::abort(); } if (R_FAILED(process_track_thread.Start())) { - /* TODO: Panic. */ + std::abort(); } - - /* TODO: What's a good timeout value to use here? */ - auto server_manager = new WaitableManager(1); - + + /* Create Server Manager. */ + static auto s_server_manager = 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", 5)); - server_manager->AddWaitable(new ServiceServer("pm:info", 1)); + s_server_manager.AddWaitable(new ServiceServer("pm:shell", 3)); + s_server_manager.AddWaitable(new ServiceServer("pm:dmnt", 2)); + s_server_manager.AddWaitable(new ServiceServer("pm:bm", 6)); + s_server_manager.AddWaitable(new ServiceServer("pm:info", 2)); /* Loop forever, servicing our services. */ - server_manager->Process(); - - /* Cleanup. */ - delete server_manager; + s_server_manager.Process(); + return 0; } 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(), }; }; 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..57d4d3bf3 --- /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", "pm:info"], + "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_debug_monitor.cpp b/stratosphere/ro/source/ro_debug_monitor.cpp new file mode 100644 index 000000000..ef6897c60 --- /dev/null +++ b/stratosphere/ro/source/ro_debug_monitor.cpp @@ -0,0 +1,29 @@ +/* + * 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 "ro_debug_monitor.hpp" +#include "ro_registration.hpp" + +Result DebugMonitorService::GetProcessModuleInfo(Out count, OutBuffer out_infos, u64 pid) { + 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_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 new file mode 100644 index 000000000..b77c4b0a1 --- /dev/null +++ b/stratosphere/ro/source/ro_main.cpp @@ -0,0 +1,130 @@ +/* + * 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_debug_monitor.hpp" +#include "ro_service.hpp" +#include "ro_registration.hpp" + +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. */ + u64 __stratosphere_title_id = TitleId_Ro; +} + +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(); + + DoWithSmSession([&]() { + rc = setsysInitialize(); + 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); +} + +void __appExit(void) { + fsdevUnmountAll(); + fsExit(); + if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) { + pminfoExit(); + } + setsysExit(); +} + +/* Helpers to create RO objects. */ +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)); + /* 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)); + } + + /* Loop forever, servicing our services. */ + s_server_manager.Process(); + + /* Cleanup */ + return 0; +} + 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_patcher.cpp b/stratosphere/ro/source/ro_patcher.cpp new file mode 100644 index 000000000..0ac52c98c --- /dev/null +++ b/stratosphere/ro/source/ro_patcher.cpp @@ -0,0 +1,186 @@ +/* + * 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: + 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: + 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/loader/source/ldr_random.hpp b/stratosphere/ro/source/ro_patcher.hpp similarity index 81% rename from stratosphere/loader/source/ldr_random.hpp rename to stratosphere/ro/source/ro_patcher.hpp index 9b74cdd74..ed88c7223 100644 --- a/stratosphere/loader/source/ldr_random.hpp +++ b/stratosphere/ro/source/ro_patcher.hpp @@ -13,13 +13,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + #pragma once #include +#include -class RandomUtils { +#include "ro_types.hpp" + +class PatchUtils { public: - static u32 GetNext(); - static u32 GetRandomU32(u32 max); - static u64 GetRandomU64(u64 max); + 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 new file mode 100644 index 000000000..74291375b --- /dev/null +++ b/stratosphere/ro/source/ro_registration.cpp @@ -0,0 +1,520 @@ +/* + * 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" +#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] = {}; + +static bool g_is_development_hardware, g_is_development_function_enabled; + +void Registration::Initialize() { + DoWithSmSession([&]() { + 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; + } +} + +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++) { + 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) { + 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; + *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); + } + } + svcCloseHandle(context->process_handle); + } + std::memset(context, 0, sizeof(*context)); +} + +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::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, expected_type, enforce_type); + 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::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; + } + } + } + + /* 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; + *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].base_address == nro_address) { + break; + } + } + if (slot == Registration::MaxNroInfos) { + 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) { + /* 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) { + 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); +} + +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 new file mode 100644 index 000000000..f416534f1 --- /dev/null +++ b/stratosphere/ro/source/ro_registration.hpp @@ -0,0 +1,102 @@ +/* + * 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_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 MaxNrrInfos = 0x40; + static constexpr size_t MaxNroInfos = 0x40; + + static constexpr u32 MagicNro0 = 0x304F524E; + public: + 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 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; + }; + 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(); + + 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); + 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 new file mode 100644 index 000000000..0b0a9e7c6 --- /dev/null +++ b/stratosphere/ro/source/ro_service.cpp @@ -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 . + */ + +#include +#include +#include +#include + +#include "ro_service.hpp" +#include "ro_registration.hpp" + +RelocatableObjectsService::~RelocatableObjectsService() { + if (this->IsInitialized()) { + Registration::UnregisterProcess(this->context); + this->context = nullptr; + } +} + +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) { + 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) { + 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) { + 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) { + if (!this->IsProcessIdValid(pid_desc.pid)) { + return ResultRoInvalidProcess; + } + + return Registration::UnloadNrr(this->context, nrr_address); +} + +Result RelocatableObjectsService::Initialize(PidDescriptor pid_desc, CopiedHandle process_h) { + /* 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) { + 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/loader/source/ldr_ro_service.hpp b/stratosphere/ro/source/ro_service.hpp similarity index 77% rename from stratosphere/loader/source/ldr_ro_service.hpp rename to stratosphere/ro/source/ro_service.hpp index f5cc4b62d..6354d0805 100644 --- a/stratosphere/loader/source/ldr_ro_service.hpp +++ b/stratosphere/ro/source/ro_service.hpp @@ -18,7 +18,8 @@ #include #include -#include "ldr_registration.hpp" + +#include "ro_registration.hpp" enum RoServiceCmd { Ro_Cmd_LoadNro = 0, @@ -30,17 +31,20 @@ enum RoServiceCmd { }; class RelocatableObjectsService final : public IServiceObject { - Handle process_handle = 0; - u64 process_id = U64_MAX; - bool has_initialized = false; + private: + Registration::RoProcessContext *context = nullptr; + RoModuleType type; public: - virtual ~RelocatableObjectsService() override { - Registration::CloseRoService(this, this->process_handle); - if (this->has_initialized) { - svcCloseHandle(this->process_handle); - } + explicit RelocatableObjectsService(RoModuleType t) : type(t) { + /* ... */ } - + virtual ~RelocatableObjectsService() override; + private: + 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); @@ -56,5 +60,6 @@ class RelocatableObjectsService final : public IServiceObject { MakeServiceCommandMeta(), MakeServiceCommandMeta(), MakeServiceCommandMeta(), + MakeServiceCommandMeta(), }; }; 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!"); diff --git a/stratosphere/sm/source/sm_registration.cpp b/stratosphere/sm/source/sm_registration.cpp index c9df5b026..eead4cc7d 100644 --- a/stratosphere/sm/source/sm_registration.cpp +++ b/stratosphere/sm/source/sm_registration.cpp @@ -301,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); 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_crypto_service.cpp b/stratosphere/spl/source/spl_crypto_service.cpp new file mode 100644 index 000000000..1e3522865 --- /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..2f56df6ec --- /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 { + 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.cpp b/stratosphere/spl/source/spl_ctr_drbg.cpp new file mode 100644 index 000000000..a468d2dcf --- /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..39f82ae0a --- /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_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_es_service.cpp b/stratosphere/spl/source/spl_es_service.cpp new file mode 100644 index 000000000..cb855d05e --- /dev/null +++ b/stratosphere/spl/source/spl_es_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_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) { + 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) { + return this->GetSecureMonitorWrapper()->UnwrapCommonTitleKey(out_access_key.GetPointer(), key_source, generation); +} + +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) { + 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) { + return this->GetSecureMonitorWrapper()->LoadElicenseKey(keyslot, this, access_key); +} diff --git a/stratosphere/spl/source/spl_es_service.hpp b/stratosphere/spl/source/spl_es_service.hpp new file mode 100644 index 000000000..47c5b58ae --- /dev/null +++ b/stratosphere/spl/source/spl_es_service.hpp @@ -0,0 +1,70 @@ +/* + * 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 { + MakeServiceCommandMetaEx(), + 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.cpp b/stratosphere/spl/source/spl_fs_service.cpp new file mode 100644 index 000000000..d0808e003 --- /dev/null +++ b/stratosphere/spl/source/spl_fs_service.cpp @@ -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 . + */ + +#include +#include + +#include "spl_fs_service.hpp" + +Result FsService::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 FsService::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 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..7bac28934 --- /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 { + 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_general_service.cpp b/stratosphere/spl/source/spl_general_service.cpp new file mode 100644 index 000000000..d83a5c5d2 --- /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->GetSecureMonitorWrapper()->GetConfig(out.GetPointer(), static_cast(which)); +} + +Result GeneralService::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 GeneralService::SetConfig(u32 which, u64 value) { + return this->GetSecureMonitorWrapper()->SetConfig(static_cast(which), value); +} + +Result GeneralService::GenerateRandomBytes(OutPointerWithClientSize out) { + return this->GetSecureMonitorWrapper()->GenerateRandomBytes(out.pointer, out.num_elements); +} + +Result GeneralService::IsDevelopment(Out is_dev) { + return this->GetSecureMonitorWrapper()->IsDevelopment(is_dev.GetPointer()); +} + +Result GeneralService::SetBootReason(BootReasonValue boot_reason) { + return this->GetSecureMonitorWrapper()->SetBootReason(boot_reason); +} + +Result GeneralService::GetBootReason(Out out) { + return this->GetSecureMonitorWrapper()->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..6d1879dac --- /dev/null +++ b/stratosphere/spl/source/spl_general_service.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 + +#include "spl_types.hpp" +#include "spl_secmon_wrapper.hpp" + +class GeneralService : public IServiceObject { + private: + SecureMonitorWrapper *secmon_wrapper; + public: + GeneralService(SecureMonitorWrapper *sw) : secmon_wrapper(sw) { + /* ... */ + } + + virtual ~GeneralService() { /* ... */ } + 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 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..7cae77b4a --- /dev/null +++ b/stratosphere/spl/source/spl_main.cpp @@ -0,0 +1,133 @@ +/* + * 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" +#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" + +#include "spl_deprecated_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); }; +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); }; +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); + + /* Initialize global context. */ + SecureMonitorWrapper::Initialize(); + + /* Create server manager. */ + static auto s_server_manager = WaitableManager(1); + + /* Create services. */ + 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)); + s_server_manager.AddWaitable(new ServiceServer("spl:fs", 2)); + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) { + s_server_manager.AddWaitable(new ServiceServer("spl:manu", 1)); + } + } else { + s_server_manager.AddWaitable(new ServiceServer("spl:", 12)); + } + + /* Loop forever, servicing our services. */ + s_server_manager.Process(); + + return 0; +} + 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..7d78ff87c --- /dev/null +++ b/stratosphere/spl/source/spl_manu_service.hpp @@ -0,0 +1,59 @@ +/* + * 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 { + 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_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..66261f087 --- /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_rsa_service.cpp b/stratosphere/spl/source/spl_rsa_service.cpp new file mode 100644 index 000000000..348edae79 --- /dev/null +++ b/stratosphere/spl/source/spl_rsa_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_rsa_service.hpp" + +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 new file mode 100644 index 000000000..91518bbe8 --- /dev/null +++ b/stratosphere/spl/source/spl_rsa_service.hpp @@ -0,0 +1,59 @@ +/* + * 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 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(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + + }; +}; diff --git a/stratosphere/spl/source/spl_secmon_wrapper.cpp b/stratosphere/spl/source/spl_secmon_wrapper.cpp new file mode 100644 index 000000000..13ab731af --- /dev/null +++ b/stratosphere/spl/source/spl_secmon_wrapper.cpp @@ -0,0 +1,917 @@ +/* + * 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" +#include "spl_smc_wrapper.hpp" +#include "spl_ctr_drbg.hpp" + +/* Convenient. */ +constexpr size_t DeviceAddressSpaceAlignSize = 0x400000; +constexpr size_t DeviceAddressSpaceAlignMask = DeviceAddressSpaceAlignSize - 1; +constexpr u32 WorkBufferMapBase = 0x80000000u; +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; +constexpr size_t LabelDigestSizeMax = 0x20; + +/* 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_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::InitializeSeEvents() { + 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); + + g_se_keyslot_available_event = CreateWriteOnlySystemEvent(); + g_se_keyslot_available_event->Signal(); +} + +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 = 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(); + } +} + +void SecureMonitorWrapper::Initialize() { + /* Initialize the Drbg. */ + InitializeCtrDrbg(); + /* Initialize SE interrupt + keyslot events. */ + InitializeSeEvents(); + /* Initialize DAS for the SE. */ + 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); +} + +Result SecureMonitorWrapper::ConvertToSplResult(SmcResult result) { + if (result == SmcResult_Success) { + return ResultSuccess; + } + if (result < SmcResult_Max) { + return MAKERESULT(Module_Spl, static_cast(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; +} + +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; + 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) { + 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 > 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->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_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; +} + +Result SecureMonitorWrapper::SetConfig(SplConfigItem which, u64 value) { + 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) { + 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) { + 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; +} + +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 = CryptAesOutMapBase + (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) { + 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; + *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 && 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; + } + + /* 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::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::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 + 0x10]; + }; + 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 + 0x10]; + }; + 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::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); +} + +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::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::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)); +} + +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::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]; + 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) { + 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 new file mode 100644 index 000000000..83534ee96 --- /dev/null +++ b/stratosphere/spl/source/spl_secmon_wrapper.hpp @@ -0,0 +1,139 @@ +/* + * 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: + const void *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); + private: + 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: + 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); + 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); + 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); + 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); + + /* 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); + + /* 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 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 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); + + /* 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(); + 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 new file mode 100644 index 000000000..5845f37ce --- /dev/null +++ b/stratosphere/spl/source/spl_smc_wrapper.cpp @@ -0,0 +1,336 @@ +/* + * 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 KeySource &source, u32 generation, u32 option) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_GenerateAesKek; + args.X[1] = source.data64[0]; + args.X[2] = source.data64[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 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.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 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.data64[0]; + args.X[3] = iv_ctr.data64[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(AesKey *out_key, const KeySource &source, u32 generation, u32 which) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_GenerateSpecificAesKey; + args.X[1] = source.data64[0]; + args.X[2] = source.data64[1]; + args.X[3] = generation; + 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]); +} + +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 KeySource &source_dec, const AccessKey &access_key_enc, const KeySource &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 KeySource &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.data64[0]; + args.X[7] = source.data64[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 KeySource &source, u32 generation) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_UnwrapCommonTitleKey; + args.X[1] = source.data64[0]; + args.X[2] = source.data64[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 KeySource &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.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 KeySource &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.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 KeySource &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.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 new file mode 100644 index 000000000..ea8798ee5 --- /dev/null +++ b/stratosphere/spl/source/spl_smc_wrapper.hpp @@ -0,0 +1,54 @@ +/* + * 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 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); + 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 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(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); + 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 KeySource &source, u32 generation); + + /* Deprecated functions. */ + 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_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..0bf024468 --- /dev/null +++ b/stratosphere/spl/source/spl_ssl_service.hpp @@ -0,0 +1,61 @@ +/* + * 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 { + 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_types.hpp b/stratosphere/spl/source/spl_types.hpp new file mode 100644 index 000000000..c4cebbe2f --- /dev/null +++ b/stratosphere/spl/source/spl_types.hpp @@ -0,0 +1,161 @@ +/* + * 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, +}; + +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, +}; + +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!"); + +#pragma pack(push, 1) +struct AesKey { + union { + u8 data[AES_128_KEY_SIZE]; + u64 data64[AES_128_KEY_SIZE / sizeof(u64)]; + }; +}; +static_assert(alignof(AesKey) == alignof(u8), "AesKey definition!"); + +struct IvCtr { + union { + u8 data[AES_128_KEY_SIZE]; + u64 data64[AES_128_KEY_SIZE / sizeof(u64)]; + }; +}; +static_assert(alignof(IvCtr) == alignof(u8), "IvCtr definition!"); + +struct Cmac { + union { + u8 data[AES_128_KEY_SIZE]; + u64 data64[AES_128_KEY_SIZE / sizeof(u64)]; + }; +}; +static_assert(alignof(Cmac) == alignof(u8), "Cmac definition!"); + +struct AccessKey { + union { + u8 data[AES_128_KEY_SIZE]; + u64 data64[AES_128_KEY_SIZE / sizeof(u64)]; + }; +}; +static_assert(alignof(AccessKey) == alignof(u8), "AccessKey definition!"); + +struct KeySource { + union { + u8 data[AES_128_KEY_SIZE]; + 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+ */ + 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_GetAesKeyslotAvailableEvent = 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