diff --git a/config_templates/system_settings.ini b/config_templates/system_settings.ini index abdc73c5e..f02491abb 100644 --- a/config_templates/system_settings.ini +++ b/config_templates/system_settings.ini @@ -1,9 +1,13 @@ -; Disable uploading error reports to Nintendo [eupld] +; Disable uploading error reports to Nintendo ; upload_enabled = u8!0x0 +[usb] +; Enable USB 3.0 superspeed for homebrew +; 0 = USB 3.0 support is system default (usually disabled), 1 = USB 3.0 support is enabled. +; usb30_force_enabled = u8!0x0 +[ro] ; 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!0x1 ; Atmosphere custom settings [atmosphere] diff --git a/exosphere/program/source/smc/secmon_smc_info.cpp b/exosphere/program/source/smc/secmon_smc_info.cpp index 468b134fb..de7fbeb72 100644 --- a/exosphere/program/source/smc/secmon_smc_info.cpp +++ b/exosphere/program/source/smc/secmon_smc_info.cpp @@ -286,6 +286,10 @@ namespace ams::secmon::smc { /* Get the log configuration. */ args.r[1] = (static_cast(static_cast(secmon::GetLogPort())) << 32) | static_cast(secmon::GetLogBaudRate()); break; + case ConfigItem::ExosphereForceEnableUsb30: + /* Get whether usb 3.0 should be force-enabled. */ + args.r[1] = GetSecmonConfiguration().IsUsb30ForceEnabled(); + break; default: return SmcResult::InvalidArgument; } diff --git a/exosphere/program/source/smc/secmon_smc_info.hpp b/exosphere/program/source/smc/secmon_smc_info.hpp index 12870324e..c7481775f 100644 --- a/exosphere/program/source/smc/secmon_smc_info.hpp +++ b/exosphere/program/source/smc/secmon_smc_info.hpp @@ -50,6 +50,7 @@ namespace ams::secmon::smc { ExosphereEmummcType = 65007, ExospherePayloadAddress = 65008, ExosphereLogConfiguration = 65009, + ExosphereForceEnableUsb30 = 65010, }; SmcResult SmcGetConfigUser(SmcArguments &args); diff --git a/fusee/fusee-secondary/src/exocfg.h b/fusee/fusee-secondary/src/exocfg.h index 36cca04dc..42885f8ec 100644 --- a/fusee/fusee-secondary/src/exocfg.h +++ b/fusee/fusee-secondary/src/exocfg.h @@ -33,6 +33,7 @@ #define EXOSPHERE_FLAG_ENABLE_USERMODE_PMU_ACCESS (1 << 4u) #define EXOSPHERE_FLAG_BLANK_PRODINFO (1 << 5u) #define EXOSPHERE_FLAG_ALLOW_WRITING_TO_CAL_SYSMMC (1 << 6u) +#define EXOSPHERE_FLAG_FORCE_ENABLE_USB_30 (1 << 7u) #define EXOSPHERE_LOG_FLAG_INVERTED (1 << 0u) diff --git a/fusee/fusee-secondary/src/nxboot.c b/fusee/fusee-secondary/src/nxboot.c index e63fa0950..ee81427e1 100644 --- a/fusee/fusee-secondary/src/nxboot.c +++ b/fusee/fusee-secondary/src/nxboot.c @@ -256,6 +256,24 @@ static int stratosphere_ini_handler(void *user, const char *section, const char return 1; } +static int system_settings_ini_handler(void *user, const char *section, const char *name, const char *value) { + uint32_t *flags = (uint32_t *)user; + if (strcmp(section, "usb") == 0) { + if (strcmp(name, "usb30_force_enabled") == 0) { + if (strcmp(value, "u8!0x1") == 0) { + *flags |= EXOSPHERE_FLAG_FORCE_ENABLE_USB_30; + } else if (strcmp(value, "u8!0x0") == 0) { + *flags &= ~(EXOSPHERE_FLAG_FORCE_ENABLE_USB_30); + } + } else { + return 0; + } + } else { + return 0; + } + return 1; +} + static bool is_nca_present(const char *nca_name) { char path[0x100]; snprintf(path, sizeof(path), "system:/contents/registered/%s.nca", nca_name); @@ -537,6 +555,15 @@ static void nxboot_configure_exosphere(uint32_t target_firmware, unsigned int ke /* Apply lcd vendor. */ exo_cfg.lcd_vendor = display_get_lcd_vendor(); + /* Read and parse system settings.ini to determine usb 3.0 enable. */ + char *settings_ini = calloc(1, 0x20000); + if (read_from_file(settings_ini, 0x1FFFF, "atmosphere/config/system_settings.ini")) { + if (ini_parse_string(settings_ini, system_settings_ini_handler, &exo_cfg.flags[0]) < 0) { + fatal_error("[NXBOOT] Failed to parse system_settings.ini!\n"); + } + } + free(settings_ini); + if ((exo_cfg.target_firmware < ATMOSPHERE_TARGET_FIRMWARE_MIN) || (exo_cfg.target_firmware > ATMOSPHERE_TARGET_FIRMWARE_MAX)) { fatal_error("[NXBOOT] Invalid Exosphere target firmware!\n"); } diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp index 6c974b1c7..1bfcfb955 100644 --- a/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp @@ -29,6 +29,7 @@ namespace ams::secmon { SecureMonitorConfigurationFlag_EnableUserModePerformanceCounterAccess = (1u << 4), SecureMonitorConfigurationFlag_ShouldUseBlankCalibrationBinary = (1u << 5), SecureMonitorConfigurationFlag_AllowWritingToCalibrationBinarySysmmc = (1u << 6), + SecureMonitorConfigurationFlag_ForceEnableUsb30 = (1u << 7), SecureMonitorConfigurationFlag_Default = SecureMonitorConfigurationFlag_IsDevelopmentFunctionEnabledForKernel, }; @@ -101,6 +102,7 @@ namespace ams::secmon { constexpr bool EnableUserModePerformanceCounterAccess() const { return (this->flags[0] & SecureMonitorConfigurationFlag_EnableUserModePerformanceCounterAccess) != 0; } constexpr bool ShouldUseBlankCalibrationBinary() const { return (this->flags[0] & SecureMonitorConfigurationFlag_ShouldUseBlankCalibrationBinary) != 0; } constexpr bool AllowWritingToCalibrationBinarySysmmc() const { return (this->flags[0] & SecureMonitorConfigurationFlag_AllowWritingToCalibrationBinarySysmmc) != 0; } + constexpr bool IsUsb30ForceEnabled() const { return (this->flags[0] & SecureMonitorConfigurationFlag_ForceEnableUsb30) != 0; } constexpr bool IsDevelopmentFunctionEnabled(bool for_kern) const { return for_kern ? this->IsDevelopmentFunctionEnabledForKernel() : this->IsDevelopmentFunctionEnabledForUser(); } }; diff --git a/libraries/libstratosphere/include/stratosphere/spl/spl_api.hpp b/libraries/libstratosphere/include/stratosphere/spl/spl_api.hpp index 506290b39..f38a0d1ec 100644 --- a/libraries/libstratosphere/include/stratosphere/spl/spl_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/spl/spl_api.hpp @@ -78,6 +78,10 @@ namespace ams::spl { return ::ams::spl::GetConfigBool(::ams::spl::ConfigItem::DisableProgramVerification); } + inline bool IsUsb30ForceEnabled() { + return ::ams::spl::GetConfigBool(::ams::spl::ConfigItem::ExosphereForceEnableUsb30); + } + Result SetBootReason(BootReasonValue boot_reason); Result GetBootReason(BootReasonValue *out); diff --git a/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp b/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp index 3e1f2d74e..fa510ab53 100644 --- a/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp @@ -223,26 +223,30 @@ namespace ams::spl { Package2Hash = 17, /* Extension config items for exosphere. */ - ExosphereApiVersion = 65000, - ExosphereNeedsReboot = 65001, - ExosphereNeedsShutdown = 65002, - ExosphereGitCommitHash = 65003, - ExosphereHasRcmBugPatch = 65004, - ExosphereBlankProdInfo = 65005, - ExosphereAllowCalWrites = 65006, - ExosphereEmummcType = 65007, - ExospherePayloadAddress = 65008, + ExosphereApiVersion = 65000, + ExosphereNeedsReboot = 65001, + ExosphereNeedsShutdown = 65002, + ExosphereGitCommitHash = 65003, + ExosphereHasRcmBugPatch = 65004, + ExosphereBlankProdInfo = 65005, + ExosphereAllowCalWrites = 65006, + ExosphereEmummcType = 65007, + ExospherePayloadAddress = 65008, + ExosphereLogConfiguration = 65009, + ExosphereForceEnableUsb30 = 65010, }; } /* Extensions to libnx spl config item enum. */ -constexpr inline SplConfigItem SplConfigItem_ExosphereApiVersion = static_cast(65000); -constexpr inline SplConfigItem SplConfigItem_ExosphereNeedsReboot = static_cast(65001); -constexpr inline SplConfigItem SplConfigItem_ExosphereNeedsShutdown = static_cast(65002); -constexpr inline SplConfigItem SplConfigItem_ExosphereGitCommitHash = static_cast(65003); -constexpr inline SplConfigItem SplConfigItem_ExosphereHasRcmBugPatch = static_cast(65004); -constexpr inline SplConfigItem SplConfigItem_ExosphereBlankProdInfo = static_cast(65005); -constexpr inline SplConfigItem SplConfigItem_ExosphereAllowCalWrites = static_cast(65006); -constexpr inline SplConfigItem SplConfigItem_ExosphereEmummcType = static_cast(65007); -constexpr inline SplConfigItem SplConfigItem_ExospherePayloadAddress = static_cast(65008); +constexpr inline SplConfigItem SplConfigItem_ExosphereApiVersion = static_cast(65000); +constexpr inline SplConfigItem SplConfigItem_ExosphereNeedsReboot = static_cast(65001); +constexpr inline SplConfigItem SplConfigItem_ExosphereNeedsShutdown = static_cast(65002); +constexpr inline SplConfigItem SplConfigItem_ExosphereGitCommitHash = static_cast(65003); +constexpr inline SplConfigItem SplConfigItem_ExosphereHasRcmBugPatch = static_cast(65004); +constexpr inline SplConfigItem SplConfigItem_ExosphereBlankProdInfo = static_cast(65005); +constexpr inline SplConfigItem SplConfigItem_ExosphereAllowCalWrites = static_cast(65006); +constexpr inline SplConfigItem SplConfigItem_ExosphereEmummcType = static_cast(65007); +constexpr inline SplConfigItem SplConfigItem_ExospherePayloadAddress = static_cast(65008); +constexpr inline SplConfigItem SplConfigItem_ExosphereLogConfiguration = static_cast(65009); +constexpr inline SplConfigItem SplConfigItem_ExosphereForceEnableUsb30 = static_cast(65010); diff --git a/libraries/libstratosphere/source/boot2/boot2_api.cpp b/libraries/libstratosphere/source/boot2/boot2_api.cpp index a9bddc42e..6e2879b86 100644 --- a/libraries/libstratosphere/source/boot2/boot2_api.cpp +++ b/libraries/libstratosphere/source/boot2/boot2_api.cpp @@ -307,6 +307,10 @@ namespace ams::boot2 { }); } + bool IsUsbRequiredToMountSdCard() { + return hos::GetVersion() >= hos::Version_9_0_0; + } + } /* Boot2 API. */ @@ -347,8 +351,10 @@ namespace ams::boot2 { /* Launch pcv. */ LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Pcv, ncm::StorageId::BuiltInSystem), 0); - /* Launch usb. */ - LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Usb, ncm::StorageId::BuiltInSystem), 0); + /* On 9.0.0+, FS depends on the USB sysmodule having been launched in order to mount the SD card. */ + if (IsUsbRequiredToMountSdCard()) { + LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Usb, ncm::StorageId::BuiltInSystem), 0); + } } /* Wait for the SD card required services to be ready. */ @@ -371,6 +377,11 @@ namespace ams::boot2 { void LaunchPostSdCardBootPrograms() { /* This code is normally run by boot2. */ + /* Launch the usb system module, if we haven't already. */ + if (!IsUsbRequiredToMountSdCard()) { + LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Usb, ncm::StorageId::BuiltInSystem), 0); + } + /* Find out whether we are maintenance mode. */ const bool maintenance = IsMaintenanceMode(); if (maintenance) { diff --git a/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp b/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp index 7a294145e..8e15ae3f9 100644 --- a/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp +++ b/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp @@ -310,6 +310,9 @@ namespace ams::settings::fwdbg { /* Disable uploading error reports to Nintendo. */ R_ABORT_UNLESS(ParseSettingsItemValue("eupld", "upload_enabled", "u8!0x0")); + /* Enable USB 3.0 superspeed for homebrew */ + R_ABORT_UNLESS(ParseSettingsItemValue("usb", "usb30_force_enabled", spl::IsUsb30ForceEnabled() ? "u8!0x1" : "u8!0x0")); + /* Control whether RO should ease its validation of NROs. */ /* (note: this is normally not necessary, and ips patches can be used.) */ R_ABORT_UNLESS(ParseSettingsItemValue("ro", "ease_nro_restriction", "u8!0x1")); diff --git a/stratosphere/loader/source/ldr_embedded_usb_patches.inc b/stratosphere/loader/source/ldr_embedded_usb_patches.inc new file mode 100644 index 000000000..946d65990 --- /dev/null +++ b/stratosphere/loader/source/ldr_embedded_usb_patches.inc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018-2021 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 . + */ + +/* Patch fallback case to mov w0, #1 rather than retrieving settings flag. */ +constexpr inline const EmbeddedPatchEntry Usb30ForceEnablePatches_9_0_0[] = { + { 0x521C, "\x20\x00\x80\x52", 4 }, +}; + +/* Patch fallback case to mov w0, #1 rather than retrieving settings flag. */ +constexpr inline const EmbeddedPatchEntry Usb30ForceEnablePatches_10_0_0[] = { + { 0x5494, "\x20\x00\x80\x52", 4 }, +}; + +/* Patch getter functions to return 1. */ +constexpr inline const EmbeddedPatchEntry Usb30ForceEnablePatches_11_0_0[] = { + { 0x85DC, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 }, + { 0x866C, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 }, +}; + +constexpr inline const EmbeddedPatch Usb30ForceEnablePatches[] = { + { ParseModuleId("C0D3F4E87E8B0FE9BBE9F1968A20767F3DC08E03"), util::size(Usb30ForceEnablePatches_9_0_0), Usb30ForceEnablePatches_9_0_0 }, + { ParseModuleId("B9C700CA8335F8BAA0D2041D8D09F772890BA988"), util::size(Usb30ForceEnablePatches_10_0_0), Usb30ForceEnablePatches_10_0_0 }, + { ParseModuleId("95BAF06A69650C215A5DD50CF8BD2A603E7AD3C2"), util::size(Usb30ForceEnablePatches_11_0_0), Usb30ForceEnablePatches_11_0_0 }, +}; \ No newline at end of file diff --git a/stratosphere/loader/source/ldr_patcher.cpp b/stratosphere/loader/source/ldr_patcher.cpp index a9b14da9d..99385fc51 100644 --- a/stratosphere/loader/source/ldr_patcher.cpp +++ b/stratosphere/loader/source/ldr_patcher.cpp @@ -30,8 +30,12 @@ namespace ams::ldr { constexpr const char * const LoaderSdMountName = "#amsldr-sdpatch"; static_assert(sizeof(LoaderSdMountName) <= fs::MountNameLengthMax); - os::Mutex g_ldr_sd_lock(false); - bool g_mounted_sd; + constinit os::SdkMutex g_ldr_sd_lock; + constinit bool g_mounted_sd; + + constinit os::SdkMutex g_embedded_patch_lock; + constinit bool g_got_embedded_patch_settings; + constinit bool g_force_enable_usb30; bool EnsureSdCardMounted() { std::scoped_lock lk(g_ldr_sd_lock); @@ -51,6 +55,59 @@ namespace ams::ldr { return (g_mounted_sd = true); } + bool IsUsb30ForceEnabled() { + std::scoped_lock lk(g_embedded_patch_lock); + + if (!g_got_embedded_patch_settings) { + g_force_enable_usb30 = spl::IsUsb30ForceEnabled(); + g_got_embedded_patch_settings = true; + } + + return g_force_enable_usb30; + } + + consteval u8 ParseNybble(char c) { + AMS_ASSUME(('0' <= c && c <= '9') || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f')); + if ('0' <= c && c <= '9') { + return c - '0' + 0x0; + } else if ('A' <= c && c <= 'F') { + return c - 'A' + 0xA; + } else /* if ('a' <= c && c <= 'f') */ { + return c - 'a' + 0xa; + } + } + + consteval ro::ModuleId ParseModuleId(const char *str) { + /* Parse a static module id. */ + ro::ModuleId module_id = {}; + + size_t ofs = 0; + while (str[0] != 0) { + AMS_ASSUME(ofs < sizeof(module_id)); + AMS_ASSUME(str[1] != 0); + + module_id.build_id[ofs] = (ParseNybble(str[0]) << 4) | (ParseNybble(str[1]) << 0); + + str += 2; + ofs++; + } + + return module_id; + } + + struct EmbeddedPatchEntry { + uintptr_t offset; + const void * const data; + size_t size; + }; + + struct EmbeddedPatch { + ro::ModuleId module_id; + size_t num_entries; + const EmbeddedPatchEntry *entries; + }; + + #include "ldr_embedded_usb_patches.inc" } @@ -65,4 +122,24 @@ namespace ams::ldr { ams::patcher::LocateAndApplyIpsPatchesToModule(LoaderSdMountName, NsoPatchesDirectory, NsoPatchesProtectedSize, NsoPatchesProtectedOffset, &module_id, reinterpret_cast(mapped_nso), mapped_size); } + /* Apply embedded patches. */ + void ApplyEmbeddedPatchesToModule(const u8 *build_id, uintptr_t mapped_nso, size_t mapped_size) { + /* Make module id. */ + ro::ModuleId module_id; + std::memcpy(&module_id.build_id, build_id, sizeof(module_id.build_id)); + + if (IsUsb30ForceEnabled()) { + for (const auto &patch : Usb30ForceEnablePatches) { + if (std::memcmp(std::addressof(patch.module_id), std::addressof(module_id), sizeof(module_id)) == 0) { + for (size_t i = 0; i < patch.num_entries; ++i) { + const auto &entry = patch.entries[i]; + if (entry.offset + entry.size <= mapped_size) { + std::memcpy(reinterpret_cast(mapped_nso + entry.offset), entry.data, entry.size); + } + } + } + } + } + } + } \ No newline at end of file diff --git a/stratosphere/loader/source/ldr_patcher.hpp b/stratosphere/loader/source/ldr_patcher.hpp index 54d7d7142..1b73a1eea 100644 --- a/stratosphere/loader/source/ldr_patcher.hpp +++ b/stratosphere/loader/source/ldr_patcher.hpp @@ -21,4 +21,7 @@ namespace ams::ldr { /* Apply IPS patches. */ void LocateAndApplyIpsPatchesToModule(const u8 *build_id, uintptr_t mapped_nso, size_t mapped_size); + /* Apply embedded patches. */ + void ApplyEmbeddedPatchesToModule(const u8 *build_id, uintptr_t mapped_nso, size_t mapped_size); + } \ No newline at end of file diff --git a/stratosphere/loader/source/ldr_process_creation.cpp b/stratosphere/loader/source/ldr_process_creation.cpp index a6a7e2b05..821c5390e 100644 --- a/stratosphere/loader/source/ldr_process_creation.cpp +++ b/stratosphere/loader/source/ldr_process_creation.cpp @@ -534,6 +534,9 @@ namespace ams::ldr { std::memset(reinterpret_cast(map_address + ro_end), 0, nso_header->rw_dst_offset - ro_end); std::memset(reinterpret_cast(map_address + rw_end), 0, nso_header->bss_size); + /* Apply embedded patches. */ + ApplyEmbeddedPatchesToModule(nso_header->build_id, map_address, nso_size); + /* Apply IPS patches. */ LocateAndApplyIpsPatchesToModule(nso_header->build_id, map_address, nso_size); }