fusee_cpp: implement nogc patches

This commit is contained in:
Michael Scire 2021-09-02 23:45:51 -07:00 committed by SciresM
parent cefdda77e5
commit 07779b787a
4 changed files with 334 additions and 20 deletions

View file

@ -32,6 +32,10 @@ namespace ams::nxboot {
NORETURN void ExceptionHandlerImpl(s32 which, u32 lr, u32 svc_lr) {
/* TODO */
*reinterpret_cast<volatile u32 *>(0x40038004) = 0xCAFEBABE;
*reinterpret_cast<volatile u32 *>(0x40038008) = which;
*reinterpret_cast<volatile u32 *>(0x4003800C) = lr;
*reinterpret_cast<volatile u32 *>(0x40038010) = svc_lr;
ErrorStop();
}

View file

@ -19,7 +19,7 @@
namespace ams::nxboot {
constexpr inline const size_t SecondaryArchiveSize = 4_MB + FrameBufferSize;
constexpr inline const size_t SecondaryArchiveSize = 8_MB;
constexpr inline const size_t InitialProcessStorageSizeMax = 3_MB / 8;
@ -46,12 +46,12 @@ namespace ams::nxboot {
u32 reserved0; /* Previously entrypoint. */
u32 metadata_offset;
u32 reserved1;
u32 flags;
u32 num_kips;
u32 reserved2[4];
u32 reserved1[4];
u32 magic;
u32 total_size;
u32 reserved3; /* Previously crt0 offset. */
u32 reserved2; /* Previously crt0 offset. */
u32 content_header_offset;
u32 num_content_headers;
u32 supported_hos_version;
@ -60,7 +60,7 @@ namespace ams::nxboot {
SecondaryArchiveContentMeta content_metas[(0x400 - 0x40) / sizeof(SecondaryArchiveContentMeta)];
SecondaryArchiveKipMeta emummc_meta;
SecondaryArchiveKipMeta kip_metas[8];
u8 reserved4[0x800 - (0x400 + 9 * sizeof(SecondaryArchiveKipMeta))];
u8 reserved3[0x800 - (0x400 + 9 * sizeof(SecondaryArchiveKipMeta))];
};
static_assert(sizeof(SecondaryArchiveHeader) == 0x800);
@ -75,6 +75,9 @@ namespace ams::nxboot {
u8 mesosphere[0xAA000]; /* 0x056000-0x100000 */
u8 kips[3_MB]; /* 0x100000-0x400000 */
u8 splash_screen_fb[FrameBufferSize]; /* 0x400000-0x7C0000 */
u8 fusee[0x20000]; /* 0x7C0000-0x7E0000 */
u8 reboot_stub[0x1000]; /* 0x7E0000-0x7E1000 */
u8 reserved[0x1F000]; /* 0x7E1000-0x800000 */
};
static_assert(sizeof(SecondaryArchive) == SecondaryArchiveSize);

View file

@ -64,8 +64,10 @@ namespace ams::nxboot {
static_assert(sizeof(InitialProcessHeader) == 0x100);
struct PatchMeta {
u32 offset;
void *data;
PatchMeta *next;
u32 start_segment;
u32 rel_offset;
const void *data;
u32 size;
};
@ -73,7 +75,8 @@ namespace ams::nxboot {
InitialProcessMeta *next = nullptr;
const InitialProcessHeader *kip;
u32 kip_size;
PatchMeta *patches;
PatchMeta *patches_head;
PatchMeta *patches_tail;
u32 patch_segments;
u64 program_id;
se::Sha256Hash kip_hash;
@ -271,7 +274,8 @@ namespace ams::nxboot {
}
/* Clear patches. */
meta->patches = nullptr;
meta->patches_head = nullptr;
meta->patches_tail = nullptr;
meta->patch_segments = 0;
/* Increase the initial process binary's size. */
@ -308,8 +312,8 @@ namespace ams::nxboot {
return true;
}
const InitialProcessMeta *FindInitialProcess(u64 program_id) {
for (const InitialProcessMeta *cur = std::addressof(g_initial_process_meta); cur != nullptr; cur = cur->next) {
InitialProcessMeta *FindInitialProcess(u64 program_id) {
for (InitialProcessMeta *cur = std::addressof(g_initial_process_meta); cur != nullptr; cur = cur->next) {
if (cur->program_id == program_id) {
return cur;
}
@ -317,6 +321,193 @@ namespace ams::nxboot {
return nullptr;
}
u32 GetPatchSegments(const InitialProcessHeader *kip, u32 offset, size_t size) {
/* Create segment mask. */
u32 segments = 0;
/* Get the segment extents. */
const u32 rx_start = kip->rx_address;
const u32 ro_start = kip->ro_address;
const u32 rw_start = kip->rw_address;
const u32 rx_end = ro_start;
const u32 ro_end = rw_start;
const u32 rw_end = rw_start + kip->rw_size;
/* If the offset is below the kip header, ignore it. */
if (offset < sizeof(*kip)) {
return segments;
}
/* Adjust the offset in bounds. */
offset -= sizeof(*kip);
/* Check if the offset strays out of bounds. */
if (offset + size > rw_end) {
return segments;
}
/* Set bits for the affected segments. */
if (util::HasOverlap(offset, size, rx_start, rx_end - rx_start)) {
segments |= (1 << 0);
}
if (util::HasOverlap(offset, size, ro_start, ro_end - ro_start)) {
segments |= (1 << 1);
}
if (util::HasOverlap(offset, size, rw_start, rw_end - rw_start)) {
segments |= (1 << 2);
}
return segments;
}
void AddPatch(InitialProcessMeta *meta, u32 offset, const void *data, size_t data_size) {
/* Determine the segment. */
const u32 segments = GetPatchSegments(meta->kip, offset, data_size);
/* If the patch hits no segments, we don't need it. */
if (segments == 0) {
return;
}
/* Update patch segments. */
meta->patch_segments |= segments;
/* Adjust offset. */
const u32 start_segment = util::CountTrailingZeros(segments);
offset -= sizeof(*meta->kip);
switch (start_segment) {
case 0: offset -= meta->kip->rx_address; break;
case 1: offset -= meta->kip->ro_address; break;
case 2: offset -= meta->kip->rw_address; break;
}
/* Create patch. */
auto *new_patch = static_cast<PatchMeta *>(AllocateAligned(sizeof(PatchMeta), alignof(PatchMeta)));
new_patch->next = nullptr;
new_patch->start_segment = start_segment;
new_patch->rel_offset = offset;
new_patch->data = data;
new_patch->size = data_size;
/* Add the patch. */
if (meta->patches_head == nullptr) {
meta->patches_head = new_patch;
} else {
meta->patches_tail->next = new_patch;
}
meta->patches_tail = new_patch;
}
constexpr const u8 NogcPatch0[] = {
0x80
};
constexpr const u8 NogcPatch1[] = {
0xE0, 0x03, 0x1F, 0x2A, 0xC0, 0x03, 0x5F, 0xD6,
};
void AddNogcPatches(InitialProcessMeta *fs_meta, FsVersion fs_version) {
switch (fs_version) {
case FsVersion_1_0_0:
case FsVersion_2_0_0:
case FsVersion_2_0_0_Exfat:
case FsVersion_2_1_0:
case FsVersion_2_1_0_Exfat:
case FsVersion_3_0_0:
case FsVersion_3_0_0_Exfat:
case FsVersion_3_0_1:
case FsVersion_3_0_1_Exfat:
/* There were no lotus firmware updates prior to 4.0.0. */
/* TODO: Implement patches, regardless? */
break;
case FsVersion_4_0_0:
case FsVersion_4_0_0_Exfat:
AddPatch(fs_meta, 0x0A3539, NogcPatch0, sizeof(NogcPatch0));
AddPatch(fs_meta, 0x0AAC44, NogcPatch1, sizeof(NogcPatch1));
break;
case FsVersion_4_1_0:
case FsVersion_4_1_0_Exfat:
AddPatch(fs_meta, 0x0A35BD, NogcPatch0, sizeof(NogcPatch0));
AddPatch(fs_meta, 0x0AACA8, NogcPatch1, sizeof(NogcPatch1));
break;
case FsVersion_5_0_0:
case FsVersion_5_0_0_Exfat:
AddPatch(fs_meta, 0x0CF4C5, NogcPatch0, sizeof(NogcPatch0));
AddPatch(fs_meta, 0x0D74A0, NogcPatch1, sizeof(NogcPatch1));
break;
case FsVersion_5_1_0:
case FsVersion_5_1_0_Exfat:
AddPatch(fs_meta, 0x0CF895, NogcPatch0, sizeof(NogcPatch0));
AddPatch(fs_meta, 0x0D7870, NogcPatch1, sizeof(NogcPatch1));
break;
case FsVersion_6_0_0:
AddPatch(fs_meta, 0x1539F5, NogcPatch0, sizeof(NogcPatch0));
AddPatch(fs_meta, 0x12CD20, NogcPatch1, sizeof(NogcPatch1));
break;
case FsVersion_6_0_0_Exfat:
AddPatch(fs_meta, 0x15F0F5, NogcPatch0, sizeof(NogcPatch0));
AddPatch(fs_meta, 0x138420, NogcPatch1, sizeof(NogcPatch1));
break;
case FsVersion_7_0_0:
AddPatch(fs_meta, 0x15C005, NogcPatch0, sizeof(NogcPatch0));
AddPatch(fs_meta, 0x134260, NogcPatch1, sizeof(NogcPatch1));
break;
case FsVersion_7_0_0_Exfat:
AddPatch(fs_meta, 0x1675B5, NogcPatch0, sizeof(NogcPatch0));
AddPatch(fs_meta, 0x13F810, NogcPatch1, sizeof(NogcPatch1));
break;
case FsVersion_8_0_0:
case FsVersion_8_1_0:
AddPatch(fs_meta, 0x15EC95, NogcPatch0, sizeof(NogcPatch0));
AddPatch(fs_meta, 0x136900, NogcPatch1, sizeof(NogcPatch1));
break;
case FsVersion_8_0_0_Exfat:
case FsVersion_8_1_0_Exfat:
AddPatch(fs_meta, 0x16A245, NogcPatch0, sizeof(NogcPatch0));
AddPatch(fs_meta, 0x141EB0, NogcPatch1, sizeof(NogcPatch1));
break;
case FsVersion_9_0_0:
case FsVersion_9_0_0_Exfat:
AddPatch(fs_meta, 0x143369, NogcPatch0, sizeof(NogcPatch0));
AddPatch(fs_meta, 0x129520, NogcPatch1, sizeof(NogcPatch1));
break;
case FsVersion_9_1_0:
case FsVersion_9_1_0_Exfat:
AddPatch(fs_meta, 0x143379, NogcPatch0, sizeof(NogcPatch0));
AddPatch(fs_meta, 0x129530, NogcPatch1, sizeof(NogcPatch1));
break;
case FsVersion_10_0_0:
case FsVersion_10_0_0_Exfat:
AddPatch(fs_meta, 0x14DF09, NogcPatch0, sizeof(NogcPatch0));
AddPatch(fs_meta, 0x13BF90, NogcPatch1, sizeof(NogcPatch1));
break;
case FsVersion_10_2_0:
case FsVersion_10_2_0_Exfat:
AddPatch(fs_meta, 0x14E369, NogcPatch0, sizeof(NogcPatch0));
AddPatch(fs_meta, 0x13C3F0, NogcPatch1, sizeof(NogcPatch1));
break;
case FsVersion_11_0_0:
case FsVersion_11_0_0_Exfat:
AddPatch(fs_meta, 0x156FB9, NogcPatch0, sizeof(NogcPatch0));
AddPatch(fs_meta, 0x1399B4, NogcPatch1, sizeof(NogcPatch1));
break;
case FsVersion_12_0_0:
case FsVersion_12_0_0_Exfat:
AddPatch(fs_meta, 0x155469, NogcPatch0, sizeof(NogcPatch0));
AddPatch(fs_meta, 0x13EB24, NogcPatch1, sizeof(NogcPatch1));
break;
case FsVersion_12_0_3:
case FsVersion_12_0_3_Exfat:
AddPatch(fs_meta, 0x155579, NogcPatch0, sizeof(NogcPatch0));
AddPatch(fs_meta, 0x13EC34, NogcPatch1, sizeof(NogcPatch1));
break;
default:
break;
}
}
}
u32 ConfigureStratosphere(const u8 *nn_package2, ams::TargetFirmware target_firmware, bool emummc_enabled, bool nogc_enabled) {
@ -393,7 +584,7 @@ namespace ams::nxboot {
/* Get meta for FS process. */
constexpr u64 FsProgramId = 0x0100000000000000;
const auto *fs_meta = FindInitialProcess(FsProgramId);
auto *fs_meta = FindInitialProcess(FsProgramId);
if (fs_meta == nullptr) {
/* Get nintendo header/data. */
const pkg2::Package2Header *nn_header = reinterpret_cast<const pkg2::Package2Header *>(nn_package2);
@ -431,7 +622,15 @@ namespace ams::nxboot {
}
}
/* TODO: Parse/prepare relevant nogc/kip patches. */
/* Parse/prepare relevant nogc/kip patches. */
{
/* Add nogc patches. */
if (nogc_enabled) {
AddNogcPatches(fs_meta, fs_version);
}
/* Add generic patches. */
}
/* Return the fs version we're using. */
return static_cast<u32>(fs_version);

View file

@ -1,6 +1,6 @@
#!/usr/bin/env python
import sys, lz4
from struct import unpack as up
import sys, lz4, hashlib
from struct import unpack as up, pack as pk
def lz4_compress(data):
try:
@ -20,6 +20,105 @@ def pad(data, size):
def get_overlay(program, i):
return program[0x2B000 + 0x14000 * i:0x2B000 + 0x14000 * (i+1)]
KIP_NAMES = ['Loader', 'NCM', 'ProcessManager', 'sm', 'boot', 'spl', 'ams_mitm']
def get_kips():
emummc = read_file('../../../../emummc/emummc.kip')
loader = read_file('../../../../stratosphere/loader/loader.kip')
ncm = read_file('../../../../stratosphere/ncm/ncm.kip')
pm = read_file('../../../../stratosphere/pm/pm.kip')
sm = read_file('../../../../stratosphere/sm/sm.kip')
boot = read_file('../../../../stratosphere/boot/boot.kip')
spl = read_file('../../../../stratosphere/spl/spl.kip')
ams_mitm = read_file('../../../../stratosphere/ams_mitm/ams_mitm.kip')
return (emummc, {
'Loader' : loader,
'NCM' : ncm,
'ProcessManager' : pm,
'sm' : sm,
'boot' : boot,
'spl' : spl,
'ams_mitm' : ams_mitm,
})
def write_kip_meta(f, kip, ofs):
# Write program id
f.write(kip[0x10:0x18])
# Write offset, size
f.write(pk('<II', ofs, len(kip)))
# Write hash
f.write(hashlib.sha256(kip).digest())
def write_header(f, all_kips):
# Unpack kips
emummc, kips = all_kips
# Write reserved0 (previously entrypoint) as infinite loop instruction.
f.write(pk('<I', 0xEAFFFFFE))
# Write metadata offset = 0x10
f.write(pk('<I', 0x20))
# Write flags
f.write(pk('<I', 0x00000000))
# Write num_kips
f.write(pk('<I', len(KIP_NAMES)))
# Write reserved2
f.write(b'\xCC' * 0x10)
# Write magic
f.write('FSS0')
# Write total size
f.write(pk('<I', 0x800000))
# Write reserved3
f.write(pk('<I', 0xCCCCCCCC))
# Write content_header_offset
f.write(pk('<I', 0x40))
# Write num_content_headers;
f.write(pk('<I', 8 + len(KIP_NAMES)))
# Write supported_hos_version;
f.write(pk('<I', 0xCCCCCCCC)) # TODO
# Write release_version;
f.write(pk('<I', 0xCCCCCCCC)) # TODO
# Write git_revision;
f.write(pk('<I', 0xCCCCCCCC)) # TODO
# Write content metas
f.write(pk('<IIBBBBI16s', 0x000800, 0x001800, 2, 0, 0, 0, 0xCCCCCCCC, 'warmboot'))
f.write(pk('<IIBBBBI16s', 0x002000, 0x002000, 12, 0, 0, 0, 0xCCCCCCCC, 'tsec_keygen'))
f.write(pk('<IIBBBBI16s', 0x004000, 0x01C000, 11, 0, 0, 0, 0xCCCCCCCC, 'exosphere_fatal'))
f.write(pk('<IIBBBBI16s', 0x048000, 0x00E000, 1, 0, 0, 0, 0xCCCCCCCC, 'exosphere'))
f.write(pk('<IIBBBBI16s', 0x056000, 0x0AA000, 10, 0, 0, 0, 0xCCCCCCCC, 'mesosphere'))
f.write(pk('<IIBBBBI16s', 0x7C0000, 0x020000, 0, 0, 0, 0, 0xCCCCCCCC, 'fusee'))
f.write(pk('<IIBBBBI16s', 0x7E0000, 0x001000, 3, 0, 0, 0, 0xCCCCCCCC, 'rebootstub'))
f.write(pk('<IIBBBBI16s', 0x100000, len(emummc), 8, 0, 0, 0, 0xCCCCCCCC, 'emummc'))
ofs = 0x100000 + len(emummc)
for kip_name in KIP_NAMES:
kip_data = kips[kip_name]
f.write(pk('<IIBBBBI16s', ofs, len(kip_data), 6, 0, 0, 0, 0xCCCCCCCC, kip_name))
ofs += len(kip_data)
# Pad to kip metas.
f.write(b'\xCC' * (0x400 - 0x40 - (0x20 * (8 + len(KIP_NAMES)))))
# Write emummc_meta. */
write_kip_meta(f, emummc, 0x100000)
# Write kip metas
ofs = 0x100000 + len(emummc)
for kip_name in KIP_NAMES:
kip_data = kips[kip_name]
write_kip_meta(f, kip_data, ofs)
ofs += len(kip_data)
# Pad to end of header
f.write(b'\xCC' * (0x800 - (0x400 + (1 + len(KIP_NAMES)) * 0x30)))
def write_kips(f, all_kips):
# Unpack kips
emummc, kips = all_kips
# Write emummc
f.write(emummc)
# Write kips
tot = len(emummc)
for kip_name in KIP_NAMES:
kip_data = kips[kip_name]
f.write(kip_data)
tot += len(kip_data)
# Pad to 3 MB
f.write(b'\xCC' * (0x300000 - tot))
def main(argc, argv):
if argc == 2:
target = argv[1]
@ -30,13 +129,15 @@ def main(argc, argv):
else:
print('Usage: %s target' % argv[0])
return 1
all_kips = get_kips()
with open('../../program%s.bin' % target, 'rb') as f:
data = f.read()
fusee_program = lz4_compress(data[:0x2B000] + get_overlay(data, 0)[:0x11000])
with open('../../program%s.lz4' % target, 'wb') as f:
f.write(lz4_compress(data[:0x2B000] + get_overlay(data, 0)[:0x11000]))
f.write(fusee_program)
with open('../../fusee-boogaloo%s.bin' % target, 'wb') as f:
# TODO: Write header
f.write('\xCC'*0x800)
# Write header
write_header(f, all_kips)
# Write warmboot
f.write(pad(read_file('../../../../exosphere/warmboot%s.bin' % target), 0x1800))
# Write TSEC Keygen
@ -51,10 +152,17 @@ def main(argc, argv):
f.write(pad(read_file('../../../../exosphere/exosphere%s.bin' % target), 0xE000))
# Write mesosphere
f.write(pad(read_file('../../../../mesosphere/mesosphere%s.bin' % target), 0xAA000))
# TODO: Write kips
f.write('\xCC'*0x300000)
# Write kips
write_kips(f, all_kips)
# Write Splash Screen
f.write(pad(read_file('../../splash_screen/splash_screen.bin'), 0x3C0000))
# Write fusee
f.write(pad(fusee_program, 0x20000))
# Write rebootstub
f.write(pad(read_file('../../../../exosphere/program/rebootstub/rebootstub%s.bin' % target), 0x1000))
# Pad to 8 MB
f.write(b'\xCC' * (0x800000 - 0x7E1000))
return 0