legacy/intermediate_fw: Intermediate firmware for T1 (#1084)

* legacy/intermediate_fw: skeleton FW with RAM shim function

* legacy/intermediate_fw: reboot from RAM

* legacy/intermediate_fw: flash erase from RAM

* legacy/intermediate_fw: port flash erase body from cm3

* legacy/intermediate_fw: erase works with flash unlock

* legacy/intermediate_fw: wait for flash controller ready

* legacy/intermediate_fw: cleanup and add comments

* legacy/intermediate_fw: disable IRQ before reboot

* legacy/intermediate_fw: also erase storage

* legacy/intermediate_fw: style

* legacy/intermediate_fw: dialogs for update bootloader/erase FW

* legacy/intermediate_fw: style

* legacy/intermediate_fw: add bootloader replacement code

* legacy/intermediate_fw: add CI build script for intermediate FW

* legacy/intermediate_fw: call bootloader update

* legacy/intermediate_fw: add bootloader update dependency

* legacy/intermediate_fw: change setup() at start of main

* legacy/intermediate_fw: deduplicate code

* docs: table for MEMORY_PROTECT combinations that work on T1

* legacy/intermediate_fw: deduplicate code

* legacy/intermediate_fw: check if running in privileged mode

* legacy/intermediate_fw: style

* legacy/intermediate_fw: ChangeLog

* legacy/intermediate_fw: make version match latest bootloader included

* legacy/intermediate_fw: style
pull/1216/head
Ondrej Mikle 4 years ago committed by GitHub
parent 21b4c5b60e
commit ff3b10a329
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -105,3 +105,21 @@ Switch your device to bootloader mode, then execute:
```sh
trezorctl firmware-update -f build/legacy/firmware/trezor.bin
```
## Combining bootloader and firmware with various `MEMORY_PROTECT` settings, signed/unsigned
Not all combinations of bootloader and firmware will work. This depends on
3 variables: MEMORY_PROTECT of bootloader, MEMORY_PROTECT of firmware, whether firmware is signed
This table shows the result for bootloader 1.8.0+ and 1.9.1+:
| Bootloader MEMORY_PROTECT | Firmware MEMORY_PROTECT | Is firmware officially signed? | Result |
| ------------------------- | ----------------------- | ------------------------------ | ------------------------------------------------------------------------------------------ |
| 1 | 1 | yes | works, official configuration |
| 1 | 1 | no | hardfault in header.S when setting VTOR and stack |
| 0 | 1 | no | works, but don't forget to comment out `check_bootloader`, otherwise it'll get overwritten |
| 0 | 0 | no | hard fault because header.S doesn't set VTOR and stack right |
| 1 | 0 | no | works |
The other three possibilities with signed firmware and `MEMORY_PROTECT!=0` for bootloader/firmware don't exist.

@ -130,7 +130,13 @@ static int known_bootloader(int r, const uint8_t *hash) {
}
#endif
void check_bootloader(void) {
/**
* If bootloader is older and known, replace with newer bootloader.
* If bootloader is unknown, halt with error message.
*
* @param shutdown_on_success: if true, shuts down device instead of return
*/
void check_bootloader(bool shutdown_on_success) {
#if MEMORY_PROTECT
uint8_t hash[32] = {0};
int r = memory_bootloader_hash(hash);
@ -178,11 +184,13 @@ void check_bootloader(void) {
// check whether the write was OK
r = memory_bootloader_hash(hash);
if (r == 32 && 0 == memcmp(hash, bl_hash, 32)) {
// OK -> show info and halt
layoutDialog(&bmp_icon_info, NULL, NULL, NULL, _("Update finished"),
_("successfully."), NULL, _("Please reconnect"),
_("the device."), NULL);
shutdown();
if (shutdown_on_success) {
// OK -> show info and halt
layoutDialog(&bmp_icon_info, NULL, NULL, NULL, _("Update finished"),
_("successfully."), NULL, _("Please reconnect"),
_("the device."), NULL);
shutdown();
}
return;
}
}
@ -192,4 +200,6 @@ void check_bootloader(void) {
_("contact our support."), NULL);
shutdown();
#endif
// prevent compiler warning when MEMORY_PROTECT==0
(void)shutdown_on_success;
}

@ -20,6 +20,8 @@
#ifndef __BL_CHECK_H__
#define __BL_CHECK_H__
void check_bootloader(void);
#include <stdbool.h>
void check_bootloader(bool shutdown_on_success);
#endif

@ -123,7 +123,7 @@ int main(void) {
// unpredictable stack protection checks
oledInit();
#else
check_bootloader();
check_bootloader(true);
setupApp();
__stack_chk_guard = random32(); // this supports compiler provided
// unpredictable stack protection checks

@ -0,0 +1,28 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased]
### Added
### Deprecated
### Removed
### Fixed
### Security
------------
### Older changelog:
Version 1.8.0 [Jun 2020]
* Initial version of intermediate firmware
* Updates bootloader to 1.8.0
* Deletes storage and the intermediate firmware code
* Version of intermediate firmware matches bootloader version included

@ -0,0 +1,60 @@
APPVER = 1.8.0
NAME = trezor
OBJS += trezor.o
OBJS += header.o
OBJS += bl_check.o
OBJS += ../vendor/trezor-crypto/memzero.o
OBJS += ../vendor/trezor-crypto/sha2.o
OPTFLAGS ?= -Og
../vendor/trezor-crypto/bip32.o: OPTFLAGS = -O3
../vendor/trezor-crypto/bip39.o: OPTFLAGS = -O3
../vendor/trezor-crypto/ecdsa.o: OPTFLAGS = -O3
../vendor/trezor-crypto/sha2.o: OPTFLAGS = -O3
../vendor/trezor-crypto/secp256k1.o: OPTFLAGS = -O3
include ../Makefile.include
CFLAGS:=$(filter-out -fstack-protector-all,$(CFLAGS))
DEBUG_LINK ?= 0
DEBUG_LOG ?= 0
CFLAGS += -Wno-sequence-point
CFLAGS += -I../vendor/nanopb -Iprotob -DPB_FIELD_16BIT=1 -DPB_ENCODE_ARRAYS_UNPACKED=1 -DPB_VALIDATE_UTF8=1
CFLAGS += -DDEBUG_LINK=$(DEBUG_LINK)
CFLAGS += -DDEBUG_LOG=$(DEBUG_LOG)
CFLAGS += -DSCM_REVISION='"$(shell git rev-parse HEAD | sed 's:\(..\):\\x\1:g')"'
CFLAGS += -DUSE_MONERO=0
ifneq ($(BITCOIN_ONLY),1)
CFLAGS += -DUSE_ETHEREUM=1
CFLAGS += -DUSE_NEM=1
MAKO_RENDER_FLAG =
else
CFLAGS += -DUSE_ETHEREUM=0
CFLAGS += -DUSE_NEM=0
MAKO_RENDER_FLAG = --bitcoin-only
endif
%:: %.mako defs
@printf " MAKO $@\n"
$(Q)$(PYTHON) ../vendor/trezor-common/tools/cointool.py render $(MAKO_RENDER_FLAG) $@.mako
bl_data.h: bl_data.py bootloader.dat
@printf " PYTHON bl_data.py\n"
$(Q)$(PYTHON) bl_data.py
clean::
rm -f bl_data.h
find -maxdepth 1 -name "*.mako" | sed 's/.mako$$//' | xargs rm -f
FIRMWARE_T1_START = 0x08010000
flash_intermediate_fw: trezor.bin
openocd -f interface/stlink-v2.cfg -c "transport select hla_swd" -f target/stm32f2x.cfg -c "init; reset halt; flash write_image erase $< $(FIRMWARE_T1_START); exit"
openocd_reset:
$(OPENOCD) -c "init; reset; exit"

@ -0,0 +1 @@
../firmware/bl_check.c

@ -0,0 +1 @@
../firmware/bl_check.h

@ -0,0 +1 @@
../firmware/bl_data.py

@ -0,0 +1 @@
../firmware/bootloader.dat

@ -0,0 +1 @@
../firmware/gettext.h

@ -0,0 +1 @@
../firmware/header.S

@ -0,0 +1,125 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "trezor.h"
#include <libopencm3/stm32/desig.h>
#include <libopencm3/stm32/flash.h>
#include <vendor/libopencm3/include/libopencmsis/core_cm3.h>
#include "bitmaps.h"
#include "bl_check.h"
#include "layout.h"
#include "memory.h"
#include "memzero.h"
#include "oled.h"
#include "rng.h"
#include "setup.h"
#include "timer.h"
#include "util.h"
/** Sector erase operation extracted from libopencm3 - flash_erase_sector
* so it can run from RAM
*/
static void __attribute__((noinline, section(".data")))
erase_sector(uint8_t sector, uint32_t psize) {
// Wait for flash controller to be ready
while ((FLASH_SR & FLASH_SR_BSY) == FLASH_SR_BSY)
;
// Set program word width
FLASH_CR &= ~(FLASH_CR_PROGRAM_MASK << FLASH_CR_PROGRAM_SHIFT);
FLASH_CR |= psize << FLASH_CR_PROGRAM_SHIFT;
/* Sector numbering is not contiguous internally! */
if (sector >= 12) {
sector += 4;
}
FLASH_CR &= ~(FLASH_CR_SNB_MASK << FLASH_CR_SNB_SHIFT);
FLASH_CR |= (sector & FLASH_CR_SNB_MASK) << FLASH_CR_SNB_SHIFT;
FLASH_CR |= FLASH_CR_SER;
FLASH_CR |= FLASH_CR_STRT;
// Wait for flash controller to be ready
while ((FLASH_SR & FLASH_SR_BSY) == FLASH_SR_BSY)
;
FLASH_CR &= ~FLASH_CR_SER;
FLASH_CR &= ~(FLASH_CR_SNB_MASK << FLASH_CR_SNB_SHIFT);
}
static void __attribute__((noinline, section(".data")))
erase_firmware_and_storage(void) {
// Flash unlock
FLASH_KEYR = FLASH_KEYR_KEY1;
FLASH_KEYR = FLASH_KEYR_KEY2;
// Erase storage sectors to prevent firmware downgrade to vulnerable version
for (int i = FLASH_STORAGE_SECTOR_FIRST; i <= FLASH_STORAGE_SECTOR_LAST;
i++) {
erase_sector(i, FLASH_CR_PROGRAM_X32);
}
// Erase firmware sectors
for (int i = FLASH_CODE_SECTOR_FIRST; i <= FLASH_CODE_SECTOR_LAST; i++) {
erase_sector(i, FLASH_CR_PROGRAM_X32);
}
// Flash lock
FLASH_CR |= FLASH_CR_LOCK;
}
void __attribute__((noinline, noreturn, section(".data"))) reboot_device(void) {
__disable_irq();
SCB_AIRCR = SCB_AIRCR_VECTKEY | SCB_AIRCR_SYSRESETREQ;
while (1)
;
}
/** Entry point of RAM shim that deletes old FW, storage and reboot */
void __attribute__((noinline, noreturn, section(".data")))
erase_fw_and_reboot(void) {
erase_firmware_and_storage();
reboot_device();
for (;;)
; // never reached, but compiler would generate error
}
int main(void) {
setupApp();
__stack_chk_guard = random32(); // this supports compiler provided
// unpredictable stack protection checks
oledInit();
if (is_mode_unprivileged()) {
layoutDialog(&bmp_icon_warning, NULL, NULL, NULL, "Cannot update", NULL,
NULL, "Unprivileged mode", "Unsigned firmware", NULL);
shutdown();
}
mpu_config_off(); // needed for flash writable, RAM RWX
timer_init();
check_bootloader(false);
layoutDialog(&bmp_icon_warning, NULL, NULL, NULL, "Erasing old data", NULL,
NULL, "DO NOT UNPLUG", "YOUR TREZOR!", NULL);
oledRefresh();
// from this point the execution is from RAM instead of flash
erase_fw_and_reboot();
return 0;
}

@ -0,0 +1,40 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __TREZOR_H__
#define __TREZOR_H__
#include <stdint.h>
#include "version.h"
#define STR(X) #X
#define VERSTR(X) STR(X)
#ifndef DEBUG_LINK
#define DEBUG_LINK 0
#endif
#ifndef DEBUG_LOG
#define DEBUG_LOG 0
#endif
/* Screen timeout */
extern uint32_t system_millis_lock_start;
#endif

@ -0,0 +1,8 @@
// Matches the bootloader version included in this firmware
#define VERSION_MAJOR 1
#define VERSION_MINOR 8
#define VERSION_PATCH 0
#define FIX_VERSION_MAJOR 1
#define FIX_VERSION_MINOR 8
#define FIX_VERSION_PATCH 0

@ -0,0 +1,15 @@
#!/usr/bin/env bash
# script/cibuild_intermediate_fw:
# Setup environment for CI to build intermediate firmware.
set -e
cd "$(dirname "$0")/.."
make -C vendor/libopencm3 lib/stm32/f2
make libtrezor.a
make -C intermediate_fw all sign
Loading…
Cancel
Save