From 62a34e6476a78fdbd29241e6aa4784b18df3f2c2 Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 12 Aug 2026 15:03:15 -0700 Subject: [PATCH] Add wolfTPM fwTPM on AMD Spartan UltraScale+ SCU35 MicroBlaze V (ECC-only fits 192KB via SYSMON TRNG + per-command-group gating) --- README.md | 15 + Xilinx/fwtpm-scu35-microblazev/.gitignore | 27 ++ Xilinx/fwtpm-scu35-microblazev/README.md | 107 ++++++ .../firmware/bench/Makefile | 78 ++++ .../firmware/bench/main.c | 83 +++++ .../firmware/bench/mbv-bram.ld | 105 ++++++ .../firmware/bench/user_settings.h | 115 ++++++ .../firmware/common/mbv_time.c | 110 ++++++ .../firmware/common/mbv_time.h | 47 +++ .../firmware/common/mbv_uart.c | 98 +++++ .../firmware/common/mbv_uart.h | 48 +++ .../firmware/common/retarget.c | 179 +++++++++ .../firmware/common/scu35_board.h | 71 ++++ .../firmware/common/startup.S | 85 +++++ .../firmware/fwtpm-mbv/Makefile | 118 ++++++ .../firmware/fwtpm-mbv/fwtpm_clock_mbv.c | 63 ++++ .../firmware/fwtpm-mbv/fwtpm_nv_ram.c | 89 +++++ .../firmware/fwtpm-mbv/fwtpm_trng_sysmon.c | 121 +++++++ .../firmware/fwtpm-mbv/main.c | 340 ++++++++++++++++++ .../firmware/fwtpm-mbv/mbv-bram.ld | 108 ++++++ .../firmware/fwtpm-mbv/user_settings.h | 239 ++++++++++++ .../firmware/hello/Makefile | 63 ++++ .../firmware/hello/main.c | 103 ++++++ .../firmware/hello/mbv-bram.ld | 105 ++++++ Xilinx/fwtpm-scu35-microblazev/fpga/README.md | 127 +++++++ .../fpga/add_sysmon.tcl | 76 ++++ .../fpga/build_sysmon.tcl | 102 ++++++ 27 files changed, 2822 insertions(+) create mode 100644 Xilinx/fwtpm-scu35-microblazev/.gitignore create mode 100644 Xilinx/fwtpm-scu35-microblazev/README.md create mode 100644 Xilinx/fwtpm-scu35-microblazev/firmware/bench/Makefile create mode 100644 Xilinx/fwtpm-scu35-microblazev/firmware/bench/main.c create mode 100644 Xilinx/fwtpm-scu35-microblazev/firmware/bench/mbv-bram.ld create mode 100644 Xilinx/fwtpm-scu35-microblazev/firmware/bench/user_settings.h create mode 100644 Xilinx/fwtpm-scu35-microblazev/firmware/common/mbv_time.c create mode 100644 Xilinx/fwtpm-scu35-microblazev/firmware/common/mbv_time.h create mode 100644 Xilinx/fwtpm-scu35-microblazev/firmware/common/mbv_uart.c create mode 100644 Xilinx/fwtpm-scu35-microblazev/firmware/common/mbv_uart.h create mode 100644 Xilinx/fwtpm-scu35-microblazev/firmware/common/retarget.c create mode 100644 Xilinx/fwtpm-scu35-microblazev/firmware/common/scu35_board.h create mode 100644 Xilinx/fwtpm-scu35-microblazev/firmware/common/startup.S create mode 100644 Xilinx/fwtpm-scu35-microblazev/firmware/fwtpm-mbv/Makefile create mode 100644 Xilinx/fwtpm-scu35-microblazev/firmware/fwtpm-mbv/fwtpm_clock_mbv.c create mode 100644 Xilinx/fwtpm-scu35-microblazev/firmware/fwtpm-mbv/fwtpm_nv_ram.c create mode 100644 Xilinx/fwtpm-scu35-microblazev/firmware/fwtpm-mbv/fwtpm_trng_sysmon.c create mode 100644 Xilinx/fwtpm-scu35-microblazev/firmware/fwtpm-mbv/main.c create mode 100644 Xilinx/fwtpm-scu35-microblazev/firmware/fwtpm-mbv/mbv-bram.ld create mode 100644 Xilinx/fwtpm-scu35-microblazev/firmware/fwtpm-mbv/user_settings.h create mode 100644 Xilinx/fwtpm-scu35-microblazev/firmware/hello/Makefile create mode 100644 Xilinx/fwtpm-scu35-microblazev/firmware/hello/main.c create mode 100644 Xilinx/fwtpm-scu35-microblazev/firmware/hello/mbv-bram.ld create mode 100644 Xilinx/fwtpm-scu35-microblazev/fpga/README.md create mode 100644 Xilinx/fwtpm-scu35-microblazev/fpga/add_sysmon.tcl create mode 100644 Xilinx/fwtpm-scu35-microblazev/fpga/build_sysmon.tcl diff --git a/README.md b/README.md index fb9d0b2..f9033cb 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,21 @@ Firmware TPM 2.0 running bare-metal on the Zynq UltraScale+ MPSoC R5 RPU in lock-step mode. PetaLinux on the A53 APU acts as TPM client over OpenAMP RPMsg via Linux remoteproc. Persistent NV in QSPI flash. +## AMD Spartan UltraScale+ SCU35 fwTPM on a MicroBlaze V soft core + +See [Xilinx/fwtpm-scu35-microblazev](Xilinx/fwtpm-scu35-microblazev). + +Firmware TPM 2.0 on a MicroBlaze V (RISC-V rv32imc) soft core in the fabric of an +AMD Spartan UltraScale+ SCU35 Evaluation Kit (`xcsu35p`, a pure FPGA), served over +UART with the raw swtpm/mssim framing - the AMD analog of the PolarFire Mi-V +example. The full RSA+ECC fwTPM is ~652 KB and needs a larger device, but a +minimal ECC-only build (`FWTPM_TINY_ECC`) fits the stock 192 KB of block RAM +(no DDR on this part): ~190 KB via wolfTPM's per-command-group gates (the +individual `FWTPM_NO_*` macros, selected explicitly in `user_settings.h`) and an +on-die SYSMONE4 fabric TRNG (added by `fpga/add_sysmon.tcl`) in place of MemUse +entropy. Hardware-validated on the SCU35 - TPM2_Startup and TPM2_GetRandom pass and +GetRandom differs across cold boots, confirming real System-Monitor entropy. + ## Microchip PolarFire SoC fwTPM on a RISC-V hart (AMP) See [Microchip/fwtpm-polarfire-miv](Microchip/fwtpm-polarfire-miv). diff --git a/Xilinx/fwtpm-scu35-microblazev/.gitignore b/Xilinx/fwtpm-scu35-microblazev/.gitignore new file mode 100644 index 0000000..c516448 --- /dev/null +++ b/Xilinx/fwtpm-scu35-microblazev/.gitignore @@ -0,0 +1,27 @@ +# Build artifacts +build/ +*.o +*.d +*.elf +*.hex +*.bin +*.lst +*.map + +# Local FPGA project / bitstreams (not redistributed) +fpga/*/ +*.pdi +*.xsa +*.bit + +# Vivado run logs / journals (can land in any dir the tool is invoked from) +vivado*.log +vivado*.jou +.Xil/ + +# Local-only notes +*.local.md + +# Python +__pycache__/ +*.pyc diff --git a/Xilinx/fwtpm-scu35-microblazev/README.md b/Xilinx/fwtpm-scu35-microblazev/README.md new file mode 100644 index 0000000..8f288a8 --- /dev/null +++ b/Xilinx/fwtpm-scu35-microblazev/README.md @@ -0,0 +1,107 @@ +# fwTPM on AMD Spartan UltraScale+ SCU35 (MicroBlaze V soft core) + +Firmware TPM 2.0 (from [wolfTPM](https://github.com/wolfSSL/wolfTPM) `fwtpm`) on a **MicroBlaze V** (RISC-V rv32imc) soft core instantiated in the fabric of an AMD **Spartan UltraScale+ SCU35 Evaluation Kit** (`xcsu35p`) - a pure FPGA with no hardened CPU. It is the AMD analog of the PolarFire Mi-V example: the fwTPM server is driven from a host over UART with the same raw swtpm + Microsoft-simulator ("mssim") framing, so the stock wolfTPM swtpm client drives it unmodified. + +## Status + +The bare-metal platform HAL and a hello-world are **hardware-validated on the stock reference-design bitstream**. The **full** (RSA+ECC) fwTPM builds but is too large for this device. The **minimal ECC-only** fwTPM (`FWTPM_TINY_ECC`) is **hardware-validated on the SCU35**: ~190 KB, fits the stock 192 KB, and runs on a bitstream with the SYSMONE4 fabric TRNG added (`fpga/add_sysmon.tcl`) - TPM2_Startup and TPM2_GetRandom pass and GetRandom differs across cold boots, confirming real System-Monitor entropy. No memory enlargement is needed. + +| Item | Status | +|------|--------| +| MicroBlaze-V HAL (AXI UARTLite, AXI Timer, startup, retarget) | Hardware-validated (SCU35) | +| hello-world (banner + timer heartbeat) | Hardware-validated (SCU35): banner + 1 s heartbeat at 225 MHz | +| fwTPM over UART, full (RSA+ECC) | Builds (~652 KB); too large for the xcsu35p | +| fwTPM over UART, minimal ECC-only (`FWTPM_TINY_ECC`) | **Hardware-validated (SCU35)**: ~190 KB fits the 192 KB xcsu35p; boots on the SYSMON-TRNG bitstream, TPM2_Startup + TPM2_GetRandom pass, GetRandom differs across cold boots (real entropy) | +| SYSMONE4 fabric TRNG (System Management Wizard) | Hardware-validated: live temp/VCCINT/VCCAUX ADC codes with jittering LSBs seed the Hash-DRBG | +| Persistent NV in AXI QuadSPI | Planned | +| SRAM PUF | Planned (needs an uninitialized fabric-SRAM primitive in the design) | + +**Memory is tight but workable.** The `xcsu35p` has only **48 block-RAM primitives = 192 KB total**, no UltraRAM, and the SCU35 board has no external DDR. The **full** fwTPM (RSA+ECC, ~652 KB) does not fit. But the **minimal ECC-only** build (`FWTPM_TINY_ECC`) fits in ~190 KB, using: ECC-P256 only, table-free AES, no SHA-1, reduced TPM context/NV, an on-die **SYSMONE4 fabric TRNG** in place of wolfCrypt MemUse entropy (saves ~30 KB), and wolfTPM's finer per-command-group gating - the individual `FWTPM_NO_*` command-group macros, selected explicitly in `user_settings.h` (wolfTPM has no "minimal" umbrella; each gate is a deliberate choice) - compiling out the key-migration / ECDH / hash-command / context / symmetric-encrypt / clock command groups. The stock 192 KB BRAM is now sufficient; running it requires only that the bitstream instantiate the SYSMON (AXI System Management) IP for the TRNG - no memory enlargement is needed. See `fpga/README.md`. + +## Platform (from the SCU35 Zephyr TRD) + +- Core: MicroBlaze V, RISC-V **rv32imc** (M + C, no atomics/FPU), reset vector `0x00000000`, AXI clock **225 MHz**. +- Console: **AXI UARTLite** `axi_uartlite_0` @ `0x40600000`, fixed 115200 8N1. +- Time base: **AXI Timer** `axi_timer_0` @ `0x41C00000` (free-running 32-bit up-counter + software accumulator). +- NV flash (future): **AXI QuadSPI** `axi_quad_spi_0` @ `0x44A00000`. +- Debug: `mdm_riscv` (JTAG over the onboard FT4232H). + +## Layout + +``` +firmware/ + common/ shared bare-metal MicroBlaze-V HAL (wolfSSL-authored) + scu35_board.h address book (UARTLite, Timer, QSPI, clock, BRAM) + mbv_uart.c/.h AXI UARTLite console driver + mbv_time.c/.h AXI Timer time base + startup.S RV32 reset/startup (reset vector 0x0) + retarget.c newlib stubs (printf -> UART, _sbrk heap) + hello/ sanity image: banner + timer heartbeat (fits 192 KB) + fwtpm-mbv/ the fwTPM server (full build ~652 KB; FWTPM_TINY_ECC fits 192 KB) + main.c HAL registration + UART swtpm/mssim command loop + fwtpm_clock_mbv.c clock HAL (AXI Timer) + entropy hi-res timer + fwtpm_nv_ram.c volatile NV backend + fwtpm_trng_sysmon.c SYSMONE4 fabric TRNG seed source (-DFWTPM_TINY_HWTRNG) + user_settings.h wolfSSL + wolfTPM config (SP-32; MemUse or SYSMON entropy) + mbv-bram.ld linker (BRAM @ 0x0) + bench/ standalone wolfCrypt benchmark (no TPM), fits 192 KB + main.c UART/timer bring-up + current_time() + benchmark_test() + user_settings.h ECC-P256/SHA-256 config (mirrors the deployed fTPM) + Makefile builds wolfcrypt/benchmark bare-metal +fpga/ + README.md how to get/rebuild the bitstream + sizing analysis + add_sysmon.tcl overlay: add the SYSMONE4 AXI TRNG to the TRD block design + build_sysmon.tcl build driver: TRD + overlay -> synth/impl -> PDI +``` + +## Prerequisites + +- The Vitis 2025.x RISC-V bare-metal toolchain: `export PATH=/opt/Xilinx//gnu/riscv/lin/bin:$PATH` (the multilib `riscv64-unknown-elf-gcc` targets `riscv32-xilinx-elf`). +- wolfSSL and wolfTPM source trees as siblings of `wolftpm-examples` (default `../../../../../wolfssl`, `../../../../../wolftpm`). +- A programmed SCU35 bitstream and `hw_server`/`xsdb` for the JTAG load (see `fpga/README.md`). + +## Build + +```bash +export PATH=/opt/Xilinx/2025.2/gnu/riscv/lin/bin:$PATH + +cd firmware/hello && make # sanity image (scu35-hello.elf, fits 192 KB) +cd firmware/fwtpm-mbv && make # full fwTPM server (scu35-fwtpm.elf, ~652 KB) + +# minimal ECC-only fwTPM that fits the 192 KB xcsu35p (~190 KB); needs the +# SYSMON TRNG bitstream from fpga/add_sysmon.tcl: +cd firmware/fwtpm-mbv && make \ + EXTRA_CFLAGS="-DFWTPM_TINY_ECC -DFWTPM_TINY_PCR8 -DFWTPM_TINY_HWTRNG" \ + EXTRA_LDFLAGS="-Wl,--defsym=__heap_size=0x3000 -Wl,--defsym=__stack_size=0x2000" + +cd firmware/bench && make # wolfCrypt benchmark (scu35-bench.elf, ~191 KB) +``` + +## Run (JTAG load over the programmed bitstream) + +Program the bitstream with the Vivado Hardware Manager (`program_hw_devices` on the `xcsu35p`), then JTAG-load the ELF onto the MicroBlaze V via `xsdb` (`hw_server` running): `targets -set -filter {name == "Hart #0"}`, `dow scu35-*.elf`, `rwr pc ` (the ELF's `_start`), `con`. The console is the design's `serial1` = **`axi_uartlite_1` (0x40700000)**, wired to the SCU35 USB-UART; read it with `uart-monitor`. This is exactly how the hello image was validated. Drive the fwTPM from the host with the shared `swtpm_uart_bridge.py` / `fwtpm_uart_test.py` clients (see the Mi-V example's `host-client/`). + +## Performance (measured on hardware) + +Direct wolfCrypt benchmark from `firmware/bench`, run on the SCU35 MicroBlaze V soft core (RISC-V rv32imc @ 225 MHz, 32-bit portable-C SP math, `-O2`). The build mirrors the deployed fTPM's algorithm set (ECC-P256, SHA-256, no RSA). Symmetric/hash throughput uses `BENCH_EMBEDDED` 1 KB buffers, so it reflects per-call cost on a soft core, not a bulk-streaming rate. + +| Operation | Result | +|-----------|--------| +| ECC P-256 keygen | 8.98 ops/sec (111 ms) | +| ECDHE P-256 agree | 8.99 ops/sec (111 ms) | +| ECDSA P-256 sign | 8.06 ops/sec (124 ms) | +| ECDSA P-256 verify | 4.44 ops/sec (225 ms) | +| SHA-256 | 1.98 MiB/s | +| SHA-1 | 6.19 MiB/s | +| SHA3-256 | 515 KiB/s | +| HMAC-SHA256 | 1.96 MiB/s | +| AES-128-CBC | 50 KiB/s | +| AES-256-GCM | 34 KiB/s | +| RNG (SHA-256 DRBG) | 761 KiB/s | + +The benchmark image uses a deterministic bench-only RNG seed (not the SYSMON TRNG), so it runs on the stock bitstream. This measures raw wolfCrypt throughput on the core; end-to-end TPM command latency additionally includes the 115200-baud UART transport. + +## See also + +- `Microchip/miv-mpf300-splash` - fwTPM on a soft Mi-V RV32 core; the closest analog (same rv32 firmware shape). +- `Xilinx/fwtpm-zc702-a9` - fwTPM on the Zynq-7000 Cortex-A9 (with the wolfCrypt SRAM PUF). diff --git a/Xilinx/fwtpm-scu35-microblazev/firmware/bench/Makefile b/Xilinx/fwtpm-scu35-microblazev/firmware/bench/Makefile new file mode 100644 index 0000000..ab26b76 --- /dev/null +++ b/Xilinx/fwtpm-scu35-microblazev/firmware/bench/Makefile @@ -0,0 +1,78 @@ +# Makefile - SCU35 MicroBlaze V wolfCrypt benchmark +# +# Standalone wolfCrypt (no wolfTPM) benchmark image for the MicroBlaze V soft +# core. Measures raw core crypto throughput and reports over the AXI UARTLite. +# Requires the wolfSSL source tree as a sibling of wolftpm-examples (override +# with WOLFSSL_DIR=...). Uses the Vitis RISC-V bare-metal toolchain. +# +# export PATH=/opt/Xilinx/2025.2/gnu/riscv/lin/bin:$PATH +# +# Copyright (C) 2006-2026 wolfSSL Inc. GPLv2+ (see source headers). + +CROSS_COMPILE ?= riscv64-unknown-elf- +CC = $(CROSS_COMPILE)gcc +SIZE = $(CROSS_COMPILE)size + +WOLFSSL_DIR ?= ../../../../../wolfssl +COMMON_DIR = ../common +TARGET = scu35-bench + +ifeq ($(wildcard $(WOLFSSL_DIR)/wolfcrypt/src/aes.c),) +$(error wolfSSL sources not found at WOLFSSL_DIR=$(WOLFSSL_DIR) - set WOLFSSL_DIR) +endif + +ARCH ?= rv32imc_zicsr_zifencei +OPT ?= -O2 +EXTRA_CFLAGS ?= +EXTRA_LDFLAGS ?= + +ARCHFLAGS = -march=$(ARCH) -mabi=ilp32 -mcmodel=medany +CFLAGS = $(ARCHFLAGS) $(OPT) -g3 -MMD -MP -ffunction-sections -fdata-sections \ + -ffreestanding -DWOLFSSL_USER_SETTINGS \ + -I. -I$(COMMON_DIR) -I$(WOLFSSL_DIR) $(EXTRA_CFLAGS) +APP_WARN = -Wall -Wextra + +ASFLAGS = $(ARCHFLAGS) +LDSCRIPT = mbv-bram.ld +NOWARN_RWX := $(shell $(CC) -Wl,--no-warn-rwx-segments -Wl,--version >/dev/null 2>&1 && echo -Wl,--no-warn-rwx-segments) +# -u _printf_float pulls in newlib float printf (benchmark prints MB/s, ops/sec). +LDFLAGS = $(ARCHFLAGS) $(OPT) -T $(LDSCRIPT) -nostartfiles --specs=nosys.specs \ + -Wl,--gc-sections -Wl,-Map=$(TARGET).map -u _printf_float \ + $(NOWARN_RWX) $(EXTRA_LDFLAGS) + +WOLF_SRCS = $(wildcard $(WOLFSSL_DIR)/wolfcrypt/src/*.c) \ + $(WOLFSSL_DIR)/wolfcrypt/benchmark/benchmark.c +APP_SRCS = main.c \ + $(COMMON_DIR)/mbv_uart.c $(COMMON_DIR)/mbv_time.c \ + $(COMMON_DIR)/retarget.c +ASM_SRCS = $(COMMON_DIR)/startup.S + +BUILD = build +WOLF_OBJS = $(patsubst %.c,$(BUILD)/wolf/%.o,$(notdir $(WOLF_SRCS))) +APP_OBJS = $(patsubst %.c,$(BUILD)/%.o,$(notdir $(APP_SRCS))) \ + $(patsubst %.S,$(BUILD)/%.o,$(notdir $(ASM_SRCS))) + +VPATH = $(COMMON_DIR):$(WOLFSSL_DIR)/wolfcrypt/src:$(WOLFSSL_DIR)/wolfcrypt/benchmark + +all: $(TARGET).elf + $(SIZE) $(TARGET).elf + +$(BUILD)/wolf/%.o: %.c | $(BUILD)/wolf + $(CC) $(CFLAGS) -c $< -o $@ +$(BUILD)/%.o: %.c | $(BUILD) + $(CC) $(CFLAGS) $(APP_WARN) -c $< -o $@ +$(BUILD)/%.o: %.S | $(BUILD) + $(CC) $(ASFLAGS) -c $< -o $@ + +$(BUILD) $(BUILD)/wolf: + mkdir -p $@ + +$(TARGET).elf: $(APP_OBJS) $(WOLF_OBJS) $(LDSCRIPT) + $(CC) $(LDFLAGS) $(APP_OBJS) $(WOLF_OBJS) -o $@ + +clean: + rm -rf $(BUILD) $(TARGET).elf $(TARGET).map + +-include $(APP_OBJS:.o=.d) $(WOLF_OBJS:.o=.d) + +.PHONY: all clean diff --git a/Xilinx/fwtpm-scu35-microblazev/firmware/bench/main.c b/Xilinx/fwtpm-scu35-microblazev/firmware/bench/main.c new file mode 100644 index 0000000..bc67786 --- /dev/null +++ b/Xilinx/fwtpm-scu35-microblazev/firmware/bench/main.c @@ -0,0 +1,83 @@ +/* main.c + * + * wolfCrypt benchmark harness for the MicroBlaze V (RISC-V rv32imc) soft core on + * the AMD Spartan UltraScale+ SCU35. Brings up the console UART and the AXI + * Timer, provides the benchmark time source and a deterministic (bench-only) + * RNG seed, then runs wolfCrypt's benchmark_test() and reports over the UART. + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfTPM. + * + * wolfTPM 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. + * + * wolfTPM 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-1301, USA + */ + +#include +#include + +#include "scu35_board.h" +#include "mbv_uart.h" +#include "mbv_time.h" + +#include +#include + +extern int benchmark_test(void* args); + +/* Benchmark time source: fractional seconds from the free-running AXI Timer. + * mbv_ticks() is a 64-bit tick count at the AXI clock (SCU35_SYS_CLK_FREQ). */ +double current_time(int reset) +{ + (void)reset; + return (double)mbv_ticks() / (double)SCU35_SYS_CLK_FREQ; +} + +/* Deterministic bench-only RNG seed (an LCG). This is NOT an entropy source and + * must never be used to generate real keys; it only makes the benchmark's + * key-generation and DRBG paths run reproducibly. */ +int bench_seed(unsigned char* out, unsigned int sz) +{ + static uint32_t s = 0x2468ACE1u; + unsigned int i; + + for (i = 0; i < sz; i++) { + s = (s * 1103515245u) + 12345u; + out[i] = (unsigned char)(s >> 16); + } + return 0; +} + +int main(void) +{ + mbv_uart_init(SCU35_CONSOLE_UART_BASE); + mbv_timer_init(); + + printf("\r\n"); + printf("========================================================\r\n"); + printf(" wolfCrypt benchmark on AMD Spartan UltraScale+ SCU35\r\n"); + printf(" MicroBlaze V (RISC-V rv32imc) soft core @ %lu MHz\r\n", + (unsigned long)(SCU35_SYS_CLK_FREQ / 1000000UL)); + printf(" 32-bit portable-C SP math, ECC-only (no RSA)\r\n"); + printf("========================================================\r\n"); + + (void)wolfCrypt_Init(); + benchmark_test(NULL); + (void)wolfCrypt_Cleanup(); + + printf("=== benchmark complete ===\r\n"); + for (;;) { + } + return 0; +} diff --git a/Xilinx/fwtpm-scu35-microblazev/firmware/bench/mbv-bram.ld b/Xilinx/fwtpm-scu35-microblazev/firmware/bench/mbv-bram.ld new file mode 100644 index 0000000..dd3a64a --- /dev/null +++ b/Xilinx/fwtpm-scu35-microblazev/firmware/bench/mbv-bram.ld @@ -0,0 +1,105 @@ +/* mbv-bram.ld + * + * Linker script for the MicroBlaze V hello-world on the SCU35, linked into the + * local BRAM at the reset vector (0x00000000). The stock reference design + * provides 192 KB of local memory; the fwTPM needs a larger-memory bitstream. + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfTPM. + * + * wolfTPM 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. + */ + +OUTPUT_ARCH("riscv") +ENTRY(_start) + +__stack_size = DEFINED(__stack_size) ? __stack_size : 8K; +__heap_size = DEFINED(__heap_size) ? __heap_size : 16K; + +MEMORY +{ + ram (rwx) : ORIGIN = 0x00000000, LENGTH = 192K +} + +SECTIONS +{ + .entry : ALIGN(4) { KEEP (*(.entry)) } > ram + + .text : ALIGN(4) + { + *(.text .text.*) + *(.gnu.linkonce.t.*) + *(.rodata .rodata.*) + *(.srodata .srodata.*) + *(.gnu.linkonce.r.*) + . = ALIGN(4); + } > ram + + .preinit_array : ALIGN(4) + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } > ram + .init_array : ALIGN(4) + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + } > ram + .fini_array : ALIGN(4) + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + } > ram + + .data : ALIGN(4) + { + _data_start = .; + __global_pointer$ = . + 0x800; + *(.data .data.*) + *(.gnu.linkonce.d.*) + *(.sdata .sdata.*) + *(.sdata2 .sdata2.*) + . = ALIGN(4); + _data_end = .; + } > ram + _data_load = LOADADDR(.data); + + .bss (NOLOAD) : ALIGN(4) + { + _bss_start = .; + *(.sbss .sbss.*) + *(.gnu.linkonce.sb.*) + *(.bss .bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(4); + _bss_end = .; + } > ram + + . = ALIGN(8); + PROVIDE (end = .); + PROVIDE (_end = .); + + .heap (NOLOAD) : ALIGN(8) + { + . = . + __heap_size; + . = ALIGN(8); + _heap_end = .; + } > ram + + .stack (NOLOAD) : ALIGN(16) + { + . = . + __stack_size; + . = ALIGN(16); + _stack_top = .; + } > ram +} diff --git a/Xilinx/fwtpm-scu35-microblazev/firmware/bench/user_settings.h b/Xilinx/fwtpm-scu35-microblazev/firmware/bench/user_settings.h new file mode 100644 index 0000000..e25144d --- /dev/null +++ b/Xilinx/fwtpm-scu35-microblazev/firmware/bench/user_settings.h @@ -0,0 +1,115 @@ +/* user_settings.h + * + * wolfCrypt benchmark configuration for the MicroBlaze V (RISC-V rv32imc) soft + * core on the AMD Spartan UltraScale+ SCU35. Standalone wolfCrypt (no wolfTPM): + * measures raw core crypto throughput on the fabric soft core. Mirrors the + * ECC-only algorithm set of the fwTPM build (no RSA) so the numbers reflect what + * the deployed fTPM actually uses, plus the symmetric/hash primitives. + * + * The RNG seed here is a deterministic bench-only source (CUSTOM_RAND_GENERATE_ + * SEED); it is NOT an entropy source and must never be used for real keys. + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfTPM. + * + * wolfTPM 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. + * + * wolfTPM 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-1301, USA + */ + +#ifndef WOLFSSL_USER_SETTINGS_H +#define WOLFSSL_USER_SETTINGS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* ---- Platform (bare-metal, no OS/filesystem) ---- */ +#define WOLFCRYPT_ONLY +#define SINGLE_THREADED +#define WOLFSSL_SMALL_STACK +#define WOLFSSL_GENERAL_ALIGNMENT 4 +#define SIZEOF_LONG_LONG 8 +#define NO_FILESYSTEM +#define NO_WRITEV +#define NO_ASN_TIME +#define WOLFSSL_ASN_TEMPLATE +#define LITTLE_ENDIAN_ORDER + +/* ---- Benchmark driver ---- */ +#define BENCH_EMBEDDED /* small buffers, short run per algorithm */ +#define WOLFSSL_USER_CURRTIME /* we supply double current_time(int) */ +#define NO_MAIN_DRIVER /* benchmark_test() is called from main.c */ + +/* ---- Single-precision math (portable C, 32-bit) ---- */ +#define WOLFSSL_SP_MATH_ALL +#define WOLFSSL_SP_SMALL +#define SP_WORD_SIZE 32 +#define WOLFSSL_HAVE_SP_ECC +#define WOLFSSL_PUBLIC_MP +#define WOLFSSL_KEY_GEN + +/* ---- ECC P-256 only (keygen / ECDSA / ECDHE) - matches the deployed fTPM ---- */ +#define HAVE_ECC +#define ECC_USER_CURVES +#undef NO_ECC256 +#define ECC_SHAMIR +#define ECC_TIMING_RESISTANT +#define HAVE_ECC_KEY_EXPORT + +/* ---- AES (GCM / CBC / CTR / CMAC) ---- */ +#define HAVE_AESGCM +#define HAVE_AES_DECRYPT +#define WOLFSSL_AES_COUNTER +#define WOLFSSL_AES_CFB +#define WOLFSSL_AES_DIRECT +#define HAVE_AES_KEYWRAP +#define WOLFSSL_CMAC + +/* ---- Hashing: SHA-1, SHA-256, SHA-3 (matches the deployed fTPM: no SHA-384/512) ---- */ +#define WOLFSSL_SHA3 +#define HAVE_HKDF +#define HAVE_HMAC + +/* ---- RNG: Hash-DRBG seeded by a deterministic bench-only source ---- + * NOT entropy - bench only. See bench_seed() in main.c. */ +#define HAVE_HASHDRBG +#define WC_NO_RNG_SEED_FALLBACK +#define CUSTOM_RAND_GENERATE_SEED bench_seed +#ifndef __ASSEMBLER__ +extern int bench_seed(unsigned char* out, unsigned int sz); +#endif +#define NO_OLD_RNGNAME + +/* ---- Disabled (not benchmarked here) ---- */ +#define NO_RSA +#define NO_DSA +#define NO_DH +#define NO_OLD_TLS +#define NO_RC4 +#define NO_MD4 +#define NO_MD5 +#define NO_DES3 +#define NO_PSK +#define NO_PWDBASED +#define NO_PKCS12 +#define NO_SESSION_CACHE +#define WOLFSSL_NO_SHAKE128 +#define WOLFSSL_NO_SHAKE256 + +#ifdef __cplusplus +} +#endif + +#endif /* WOLFSSL_USER_SETTINGS_H */ diff --git a/Xilinx/fwtpm-scu35-microblazev/firmware/common/mbv_time.c b/Xilinx/fwtpm-scu35-microblazev/firmware/common/mbv_time.c new file mode 100644 index 0000000..39493b2 --- /dev/null +++ b/Xilinx/fwtpm-scu35-microblazev/firmware/common/mbv_time.c @@ -0,0 +1,110 @@ +/* mbv_time.c + * + * Timekeeping for the MicroBlaze V soft core using AXI Timer 0. + * + * AXI Timer register map (offsets from SCU35_TIMER0_BASE, PG079): + * 0x00 TCSR0 control/status + * 0x04 TLR0 load register + * 0x08 TCR0 current counter value (read for elapsed time) + * + * TCSR0 bits: UDT (bit1, 0 = count up), ARHT (bit4, auto-reload), LOAD (bit5), + * ENT (bit7, enable). We run timer 0 as a free-running 32-bit up-counter at the + * AXI clock and extend it to 64 bits with a software accumulator. At 225 MHz the + * 32-bit counter wraps about every 19 s, far longer than the hello heartbeat or + * any UART idle gap between TPM commands, so no wrap is dropped. + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfTPM. + * + * wolfTPM 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. + * + * wolfTPM 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-1301, USA + */ + +#include "mbv_time.h" +#include "scu35_board.h" + +#define TMR_TCSR0_OFFSET 0x00u +#define TMR_TLR0_OFFSET 0x04u +#define TMR_TCR0_OFFSET 0x08u + +#define TCSR_ARHT 0x00000010u /* auto reload/hold */ +#define TCSR_LOAD 0x00000020u /* load counter from TLR */ +#define TCSR_ENT 0x00000080u /* enable timer */ + +/* Software accumulator that extends the 32-bit up-counter to a monotonic 64-bit + * tick count. Updated on every mbv_ticks() call. */ +static uint64_t s_accum; +static uint32_t s_last; + +static volatile uint32_t* tmr_reg(uint32_t offset) +{ + return (volatile uint32_t*)(SCU35_TIMER0_BASE + offset); +} + +static uint32_t tmr_value(void) +{ + return *tmr_reg(TMR_TCR0_OFFSET); +} + +void mbv_timer_init(void) +{ + *tmr_reg(TMR_TCSR0_OFFSET) = 0u; /* disable */ + *tmr_reg(TMR_TLR0_OFFSET) = 0u; /* reload value 0 */ + *tmr_reg(TMR_TCSR0_OFFSET) = TCSR_LOAD; /* load 0 into counter */ + *tmr_reg(TMR_TCSR0_OFFSET) = TCSR_ENT | TCSR_ARHT; /* up, auto-reload, on */ + + s_last = tmr_value(); + s_accum = 0; +} + +uint64_t mbv_ticks(void) +{ + uint32_t now; + uint32_t delta; + + now = tmr_value(); + delta = (uint32_t)(now - s_last); /* up-counter: elapsed since s_last */ + s_last = now; + s_accum += (uint64_t)delta; + + return s_accum; +} + +uint64_t mbv_millis(void) +{ + return mbv_ticks() / (uint64_t)(SCU35_SYS_CLK_FREQ / 1000U); +} + +void mbv_delay_us(uint32_t us) +{ + uint32_t start = tmr_value(); + uint32_t ticks = (uint32_t)(((uint64_t)us * (uint64_t)SCU35_SYS_CLK_FREQ) + / 1000000U); + + while ((uint32_t)(tmr_value() - start) < ticks) { + /* busy wait */ + } +} + +void mbv_delay_ms(uint32_t ms) +{ + uint32_t start = tmr_value(); + uint32_t ticks = (uint32_t)(((uint64_t)ms * (uint64_t)SCU35_SYS_CLK_FREQ) + / 1000U); + + while ((uint32_t)(tmr_value() - start) < ticks) { + /* busy wait */ + } +} diff --git a/Xilinx/fwtpm-scu35-microblazev/firmware/common/mbv_time.h b/Xilinx/fwtpm-scu35-microblazev/firmware/common/mbv_time.h new file mode 100644 index 0000000..326acdc --- /dev/null +++ b/Xilinx/fwtpm-scu35-microblazev/firmware/common/mbv_time.h @@ -0,0 +1,47 @@ +/* mbv_time.h + * + * Timekeeping for the MicroBlaze V soft core using AXI Timer 0 as a free-running + * 32-bit up-counter, extended to 64 bits by a software accumulator. Call + * mbv_timer_init() once before using the clock or delays, and call mbv_ticks() + * (or mbv_millis()) at least once per counter wrap (~19 s at 225 MHz) to stay + * monotonic. + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfTPM. + * + * wolfTPM 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. + * + * wolfTPM 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-1301, USA + */ + +#ifndef MBV_TIME_H +#define MBV_TIME_H + +#include + +/* Configure AXI Timer 0 as a free-running up-counter. Call once at startup. */ +void mbv_timer_init(void); + +/* Monotonic 64-bit tick count at SCU35_SYS_CLK_FREQ. Software accumulator over + * the 32-bit counter: must be called at least once per counter wrap. */ +uint64_t mbv_ticks(void); + +/* Milliseconds since mbv_timer_init(). */ +uint64_t mbv_millis(void); + +/* Busy-wait delays. */ +void mbv_delay_ms(uint32_t ms); +void mbv_delay_us(uint32_t us); + +#endif /* MBV_TIME_H */ diff --git a/Xilinx/fwtpm-scu35-microblazev/firmware/common/mbv_uart.c b/Xilinx/fwtpm-scu35-microblazev/firmware/common/mbv_uart.c new file mode 100644 index 0000000..abc48b9 --- /dev/null +++ b/Xilinx/fwtpm-scu35-microblazev/firmware/common/mbv_uart.c @@ -0,0 +1,98 @@ +/* mbv_uart.c + * + * Minimal polled AXI UARTLite console driver for the MicroBlaze V soft core. + * + * AXI UARTLite register map (offsets from the controller base, PG142): + * 0x00 RX FIFO (read received byte) + * 0x04 TX FIFO (write byte to transmit) + * 0x08 STAT status + * 0x0C CTRL control (FIFO resets, interrupt enable) + * + * STAT bits: RX_VALID (bit0) = RX FIFO has data, TX_FULL (bit3) = TX FIFO full. + * CTRL bits: RST_TX (bit0), RST_RX (bit1). Baud is fixed by the bitstream. + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfTPM. + * + * wolfTPM 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. + * + * wolfTPM 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-1301, USA + */ + +#include "mbv_uart.h" + +#define UL_RX_FIFO 0x00u +#define UL_TX_FIFO 0x04u +#define UL_STAT 0x08u +#define UL_CTRL 0x0Cu + +#define UL_STAT_RX_VALID 0x00000001u +#define UL_STAT_TX_FULL 0x00000008u + +#define UL_CTRL_RST_TX 0x00000001u +#define UL_CTRL_RST_RX 0x00000002u + +static volatile uint32_t* ul_reg(uintptr_t base, uint32_t offset) +{ + return (volatile uint32_t*)(base + offset); +} + +void mbv_uart_init(uintptr_t base) +{ + /* Clear both FIFOs (the reset bits are self-clearing). */ + *ul_reg(base, UL_CTRL) = UL_CTRL_RST_TX | UL_CTRL_RST_RX; +} + +void mbv_uart_putc(uintptr_t base, char c) +{ + while ((*ul_reg(base, UL_STAT) & UL_STAT_TX_FULL) != 0u) { + /* wait for TX FIFO space */ + } + *ul_reg(base, UL_TX_FIFO) = (uint32_t)(uint8_t)c; +} + +void mbv_uart_write(uintptr_t base, const uint8_t* buf, size_t len) +{ + size_t i; + + if (buf == NULL) { + return; + } + for (i = 0; i < len; i++) { + mbv_uart_putc(base, (char)buf[i]); + } +} + +void mbv_uart_puts(uintptr_t base, const char* str) +{ + if (str == NULL) { + return; + } + while (*str != '\0') { + mbv_uart_putc(base, *str); + str++; + } +} + +int mbv_uart_getc(uintptr_t base, uint8_t* out) +{ + if (out == NULL) { + return 0; + } + if ((*ul_reg(base, UL_STAT) & UL_STAT_RX_VALID) == 0u) { + return 0; + } + *out = (uint8_t)(*ul_reg(base, UL_RX_FIFO) & 0xFFu); + return 1; +} diff --git a/Xilinx/fwtpm-scu35-microblazev/firmware/common/mbv_uart.h b/Xilinx/fwtpm-scu35-microblazev/firmware/common/mbv_uart.h new file mode 100644 index 0000000..c084271 --- /dev/null +++ b/Xilinx/fwtpm-scu35-microblazev/firmware/common/mbv_uart.h @@ -0,0 +1,48 @@ +/* mbv_uart.h + * + * Minimal polled AXI UARTLite console driver for the MicroBlaze V soft core on + * the SCU35. AXI UARTLite has a fixed baud rate set at bitstream build time + * (115200 here), so there is no runtime baud configuration. + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfTPM. + * + * wolfTPM 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. + * + * wolfTPM 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-1301, USA + */ + +#ifndef MBV_UART_H +#define MBV_UART_H + +#include +#include + +/* Reset the UARTLite RX/TX FIFOs. Baud is fixed by the bitstream. */ +void mbv_uart_init(uintptr_t base); + +/* Blocking single character transmit (waits for TX FIFO space). */ +void mbv_uart_putc(uintptr_t base, char c); + +/* Blocking write of len bytes. */ +void mbv_uart_write(uintptr_t base, const uint8_t* buf, size_t len); + +/* Blocking write of a NUL-terminated string. */ +void mbv_uart_puts(uintptr_t base, const char* str); + +/* Non-blocking receive: returns 1 and stores a byte in *out if one is + * available, else returns 0. */ +int mbv_uart_getc(uintptr_t base, uint8_t* out); + +#endif /* MBV_UART_H */ diff --git a/Xilinx/fwtpm-scu35-microblazev/firmware/common/retarget.c b/Xilinx/fwtpm-scu35-microblazev/firmware/common/retarget.c new file mode 100644 index 0000000..65c62fa --- /dev/null +++ b/Xilinx/fwtpm-scu35-microblazev/firmware/common/retarget.c @@ -0,0 +1,179 @@ +/* retarget.c + * + * Newlib bare-metal syscall stubs for the MicroBlaze V firmware. Routes stdout + * and stderr to the console AXI UARTLite and provides a simple _sbrk heap that + * grows from the linker 'end' symbol up to _heap_end. + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfTPM. + * + * wolfTPM 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. + * + * wolfTPM 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-1301, USA + */ + +#include +#include +#include +#include +#include +#include + +#include "scu35_board.h" +#include "mbv_uart.h" +#include "mbv_time.h" + +/* Linker-provided heap bounds. */ +extern char end[]; /* start of heap (after .bss) */ +extern char _heap_end[]; + +static char* heap_ptr = NULL; + +#ifndef SCU35_CONSOLE_UART_BASE +/* Fallback only; scu35_board.h defines this as UARTLITE1 (the USB-UART console, + * design serial1). Kept in sync so stdout never routes to the wrong UART. */ +#define SCU35_CONSOLE_UART_BASE SCU35_UARTLITE1_BASE +#endif + +int _write(int fd, const char* buf, int len) +{ + static char prev = 0; /* last byte emitted, kept across calls */ + int i; + + (void)fd; + if (buf == NULL) { + return -1; + } + for (i = 0; i < len; i++) { + /* Translate LF to CRLF, but do not double a CR the caller already sent. */ + if (buf[i] == '\n' && prev != '\r') { + mbv_uart_putc(SCU35_CONSOLE_UART_BASE, '\r'); + } + mbv_uart_putc(SCU35_CONSOLE_UART_BASE, buf[i]); + prev = buf[i]; + } + return len; +} + +int _read(int fd, char* buf, int len) +{ + int count = 0; + uint8_t c; + + (void)fd; + if (buf == NULL) { + return -1; + } + if (len <= 0) { + return (len == 0) ? 0 : -1; + } + while (count < len) { + if (mbv_uart_getc(SCU35_CONSOLE_UART_BASE, &c)) { + buf[count++] = (char)c; + if (c == '\n' || c == '\r') { + break; + } + } + else if (count > 0) { + break; + } + } + return count; +} + +void* _sbrk(ptrdiff_t incr) +{ + char* prev; + char* next; + + if (heap_ptr == NULL) { + heap_ptr = end; + } + next = heap_ptr + incr; + if (next < end || next > _heap_end) { + errno = ENOMEM; + return (void*)-1; + } + prev = heap_ptr; + heap_ptr = next; + return (void*)prev; +} + +int _close(int fd) +{ + (void)fd; + return -1; +} + +int _fstat(int fd, struct stat* st) +{ + (void)fd; + if (st == NULL) { + return -1; + } + st->st_mode = S_IFCHR; + return 0; +} + +int _isatty(int fd) +{ + (void)fd; + return 1; +} + +off_t _lseek(int fd, off_t offset, int whence) +{ + (void)fd; + (void)offset; + (void)whence; + return 0; +} + +int _getpid(void) +{ + return 1; +} + +int _kill(int pid, int sig) +{ + (void)pid; + (void)sig; + errno = EINVAL; + return -1; +} + +void _exit(int code) +{ + (void)code; + for (;;) { + /* park */ + } +} + +/* No RTC; report a monotonic time since boot (from AXI Timer) so the output + * object is always defined. Not wall-clock, so wolfCrypt also defines + * NO_ASN_TIME. */ +int _gettimeofday(struct timeval* tv, void* tz) +{ + uint64_t ms; + + (void)tz; + if (tv == NULL) { + return -1; + } + ms = mbv_millis(); + tv->tv_sec = (time_t)(ms / 1000U); + tv->tv_usec = (suseconds_t)((ms % 1000U) * 1000U); + return 0; +} diff --git a/Xilinx/fwtpm-scu35-microblazev/firmware/common/scu35_board.h b/Xilinx/fwtpm-scu35-microblazev/firmware/common/scu35_board.h new file mode 100644 index 0000000..5f92506 --- /dev/null +++ b/Xilinx/fwtpm-scu35-microblazev/firmware/common/scu35_board.h @@ -0,0 +1,71 @@ +/* scu35_board.h + * + * Board definition for a MicroBlaze V (RISC-V rv32imc) soft core on the AMD + * Spartan UltraScale+ SCU35 Evaluation Kit (xcsu35p). Addresses and the core + * clock match the AMD "SCU35 Zephyr RTOS IO" reference design used as the + * hardware platform (AXI UARTLite / AXI Timer / AXI QuadSPI on the MicroBlaze V + * AXI bus, local BRAM at the reset vector). If your bitstream uses different + * addresses, a different clock, or more local memory, override the values below. + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfTPM. + * + * wolfTPM 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. + * + * wolfTPM 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-1301, USA + */ + +#ifndef SCU35_BOARD_H +#define SCU35_BOARD_H + +#include + +/* MicroBlaze V AXI/core clock (Hz). The reference design runs the AXI domain at + * 225 MHz (axi_uartlite/axi_timer C_S_AXI_ACLK_FREQ_HZ). */ +#ifndef SCU35_SYS_CLK_FREQ +#define SCU35_SYS_CLK_FREQ 225000000UL +#endif + +/* AXI peripheral base addresses (from the reference design). */ +#ifndef SCU35_UARTLITE0_BASE +#define SCU35_UARTLITE0_BASE 0x40600000UL /* console */ +#endif +#ifndef SCU35_UARTLITE1_BASE +#define SCU35_UARTLITE1_BASE 0x40700000UL +#endif +#ifndef SCU35_TIMER0_BASE +#define SCU35_TIMER0_BASE 0x41C00000UL +#endif +#ifndef SCU35_QSPI0_BASE +#define SCU35_QSPI0_BASE 0x44A00000UL /* AXI QuadSPI (NV flash) */ +#endif + +/* Console UART is a fixed-baud AXI UARTLite (configured in the bitstream). On + * the SCU35 reference design the USB-UART is wired to UARTLite 1 (0x40700000) + * - it is the design's "serial1" console; UARTLite 0 goes to a header. */ +#ifndef SCU35_CONSOLE_UART_BASE +#define SCU35_CONSOLE_UART_BASE SCU35_UARTLITE1_BASE +#endif +#ifndef SCU35_CONSOLE_BAUD +#define SCU35_CONSOLE_BAUD 115200UL +#endif + +/* Local BRAM at the reset vector. The stock reference design provides 192 KB + * (0x00000000..0x0002FFFF), sized for its Zephyr image; the fwTPM needs a + * larger-memory bitstream (see fpga/README.md). */ +#ifndef SCU35_RAM_BASE +#define SCU35_RAM_BASE 0x00000000UL +#endif + +#endif /* SCU35_BOARD_H */ diff --git a/Xilinx/fwtpm-scu35-microblazev/firmware/common/startup.S b/Xilinx/fwtpm-scu35-microblazev/firmware/common/startup.S new file mode 100644 index 0000000..ca826f9 --- /dev/null +++ b/Xilinx/fwtpm-scu35-microblazev/firmware/common/startup.S @@ -0,0 +1,85 @@ +/* startup.S + * + * Minimal bare-metal RV32 startup for the MicroBlaze V soft core. The core reset + * vector is 0x00000000, where .entry (this _start) is placed by the linker. We + * set gp and sp, clear .bss, copy .data from its load address, install a trap + * handler, run the C library init array, and call main(). + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfTPM. + * + * wolfTPM 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. + * + * wolfTPM 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-1301, USA + */ + + .section .entry, "ax" + .globl _start + .type _start, @function +_start: + /* Global pointer (relaxation must be off across this load). */ + .option push + .option norelax + la gp, __global_pointer$ + .option pop + + /* Stack pointer. */ + la sp, _stack_top + + /* Zero the .bss / .sbss region. */ + la a0, _bss_start + la a1, _bss_end + bgeu a0, a1, 2f +1: + sw zero, 0(a0) + addi a0, a0, 4 + bltu a0, a1, 1b +2: + /* Copy initialized data from its load address to RAM. This is a no-op when + * the image is loaded (via JTAG) with LMA == VMA. */ + la a0, _data_load + la a1, _data_start + la a2, _data_end + bgeu a1, a2, 4f +3: + lw t0, 0(a0) + sw t0, 0(a1) + addi a0, a0, 4 + addi a1, a1, 4 + bltu a1, a2, 3b +4: + /* Install the trap handler (direct mode). */ + la t0, trap_entry + csrw mtvec, t0 + + /* Run C library constructors (init_array). */ + call __libc_init_array + + /* main(0, NULL). */ + li a0, 0 + li a1, 0 + call main + + /* main should not return; park the core if it does. */ +hang: + j hang + .size _start, . - _start + + .align 2 + .globl trap_entry + .type trap_entry, @function +trap_entry: + /* Minimal handler: park the core. */ + j trap_entry + .size trap_entry, . - trap_entry diff --git a/Xilinx/fwtpm-scu35-microblazev/firmware/fwtpm-mbv/Makefile b/Xilinx/fwtpm-scu35-microblazev/firmware/fwtpm-mbv/Makefile new file mode 100644 index 0000000..9572234 --- /dev/null +++ b/Xilinx/fwtpm-scu35-microblazev/firmware/fwtpm-mbv/Makefile @@ -0,0 +1,118 @@ +# Makefile - SCU35 MicroBlaze V wolfTPM fwTPM +# +# Builds the wolfTPM firmware TPM (fwTPM) served over the AXI UARTLite. Requires +# the wolfSSL and wolfTPM source trees as siblings of wolftpm-examples (override +# with WOLFSSL_DIR=... / WOLFTPM_DIR=...). Uses the Vitis RISC-V bare-metal +# toolchain (multilib riscv64-unknown-elf-gcc, targets riscv32-xilinx-elf). +# +# export PATH=/opt/Xilinx/2025.2/gnu/riscv/lin/bin:$PATH +# +# NOTE: the default (RSA+ECC) image is ~652 KB and needs a larger device. The +# minimal ECC-only build (EXTRA_CFLAGS="-DFWTPM_TINY_ECC -DFWTPM_TINY_PCR8 -DFWTPM_TINY_HWTRNG", +# reduced-heap link below) is ~190 KB and fits the stock 192 KB xcsu35p - see +# fpga/README.md. The SYSMON TRNG build needs the bitstream from add_sysmon.tcl. +# +# Copyright (C) 2006-2026 wolfSSL Inc. GPLv2+ (see source headers). + +CROSS_COMPILE ?= riscv64-unknown-elf- +CC = $(CROSS_COMPILE)gcc +OBJCOPY = $(CROSS_COMPILE)objcopy +SIZE = $(CROSS_COMPILE)size + +WOLFSSL_DIR ?= ../../../../../wolfssl +WOLFTPM_DIR ?= ../../../../../wolftpm +COMMON_DIR = ../common +TARGET = scu35-fwtpm + +ifeq ($(wildcard $(WOLFSSL_DIR)/wolfcrypt/src/aes.c),) +$(error wolfSSL sources not found at WOLFSSL_DIR=$(WOLFSSL_DIR) - set WOLFSSL_DIR to your wolfssl checkout) +endif +ifeq ($(wildcard $(WOLFTPM_DIR)/src/tpm2_packet.c),) +$(error wolfTPM sources not found at WOLFTPM_DIR=$(WOLFTPM_DIR) - set WOLFTPM_DIR to your wolftpm checkout) +endif + +ARCH ?= rv32imc_zicsr_zifencei +OPT ?= -Os +# Optional build flags (EXTRA_CFLAGS): +# -DFWTPM_ENABLE_PQC ECC + ML-DSA / ML-KEM instead of RSA (larger). +# -DFWTPM_TINY_ECC minimal ECC-P256-only fTPM: no RSA, no P-384, SHA-256 +# only (no SHA-1), table-free AES, reduced context slots, +# and the policy / attestation / credential / +# dictionary-attack / parameter-encryption command groups +# gated out. Also selects wolfTPM's finer per-command-group +# gates explicitly (key-migration / ECDH / hash-cmd / +# context / sym-encrypt / clock) in user_settings.h - no +# umbrella macro. Code drops from ~271 KB to ~144 KB. +# -DFWTPM_TINY_HWTRNG seed the DRBG from the fabric SYSMONE4 System Monitor +# (see fwtpm_trng_sysmon.c) instead of wolfCrypt MemUse +# entropy, removing ~30 KB. Requires the add_sysmon.tcl +# bitstream. This is what brings the image under 192 KB. +# -DFWTPM_TINY_PCR8 lower IMPLEMENTATION_PCR from 24 to 8, shrinking the PCR +# arrays (~2.7 KB). Needed for the fitting build below. +# +# The fitting build for the stock 192 KB xcsu35p (measured ~190 KB, ~1.5 KB +# spare) uses all three flags plus the reduced-heap link: +# make EXTRA_CFLAGS="-DFWTPM_TINY_ECC -DFWTPM_TINY_PCR8 -DFWTPM_TINY_HWTRNG" \ +# EXTRA_LDFLAGS="-Wl,--defsym=__heap_size=0x3000 -Wl,--defsym=__stack_size=0x2000" +EXTRA_CFLAGS ?= +EXTRA_LDFLAGS ?= + +ARCHFLAGS = -march=$(ARCH) -mabi=ilp32 -mcmodel=medany +CFLAGS = $(ARCHFLAGS) $(OPT) -g3 -MMD -MP -ffunction-sections -fdata-sections \ + -ffreestanding -DWOLFSSL_USER_SETTINGS -DWOLFTPM_USER_SETTINGS \ + -I. -I$(COMMON_DIR) -I$(WOLFSSL_DIR) -I$(WOLFTPM_DIR) \ + $(EXTRA_CFLAGS) +APP_WARN = -Wall -Wextra + +ASFLAGS = $(ARCHFLAGS) +LDSCRIPT = mbv-bram.ld +NOWARN_RWX := $(shell $(CC) -Wl,--no-warn-rwx-segments -Wl,--version >/dev/null 2>&1 && echo -Wl,--no-warn-rwx-segments) +LDFLAGS = $(ARCHFLAGS) $(OPT) -T $(LDSCRIPT) -nostartfiles --specs=nosys.specs \ + -Wl,--gc-sections -Wl,-Map=$(TARGET).map $(NOWARN_RWX) $(EXTRA_LDFLAGS) + +WOLF_SRCS = $(wildcard $(WOLFSSL_DIR)/wolfcrypt/src/*.c) +TPM_SRCS = $(WOLFTPM_DIR)/src/fwtpm/fwtpm.c \ + $(WOLFTPM_DIR)/src/fwtpm/fwtpm_command.c \ + $(WOLFTPM_DIR)/src/fwtpm/fwtpm_nv.c \ + $(WOLFTPM_DIR)/src/fwtpm/fwtpm_crypto.c \ + $(WOLFTPM_DIR)/src/tpm2_util.c \ + $(WOLFTPM_DIR)/src/tpm2_packet.c \ + $(WOLFTPM_DIR)/src/tpm2_param_enc.c \ + $(WOLFTPM_DIR)/src/tpm2_crypto.c +APP_SRCS = main.c fwtpm_nv_ram.c fwtpm_clock_mbv.c fwtpm_trng_sysmon.c \ + $(COMMON_DIR)/mbv_uart.c $(COMMON_DIR)/mbv_time.c \ + $(COMMON_DIR)/retarget.c +ASM_SRCS = $(COMMON_DIR)/startup.S + +BUILD = build +WOLF_OBJS = $(patsubst %.c,$(BUILD)/wolf/%.o,$(notdir $(WOLF_SRCS))) +TPM_OBJS = $(patsubst %.c,$(BUILD)/tpm/%.o,$(notdir $(TPM_SRCS))) +APP_OBJS = $(patsubst %.c,$(BUILD)/%.o,$(notdir $(APP_SRCS))) \ + $(patsubst %.S,$(BUILD)/%.o,$(notdir $(ASM_SRCS))) + +VPATH = $(COMMON_DIR):$(WOLFSSL_DIR)/wolfcrypt/src:$(WOLFTPM_DIR)/src:$(WOLFTPM_DIR)/src/fwtpm + +all: $(TARGET).elf + $(SIZE) $(TARGET).elf + +$(BUILD)/wolf/%.o: %.c | $(BUILD)/wolf + $(CC) $(CFLAGS) -c $< -o $@ +$(BUILD)/tpm/%.o: %.c | $(BUILD)/tpm + $(CC) $(CFLAGS) -c $< -o $@ +$(BUILD)/%.o: %.c | $(BUILD) + $(CC) $(CFLAGS) $(APP_WARN) -c $< -o $@ +$(BUILD)/%.o: %.S | $(BUILD) + $(CC) $(ASFLAGS) -c $< -o $@ + +$(BUILD) $(BUILD)/wolf $(BUILD)/tpm: + mkdir -p $@ + +$(TARGET).elf: $(APP_OBJS) $(TPM_OBJS) $(WOLF_OBJS) $(LDSCRIPT) + $(CC) $(LDFLAGS) $(APP_OBJS) $(TPM_OBJS) $(WOLF_OBJS) -o $@ + +clean: + rm -rf $(BUILD) $(TARGET).elf $(TARGET).map + +-include $(APP_OBJS:.o=.d) $(TPM_OBJS:.o=.d) $(WOLF_OBJS:.o=.d) + +.PHONY: all clean diff --git a/Xilinx/fwtpm-scu35-microblazev/firmware/fwtpm-mbv/fwtpm_clock_mbv.c b/Xilinx/fwtpm-scu35-microblazev/firmware/fwtpm-mbv/fwtpm_clock_mbv.c new file mode 100644 index 0000000..a3c8119 --- /dev/null +++ b/Xilinx/fwtpm-scu35-microblazev/firmware/fwtpm-mbv/fwtpm_clock_mbv.c @@ -0,0 +1,63 @@ +/* fwtpm_clock_mbv.c + * + * FWTPM_CLOCK_HAL implementation for the MicroBlaze V soft core (SCU35). Uses + * AXI Timer 0 for monotonic millisecond time, and the raw AXI Timer counter as + * the high-resolution timestamp that wolfCrypt's MemUse entropy source samples + * via CUSTOM_ENTROPY_TIMEHIRES. + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfTPM. + * + * wolfTPM 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. + * + * wolfTPM 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-1301, USA + */ + +#include "user_settings.h" + +#include + +#include + +#include "scu35_board.h" +#include "mbv_time.h" + +/* AXI Timer 0 counter register (TCR0), read directly for the entropy timer. */ +#define MBV_TCR0 (*(volatile uint32_t*)(SCU35_TIMER0_BASE + 0x08u)) + +static UINT64 fwtpm_mbv_get_ms(void* halCtx) +{ + (void)halCtx; + return (UINT64)mbv_millis(); +} + +int FWTPM_Clock_MBV_Init(FWTPM_CTX* ctx) +{ + mbv_timer_init(); + return FWTPM_Clock_SetHAL(ctx, fwtpm_mbv_get_ms, (void*)0); +} + +/* XSLEEP_MS shim (see user_settings.h). */ +void fwtpm_sleep_ms(unsigned int ms) +{ + mbv_delay_ms((uint32_t)ms); +} + +/* High-resolution time source for MemUse entropy: the free-running AXI Timer + * counter, advancing every AXI clock (~225 MHz). MemUse conditions this jitter + * through SHA3-256 and gates it behind SP800-90B health tests. */ +unsigned long long fwtpm_entropy_timer(void) +{ + return (unsigned long long)MBV_TCR0; +} diff --git a/Xilinx/fwtpm-scu35-microblazev/firmware/fwtpm-mbv/fwtpm_nv_ram.c b/Xilinx/fwtpm-scu35-microblazev/firmware/fwtpm-mbv/fwtpm_nv_ram.c new file mode 100644 index 0000000..a4ad965 --- /dev/null +++ b/Xilinx/fwtpm-scu35-microblazev/firmware/fwtpm-mbv/fwtpm_nv_ram.c @@ -0,0 +1,89 @@ +/* fwtpm_nv_ram.c + * + * Volatile RAM-backed FWTPM_NV_HAL for the MicroBlaze V fwTPM bring-up. A flat + * buffer (NV_RAM_SIZE: 8 KiB under FWTPM_TINY_ECC, 64 KiB otherwise) whose + * contents are lost across power cycles. The wolfTPM core owns the + * log-structured NV journal on top of this flat store. Persistent NV in the AXI + * QuadSPI flash is a follow-on (see README). + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfTPM. + * + * wolfTPM 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. + * + * wolfTPM 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-1301, USA + */ + +#include "user_settings.h" + +#include +#include + +#include +#include + +#ifdef FWTPM_TINY_ECC +#define NV_RAM_SIZE 0x2000U /* 8 KiB (tiny build: 2 small NV indices) */ +#else +#define NV_RAM_SIZE 0x10000U /* 64 KiB */ +#endif + +static uint8_t g_nv_ram[NV_RAM_SIZE]; + +static int nv_ram_in_bounds(word32 offset, word32 size) +{ + return (size <= NV_RAM_SIZE && offset <= NV_RAM_SIZE - size); +} + +static int nv_ram_read(void* halCtx, word32 offset, byte* buf, word32 size) +{ + (void)halCtx; + if (!nv_ram_in_bounds(offset, size)) { + return -1; + } + memcpy(buf, &g_nv_ram[offset], size); + return 0; +} + +static int nv_ram_write(void* halCtx, word32 offset, const byte* buf, + word32 size) +{ + (void)halCtx; + if (!nv_ram_in_bounds(offset, size)) { + return -1; + } + memcpy(&g_nv_ram[offset], buf, size); + return 0; +} + +static int nv_ram_erase(void* halCtx, word32 offset, word32 size) +{ + (void)halCtx; + if (!nv_ram_in_bounds(offset, size)) { + return -1; + } + memset(&g_nv_ram[offset], 0xFF, size); + return 0; +} + +int mbv_nv_ram_init(FWTPM_NV_HAL* hal) +{ + memset(g_nv_ram, 0xFF, sizeof(g_nv_ram)); + hal->read = nv_ram_read; + hal->write = nv_ram_write; + hal->erase = nv_ram_erase; + hal->ctx = NULL; + hal->maxSize = NV_RAM_SIZE; + return 0; +} diff --git a/Xilinx/fwtpm-scu35-microblazev/firmware/fwtpm-mbv/fwtpm_trng_sysmon.c b/Xilinx/fwtpm-scu35-microblazev/firmware/fwtpm-mbv/fwtpm_trng_sysmon.c new file mode 100644 index 0000000..ec2e3d8 --- /dev/null +++ b/Xilinx/fwtpm-scu35-microblazev/firmware/fwtpm-mbv/fwtpm_trng_sysmon.c @@ -0,0 +1,121 @@ +/* fwtpm_trng_sysmon.c + * + * FPGA hardware-entropy seed source for the MicroBlaze V fwTPM, using the + * Spartan UltraScale+ SYSMONE4 (System Monitor) as a noise source instead of + * wolfCrypt's MemUse entropy. Built with -DFWTPM_TINY_HWTRNG. + * + * The SYSMONE4 is a hardened on-die ADC that measures temperature and supply + * rails (VCCINT/VCCAUX/...). The low bits of its conversions carry thermal and + * electrical noise. Exposed to the MicroBlaze V through an AXI System Management + * Wizard slave, its DRP registers are read as memory-mapped words; this driver + * oversamples the noisy LSBs of several channels and mixes them to build a seed + * for wolfCrypt's Hash-DRBG (wired via CUSTOM_RAND_GENERATE_SEED). + * + * NOTE: this requires the FPGA design to instantiate the AXI System Management + * Wizard (SYSMONE4) at SCU35_SYSMON_BASE. For production use, condition and + * health-test the raw LSB stream (SP800-90B) before use; the DRBG provides the + * cryptographic expansion. + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfTPM. + * + * wolfTPM 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. + * + * wolfTPM 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-1301, USA + */ + +#include "user_settings.h" + +#ifdef FWTPM_TINY_HWTRNG + +#include +#include + +#include "scu35_board.h" +#include "mbv_time.h" + +/* AXI System Management Wizard base (SYSMONE4). Add the IP to the design and set + * this to its assigned AXI slot. */ +#ifndef SCU35_SYSMON_BASE +#define SCU35_SYSMON_BASE 0x44A30000UL +#endif + +/* AXI System Management Wizard register map: the on-chip measurement registers + * sit at fixed offsets 0x400 (temperature), 0x404 (VCCINT), 0x408 (VCCAUX) - + * 16-bit right-justified ADC codes in bits [15:4], whose low bits carry thermal + * and electrical noise. These offsets are HW-validated on the SCU35. */ +#define SYSMON_TEMP 0x400u /* on-chip temperature */ +#define SYSMON_VCCINT 0x404u /* VCCINT supply */ +#define SYSMON_VCCAUX 0x408u /* VCCAUX supply */ + +static uint32_t sysmon_rd(uint32_t off) +{ + return *(volatile uint32_t*)(uintptr_t)(SCU35_SYSMON_BASE + off); +} + +/* Fill out[0..sz) with seed bytes derived from the SYSMON ADC channels and the + * free-running AXI timer. The noisy ADC low bits are the entropy; each full + * 32-bit sample word is folded across the WHOLE request into a running 32-bit + * accumulator (rotate-left + xor the sample + FNV multiply mix), so state + * carries between output bytes instead of resetting, and the well-mixed high + * byte is emitted. A stuck-source health check fails closed (non-zero return) + * if the temperature channel never varies across the request, so a missing or + * wedged SYSMON cannot hand the DRBG a constant seed. + * + * NOTE: bring-up entropy source. For production, run the raw ADC stream through + * an SP800-90B-conditioned extractor with continuous health tests before use; + * the DRBG provides only the cryptographic expansion. Returns 0 on success. */ +int mbv_trng_seed(unsigned char* out, unsigned int sz) +{ + unsigned int i; + unsigned int j; + uint32_t acc = 0x811C9DC5u; /* nonzero FNV offset basis */ + uint32_t tmin = 0xFFFFFFFFu; + uint32_t tmax = 0u; + uint32_t temps = 0u; + + if (out == NULL) { + return -1; + } + for (i = 0; i < sz; i++) { + /* Oversample the rotating channels; fold each full sample word in. */ + for (j = 0; j < 16u; j++) { + uint32_t s; + switch (j & 0x3u) { + case 0: + s = sysmon_rd(SYSMON_TEMP); + if (s < tmin) { tmin = s; } + if (s > tmax) { tmax = s; } + temps++; + break; + case 1: s = sysmon_rd(SYSMON_VCCINT); break; + case 2: s = sysmon_rd(SYSMON_VCCAUX); break; + default: s = (uint32_t)mbv_ticks(); break; + } + acc = (acc << 1) | (acc >> 31); /* rotate-left 1 */ + acc ^= s; /* inject full sample word */ + acc *= 16777619u; /* FNV multiply mix (avalanche) */ + } + out[i] = (unsigned char)(acc >> 24); /* emit well-mixed high bits */ + } + /* Fail closed if the temperature ADC never moved across the whole request + * (SYSMON absent or stuck): never seed the DRBG from a constant source. A + * live monitor's low bits jitter read-to-read, so tmax > tmin holds. */ + if (temps > 1u && tmax == tmin) { + return -1; + } + return 0; +} + +#endif /* FWTPM_TINY_HWTRNG */ diff --git a/Xilinx/fwtpm-scu35-microblazev/firmware/fwtpm-mbv/main.c b/Xilinx/fwtpm-scu35-microblazev/firmware/fwtpm-mbv/main.c new file mode 100644 index 0000000..a2b272e --- /dev/null +++ b/Xilinx/fwtpm-scu35-microblazev/firmware/fwtpm-mbv/main.c @@ -0,0 +1,340 @@ +/* main.c + * + * wolfTPM firmware TPM (fwTPM) on a MicroBlaze V (RISC-V rv32imc) soft core + * (AMD Spartan UltraScale+ SCU35). Registers the NV / clock HALs, initializes + * the fwTPM engine, runs a small standalone self-test (TPM2_Startup -> + * TPM2_GetRandom), then serves TPM2 commands over the AXI UARTLite console using + * the same framing as the STM32H5, Mi-V and Zynq-7000 ports (raw swtpm + + * Microsoft-simulator "mssim"), so the wolfTPM swtpm client can drive it. + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfTPM. + * + * wolfTPM 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. + * + * wolfTPM 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-1301, USA + */ + +#include "user_settings.h" +#include +#include +#include + +#include +#include +#include + +#include "scu35_board.h" +#include "mbv_uart.h" +#include "mbv_time.h" + +/* Port HAL initializers (this example). */ +extern int mbv_nv_ram_init(FWTPM_NV_HAL* hal); +extern int FWTPM_Clock_MBV_Init(FWTPM_CTX* ctx); + +/* Static fwTPM context (large - keep off the stack). */ +static FWTPM_CTX g_ctx; + +/* mssim platform command codes (Microsoft TPM simulator protocol). */ +#define MSSIM_SIGNAL_POWER_ON 1 +#define MSSIM_SIGNAL_POWER_OFF 2 +#define MSSIM_SEND_COMMAND 8 +#define MSSIM_SIGNAL_RESET 17 +#define MSSIM_SESSION_END 20 +#define MSSIM_STOP 21 + +#define FWTPM_FRAME_TIMEOUT_MS 2000U + +static const uint8_t g_tpmRcFailure[10] = { + 0x80, 0x01, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x01, 0x01 +}; + +/* ---- raw UART byte transport over AXI UARTLite ---- */ +static int UartRecv(uint8_t* buf, uint32_t sz) +{ + uint32_t i; + for (i = 0; i < sz; i++) { + while (mbv_uart_getc(SCU35_CONSOLE_UART_BASE, &buf[i]) == 0) { + /* Service the software tick accumulator while blocking so idle time + * is not dropped from the TPM clock (32-bit counter wraps ~19 s). */ + (void)mbv_ticks(); + } + } + return 0; +} + +static int UartRecvTO(uint8_t* buf, uint32_t sz, uint32_t timeoutMs) +{ + uint32_t i; + /* Frame-level deadline: timeoutMs bounds the whole frame, not each byte, so + * a large declared length cannot stretch the wait to sz * timeoutMs. */ + uint64_t deadline = mbv_millis() + timeoutMs; + + for (i = 0; i < sz; i++) { + while (mbv_uart_getc(SCU35_CONSOLE_UART_BASE, &buf[i]) == 0) { + (void)mbv_ticks(); + if (mbv_millis() >= deadline) { + return -1; + } + } + } + return 0; +} + +static int UartSend(const uint8_t* buf, uint32_t sz) +{ + mbv_uart_write(SCU35_CONSOLE_UART_BASE, buf, sz); + return 0; +} + +static void UartDrain(uint32_t maxBytes) +{ + uint8_t b; + uint32_t got = 0U; + uint64_t idleStart = mbv_millis(); + + while (got < maxBytes) { + if (mbv_uart_getc(SCU35_CONSOLE_UART_BASE, &b) != 0) { + got++; + idleStart = mbv_millis(); + } + else if ((mbv_millis() - idleStart) > 10U) { + break; + } + } +} + +static uint32_t LoadU32BE(const uint8_t* p) +{ + return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | + ((uint32_t)p[2] << 8) | (uint32_t)p[3]; +} + +static void StoreU32BE(uint8_t* p, uint32_t v) +{ + p[0] = (uint8_t)(v >> 24); + p[1] = (uint8_t)(v >> 16); + p[2] = (uint8_t)(v >> 8); + p[3] = (uint8_t)v; +} + +static void UartSendAck(void) +{ + uint8_t ack[4] = { 0, 0, 0, 0 }; + UartSend(ack, 4); +} + +/* ---- standalone self-test ---- */ +static void FwTPM_SelfTest(FWTPM_CTX* ctx) +{ + static const uint8_t cmdStartup[] = { + 0x80, 0x01, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x01, 0x44, 0x00, 0x00 + }; + static const uint8_t cmdGetRandom[] = { + 0x80, 0x01, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x01, 0x7B, 0x00, 0x10 + }; + int rspSize; + uint32_t rc; + int i; + + printf("Self-test:\r\n"); + + rspSize = FWTPM_MAX_COMMAND_SIZE; + FWTPM_ProcessCommand(ctx, cmdStartup, (int)sizeof(cmdStartup), + ctx->rspBuf, &rspSize, 0); + rc = (rspSize >= 10) ? LoadU32BE(ctx->rspBuf + 6) : 0xFFFFFFFFu; + printf(" TPM2_Startup rc=0x%08lX %s\r\n", (unsigned long)rc, + (rc == 0) ? "OK" : "FAIL"); + + rspSize = FWTPM_MAX_COMMAND_SIZE; + FWTPM_ProcessCommand(ctx, cmdGetRandom, (int)sizeof(cmdGetRandom), + ctx->rspBuf, &rspSize, 0); + rc = (rspSize >= 10) ? LoadU32BE(ctx->rspBuf + 6) : 0xFFFFFFFFu; + printf(" TPM2_GetRandom rc=0x%08lX %s", (unsigned long)rc, + (rc == 0) ? "OK bytes=" : "FAIL"); + if (rc == 0 && rspSize >= 12) { + int n = (int)(((uint16_t)ctx->rspBuf[10] << 8) | ctx->rspBuf[11]); + for (i = 0; i < n && (12 + i) < rspSize; i++) { + printf("%02X", ctx->rspBuf[12 + i]); + } + } + printf("\r\n"); +} + +/* ---- TPM2 command server over UART (raw swtpm + mssim framing) ---- */ +static void FwTPM_UartCommandLoop(FWTPM_CTX* ctx) +{ + uint8_t hdr[4]; + uint16_t tag; + uint32_t mssimCmd; + uint8_t locality; + uint32_t cmdSize, remaining; + int rspSize; + uint32_t rspSzOut; + uint8_t rspHdr[4]; + + for (;;) { + if (UartRecv(hdr, 4) != 0) { + continue; + } + + tag = ((uint16_t)hdr[0] << 8) | (uint16_t)hdr[1]; + if (tag == 0x8001 || tag == 0x8002) { + memcpy(ctx->cmdBuf, hdr, 4); + if (UartRecvTO(ctx->cmdBuf + 4, 6, FWTPM_FRAME_TIMEOUT_MS) != 0) { + UartSend(g_tpmRcFailure, sizeof(g_tpmRcFailure)); + continue; + } + cmdSize = LoadU32BE(ctx->cmdBuf + 2); + if (cmdSize < 10 || cmdSize > FWTPM_MAX_COMMAND_SIZE) { + if (cmdSize > 10U) { + UartDrain(cmdSize - 10U); + } + UartSend(g_tpmRcFailure, sizeof(g_tpmRcFailure)); + continue; + } + remaining = cmdSize - 10; + if (remaining > 0) { + if (UartRecvTO(ctx->cmdBuf + 10, remaining, + FWTPM_FRAME_TIMEOUT_MS) != 0) { + UartSend(g_tpmRcFailure, sizeof(g_tpmRcFailure)); + continue; + } + } + rspSize = FWTPM_MAX_COMMAND_SIZE; + FWTPM_ProcessCommand(ctx, ctx->cmdBuf, (int)cmdSize, + ctx->rspBuf, &rspSize, 0); + if (rspSize > 0) { + UartSend(ctx->rspBuf, (uint32_t)rspSize); + } + else { + UartSend(g_tpmRcFailure, sizeof(g_tpmRcFailure)); + } + continue; + } + + mssimCmd = LoadU32BE(hdr); + if (mssimCmd == MSSIM_SESSION_END) { + continue; + } + if (mssimCmd == MSSIM_STOP) { + UartSendAck(); + return; + } + if (mssimCmd == MSSIM_SIGNAL_POWER_ON) { + ctx->powerOn = 1; + UartSendAck(); + continue; + } + if (mssimCmd == MSSIM_SIGNAL_POWER_OFF) { + ctx->powerOn = 0; + ctx->wasStarted = 0; + UartSendAck(); + continue; + } + if (mssimCmd == MSSIM_SIGNAL_RESET) { + ctx->wasStarted = 0; + UartSendAck(); + continue; + } + if (mssimCmd != MSSIM_SEND_COMMAND) { + UartSendAck(); + continue; + } + + if (UartRecvTO(&locality, 1, FWTPM_FRAME_TIMEOUT_MS) != 0) { + StoreU32BE(rspHdr, 0); + UartSend(rspHdr, 4); + UartSendAck(); + continue; + } + if (UartRecvTO(hdr, 4, FWTPM_FRAME_TIMEOUT_MS) != 0) { + StoreU32BE(rspHdr, 0); + UartSend(rspHdr, 4); + UartSendAck(); + continue; + } + cmdSize = LoadU32BE(hdr); + if (cmdSize == 0 || cmdSize > FWTPM_MAX_COMMAND_SIZE) { + if (cmdSize > 0U) { + UartDrain(cmdSize); + } + StoreU32BE(rspHdr, 0); + UartSend(rspHdr, 4); + UartSendAck(); + continue; + } + if (UartRecvTO(ctx->cmdBuf, cmdSize, FWTPM_FRAME_TIMEOUT_MS) != 0) { + StoreU32BE(rspHdr, 0); + UartSend(rspHdr, 4); + UartSendAck(); + continue; + } + + rspSize = FWTPM_MAX_COMMAND_SIZE; + FWTPM_ProcessCommand(ctx, ctx->cmdBuf, (int)cmdSize, + ctx->rspBuf, &rspSize, (int)locality); + + rspSzOut = (rspSize > 0) ? (uint32_t)rspSize : 0; + StoreU32BE(rspHdr, rspSzOut); + UartSend(rspHdr, 4); + if (rspSzOut != 0) { + UartSend(ctx->rspBuf, rspSzOut); + } + UartSendAck(); + } +} + +int main(void) +{ + FWTPM_CTX* ctx = &g_ctx; + FWTPM_NV_HAL nvHal; + int rc; + + mbv_uart_init(SCU35_CONSOLE_UART_BASE); + + printf("\r\n========================================================\r\n"); + printf(" wolfTPM fwTPM on AMD Spartan UltraScale+ SCU35\r\n"); + printf(" MicroBlaze V (RISC-V rv32imc) soft core, over UART\r\n"); + printf("========================================================\r\n"); + + memset(ctx, 0, sizeof(*ctx)); + memset(&nvHal, 0, sizeof(nvHal)); + + rc = mbv_nv_ram_init(&nvHal); + if (rc == 0) { + rc = FWTPM_NV_SetHAL(ctx, &nvHal); + } + if (rc == 0) { + rc = FWTPM_Clock_MBV_Init(ctx); + } + if (rc == 0) { + rc = FWTPM_Init(ctx); + } + if (rc != 0) { + printf("fwTPM init failed: %d\r\n", rc); + for (;;) { } + } + printf("fwTPM %s initialized (CTX %u bytes)\r\n", + FWTPM_GetVersionString(), (unsigned int)sizeof(FWTPM_CTX)); + + FwTPM_SelfTest(ctx); + + printf("Serving TPM2 over UART (swtpm/mssim). No more console output.\r\n"); + FwTPM_UartCommandLoop(ctx); + + FWTPM_Cleanup(ctx); + for (;;) { } + return 0; +} diff --git a/Xilinx/fwtpm-scu35-microblazev/firmware/fwtpm-mbv/mbv-bram.ld b/Xilinx/fwtpm-scu35-microblazev/firmware/fwtpm-mbv/mbv-bram.ld new file mode 100644 index 0000000..f3dcabe --- /dev/null +++ b/Xilinx/fwtpm-scu35-microblazev/firmware/fwtpm-mbv/mbv-bram.ld @@ -0,0 +1,108 @@ +/* mbv-bram.ld + * + * Linker script for the MicroBlaze V wolfTPM fwTPM on the SCU35, linked into the + * local BRAM at the reset vector (0x00000000). The RAM length here (768 KB) + * covers the fwTPM footprint (~652 KB); the stock reference design provides only + * 192 KB, + * so running this image requires a bitstream with enlarged local memory (see + * fpga/README.md). Size it to your bitstream's actual local-memory size. + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfTPM. + * + * wolfTPM 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. + */ + +OUTPUT_ARCH("riscv") +ENTRY(_start) + +__stack_size = DEFINED(__stack_size) ? __stack_size : 24K; +__heap_size = DEFINED(__heap_size) ? __heap_size : 128K; + +MEMORY +{ + ram (rwx) : ORIGIN = 0x00000000, LENGTH = 768K +} + +SECTIONS +{ + .entry : ALIGN(4) { KEEP (*(.entry)) } > ram + + .text : ALIGN(4) + { + *(.text .text.*) + *(.gnu.linkonce.t.*) + *(.rodata .rodata.*) + *(.srodata .srodata.*) + *(.gnu.linkonce.r.*) + . = ALIGN(4); + } > ram + + .preinit_array : ALIGN(4) + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } > ram + .init_array : ALIGN(4) + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + } > ram + .fini_array : ALIGN(4) + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + } > ram + + .data : ALIGN(4) + { + _data_start = .; + __global_pointer$ = . + 0x800; + *(.data .data.*) + *(.gnu.linkonce.d.*) + *(.sdata .sdata.*) + *(.sdata2 .sdata2.*) + . = ALIGN(4); + _data_end = .; + } > ram + _data_load = LOADADDR(.data); + + .bss (NOLOAD) : ALIGN(4) + { + _bss_start = .; + *(.sbss .sbss.*) + *(.gnu.linkonce.sb.*) + *(.bss .bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(4); + _bss_end = .; + } > ram + + . = ALIGN(8); + PROVIDE (end = .); + PROVIDE (_end = .); + + .heap (NOLOAD) : ALIGN(8) + { + . = . + __heap_size; + . = ALIGN(8); + _heap_end = .; + } > ram + + .stack (NOLOAD) : ALIGN(16) + { + . = . + __stack_size; + . = ALIGN(16); + _stack_top = .; + } > ram +} diff --git a/Xilinx/fwtpm-scu35-microblazev/firmware/fwtpm-mbv/user_settings.h b/Xilinx/fwtpm-scu35-microblazev/firmware/fwtpm-mbv/user_settings.h new file mode 100644 index 0000000..68371a9 --- /dev/null +++ b/Xilinx/fwtpm-scu35-microblazev/firmware/fwtpm-mbv/user_settings.h @@ -0,0 +1,239 @@ +/* user_settings.h + * + * Combined wolfSSL + wolfTPM settings for the wolfTPM firmware TPM (fwTPM) on a + * MicroBlaze V (RISC-V rv32imc) soft core on the AMD Spartan UltraScale+ SCU35. + * Modeled on the Mi-V and Zynq-7000 standalone-UART ports: raw swtpm/mssim + * transport over AXI UARTLite, 32-bit portable-C SP math, and a Hash-DRBG + * seeded either by wolfCrypt's MemUse entropy (default) or, with + * FWTPM_TINY_HWTRNG, by the on-die SYSMONE4 System Monitor read over AXI. + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfTPM. + * + * wolfTPM 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. + * + * wolfTPM 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-1301, USA + */ + +#ifndef WOLFSSL_USER_SETTINGS_H +#define WOLFSSL_USER_SETTINGS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* ---- wolfTPM fwTPM ---- */ +#define WOLFTPM_FWTPM +#define WOLFTPM_SMALL_STACK + +/* No POSIX sleep on bare metal; back XSLEEP_MS with the AXI Timer delay via a + * shim (fwtpm_sleep_ms -> mbv_delay_ms). */ +#ifndef __ASSEMBLER__ +extern void fwtpm_sleep_ms(unsigned int ms); +#endif +#define XSLEEP_MS(ms) fwtpm_sleep_ms((unsigned int)(ms)) + +/* ---- Platform (bare-metal, no OS/filesystem) ---- */ +#define WOLFCRYPT_ONLY +#define SINGLE_THREADED +#define WOLFSSL_SMALL_STACK +#define WOLFSSL_GENERAL_ALIGNMENT 4 +#define SIZEOF_LONG_LONG 8 +#define WOLFSSL_USER_IO +#define WOLFSSL_NO_SOCK +#define NO_FILESYSTEM +#define NO_MAIN_DRIVER +#define NO_WRITEV +#define NO_ASN_TIME /* no RTC */ +#define WOLFSSL_ASN_TEMPLATE +#define LITTLE_ENDIAN_ORDER + +/* ---- Single-precision math (portable C, 32-bit, arbitrary sizes for TPM) ---- */ +#define WOLFSSL_SP_MATH_ALL +#define WOLFSSL_SP_SMALL +#define SP_WORD_SIZE 32 +#ifndef FWTPM_TINY_ECC +#define WOLFSSL_SP_384 +#endif +#define WOLFSSL_HAVE_SP_ECC + +#define WOLFSSL_PUBLIC_MP +#define WOLFSSL_KEY_GEN + +/* ---- RSA vs PQC vs tiny-ECC (mutually exclusive) ---- + * FWTPM_TINY_ECC builds a minimal ECC-P256-only fTPM (no RSA, no P-384, SHA-256 + * only, reduced context slots) to explore fitting a small soft core. */ +#if defined(FWTPM_ENABLE_PQC) || defined(FWTPM_TINY_ECC) +#define NO_RSA +#else +#define WOLFSSL_HAVE_SP_RSA +#define WC_RSA_BLINDING +#define WC_RSA_PSS +#define WC_RSA_NO_PADDING +#endif + +/* ---- ECC P-256 (+ P-384 unless tiny) ---- */ +#define HAVE_ECC +#define ECC_USER_CURVES +#undef NO_ECC256 +#ifndef FWTPM_TINY_ECC +#define HAVE_ECC384 +#endif +#ifndef FWTPM_TINY_ECC +#define ECC_SHAMIR /* faster verify, larger code - drop for tiny */ +#endif +#define ECC_TIMING_RESISTANT +#define HAVE_ECC_KEY_EXPORT + +#ifdef FWTPM_TINY_ECC +/* Shrink the fwTPM context: fewer object/NV slots, smaller NV data and command + * buffers. ECC keys are small (FWTPM_MAX_PRIVKEY_DER=256 under NO_RSA). */ +#define FWTPM_MAX_COMMAND_SIZE 1024 /* ECC commands fit; halves cmd/rsp bufs */ +#define TPM_MAX_DIGEST_SIZE 32 /* SHA-256 max (no SHA-384/512) */ +#define FWTPM_MAX_OBJECTS 2 +#define FWTPM_MAX_PERSISTENT 2 +#define FWTPM_MAX_PRIMARY_CACHE 2 +#define FWTPM_MAX_SESSIONS 2 +#define FWTPM_MAX_HASH_SEQ 2 +#define FWTPM_MAX_SIGN_SEQ 2 +#define FWTPM_MAX_NV_INDICES 2 +#define FWTPM_MAX_NV_DATA 256 +/* IMPLEMENTATION_PCR defaults to 24 (TCG). A minimal fTPM can lower it to + * shrink the PCR arrays (~200 B/PCR/bank); override here if fewer PCRs are OK. */ +#ifdef FWTPM_TINY_PCR8 +#define IMPLEMENTATION_PCR 8 +#endif +/* Gate out fwTPM command groups the minimal build does not need (policy, + * attestation, credentials, dictionary-attack lockout, parameter encryption). */ +#define FWTPM_NO_POLICY +#define FWTPM_NO_ATTESTATION +#define FWTPM_NO_CREDENTIAL +#define FWTPM_NO_DA +#define FWTPM_NO_PARAM_ENC +/* wolfCrypt trims: no SHA-1 (SHA-256 PCR bank only), table-free AES core. + * AES itself cannot be removed - the fwTPM context-protection key and AES-GCM + * require it. */ +#define NO_SHA +#define WOLFSSL_AES_SMALL_TABLES +/* wolfTPM finer command gating: compile out the command groups a minimal ECC + * attestation + NV fTPM does not need. wolfTPM has no "minimal" umbrella macro + * on purpose - each gate removes real TPM functionality, so the set is selected + * explicitly here. Together with the five gates above, this is what brings the + * image under the 192 KB device, while retaining Startup / GetCapability / + * GetRandom / PCR / Create / Load / Sign / VerifySignature / NV / sessions. + * Comment out any line below to keep that command group (at a size cost). */ +#define FWTPM_NO_KEY_MIGRATION /* Import / Duplicate / Rewrap */ +#define FWTPM_NO_ECDH /* ECDH / EC_Ephemeral / ZGen / ECC_Parameters */ +#define FWTPM_NO_HASH_CMDS /* Hash / HMAC + hash sequence commands */ +#define FWTPM_NO_CONTEXT /* ContextSave / ContextLoad */ +#define FWTPM_NO_SYM_ENCRYPT /* EncryptDecrypt / EncryptDecrypt2 */ +#define FWTPM_NO_CLOCK /* ReadClock / ClockSet / ClockRateAdjust */ +#endif + +/* ---- AES ---- */ +#define HAVE_AESGCM +#define GCM_SMALL +#define HAVE_AES_DECRYPT +#define WOLFSSL_AES_CFB +#define WOLFSSL_AES_DIRECT +#define HAVE_AES_KEYWRAP +#define WOLFSSL_CMAC + +/* ---- Hashing ---- */ +#ifndef FWTPM_TINY_ECC +#define WOLFSSL_SHA384 /* needed by ECC P-384 */ +#define WOLFSSL_SHA512 +#endif +#ifndef FWTPM_TINY_HWTRNG +#define WOLFSSL_SHA3 /* required by the MemUse entropy conditioner (SHA3-256) */ +#endif +#define HAVE_HKDF +#define HAVE_HMAC + +/* ---- RNG: Hash-DRBG seeded by wolfCrypt MemUse entropy ---- + * The SCU35 fabric provides no hardware TRNG, so MemUse (memory-timing jitter + * conditioned through SHA3-256, gated fail-closed by SP800-90B health tests) + * seeds the DRBG. The high-resolution time source is the AXI Timer counter + * (fwtpm_entropy_timer in fwtpm_clock_mbv.c) via CUSTOM_ENTROPY_TIMEHIRES. */ +#define HAVE_HASHDRBG +#define WC_NO_RNG_SEED_FALLBACK +#ifdef FWTPM_TINY_HWTRNG +/* Seed the Hash-DRBG from an FPGA hardware entropy source (the SU35P SYSMONE4 + * System Monitor, read as an AXI peripheral) instead of MemUse. This removes the + * 16 KB MemUse state, the SHA3-256 conditioner and the health-test buffers + * (~22 KB RAM + ~6 KB code), and provides real electrical noise as entropy. + * mbv_trng_seed() is in fwtpm_trng_sysmon.c. */ +#define CUSTOM_RAND_GENERATE_SEED mbv_trng_seed +#ifndef __ASSEMBLER__ +extern int mbv_trng_seed(unsigned char* out, unsigned int sz); +#endif +#else +#define HAVE_ENTROPY_MEMUSE +#define ENTROPY_MEMUSE_FORCE_FAILURE +#ifdef FWTPM_TINY_ECC +/* Smallest in-bounds MemUse state: BITS=11 -> 2048 word64 = 16 KB (vs 64 KB). + * The index bound (255<<(BITS-8)) + (UPDATES-1)<<1 < 2^BITS caps UPDATES at 4. */ +#define ENTROPY_NUM_WORDS_BITS 11 +#define ENTROPY_NUM_UPDATES 4 +#else +#define ENTROPY_NUM_WORDS_BITS 13 +#define ENTROPY_NUM_UPDATES 16 +#endif +#endif +#define CUSTOM_ENTROPY_TIMEHIRES() fwtpm_entropy_timer() +#ifndef __ASSEMBLER__ +extern unsigned long long fwtpm_entropy_timer(void); +#endif +#define NO_OLD_RNGNAME + +/* ---- Disabled legacy/unused ---- */ +#define NO_OLD_TLS +#define NO_DSA +#define NO_DH +#define NO_RC4 +#define NO_MD4 +#define NO_MD5 +#define NO_DES3 +#define NO_PSK +#define NO_PWDBASED +#define NO_PKCS12 +#define NO_SESSION_CACHE + +#ifdef FWTPM_ENABLE_PQC +#define WOLFTPM_V185 +#define WOLFSSL_EXPERIMENTAL_SETTINGS +#define WOLFSSL_SHAKE128 +#define WOLFSSL_SHAKE256 +#define WOLFSSL_HAVE_MLDSA +#define WOLFSSL_WC_DILITHIUM +#define WOLFSSL_DILITHIUM_NO_LARGE_CODE +#define WOLFSSL_MLDSA_SIGN_SMALL_MEM +#define WOLFSSL_MLDSA_SIGN_SMALL_MEM_PRECALC +#define WOLFSSL_MLDSA_VERIFY_SMALL_MEM +#define WOLFSSL_MLDSA_MAKE_KEY_SMALL_MEM +#define WOLFSSL_HAVE_MLKEM +#define WOLFSSL_WC_MLKEM +#define WOLFSSL_MLKEM_SMALL +#define WOLFSSL_NO_ML_KEM_512 +#define WOLFSSL_NO_ML_KEM_1024 +#else +#define WOLFSSL_NO_SHAKE128 +#define WOLFSSL_NO_SHAKE256 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* WOLFSSL_USER_SETTINGS_H */ diff --git a/Xilinx/fwtpm-scu35-microblazev/firmware/hello/Makefile b/Xilinx/fwtpm-scu35-microblazev/firmware/hello/Makefile new file mode 100644 index 0000000..7af530b --- /dev/null +++ b/Xilinx/fwtpm-scu35-microblazev/firmware/hello/Makefile @@ -0,0 +1,63 @@ +# Makefile - SCU35 MicroBlaze V hello-world +# +# Bare-metal MicroBlaze V (RISC-V rv32imc) sanity image (banner + AXI Timer +# heartbeat) that proves the platform and the common HAL before the wolfTPM +# fwTPM build. Uses the Vitis RISC-V bare-metal toolchain. +# +# Toolchain: the Vitis RISC-V bare-metal GCC. Put its bin on PATH, e.g. +# export PATH=/opt/Xilinx/2025.2/gnu/riscv/lin/bin:$PATH +# The multilib driver riscv64-unknown-elf-gcc targets riscv32-xilinx-elf and +# builds the rv32imc multilib used here. +# +# Copyright (C) 2006-2026 wolfSSL Inc. GPLv2+ (see source headers). + +CROSS_COMPILE ?= riscv64-unknown-elf- +CC = $(CROSS_COMPILE)gcc +OBJCOPY = $(CROSS_COMPILE)objcopy +SIZE = $(CROSS_COMPILE)size + +COMMON_DIR = ../common +TARGET = scu35-hello + +ARCH ?= rv32imc_zicsr_zifencei +OPT ?= -O2 + +ARCHFLAGS = -march=$(ARCH) -mabi=ilp32 -mcmodel=medany +CFLAGS = $(ARCHFLAGS) $(OPT) -g3 -MMD -MP -ffunction-sections -fdata-sections \ + -ffreestanding -Wall -Wextra -I. -I$(COMMON_DIR) +ASFLAGS = $(ARCHFLAGS) +LDSCRIPT = mbv-bram.ld +NOWARN_RWX := $(shell $(CC) -Wl,--no-warn-rwx-segments -Wl,--version >/dev/null 2>&1 && echo -Wl,--no-warn-rwx-segments) +LDFLAGS = $(ARCHFLAGS) $(OPT) -T $(LDSCRIPT) -nostartfiles --specs=nosys.specs \ + -Wl,--gc-sections -Wl,-Map=$(TARGET).map $(NOWARN_RWX) + +APP_SRCS = main.c \ + $(COMMON_DIR)/mbv_uart.c $(COMMON_DIR)/mbv_time.c +ASM_SRCS = $(COMMON_DIR)/startup.S + +BUILD = build +APP_OBJS = $(patsubst %.c,$(BUILD)/%.o,$(notdir $(APP_SRCS))) \ + $(patsubst %.S,$(BUILD)/%.o,$(notdir $(ASM_SRCS))) + +VPATH = $(COMMON_DIR) + +all: $(TARGET).elf + $(SIZE) $(TARGET).elf + +$(BUILD)/%.o: %.c | $(BUILD) + $(CC) $(CFLAGS) -c $< -o $@ +$(BUILD)/%.o: %.S | $(BUILD) + $(CC) $(ASFLAGS) -c $< -o $@ + +$(BUILD): + mkdir -p $@ + +$(TARGET).elf: $(APP_OBJS) $(LDSCRIPT) + $(CC) $(LDFLAGS) $(APP_OBJS) -o $@ + +clean: + rm -rf $(BUILD) $(TARGET).elf $(TARGET).map + +-include $(APP_OBJS:.o=.d) + +.PHONY: all clean diff --git a/Xilinx/fwtpm-scu35-microblazev/firmware/hello/main.c b/Xilinx/fwtpm-scu35-microblazev/firmware/hello/main.c new file mode 100644 index 0000000..2c114d7 --- /dev/null +++ b/Xilinx/fwtpm-scu35-microblazev/firmware/hello/main.c @@ -0,0 +1,103 @@ +/* main.c + * + * Hello-world over the AXI UARTLite with an AXI Timer heartbeat on the + * MicroBlaze V soft core (SCU35). Proves the FPGA soft-core platform, the + * console UART and the timer before layering wolfCrypt and the wolfTPM fwTPM on + * top. This bring-up image fits the stock reference design's 192 KB BRAM; the + * fwTPM needs a larger-memory bitstream (see fpga/README.md). + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfTPM. + * + * wolfTPM 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. + * + * wolfTPM 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-1301, USA + */ + +#include + +#include "scu35_board.h" +#include "mbv_uart.h" +#include "mbv_time.h" + +static void uputs(const char* s) +{ + while (*s != '\0') { + mbv_uart_putc(SCU35_CONSOLE_UART_BASE, *s++); + } +} + +static void uputu(uint32_t v) +{ + char buf[10]; + int i = 0; + + if (v == 0u) { + mbv_uart_putc(SCU35_CONSOLE_UART_BASE, '0'); + return; + } + while (v > 0u) { + buf[i++] = (char)('0' + (v % 10u)); + v /= 10u; + } + while (i > 0) { + mbv_uart_putc(SCU35_CONSOLE_UART_BASE, buf[--i]); + } +} + +static void uputhex32(uint32_t v) +{ + static const char hexd[] = "0123456789ABCDEF"; + int i; + + for (i = 28; i >= 0; i -= 4) { + mbv_uart_putc(SCU35_CONSOLE_UART_BASE, hexd[(v >> i) & 0xFu]); + } +} + +int main(void) +{ + uint32_t counter = 0; + + mbv_uart_init(SCU35_CONSOLE_UART_BASE); + mbv_timer_init(); + + uputs("\r\n"); + uputs("========================================================\r\n"); + uputs(" wolfSSL / wolfTPM on AMD Spartan UltraScale+ SCU35\r\n"); + uputs(" MicroBlaze V (RISC-V rv32imc) soft core - Hello World\r\n"); + uputs("========================================================\r\n"); + uputs("Core clock : "); + uputu((uint32_t)SCU35_SYS_CLK_FREQ); + uputs(" Hz\r\n"); + uputs("Console : AXI UARTLite @ 0x"); + uputhex32((uint32_t)SCU35_CONSOLE_UART_BASE); + uputs(", "); + uputu((uint32_t)SCU35_CONSOLE_BAUD); + uputs(" 8N1\r\n"); + uputs("A heartbeat prints each second.\r\n\r\n"); + + for (;;) { + uputs("heartbeat "); + uputu(counter); + uputs(" (uptime "); + uputu((uint32_t)mbv_millis()); + uputs(" ms)\r\n"); + counter++; + + mbv_delay_ms(1000); + } + + return 0; +} diff --git a/Xilinx/fwtpm-scu35-microblazev/firmware/hello/mbv-bram.ld b/Xilinx/fwtpm-scu35-microblazev/firmware/hello/mbv-bram.ld new file mode 100644 index 0000000..dd3a64a --- /dev/null +++ b/Xilinx/fwtpm-scu35-microblazev/firmware/hello/mbv-bram.ld @@ -0,0 +1,105 @@ +/* mbv-bram.ld + * + * Linker script for the MicroBlaze V hello-world on the SCU35, linked into the + * local BRAM at the reset vector (0x00000000). The stock reference design + * provides 192 KB of local memory; the fwTPM needs a larger-memory bitstream. + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfTPM. + * + * wolfTPM 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. + */ + +OUTPUT_ARCH("riscv") +ENTRY(_start) + +__stack_size = DEFINED(__stack_size) ? __stack_size : 8K; +__heap_size = DEFINED(__heap_size) ? __heap_size : 16K; + +MEMORY +{ + ram (rwx) : ORIGIN = 0x00000000, LENGTH = 192K +} + +SECTIONS +{ + .entry : ALIGN(4) { KEEP (*(.entry)) } > ram + + .text : ALIGN(4) + { + *(.text .text.*) + *(.gnu.linkonce.t.*) + *(.rodata .rodata.*) + *(.srodata .srodata.*) + *(.gnu.linkonce.r.*) + . = ALIGN(4); + } > ram + + .preinit_array : ALIGN(4) + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } > ram + .init_array : ALIGN(4) + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + } > ram + .fini_array : ALIGN(4) + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + } > ram + + .data : ALIGN(4) + { + _data_start = .; + __global_pointer$ = . + 0x800; + *(.data .data.*) + *(.gnu.linkonce.d.*) + *(.sdata .sdata.*) + *(.sdata2 .sdata2.*) + . = ALIGN(4); + _data_end = .; + } > ram + _data_load = LOADADDR(.data); + + .bss (NOLOAD) : ALIGN(4) + { + _bss_start = .; + *(.sbss .sbss.*) + *(.gnu.linkonce.sb.*) + *(.bss .bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(4); + _bss_end = .; + } > ram + + . = ALIGN(8); + PROVIDE (end = .); + PROVIDE (_end = .); + + .heap (NOLOAD) : ALIGN(8) + { + . = . + __heap_size; + . = ALIGN(8); + _heap_end = .; + } > ram + + .stack (NOLOAD) : ALIGN(16) + { + . = . + __stack_size; + . = ALIGN(16); + _stack_top = .; + } > ram +} diff --git a/Xilinx/fwtpm-scu35-microblazev/fpga/README.md b/Xilinx/fwtpm-scu35-microblazev/fpga/README.md new file mode 100644 index 0000000..7406c00 --- /dev/null +++ b/Xilinx/fwtpm-scu35-microblazev/fpga/README.md @@ -0,0 +1,127 @@ +# SCU35 FPGA design for the MicroBlaze V fwTPM + +The firmware in this example runs on a MicroBlaze V system in the SCU35 fabric. The hardware platform is AMD's **"SCU35 Zephyr RTOS IO" Target Reference Design (TRD)**, which already instantiates everything the fwTPM needs: a MicroBlaze V (rv32imc) core, AXI UARTLite (console), AXI Timer, AXI QuadSPI, GPIO, and the `mdm_riscv` JTAG debug module. This example does not redistribute the bitstream; obtain the TRD from AMD. + +## Memory: the full fwTPM does not fit, but the minimal ECC build does + +The stock TRD gives the MicroBlaze V **192 KB** of local BRAM (`0x00000000-0x0002FFFF`), sized for its ~154 KB Zephyr image. The `xcsu35p` has only **48 block-RAM (RAMB36) primitives = 192 KB total**, no UltraRAM, and the board has no external DDR - enlarging the local memory does not help (verified with Vivado 2025.1: a single 512 KB LMB bank alone exceeds the device's BRAM). + +- **Full fwTPM (RSA+ECC, ~652 KB):** does not fit; needs a larger AMD device (a bigger Spartan UltraScale+ part or one with external DDR, ~768 KB of code/data memory). The firmware here builds and is ready for such a target. +- **Minimal ECC-only fwTPM (`FWTPM_TINY_ECC`, ~190 KB):** **fits the stock 192 KB BRAM** - no memory enlargement required. See the sizing breakdown below. The only bitstream change it needs is the SYSMON (AXI System Management) IP for the hardware TRNG. + +The stock bitstream as shipped also runs the `hello` image (already hardware-validated). + +### Can an ECC-only fTPM fit 192 KB? Yes - measured. + +It took the full stack of reductions - ECC-only, table-free AES, no SHA-1, +reduced context/NV, a **fabric hardware TRNG (SYSMONE4)** replacing MemUse, and +**wolfTPM's finer per-command-group gating** - but the ECC-only fTPM now fits. +Measured on this toolchain (rv32imc, `-Os`, reduced-heap link): + +| Build | Total | Code (text) | +|-------|-------|-------------| +| Default (RSA + ECC, MemUse entropy) | ~652 KB | ~271 KB | +| `-DFWTPM_TINY_ECC` (all cuts, MemUse) | ~254 KB | ~172 KB | +| `-DFWTPM_TINY_ECC -DFWTPM_TINY_PCR8 -DFWTPM_TINY_HWTRNG` (SYSMON TRNG + command gates) | **~190 KB** | **~144 KB** | +| Device budget | **192 KB** | | + +The last row **fits with ~1.5 KB to spare.** Two changes closed the final ~62 KB +gap: replacing wolfCrypt MemUse entropy with the on-die SYSMONE4 System Monitor +(saves the 16 KB entropy state + SHA-3, ~30 KB total) and wolfTPM's finer command +gating - the individual `FWTPM_NO_*` command-group macros the example selects +explicitly under `FWTPM_TINY_ECC` in `user_settings.h` (wolfTPM has no umbrella +macro; each gate is a deliberate choice) - which compiles out the key-migration / +ECDH / hash-command / context / symmetric-encrypt / clock command groups +(~20 KB). Both are documented below and in the firmware's `user_settings.h`. + +### The two levers that closed the gap + +`FWTPM_TINY_ECC` first applies every firmware-side reduction: ECC-P256 only (no +RSA, no P-384), SHA-256 only (no SHA-1), table-free AES, reduced fwTPM context +slots and buffers, an 8 KB NV store, and wolfTPM's `FWTPM_NO_POLICY` / +`NO_ATTESTATION` / `NO_CREDENTIAL` / `NO_DA` / `NO_PARAM_ENC` command gating. +That alone takes the image from 652 KB down to ~254 KB (a 61% cut) - but it is +still ~62 KB over the 192 KB device with MemUse entropy in the picture. Two +further changes close that last gap: + +1. **Fabric hardware TRNG (SYSMONE4).** Building with `-DFWTPM_TINY_HWTRNG` + replaces wolfCrypt's MemUse entropy (a 16 KB memory-jitter state plus the + SHA-3 conditioner and SP800-90B health-test buffers) with the on-die System + Monitor read over an AXI System Management Wizard. This removes ~30 KB of + code+RAM and provides real electrical noise. The wizard is not in the stock + TRD, so `add_sysmon.tcl` adds it (see "Rebuild the bitstream" below). +2. **wolfTPM finer command gating.** Under `FWTPM_TINY_ECC` the example selects + the individual `FWTPM_NO_*` command-group macros explicitly in + `user_settings.h` (wolfTPM has no umbrella macro - each is a deliberate + choice). It compiles out the key-migration, + ECDH, hash/HMAC-command, context save/load, symmetric-encrypt and clock + command groups (~20 KB) that a minimal ECC attestation + NV fTPM does not + need, while keeping Startup / GetCapability / GetRandom / PCR / Create / + Load / Sign / VerifySignature / NV / sessions. + +Together these bring the image to ~190 KB - it fits the stock 192 KB BRAM with +~1.5 KB to spare, so no memory enlargement is needed. + +Constraints worth noting: **AES cannot be removed** (the fwTPM +context-protection key and AES-GCM require it), an **RSA-only** build is larger +(RSA code + 1280-byte key slots vs 256), and an **ML-DSA-only** build is larger +still (Dilithium code + multi-KB keys) - so ECC-P256 is the algorithm that fits. + +## Rebuild the bitstream with the SYSMON TRNG + +The minimal build reads the System Monitor at `0x44A30000`, which the stock TRD +does not instantiate. Two wolfSSL-authored scripts add it without modifying any +AMD source file: + +- `add_sysmon.tcl` - overlay that adds the AXI System Management Wizard + (SYSMONE4, AXI4-Lite, continuous sequencer over temp/VCCINT/VCCAUX) to the + block design and maps it at `0x44A30000`. +- `build_sysmon.tcl` - build driver that creates the project, sources the TRD's + `config_bd.tcl` unmodified, applies the overlay, then synthesizes and + implements to a device image. + +```bash +export XILINX_VIVADO=/tools/Xilinx/2025.1/Vivado +cd fpga +$XILINX_VIVADO/bin/vivado -mode batch -notrace -source build_sysmon.tcl \ + -tclargs -trd /path/to/scu35-zephyr-rtos-io-trd/hw -jobs 8 +# -> build_sysmon/scu35_sysmon_wrapper.pdi +``` + +If you only need to run the `hello` image or a non-TRNG firmware subset, the +stock TRD bitstream is sufficient and no rebuild is needed. + +### Hardware validation (SCU35, Vivado 2025.1) + +The rebuilt bitstream and the ~190 KB minimal fwTPM were validated on the board: + +- Reading the System Monitor over JTAG returns live, plausible ADC codes whose + low bits jitter run-to-run - temperature `~0xA0xx`, VCCINT, VCCAUX - confirming + the wizard is configured, the register map (`0x400/0x404/0x408`) is correct, + and the noise source is real (not a stuck/zero read that would give a constant + DRBG seed). +- The fwTPM boots on `Hart #0`, `TPM2_Startup` and `TPM2_GetRandom` return + `rc=0x00000000`, and `TPM2_GetRandom` returns **different** bytes across cold + reloads - end-to-end proof the Hash-DRBG is seeded from the SYSMON entropy. +- Note: JTAG PDI programming needs the board in JTAG boot mode; if `device + program` reports `ROM State 0xD, Error 0x94D000`, power-cycle the board into + JTAG boot mode and retry. + +## Program the bitstream and load firmware + +1. Program the PDI to the `xcsu35p` (Vivado Hardware Manager, or `xsdb` `device program .pdi` with the board in JTAG boot mode). Use `build_sysmon/scu35_sysmon_wrapper.pdi` for the minimal fwTPM (SYSMON TRNG); the stock TRD PDI otherwise. +2. Start `hw_server`; with `xsdb`, select the MicroBlaze V RISC-V core target, `dow` the firmware ELF, set the PC to `0x0`, and `con`. +3. Read the console (`axi_uartlite_1`, the design's `serial1` = ttyUSB24) over the SCU35 FT4232H UART with `uart-monitor`. + +## Address map (from the TRD, for the firmware `scu35_board.h`) + +| Block | Base | +|-------|------| +| Local BRAM (reset vector) | `0x00000000` | +| AXI UARTLite 0 | `0x40600000` | +| AXI UARTLite 1 (console, design `serial1`) | `0x40700000` | +| AXI Timer 0 | `0x41C00000` | +| AXI QuadSPI 0 | `0x44A00000` | +| AXI System Management (SYSMONE4 TRNG, added by `add_sysmon.tcl`) | `0x44A30000` | + +Core: MicroBlaze V rv32imc, AXI clock 225 MHz. diff --git a/Xilinx/fwtpm-scu35-microblazev/fpga/add_sysmon.tcl b/Xilinx/fwtpm-scu35-microblazev/fpga/add_sysmon.tcl new file mode 100644 index 0000000..ec3a051 --- /dev/null +++ b/Xilinx/fwtpm-scu35-microblazev/fpga/add_sysmon.tcl @@ -0,0 +1,76 @@ +# add_sysmon.tcl +# +# wolfSSL overlay for the AMD "SCU35 Zephyr RTOS IO" Target Reference Design. +# Adds an AXI System Management Wizard (SYSMONE4) as a fabric hardware entropy +# source for the MicroBlaze V fwTPM, so the minimal ECC build can seed its +# Hash-DRBG from the on-die System Monitor instead of wolfCrypt MemUse entropy +# (built with -DFWTPM_TINY_HWTRNG; driver: firmware/fwtpm-mbv/fwtpm_trng_sysmon.c). +# +# Run this AFTER the base block design has been created and is current (i.e. +# after the TRD's config_bd.tcl has been sourced), and BEFORE synthesis. It +# does not modify any AMD source file; it only adds one IP and its connections. +# +# The System Management Wizard is placed in AXI4-Lite mode with the continuous +# sequencer over the on-chip temperature / VCCINT / VCCAUX sensors. Its AXI +# register map exposes those measurements at offsets 0x400 / 0x404 / 0x408 +# (DRP address * 4, with the +0x200 System-Management IP offset), which is what +# the firmware driver reads. It is mapped at 0x44A30000 to match +# SCU35_SYSMON_BASE in the firmware. +# +# Copyright (C) 2006-2026 wolfSSL Inc. +# +# This file is part of wolfTPM. +# +# wolfTPM 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. +# +# wolfTPM 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-1301, USA + +# 1. Instantiate the System Management Wizard (SYSMONE4), AXI4-Lite interface, +# continuous sequencer over the on-chip temperature / VCCINT / VCCAUX sensors. +set sysmon [create_bd_cell -type ip \ + -vlnv xilinx.com:ip:system_management_wiz:1.3 system_management_wiz_0] +set_property -dict [list \ + CONFIG.INTERFACE_SELECTION {Enable_AXI} \ + CONFIG.DCLK_FREQUENCY {225} \ + CONFIG.ADC_CONVERSION_RATE {200} \ + CONFIG.SEQUENCER_MODE {Continuous} \ + CONFIG.CHANNEL_ENABLE_TEMPERATURE {true} \ + CONFIG.CHANNEL_ENABLE_VCCINT {true} \ + CONFIG.CHANNEL_ENABLE_VCCAUX {true} \ +] $sysmon + +# 2. Add one master port to the MicroBlaze V AXI SmartConnect and wire it to the +# wizard's AXI4-Lite slave. Read the current master count and grow by one so +# the overlay survives small TRD revisions (the stock TRD has 19). +set sc microblaze_riscv_0_axi_periph +set num_mi [get_property CONFIG.NUM_MI [get_bd_cells $sc]] +set new_mi [format "M%02d_AXI" $num_mi] +set_property CONFIG.NUM_MI [expr {$num_mi + 1}] [get_bd_cells $sc] +connect_bd_intf_net \ + [get_bd_intf_pins $sc/$new_mi] \ + [get_bd_intf_pins system_management_wiz_0/S_AXI_LITE] + +# 3. Clock and reset from the MicroBlaze V 225 MHz AXI domain (DCLK is the AXI +# clock in AXI4-Lite mode; no separate dclk_in port is exposed). +connect_bd_net [get_bd_pins system_management_wiz_0/s_axi_aclk] \ + [get_bd_pins clk_wiz_1/clk_out1] +connect_bd_net [get_bd_pins system_management_wiz_0/s_axi_aresetn] \ + [get_bd_pins rst_clk_wiz_1_100M/peripheral_aresetn] + +# 4. Map the wizard at 0x44A30000 (matches SCU35_SYSMON_BASE in the firmware). +assign_bd_address -offset 0x44A30000 -range 0x00010000 \ + -target_address_space [get_bd_addr_spaces microblaze_riscv_0/Data] \ + [get_bd_addr_segs system_management_wiz_0/S_AXI_LITE/Reg] -force + +validate_bd_design +save_bd_design diff --git a/Xilinx/fwtpm-scu35-microblazev/fpga/build_sysmon.tcl b/Xilinx/fwtpm-scu35-microblazev/fpga/build_sysmon.tcl new file mode 100644 index 0000000..1f80b81 --- /dev/null +++ b/Xilinx/fwtpm-scu35-microblazev/fpga/build_sysmon.tcl @@ -0,0 +1,102 @@ +# build_sysmon.tcl +# +# wolfSSL build driver that regenerates the AMD "SCU35 Zephyr RTOS IO" TRD +# device image with the fabric hardware TRNG (SYSMONE4) added by add_sysmon.tcl, +# for the minimal ECC-only MicroBlaze V fwTPM (-DFWTPM_TINY_HWTRNG). +# +# It reuses the AMD TRD's block-design script (config_bd.tcl) and constraints +# unmodified, sourcing them by path; it does not copy or alter any AMD source. +# The firmware ELF is JTAG-loaded at run time, so no software image is embedded. +# +# Usage (from this fpga/ directory): +# vivado -mode batch -notrace -source build_sysmon.tcl -tclargs \ +# -trd /path/to/scu35-zephyr-rtos-io-trd/hw [-jobs 8] +# +# Output: build_sysmon/scu35_sysmon_wrapper.pdi +# +# Copyright (C) 2006-2026 wolfSSL Inc. +# +# This file is part of wolfTPM. +# +# wolfTPM 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. +# +# wolfTPM 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-1301, USA + +set script_folder [file dirname [file normalize [info script]]] +set jobs 8 +set trd_hw "" + +for {set i 0} {$i < $argc} {incr i} { + switch -- [lindex $argv $i] { + -jobs { incr i; set jobs [lindex $argv $i] } + -trd { incr i; set trd_hw [lindex $argv $i] } + } +} +if {$trd_hw eq ""} { + error "Pass -trd /path/to/scu35-zephyr-rtos-io-trd/hw" +} + +set proj_name scu35_sysmon +set bd_name scu35_zephyr_rtos_io_trd +set proj_board [get_board_parts "*:scu35:*" -latest_file_version] +puts "Board Part: $proj_board" + +create_project -name ${proj_name} -force -dir ./build_sysmon \ + -part [get_property PART_NAME [get_board_parts $proj_board]] +set_property board_part $proj_board [current_project] + +import_files -fileset constrs_1 $trd_hw/xdc/EK_SCU35_05204-01_251124.xdc +import_files -fileset sources_1 $trd_hw/src/fanout_pmod.v +update_compile_order +update_ip_catalog + +# Base block design (AMD TRD, unmodified) named to match the wrapper the TRD +# tooling expects; add_sysmon.tcl references cells by that design. +set design_name $bd_name +create_bd_design $bd_name +current_bd_design $bd_name +current_bd_instance [get_bd_cells /] +source $trd_hw/scripts/config_bd.tcl + +# wolfSSL overlay: add the SYSMONE4 hardware TRNG (validates + saves the BD). +source $script_folder/add_sysmon.tcl + +make_wrapper -files [get_files \ + ./build_sysmon/${proj_name}.srcs/sources_1/bd/$bd_name/${bd_name}.bd] -top +import_files -force -norecurse \ + ./build_sysmon/${proj_name}.srcs/sources_1/bd/$bd_name/hdl/${bd_name}_wrapper.v +update_compile_order +set_property top ${bd_name}_wrapper [current_fileset] +update_compile_order -fileset sources_1 +save_bd_design + +generate_target all [get_files \ + ./build_sysmon/${proj_name}.srcs/sources_1/bd/$bd_name/${bd_name}.bd] +set_property synth_checkpoint_mode Hierarchical [get_files \ + ./build_sysmon/${proj_name}.srcs/sources_1/bd/$bd_name/${bd_name}.bd] + +launch_runs synth_1 -jobs ${jobs} +wait_on_run synth_1 + +launch_runs impl_1 -to_step write_bitstream -jobs ${jobs} +wait_on_run impl_1 + +set pdi ./build_sysmon/${proj_name}.runs/impl_1/${bd_name}_wrapper.pdi +if {[file exists $pdi]} { + file copy -force $pdi ./build_sysmon/scu35_sysmon_wrapper.pdi + puts "SYSMON PDI: [file normalize ./build_sysmon/scu35_sysmon_wrapper.pdi]" +} else { + error "Expected PDI not found: $pdi" +} + +exit