bootloader: cleanup, more documentation, added scripts for checking

pull/25/head
Pavol Rusnak 7 years ago
parent 4c8d3e4a35
commit 2d7c6b0c34
No known key found for this signature in database
GPG Key ID: 91F3B339B9A02A3D

@ -13,7 +13,7 @@ vendor: ## update git submodules
git submodule update --init
res: ## update resources
./tools/res_collect.py
./tools/res_collect
build: build_stmhal build_unix build_cross ## build stmhal, unix and mpy-cross micropython ports

@ -1,15 +1,26 @@
#TREZOR Core Bootloader
Bootloader code is split into two stages. See [Memory Layout](memory.md) for more info about in which sectors the code is stored.
Bootloader is split into two stages. See [Memory Layout](memory.md) for info about in which sectors each stage is stored.
First stage checks the integrity and signatures of second stage and runs it if everything is OK.
However, if first stage bootloader finds a valid second stage bootloader on the SD card (in raw format, no filesystem), it will replace the internal second stage, allowing an upgrade of the second stage.
First stage is stored in write-protected area, which means it is non-upgradable. Only second stage bootloader update is allowed.
First stage is stored in write-protected area, which means only upgrade of the second stage bootloader is allowed.
##First Stage Bootloader
First stage checks the integrity and signatures of the second stage and runs it if everything is OK.
If first stage bootloader finds a valid second stage bootloader image on the SD card (in raw format, no filesystem),
it will replace the internal second stage, allowing a second stage update via SD card.
##Second Stage Bootloader
Second stage checks the integrity and signatures of the firmware and runs it if everything is OK.
If second stage bootloader detects a pressed finger on the display or there is no firmware loaded in the device,
it will start in a firmware update mode, allowing a firmware update via USB.
##Common notes
* Hash function used is SHA-256 and signature system is Ed25519 (allows combining signatures by multiple keys into one).
* Hash function used below is SHA-256 and signature system is Ed25519 (allows combining signatures by multiple keys into one).
* All multibyte integer values are little endian.
##Bootloader Format
@ -19,6 +30,8 @@ TREZOR Core (second stage) bootloader consists of 2 parts:
1. bootloader header
2. bootloader code
There is a tool called [check_bootloader](../tools/check_bootloader) which parses and checks validity of the bootloader including its header.
###Bootloader Header
Total length of bootloader header is 256 bytes.
@ -26,16 +39,16 @@ Total length of bootloader header is 256 bytes.
| offset | length | name | description |
|-------:|-------:|------|-------------|
| 0x0000 | 4 | magic | firmware magic `TRZB` |
| 0x0004 | 4 | hlen | length of the bootloader header |
| 0x0004 | 4 | hdrlen | length of the bootloader header |
| 0x0008 | 4 | expiry | valid until timestamp (0=infinity) |
| 0x000C | 4 | codelen | length of the bootloader code |
| 0x000C | 4 | codelen | length of the bootloader code (without the header) |
| 0x0010 | 1 | vmajor | version (major) |
| 0x0011 | 1 | vminor | version (minor) |
| 0x0012 | 1 | vpatch | version (patch) |
| 0x0013 | 1 | vbuild | version (build) |
| 0x0014 | 1 | slsigidx | SatoshiLabs signature indexes (bitmap) |
| 0x0015 | 64 | slsig | SatoshiLabs signature |
| 0x0079 | 135 | reserved | not used yet |
| 0x0014 | 1 | sigidx | SatoshiLabs signature indexes (bitmap) |
| 0x0015 | 64 | sig | SatoshiLabs signature |
| 0x0055 | 171 | reserved | not used yet (zeroed) |
##Firmware Format
@ -45,6 +58,8 @@ TREZOR Core firmware consists of 3 parts:
2. firmware header
3. firmware code
There is a tool called [check_firmware](../tools/check_firmware) which parses and checks validity of the firmware including the both headers.
###Vendor Header
Total length of vendor header is 82 + 32 * (number of pubkeys) + (length of vendor string) + (length of vendor image) bytes rounded up to the closest multiply of 256 bytes.
@ -52,19 +67,19 @@ Total length of vendor header is 82 + 32 * (number of pubkeys) + (length of vend
| offset | length | name | description |
|-------:|-------:|------|-------------|
| 0x0000 | 4 | magic | firmware magic `TRZV` |
| 0x0004 | 4 | hlen | length of the vendor header |
| 0x0004 | 4 | hdrlen | length of the vendor header |
| 0x0008 | 4 | expiry | valid until timestamp (0=infinity) |
| 0x000C | 1 | vsig_m | number of signatures needed to run the firmware from this vendor |
| 0x000D | 1 | vsig_n | number of pubkeys vendor wants to use for signing |
| 0x000E | 2 | reserved | not used yet |
| 0x000E | 2 | reserved | not used yet (zeroed) |
| 0x0010 | 32 | vpub1 | vendor pubkey 1 |
| ... | ... | ... | ... |
| ? | 32 | vpubn | vendor pubkey n |
| ? | 1 | vstr_len | vendor string length |
| ? | ? | vstr | vendor string |
| ? | ? | vimg | vendor image (in [TOIf format](toif.md)) |
| ? | 1 | slsigidx | SatoshiLabs signature indexes (bitmap) |
| ? | 64 | slsig | SatoshiLabs signature |
| ? | 1 | sigidx | SatoshiLabs signature indexes (bitmap) |
| ? | 64 | sig | SatoshiLabs signature |
###Firmware Header
@ -73,16 +88,16 @@ Total length of firmware header is 256 bytes.
| offset | length | name | description |
|-------:|-------:|------|-------------|
| 0x0000 | 4 | magic | firmware magic `TRZF` |
| 0x0004 | 4 | hlen | length of the firmware header |
| 0x0004 | 4 | hdrlen | length of the firmware header |
| 0x0008 | 4 | expiry | valid until timestamp (0=infinity) |
| 0x000C | 4 | codelen | length of the firmware code |
| 0x000C | 4 | codelen | length of the firmware code (without the header) |
| 0x0010 | 1 | vmajor | version (major) |
| 0x0011 | 1 | vminor | version (minor) |
| 0x0012 | 1 | vpatch | version (patch) |
| 0x0013 | 1 | vbuild | version (build) |
| 0x0014 | 1 | vndsigidx | vendor signature indexes (bitmap) |
| 0x0015 | 64 | vndsig | vendor signature |
| 0x0079 | 135 | reserved | not used yet |
| 0x0014 | 1 | sigidx | vendor signature indexes (bitmap) |
| 0x0015 | 64 | sig | vendor signature |
| 0x0055 | 171 | reserved | not used yet (zeroed) |
##Various ideas

@ -4,18 +4,18 @@
| sector | range | size | function
|-----------|-------------------------|--------:|----------------------
| Sector 0 | 0x08000000 - 0x08003FFF | 16 KiB | bootloader 1st stage
| Sector 1 | 0x08004000 - 0x08007FFF | 16 KiB | storage area
| Sector 0 | 0x08000000 - 0x08003FFF | 16 KiB | bootloader 1st stage (write-protected)
| Sector 1 | 0x08004000 - 0x08007FFF | 16 KiB | bootloader 1st stage (write-protected)
| Sector 2 | 0x08008000 - 0x0800BFFF | 16 KiB | storage area
| Sector 3 | 0x0800C000 - 0x0800FFFF | 16 KiB | storage area
| Sector 4 | 0x08010000 - 0x0801FFFF | 64 KiB | bootloader 2nd stage
| Sector 5 | 0x08020000 - 0x0803FFFF | 128 KiB | application code
| Sector 6 | 0x08040000 - 0x0805FFFF | 128 KiB | application code
| Sector 7 | 0x08060000 - 0x0807FFFF | 128 KiB | application code
| Sector 8 | 0x08080000 - 0x0809FFFF | 128 KiB | application code
| Sector 9 | 0x080A0000 - 0x080BFFFF | 128 KiB | application code
| Sector 10 | 0x080C0000 - 0x080DFFFF | 128 KiB | application code
| Sector 11 | 0x080E0000 - 0x080FFFFF | 128 KiB | application code
| Sector 5 | 0x08020000 - 0x0803FFFF | 128 KiB | firmware
| Sector 6 | 0x08040000 - 0x0805FFFF | 128 KiB | firmware
| Sector 7 | 0x08060000 - 0x0807FFFF | 128 KiB | firmware
| Sector 8 | 0x08080000 - 0x0809FFFF | 128 KiB | firmware
| Sector 9 | 0x080A0000 - 0x080BFFFF | 128 KiB | firmware
| Sector 10 | 0x080C0000 - 0x080DFFFF | 128 KiB | firmware
| Sector 11 | 0x080E0000 - 0x080FFFFF | 128 KiB | firmware
##RAM

@ -80,6 +80,7 @@ SRC_LIB = $(addprefix lib/,\
)
SRC_C = \
bootloader/basic.c \
bootloader/bootloader.c \
bootloader/crypto.c \
bootloader/ui.c \

@ -0,0 +1,26 @@
#include STM32_HAL_H
// ### from main.c
void __attribute__((noreturn)) __fatal_error(const char *msg) {
for (volatile uint32_t delay = 0; delay < 10000000; delay++) {
}
// TODO: printf("FATAL ERROR: %s\n", msg);
for (;;) {
__WFI();
}
}
void nlr_jump_fail(void *val) {
__fatal_error("FATAL: uncaught exception");
}
// ### from stm32_it.c
void SysTick_Handler(void) {
extern uint32_t uwTick;
uwTick += 1;
SysTick->CTRL;
}
// ###

@ -2,75 +2,10 @@
#include "crypto.h"
#include "ui.h"
#include "display.h"
// ### from main.c
void flash_error(int n) {
for (int i = 0; i < n; i++) {
// blink(on)
HAL_Delay(250);
// blink(off)
HAL_Delay(250);
}
}
void __attribute__((noreturn)) __fatal_error(const char *msg) {
for (volatile uint32_t delay = 0; delay < 10000000; delay++) {
}
// TODO: printf("FATAL ERROR: %s\n", msg);
for (;;) {
__WFI();
}
}
void nlr_jump_fail(void *val) {
__fatal_error("FATAL: uncaught exception");
}
void SystemClock_Config(void);
// ### from stm32_it.c
/**
* @brief This function handles SysTick Handler.
* @param None
* @retval None
*/
void SysTick_Handler(void) {
// Instead of calling HAL_IncTick we do the increment here of the counter.
// This is purely for efficiency, since SysTick is called 1000 times per
// second at the highest interrupt priority.
// Note: we don't need uwTick to be declared volatile here because this is
// the only place where it can be modified, and the code is more efficient
// without the volatile specifier.
extern uint32_t uwTick;
uwTick += 1;
// Read the systick control regster. This has the side effect of clearing
// the COUNTFLAG bit, which makes the logic in sys_tick_get_microseconds
// work properly.
SysTick->CTRL;
#if 0
// Right now we have the storage and DMA controllers to process during
// this interrupt and we use custom dispatch handlers. If this needs to
// be generalised in the future then a dispatch table can be used as
// follows: ((void(*)(void))(systick_dispatch[uwTick & 0xf]))();
if (STORAGE_IDLE_TICK(uwTick)) {
NVIC->STIR = FLASH_IRQn;
}
if (DMA_IDLE_ENABLED() && DMA_IDLE_TICK(uwTick)) {
dma_idle_handler(uwTick);
}
#endif
}
// ###
int main(void) {
HAL_Init();
@ -101,7 +36,5 @@ int main(void) {
HAL_Delay(250);
}
__fatal_error("end");
return 0;
}

@ -10,7 +10,7 @@
void screen_welcome(void)
{
display_image(0, 0, 240, 240, toi_trezor, sizeof(toi_trezor));
display_icon(0, 0, 240, 240, toi_trezor, sizeof(toi_trezor), ui_WHITE, ui_BLACK);
display_text(0, 240, "bootloader", 10, FONT_MONO, ui_WHITE, ui_BLACK);
}

@ -0,0 +1,38 @@
#!/usr/bin/env python3
import sys
import struct
# bootloader header specification: https://github.com/trezor/trezor-core/blob/master/docs/bootloader.md#bootloader-header
def check_bootloader(filename):
data = open(filename, 'rb').read()
hdr = struct.unpack('<4sIIIBBBBB64s171s', data[:256])
code = data[256:]
magic, hdrlen, expiry, codelen, vmajor, vminor, vpatch, vbuild, sigidx, sig, reserved = hdr
sigidx = tuple([ i + 1 for i in range(8) if sigidx & (1 << i) ])
print('magic :', magic)
assert magic == b'TRZB'
print('hdrlen :', hdrlen)
assert hdrlen == 256
print('expiry :', expiry)
print('codelen :', codelen)
print('vmajor :', vmajor)
print('vminor :', vminor)
print('vpatch :', vpatch)
print('vbuild :', vbuild)
print('sigidx :', sigidx)
print('sig :', sig)
assert reserved == 171 * b'\x00'
assert codelen == 64*1024
assert codelen == len(code)
def main():
if len(sys.argv) < 2:
print('Usage: check_bootloader bootloader.bin')
return 1
fn = sys.argv[1]
check_bootloader(fn)
main()

@ -0,0 +1,17 @@
#!/usr/bin/env python3
import sys
def process_firmware(filename):
data = open(filename, 'rb').read()
def main():
if len(sys.argv) < 2:
print('Usage: check_firmware firmware.bin')
return 1
fn = sys.argv[1]
process_firmware(fn)
main()

@ -73,7 +73,7 @@ def process_image(ifn):
def main():
if len(sys.argv) < 2:
print('Usage toi2png image.toi[fg]')
print('Usage: toi2png image.toi[fg]')
return 1
ifn = sys.argv[1]

Loading…
Cancel
Save