diff --git a/core/SConscript.bootloader_emu b/core/SConscript.bootloader_emu index c7483132a..9adc1a846 100644 --- a/core/SConscript.bootloader_emu +++ b/core/SConscript.bootloader_emu @@ -154,6 +154,8 @@ SOURCE_TREZORHAL = [ 'embed/trezorhal/unix/random_delays.c', 'embed/trezorhal/unix/rng.c', 'embed/trezorhal/unix/secret.c', + 'embed/trezorhal/unix/systick.c', + 'embed/trezorhal/unix/systimer.c', 'embed/trezorhal/unix/usb.c', ] diff --git a/core/SConscript.unix b/core/SConscript.unix index 6a65b17db..132d88dfb 100644 --- a/core/SConscript.unix +++ b/core/SConscript.unix @@ -424,6 +424,8 @@ SOURCE_UNIX = [ 'embed/trezorhal/unix/flash.c', 'embed/trezorhal/unix/random_delays.c', 'embed/trezorhal/unix/rng.c', + 'embed/trezorhal/unix/systick.c', + 'embed/trezorhal/unix/systimer.c', 'embed/trezorhal/unix/time_estimate.c', 'embed/trezorhal/unix/usb.c', 'embed/unix/main_main.c', diff --git a/core/embed/boardloader/main.c b/core/embed/boardloader/main.c index b9f5cbb8d..7aa83445c 100644 --- a/core/embed/boardloader/main.c +++ b/core/embed/boardloader/main.c @@ -32,6 +32,7 @@ #include "model.h" #include "mpu.h" #include "rng.h" +#include "systimer.h" #include "terminal.h" #ifdef USE_SD_CARD @@ -232,6 +233,9 @@ static secbool copy_sdcard(void) { #endif int main(void) { + systick_init(); + systimer_init(); + reset_flags_reset(); // need the systick timer running before many HAL operations. diff --git a/core/embed/bootloader/main.c b/core/embed/bootloader/main.c index 9fae747c4..3095c598f 100644 --- a/core/embed/bootloader/main.c +++ b/core/embed/bootloader/main.c @@ -33,6 +33,7 @@ #include "random_delays.h" #include "secbool.h" #include "secret.h" +#include "systimer.h" #ifdef USE_DMA2D #ifdef NEW_RENDERING @@ -359,7 +360,10 @@ int bootloader_main(void) { #endif secbool stay_in_bootloader = secfalse; - random_delays_init(); + systick_init(); + systimer_init(); + + rdi_init(); #if defined TREZOR_MODEL_T set_core_clock(CLOCK_180_MHZ); diff --git a/core/embed/bootloader_ci/main.c b/core/embed/bootloader_ci/main.c index e58a1a95b..e58906273 100644 --- a/core/embed/bootloader_ci/main.c +++ b/core/embed/bootloader_ci/main.c @@ -203,7 +203,10 @@ static void check_bootloader_version(void) { #endif int main(void) { - random_delays_init(); + systick_init(); + systimer_init(); + + rdi_init(); #ifdef USE_TOUCH touch_init(); #endif diff --git a/core/embed/firmware/delay.c b/core/embed/firmware/delay.c index a3d6da32a..037b0665b 100644 --- a/core/embed/firmware/delay.c +++ b/core/embed/firmware/delay.c @@ -48,27 +48,10 @@ #include "py/runtime.h" #include "py/mphal.h" #include "irq.h" +#include "systick.h" extern __IO uint32_t uwTick; -// We provide our own version of HAL_Delay that calls __WFI while waiting, -// and works when interrupts are disabled. This function is intended to be -// used only by the ST HAL functions. -void HAL_Delay(uint32_t Delay) { - if (IS_IRQ_ENABLED(query_irq())) { - // IRQs enabled, so can use systick counter to do the delay - uint32_t start = uwTick; - // Wraparound of tick is taken care of by 2's complement arithmetic. - while (uwTick - start < Delay) { - // Enter sleep mode, waiting for (at least) the SysTick interrupt. - __WFI(); - } - } else { - // IRQs disabled, use mp_hal_delay_ms routine. - mp_hal_delay_ms(Delay); - } -} - // Core delay function that does an efficient sleep and may switch thread context. // If IRQs are enabled then we must have the GIL. void mp_hal_delay_ms(mp_uint_t Delay) { @@ -110,9 +93,9 @@ void mp_hal_delay_us(mp_uint_t usec) { } mp_uint_t mp_hal_ticks_ms(void) { - return uwTick; + return systick_ms(); } mp_uint_t mp_hal_ticks_us(void) { - return uwTick * 1000; + return systick_ms() * 1000; } diff --git a/core/embed/firmware/main.c b/core/embed/firmware/main.c index a1f8ffed1..c6296ff6f 100644 --- a/core/embed/firmware/main.c +++ b/core/embed/firmware/main.c @@ -51,6 +51,7 @@ #include "random_delays.h" #include "rust_ui.h" #include "secure_aes.h" +#include "systimer.h" #include TREZOR_BOARD @@ -132,8 +133,10 @@ static void optiga_log_hex(const char *prefix, const uint8_t *data, int main(void) { svc_init(); + systick_init(); + systimer_init(); - random_delays_init(); + rdi_init(); #ifdef RDI rdi_start(); diff --git a/core/embed/prodtest/main.c b/core/embed/prodtest/main.c index 973e3e76c..5f9e0e078 100644 --- a/core/embed/prodtest/main.c +++ b/core/embed/prodtest/main.c @@ -42,6 +42,7 @@ #include "sdcard.h" #include "secbool.h" #include "supervise.h" +#include "systimer.h" #include "touch.h" #include "usb.h" #include "version.h" @@ -777,8 +778,11 @@ void cpuid_read(void) { int main(void) { svc_init(); + systick_init(); + systimer_init(); + rdi_init(); display_init(DISPLAY_RETAIN_CONTENT); - random_delays_init(); + #ifdef STM32U5 secure_aes_init(); #endif diff --git a/core/embed/reflash/main.c b/core/embed/reflash/main.c index b98a3bceb..114870872 100644 --- a/core/embed/reflash/main.c +++ b/core/embed/reflash/main.c @@ -33,6 +33,7 @@ #include "sbu.h" #include "sdcard.h" #include "secbool.h" +#include "systimer.h" #include "terminal.h" #include "touch.h" @@ -68,6 +69,9 @@ static void flash_from_sdcard(const flash_area_t* area, uint32_t source, } int main(void) { + systick_init(); + systimer_init(); + sdcard_init(); touch_init(); diff --git a/core/embed/rust/build.rs b/core/embed/rust/build.rs index e1cdaaefb..4f438d77f 100644 --- a/core/embed/rust/build.rs +++ b/core/embed/rust/build.rs @@ -399,9 +399,9 @@ fn generate_trezorhal_bindings() { .allowlist_function("random_uniform") // rgb led .allowlist_function("rgb_led_set_color") - // time - .allowlist_function("hal_delay") - .allowlist_function("hal_ticks_ms") + // systick + .allowlist_function("systick_delay_ms") + .allowlist_function("systick_ms") // toif .allowlist_type("toif_format_t") // dma2d diff --git a/core/embed/rust/src/trezorhal/time.rs b/core/embed/rust/src/trezorhal/time.rs index 30c9454cb..8c4e1e55c 100644 --- a/core/embed/rust/src/trezorhal/time.rs +++ b/core/embed/rust/src/trezorhal/time.rs @@ -3,11 +3,11 @@ use crate::time::Duration; use super::ffi; pub fn ticks_ms() -> u32 { - unsafe { ffi::hal_ticks_ms() as _ } + unsafe { ffi::systick_ms() as _ } } pub fn sleep(delay: Duration) { unsafe { - ffi::hal_delay(delay.to_millis() as _); + ffi::systick_delay_ms(delay.to_millis() as _); } } diff --git a/core/embed/trezorhal/common.h b/core/embed/trezorhal/common.h index 07db158bf..6675a6f0c 100644 --- a/core/embed/trezorhal/common.h +++ b/core/embed/trezorhal/common.h @@ -26,6 +26,7 @@ #include "error_handling.h" #include "platform.h" +#include "systick.h" #ifndef MIN_8bits #define MIN_8bits(a, b) \ @@ -54,10 +55,6 @@ void __attribute__((noreturn)) trezor_shutdown(void); -void hal_delay(uint32_t ms); -uint32_t hal_ticks_ms(); -void hal_delay_us(uint16_t delay_us); - // Invalidates firmware on the device // Note: only works when write access to firmware area is enabled by MPU void invalidate_firmware(void); diff --git a/core/embed/trezorhal/random_delays.h b/core/embed/trezorhal/random_delays.h index ae8e0073f..19e136c4e 100644 --- a/core/embed/trezorhal/random_delays.h +++ b/core/embed/trezorhal/random_delays.h @@ -22,12 +22,11 @@ #include -void random_delays_init(void); +void rdi_init(void); void rdi_start(void); void rdi_stop(void); void rdi_refresh_session_delay(void); -void rdi_handler(uint32_t uw_tick); void wait_random(void); #endif diff --git a/core/embed/trezorhal/stm32f4/common.c b/core/embed/trezorhal/stm32f4/common.c index 6eb76497f..6da54de95 100644 --- a/core/embed/trezorhal/stm32f4/common.c +++ b/core/embed/trezorhal/stm32f4/common.c @@ -36,8 +36,6 @@ #include "backlight_pwm.h" #endif -uint32_t systick_val_copy = 0; - // from util.s extern void shutdown_privileged(void); @@ -54,20 +52,6 @@ void __attribute__((noreturn)) trezor_shutdown(void) { ; } -void hal_delay(uint32_t ms) { HAL_Delay(ms); } -uint32_t hal_ticks_ms() { return HAL_GetTick(); } -void hal_delay_us(uint16_t delay_us) { - uint32_t val = svc_get_systick_val(); - uint32_t t = hal_ticks_ms() * 1000 + - (((SystemCoreClock / 1000) - val) / (SystemCoreClock / 1000000)); - uint32_t t2 = t; - do { - val = svc_get_systick_val(); - t2 = hal_ticks_ms() * 1000 + - (((SystemCoreClock / 1000) - val) / (SystemCoreClock / 1000000)); - } while ((t2 - t) < delay_us); -} - // reference RM0090 section 35.12.1 Figure 413 #define USB_OTG_HS_DATA_FIFO_RAM (USB_OTG_HS_PERIPH_BASE + 0x20000U) #define USB_OTG_HS_DATA_FIFO_SIZE (4096U) diff --git a/core/embed/trezorhal/stm32f4/platform.c b/core/embed/trezorhal/stm32f4/platform.c index e7323d403..595b9b137 100644 --- a/core/embed/trezorhal/stm32f4/platform.c +++ b/core/embed/trezorhal/stm32f4/platform.c @@ -21,6 +21,7 @@ #include "platform.h" #include "rng.h" +#include "systick.h" #include TREZOR_BOARD const uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, @@ -193,7 +194,7 @@ void set_core_clock(clock_settings_t settings) { /* Enable PLL as main clock */ RCC->CFGR = (RCC->CFGR & ~(RCC_CFGR_SW)) | RCC_CFGR_SW_PLL; - HAL_InitTick(TICK_INT_PRIORITY); + systick_update_freq(); // turn off the HSI as it is now unused (it will be turned on again // automatically if a clock security failure occurs) diff --git a/core/embed/trezorhal/stm32f4/random_delays.c b/core/embed/trezorhal/stm32f4/random_delays.c index 7a27cc4eb..afc8079fc 100644 --- a/core/embed/trezorhal/stm32f4/random_delays.c +++ b/core/embed/trezorhal/stm32f4/random_delays.c @@ -44,6 +44,7 @@ https://link.springer.com/content/pdf/10.1007%2F978-3-540-72354-7_3.pdf #include "common.h" #include "memzero.h" #include "rand.h" +#include "systimer.h" // from util.s extern void shutdown_privileged(void); @@ -151,7 +152,16 @@ static void wait(uint32_t delay) { : "r0", "r1"); } -void random_delays_init() { drbg_init(); } +// forward declaration +static void rdi_handler(void *context); + +void rdi_init() { + drbg_init(); + + systimer_t *timer = systimer_create(rdi_handler, NULL); + ensure(sectrue * (timer != NULL), "rdi_init failed"); + systimer_set_periodic(timer, 1); +} void rdi_start(void) { ensure(drbg_initialized, NULL); @@ -174,7 +184,7 @@ void rdi_refresh_session_delay(void) { refresh_session_delay = true; } -void rdi_handler(uint32_t uw_tick) { +static void rdi_handler(void *context) { if (rdi_disabled == secfalse) { // if rdi enabled if (refresh_session_delay) { session_delay = drbg_random8(); diff --git a/core/embed/trezorhal/stm32f4/supervise.c b/core/embed/trezorhal/stm32f4/supervise.c index bcb6e6767..d1cfd372e 100644 --- a/core/embed/trezorhal/stm32f4/supervise.c +++ b/core/embed/trezorhal/stm32f4/supervise.c @@ -102,9 +102,6 @@ void SVC_C_Handler(uint32_t *stack) { // raising privileges. stack[6] = (uintptr_t)_reboot_to_bootloader; return; - case SVC_GET_SYSTICK_VAL: - systick_val_copy = SysTick->VAL; - break; case SVC_REBOOT: NVIC_SystemReset(); break; diff --git a/core/embed/trezorhal/stm32f4/supervise.h b/core/embed/trezorhal/stm32f4/supervise.h index 3d478e882..e69341737 100644 --- a/core/embed/trezorhal/stm32f4/supervise.h +++ b/core/embed/trezorhal/stm32f4/supervise.h @@ -5,17 +5,13 @@ #define SVC_SET_PRIORITY 2 #define SVC_SHUTDOWN 4 #define SVC_REBOOT_TO_BOOTLOADER 5 -#define SVC_GET_SYSTICK_VAL 6 -#define SVC_REBOOT 7 +#define SVC_REBOOT 6 #include #include "boot_args.h" #include "common.h" #include "image.h" -// from common.c -extern uint32_t systick_val_copy; - // from util.s extern void shutdown_privileged(void); @@ -76,13 +72,3 @@ static inline void svc_shutdown(void) { void svc_reboot_to_bootloader(void); void svc_reboot(void); - -static inline uint32_t svc_get_systick_val(void) { - if (is_mode_unprivileged() && !is_mode_handler()) { - __asm__ __volatile__("svc %0" ::"i"(SVC_GET_SYSTICK_VAL) : "memory"); - return systick_val_copy; - } else { - systick_val_copy = SysTick->VAL; - return systick_val_copy; - } -} diff --git a/core/embed/trezorhal/stm32f4/systick.c b/core/embed/trezorhal/stm32f4/systick.c index 5afa8a5bf..6de800b09 100644 --- a/core/embed/trezorhal/stm32f4/systick.c +++ b/core/embed/trezorhal/stm32f4/systick.c @@ -1,5 +1,3 @@ -// clang-format off - /* * This file is part of the Trezor project, https://trezor.io/ * @@ -19,52 +17,186 @@ * along with this program. If not, see . */ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2013, 2014 Damien P. George - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ +#include +#include #include "irq.h" +#include "platform.h" +#include "systemview.h" + #include "systick.h" +#include "systick_internal.h" +#include "systimer.h" -#ifdef RDI - #include "random_delays.h" -#endif +// SysTick driver state +typedef struct { + // Set if the driver is initialized + bool initialized; + // Number of hw cycles per millisecond (tick period) + uint32_t cycles_per_ms; + // Number of hw cycles per microsecond + uint32_t cycles_per_us; + // Current tick value in hardware cycles + volatile uint64_t cycles; + // Number of ticks (ms) since the system start + volatile uint32_t ticks; +} systick_driver_t; -#include "systemview.h" +static systick_driver_t g_systick_driver = { + .initialized = false, +}; + +void systick_init(void) { + systick_driver_t* drv = &g_systick_driver; + + if (drv->initialized) { + return; + } + + drv->cycles = 0; + drv->ticks = 0; -extern __IO uint32_t uwTick; + // Set 1ms tick period + drv->cycles_per_ms = HAL_RCC_GetSysClockFreq() / 1000; + drv->cycles_per_us = drv->cycles_per_ms / 1000; + + // Initialize and enable SysTick timer + SysTick_Config(drv->cycles_per_ms); + + // We need to ensure that SysTick has the expected priority. + // The SysTick priority is configured in the boardloader, + // and some early versions didn't set this properly. + NVIC_SetPriority(SysTick_IRQn, IRQ_PRI_NORMAL); + + drv->initialized = true; +} + +void systick_deinit(void) { + systick_driver_t* drv = &g_systick_driver; + + if (!drv->initialized) { + return; + } + + NVIC_DisableIRQ(SysTick_IRQn); + SysTick->CTRL = 0; + NVIC_ClearPendingIRQ(SysTick_IRQn); + + drv->initialized = false; +} + +void systick_update_freq(void) { + systick_driver_t* drv = &g_systick_driver; + + if (!drv->initialized) { + return; + } + + uint32_t clock_freq = HAL_RCC_GetSysClockFreq(); + drv->cycles_per_ms = clock_freq / 1000; + drv->cycles_per_us = drv->cycles_per_ms / 1000; + SysTick_Config(drv->cycles_per_ms); + + // We need to ensure that SysTick has the expected priority. + // The SysTick priority is configured in the boardloader, + // and some early versions didn't set this properly. + NVIC_SetPriority(SysTick_IRQn, IRQ_PRI_NORMAL); +} + +uint64_t systick_cycles(void) { + systick_driver_t* drv = &g_systick_driver; + + uint32_t irq_state = disable_irq(); + + // Current value of the SysTick counter + uint32_t val = SysTick->VAL; + + // Check if the SysTick has already counted down to 0 or wrapped around + // Reading CTRL register automatically clears COUNTFLAG + if ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) != 0) { + // Re-read the current value of the SysTick counter + val = SysTick->VAL; + // Update the hardware cycles counter + // Since we have cleared COUNTFLAG, SysTick_Handler will not increment + drv->cycles += drv->cycles_per_ms; + // Increment regular ticks counter + drv->ticks++; + } + + uint64_t cycles = drv->cycles + ((val > 0) ? (drv->cycles_per_ms - val) : 0); + + enable_irq(irq_state); + + return cycles; +} + +uint64_t systick_us_to_cycles(uint64_t us) { + systick_driver_t* drv = &g_systick_driver; + + return us * drv->cycles_per_us; +} + +uint32_t systick_ms(void) { + systick_driver_t* drv = &g_systick_driver; + + return drv->ticks; +} + +uint64_t systick_us(void) { + systick_driver_t* drv = &g_systick_driver; + + return systick_cycles() / drv->cycles_per_us; +} void SysTick_Handler(void) { SEGGER_SYSVIEW_RecordEnterISR(); - // this is a millisecond tick counter that wraps after approximately - // 49.71 days = (0xffffffff / (24 * 60 * 60 * 1000)) - uint32_t uw_tick = uwTick + 1; - uwTick = uw_tick; -#ifdef RDI - rdi_handler(uw_tick); -#endif + + systick_driver_t* drv = &g_systick_driver; + + if (drv->initialized) { + // Increment `cycles` counter if COUNTFLAG is set. + // If COUNTFLAG is not set, `cycles` were already incremented + // in `systick_cycles()` that also cleared the COUNTFLAG. + if ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) != 0) { + // Clear COUNTFLAG by reading VAL + (void)SysTick->VAL; + // Increment cycles counter by SysTick period + drv->cycles += drv->cycles_per_ms; + // Increment regular ticks counter + drv->ticks++; + } + + // Invoke callbacks of expired timers + systimer_dispatch_expired_timers(drv->cycles); + } SEGGER_SYSVIEW_RecordExitISR(); } + +void systick_delay_us(uint64_t us) { + uint64_t delay_cycles = systick_us_to_cycles(us); + int64_t cycles_per_ms = systick_us_to_cycles(1000); + + uint64_t end = systick_cycles() + delay_cycles; + bool irq_enabled = IS_IRQ_ENABLED(query_irq()); + int64_t diff; + + while ((diff = end - systick_cycles()) > 0) { + if (irq_enabled && (diff > cycles_per_ms)) { + // Enter sleep mode and wait for (at least) + // the SysTick interrupt. + __WFI(); + } + } +} + +void systick_delay_ms(uint32_t ms) { systick_delay_us((uint64_t)ms * 1000); } + +// We provide our own version of HAL_Delay that calls __WFI while waiting, +// and works when interrupts are disabled. This function is intended to be +// used only by the ST HAL functions. +void HAL_Delay(uint32_t ms) { systick_delay_ms(ms); } + +// We provide our own version of HAL_GetTick that replaces the default +// ST HAL function reading uwTick global variable. +uint32_t HAL_GetTick(void) { return systick_ms(); } diff --git a/core/embed/trezorhal/stm32f4/systick.h b/core/embed/trezorhal/stm32f4/systick.h deleted file mode 100644 index 7c94a91a3..000000000 --- a/core/embed/trezorhal/stm32f4/systick.h +++ /dev/null @@ -1,37 +0,0 @@ -// clang-format off - -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2013, 2014 Damien P. George - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef MICROPY_INCLUDED_STM32_SYSTICK_H -#define MICROPY_INCLUDED_STM32_SYSTICK_H - -#include -#include - -// Works for x between 0 and 16 inclusive -#define POW2_CEIL(x) ((((x) - 1) | ((x) - 1) >> 1 | ((x) - 1) >> 2 | ((x) - 1) >> 3) + 1) - -#endif // MICROPY_INCLUDED_STM32_SYSTICK_H diff --git a/core/embed/trezorhal/stm32f4/systick_internal.h b/core/embed/trezorhal/stm32f4/systick_internal.h new file mode 100644 index 000000000..0f7a9a071 --- /dev/null +++ b/core/embed/trezorhal/stm32f4/systick_internal.h @@ -0,0 +1,12 @@ +#ifndef TREZORHAL_SYSTICK_INTERNAL_H +#define TREZORHAL_SYSTICK_INTERNAL_H + +#include + +#include "systick.h" + +// Internal function called from interrupt context. +// Handles expired timers and invoked their callbacks. +void systimer_dispatch_expired_timers(uint64_t cycles); + +#endif // TREZORHAL_SYSTICK_INTERNAL_H diff --git a/core/embed/trezorhal/stm32f4/systimer.c b/core/embed/trezorhal/stm32f4/systimer.c new file mode 100644 index 000000000..57fa8b688 --- /dev/null +++ b/core/embed/trezorhal/stm32f4/systimer.c @@ -0,0 +1,231 @@ +/* + * 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 . + */ + +#include +#include + +#include "irq.h" +#include "platform.h" +#include "systick_internal.h" +#include "systimer.h" + +// Maximum number of registerd user timer +// +// Consider different implementation (i.e. priority queue +// using binary heap if MAX_SYSTIMERS exceeds 10 or more) +#define MAX_SYSTIMERS 4 + +// User timer instance +struct systimer { + // User callback function + // Non-NULL if the timer entry is valid + volatile systimer_callback_t callback; + // User callback context + void* context; + // Set if the timer is suspended + volatile bool suspended; + // Set if the timer is scheduled + volatile bool scheduled; + // Expiration time (valid if scheduled is set) + volatile uint64_t expiration; + // Period (= 0 for non-periodic timers) + volatile uint64_t period; +}; + +// systimer driver state +typedef struct { + // Set if the driver is initialized + bool initialized; + // Registered timers + // (unused slots have callback field set to NULL) + systimer_t timers[MAX_SYSTIMERS]; +} systimer_driver_t; + +static systimer_driver_t g_systimer_driver = { + .initialized = false, +}; + +void systimer_init(void) { + systimer_driver_t* drv = &g_systimer_driver; + + if (drv->initialized) { + return; + } + + memset(drv, 0, sizeof(systimer_driver_t)); + drv->initialized = true; +} + +void systimer_deinit(void) { + systimer_driver_t* drv = &g_systimer_driver; + + drv->initialized = false; +} + +static inline bool timer_valid(systimer_driver_t* drv, systimer_t* timer) { + return drv->initialized && (timer >= &drv->timers[0]) && + (timer < &drv->timers[MAX_SYSTIMERS]); +} + +systimer_t* systimer_create(systimer_callback_t callback, void* context) { + systimer_driver_t* drv = &g_systimer_driver; + + if (!drv->initialized) { + return NULL; + } + + if (callback == NULL) { + // Since the callback is used to determine if the + // timer is valid, it must be non-NULL. + return NULL; + } + + uint32_t irq_state = disable_irq(); + + // Find a free timer entry + for (int i = 0; i < MAX_SYSTIMERS; i++) { + systimer_t* timer = &drv->timers[i]; + + if (timer->callback == NULL) { + timer->scheduled = false; + timer->suspended = false; + timer->context = context; + timer->callback = callback; + + enable_irq(irq_state); + return timer; + } + } + + // No free timer entry found + enable_irq(irq_state); + return NULL; +} + +void systimer_delete(systimer_t* timer) { + systimer_driver_t* drv = &g_systimer_driver; + if (!timer_valid(drv, timer)) { + return; + } + + timer->callback = NULL; +} + +void systimer_set(systimer_t* timer, uint32_t delay_ms) { + systimer_driver_t* drv = &g_systimer_driver; + + if (!timer_valid(drv, timer)) { + return; + } + + uint64_t delay = systick_us_to_cycles((uint64_t)delay_ms * 1000); + uint64_t expiration = systick_cycles() + delay; + + uint32_t irq_state = disable_irq(); + timer->expiration = expiration; + timer->period = 0; + timer->scheduled = true; + enable_irq(irq_state); +} + +void systimer_set_periodic(systimer_t* timer, uint32_t period_ms) { + systimer_driver_t* drv = &g_systimer_driver; + + if (!timer_valid(drv, timer)) { + return; + } + + uint64_t period = systick_us_to_cycles((uint64_t)period_ms * 1000); + uint64_t expiration = systick_cycles() + period; + + uint32_t irq_state = disable_irq(); + timer->expiration = expiration; + timer->period = period; + timer->scheduled = true; + enable_irq(irq_state); +} + +bool systimer_unset(systimer_t* timer) { + systimer_driver_t* drv = &g_systimer_driver; + + if (!timer_valid(drv, timer)) { + return false; + } + + uint32_t irq_state = disable_irq(); + bool was_scheduled = timer->scheduled; + timer->scheduled = false; + enable_irq(irq_state); + return was_scheduled; +} + +systimer_key_t systimer_suspend(systimer_t* timer) { + systimer_driver_t* drv = &g_systimer_driver; + + if (!timer_valid(drv, timer)) { + return false; + } + + uint32_t irq_state = disable_irq(); + bool was_suspended = timer->suspended; + timer->suspended = true; + enable_irq(irq_state); + return was_suspended; +} + +void systimer_resume(systimer_t* timer, systimer_key_t key) { + systimer_driver_t* drv = &g_systimer_driver; + + if (!timer_valid(drv, timer)) { + return; + } + + timer->suspended = key; +} + +// Called from interrupt context +void systimer_dispatch_expired_timers(uint64_t cycles) { + systimer_driver_t* drv = &g_systimer_driver; + + if (!drv->initialized) { + return; + } + + // Go through all timer slots and invoke callbacks of expired timers + // This algorithm is not efficient for large number of timers + // but it is good enough if MAX_SYSTIMERS ~ 10 + + for (int i = 0; i < MAX_SYSTIMERS; i++) { + systimer_t* timer = &drv->timers[i]; + + if (timer->callback != NULL && !timer->suspended && timer->scheduled) { + if (cycles >= timer->expiration) { + if (timer->period > 0) { + // Reschedule periodic timer + timer->expiration = cycles + timer->period; + } else { + // Stop one-shot timer + timer->scheduled = false; + } + // Callback is invoked from interrupt context + timer->callback(timer->context); + } + } + } +} diff --git a/core/embed/trezorhal/stm32u5/common.c b/core/embed/trezorhal/stm32u5/common.c index 711c6066e..d21a8116a 100644 --- a/core/embed/trezorhal/stm32u5/common.c +++ b/core/embed/trezorhal/stm32u5/common.c @@ -32,8 +32,6 @@ #include "stm32u5xx_ll_utils.h" -uint32_t systick_val_copy = 0; - // from util.s extern void shutdown_privileged(void); @@ -55,20 +53,6 @@ void __attribute__((noreturn)) trezor_shutdown(void) { ; } -void hal_delay(uint32_t ms) { HAL_Delay(ms); } -uint32_t hal_ticks_ms() { return HAL_GetTick(); } -void hal_delay_us(uint16_t delay_us) { - uint32_t val = svc_get_systick_val(); - uint32_t t = hal_ticks_ms() * 1000 + - (((SystemCoreClock / 1000) - val) / (SystemCoreClock / 1000000)); - uint32_t t2 = t; - do { - val = svc_get_systick_val(); - t2 = hal_ticks_ms() * 1000 + - (((SystemCoreClock / 1000) - val) / (SystemCoreClock / 1000000)); - } while ((t2 - t) < delay_us); -} - uint32_t __stack_chk_guard = 0; void __attribute__((noreturn)) __stack_chk_fail(void) { diff --git a/core/embed/trezorhal/stm32u5/systick.h b/core/embed/trezorhal/stm32u5/systick.h deleted file mode 120000 index 1d1261d2c..000000000 --- a/core/embed/trezorhal/stm32u5/systick.h +++ /dev/null @@ -1 +0,0 @@ -../stm32f4/systick.h \ No newline at end of file diff --git a/core/embed/trezorhal/stm32u5/systick_internal.h b/core/embed/trezorhal/stm32u5/systick_internal.h new file mode 120000 index 000000000..206073286 --- /dev/null +++ b/core/embed/trezorhal/stm32u5/systick_internal.h @@ -0,0 +1 @@ +../stm32f4/systick_internal.h \ No newline at end of file diff --git a/core/embed/trezorhal/stm32u5/systimer.c b/core/embed/trezorhal/stm32u5/systimer.c new file mode 120000 index 000000000..035d4ff20 --- /dev/null +++ b/core/embed/trezorhal/stm32u5/systimer.c @@ -0,0 +1 @@ +../stm32f4/systimer.c \ No newline at end of file diff --git a/core/embed/trezorhal/systick.h b/core/embed/trezorhal/systick.h new file mode 100644 index 000000000..af2cb6779 --- /dev/null +++ b/core/embed/trezorhal/systick.h @@ -0,0 +1,96 @@ +/* + * 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 . + */ + +#ifndef TREZORHAL_SYSTICK_H +#define TREZORHAL_SYSTICK_H + +#include +#include + +// Initializes systick subsystem +// +// Before calling this function, none of the other functions +// from this module should be called. +void systick_init(void); + +// Deinitialize systick subsystem +// +// The function should be called before jumping to the +// next bootloader stage or firmware. +void systick_deinit(void); + +// Updates systick subsystem with new system clock frequency +// +// The function should be called after the system clock frequency +// has been changed. +void systick_update_freq(void); + +// ---------------------------------------------------------------------------- +// Tick functions + +// Returns number of system clock cycles since the system start. +// +// Read monotonic counter with high resolution (Cortex-M SysTick clock) +// (On 160MHz CPU, 1 cycles is 1 / 160MHz = 6.25ns) +uint64_t systick_cycles(void); + +// Returns number of microseconds since the system start. +uint64_t systick_us(void); + +// Returns number of ticks (milliseconds) since the system start. +// +// The returned value is a 32-bit unsigned integer that wraps +// around every 49.7 days. +uint32_t systick_ms(void); + +// Converts microseconds to system clock cycles +uint64_t systick_us_to_cycles(uint64_t us); + +// Number of ticks (milliseconds) +typedef uint32_t ticks_t; + +// +#define ticks() systick_ms() + +// Helper function for building expiration time +#define ticks_timeout(timeout) (systick_ms() + (timeout)) + +// Helper function for checking ticks expiration +// +// It copes with the wrap-around of the `ticks_t` type but +// still assumes that the difference between the two ticks +// is less than half of the `ticks_t` range. +#define ticks_expired(ticks) ((int32_t)(systick_ms() - (ticks)) >= 0) + +// ---------------------------------------------------------------------------- +// Delay functions + +// Waits for at least `ms` milliseconds +void systick_delay_ms(uint32_t ms); + +// Waits for at least `us` microseconds +void systick_delay_us(uint64_t us); + +// legacy functions + +static inline uint32_t hal_ticks_ms(void) { return systick_ms(); } +static inline void hal_delay(uint32_t ms) { systick_delay_ms(ms); } +static inline void hal_delay_us(uint64_t us) { systick_delay_us(us); } + +#endif // TREZORHAL_SYSTICK_H diff --git a/core/embed/trezorhal/systimer.h b/core/embed/trezorhal/systimer.h new file mode 100644 index 000000000..f8324054a --- /dev/null +++ b/core/embed/trezorhal/systimer.h @@ -0,0 +1,93 @@ +/* + * 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 . + */ + +#ifndef TREZORHAL_SYSTIMER_H +#define TREZORHAL_SYSTIMER_H + +#include +#include + +// Initializes systimer subsystem +// +// Before calling this function, none of the other functions +// from this module should be called. +void systimer_init(void); + +// Deinitialize sytimer subsystem +void systimer_deinit(void); + +// Timer handle +typedef struct systimer systimer_t; + +// Timer callback routine invoked when timer expires +// +// The callback should be as short as possible and should not +// block. It is invoked from the timer interrupt context. +// +// `context` is the pointer passed to `timer_create` +typedef void (*systimer_callback_t)(void* context); + +// Initializes the timer and returns its handle. +// +// There a limited number of timers and `NULL` is returned +// if no timer is available. +systimer_t* systimer_create(systimer_callback_t callback, void* context); + +// Deletes the timer +// +// Timer is unset and its resources are released. +void systimer_delete(systimer_t* timer); + +// Sets the timer to expire in `delay_ms` milliseconds +// +// If the timer is already set, it will be rescheduled. +void systimer_set(systimer_t* timer, uint32_t delay_ms); + +// Sets the timer to expire peridically every `period_ms` milliseconds +// +// If the timer is already set, it will be rescheduled. +void systimer_set_periodic(systimer_t* timer, uint32_t period_ms); + +// Unsets the timer (cancels the expiration) +// +// Timer is not deleted and can be set again. +// +// Returns `true` if the timer was unset before its expiration +// so the callback will not be invoked. +bool systimer_unset(systimer_t* timer); + +// Timer suspension state (opaque type). +// Allows to recursively suspend/resume timer. +typedef bool systimer_key_t; + +// Suspends timer callback invocation +// +// The purpose of this function is to prevent the timer callback +// from being invoked for synchronization purposes. The function +// returns a lock that should be passed to `timer_resume()` to +// resume the timer callback invocation. +systimer_key_t systimer_suspend(systimer_t* timer); + +// Resumes timer callback invocation +// +// The timer callback invocation is resumed. The `key` should +// be the same as returned by `timer_suspend()`. +void systimer_resume(systimer_t* timer, systimer_key_t key); + +#endif // TREZORHAL_SYSTIMER_H diff --git a/core/embed/trezorhal/unix/common.c b/core/embed/trezorhal/unix/common.c index 6a840870e..71830560a 100644 --- a/core/embed/trezorhal/unix/common.c +++ b/core/embed/trezorhal/unix/common.c @@ -40,14 +40,6 @@ void __attribute__((noreturn)) trezor_shutdown(void) { exit(3); } -void hal_delay(uint32_t ms) { usleep(1000 * ms); } - -uint32_t hal_ticks_ms() { - struct timeval tv; - gettimeofday(&tv, NULL); - return tv.tv_sec * 1000 + tv.tv_usec / 1000; -} - static int SDLCALL emulator_event_filter(void *userdata, SDL_Event *event) { switch (event->type) { case SDL_QUIT: diff --git a/core/embed/trezorhal/unix/random_delays.c b/core/embed/trezorhal/unix/random_delays.c index 080bc683f..b9bcbe5ca 100644 --- a/core/embed/trezorhal/unix/random_delays.c +++ b/core/embed/trezorhal/unix/random_delays.c @@ -21,4 +21,4 @@ void wait_random(void) {} -void random_delays_init(void) {} +void rdi_init(void) {} diff --git a/core/embed/trezorhal/unix/random_delays.h b/core/embed/trezorhal/unix/random_delays.h index e720d6a7a..01191b24c 100644 --- a/core/embed/trezorhal/unix/random_delays.h +++ b/core/embed/trezorhal/unix/random_delays.h @@ -20,7 +20,7 @@ #ifndef __TREZORHAL_RANDOM_DELAYS_H__ #define __TREZORHAL_RANDOM_DELAYS_H__ -void random_delays_init(void); +void rdi_init(void); void wait_random(void); #endif diff --git a/core/embed/trezorhal/unix/systick.c b/core/embed/trezorhal/unix/systick.c new file mode 100644 index 000000000..11235a39d --- /dev/null +++ b/core/embed/trezorhal/unix/systick.c @@ -0,0 +1,114 @@ +/* + * 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 . + */ + +#include +#include + +#include +#include +#include + +#include "systick.h" + +// Systick driver state +typedef struct { + // Set if the driver is initialized + bool initialized; + // Instant time [us] of driver initialization + uint64_t initial_time; + +} systick_driver_t; + +static systick_driver_t g_systick_driver = { + .initialized = false, +}; + +// Returns number of microseconds since the os started +static uint64_t get_monotonic_clock(void) { + struct timespec tp; + + clock_gettime(CLOCK_MONOTONIC, &tp); + return tp.tv_sec * 1000000UL + tp.tv_nsec / 1000; +} + +void systick_init(void) { + systick_driver_t* drv = &g_systick_driver; + + if (drv->initialized) { + return; + } + + memset(drv, 0, sizeof(systick_driver_t)); + drv->initial_time = get_monotonic_clock(); + drv->initialized = true; +} + +void systick_deinit(void) { + systick_driver_t* drv = &g_systick_driver; + + drv->initialized = false; +} + +void systick_update_freq(void){}; + +uint32_t systick_ms() { + systick_driver_t* drv = &g_systick_driver; + + if (!drv->initialized) { + return 0; + } + + return (get_monotonic_clock() - drv->initial_time) / 1000; +} + +uint64_t systick_us(void) { + systick_driver_t* drv = &g_systick_driver; + + if (!drv->initialized) { + return 0; + } + + return get_monotonic_clock() - drv->initial_time; +} + +void systick_delay_us(uint64_t us) { + systick_driver_t* drv = &g_systick_driver; + + if (!drv->initialized) { + return; + } + + struct timespec tp; + tp.tv_sec = us / 1000000; + tp.tv_nsec = (us % 1000000) * 1000; + nanosleep(&tp, NULL); +} + +void systick_delay_ms(uint32_t ms) { + systick_driver_t* drv = &g_systick_driver; + + if (!drv->initialized) { + return; + } + + struct timespec tp; + tp.tv_sec = ms / 1000; + tp.tv_nsec = (ms % 1000) * 1000000; + nanosleep(&tp, NULL); +} diff --git a/core/embed/trezorhal/unix/systimer.c b/core/embed/trezorhal/unix/systimer.c new file mode 100644 index 000000000..a678efd98 --- /dev/null +++ b/core/embed/trezorhal/unix/systimer.c @@ -0,0 +1,52 @@ +/* + * 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 . + */ + +#include + +#include "systimer.h" + +// systimer driver state +typedef struct { + // Set if the driver is initialized + bool initialized; +} systimer_driver_t; + +static systimer_driver_t g_systimer_driver = { + .initialized = false, +}; + +void systimer_init(void) { + systimer_driver_t* drv = &g_systimer_driver; + + if (drv->initialized) { + return; + } + + memset(&drv, 0, sizeof(systimer_driver_t)); + drv->initialized = true; +} + +void systimer_deinit(void) { + systimer_driver_t* drv = &g_systimer_driver; + + drv->initialized = false; +} + +// Timer driver is not fully implemented for unix platform +// since not neeeded for the emulator diff --git a/core/embed/unix/main.c b/core/embed/unix/main.c index ebabd7c3a..58ef80813 100644 --- a/core/embed/unix/main.c +++ b/core/embed/unix/main.c @@ -53,6 +53,7 @@ #include "py/repl.h" #include "py/runtime.h" #include "py/stackctrl.h" +#include "systimer.h" #include "touch.h" #include "common.h" @@ -486,6 +487,9 @@ MP_NOINLINE int main_(int argc, char **argv) { pre_process_options(argc, argv); + systick_init(); + systimer_init(); + display_init(DISPLAY_RESET_CONTENT); #if USE_TOUCH diff --git a/core/site_scons/models/stm32f4_common.py b/core/site_scons/models/stm32f4_common.py index 839be0364..627c5bee4 100644 --- a/core/site_scons/models/stm32f4_common.py +++ b/core/site_scons/models/stm32f4_common.py @@ -53,6 +53,7 @@ def stm32f4_common_files(env, defines, sources, paths): "embed/trezorhal/stm32f4/platform.c", "embed/trezorhal/stm32f4/secret.c", "embed/trezorhal/stm32f4/systick.c", + "embed/trezorhal/stm32f4/systimer.c", "embed/trezorhal/stm32f4/supervise.c", "embed/trezorhal/stm32f4/time_estimate.c", "embed/trezorhal/stm32f4/random_delays.c", diff --git a/core/site_scons/models/stm32u5_common.py b/core/site_scons/models/stm32u5_common.py index 17a4e47fb..5dba7f550 100644 --- a/core/site_scons/models/stm32u5_common.py +++ b/core/site_scons/models/stm32u5_common.py @@ -64,6 +64,7 @@ def stm32u5_common_files(env, defines, sources, paths): "embed/trezorhal/stm32u5/secret.c", "embed/trezorhal/stm32u5/secure_aes.c", "embed/trezorhal/stm32u5/systick.c", + "embed/trezorhal/stm32f4/systimer.c", "embed/trezorhal/stm32f4/supervise.c", "embed/trezorhal/stm32u5/random_delays.c", "embed/trezorhal/stm32u5/rng.c",