Skip to content
Merged
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
34 changes: 34 additions & 0 deletions src/port/stm32h563/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ ENABLE_MQTT ?= 0
# MQTT Broker: set ENABLE_MQTT_BROKER=1 to include wolfMQTT broker (requires TLS)
ENABLE_MQTT_BROKER ?= 0

# TFTP client demo: set ENABLE_TFTP=1 to include the wolfIP TFTP client
# that downloads a firmware image at boot and stages it into the
# wolfBoot update partition. TZEN=0 only.
ENABLE_TFTP ?= 0

# FreeRTOS integration: set FREERTOS=1 to run the HTTPS server from a
# FreeRTOS task using the blocking BSD socket wrapper layer.
FREERTOS ?= 0
Expand Down Expand Up @@ -328,6 +333,33 @@ endif

endif # ENABLE_MQTT_BROKER

# -----------------------------------------------------------------------------
# TFTP Client Demo (wolfIP TFTP) - one-shot RRQ GET into wolfBoot update partition
# -----------------------------------------------------------------------------
ifeq ($(ENABLE_TFTP),1)

ifeq ($(TZEN),1)
$(error ENABLE_TFTP=1 currently only supports TZEN=0)
endif

CFLAGS += -DENABLE_TFTP -DWOLFIP_ENABLE_TFTP=1
CFLAGS += -I$(ROOT)/src/tftp

# wolfBoot partition layout. Defaults match
# ../wolfboot/config/examples/stm32h5-no-tz.config. Override on the
# command line to match a different wolfBoot config.
WOLFBOOT_PARTITION_UPDATE_ADDRESS ?= 0x08100000
WOLFBOOT_PARTITION_SIZE ?= 0xA0000
WOLFBOOT_SECTOR_SIZE ?= 0x4000
CFLAGS += -DWOLFBOOT_PARTITION_UPDATE_ADDRESS=$(WOLFBOOT_PARTITION_UPDATE_ADDRESS)UL
CFLAGS += -DWOLFBOOT_PARTITION_SIZE=$(WOLFBOOT_PARTITION_SIZE)UL
CFLAGS += -DWOLFBOOT_SECTOR_SIZE=$(WOLFBOOT_SECTOR_SIZE)UL

SRCS += tftp_client_demo.c
SRCS += $(ROOT)/src/tftp/wolftftp.c

endif # ENABLE_TFTP

# -----------------------------------------------------------------------------
# Build rules
# -----------------------------------------------------------------------------
Expand Down Expand Up @@ -432,6 +464,7 @@ help:
@echo " ENABLE_SSH=1 Enable SSH server (requires TLS + wolfSSH)"
@echo " ENABLE_MQTT=1 Enable MQTT client (requires TLS + wolfMQTT)"
@echo " ENABLE_MQTT_BROKER=1 Enable MQTT broker (requires TLS + wolfMQTT)"
@echo " ENABLE_TFTP=1 Enable TFTP client demo (RRQ GET into wolfBoot update partition; TZEN=0 only)"
@echo " FREERTOS=1 Run HTTPS in a FreeRTOS task via BSD socket wrappers"
@echo " FREERTOS_PATH= Path to FreeRTOS-Kernel (default: $(ROOT)/../FreeRTOS_Kernel)"
@echo " WOLFSSL_ROOT= Path to wolfSSL (default: ../wolfssl)"
Expand All @@ -448,6 +481,7 @@ help:
@echo " make ENABLE_TLS=1 ENABLE_TLS_CLIENT=1 # TLS client (Google test)"
@echo " make ENABLE_TLS=1 ENABLE_MQTT=1 # TLS + MQTT client"
@echo " make ENABLE_TLS=1 ENABLE_MQTT_BROKER=1 # TLS + MQTT broker"
@echo " make ENABLE_TFTP=1 # TFTP client demo (one-shot RRQ at boot)"
@echo " make FREERTOS=1 ENABLE_HTTPS=1 # FreeRTOS HTTPS via BSD sockets"
@echo " make ENABLE_TLS=1 ENABLE_HTTPS=1 ENABLE_SSH=1 ENABLE_MQTT=1 ENABLE_MQTT_BROKER=1 # Full featured"
@echo ""
Expand Down
179 changes: 179 additions & 0 deletions src/port/stm32h563/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -893,6 +893,183 @@ mqttclient -h <device-ip> -p 8883 -t -A /tmp/wolfip_cert.pem \
| `../certs.h` | Embedded ECC P-256 cert/key (shared with TLS/HTTPS) |
| `user_settings.h` | wolfMQTT broker compile-time config |

