mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-27 08:38:07 +00:00
core/bootloader_ic: introduce CI bootloader for TT device tests (#1182)
This commit is contained in:
parent
90f23afb72
commit
8d3d3be09c
@ -7,6 +7,7 @@ SCONS = scons -Q -j $(JOBS)
|
||||
BUILD_DIR = build
|
||||
BOARDLOADER_BUILD_DIR = $(BUILD_DIR)/boardloader
|
||||
BOOTLOADER_BUILD_DIR = $(BUILD_DIR)/bootloader
|
||||
BOOTLOADER_CI_BUILD_DIR = $(BUILD_DIR)/bootloader_ci
|
||||
PRODTEST_BUILD_DIR = $(BUILD_DIR)/prodtest
|
||||
REFLASH_BUILD_DIR = $(BUILD_DIR)/reflash
|
||||
FIRMWARE_BUILD_DIR = $(BUILD_DIR)/firmware
|
||||
@ -127,6 +128,9 @@ build_boardloader: ## build boardloader
|
||||
build_bootloader: ## build bootloader
|
||||
$(SCONS) CFLAGS="$(CFLAGS)" PRODUCTION="$(PRODUCTION)" $(BOOTLOADER_BUILD_DIR)/bootloader.bin
|
||||
|
||||
build_bootloader_ci: ## build CI device testing bootloader
|
||||
$(SCONS) CFLAGS="$(CFLAGS)" PRODUCTION="$(PRODUCTION)" $(BOOTLOADER_CI_BUILD_DIR)/bootloader.bin
|
||||
|
||||
build_prodtest: ## build production test firmware
|
||||
$(SCONS) CFLAGS="$(CFLAGS)" PRODUCTION="$(PRODUCTION)" $(PRODTEST_BUILD_DIR)/prodtest.bin
|
||||
|
||||
@ -186,6 +190,9 @@ flash_boardloader: $(BOARDLOADER_BUILD_DIR)/boardloader.bin ## flash boardloader
|
||||
flash_bootloader: $(BOOTLOADER_BUILD_DIR)/bootloader.bin ## flash bootloader using OpenOCD
|
||||
$(OPENOCD) -c "init; reset halt; flash write_image erase $< $(BOOTLOADER_START); exit"
|
||||
|
||||
flash_bootloader_ci: $(BOOTLOADER_CI_BUILD_DIR)/bootloader.bin ## flash CI bootloader using OpenOCD
|
||||
$(OPENOCD) -c "init; reset halt; flash write_image erase $< $(BOOTLOADER_START); exit"
|
||||
|
||||
flash_prodtest: $(PRODTEST_BUILD_DIR)/prodtest.bin ## flash prodtest using OpenOCD
|
||||
$(OPENOCD) -c "init; reset halt; flash write_image erase $< $(PRODTEST_START); exit"
|
||||
|
||||
@ -210,6 +217,9 @@ flash_erase_storage: ## erase storage sectors from flash
|
||||
flash_bootloader_jlink: $(BOOTLOADER_BUILD_DIR)/bootloader.bin ## flash bootloader using JLink
|
||||
JLinkExe -commanderscript embed/bootloader/bootloader_flash.jlink
|
||||
|
||||
flash_bootloader_ci_jlink: $(BOOTLOADER_CI_BUILD_DIR)/bootloader.bin ## flash CI bootloader using JLink
|
||||
JLinkExe -commanderscript embed/bootloader_ci/bootloader_flash.jlink
|
||||
|
||||
flash_firmware_jlink: $(FIRMWARE_BUILD_DIR)/firmware.bin ## flash firmware using JLink. file names must end in .bin for JLink
|
||||
cp -f $<.p1 $<.p1.bin
|
||||
cp -f $<.p2 $<.p2.bin
|
||||
|
197
core/SConscript.bootloader_ci
Normal file
197
core/SConscript.bootloader_ci
Normal file
@ -0,0 +1,197 @@
|
||||
# pylint: disable=E0602
|
||||
|
||||
import os
|
||||
|
||||
CCFLAGS_MOD = ''
|
||||
CPPPATH_MOD = []
|
||||
CPPDEFINES_MOD = []
|
||||
SOURCE_MOD = []
|
||||
|
||||
# modtrezorcrypto
|
||||
CCFLAGS_MOD += '-Wno-sequence-point '
|
||||
CPPPATH_MOD += [
|
||||
'vendor/trezor-crypto',
|
||||
]
|
||||
CPPDEFINES_MOD += [
|
||||
'AES_128',
|
||||
'AES_192',
|
||||
'USE_KECCAK',
|
||||
'ED25519_NO_PRECOMP',
|
||||
]
|
||||
SOURCE_MOD += [
|
||||
'vendor/trezor-crypto/blake2s.c',
|
||||
'vendor/trezor-crypto/ed25519-donna/curve25519-donna-32bit.c',
|
||||
'vendor/trezor-crypto/ed25519-donna/curve25519-donna-helpers.c',
|
||||
'vendor/trezor-crypto/ed25519-donna/ed25519.c',
|
||||
'vendor/trezor-crypto/ed25519-donna/ed25519-donna-32bit-tables.c',
|
||||
'vendor/trezor-crypto/ed25519-donna/ed25519-donna-impl-base.c',
|
||||
'vendor/trezor-crypto/ed25519-donna/modm-donna-32bit.c',
|
||||
'vendor/trezor-crypto/hmac_drbg.c',
|
||||
'vendor/trezor-crypto/memzero.c',
|
||||
'vendor/trezor-crypto/rand.c',
|
||||
'vendor/trezor-crypto/sha2.c',
|
||||
]
|
||||
|
||||
# modtrezorui
|
||||
CPPPATH_MOD += [
|
||||
'vendor/micropython/extmod/uzlib',
|
||||
]
|
||||
CPPDEFINES_MOD += [
|
||||
'TREZOR_FONT_NORMAL_ENABLE',
|
||||
'TREZOR_FONT_MONO_ENABLE',
|
||||
]
|
||||
SOURCE_MOD += [
|
||||
'embed/extmod/modtrezorui/display.c',
|
||||
'embed/extmod/modtrezorui/font_bitmap.c',
|
||||
'embed/extmod/modtrezorui/font_roboto_regular_20.c',
|
||||
'embed/extmod/modtrezorui/font_robotomono_regular_20.c',
|
||||
'embed/extmod/modtrezorui/qr-code-generator/qrcodegen.c',
|
||||
'vendor/micropython/extmod/uzlib/adler32.c',
|
||||
'vendor/micropython/extmod/uzlib/crc32.c',
|
||||
'vendor/micropython/extmod/uzlib/tinflate.c',
|
||||
]
|
||||
|
||||
SOURCE_STMHAL = [
|
||||
'vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c',
|
||||
'vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_cortex.c',
|
||||
'vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash.c',
|
||||
'vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ex.c',
|
||||
'vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_gpio.c',
|
||||
'vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_i2c.c',
|
||||
'vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd.c',
|
||||
'vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd_ex.c',
|
||||
'vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr.c',
|
||||
'vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc.c',
|
||||
'vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_sd.c',
|
||||
'vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_spi.c',
|
||||
'vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_sram.c',
|
||||
'vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim.c',
|
||||
'vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim_ex.c',
|
||||
'vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_fmc.c',
|
||||
'vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_sdmmc.c',
|
||||
'vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_usb.c',
|
||||
]
|
||||
|
||||
SOURCE_NANOPB = [
|
||||
'vendor/nanopb/pb_common.c',
|
||||
'vendor/nanopb/pb_decode.c',
|
||||
'vendor/nanopb/pb_encode.c',
|
||||
]
|
||||
|
||||
SOURCE_BOOTLOADER = [
|
||||
'embed/bootloader_ci/startup.s',
|
||||
'embed/bootloader_ci/header.S',
|
||||
'embed/bootloader_ci/bootui.c',
|
||||
'embed/bootloader_ci/main.c',
|
||||
'embed/bootloader_ci/messages.c',
|
||||
'embed/bootloader_ci/protob/messages.pb.c',
|
||||
]
|
||||
|
||||
SOURCE_TREZORHAL = [
|
||||
'embed/trezorhal/common.c',
|
||||
'embed/trezorhal/image.c',
|
||||
'embed/trezorhal/flash.c',
|
||||
'embed/trezorhal/mini_printf.c',
|
||||
'embed/trezorhal/mpu.c',
|
||||
'embed/trezorhal/rng.c',
|
||||
'embed/trezorhal/stm32.c',
|
||||
'embed/trezorhal/systick.c',
|
||||
'embed/trezorhal/touch.c',
|
||||
'embed/trezorhal/usb.c',
|
||||
'embed/trezorhal/usbd_conf.c',
|
||||
'embed/trezorhal/usbd_core.c',
|
||||
'embed/trezorhal/usbd_ctlreq.c',
|
||||
'embed/trezorhal/usbd_ioreq.c',
|
||||
'embed/trezorhal/util.s',
|
||||
'embed/trezorhal/vectortable.s',
|
||||
]
|
||||
|
||||
env = Environment(ENV=os.environ, CFLAGS='%s -DPRODUCTION=%s' % (ARGUMENTS.get('CFLAGS', ''), ARGUMENTS.get('PRODUCTION', '0')))
|
||||
|
||||
env.Replace(
|
||||
AS='arm-none-eabi-as',
|
||||
AR='arm-none-eabi-ar',
|
||||
CC='arm-none-eabi-gcc',
|
||||
LINK='arm-none-eabi-gcc',
|
||||
SIZE='arm-none-eabi-size',
|
||||
STRIP='arm-none-eabi-strip',
|
||||
OBJCOPY='arm-none-eabi-objcopy', )
|
||||
|
||||
env.Replace(
|
||||
TREZOR_MODEL=env.get('ENV').get('TREZOR_MODEL', 'T'), )
|
||||
|
||||
if env.get('TREZOR_MODEL') == 'T':
|
||||
CPU_ASFLAGS = '-mthumb -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16'
|
||||
CPU_CCFLAGS = '-mthumb -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -mtune=cortex-m4 '
|
||||
CPU_MODEL = 'STM32F427xx'
|
||||
elif env.get('TREZOR_MODEL') == '1':
|
||||
CPU_ASFLAGS = '-mthumb -mcpu=cortex-m3 -mfloat-abi=soft'
|
||||
CPU_CCFLAGS = '-mthumb -mtune=cortex-m3 -mcpu=cortex-m3 -mfloat-abi=soft '
|
||||
CPU_MODEL = 'STM32F405xx'
|
||||
else:
|
||||
raise ValueError('Unknown Trezor model')
|
||||
|
||||
env.Replace(
|
||||
COPT=env.get('ENV').get('OPTIMIZE', '-Og'),
|
||||
CCFLAGS='$COPT '
|
||||
'-g3 '
|
||||
'-nostdlib '
|
||||
'-std=gnu99 -Wall -Werror -Wdouble-promotion -Wpointer-arith -Wno-missing-braces -fno-common '
|
||||
'-fsingle-precision-constant -fdata-sections -ffunction-sections '
|
||||
'-ffreestanding '
|
||||
'-fstack-protector-all '
|
||||
+ CPU_CCFLAGS + CCFLAGS_MOD,
|
||||
CCFLAGS_QSTR='-DNO_QSTR -DN_X64 -DN_X86 -DN_THUMB',
|
||||
LINKFLAGS='-T embed/bootloader_ci/memory.ld -Wl,--gc-sections -Wl,-Map=build/bootloader_ci/bootloader.map -Wl,--warn-common',
|
||||
CPPPATH=[
|
||||
'embed/bootloader_ci',
|
||||
'embed/bootloader_ci/nanopb',
|
||||
'embed/bootloader_ci/protob',
|
||||
'embed/trezorhal',
|
||||
'embed/extmod/modtrezorui',
|
||||
'vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Inc',
|
||||
'vendor/micropython/lib/stm32lib/CMSIS/STM32F4xx/Include',
|
||||
'vendor/micropython/lib/cmsis/inc',
|
||||
'vendor/nanopb',
|
||||
] + CPPPATH_MOD,
|
||||
CPPDEFINES=[
|
||||
('TREZOR_MODEL', '$TREZOR_MODEL'),
|
||||
CPU_MODEL,
|
||||
'USE_HAL_DRIVER',
|
||||
('STM32_HAL_H', '"<stm32f4xx.h>"'),
|
||||
'PB_FIELD_16BIT',
|
||||
'PB_ENCODE_ARRAYS_UNPACKED',
|
||||
'PB_VALIDATE_UTF8',
|
||||
] + CPPDEFINES_MOD,
|
||||
ASFLAGS=CPU_ASFLAGS,
|
||||
ASPPFLAGS='$CFLAGS $CCFLAGS', )
|
||||
|
||||
env.Replace(
|
||||
HEADERTOOL='tools/headertool.py',
|
||||
)
|
||||
|
||||
#
|
||||
# Program objects
|
||||
#
|
||||
|
||||
obj_program = []
|
||||
obj_program += env.Object(source=SOURCE_MOD)
|
||||
obj_program += env.Object(source=SOURCE_BOOTLOADER)
|
||||
obj_program += env.Object(source=SOURCE_NANOPB)
|
||||
obj_program += env.Object(source=SOURCE_STMHAL)
|
||||
obj_program += env.Object(source=SOURCE_TREZORHAL)
|
||||
|
||||
program_elf = env.Command(
|
||||
target='bootloader.elf',
|
||||
source=obj_program,
|
||||
action=
|
||||
'$LINK -o $TARGET $CCFLAGS $CFLAGS $LINKFLAGS $SOURCES -lc_nano -lgcc',
|
||||
)
|
||||
|
||||
program_bin = env.Command(
|
||||
target='bootloader.bin',
|
||||
source=program_elf,
|
||||
action=[
|
||||
'$OBJCOPY -O binary -j .header -j .flash -j .data $SOURCE $TARGET',
|
||||
'$HEADERTOOL $TARGET ' + ('-D' if ARGUMENTS.get('PRODUCTION', '0') == '0' else ''),
|
||||
], )
|
@ -6,3 +6,4 @@ SConscript('SConscript.reflash', variant_dir='build/reflash', duplicate=False)
|
||||
SConscript('SConscript.prodtest', variant_dir='build/prodtest', duplicate=False)
|
||||
SConscript('SConscript.firmware', variant_dir='build/firmware', duplicate=False)
|
||||
SConscript('SConscript.unix', variant_dir='build/unix', duplicate=False)
|
||||
SConscript('SConscript.bootloader_ci', variant_dir='build/bootloader_ci', duplicate=False)
|
||||
|
26
core/embed/bootloader_ci/ChangeLog
Normal file
26
core/embed/bootloader_ci/ChangeLog
Normal file
@ -0,0 +1,26 @@
|
||||
# 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.0.0 [Aug 2020]
|
||||
* initial version
|
||||
|
16
core/embed/bootloader_ci/README.md
Normal file
16
core/embed/bootloader_ci/README.md
Normal file
@ -0,0 +1,16 @@
|
||||
# Bootloader for CI device tests
|
||||
|
||||
This bootloader always runs into bootloader mode, waits for firmware to be
|
||||
uploaded, then runs the firmware.
|
||||
|
||||
Storage is not erased. If you wish to erase storage, do it inside your test.
|
||||
|
||||
All user interaction is removed (no clicking or confirmations required)
|
||||
so that it can be used in an automated way for tests.
|
||||
|
||||
The bootloader will run any firmware that looks "sane" (good header, hashes),
|
||||
but does not check any trust flags.
|
||||
|
||||
Firmware must be compiled with `PRODUCTION=0` because otherwise it would
|
||||
replace the bootloader and lock device.
|
||||
|
7
core/embed/bootloader_ci/bootloader_flash.jlink
Normal file
7
core/embed/bootloader_ci/bootloader_flash.jlink
Normal file
@ -0,0 +1,7 @@
|
||||
device stm32f427vi
|
||||
if swd
|
||||
speed auto
|
||||
loadbin build/bootloader_ci/bootloader.bin 0x08020000
|
||||
r
|
||||
g
|
||||
exit
|
1
core/embed/bootloader_ci/bootui.c
Symbolic link
1
core/embed/bootloader_ci/bootui.c
Symbolic link
@ -0,0 +1 @@
|
||||
../bootloader/bootui.c
|
1
core/embed/bootloader_ci/bootui.h
Symbolic link
1
core/embed/bootloader_ci/bootui.h
Symbolic link
@ -0,0 +1 @@
|
||||
../bootloader/bootui.h
|
1
core/embed/bootloader_ci/header.S
Symbolic link
1
core/embed/bootloader_ci/header.S
Symbolic link
@ -0,0 +1 @@
|
||||
../bootloader/header.S
|
1
core/embed/bootloader_ci/icon_cancel.h
Symbolic link
1
core/embed/bootloader_ci/icon_cancel.h
Symbolic link
@ -0,0 +1 @@
|
||||
../bootloader/icon_cancel.h
|
1
core/embed/bootloader_ci/icon_confirm.h
Symbolic link
1
core/embed/bootloader_ci/icon_confirm.h
Symbolic link
@ -0,0 +1 @@
|
||||
../bootloader/icon_confirm.h
|
1
core/embed/bootloader_ci/icon_done.h
Symbolic link
1
core/embed/bootloader_ci/icon_done.h
Symbolic link
@ -0,0 +1 @@
|
||||
../bootloader/icon_done.h
|
1
core/embed/bootloader_ci/icon_fail.h
Symbolic link
1
core/embed/bootloader_ci/icon_fail.h
Symbolic link
@ -0,0 +1 @@
|
||||
../bootloader/icon_fail.h
|
1
core/embed/bootloader_ci/icon_info.h
Symbolic link
1
core/embed/bootloader_ci/icon_info.h
Symbolic link
@ -0,0 +1 @@
|
||||
../bootloader/icon_info.h
|
1
core/embed/bootloader_ci/icon_install.h
Symbolic link
1
core/embed/bootloader_ci/icon_install.h
Symbolic link
@ -0,0 +1 @@
|
||||
../bootloader/icon_install.h
|
1
core/embed/bootloader_ci/icon_logo.h
Symbolic link
1
core/embed/bootloader_ci/icon_logo.h
Symbolic link
@ -0,0 +1 @@
|
||||
../bootloader/icon_logo.h
|
1
core/embed/bootloader_ci/icon_safeplace.h
Symbolic link
1
core/embed/bootloader_ci/icon_safeplace.h
Symbolic link
@ -0,0 +1 @@
|
||||
../bootloader/icon_safeplace.h
|
1
core/embed/bootloader_ci/icon_welcome.h
Symbolic link
1
core/embed/bootloader_ci/icon_welcome.h
Symbolic link
@ -0,0 +1 @@
|
||||
../bootloader/icon_welcome.h
|
1
core/embed/bootloader_ci/icon_wipe.h
Symbolic link
1
core/embed/bootloader_ci/icon_wipe.h
Symbolic link
@ -0,0 +1 @@
|
||||
../bootloader/icon_wipe.h
|
308
core/embed/bootloader_ci/main.c
Normal file
308
core/embed/bootloader_ci/main.c
Normal file
@ -0,0 +1,308 @@
|
||||
/*
|
||||
* This file is part of the Trezor project, https://trezor.io/
|
||||
*
|
||||
* Copyright (c) SatoshiLabs
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "display.h"
|
||||
#include "flash.h"
|
||||
#include "image.h"
|
||||
#include "mini_printf.h"
|
||||
#include "mpu.h"
|
||||
#include "rng.h"
|
||||
#include "secbool.h"
|
||||
#include "touch.h"
|
||||
#include "usb.h"
|
||||
#include "version.h"
|
||||
|
||||
#include "bootui.h"
|
||||
#include "messages.h"
|
||||
// #include "mpu.h"
|
||||
|
||||
const uint8_t BOOTLOADER_KEY_M = 2;
|
||||
const uint8_t BOOTLOADER_KEY_N = 3;
|
||||
static const uint8_t * const BOOTLOADER_KEYS[] = {
|
||||
(const uint8_t *)"\xc2\xc8\x7a\x49\xc5\xa3\x46\x09\x77\xfb\xb2\xec\x9d\xfe\x60\xf0\x6b\xd6\x94\xdb\x82\x44\xbd\x49\x81\xfe\x3b\x7a\x26\x30\x7f\x3f",
|
||||
(const uint8_t *)"\x80\xd0\x36\xb0\x87\x39\xb8\x46\xf4\xcb\x77\x59\x30\x78\xde\xb2\x5d\xc9\x48\x7a\xed\xcf\x52\xe3\x0b\x4f\xb7\xcd\x70\x24\x17\x8a",
|
||||
(const uint8_t *)"\xb8\x30\x7a\x71\xf5\x52\xc6\x0a\x4c\xbb\x31\x7f\xf4\x8b\x82\xcd\xbf\x6b\x6b\xb5\xf0\x4c\x92\x0f\xec\x7b\xad\xf0\x17\x88\x37\x51",
|
||||
// comment the lines above and uncomment the lines below to use a custom signed vendorheader
|
||||
// (const uint8_t *)"\xd7\x59\x79\x3b\xbc\x13\xa2\x81\x9a\x82\x7c\x76\xad\xb6\xfb\xa8\xa4\x9a\xee\x00\x7f\x49\xf2\xd0\x99\x2d\x99\xb8\x25\xad\x2c\x48",
|
||||
// (const uint8_t *)"\x63\x55\x69\x1c\x17\x8a\x8f\xf9\x10\x07\xa7\x47\x8a\xfb\x95\x5e\xf7\x35\x2c\x63\xe7\xb2\x57\x03\x98\x4c\xf7\x8b\x26\xe2\x1a\x56",
|
||||
// (const uint8_t *)"\xee\x93\xa4\xf6\x6f\x8d\x16\xb8\x19\xbb\x9b\xeb\x9f\xfc\xcd\xfc\xdc\x14\x12\xe8\x7f\xee\x6a\x32\x4c\x2a\x99\xa1\xe0\xe6\x71\x48",
|
||||
};
|
||||
|
||||
#define USB_IFACE_NUM 0
|
||||
|
||||
static void usb_init_all(secbool usb21_landing) {
|
||||
usb_dev_info_t dev_info = {
|
||||
.device_class = 0x00,
|
||||
.device_subclass = 0x00,
|
||||
.device_protocol = 0x00,
|
||||
.vendor_id = 0x1209,
|
||||
.product_id = 0x53C0,
|
||||
.release_num = 0x0200,
|
||||
.manufacturer = "SatoshiLabs",
|
||||
.product = "TREZOR",
|
||||
.serial_number = "000000000000000000000000",
|
||||
.interface = "TREZOR Interface",
|
||||
.usb21_enabled = sectrue,
|
||||
.usb21_landing = usb21_landing,
|
||||
};
|
||||
|
||||
static uint8_t rx_buffer[USB_PACKET_SIZE];
|
||||
|
||||
static const usb_webusb_info_t webusb_info = {
|
||||
.iface_num = USB_IFACE_NUM,
|
||||
.ep_in = USB_EP_DIR_IN | 0x01,
|
||||
.ep_out = USB_EP_DIR_OUT | 0x01,
|
||||
.subclass = 0,
|
||||
.protocol = 0,
|
||||
.max_packet_len = sizeof(rx_buffer),
|
||||
.rx_buffer = rx_buffer,
|
||||
.polling_interval = 1,
|
||||
};
|
||||
|
||||
usb_init(&dev_info);
|
||||
|
||||
ensure(usb_webusb_add(&webusb_info), NULL);
|
||||
|
||||
usb_start();
|
||||
}
|
||||
|
||||
static secbool bootloader_usb_loop(const vendor_header *const vhdr,
|
||||
const image_header *const hdr) {
|
||||
// if both are NULL, we don't have a firmware installed
|
||||
// let's show a webusb landing page in this case
|
||||
usb_init_all((vhdr == NULL && hdr == NULL) ? sectrue : secfalse);
|
||||
|
||||
uint8_t buf[USB_PACKET_SIZE];
|
||||
|
||||
for (;;) {
|
||||
int r = usb_webusb_read_blocking(USB_IFACE_NUM, buf, USB_PACKET_SIZE,
|
||||
USB_TIMEOUT);
|
||||
if (r != USB_PACKET_SIZE) {
|
||||
continue;
|
||||
}
|
||||
uint16_t msg_id;
|
||||
uint32_t msg_size;
|
||||
if (sectrue != msg_parse_header(buf, &msg_id, &msg_size)) {
|
||||
// invalid header -> discard
|
||||
continue;
|
||||
}
|
||||
switch (msg_id) {
|
||||
case 0: // Initialize
|
||||
process_msg_Initialize(USB_IFACE_NUM, msg_size, buf, vhdr, hdr);
|
||||
break;
|
||||
case 1: // Ping
|
||||
process_msg_Ping(USB_IFACE_NUM, msg_size, buf);
|
||||
break;
|
||||
case 5: // WipeDevice
|
||||
ui_fadeout();
|
||||
ui_screen_wipe_confirm();
|
||||
ui_fadein();
|
||||
int response = ui_user_input(INPUT_CONFIRM | INPUT_CANCEL);
|
||||
if (INPUT_CANCEL == response) {
|
||||
ui_fadeout();
|
||||
ui_screen_info(secfalse, vhdr, hdr);
|
||||
ui_fadein();
|
||||
send_user_abort(USB_IFACE_NUM, "Wipe cancelled");
|
||||
break;
|
||||
}
|
||||
ui_fadeout();
|
||||
ui_screen_wipe();
|
||||
ui_fadein();
|
||||
r = process_msg_WipeDevice(USB_IFACE_NUM, msg_size, buf);
|
||||
if (r < 0) { // error
|
||||
ui_fadeout();
|
||||
ui_screen_fail();
|
||||
ui_fadein();
|
||||
usb_stop();
|
||||
usb_deinit();
|
||||
return secfalse; // shutdown
|
||||
} else { // success
|
||||
ui_fadeout();
|
||||
ui_screen_done(0, sectrue);
|
||||
ui_fadein();
|
||||
usb_stop();
|
||||
usb_deinit();
|
||||
return secfalse; // shutdown
|
||||
}
|
||||
break;
|
||||
case 6: // FirmwareErase
|
||||
process_msg_FirmwareErase(USB_IFACE_NUM, msg_size, buf);
|
||||
break;
|
||||
case 7: // FirmwareUpload
|
||||
r = process_msg_FirmwareUpload(USB_IFACE_NUM, msg_size, buf);
|
||||
if (r < 0 && r != -4) { // error, but not user abort (-4)
|
||||
ui_fadeout();
|
||||
ui_screen_fail();
|
||||
ui_fadein();
|
||||
usb_stop();
|
||||
usb_deinit();
|
||||
return secfalse; // shutdown
|
||||
} else if (r == 0) { // last chunk received
|
||||
ui_screen_install_progress_upload(1000);
|
||||
ui_fadeout();
|
||||
ui_screen_done(4, sectrue);
|
||||
ui_fadein();
|
||||
ui_screen_done(3, secfalse);
|
||||
hal_delay(1000);
|
||||
ui_screen_done(2, secfalse);
|
||||
hal_delay(1000);
|
||||
ui_screen_done(1, secfalse);
|
||||
hal_delay(1000);
|
||||
usb_stop();
|
||||
usb_deinit();
|
||||
ui_fadeout();
|
||||
return sectrue; // jump to firmware
|
||||
}
|
||||
break;
|
||||
case 55: // GetFeatures
|
||||
process_msg_GetFeatures(USB_IFACE_NUM, msg_size, buf, vhdr, hdr);
|
||||
break;
|
||||
default:
|
||||
process_msg_unknown(USB_IFACE_NUM, msg_size, buf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
secbool load_vendor_header_keys(const uint8_t *const data,
|
||||
vendor_header *const vhdr) {
|
||||
return load_vendor_header(data, BOOTLOADER_KEY_M, BOOTLOADER_KEY_N,
|
||||
BOOTLOADER_KEYS, vhdr);
|
||||
}
|
||||
|
||||
static secbool check_vendor_keys_lock(const vendor_header *const vhdr) {
|
||||
uint8_t lock[FLASH_OTP_BLOCK_SIZE];
|
||||
ensure(flash_otp_read(FLASH_OTP_BLOCK_VENDOR_KEYS_LOCK, 0, lock,
|
||||
FLASH_OTP_BLOCK_SIZE),
|
||||
NULL);
|
||||
if (0 ==
|
||||
memcmp(lock,
|
||||
"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
|
||||
"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF",
|
||||
FLASH_OTP_BLOCK_SIZE)) {
|
||||
return sectrue;
|
||||
}
|
||||
uint8_t hash[32];
|
||||
vendor_keys_hash(vhdr, hash);
|
||||
return sectrue * (0 == memcmp(lock, hash, 32));
|
||||
}
|
||||
|
||||
// protection against bootloader downgrade
|
||||
|
||||
#if PRODUCTION
|
||||
|
||||
static void check_bootloader_version(void) {
|
||||
uint8_t bits[FLASH_OTP_BLOCK_SIZE];
|
||||
for (int i = 0; i < FLASH_OTP_BLOCK_SIZE * 8; i++) {
|
||||
if (i < VERSION_MONOTONIC) {
|
||||
bits[i / 8] &= ~(1 << (7 - (i % 8)));
|
||||
} else {
|
||||
bits[i / 8] |= (1 << (7 - (i % 8)));
|
||||
}
|
||||
}
|
||||
ensure(flash_otp_write(FLASH_OTP_BLOCK_BOOTLOADER_VERSION, 0, bits,
|
||||
FLASH_OTP_BLOCK_SIZE),
|
||||
NULL);
|
||||
|
||||
uint8_t bits2[FLASH_OTP_BLOCK_SIZE];
|
||||
ensure(flash_otp_read(FLASH_OTP_BLOCK_BOOTLOADER_VERSION, 0, bits2,
|
||||
FLASH_OTP_BLOCK_SIZE),
|
||||
NULL);
|
||||
|
||||
ensure(sectrue * (0 == memcmp(bits, bits2, FLASH_OTP_BLOCK_SIZE)),
|
||||
"Bootloader downgraded");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int main(void) {
|
||||
drbg_init();
|
||||
touch_init();
|
||||
touch_power_on();
|
||||
|
||||
mpu_config_bootloader();
|
||||
|
||||
#if PRODUCTION
|
||||
check_bootloader_version();
|
||||
#endif
|
||||
|
||||
display_clear();
|
||||
|
||||
vendor_header vhdr;
|
||||
image_header hdr;
|
||||
secbool firmware_present;
|
||||
|
||||
// detect whether the devices contains a valid firmware
|
||||
|
||||
firmware_present =
|
||||
load_vendor_header_keys((const uint8_t *)FIRMWARE_START, &vhdr);
|
||||
if (sectrue == firmware_present) {
|
||||
firmware_present = check_vendor_keys_lock(&vhdr);
|
||||
}
|
||||
if (sectrue == firmware_present) {
|
||||
firmware_present = load_image_header(
|
||||
(const uint8_t *)(FIRMWARE_START + vhdr.hdrlen), FIRMWARE_IMAGE_MAGIC,
|
||||
FIRMWARE_IMAGE_MAXSIZE, vhdr.vsig_m, vhdr.vsig_n, vhdr.vpub, &hdr);
|
||||
}
|
||||
if (sectrue == firmware_present) {
|
||||
firmware_present =
|
||||
check_image_contents(&hdr, IMAGE_HEADER_SIZE + vhdr.hdrlen,
|
||||
FIRMWARE_SECTORS, FIRMWARE_SECTORS_COUNT);
|
||||
}
|
||||
|
||||
// always start bootloader even if firmware is already present
|
||||
// show intro animation
|
||||
|
||||
// no ui_fadeout(); - we already start from black screen
|
||||
ui_screen_third();
|
||||
ui_fadein();
|
||||
|
||||
// and start the usb loop
|
||||
if (bootloader_usb_loop(NULL, NULL) != sectrue) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
ensure(load_vendor_header_keys((const uint8_t *)FIRMWARE_START, &vhdr),
|
||||
"invalid vendor header");
|
||||
|
||||
ensure(check_vendor_keys_lock(&vhdr), "unauthorized vendor keys");
|
||||
|
||||
ensure(load_image_header((const uint8_t *)(FIRMWARE_START + vhdr.hdrlen),
|
||||
FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE,
|
||||
vhdr.vsig_m, vhdr.vsig_n, vhdr.vpub, &hdr),
|
||||
"invalid firmware header");
|
||||
|
||||
ensure(check_image_contents(&hdr, IMAGE_HEADER_SIZE + vhdr.hdrlen,
|
||||
FIRMWARE_SECTORS, FIRMWARE_SECTORS_COUNT),
|
||||
"invalid firmware hash");
|
||||
|
||||
// do not check any trust flags on header, proceed
|
||||
|
||||
// mpu_config_firmware();
|
||||
// jump_to_unprivileged(FIRMWARE_START + vhdr.hdrlen + IMAGE_HEADER_SIZE);
|
||||
|
||||
mpu_config_off();
|
||||
jump_to(FIRMWARE_START + vhdr.hdrlen + IMAGE_HEADER_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
1
core/embed/bootloader_ci/memory.ld
Symbolic link
1
core/embed/bootloader_ci/memory.ld
Symbolic link
@ -0,0 +1 @@
|
||||
../bootloader/memory.ld
|
654
core/embed/bootloader_ci/messages.c
Normal file
654
core/embed/bootloader_ci/messages.c
Normal file
@ -0,0 +1,654 @@
|
||||
/*
|
||||
* This file is part of the Trezor project, https://trezor.io/
|
||||
*
|
||||
* Copyright (c) SatoshiLabs
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <pb.h>
|
||||
#include <pb_decode.h>
|
||||
#include <pb_encode.h>
|
||||
#include "messages.pb.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "flash.h"
|
||||
#include "image.h"
|
||||
#include "secbool.h"
|
||||
#include "usb.h"
|
||||
#include "version.h"
|
||||
|
||||
#include "bootui.h"
|
||||
#include "messages.h"
|
||||
|
||||
#include "memzero.h"
|
||||
|
||||
#define MSG_HEADER1_LEN 9
|
||||
#define MSG_HEADER2_LEN 1
|
||||
|
||||
secbool msg_parse_header(const uint8_t *buf, uint16_t *msg_id,
|
||||
uint32_t *msg_size) {
|
||||
if (buf[0] != '?' || buf[1] != '#' || buf[2] != '#') {
|
||||
return secfalse;
|
||||
}
|
||||
*msg_id = (buf[3] << 8) + buf[4];
|
||||
*msg_size = (buf[5] << 24) + (buf[6] << 16) + (buf[7] << 8) + buf[8];
|
||||
return sectrue;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint8_t iface_num;
|
||||
uint8_t packet_index;
|
||||
uint8_t packet_pos;
|
||||
uint8_t buf[USB_PACKET_SIZE];
|
||||
} usb_write_state;
|
||||
|
||||
/* we don't use secbool/sectrue/secfalse here as it is a nanopb api */
|
||||
static bool _usb_write(pb_ostream_t *stream, const pb_byte_t *buf,
|
||||
size_t count) {
|
||||
usb_write_state *state = (usb_write_state *)(stream->state);
|
||||
|
||||
size_t written = 0;
|
||||
// while we have data left
|
||||
while (written < count) {
|
||||
size_t remaining = count - written;
|
||||
// if all remaining data fit into our packet
|
||||
if (state->packet_pos + remaining <= USB_PACKET_SIZE) {
|
||||
// append data from buf to state->buf
|
||||
memcpy(state->buf + state->packet_pos, buf + written, remaining);
|
||||
// advance position
|
||||
state->packet_pos += remaining;
|
||||
// and return
|
||||
return true;
|
||||
} else {
|
||||
// append data that fits
|
||||
memcpy(state->buf + state->packet_pos, buf + written,
|
||||
USB_PACKET_SIZE - state->packet_pos);
|
||||
written += USB_PACKET_SIZE - state->packet_pos;
|
||||
// send packet
|
||||
int r = usb_webusb_write_blocking(state->iface_num, state->buf,
|
||||
USB_PACKET_SIZE, USB_TIMEOUT);
|
||||
ensure(sectrue * (r == USB_PACKET_SIZE), NULL);
|
||||
// prepare new packet
|
||||
state->packet_index++;
|
||||
memzero(state->buf, USB_PACKET_SIZE);
|
||||
state->buf[0] = '?';
|
||||
state->packet_pos = MSG_HEADER2_LEN;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void _usb_write_flush(usb_write_state *state) {
|
||||
// if packet is not filled up completely
|
||||
if (state->packet_pos < USB_PACKET_SIZE) {
|
||||
// pad it with zeroes
|
||||
memzero(state->buf + state->packet_pos,
|
||||
USB_PACKET_SIZE - state->packet_pos);
|
||||
}
|
||||
// send packet
|
||||
int r = usb_webusb_write_blocking(state->iface_num, state->buf,
|
||||
USB_PACKET_SIZE, USB_TIMEOUT);
|
||||
ensure(sectrue * (r == USB_PACKET_SIZE), NULL);
|
||||
}
|
||||
|
||||
static secbool _send_msg(uint8_t iface_num, uint16_t msg_id,
|
||||
const pb_msgdesc_t *fields, const void *msg) {
|
||||
// determine message size by serializing it into a dummy stream
|
||||
pb_ostream_t sizestream = {.callback = NULL,
|
||||
.state = NULL,
|
||||
.max_size = SIZE_MAX,
|
||||
.bytes_written = 0,
|
||||
.errmsg = NULL};
|
||||
if (false == pb_encode(&sizestream, fields, msg)) {
|
||||
return secfalse;
|
||||
}
|
||||
const uint32_t msg_size = sizestream.bytes_written;
|
||||
|
||||
usb_write_state state = {
|
||||
.iface_num = iface_num,
|
||||
.packet_index = 0,
|
||||
.packet_pos = MSG_HEADER1_LEN,
|
||||
.buf =
|
||||
{
|
||||
'?',
|
||||
'#',
|
||||
'#',
|
||||
(msg_id >> 8) & 0xFF,
|
||||
msg_id & 0xFF,
|
||||
(msg_size >> 24) & 0xFF,
|
||||
(msg_size >> 16) & 0xFF,
|
||||
(msg_size >> 8) & 0xFF,
|
||||
msg_size & 0xFF,
|
||||
},
|
||||
};
|
||||
|
||||
pb_ostream_t stream = {.callback = &_usb_write,
|
||||
.state = &state,
|
||||
.max_size = SIZE_MAX,
|
||||
.bytes_written = 0,
|
||||
.errmsg = NULL};
|
||||
|
||||
if (false == pb_encode(&stream, fields, msg)) {
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
_usb_write_flush(&state);
|
||||
|
||||
return sectrue;
|
||||
}
|
||||
|
||||
#define MSG_SEND_INIT(TYPE) TYPE msg_send = TYPE##_init_default
|
||||
#define MSG_SEND_ASSIGN_VALUE(FIELD, VALUE) \
|
||||
{ \
|
||||
msg_send.has_##FIELD = true; \
|
||||
msg_send.FIELD = VALUE; \
|
||||
}
|
||||
#define MSG_SEND_ASSIGN_STRING(FIELD, VALUE) \
|
||||
{ \
|
||||
msg_send.has_##FIELD = true; \
|
||||
memzero(msg_send.FIELD, sizeof(msg_send.FIELD)); \
|
||||
strncpy(msg_send.FIELD, VALUE, sizeof(msg_send.FIELD) - 1); \
|
||||
}
|
||||
#define MSG_SEND_ASSIGN_STRING_LEN(FIELD, VALUE, LEN) \
|
||||
{ \
|
||||
msg_send.has_##FIELD = true; \
|
||||
memzero(msg_send.FIELD, sizeof(msg_send.FIELD)); \
|
||||
strncpy(msg_send.FIELD, VALUE, MIN(LEN, sizeof(msg_send.FIELD) - 1)); \
|
||||
}
|
||||
#define MSG_SEND_ASSIGN_BYTES(FIELD, VALUE, LEN) \
|
||||
{ \
|
||||
msg_send.has_##FIELD = true; \
|
||||
memzero(msg_send.FIELD.bytes, sizeof(msg_send.FIELD.bytes)); \
|
||||
memcpy(msg_send.FIELD.bytes, VALUE, \
|
||||
MIN(LEN, sizeof(msg_send.FIELD.bytes))); \
|
||||
msg_send.FIELD.size = MIN(LEN, sizeof(msg_send.FIELD.bytes)); \
|
||||
}
|
||||
#define MSG_SEND(TYPE) \
|
||||
_send_msg(iface_num, MessageType_MessageType_##TYPE, TYPE##_fields, &msg_send)
|
||||
|
||||
typedef struct {
|
||||
uint8_t iface_num;
|
||||
uint8_t packet_index;
|
||||
uint8_t packet_pos;
|
||||
uint8_t *buf;
|
||||
} usb_read_state;
|
||||
|
||||
static void _usb_webusb_read_retry(uint8_t iface_num, uint8_t *buf) {
|
||||
for (int retry = 0;; retry++) {
|
||||
int r =
|
||||
usb_webusb_read_blocking(iface_num, buf, USB_PACKET_SIZE, USB_TIMEOUT);
|
||||
if (r != USB_PACKET_SIZE) { // reading failed
|
||||
if (r == 0 && retry < 10) {
|
||||
// only timeout => let's try again
|
||||
} else {
|
||||
// error
|
||||
error_shutdown("Error reading", "from USB.", "Try different",
|
||||
"USB cable.");
|
||||
}
|
||||
}
|
||||
return; // success
|
||||
}
|
||||
}
|
||||
|
||||
/* we don't use secbool/sectrue/secfalse here as it is a nanopb api */
|
||||
static bool _usb_read(pb_istream_t *stream, uint8_t *buf, size_t count) {
|
||||
usb_read_state *state = (usb_read_state *)(stream->state);
|
||||
|
||||
size_t read = 0;
|
||||
// while we have data left
|
||||
while (read < count) {
|
||||
size_t remaining = count - read;
|
||||
// if all remaining data fit into our packet
|
||||
if (state->packet_pos + remaining <= USB_PACKET_SIZE) {
|
||||
// append data from buf to state->buf
|
||||
memcpy(buf + read, state->buf + state->packet_pos, remaining);
|
||||
// advance position
|
||||
state->packet_pos += remaining;
|
||||
// and return
|
||||
return true;
|
||||
} else {
|
||||
// append data that fits
|
||||
memcpy(buf + read, state->buf + state->packet_pos,
|
||||
USB_PACKET_SIZE - state->packet_pos);
|
||||
read += USB_PACKET_SIZE - state->packet_pos;
|
||||
// read next packet (with retry)
|
||||
_usb_webusb_read_retry(state->iface_num, state->buf);
|
||||
// prepare next packet
|
||||
state->packet_index++;
|
||||
state->packet_pos = MSG_HEADER2_LEN;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void _usb_read_flush(usb_read_state *state) { (void)state; }
|
||||
|
||||
static secbool _recv_msg(uint8_t iface_num, uint32_t msg_size, uint8_t *buf,
|
||||
const pb_msgdesc_t *fields, void *msg) {
|
||||
usb_read_state state = {.iface_num = iface_num,
|
||||
.packet_index = 0,
|
||||
.packet_pos = MSG_HEADER1_LEN,
|
||||
.buf = buf};
|
||||
|
||||
pb_istream_t stream = {.callback = &_usb_read,
|
||||
.state = &state,
|
||||
.bytes_left = msg_size,
|
||||
.errmsg = NULL};
|
||||
|
||||
if (false == pb_decode_noinit(&stream, fields, msg)) {
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
_usb_read_flush(&state);
|
||||
|
||||
return sectrue;
|
||||
}
|
||||
|
||||
#define MSG_RECV_INIT(TYPE) TYPE msg_recv = TYPE##_init_default
|
||||
#define MSG_RECV_CALLBACK(FIELD, CALLBACK, ARGUMENT) \
|
||||
{ \
|
||||
msg_recv.FIELD.funcs.decode = &CALLBACK; \
|
||||
msg_recv.FIELD.arg = (void *)ARGUMENT; \
|
||||
}
|
||||
#define MSG_RECV(TYPE) \
|
||||
_recv_msg(iface_num, msg_size, buf, TYPE##_fields, &msg_recv)
|
||||
|
||||
void send_user_abort(uint8_t iface_num, const char *msg) {
|
||||
MSG_SEND_INIT(Failure);
|
||||
MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ActionCancelled);
|
||||
MSG_SEND_ASSIGN_STRING(message, msg);
|
||||
MSG_SEND(Failure);
|
||||
}
|
||||
|
||||
static void send_msg_features(uint8_t iface_num,
|
||||
const vendor_header *const vhdr,
|
||||
const image_header *const hdr) {
|
||||
MSG_SEND_INIT(Features);
|
||||
MSG_SEND_ASSIGN_STRING(vendor, "trezor.io");
|
||||
MSG_SEND_ASSIGN_VALUE(major_version, VERSION_MAJOR);
|
||||
MSG_SEND_ASSIGN_VALUE(minor_version, VERSION_MINOR);
|
||||
MSG_SEND_ASSIGN_VALUE(patch_version, VERSION_PATCH);
|
||||
MSG_SEND_ASSIGN_VALUE(bootloader_mode, true);
|
||||
MSG_SEND_ASSIGN_STRING(model, "T");
|
||||
if (vhdr && hdr) {
|
||||
MSG_SEND_ASSIGN_VALUE(firmware_present, true);
|
||||
MSG_SEND_ASSIGN_VALUE(fw_major, (hdr->version & 0xFF));
|
||||
MSG_SEND_ASSIGN_VALUE(fw_minor, ((hdr->version >> 8) & 0xFF));
|
||||
MSG_SEND_ASSIGN_VALUE(fw_patch, ((hdr->version >> 16) & 0xFF));
|
||||
MSG_SEND_ASSIGN_STRING_LEN(fw_vendor, vhdr->vstr, vhdr->vstr_len);
|
||||
uint8_t hash[32];
|
||||
vendor_keys_hash(vhdr, hash);
|
||||
MSG_SEND_ASSIGN_BYTES(fw_vendor_keys, hash, 32);
|
||||
} else {
|
||||
MSG_SEND_ASSIGN_VALUE(firmware_present, false);
|
||||
}
|
||||
MSG_SEND(Features);
|
||||
}
|
||||
|
||||
void process_msg_Initialize(uint8_t iface_num, uint32_t msg_size, uint8_t *buf,
|
||||
const vendor_header *const vhdr,
|
||||
const image_header *const hdr) {
|
||||
MSG_RECV_INIT(Initialize);
|
||||
MSG_RECV(Initialize);
|
||||
send_msg_features(iface_num, vhdr, hdr);
|
||||
}
|
||||
|
||||
void process_msg_GetFeatures(uint8_t iface_num, uint32_t msg_size, uint8_t *buf,
|
||||
const vendor_header *const vhdr,
|
||||
const image_header *const hdr) {
|
||||
MSG_RECV_INIT(GetFeatures);
|
||||
MSG_RECV(GetFeatures);
|
||||
send_msg_features(iface_num, vhdr, hdr);
|
||||
}
|
||||
|
||||
void process_msg_Ping(uint8_t iface_num, uint32_t msg_size, uint8_t *buf) {
|
||||
MSG_RECV_INIT(Ping);
|
||||
MSG_RECV(Ping);
|
||||
|
||||
MSG_SEND_INIT(Success);
|
||||
MSG_SEND_ASSIGN_STRING(message, msg_recv.message);
|
||||
MSG_SEND(Success);
|
||||
}
|
||||
|
||||
static uint32_t firmware_remaining, firmware_block, chunk_requested;
|
||||
|
||||
void process_msg_FirmwareErase(uint8_t iface_num, uint32_t msg_size,
|
||||
uint8_t *buf) {
|
||||
firmware_remaining = 0;
|
||||
firmware_block = 0;
|
||||
chunk_requested = 0;
|
||||
|
||||
MSG_RECV_INIT(FirmwareErase);
|
||||
MSG_RECV(FirmwareErase);
|
||||
|
||||
firmware_remaining = msg_recv.has_length ? msg_recv.length : 0;
|
||||
if ((firmware_remaining > 0) &&
|
||||
((firmware_remaining % sizeof(uint32_t)) == 0) &&
|
||||
(firmware_remaining <= (FIRMWARE_SECTORS_COUNT * IMAGE_CHUNK_SIZE))) {
|
||||
// request new firmware
|
||||
chunk_requested = (firmware_remaining > IMAGE_INIT_CHUNK_SIZE)
|
||||
? IMAGE_INIT_CHUNK_SIZE
|
||||
: firmware_remaining;
|
||||
MSG_SEND_INIT(FirmwareRequest);
|
||||
MSG_SEND_ASSIGN_VALUE(offset, 0);
|
||||
MSG_SEND_ASSIGN_VALUE(length, chunk_requested);
|
||||
MSG_SEND(FirmwareRequest);
|
||||
} else {
|
||||
// invalid firmware size
|
||||
MSG_SEND_INIT(Failure);
|
||||
MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_DataError);
|
||||
MSG_SEND_ASSIGN_STRING(message, "Wrong firmware size");
|
||||
MSG_SEND(Failure);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t chunk_size = 0;
|
||||
// SRAM is unused, so we can use it for chunk buffer
|
||||
uint8_t *const chunk_buffer = (uint8_t *const)0x20000000;
|
||||
|
||||
/* we don't use secbool/sectrue/secfalse here as it is a nanopb api */
|
||||
static bool _read_payload(pb_istream_t *stream, const pb_field_t *field,
|
||||
void **arg) {
|
||||
#define BUFSIZE 32768
|
||||
|
||||
uint32_t offset = (uint32_t)(*arg);
|
||||
|
||||
if (stream->bytes_left > IMAGE_CHUNK_SIZE) {
|
||||
chunk_size = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (offset == 0) {
|
||||
// clear chunk buffer
|
||||
memset(chunk_buffer, 0xFF, IMAGE_CHUNK_SIZE);
|
||||
}
|
||||
|
||||
uint32_t chunk_written = offset;
|
||||
chunk_size = offset + stream->bytes_left;
|
||||
|
||||
while (stream->bytes_left) {
|
||||
// update loader but skip first block
|
||||
if (firmware_block > 0) {
|
||||
ui_screen_install_progress_upload(
|
||||
250 + 750 * (firmware_block * IMAGE_CHUNK_SIZE + chunk_written) /
|
||||
(firmware_block * IMAGE_CHUNK_SIZE + firmware_remaining));
|
||||
}
|
||||
// read data
|
||||
if (!pb_read(
|
||||
stream, (pb_byte_t *)(chunk_buffer + chunk_written),
|
||||
(stream->bytes_left > BUFSIZE) ? BUFSIZE : stream->bytes_left)) {
|
||||
chunk_size = 0;
|
||||
return false;
|
||||
}
|
||||
chunk_written += BUFSIZE;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
secbool load_vendor_header_keys(const uint8_t *const data,
|
||||
vendor_header *const vhdr);
|
||||
|
||||
static int version_compare(uint32_t vera, uint32_t verb) {
|
||||
int a, b;
|
||||
a = vera & 0xFF;
|
||||
b = verb & 0xFF;
|
||||
if (a != b) return a - b;
|
||||
a = (vera >> 8) & 0xFF;
|
||||
b = (verb >> 8) & 0xFF;
|
||||
if (a != b) return a - b;
|
||||
a = (vera >> 16) & 0xFF;
|
||||
b = (verb >> 16) & 0xFF;
|
||||
if (a != b) return a - b;
|
||||
a = (vera >> 24) & 0xFF;
|
||||
b = (verb >> 24) & 0xFF;
|
||||
return a - b;
|
||||
}
|
||||
|
||||
static void detect_installation(vendor_header *current_vhdr,
|
||||
image_header *current_hdr,
|
||||
const vendor_header *const new_vhdr,
|
||||
const image_header *const new_hdr,
|
||||
secbool *is_new, secbool *is_upgrade,
|
||||
secbool *is_downgrade_wipe) {
|
||||
*is_new = secfalse;
|
||||
*is_upgrade = secfalse;
|
||||
*is_downgrade_wipe = secfalse;
|
||||
if (sectrue !=
|
||||
load_vendor_header_keys((const uint8_t *)FIRMWARE_START, current_vhdr)) {
|
||||
*is_new = sectrue;
|
||||
return;
|
||||
}
|
||||
if (sectrue !=
|
||||
load_image_header((const uint8_t *)FIRMWARE_START + current_vhdr->hdrlen,
|
||||
FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE,
|
||||
current_vhdr->vsig_m, current_vhdr->vsig_n,
|
||||
current_vhdr->vpub, current_hdr)) {
|
||||
*is_new = sectrue;
|
||||
return;
|
||||
}
|
||||
uint8_t hash1[32], hash2[32];
|
||||
vendor_keys_hash(new_vhdr, hash1);
|
||||
vendor_keys_hash(current_vhdr, hash2);
|
||||
if (0 != memcmp(hash1, hash2, 32)) {
|
||||
return;
|
||||
}
|
||||
if (version_compare(new_hdr->version, current_hdr->fix_version) < 0) {
|
||||
*is_downgrade_wipe = sectrue;
|
||||
return;
|
||||
}
|
||||
*is_upgrade = sectrue;
|
||||
}
|
||||
|
||||
static int firmware_upload_chunk_retry = FIRMWARE_UPLOAD_CHUNK_RETRY_COUNT;
|
||||
static uint32_t headers_offset = 0;
|
||||
static uint32_t read_offset = 0;
|
||||
|
||||
int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size,
|
||||
uint8_t *buf) {
|
||||
MSG_RECV_INIT(FirmwareUpload);
|
||||
MSG_RECV_CALLBACK(payload, _read_payload, read_offset);
|
||||
secbool r = MSG_RECV(FirmwareUpload);
|
||||
|
||||
if (sectrue != r || chunk_size != (chunk_requested + read_offset)) {
|
||||
MSG_SEND_INIT(Failure);
|
||||
MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_DataError);
|
||||
MSG_SEND_ASSIGN_STRING(message, "Invalid chunk size");
|
||||
MSG_SEND(Failure);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static image_header hdr;
|
||||
secbool is_upgrade = secfalse;
|
||||
secbool is_downgrade_wipe = secfalse;
|
||||
|
||||
if (firmware_block == 0) {
|
||||
if (headers_offset == 0) {
|
||||
// first block and headers are not yet parsed
|
||||
vendor_header vhdr;
|
||||
if (sectrue != load_vendor_header_keys(chunk_buffer, &vhdr)) {
|
||||
MSG_SEND_INIT(Failure);
|
||||
MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError);
|
||||
MSG_SEND_ASSIGN_STRING(message, "Invalid vendor header");
|
||||
MSG_SEND(Failure);
|
||||
return -2;
|
||||
}
|
||||
if (sectrue != load_image_header(chunk_buffer + vhdr.hdrlen,
|
||||
FIRMWARE_IMAGE_MAGIC,
|
||||
FIRMWARE_IMAGE_MAXSIZE, vhdr.vsig_m,
|
||||
vhdr.vsig_n, vhdr.vpub, &hdr)) {
|
||||
MSG_SEND_INIT(Failure);
|
||||
MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError);
|
||||
MSG_SEND_ASSIGN_STRING(message, "Invalid firmware header");
|
||||
MSG_SEND(Failure);
|
||||
return -3;
|
||||
}
|
||||
|
||||
vendor_header current_vhdr;
|
||||
image_header current_hdr;
|
||||
secbool is_new = secfalse;
|
||||
detect_installation(¤t_vhdr, ¤t_hdr, &vhdr, &hdr, &is_new,
|
||||
&is_upgrade, &is_downgrade_wipe);
|
||||
|
||||
// no user confirmations, go directly to upload
|
||||
|
||||
headers_offset = IMAGE_HEADER_SIZE + vhdr.hdrlen;
|
||||
read_offset = IMAGE_INIT_CHUNK_SIZE;
|
||||
|
||||
// request the rest of the first chunk
|
||||
MSG_SEND_INIT(FirmwareRequest);
|
||||
chunk_requested = IMAGE_CHUNK_SIZE - read_offset;
|
||||
MSG_SEND_ASSIGN_VALUE(offset, read_offset);
|
||||
MSG_SEND_ASSIGN_VALUE(length, chunk_requested);
|
||||
MSG_SEND(FirmwareRequest);
|
||||
|
||||
firmware_remaining -= read_offset;
|
||||
return (int)firmware_remaining;
|
||||
} else {
|
||||
// first block with the headers parsed -> the first chunk is now complete
|
||||
read_offset = 0;
|
||||
|
||||
ui_fadeout();
|
||||
ui_screen_install();
|
||||
ui_fadein();
|
||||
|
||||
ensure(flash_erase_sectors(FIRMWARE_SECTORS, FIRMWARE_SECTORS_COUNT,
|
||||
ui_screen_install_progress_erase),
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
// should not happen, but double-check
|
||||
if (firmware_block >= FIRMWARE_SECTORS_COUNT) {
|
||||
MSG_SEND_INIT(Failure);
|
||||
MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError);
|
||||
MSG_SEND_ASSIGN_STRING(message, "Firmware too big");
|
||||
MSG_SEND(Failure);
|
||||
return -5;
|
||||
}
|
||||
|
||||
if (sectrue != check_single_hash(hdr.hashes + firmware_block * 32,
|
||||
chunk_buffer + headers_offset,
|
||||
chunk_size - headers_offset)) {
|
||||
if (firmware_upload_chunk_retry > 0) {
|
||||
--firmware_upload_chunk_retry;
|
||||
MSG_SEND_INIT(FirmwareRequest);
|
||||
MSG_SEND_ASSIGN_VALUE(offset, firmware_block * IMAGE_CHUNK_SIZE);
|
||||
MSG_SEND_ASSIGN_VALUE(length, chunk_requested);
|
||||
MSG_SEND(FirmwareRequest);
|
||||
return (int)firmware_remaining;
|
||||
}
|
||||
|
||||
MSG_SEND_INIT(Failure);
|
||||
MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError);
|
||||
MSG_SEND_ASSIGN_STRING(message, "Invalid chunk hash");
|
||||
MSG_SEND(Failure);
|
||||
return -6;
|
||||
}
|
||||
|
||||
ensure(flash_unlock_write(), NULL);
|
||||
|
||||
const uint32_t *const src = (const uint32_t *const)chunk_buffer;
|
||||
for (int i = 0; i < chunk_size / sizeof(uint32_t); i++) {
|
||||
ensure(flash_write_word(FIRMWARE_SECTORS[firmware_block],
|
||||
i * sizeof(uint32_t), src[i]),
|
||||
NULL);
|
||||
}
|
||||
|
||||
ensure(flash_lock_write(), NULL);
|
||||
|
||||
headers_offset = 0;
|
||||
firmware_remaining -= chunk_requested;
|
||||
firmware_block++;
|
||||
firmware_upload_chunk_retry = FIRMWARE_UPLOAD_CHUNK_RETRY_COUNT;
|
||||
|
||||
if (firmware_remaining > 0) {
|
||||
chunk_requested = (firmware_remaining > IMAGE_CHUNK_SIZE)
|
||||
? IMAGE_CHUNK_SIZE
|
||||
: firmware_remaining;
|
||||
MSG_SEND_INIT(FirmwareRequest);
|
||||
MSG_SEND_ASSIGN_VALUE(offset, firmware_block * IMAGE_CHUNK_SIZE);
|
||||
MSG_SEND_ASSIGN_VALUE(length, chunk_requested);
|
||||
MSG_SEND(FirmwareRequest);
|
||||
} else {
|
||||
MSG_SEND_INIT(Success);
|
||||
MSG_SEND(Success);
|
||||
}
|
||||
return (int)firmware_remaining;
|
||||
}
|
||||
|
||||
int process_msg_WipeDevice(uint8_t iface_num, uint32_t msg_size, uint8_t *buf) {
|
||||
static const uint8_t sectors[] = {
|
||||
FLASH_SECTOR_STORAGE_1,
|
||||
FLASH_SECTOR_STORAGE_2,
|
||||
// 3, // skip because of MPU protection
|
||||
FLASH_SECTOR_FIRMWARE_START,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
FLASH_SECTOR_FIRMWARE_END,
|
||||
FLASH_SECTOR_UNUSED_START,
|
||||
13,
|
||||
14,
|
||||
// FLASH_SECTOR_UNUSED_END, // skip because of MPU protection
|
||||
FLASH_SECTOR_FIRMWARE_EXTRA_START,
|
||||
18,
|
||||
19,
|
||||
20,
|
||||
21,
|
||||
22,
|
||||
FLASH_SECTOR_FIRMWARE_EXTRA_END,
|
||||
};
|
||||
if (sectrue !=
|
||||
flash_erase_sectors(sectors, sizeof(sectors), ui_screen_wipe_progress)) {
|
||||
MSG_SEND_INIT(Failure);
|
||||
MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError);
|
||||
MSG_SEND_ASSIGN_STRING(message, "Could not erase flash");
|
||||
MSG_SEND(Failure);
|
||||
return -1;
|
||||
} else {
|
||||
MSG_SEND_INIT(Success);
|
||||
MSG_SEND(Success);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void process_msg_unknown(uint8_t iface_num, uint32_t msg_size, uint8_t *buf) {
|
||||
// consume remaining message
|
||||
int remaining_chunks = 0;
|
||||
|
||||
if (msg_size > (USB_PACKET_SIZE - MSG_HEADER1_LEN)) {
|
||||
// calculate how many blocks need to be read to drain the message (rounded
|
||||
// up to not leave any behind)
|
||||
remaining_chunks = (msg_size - (USB_PACKET_SIZE - MSG_HEADER1_LEN) +
|
||||
((USB_PACKET_SIZE - MSG_HEADER2_LEN) - 1)) /
|
||||
(USB_PACKET_SIZE - MSG_HEADER2_LEN);
|
||||
}
|
||||
|
||||
for (int i = 0; i < remaining_chunks; i++) {
|
||||
// read next packet (with retry)
|
||||
_usb_webusb_read_retry(iface_num, buf);
|
||||
}
|
||||
|
||||
MSG_SEND_INIT(Failure);
|
||||
MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_UnexpectedMessage);
|
||||
MSG_SEND_ASSIGN_STRING(message, "Unexpected message");
|
||||
MSG_SEND(Failure);
|
||||
}
|
1
core/embed/bootloader_ci/messages.h
Symbolic link
1
core/embed/bootloader_ci/messages.h
Symbolic link
@ -0,0 +1 @@
|
||||
../bootloader/messages.h
|
1
core/embed/bootloader_ci/protob
Symbolic link
1
core/embed/bootloader_ci/protob
Symbolic link
@ -0,0 +1 @@
|
||||
../bootloader/protob
|
1
core/embed/bootloader_ci/startup.s
Symbolic link
1
core/embed/bootloader_ci/startup.s
Symbolic link
@ -0,0 +1 @@
|
||||
../bootloader/startup.s
|
1
core/embed/bootloader_ci/version.h
Symbolic link
1
core/embed/bootloader_ci/version.h
Symbolic link
@ -0,0 +1 @@
|
||||
../bootloader/version.h
|
Loading…
Reference in New Issue
Block a user