From c5d021c172e56fd01d02c2dd093064f0d3629a05 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 3 Sep 2021 00:40:26 -0700 Subject: [PATCH] fusee_cpp: implement ips patching of kips --- fusee_cpp/program/program.ld | 12 - fusee_cpp/program/program_ovl.ld | 4 + fusee_cpp/program/source/fusee_main.cpp | 9 + .../program/source/fusee_overlay_manager.cpp | 18 ++ .../program/source/fusee_overlay_manager.hpp | 3 + .../program/source/fusee_setup_horizon.cpp | 6 +- .../program/source/fusee_stratosphere.cpp | 243 ++++++++++++++++-- 7 files changed, 253 insertions(+), 42 deletions(-) diff --git a/fusee_cpp/program/program.ld b/fusee_cpp/program/program.ld index 0bc3f3a5b..8dc8e938c 100644 --- a/fusee_cpp/program/program.ld +++ b/fusee_cpp/program/program.ld @@ -17,18 +17,6 @@ SECTIONS BYTE(00); } >main AT>glob - .ovl_data : - { - FILL(0x00000000) - fusee_mtc_erista.o(SORT(.data*)); - fusee_mtc_erista.o(SORT(.bss*)); - fusee_mtc_mariko.o(SORT(.data*)); - fusee_mtc_mariko.o(SORT(.bss*)); - . = ALIGN(16); - . = . + 15; - BYTE(00); - } >main AT>glob - .text : { FILL(0x00000000) diff --git a/fusee_cpp/program/program_ovl.ld b/fusee_cpp/program/program_ovl.ld index e0e27de9f..db7fcb3f6 100644 --- a/fusee_cpp/program/program_ovl.ld +++ b/fusee_cpp/program/program_ovl.ld @@ -26,6 +26,8 @@ SECTIONS { KEEP(*(.text._ZN3ams6nxboot22DoMemoryTrainingEristaEiPv)) fusee_mtc_erista.o(.text*); fusee_mtc_erista.o(SORT(.rodata*)); + fusee_mtc_erista.o(SORT(.data*)); + fusee_mtc_erista.o(SORT(.bss*)); FILL(0x00000000) . = ORIGIN(ovl) + LENGTH(ovl) - 1; BYTE(0x00); @@ -34,6 +36,8 @@ SECTIONS { KEEP(*(.text._ZN3ams6nxboot22DoMemoryTrainingMarikoEPbiPv)) fusee_mtc_mariko.o(.text*); fusee_mtc_mariko.o(SORT(.rodata*)); + fusee_mtc_mariko.o(SORT(.data*)); + fusee_mtc_mariko.o(SORT(.bss*)); FILL(0x00000000) . = ORIGIN(ovl) + LENGTH(ovl) - 1; BYTE(0x00); diff --git a/fusee_cpp/program/source/fusee_main.cpp b/fusee_cpp/program/source/fusee_main.cpp index c0fce8e6a..6fbbd07d9 100644 --- a/fusee_cpp/program/source/fusee_main.cpp +++ b/fusee_cpp/program/source/fusee_main.cpp @@ -110,6 +110,9 @@ namespace ams::nxboot { /* Read the rest of the archive file. */ ReadFullSecondaryArchive(); + /* Save the memory training overlay. */ + SaveMemoryTrainingOverlay(); + /* Initialize display (splash screen will be visible from this point onwards). */ InitializeDisplay(); ShowDisplay(); @@ -120,6 +123,12 @@ namespace ams::nxboot { /* Perform rest of the boot process. */ SetupAndStartHorizon(); + /* Restore the memory training overlay. */ + RestoreMemoryTrainingOverlay(); + + /* Restore memory clock rate. */ + RestoreMemoryClockRate(); + /* Finalize display. */ FinalizeDisplay(); diff --git a/fusee_cpp/program/source/fusee_overlay_manager.cpp b/fusee_cpp/program/source/fusee_overlay_manager.cpp index 3cd654eaa..06d2a5bc3 100644 --- a/fusee_cpp/program/source/fusee_overlay_manager.cpp +++ b/fusee_cpp/program/source/fusee_overlay_manager.cpp @@ -49,4 +49,22 @@ namespace ams::nxboot { } } + void SaveMemoryTrainingOverlay() { + if (fuse::GetSocType() == fuse::SocType_Erista) { + /* NOTE: Erista does not do memory clock restoration. */ + /* std::memcpy(const_cast(GetSecondaryArchive().ovl_mtc_erista), GetOverlayDestination(), sizeof(SecondaryArchive{}.ovl_mtc_erista)); */ + } else { + std::memcpy(const_cast(GetSecondaryArchive().ovl_mtc_mariko), GetOverlayDestination(), sizeof(SecondaryArchive{}.ovl_mtc_mariko)); + } + } + + void RestoreMemoryTrainingOverlay() { + if (fuse::GetSocType() == fuse::SocType_Erista) { + /* NOTE: Erista does not do memory clock restoration. */ + /* std::memcpy(GetOverlayDestination(), GetSecondaryArchive().ovl_mtc_erista, sizeof(SecondaryArchive{}.ovl_mtc_erista)); */ + } else { + std::memcpy(GetOverlayDestination(), GetSecondaryArchive().ovl_mtc_mariko, sizeof(SecondaryArchive{}.ovl_mtc_mariko)); + } + } + } diff --git a/fusee_cpp/program/source/fusee_overlay_manager.hpp b/fusee_cpp/program/source/fusee_overlay_manager.hpp index d1d9b1212..2d842f80f 100644 --- a/fusee_cpp/program/source/fusee_overlay_manager.hpp +++ b/fusee_cpp/program/source/fusee_overlay_manager.hpp @@ -26,4 +26,7 @@ namespace ams::nxboot { void LoadOverlay(fs::FileHandle archive_file, OverlayId ovl); + void SaveMemoryTrainingOverlay(); + void RestoreMemoryTrainingOverlay(); + } diff --git a/fusee_cpp/program/source/fusee_setup_horizon.cpp b/fusee_cpp/program/source/fusee_setup_horizon.cpp index e7ff63240..d00b9fabd 100644 --- a/fusee_cpp/program/source/fusee_setup_horizon.cpp +++ b/fusee_cpp/program/source/fusee_setup_horizon.cpp @@ -85,11 +85,11 @@ namespace ams::nxboot { x <<= 4; if ('0' <= c && c <= '9') { - x |= c - '0'; + x |= (c - '0'); } else if ('a' <= c && c <= 'f') { - x |= c - 'a'; + x |= (c - 'a') + 10; } else if ('A' <= c && c <= 'F') { - x |= c - 'A'; + x |= (c - 'A') + 10; } } } diff --git a/fusee_cpp/program/source/fusee_stratosphere.cpp b/fusee_cpp/program/source/fusee_stratosphere.cpp index 84591da3f..9648405ff 100644 --- a/fusee_cpp/program/source/fusee_stratosphere.cpp +++ b/fusee_cpp/program/source/fusee_stratosphere.cpp @@ -65,6 +65,7 @@ namespace ams::nxboot { struct PatchMeta { PatchMeta *next; + bool is_memset; u32 start_segment; u32 rel_offset; const void *data; @@ -321,6 +322,15 @@ namespace ams::nxboot { return nullptr; } + InitialProcessMeta *FindInitialProcess(const se::Sha256Hash &hash) { + for (InitialProcessMeta *cur = std::addressof(g_initial_process_meta); cur != nullptr; cur = cur->next) { + if (std::memcmp(std::addressof(cur->kip_hash), std::addressof(hash), sizeof(hash)) == 0) { + return cur; + } + } + return nullptr; + } + u32 GetPatchSegments(const InitialProcessHeader *kip, u32 offset, size_t size) { /* Create segment mask. */ u32 segments = 0; @@ -360,7 +370,7 @@ namespace ams::nxboot { return segments; } - void AddPatch(InitialProcessMeta *meta, u32 offset, const void *data, size_t data_size) { + void AddPatch(InitialProcessMeta *meta, u32 offset, const void *data, size_t data_size, bool is_memset = false) { /* Determine the segment. */ const u32 segments = GetPatchSegments(meta->kip, offset, data_size); @@ -385,6 +395,7 @@ namespace ams::nxboot { auto *new_patch = static_cast(AllocateAligned(sizeof(PatchMeta), alignof(PatchMeta))); new_patch->next = nullptr; + new_patch->is_memset = is_memset; new_patch->start_segment = start_segment; new_patch->rel_offset = offset; new_patch->data = data; @@ -400,6 +411,78 @@ namespace ams::nxboot { meta->patches_tail = new_patch; } + void AddIps24PatchToKip(InitialProcessMeta *meta, const u8 *ips, s32 size) { + while (size > 0) { + /* Read offset, stopping at EOF */ + const u32 offset = (static_cast(ips[0]) << 16) | (static_cast(ips[1]) << 8) | (static_cast(ips[2]) << 0); + if (offset == 0x454F46) { + break; + } + + /* Read size. */ + const u16 cur_size = (static_cast(ips[3]) << 8) | (static_cast(ips[4]) << 0); + + if (cur_size > 0) { + /* Add patch. */ + AddPatch(meta, offset, ips + 5, cur_size, false); + + /* Advance. */ + ips += (5 + cur_size); + size -= (5 + cur_size); + } else { + /* Read RLE size */ + const u16 rle_size = (static_cast(ips[5]) << 8) | (static_cast(ips[6]) << 0); + + /* Add patch. */ + AddPatch(meta, offset, ips + 7, rle_size, true); + + /* Advance. */ + ips += 8; + size -= 8; + } + } + } + + void AddIps32PatchToKip(InitialProcessMeta *meta, const u8 *ips, s32 size) { + while (size > 0) { + /* Read offset, stopping at EOF */ + const u32 offset = (static_cast(ips[0]) << 24) | (static_cast(ips[1]) << 16) | (static_cast(ips[2]) << 8) | (static_cast(ips[3]) << 0); + if (offset == 0x45454F46) { + break; + } + + /* Read size. */ + const u16 cur_size = (static_cast(ips[4]) << 8) | (static_cast(ips[5]) << 0); + + if (cur_size > 0) { + /* Add patch. */ + AddPatch(meta, offset, ips + 6, cur_size, false); + + /* Advance. */ + ips += (6 + cur_size); + size -= (6 + cur_size); + } else { + /* Read RLE size */ + const u16 rle_size = (static_cast(ips[6]) << 8) | (static_cast(ips[7]) << 0); + + /* Add patch. */ + AddPatch(meta, offset, ips + 8, rle_size, true); + + /* Advance. */ + ips += 9; + size -= 9; + } + } + } + + void AddIpsPatchToKip(InitialProcessMeta *meta, const u8 *ips, s32 size) { + if (std::memcmp(ips, "PATCH", 5) == 0) { + AddIps24PatchToKip(meta, ips + 5, size - 5); + } else if (std::memcmp(ips, "IPS32", 5) == 0) { + AddIps32PatchToKip(meta, ips + 5, size - 5); + } + } + constexpr const u8 NogcPatch0[] = { 0x80 }; @@ -508,6 +591,32 @@ namespace ams::nxboot { } } + void *ReadFile(s64 *out_size, const char *path, size_t align = 0x10) { + fs::FileHandle file; + if (R_SUCCEEDED(fs::OpenFile(std::addressof(file), path, fs::OpenMode_Read))) { + ON_SCOPE_EXIT { fs::CloseFile(file); }; + + Result result; + + /* Get the kip size. */ + if (R_FAILED((result = fs::GetFileSize(out_size, file)))) { + ShowFatalError("Failed to get size (0x%08" PRIx32 ") of %s!\n", result.GetValue(), path); + } + + /* Allocate file. */ + void *data = AllocateAligned(*out_size, std::max(align, 0x10)); + + /* Read the file. */ + if (R_FAILED((result = fs::ReadFile(file, 0, data, *out_size)))) { + ShowFatalError("Failed to read (0x%08" PRIx32 ") %s!\n", result.GetValue(), path); + } + + return data; + } else { + return nullptr; + } + } + } u32 ConfigureStratosphere(const u8 *nn_package2, ams::TargetFirmware target_firmware, bool emummc_enabled, bool nogc_enabled) { @@ -524,6 +633,11 @@ namespace ams::nxboot { s64 count; fs::DirectoryEntry entries[1]; while (R_SUCCEEDED(fs::ReadDirectory(std::addressof(count), entries, kip_dir, util::size(entries))) && count > 0) { + /* Check that file is a file. */ + if (fs::GetEntryType(entries[0]) != fs::DirectoryEntryType_File) { + continue; + } + /* Get filename length. */ const int name_len = std::strlen(entries[0].file_name); @@ -537,32 +651,9 @@ namespace ams::nxboot { continue; } - /* Check that file is a file. */ - if (fs::GetEntryType(entries[0]) != fs::DirectoryEntryType_File) { - continue; - } - - /* Open the kip. */ - fs::FileHandle kip_file; - if (R_SUCCEEDED(fs::OpenFile(std::addressof(kip_file), kip_path, fs::OpenMode_Read))) { - ON_SCOPE_EXIT { fs::CloseFile(kip_file); }; - - Result result; - - /* Get the kip size. */ - s64 file_size; - if (R_FAILED((result = fs::GetFileSize(std::addressof(file_size), kip_file)))) { - ShowFatalError("Failed to get size (0x%08" PRIx32 ") of %s!\n", result.GetValue(), kip_path); - } - - /* Allocate kip. */ - InitialProcessHeader *kip = static_cast(AllocateAligned(file_size, alignof(InitialProcessHeader))); - - /* Read the kip. */ - if (R_FAILED((result = fs::ReadFile(kip_file, 0, kip, file_size)))) { - ShowFatalError("Failed to read (0x%08" PRIx32 ") %s!\n", result.GetValue(), kip_path); - } - + /* Read the kip. */ + s64 file_size; + if (InitialProcessHeader *kip = static_cast(ReadFile(std::addressof(file_size), kip_path, alignof(InitialProcessHeader))); kip != nullptr) { /* If the kip is valid, add it. */ if (kip->magic == InitialProcessHeader::Magic && file_size == GetInitialProcessSize(kip)) { AddInitialProcess(kip); @@ -630,6 +721,104 @@ namespace ams::nxboot { } /* Add generic patches. */ + { + /* Create patch path. */ + char patch_path[0x220]; + std::memcpy(patch_path, "sdmc:/atmosphere/kip_patches", 0x1D); + + fs::DirectoryHandle patch_root_dir; + if (R_SUCCEEDED(fs::OpenDirectory(std::addressof(patch_root_dir), patch_path))) { + ON_SCOPE_EXIT { fs::CloseDirectory(patch_root_dir); }; + + s64 count; + fs::DirectoryEntry entries[1]; + while (R_SUCCEEDED(fs::ReadDirectory(std::addressof(count), entries, patch_root_dir, util::size(entries))) && count > 0) { + /* Check that dir is a dir. */ + if (fs::GetEntryType(entries[0]) != fs::DirectoryEntryType_Directory) { + continue; + } + + /* For compatibility, ignore the old "default_nogc" patches. */ + if (std::strcmp(entries[0].file_name, "default_nogc") == 0) { + continue; + } + + /* Get filename length. */ + const int dir_len = std::strlen(entries[0].file_name); + + /* Adjust patch path. */ + patch_path[0x1C] = '/'; + std::memcpy(patch_path + 0x1D, entries[0].file_name, dir_len + 1); + + /* Try to open the patch subdirectory. */ + fs::DirectoryHandle patch_dir; + if (R_SUCCEEDED(fs::OpenDirectory(std::addressof(patch_dir), patch_path))) { + ON_SCOPE_EXIT { fs::CloseDirectory(patch_dir); }; + + /* Read patches. */ + while (R_SUCCEEDED(fs::ReadDirectory(std::addressof(count), entries, patch_dir, util::size(entries))) && count > 0) { + /* Check that file is a file. */ + if (fs::GetEntryType(entries[0]) != fs::DirectoryEntryType_File) { + continue; + } + + /* Get filename length. */ + const int name_len = std::strlen(entries[0].file_name); + + /* Adjust patch path. */ + patch_path[0x1D + dir_len] = '/'; + std::memcpy(patch_path + 0x1D + dir_len + 1, entries[0].file_name, name_len + 1); + + /* Check that file is "{hex}.ips" file. */ + const int path_len = 0x1D + dir_len + 1 + name_len; + if (name_len != 0x44 || std::memcmp(patch_path + path_len - 4, ".ips", 5) != 0) { + continue; + } + + /* Check that the filename is hex. */ + bool valid_name = true; + se::Sha256Hash patch_name = {}; + u32 shift = 4; + for (int i = 0; i < name_len - 4; ++i) { + const char c = entries[0].file_name[i]; + + u8 val; + if ('0' <= c && c <= '9') { + val = (c - '0'); + } else if ('a' <= c && c <= 'f') { + val = (c - 'a') + 10; + } else if ('A' <= c && c <= 'F') { + val = (c - 'A') + 10; + } else { + valid_name = false; + break; + } + + patch_name.bytes[i >> 1] |= val << shift; + shift ^= 4; + } + + /* Ignore invalid patches. */ + if (!valid_name) { + continue; + } + + /* Find kip for the patch. */ + auto *kip_meta = FindInitialProcess(patch_name); + if (kip_meta == nullptr) { + continue; + } + + /* Read the ips patch. */ + s64 file_size; + if (u8 *ips = static_cast(ReadFile(std::addressof(file_size), patch_path)); ips != nullptr) { + AddIpsPatchToKip(kip_meta, ips, static_cast(file_size)); + } + } + } + } + } + } } /* Return the fs version we're using. */