diff --git a/stm32/mk4-bootloader/Makefile b/stm32/mk4-bootloader/Makefile index 78ff40dd..3ab3c569 100644 --- a/stm32/mk4-bootloader/Makefile +++ b/stm32/mk4-bootloader/Makefile @@ -43,17 +43,18 @@ OBJS += stm32l4xx_hal_sd.o stm32l4xx_ll_sdmmc.o micro-ecc/uECC.o: c_flags += -Wno-undef -Wno-redundant-decls # Where we will end up in the memory map (at start of flash) -# - reserve last 8k flash page for other purposes +# - reserve last 2x 8k flash pages for other purposes BL_FLASH_BASE = 0x08000000 -BL_FLASH_SIZE = 0x1e000 -BL_FLASH_LAST = 0x0801e000 +BL_FLASH_SIZE = 0x1c000 +BL_FLASH_LAST = 0x0801c000 +# Where micropython ends up in flash. MPY_FLASH_BASE = 0x08020000 # Final 8k bytes of flash reserved for secret data (not code) # - must be page-aligned, contains pairing secret -BL_NVROM_BASE = 0x0801e000 # = BL_FLASH_LAST -BL_NVROM_SIZE = 0x2000 # = 8k +BL_NVROM_BASE = 0x0801c000 # = BL_FLASH_LAST +BL_NVROM_SIZE = 0x4000 # = 16k # Top 8k of SRAM3 is reserved for us. # We wipe that area before and after using it. diff --git a/stm32/mk4-bootloader/dispatch.c b/stm32/mk4-bootloader/dispatch.c index 4358287b..a2483969 100644 --- a/stm32/mk4-bootloader/dispatch.c +++ b/stm32/mk4-bootloader/dispatch.c @@ -561,8 +561,9 @@ firewall_dispatch(int method_num, uint8_t *buf_io, int len_in, int *avail = (int *)(buf_io+0); int *consumed = (int *)(buf_io+4); + int *total = (int *)(buf_io+8); - mcu_key_usage(avail, consumed); + mcu_key_usage(avail, consumed, total); break; } diff --git a/stm32/mk4-bootloader/firewall.c b/stm32/mk4-bootloader/firewall.c index 42721932..0b4291ba 100644 --- a/stm32/mk4-bootloader/firewall.c +++ b/stm32/mk4-bootloader/firewall.c @@ -31,6 +31,9 @@ firewall_setup(void) return; } +#if 0 +enabled firewall, even debg + #if RELEASE // REMINDERS: // - cannot debug anything in boot loader w/ firewall enabled (no readback, no bkpt) @@ -44,13 +47,15 @@ firewall_setup(void) #else // for debug builds, never enable firewall return; +#endif #endif extern int firewall_starts; // see startup.S ... aligned@256 (0x08000300) uint32_t start = (uint32_t)&firewall_starts; uint32_t len = BL_FLASH_SIZE - (start - BL_FLASH_BASE); -#if 1 +#if 0 + // passing fine ASSERT(start); ASSERT(!(start & 0xff)); ASSERT(len>256); @@ -64,12 +69,10 @@ firewall_setup(void) // // - many of the bits in these registers are not-implemented and are forced to zero // - that prevents the firewall being used for things like protecting OTP area - // - (Mk1-3) volatile data is SRAM1 only, so doesn't help us, since we're using SRAM2 - // - (Mk4) we are in SRAM1, so we could protect all our RAM ... but errata 2.4.2 fucks that // - on-chip DFU will erase up to start (0x300), which borks the reset vector // but sensitive stuff is still there (which would allow bypass) - // - so it's important to enable option bytes to set write-protect on entire bootloader - // - to disable debug and complete protection, must enable write-protect "level 2" + // - so it's important to enable option bytes to set write-protect flash of entire bootloader + // - to disable debug and complete protection, must enable write-protect "level 2" (RDP=2) // FIREWALL_InitTypeDef init = { diff --git a/stm32/mk4-bootloader/link-script.ld b/stm32/mk4-bootloader/link-script.ld index 96c0da49..418957d3 100644 --- a/stm32/mk4-bootloader/link-script.ld +++ b/stm32/mk4-bootloader/link-script.ld @@ -91,6 +91,7 @@ SECTIONS /* Some very manual linking! I've tried doing it right, and couldn't get it to work well */ addr_rom_secrets = BL_NVROM_BASE; + addr_mcu_keys = BL_NVROM_BASE + 0x2000; /* if you initialize a global var to some non-zero value, then that data ends up in .relocate as read-only data and used briefly at startup (when copied to RAM). diff --git a/stm32/mk4-bootloader/pins.c b/stm32/mk4-bootloader/pins.c index 98e4e769..7dcc446b 100644 --- a/stm32/mk4-bootloader/pins.c +++ b/stm32/mk4-bootloader/pins.c @@ -48,6 +48,7 @@ void pin_setup0(void) // We want to block any cached PIN value from previous runs working // after a reboot, so we include a non-secret nonce that is picked at // power up. Challenge is we don't have any non-volatile RAM space. + // Ideally, this value would not be changable by mpy, but we don't have any of that. // Populate unused registers in CRC unit w/ some noise __HAL_RCC_CRC_CLK_ENABLE(); diff --git a/stm32/mk4-bootloader/psram.c b/stm32/mk4-bootloader/psram.c index b8bd43d6..d9bff654 100644 --- a/stm32/mk4-bootloader/psram.c +++ b/stm32/mk4-bootloader/psram.c @@ -327,19 +327,35 @@ psram_do_upgrade(const uint8_t *start, uint32_t size) for(uint32_t pos=0; pos < size; pos += 8) { uint32_t dest = FIRMWARE_START+pos; - if(dest % (4*FLASH_PAGE_SIZE) == 0) { + if(dest % (4*FLASH_ERASE_SIZE) == 0) { // show some progress oled_show_progress(screen_upgrading, pos*100/size); } - if(dest % FLASH_PAGE_SIZE == 0) { + if(dest % FLASH_ERASE_SIZE == 0) { // page erase as we go rv = flash_page_erase(dest); +#if 1 + if(rv) { + puts2("erase rv="); + puthex2(rv); + putchar('\n'); + } +#endif ASSERT(rv == 0); } memcpy(&tmp, start+pos, 8); rv = flash_burn(dest, tmp); +#if 1 + if(rv) { + puts2("burn rv="); + puthex2(rv); + puts2(" addr="); + puthex8(dest); + putchar('\n'); + } +#endif ASSERT(rv == 0); } diff --git a/stm32/mk4-bootloader/secrets.h b/stm32/mk4-bootloader/secrets.h index c40ab0f5..8498498d 100644 --- a/stm32/mk4-bootloader/secrets.h +++ b/stm32/mk4-bootloader/secrets.h @@ -3,6 +3,7 @@ // This is what we're keeping secret... Kept in flash, written exactly once. // field groups must be **64-bit=8byte** aligned so they can be written independently. +// - contents cannot be changed once unit is "bagged" and flash protection set typedef struct { // Pairing secret: picked once at factory when turned on // for the first time, along with most values here. @@ -25,18 +26,20 @@ typedef struct { uint8_t auth_pubkey[64]; // aka pubkey C (AUTH) in SE2, and privkey in SE1 } se2; - // Replaceable MCU keys; can be overwritten; use first non zero/ones value. - struct _mcu_key_t { - uint8_t value[32]; - } mcu_keys[128]; - - // ... plus lots more space ... + // ... plus lots more unused space ... } rom_secrets_t; -// This area is defined in linker script as last page of boot loader flash. -#define rom_secrets ((rom_secrets_t *)BL_NVROM_BASE) +// Replaceable MCU keys; can be overwritten; use first non zero/ones value. +typedef struct _mcu_key_t { + uint8_t value[32]; +} mcu_key_t; + +// This area is defined in linker script as last 2 pages of boot loader flash. +#define rom_secrets ((const rom_secrets_t *)BL_NVROM_BASE) + +#define MCU_KEYS ((mcu_key_t *)(BL_NVROM_BASE + 0x2000)) +#define NUM_MCU_KEYS (0x2000 / 32) typedef struct _se2_secrets_t se2_secrets_t; -typedef struct _mcu_key_t mcu_key_t; // EOF diff --git a/stm32/mk4-bootloader/storage.c b/stm32/mk4-bootloader/storage.c index 36b7b681..78e12f85 100644 --- a/stm32/mk4-bootloader/storage.c +++ b/stm32/mk4-bootloader/storage.c @@ -10,7 +10,6 @@ // // #include "basics.h" -#include "storage.h" #include "rng.h" #include "oled.h" #include "ae.h" @@ -22,11 +21,13 @@ #include "assets/screens.h" #include "stm32l4xx_hal.h" #include "constant_time.h" +#include "storage.h" // Number of flash pages to write-protect (ie. our size in flash pages) // - written into FLASH->WRP1AR // - once done, can't change bootrom via DFU (no error given, but doesn't change) -const uint32_t num_pages_locked = ((BL_FLASH_SIZE + BL_NVROM_SIZE) / FLASH_PAGE_SIZE)-1; // == 15 +// - MCU_KEYS area (page) says writable +const uint32_t num_pages_locked = ((BL_FLASH_SIZE + FLASH_PAGE_SIZE) / FLASH_PAGE_SIZE)-1; // == 14 // flash_setup0() // @@ -43,6 +44,10 @@ flash_setup0(void) // turn on clock to flash registers __HAL_RCC_FLASH_CLK_ENABLE(); + + STATIC_ASSERT(FLASH_PAGE_SIZE == 0x2000); // 8k pages, because DBANK=0 + STATIC_ASSERT(num_pages_locked == 14); + STATIC_ASSERT((uint32_t)MCU_KEYS == BL_FLASH_BASE + BL_FLASH_SIZE + FLASH_PAGE_SIZE); } // _flash_wait_done() @@ -184,7 +189,6 @@ flash_burn(uint32_t address, uint64_t val) *(__IO uint32_t *)(address+4) = (uint32_t)(val >> 32); rv = _flash_wait_done(); - if(rv) return rv; // If the program operation is completed, disable the PG or FSTPG Bit CLEAR_BIT(FLASH->CR, FLASH_CR_PG); @@ -193,7 +197,7 @@ flash_burn(uint32_t address, uint64_t val) __HAL_FLASH_DATA_CACHE_RESET(); __HAL_FLASH_DATA_CACHE_ENABLE(); - return 0; + return rv; } // flash_page_erase() @@ -205,10 +209,10 @@ flash_burn(uint32_t address, uint64_t val) int flash_page_erase(uint32_t address) { - uint32_t page_num = (address & 0x7ffffff) / FLASH_PAGE_SIZE; + uint32_t page_num = (address & 0x7ffffff) / FLASH_ERASE_SIZE; // protect ourselves! - if(page_num < ((BL_FLASH_SIZE + BL_NVROM_SIZE) / FLASH_PAGE_SIZE)) { + if(page_num < ((BL_FLASH_SIZE + BL_NVROM_SIZE) / FLASH_ERASE_SIZE)) { return 1; } @@ -238,7 +242,7 @@ flash_page_erase(uint32_t address) SET_BIT(FLASH->CR, FLASH_CR_STRT); // Wait til done - _flash_wait_done(); + int rv = _flash_wait_done(); // If the erase operation is completed, disable the PER Bit CLEAR_BIT(FLASH->CR, (FLASH_CR_PER | FLASH_CR_PNB)); @@ -247,7 +251,7 @@ flash_page_erase(uint32_t address) __HAL_FLASH_DATA_CACHE_RESET(); __HAL_FLASH_DATA_CACHE_ENABLE(); - return 0; + return rv; } @@ -542,7 +546,7 @@ flash_lockdown_hard(uint8_t rdp_level_code) // see FLASH_OB_WRPConfig() flash_ob_lock(false); - // lock first 32k against any writes + // lock first 128k against any writes FLASH->WRP1AR = (num_pages_locked << 16); FLASH->WRP1BR = 0xff; // unused. FLASH->WRP2AR = 0xff; // unused. @@ -602,9 +606,9 @@ mcu_key_get(bool *valid) // get current "mcu_key" value; first byte will never be 0x0 or 0xff // - except if no key set yet/recently wiped // - if none set, returns ptr to first available slot which will be all ones - const mcu_key_t *ptr = rom_secrets->mcu_keys, *avail=NULL; + const mcu_key_t *ptr = MCU_KEYS, *avail=NULL; - for(int i=0; imcu_keys); i++, ptr++) { + for(int i=0; ivalue[0] == 0xff) { if(!avail) { avail = ptr; @@ -633,8 +637,6 @@ mcu_key_clear(const mcu_key_t *cur) if(!valid) return; } - STATIC_ASSERT(offsetof(rom_secrets_t, mcu_keys) % 8 == 0); - // no delays here since decision has been made, and don't // want to give them more time to interrupt us flash_setup0(); @@ -650,12 +652,12 @@ mcu_key_clear(const mcu_key_t *cur) // mcu_key_usage() // void -mcu_key_usage(int *avail_out, int *consumed_out) +mcu_key_usage(int *avail_out, int *consumed_out, int *total_out) { - const mcu_key_t *ptr = rom_secrets->mcu_keys; + const mcu_key_t *ptr = MCU_KEYS; int avail = 0, used = 0; - for(int i=0; imcu_keys); i++, ptr++) { + for(int i=0; ivalue[0] == 0xff) { avail ++; } else if(ptr->value[0] == 0x00) { @@ -665,6 +667,7 @@ mcu_key_usage(int *avail_out, int *consumed_out) *avail_out = avail; *consumed_out = used; + *total_out = NUM_MCU_KEYS; } // mcu_key_pick() @@ -681,6 +684,7 @@ mcu_key_pick(void) sha256_single(n.value, 32, n.value); } while(n.value[0] == 0x0 || n.value[0] == 0xff); + int err = 0; const mcu_key_t *cur; do { @@ -689,7 +693,7 @@ mcu_key_pick(void) if(!cur) { // no free slots. we are brick. - puts("mk full"); + puts("mcu full"); oled_show(screen_brick); LOCKUP_FOREVER(); @@ -716,17 +720,35 @@ mcu_key_pick(void) uint64_t v; memcpy(&v, fr, sizeof(v)); - flash_burn(pos, v); + err = flash_burn(pos, v); + if(err) break; } flash_lock(); -#if 1 - // check it + // NOTE: Errors not expected, but lets be graceful about them. + + if(err) { + // what to do? + puts("burn fail: "); + puthex2(err); + putchar('\n'); + + return NULL; + } + + // check it carefully bool valid = false; const mcu_key_t *after = mcu_key_get(&valid); - ASSERT(valid); - ASSERT(after == cur); -#endif + + if(!valid) { + puts("!valid?"); + return NULL; + } + + if(after != cur || !check_equal(after->value, n.value, 32)) { + puts("bad val?"); + return NULL; + } return cur; } diff --git a/stm32/mk4-bootloader/storage.h b/stm32/mk4-bootloader/storage.h index f68c84a6..9683f53e 100644 --- a/stm32/mk4-bootloader/storage.h +++ b/stm32/mk4-bootloader/storage.h @@ -8,6 +8,14 @@ #include "secrets.h" #include "stm32l4xx_hal.h" +// ../../external/micropython/lib/stm32lib/STM32L4xx_HAL_Driver/Inc/stm32l4xx_hal_flash.h +// has 3 values, but 8k is right one for our setup. DBANK=0 +#undef FLASH_PAGE_SIZE +#define FLASH_PAGE_SIZE ((uint32_t)0x2000) + +// but when erasing "pages" they are half as big, since only in one physical bank +#define FLASH_ERASE_SIZE ((uint32_t)0x1000) + // Details of the OTP area. 64-bit slots. #define OPT_FLASH_BASE 0x1FFF7000 #define NUM_OPT_SLOTS 128 @@ -47,7 +55,7 @@ int record_highwater_version(const uint8_t timestamp[8]); const mcu_key_t *mcu_key_get(bool *valid); void mcu_key_clear(const mcu_key_t *cur); const mcu_key_t *mcu_key_pick(void); -void mcu_key_usage(int *avail_out, int *consumed_out); +void mcu_key_usage(int *avail_out, int *consumed_out, int *total_out); void fast_brick(void); void fast_wipe(void); diff --git a/stm32/mk4-bootloader/version.h b/stm32/mk4-bootloader/version.h index 61f3de1b..a82433f6 100644 --- a/stm32/mk4-bootloader/version.h +++ b/stm32/mk4-bootloader/version.h @@ -5,7 +5,7 @@ #include // Public version number for humans. Lots more version data added by Makefile. -#define RELEASE_VERSION "3.0.0" +#define RELEASE_VERSION "3.0.2" extern const char version_string[];