mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-22 14:28:07 +00:00
feat(core): Copy firmware image header to bootloader RAM to be able to skip directly to firmware install, jumping from firmware to bootloader
Change linking of util.s and limited version of util.s to avoid mistakes with boardloader etc.
This commit is contained in:
parent
8a1573311c
commit
b69ef9d168
@ -22,7 +22,7 @@ FEATURES_WANTED = ["sd_card"]
|
||||
|
||||
CCFLAGS_MOD = ''
|
||||
CPPPATH_MOD = []
|
||||
CPPDEFINES_MOD = []
|
||||
CPPDEFINES_MOD = ["BOARDLOADER"]
|
||||
SOURCE_MOD = []
|
||||
CPPDEFINES_HAL = []
|
||||
SOURCE_HAL = []
|
||||
@ -71,7 +71,7 @@ SOURCE_BOARDLOADER = [
|
||||
'embed/boardloader/main.c',
|
||||
]
|
||||
|
||||
env = Environment(ENV=os.environ, CFLAGS='%s -DPRODUCTION=%s' % (ARGUMENTS.get('CFLAGS', ''), ARGUMENTS.get('PRODUCTION', '0')))
|
||||
env = Environment(ENV=os.environ, CFLAGS='%s -DPRODUCTION=%s' % (ARGUMENTS.get('CFLAGS', ''), ARGUMENTS.get('PRODUCTION', '0')), CONSTRAINTS=["limited_util_s"])
|
||||
|
||||
FEATURES_AVAILABLE = tools.configure_board(TREZOR_MODEL, FEATURES_WANTED, env, CPPDEFINES_HAL, SOURCE_HAL, PATH_HAL)
|
||||
|
||||
|
@ -109,7 +109,7 @@ tools.add_font('DEMIBOLD', FONT_DEMIBOLD, CPPDEFINES_MOD, SOURCE_MOD)
|
||||
tools.add_font('MONO', FONT_MONO, CPPDEFINES_MOD, SOURCE_MOD)
|
||||
tools.add_font('BIG', FONT_BIG, CPPDEFINES_MOD, SOURCE_MOD)
|
||||
|
||||
env = Environment(ENV=os.environ, CFLAGS='%s -DPRODUCTION=%s' % (ARGUMENTS.get('CFLAGS', ''), ARGUMENTS.get('PRODUCTION', '0')))
|
||||
env = Environment(ENV=os.environ, CFLAGS='%s -DPRODUCTION=%s' % (ARGUMENTS.get('CFLAGS', ''), ARGUMENTS.get('PRODUCTION', '0')), CONSTRAINTS=["limited_util_s"])
|
||||
|
||||
FEATURES_AVAILABLE = tools.configure_board(TREZOR_MODEL, FEATURES_WANTED, env, CPPDEFINES_HAL, SOURCE_HAL, PATH_HAL)
|
||||
|
||||
|
@ -79,7 +79,7 @@ tools.add_font('DEMIBOLD', FONT_DEMIBOLD, CPPDEFINES_MOD, SOURCE_MOD)
|
||||
tools.add_font('MONO', FONT_MONO, CPPDEFINES_MOD, SOURCE_MOD)
|
||||
tools.add_font('BIG', FONT_BIG, CPPDEFINES_MOD, SOURCE_MOD)
|
||||
|
||||
env = Environment(ENV=os.environ, CFLAGS='%s -DPRODUCTION=%s' % (ARGUMENTS.get('CFLAGS', ''), ARGUMENTS.get('PRODUCTION', '0')))
|
||||
env = Environment(ENV=os.environ, CFLAGS='%s -DPRODUCTION=%s' % (ARGUMENTS.get('CFLAGS', ''), ARGUMENTS.get('PRODUCTION', '0')), CONSTRAINTS=["limited_util_s"])
|
||||
|
||||
FEATURES_AVAILABLE = tools.configure_board(TREZOR_MODEL, FEATURES_WANTED, env, CPPDEFINES_HAL, SOURCE_HAL, PATH_HAL)
|
||||
|
||||
|
2
core/embed/boardloader/.changelog.d/3205.added
Normal file
2
core/embed/boardloader/.changelog.d/3205.added
Normal file
@ -0,0 +1,2 @@
|
||||
Added firmware update without interaction.
|
||||
Split builds of different parts to use simle util.s assembler, while FW+bootloader use interconnected ones.
|
2
core/embed/bootloader/.changelog.d/3205.added
Normal file
2
core/embed/bootloader/.changelog.d/3205.added
Normal file
@ -0,0 +1,2 @@
|
||||
Added firmware update without interaction.
|
||||
Split builds of different parts to use simle util.s assembler, while FW+bootloader use interconnected ones.
|
@ -23,6 +23,9 @@ ccmram_end = ORIGIN(CCMRAM) + LENGTH(CCMRAM);
|
||||
sram_start = ORIGIN(SRAM);
|
||||
sram_end = ORIGIN(SRAM) + LENGTH(SRAM);
|
||||
|
||||
/* IMAGE_HEADER_SIZE is 0x400, this is for interaction-less firmware update start */
|
||||
firmware_header_start = ccmram_end - 0x400;
|
||||
|
||||
_codelen = SIZEOF(.flash) + SIZEOF(.data);
|
||||
|
||||
SECTIONS {
|
||||
|
@ -7,7 +7,7 @@
|
||||
reset_handler:
|
||||
// setup environment for subsequent stage of code
|
||||
ldr r0, =ccmram_start // r0 - point to beginning of CCMRAM
|
||||
ldr r1, =ccmram_end // r1 - point to byte after the end of CCMRAM
|
||||
ldr r1, =firmware_header_start // r1 - point to byte where firmware image header might start
|
||||
ldr r2, =0 // r2 - the word-sized value to be written
|
||||
bl memset_reg
|
||||
|
||||
|
2
core/embed/bootloader_ci/.changelog.d/3205.added
Normal file
2
core/embed/bootloader_ci/.changelog.d/3205.added
Normal file
@ -0,0 +1,2 @@
|
||||
Added firmware update without interaction.
|
||||
Split builds of different parts to use simle util.s assembler, while FW+bootloader use interconnected ones.
|
@ -23,6 +23,9 @@ ccmram_end = ORIGIN(CCMRAM) + LENGTH(CCMRAM);
|
||||
sram_start = ORIGIN(SRAM);
|
||||
sram_end = ORIGIN(SRAM) + LENGTH(SRAM);
|
||||
|
||||
/* IMAGE_HEADER_SIZE is 0x400, this is for interaction-less firmware update start */
|
||||
firmware_header_start = ccmram_end - 0x400;
|
||||
|
||||
_codelen = SIZEOF(.flash) + SIZEOF(.data);
|
||||
|
||||
SECTIONS {
|
||||
|
@ -88,6 +88,9 @@
|
||||
// from util.s
|
||||
extern void shutdown_privileged(void);
|
||||
|
||||
// from linker script
|
||||
extern uint8_t firmware_header_start;
|
||||
|
||||
int main(void) {
|
||||
random_delays_init();
|
||||
|
||||
@ -231,12 +234,17 @@ void BusFault_Handler(void) { error_shutdown("INTERNAL ERROR", "(BF)"); }
|
||||
void UsageFault_Handler(void) { error_shutdown("INTERNAL ERROR", "(UF)"); }
|
||||
|
||||
__attribute__((noreturn)) void reboot_to_bootloader() {
|
||||
mpu_config_bootloader();
|
||||
jump_to_with_flag(BOOTLOADER_START + IMAGE_HEADER_SIZE,
|
||||
STAY_IN_BOOTLOADER_FLAG);
|
||||
for (;;)
|
||||
;
|
||||
}
|
||||
|
||||
void copy_image_header_for_bootloader(const uint8_t *image_header) {
|
||||
memcpy(&firmware_header_start, image_header, IMAGE_HEADER_SIZE);
|
||||
}
|
||||
|
||||
void SVC_C_Handler(uint32_t *stack) {
|
||||
uint8_t svc_number = ((uint8_t *)stack[6])[-2];
|
||||
switch (svc_number) {
|
||||
@ -259,7 +267,11 @@ void SVC_C_Handler(uint32_t *stack) {
|
||||
for (;;)
|
||||
;
|
||||
break;
|
||||
case SVC_REBOOT_COPY_IMAGE_HEADER:
|
||||
copy_image_header_for_bootloader((uint8_t *)stack[0]);
|
||||
// break is omitted here because we want to continue to reboot below
|
||||
case SVC_REBOOT_TO_BOOTLOADER:
|
||||
|
||||
ensure_compatible_settings();
|
||||
mpu_config_bootloader();
|
||||
__asm__ volatile("msr control, %0" ::"r"(0x0));
|
||||
|
@ -22,6 +22,9 @@ data_size = SIZEOF(.data);
|
||||
ccmram_start = ORIGIN(CCMRAM);
|
||||
ccmram_end = ORIGIN(CCMRAM) + LENGTH(CCMRAM);
|
||||
|
||||
/* IMAGE_HEADER_SIZE is 0x400, this is for interaction-less firmware update start */
|
||||
firmware_header_start = ccmram_end - 0x400;
|
||||
|
||||
/* used by the startup code to wipe memory */
|
||||
sram_start = ORIGIN(SRAM);
|
||||
sram_end = ORIGIN(SRAM) + LENGTH(SRAM);
|
||||
|
159
core/embed/trezorhal/stm32f4/limited_util.s
Normal file
159
core/embed/trezorhal/stm32f4/limited_util.s
Normal file
@ -0,0 +1,159 @@
|
||||
.syntax unified
|
||||
|
||||
.text
|
||||
|
||||
.global memset_reg
|
||||
.type memset_reg, STT_FUNC
|
||||
memset_reg:
|
||||
// call with the following (note that the arguments are not validated prior to use):
|
||||
// r0 - address of first word to write (inclusive)
|
||||
// r1 - address of first word following the address in r0 to NOT write (exclusive)
|
||||
// r2 - word value to be written
|
||||
// both addresses in r0 and r1 needs to be divisible by 4!
|
||||
.L_loop_begin:
|
||||
str r2, [r0], 4 // store the word in r2 to the address in r0, post-indexed
|
||||
cmp r0, r1
|
||||
bne .L_loop_begin
|
||||
bx lr
|
||||
|
||||
.global jump_to
|
||||
.type jump_to, STT_FUNC
|
||||
jump_to:
|
||||
mov r4, r0 // save input argument r0 (the address of the next stage's vector table) (r4 is callee save)
|
||||
// this subroutine re-points the exception handlers before the C code
|
||||
// that comprises them has been given a good environment to run.
|
||||
// therefore, this code needs to disable interrupts before the VTOR
|
||||
// update. then, the reset_handler of the next stage needs to re-enable interrupts.
|
||||
// the following prevents activation of all exceptions except Non-Maskable Interrupt (NMI).
|
||||
// according to "ARM Cortex-M Programming Guide to Memory Barrier Instructions" Application Note 321, section 4.8:
|
||||
// "there is no requirement to insert memory barrier instructions after CPSID".
|
||||
cpsid f
|
||||
// wipe memory at the end of the current stage of code
|
||||
bl clear_otg_hs_memory
|
||||
ldr r0, =ccmram_start // r0 - point to beginning of CCMRAM
|
||||
ldr r1, =ccmram_end // r1 - point to byte after the end of CCMRAM
|
||||
ldr r2, =0 // r2 - the word-sized value to be written
|
||||
bl memset_reg
|
||||
ldr r0, =sram_start // r0 - point to beginning of SRAM
|
||||
ldr r1, =sram_end // r1 - point to byte after the end of SRAM
|
||||
ldr r2, =0 // r2 - the word-sized value to be written
|
||||
bl memset_reg
|
||||
mov lr, r4
|
||||
// clear out the general purpose registers before the next stage's code can run (even the NMI exception handler)
|
||||
ldr r0, =0
|
||||
mov r1, r0
|
||||
mov r2, r0
|
||||
mov r3, r0
|
||||
mov r4, r0
|
||||
mov r5, r0
|
||||
mov r6, r0
|
||||
mov r7, r0
|
||||
mov r8, r0
|
||||
mov r9, r0
|
||||
mov r10, r0
|
||||
mov r11, r0
|
||||
mov r12, r0
|
||||
// give the next stage a fresh main stack pointer
|
||||
ldr r0, [lr] // set r0 to the main stack pointer in the next stage's vector table
|
||||
msr msp, r0 // give the next stage its main stack pointer
|
||||
// point to the next stage's exception handlers
|
||||
// AN321, section 4.11: "a memory barrier is not required after a VTOR update"
|
||||
.set SCB_VTOR, 0xE000ED08 // reference "Cortex-M4 Devices Generic User Guide" section 4.3
|
||||
ldr r0, =SCB_VTOR
|
||||
str lr, [r0]
|
||||
mov r0, r1 // zero out r0
|
||||
// go on to the next stage
|
||||
ldr lr, [lr, 4] // set lr to the next stage's reset_handler
|
||||
bx lr
|
||||
|
||||
.global jump_to_unprivileged
|
||||
.type jump_to_unprivileged, STT_FUNC
|
||||
jump_to_unprivileged:
|
||||
mov r4, r0 // save input argument r0 (the address of the next stage's vector table) (r4 is callee save)
|
||||
// this subroutine re-points the exception handlers before the C code
|
||||
// that comprises them has been given a good environment to run.
|
||||
// therefore, this code needs to disable interrupts before the VTOR
|
||||
// update. then, the reset_handler of the next stage needs to re-enable interrupts.
|
||||
// the following prevents activation of all exceptions except Non-Maskable Interrupt (NMI).
|
||||
// according to "ARM Cortex-M Programming Guide to Memory Barrier Instructions" Application Note 321, section 4.8:
|
||||
// "there is no requirement to insert memory barrier instructions after CPSID".
|
||||
cpsid f
|
||||
// wipe memory at the end of the current stage of code
|
||||
bl clear_otg_hs_memory
|
||||
ldr r0, =ccmram_start // r0 - point to beginning of CCMRAM
|
||||
ldr r1, =ccmram_end // r1 - point to byte after the end of CCMRAM
|
||||
ldr r2, =0 // r2 - the word-sized value to be written
|
||||
bl memset_reg
|
||||
ldr r0, =sram_start // r0 - point to beginning of SRAM
|
||||
ldr r1, =sram_end // r1 - point to byte after the end of SRAM
|
||||
ldr r2, =0 // r2 - the word-sized value to be written
|
||||
bl memset_reg
|
||||
mov lr, r4
|
||||
// clear out the general purpose registers before the next stage's code can run (even the NMI exception handler)
|
||||
ldr r0, =0
|
||||
mov r1, r0
|
||||
mov r2, r0
|
||||
mov r3, r0
|
||||
mov r4, r0
|
||||
mov r5, r0
|
||||
mov r6, r0
|
||||
mov r7, r0
|
||||
mov r8, r0
|
||||
mov r9, r0
|
||||
mov r10, r0
|
||||
mov r11, r0
|
||||
mov r12, r0
|
||||
// give the next stage a fresh main stack pointer
|
||||
ldr r0, [lr] // set r0 to the main stack pointer in the next stage's vector table
|
||||
msr msp, r0 // give the next stage its main stack pointer
|
||||
// point to the next stage's exception handlers
|
||||
// AN321, section 4.11: "a memory barrier is not required after a VTOR update"
|
||||
.set SCB_VTOR, 0xE000ED08 // reference "Cortex-M4 Devices Generic User Guide" section 4.3
|
||||
ldr r0, =SCB_VTOR
|
||||
str lr, [r0]
|
||||
mov r0, r1 // zero out r0
|
||||
// go on to the next stage
|
||||
ldr lr, [lr, 4] // set lr to the next stage's reset_handler
|
||||
// switch to unprivileged mode
|
||||
ldr r0, =1
|
||||
msr control, r0
|
||||
isb
|
||||
// jump
|
||||
bx lr
|
||||
|
||||
.global shutdown_privileged
|
||||
.type shutdown_privileged, STT_FUNC
|
||||
// The function must be called from the privileged mode
|
||||
shutdown_privileged:
|
||||
cpsid f // disable all exceptions (except for NMI), the instruction is ignored in unprivileged mode
|
||||
// if the exceptions weren't disabled, an exception handler (for example systick handler)
|
||||
// could be called after the memory is erased, which would lead to another exception
|
||||
ldr r0, =0
|
||||
mov r1, r0
|
||||
mov r2, r0
|
||||
mov r3, r0
|
||||
mov r4, r0
|
||||
mov r5, r0
|
||||
mov r6, r0
|
||||
mov r7, r0
|
||||
mov r8, r0
|
||||
mov r9, r0
|
||||
mov r10, r0
|
||||
mov r11, r0
|
||||
mov r12, r0
|
||||
ldr lr, =0xffffffff
|
||||
ldr r0, =ccmram_start
|
||||
ldr r1, =ccmram_end
|
||||
// set to value in r2
|
||||
bl memset_reg
|
||||
ldr r0, =sram_start
|
||||
ldr r1, =sram_end
|
||||
// set to value in r2
|
||||
bl memset_reg
|
||||
bl clear_otg_hs_memory
|
||||
ldr r0, =1
|
||||
msr control, r0 // jump to unprivileged mode
|
||||
ldr r0, =0
|
||||
b . // loop forever
|
||||
|
||||
.end
|
@ -5,10 +5,12 @@
|
||||
#define SVC_SET_PRIORITY 2
|
||||
#define SVC_SHUTDOWN 4
|
||||
#define SVC_REBOOT_TO_BOOTLOADER 5
|
||||
#define SVC_REBOOT_COPY_IMAGE_HEADER 6
|
||||
|
||||
// from util.s
|
||||
extern void shutdown_privileged(void);
|
||||
extern void reboot_to_bootloader(void);
|
||||
extern void copy_image_header_for_bootloader(const uint8_t *image_header);
|
||||
|
||||
static inline uint32_t is_mode_unprivileged(void) {
|
||||
uint32_t r0;
|
||||
@ -58,6 +60,7 @@ static inline void svc_shutdown(void) {
|
||||
shutdown_privileged();
|
||||
}
|
||||
}
|
||||
|
||||
static inline void svc_reboot_to_bootloader(void) {
|
||||
if (is_mode_unprivileged() && !is_mode_handler()) {
|
||||
__asm__ __volatile__("svc %0" ::"i"(SVC_REBOOT_TO_BOOTLOADER) : "memory");
|
||||
@ -65,3 +68,14 @@ static inline void svc_reboot_to_bootloader(void) {
|
||||
reboot_to_bootloader();
|
||||
}
|
||||
}
|
||||
|
||||
static inline void svc_reboot_copy_image_header(const uint8_t *image_address) {
|
||||
if (is_mode_unprivileged() && !is_mode_handler()) {
|
||||
register const uint8_t *r0 __asm__("r0") = image_address;
|
||||
__asm__ __volatile__("svc %0" ::"i"(SVC_REBOOT_COPY_IMAGE_HEADER), "r"(r0)
|
||||
: "memory");
|
||||
} else {
|
||||
copy_image_header_for_bootloader(image_address);
|
||||
reboot_to_bootloader();
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ jump_to_with_flag:
|
||||
// wipe memory at the end of the current stage of code
|
||||
bl clear_otg_hs_memory
|
||||
ldr r0, =ccmram_start // r0 - point to beginning of CCMRAM
|
||||
ldr r1, =ccmram_end // r1 - point to byte after the end of CCMRAM
|
||||
ldr r1, =firmware_header_start // r1 - point to byte after the end of CCMRAM
|
||||
ldr r2, =0 // r2 - the word-sized value to be written
|
||||
bl memset_reg
|
||||
ldr r0, =sram_start // r0 - point to beginning of SRAM
|
||||
|
@ -45,10 +45,21 @@ def stm32f4_common_files(env, defines, sources, paths):
|
||||
"embed/trezorhal/stm32f4/systick.c",
|
||||
"embed/trezorhal/stm32f4/random_delays.c",
|
||||
"embed/trezorhal/stm32f4/rng.c",
|
||||
"embed/trezorhal/stm32f4/util.s",
|
||||
"embed/trezorhal/stm32f4/vectortable.s",
|
||||
]
|
||||
|
||||
# boardloader needs separate assembler for some function unencumbered by various FW+bootloader hacks
|
||||
# this helps to prevent making a bug in boardloader which may be hard to fix since it's locked with write-protect
|
||||
env_constraints = env.get("CONSTRAINTS")
|
||||
if env_constraints and "limited_util_s" in env_constraints:
|
||||
sources += [
|
||||
"embed/trezorhal/stm32f4/limited_util.s",
|
||||
]
|
||||
else:
|
||||
sources += [
|
||||
"embed/trezorhal/stm32f4/util.s",
|
||||
]
|
||||
|
||||
env.get("ENV")["RUST_INCLUDES"] = (
|
||||
"-I../trezorhal/stm32f4;"
|
||||
"-I../../vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Inc;"
|
||||
|
Loading…
Reference in New Issue
Block a user