mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-04-15 06:45:59 +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
core
.changelog.d
embed
bootloader
extmod/modtrezorutils
firmware
trezorhal
mocks/generated
src
python
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
|
||||
|
||||
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
|
||||
|
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"
|
||||
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
|
||||
|
||||
|
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,
|
||||
# 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…
Reference in New Issue
Block a user