diff --git a/arch.mk b/arch.mk index 42a08fc1..301f3627 100644 --- a/arch.mk +++ b/arch.mk @@ -1007,11 +1007,16 @@ ifeq ($(ARCH),PPC) endif ifneq ($(NO_ASM),1) - # Use the SHA256 and SP math all assembly accelerations + # Use the SHA256/SHA512 and SP math assembly accelerations. + # (wolfSSL PR 10767 added the SHA-512 PPC32 asm transform referenced by + # sha512.c; --gc-sections prunes it when sha512.o is not linked, e.g. ED25519.) CFLAGS+=-DWOLFSSL_SP_PPC CFLAGS+=-DWOLFSSL_PPC32_ASM -DWOLFSSL_PPC32_ASM_INLINE #CFLAGS+=-DWOLFSSL_PPC32_ASM_SMALL MATH_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/ppc32/ppc32-sha256-asm_c.o + # Add the SHA-512 PPC32 asm object only if its source is present (wildcard): + # PR 10767 provides it; older checkouts fall back to C sha512. + MATH_OBJS+=$(patsubst %.c,%.o,$(wildcard $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/ppc32/ppc32-sha512-asm_c.c)) endif endif diff --git a/include/user_settings.h b/include/user_settings.h index 896db30a..f6beb2a8 100644 --- a/include/user_settings.h +++ b/include/user_settings.h @@ -217,8 +217,12 @@ extern int tolower(int c); #if !defined(PKCS11_SMALL) && !defined(WOLFCRYPT_TEST) && !defined(WOLFCRYPT_BENCHMARK) # define HAVE_ECC_CDH #endif + /* Don't force SP_MATH when a config selects SP_MATH_ALL (SPMATHALL=1, + * e.g. T1024/T1040); the two SP math backends are mutually exclusive. */ +# if !defined(WOLFSSL_SP_MATH_ALL) # define WOLFSSL_SP_MATH # define WOLFSSL_SP_SMALL +# endif # define WOLFSSL_HAVE_SP_ECC # define WOLFSSL_KEY_GEN # define HAVE_ECC_KEY_EXPORT @@ -622,6 +626,15 @@ extern int tolower(int c); #define CUSTOM_RAND_GENERATE_SEED my_rng_seed_gen #define CUSTOM_RAND_GENERATE_BLOCK my_rng_seed_gen extern int my_rng_seed_gen(unsigned char* output, unsigned int sz); + + /* Full test/benchmark algo set: AES ECB/CBC/CTR/GCM/XTS, + * SHA-256/384/512, SHA-3. (AES-CBC/GCM added below; SHA-384/512 + * from HASH=SHA384.) */ + #define WOLFSSL_AES_COUNTER + #define HAVE_AES_ECB + #define WOLFSSL_AES_XTS + #define WOLFSSL_AES_DIRECT + #define WOLFSSL_SHA3 #endif #define HAVE_AESGCM diff --git a/test-app/Makefile b/test-app/Makefile index 2369dbfa..7ccd9030 100644 --- a/test-app/Makefile +++ b/test-app/Makefile @@ -6,19 +6,32 @@ WOLFBOOT_LIB_WOLFSSL?=../lib/wolfssl WOLFBOOT_LIB_WOLFTPM?=../lib/wolfTPM WOLFBOOT_LIB_WOLFHSM?=../lib/wolfHSM +WOLFBOOT_LIB_WOLFIP?=../lib/wolfip WOLFSSL_LOCAL_OBJDIR?=wolfssl_obj WOLFTPM_LOCAL_OBJDIR?=wolftpm_obj WOLFHSM_LOCAL_OBJDIR?=wolfhsm_obj +WOLFIP_LOCAL_OBJDIR?=wolfip_obj vpath %.c $(WOLFBOOT_LIB_WOLFSSL) vpath %.S $(WOLFBOOT_LIB_WOLFSSL) vpath %.c $(WOLFBOOT_LIB_WOLFTPM) vpath %.S $(WOLFBOOT_LIB_WOLFTPM) vpath %.c $(WOLFBOOT_LIB_WOLFHSM) vpath %.S $(WOLFBOOT_LIB_WOLFHSM) +vpath %.c $(WOLFBOOT_LIB_WOLFIP) WOLFBOOT_ROOT?=.. TARGET?=none ARCH?=ARM + +# PPC64 (e5500/e6500) vs 32-bit (e500/e500mc), set early so the wolfCrypt asm +# wiring below sees it; per-TARGET blocks later re-assert the same value. +ifeq ($(ARCH),PPC) + ifneq ($(filter nxp_t2080 nxp_t1024 nxp_t1040,$(TARGET)),) + PPC64=1 + else + PPC64=0 + endif +endif MCUXPRESSO_CMSIS?=$(MCUXPRESSO)/CMSIS ifneq (,$(filter $(TARGET),mcxa mcxw mcxn)) ifneq ($(WOLFCRYPT_TZ_PSA),1) @@ -181,6 +194,7 @@ ifeq ($(WOLFCRYPT_SUPPORT),1) # Add SHA implementations (needed for test/benchmark) APP_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.o APP_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha512.o + APP_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha3.o APP_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/hmac.o # Add RNG support (needed for ECC signing and tests) @@ -219,32 +233,70 @@ ifeq ($(WOLFCRYPT_SUPPORT),1) endif endif - # Power PC + # Power PC (wolfSSL PRs 10740 + 10767: AES/SHA-2/SHA-3 PPC asm) ifeq ($(ARCH),PPC) - # SP mp int PowerPC ASM + # SP big-number math stays on the 32-bit PPC asm path (matches the + # bootloader and the SP_WORD_SIZE=32 user_settings.h forces for PPC); the + # AES/SHA asm under test is independent of SP math. + CFLAGS+=-DWOLFSSL_SP_PPC + # PPC_ASM=0 builds wolfCrypt pure-C (no PPC64/PPC32 AES/SHA asm) for the + # C-vs-asm benchmark baseline. Default on. + PPC_ASM?=1 + ifeq ($(PPC_ASM),1) ifeq ($(PPC64),1) - CFLAGS+=-DWOLFSSL_SP_PPC64 - else - CFLAGS+=-DWOLFSSL_SP_PPC - endif - - # SHA256 - APP_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/ppc32/ppc32-sha256-asm_c.o - CFLAGS+=-DWOLFSSL_PPC32_ASM - CFLAGS+=-DWOLFSSL_PPC32_ASM_INLINE - CFLAGS+=-DWOLFSSL_PPC32_ASM_SMALL - - ifeq ($(PPC64),1) # requires wolfssl PR 9852 - # AES - APP_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/ppc64/ppc64-aes-asm_c.o + # e5500/e6500: PPC64 base-scalar ASM (AES T-table, SHA-2/SHA-3 via + # 64-bit GPR ops). These are NOT POWER8 -- no VSX/vector-crypto -- so + # WOLFSSL_PPC64_ASM_CRYPTO / _POWER8 must NOT be set (they emit + # vcipher/vshasigma and would trap). CFLAGS+=-DWOLFSSL_PPC64_ASM CFLAGS+=-DWOLFSSL_PPC64_ASM_INLINE CFLAGS+=-DWOLFSSL_PPC64_ASM_SMALL CFLAGS+=-DWOLFSSL_PPC64_ASM_AES_NO_HARDEN + APP_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/ppc64/ppc64-aes-asm_c.o + APP_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/ppc64/ppc64-sha256-asm_c.o + APP_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/ppc64/ppc64-sha512-asm_c.o + APP_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/ppc64/ppc64-sha3-asm_c.o + else + # e500mc: PPC32 base-scalar ASM, condition-register variant (PR 10740). + # INLINE_REG selects the *_cr.c files and needs the assembler to accept + # symbolic register names (-Wa,-mregnames). + CFLAGS+=-DWOLFSSL_PPC32_ASM + CFLAGS+=-DWOLFSSL_PPC32_ASM_INLINE + CFLAGS+=-DWOLFSSL_PPC32_ASM_INLINE_REG -Wa,-mregnames + CFLAGS+=-DWOLFSSL_PPC32_ASM_SMALL + APP_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/ppc32/ppc32-aes-asm_cr.o + APP_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/ppc32/ppc32-sha256-asm_cr.o + APP_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/ppc32/ppc32-sha512-asm_cr.o + APP_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/ppc32/ppc32-sha3-asm_cr.o endif + endif # PPC_ASM CFLAGS+=-fomit-frame-pointer endif + + # AArch64 (ARMv8): armv8 asm + SP arm64 objects (as arch.mk links for the + # bootloader). The test-app is a separate link with full AES/SHA/ECC under + # WOLFSSL_ARMASM (and the __aarch64__ SP asm in user_settings.h). + ifeq ($(ARCH),AARCH64) + ifneq ($(NO_ARM_ASM),1) + ifeq ($(SPMATH),1) + APP_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sp_arm64.o + endif + APP_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/cpuid.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/arm/armv8-aes-asm_c.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/arm/armv8-sha256-asm_c.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/arm/armv8-sha512-asm_c.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/arm/armv8-sha3-asm_c.o + # armv8 asm uses crypto mnemonics (aese/sha256h); arch.mk's +crypto is + # lost to a later -mstrict-align, so restore it here for the asm. + CFLAGS+=-march=armv8-a+crypto + # libgcc for SP math 128-bit helpers (__udivti3); link is -nodefaultlibs. + LIBS+=-lgcc + # syscalls.c _sbrk references _Min_Heap_Size; no heap, so anchor it at + # zero (ARM does the same). + LDFLAGS+=-Wl,--defsym=_Min_Heap_Size=0 + endif + endif endif CFLAGS+=-DWOLFSSL_USER_SETTINGS @@ -631,8 +683,6 @@ ifeq ($(TARGET),nxp_t2080) ifneq ($(SIGN),NONE) APP_OBJS+=../src/keystore.o endif - CFLAGS+=-ffunction-sections -fdata-sections - # PowerPC e6500 PPC64=1 endif @@ -1131,11 +1181,69 @@ ifeq ($(TARGET),aurix_tc3xx) endif endif +# On PPC64 (e5500/e6500) arch.mk turns on PPC32 SHA asm for the bootloader's +# image-verify link; strip it from the test-app's wolfCrypt so PPC_ASM alone +# controls the app's AES/SHA asm. +ifeq ($(PPC64),1) + CFLAGS:=$(filter-out -DWOLFSSL_PPC32_ASM -DWOLFSSL_PPC32_ASM_INLINE,$(CFLAGS)) +endif + # Capture final flags for locally built wolfSSL objects. WOLFSSL_CFLAGS:=$(CFLAGS) WOLFTPM_CFLAGS:=$(CFLAGS) WOLFHSM_CFLAGS:=$(CFLAGS) +# Optional wolfIP network stack in the test-app. +# ENABLE_WOLFIP=1 wolfIP ethernet port (nxp_fman, or nxp_enetc on +# LS1028A) + a link/PHY smoke test. +# WOLFBOOT_TEST_TFTP=1 additionally pull in wolfIP core + TFTP client for +# the full network TFTP-fetch test. +# Point WOLFBOOT_LIB_WOLFIP at a wolfIP checkout, e.g. +# make WOLFBOOT_LIB_WOLFIP=/path/to/wolfip ENABLE_WOLFIP=1 ... +ifeq ($(ENABLE_WOLFIP),1) + # Fail early with a clear message if the wolfIP checkout is missing. + ifeq ($(wildcard $(WOLFBOOT_LIB_WOLFIP)/src/wolfip.c),) + $(error ENABLE_WOLFIP=1 but no wolfIP sources found at WOLFBOOT_LIB_WOLFIP=$(WOLFBOOT_LIB_WOLFIP). Set WOLFBOOT_LIB_WOLFIP to a wolfIP checkout, e.g. make WOLFBOOT_LIB_WOLFIP=/path/to/wolfip ENABLE_WOLFIP=1 ...) + endif + # Port by target: LS1028A (AArch64) uses ENETC; QorIQ PowerPC uses FMan. + ifeq ($(TARGET),nxp_ls1028a) + WOLFIP_PORT:=nxp_enetc + WOLFIP_PORT_OBJ:=nxp_enetc.o + CFLAGS+=-DWOLFIP_PORT_ENETC + else + WOLFIP_PORT:=nxp_fman + WOLFIP_PORT_OBJ:=nxp_fman.o + # The PPC test-app's _app_entry does no BSS-PLT fixup, so -mbss-plt + # (arch.mk) would route cross-module libc calls (e.g. variable-size memcpy + # in the wolfIP driver) through an uninitialized PLT stub branching to 0; + # and default _FORTIFY_SOURCE rewrites memcpy/memmove into the _chk glibc + # symbols (absent here -> R_PPC_JMP_SLOT imports, zero GOT slots). -fno-plt + # + fortify-off keep those calls direct and unfortified. Scoped to the + # wolfIP build so default builds are unchanged. + CFLAGS+=-ffunction-sections -fdata-sections + CFLAGS+=-fno-plt -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 + endif + WOLFIP_PORTDIR:=$(WOLFBOOT_LIB_WOLFIP)/src/port/$(WOLFIP_PORT) + CFLAGS+=-DENABLE_WOLFIP + # wolfIP core (wolfip.c) provides the stack incl. DHCP/ICMP/TCP/UDP. + WOLFIP_OBJS:=$(WOLFIP_PORTDIR)/$(WOLFIP_PORT_OBJ) \ + $(WOLFBOOT_LIB_WOLFIP)/src/wolfip.o + ifeq ($(WOLFBOOT_TEST_TFTP),1) + CFLAGS+=-DWOLFBOOT_TEST_TFTP + WOLFIP_OBJS+=$(WOLFBOOT_LIB_WOLFIP)/src/tftp/wolftftp.o + endif + ifeq ($(WOLFIP_SPEED_TEST),1) + CFLAGS+=-DWOLFIP_SPEED_TEST + endif + APP_OBJS+=$(WOLFIP_OBJS) wolfip_tftp_test.o + # wolfIP TUs need the port headers first (so config.h resolves to the + # port copy) and tolerate wolfIP's own style; drop -Werror/-Wstack-usage. + WOLFIP_CFLAGS:=-I"$(WOLFIP_PORTDIR)" -I"$(WOLFBOOT_LIB_WOLFIP)" \ + $(filter-out -Werror -Wstack-usage=$(STACK_USAGE_LIMIT),$(CFLAGS)) \ + -DWOLFIP_ENABLE_TFTP=$(if $(filter 1,$(WOLFBOOT_TEST_TFTP)),1,0) \ + -Wno-unused -Wno-sign-compare -Wno-missing-field-initializers +endif + ifeq ($(WOLFHSM_CLIENT),1) CFLAGS += -DSTRING_USER -I"$(WOLFBOOT_LIB_WOLFSSL)" APP_OBJS += $(WOLFHSM_OBJS) @@ -1170,6 +1278,9 @@ image.srec: image.elf APP_OBJS := $(patsubst $(WOLFBOOT_LIB_WOLFSSL)/%, \ $(WOLFSSL_LOCAL_OBJDIR)/%, $(APP_OBJS)) +APP_OBJS := $(patsubst $(WOLFBOOT_LIB_WOLFIP)/%, \ + $(WOLFIP_LOCAL_OBJDIR)/%, $(APP_OBJS)) + ifeq ($(ELF_FLASH_SCATTER),1) # When ELF_FLASH_SCATTER=1, preprocess the ELF file with the squashelf tool SQUASHELF_TOOL = ../tools/squashelf/squashelf @@ -1246,10 +1357,22 @@ $(WOLFHSM_LOCAL_OBJDIR)/%.o: %.S $(Q)mkdir -p $(dir $@) $(Q)$(CC) $(WOLFHSM_CFLAGS) -c $(OUTPUT_FLAG) $@ $< + +$(WOLFIP_LOCAL_OBJDIR)/%.o: %.c + @echo "\t[CC-$(ARCH)] $@" + $(Q)mkdir -p $(dir $@) + $(Q)$(CC) $(WOLFIP_CFLAGS) -c $(OUTPUT_FLAG) $@ $< + +# Local test-app wolfIP glue, built with the wolfIP include/flag set. +wolfip_tftp_test.o: wolfip_tftp_test.c + @echo "\t[CC-$(ARCH)] $@" + $(Q)$(CC) $(WOLFIP_CFLAGS) -c $(OUTPUT_FLAG) $@ $< + + clean: $(Q)rm -f *.bin *.elf tags *.o $(LSCRIPT) $(APP_OBJS) wcs/*.o $(Q)rm -rf $(WOLFSSL_LOCAL_OBJDIR) $(WOLFTPM_LOCAL_OBJDIR) \ - $(WOLFHSM_LOCAL_OBJDIR) + $(WOLFHSM_LOCAL_OBJDIR) $(WOLFIP_LOCAL_OBJDIR) $(LSCRIPT): $(LSCRIPT_TEMPLATE) FORCE $(Q)printf "%d" $(WOLFBOOT_PARTITION_BOOT_ADDRESS) > .wolfboot-offset diff --git a/test-app/wolfip_tftp_test.c b/test-app/wolfip_tftp_test.c new file mode 100644 index 00000000..96b1e8b4 --- /dev/null +++ b/test-app/wolfip_tftp_test.c @@ -0,0 +1,544 @@ +/* wolfip_tftp_test.c + * + * Optional wolfIP network test for the wolfBoot test-app. Ethernet port and ms + * timebase are compile-time selected: NXP QorIQ FMan (T2080/T1024/T1040, + * big-endian PowerPC) or NXP Layerscape ENETC (LS1028A, AArch64). Modes: + * default DHCP lease only + * WOLFBOOT_TEST_TFTP + TFTP RRQ fetch into RAM, report size+checksum + * (optionally verify them via TFTP_EXPECT_SIZE/SUM) + * WOLFIP_SPEED_TEST + TCP throughput server on port 9 (benchmark) + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include + +#include "printf.h" +#include "wolfip.h" +#ifdef WOLFBOOT_TEST_TFTP +#include "src/tftp/wolftftp.h" +#endif + +/* Port selection: WOLFIP_PORT_ENETC -> LS1028A ENETC, else QorIQ FMan. Driver + * entry points are wrapped so the bring-up code below is port-agnostic. */ +#if defined(WOLFIP_PORT_ENETC) +#include "nxp_enetc.h" +#define WOLFIP_NETDEV_INIT nxp_enetc_init +#define WOLFIP_NETDEV_PHY_ADDR nxp_enetc_phy_addr +#define WOLFIP_NETDEV_PHY_READ nxp_enetc_phy_read +#define WOLFIP_NETDEV_LINK_UP nxp_enetc_link_up +#define WOLFIP_NETDEV_SET_LOG nxp_enetc_set_log +#define WOLFIP_NETDEV_NAME "ENETC" +#else +#include "nxp_fman.h" +#define WOLFIP_NETDEV_INIT nxp_fman_init +#define WOLFIP_NETDEV_PHY_ADDR nxp_fman_phy_addr +#define WOLFIP_NETDEV_PHY_READ nxp_fman_phy_read +#define WOLFIP_NETDEV_PHY_READ_AT nxp_fman_phy_read_at +#define WOLFIP_NETDEV_LINK_UP nxp_fman_link_up +#define WOLFIP_NETDEV_NAME "FMan mEMAC" +#endif + +/* Host running in.tftpd / the throughput peer (same subnet as the DHCP lease). + * Override from the build config, e.g. + * CFLAGS_EXTRA+=-DHOST_IP_STR='"192.168.1.5"' -DTFTP_FILENAME='"img.bin"'. */ +#ifndef HOST_IP_STR +#define HOST_IP_STR "10.0.4.24" +#endif +#ifndef TFTP_FILENAME +#define TFTP_FILENAME "wolfip_test.bin" +#endif +/* Optional integrity check: when non-zero, the fetched byte count and/or + * additive 32-bit checksum must match or the test FAILS; 0 (default) just + * reports the measured values. E.g. CFLAGS_EXTRA+=-DTFTP_EXPECT_SIZE=8192 + * -DTFTP_EXPECT_SUM=0xFF000. */ +#ifndef TFTP_EXPECT_SIZE +#define TFTP_EXPECT_SIZE 0U +#endif +#ifndef TFTP_EXPECT_SUM +#define TFTP_EXPECT_SUM 0U +#endif + +/* Millisecond clock from a free-running counter, self-contained (no bootloader + * symbol). wolfIP timers are coarse so exactness is moot. */ +#if defined(__aarch64__) +/* ARMv8 generic timer: CNTPCT_EL0 counts at CNTFRQ_EL0 Hz. */ +static uint64_t read_tb(void) +{ + uint64_t v; + __asm__ __volatile__("isb; mrs %0, cntpct_el0" : "=r"(v)); + return v; +} +static uint64_t tb_hz(void) +{ + static uint64_t cached = 0; /* CNTFRQ is a boot constant; read once */ + uint64_t f; + if (cached != 0) + return cached; + __asm__ __volatile__("mrs %0, cntfrq_el0" : "=r"(f)); + cached = f ? f : 25000000ULL; /* LS1028A SYSCLK-derived default */ + return cached; +} +static uint64_t now_ms(void) +{ + return read_tb() / (tb_hz() / 1000ULL); +} +#else +/* PowerPC e5500/e6500 Time Base via SPRs (TBL=268, TBU=269) = platform clock + * / 16; default is the VPX3-152 value (600 MHz -> 37.5 MHz). Other platform + * clocks MUST override this -- it scales the wolfIP TCP/DHCP timers fed by + * now_ms, not just harness timeouts -- e.g. -DTIMEBASE_HZ=50000000ULL. */ +#ifndef TIMEBASE_HZ +#define TIMEBASE_HZ 37500000ULL +#endif + +static uint64_t read_tb(void) +{ + uint32_t hi, lo, hi2; + do { + __asm__ __volatile__("mfspr %0, 269" : "=r"(hi)); + __asm__ __volatile__("mfspr %0, 268" : "=r"(lo)); + __asm__ __volatile__("mfspr %0, 269" : "=r"(hi2)); + } while (hi != hi2); + return ((uint64_t)hi << 32) | lo; +} + +static uint64_t now_ms(void) +{ + return (uint64_t)(read_tb() / (TIMEBASE_HZ / 1000ULL)); +} +#endif + +/* wolfIP needs an RNG (TCP ISN, DHCP xid, ephemeral ports). LFSR seeded + * from the free-running timebase. NOT cryptographic. */ +uint32_t wolfIP_getrandom(void) +{ + static uint32_t lfsr; + if (lfsr == 0) + lfsr = (uint32_t)read_tb() | 1U; + lfsr ^= (uint32_t)read_tb(); + lfsr ^= lfsr << 13; + lfsr ^= lfsr >> 17; + lfsr ^= lfsr << 5; + return lfsr; +} + +#define DHCP_TIMEOUT_MS 30000ULL + +#if defined(WOLFIP_NETDEV_SET_LOG) && defined(WOLFIP_DRIVER_LOG) +/* Driver bring-up stage logger (opt-in via -DWOLFIP_DRIVER_LOG). */ +static void wolfip_driver_log(const char *msg) +{ + wolfBoot_printf(" drv: %s\r\n", msg); +} +#endif + +/* Bring up the driver + wolfIP + DHCP. Returns the stack on a bound lease, + * NULL on failure. Prints PHY/link and the lease. */ +static struct wolfIP *wolfip_bringup(void) +{ + struct wolfIP *s = NULL; + struct wolfIP_ll_dev *ll; + uint64_t start, now; + ip4 ip = 0, nm = 0, gw = 0; + int rc, addr; + uint16_t id1, bsr; + + wolfIP_init_static(&s); + ll = wolfIP_getdev(s); + + wolfBoot_printf("wolfIP: bringing up %s\r\n", WOLFIP_NETDEV_NAME); +#if defined(WOLFIP_NETDEV_SET_LOG) && defined(WOLFIP_DRIVER_LOG) + /* Print each bring-up stage so a hang/fault is localized by the last + * marker (enable with -DWOLFIP_DRIVER_LOG). */ + WOLFIP_NETDEV_SET_LOG(wolfip_driver_log); +#endif + rc = WOLFIP_NETDEV_INIT(ll, NULL); + if (rc < 0) { + wolfBoot_printf("wolfIP: netdev init failed rc=%d\r\n", rc); + return NULL; + } + addr = WOLFIP_NETDEV_PHY_ADDR(); + id1 = WOLFIP_NETDEV_PHY_READ(0x02); + bsr = WOLFIP_NETDEV_PHY_READ(0x01); + wolfBoot_printf("wolfIP: PHY addr=%d ID1=0x%x BSR=0x%x link=%s\r\n", + addr, id1, bsr, WOLFIP_NETDEV_LINK_UP() ? "UP" : "down"); + +#if defined(WOLFIP_PHY_SCAN) && defined(WOLFIP_NETDEV_PHY_READ_AT) + /* MDIO bus scan: print ID1/ID2/BSR for every clause-22 address to identify + * the cabled port (BSR bit 2 = link up). Diagnostic; -DWOLFIP_PHY_SCAN. */ + { + int a; + uint16_t i1, i2, bs; + wolfBoot_printf("wolfIP: MDIO scan (addr: ID1 ID2 BSR link)\r\n"); + for (a = 0; a < 32; a++) { + i1 = WOLFIP_NETDEV_PHY_READ_AT((uint8_t)a, 0x02); + if (i1 == 0xFFFFU || i1 == 0x0000U) + continue; /* no PHY at this address */ + i2 = WOLFIP_NETDEV_PHY_READ_AT((uint8_t)a, 0x03); + bs = WOLFIP_NETDEV_PHY_READ_AT((uint8_t)a, 0x01); + wolfBoot_printf(" %d: 0x%x 0x%x 0x%x %s\r\n", + a, i1, i2, bs, (bs & 0x0004U) ? "UP" : "down"); + } + } +#endif + + (void)wolfIP_poll(s, now_ms()); + wolfBoot_printf("wolfIP: starting DHCP...\r\n"); + (void)dhcp_client_init(s); + + start = now_ms(); + for (;;) { + now = now_ms(); + (void)wolfIP_poll(s, now); + if (dhcp_bound(s)) { + wolfIP_ipconfig_get(s, &ip, &nm, &gw); + wolfBoot_printf("wolfIP: DHCP bound ip=%u.%u.%u.%u gw=%u.%u.%u.%u\r\n", + (unsigned)((ip >> 24) & 0xFF), (unsigned)((ip >> 16) & 0xFF), + (unsigned)((ip >> 8) & 0xFF), (unsigned)(ip & 0xFF), + (unsigned)((gw >> 24) & 0xFF), (unsigned)((gw >> 16) & 0xFF), + (unsigned)((gw >> 8) & 0xFF), (unsigned)(gw & 0xFF)); + return s; + } + if ((now - start) > DHCP_TIMEOUT_MS) { + wolfBoot_printf("wolfIP: DHCP timed out\r\n"); + return NULL; + } + } +} + +#ifdef WOLFBOOT_TEST_TFTP +/* ---- TFTP RRQ fetch into a RAM buffer + integrity check ------------- */ +#define TFTP_RAM_MAX (128U * 1024U) +#define TFTP_LOCAL_PORT 6900U + +struct tftp_ram_ctx { + uint8_t buf[TFTP_RAM_MAX]; + uint32_t len; /* highest offset+len written */ + uint32_t sum; /* additive 32-bit checksum of all bytes */ + int overflow; +}; +static struct tftp_ram_ctx g_tftp; + +struct tftp_glue { struct wolfIP *s; int sock; }; + +/* Zero a local struct without pulling in a libc memset (freestanding app). */ +static void zmem(void *p, unsigned int n) +{ + unsigned int i; + for (i = 0; i < n; i++) + ((uint8_t *)p)[i] = 0; +} + +static int tftp_io_open(void *arg, const char *name, int is_write, + uint32_t *size_hint, void **handle) +{ + (void)name; (void)is_write; (void)size_hint; + g_tftp.len = 0; + g_tftp.sum = 0; + g_tftp.overflow = 0; + *handle = &g_tftp; + return 0; +} + +static int tftp_io_write(void *arg, void *handle, uint32_t offset, + const uint8_t *buf, uint16_t len) +{ + struct tftp_ram_ctx *c = (struct tftp_ram_ctx *)handle; + uint16_t i; + (void)arg; + /* Overflow-safe bounds check: never compute offset+len (could wrap a + * 32-bit offset). Compare against the remaining space instead. */ + if (offset > TFTP_RAM_MAX || len > TFTP_RAM_MAX - offset) { + c->overflow = 1; + return -1; + } + for (i = 0; i < len; i++) { + c->buf[offset + i] = buf[i]; + c->sum += buf[i]; + } + if (offset + len > c->len) + c->len = offset + len; + return 0; +} + +static int tftp_send(void *arg, uint16_t local_port, + const struct wolftftp_endpoint *remote, const uint8_t *buf, uint16_t len) +{ + struct tftp_glue *g = (struct tftp_glue *)arg; + struct wolfIP_sockaddr_in dst; + int ret; + (void)local_port; + zmem(&dst, sizeof(dst)); + dst.sin_family = AF_INET; + dst.sin_port = ee16(remote->port); + dst.sin_addr.s_addr = ee32(remote->ip); + ret = wolfIP_sock_sendto(g->s, g->sock, buf, len, 0, + (struct wolfIP_sockaddr *)&dst, sizeof(dst)); + return (ret == (int)len) ? 0 : (ret < 0 ? ret : -1); +} + +static int run_tftp_fetch(struct wolfIP *s) +{ + struct tftp_glue glue; + struct wolftftp_transport_ops transport; + struct wolftftp_io_ops io; + struct wolftftp_transfer_cfg cfg; + struct wolftftp_client client; + struct wolftftp_endpoint srv; + struct wolfIP_sockaddr_in bind_addr; + uint8_t pkt[1500]; + int sock, ret = -1; + uint64_t deadline, now; + + sock = wolfIP_sock_socket(s, AF_INET, IPSTACK_SOCK_DGRAM, 0); + if (sock < 0) { + wolfBoot_printf("TFTP: udp socket failed\r\n"); + return -1; + } + zmem(&bind_addr, sizeof(bind_addr)); + bind_addr.sin_family = AF_INET; + bind_addr.sin_port = ee16(TFTP_LOCAL_PORT); + if (wolfIP_sock_bind(s, sock, (struct wolfIP_sockaddr *)&bind_addr, + sizeof(bind_addr)) < 0) { + wolfBoot_printf("TFTP: bind to local port %u failed\r\n", + TFTP_LOCAL_PORT); + goto out; + } + + glue.s = s; glue.sock = sock; + zmem(&transport, sizeof(transport)); + transport.send = tftp_send; + transport.arg = &glue; + zmem(&io, sizeof(io)); + io.open = tftp_io_open; + io.write = tftp_io_write; + io.arg = &g_tftp; + zmem(&cfg, sizeof(cfg)); + cfg.local_port = TFTP_LOCAL_PORT; + cfg.blksize = WOLFTFTP_DEFAULT_BLKSIZE; + cfg.timeout_s = WOLFTFTP_DEFAULT_TIMEOUT_S; + cfg.windowsize = 1; + cfg.max_retries = 5; + + wolftftp_client_init(&client, &transport, &io, &cfg); + zmem(&srv, sizeof(srv)); + srv.ip = atoip4(HOST_IP_STR); + srv.port = 69; + + wolfBoot_printf("TFTP: RRQ %s from %s\r\n", TFTP_FILENAME, HOST_IP_STR); + if (wolftftp_client_start_rrq(&client, &srv, TFTP_FILENAME) != 0) { + wolfBoot_printf("TFTP: start_rrq failed\r\n"); + goto out; + } + + now = now_ms(); + deadline = now + 20000ULL; + while (client.state != WOLFTFTP_CLIENT_COMPLETE && + client.state != WOLFTFTP_CLIENT_ERROR && + now < deadline) { + struct wolfIP_sockaddr_in rem; + uint32_t rlen; + int n; + (void)wolfIP_poll(s, now); + for (;;) { + rlen = sizeof(rem); + n = wolfIP_sock_recvfrom(s, sock, pkt, sizeof(pkt), 0, + (struct wolfIP_sockaddr *)&rem, &rlen); + if (n <= 0) + break; + { + struct wolftftp_endpoint rep; + rep.ip = ee32(rem.sin_addr.s_addr); + rep.port = ee16(rem.sin_port); + (void)wolftftp_client_receive(&client, TFTP_LOCAL_PORT, + &rep, pkt, (uint16_t)n); + } + } + (void)wolftftp_client_poll(&client, (uint32_t)now); + now = now_ms(); + } + + if (client.state != WOLFTFTP_CLIENT_COMPLETE) { + wolfBoot_printf("TFTP: FAILED (state=%d status=%d)\r\n", + client.state, client.last_status); + goto out; + } + if (g_tftp.overflow) { + wolfBoot_printf("TFTP: file exceeds %u byte RAM buffer\r\n", TFTP_RAM_MAX); + goto out; + } + wolfBoot_printf("TFTP: got %u bytes, checksum 0x%x\r\n", + (unsigned)g_tftp.len, (unsigned)g_tftp.sum); +#if (TFTP_EXPECT_SIZE != 0U) + if (g_tftp.len != (uint32_t)TFTP_EXPECT_SIZE) { + wolfBoot_printf("TFTP: size mismatch (expected %u)\r\n", + (unsigned)TFTP_EXPECT_SIZE); + goto out; + } +#endif +#if (TFTP_EXPECT_SUM != 0U) + if (g_tftp.sum != (uint32_t)TFTP_EXPECT_SUM) { + wolfBoot_printf("TFTP: checksum mismatch (expected 0x%x)\r\n", + (unsigned)TFTP_EXPECT_SUM); + goto out; + } +#endif + ret = 0; + +out: + (void)wolfIP_sock_close(s, sock); + return ret; +} +#endif /* WOLFBOOT_TEST_TFTP */ + +#ifdef WOLFIP_SPEED_TEST +/* ---- Benchmark: one-connection TCP throughput server on port 9 ------- * + * RX sinks everything the peer sends; TX pushes a chargen buffer whenever + * writable. Prints B/s on close. + * RX (board sinks): dd if=/dev/zero bs=1460 count=N | nc 9 + * TX (board sources): nc 9 /dev/null */ +#define SPEED_PORT 9 +static int speed_listen_fd = -1; +static int speed_client_fd = -1; +static uint64_t speed_rx_bytes, speed_tx_bytes, speed_start_ms; +static uint8_t speed_buf[1460]; + +static void speed_print(void) +{ + uint64_t ms = now_ms() - speed_start_ms; + if (ms == 0) ms = 1; + wolfBoot_printf("SPEED done %ums RX %u B (~%u B/s) TX %u B (~%u B/s)\r\n", + (unsigned)ms, (unsigned)speed_rx_bytes, + (unsigned)((speed_rx_bytes * 1000ULL) / ms), + (unsigned)speed_tx_bytes, + (unsigned)((speed_tx_bytes * 1000ULL) / ms)); +} + +static void speed_cb(int fd, uint16_t event, void *arg) +{ + struct wolfIP *s = (struct wolfIP *)arg; + int n, k; + + if (fd == speed_listen_fd) { + if (event & CB_EVENT_READABLE) { + int c = wolfIP_sock_accept(s, speed_listen_fd, NULL, NULL); + if (c >= 0) { + if (speed_client_fd >= 0) { + (void)wolfIP_sock_close(s, c); + } else { + speed_client_fd = c; + speed_rx_bytes = 0; speed_tx_bytes = 0; + speed_start_ms = now_ms(); + wolfIP_register_callback(s, c, speed_cb, s); + wolfBoot_printf("SPEED client connected\r\n"); + } + } + } + return; + } + if (fd != speed_client_fd) + return; + + if (event & CB_EVENT_READABLE) { + k = 0; + do { + n = wolfIP_sock_recvfrom(s, fd, speed_buf, sizeof(speed_buf), 0, + NULL, NULL); + if (n > 0) + speed_rx_bytes += (uint64_t)n; + } while (n > 0 && ++k < 32); + } + if (event & CB_EVENT_WRITABLE) { + k = 0; + do { + n = wolfIP_sock_send(s, fd, speed_buf, sizeof(speed_buf), 0); + if (n > 0) + speed_tx_bytes += (uint64_t)n; + } while (n > 0 && ++k < 32); + } + if (event & CB_EVENT_CLOSED) { + speed_print(); + (void)wolfIP_sock_close(s, fd); + speed_client_fd = -1; + } +} + +static int run_speed_server(struct wolfIP *s) +{ + struct wolfIP_sockaddr_in addr; + + speed_listen_fd = wolfIP_sock_socket(s, AF_INET, IPSTACK_SOCK_STREAM, 0); + if (speed_listen_fd < 0) { + wolfBoot_printf("SPEED: socket failed\r\n"); + return -1; + } + wolfIP_register_callback(s, speed_listen_fd, speed_cb, s); + zmem(&addr, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = ee16(SPEED_PORT); + if (wolfIP_sock_bind(s, speed_listen_fd, + (struct wolfIP_sockaddr *)&addr, sizeof(addr)) < 0) { + wolfBoot_printf("SPEED: bind to port %d failed\r\n", SPEED_PORT); + (void)wolfIP_sock_close(s, speed_listen_fd); + speed_listen_fd = -1; + return -1; + } + if (wolfIP_sock_listen(s, speed_listen_fd, 1) < 0) { + wolfBoot_printf("SPEED: listen on port %d failed\r\n", SPEED_PORT); + (void)wolfIP_sock_close(s, speed_listen_fd); + speed_listen_fd = -1; + return -1; + } + + wolfBoot_printf("SPEED: TCP throughput server on port %d. Drive from host:\r\n", + SPEED_PORT); + wolfBoot_printf(" RX: dd if=/dev/zero bs=1460 count=20000 | nc %d\r\n", + SPEED_PORT); + wolfBoot_printf(" TX: nc %d /dev/null\r\n", + SPEED_PORT); + for (;;) + (void)wolfIP_poll(s, now_ms()); + /* not reached */ +} +#endif /* WOLFIP_SPEED_TEST */ + +int wolfip_tftp_test_run(void) +{ + struct wolfIP *s = wolfip_bringup(); + if (s == NULL) + return -1; +#if defined(WOLFBOOT_TEST_TFTP) + return run_tftp_fetch(s); +#elif defined(WOLFIP_SPEED_TEST) + return run_speed_server(s); /* loops forever */ +#else + return 0; /* DHCP lease only, no transfer */ +#endif +} + +void wolfip_tftp_test_report(void) +{ + wolfBoot_printf("\r\nStarting wolfIP network test...\r\n"); + if (wolfip_tftp_test_run() == 0) + wolfBoot_printf("WOLFIP_TEST: PASS\r\n"); + else + wolfBoot_printf("WOLFIP_TEST: FAIL\r\n"); +} diff --git a/test-app/wolfip_tftp_test.h b/test-app/wolfip_tftp_test.h new file mode 100644 index 00000000..25b24a98 --- /dev/null +++ b/test-app/wolfip_tftp_test.h @@ -0,0 +1,35 @@ +/* wolfip_tftp_test.h + * + * Optional wolfIP network test entry for the wolfBoot test-app. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ +#ifndef WOLFIP_TFTP_TEST_H +#define WOLFIP_TFTP_TEST_H + +/* Run the wolfIP network test. Returns 0 on success, negative on failure. + * Without WOLFBOOT_TEST_TFTP this is a link/PHY bring-up smoke test; + * with WOLFBOOT_TEST_TFTP it performs a full TFTP fetch and verify. */ +int wolfip_tftp_test_run(void); + +/* Run the test and print the "Starting..." banner and PASS/FAIL result. + * Convenience wrapper so each target app's main() is a single call. */ +void wolfip_tftp_test_report(void); + +#endif /* WOLFIP_TFTP_TEST_H */