forked from Mirrors/usk
834 lines
26 KiB
C
834 lines
26 KiB
C
#include "pico/stdlib.h"
|
|
#include "hardware/pio.h"
|
|
#include "hardware/watchdog.h"
|
|
#include "pins.h"
|
|
#include "misc.h"
|
|
#include "fuses.h"
|
|
#include "payload.h"
|
|
#include "config.h"
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "emmc.pio.h"
|
|
#include "mmc_defs.h"
|
|
#include "board_detect.h"
|
|
|
|
#define SM_CLK 0
|
|
#define SM_CMDIN 1
|
|
#define SM_DATIN 2
|
|
#define SM_OUT 3
|
|
|
|
extern uint32_t clk_pio_offset;
|
|
extern uint32_t sdin_pio_offset;
|
|
extern uint32_t sdout_pio_offset;
|
|
|
|
uint16_t crc_itu_t_table[256];
|
|
|
|
/*const uint16_t crc_itu_t_table[256] = {
|
|
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
|
|
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
|
|
0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
|
|
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
|
|
0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
|
|
0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
|
|
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
|
|
0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
|
|
0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
|
|
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
|
|
0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
|
|
0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
|
|
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
|
|
0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
|
|
0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
|
|
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
|
|
0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
|
|
0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
|
|
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
|
|
0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
|
|
0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
|
|
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
|
|
0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
|
|
0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
|
|
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
|
|
0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
|
|
0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
|
|
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
|
|
0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
|
|
0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
|
|
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
|
|
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
|
|
};*/
|
|
|
|
void crc_prepare_table()
|
|
{
|
|
const uint16_t LengthCRC = 16;
|
|
const uint16_t polynomial = 0x1021;
|
|
const uint16_t mask = (1 << (LengthCRC - 1));
|
|
for (int32_t i = 0; i < 256; i++)
|
|
{
|
|
uint16_t crc = i << 8;
|
|
for (uint8_t bit = 0; bit < 8; bit++)
|
|
{
|
|
if (crc & mask)
|
|
crc = (crc << 1) ^ polynomial;
|
|
else
|
|
crc <<= 1;
|
|
}
|
|
crc_itu_t_table[i] = crc;
|
|
}
|
|
}
|
|
|
|
extern bool mariko;
|
|
|
|
static inline uint16_t crc_itu_t_byte(uint16_t crc, const uint8_t data)
|
|
{
|
|
return (crc << 8) ^ crc_itu_t_table[((crc >> 8) ^ data) & 0xff];
|
|
}
|
|
|
|
uint16_t crc_itu_t(uint16_t crc, const uint8_t *buffer, size_t len)
|
|
{
|
|
static bool table_prepared = false;
|
|
if (!table_prepared)
|
|
{
|
|
crc_prepare_table();
|
|
}
|
|
while (len--)
|
|
crc = crc_itu_t_byte(crc, *buffer++);
|
|
return crc;
|
|
}
|
|
|
|
extern void zzz();
|
|
|
|
uint16_t payload_crc()
|
|
{
|
|
static int pay_crc = -1;
|
|
if(pay_crc == -1)
|
|
pay_crc = crc_itu_t(0, payload, sizeof(payload));
|
|
return pay_crc;
|
|
}
|
|
|
|
int crc7(uint8_t *buffer, int size)
|
|
{
|
|
uint8_t crc = 0;
|
|
for (int i = 0; i < size; i++) {
|
|
uint8_t c = buffer[i];
|
|
for (int j = 0; j < 8; j++) {
|
|
crc <<= 1;
|
|
if ((crc ^ c) & 0x80)
|
|
crc ^= 9;
|
|
c <<= 1;
|
|
}
|
|
crc &= 0x7Fu;
|
|
}
|
|
return crc;
|
|
}
|
|
|
|
void __time_critical_func(cmd_write)(uint8_t cmd, uint32_t argument)
|
|
{
|
|
uint8_t data[7];
|
|
// switch to the CMD write
|
|
pio_sm_set_enabled(pio0, SM_OUT, false);
|
|
pio_sm_set_out_pins(pio0, SM_OUT, PIN_CMD, 1);
|
|
pio_sm_set_enabled(pio0, SM_OUT, true);
|
|
|
|
data[0] = cmd | 0x40;
|
|
*(uint32_t *) &data[1] = __builtin_bswap32(argument);
|
|
data[5] = (crc7(data, 5) << 1) | 1;
|
|
uint32_t fifo[3];
|
|
fifo[0] = 0xFFCF0000 | (data[0] << 8) | data[1];
|
|
fifo[1] = __builtin_bswap32(*(uint32_t*)(data + 2));
|
|
fifo[2] = 0x80000000;
|
|
for (int i = 0; i < 3; i++) {
|
|
pio_sm_put_blocking(pio0, SM_OUT, ~fifo[i]);
|
|
}
|
|
}
|
|
|
|
uint8_t data_buf[514];
|
|
|
|
bool __time_critical_func(dat_write)()
|
|
{
|
|
// switch to the DAT write
|
|
pio_sm_set_enabled(pio0, SM_OUT, false);
|
|
pio_sm_set_out_pins(pio0, SM_OUT, PIN_DAT, 1);
|
|
pio_sm_set_enabled(pio0, SM_OUT, true);
|
|
uint8_t * buffer = data_buf;
|
|
uint16_t crc = crc_itu_t(0, buffer, 512);
|
|
uint32_t words[130];
|
|
int size_bits = 514 * 8 + 2;
|
|
words[0] = ((size_bits ^ 0xFFFF) << 16) | (buffer[0] << 7) | (buffer[1] >> 1);
|
|
int last_one = buffer[1] & 1;
|
|
for (int i = 1; i < 128; i++) {
|
|
words[i] = (last_one << 31) | (buffer[-2 + i*4] << 23) | (buffer[-1 + i*4] << 15) | (buffer[0 + i*4] << 7) | (buffer[1 + i*4] >> 1);
|
|
last_one = buffer[1 + i*4] & 1;
|
|
}
|
|
words[128] = (last_one << 31) | (buffer[510] << 23) | (buffer[511] << 15) | ((crc >> 8) << 7) | ((crc & 0xFF) >> 1);
|
|
words[129] = ((crc & 1) << 31) | (3 << 29);
|
|
|
|
for (int i = 0; i < 130; i++) {
|
|
pio_sm_put_blocking(pio0, SM_OUT, ~words[i]);
|
|
}
|
|
while(!pio_sm_is_tx_fifo_empty(pio0, SM_OUT));
|
|
sleep_us(300);
|
|
int attempts = 200;
|
|
while (--attempts) {
|
|
if (gpio_get(PIN_DAT))
|
|
return true;
|
|
sleep_ms(1);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void __time_critical_func(cmd_read_request)(int size)
|
|
{
|
|
// set initial state
|
|
pio_sm_clear_fifos(pio0, SM_CMDIN);
|
|
pio_sm_exec(pio0, SM_CMDIN, pio_encode_jmp(sdin_pio_offset));
|
|
pio_sm_put_blocking(pio0, SM_CMDIN, size * 8 - 1);
|
|
}
|
|
|
|
void __time_critical_func(dat_read_request)(int size)
|
|
{
|
|
// set initial state
|
|
pio_sm_clear_fifos(pio0, SM_DATIN);
|
|
pio_sm_exec(pio0, SM_DATIN, pio_encode_jmp(sdin_pio_offset));
|
|
pio_sm_put_blocking(pio0, SM_DATIN, size * 8 - 1);
|
|
}
|
|
|
|
bool __time_critical_func(dat_read_data)(uint8_t * buf, int size)
|
|
{
|
|
absolute_time_t tio = make_timeout_time_us(20000);
|
|
while (!time_reached(tio) && size)
|
|
{
|
|
if (!pio_sm_is_rx_fifo_empty(pio0, SM_DATIN))
|
|
{
|
|
tio = make_timeout_time_us(300);
|
|
uint32_t data = pio_sm_get(pio0, SM_DATIN);
|
|
uint32_t data_corrected = data;
|
|
if (size < 4)
|
|
data_corrected <<= (4 - size)*8;
|
|
for(int i = 3; i >= 0 && size > 0; i--)
|
|
{
|
|
*buf = (data_corrected >> (i*8)) & 0xFF;
|
|
buf++;
|
|
size -= 1;
|
|
}
|
|
}
|
|
}
|
|
return size == 0;
|
|
}
|
|
|
|
bool __time_critical_func(cmd_read_data)(uint8_t * buf, int size)
|
|
{
|
|
int last_bit = 0;
|
|
uint8_t * b = buf;
|
|
absolute_time_t tio = make_timeout_time_us(1000);
|
|
while (!time_reached(tio) && size)
|
|
{
|
|
if (!pio_sm_is_rx_fifo_empty(pio0, SM_CMDIN))
|
|
{
|
|
tio = make_timeout_time_us(300);
|
|
uint32_t data = pio_sm_get(pio0, SM_CMDIN);
|
|
uint32_t data_corrected = data;
|
|
if (size < 4)
|
|
data_corrected <<= (4 - size)*8;
|
|
data_corrected = (data_corrected >> 1) | (last_bit << 31);
|
|
last_bit = data & 1;
|
|
for(int i = 3; i >= 0 && size > 0; i--)
|
|
{
|
|
*buf = (data_corrected >> (i*8)) & 0xFF;
|
|
buf++;
|
|
size -= 1;
|
|
}
|
|
}
|
|
}
|
|
return size == 0;
|
|
}
|
|
|
|
bool __time_critical_func(simple_cmd_exec)(int cmd, int arg)
|
|
{
|
|
cmd_write(cmd, arg);
|
|
pio_sm_exec_wait_blocking(pio0, SM_CMDIN, pio_encode_irq_wait(false, 1));
|
|
return true;
|
|
}
|
|
|
|
bool __time_critical_func(simple_cmd_exec_with_ret_s)(int cmd, int arg, uint32_t * ret)
|
|
{
|
|
uint8_t buffer[6];
|
|
cmd_read_request(6);
|
|
cmd_write(cmd, arg);
|
|
bool result = cmd_read_data(buffer, 6);
|
|
if (result) {
|
|
*ret = __builtin_bswap32(*(uint32_t*)(buffer+1));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool __time_critical_func(simple_cmd_exec_with_ret)(int cmd, int arg, uint32_t * ret)
|
|
{
|
|
bool res = false;
|
|
for(int i = 0; i < 2; i++)
|
|
{
|
|
res = simple_cmd_exec_with_ret_s(cmd, arg, ret);
|
|
if (res)
|
|
break;
|
|
sleep_us(250);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
uint8_t cid_buf[17];
|
|
|
|
bool __time_critical_func(cmd_exec_cid)()
|
|
{
|
|
cmd_read_request(17);
|
|
cmd_write(MMC_ALL_SEND_CID, 0);
|
|
bool result = cmd_read_data(cid_buf, 17);
|
|
if (result)
|
|
{
|
|
if (crc7(cid_buf + 1, 15) != (cid_buf[16] >> 1))
|
|
return false;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool __time_critical_func(cmd_mmc_read)(int block)
|
|
{
|
|
dat_read_request(514);
|
|
cmd_write(MMC_READ_SINGLE_BLOCK, block);
|
|
bool result = dat_read_data(data_buf, 514);
|
|
if (result)
|
|
{
|
|
if (crc_itu_t(0, data_buf, 512) != ((data_buf[512] << 8) | data_buf[513]))
|
|
return false;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool __time_critical_func(cmd_mmc_write)(int block)
|
|
{
|
|
uint32_t ret;
|
|
if(!simple_cmd_exec_with_ret(MMC_WRITE_BLOCK, block, &ret))
|
|
return false;
|
|
return dat_write();
|
|
}
|
|
|
|
#define DIVIDER 266 //266 - 375 KHz, 133 - 750 KHz
|
|
|
|
void start_mmc()
|
|
{
|
|
// clocks
|
|
pio_sm_config c = sd_clk_program_get_default_config(clk_pio_offset);
|
|
sm_config_set_clkdiv_int_frac(&c, DIVIDER, 0); // 375 KHz
|
|
sm_config_set_sideset_pins (&c, PIN_CLK); // to keep CPU in reset but not wdog
|
|
sm_config_set_set_pins (&c, PIN_RST, 1); // SD clock
|
|
pio_sm_init(pio0, SM_CLK, clk_pio_offset, &c);
|
|
pio_sm_set_consecutive_pindirs (pio0, SM_CLK, PIN_CLK, 1, true);
|
|
|
|
// CMD sniffer
|
|
c = in_cmd_or_dat_program_get_default_config(sdin_pio_offset);
|
|
sm_config_set_clkdiv_int_frac(&c, DIVIDER, 0);
|
|
sm_config_set_in_pins (&c, PIN_CMD);
|
|
sm_config_set_in_shift(&c, false, true, 32);
|
|
sm_config_set_out_shift(&c, false, true, 32);
|
|
sm_config_set_jmp_pin (&c, PIN_CMD);
|
|
pio_sm_init(pio0, SM_CMDIN, sdin_pio_offset, &c);
|
|
|
|
// DAT sniffer
|
|
c = in_cmd_or_dat_program_get_default_config(sdin_pio_offset);
|
|
sm_config_set_clkdiv_int_frac(&c, DIVIDER, 0);
|
|
sm_config_set_in_pins (&c, PIN_DAT);
|
|
sm_config_set_in_shift(&c, false, true, 32);
|
|
sm_config_set_out_shift(&c, false, true, 32);
|
|
sm_config_set_jmp_pin (&c, PIN_DAT);
|
|
pio_sm_init(pio0, SM_DATIN, sdin_pio_offset, &c);
|
|
|
|
// CMD writer
|
|
c = out_cmd_or_dat_program_get_default_config(sdout_pio_offset);
|
|
sm_config_set_clkdiv_int_frac(&c, DIVIDER, 0);
|
|
sm_config_set_out_pins (&c, PIN_CMD, 1);
|
|
sm_config_set_in_pins (&c, PIN_CLK);
|
|
sm_config_set_out_shift(&c, false, true, 32);
|
|
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
|
|
sm_config_set_jmp_pin (&c, PIN_CLK);
|
|
pio_sm_init(pio0, SM_OUT, sdout_pio_offset, &c);
|
|
|
|
// put CPU into reset state
|
|
reset_cpu();
|
|
|
|
// enable SMs synced
|
|
pio_enable_sm_mask_in_sync(pio0, 0xF);
|
|
|
|
// configure pins
|
|
for (int i = 0; i < 32; i++)
|
|
{
|
|
if (i == PIN_CLK || i == PIN_RST || i == PIN_CMD || (i == PIN_DAT))
|
|
{
|
|
gpio_init(i);
|
|
gpio_pull_up(i);
|
|
gpio_set_input_hysteresis_enabled(i, true);
|
|
gpio_set_slew_rate(i, GPIO_SLEW_RATE_FAST);
|
|
gpio_set_drive_strength(i, GPIO_DRIVE_STRENGTH_2MA);
|
|
pio_gpio_init(pio0, i);
|
|
}
|
|
}
|
|
gpio_enable_input_output(PIN_RST);
|
|
}
|
|
|
|
void stop_mmc()
|
|
{
|
|
pio_set_sm_mask_enabled(pio0, 0xF, false);
|
|
for (int i = 0; i < 32; i++)
|
|
{
|
|
if (i == PIN_CLK || i == PIN_RST || i == PIN_CMD || (i == PIN_DAT))
|
|
{
|
|
gpio_deinit(i);
|
|
gpio_set_input_hysteresis_enabled(i, false);
|
|
gpio_disable_pulls(i);
|
|
}
|
|
}
|
|
gpio_disable_input_output(PIN_RST);
|
|
}
|
|
|
|
bool init_op_cond() {
|
|
uint32_t res = 0;
|
|
simple_cmd_exec(MMC_GO_IDLE_STATE, 0);
|
|
if (!simple_cmd_exec_with_ret(MMC_SEND_OP_COND, 0, &res))
|
|
return false;
|
|
int retry = 100;
|
|
while(--retry > 0) {
|
|
if (!simple_cmd_exec_with_ret(MMC_SEND_OP_COND, SD_OCR_CCS | SD_OCR_VDD_18, &res))
|
|
return false;
|
|
if ((res & 0xFF000000) == (MMC_CARD_BUSY | SD_OCR_CCS))
|
|
return true;
|
|
sleep_ms(1);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
uint32_t mmc_init_table[] = {
|
|
MMC_SET_RELATIVE_ADDR, 2 << 16, 0x1E00, R1_STATE_IDENT << 9,
|
|
MMC_SELECT_CARD, 2 << 16, 0x1E00, (R1_STATE_IDENT | R1_STATE_READY) << 9,
|
|
MMC_SEND_STATUS, 2 << 16, 0x1E00, R1_STATE_TRAN << 9,
|
|
MMC_SET_BLOCKLEN, 512, 0x1E00, R1_STATE_TRAN << 9,
|
|
MMC_SWITCH, (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (EXT_CSD_PART_CONFIG << 16) | (1 << 8), 0x1E00, R1_STATE_TRAN << 9
|
|
};
|
|
|
|
bool mmc_initialize() {
|
|
if (!init_op_cond()) {
|
|
return false;
|
|
}
|
|
if (!cmd_exec_cid()) {
|
|
return false;
|
|
}
|
|
for (int i = 0; i < sizeof(mmc_init_table)/sizeof(mmc_init_table[0]); i += 4)
|
|
{
|
|
uint32_t res= 0;
|
|
if (!simple_cmd_exec_with_ret(mmc_init_table[i], mmc_init_table[i+1], &res)) {
|
|
return false;
|
|
}
|
|
if (res & mmc_init_table[i+2] != mmc_init_table[i+3]) {
|
|
return false;
|
|
}
|
|
}
|
|
for(int i = 0; i < 100; i++) {
|
|
sleep_ms(1);
|
|
if (gpio_get(PIN_DAT))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void reinit_mmc() {
|
|
bool result = true;
|
|
for (int i = 0; i <= 9; i++) {
|
|
result = mmc_initialize();
|
|
if (result) {
|
|
break;
|
|
}
|
|
}
|
|
if (!result)
|
|
{
|
|
halt_with_error(8, 4);
|
|
}
|
|
}
|
|
|
|
bool is_space_bl = false;
|
|
bool is_command = false;
|
|
|
|
void write_data(int block, const uint8_t * data, int size) {
|
|
for (int i = 0; i < size; i += 512) {
|
|
bool was_success = false;
|
|
bool copy_done = false;
|
|
bool write_done = false;
|
|
for (int j = 0; j <= 5; j++) {
|
|
if (j == 3) {
|
|
reinit_mmc();
|
|
write_done = false;
|
|
}
|
|
if (!copy_done) {
|
|
memcpy(data_buf, data + i, 512);
|
|
copy_done = true;
|
|
}
|
|
if (!write_done) {
|
|
if (!cmd_mmc_write(block + i / 512))
|
|
continue;
|
|
write_done = true;
|
|
}
|
|
if (!cmd_mmc_read(block + i / 512))
|
|
continue;
|
|
if (memcmp(data_buf, data + i, 512) == 0) {
|
|
was_success = true;
|
|
break;
|
|
}
|
|
write_done = false;
|
|
}
|
|
if (!was_success)
|
|
{
|
|
if (write_done)
|
|
halt_with_error(9, 4);
|
|
else
|
|
halt_with_error(10, 4);
|
|
}
|
|
}
|
|
}
|
|
|
|
extern int boot_slot;
|
|
uint8_t temp_buf[512];
|
|
|
|
struct fw_header {
|
|
uint32_t size;
|
|
uint32_t crc;
|
|
uint8_t data[];
|
|
};
|
|
|
|
#define fw_slot_0 ((struct fw_header *) (XIP_BASE + 0x10000))
|
|
#define fw_slot_1 ((struct fw_header *) (XIP_BASE + 0x48000))
|
|
|
|
bool do_burn_fuses = false;
|
|
|
|
bool update_firmware(uint32_t start_block, uint32_t size_blocks) {
|
|
int update_slot = boot_slot ^ 1;
|
|
int fw_offset = update_slot == 0 ? 0x10000 : 0x48000;
|
|
bool valid = true;
|
|
bool rollback = false;
|
|
memset(temp_buf, 0, 256);
|
|
*(uint32_t*)temp_buf = 0x515205c5;
|
|
write_data(1, temp_buf, 512);
|
|
// input validation
|
|
if (start_block == 0xFFFFFFFF && size_blocks == 0xFFFFFFFF) {
|
|
return true;
|
|
} else if (start_block + size_blocks > 0x1FFF) {
|
|
return false;
|
|
}
|
|
else if (size_blocks > 0x1c0 || start_block > 0x1FFF) {
|
|
return false;
|
|
}
|
|
// write the new firmware
|
|
for (int b = start_block; b < start_block + size_blocks; b++)
|
|
{
|
|
if (!cmd_mmc_read(b) && !cmd_mmc_read(b)) {
|
|
halt_with_error(12, 4);
|
|
}
|
|
if (b == start_block)
|
|
{
|
|
struct fw_header * new_fw = (struct fw_header *)data_buf;
|
|
struct fw_header * cur_fw = boot_slot ? fw_slot_1 : fw_slot_0;
|
|
if (cur_fw->crc == new_fw->crc) // do not allow to write the same firmware twice
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
int off = fw_offset + (b - start_block) * 0x200;
|
|
if (off % 0x1000 == 0)
|
|
flash_range_erase(off, 0x1000);
|
|
flash_range_program(off, data_buf, 0x200);
|
|
}
|
|
return true;
|
|
}
|
|
struct fw_info
|
|
{
|
|
uint32_t signature;
|
|
uint32_t fw_major;
|
|
uint32_t fw_minor;
|
|
uint32_t sdloader_hash;
|
|
uint32_t firmware_hash;
|
|
uint32_t fuse_count;
|
|
uint32_t bct_sub_fingerprint; // first 4 bytes of BctNormalSub
|
|
};
|
|
bool was_self_reset = false;
|
|
extern int boot_try;
|
|
bool check_and_resync_bct();
|
|
bool fast_check() {
|
|
start_mmc();
|
|
reinit_mmc();
|
|
if (!cmd_mmc_read(1) && !cmd_mmc_read(1)) {
|
|
halt_with_error(11, 4);
|
|
}
|
|
if (*(uint32_t*)(data_buf + 0x20) == (mariko ? 0xA56CA203 : 0x69696969)) {
|
|
is_space_bl = true;
|
|
}
|
|
if (*(uint32_t*)(data_buf) == 0x515205c5) { // chip reset
|
|
is_command = true;
|
|
}
|
|
if (*(uint32_t*)(data_buf) == 0x6db92148) { // firmware update
|
|
is_command = true;
|
|
if (boot_try == 0) {
|
|
// do the firmware update
|
|
if (update_firmware(*(uint32_t*)(data_buf + 4), *(uint32_t*)(data_buf + 8))) {
|
|
// stop mmc subsystem
|
|
stop_mmc();
|
|
// go to the bootloader and load new firmware
|
|
finish_pins_except_leds();
|
|
watchdog_enable(0, false);
|
|
while(1);
|
|
}
|
|
}
|
|
}
|
|
// regulator i2c
|
|
{
|
|
const static char i2c_sda[] = {0x81, 0xFF, 0x7F, 0x00, 0xF8, 0x01, 0xE0, 0xE1, 0x1F, 0x7E, 0x00, 0x00, 0x00, 0x80, 0x9F};
|
|
const static char i2c_scl[] = {0x33, 0x33, 0x33, 0x33, 0x33, 0xCC, 0xCC, 0xCC, 0xCC, 0x0C, 0x33, 0x33, 0x33, 0x33, 0xC3};
|
|
gpio_pull_up(sda_pin()); // sda
|
|
gpio_pull_up(scl_pin()); // scl
|
|
gpio_init(sda_pin());
|
|
gpio_init(scl_pin());
|
|
for (int i = 0; i < sizeof(i2c_sda); i++)
|
|
{
|
|
for (int j = 0; j < 8; j++)
|
|
{
|
|
bool sda_bit = i2c_sda[i] & (1 << j);
|
|
bool scl_bit = i2c_scl[i] & (1 << j);
|
|
gpio_set_dir(sda_pin(), !sda_bit);
|
|
gpio_set_dir(scl_pin(), !scl_bit);
|
|
sleep_us(1);
|
|
}
|
|
}
|
|
gpio_disable_pulls(sda_pin());
|
|
gpio_disable_pulls(scl_pin());
|
|
gpio_deinit(sda_pin());
|
|
gpio_deinit(scl_pin());
|
|
}
|
|
// check if a syscfw update has written a new BCT to BctNormalSub since the last
|
|
// write_payload(). write_descriptor() stores a fingerprint of BctNormalSub's
|
|
// pubkey at the time write_payload() ran. if this has changed, Atmosphere has dropped
|
|
// a Main write and Sub is now ahead, resync Sub> Main and trigger rewrite_payload().
|
|
// on every normal boot this reads two blocks and returns is_space_bl unchanged.
|
|
if (is_space_bl && cmd_mmc_read(0x1FFF)) {
|
|
struct fw_info * fwi = (struct fw_info *)data_buf;
|
|
if (fwi->signature == 0x9cabe959) {
|
|
uint32_t stored = fwi->bct_sub_fingerprint;
|
|
if (cmd_mmc_read(0x040)) {
|
|
uint32_t current = *(uint32_t *)(data_buf + 0x10);
|
|
if (stored != current) {
|
|
// BctNormalSub has changed since last write_payload(), resync and rewrite
|
|
check_and_resync_bct();
|
|
stop_mmc();
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
stop_mmc();
|
|
return is_space_bl;
|
|
}
|
|
|
|
void copy_bct(int start, int end) {
|
|
for (int i = 0; i < 0x2800; i += 0x200) {
|
|
bool was_success = false;
|
|
bool was_read = false;
|
|
bool was_write = false;
|
|
for (int k = 0; k <= 5; k++) {
|
|
if (k == 3) {
|
|
reinit_mmc();
|
|
was_read = false;
|
|
was_write = false;
|
|
}
|
|
if (!was_read) {
|
|
if (!cmd_mmc_read(start + i / 512))
|
|
continue;
|
|
was_read = true;
|
|
memcpy(temp_buf, data_buf, 512);
|
|
}
|
|
if (!was_write) {
|
|
if (!cmd_mmc_write(end + i / 512))
|
|
continue;
|
|
was_write = true;
|
|
}
|
|
if (!cmd_mmc_read(end + i / 512))
|
|
continue;
|
|
if (memcmp(data_buf, temp_buf, 512) == 0) {
|
|
was_success = true;
|
|
break;
|
|
}
|
|
was_read = false;
|
|
was_write = false;
|
|
}
|
|
if (!was_success) {
|
|
if (was_read && !was_write)
|
|
halt_with_error(13, 4);
|
|
else if (was_write)
|
|
halt_with_error(14, 4);
|
|
else
|
|
halt_with_error(15, 4);
|
|
}
|
|
}
|
|
}
|
|
|
|
extern bool do_burn_fuses;
|
|
|
|
void write_descriptor()
|
|
{
|
|
int desc_block = 0x1FFF;
|
|
// prepare firmware information
|
|
memset(temp_buf, 0, 512);
|
|
struct fw_info * fwi = (struct fw_info*)temp_buf;
|
|
fwi->signature = 0x9cabe959;
|
|
fwi->fuse_count = count_fuses();
|
|
if((fwi->fuse_count & 1) != boot_slot)
|
|
fwi->fuse_count += 1;
|
|
fwi->fw_major = VER_HI;
|
|
fwi->fw_minor = VER_LO;
|
|
fwi->sdloader_hash = payload_crc();
|
|
fwi->firmware_hash = boot_slot ? fw_slot_1->crc : fw_slot_0->crc;
|
|
// store first 4 bytes of BctNormalSub pubkey
|
|
// so we can detect on next boot if a syscfw update has written a new BCT to Sub
|
|
fwi->bct_sub_fingerprint = 0;
|
|
if (cmd_mmc_read(0x040))
|
|
fwi->bct_sub_fingerprint = *(uint32_t *)(data_buf + 0x10);
|
|
// write the info block
|
|
write_data(desc_block, temp_buf, 512);
|
|
}
|
|
|
|
unsigned char data_bct[0x2800];
|
|
|
|
void prepare_erista_bct()
|
|
{
|
|
memset(data_bct, 0, 0x2800);
|
|
data_bct[1] = 2;
|
|
data_bct[4] = 0xF;
|
|
data_bct[5] = 0xe;
|
|
data_bct[0x210] = 0x59;
|
|
memset(data_bct + 0x211, 0x69, 0xFE);
|
|
data_bct[0x30F] = 0xFF;
|
|
memcpy(data_bct + 0x320, erista_bct_sign, 0x100);
|
|
memcpy(data_bct + 0x232C, erista_bct_sd_sign, 0x130);
|
|
data_bct[0x530] = 0x1;
|
|
data_bct[0x53F] = 0x1;
|
|
data_bct[0x540] = 0x1;
|
|
data_bct[0x532] = 0x21;
|
|
data_bct[0x534] = 0x0E;
|
|
data_bct[0x544] = 0x04;
|
|
data_bct[0x538] = 0x09;
|
|
data_bct[0x548] = 0x09;
|
|
data_bct[0x54C] = 0x02;
|
|
data_bct[0x27EC] = 0x80;
|
|
}
|
|
|
|
void prepare_mariko_bct()
|
|
{
|
|
memset(data_bct, 0, 0x2800);
|
|
data_bct[1] = 8;
|
|
data_bct[0x10] = 0x59;
|
|
memset(data_bct + 0x11, 0x69, 0xFE);
|
|
data_bct[0x10F] = 0xFF;
|
|
memcpy(data_bct + 0x220, mariko_bct_sign, 0x100);
|
|
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);
|
|
}
|
|
|
|
bool 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 false;
|
|
|
|
// 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);
|
|
|
|
bool resynced = false;
|
|
|
|
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);
|
|
resynced = true;
|
|
}
|
|
|
|
// 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);
|
|
resynced = true;
|
|
}
|
|
|
|
return resynced;
|
|
}
|
|
|
|
void write_payload() {
|
|
static bool prepared = false;
|
|
if (!prepared)
|
|
{
|
|
if (!mariko)
|
|
prepare_erista_bct();
|
|
else
|
|
prepare_mariko_bct();
|
|
prepared = true;
|
|
}
|
|
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);
|
|
copy_bct(0x20, 0x7C0);
|
|
}
|
|
write_data(0x1F80, payload, sizeof(payload));
|
|
write_data(0x0, data_bct, 0x2800);
|
|
write_data(0x20, data_bct, 0x2800);
|
|
write_descriptor();
|
|
stop_mmc();
|
|
if (!is_space_bl && !is_command && !was_self_reset)
|
|
halt_with_error(0, 0);
|
|
}
|