feat(core): jump back and stay in bootloader for TT via reverse SVC call trampoline

pull/2314/head
Ondrej Mikle 2 years ago committed by matejcik
parent 61a0d69baa
commit c563c987e1

@ -0,0 +1 @@
Jump and stay in bootloader from firmware through SVC call reverse trampoline.

@ -0,0 +1 @@
Jump and stay in bootloader from firmware through SVC call reverse trampoline.

@ -242,6 +242,10 @@ static void check_bootloader_version(void) {
#endif
int main(void) {
// grab "stay in bootloader" flag as soon as possible
register uint32_t r11 __asm__("r11");
volatile uint32_t stay_in_bootloader_flag = r11;
random_delays_init();
// display_init_seq();
#if defined TREZOR_MODEL_T
@ -281,6 +285,9 @@ int main(void) {
vendor_header vhdr;
image_header hdr;
secbool stay_in_bootloader = secfalse; // flag to stay in bootloader
if (stay_in_bootloader_flag == STAY_IN_BOOTLOADER_FLAG) {
stay_in_bootloader = sectrue;
}
// detect whether the devices contains a valid firmware

@ -19,6 +19,9 @@
#include "py/objstr.h"
#include "py/runtime.h"
#ifndef TREZOR_EMULATOR
#include "supervise.h"
#endif
#include "version.h"
@ -245,6 +248,19 @@ STATIC mp_obj_t mod_trezorutils_get_firmware_chunk(const mp_obj_t index_obj,
STATIC MP_DEFINE_CONST_FUN_OBJ_3(mod_trezorutils_get_firmware_chunk_obj,
mod_trezorutils_get_firmware_chunk);
/// def reboot_to_bootloader() -> None:
/// """
/// Reboots to bootloader.
/// """
STATIC mp_obj_t mod_trezorutils_reboot_to_bootloader() {
#ifndef TREZOR_EMULATOR
svc_reboot_to_bootloader();
#endif
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorutils_reboot_to_bootloader_obj,
mod_trezorutils_reboot_to_bootloader);
STATIC mp_obj_str_t mod_trezorutils_revision_obj = {
{&mp_type_bytes}, 0, sizeof(SCM_REVISION) - 1, (const byte *)SCM_REVISION};
@ -272,7 +288,8 @@ STATIC const mp_rom_map_elem_t mp_module_trezorutils_globals_table[] = {
MP_ROM_PTR(&mod_trezorutils_firmware_sector_size_obj)},
{MP_ROM_QSTR(MP_QSTR_FIRMWARE_SECTORS_COUNT),
MP_ROM_INT(FIRMWARE_SECTORS_COUNT)},
{MP_ROM_QSTR(MP_QSTR_reboot_to_bootloader),
MP_ROM_PTR(&mod_trezorutils_reboot_to_bootloader_obj)},
// various built-in constants
{MP_ROM_QSTR(MP_QSTR_SCM_REVISION),
MP_ROM_PTR(&mod_trezorutils_revision_obj)},

@ -41,6 +41,7 @@
#include "compiler_traits.h"
#include "display.h"
#include "flash.h"
#include "image.h"
#include "mpu.h"
#include "random_delays.h"
#ifdef SYSTEM_VIEW
@ -180,6 +181,13 @@ void UsageFault_Handler(void) {
error_shutdown("Internal error", "(UF)", NULL, NULL);
}
__attribute__((noreturn)) void reboot_to_bootloader() {
jump_to_with_flag(BOOTLOADER_START + IMAGE_HEADER_SIZE,
STAY_IN_BOOTLOADER_FLAG);
for (;;)
;
}
void SVC_C_Handler(uint32_t *stack) {
uint8_t svc_number = ((uint8_t *)stack[6])[-2];
switch (svc_number) {
@ -202,6 +210,16 @@ void SVC_C_Handler(uint32_t *stack) {
for (;;)
;
break;
case SVC_REBOOT_TO_BOOTLOADER:
mpu_config_bootloader();
__asm__ volatile("msr control, %0" ::"r"(0x0));
__asm__ volatile("isb");
// See stack layout in
// https://developer.arm.com/documentation/ka004005/latest We are changing
// return address in PC to land into reboot to avoid any bug with ROP and
// raising privileges.
stack[6] = (uintptr_t)reboot_to_bootloader;
return;
default:
stack[0] = 0xffffffff;
break;

@ -49,6 +49,8 @@
})
#endif
#define STAY_IN_BOOTLOADER_FLAG 0x0FC35A96
void shutdown(void);
void __attribute__((noreturn))
@ -78,5 +80,6 @@ extern uint8_t HW_ENTROPY_DATA[HW_ENTROPY_LEN];
void memset_reg(volatile void *start, volatile void *stop, uint32_t val);
void jump_to(uint32_t address);
void jump_to_unprivileged(uint32_t address);
void jump_to_with_flag(uint32_t address, uint32_t register_flag);
#endif

@ -4,9 +4,11 @@
#define SVC_DISABLE_IRQ 1
#define SVC_SET_PRIORITY 2
#define SVC_SHUTDOWN 4
#define SVC_REBOOT_TO_BOOTLOADER 5
// from util.s
extern void shutdown_privileged(void);
extern void reboot_to_bootloader(void);
static inline uint32_t is_mode_unprivileged(void) {
uint32_t r0;
@ -50,3 +52,10 @@ static inline void svc_shutdown(void) {
shutdown_privileged();
}
}
static inline void svc_reboot_to_bootloader(void) {
if (is_mode_unprivileged()) {
__asm__ __volatile__("svc %0" ::"i"(SVC_REBOOT_TO_BOOTLOADER) : "memory");
} else {
reboot_to_bootloader();
}
}

@ -16,10 +16,22 @@ memset_reg:
bne .L_loop_begin
bx lr
// Jump to address given in first argument R0 that points to next's stage's VTOR
// Clear memory and all registers before jump
.global jump_to
.type jump_to, STT_FUNC
jump_to:
ldr r1, =0
bl jump_to_with_flag
// Jump to address given in first argument R0 that points to next's stage's VTOR
// Clear memory and all registers before jump. Second argument R1 is copied to R11
// and kept after jump.
.global jump_to_with_flag
.type jump_to_with_flag, STT_FUNC
jump_to_with_flag:
mov r4, r0 // save input argument r0 (the address of the next stage's vector table) (r4 is callee save)
mov r11, r1 // save second argument in "flag" register because we'll be cleaning RAM
// 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
@ -39,7 +51,7 @@ jump_to:
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)
// clear out the general purpose registers before the next stage's except the register with flag R11
ldr r0, =0
mov r1, r0
mov r2, r0
@ -51,7 +63,6 @@ jump_to:
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

@ -72,6 +72,13 @@ def get_firmware_chunk(index: int, offset: int, buffer: bytearray) -> None:
"""
Reads a chunk of the firmware into `buffer`.
"""
# extmod/modtrezorutils/modtrezorutils.c
def reboot_to_bootloader() -> None:
"""
Reboots to bootloader.
"""
SCM_REVISION: bytes
VERSION_MAJOR: int
VERSION_MINOR: int

@ -371,6 +371,8 @@ apps.management.get_next_u2f_counter
import apps.management.get_next_u2f_counter
apps.management.get_nonce
import apps.management.get_nonce
apps.management.reboot_to_bootloader
import apps.management.reboot_to_bootloader
apps.management.recovery_device
import apps.management.recovery_device
apps.management.recovery_device.homescreen

@ -0,0 +1,17 @@
from typing import TYPE_CHECKING
from trezor import utils, wire
from trezor.messages import RebootToBootloader, Success
if TYPE_CHECKING:
from typing import NoReturn
pass
async def reboot_to_bootloader(ctx: wire.Context, msg: RebootToBootloader) -> NoReturn:
await ctx.write(Success())
# writing synchronously twice makes USB flush properly before reboot
await ctx.write(Success())
utils.reboot_to_bootloader()
raise RuntimeError

@ -49,6 +49,8 @@ def find_message_handler_module(msg_type: int) -> str:
return "apps.management.change_wipe_code"
elif msg_type == MessageType.GetNonce:
return "apps.management.get_nonce"
elif msg_type == MessageType.RebootToBootloader:
return "apps.management.reboot_to_bootloader"
if utils.MODEL in ("T",) and msg_type == MessageType.SdProtect:
return "apps.management.sd_protect"

@ -16,6 +16,7 @@ from trezorutils import ( # noqa: F401
get_firmware_chunk,
halt,
memcpy,
reboot_to_bootloader,
)
from typing import TYPE_CHECKING

@ -0,0 +1 @@
Jump and stay in bootloader from firmware through SVC call reverse trampoline.

@ -278,8 +278,4 @@ def reboot_to_bootloader(obj: "TrezorConnection") -> str:
# avoid using @with_client because it closes the session afterwards,
# which triggers double prompt on device
with obj.client_context() as client:
if client.features.model != "1":
click.echo(
f"Warning: Rebooting into bootloader not supported on Trezor {client.features.model}"
)
return device.reboot_to_bootloader(client)

Loading…
Cancel
Save