new flash layout

This commit is contained in:
Peter D. Gray 2022-02-01 12:42:52 -05:00
parent a213801f99
commit 820f717f69
No known key found for this signature in database
GPG Key ID: F0E6CC6AFC16CF7B
10 changed files with 103 additions and 47 deletions

View File

@ -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.

View File

@ -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;
}

View File

@ -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 = {

View File

@ -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).

View File

@ -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();

View File

@ -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);
}

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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[];