From 10a51de9cd5f5e0a84c0d5eb578ca7589c2b63c7 Mon Sep 17 00:00:00 2001 From: David Garske Date: Mon, 11 May 2026 09:39:14 -0700 Subject: [PATCH] stm32h5: TZEN=1 NS app under wolfBoot --- .../workflows/stm32h563-m33mu-ssh-tzen.yml | 38 ++- src/port/stm32/stm32_eth.c | 88 +++++-- src/port/stm32/stm32_eth.h | 7 + src/port/stm32h563/Makefile | 54 +++- src/port/stm32h563/config.h | 13 + src/port/stm32h563/main.c | 238 +++++++++++------- src/port/stm32h563/startup.c | 10 + src/port/stm32h563/target_tzen.ld | 55 ++-- 8 files changed, 365 insertions(+), 138 deletions(-) diff --git a/.github/workflows/stm32h563-m33mu-ssh-tzen.yml b/.github/workflows/stm32h563-m33mu-ssh-tzen.yml index 10cf326b..4b60a9e3 100644 --- a/.github/workflows/stm32h563-m33mu-ssh-tzen.yml +++ b/.github/workflows/stm32h563-m33mu-ssh-tzen.yml @@ -9,9 +9,9 @@ on: jobs: stm32h563_m33mu_ssh_tzen: runs-on: ubuntu-latest - timeout-minutes: 25 + timeout-minutes: 45 container: - image: ghcr.io/wolfssl/wolfboot-ci-m33mu:v1.2 + image: ghcr.io/wolfssl/wolfboot-ci-m33mu:latest options: --privileged steps: @@ -24,7 +24,7 @@ jobs: apt-get install -y sudo dnsmasq iproute2 netcat-openbsd git \ openssh-client sshpass - - name: Fetch wolfSSL/wolfSSH + - name: Fetch wolfSSL/wolfSSH/wolfBoot run: | set -euo pipefail if [ ! -d ../wolfssl ]; then @@ -33,18 +33,30 @@ jobs: if [ ! -d ../wolfssh ]; then git clone --depth 1 --branch master https://github.com/wolfSSL/wolfssh.git ../wolfssh fi + if [ ! -d ../wolfboot ]; then + git clone --depth 1 --branch stm32tz https://github.com/dgarske/wolfboot.git ../wolfboot + git -C ../wolfboot submodule update --init --single-branch + fi + + - name: Build wolfBoot (stm32h5-tz) + run: | + set -euo pipefail + cd ../wolfboot + cp config/examples/stm32h5-tz.config .config + make wolfboot.bin - - name: Build STM32H563 SSH (TZEN on) + - name: Build STM32H563 SSH NS app (TZEN on) and sign run: | set -euo pipefail make -C src/port/stm32h563 clean TZEN=1 ENABLE_SSH=1 - make -C src/port/stm32h563 TZEN=1 ENABLE_SSH=1 \ + make -C src/port/stm32h563 TZEN=1 ENABLE_SSH=1 signed \ + WOLFBOOT_ROOT="$GITHUB_WORKSPACE/../wolfboot" \ CC=arm-none-eabi-gcc OBJCOPY=arm-none-eabi-objcopy strings src/port/stm32h563/app.bin > /tmp/wolfip-app.strings grep -Fq "Initializing SSH server" /tmp/wolfip-app.strings - name: Run m33mu + DHCP + SSH test - timeout-minutes: 15 + timeout-minutes: 30 run: | set -euo pipefail @@ -74,8 +86,14 @@ jobs: CONF sudo dnsmasq --conf-file=/tmp/dnsmasq.conf --pid-file=/tmp/dnsmasq.pid - sudo m33mu src/port/stm32h563/app.bin \ - --cpu stm32h563 --tap:tap0 --uart-stdout --timeout 180 --quit-on-faults \ + # wolfBoot does a full software ECC256 verify of the 300 KB + # signed NS image, which takes ~10 minutes inside m33mu before + # it BLXNSes into wolfIP. Allow up to 25 minutes total runtime + # and ~20 minutes for the DHCP lease to appear. + sudo m33mu \ + ../wolfboot/wolfboot.bin \ + src/port/stm32h563/app_v1_signed.bin:0x60000 \ + --cpu stm32h563 --tap:tap0 --uart-stdout --timeout 1500 --quit-on-faults \ 2>&1 | tee /tmp/m33mu.log & sleep 1 m33mu_pid="$(pgrep -n -x m33mu || true)" @@ -84,7 +102,7 @@ jobs: fi ip="" - for _ in $(seq 1 90); do + for _ in $(seq 1 1200); do if [ -s /tmp/dnsmasq.leases ]; then ip="$(tail -n1 /tmp/dnsmasq.leases | cut -d' ' -f3)" fi @@ -102,7 +120,7 @@ jobs: echo "Leased IP: ${ip}" ok=0 - for _ in $(seq 1 60); do + for _ in $(seq 1 240); do if ! pgrep -x m33mu >/dev/null 2>&1; then echo "m33mu exited before SSH check." tail -n 200 /tmp/m33mu.log || true diff --git a/src/port/stm32/stm32_eth.c b/src/port/stm32/stm32_eth.c index ae49c05b..92af661d 100644 --- a/src/port/stm32/stm32_eth.c +++ b/src/port/stm32/stm32_eth.c @@ -16,11 +16,11 @@ #endif #if defined(STM32H5) -#if TZEN_ENABLED -#define ETH_BASE 0x50028000UL /* Secure alias */ -#else -#define ETH_BASE 0x40028000UL /* Non-secure alias */ -#endif +/* Always use the NS alias of ETH on H5. Both wolfIP build modes run in + * the Non-Secure world: TZEN=0 has no security distinction, and TZEN=1 + * runs as a NS application launched by wolfBoot (plain LDR/STR carry + * HNONSEC=1 and would fault on a 0x5xxxxxxx access). */ +#define ETH_BASE 0x40028000UL #define STM32_ETH_NEEDS_MDIO_DELAY 0 #elif defined(STM32H7) #define ETH_BASE 0x40028000UL @@ -207,14 +207,16 @@ struct eth_desc { #if defined(STM32H7) || defined(STM32N6) #define ETH_SECTION __attribute__((section(".eth_buffers"))) #elif defined(STM32H5) -#if TZEN_ENABLED -#define ETH_SECTION __attribute__((section(".eth_buffers"))) -#else +/* H5 places ETH descriptors and buffers in the regular .bss region. + * Both TZEN=0 (TrustZone disabled) and TZEN=1 (NS app under wolfBoot) + * link RAM into a single NS-accessible region; no separate ETHMEM + * alias is needed since the CPU and ETH DMA both run in NS world and + * share the same NS view of physical SRAM. */ #define ETH_SECTION #endif -#endif -/* DMA buffer address — use secure alias (0x34), matching RISAF SEC=1. */ +/* DMA descriptor / buffer addresses are taken as-is. NS pointers map + * directly to NS DMA accesses on H5; H7 / N6 use identity mapping. */ #define ETH_DMA_ADDR(ptr) ((uint32_t)(ptr)) static struct eth_desc rx_ring[RX_DESC_COUNT] __attribute__((aligned(32))) ETH_SECTION; @@ -235,6 +237,9 @@ static int32_t phy_addr = -1; static uint32_t rx_poll_count = 0; static uint32_t rx_pkt_count = 0; +#ifdef DEBUG_H5_ETH +static uint32_t tx_send_count = 0; +#endif static uint16_t eth_mdio_read(uint32_t phy, uint32_t reg); static void eth_mdio_write(uint32_t phy, uint32_t reg, uint16_t value); @@ -256,7 +261,7 @@ static int eth_hw_reset(void) * (HAL uses 1 second). Do NOT manually clear SWR — it corrupts DMA. */ timeout = 50000000U; /* ~80ms at 600MHz */ #else - timeout = 1000000U; + timeout = 5000000U; #endif ETH_DMAMR |= ETH_DMAMR_SWR; @@ -664,9 +669,10 @@ static void eth_phy_init(void) } } - /* Reset PHY. */ + /* Reset PHY. PHY reset typically completes in <1 ms; allow ~1 s + * worst case at the H5's 32 MHz HSI / CR=4 MDC speed. */ eth_mdio_write((uint32_t)phy_addr, PHY_REG_BCR, PHY_BCR_RESET); - timeout = 100000U; + timeout = 5000U; do { ctrl = eth_mdio_read((uint32_t)phy_addr, PHY_REG_BCR); } while ((ctrl & PHY_BCR_RESET) != 0U && --timeout != 0U); @@ -677,15 +683,19 @@ static void eth_phy_init(void) ctrl |= PHY_BCR_AUTONEG_ENABLE | PHY_BCR_RESTART_AUTONEG; eth_mdio_write((uint32_t)phy_addr, PHY_REG_BCR, ctrl); - /* Wait for auto-negotiation complete. */ - timeout = 100000U; + /* Wait for auto-negotiation complete. With a cable plugged in + * aneg takes ~3 s; without a cable it never completes. Cap the + * wait so boot proceeds (with link DOWN) in ~5 s instead of ~40 s. */ + timeout = 20000U; do { bsr = eth_mdio_read((uint32_t)phy_addr, PHY_REG_BSR); bsr |= eth_mdio_read((uint32_t)phy_addr, PHY_REG_BSR); } while ((bsr & PHY_BSR_AUTONEG_COMPLETE) == 0U && --timeout != 0U); - /* Wait for link up. */ - timeout = 100000U; + /* Wait for link up. Link comes up promptly once aneg finishes; + * a short timeout is enough -- if there is no cable the previous + * loop has already burned its budget. */ + timeout = 5000U; do { bsr = eth_mdio_read((uint32_t)phy_addr, PHY_REG_BSR); bsr |= eth_mdio_read((uint32_t)phy_addr, PHY_REG_BSR); @@ -779,6 +789,9 @@ static int eth_send(struct wolfIP_ll_dev *dev, void *frame, uint32_t len) next_idx = (tx_idx + 1U) % TX_DESC_COUNT; ETH_DMACTXDTPR = ETH_DMA_ADDR(&tx_ring[next_idx]); tx_idx = next_idx; +#ifdef DEBUG_H5_ETH + tx_send_count++; +#endif return (int)len; } @@ -827,6 +840,47 @@ uint32_t stm32_eth_get_dmacsr(void) return val; } +#ifdef DEBUG_H5_ETH +/* H5 MAC frame counters (MMC) - read-only, free-running. + * ETH base 0x40028000, MMC offsets: + * 0x0114 MMC Tx Single Collision Good Frames -> not us + * 0x014C TX FRAMES OK BY CNT (TXSNGLCOLG_GBPF.. actual) + * Simpler: use generic MAC_TX_PACKET_COUNT_GOOD = 0x40028068 + * and MAC_RX_PACKET_COUNT_GOOD = 0x4002809C + * But H5 uses MMC: TX 0x0768 RX 0x0794 (good_packet counters). + * Reference: RM0481 Rev2 Sec 60.8.4. + */ +#define ETH_MMC_TX_PACKET_GOOD ETH_REG(0x0768U) +#define ETH_MMC_RX_PACKET_GOOD ETH_REG(0x0794U) +#define ETH_MMC_RX_CRC_ERR ETH_REG(0x0794U + 0x10U) /* 0x07A4 */ + +uint32_t stm32_eth_get_tx_count(void) +{ + return tx_send_count; +} + +uint32_t stm32_eth_get_tx_des3(uint32_t i) +{ + if (i >= TX_DESC_COUNT) return 0xFFFFFFFFU; + return tx_ring[i].des3; +} + +uint32_t stm32_eth_get_mac_rx_frames(void) +{ + return ETH_MMC_RX_PACKET_GOOD; +} + +uint32_t stm32_eth_get_mac_tx_frames(void) +{ + return ETH_MMC_TX_PACKET_GOOD; +} + +uint32_t stm32_eth_get_mac_rx_errors(void) +{ + return ETH_MMC_RX_CRC_ERR; +} +#endif + int stm32_eth_init(struct wolfIP_ll_dev *ll, const uint8_t *mac) { uint8_t local_mac[6]; diff --git a/src/port/stm32/stm32_eth.h b/src/port/stm32/stm32_eth.h index 989d39e8..d2b22f51 100644 --- a/src/port/stm32/stm32_eth.h +++ b/src/port/stm32/stm32_eth.h @@ -16,5 +16,12 @@ int stm32_eth_init(struct wolfIP_ll_dev *ll, const uint8_t *mac); void stm32_eth_get_stats(uint32_t *polls, uint32_t *pkts); uint32_t stm32_eth_get_rx_des3(void); uint32_t stm32_eth_get_dmacsr(void); +#ifdef DEBUG_H5_ETH +uint32_t stm32_eth_get_tx_count(void); +uint32_t stm32_eth_get_tx_des3(uint32_t i); +uint32_t stm32_eth_get_mac_rx_frames(void); +uint32_t stm32_eth_get_mac_tx_frames(void); +uint32_t stm32_eth_get_mac_rx_errors(void); +#endif #endif /* WOLFIP_STM32_ETH_H */ diff --git a/src/port/stm32h563/Makefile b/src/port/stm32h563/Makefile index 8bcadf2e..34300eaa 100644 --- a/src/port/stm32h563/Makefile +++ b/src/port/stm32h563/Makefile @@ -57,10 +57,25 @@ WOLFSSL_SP_NO_ASM ?= 0 WOLFSSL_ROOT ?= $(ROOT)/../wolfssl WOLFSSH_ROOT ?= $(ROOT)/../wolfssh WOLFMQTT_ROOT ?= $(ROOT)/../wolfmqtt +# wolfBoot tree, used for TZEN=1 to sign the NS image and to provide +# the secure stub binary that gets concatenated into factory.bin. +# Override on the command line for local clones, e.g.: +# make TZEN=1 WOLFBOOT_ROOT=$(HOME)/GitHub/wolfboot-alt3 ... +WOLFBOOT_ROOT ?= $(ROOT)/../wolfboot FREERTOS_PORT_DIR := $(FREERTOS_PATH)/portable/GCC/ARM_CM33_NTZ/non_secure -# Base compiler flags -CFLAGS := -mcpu=cortex-m33 -mthumb -mcmse -Os -ffreestanding -fdata-sections -ffunction-sections +# wolfBoot boot-partition layout (must match wolfBoot's stm32h5-tz.config). +# WOLFBOOT_BOOT_OFFSET is the offset (from 0x08000000) of the boot +# partition; we pad wolfboot.bin up to this offset before appending the +# signed NS image to produce factory.bin. +WOLFBOOT_BOOT_OFFSET ?= 0x60000 +WOLFBOOT_SIGN_KEY ?= $(WOLFBOOT_ROOT)/wolfboot_signing_private_key.der + +# Base compiler flags. -mcmse is intentionally NOT set: the NS app does +# not declare or call any NSC entry stubs (no cmse_nonsecure_entry +# functions). Adding -mcmse without those would be benign but +# unnecessary and pulls in the secure-mode support runtime. +CFLAGS := -mcpu=cortex-m33 -mthumb -Os -ffreestanding -fdata-sections -ffunction-sections CFLAGS += -g -ggdb -Wall -Wextra -Werror CFLAGS += -I. -I$(ROOT) -I$(ROOT)/src -I$(ROOT)/src/port -I$(ROOT)/src/port/stm32 CFLAGS += -DSTM32H5 @@ -354,6 +369,40 @@ app.elf: $(OBJS) $(LDSCRIPT) app.bin: app.elf $(OBJCOPY) -O binary $< $@ +# ----------------------------------------------------------------------------- +# TZEN=1: sign the NS image with wolfBoot keytools and combine with +# wolfboot.bin to produce factory.bin (single artifact for OpenOCD). +# ----------------------------------------------------------------------------- + +# Image header size MUST match the IMAGE_HEADER_SIZE that wolfBoot was +# built with (config/examples/stm32h5-tz.config sets 1024). Sign reads +# it from the env; the default for ECC256 is only 256 bytes, which +# would put the vector table at 0x08060100 instead of 0x08060400. +WOLFBOOT_IMAGE_HEADER_SIZE ?= 1024 + +# Signed NS image: wolfBoot's sign tool prepends a 1024-byte image +# header; the resulting app_v1_signed.bin loads at 0x08060000 and the +# vector table lands at 0x08060400 (which target_tzen.ld assumes). +app_v1_signed.bin: app.bin $(WOLFBOOT_SIGN_KEY) + IMAGE_HEADER_SIZE=$(WOLFBOOT_IMAGE_HEADER_SIZE) \ + $(WOLFBOOT_ROOT)/tools/keytools/sign --ecc256 --sha256 \ + app.bin $(WOLFBOOT_SIGN_KEY) 1 + +signed: app_v1_signed.bin + +# Combined factory image: wolfBoot stub padded out to the boot +# partition offset, followed by the signed NS image. Flash with +# `program factory.bin 0x08000000` in one step. +factory.bin: app_v1_signed.bin $(WOLFBOOT_ROOT)/wolfboot.bin + $(OBJCOPY) -I binary -O binary --pad-to=$(WOLFBOOT_BOOT_OFFSET) \ + --gap-fill=0xFF $(WOLFBOOT_ROOT)/wolfboot.bin wolfboot_padded.bin + cat wolfboot_padded.bin app_v1_signed.bin > factory.bin + @echo "factory.bin: $$(stat -c%s factory.bin) bytes" + @echo " wolfBoot: $$(stat -c%s $(WOLFBOOT_ROOT)/wolfboot.bin) bytes (padded to $(WOLFBOOT_BOOT_OFFSET))" + @echo " signed app: $$(stat -c%s app_v1_signed.bin) bytes" + +factory: factory.bin + %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ @@ -363,6 +412,7 @@ $(WOLFSSL_ROOT)/%.o: $(WOLFSSL_ROOT)/%.c clean: rm -f *.o app.elf app.bin + rm -f app_v1_signed.bin factory.bin wolfboot_padded.bin rm -f $(ROOT)/src/*.o rm -f $(ROOT)/src/port/*.o rm -f $(ROOT)/src/port/stm32/*.o diff --git a/src/port/stm32h563/config.h b/src/port/stm32h563/config.h index 099670c4..7763be7c 100644 --- a/src/port/stm32h563/config.h +++ b/src/port/stm32h563/config.h @@ -28,11 +28,24 @@ #define ETHERNET #define LINK_MTU 1536 +#if TZEN_ENABLED +/* TZEN=1: NS app under wolfBoot has only 384 KB NS RAM (SRAM2+SRAM3, + * since wolfBoot keeps SRAM1 secure for its own .bss/heap). Scale the + * preallocated socket pool down to fit baseline TCP echo + TLS in that + * window; the MQTT broker config (5 client slots, 4xMTU buffers) does + * not fit and is not enabled in this mode yet. */ +#define MAX_TCPSOCKETS 8 +#define MAX_UDPSOCKETS 2 +#define MAX_ICMPSOCKETS 1 +#define RXBUF_SIZE (LINK_MTU * 2) +#define TXBUF_SIZE (LINK_MTU * 2) +#else #define MAX_TCPSOCKETS 17 /* 12 base + 5 for MQTT broker (listen + 4 clients) */ #define MAX_UDPSOCKETS 2 #define MAX_ICMPSOCKETS 1 /* Reduced from 2 */ #define RXBUF_SIZE (LINK_MTU * 4) /* Reduced for RAM fit with broker */ #define TXBUF_SIZE (LINK_MTU * 4) /* Reduced for RAM fit with broker */ +#endif #define MAX_NEIGHBORS 16 diff --git a/src/port/stm32h563/main.c b/src/port/stm32h563/main.c index ff4f0d4e..95b1c827 100644 --- a/src/port/stm32h563/main.c +++ b/src/port/stm32h563/main.c @@ -77,7 +77,7 @@ static int tls_client_test_done = 0; #endif /* Forward declarations */ -static void uart_puts(const char *s); +void uart_puts(const char *s); static void delay(uint32_t count); /* ========================================================================= @@ -221,13 +221,13 @@ static int https_status_handler(struct httpd *httpd, struct http_client *hc, #define ECHO_PORT 7 #define RX_BUF_SIZE 1024 -#if TZEN_ENABLED -#define RCC_BASE 0x54020C00u /* Secure alias */ -#define ETH_BASE_DBG 0x50028000u /* Secure ETH for debug */ -#else -#define RCC_BASE 0x44020C00u /* Non-secure alias */ -#define ETH_BASE_DBG 0x40028000u /* Non-secure ETH for debug */ -#endif +/* Non-secure peripheral aliases. wolfIP runs in NS world in both modes: + * TZEN=0: TrustZone disabled, only NS aliases exist. + * TZEN=1: wolfBoot is the secure stub and BLXNSs into this app, which + * runs entirely in NS. Plain LDR/STR carry HNONSEC=1 and must + * target the NS aliases (0x4xxxxxxx). */ +#define RCC_BASE 0x44020C00u +#define ETH_BASE_DBG 0x40028000u #define RCC_AHB1ENR (*(volatile uint32_t *)(RCC_BASE + 0x88u)) #define RCC_AHB2ENR (*(volatile uint32_t *)(RCC_BASE + 0x8Cu)) #define RCC_APB3ENR (*(volatile uint32_t *)(RCC_BASE + 0xA8u)) @@ -240,43 +240,11 @@ static int https_status_handler(struct httpd *httpd, struct http_client *hc, #define RCC_CR_HSI48RDY (1u << 13) #define RCC_CCIPR5_RNGSEL_Msk (3u << 4) -/* SAU (Security Attribution Unit) - mark memory regions as non-secure */ -#define SAU_CTRL (*(volatile uint32_t *)0xE000EDD0u) -#define SAU_RNR (*(volatile uint32_t *)0xE000EDD8u) -#define SAU_RBAR (*(volatile uint32_t *)0xE000EDDCu) -#define SAU_RLAR (*(volatile uint32_t *)0xE000EDE0u) - -/* GTZC (Global TrustZone Controller) - unlock SRAM for DMA access */ -#if TZEN_ENABLED -/* Secure addresses when running in secure mode with TZEN=1 */ -#define GTZC1_BASE 0x50032400u /* Secure alias */ -#else -#define GTZC1_BASE 0x40032400u /* Non-secure alias */ -#endif -#define GTZC1_MPCBB1_CR (*(volatile uint32_t *)(GTZC1_BASE + 0x800u)) -#define GTZC1_MPCBB2_CR (*(volatile uint32_t *)(GTZC1_BASE + 0xC00u)) -#define GTZC1_MPCBB3_CR (*(volatile uint32_t *)(GTZC1_BASE + 0x1000u)) -/* MPCBB SECCFGR registers - each bit controls 256 bytes of SRAM */ -#define GTZC1_MPCBB3_SECCFGR(n) (*(volatile uint32_t *)(GTZC1_BASE + 0x1000u + 0x100u + ((n) * 4u))) -/* MPCBB PRIVCFGR registers - each bit controls privilege for 256 bytes of SRAM */ -#define GTZC1_MPCBB3_PRIVCFGR(n) (*(volatile uint32_t *)(GTZC1_BASE + 0x1000u + 0x200u + ((n) * 4u))) -/* TZSC SECCFGR registers - control peripheral security */ -#define GTZC1_TZSC_SECCFGR1 (*(volatile uint32_t *)(GTZC1_BASE + 0x010u)) -#define GTZC1_TZSC_SECCFGR2 (*(volatile uint32_t *)(GTZC1_BASE + 0x014u)) -#define GTZC1_TZSC_SECCFGR3 (*(volatile uint32_t *)(GTZC1_BASE + 0x018u)) - -/* GPIO base addresses */ -#if TZEN_ENABLED -#define GPIOA_BASE 0x52020000u /* Secure alias */ -#define GPIOB_BASE 0x52020400u -#define GPIOC_BASE 0x52020800u -#define GPIOG_BASE 0x52021800u -#else -#define GPIOA_BASE 0x42020000u /* Non-secure alias */ +/* GPIO base addresses (NS aliases; see RCC_BASE comment above). */ +#define GPIOA_BASE 0x42020000u #define GPIOB_BASE 0x42020400u #define GPIOC_BASE 0x42020800u #define GPIOG_BASE 0x42021800u -#endif /* GPIO register offsets */ #define GPIO_MODER(base) (*(volatile uint32_t *)((base) + 0x00u)) @@ -285,13 +253,10 @@ static int https_status_handler(struct httpd *httpd, struct http_client *hc, #define GPIO_AFRL(base) (*(volatile uint32_t *)((base) + 0x20u)) #define GPIO_AFRH(base) (*(volatile uint32_t *)((base) + 0x24u)) -/* SBS (System Bus Security) for RMII selection */ -/* SBS register definitions - PMCR is at offset 0x100 in SBS structure */ -#if TZEN_ENABLED -#define SBS_BASE 0x54000400u /* Secure alias */ -#else -#define SBS_BASE 0x44000400u /* Non-secure alias */ -#endif +/* SBS (System Bus Security) for RMII selection. + * PMCR is at offset 0x100 in SBS structure. SBS is NS-default in TZSC + * and is used unconditionally from NS world here. */ +#define SBS_BASE 0x44000400u #define SBS_PMCR (*(volatile uint32_t *)(SBS_BASE + 0x100u)) #define SBS_PMCR_ETH_SEL_RMII (4u << 21) @@ -310,11 +275,7 @@ static int https_status_handler(struct httpd *httpd, struct http_client *hc, #define GPIO_BSRR(base) (*(volatile uint32_t *)((base) + 0x18u)) #define LED2_PIN 4u -#if TZEN_ENABLED -#define RNG_BASE 0x520C0800u -#else #define RNG_BASE 0x420C0800u -#endif #define RNG_CR (*(volatile uint32_t *)(RNG_BASE + 0x00u)) #define RNG_SR (*(volatile uint32_t *)(RNG_BASE + 0x04u)) #define RNG_DR (*(volatile uint32_t *)(RNG_BASE + 0x08u)) @@ -340,9 +301,16 @@ static uint8_t rx_buf[RX_BUF_SIZE]; static void rng_init(void) { uint32_t rng_cr; + uint32_t timeout; RCC_CR |= RCC_CR_HSI48ON; - while ((RCC_CR & RCC_CR_HSI48RDY) == 0u) { + /* Bounded waits throughout so emulators that do not model the + * HSI48 / RNG status bits (m33mu) cannot deadlock here. The RNG + * may not produce data on the emulator, but boot completes and + * higher-level code can fall back. */ + timeout = 100000u; + while (((RCC_CR & RCC_CR_HSI48RDY) == 0u) && (timeout != 0u)) { + timeout--; } /* Select HSI48 as the RNG kernel clock. */ @@ -359,10 +327,14 @@ static void rng_init(void) rng_cr |= 0x0Du << RNG_CR_CONFIG3_SHIFT; RNG_CR = RNG_CR_CONDRST | rng_cr; - while ((RNG_CR & RNG_CR_CONDRST) == 0u) { + timeout = 100000u; + while (((RNG_CR & RNG_CR_CONDRST) == 0u) && (timeout != 0u)) { + timeout--; } RNG_CR = rng_cr | RNG_CR_RNGEN; - while ((RNG_SR & RNG_SR_DRDY) == 0u) { + timeout = 100000u; + while (((RNG_SR & RNG_SR_DRDY) == 0u) && (timeout != 0u)) { + timeout--; } } @@ -513,7 +485,7 @@ static void uart_putc(char c) USART3_TDR = (uint32_t)c; } -static void uart_puts(const char *s) +void uart_puts(const char *s) { while (*s) { if (*s == '\n') uart_putc('\r'); @@ -603,7 +575,7 @@ void wolfmqtt_log(const char *fmt, ...) uart_puts("...[truncated]\n"); } -static void uart_puthex(uint32_t val) +void uart_puthex(uint32_t val) { const char hex[] = "0123456789ABCDEF"; uart_puts("0x"); @@ -774,6 +746,51 @@ static void echo_cb(int fd, uint16_t event, void *arg) } #endif +#if TZEN_ENABLED +/* When the NS app is launched by wolfBoot, the chip arrives here with + * the PLL programmed for 250 MHz SYSCLK / 250 MHz PCLK1 + * (hal/stm32h5.c:clock_pll_on, plln=62 pllp=2). wolfIP's USART3 BRR + * and SysTick math assume HSI 64 MHz / HSIDIV=/2 / PCLK1 32 MHz (the + * H5 reset default). Rather than recompute every peripheral divisor, + * we revert clocks to that reset state before any peripheral init so + * the rest of the port behaves identically to the TZEN=0 path. */ +static void revert_sysclk_to_hsi(void) +{ + volatile uint32_t *rcc_cr = (volatile uint32_t *)(RCC_BASE + 0x00u); + volatile uint32_t *rcc_cfgr1 = (volatile uint32_t *)(RCC_BASE + 0x1Cu); + volatile uint32_t *rcc_cfgr2 = (volatile uint32_t *)(RCC_BASE + 0x20u); + uint32_t reg; + uint32_t timeout; + + /* CFGR1.SW: 0=HSI, 1=CSI, 2=HSE, 3=PLL1. Select HSI and wait for + * SWS (bits [3:5]) to confirm. Bounded retry so emulators that + * do not toggle the SWS feedback bits (m33mu) cannot deadlock. */ + reg = *rcc_cfgr1; + reg &= ~0x7u; + *rcc_cfgr1 = reg; + timeout = 100000u; + while (((*rcc_cfgr1 & (0x7u << 3)) != 0u) && (timeout != 0u)) { + timeout--; + } + + /* CR.HSIDIV (bits 4:3, shift 3): 0=/1 (64M), 1=/2 (32M), 2=/4, 3=/8. + * Per RM0481 Table 108. Force /2 to match the H5 reset default. + * Wait for HSIDIVF (bit 5) to confirm propagation. Same bounded + * retry so emulators without HSIDIVF feedback cannot deadlock. */ + reg = *rcc_cr; + reg &= ~(0x3u << 3); + reg |= (0x1u << 3); + *rcc_cr = reg; + timeout = 100000u; + while (((*rcc_cr & (1u << 5)) == 0u) && (timeout != 0u)) { + timeout--; + } + + /* AHB and APB prescalers back to /1. */ + *rcc_cfgr2 = 0u; +} +#endif + int main(void) { struct wolfIP_ll_dev *ll; @@ -783,6 +800,10 @@ int main(void) uint64_t tick = 0; int ret; +#if TZEN_ENABLED + revert_sysclk_to_hsi(); +#endif + /* Initialize LED first for debug - keep ON to confirm code is running */ led_init(); led_on(); /* LED ON = Reset_Handler ran, main() started */ @@ -810,33 +831,12 @@ int main(void) uart_puts("\n\n=== wolfIP STM32H563 Echo Server ===\n"); #endif -#if TZEN_ENABLED - /* Configure TrustZone for Ethernet DMA access */ - uart_puts("Configuring TrustZone for Ethernet DMA...\n"); - { - uint32_t i; - - /* Expose only the Ethernet DMA window as non-secure. Leaving ALLNS - * clear keeps the rest of secure flash/SRAM/peripherals secure. */ - SAU_RNR = 0u; - SAU_RBAR = 0x20098000u & 0xFFFFFFE0u; - SAU_RLAR = (0x2009FFFFu & 0xFFFFFFE0u) | 1u; - SAU_CTRL = 0x01u; /* ENABLE */ - __asm volatile ("dsb sy" ::: "memory"); - __asm volatile ("isb sy" ::: "memory"); - - /* Mark MPCBB3 registers 36-39 as non-secure for ETHMEM */ - for (i = 36; i <= 39; i++) { - GTZC1_MPCBB3_SECCFGR(i) = 0x00000000u; - GTZC1_MPCBB3_PRIVCFGR(i) = 0x00000000u; - } - __asm volatile ("dsb sy" ::: "memory"); - - /* Mark Ethernet MAC as non-secure in TZSC */ - GTZC1_TZSC_SECCFGR3 &= ~(1u << 11); - __asm volatile ("dsb sy" ::: "memory"); - } -#endif + /* Under TZEN=1 the application is launched by wolfBoot in NS world + * (BLXNS from the secure stub). wolfBoot's hal_gtzc_init / + * hal_tz_sau_init / periph_unsecure already programmed SAU, GTZC + * MPCBB, and TZSC; this app does not (and cannot, from NS) touch + * those. ETH, RCC, GPIO, RNG, and SBS are NS-default in TZSC and + * accessible directly via the NS aliases used above. */ uart_puts("Initializing wolfIP stack...\n"); wolfIP_init_static(&IPStack); @@ -859,6 +859,17 @@ int main(void) __asm volatile ("dsb sy" ::: "memory"); delay(10000); +#if TZEN_ENABLED + /* The LAN8742A on NUCLEO-H563ZI is held in reset via NRST while + * wolfBoot's SYSRESETREQ asserts it; the PHY needs up to 167 ms + * after deassert to deliver REF_CLK on PA1. Without REF_CLK the + * subsequent ETH DMA software reset (DMAMR.SWR) hangs and + * stm32_eth_init returns -2. The TZEN=0 path inherits a much + * longer board-power-on settle, so this only matters for the + * wolfBoot-launched flow. ~200 ms at 32 MHz is enough headroom. */ + delay(2000000); +#endif + uart_puts("Initializing Ethernet MAC...\n"); ll = wolfIP_getdev(IPStack); ret = stm32_eth_init(ll, NULL); @@ -900,6 +911,38 @@ int main(void) uart_puts(" DHCP discover sent, waiting for lease...\n"); /* Wait for DHCP to complete - poll frequently */ dhcp_start_tick = tick; +#ifdef DEBUG_H5_ETH + { + uint32_t dbg_polls = 0, dbg_pkts = 0; + volatile uint32_t *probe_ns = (volatile uint32_t *)0x2009aa40u; + volatile uint32_t *probe_s = (volatile uint32_t *)0x3009aa40u; + uint32_t before_ns, after_ns, after_s; + /* CPU write to NS alias, read back via NS and S */ + before_ns = *probe_ns; + *probe_ns = 0xC1A55B17u; /* known pattern */ + __asm volatile ("dsb sy" ::: "memory"); + after_ns = *probe_ns; + after_s = *probe_s; + uart_puts(" PROBE 0x2009aa40 before=0x"); uart_puthex(before_ns); + uart_puts(" after_ns=0x"); uart_puthex(after_ns); + uart_puts(" after_s=0x"); uart_puthex(after_s); + uart_puts("\n"); + stm32_eth_get_stats(&dbg_polls, &dbg_pkts); + uart_puts(" ETH t0: polls="); + uart_putdec(dbg_polls); + uart_puts(" rx_pkts="); + uart_putdec(dbg_pkts); + uart_puts(" tx_pkts="); + uart_putdec(stm32_eth_get_tx_count()); + uart_puts(" mac_tx="); + uart_putdec(stm32_eth_get_mac_tx_frames()); + uart_puts(" mac_rx="); + uart_putdec(stm32_eth_get_mac_rx_frames()); + uart_puts(" dmacsr=0x"); + uart_puthex(stm32_eth_get_dmacsr()); + uart_puts("\n"); + } +#endif while (!dhcp_bound(IPStack) && dhcp_client_is_running(IPStack)) { /* Poll the stack - this processes received packets and sends pending data */ (void)wolfIP_poll(IPStack, tick); @@ -907,6 +950,31 @@ int main(void) * volatile loop ~8 cycles/iter: 8000 * 8 / 64MHz = 1ms */ tick++; delay(8000); +#ifdef DEBUG_H5_ETH + if ((tick - dhcp_start_tick) % 500 == 0) { + uint32_t dbg_polls = 0, dbg_pkts = 0; + stm32_eth_get_stats(&dbg_polls, &dbg_pkts); + uart_puts(" ETH t="); + uart_putdec(tick - dhcp_start_tick); + uart_puts(" rx_pkts="); + uart_putdec(dbg_pkts); + uart_puts(" tx_pkts="); + uart_putdec(stm32_eth_get_tx_count()); + uart_puts(" mac_tx="); + uart_putdec(stm32_eth_get_mac_tx_frames()); + uart_puts(" mac_rx="); + uart_putdec(stm32_eth_get_mac_rx_frames()); + uart_puts(" rx_crc_err="); + uart_putdec(stm32_eth_get_mac_rx_errors()); + uart_puts(" dmacsr=0x"); + uart_puthex(stm32_eth_get_dmacsr()); + uart_puts(" rx_des3=0x"); + uart_puthex(stm32_eth_get_rx_des3()); + uart_puts(" tx0_des3=0x"); + uart_puthex(stm32_eth_get_tx_des3(0)); + uart_puts("\n"); + } +#endif /* Check for timeout */ if ((tick - dhcp_start_tick) > dhcp_timeout) break; diff --git a/src/port/stm32h563/startup.c b/src/port/stm32h563/startup.c index 53cecea1..b84c57a9 100644 --- a/src/port/stm32h563/startup.c +++ b/src/port/stm32h563/startup.c @@ -34,6 +34,16 @@ void Reset_Handler(void) uint32_t *src; uint32_t *dst; +#if TZEN_ENABLED + /* Point VTOR_NS at our vector table. wolfBoot's BLXNS sets VTOR_S + * but does not write VTOR_NS; without this, NS exceptions vector + * to address 0 (wolfBoot's S vectors) and lock the CPU. The NS + * vector table is at the start of FLASH (0x08060400, after the + * 1024-byte wolfBoot header). */ + *(volatile uint32_t *)0xE000ED08u = 0x08060400u; + __asm volatile ("dsb sy" ::: "memory"); +#endif + src = &_sidata; for (dst = &_sdata; dst < &_edata; ) { *dst++ = *src++; diff --git a/src/port/stm32h563/target_tzen.ld b/src/port/stm32h563/target_tzen.ld index ffcdbd45..da04637e 100644 --- a/src/port/stm32h563/target_tzen.ld +++ b/src/port/stm32h563/target_tzen.ld @@ -1,39 +1,52 @@ -/* Linker script for STM32H563 with TrustZone ENABLED (TZEN=1) +/* Linker script for STM32H563 wolfIP NS application under wolfBoot + * (TZEN=1, with wolfBoot acting as the Secure boot stub). * - * When TrustZone is enabled: - * - Secure flash alias: 0x0C000000 - * - Secure SRAM alias: 0x30000000 - * - We run in secure mode and configure GTZC to allow DMA access + * Memory layout matches wolfBoot config/examples/stm32h5-tz.config: + * wolfBoot (Secure): 0x0C000000 .. 0x0C040000 + * wolfBoot keyvault: 0x0C040000 .. 0x0C05C000 + * wolfBoot NSC veneer: 0x0C05C000 .. 0x0C060000 + * wolfBoot image hdr: 0x08060000 .. 0x08060400 (1024 B, prepended by sign) + * NS app vectors+text: 0x08060400 .. 0x080FFFFF (boot partition end) + * + * RAM: wolfBoot (patched hal_gtzc_init / hal_tz_sau_init) keeps SRAM1 + * secure for its own use and cedes SRAM2 + SRAM3 to NS: + * NS RAM: 0x20040000 .. 0x2009FFFF (384 KB, SRAM2 + SRAM3) + * + * The NS app runs in Non-Secure world; plain LDR/STR carry HNONSEC=1, + * matching the H5 product-default NS attribution for the ETH peripheral + * and DMA. ETH descriptors and buffers live in the same NS RAM region + * as everything else -- no separate ETHMEM alias is needed. */ MEMORY { - /* Secure addresses (TrustZone enabled) */ - FLASH (rx) : ORIGIN = 0x0C000000, LENGTH = 0x00200000 - RAM (rwx) : ORIGIN = 0x30000000, LENGTH = 0x00090000 /* 576KB secure SRAM (SRAM1+SRAM2+part of SRAM3) */ - - /* Non-secure SRAM for Ethernet DMA buffers (must be marked NS in GTZC) */ - /* Using end of SRAM3 at 0x20098000 - 0x200A0000 (32KB for ETH) */ - ETHMEM (rwx) : ORIGIN = 0x20098000, LENGTH = 0x00008000 + FLASH (rx) : ORIGIN = 0x08060400, LENGTH = 0x0009FC00 /* 639 KB */ + RAM (rwx) : ORIGIN = 0x20040000, LENGTH = 0x00060000 /* 384 KB */ } _estack = ORIGIN(RAM) + LENGTH(RAM); _sidata = LOADADDR(.data); -/* Heap and stack sizes */ -_Min_Heap_Size = 0x10000; /* 64KB heap */ -_Min_Stack_Size = 0x8000; /* 32KB stack (SP math needs headroom) */ +/* Heap and stack sizes. Smaller than the TZEN=0 build because the NS + * RAM window is 384 KB (SRAM2+SRAM3) instead of 640 KB (full SRAM). + * SP math is fine on a 16 KB stack for the bare TCP echo build; TLS + * configurations should override these via EXTRA_LDFLAGS or revisit. */ +_Min_Heap_Size = 0x8000; /* 32KB heap */ +_Min_Stack_Size = 0x4000; /* 16KB stack */ _heap_limit = _estack - _Min_Stack_Size; SECTIONS { .isr_vector : { + . = ALIGN(4); KEEP(*(.isr_vector)) + . = ALIGN(4); } > FLASH .text : { + . = ALIGN(4); *(.text*) *(.rodata*) *(.ARM.extab* .gnu.linkonce.armextab.*) @@ -41,6 +54,7 @@ SECTIONS *(.glue_7) *(.glue_7t) *(.eh_frame) + . = ALIGN(4); } > FLASH .preinit_array : @@ -66,8 +80,10 @@ SECTIONS .data : { + . = ALIGN(4); _sdata = .; *(.data*) + . = ALIGN(4); _edata = .; } > RAM AT > FLASH @@ -81,15 +97,6 @@ SECTIONS _ebss = .; } > RAM - /* Ethernet DMA buffers - placed in non-secure memory region */ - .eth_buffers (NOLOAD) : ALIGN(32) - { - _eth_start = .; - *(.eth_buffers*) - . = ALIGN(32); - _eth_end = .; - } > ETHMEM - /* Reserve minimum heap + stack space so the linker errors if RAM is full. * Heap starts just after BSS at 'end'/'_end'. Stack grows down from * _estack (top of RAM); _heap_limit = _estack - _Min_Stack_Size is the