From 5a5d9b206bb4d692bc35a34b519c9b4bbb544f52 Mon Sep 17 00:00:00 2001 From: CTCaer Date: Thu, 19 Mar 2026 16:32:12 +0200 Subject: [PATCH 1/2] exosphere: automatically adjust dram id if needed Checks if programmed memory size matches the one from fused dram id. If not, adjust it properly so PCV can do proper training and not crash. --- .../program/source/smc/secmon_smc_info.cpp | 38 +++++++++++++++++-- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/exosphere/program/source/smc/secmon_smc_info.cpp b/exosphere/program/source/smc/secmon_smc_info.cpp index 11f4a693a..01fa3b93c 100644 --- a/exosphere/program/source/smc/secmon_smc_info.cpp +++ b/exosphere/program/source/smc/secmon_smc_info.cpp @@ -169,6 +169,27 @@ namespace ams::secmon::smc { return value.value; } + fuse::DramId GetDramIdAdjusted() { + const auto dram_id = fuse::GetDramId(); + AMS_ABORT_UNLESS(dram_id < fuse::DramId_Count); + + const auto fuse_mem_size = DramIdToMemorySize[dram_id]; + const auto phys_mem_size = GetPhysicalMemorySize(); + + AMS_ABORT_UNLESS(fuse_mem_size <= phys_mem_size); + + if (fuse_mem_size == phys_mem_size) { + return dram_id; + } + + /* Adjust Dram ID to match density/ranks. */ + if (GetSocType() == fuse::SocType_Erista) { + return fuse::DramId_IcosaSamsung6GB; + } else { /* fuse::SocType_Mariko */ + return fuse::DramId_IowaSamsung1y8GBX; + } + } + constinit u64 g_payload_address = 0; constinit bool g_set_true_target_firmware = false; @@ -178,7 +199,7 @@ namespace ams::secmon::smc { args.r[1] = GetBootConfig().signed_data.IsProgramVerificationDisabled(); break; case ConfigItem::DramId: - args.r[1] = fuse::GetDramId(); + args.r[1] = GetDramIdAdjusted(); /* Nintendo: fuse::GetDramId() */ break; case ConfigItem::SecurityEngineInterruptNumber: args.r[1] = SecurityEngineUserInterruptId; @@ -471,9 +492,18 @@ namespace ams::secmon::smc { /* For exosphere's usage. */ pkg1::MemorySize GetPhysicalMemorySize() { - const auto dram_id = fuse::GetDramId(); - AMS_ABORT_UNLESS(dram_id < fuse::DramId_Count); - return DramIdToMemorySize[dram_id]; + const uintptr_t MC = secmon::MemoryRegionVirtualDeviceMemoryController.GetAddress(); + const u32 mem_size = reg::Read(MC + MC_EMEM_CFG) & 0x3FFF; + + switch (mem_size >> 10) { + case 4: + default: + return pkg1::MemorySize_4GB; + case 6: + return pkg1::MemorySize_6GB; + case 8: + return pkg1::MemorySize_8GB; + } } } From 5dd9816e3cf8a4d5793968759537d5b6d7372668 Mon Sep 17 00:00:00 2001 From: CTCaer Date: Thu, 19 Mar 2026 16:35:36 +0200 Subject: [PATCH 2/2] exosphere: allow memory mode to be used on retail --- config_templates/exosphere.ini | 8 ++++++++ .../program/source/smc/secmon_smc_info.cpp | 19 ++++++++++++------- fusee/program/source/fusee_setup_horizon.cpp | 6 ++++++ .../secmon/secmon_monitor_context.hpp | 2 ++ 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/config_templates/exosphere.ini b/config_templates/exosphere.ini index 8df772ccf..f815b8cc7 100644 --- a/config_templates/exosphere.ini +++ b/config_templates/exosphere.ini @@ -15,6 +15,13 @@ # Desc: Controls whether userland has access to the PMU registers. # NOTE: It is unknown what effects this has on official code. +# Key: enable_mem_mode, default: 0. +# Desc: Controls whether boot config memory mode is taken into account +# for retail units. This does not affect development units. +# NOTE: On retail units max ram size is capped to 4GB. +# Enabling this will use the boot config memory mode parameter, +# which by default is auto and size gets set based on physical size. + # Key: blank_prodinfo_sysmmc, default: 0. # Desc: Controls whether PRODINFO should be blanked in sysmmc. # This will cause the system to see dummied out keys and @@ -51,6 +58,7 @@ debugmode=1 debugmode_user=0 disable_user_exception_handlers=0 enable_user_pmu_access=0 +enable_mem_mode=0 blank_prodinfo_sysmmc=0 blank_prodinfo_emummc=0 allow_writing_to_cal_sysmmc=0 diff --git a/exosphere/program/source/smc/secmon_smc_info.cpp b/exosphere/program/source/smc/secmon_smc_info.cpp index 01fa3b93c..88cdeefe0 100644 --- a/exosphere/program/source/smc/secmon_smc_info.cpp +++ b/exosphere/program/source/smc/secmon_smc_info.cpp @@ -132,10 +132,13 @@ namespace ams::secmon::smc { } u32 GetMemoryMode() { - /* Unless development function is enabled, we're 4 GB. */ + /* Unless development function or forced boot config memory size is enabled, we're 4 GB. */ u32 memory_mode = pkg1::MemoryMode_4GB; - if (const auto &bcd = GetBootConfig().data; bcd.IsDevelopmentFunctionEnabled()) { + const auto &bcd = GetBootConfig().data; + const auto &sc = GetSecmonConfiguration(); /* Exosphere extensions */ + + if (bcd.IsDevelopmentFunctionEnabled() || sc.IsBootConfigMemoryModeEnabled()) { memory_mode = GetMemoryMode(bcd.GetMemoryMode()); } @@ -146,17 +149,19 @@ namespace ams::secmon::smc { pkg1::MemorySize memory_size = pkg1::MemorySize_4GB; util::BitPack32 value = {}; - if (const auto &bcd = GetBootConfig().data; bcd.IsDevelopmentFunctionEnabled()) { - memory_size = GetMemorySize(GetMemoryMode(bcd.GetMemoryMode())); + const auto &bcd = GetBootConfig().data; + const auto &sc = GetSecmonConfiguration(); /* Exosphere extensions */ + if (bcd.IsDevelopmentFunctionEnabled()) { value.Set(bcd.GetKernelFlags1()); value.Set(bcd.GetKernelFlags0()); } - value.Set(memory_size); + if (bcd.IsDevelopmentFunctionEnabled() || sc.IsBootConfigMemoryModeEnabled()) { + memory_size = GetMemorySize(GetMemoryMode(bcd.GetMemoryMode())); + } - /* Exosphere extensions. */ - const auto &sc = GetSecmonConfiguration(); + value.Set(memory_size); if (!sc.DisableUserModeExceptionHandlers()) { value.Set(true); diff --git a/fusee/program/source/fusee_setup_horizon.cpp b/fusee/program/source/fusee_setup_horizon.cpp index 7ec68d4a3..cd0e31802 100644 --- a/fusee/program/source/fusee_setup_horizon.cpp +++ b/fusee/program/source/fusee_setup_horizon.cpp @@ -543,6 +543,12 @@ namespace ams::nxboot { } else { storage_ctx.flags[0] &= ~secmon::SecureMonitorConfigurationFlag_EnableUserModePerformanceCounterAccess; } + } else if (std::strcmp(entry.key, "enable_mem_mode") == 0) { + if (entry.value[0] == '1') { + storage_ctx.flags[0] |= secmon::SecureMonitorConfigurationFlag_BootConfigMemoryModeEnabled; + } else { + storage_ctx.flags[0] &= ~secmon::SecureMonitorConfigurationFlag_BootConfigMemoryModeEnabled; + } } else if (std::strcmp(entry.key, "blank_prodinfo_sysmmc") == 0) { if (!emummc_enabled) { if (entry.value[0] == '1') { diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp index c37b76c82..28517ea74 100644 --- a/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp @@ -30,6 +30,7 @@ namespace ams::secmon { SecureMonitorConfigurationFlag_ShouldUseBlankCalibrationBinary = (1u << 5), SecureMonitorConfigurationFlag_AllowWritingToCalibrationBinarySysmmc = (1u << 6), SecureMonitorConfigurationFlag_ForceEnableUsb30 = (1u << 7), + SecureMonitorConfigurationFlag_BootConfigMemoryModeEnabled = (1u << 8), SecureMonitorConfigurationFlag_Default = SecureMonitorConfigurationFlag_IsDevelopmentFunctionEnabledForKernel, }; @@ -103,6 +104,7 @@ namespace ams::secmon { 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 IsBootConfigMemoryModeEnabled() const { return (this->flags[0] & SecureMonitorConfigurationFlag_BootConfigMemoryModeEnabled) != 0; } constexpr bool IsDevelopmentFunctionEnabled(bool for_kern) const { return for_kern ? this->IsDevelopmentFunctionEnabledForKernel() : this->IsDevelopmentFunctionEnabledForUser(); } };