Compare commits

...

2 commits

4 changed files with 178 additions and 77 deletions

View file

@ -6,11 +6,17 @@
#include "glitch.h" #include "glitch.h"
#include "misc.h" #include "misc.h"
#include "fuses.h" #include "fuses.h"
#include "board_detect.h"
bool mariko = true; bool mariko = true;
bool board_detected = false; bool board_detected = false;
// maximum number of retries for boot detection before giving up
#define BOOT_DETECT_MAX_RETRIES 4
bool wait_for_boot(int timeout_ms) { bool wait_for_boot(int timeout_ms) {
// retry the entire boot detection sequence up to BOOT_DETECT_MAX_RETRIES times
for (int retry = 0; retry < BOOT_DETECT_MAX_RETRIES; retry++) {
absolute_time_t tio_full = make_timeout_time_ms(timeout_ms); absolute_time_t tio_full = make_timeout_time_ms(timeout_ms);
absolute_time_t tio_cmd1 = tio_full; absolute_time_t tio_cmd1 = tio_full;
init_glitch_pio(); init_glitch_pio();
@ -25,7 +31,8 @@ bool wait_for_boot(int timeout_ms) {
{ {
if (reset_attempts > 4) if (reset_attempts > 4)
{ {
halt_with_error(0, 3); // internal timeout, break and retry the whole sequence
break;
} }
reset_attempts++; reset_attempts++;
reset_cpu(); reset_cpu();
@ -45,7 +52,9 @@ bool wait_for_boot(int timeout_ms) {
} }
else if (last_word == 0x51000000 && word == 0x0055) //read block 0 else if (last_word == 0x51000000 && word == 0x0055) //read block 0
{ {
tio_full = make_timeout_time_ms(100); // OLED models sometimes need more time between block 0 and block 1
// original was 100ms, increased to 250ms for (hopefully) better OLED compatibility
tio_full = make_timeout_time_ms(250);
was_read_zero = true; was_read_zero = true;
} else if (was_read_zero && last_word == 0x4D000200 && word == 0x00B1) // read status - erista only } else if (was_read_zero && last_word == 0x4D000200 && word == 0x00B1) // read status - erista only
{ {
@ -58,6 +67,23 @@ bool wait_for_boot(int timeout_ms) {
last_word = word; last_word = word;
} }
} }
// 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)
// clean up GPIO pins
for (int i = PIN_CLK; i <= PIN_DAT; i++)
{
gpio_deinit(i);
gpio_disable_pulls(i);
gpio_disable_input_output(i);
}
gpio_deinit(gli_pin());
// only halt with error if all retries are exhausted
if (retry == BOOT_DETECT_MAX_RETRIES - 1) {
if (was_read_zero) { if (was_read_zero) {
halt_with_error(1, 3); halt_with_error(1, 3);
} }
@ -66,5 +92,11 @@ bool wait_for_boot(int timeout_ms) {
} else { } else {
halt_with_error(3, 3); halt_with_error(3, 3);
} }
}
// small delay before retrying to let hardware settle
sleep_ms(50);
}
return false; return false;
} }

View file

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

View file

@ -65,7 +65,9 @@ void init_glitch_pio() {
} }
void deinit_glitch_pio() { void deinit_glitch_pio() {
pio_set_sm_mask_enabled(pio1, 0x3, false); // original code only disabled SM 0 and 1 (mask 0x3 = 0b011)
// SM 2 (G_DAT0_SM) is also used, so all 3 (mask 0x7 = 0b111) need to be disabled
pio_set_sm_mask_enabled(pio1, 0x7, false);
for (int i = PIN_CLK; i <= PIN_DAT; i++) for (int i = PIN_CLK; i <= PIN_DAT; i++)
{ {
gpio_deinit(i); gpio_deinit(i);

View file

@ -708,6 +708,69 @@ void prepare_mariko_bct()
memcpy(data_bct + 0x480, mariko_bct_data, 0x2380); 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() { void write_payload() {
static bool prepared = false; static bool prepared = false;
if (!prepared) if (!prepared)
@ -720,6 +783,10 @@ void write_payload() {
} }
start_mmc(); start_mmc();
reinit_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) if (!is_space_bl && !is_command)
{ {
copy_bct(0x0, 0x7A0); copy_bct(0x0, 0x7A0);