test-app: add wolfIP + wolfCrypt test harness and PPC crypto asm

pull/845/head
David Garske 2026-07-23 12:50:44 -07:00 committed by Daniele Lacamera
parent 4ab9891830
commit 29c9ea1ce1
5 changed files with 740 additions and 20 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 <stdint.h>
#include <stddef.h>
#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 <ip> 9
* TX (board sources): nc <ip> 9 </dev/null | pv -r >/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 <ip> %d\r\n",
SPEED_PORT);
wolfBoot_printf(" TX: nc <ip> %d </dev/null | pv -r >/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");
}

View File

@ -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 */