@@ -893,6 +893,183 @@ mqttclient -h <device-ip> -p 8883 -t -A /tmp/wolfip_cert.pem \
893893| ` ../certs.h ` | Embedded ECC P-256 cert/key (shared with TLS/HTTPS) |
894894| ` user_settings.h ` | wolfMQTT broker compile-time config |
895895
896+ ## TFTP Client
897+
898+ When built with ` ENABLE_TFTP=1 ` , the device runs a one-shot wolfIP TFTP
899+ RRQ (GET) client at boot. After the network comes up, the client fetches
900+ a single file from a host-side TFTP server and stages the bytes directly
901+ into the wolfBoot update partition. On success the wolfBoot update flag
902+ is written to the trailer of the update partition; the next reset hands
903+ control to wolfBoot, which can verify the staged image and swap it in.
904+
905+ ` ENABLE_TFTP=1 ` is ** TZEN=0 only** in this release. The build errors out
906+ if ` TZEN=1 ` is also set.
907+
908+ ### Building TFTP Mode
909+
910+ ``` bash
911+ cd src/port/stm32h563
912+ make clean
913+ make ENABLE_TFTP=1
914+ ```
915+
916+ The Makefile pulls in ` ../../tftp/wolftftp.c ` from wolfIP's TFTP module
917+ and ` tftp_client_demo.c ` from this port, and sets
918+ ` -DWOLFIP_ENABLE_TFTP=1 ` . No external dependencies (no wolfSSL,
919+ wolfSSH, wolfMQTT). The demo can be combined with the other services -
920+ e.g. ` make ENABLE_TFTP=1 ENABLE_HTTPS=1 ENABLE_SSH=1 ` .
921+
922+ ### Configuration
923+
924+ The defaults in ` config.h ` and the Makefile target a host on the
925+ ` 192.168.12.0/24 ` static-fallback subnet. Override them on the command
926+ line via ` EXTRA_CFLAGS ` :
927+
928+ ``` bash
929+ make ENABLE_TFTP=1 \
930+ EXTRA_CFLAGS=' -DTFTP_SERVER_IP=\"10.0.4.24\" -DTFTP_FETCH_FILENAME=\"app_v2_signed.bin\"'
931+ ```
932+
933+ | Setting | Default | Where |
934+ | ---------| ---------| -------|
935+ | ` TFTP_SERVER_IP ` | ` "192.168.12.10" ` | ` config.h ` |
936+ | ` TFTP_FETCH_FILENAME ` | ` "app_v2_signed.bin" ` | ` config.h ` |
937+ | ` WOLFBOOT_PARTITION_UPDATE_ADDRESS ` | ` 0x08100000 ` | ` Makefile ` (matches ` stm32h5-no-tz.config ` ) |
938+ | ` WOLFBOOT_PARTITION_SIZE ` | ` 0xA0000 ` (640 KB) | ` Makefile ` |
939+ | ` WOLFBOOT_SECTOR_SIZE ` | ` 0x4000 ` (16 KB) | ` Makefile ` |
940+ | TFTP client local UDP port | ` 20100 ` | ` tftp_client_demo.c ` |
941+ | TFTP block size | ` 1428 ` | ` tftp_client_demo.c ` |
942+ | TFTP window size | ` 8 ` | ` tftp_client_demo.c ` |
943+
944+ Override the partition layout on the command line if your wolfBoot is
945+ configured differently:
946+
947+ ``` bash
948+ make ENABLE_TFTP=1 WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x08080000 \
949+ WOLFBOOT_PARTITION_SIZE=0x70000
950+ ```
951+
952+ ### Host Setup (` tftpd-hpa ` )
953+
954+ ``` bash
955+ sudo apt-get install tftpd-hpa
956+ echo " TFTP test fixture" | sudo tee /srv/tftp/app_v2_signed.bin
957+ sudo systemctl restart tftpd-hpa
958+
959+ # Sanity check from another host on the same LAN:
960+ tftp < host-ip> -c get app_v2_signed.bin
961+ ```
962+
963+ ` /etc/default/tftpd-hpa ` (default Ubuntu/Debian install) listens on
964+ ` :69 ` and serves ` /srv/tftp ` . The TFTP daemon accepts files mode ` 0666 `
965+ by default; restart it after dropping a new fixture.
966+
967+ ### Expected Serial Output (TFTP Mode)
968+
969+ ```
970+ DHCP configuration received:
971+ IP: 10.0.4.116
972+ Mask: 255.255.255.0
973+ GW: 10.0.4.1
974+ Creating TCP socket on port 7...
975+ Initializing TFTP client demo...
976+ TFTP server: 10.0.4.24
977+ TFTP file: test.txt
978+ TFTP: RRQ sent
979+ Entering main loop. Ready for connections!
980+ TCP Echo: port 7
981+ TFTP: open update partition (erase on demand)
982+ TFTP: programmed bytes=44
983+ TFTP: update flag set, reset to apply
984+ TFTP: close status=0
985+ ```
986+
987+ ` programmed bytes=N ` matches the file's size on the host. `close
988+ status=0` means success. Negative values map to ` WOLFTFTP_ERR_ * ` codes
989+ in ` ../../tftp/wolftftp.h ` (` -1000 ` IO, ` -1001 ` STATE, ` -1002 ` PACKET,
990+ ` -1003 ` TIMEOUT, ` -1004 ` SIZE, ` -1005 ` VERIFY, ` -1006 ` UNSUPPORTED,
991+ ` -1007 ` TID).
992+
993+ ### Verifying the Staged Image
994+
995+ Halt the target and dump the update partition to confirm the bytes
996+ match the host file:
997+
998+ ``` bash
999+ $OPENOCD -s $OPENOCD_SCRIPTS -f interface/stlink-dap.cfg \
1000+ -c " adapter serial $H5_SN " \
1001+ -f target/stm32h5x.cfg \
1002+ -c " init; halt" \
1003+ -c " dump_image /tmp/h5_update_head.bin 0x08100000 64" \
1004+ -c " dump_image /tmp/h5_update_tail.bin 0x0819FF00 256" \
1005+ -c " resume; shutdown"
1006+
1007+ # Compare:
1008+ xxd /srv/tftp/app_v2_signed.bin | head
1009+ xxd /tmp/h5_update_head.bin
1010+
1011+ # The last byte of the partition (0x0819FFFF) is the wolfBoot update
1012+ # trigger - it should read 0x70 (IMG_STATE_UPDATING):
1013+ tail -c 1 /tmp/h5_update_tail.bin | xxd
1014+ ```
1015+
1016+ ### How It Works
1017+
1018+ The demo uses the wolfIP UDP socket API directly (no BSD wrapper) and
1019+ plugs the TFTP state machine's ` transport ` callback into
1020+ ` wolfIP_sock_sendto() ` . The main loop drains the UDP socket with
1021+ ` wolfIP_sock_recvfrom() ` , hands incoming datagrams to
1022+ ` wolftftp_client_receive() ` , and lets ` wolftftp_client_poll() ` drive
1023+ retransmits and timeouts.
1024+
1025+ The ` io.write ` callback in ` tftp_client_demo.c ` buffers each incoming
1026+ DATA block into a 16-byte staging qword. When the staging buffer fills
1027+ up, the demo:
1028+
1029+ 1 . Lazily erases the 8 KB page that holds the destination address (if
1030+ it has not already been erased on this transfer).
1031+ 2 . Programs the 16-byte quad-word at the destination address. STM32H5
1032+ flash programs in 128-bit (16-byte) quanta with per-qword ECC; each
1033+ qword can only be programmed once between erases.
1034+
1035+ On successful transfer completion the demo writes
1036+ ` IMG_STATE_UPDATING ` (` 0x70 ` ) to the very last byte of the update
1037+ partition (`WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE
1038+ - 1`). That single byte is the wolfBoot update trigger; on the next
1039+ reset wolfBoot reads the trailer and swaps in the staged image.
1040+
1041+ ### Limitations / Out of Scope
1042+
1043+ - ** wolfBoot is not bundled in the demo's flash image.** The current
1044+ ` target.ld ` places the application at ` 0x08000000 ` , so when the
1045+ unmodified demo boots there is no wolfBoot bootloader to consume the
1046+ update flag. The TFTP staging side is correct and hardware-verified;
1047+ the round-trip "fetch -> reset -> v2 boots" requires shifting the
1048+ app linker script to ` 0x08060000 ` , installing a wolfBoot built from
1049+ ` config/examples/stm32h5-no-tz.config ` , and re-packing ` factory.bin `
1050+ as ` wolfboot_padded.bin + signed app ` . That packaging is out of scope
1051+ for the ` ENABLE_TFTP=1 ` flag.
1052+ - ** TZEN=1 is not supported.** The flash HAL uses the non-secure
1053+ register view (` FLASH_NS_* ` at ` 0x40022000 ` ). A TrustZone-aware
1054+ version would need the secure aliases plus
1055+ ` hal_tz_claim_nonsecure_area() ` bracketing each program/erase.
1056+ - ** Client only, RRQ only.** No WRQ (PUT), and no on-target TFTP
1057+ server.
1058+ - ** No authentication or integrity check on the wire.** TFTP is
1059+ unauthenticated by design. The staged image is verified by wolfBoot
1060+ on the next boot via its signature check; that catches a tampered
1061+ or corrupted image but does not protect the partition until the next
1062+ reset window.
1063+
1064+ ### TFTP Files
1065+
1066+ | File | Description |
1067+ | ------| -------------|
1068+ | ` tftp_client_demo.c ` | UDP glue + STM32H5 flash HAL + wolfBoot trigger |
1069+ | ` tftp_client_demo.h ` | ` _start ` /` _poll ` /` _status ` API |
1070+ | ` ../../tftp/wolftftp.c ` | Library: TFTP state machine (RFC 1350 + RFC 2347 options) |
1071+ | ` ../../tftp/wolftftp.h ` | Library: public API (` wolftftp_client_* ` , ` wolftftp_server_* ` ) |
1072+
8961073## Files
8971074
8981075| File | Description |
@@ -915,6 +1092,8 @@ mqttclient -h <device-ip> -p 8883 -t -A /tmp/wolfip_cert.pem \
9151092| ` ssh_server.c/h ` | SSH shell server (SSH builds only) |
9161093| ` ssh_keys.h ` | Embedded SSH host key (SSH builds only) |
9171094| ` mqtt_client.c/h ` | MQTT client state machine (MQTT builds only) |
1095+ | ` tftp_client_demo.c/h ` | TFTP client demo + inline H5 flash HAL (TFTP builds only) |
1096+ | ` ../../tftp/wolftftp.c ` | wolfIP TFTP library (TFTP builds only) |
9181097| ` ../wolfssl_io.c ` | wolfSSL I/O callbacks for wolfIP (TLS builds only) |
9191098| ` ../wolfssh_io.c ` | wolfSSH I/O callbacks for wolfIP (SSH builds only) |
9201099| ` ../wolfmqtt_io.c ` | wolfMQTT I/O callbacks for wolfIP (MQTT builds only) |
0 commit comments