fusee_cpp: implement ips patching of kips

This commit is contained in:
Michael Scire 2021-09-03 00:40:26 -07:00 committed by SciresM
parent 07779b787a
commit c5d021c172
7 changed files with 253 additions and 42 deletions

View file

@ -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)

View file

@ -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);

View file

@ -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();

View file

@ -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<u8 *>(GetSecondaryArchive().ovl_mtc_erista), GetOverlayDestination(), sizeof(SecondaryArchive{}.ovl_mtc_erista)); */
} else {
std::memcpy(const_cast<u8 *>(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));
}
}
}

View file

@ -26,4 +26,7 @@ namespace ams::nxboot {
void LoadOverlay(fs::FileHandle archive_file, OverlayId ovl);
void SaveMemoryTrainingOverlay();
void RestoreMemoryTrainingOverlay();
}

View file

@ -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;
}
}
}

View file

@ -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<PatchMeta *>(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<u32>(ips[0]) << 16) | (static_cast<u32>(ips[1]) << 8) | (static_cast<u32>(ips[2]) << 0);
if (offset == 0x454F46) {
break;
}
/* Read size. */
const u16 cur_size = (static_cast<u32>(ips[3]) << 8) | (static_cast<u32>(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<u32>(ips[5]) << 8) | (static_cast<u32>(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<u32>(ips[0]) << 24) | (static_cast<u32>(ips[1]) << 16) | (static_cast<u32>(ips[2]) << 8) | (static_cast<u32>(ips[3]) << 0);
if (offset == 0x45454F46) {
break;
}
/* Read size. */
const u16 cur_size = (static_cast<u32>(ips[4]) << 8) | (static_cast<u32>(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<u32>(ips[6]) << 8) | (static_cast<u32>(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<size_t>(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<InitialProcessHeader *>(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);
}
s64 file_size;
if (InitialProcessHeader *kip = static_cast<InitialProcessHeader *>(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<u8 *>(ReadFile(std::addressof(file_size), patch_path)); ips != nullptr) {
AddIpsPatchToKip(kip_meta, ips, static_cast<s32>(file_size));
}
}
}
}
}
}
}
/* Return the fs version we're using. */