## TFTP Client

When built with `ENABLE_TFTP=1`, the device runs a one-shot wolfIP TFTP
RRQ (GET) client at boot. After the network comes up, the client fetches
a single file from a host-side TFTP server and stages the bytes directly
into the wolfBoot update partition. On success the wolfBoot update flag
is written to the trailer of the update partition; the next reset hands
control to wolfBoot, which can verify the staged image and swap it in.

`ENABLE_TFTP=1` is **TZEN=0 only** in this release. The build errors out
if `TZEN=1` is also set.

### Building TFTP Mode

```bash
cd src/port/stm32h563
make clean
make ENABLE_TFTP=1
```

The Makefile pulls in `../../tftp/wolftftp.c` from wolfIP's TFTP module
and `tftp_client_demo.c` from this port, and sets
`-DWOLFIP_ENABLE_TFTP=1`. No external dependencies (no wolfSSL,
wolfSSH, wolfMQTT). The demo can be combined with the other services -
e.g. `make ENABLE_TFTP=1 ENABLE_HTTPS=1 ENABLE_SSH=1`.

### Configuration

The defaults in `config.h` and the Makefile target a host on the
`192.168.12.0/24` static-fallback subnet. Override them on the command
line via `EXTRA_CFLAGS`:

```bash
make ENABLE_TFTP=1 \
EXTRA_CFLAGS='-DTFTP_SERVER_IP=\"10.0.4.24\" -DTFTP_FETCH_FILENAME=\"app_v2_signed.bin\"'
```

| Setting | Default | Where |
|---------|---------|-------|
| `TFTP_SERVER_IP` | `"192.168.12.10"` | `config.h` |
| `TFTP_FETCH_FILENAME` | `"app_v2_signed.bin"` | `config.h` |
| `WOLFBOOT_PARTITION_UPDATE_ADDRESS` | `0x08100000` | `Makefile` (matches `stm32h5-no-tz.config`) |
| `WOLFBOOT_PARTITION_SIZE` | `0xA0000` (640 KB) | `Makefile` |
| `WOLFBOOT_SECTOR_SIZE` | `0x4000` (16 KB) | `Makefile` |
| TFTP client local UDP port | `20100` | `tftp_client_demo.c` |
| TFTP block size | `1428` | `tftp_client_demo.c` |
| TFTP window size | `8` | `tftp_client_demo.c` |

Override the partition layout on the command line if your wolfBoot is
configured differently:

```bash
make ENABLE_TFTP=1 WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x08080000 \
WOLFBOOT_PARTITION_SIZE=0x70000
```

### Host Setup (`tftpd-hpa`)

```bash
sudo apt-get install tftpd-hpa
echo "TFTP test fixture" | sudo tee /srv/tftp/app_v2_signed.bin
sudo systemctl restart tftpd-hpa

# Sanity check from another host on the same LAN:
tftp <host-ip> -c get app_v2_signed.bin
```

`/etc/default/tftpd-hpa` (default Ubuntu/Debian install) listens on
`:69` and serves `/srv/tftp`. The TFTP daemon accepts files mode `0666`
by default; restart it after dropping a new fixture.

### Expected Serial Output (TFTP Mode)

