mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-07-03 13:22:33 +00:00
feat(core): jump back and stay in bootloader for TT via reverse SVC call trampoline
This commit is contained in:
parent
61a0d69baa
commit
c563c987e1
1
core/.changelog.d/2284.added
Normal file
1
core/.changelog.d/2284.added
Normal file
@ -0,0 +1 @@
|
|||||||
|
Jump and stay in bootloader from firmware through SVC call reverse trampoline.
|
1
core/embed/bootloader/.changelog.d/2284.added
Normal file
1
core/embed/bootloader/.changelog.d/2284.added
Normal file
@ -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
|
#endif
|
||||||
|
|
||||||
int main(void) {
|
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();
|
random_delays_init();
|
||||||
// display_init_seq();
|
// display_init_seq();
|
||||||
#if defined TREZOR_MODEL_T
|
#if defined TREZOR_MODEL_T
|
||||||
@ -281,6 +285,9 @@ int main(void) {
|
|||||||
vendor_header vhdr;
|
vendor_header vhdr;
|
||||||
image_header hdr;
|
image_header hdr;
|
||||||
secbool stay_in_bootloader = secfalse; // flag to stay in bootloader
|
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
|
// detect whether the devices contains a valid firmware
|
||||||
|
|
||||||
|
@ -19,6 +19,9 @@
|
|||||||
|
|
||||||
#include "py/objstr.h"
|
#include "py/objstr.h"
|
||||||
#include "py/runtime.h"
|
#include "py/runtime.h"
|
||||||
|
#ifndef TREZOR_EMULATOR
|
||||||
|
#include "supervise.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "version.h"
|
#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,
|
STATIC MP_DEFINE_CONST_FUN_OBJ_3(mod_trezorutils_get_firmware_chunk_obj,
|
||||||
mod_trezorutils_get_firmware_chunk);
|
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 = {
|
STATIC mp_obj_str_t mod_trezorutils_revision_obj = {
|
||||||
{&mp_type_bytes}, 0, sizeof(SCM_REVISION) - 1, (const byte *)SCM_REVISION};
|
{&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_PTR(&mod_trezorutils_firmware_sector_size_obj)},
|
||||||
{MP_ROM_QSTR(MP_QSTR_FIRMWARE_SECTORS_COUNT),
|
{MP_ROM_QSTR(MP_QSTR_FIRMWARE_SECTORS_COUNT),
|
||||||
MP_ROM_INT(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
|
// various built-in constants
|
||||||
{MP_ROM_QSTR(MP_QSTR_SCM_REVISION),
|
{MP_ROM_QSTR(MP_QSTR_SCM_REVISION),
|
||||||
MP_ROM_PTR(&mod_trezorutils_revision_obj)},
|
MP_ROM_PTR(&mod_trezorutils_revision_obj)},
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
#include "compiler_traits.h"
|
#include "compiler_traits.h"
|
||||||
#include "display.h"
|
#include "display.h"
|
||||||
#include "flash.h"
|
#include "flash.h"
|
||||||
|
#include "image.h"
|
||||||
#include "mpu.h"
|
#include "mpu.h"
|
||||||
#include "random_delays.h"
|
#include "random_delays.h"
|
||||||
#ifdef SYSTEM_VIEW
|
#ifdef SYSTEM_VIEW
|
||||||
@ -180,6 +181,13 @@ void UsageFault_Handler(void) {
|
|||||||
error_shutdown("Internal error", "(UF)", NULL, NULL);
|
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) {
|
void SVC_C_Handler(uint32_t *stack) {
|
||||||
uint8_t svc_number = ((uint8_t *)stack[6])[-2];
|
uint8_t svc_number = ((uint8_t *)stack[6])[-2];
|
||||||
switch (svc_number) {
|
switch (svc_number) {
|
||||||
@ -202,6 +210,16 @@ void SVC_C_Handler(uint32_t *stack) {
|
|||||||
for (;;)
|
for (;;)
|
||||||
;
|
;
|
||||||
break;
|
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:
|
default:
|
||||||
stack[0] = 0xffffffff;
|
stack[0] = 0xffffffff;
|
||||||
break;
|
break;
|
||||||
|
@ -49,6 +49,8 @@
|
|||||||
})
|
})
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define STAY_IN_BOOTLOADER_FLAG 0x0FC35A96
|
||||||
|
|
||||||
void shutdown(void);
|
void shutdown(void);
|
||||||
|
|
||||||
void __attribute__((noreturn))
|
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 memset_reg(volatile void *start, volatile void *stop, uint32_t val);
|
||||||
void jump_to(uint32_t address);
|
void jump_to(uint32_t address);
|
||||||
void jump_to_unprivileged(uint32_t address);
|
void jump_to_unprivileged(uint32_t address);
|
||||||
|
void jump_to_with_flag(uint32_t address, uint32_t register_flag);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -4,9 +4,11 @@
|
|||||||
#define SVC_DISABLE_IRQ 1
|
#define SVC_DISABLE_IRQ 1
|
||||||
#define SVC_SET_PRIORITY 2
|
#define SVC_SET_PRIORITY 2
|
||||||
#define SVC_SHUTDOWN 4
|
#define SVC_SHUTDOWN 4
|
||||||
|
#define SVC_REBOOT_TO_BOOTLOADER 5
|
||||||
|
|
||||||
// from util.s
|
// from util.s
|
||||||
extern void shutdown_privileged(void);
|
extern void shutdown_privileged(void);
|
||||||
|
extern void reboot_to_bootloader(void);
|
||||||
|
|
||||||
static inline uint32_t is_mode_unprivileged(void) {
|
static inline uint32_t is_mode_unprivileged(void) {
|
||||||
uint32_t r0;
|
uint32_t r0;
|
||||||
@ -50,3 +52,10 @@ static inline void svc_shutdown(void) {
|
|||||||
shutdown_privileged();
|
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
|
bne .L_loop_begin
|
||||||
bx lr
|
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
|
.global jump_to
|
||||||
.type jump_to, STT_FUNC
|
.type jump_to, STT_FUNC
|
||||||
jump_to:
|
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 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
|
// this subroutine re-points the exception handlers before the C code
|
||||||
// that comprises them has been given a good environment to run.
|
// that comprises them has been given a good environment to run.
|
||||||
// therefore, this code needs to disable interrupts before the VTOR
|
// 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
|
ldr r2, =0 // r2 - the word-sized value to be written
|
||||||
bl memset_reg
|
bl memset_reg
|
||||||
mov lr, r4
|
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
|
ldr r0, =0
|
||||||
mov r1, r0
|
mov r1, r0
|
||||||
mov r2, r0
|
mov r2, r0
|
||||||
@ -51,7 +63,6 @@ jump_to:
|
|||||||
mov r8, r0
|
mov r8, r0
|
||||||
mov r9, r0
|
mov r9, r0
|
||||||
mov r10, r0
|
mov r10, r0
|
||||||
mov r11, r0
|
|
||||||
mov r12, r0
|
mov r12, r0
|
||||||
// give the next stage a fresh main stack pointer
|
// 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
|
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`.
|
Reads a chunk of the firmware into `buffer`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# extmod/modtrezorutils/modtrezorutils.c
|
||||||
|
def reboot_to_bootloader() -> None:
|
||||||
|
"""
|
||||||
|
Reboots to bootloader.
|
||||||
|
"""
|
||||||
SCM_REVISION: bytes
|
SCM_REVISION: bytes
|
||||||
VERSION_MAJOR: int
|
VERSION_MAJOR: int
|
||||||
VERSION_MINOR: int
|
VERSION_MINOR: int
|
||||||
|
@ -371,6 +371,8 @@ apps.management.get_next_u2f_counter
|
|||||||
import apps.management.get_next_u2f_counter
|
import apps.management.get_next_u2f_counter
|
||||||
apps.management.get_nonce
|
apps.management.get_nonce
|
||||||
import apps.management.get_nonce
|
import apps.management.get_nonce
|
||||||
|
apps.management.reboot_to_bootloader
|
||||||
|
import apps.management.reboot_to_bootloader
|
||||||
apps.management.recovery_device
|
apps.management.recovery_device
|
||||||
import apps.management.recovery_device
|
import apps.management.recovery_device
|
||||||
apps.management.recovery_device.homescreen
|
apps.management.recovery_device.homescreen
|
||||||
|
17
core/src/apps/management/reboot_to_bootloader.py
Normal file
17
core/src/apps/management/reboot_to_bootloader.py
Normal file
@ -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"
|
return "apps.management.change_wipe_code"
|
||||||
elif msg_type == MessageType.GetNonce:
|
elif msg_type == MessageType.GetNonce:
|
||||||
return "apps.management.get_nonce"
|
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:
|
if utils.MODEL in ("T",) and msg_type == MessageType.SdProtect:
|
||||||
return "apps.management.sd_protect"
|
return "apps.management.sd_protect"
|
||||||
|
@ -16,6 +16,7 @@ from trezorutils import ( # noqa: F401
|
|||||||
get_firmware_chunk,
|
get_firmware_chunk,
|
||||||
halt,
|
halt,
|
||||||
memcpy,
|
memcpy,
|
||||||
|
reboot_to_bootloader,
|
||||||
)
|
)
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
1
python/.changelog.d/2284.added
Normal file
1
python/.changelog.d/2284.added
Normal file
@ -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,
|
# avoid using @with_client because it closes the session afterwards,
|
||||||
# which triggers double prompt on device
|
# which triggers double prompt on device
|
||||||
with obj.client_context() as client:
|
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)
|
return device.reboot_to_bootloader(client)
|
||||||
|
Loading…
Reference in New Issue
Block a user