mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-22 15:38:11 +00:00
bootloader: cleanup, more documentation, added scripts for checking
This commit is contained in:
parent
4c8d3e4a35
commit
2d7c6b0c34
2
Makefile
2
Makefile
@ -13,7 +13,7 @@ vendor: ## update git submodules
|
|||||||
git submodule update --init
|
git submodule update --init
|
||||||
|
|
||||||
res: ## update resources
|
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
|
build: build_stmhal build_unix build_cross ## build stmhal, unix and mpy-cross micropython ports
|
||||||
|
|
||||||
|
@ -1,15 +1,26 @@
|
|||||||
#TREZOR Core Bootloader
|
#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.
|
First stage is stored in write-protected area, which means it is non-upgradable. Only second stage bootloader update is allowed.
|
||||||
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 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
|
##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.
|
* All multibyte integer values are little endian.
|
||||||
|
|
||||||
##Bootloader Format
|
##Bootloader Format
|
||||||
@ -19,6 +30,8 @@ TREZOR Core (second stage) bootloader consists of 2 parts:
|
|||||||
1. bootloader header
|
1. bootloader header
|
||||||
2. bootloader code
|
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
|
###Bootloader Header
|
||||||
|
|
||||||
Total length of bootloader header is 256 bytes.
|
Total length of bootloader header is 256 bytes.
|
||||||
@ -26,16 +39,16 @@ Total length of bootloader header is 256 bytes.
|
|||||||
| offset | length | name | description |
|
| offset | length | name | description |
|
||||||
|-------:|-------:|------|-------------|
|
|-------:|-------:|------|-------------|
|
||||||
| 0x0000 | 4 | magic | firmware magic `TRZB` |
|
| 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) |
|
| 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) |
|
| 0x0010 | 1 | vmajor | version (major) |
|
||||||
| 0x0011 | 1 | vminor | version (minor) |
|
| 0x0011 | 1 | vminor | version (minor) |
|
||||||
| 0x0012 | 1 | vpatch | version (patch) |
|
| 0x0012 | 1 | vpatch | version (patch) |
|
||||||
| 0x0013 | 1 | vbuild | version (build) |
|
| 0x0013 | 1 | vbuild | version (build) |
|
||||||
| 0x0014 | 1 | slsigidx | SatoshiLabs signature indexes (bitmap) |
|
| 0x0014 | 1 | sigidx | SatoshiLabs signature indexes (bitmap) |
|
||||||
| 0x0015 | 64 | slsig | SatoshiLabs signature |
|
| 0x0015 | 64 | sig | SatoshiLabs signature |
|
||||||
| 0x0079 | 135 | reserved | not used yet |
|
| 0x0055 | 171 | reserved | not used yet (zeroed) |
|
||||||
|
|
||||||
##Firmware Format
|
##Firmware Format
|
||||||
|
|
||||||
@ -45,6 +58,8 @@ TREZOR Core firmware consists of 3 parts:
|
|||||||
2. firmware header
|
2. firmware header
|
||||||
3. firmware code
|
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
|
###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.
|
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 |
|
| offset | length | name | description |
|
||||||
|-------:|-------:|------|-------------|
|
|-------:|-------:|------|-------------|
|
||||||
| 0x0000 | 4 | magic | firmware magic `TRZV` |
|
| 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) |
|
| 0x0008 | 4 | expiry | valid until timestamp (0=infinity) |
|
||||||
| 0x000C | 1 | vsig_m | number of signatures needed to run the firmware from this vendor |
|
| 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 |
|
| 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 |
|
| 0x0010 | 32 | vpub1 | vendor pubkey 1 |
|
||||||
| ... | ... | ... | ... |
|
| ... | ... | ... | ... |
|
||||||
| ? | 32 | vpubn | vendor pubkey n |
|
| ? | 32 | vpubn | vendor pubkey n |
|
||||||
| ? | 1 | vstr_len | vendor string length |
|
| ? | 1 | vstr_len | vendor string length |
|
||||||
| ? | ? | vstr | vendor string |
|
| ? | ? | vstr | vendor string |
|
||||||
| ? | ? | vimg | vendor image (in [TOIf format](toif.md)) |
|
| ? | ? | vimg | vendor image (in [TOIf format](toif.md)) |
|
||||||
| ? | 1 | slsigidx | SatoshiLabs signature indexes (bitmap) |
|
| ? | 1 | sigidx | SatoshiLabs signature indexes (bitmap) |
|
||||||
| ? | 64 | slsig | SatoshiLabs signature |
|
| ? | 64 | sig | SatoshiLabs signature |
|
||||||
|
|
||||||
###Firmware Header
|
###Firmware Header
|
||||||
|
|
||||||
@ -73,16 +88,16 @@ Total length of firmware header is 256 bytes.
|
|||||||
| offset | length | name | description |
|
| offset | length | name | description |
|
||||||
|-------:|-------:|------|-------------|
|
|-------:|-------:|------|-------------|
|
||||||
| 0x0000 | 4 | magic | firmware magic `TRZF` |
|
| 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) |
|
| 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) |
|
| 0x0010 | 1 | vmajor | version (major) |
|
||||||
| 0x0011 | 1 | vminor | version (minor) |
|
| 0x0011 | 1 | vminor | version (minor) |
|
||||||
| 0x0012 | 1 | vpatch | version (patch) |
|
| 0x0012 | 1 | vpatch | version (patch) |
|
||||||
| 0x0013 | 1 | vbuild | version (build) |
|
| 0x0013 | 1 | vbuild | version (build) |
|
||||||
| 0x0014 | 1 | vndsigidx | vendor signature indexes (bitmap) |
|
| 0x0014 | 1 | sigidx | vendor signature indexes (bitmap) |
|
||||||
| 0x0015 | 64 | vndsig | vendor signature |
|
| 0x0015 | 64 | sig | vendor signature |
|
||||||
| 0x0079 | 135 | reserved | not used yet |
|
| 0x0055 | 171 | reserved | not used yet (zeroed) |
|
||||||
|
|
||||||
##Various ideas
|
##Various ideas
|
||||||
|
|
||||||
|
@ -4,18 +4,18 @@
|
|||||||
|
|
||||||
| sector | range | size | function
|
| sector | range | size | function
|
||||||
|-----------|-------------------------|--------:|----------------------
|
|-----------|-------------------------|--------:|----------------------
|
||||||
| Sector 0 | 0x08000000 - 0x08003FFF | 16 KiB | bootloader 1st stage
|
| Sector 0 | 0x08000000 - 0x08003FFF | 16 KiB | bootloader 1st stage (write-protected)
|
||||||
| Sector 1 | 0x08004000 - 0x08007FFF | 16 KiB | storage area
|
| Sector 1 | 0x08004000 - 0x08007FFF | 16 KiB | bootloader 1st stage (write-protected)
|
||||||
| Sector 2 | 0x08008000 - 0x0800BFFF | 16 KiB | storage area
|
| Sector 2 | 0x08008000 - 0x0800BFFF | 16 KiB | storage area
|
||||||
| Sector 3 | 0x0800C000 - 0x0800FFFF | 16 KiB | storage area
|
| Sector 3 | 0x0800C000 - 0x0800FFFF | 16 KiB | storage area
|
||||||
| Sector 4 | 0x08010000 - 0x0801FFFF | 64 KiB | bootloader 2nd stage
|
| Sector 4 | 0x08010000 - 0x0801FFFF | 64 KiB | bootloader 2nd stage
|
||||||
| Sector 5 | 0x08020000 - 0x0803FFFF | 128 KiB | application code
|
| Sector 5 | 0x08020000 - 0x0803FFFF | 128 KiB | firmware
|
||||||
| Sector 6 | 0x08040000 - 0x0805FFFF | 128 KiB | application code
|
| Sector 6 | 0x08040000 - 0x0805FFFF | 128 KiB | firmware
|
||||||
| Sector 7 | 0x08060000 - 0x0807FFFF | 128 KiB | application code
|
| Sector 7 | 0x08060000 - 0x0807FFFF | 128 KiB | firmware
|
||||||
| Sector 8 | 0x08080000 - 0x0809FFFF | 128 KiB | application code
|
| Sector 8 | 0x08080000 - 0x0809FFFF | 128 KiB | firmware
|
||||||
| Sector 9 | 0x080A0000 - 0x080BFFFF | 128 KiB | application code
|
| Sector 9 | 0x080A0000 - 0x080BFFFF | 128 KiB | firmware
|
||||||
| Sector 10 | 0x080C0000 - 0x080DFFFF | 128 KiB | application code
|
| Sector 10 | 0x080C0000 - 0x080DFFFF | 128 KiB | firmware
|
||||||
| Sector 11 | 0x080E0000 - 0x080FFFFF | 128 KiB | application code
|
| Sector 11 | 0x080E0000 - 0x080FFFFF | 128 KiB | firmware
|
||||||
|
|
||||||
##RAM
|
##RAM
|
||||||
|
|
||||||
|
@ -80,6 +80,7 @@ SRC_LIB = $(addprefix lib/,\
|
|||||||
)
|
)
|
||||||
|
|
||||||
SRC_C = \
|
SRC_C = \
|
||||||
|
bootloader/basic.c \
|
||||||
bootloader/bootloader.c \
|
bootloader/bootloader.c \
|
||||||
bootloader/crypto.c \
|
bootloader/crypto.c \
|
||||||
bootloader/ui.c \
|
bootloader/ui.c \
|
||||||
|
26
micropython/stmhal/bootloader/basic.c
Normal file
26
micropython/stmhal/bootloader/basic.c
Normal file
@ -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 "crypto.h"
|
||||||
#include "ui.h"
|
#include "ui.h"
|
||||||
|
|
||||||
#include "display.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);
|
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) {
|
int main(void) {
|
||||||
|
|
||||||
HAL_Init();
|
HAL_Init();
|
||||||
@ -101,7 +36,5 @@ int main(void) {
|
|||||||
HAL_Delay(250);
|
HAL_Delay(250);
|
||||||
}
|
}
|
||||||
|
|
||||||
__fatal_error("end");
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
void screen_welcome(void)
|
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);
|
display_text(0, 240, "bootloader", 10, FONT_MONO, ui_WHITE, ui_BLACK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
38
tools/check_bootloader
Executable file
38
tools/check_bootloader
Executable file
@ -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()
|
17
tools/check_firmware
Executable file
17
tools/check_firmware
Executable file
@ -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():
|
def main():
|
||||||
if len(sys.argv) < 2:
|
if len(sys.argv) < 2:
|
||||||
print('Usage toi2png image.toi[fg]')
|
print('Usage: toi2png image.toi[fg]')
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
ifn = sys.argv[1]
|
ifn = sys.argv[1]
|
||||||
|
Loading…
Reference in New Issue
Block a user