```
DHCP configuration received:
IP: 10.0.4.116
Mask: 255.255.255.0
GW: 10.0.4.1
Creating TCP socket on port 7...
Initializing TFTP client demo...
TFTP server: 10.0.4.24
TFTP file: test.txt
TFTP: RRQ sent
Entering main loop. Ready for connections!
TCP Echo: port 7
TFTP: open update partition (erase on demand)
TFTP: programmed bytes=44
TFTP: update flag set, reset to apply
TFTP: close status=0
```

`programmed bytes=N` matches the file's size on the host. `close
status=0` means success. Negative values map to `WOLFTFTP_ERR_*` codes
in `../../tftp/wolftftp.h` (`-1000` IO, `-1001` STATE, `-1002` PACKET,
`-1003` TIMEOUT, `-1004` SIZE, `-1005` VERIFY, `-1006` UNSUPPORTED,
`-1007` TID).

### Verifying the Staged Image

Halt the target and dump the update partition to confirm the bytes
match the host file:

```bash
$OPENOCD -s $OPENOCD_SCRIPTS -f interface/stlink-dap.cfg \
-c "adapter serial $H5_SN" \
-f target/stm32h5x.cfg \
-c "init; halt" \
-c "dump_image /tmp/h5_update_head.bin 0x08100000 64" \
-c "dump_image /tmp/h5_update_tail.bin 0x0819FF00 256" \
-c "resume; shutdown"

# Compare:
xxd /srv/tftp/app_v2_signed.bin | head
xxd /tmp/h5_update_head.bin

