Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 28 additions & 10 deletions .github/workflows/stm32h563-m33mu-ssh-tzen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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)"
Expand All @@ -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
Expand All @@ -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
Expand Down
88 changes: 71 additions & 17 deletions src/port/stm32/stm32_eth.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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];
Expand Down
7 changes: 7 additions & 0 deletions src/port/stm32/stm32_eth.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
54 changes: 52 additions & 2 deletions src/port/stm32h563/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 $@

Expand All @@ -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
Expand Down
13 changes: 13 additions & 0 deletions src/port/stm32h563/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Loading
Loading