From 47ad6c21ef14c091ad2fd3a8961d6e9108b867bb Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 9 Feb 2026 18:48:47 +0100 Subject: [PATCH] Updated benchmark to run on rpi + stm32h563 --- config/examples/stm32h5-no-tz.config | 34 ++++++++++ include/image.h | 1 + options.mk | 12 +++- src/image.c | 1 + test-app/ARM-stm32h5-ns.ld | 2 +- test-app/ARM-stm32h5.ld | 2 +- test-app/Makefile | 14 ++-- test-app/app_stm32h5.c | 37 +++++++++++ test-app/app_stm32h7.c | 6 +- test-app/startup_arm.c | 6 ++ tools/scripts/benchmark.sh | 54 +++++----------- tools/scripts/boot-time.py | 95 ++++++++++++++++++++++++++++ 12 files changed, 217 insertions(+), 47 deletions(-) create mode 100644 config/examples/stm32h5-no-tz.config create mode 100644 tools/scripts/boot-time.py diff --git a/config/examples/stm32h5-no-tz.config b/config/examples/stm32h5-no-tz.config new file mode 100644 index 0000000000..6629d80b48 --- /dev/null +++ b/config/examples/stm32h5-no-tz.config @@ -0,0 +1,34 @@ +ARCH?=ARM +TZEN?=0 +TARGET?=stm32h5 +SIGN?=ECC256 +HASH?=SHA256 +DEBUG?=0 +VTOR?=1 +CORTEX_M0?=0 +CORTEX_M33?=1 +NO_ASM?=0 +NO_MPU=1 +EXT_FLASH?=0 +SPI_FLASH?=0 +ALLOW_DOWNGRADE?=0 +NVM_FLASH_WRITEONCE?=1 +WOLFBOOT_VERSION?=1 +V?=0 +SPMATH?=1 +RAM_CODE?=0 +DUALBANK_SWAP?=0 +WOLFBOOT_PARTITION_SIZE?=0xA0000 +#Double sector size to fit header in ML-DSA-87 +WOLFBOOT_SECTOR_SIZE?=0x4000 +WOLFBOOT_KEYVAULT_ADDRESS?=0x0C040000 +WOLFBOOT_KEYVAULT_SIZE?=0x1C000 +WOLFBOOT_NSC_ADDRESS?=0x0C05C000 +WOLFBOOT_NSC_SIZE?=0x4000 +WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x08060000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x08100000 +WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x081A0000 +FLAGS_HOME=0 +DISABLE_BACKUP=0 +IMAGE_HEADER_SIZE?=1024 +ARMORED=1 diff --git a/include/image.h b/include/image.h index 51c8ba9ad5..6f9b5924b8 100644 --- a/include/image.h +++ b/include/image.h @@ -444,6 +444,7 @@ static void __attribute__((noinline)) wolfBoot_image_clear_signature_ok( asm volatile("bne hnope"); \ /* Repeat memcmp call */ \ compare_res = XMEMCMP(digest, img->sha_hash, WOLFBOOT_SHA_DIGEST_SIZE); \ + compare_res; \ /* Redundant checks that ensure the function actually returned 0 */ \ asm volatile("cmp r0, #0":::"cc"); \ asm volatile("cmp r0, #0":::"cc"); \ diff --git a/options.mk b/options.mk index 0ccf841ecb..175206dba0 100644 --- a/options.mk +++ b/options.mk @@ -1,11 +1,21 @@ WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/asn.o USE_GCC?=1 +WOLFBOOT_TEST_FILLER?=0 +WOLFBOOT_TIME_TEST?=0 # Support for Built-in ROT into OTP flash memory ifeq ($(FLASH_OTP_KEYSTORE),1) CFLAGS+=-D"FLASH_OTP_KEYSTORE" endif +ifeq ($(WOLFBOOT_TEST_FILLER),1) + CFLAGS+=-D"WOLFBOOT_TEST_FILLER" +endif + +ifeq ($(WOLFBOOT_TIME_TEST),1) + CFLAGS+=-D"WOLFBOOT_TIME_TEST" +endif + # Support for TPM signature verification ifeq ($(WOLFBOOT_TPM_VERIFY),1) WOLFTPM:=1 @@ -200,7 +210,7 @@ ifeq ($(SIGN),ECC521) ifneq ($(SPMATH),1) STACK_USAGE=11256 else - STACK_USAGE=8288 + STACK_USAGE=8480 endif endif endif diff --git a/src/image.c b/src/image.c index d1f2c6dcc3..ec3de4d447 100644 --- a/src/image.c +++ b/src/image.c @@ -2202,6 +2202,7 @@ int wolfBoot_verify_authenticity(struct wolfBoot_image *img) wolfBoot_printf("Verification of hybrid signature\n"); wolfBoot_verify_signature_secondary(key_slot, img, stored_secondary_signature); + (void)stored_secondary_signature_size; wolfBoot_printf("Done.\n"); } } diff --git a/test-app/ARM-stm32h5-ns.ld b/test-app/ARM-stm32h5-ns.ld index 82000cfd0e..cef93e419c 100644 --- a/test-app/ARM-stm32h5-ns.ld +++ b/test-app/ARM-stm32h5-ns.ld @@ -1,7 +1,7 @@ MEMORY { FLASH (rx) : ORIGIN = @WOLFBOOT_TEST_APP_ADDRESS@, LENGTH = @WOLFBOOT_TEST_APP_SIZE@ - RAM (rwx) : ORIGIN = 0x20050000, LENGTH = 0x40000 + RAM (rwx) : ORIGIN = 0x20050000, LENGTH = 0x50000 } SECTIONS diff --git a/test-app/ARM-stm32h5.ld b/test-app/ARM-stm32h5.ld index 88e7749383..419362232c 100644 --- a/test-app/ARM-stm32h5.ld +++ b/test-app/ARM-stm32h5.ld @@ -1,7 +1,7 @@ MEMORY { FLASH (rx) : ORIGIN = @WOLFBOOT_TEST_APP_ADDRESS@, LENGTH = @WOLFBOOT_TEST_APP_SIZE@ - RAM (rwx) : ORIGIN = 0x30000000, LENGTH = 64K /* Run in lowmem */ + RAM (rwx) : ORIGIN = 0x30000000, LENGTH = 256K } SECTIONS diff --git a/test-app/Makefile b/test-app/Makefile index e978a8fefb..f0f1bd0878 100644 --- a/test-app/Makefile +++ b/test-app/Makefile @@ -289,10 +289,12 @@ ifeq ($(TARGET),stm32h5) LDFLAGS+=-Wl,-gc-sections -Wl,-Map=image.map CFLAGS+=-I.. APP_OBJS+=../hal/uart/uart_drv_$(UART_TARGET).o - ifeq ($(FLASH_OTP_KEYSTORE),1) - APP_OBJS+=../src/flash_otp_keystore.o - else - APP_OBJS+=../src/keystore.o + ifneq ($(SIGN),NONE) + ifeq ($(FLASH_OTP_KEYSTORE),1) + APP_OBJS+=../src/flash_otp_keystore.o + else + APP_OBJS+=../src/keystore.o + endif endif endif @@ -330,7 +332,9 @@ endif ifeq ($(TARGET),va416x0) APP_OBJS+=$(SDK_OBJS) LSCRIPT_TEMPLATE=ARM-va416x0.ld - APP_OBJS+=../src/keystore.o + ifneq ($(SIGN),NONE) + APP_OBJS+=../src/keystore.o + endif endif ifeq ($(TARGET),sim) diff --git a/test-app/app_stm32h5.c b/test-app/app_stm32h5.c index 544181105d..ad8650c031 100644 --- a/test-app/app_stm32h5.c +++ b/test-app/app_stm32h5.c @@ -31,7 +31,9 @@ #include "hal/stm32h5.h" #include "uart_drv.h" #include "wolfboot/wolfboot.h" +#ifndef WOLFBOOT_NO_SIGN #include "keystore.h" +#endif #include "target.h" #ifdef WOLFBOOT_TPM @@ -76,9 +78,20 @@ static int uart_poll(void); #define LED_BOOT_PIN (4) /* PG4 - Nucleo - Red Led */ #define LED_USR_PIN (0) /* PB0 - Nucleo - Green Led */ #define LED_EXTRA_PIN (4) /* PF4 - Nucleo - Orange Led */ +#define BOOT_TIME_PIN (13) /* PA13 - scope trigger */ + +#ifdef WOLFBOOT_TEST_FILLER +#define FILLER_SIZE (64 * 1024) +static volatile uint8_t filler_data[FILLER_SIZE] = { 0x01, 0x02, 0x03 }; +#endif #define NVIC_USART3_IRQN (60) +#ifndef GPIOA_MODER +#define GPIOA_MODER (*(volatile uint32_t *)(GPIOA_BASE + 0x00)) +#define GPIOA_PUPDR (*(volatile uint32_t *)(GPIOA_BASE + 0x0C)) +#define GPIOA_BSRR (*(volatile uint32_t *)(GPIOA_BASE + 0x18)) +#endif /* SysTick */ static uint32_t cpu_freq = 250000000; @@ -119,6 +132,21 @@ static void boot_led_on(void) GPIOG_BSRR |= (1 << (pin)); } +void boot_time_pin_on_early(void) +{ + uint32_t reg; + uint32_t pin = BOOT_TIME_PIN; + + RCC_AHB2ENR_CLOCK_ER |= GPIOA_AHB2ENR1_CLOCK_ER; + /* Delay after an RCC peripheral clock enabling */ + reg = RCC_AHB2ENR_CLOCK_ER; + + reg = GPIOA_MODER & ~(0x03 << (pin * 2)); + GPIOA_MODER = reg | (1 << (pin * 2)); + GPIOA_PUPDR &= ~(0x03 << (pin * 2)); + GPIOA_BSRR |= (1 << (pin)); +} + static void boot_led_off(void) { GPIOG_BSRR |= (1 << (LED_BOOT_PIN + 16)); @@ -542,6 +570,7 @@ static int cmd_info(const char *args) printf("No image in update partition.\r\n"); } +#ifndef WOLFBOOT_NO_SIGN printf("\r\n"); printf("Bootloader OTP keystore information\r\n"); printf("====================================\r\n"); @@ -559,6 +588,10 @@ static int cmd_info(const char *args) printf(" ====================================\r\n "); print_hex(keybuf, size, 0); } +#else + printf("\r\n"); + printf("Signing disabled (SIGN=NONE)\r\n"); +#endif return 0; } @@ -1295,6 +1328,10 @@ void main(void) /* Turn on boot LED */ boot_led_on(); +#ifdef WOLFBOOT_TEST_FILLER + filler_data[FILLER_SIZE - 1] = 0xAA; +#endif + /* Enable SysTick */ systick_enable(); diff --git a/test-app/app_stm32h7.c b/test-app/app_stm32h7.c index bcf886e3d9..c7e24435a1 100644 --- a/test-app/app_stm32h7.c +++ b/test-app/app_stm32h7.c @@ -363,8 +363,10 @@ void uart_print(const char *s) } } -#define FILLER_SIZE (100 * 1024) +#ifdef WOLFBOOT_TEST_FILLER +#define FILLER_SIZE (64 * 1024) static volatile uint8_t filler_data[FILLER_SIZE] = { 0x01, 0x02, 0x03 }; +#endif void main(void) { @@ -377,7 +379,9 @@ void main(void) if (FIRMWARE_A) ld3_write(LED_INIT); +#ifdef WOLFBOOT_TEST_FILLER filler_data[FILLER_SIZE - 1] = 0xAA; +#endif /* LED Indicator of successful UART initialization. SUCCESS = ON, FAIL = OFF */ if (uart_setup(115200) < 0) ld2_write(LED_OFF); diff --git a/test-app/startup_arm.c b/test-app/startup_arm.c index 68e6c37465..ff65084d34 100644 --- a/test-app/startup_arm.c +++ b/test-app/startup_arm.c @@ -63,6 +63,12 @@ extern void main(void); void isr_reset(void) { register unsigned int *src, *dst; + +#if defined(WOLFBOOT_TIME_TEST) && defined(TARGET_stm32h5) + extern void boot_time_pin_on_early(void); + boot_time_pin_on_early(); +#endif + src = (unsigned int *) &_stored_data; dst = (unsigned int *) &_start_data; while (dst < (unsigned int *)&_end_data) { diff --git a/tools/scripts/benchmark.sh b/tools/scripts/benchmark.sh index a76d60ce6f..50638dc243 100755 --- a/tools/scripts/benchmark.sh +++ b/tools/scripts/benchmark.sh @@ -1,34 +1,12 @@ #!/bin/bash # function run_on_board() { - # GPIO2: RST - # GPIO3: BOOT (input) + # GPIO2 (pin 27): RST (output) -- connected to STM32H5 NRST + # GPIO4 (pin 16): BOOT (input) -- connected to STM32H5 PA14 - if ! (st-flash reset &>/dev/null); then - echo -n "No data." - else + openocd -f board/st_nucleo_h5.cfg -c "reset_config connect_assert_srst; init; program wolfboot.bin 0x08000000; program test-app/image_v1_signed.bin 0x08060000; shutdown" &>/dev/null sleep 1 - st-flash --connect-under-reset write factory.bin 0x8000000 &>/dev/null - sleep .2 - echo "2" > /sys/class/gpio/export 2>/dev/null - echo "out" > /sys/class/gpio/gpio2/direction - echo "1" > /sys/class/gpio/gpio2/value # Release reset - echo "0" > /sys/class/gpio/gpio2/value # Keep reset low - sleep 1 - echo -n " | " - echo "1" > /sys/class/gpio/gpio2/value # Release reset - START=`date +%s.%N` - while (test `cat /sys/class/gpio/gpio4/value` -eq 0); do - sleep .01 - done - while (test `cat /sys/class/gpio/gpio4/value` -eq 0); do - sleep .01 - done - END=`date +%s.%N` - echo "scale=3; $END/1 - $START/1 "| bc - echo "in" > /sys/class/gpio/gpio2/direction - echo "2" >/sys/class/gpio/unexport 2>/dev/null - fi + python3 tools/scripts/boot-time.py } function set_benchmark { @@ -37,16 +15,16 @@ function set_benchmark { CONFIG=$@ # Name echo -n "| " - echo -n $NAME + echo -n "$NAME" echo -n " | " # Configuration echo -n $CONFIG | tr -d '\n' echo -n " | " make clean &>/dev/null make keysclean &>/dev/null - make $@ &>/dev/null || make $@ factory.bin - make $@ stack-usage &>/dev/null - make $@ image-header-size &>/dev/null + make WOLFBOOT_TEST_FILLER=1 WOLFBOOT_TIME_TEST=1 $@ &>/dev/null + make WOLFBOOT_TEST_FILLER=1 WOLFBOOT_TIME_TEST=1 $@ stack-usage &>/dev/null + make WOLFBOOT_TEST_FILLER=1 WOLFBOOT_TIME_TEST=1 $@ image-header-size &>/dev/null # Bootloader size echo -n `ls -l wolfboot.bin | cut -d " " -f 5 | tr -d '\n'` echo -n " | " @@ -55,20 +33,21 @@ function set_benchmark { echo -n " | " # Image header size cat .image_header_size | tr -d '\n' + # Application size + echo -n " | " + echo -n `ls -l test-app/image.bin | cut -d " " -f 5 | tr -d '\n'` # Boot time + echo -n " | " run_on_board 2>&1 | tr -d '\n' echo " |" } -echo "4" > /sys/class/gpio/export 2>/dev/null -echo "2" > /sys/class/gpio/unexport 2>/dev/null make keytools &>/dev/null -cp config/examples/stm32h7.config .config -echo "in" > /sys/class/gpio/gpio4/direction -# Output benchmark results in a Markdown table -echo "| Name | Configuration | Bootloader size | Stack size | Image header size | Boot time |" -echo "|------|---------------|-----------------|------------|-------------------|-----------|" +cp config/examples/stm32h5-no-tz.config .config +# Output benchmark results in a Markdown table +echo "| Name | Configuration | Bootloader size | Stack size | Image header size | Application size | Boot time |" +echo "|------|---------------|-----------------|------------|-------------------|------------------|-----------|" set_benchmark "SHA2 only" SIGN=NONE set_benchmark "SHA384 only" SIGN=NONE HASH=SHA384 @@ -97,4 +76,3 @@ set_benchmark "LMS 1-10-8" SIGN=LMS LMS_LEVELS=1 LMS_HEIGHT=10 LMS_WINTERNITZ=8 set_benchmark "XMSS-SHA2_10_256'" XMSS_PARAMS='XMSS-SHA2_10_256' SIGN=XMSS IMAGE_SIGNATURE_SIZE=2500 IMAGE_HEADER_SIZE=8192 set_benchmark "ML_DSA-65 hybrid with ECDSA384" SIGN=ML_DSA ML_DSA_LEVEL=3 IMAGE_SIGNATURE_SIZE=3309 IMAGE_HEADER_SIZE=8192 SIGN_SECONDARY=ECC384 WOLFBOOT_UNIVERSAL_KEYSTORE=1 set_benchmark "ML_DSA-87 hybrid with ECDSA521" SIGN=ML_DSA ML_DSA_LEVEL=5 IMAGE_SIGNATURE_SIZE=4627 IMAGE_HEADER_SIZE=12288 SIGN_SECONDARY=ECC521 WOLFBOOT_UNIVERSAL_KEYSTORE=1 - diff --git a/tools/scripts/boot-time.py b/tools/scripts/boot-time.py new file mode 100644 index 0000000000..ac8374fee4 --- /dev/null +++ b/tools/scripts/boot-time.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +import select +import time +from datetime import datetime, timedelta + +import gpiod +from gpiod.line import Direction, Edge, Bias, Value + +CHIP = "/dev/gpiochip0" +GPIO2 = 2 +GPIO4 = 4 + +HOLD_LOW_S = 0.5 +DEBOUNCE_MS_GPIO4 = 0 # set e.g. to 100 if you want + +def now(): + return datetime.now().strftime("%H:%M:%S.%f")[:-3] + + +def value_to_01(v: Value) -> int: + return 1 if v == Value.ACTIVE else 0 + +def get_val(req, off: int) -> int: + return value_to_01(req.get_value(off)) + +def dump_vals(req, offs): + parts = [] + for o in offs: + try: + parts.append(f"{o}={get_val(req, o)}") + except Exception as e: + parts.append(f"{o}=? ({e})") + return ", ".join(parts) + +def drain_events(req): + try: + evs = req.read_edge_events() + except Exception: + pass + +def main(): + + cfg_initial = { + (GPIO2,): gpiod.LineSettings( + direction=Direction.OUTPUT, + output_value=Value.INACTIVE, # 0 + ), + (GPIO4,): gpiod.LineSettings( + direction=Direction.INPUT, + bias=Bias.PULL_DOWN, + edge_detection=Edge.RISING, + debounce_period=timedelta(milliseconds=DEBOUNCE_MS_GPIO4), + ), + } + + with gpiod.request_lines(CHIP, consumer="release-to-edge", config=cfg_initial) as req: + + # Ensure we don't accidentally read an earlier GPIO4 edge + #drain_events(req) + + time.sleep(HOLD_LOW_S) + + + # Reconfigure GPIO2 to input (no edge needed on GPIO2) + cfg_release = { + (GPIO2,): gpiod.LineSettings( + direction=Direction.INPUT, + bias=Bias.DISABLED, # keep bias none (you said bias not working anyway) + edge_detection=Edge.NONE, + ), + (GPIO4,): None, # unchanged + } + + # Take a best-effort timestamp around the reconfigure call. + # Using midpoint reduces the error to roughly half of the call duration. + t_before = time.monotonic_ns() + req.reconfigure_lines(cfg_release) + t_after = time.monotonic_ns() + t0 = (t_before + t_after) // 2 + + + t4 = None + while t4 is None: + select.select([req.fd], [], []) + for ev in req.read_edge_events(): + if ev.line_offset == GPIO4 and ev.event_type == gpiod.EdgeEvent.Type.RISING_EDGE: + t4 = ev.timestamp_ns + break + + delta_ns = t4 - t0 + print(f"{delta_ns/1e6:.3f} ms") + +if __name__ == "__main__": + main() +