# The last byte of the partition (0x0819FFFF) is the wolfBoot update
# trigger - it should read 0x70 (IMG_STATE_UPDATING):
tail -c 1 /tmp/h5_update_tail.bin | xxd
```

### How It Works

The demo uses the wolfIP UDP socket API directly (no BSD wrapper) and
plugs the TFTP state machine's `transport` callback into
`wolfIP_sock_sendto()`. The main loop drains the UDP socket with
`wolfIP_sock_recvfrom()`, hands incoming datagrams to
`wolftftp_client_receive()`, and lets `wolftftp_client_poll()` drive
retransmits and timeouts.

The `io.write` callback in `tftp_client_demo.c` buffers each incoming
DATA block into a 16-byte staging qword. When the staging buffer fills
up, the demo:

1. Lazily erases the 8 KB page that holds the destination address (if
it has not already been erased on this transfer).
2. Programs the 16-byte quad-word at the destination address. STM32H5
flash programs in 128-bit (16-byte) quanta with per-qword ECC; each
qword can only be programmed once between erases.

On successful transfer completion the demo writes
`IMG_STATE_UPDATING` (`0x70`) to the very last byte of the update
partition (`WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE
- 1`). That single byte is the wolfBoot update trigger; on the next
reset wolfBoot reads the trailer and swaps in the staged image.

### Limitations / Out of Scope

- **wolfBoot is not bundled in the demo's flash image.** The current
`target.ld` places the application at `0x08000000`, so when the
unmodified demo boots there is no wolfBoot bootloader to consume the
update flag. The TFTP staging side is correct and hardware-verified;
the round-trip "fetch -> reset -> v2 boots" requires shifting the
app linker script to `0x08060000`, installing a wolfBoot built from
`config/examples/stm32h5-no-tz.config`, and re-packing `factory.bin`
as `wolfboot_padded.bin + signed app`. That packaging is out of scope
for the `ENABLE_TFTP=1` flag.
- **TZEN=1 is not supported.** The flash HAL uses the non-secure
register view (`FLASH_NS_*` at `0x40022000`). A TrustZone-aware
version would need the secure aliases plus
`hal_tz_claim_nonsecure_area()` bracketing each program/erase.
- **Client only, RRQ only.** No WRQ (PUT), and no on-target TFTP
server.
- **No authentication or integrity check on the wire.** TFTP is
unauthenticated by design. The staged image is verified by wolfBoot
on the next boot via its signature check; that catches a tampered
or corrupted image but does not protect the partition until the next
reset window.

### TFTP Files

| File | Description |
|------|-------------|
| `tftp_client_demo.c` | UDP glue + STM32H5 flash HAL + wolfBoot trigger |
| `tftp_client_demo.h` | `_start`/`_poll`/`_status` API |
| `../../tftp/wolftftp.c` | Library: TFTP state machine (RFC 1350 + RFC 2347 options) |
| `../../tftp/wolftftp.h` | Library: public API (`wolftftp_client_*`, `wolftftp_server_*`) |

## Files

| File | Description |
Expand All @@ -915,6 +1092,8 @@ mqttclient -h <device-ip> -p 8883 -t -A /tmp/wolfip_cert.pem \
| `ssh_server.c/h` | SSH shell server (SSH builds only) |
| `ssh_keys.h` | Embedded SSH host key (SSH builds only) |
| `mqtt_client.c/h` | MQTT client state machine (MQTT builds only) |
| `tftp_client_demo.c/h` | TFTP client demo + inline H5 flash HAL (TFTP builds only) |
| `../../tftp/wolftftp.c` | wolfIP TFTP library (TFTP builds only) |
| `../wolfssl_io.c` | wolfSSL I/O callbacks for wolfIP (TLS builds only) |
| `../wolfssh_io.c` | wolfSSH I/O callbacks for wolfIP (SSH builds only) |
| `../wolfmqtt_io.c` | wolfMQTT I/O callbacks for wolfIP (MQTT builds only) |
Expand Down
10 changes: 10 additions & 0 deletions src/port/stm32h563/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,14 @@
#define DHCP_REQUEST_RETRIES 1
#endif

/* TFTP client demo (ENABLE_TFTP=1). On boot, after DHCP, the demo fetches
* TFTP_FETCH_FILENAME from a tftpd-hpa running on TFTP_SERVER_IP and
* stages it into the wolfBoot update partition. */
#ifndef TFTP_SERVER_IP
#define TFTP_SERVER_IP "192.168.12.10"
#endif
#ifndef TFTP_FETCH_FILENAME
#define TFTP_FETCH_FILENAME "app_v2_signed.bin"
#endif

#endif /* WOLF_CONFIG_H */
24 changes: 24 additions & 0 deletions src/port/stm32h563/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@
extern volatile unsigned long broker_uptime_sec;
#endif

#ifdef ENABLE_TFTP
#include "tftp_client_demo.h"
#endif

#ifdef ENABLE_TLS_CLIENT

/* Google IP for TLS client test (run: dig +short google.com) */
Expand Down Expand Up @@ -1074,6 +1078,22 @@ int main(void)
}
#endif

#ifdef ENABLE_TFTP
uart_puts("Initializing TFTP client demo...\n");
{
ip4 srv = atoip4(TFTP_SERVER_IP);
uart_puts(" TFTP server: ");
uart_putip4(srv);
uart_puts("\n TFTP file: ");
uart_puts(TFTP_FETCH_FILENAME);
uart_puts("\n");
if (tftp_client_demo_start(IPStack, srv, TFTP_FETCH_FILENAME,
uart_puts) < 0) {
uart_puts("ERROR: TFTP client init failed\n");
}
}
#endif

uart_puts("Entering main loop. Ready for connections!\n");
uart_puts(" TCP Echo: port 7\n");
#ifdef ENABLE_TLS_CLIENT
Expand Down Expand Up @@ -1103,6 +1123,10 @@ int main(void)
ssh_server_poll();
#endif

#ifdef ENABLE_TFTP
tftp_client_demo_poll(tick);
#endif

#ifdef ENABLE_MQTT
/* Poll MQTT client */
mqtt_client_poll();
Expand Down
Loading
Loading