new flash layout
This commit is contained in:
parent
a213801f99
commit
820f717f69
@ -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.
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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 = {
|
||||
|
||||
@ -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).
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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; i<numberof(rom_secrets->mcu_keys); i++, ptr++) {
|
||||
for(int i=0; i<NUM_MCU_KEYS; i++, ptr++) {
|
||||
if(ptr->value[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; i<numberof(rom_secrets->mcu_keys); i++, ptr++) {
|
||||
for(int i=0; i<NUM_MCU_KEYS; i++, ptr++) {
|
||||
if(ptr->value[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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
// 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[];
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user