ams: allow bootloader to merely approximate correct target firmware

This commit is contained in:
Michael Scire 2021-10-11 00:54:17 -07:00
parent d41de21753
commit 5708bb1557
24 changed files with 281 additions and 396 deletions

View file

@ -164,6 +164,7 @@ namespace ams::secmon::smc {
}
constinit u64 g_payload_address = 0;
constinit bool g_set_true_target_firmware = false;
SmcResult GetConfig(SmcArguments &args, bool kern) {
switch (static_cast<ConfigItem>(args.r[1])) {
@ -240,11 +241,15 @@ namespace ams::secmon::smc {
break;
case ConfigItem::ExosphereApiVersion:
/* Get information about the current exosphere version. */
args.r[1] = (static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MAJOR & 0xFF) << 56) |
(static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MINOR & 0xFF) << 48) |
(static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MICRO & 0xFF) << 40) |
(static_cast<u64>(GetKeyGeneration()) << 32) |
(static_cast<u64>(GetTargetFirmware()) << 0);
if (kern || g_set_true_target_firmware) {
args.r[1] = (static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MAJOR & 0xFF) << 56) |
(static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MINOR & 0xFF) << 48) |
(static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MICRO & 0xFF) << 40) |
(static_cast<u64>(GetKeyGeneration()) << 32) |
(static_cast<u64>(GetTargetFirmware()) << 0);
} else {
return SmcResult::NotInitialized;
}
break;
case ConfigItem::ExosphereNeedsReboot:
/* We are executing, so we aren't in the process of rebooting. */
@ -297,6 +302,18 @@ namespace ams::secmon::smc {
(static_cast<u64>(ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR & 0xFF) << 16) |
(static_cast<u64>(ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO & 0xFF) << 8);
break;
case ConfigItem::ExosphereApproximateApiVersion:
/* Get information about the current exosphere version. */
if (!g_set_true_target_firmware) {
args.r[1] = (static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MAJOR & 0xFF) << 56) |
(static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MINOR & 0xFF) << 48) |
(static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MICRO & 0xFF) << 40) |
(static_cast<u64>(GetKeyGeneration()) << 32) |
(static_cast<u64>(GetTargetFirmware()) << 0);
} else {
return SmcResult::Busy;
}
break;
default:
return SmcResult::InvalidArgument;
}
@ -312,6 +329,14 @@ namespace ams::secmon::smc {
/* Configure the HiZ mode. */
SetChargerHiZModeEnabled(static_cast<bool>(args.r[3]));
break;
case ConfigItem::ExosphereApiVersion:
if (!g_set_true_target_firmware) {
::ams::secmon::impl::SetTargetFirmware(static_cast<ams::TargetFirmware>(args.r[3] & 0xFFFFFFFF));
g_set_true_target_firmware = true;
} else {
return SmcResult::Busy;
}
break;
case ConfigItem::ExosphereNeedsReboot:
if (soc_type == fuse::SocType_Erista) {
switch (static_cast<UserRebootType>(args.r[3])) {

View file

@ -40,18 +40,19 @@ namespace ams::secmon::smc {
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,
ExosphereLogConfiguration = 65009,
ExosphereForceEnableUsb30 = 65010,
ExosphereSupportedHosVersion = 65011,
ExosphereApiVersion = 65000,
ExosphereNeedsReboot = 65001,
ExosphereNeedsShutdown = 65002,
ExosphereGitCommitHash = 65003,
ExosphereHasRcmBugPatch = 65004,
ExosphereBlankProdInfo = 65005,
ExosphereAllowCalWrites = 65006,
ExosphereEmummcType = 65007,
ExospherePayloadAddress = 65008,
ExosphereLogConfiguration = 65009,
ExosphereForceEnableUsb30 = 65010,
ExosphereSupportedHosVersion = 65011,
ExosphereApproximateApiVersion = 65012,
};
SmcResult SmcGetConfigUser(SmcArguments &args);

View file

@ -27,7 +27,7 @@ bool diskio_write_sd_card(size_t sector_index, size_t sector_count, const void *
}
bool diskio_read_system(void *dst, size_t size, size_t sector_index, size_t sector_count) {
return R_SUCCEEDED(::ams::nxboot::ReadSystem(sector_index * 0x200, dst, size));
return false;
}
bool diskio_write_system(size_t sector_index, size_t sector_count, const void *src, size_t size) {

View file

@ -27,10 +27,8 @@ namespace ams::fs {
constexpr size_t MaxDirectories = 2;
constinit bool g_is_sd_mounted = false;
constinit bool g_is_sys_mounted = false;
alignas(0x10) constinit FATFS g_sd_fs = {};
alignas(0x10) constinit FATFS g_sys_fs = {};
alignas(0x10) constinit FIL g_files[MaxFiles] = {};
alignas(0x10) constinit DIR g_dirs[MaxDirectories] = {};
@ -131,18 +129,6 @@ namespace ams::fs {
g_is_sd_mounted = false;
}
bool MountSystem() {
AMS_ASSERT(!g_is_sys_mounted);
g_is_sys_mounted = f_mount(std::addressof(g_sys_fs), "sys:", 1) == FR_OK;
return g_is_sys_mounted;
}
void UnmountSystem() {
AMS_ASSERT(g_is_sys_mounted);
f_unmount("sys:");
g_is_sys_mounted = false;
}
Result GetEntryType(DirectoryEntryType *out_entry_type, bool *out_archive, const char *path) {
/* Get the file info. */
FILINFO info;

View file

@ -98,9 +98,6 @@ namespace ams::fs {
bool MountSdCard();
void UnmountSdCard();
bool MountSystem();
void UnmountSystem();
Result GetEntryType(DirectoryEntryType *out_entry_type, bool *out_archive, const char *path);
Result CreateFile(const char *path, s64 size);

View file

@ -181,112 +181,6 @@ namespace ams::nxboot {
constinit fs::IStorage *g_boot0_storage = nullptr;
constinit fs::IStorage *g_user_storage = nullptr;
class SystemPartitionStorage : public fs::IStorage {
private:
static constexpr size_t CacheEntries = BITSIZEOF(u32);
static constexpr size_t SectorSize = 0x4000;
private:
fs::SubStorage m_storage;
u8 *m_sector_cache;
u32 *m_sector_ids;
u32 m_sector_flags;
u32 m_next_idx;
private:
Result LoadSector(u8 *sector, u32 sector_id) {
/* Read the sector data. */
R_TRY(m_storage.Read(static_cast<s64>(sector_id) * SectorSize, sector, SectorSize));
/* Decrypt the sector. */
se::DecryptAes128Xts(sector, SectorSize, pkg1::AesKeySlot_BootloaderSystem0, pkg1::AesKeySlot_BootloaderSystem1, sector, SectorSize, sector_id);
/* Mark the sector as freshly loaded. */
m_sector_flags &= ~(1u << (sector_id % CacheEntries));
return ResultSuccess();
}
Result GetSector(u8 **out_sector, u32 sector_id) {
/* Try to find in the cache. */
for (size_t i = 0; i < CacheEntries; ++i) {
if (m_sector_ids[i] == sector_id) {
m_sector_flags &= ~(1u << i);
*out_sector = m_sector_cache + SectorSize * i;
return ResultSuccess();
}
}
/* Find a sector to evict. */
while ((m_sector_flags & (1u << m_next_idx)) == 0) {
m_sector_flags |= (1u << m_next_idx);
m_next_idx = (m_next_idx + 1) % CacheEntries;
}
/* Get the chosen sector. */
*out_sector = m_sector_cache + SectorSize * m_next_idx;
m_next_idx = (m_next_idx + 1) % CacheEntries;
/* Load the sector. */
return this->LoadSector(*out_sector, sector_id);
}
public:
SystemPartitionStorage(s64 ofs, s64 size) : m_storage(*g_user_storage, ofs, size) {
/* Allocate sector cache. */
m_sector_cache = static_cast<u8 *>(AllocateAligned(CacheEntries * SectorSize, SectorSize));
/* Allocate sector ids. */
m_sector_ids = static_cast<u32 *>(AllocateAligned(CacheEntries * sizeof(u32), alignof(u32)));
for (size_t i = 0; i < CacheEntries; ++i) {
m_sector_ids[i] = std::numeric_limits<u32>::max();
}
/* All sectors are dirty. */
m_sector_flags = ~0u;
/* Next sector is 0. */
m_next_idx = 0;
}
virtual Result Read(s64 offset, void *buffer, size_t size) override {
u32 sector_id = offset / SectorSize;
s64 subofs = offset % SectorSize;
u8 *cur_dst = static_cast<u8 *>(buffer);
while (size > 0) {
/* Get the current sector. */
u8 *sector;
R_TRY(this->GetSector(std::addressof(sector), sector_id++));
/* Copy the data. */
const size_t cur_size = std::min<size_t>(SectorSize - subofs, size);
std::memcpy(cur_dst, sector + subofs, cur_size);
/* Advance. */
cur_dst += cur_size;
size -= cur_size;
subofs = 0;
}
return ResultSuccess();
}
virtual Result Flush() override {
return m_storage.Flush();
}
virtual Result GetSize(s64 *out) override {
return m_storage.GetSize(out);
}
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
return fs::ResultUnsupportedOperation();
}
virtual Result SetSize(s64 size) override {
return fs::ResultUnsupportedOperation();
}
};
constinit SystemPartitionStorage *g_system_storage = nullptr;
constinit fs::SubStorage *g_package2_storage = nullptr;
struct Guid {
@ -333,10 +227,6 @@ namespace ams::nxboot {
};
static_assert(sizeof(Gpt) == 16_KB + 0x200);
constexpr const u16 SystemPartitionName[] = {
'S', 'Y', 'S', 'T', 'E', 'M', 0
};
constexpr const u16 Package2PartitionName[] = {
'B', 'C', 'P', 'K', 'G', '2', '-', '1', '-', 'N', 'o', 'r', 'm', 'a', 'l', '-', 'M', 'a', 'i', 'n', 0
};
@ -447,27 +337,15 @@ namespace ams::nxboot {
const s64 offset = INT64_C(0x200) * gpt->entries[i].starting_lba;
const u64 size = UINT64_C(0x200) * (gpt->entries[i].ending_lba + 1 - gpt->entries[i].starting_lba);
if (std::memcmp(gpt->entries[i].partition_name, SystemPartitionName, sizeof(SystemPartitionName)) == 0) {
g_system_storage = AllocateObject<SystemPartitionStorage>(offset, size);
} else if (std::memcmp(gpt->entries[i].partition_name, Package2PartitionName, sizeof(Package2PartitionName)) == 0) {
if (std::memcmp(gpt->entries[i].partition_name, Package2PartitionName, sizeof(Package2PartitionName)) == 0) {
g_package2_storage = AllocateObject<fs::SubStorage>(*g_user_storage, offset, size);
}
}
/* Check that we created system storage. */
if (g_system_storage == nullptr) {
ShowFatalError("Failed to initialize SYSTEM\n");
}
/* Check that we created package2 storage. */
if (g_package2_storage == nullptr) {
ShowFatalError("Failed to initialize Package2\n");
}
/* Mount system. */
if (!fs::MountSystem()) {
ShowFatalError("Failed to mount SYSTEM\n");
}
}
Result ReadBoot0(s64 offset, void *dst, size_t size) {
@ -478,8 +356,4 @@ namespace ams::nxboot {
return g_package2_storage->Read(offset, dst, size);
}
Result ReadSystem(s64 offset, void *dst, size_t size) {
return g_system_storage->Read(offset, dst, size);
}
}
}

View file

@ -118,26 +118,12 @@ namespace ams::nxboot {
return R_SUCCEEDED(fs::GetEntryType(std::addressof(entry_type), std::addressof(archive), path)) && entry_type == fs::DirectoryEntryType_Directory;
}
[[maybe_unused]] bool IsFileExist(const char *path) {
bool IsFileExist(const char *path) {
fs::DirectoryEntryType entry_type;
bool archive;
return R_SUCCEEDED(fs::GetEntryType(std::addressof(entry_type), std::addressof(archive), path)) && entry_type == fs::DirectoryEntryType_File;
}
bool IsConcatenationFileExist(const char *path) {
fs::DirectoryEntryType entry_type;
bool archive;
return R_SUCCEEDED(fs::GetEntryType(std::addressof(entry_type), std::addressof(archive), path)) && ((entry_type == fs::DirectoryEntryType_File) || (entry_type == fs::DirectoryEntryType_Directory && archive));
}
constinit char g_nca_path[0x40] = "sys:/contents/registered/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.nca";
bool IsNcaExist(const char *nca_name) {
std::memcpy(g_nca_path + 0x19, nca_name, 0x20);
return IsConcatenationFileExist(g_nca_path);
}
bool ConfigureEmummc() {
/* Set magic. */
g_emummc_cfg.base_cfg.magic = secmon::EmummcBaseConfiguration::Magic;
@ -220,143 +206,56 @@ namespace ams::nxboot {
return package1;
}
ams::TargetFirmware GetTargetFirmware(const u8 *package1) {
/* Get first an approximation of the target firmware. */
ams::TargetFirmware target_firmware = ams::TargetFirmware_Current;
ams::TargetFirmware GetApproximateTargetFirmware(const u8 *package1) {
/* Get an approximation of the target firmware. */
switch (package1[0x1F]) {
case 0x01:
target_firmware = ams::TargetFirmware_1_0_0;
break;
return ams::TargetFirmware_1_0_0;
case 0x02:
target_firmware = ams::TargetFirmware_2_0_0;
break;
return ams::TargetFirmware_2_0_0;
case 0x04:
target_firmware = ams::TargetFirmware_3_0_0;
break;
return ams::TargetFirmware_3_0_0;
case 0x07:
target_firmware = ams::TargetFirmware_4_0_0;
break;
return ams::TargetFirmware_4_0_0;
case 0x0B:
target_firmware = ams::TargetFirmware_5_0_0;
break;
return ams::TargetFirmware_5_0_0;
case 0x0E:
if (std::memcmp(package1 + 0x10, "20180802", 8) == 0) {
target_firmware = ams::TargetFirmware_6_0_0;
return ams::TargetFirmware_6_0_0;
} else if (std::memcmp(package1 + 0x10, "20181107", 8) == 0) {
target_firmware = ams::TargetFirmware_6_2_0;
} else {
ShowFatalError("Unable to identify package1!\n");
return ams::TargetFirmware_6_2_0;
}
break;
case 0x0F:
target_firmware = ams::TargetFirmware_7_0_0;
break;
return ams::TargetFirmware_7_0_0;
case 0x10:
if (std::memcmp(package1 + 0x10, "20190314", 8) == 0) {
target_firmware = ams::TargetFirmware_8_0_0;
return ams::TargetFirmware_8_0_0;
} else if (std::memcmp(package1 + 0x10, "20190531", 8) == 0) {
target_firmware = ams::TargetFirmware_8_1_0;
return ams::TargetFirmware_8_1_0;
} else if (std::memcmp(package1 + 0x10, "20190809", 8) == 0) {
target_firmware = ams::TargetFirmware_9_0_0;
return ams::TargetFirmware_9_0_0;
} else if (std::memcmp(package1 + 0x10, "20191021", 8) == 0) {
target_firmware = ams::TargetFirmware_9_1_0;
return ams::TargetFirmware_9_1_0;
} else if (std::memcmp(package1 + 0x10, "20200303", 8) == 0) {
target_firmware = ams::TargetFirmware_10_0_0;
return ams::TargetFirmware_10_0_0;
} else if (std::memcmp(package1 + 0x10, "20201030", 8) == 0) {
target_firmware = ams::TargetFirmware_11_0_0;
return ams::TargetFirmware_11_0_0;
} else if (std::memcmp(package1 + 0x10, "20210129", 8) == 0) {
target_firmware = ams::TargetFirmware_12_0_0;
return ams::TargetFirmware_12_0_0;
} else if (std::memcmp(package1 + 0x10, "20210422", 8) == 0) {
target_firmware = ams::TargetFirmware_12_0_2;
return ams::TargetFirmware_12_0_2;
} else if (std::memcmp(package1 + 0x10, "20210607", 8) == 0) {
target_firmware = ams::TargetFirmware_12_1_0;
return ams::TargetFirmware_12_1_0;
} else if (std::memcmp(package1 + 0x10, "20210805", 8) == 0) {
target_firmware = ams::TargetFirmware_13_0_0;
} else {
ShowFatalError("Unable to identify package1!\n");
return ams::TargetFirmware_13_0_0;
}
break;
default:
ShowFatalError("Unable to identify package1!\n");
break;
}
#define CHECK_NCA(NCA_ID, VERSION) do { if (IsNcaExist(NCA_ID)) { return ams::TargetFirmware_##VERSION; } } while(0)
if (target_firmware >= ams::TargetFirmware_13_0_0) {
CHECK_NCA("bf2337ee88bd9f963a33b3ecbbc3732a", 13_0_0);
} else if (target_firmware >= ams::TargetFirmware_12_1_0) {
CHECK_NCA("9d9d83d68d9517f245f3e8cd7f93c416", 12_1_0);
} else if (target_firmware >= ams::TargetFirmware_12_0_2) {
CHECK_NCA("a1863a5c0e1cedd442f5e60b0422dc15", 12_0_3);
CHECK_NCA("63d928b5a3016fe8cc0e76d2f06f4e98", 12_0_2);
} else if (target_firmware >= ams::TargetFirmware_12_0_0) {
CHECK_NCA("e65114b456f9d0b566a80e53bade2d89", 12_0_1);
CHECK_NCA("bd4185843550fbba125b20787005d1d2", 12_0_0);
} else if (target_firmware >= ams::TargetFirmware_11_0_0) {
CHECK_NCA("56211c7a5ed20a5332f5cdda67121e37", 11_0_1);
CHECK_NCA("594c90bcdbcccad6b062eadba0cd0e7e", 11_0_0);
} else if (target_firmware >= ams::TargetFirmware_10_0_0) {
CHECK_NCA("26325de4db3909e0ef2379787c7e671d", 10_2_0);
CHECK_NCA("5077973537f6735b564dd7475b779f87", 10_1_1); /* Exclusive to China. */
CHECK_NCA("fd1faed0ca750700d254c0915b93d506", 10_1_0);
CHECK_NCA("34728c771299443420820d8ae490ea41", 10_0_4);
CHECK_NCA("5b1df84f88c3334335bbb45d8522cbb4", 10_0_3);
CHECK_NCA("e951bc9dedcd54f65ffd83d4d050f9e0", 10_0_2);
CHECK_NCA("36ab1acf0c10a2beb9f7d472685f9a89", 10_0_1);
CHECK_NCA("5625cdc21d5f1ca52f6c36ba261505b9", 10_0_0);
} else if (target_firmware >= ams::TargetFirmware_9_1_0) {
CHECK_NCA("09ef4d92bb47b33861e695ba524a2c17", 9_2_0);
CHECK_NCA("c5fbb49f2e3648c8cfca758020c53ecb", 9_1_0);
} else if (target_firmware >= ams::TargetFirmware_9_0_0) {
CHECK_NCA("fd1ffb82dc1da76346343de22edbc97c", 9_0_1);
CHECK_NCA("a6af05b33f8f903aab90c8b0fcbcc6a4", 9_0_0);
} else if (target_firmware >= ams::TargetFirmware_8_1_0) {
CHECK_NCA("724d9b432929ea43e787ad81bf09ae65", 8_1_1); /* 8.1.1-100 from Lite */
CHECK_NCA("e9bb0602e939270a9348bddd9b78827b", 8_1_1); /* 8.1.1-12 from chinese gamecard */
CHECK_NCA("7eedb7006ad855ec567114be601b2a9d", 8_1_0);
} else if (target_firmware >= ams::TargetFirmware_8_0_0) {
CHECK_NCA("6c5426d27c40288302ad616307867eba", 8_0_1);
CHECK_NCA("4fe7b4abcea4a0bcc50975c1a926efcb", 8_0_0);
} else if (target_firmware >= ams::TargetFirmware_7_0_0) {
CHECK_NCA("e6b22c40bb4fa66a151f1dc8db5a7b5c", 7_0_1);
CHECK_NCA("c613bd9660478de69bc8d0e2e7ea9949", 7_0_0);
} else if (target_firmware >= ams::TargetFirmware_6_2_0) {
CHECK_NCA("6dfaaf1a3cebda6307aa770d9303d9b6", 6_2_0);
} else if (target_firmware >= ams::TargetFirmware_6_0_0) {
CHECK_NCA("1d21680af5a034d626693674faf81b02", 6_1_0);
CHECK_NCA("663e74e45ffc86fbbaeb98045feea315", 6_0_1);
CHECK_NCA("258c1786b0f6844250f34d9c6f66095b", 6_0_0); /* Release 6.0.0-5.0 */
CHECK_NCA("286e30bafd7e4197df6551ad802dd815", 6_0_0); /* Pre-Release 6.0.0-4.0 */
} else if (target_firmware >= ams::TargetFirmware_5_0_0) {
CHECK_NCA("fce3b0ea366f9c95fe6498b69274b0e7", 5_1_0);
CHECK_NCA("c5758b0cb8c6512e8967e38842d35016", 5_0_2);
CHECK_NCA("53eb605d4620e8fd50064b24fd57783a", 5_0_1);
CHECK_NCA("09a2f9c16ce1c121ae6d231b35d17515", 5_0_0);
} else if (target_firmware >= ams::TargetFirmware_4_0_0) {
CHECK_NCA("77e1ae7661ad8a718b9b13b70304aeea", 4_1_0);
CHECK_NCA("d0e5d20e3260f3083bcc067483b71274", 4_0_1);
CHECK_NCA("483a24ee3fd7149f9112d1931166a678", 4_0_0);
} else if (target_firmware >= ams::TargetFirmware_3_0_0) {
CHECK_NCA("704129fc89e1fcb85c37b3112e51b0fc", 3_0_2);
CHECK_NCA("1fb00543307337d523ccefa9923e0c50", 3_0_1);
CHECK_NCA("6ebd3447473bade18badbeb5032af87d", 3_0_0);
} else if (target_firmware >= ams::TargetFirmware_2_0_0) {
CHECK_NCA("d1c991c53a8a9038f8c3157a553d876d", 2_3_0);
CHECK_NCA("7f90353dff2d7ce69e19e07ebc0d5489", 2_2_0);
CHECK_NCA("e9b3e75fce00e52fe646156634d229b4", 2_1_0);
CHECK_NCA("7a1f79f8184d4b9bae1755090278f52c", 2_0_0);
} else if (target_firmware >= ams::TargetFirmware_1_0_0) {
CHECK_NCA("a1b287e07f8455e8192f13d0e45a2aaf", 1_0_0); /* 1.0.0 from Factory */
CHECK_NCA("117f7b9c7da3e8cef02340596af206b3", 1_0_0); /* 1.0.0 from Gamecard */
} else {
ShowFatalError("Unable to determine target firmware!\n");
}
#undef CHECK_NCA
/* If we didn't find a more specific firmware, return our package1 approximation. */
return target_firmware;
ShowFatalError("Unable to identify package1!\n");
}
u8 *LoadBootConfigAndPackage2() {
@ -825,7 +724,7 @@ namespace ams::nxboot {
const u8 * const package1 = LoadPackage1(soc_type);
/* Get target firmware. */
const auto target_firmware = GetTargetFirmware(package1);
const auto target_firmware = GetApproximateTargetFirmware(package1);
/* Read/decrypt package2. */
u8 * const package2 = LoadBootConfigAndPackage2();

View file

@ -70,6 +70,10 @@ namespace ams::secmon {
GetConfigurationContext().secmon_cfg.key_generation = generation;
}
ALWAYS_INLINE void SetTargetFirmware(ams::TargetFirmware target_firmware) {
GetConfigurationContext().secmon_cfg.target_firmware = target_firmware;
}
ALWAYS_INLINE pkg1::BootConfig *GetBootConfigStorage() {
return std::addressof(GetConfigurationContext().boot_config);
}

View file

@ -33,6 +33,6 @@
AMS_SF_METHOD_INFO(C, H, 11, Result, ActivateContentMetaDatabase, (ncm::StorageId storage_id), (storage_id), hos::Version_2_0_0) \
AMS_SF_METHOD_INFO(C, H, 12, Result, InactivateContentMetaDatabase, (ncm::StorageId storage_id), (storage_id), hos::Version_2_0_0) \
AMS_SF_METHOD_INFO(C, H, 13, Result, InvalidateRightsIdCache, (), (), hos::Version_9_0_0) \
AMS_SF_METHOD_INFO(C, H, 14, Result, GetMemoryReport, (sf::Out<ncm::MemoryReport> out), (out), hos::Version_10_0_0)
AMS_SF_METHOD_INFO(C, H, 14, Result, GetMemoryReport, (sf::Out<ncm::MemoryReport> out), (out), hos::Version_10_0_0)
AMS_SF_DEFINE_INTERFACE(ams::ncm, IContentManager, AMS_NCM_I_CONTENT_MANAGER_INTERFACE_INFO);

View file

@ -39,6 +39,7 @@ namespace ams::spl {
Result DecryptAesKey(void *dst, size_t dst_size, const void *src, size_t src_size, s32 generation, u32 option);
Result GetConfig(u64 *out, ConfigItem item);
Result SetConfig(ConfigItem item, u64 v);
bool IsDevelopment();
MemoryArrangement GetMemoryArrangement();

View file

@ -231,18 +231,19 @@ 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,
ExosphereLogConfiguration = 65009,
ExosphereForceEnableUsb30 = 65010,
ExosphereSupportedHosVersion = 65011,
ExosphereApiVersion = 65000,
ExosphereNeedsReboot = 65001,
ExosphereNeedsShutdown = 65002,
ExosphereGitCommitHash = 65003,
ExosphereHasRcmBugPatch = 65004,
ExosphereBlankProdInfo = 65005,
ExosphereAllowCalWrites = 65006,
ExosphereEmummcType = 65007,
ExospherePayloadAddress = 65008,
ExosphereLogConfiguration = 65009,
ExosphereForceEnableUsb30 = 65010,
ExosphereSupportedHosVersion = 65011,
ExosphereApproximateApiVersion = 65012, /* NOTE: Internal use only. */
};
}

View file

@ -14,7 +14,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "hos_version_api_private.hpp"
#include "../os/impl/os_rng_manager.hpp"
namespace ams::os {
@ -34,23 +33,29 @@ extern "C" {
namespace ams::hos {
namespace {
bool CanAllowTemporaryApproximateVersion() {
/* Check if we're a program that can use a temporary approximate version. */
const auto program_id = os::GetCurrentProgramId();
return program_id == ncm::SystemProgramId::Pm || /* Needed so that boot has permissions to use fsp-srv. */
program_id == ncm::SystemProgramId::Sm || /* Needed so that boot can acquire fsp-srv. */
program_id == ncm::SystemProgramId::Boot || /* Needed so that boot can set the true target firmware. */
program_id == ncm::SystemProgramId::Spl || /* Needed so that FS can complete initialization. */
program_id == ncm::SystemProgramId::Ncm; /* Needed so that FS can determine where SystemVersion is located. */
}
}
void InitializeVersionInternal(bool allow_approximate);
void InitializeForStratosphere() {
/* Initialize the global os resource managers. This *must* be done before anything else in stratosphere. */
os::InitializeForStratosphereInternal();
/* Initialize hos::Version API. */
hos::SetVersionForLibnxInternal();
/* Check that we're running under mesosphere. */
AMS_ABORT_UNLESS(svc::IsKernelMesosphere());
}
void InitializeForStratosphereDebug(hos::Version debug_version) {
/* Initialize the global os resource managers. This *must* be done before anything else in stratosphere. */
os::InitializeForStratosphereInternal();
/* Initialize hos::Version API. */
hos::SetVersionForLibnxInternalDebug(debug_version);
hos::InitializeVersionInternal(CanAllowTemporaryApproximateVersion());
/* Check that we're running under mesosphere. */
AMS_ABORT_UNLESS(svc::IsKernelMesosphere());

View file

@ -14,69 +14,139 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "hos_version_api_private.hpp"
namespace ams::hos {
namespace {
hos::Version g_hos_version;
bool g_has_cached;
os::SdkMutex g_mutex;
constinit os::SdkMutex g_hos_init_lock;
constinit hos::Version g_hos_version;
constinit bool g_set_hos_version;
void CacheValues() {
if (__atomic_load_n(&g_has_cached, __ATOMIC_SEQ_CST)) {
return;
Result GetExosphereApiInfo(exosphere::ApiInfo *out) {
u64 exosphere_cfg;
R_TRY_CATCH(spl::impl::GetConfig(std::addressof(exosphere_cfg), spl::ConfigItem::ExosphereApiVersion)) {
R_CATCH_RETHROW(spl::ResultSecureMonitorNotInitialized)
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
*out = { exosphere_cfg };
return ResultSuccess();
}
Result GetApproximateExosphereApiInfo(exosphere::ApiInfo *out) {
u64 exosphere_cfg;
R_TRY_CATCH(spl::impl::GetConfig(std::addressof(exosphere_cfg), spl::ConfigItem::ExosphereApproximateApiVersion)) {
R_CATCH_RETHROW(spl::ResultSecureMonitorBusy)
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
*out = { exosphere_cfg };
return ResultSuccess();
}
exosphere::ApiInfo GetExosphereApiInfo(bool allow_approximate) {
exosphere::ApiInfo info;
while (true) {
if (R_SUCCEEDED(GetExosphereApiInfo(std::addressof(info)))) {
return info;
}
if (allow_approximate && R_SUCCEEDED(GetApproximateExosphereApiInfo(std::addressof(info)))) {
return info;
}
svc::SleepThread(TimeSpan::FromMilliSeconds(25).GetNanoSeconds());
}
}
std::scoped_lock lk(g_mutex);
settings::FirmwareVersion GetSettingsFirmwareVersion() {
/* Mount the system version title. */
R_ABORT_UNLESS(ams::fs::MountSystemData("sysver", ncm::SystemDataId::SystemVersion));
ON_SCOPE_EXIT { ams::fs::Unmount("sysver"); };
if (g_has_cached) {
return;
}
/* Read the firmware version file. */
ams::fs::FileHandle file;
R_ABORT_UNLESS(ams::fs::OpenFile(std::addressof(file), "sysver:/file", fs::OpenMode_Read));
ON_SCOPE_EXIT { ams::fs::CloseFile(file); };
/* Hos version is a direct copy of target firmware, just renamed. */
g_hos_version = static_cast<hos::Version>(exosphere::GetApiInfo().GetTargetFirmware());
/* Must be possible to read firmware version from file. */
settings::FirmwareVersion firmware_version;
R_ABORT_UNLESS(ams::fs::ReadFile(file, 0, std::addressof(firmware_version), sizeof(firmware_version)));
/* Ensure that this is a hos version we can sanely *try* to run. */
/* To be friendly, we will only require that we recognize the major and minor versions. */
/* We can consider only recognizing major in the future, but micro seems safe to ignore as */
/* there are no breaking IPC changes in minor updates. */
{
constexpr u32 MaxMajor = (static_cast<u32>(hos::Version_Max) >> 24) & 0xFF;
constexpr u32 MaxMinor = (static_cast<u32>(hos::Version_Max) >> 16) & 0xFF;
const u32 major = (static_cast<u32>(g_hos_version) >> 24) & 0xFF;
const u32 minor = (static_cast<u32>(g_hos_version) >> 16) & 0xFF;
const bool is_safely_tryable_version = (g_hos_version <= hos::Version_Max) || (major == MaxMajor && minor <= MaxMinor);
AMS_ABORT_UNLESS(is_safely_tryable_version);
}
__atomic_store_n(&g_has_cached, true, __ATOMIC_SEQ_CST);
return firmware_version;
}
}
void InitializeVersionInternal(bool allow_approximate) {
/* Get the current (and previous approximation of) target firmware. */
hos::Version prev, current;
bool has_prev = false;
{
/* Acquire exclusive access to set hos version. */
std::scoped_lock lk(g_hos_init_lock);
/* Save the previous value of g_hos_version. */
prev = g_hos_version;
has_prev = g_set_hos_version;
/* Set hos version = exosphere api version target firmware. */
g_hos_version = static_cast<hos::Version>(GetExosphereApiInfo(allow_approximate).GetTargetFirmware());
/* Save the current value of g_hos_version. */
current = g_hos_version;
/* Note that we've set a previous hos version. */
g_set_hos_version = true;
}
/* Ensure that this is a hos version we can sanely *try* to run. */
/* To be friendly, we will only require that we recognize the major and minor versions. */
/* We can consider only recognizing major in the future, but micro seems safe to ignore as */
/* there are no breaking IPC changes in minor updates. */
{
constexpr u32 MaxMajor = (static_cast<u32>(hos::Version_Max) >> 24) & 0xFF;
constexpr u32 MaxMinor = (static_cast<u32>(hos::Version_Max) >> 16) & 0xFF;
const u32 major = (static_cast<u32>(current) >> 24) & 0xFF;
const u32 minor = (static_cast<u32>(current) >> 16) & 0xFF;
const bool is_safely_tryable_version = (current <= hos::Version_Max) || (major == MaxMajor && minor <= MaxMinor);
AMS_ABORT_UNLESS(is_safely_tryable_version);
}
/* Ensure that this is a hos version compatible with previous approximations. */
if (has_prev) {
AMS_ABORT_UNLESS(current >= prev);
const u32 current_major = (static_cast<u32>(current) >> 24) & 0xFF;
const u32 prev_major = (static_cast<u32>(prev) >> 24) & 0xFF;
AMS_ABORT_UNLESS(current_major == prev_major);
}
/* Set the version for libnx. */
{
const u32 major = (static_cast<u32>(current) >> 24) & 0xFF;
const u32 minor = (static_cast<u32>(current) >> 16) & 0xFF;
const u32 micro = (static_cast<u32>(current) >> 8) & 0xFF;
hosversionSet((BIT(31)) | (MAKEHOSVERSION(major, minor, micro)));
}
}
void SetNonApproximateVersionInternal() {
/* Get the settings . */
const auto firmware_version = GetSettingsFirmwareVersion();
/* Set the exosphere api version. */
R_ABORT_UNLESS(spl::SetConfig(spl::ConfigItem::ExosphereApiVersion, (static_cast<u32>(firmware_version.major) << 24) | (static_cast<u32>(firmware_version.minor) << 16) | (static_cast<u32>(firmware_version.micro) << 8)));
/* Update our own version value. */
InitializeVersionInternal(false);
}
::ams::hos::Version GetVersion() {
CacheValues();
return g_hos_version;
}
void SetVersionForLibnxInternalDebug(hos::Version debug_version) {
std::scoped_lock lk(g_mutex);
g_hos_version = debug_version;
__atomic_store_n(&g_has_cached, true, __ATOMIC_SEQ_CST);
SetVersionForLibnxInternal();
}
void SetVersionForLibnxInternal() {
const u32 hos_version_val = static_cast<u32>(hos::GetVersion());
const u32 major = (hos_version_val >> 24) & 0xFF;
const u32 minor = (hos_version_val >> 16) & 0xFF;
const u32 micro = (hos_version_val >> 8) & 0xFF;
hosversionSet((BIT(31)) | (MAKEHOSVERSION(major, minor, micro)));
}
}

View file

@ -1,24 +0,0 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::hos {
void SetVersionForLibnxInternal();
void SetVersionForLibnxInternalDebug(hos::Version debug_version);
}

View file

@ -35,6 +35,7 @@ namespace ams::powctl::impl::board::nintendo::nx {
if (AMS_LIKELY(!g_constructed_max17050_driver)) {
util::ConstructAt(g_max17050_driver);
g_constructed_max17050_driver = true;
}
}

View file

@ -35,6 +35,7 @@ namespace ams::powctl::impl::board::nintendo::nx {
if (AMS_LIKELY(!g_constructed_bq24193_driver)) {
util::ConstructAt(g_bq24193_driver);
g_constructed_bq24193_driver = true;
}
}

View file

@ -18,6 +18,12 @@
#include "spl_device_address_mapper.hpp"
#include "spl_key_slot_cache.hpp"
namespace ams::hos {
void InitializeVersionInternal(bool allow_approximate);
}
namespace ams::spl::impl {
namespace {
@ -536,7 +542,13 @@ namespace ams::spl::impl {
}
Result SetConfig(ConfigItem key, u64 value) {
return smc::ConvertResult(smc::SetConfig(key, value));
R_TRY(smc::ConvertResult(smc::SetConfig(key, value)));
/* Work around for temporary version. */
if (key == ConfigItem::ExosphereApiVersion) {
hos::InitializeVersionInternal(false);
}
return ResultSuccess();
}
Result GenerateRandomBytes(void *out, size_t size) {

View file

@ -216,6 +216,10 @@ namespace ams::spl {
return splGetConfig(static_cast<::SplConfigItem>(item), out);
}
Result SetConfig(ConfigItem item, u64 v) {
return splSetConfig(static_cast<::SplConfigItem>(item), v);
}
bool IsDevelopment() {
bool is_dev;
R_ABORT_UNLESS(splIsDevelopment(std::addressof(is_dev)));

View file

@ -123,7 +123,8 @@ namespace ams::mitm::bpc {
std::memcpy(g_reboot_payload, payload, payload_size);
/* Note to the secure monitor that we have a payload. */
spl::smc::SetConfig(spl::ConfigItem::ExospherePayloadAddress, g_reboot_payload, nullptr, 0);
spl::smc::AsyncOperationKey dummy;
spl::smc::SetConfig(std::addressof(dummy), spl::ConfigItem::ExospherePayloadAddress, nullptr, 0, g_reboot_payload);
/* NOTE: Preferred reboot type may be overrwritten when parsed from settings during boot. */
g_reboot_type = RebootType::ToPayload;

View file

@ -68,21 +68,31 @@ namespace ams {
}
namespace hos {
void SetNonApproximateVersionInternal();
}
namespace init {
void InitializeSystemModule() {
/* Initialize heaps. */
boot::InitializeHeaps();
/* Set fs allocator. */
fs::SetAllocator(boot::Allocate, boot::Deallocate);
/* Initialize services we need. */
/* Connect to sm. */
R_ABORT_UNLESS(sm::Initialize());
/* Initialize fs. */
fs::InitializeForSystem();
fs::SetAllocator(boot::Allocate, boot::Deallocate);
fs::SetEnabledAutoAbort(false);
/* Initialize spl. */
spl::Initialize();
R_ABORT_UNLESS(pmshellInitialize());
/* Set the true hos version. */
hos::SetNonApproximateVersionInternal();
/* Verify that we can sanely execute. */
ams::CheckApiVersion();
@ -174,6 +184,7 @@ namespace ams {
boot::FinalizeGpioDriverLibrary();
/* Tell PM to start boot2. */
R_ABORT_UNLESS(pmshellInitialize());
R_ABORT_UNLESS(pmshellNotifyBootFinished());
}

View file

@ -195,9 +195,6 @@ namespace ams {
/* Initialize our connection to sm. */
R_ABORT_UNLESS(sm::Initialize());
/* Verify that we can sanely execute. */
ams::CheckApiVersion();
}
void FinalizeSystemModule() { /* ... */ }

View file

@ -165,6 +165,12 @@ namespace ams {
}
namespace hos {
void InitializeVersionInternal(bool allow_approximate);
}
namespace init {
void InitializeSystemModule() {
@ -182,6 +188,12 @@ namespace ams {
/* Use our manager extension to tell SM that the FS bug has been worked around. */
R_ABORT_UNLESS(sm::manager::EndInitialDefers());
/* Wait for the true hos version to be available. */
hos::InitializeVersionInternal(false);
/* Now that the true hos version is available, we should once more end defers (alerting sm to the available hos version). */
R_ABORT_UNLESS(sm::manager::EndInitialDefers());
/* Initialize remaining services we need. */
R_ABORT_UNLESS(ldrPmInitialize());
spl::Initialize();

View file

@ -17,6 +17,12 @@
#include "sm_service_manager.hpp"
#include "../sm_wait_list.hpp"
namespace ams::hos {
void InitializeVersionInternal(bool allow_approximate);
}
namespace ams::sm::impl {
namespace {
@ -862,8 +868,12 @@ namespace ams::sm::impl {
std::scoped_lock lk(g_mutex);
/* Note that we have ended the initial deferral period. */
const bool had_ended_defers = g_ended_initial_defers;
g_ended_initial_defers = true;
/* Something about deferral state has changed, so we should refresh our hos version. */
hos::InitializeVersionInternal(!had_ended_defers);
/* This might undefer some requests. */
for (const auto &service_name : InitiallyDeferredServices) {
TriggerResume(service_name);

View file

@ -162,9 +162,6 @@ namespace ams {
void InitializeSystemModule() {
/* Initialize our connection to sm. */
R_ABORT_UNLESS(sm::Initialize());
/* Verify that we can sanely execute. */
ams::CheckApiVersion();
}
void FinalizeSystemModule() { /* ... */ }