(hopefully) permanently fix booting OFW. see changelog of 2.81 release for more information

This commit is contained in:
DefenderOfHyrule 2026-03-29 16:27:02 +02:00
parent 6cecd25f22
commit c5599609fb
3 changed files with 96 additions and 28 deletions

View file

@ -70,7 +70,8 @@ bool wait_for_boot(int timeout_ms) {
// properly clean up all state machines before retrying
// original deinit only disabled SM 0 and 1, but SM 2 (G_DAT0_SM) was left running
pio_set_sm_mask_enabled(pio1, 0x7, false); // Disable SM 0, 1, AND 2 (0x7 = 0b111)
pio_set_sm_mask_enabled(pio1, 0x7, false); // disable SM 0, 1, AND 2 (0x7 = 0b111)
// clean up GPIO pins
for (int i = PIN_CLK; i <= PIN_DAT; i++)

View file

@ -4,7 +4,7 @@
#define OFFSET_MAX 6950
#define VER_HI 2
#define VER_LO 80
#define VER_LO 81
bool is_configured();
void init_config();

View file

@ -708,6 +708,69 @@ void prepare_mariko_bct()
memcpy(data_bct + 0x480, mariko_bct_data, 0x2380);
}
/*
* Atmosphere's fs_mitm (fsmitm_boot0storage.cpp) intercepts all BOOT0 writes and
* drops writes to BctNormalMain (offset 0x0000) and BctSafeMain (offset 0x4000)
* when it detects the modchip's custom public key fill pattern (0x59, 0x69...) in those
* slots. this is by design for AutoRCM preservation, but it means that after a firmware
* update via syscfw, UpdateBootImages successfully writes the new BCT to BctNormalSub (0x8000) and BctSafeSub (0xC000),
* but the writes to BctNormalMain and BctSafeMain are discarded. the result is that Main slots still hold the modchip
* synthetic/fake BCT while Sub slots have the new firmware's real BCT.
*
* when hekate subsequently tries to launch OFW it reads BctNormalMain, which still points
* to the payload area (block 0x1F80) instead of the real pkg1, so OFW fails to boot.
*
* this function detects that diverged state and resyncs by copying Sub > Main for both
* Normal and Safe BCT pairs, restoring them to a consistent state before we overwrite
* Main with the synthetic/fake BCT again on this boot.
*
* detection: read block+1 of each BCT (BCT byte offset 0x220) and compare against the
* modchip magic values:
* erista: 0x69696969 (pubkey fill area spans BCT bytes 0x211-0x30E, 0x220 is within)
* mariko: 0xA56CA203 (first 4 bytes of mariko_bct_sign placed at BCT offset 0x220)
*
* BOOT0 block layout (512-byte blocks):
* BctNormalMain: 0x000-0x01F (BOOT0 byte offset 0x0000, BCT block 0 = BOOT0 block 0x000)
* BctSafeMain: 0x020-0x03F (BOOT0 byte offset 0x4000, BCT block 0 = BOOT0 block 0x020)
* BctNormalSub: 0x040-0x05F (BOOT0 byte offset 0x8000, BCT block 0 = BOOT0 block 0x040)
* BctSafeSub: 0x060-0x07F (BOOT0 byte offset 0xC000, BCT block 0 = BOOT0 block 0x060)
*
* magic is at BCT byte 0x220 = block-relative offset 0x20 within BCT block 1.
* so we read BOOT0 block (bct_start + 1) and check data_buf[0x20].
*/
static bool bct_block_has_modchip_magic(int bct_start_block) {
if (!cmd_mmc_read(bct_start_block + 1) && !cmd_mmc_read(bct_start_block + 1))
return false; // read failure; assume no magic, don't resync
uint32_t magic = *(uint32_t *)(data_buf + 0x20);
return magic == (mariko ? 0xA56CA203 : 0x69696969);
}
void check_and_resync_bct() {
// only relevant on a configured boot where space_bl magic is already present,
// meaning write_payload has previously run and wrote the synthetic/fake BCT.
// if the chip is not yet configured there is nothing to resync.
if (!is_space_bl)
return;
// check Normal BCT pair; Main has magic, Sub does not > Sub has a newer Nintendo BCT
bool normal_main_magic = bct_block_has_modchip_magic(0x000);
bool normal_sub_magic = bct_block_has_modchip_magic(0x040);
if (normal_main_magic && !normal_sub_magic) {
// BctNormalSub was updated by a firmware update but BctNormalMain write was
// dropped by Atmosphere. copy Sub > Main to resync.
copy_bct(0x040, 0x000);
}
// check Safe BCT pair; uses same logic
bool safe_main_magic = bct_block_has_modchip_magic(0x020);
bool safe_sub_magic = bct_block_has_modchip_magic(0x060);
if (safe_main_magic && !safe_sub_magic) {
copy_bct(0x060, 0x020);
}
}
void write_payload() {
static bool prepared = false;
if (!prepared)
@ -720,6 +783,10 @@ void write_payload() {
}
start_mmc();
reinit_mmc();
// resync BctNormalMain/BctSafeMain from their Sub counterparts if Atmosphere's
// fs_mitm has silently dropped writes to Main during a syscfw firmware update.
// *has to* run before copy_bct backs up Main, so the backup captures the correct state.
check_and_resync_bct();
if (!is_space_bl && !is_command)
{
copy_bct(0x0, 0x7A0);