diff --git a/stratosphere/boot/source/boot_repair_boot_images.cpp b/stratosphere/boot/source/boot_repair_boot_images.cpp index 8b020cc75..e2f1bf444 100644 --- a/stratosphere/boot/source/boot_repair_boot_images.cpp +++ b/stratosphere/boot/source/boot_repair_boot_images.cpp @@ -20,10 +20,10 @@ static u8 __attribute__((__aligned__(0x1000))) g_boot_image_work_buffer[0x10000]; void Boot::CheckAndRepairBootImages() { - const BootImageUpdateType boot_image_update_type = Updater::GetBootImageUpdateType(Boot::GetHardwareType()); + const auto boot_image_update_type = sts::updater::GetBootImageUpdateType(Boot::GetHardwareType()); bool repaired_normal, repaired_safe; - if (R_SUCCEEDED(Updater::VerifyBootImagesAndRepairIfNeeded(&repaired_normal, &repaired_safe, g_boot_image_work_buffer, sizeof(g_boot_image_work_buffer), boot_image_update_type)) && repaired_normal) { + if (R_SUCCEEDED(sts::updater::VerifyBootImagesAndRepairIfNeeded(&repaired_normal, &repaired_safe, g_boot_image_work_buffer, sizeof(g_boot_image_work_buffer), boot_image_update_type)) && repaired_normal) { /* Nintendo only performs a reboot on successful normal repair. */ Boot::RebootSystem(); } diff --git a/stratosphere/boot/source/updater/updater_api.cpp b/stratosphere/boot/source/updater/updater_api.cpp index eca58a62b..2d9eda266 100644 --- a/stratosphere/boot/source/updater/updater_api.cpp +++ b/stratosphere/boot/source/updater/updater_api.cpp @@ -19,508 +19,544 @@ #include "updater_api.hpp" #include "updater_bis_save.hpp" +#include "updater_files.hpp" +#include "updater_paths.hpp" -Result Updater::ValidateWorkBuffer(const void *work_buffer, size_t work_buffer_size) { - if (work_buffer_size < BctSize + EksSize) { - return ResultUpdaterTooSmallWorkBuffer; - } - if (reinterpret_cast(work_buffer) & 0xFFF) { - return ResultUpdaterMisalignedWorkBuffer; - } - if (reinterpret_cast(work_buffer_size) & 0x1FF) { - return ResultUpdaterMisalignedWorkBuffer; - } - return ResultSuccess; -} +namespace sts::updater { -BootImageUpdateType Updater::GetBootImageUpdateType(HardwareType hw_type) { - switch (hw_type) { - case HardwareType_Icosa: - case HardwareType_Copper: - return BootImageUpdateType_Erista; - case HardwareType_Hoag: - case HardwareType_Iowa: - return BootImageUpdateType_Mariko; - default: - std::abort(); - } -} + namespace { -bool Updater::HasEks(BootImageUpdateType boot_image_update_type) { - switch (boot_image_update_type) { - case BootImageUpdateType_Erista: - return true; - case BootImageUpdateType_Mariko: - return false; - default: - std::abort(); - } -} + /* Validation Prototypes. */ + Result ValidateWorkBuffer(const void *work_buffer, size_t work_buffer_size); -bool Updater::HasAutoRcmPreserve(BootImageUpdateType boot_image_update_type) { - switch (boot_image_update_type) { - case BootImageUpdateType_Erista: - return true; - case BootImageUpdateType_Mariko: - return false; - default: - std::abort(); - } -} + /* Configuration Prototypes. */ + bool HasEks(BootImageUpdateType boot_image_update_type); + bool HasAutoRcmPreserve(BootImageUpdateType boot_image_update_type); + u32 GetNcmTitleType(BootModeType mode); + Result GetBootImagePackageDataId(u64 *out_data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size); -u32 Updater::GetNcmTitleType(BootModeType mode) { - switch (mode) { - case BootModeType_Normal: - return NcmContentMetaType_BootImagePackage; - case BootModeType_Safe: - return NcmContentMetaType_BootImagePackageSafe; - default: - std::abort(); - } -} + /* Verification Prototypes. */ + Result GetVerificationState(VerificationState *out, void *work_buffer, size_t work_buffer_size); + Result VerifyBootImages(u64 data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type); + Result VerifyBootImagesNormal(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type); + Result VerifyBootImagesSafe(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type); -Result Updater::GetVerificationState(VerificationState *out, void *work_buffer, size_t work_buffer_size) { - /* Always set output to true before doing anything else. */ - out->needs_verify_normal = true; - out->needs_verify_safe = true; + /* Update Prototypes. */ + Result SetVerificationNeeded(BootModeType mode, bool needed, void *work_buffer, size_t work_buffer_size); + Result UpdateBootImages(u64 data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type); + Result UpdateBootImagesNormal(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type); + Result UpdateBootImagesSafe(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type); - /* Ensure work buffer is big enough for us to do what we want to do. */ - R_TRY(ValidateWorkBuffer(work_buffer, work_buffer_size)); + /* Package helpers. */ + Result ValidateBctFileHash(Boot0Accessor &accessor, Boot0Partition which, const void *stored_hash, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type); + Result GetPackage2Hash(void *dst_hash, size_t package2_size, void *work_buffer, size_t work_buffer_size, Package2Type which); + Result WritePackage2(void *work_buffer, size_t work_buffer_size, Package2Type which, BootImageUpdateType boot_image_update_type); - /* Initialize boot0 save accessor. */ - BisSave save; - R_TRY(save.Initialize(work_buffer, work_buffer_size)); - ON_SCOPE_EXIT { save.Finalize(); }; - - /* Load save from NAND. */ - R_TRY(save.Load()); - - /* Read data from save. */ - out->needs_verify_normal = save.GetNeedsVerification(BootModeType_Normal); - out->needs_verify_safe = save.GetNeedsVerification(BootModeType_Safe); - return ResultSuccess; -} - -Result Updater::VerifyBootImagesAndRepairIfNeeded(bool *out_repaired_normal, bool *out_repaired_safe, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) { - - /* Always set output to false before doing anything else. */ - *out_repaired_normal = false; - *out_repaired_safe = false; - - /* Ensure work buffer is big enough for us to do what we want to do. */ - R_TRY(ValidateWorkBuffer(work_buffer, work_buffer_size)); - - /* Get verification state from NAND. */ - VerificationState verification_state; - R_TRY(GetVerificationState(&verification_state, work_buffer, work_buffer_size)); - - /* If we don't need to verify anything, we're done. */ - if (!verification_state.needs_verify_normal && !verification_state.needs_verify_safe) { - return ResultSuccess; - } - - /* Get a session to ncm. */ - DoWithSmSession([&]() { - if (R_FAILED(ncmInitialize())) { - std::abort(); - } - }); - ON_SCOPE_EXIT { ncmExit(); }; - - /* Verify normal, verify safe as needed. */ - if (verification_state.needs_verify_normal) { - R_TRY_CATCH(VerifyBootImagesAndRepairIfNeeded(out_repaired_normal, BootModeType_Normal, work_buffer, work_buffer_size, boot_image_update_type)) { - R_CATCH(ResultUpdaterBootImagePackageNotFound) { - /* Nintendo considers failure to locate bip a success. TODO: don't do that? */ + /* Implementations. */ + Result ValidateWorkBuffer(const void *work_buffer, size_t work_buffer_size) { + if (work_buffer_size < BctSize + EksSize) { + return ResultUpdaterTooSmallWorkBuffer; } - } R_END_TRY_CATCH; - } - - if (verification_state.needs_verify_safe) { - R_TRY_CATCH(VerifyBootImagesAndRepairIfNeeded(out_repaired_safe, BootModeType_Safe, work_buffer, work_buffer_size, boot_image_update_type)) { - R_CATCH(ResultUpdaterBootImagePackageNotFound) { - /* Nintendo considers failure to locate bip a success. TODO: don't do that? */ + if (reinterpret_cast(work_buffer) & 0xFFF) { + return ResultUpdaterMisalignedWorkBuffer; } - } R_END_TRY_CATCH; - } - - return ResultSuccess; -} - -Result Updater::VerifyBootImagesAndRepairIfNeeded(bool *out_repaired, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) { - /* Get system data id for boot images (819/81A/81B/81C). */ - u64 bip_data_id; - R_TRY(GetBootImagePackageDataId(&bip_data_id, mode, work_buffer, work_buffer_size)); - - /* Verify the boot images in NAND. */ - R_TRY_CATCH(VerifyBootImages(bip_data_id, mode, work_buffer, work_buffer_size, boot_image_update_type)) { - R_CATCH(ResultUpdaterNeedsRepairBootImages) { - /* Perform repair. */ - *out_repaired = true; - R_TRY(UpdateBootImages(bip_data_id, mode, work_buffer, work_buffer_size, boot_image_update_type)); + if (reinterpret_cast(work_buffer_size) & 0x1FF) { + return ResultUpdaterMisalignedWorkBuffer; + } + return ResultSuccess; } - } R_END_TRY_CATCH; - /* We've either just verified or just repaired. Either way, we don't need to verify any more. */ - return SetVerificationNeeded(mode, false, work_buffer, work_buffer_size); -} - -Result Updater::GetBootImagePackageDataId(u64 *out_data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size) { - /* Ensure we can read content metas. */ - constexpr size_t MaxContentMetas = 0x40; - if (work_buffer_size < sizeof(NcmMetaRecord) * MaxContentMetas) { - std::abort(); - } - - /* Open NAND System meta database, list contents. */ - NcmContentMetaDatabase meta_db; - R_TRY(ncmOpenContentMetaDatabase(FsStorageId_NandSystem, &meta_db)); - ON_SCOPE_EXIT { serviceClose(&meta_db.s); }; - - NcmMetaRecord *records = reinterpret_cast(work_buffer); - - const u32 title_type = GetNcmTitleType(mode); - u32 written_entries; - u32 total_entries; - R_TRY(ncmContentMetaDatabaseList(&meta_db, title_type, 0, 0, UINT64_MAX, records, MaxContentMetas * sizeof(*records), &written_entries, &total_entries)); - if (total_entries == 0) { - return ResultUpdaterBootImagePackageNotFound; - } - - if (total_entries != written_entries) { - std::abort(); - } - - /* Output is sorted, return the lowest valid exfat entry. */ - if (total_entries > 1) { - for (size_t i = 0; i < total_entries; i++) { - u8 attr; - R_TRY(ncmContentMetaDatabaseGetAttributes(&meta_db, &records[i], &attr)); - - if (attr & NcmContentMetaAttribute_Exfat) { - *out_data_id = records[i].titleId; - return ResultSuccess; + bool HasEks(BootImageUpdateType boot_image_update_type) { + switch (boot_image_update_type) { + case BootImageUpdateType::Erista: + return true; + case BootImageUpdateType::Mariko: + return false; + default: + std::abort(); } } - } - /* If there's only one entry or no exfat entries, return that entry. */ - *out_data_id = records[0].titleId; - return ResultSuccess; -} - -Result Updater::VerifyBootImages(u64 data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) { - switch (mode) { - case BootModeType_Normal: - return VerifyBootImagesNormal(data_id, work_buffer, work_buffer_size, boot_image_update_type); - case BootModeType_Safe: - return VerifyBootImagesSafe(data_id, work_buffer, work_buffer_size, boot_image_update_type); - default: - std::abort(); - } -} - -Result Updater::ValidateBctFileHash(Boot0Accessor &accessor, Boot0Partition which, const void *stored_hash, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) { - /* Ensure work buffer is big enough for us to do what we want to do. */ - R_TRY(ValidateWorkBuffer(work_buffer, work_buffer_size)); - - void *bct = reinterpret_cast(reinterpret_cast(work_buffer) + 0); - void *work = reinterpret_cast(reinterpret_cast(work_buffer) + BctSize); - - size_t size; - R_TRY(ReadFile(&size, bct, BctSize, GetBctPath(boot_image_update_type))); - if (HasEks(boot_image_update_type)) { - R_TRY(accessor.UpdateEks(bct, work)); - } - if (HasAutoRcmPreserve(boot_image_update_type)) { - R_TRY(accessor.PreserveAutoRcm(bct, work, which)); - } - - u8 file_hash[SHA256_HASH_SIZE]; - sha256CalculateHash(file_hash, bct, BctSize); - - if (std::memcmp(file_hash, stored_hash, SHA256_HASH_SIZE) == 0) { - return ResultSuccess; - } - - return ResultUpdaterNeedsRepairBootImages; -} - -Result Updater::VerifyBootImagesNormal(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) { - /* Ensure work buffer is big enough for us to do what we want to do. */ - R_TRY(ValidateWorkBuffer(work_buffer, work_buffer_size)); - - R_TRY_CATCH(romfsMountFromDataArchive(data_id, FsStorageId_NandSystem, GetBootImagePackageMountPath())) { - R_CATCH(ResultFsTargetNotFound) { - return ResultUpdaterBootImagePackageNotFound; - } - } R_END_TRY_CATCH; - ON_SCOPE_EXIT { if (R_FAILED(romfsUnmount(GetBootImagePackageMountPath()))) { std::abort(); } }; - - /* Read and validate hashes of boot images. */ - { - size_t size; - u8 nand_hash[SHA256_HASH_SIZE]; - u8 file_hash[SHA256_HASH_SIZE]; - - Boot0Accessor boot0_accessor; - R_TRY(boot0_accessor.Initialize()); - ON_SCOPE_EXIT { boot0_accessor.Finalize(); }; - - /* Compare BCT hashes. */ - R_TRY(boot0_accessor.GetHash(nand_hash, BctSize, work_buffer, work_buffer_size, Boot0Partition::BctNormalMain)); - R_TRY(ValidateBctFileHash(boot0_accessor, Boot0Partition::BctNormalMain, nand_hash, work_buffer, work_buffer_size, boot_image_update_type)); - - /* Compare BCT Sub hashes. */ - R_TRY(boot0_accessor.GetHash(nand_hash, BctSize, work_buffer, work_buffer_size, Boot0Partition::BctNormalSub)); - R_TRY(ValidateBctFileHash(boot0_accessor, Boot0Partition::BctNormalSub, nand_hash, work_buffer, work_buffer_size, boot_image_update_type)); - - /* Compare Package1 Normal/Sub hashes. */ - R_TRY(GetFileHash(&size, file_hash, GetPackage1Path(boot_image_update_type), work_buffer, work_buffer_size)); - R_TRY(boot0_accessor.GetHash(nand_hash, size, work_buffer, work_buffer_size, Boot0Partition::Package1NormalMain)); - if (std::memcmp(file_hash, nand_hash, SHA256_HASH_SIZE) != 0) { - return ResultUpdaterNeedsRepairBootImages; - } - R_TRY(boot0_accessor.GetHash(nand_hash, size, work_buffer, work_buffer_size, Boot0Partition::Package1NormalSub)); - if (std::memcmp(file_hash, nand_hash, SHA256_HASH_SIZE) != 0) { - return ResultUpdaterNeedsRepairBootImages; + bool HasAutoRcmPreserve(BootImageUpdateType boot_image_update_type) { + switch (boot_image_update_type) { + case BootImageUpdateType::Erista: + return true; + case BootImageUpdateType::Mariko: + return false; + default: + std::abort(); + } } - /* Compare Package2 Normal/Sub hashes. */ - R_TRY(GetFileHash(&size, file_hash, GetPackage2Path(boot_image_update_type), work_buffer, work_buffer_size)); - R_TRY(GetPackage2Hash(nand_hash, size, work_buffer, work_buffer_size, Package2Type::NormalMain)); - if (std::memcmp(file_hash, nand_hash, SHA256_HASH_SIZE) != 0) { - return ResultUpdaterNeedsRepairBootImages; - } - R_TRY(GetPackage2Hash(nand_hash, size, work_buffer, work_buffer_size, Package2Type::NormalSub)); - if (std::memcmp(file_hash, nand_hash, SHA256_HASH_SIZE) != 0) { - return ResultUpdaterNeedsRepairBootImages; - } - } - - return ResultSuccess; -} - -Result Updater::VerifyBootImagesSafe(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) { - /* Ensure work buffer is big enough for us to do what we want to do. */ - R_TRY(ValidateWorkBuffer(work_buffer, work_buffer_size)); - - R_TRY_CATCH(romfsMountFromDataArchive(data_id, FsStorageId_NandSystem, GetBootImagePackageMountPath())) { - R_CATCH(ResultFsTargetNotFound) { - return ResultUpdaterBootImagePackageNotFound; - } - } R_END_TRY_CATCH; - ON_SCOPE_EXIT { if (R_FAILED(romfsUnmount(GetBootImagePackageMountPath()))) { std::abort(); } }; - - /* Read and validate hashes of boot images. */ - { - size_t size; - u8 nand_hash[SHA256_HASH_SIZE]; - u8 file_hash[SHA256_HASH_SIZE]; - - Boot0Accessor boot0_accessor; - R_TRY(boot0_accessor.Initialize()); - ON_SCOPE_EXIT { boot0_accessor.Finalize(); }; - - Boot1Accessor boot1_accessor; - R_TRY(boot1_accessor.Initialize()); - ON_SCOPE_EXIT { boot1_accessor.Finalize(); }; - - - /* Compare BCT hashes. */ - R_TRY(boot0_accessor.GetHash(nand_hash, BctSize, work_buffer, work_buffer_size, Boot0Partition::BctSafeMain)); - R_TRY(ValidateBctFileHash(boot0_accessor, Boot0Partition::BctSafeMain, nand_hash, work_buffer, work_buffer_size, boot_image_update_type)); - - /* Compare BCT Sub hashes. */ - R_TRY(boot0_accessor.GetHash(nand_hash, BctSize, work_buffer, work_buffer_size, Boot0Partition::BctSafeSub)); - R_TRY(ValidateBctFileHash(boot0_accessor, Boot0Partition::BctSafeSub, nand_hash, work_buffer, work_buffer_size, boot_image_update_type)); - - /* Compare Package1 Normal/Sub hashes. */ - R_TRY(GetFileHash(&size, file_hash, GetPackage1Path(boot_image_update_type), work_buffer, work_buffer_size)); - R_TRY(boot1_accessor.GetHash(nand_hash, size, work_buffer, work_buffer_size, Boot1Partition::Package1SafeMain)); - if (std::memcmp(file_hash, nand_hash, SHA256_HASH_SIZE) != 0) { - return ResultUpdaterNeedsRepairBootImages; - } - R_TRY(boot1_accessor.GetHash(nand_hash, size, work_buffer, work_buffer_size, Boot1Partition::Package1SafeSub)); - if (std::memcmp(file_hash, nand_hash, SHA256_HASH_SIZE) != 0) { - return ResultUpdaterNeedsRepairBootImages; + u32 GetNcmTitleType(BootModeType mode) { + switch (mode) { + case BootModeType::Normal: + return NcmContentMetaType_BootImagePackage; + case BootModeType::Safe: + return NcmContentMetaType_BootImagePackageSafe; + default: + std::abort(); + } } - /* Compare Package2 Normal/Sub hashes. */ - R_TRY(GetFileHash(&size, file_hash, GetPackage2Path(boot_image_update_type), work_buffer, work_buffer_size)); - R_TRY(GetPackage2Hash(nand_hash, size, work_buffer, work_buffer_size, Package2Type::SafeMain)); - if (std::memcmp(file_hash, nand_hash, SHA256_HASH_SIZE) != 0) { - return ResultUpdaterNeedsRepairBootImages; + Result GetVerificationState(VerificationState *out, void *work_buffer, size_t work_buffer_size) { + /* Always set output to true before doing anything else. */ + out->needs_verify_normal = true; + out->needs_verify_safe = true; + + /* Ensure work buffer is big enough for us to do what we want to do. */ + R_TRY(ValidateWorkBuffer(work_buffer, work_buffer_size)); + + /* Initialize boot0 save accessor. */ + BisSave save; + R_TRY(save.Initialize(work_buffer, work_buffer_size)); + ON_SCOPE_EXIT { save.Finalize(); }; + + /* Load save from NAND. */ + R_TRY(save.Load()); + + /* Read data from save. */ + out->needs_verify_normal = save.GetNeedsVerification(BootModeType::Normal); + out->needs_verify_safe = save.GetNeedsVerification(BootModeType::Safe); + return ResultSuccess; } - R_TRY(GetPackage2Hash(nand_hash, size, work_buffer, work_buffer_size, Package2Type::SafeSub)); - if (std::memcmp(file_hash, nand_hash, SHA256_HASH_SIZE) != 0) { - return ResultUpdaterNeedsRepairBootImages; + + Result VerifyBootImagesAndRepairIfNeeded(bool *out_repaired, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) { + /* Get system data id for boot images (819/81A/81B/81C). */ + u64 bip_data_id = 0; + R_TRY(GetBootImagePackageDataId(&bip_data_id, mode, work_buffer, work_buffer_size)); + + /* Verify the boot images in NAND. */ + R_TRY_CATCH(VerifyBootImages(bip_data_id, mode, work_buffer, work_buffer_size, boot_image_update_type)) { + R_CATCH(ResultUpdaterNeedsRepairBootImages) { + /* Perform repair. */ + *out_repaired = true; + R_TRY(UpdateBootImages(bip_data_id, mode, work_buffer, work_buffer_size, boot_image_update_type)); + } + } R_END_TRY_CATCH; + + /* We've either just verified or just repaired. Either way, we don't need to verify any more. */ + return SetVerificationNeeded(mode, false, work_buffer, work_buffer_size); } - } - return ResultSuccess; -} + Result GetBootImagePackageDataId(u64 *out_data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size) { + /* Ensure we can read content metas. */ + constexpr size_t MaxContentMetas = 0x40; + if (work_buffer_size < sizeof(NcmMetaRecord) * MaxContentMetas) { + std::abort(); + } -Result Updater::UpdateBootImages(u64 data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) { - switch (mode) { - case BootModeType_Normal: - return UpdateBootImagesNormal(data_id, work_buffer, work_buffer_size, boot_image_update_type); - case BootModeType_Safe: - return UpdateBootImagesSafe(data_id, work_buffer, work_buffer_size, boot_image_update_type); - default: - std::abort(); - } -} + /* Open NAND System meta database, list contents. */ + NcmContentMetaDatabase meta_db; + R_TRY(ncmOpenContentMetaDatabase(FsStorageId_NandSystem, &meta_db)); + ON_SCOPE_EXIT { serviceClose(&meta_db.s); }; -Result Updater::UpdateBootImagesNormal(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) { - /* Ensure work buffer is big enough for us to do what we want to do. */ - R_TRY(ValidateWorkBuffer(work_buffer, work_buffer_size)); + NcmMetaRecord *records = reinterpret_cast(work_buffer); - R_TRY_CATCH(romfsMountFromDataArchive(data_id, FsStorageId_NandSystem, GetBootImagePackageMountPath())) { - R_CATCH(ResultFsTargetNotFound) { - return ResultUpdaterBootImagePackageNotFound; + const u32 title_type = GetNcmTitleType(mode); + u32 written_entries; + u32 total_entries; + R_TRY(ncmContentMetaDatabaseList(&meta_db, title_type, 0, 0, UINT64_MAX, records, MaxContentMetas * sizeof(*records), &written_entries, &total_entries)); + if (total_entries == 0) { + return ResultUpdaterBootImagePackageNotFound; + } + + if (total_entries != written_entries) { + std::abort(); + } + + /* Output is sorted, return the lowest valid exfat entry. */ + if (total_entries > 1) { + for (size_t i = 0; i < total_entries; i++) { + u8 attr; + R_TRY(ncmContentMetaDatabaseGetAttributes(&meta_db, &records[i], &attr)); + + if (attr & NcmContentMetaAttribute_Exfat) { + *out_data_id = records[i].titleId; + return ResultSuccess; + } + } + } + + /* If there's only one entry or no exfat entries, return that entry. */ + *out_data_id = records[0].titleId; + return ResultSuccess; } - } R_END_TRY_CATCH; - ON_SCOPE_EXIT { if (R_FAILED(romfsUnmount(GetBootImagePackageMountPath()))) { std::abort(); } }; - { - Boot0Accessor boot0_accessor; - R_TRY(boot0_accessor.Initialize()); - ON_SCOPE_EXIT { boot0_accessor.Finalize(); }; + Result VerifyBootImages(u64 data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) { + switch (mode) { + case BootModeType::Normal: + return VerifyBootImagesNormal(data_id, work_buffer, work_buffer_size, boot_image_update_type); + case BootModeType::Safe: + return VerifyBootImagesSafe(data_id, work_buffer, work_buffer_size, boot_image_update_type); + default: + std::abort(); + } + } - /* Write Package1 sub. */ - R_TRY(boot0_accessor.Clear(work_buffer, work_buffer_size, Boot0Partition::Package1NormalSub)); - R_TRY(boot0_accessor.Write(GetPackage1Path(boot_image_update_type), work_buffer, work_buffer_size, Boot0Partition::Package1NormalSub)); + Result VerifyBootImagesNormal(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) { + /* Ensure work buffer is big enough for us to do what we want to do. */ + R_TRY(ValidateWorkBuffer(work_buffer, work_buffer_size)); - /* Write Package2 sub. */ - R_TRY(WritePackage2(work_buffer, work_buffer_size, Package2Type::NormalSub, boot_image_update_type)); + R_TRY_CATCH(romfsMountFromDataArchive(data_id, FsStorageId_NandSystem, GetBootImagePackageMountPath())) { + R_CATCH(ResultFsTargetNotFound) { + return ResultUpdaterBootImagePackageNotFound; + } + } R_END_TRY_CATCH; + ON_SCOPE_EXIT { if (R_FAILED(romfsUnmount(GetBootImagePackageMountPath()))) { std::abort(); } }; + + /* Read and validate hashes of boot images. */ + { + size_t size; + u8 nand_hash[SHA256_HASH_SIZE]; + u8 file_hash[SHA256_HASH_SIZE]; + + Boot0Accessor boot0_accessor; + R_TRY(boot0_accessor.Initialize()); + ON_SCOPE_EXIT { boot0_accessor.Finalize(); }; + + /* Compare BCT hashes. */ + R_TRY(boot0_accessor.GetHash(nand_hash, BctSize, work_buffer, work_buffer_size, Boot0Partition::BctNormalMain)); + R_TRY(ValidateBctFileHash(boot0_accessor, Boot0Partition::BctNormalMain, nand_hash, work_buffer, work_buffer_size, boot_image_update_type)); + + /* Compare BCT Sub hashes. */ + R_TRY(boot0_accessor.GetHash(nand_hash, BctSize, work_buffer, work_buffer_size, Boot0Partition::BctNormalSub)); + R_TRY(ValidateBctFileHash(boot0_accessor, Boot0Partition::BctNormalSub, nand_hash, work_buffer, work_buffer_size, boot_image_update_type)); + + /* Compare Package1 Normal/Sub hashes. */ + R_TRY(GetFileHash(&size, file_hash, GetPackage1Path(boot_image_update_type), work_buffer, work_buffer_size)); + R_TRY(boot0_accessor.GetHash(nand_hash, size, work_buffer, work_buffer_size, Boot0Partition::Package1NormalMain)); + if (std::memcmp(file_hash, nand_hash, SHA256_HASH_SIZE) != 0) { + return ResultUpdaterNeedsRepairBootImages; + } + R_TRY(boot0_accessor.GetHash(nand_hash, size, work_buffer, work_buffer_size, Boot0Partition::Package1NormalSub)); + if (std::memcmp(file_hash, nand_hash, SHA256_HASH_SIZE) != 0) { + return ResultUpdaterNeedsRepairBootImages; + } + + /* Compare Package2 Normal/Sub hashes. */ + R_TRY(GetFileHash(&size, file_hash, GetPackage2Path(boot_image_update_type), work_buffer, work_buffer_size)); + R_TRY(GetPackage2Hash(nand_hash, size, work_buffer, work_buffer_size, Package2Type::NormalMain)); + if (std::memcmp(file_hash, nand_hash, SHA256_HASH_SIZE) != 0) { + return ResultUpdaterNeedsRepairBootImages; + } + R_TRY(GetPackage2Hash(nand_hash, size, work_buffer, work_buffer_size, Package2Type::NormalSub)); + if (std::memcmp(file_hash, nand_hash, SHA256_HASH_SIZE) != 0) { + return ResultUpdaterNeedsRepairBootImages; + } + } + + return ResultSuccess; + } + + Result VerifyBootImagesSafe(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) { + /* Ensure work buffer is big enough for us to do what we want to do. */ + R_TRY(ValidateWorkBuffer(work_buffer, work_buffer_size)); + + R_TRY_CATCH(romfsMountFromDataArchive(data_id, FsStorageId_NandSystem, GetBootImagePackageMountPath())) { + R_CATCH(ResultFsTargetNotFound) { + return ResultUpdaterBootImagePackageNotFound; + } + } R_END_TRY_CATCH; + ON_SCOPE_EXIT { if (R_FAILED(romfsUnmount(GetBootImagePackageMountPath()))) { std::abort(); } }; + + /* Read and validate hashes of boot images. */ + { + size_t size; + u8 nand_hash[SHA256_HASH_SIZE]; + u8 file_hash[SHA256_HASH_SIZE]; + + Boot0Accessor boot0_accessor; + R_TRY(boot0_accessor.Initialize()); + ON_SCOPE_EXIT { boot0_accessor.Finalize(); }; + + Boot1Accessor boot1_accessor; + R_TRY(boot1_accessor.Initialize()); + ON_SCOPE_EXIT { boot1_accessor.Finalize(); }; + + + /* Compare BCT hashes. */ + R_TRY(boot0_accessor.GetHash(nand_hash, BctSize, work_buffer, work_buffer_size, Boot0Partition::BctSafeMain)); + R_TRY(ValidateBctFileHash(boot0_accessor, Boot0Partition::BctSafeMain, nand_hash, work_buffer, work_buffer_size, boot_image_update_type)); + + /* Compare BCT Sub hashes. */ + R_TRY(boot0_accessor.GetHash(nand_hash, BctSize, work_buffer, work_buffer_size, Boot0Partition::BctSafeSub)); + R_TRY(ValidateBctFileHash(boot0_accessor, Boot0Partition::BctSafeSub, nand_hash, work_buffer, work_buffer_size, boot_image_update_type)); + + /* Compare Package1 Normal/Sub hashes. */ + R_TRY(GetFileHash(&size, file_hash, GetPackage1Path(boot_image_update_type), work_buffer, work_buffer_size)); + R_TRY(boot1_accessor.GetHash(nand_hash, size, work_buffer, work_buffer_size, Boot1Partition::Package1SafeMain)); + if (std::memcmp(file_hash, nand_hash, SHA256_HASH_SIZE) != 0) { + return ResultUpdaterNeedsRepairBootImages; + } + R_TRY(boot1_accessor.GetHash(nand_hash, size, work_buffer, work_buffer_size, Boot1Partition::Package1SafeSub)); + if (std::memcmp(file_hash, nand_hash, SHA256_HASH_SIZE) != 0) { + return ResultUpdaterNeedsRepairBootImages; + } + + /* Compare Package2 Normal/Sub hashes. */ + R_TRY(GetFileHash(&size, file_hash, GetPackage2Path(boot_image_update_type), work_buffer, work_buffer_size)); + R_TRY(GetPackage2Hash(nand_hash, size, work_buffer, work_buffer_size, Package2Type::SafeMain)); + if (std::memcmp(file_hash, nand_hash, SHA256_HASH_SIZE) != 0) { + return ResultUpdaterNeedsRepairBootImages; + } + R_TRY(GetPackage2Hash(nand_hash, size, work_buffer, work_buffer_size, Package2Type::SafeSub)); + if (std::memcmp(file_hash, nand_hash, SHA256_HASH_SIZE) != 0) { + return ResultUpdaterNeedsRepairBootImages; + } + } + + return ResultSuccess; + } + + Result UpdateBootImages(u64 data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) { + switch (mode) { + case BootModeType::Normal: + return UpdateBootImagesNormal(data_id, work_buffer, work_buffer_size, boot_image_update_type); + case BootModeType::Safe: + return UpdateBootImagesSafe(data_id, work_buffer, work_buffer_size, boot_image_update_type); + default: + std::abort(); + } + } + + Result UpdateBootImagesNormal(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) { + /* Ensure work buffer is big enough for us to do what we want to do. */ + R_TRY(ValidateWorkBuffer(work_buffer, work_buffer_size)); + + R_TRY_CATCH(romfsMountFromDataArchive(data_id, FsStorageId_NandSystem, GetBootImagePackageMountPath())) { + R_CATCH(ResultFsTargetNotFound) { + return ResultUpdaterBootImagePackageNotFound; + } + } R_END_TRY_CATCH; + ON_SCOPE_EXIT { if (R_FAILED(romfsUnmount(GetBootImagePackageMountPath()))) { std::abort(); } }; + + { + Boot0Accessor boot0_accessor; + R_TRY(boot0_accessor.Initialize()); + ON_SCOPE_EXIT { boot0_accessor.Finalize(); }; + + /* Write Package1 sub. */ + R_TRY(boot0_accessor.Clear(work_buffer, work_buffer_size, Boot0Partition::Package1NormalSub)); + R_TRY(boot0_accessor.Write(GetPackage1Path(boot_image_update_type), work_buffer, work_buffer_size, Boot0Partition::Package1NormalSub)); + + /* Write Package2 sub. */ + R_TRY(WritePackage2(work_buffer, work_buffer_size, Package2Type::NormalSub, boot_image_update_type)); + + /* Write BCT sub + BCT main, in that order. */ + { + void *bct = reinterpret_cast(reinterpret_cast(work_buffer) + 0); + void *work = reinterpret_cast(reinterpret_cast(work_buffer) + BctSize); + + size_t size; + R_TRY(ReadFile(&size, bct, BctSize, GetBctPath(boot_image_update_type))); + if (HasEks(boot_image_update_type)) { + R_TRY(boot0_accessor.UpdateEks(bct, work)); + } + + /* Only preserve autorcm if on a unit with unpatched rcm bug. */ + if (HasAutoRcmPreserve(boot_image_update_type) && !IsRcmBugPatched()) { + R_TRY(boot0_accessor.PreserveAutoRcm(bct, work, Boot0Partition::BctNormalSub)); + R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctNormalSub)); + R_TRY(boot0_accessor.PreserveAutoRcm(bct, work, Boot0Partition::BctNormalMain)); + R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctNormalMain)); + } else { + R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctNormalSub)); + R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctNormalMain)); + } + } + + /* Write Package2 main. */ + R_TRY(WritePackage2(work_buffer, work_buffer_size, Package2Type::NormalMain, boot_image_update_type)); + + /* Write Package1 main. */ + R_TRY(boot0_accessor.Clear(work_buffer, work_buffer_size, Boot0Partition::Package1NormalMain)); + R_TRY(boot0_accessor.Write(GetPackage1Path(boot_image_update_type), work_buffer, work_buffer_size, Boot0Partition::Package1NormalMain)); + } + + return ResultSuccess; + } + + Result UpdateBootImagesSafe(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) { + /* Ensure work buffer is big enough for us to do what we want to do. */ + R_TRY(ValidateWorkBuffer(work_buffer, work_buffer_size)); + + R_TRY_CATCH(romfsMountFromDataArchive(data_id, FsStorageId_NandSystem, GetBootImagePackageMountPath())) { + R_CATCH(ResultFsTargetNotFound) { + return ResultUpdaterBootImagePackageNotFound; + } + } R_END_TRY_CATCH; + ON_SCOPE_EXIT { if (R_FAILED(romfsUnmount(GetBootImagePackageMountPath()))) { std::abort(); } }; + + { + Boot0Accessor boot0_accessor; + R_TRY(boot0_accessor.Initialize()); + ON_SCOPE_EXIT { boot0_accessor.Finalize(); }; + + Boot1Accessor boot1_accessor; + R_TRY(boot1_accessor.Initialize()); + ON_SCOPE_EXIT { boot1_accessor.Finalize(); }; + + + /* Write Package1 sub. */ + R_TRY(boot1_accessor.Clear(work_buffer, work_buffer_size, Boot1Partition::Package1SafeSub)); + R_TRY(boot1_accessor.Write(GetPackage1Path(boot_image_update_type), work_buffer, work_buffer_size, Boot1Partition::Package1SafeSub)); + + /* Write Package2 sub. */ + R_TRY(WritePackage2(work_buffer, work_buffer_size, Package2Type::SafeSub, boot_image_update_type)); + + /* Write BCT sub + BCT main, in that order. */ + { + void *bct = reinterpret_cast(reinterpret_cast(work_buffer) + 0); + void *work = reinterpret_cast(reinterpret_cast(work_buffer) + BctSize); + + size_t size; + R_TRY(ReadFile(&size, bct, BctSize, GetBctPath(boot_image_update_type))); + if (HasEks(boot_image_update_type)) { + R_TRY(boot0_accessor.UpdateEks(bct, work)); + } + /* Only preserve autorcm if on a unit with unpatched rcm bug. */ + if (HasAutoRcmPreserve(boot_image_update_type) && !IsRcmBugPatched()) { + R_TRY(boot0_accessor.PreserveAutoRcm(bct, work, Boot0Partition::BctSafeSub)); + R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctSafeSub)); + R_TRY(boot0_accessor.PreserveAutoRcm(bct, work, Boot0Partition::BctSafeMain)); + R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctSafeMain)); + } else { + R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctSafeSub)); + R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctSafeMain)); + } + } + + /* Write Package2 main. */ + R_TRY(WritePackage2(work_buffer, work_buffer_size, Package2Type::SafeMain, boot_image_update_type)); + + /* Write Package1 main. */ + R_TRY(boot1_accessor.Clear(work_buffer, work_buffer_size, Boot1Partition::Package1SafeMain)); + R_TRY(boot1_accessor.Write(GetPackage1Path(boot_image_update_type), work_buffer, work_buffer_size, Boot1Partition::Package1SafeMain)); + } + + return ResultSuccess; + } + + Result SetVerificationNeeded(BootModeType mode, bool needed, void *work_buffer, size_t work_buffer_size) { + /* Ensure work buffer is big enough for us to do what we want to do. */ + R_TRY(ValidateWorkBuffer(work_buffer, work_buffer_size)); + + /* Initialize boot0 save accessor. */ + BisSave save; + R_TRY(save.Initialize(work_buffer, work_buffer_size)); + ON_SCOPE_EXIT { save.Finalize(); }; + + /* Load save from NAND. */ + R_TRY(save.Load()); + + /* Set whether we need to verify, then save to nand. */ + save.SetNeedsVerification(mode, needed); + R_TRY(save.Save()); + + return ResultSuccess; + } + + Result ValidateBctFileHash(Boot0Accessor &accessor, Boot0Partition which, const void *stored_hash, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) { + /* Ensure work buffer is big enough for us to do what we want to do. */ + R_TRY(ValidateWorkBuffer(work_buffer, work_buffer_size)); - /* Write BCT sub + BCT main, in that order. */ - { void *bct = reinterpret_cast(reinterpret_cast(work_buffer) + 0); void *work = reinterpret_cast(reinterpret_cast(work_buffer) + BctSize); size_t size; R_TRY(ReadFile(&size, bct, BctSize, GetBctPath(boot_image_update_type))); if (HasEks(boot_image_update_type)) { - R_TRY(boot0_accessor.UpdateEks(bct, work)); + R_TRY(accessor.UpdateEks(bct, work)); + } + if (HasAutoRcmPreserve(boot_image_update_type)) { + R_TRY(accessor.PreserveAutoRcm(bct, work, which)); } - /* Only preserve autorcm if on a unit with unpatched rcm bug. */ - if (HasAutoRcmPreserve(boot_image_update_type) && !IsRcmBugPatched()) { - R_TRY(boot0_accessor.PreserveAutoRcm(bct, work, Boot0Partition::BctNormalSub)); - R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctNormalSub)); - R_TRY(boot0_accessor.PreserveAutoRcm(bct, work, Boot0Partition::BctNormalMain)); - R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctNormalMain)); - } else { - R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctNormalSub)); - R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctNormalMain)); + u8 file_hash[SHA256_HASH_SIZE]; + sha256CalculateHash(file_hash, bct, BctSize); + + if (std::memcmp(file_hash, stored_hash, SHA256_HASH_SIZE) != 0) { + return ResultUpdaterNeedsRepairBootImages; } + + return ResultSuccess; } - /* Write Package2 main. */ - R_TRY(WritePackage2(work_buffer, work_buffer_size, Package2Type::NormalMain, boot_image_update_type)); + Result GetPackage2Hash(void *dst_hash, size_t package2_size, void *work_buffer, size_t work_buffer_size, Package2Type which) { + Package2Accessor accessor(which); + R_TRY(accessor.Initialize()); + ON_SCOPE_EXIT { accessor.Finalize(); }; + + return accessor.GetHash(dst_hash, package2_size, work_buffer, work_buffer_size, Package2Partition::Package2); + } + + Result WritePackage2(void *work_buffer, size_t work_buffer_size, Package2Type which, BootImageUpdateType boot_image_update_type) { + Package2Accessor accessor(which); + R_TRY(accessor.Initialize()); + ON_SCOPE_EXIT { accessor.Finalize(); }; + + return accessor.Write(GetPackage2Path(boot_image_update_type), work_buffer, work_buffer_size, Package2Partition::Package2); + } - /* Write Package1 main. */ - R_TRY(boot0_accessor.Clear(work_buffer, work_buffer_size, Boot0Partition::Package1NormalMain)); - R_TRY(boot0_accessor.Write(GetPackage1Path(boot_image_update_type), work_buffer, work_buffer_size, Boot0Partition::Package1NormalMain)); } - return ResultSuccess; -} - -Result Updater::UpdateBootImagesSafe(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) { - /* Ensure work buffer is big enough for us to do what we want to do. */ - R_TRY(ValidateWorkBuffer(work_buffer, work_buffer_size)); - - R_TRY_CATCH(romfsMountFromDataArchive(data_id, FsStorageId_NandSystem, GetBootImagePackageMountPath())) { - R_CATCH(ResultFsTargetNotFound) { - return ResultUpdaterBootImagePackageNotFound; + BootImageUpdateType GetBootImageUpdateType(HardwareType hw_type) { + switch (hw_type) { + case HardwareType_Icosa: + case HardwareType_Copper: + return BootImageUpdateType::Erista; + case HardwareType_Hoag: + case HardwareType_Iowa: + return BootImageUpdateType::Mariko; + default: + std::abort(); } - } R_END_TRY_CATCH; - ON_SCOPE_EXIT { if (R_FAILED(romfsUnmount(GetBootImagePackageMountPath()))) { std::abort(); } }; - - { - Boot0Accessor boot0_accessor; - R_TRY(boot0_accessor.Initialize()); - ON_SCOPE_EXIT { boot0_accessor.Finalize(); }; - - Boot1Accessor boot1_accessor; - R_TRY(boot1_accessor.Initialize()); - ON_SCOPE_EXIT { boot1_accessor.Finalize(); }; - - - /* Write Package1 sub. */ - R_TRY(boot1_accessor.Clear(work_buffer, work_buffer_size, Boot1Partition::Package1SafeSub)); - R_TRY(boot1_accessor.Write(GetPackage1Path(boot_image_update_type), work_buffer, work_buffer_size, Boot1Partition::Package1SafeSub)); - - /* Write Package2 sub. */ - R_TRY(WritePackage2(work_buffer, work_buffer_size, Package2Type::SafeSub, boot_image_update_type)); - - /* Write BCT sub + BCT main, in that order. */ - { - void *bct = reinterpret_cast(reinterpret_cast(work_buffer) + 0); - void *work = reinterpret_cast(reinterpret_cast(work_buffer) + BctSize); - - size_t size; - R_TRY(ReadFile(&size, bct, BctSize, GetBctPath(boot_image_update_type))); - if (HasEks(boot_image_update_type)) { - R_TRY(boot0_accessor.UpdateEks(bct, work)); - } - /* Only preserve autorcm if on a unit with unpatched rcm bug. */ - if (HasAutoRcmPreserve(boot_image_update_type) && !IsRcmBugPatched()) { - R_TRY(boot0_accessor.PreserveAutoRcm(bct, work, Boot0Partition::BctSafeSub)); - R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctSafeSub)); - R_TRY(boot0_accessor.PreserveAutoRcm(bct, work, Boot0Partition::BctSafeMain)); - R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctSafeMain)); - } else { - R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctSafeSub)); - R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctSafeMain)); - } - } - - /* Write Package2 main. */ - R_TRY(WritePackage2(work_buffer, work_buffer_size, Package2Type::SafeMain, boot_image_update_type)); - - /* Write Package1 main. */ - R_TRY(boot1_accessor.Clear(work_buffer, work_buffer_size, Boot1Partition::Package1SafeMain)); - R_TRY(boot1_accessor.Write(GetPackage1Path(boot_image_update_type), work_buffer, work_buffer_size, Boot1Partition::Package1SafeMain)); } - return ResultSuccess; -} - -Result Updater::SetVerificationNeeded(BootModeType mode, bool needed, void *work_buffer, size_t work_buffer_size) { - /* Ensure work buffer is big enough for us to do what we want to do. */ - R_TRY(ValidateWorkBuffer(work_buffer, work_buffer_size)); - - /* Initialize boot0 save accessor. */ - BisSave save; - R_TRY(save.Initialize(work_buffer, work_buffer_size)); - ON_SCOPE_EXIT { save.Finalize(); }; - - /* Load save from NAND. */ - R_TRY(save.Load()); - - /* Set whether we need to verify, then save to nand. */ - save.SetNeedsVerification(mode, needed); - R_TRY(save.Save()); - - return ResultSuccess; -} - -Result Updater::GetPackage2Hash(void *dst_hash, size_t package2_size, void *work_buffer, size_t work_buffer_size, Package2Type which) { - Package2Accessor accessor(which); - R_TRY(accessor.Initialize()); - ON_SCOPE_EXIT { accessor.Finalize(); }; - - return accessor.GetHash(dst_hash, package2_size, work_buffer, work_buffer_size, Package2Partition::Package2); -} - -Result Updater::WritePackage2(void *work_buffer, size_t work_buffer_size, Package2Type which, BootImageUpdateType boot_image_update_type) { - Package2Accessor accessor(which); - R_TRY(accessor.Initialize()); - ON_SCOPE_EXIT { accessor.Finalize(); }; - - return accessor.Write(GetPackage2Path(boot_image_update_type), work_buffer, work_buffer_size, Package2Partition::Package2); + Result VerifyBootImagesAndRepairIfNeeded(bool *out_repaired_normal, bool *out_repaired_safe, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) { + /* Always set output to false before doing anything else. */ + *out_repaired_normal = false; + *out_repaired_safe = false; + + /* Ensure work buffer is big enough for us to do what we want to do. */ + R_TRY(ValidateWorkBuffer(work_buffer, work_buffer_size)); + + /* Get verification state from NAND. */ + VerificationState verification_state; + R_TRY(GetVerificationState(&verification_state, work_buffer, work_buffer_size)); + + /* If we don't need to verify anything, we're done. */ + if (!verification_state.needs_verify_normal && !verification_state.needs_verify_safe) { + return ResultSuccess; + } + + /* Get a session to ncm. */ + DoWithSmSession([&]() { + if (R_FAILED(ncmInitialize())) { + std::abort(); + } + }); + ON_SCOPE_EXIT { ncmExit(); }; + + /* Verify normal, verify safe as needed. */ + if (verification_state.needs_verify_normal) { + R_TRY_CATCH(VerifyBootImagesAndRepairIfNeeded(out_repaired_normal, BootModeType::Normal, work_buffer, work_buffer_size, boot_image_update_type)) { + R_CATCH(ResultUpdaterBootImagePackageNotFound) { + /* Nintendo considers failure to locate bip a success. TODO: don't do that? */ + } + } R_END_TRY_CATCH; + } + + if (verification_state.needs_verify_safe) { + R_TRY_CATCH(VerifyBootImagesAndRepairIfNeeded(out_repaired_safe, BootModeType::Safe, work_buffer, work_buffer_size, boot_image_update_type)) { + R_CATCH(ResultUpdaterBootImagePackageNotFound) { + /* Nintendo considers failure to locate bip a success. TODO: don't do that? */ + } + } R_END_TRY_CATCH; + } + + return ResultSuccess; + } + } diff --git a/stratosphere/boot/source/updater/updater_api.hpp b/stratosphere/boot/source/updater/updater_api.hpp index 1ca08e652..3eb45c43c 100644 --- a/stratosphere/boot/source/updater/updater_api.hpp +++ b/stratosphere/boot/source/updater/updater_api.hpp @@ -20,43 +20,10 @@ #include "updater_types.hpp" -class Boot0Accessor; -enum class Boot0Partition; -enum class Package2Type; +namespace sts::updater { -class Updater { - private: - static bool HasEks(BootImageUpdateType boot_image_update_type); - static bool HasAutoRcmPreserve(BootImageUpdateType boot_image_update_type); - static u32 GetNcmTitleType(BootModeType mode); - static Result ValidateWorkBuffer(const void *work_buffer, size_t work_buffer_size); - static Result GetVerificationState(VerificationState *out, void *work_buffer, size_t work_buffer_size); - static Result VerifyBootImagesAndRepairIfNeeded(bool *out_repaired, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type); - static Result GetBootImagePackageDataId(u64 *out_data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size); - static Result VerifyBootImages(u64 data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type); - static Result VerifyBootImagesNormal(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type); - static Result VerifyBootImagesSafe(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type); - static Result UpdateBootImages(u64 data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type); - static Result UpdateBootImagesNormal(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type); - static Result UpdateBootImagesSafe(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type); - static Result SetVerificationNeeded(BootModeType mode, bool needed, void *work_buffer, size_t work_buffer_size); - static Result RepairBootImages(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type); + /* Public API. */ + BootImageUpdateType GetBootImageUpdateType(HardwareType hw_type); + Result VerifyBootImagesAndRepairIfNeeded(bool *out_repaired_normal, bool *out_repaired_safe, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type); - /* Path functionality. */ - static const char *GetBootImagePackageMountPath(); - static const char *GetBctPath(BootImageUpdateType boot_image_update_type); - static const char *GetPackage1Path(BootImageUpdateType boot_image_update_type); - static const char *GetPackage2Path(BootImageUpdateType boot_image_update_type); - - /* File helpers. */ - static Result ReadFile(size_t *out_size, void *dst, size_t dst_size, const char *path); - static Result GetFileHash(size_t *out_size, void *dst_hash, const char *path, void *work_buffer, size_t work_buffer_size); - - /* Package helpers. */ - static Result ValidateBctFileHash(Boot0Accessor &accessor, Boot0Partition which, const void *stored_hash, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type); - static Result GetPackage2Hash(void *dst_hash, size_t package2_size, void *work_buffer, size_t work_buffer_size, Package2Type which); - static Result WritePackage2(void *work_buffer, size_t work_buffer_size, Package2Type which, BootImageUpdateType boot_image_update_type); - public: - static BootImageUpdateType GetBootImageUpdateType(HardwareType hw_type); - static Result VerifyBootImagesAndRepairIfNeeded(bool *out_repaired_normal, bool *out_repaired_safe, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type); -}; \ No newline at end of file +} diff --git a/stratosphere/boot/source/updater/updater_bis_management.cpp b/stratosphere/boot/source/updater/updater_bis_management.cpp index 355976569..27912c0e0 100644 --- a/stratosphere/boot/source/updater/updater_bis_management.cpp +++ b/stratosphere/boot/source/updater/updater_bis_management.cpp @@ -19,146 +19,150 @@ #include "updater_bis_management.hpp" -Result BisAccessor::Initialize() { - R_TRY(fsOpenBisStorage(&this->storage, this->partition_id)); - this->active = true; - return ResultSuccess; -} +namespace sts::updater { -void BisAccessor::Finalize() { - if (this->active) { - fsStorageClose(&this->storage); - this->active = false; - } -} - -Result BisAccessor::Read(void *dst, size_t size, u64 offset) { - if (offset % SectorAlignment) { - std::abort(); - } - return fsStorageRead(&this->storage, offset, dst, size); -} - -Result BisAccessor::Write(u64 offset, const void *src, size_t size) { - if (offset % SectorAlignment) { - std::abort(); - } - return fsStorageWrite(&this->storage, offset, src, size); -} - -Result BisAccessor::Write(u64 offset, size_t size, const char *bip_path, void *work_buffer, size_t work_buffer_size) { - if (offset % SectorAlignment != 0 || work_buffer_size % SectorAlignment != 0) { - std::abort(); + Result BisAccessor::Initialize() { + R_TRY(fsOpenBisStorage(&this->storage, this->partition_id)); + this->active = true; + return ResultSuccess; } - FILE *bip_fp = fopen(bip_path, "rb"); - if (bip_fp == NULL) { - return ResultUpdaterInvalidBootImagePackage; - } - ON_SCOPE_EXIT { fclose(bip_fp); }; - - size_t written = 0; - while (true) { - std::memset(work_buffer, 0, work_buffer_size); - size_t read_size = fread(work_buffer, 1, work_buffer_size, bip_fp); - if (read_size != work_buffer_size) { - if (ferror(bip_fp)) { - return fsdevGetLastResult(); - } + void BisAccessor::Finalize() { + if (this->active) { + fsStorageClose(&this->storage); + this->active = false; } - if (written + read_size > size) { + } + + Result BisAccessor::Read(void *dst, size_t size, u64 offset) { + if (offset % SectorAlignment) { + std::abort(); + } + return fsStorageRead(&this->storage, offset, dst, size); + } + + Result BisAccessor::Write(u64 offset, const void *src, size_t size) { + if (offset % SectorAlignment) { + std::abort(); + } + return fsStorageWrite(&this->storage, offset, src, size); + } + + Result BisAccessor::Write(u64 offset, size_t size, const char *bip_path, void *work_buffer, size_t work_buffer_size) { + if (offset % SectorAlignment != 0 || work_buffer_size % SectorAlignment != 0) { std::abort(); } - size_t aligned_size = ((read_size + SectorAlignment - 1) / SectorAlignment) * SectorAlignment; - R_TRY(this->Write(offset + written, work_buffer, aligned_size)); - written += read_size; - - if (read_size != work_buffer_size) { - break; + FILE *bip_fp = fopen(bip_path, "rb"); + if (bip_fp == NULL) { + return ResultUpdaterInvalidBootImagePackage; } - } - return ResultSuccess; -} + ON_SCOPE_EXIT { fclose(bip_fp); }; -Result BisAccessor::Clear(u64 offset, u64 size, void *work_buffer, size_t work_buffer_size) { - if (offset % SectorAlignment != 0 || work_buffer_size % SectorAlignment != 0) { - std::abort(); + size_t written = 0; + while (true) { + std::memset(work_buffer, 0, work_buffer_size); + size_t read_size = fread(work_buffer, 1, work_buffer_size, bip_fp); + if (read_size != work_buffer_size) { + if (ferror(bip_fp)) { + return fsdevGetLastResult(); + } + } + if (written + read_size > size) { + std::abort(); + } + + size_t aligned_size = ((read_size + SectorAlignment - 1) / SectorAlignment) * SectorAlignment; + R_TRY(this->Write(offset + written, work_buffer, aligned_size)); + written += read_size; + + if (read_size != work_buffer_size) { + break; + } + } + return ResultSuccess; } - std::memset(work_buffer, 0, work_buffer_size); + Result BisAccessor::Clear(u64 offset, u64 size, void *work_buffer, size_t work_buffer_size) { + if (offset % SectorAlignment != 0 || work_buffer_size % SectorAlignment != 0) { + std::abort(); + } - size_t written = 0; - while (written < size) { - size_t cur_write_size = std::min(work_buffer_size, size - written); - R_TRY(this->Write(offset + written, work_buffer, cur_write_size)); - written += cur_write_size; - } - return ResultSuccess; -} + std::memset(work_buffer, 0, work_buffer_size); -Result BisAccessor::GetHash(void *dst, u64 offset, u64 size, u64 hash_size, void *work_buffer, size_t work_buffer_size) { - if (offset % SectorAlignment != 0 || work_buffer_size % SectorAlignment != 0) { - std::abort(); + size_t written = 0; + while (written < size) { + size_t cur_write_size = std::min(work_buffer_size, size - written); + R_TRY(this->Write(offset + written, work_buffer, cur_write_size)); + written += cur_write_size; + } + return ResultSuccess; } - Sha256Context sha_ctx; - sha256ContextCreate(&sha_ctx); + Result BisAccessor::GetHash(void *dst, u64 offset, u64 size, u64 hash_size, void *work_buffer, size_t work_buffer_size) { + if (offset % SectorAlignment != 0 || work_buffer_size % SectorAlignment != 0) { + std::abort(); + } - size_t total_read = 0; - while (total_read < hash_size) { - size_t cur_read_size = std::min(work_buffer_size, size - total_read); - size_t cur_update_size = std::min(cur_read_size, hash_size - total_read); - R_TRY(this->Read(work_buffer, cur_read_size, offset + total_read)); - sha256ContextUpdate(&sha_ctx, work_buffer, cur_update_size); - total_read += cur_read_size; - } - sha256ContextGetHash(&sha_ctx, dst); + Sha256Context sha_ctx; + sha256ContextCreate(&sha_ctx); - return ResultSuccess; -} + size_t total_read = 0; + while (total_read < hash_size) { + size_t cur_read_size = std::min(work_buffer_size, size - total_read); + size_t cur_update_size = std::min(cur_read_size, hash_size - total_read); + R_TRY(this->Read(work_buffer, cur_read_size, offset + total_read)); + sha256ContextUpdate(&sha_ctx, work_buffer, cur_update_size); + total_read += cur_read_size; + } + sha256ContextGetHash(&sha_ctx, dst); -size_t Boot0Accessor::GetBootloaderVersion(void *bct) { - u32 version = *reinterpret_cast(reinterpret_cast(bct) + BctVersionOffset); - if (version > BctVersionMax) { - std::abort(); + return ResultSuccess; } - return static_cast(version); -} + size_t Boot0Accessor::GetBootloaderVersion(void *bct) { + u32 version = *reinterpret_cast(reinterpret_cast(bct) + BctVersionOffset); + if (version > BctVersionMax) { + std::abort(); + } -size_t Boot0Accessor::GetEksIndex(size_t bootloader_version) { - if (bootloader_version > BctVersionMax) { - std::abort(); + return static_cast(version); + } + + size_t Boot0Accessor::GetEksIndex(size_t bootloader_version) { + if (bootloader_version > BctVersionMax) { + std::abort(); + } + + return (bootloader_version > 0) ? bootloader_version - 1 : 0; + } + + void Boot0Accessor::CopyEks(void *dst_bct, const void *src_eks, size_t eks_index) { + std::memcpy(reinterpret_cast(dst_bct) + BctEksOffset, reinterpret_cast(src_eks) + eks_index * EksEntrySize, EksBlobSize); + } + + Result Boot0Accessor::UpdateEks(void *dst_bct, void *eks_work_buffer) { + size_t read_size; + R_TRY(this->Read(&read_size, eks_work_buffer, EksSize, Boot0Partition::Eks)); + + return this->UpdateEksManually(dst_bct, eks_work_buffer); + } + + Result Boot0Accessor::UpdateEksManually(void *dst_bct, const void *src_eks) { + this->CopyEks(dst_bct, src_eks, GetEksIndex(GetBootloaderVersion(dst_bct))); + return ResultSuccess; + } + + Result Boot0Accessor::PreserveAutoRcm(void *dst_bct, void *work_buffer, Boot0Partition which) { + std::memset(work_buffer, 0, BctSize); + + size_t read_size; + R_TRY(this->Read(&read_size, work_buffer, BctSize, which)); + + void *dst_pubk = reinterpret_cast(reinterpret_cast(dst_bct) + BctPubkOffset); + void *src_pubk = reinterpret_cast(reinterpret_cast(work_buffer) + BctPubkOffset); + std::memcpy(dst_pubk, src_pubk, BctPubkSize); + return ResultSuccess; } - return (bootloader_version > 0) ? bootloader_version - 1 : 0; } - -void Boot0Accessor::CopyEks(void *dst_bct, const void *src_eks, size_t eks_index) { - std::memcpy(reinterpret_cast(dst_bct) + BctEksOffset, reinterpret_cast(src_eks) + eks_index * EksEntrySize, EksBlobSize); -} - -Result Boot0Accessor::UpdateEks(void *dst_bct, void *eks_work_buffer) { - size_t read_size; - R_TRY(this->Read(&read_size, eks_work_buffer, EksSize, Boot0Partition::Eks)); - - return this->UpdateEksManually(dst_bct, eks_work_buffer); -} - -Result Boot0Accessor::UpdateEksManually(void *dst_bct, const void *src_eks) { - this->CopyEks(dst_bct, src_eks, GetEksIndex(GetBootloaderVersion(dst_bct))); - return ResultSuccess; -} - -Result Boot0Accessor::PreserveAutoRcm(void *dst_bct, void *work_buffer, Boot0Partition which) { - std::memset(work_buffer, 0, BctSize); - - size_t read_size; - R_TRY(this->Read(&read_size, work_buffer, BctSize, which)); - - void *dst_pubk = reinterpret_cast(reinterpret_cast(dst_bct) + BctPubkOffset); - void *src_pubk = reinterpret_cast(reinterpret_cast(work_buffer) + BctPubkOffset); - std::memcpy(dst_pubk, src_pubk, BctPubkSize); - return ResultSuccess; -} \ No newline at end of file diff --git a/stratosphere/boot/source/updater/updater_bis_management.hpp b/stratosphere/boot/source/updater/updater_bis_management.hpp index f8f3f196b..b536e9700 100644 --- a/stratosphere/boot/source/updater/updater_bis_management.hpp +++ b/stratosphere/boot/source/updater/updater_bis_management.hpp @@ -20,217 +20,220 @@ #include "updater_types.hpp" -class BisAccessor { - public: - static constexpr size_t SectorAlignment = 0x200; - private: - FsStorage storage = {}; - FsBisStorageId partition_id; - bool active; - public: - BisAccessor(FsBisStorageId id) : partition_id(id), active(false) { } - ~BisAccessor() { - if (this->active) { - fsStorageClose(&storage); - } - } +namespace sts::updater { - public: - Result Initialize(); - void Finalize(); - protected: - Result Read(void *dst, size_t size, u64 offset); - Result Write(u64 offset, const void *src, size_t size); - Result Write(u64 offset, size_t size, const char *bip_path, void *work_buffer, size_t work_buffer_size); - Result Clear(u64 offset, u64 size, void *work_buffer, size_t work_buffer_size); - Result GetHash(void *dst, u64 offset, u64 size, u64 hash_size, void *work_buffer, size_t work_buffer_size); -}; - -template -struct OffsetSizeEntry { - EnumType which; - u64 offset; - size_t size; -}; - -enum class Boot0Partition { - BctNormalMain, - BctSafeMain, - BctNormalSub, - BctSafeSub, - BctSave, - Package1NormalMain, - Package1NormalSub, - Eks, - Count, -}; - -enum class Boot1Partition { - Package1SafeMain, - Package1SafeSub, - Package1RepairMain, - Package1RepairSub, - Count, -}; - -enum class Package2Partition { - BootConfig, - Package2, - Count, -}; - -struct Boot0Meta { - using EnumType = Boot0Partition; - using OffsetSizeType = OffsetSizeEntry; - - static constexpr size_t NumEntries = static_cast(EnumType::Count); - static constexpr OffsetSizeType Entries[NumEntries] = { - {Boot0Partition::BctNormalMain, 0 * BctSize, BctSize}, - {Boot0Partition::BctSafeMain, 1 * BctSize, BctSize}, - {Boot0Partition::BctNormalSub, 2 * BctSize, BctSize}, - {Boot0Partition::BctSafeSub, 3 * BctSize, BctSize}, - {Boot0Partition::BctSave, 63 * BctSize, BctSize}, - {Boot0Partition::Package1NormalMain, 0x100000, 0x40000}, - {Boot0Partition::Package1NormalSub, 0x140000, 0x40000}, - {Boot0Partition::Eks, 0x180000, EksSize}, - }; -}; - -struct Boot1Meta { - using EnumType = Boot1Partition; - using OffsetSizeType = OffsetSizeEntry; - - static constexpr size_t NumEntries = static_cast(EnumType::Count); - static constexpr OffsetSizeType Entries[NumEntries] = { - {Boot1Partition::Package1SafeMain, 0x00000, 0x40000}, - {Boot1Partition::Package1SafeSub, 0x40000, 0x40000}, - {Boot1Partition::Package1RepairMain, 0x80000, 0x40000}, - {Boot1Partition::Package1RepairSub, 0xC0000, 0x40000}, - }; -}; - -struct Package2Meta { - using EnumType = Package2Partition; - using OffsetSizeType = OffsetSizeEntry; - - static constexpr size_t NumEntries = static_cast(EnumType::Count); - static constexpr OffsetSizeType Entries[NumEntries] = { - {Package2Partition::BootConfig, 0x0000, 0x004000}, - {Package2Partition::Package2, 0x4000, 0x7FC000}, - }; -}; - -template -class PartitionAccessor : public BisAccessor { - public: - using EnumType = typename Meta::EnumType; - using OffsetSizeType = typename Meta::OffsetSizeType; - public: - PartitionAccessor(FsBisStorageId id) : BisAccessor(id) { } - private: - constexpr const OffsetSizeType *FindEntry(EnumType which) { - for (size_t i = 0; i < Meta::NumEntries; i++) { - if (Meta::Entries[i].which == which) { - return &Meta::Entries[i]; + class BisAccessor { + public: + static constexpr size_t SectorAlignment = 0x200; + private: + FsStorage storage = {}; + FsBisStorageId partition_id; + bool active; + public: + BisAccessor(FsBisStorageId id) : partition_id(id), active(false) { } + ~BisAccessor() { + if (this->active) { + fsStorageClose(&storage); } } - std::abort(); - } - public: - Result Read(size_t *out_size, void *dst, size_t size, EnumType which) { - const auto entry = FindEntry(which); - if (size < entry->size) { + + public: + Result Initialize(); + void Finalize(); + protected: + Result Read(void *dst, size_t size, u64 offset); + Result Write(u64 offset, const void *src, size_t size); + Result Write(u64 offset, size_t size, const char *bip_path, void *work_buffer, size_t work_buffer_size); + Result Clear(u64 offset, u64 size, void *work_buffer, size_t work_buffer_size); + Result GetHash(void *dst, u64 offset, u64 size, u64 hash_size, void *work_buffer, size_t work_buffer_size); + }; + + template + struct OffsetSizeEntry { + EnumType which; + u64 offset; + size_t size; + }; + + enum class Boot0Partition { + BctNormalMain, + BctSafeMain, + BctNormalSub, + BctSafeSub, + BctSave, + Package1NormalMain, + Package1NormalSub, + Eks, + Count, + }; + + enum class Boot1Partition { + Package1SafeMain, + Package1SafeSub, + Package1RepairMain, + Package1RepairSub, + Count, + }; + + enum class Package2Partition { + BootConfig, + Package2, + Count, + }; + + struct Boot0Meta { + using EnumType = Boot0Partition; + using OffsetSizeType = OffsetSizeEntry; + + static constexpr size_t NumEntries = static_cast(EnumType::Count); + static constexpr OffsetSizeType Entries[NumEntries] = { + {Boot0Partition::BctNormalMain, 0 * BctSize, BctSize}, + {Boot0Partition::BctSafeMain, 1 * BctSize, BctSize}, + {Boot0Partition::BctNormalSub, 2 * BctSize, BctSize}, + {Boot0Partition::BctSafeSub, 3 * BctSize, BctSize}, + {Boot0Partition::BctSave, 63 * BctSize, BctSize}, + {Boot0Partition::Package1NormalMain, 0x100000, 0x40000}, + {Boot0Partition::Package1NormalSub, 0x140000, 0x40000}, + {Boot0Partition::Eks, 0x180000, EksSize}, + }; + }; + + struct Boot1Meta { + using EnumType = Boot1Partition; + using OffsetSizeType = OffsetSizeEntry; + + static constexpr size_t NumEntries = static_cast(EnumType::Count); + static constexpr OffsetSizeType Entries[NumEntries] = { + {Boot1Partition::Package1SafeMain, 0x00000, 0x40000}, + {Boot1Partition::Package1SafeSub, 0x40000, 0x40000}, + {Boot1Partition::Package1RepairMain, 0x80000, 0x40000}, + {Boot1Partition::Package1RepairSub, 0xC0000, 0x40000}, + }; + }; + + struct Package2Meta { + using EnumType = Package2Partition; + using OffsetSizeType = OffsetSizeEntry; + + static constexpr size_t NumEntries = static_cast(EnumType::Count); + static constexpr OffsetSizeType Entries[NumEntries] = { + {Package2Partition::BootConfig, 0x0000, 0x004000}, + {Package2Partition::Package2, 0x4000, 0x7FC000}, + }; + }; + + template + class PartitionAccessor : public BisAccessor { + public: + using EnumType = typename Meta::EnumType; + using OffsetSizeType = typename Meta::OffsetSizeType; + public: + PartitionAccessor(FsBisStorageId id) : BisAccessor(id) { } + private: + constexpr const OffsetSizeType *FindEntry(EnumType which) { + for (size_t i = 0; i < Meta::NumEntries; i++) { + if (Meta::Entries[i].which == which) { + return &Meta::Entries[i]; + } + } std::abort(); } + public: + Result Read(size_t *out_size, void *dst, size_t size, EnumType which) { + const auto entry = FindEntry(which); + if (size < entry->size) { + std::abort(); + } - R_TRY(BisAccessor::Read(dst, entry->size, entry->offset)); + R_TRY(BisAccessor::Read(dst, entry->size, entry->offset)); - *out_size = entry->size; - return ResultSuccess; - } - - Result Write(const void *src, size_t size, EnumType which) { - const auto entry = FindEntry(which); - if (size > entry->size || size % BisAccessor::SectorAlignment != 0) { - std::abort(); + *out_size = entry->size; + return ResultSuccess; } - return BisAccessor::Write(entry->offset, src, size); + Result Write(const void *src, size_t size, EnumType which) { + const auto entry = FindEntry(which); + if (size > entry->size || size % BisAccessor::SectorAlignment != 0) { + std::abort(); + } + + return BisAccessor::Write(entry->offset, src, size); + } + + Result Write(const char *bip_path, void *work_buffer, size_t work_buffer_size, EnumType which) { + const auto entry = FindEntry(which); + return BisAccessor::Write(entry->offset, entry->size, bip_path, work_buffer, work_buffer_size); + } + + Result Clear(void *work_buffer, size_t work_buffer_size, EnumType which) { + const auto entry = FindEntry(which); + return BisAccessor::Clear(entry->offset, entry->size, work_buffer, work_buffer_size); + } + + Result GetHash(void *dst, u64 hash_size, void *work_buffer, size_t work_buffer_size, EnumType which) { + const auto entry = FindEntry(which); + return BisAccessor::GetHash(dst, entry->offset, entry->size, hash_size, work_buffer, work_buffer_size); + } + }; + + enum class Package2Type { + NormalMain, + NormalSub, + SafeMain, + SafeSub, + RepairMain, + RepairSub, + }; + + static constexpr FsBisStorageId GetPackage2StorageId(Package2Type which) { + switch (which) { + case Package2Type::NormalMain: + return FsBisStorageId_BootConfigAndPackage2NormalMain; + case Package2Type::NormalSub: + return FsBisStorageId_BootConfigAndPackage2NormalSub; + case Package2Type::SafeMain: + return FsBisStorageId_BootConfigAndPackage2SafeMain; + case Package2Type::SafeSub: + return FsBisStorageId_BootConfigAndPackage2SafeSub; + case Package2Type::RepairMain: + return FsBisStorageId_BootConfigAndPackage2RepairMain; + case Package2Type::RepairSub: + return FsBisStorageId_BootConfigAndPackage2RepairSub; + default: + std::abort(); } - - Result Write(const char *bip_path, void *work_buffer, size_t work_buffer_size, EnumType which) { - const auto entry = FindEntry(which); - return BisAccessor::Write(entry->offset, entry->size, bip_path, work_buffer, work_buffer_size); - } - - Result Clear(void *work_buffer, size_t work_buffer_size, EnumType which) { - const auto entry = FindEntry(which); - return BisAccessor::Clear(entry->offset, entry->size, work_buffer, work_buffer_size); - } - - Result GetHash(void *dst, u64 hash_size, void *work_buffer, size_t work_buffer_size, EnumType which) { - const auto entry = FindEntry(which); - return BisAccessor::GetHash(dst, entry->offset, entry->size, hash_size, work_buffer, work_buffer_size); - } -}; - -enum class Package2Type { - NormalMain, - NormalSub, - SafeMain, - SafeSub, - RepairMain, - RepairSub, -}; - -static constexpr FsBisStorageId GetPackage2StorageId(Package2Type which) { - switch (which) { - case Package2Type::NormalMain: - return FsBisStorageId_BootConfigAndPackage2NormalMain; - case Package2Type::NormalSub: - return FsBisStorageId_BootConfigAndPackage2NormalSub; - case Package2Type::SafeMain: - return FsBisStorageId_BootConfigAndPackage2SafeMain; - case Package2Type::SafeSub: - return FsBisStorageId_BootConfigAndPackage2SafeSub; - case Package2Type::RepairMain: - return FsBisStorageId_BootConfigAndPackage2RepairMain; - case Package2Type::RepairSub: - return FsBisStorageId_BootConfigAndPackage2RepairSub; - default: - std::abort(); } + + class Boot0Accessor : public PartitionAccessor { + public: + static constexpr FsBisStorageId PartitionId = FsBisStorageId_Boot0; + static constexpr size_t BctPubkOffset = 0x210; + static constexpr size_t BctPubkSize = 0x100; + static constexpr size_t BctEksOffset = 0x450; + static constexpr size_t BctVersionOffset = 0x2330; + static constexpr size_t BctVersionMax = 0x20; + public: + Boot0Accessor() : PartitionAccessor(PartitionId) { } + private: + static size_t GetBootloaderVersion(void *bct); + static size_t GetEksIndex(size_t bootloader_version); + static void CopyEks(void *dst_bct, const void *src_eks, size_t eks_index); + public: + Result UpdateEks(void *dst_bct, void *eks_work_buffer); + Result UpdateEksManually(void *dst_bct, const void *src_eks); + Result PreserveAutoRcm(void *dst_bct, void *work_buffer, Boot0Partition which); + }; + + class Boot1Accessor : public PartitionAccessor { + public: + static constexpr FsBisStorageId PartitionId = FsBisStorageId_Boot1; + public: + Boot1Accessor() : PartitionAccessor(PartitionId) { } + }; + + class Package2Accessor : public PartitionAccessor { + public: + Package2Accessor(Package2Type which) : PartitionAccessor(GetPackage2StorageId(which)) { } + }; + } - -class Boot0Accessor : public PartitionAccessor { - public: - static constexpr FsBisStorageId PartitionId = FsBisStorageId_Boot0; - static constexpr size_t BctPubkOffset = 0x210; - static constexpr size_t BctPubkSize = 0x100; - static constexpr size_t BctEksOffset = 0x450; - static constexpr size_t BctVersionOffset = 0x2330; - static constexpr size_t BctVersionMax = 0x20; - public: - Boot0Accessor() : PartitionAccessor(PartitionId) { } - private: - static size_t GetBootloaderVersion(void *bct); - static size_t GetEksIndex(size_t bootloader_version); - static void CopyEks(void *dst_bct, const void *src_eks, size_t eks_index); - public: - Result UpdateEks(void *dst_bct, void *eks_work_buffer); - Result UpdateEksManually(void *dst_bct, const void *src_eks); - Result PreserveAutoRcm(void *dst_bct, void *work_buffer, Boot0Partition which); -}; - -class Boot1Accessor : public PartitionAccessor { - public: - static constexpr FsBisStorageId PartitionId = FsBisStorageId_Boot1; - public: - Boot1Accessor() : PartitionAccessor(PartitionId) { } -}; - -class Package2Accessor : public PartitionAccessor { - public: - Package2Accessor(Package2Type which) : PartitionAccessor(GetPackage2StorageId(which)) { } -}; - diff --git a/stratosphere/boot/source/updater/updater_bis_save.cpp b/stratosphere/boot/source/updater/updater_bis_save.cpp index 0df33affc..a06158763 100644 --- a/stratosphere/boot/source/updater/updater_bis_save.cpp +++ b/stratosphere/boot/source/updater/updater_bis_save.cpp @@ -19,44 +19,48 @@ #include "updater_bis_save.hpp" -size_t BisSave::GetVerificationFlagOffset(BootModeType mode) { - switch (mode) { - case BootModeType_Normal: - return 0; - case BootModeType_Safe: - return 1; - default: - return 2; - } -} +namespace sts::updater { -Result BisSave::Initialize(void *work_buffer, size_t work_buffer_size) { - if (work_buffer_size < SaveSize || reinterpret_cast(work_buffer) & 0xFFF || work_buffer_size & 0x1FF) { - std::abort(); + size_t BisSave::GetVerificationFlagOffset(BootModeType mode) { + switch (mode) { + case BootModeType::Normal: + return 0; + case BootModeType::Safe: + return 1; + default: + return 2; + } } - R_TRY(this->accessor.Initialize()); - this->save_buffer = work_buffer; - return ResultSuccess; -} + Result BisSave::Initialize(void *work_buffer, size_t work_buffer_size) { + if (work_buffer_size < SaveSize || reinterpret_cast(work_buffer) & 0xFFF || work_buffer_size & 0x1FF) { + std::abort(); + } -void BisSave::Finalize() { - this->accessor.Finalize(); -} + R_TRY(this->accessor.Initialize()); + this->save_buffer = work_buffer; + return ResultSuccess; + } -Result BisSave::Load() { - size_t read_size; - return this->accessor.Read(&read_size, this->save_buffer, SaveSize, Boot0Partition::BctSave); -} + void BisSave::Finalize() { + this->accessor.Finalize(); + } -Result BisSave::Save() { - return this->accessor.Write(this->save_buffer, SaveSize, Boot0Partition::BctSave); -} + Result BisSave::Load() { + size_t read_size; + return this->accessor.Read(&read_size, this->save_buffer, SaveSize, Boot0Partition::BctSave); + } -bool BisSave::GetNeedsVerification(BootModeType mode) { - return reinterpret_cast(this->save_buffer)[GetVerificationFlagOffset(mode)] != 0; -} + Result BisSave::Save() { + return this->accessor.Write(this->save_buffer, SaveSize, Boot0Partition::BctSave); + } -void BisSave::SetNeedsVerification(BootModeType mode, bool needs_verification) { - reinterpret_cast(this->save_buffer)[GetVerificationFlagOffset(mode)] = needs_verification ? 1 : 0; -} \ No newline at end of file + bool BisSave::GetNeedsVerification(BootModeType mode) { + return reinterpret_cast(this->save_buffer)[GetVerificationFlagOffset(mode)] != 0; + } + + void BisSave::SetNeedsVerification(BootModeType mode, bool needs_verification) { + reinterpret_cast(this->save_buffer)[GetVerificationFlagOffset(mode)] = needs_verification ? 1 : 0; + } + +} diff --git a/stratosphere/boot/source/updater/updater_bis_save.hpp b/stratosphere/boot/source/updater/updater_bis_save.hpp index 574398304..fdddca2dc 100644 --- a/stratosphere/boot/source/updater/updater_bis_save.hpp +++ b/stratosphere/boot/source/updater/updater_bis_save.hpp @@ -21,22 +21,26 @@ #include "updater_types.hpp" #include "updater_bis_management.hpp" -class BisSave { - public: - static constexpr size_t SaveSize = BctSize; - private: - Boot0Accessor accessor; - void *save_buffer; - public: - BisSave() : save_buffer(nullptr) { } - private: - static size_t GetVerificationFlagOffset(BootModeType mode); - public: - Result Initialize(void *work_buffer, size_t work_buffer_size); - void Finalize(); +namespace sts::updater { - Result Load(); - Result Save(); - bool GetNeedsVerification(BootModeType mode); - void SetNeedsVerification(BootModeType mode, bool needs_verification); -}; + class BisSave { + public: + static constexpr size_t SaveSize = BctSize; + private: + Boot0Accessor accessor; + void *save_buffer; + public: + BisSave() : save_buffer(nullptr) { } + private: + static size_t GetVerificationFlagOffset(BootModeType mode); + public: + Result Initialize(void *work_buffer, size_t work_buffer_size); + void Finalize(); + + Result Load(); + Result Save(); + bool GetNeedsVerification(BootModeType mode); + void SetNeedsVerification(BootModeType mode, bool needs_verification); + }; + +} diff --git a/stratosphere/boot/source/updater/updater_files.cpp b/stratosphere/boot/source/updater/updater_files.cpp index 7c05a0089..759d4abb3 100644 --- a/stratosphere/boot/source/updater/updater_files.cpp +++ b/stratosphere/boot/source/updater/updater_files.cpp @@ -19,50 +19,54 @@ #include "updater_api.hpp" -Result Updater::ReadFile(size_t *out_size, void *dst, size_t dst_size, const char *path) { - FILE *fp = fopen(path, "rb"); - if (fp == NULL) { - return ResultUpdaterInvalidBootImagePackage; - } - ON_SCOPE_EXIT { fclose(fp); }; +namespace sts::updater { - std::memset(dst, 0, dst_size); - size_t read_size = fread(dst, 1, dst_size, fp); - if (ferror(fp)) { - return fsdevGetLastResult(); - } - *out_size = read_size; - return ResultSuccess; -} + Result ReadFile(size_t *out_size, void *dst, size_t dst_size, const char *path) { + FILE *fp = fopen(path, "rb"); + if (fp == NULL) { + return ResultUpdaterInvalidBootImagePackage; + } + ON_SCOPE_EXIT { fclose(fp); }; -Result Updater::GetFileHash(size_t *out_size, void *dst_hash, const char *path, void *work_buffer, size_t work_buffer_size) { - FILE *fp = fopen(path, "rb"); - if (fp == NULL) { - return ResultUpdaterInvalidBootImagePackage; - } - ON_SCOPE_EXIT { fclose(fp); }; - - Sha256Context sha_ctx; - sha256ContextCreate(&sha_ctx); - - size_t total_size = 0; - while (true) { - size_t read_size = fread(work_buffer, 1, work_buffer_size, fp); + std::memset(dst, 0, dst_size); + size_t read_size = fread(dst, 1, dst_size, fp); if (ferror(fp)) { return fsdevGetLastResult(); } - if (read_size == 0) { - break; - } - - sha256ContextUpdate(&sha_ctx, work_buffer, read_size); - total_size += read_size; - if (read_size != work_buffer_size) { - break; - } + *out_size = read_size; + return ResultSuccess; + } + + Result GetFileHash(size_t *out_size, void *dst_hash, const char *path, void *work_buffer, size_t work_buffer_size) { + FILE *fp = fopen(path, "rb"); + if (fp == NULL) { + return ResultUpdaterInvalidBootImagePackage; + } + ON_SCOPE_EXIT { fclose(fp); }; + + Sha256Context sha_ctx; + sha256ContextCreate(&sha_ctx); + + size_t total_size = 0; + while (true) { + size_t read_size = fread(work_buffer, 1, work_buffer_size, fp); + if (ferror(fp)) { + return fsdevGetLastResult(); + } + if (read_size == 0) { + break; + } + + sha256ContextUpdate(&sha_ctx, work_buffer, read_size); + total_size += read_size; + if (read_size != work_buffer_size) { + break; + } + } + + sha256ContextGetHash(&sha_ctx, dst_hash); + *out_size = total_size; + return ResultSuccess; } - sha256ContextGetHash(&sha_ctx, dst_hash); - *out_size = total_size; - return ResultSuccess; } diff --git a/stratosphere/boot/source/updater/updater_files.hpp b/stratosphere/boot/source/updater/updater_files.hpp new file mode 100644 index 000000000..0c1fb320e --- /dev/null +++ b/stratosphere/boot/source/updater/updater_files.hpp @@ -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 . + */ + +#pragma once +#include +#include + +#include "updater_types.hpp" + +namespace sts::updater { + + /* File helpers. */ + Result ReadFile(size_t *out_size, void *dst, size_t dst_size, const char *path); + Result GetFileHash(size_t *out_size, void *dst_hash, const char *path, void *work_buffer, size_t work_buffer_size); + +} diff --git a/stratosphere/boot/source/updater/updater_paths.cpp b/stratosphere/boot/source/updater/updater_paths.cpp index 7a83cd2fa..3fa4920b6 100644 --- a/stratosphere/boot/source/updater/updater_paths.cpp +++ b/stratosphere/boot/source/updater/updater_paths.cpp @@ -18,89 +18,102 @@ #include #include -#include "updater_api.hpp" +#include "updater_paths.hpp" -static const char *BootImagePackageMountPath = "bip"; -static const char *BctPathNx = "bip:/nx/bct"; -static const char *Package1PathNx = "bip:/nx/package1"; -static const char *Package2PathNx = "bip:/nx/package2"; -static const char *BctPathA = "bip:/a/bct"; -static const char *Package1PathA = "bip:/a/package1"; -static const char *Package2PathA = "bip:/a/package2"; +namespace sts::updater { -const char *Updater::GetBootImagePackageMountPath() { - return BootImagePackageMountPath; -} + namespace { -static const char *ChooseCandidatePath(const char **candidates, size_t num_candidates) { - if (num_candidates == 0) { - std::abort(); - } + /* Actual paths. */ + constexpr const char *BootImagePackageMountPath = "bip"; + constexpr const char *BctPathNx = "bip:/nx/bct"; + constexpr const char *Package1PathNx = "bip:/nx/package1"; + constexpr const char *Package2PathNx = "bip:/nx/package2"; + constexpr const char *BctPathA = "bip:/a/bct"; + constexpr const char *Package1PathA = "bip:/a/package1"; + constexpr const char *Package2PathA = "bip:/a/package2"; - for (size_t i = 0; i < num_candidates; i++) { - struct stat buf; - if (stat(candidates[i], &buf) != 0) { - continue; + const char *ChooseCandidatePath(const char * const *candidates, size_t num_candidates) { + if (num_candidates == 0) { + std::abort(); + } + + for (size_t i = 0; i < num_candidates; i++) { + struct stat buf; + if (stat(candidates[i], &buf) != 0) { + continue; + } + + if (!S_ISREG(buf.st_mode)) { + continue; + } + + return candidates[i]; + } + + /* Nintendo just uses the last candidate if they all fail...should we abort? */ + return candidates[num_candidates - 1]; } - if (!S_ISREG(buf.st_mode)) { - continue; + } + + const char *GetBootImagePackageMountPath() { + return BootImagePackageMountPath; + } + + + const char *GetBctPath(BootImageUpdateType boot_image_update_type) { + switch (boot_image_update_type) { + case BootImageUpdateType::Erista: + { + constexpr const char *candidates[] = {BctPathNx}; + return ChooseCandidatePath(candidates, sizeof(candidates) / sizeof(candidates[0])); + } + case BootImageUpdateType::Mariko: + { + constexpr const char *candidates[] = {BctPathA, BctPathNx}; + return ChooseCandidatePath(candidates, sizeof(candidates) / sizeof(candidates[0])); + } + default: + std::abort(); + } + } + + const char *GetPackage1Path(BootImageUpdateType boot_image_update_type) { + switch (boot_image_update_type) { + case BootImageUpdateType::Erista: + { + constexpr const char *candidates[] = {Package1PathNx}; + return ChooseCandidatePath(candidates, sizeof(candidates) / sizeof(candidates[0])); + } + case BootImageUpdateType::Mariko: + { + constexpr const char *candidates[] = {Package1PathA, Package1PathNx}; + return ChooseCandidatePath(candidates, sizeof(candidates) / sizeof(candidates[0])); + } + default: + std::abort(); + } + } + + const char *GetPackage2Path(BootImageUpdateType boot_image_update_type) { + switch (boot_image_update_type) { + case BootImageUpdateType::Erista: + { + constexpr const char *candidates[] = {Package2PathNx}; + return ChooseCandidatePath(candidates, sizeof(candidates) / sizeof(candidates[0])); + } + case BootImageUpdateType::Mariko: + { + constexpr const char *candidates[] = {Package2PathA, Package2PathNx}; + return ChooseCandidatePath(candidates, sizeof(candidates) / sizeof(candidates[0])); + } + default: + std::abort(); } - - return candidates[i]; } - /* Nintendo just uses the last candidate if they all fail...should we abort? */ - return candidates[num_candidates - 1]; } -const char *Updater::GetBctPath(BootImageUpdateType boot_image_update_type) { - switch (boot_image_update_type) { - case BootImageUpdateType_Erista: - { - const char *candidates[] = {BctPathNx}; - return ChooseCandidatePath(candidates, sizeof(candidates) / sizeof(candidates[0])); - } - case BootImageUpdateType_Mariko: - { - const char *candidates[] = {BctPathA, BctPathNx}; - return ChooseCandidatePath(candidates, sizeof(candidates) / sizeof(candidates[0])); - } - default: - std::abort(); - } -} -const char *Updater::GetPackage1Path(BootImageUpdateType boot_image_update_type) { - switch (boot_image_update_type) { - case BootImageUpdateType_Erista: - { - const char *candidates[] = {Package1PathNx}; - return ChooseCandidatePath(candidates, sizeof(candidates) / sizeof(candidates[0])); - } - case BootImageUpdateType_Mariko: - { - const char *candidates[] = {Package1PathA, Package1PathNx}; - return ChooseCandidatePath(candidates, sizeof(candidates) / sizeof(candidates[0])); - } - default: - std::abort(); - } -} -const char *Updater::GetPackage2Path(BootImageUpdateType boot_image_update_type) { - switch (boot_image_update_type) { - case BootImageUpdateType_Erista: - { - const char *candidates[] = {Package2PathNx}; - return ChooseCandidatePath(candidates, sizeof(candidates) / sizeof(candidates[0])); - } - case BootImageUpdateType_Mariko: - { - const char *candidates[] = {Package2PathA, Package2PathNx}; - return ChooseCandidatePath(candidates, sizeof(candidates) / sizeof(candidates[0])); - } - default: - std::abort(); - } -} \ No newline at end of file diff --git a/stratosphere/boot/source/updater/updater_paths.hpp b/stratosphere/boot/source/updater/updater_paths.hpp new file mode 100644 index 000000000..2197b35b1 --- /dev/null +++ b/stratosphere/boot/source/updater/updater_paths.hpp @@ -0,0 +1,31 @@ +/* + * 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 "updater_types.hpp" + +namespace sts::updater { + + /* Path functionality. */ + const char *GetBootImagePackageMountPath(); + const char *GetBctPath(BootImageUpdateType boot_image_update_type); + const char *GetPackage1Path(BootImageUpdateType boot_image_update_type); + const char *GetPackage2Path(BootImageUpdateType boot_image_update_type); + +} diff --git a/stratosphere/boot/source/updater/updater_types.hpp b/stratosphere/boot/source/updater/updater_types.hpp index c50b7f611..af5cca8c3 100644 --- a/stratosphere/boot/source/updater/updater_types.hpp +++ b/stratosphere/boot/source/updater/updater_types.hpp @@ -21,22 +21,28 @@ /* TODO: Better way to do this? */ #include "../boot_types.hpp" -enum BootImageUpdateType { - BootImageUpdateType_Erista, - BootImageUpdateType_Mariko, -}; +namespace sts::updater { -enum BootModeType { - BootModeType_Normal, - BootModeType_Safe, -}; + /* Types. */ + enum class BootImageUpdateType { + Erista, + Mariko, + }; -static constexpr size_t BctSize = 0x4000; -static constexpr size_t EksSize = 0x4000; -static constexpr size_t EksEntrySize = 0x200; -static constexpr size_t EksBlobSize = 0xB0; + enum class BootModeType { + Normal, + Safe, + }; -struct VerificationState { - bool needs_verify_normal; - bool needs_verify_safe; -}; \ No newline at end of file + struct VerificationState { + bool needs_verify_normal; + bool needs_verify_safe; + }; + + /* Convenience size definitions. */ + constexpr size_t BctSize = 0x4000; + constexpr size_t EksSize = 0x4000; + constexpr size_t EksEntrySize = 0x200; + constexpr size_t EksBlobSize = 0xB0; + +} \ No newline at end of file