Add fwTPM example for Xilinx ZCU102 Cortex-R5 (lock-step)#3
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds a new end-to-end fwTPM example targeting the Xilinx ZCU102 where Linux (A53) loads and communicates with a bare-metal Cortex-R5 lock-step fwTPM instance via Linux remoteproc + OpenAMP RPMsg, along with a PetaLinux project and a Linux-side smoke test.
Changes:
- Added ZCU102 R5 lock-step bare-metal fwTPM firmware (resource table, MPU setup, RPMsg transport, NV HALs, linker script, build system).
- Added a self-contained PetaLinux 2025.2 project (DT overlay carveouts/lock-step/IPI mailbox + recipes for firmware staging and smoke test + kernel config fragment).
- Added host-side helpers and documentation (SD deploy script, READMEs, repo top-level README entry, gitignore updates).
Reviewed changes
Copilot reviewed 46 out of 49 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| Xilinx/fwtpm-zcu102-r5/README.md | ZCU102 fwTPM architecture/build/boot documentation for the new example. |
| Xilinx/fwtpm-zcu102-r5/deploy-to-sdcard.sh | Host script to deploy PetaLinux artifacts to an SD card. |
| Xilinx/fwtpm-zcu102-r5/firmware/fwtpm-r5/Makefile | Build rules for the R5 bare-metal fwTPM firmware (Tier-1 objects, Tier-2 ELF link). |
| Xilinx/fwtpm-zcu102-r5/firmware/fwtpm-r5/boot.S | R5 reset/vector entry and TCM stub for remoteproc reload robustness. |
| Xilinx/fwtpm-zcu102-r5/firmware/fwtpm-r5/lscript.ld | Linker script for rproc DDR carveout + TCM vectors + trace/resource sections. |
| Xilinx/fwtpm-zcu102-r5/firmware/fwtpm-r5/mpu_setup.c | Minimal ARMv7-R MPU programming for Normal-NC + Device regions. |
| Xilinx/fwtpm-zcu102-r5/firmware/fwtpm-r5/main.c | Firmware entry: MPU init, HAL registration, fwTPM init, RPMsg server loop. |
| Xilinx/fwtpm-zcu102-r5/firmware/fwtpm-r5/user_settings.h | wolfSSL/wolfTPM build-time configuration for bare-metal R5. |
| Xilinx/fwtpm-zcu102-r5/firmware/fwtpm-r5/fwtpm_clock_zynqmp.c | Clock HAL (PMU CCNT) and RNG seed callback. |
| Xilinx/fwtpm-zcu102-r5/firmware/fwtpm-r5/fwtpm_nv_ram.c | Volatile RAM-backed NV HAL (V1 fallback). |
| Xilinx/fwtpm-zcu102-r5/firmware/fwtpm-r5/fwtpm_nv_qspi.c | QSPI-backed NV HAL skeleton using XQspiPsu. |
| Xilinx/fwtpm-zcu102-r5/firmware/fwtpm-r5/fwtpm_rpmsg.c | OpenAMP RPMsg endpoint bridging to FWTPM_ProcessCommand + poll loop. |
| Xilinx/fwtpm-zcu102-r5/firmware/fwtpm-r5/platform_info.c | libmetal device registration + remoteproc ops + IPI helpers for OpenAMP. |
| Xilinx/fwtpm-zcu102-r5/firmware/fwtpm-r5/rsc_table.c | remoteproc resource table (rpmsg vdev + optional trace ring). |
| Xilinx/fwtpm-zcu102-r5/firmware/fwtpm-r5/bsp_stubs.c | Newlib/BSP stubs: _sbrk + outbyte() -> trace buffer ring. |
| Xilinx/fwtpm-zcu102-r5/firmware/fwtpm-r5/zcu102_r5.h | Local header for platform HAL APIs used across firmware sources. |
| Xilinx/fwtpm-zcu102-r5/firmware/fwtpm-r5/include/xparameters.h | Hand-rolled minimal platform constants and carveout addresses. |
| Xilinx/fwtpm-zcu102-r5/petalinux/README.md | Instructions for building and validating the included PetaLinux project. |
| Xilinx/fwtpm-zcu102-r5/petalinux/.gitignore | Ignores PetaLinux build outputs/imported hardware, preserves key configs. |
| Xilinx/fwtpm-zcu102-r5/petalinux/.petalinux/metadata | Captures PetaLinux version and project metadata for the committed project. |
| Xilinx/fwtpm-zcu102-r5/petalinux/project-spec/attributes | PetaLinux project attributes (e.g., defconfig selection). |
| Xilinx/fwtpm-zcu102-r5/petalinux/project-spec/configs/config | Committed PetaLinux system configuration used as build input. |
| Xilinx/fwtpm-zcu102-r5/petalinux/project-spec/configs/flash_parts.txt | Flash layout config for PetaLinux tooling. |
| Xilinx/fwtpm-zcu102-r5/petalinux/project-spec/configs/rootfsconfigs/user-rootfsconfig | Rootfs config list (user-rootfsconfig) for the project. |
| Xilinx/fwtpm-zcu102-r5/petalinux/project-spec/meta-user/README | Meta-user layer README (currently template content). |
| Xilinx/fwtpm-zcu102-r5/petalinux/project-spec/meta-user/COPYING.MIT | MIT license text for the meta-user layer template. |
| Xilinx/fwtpm-zcu102-r5/petalinux/project-spec/meta-user/conf/layer.conf | Yocto layer configuration for meta-user (scarthgap compatibility). |
| Xilinx/fwtpm-zcu102-r5/petalinux/project-spec/meta-user/conf/petalinuxbsp.conf | Adds fwtpm-r5 and fwtpm-rpmsg-test packages to IMAGE_INSTALL. |
| Xilinx/fwtpm-zcu102-r5/petalinux/project-spec/meta-user/conf/user-rootfsconfig | Meta-user rootfs package listing file. |
| Xilinx/fwtpm-zcu102-r5/petalinux/project-spec/meta-user/recipes-bsp/device-tree/device-tree.bbappend | Hooks in system-user.dtsi overlay for the device-tree build. |
| Xilinx/fwtpm-zcu102-r5/petalinux/project-spec/meta-user/recipes-bsp/device-tree/device-tree-sdt.inc | SDT include wiring for extra DT include files. |
| Xilinx/fwtpm-zcu102-r5/petalinux/project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi | DT overlay: lock-step cluster mode, carveouts, IPI mailbox, QSPI partition, GEM3 PHY. |
| Xilinx/fwtpm-zcu102-r5/petalinux/project-spec/meta-user/recipes-bsp/fwtpm-r5/fwtpm-r5_0.1.0.bb | Recipe to stage the prebuilt R5 ELF into firmware directory for remoteproc. |
| Xilinx/fwtpm-zcu102-r5/petalinux/project-spec/meta-user/recipes-apps/fwtpm-rpmsg-test/fwtpm-rpmsg-test_0.1.0.bb | Recipe to build/install the RPMsg smoke-test client. |
| Xilinx/fwtpm-zcu102-r5/petalinux/project-spec/meta-user/recipes-apps/fwtpm-rpmsg-test/files/fwtpm_rpmsg_test.c | Source for the smoke-test client (duplicate of linux-client version). |
| Xilinx/fwtpm-zcu102-r5/petalinux/project-spec/meta-user/recipes-kernel/linux/linux-xlnx_%.bbappend | Adds kernel feature fragments (bsp.cfg + openamp.cfg). |
| Xilinx/fwtpm-zcu102-r5/petalinux/project-spec/meta-user/recipes-kernel/linux/linux-xlnx/bsp.cfg | Empty kernel fragment placeholder referenced by bbappend. |
| Xilinx/fwtpm-zcu102-r5/petalinux/project-spec/meta-user/recipes-kernel/linux/linux-xlnx/openamp.cfg | Kernel config fragment enabling remoteproc/rpmsg/mailbox. |
| Xilinx/fwtpm-zcu102-r5/petalinux/project-spec/meta-user/recipes-bsp/u-boot/u-boot-xlnx_%.bbappend | U-Boot bbappend adding platform-top.h and bsp.cfg. |
| Xilinx/fwtpm-zcu102-r5/petalinux/project-spec/meta-user/recipes-bsp/u-boot/files/platform-top.h | U-Boot platform-top config header inclusion shim. |
| Xilinx/fwtpm-zcu102-r5/petalinux/project-spec/meta-user/recipes-bsp/u-boot/files/bsp.cfg | U-Boot config selection for platform-top. |
| Xilinx/fwtpm-zcu102-r5/petalinux/project-spec/meta-user/recipes-bsp/u-boot/files/0001-ubifs-distroboot-support.patch | U-Boot patch file (currently not wired into bbappend). |
| Xilinx/fwtpm-zcu102-r5/petalinux/project-spec/meta-user/meta-xilinx-tools/recipes-bsp/uboot-device-tree/uboot-device-tree.bbappend | Adds a DT include for the U-Boot device-tree build. |
| Xilinx/fwtpm-zcu102-r5/petalinux/project-spec/meta-user/meta-xilinx-tools/recipes-bsp/uboot-device-tree/files/system-user.dtsi | Minimal U-Boot DT include overlay file. |
| Xilinx/fwtpm-zcu102-r5/linux-client/Makefile | Standalone build for the A53 Linux smoke-test client. |
| Xilinx/fwtpm-zcu102-r5/linux-client/fwtpm_rpmsg_test.c | Standalone A53 Linux RPMsg smoke-test client source. |
| README.md | Top-level repo README updated to mention the new ZCU102 example. |
| .gitignore | Repo gitignore updated for ZCU102 example build artifacts (BSP/hw/petalinux outputs/client binary). |
Comments suppressed due to low confidence (2)
Xilinx/fwtpm-zcu102-r5/README.md:211
- In the Memory Map table, the QSPI fwtpm-nv partition is listed at 0x07F00000, but system-user.dtsi defines it at 0x07FF0000. This discrepancy can cause readers to partition flash incorrectly and (if the QSPI HAL is enabled) can lead to writes to the wrong offset; please make the address consistent everywhere.
| RPMsg buffer pool | 0x3EE08000 | 256 KiB | shared payload buffers |
| QSPI fwtpm-nv | 0x07F00000 | 64 KiB | persistent NV (R5 owns) |
Xilinx/fwtpm-zcu102-r5/firmware/fwtpm-r5/fwtpm_nv_qspi.c:253
- This file uses printf() for QSPI init errors, but the port documentation indicates newlib stdio is not usable on this bare-metal R5 (no _write backend). If printf isn’t wired to outbyte/xil_printf, these diagnostics may hang precisely when flash init fails; prefer xil_printf/outbyte-based logging for error reporting here.
cfg = XQspiPsu_LookupConfig(XPAR_XQSPIPSU_0_BASEADDR);
if (cfg == NULL) {
printf("XQspiPsu LookupConfig failed\n");
return -1;
}
rc = XQspiPsu_CfgInitialize(&g_qspi, cfg, cfg->BaseAddress);
if (rc != XST_SUCCESS) {
printf("XQspiPsu CfgInitialize failed: %d\n", rc);
return rc;
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
aidangarske
left a comment
There was a problem hiding this comment.
Skoll Code Review
Scan type: reviewOverall recommendation: REQUEST_CHANGES
Findings: 7 total — 7 posted, 0 skipped
7 finding(s) posted as inline comments (see file-level comments below)
Posted findings
- [High] RPMsg transport is too small for advertised ML-DSA-87 commands —
Xilinx/fwtpm-zcu102-r5/firmware/fwtpm-r5/fwtpm_rpmsg.c:44-59 - [High] QSPI FAST_READ omits the required dummy byte —
Xilinx/fwtpm-zcu102-r5/firmware/fwtpm-r5/fwtpm_nv_qspi.c:120-137 - [High] QSPI init selects the lower flash while the NV offset is in the upper stacked range —
Xilinx/fwtpm-zcu102-r5/firmware/fwtpm-r5/fwtpm_nv_qspi.c:295-297 - [High] NV HAL bounds checks can wrap and QSPI erase lacks a partition bounds check —
Xilinx/fwtpm-zcu102-r5/firmware/fwtpm-r5/fwtpm_nv_qspi.c:116-202 - [High] Generated bootargs point at mmcblk0p4 while the deploy flow creates the rootfs on partition 2 —
Xilinx/fwtpm-zcu102-r5/petalinux/project-spec/configs/config:259-319 - [High] Firmware staging recipe cannot be populated from a clean checkout —
Xilinx/fwtpm-zcu102-r5/petalinux/project-spec/meta-user/recipes-bsp/fwtpm-r5/fwtpm-r5_0.1.0.bb:9-10 - [Medium] PMU cycle-counter clock loses elapsed time after normal idle intervals —
Xilinx/fwtpm-zcu102-r5/firmware/fwtpm-r5/fwtpm_clock_zynqmp.c:51-66
Review generated by Skoll
| P1=$(readlink -f /dev/disk/by-label/boot 2>/dev/null || true) | ||
| P2=$(readlink -f /dev/disk/by-label/rootfs 2>/dev/null || true) | ||
| [[ -b "$P1" ]] || P1="${DEV}1" | ||
| [[ -b "$P2" ]] || P2="${DEV}2" | ||
| # Sanity: both resolved partitions must live on the requested device. | ||
| [[ "$P1" == "${DEV}"* ]] || { echo "label 'boot' resolves to $P1, not on $DEV" >&2; exit 1; } | ||
| [[ "$P2" == "${DEV}"* ]] || { echo "label 'rootfs' resolves to $P2, not on $DEV" >&2; exit 1; } |
| mode**, reserves a 1 MiB DDR carveout for the R5 fwTPM firmware | ||
| (matching `firmware/fwtpm-r5/lscript.ld`), and installs: | ||
|
|
||
| - `/usr/lib/firmware/fwtpm_r5.elf` -- the R5 firmware blob |
| | `rproc_0_reserved` size | 384 KiB | **1 MiB** (fits full fwtpm_r5.elf) | | ||
| | QSPI `fwtpm-nv` partition | not declared | **64 KiB at 0x07FF0000** | | ||
| | OpenAMP kernel options | mostly default | `CONFIG_RPMSG_CHAR=y` + ZynqMP IPI | | ||
| | `/usr/lib/firmware/fwtpm_r5.elf`| absent | **staged at boot** | |
| # -> 00000000: 0000 0001 .... | ||
|
|
||
| # fwTPM firmware is staged | ||
| ls -la /usr/lib/firmware/fwtpm_r5.elf |
| PETALINUX_VER=2025.2 | ||
| VALIDATE_HW_CHKSUM=1 | ||
| HARDWARE_PATH=/home/davidgarske/GitHub/wolftpm-examples/Xilinx/fwtpm-zcu102-r5/hw/zcu102.xsa | ||
| HDF_EXT=xsa | ||
| YOCTO_SDK=8576b661a268f6a1638f445924bb6001e14cdc81b43e38c84762381e5ff54d50 |
| printf("XScuGic_CfgInitialize: %d\n", rc); | ||
| return rc; |
| printf("XScuGic_Connect: %d\n", rc); | ||
| return rc; |
| #include <stdint.h> | ||
| #include <stdio.h> | ||
| #include <string.h> | ||
|
|
| printf("qspi_wait_done: WIP timeout, status=0x%02x\n", status); | ||
| return XST_FAILURE; |
| FILESEXTRAPATHS:prepend := "${THISDIR}/files:" | ||
|
|
||
| SRC_URI:append = " file://platform-top.h file://bsp.cfg" |
Summary
Adds two production-class wolfTPM firmware TPM (fwTPM) examples to
wolftpm-examples, each demonstrating a different deployment topology. Together they cover the two canonical ways to run wolfTPM as a software TPM 2.0 server inside the same SoC as the application:mssim-style framed transport over UART. NSC veneers gate every secure-side call.remoteproc. Persistent NV is in QSPI flash. Both TCG TPM 2.0 v1.85 PQC primitives (ML-KEM 512/768/1024, ML-DSA 44/65/87) are enabled.Architecture
STM32/fwtpm-stm32h5/— TrustZone-isolated fwTPMpartition_stm32h563xx.hcarves NS/S address space.fwtpm_nsc.{c,h}; every TPM2 command crosses the secure gate.Xilinx/fwtpm-zcu102-r5/— AMP fwTPM on R5 lock-step + OpenAMP RPMsgKey design choices, all observable on hardware:
xlnx,cluster-mode=<1>+xlnx,tcm-mode=<1>combine RPU0+RPU1 into one logical safety-class processor with 256 KiB merged TCM at0x00000000. Singleremoteproc0instance manages both cores.0x3ED00000(rproc_0_reserved) holds R5 .text + .rodata + .data + .bss + heap + stack + 32 KiB trace ring. RPMsg vrings (16 KiB each) and 256 KiB rpmsg buffer pool sit at0x3EE00000+, deliberately outside the firmware carveout so APU vring writes never collide with R5 .bss.0xF0000000covering the full LPD aperture (GIC400 at0xF9000000+ IPI at0xFF300000+ UART/QSPI/CSU at0xFF000000+). Caches stay off so the trace ring stays APU-coherent without dcache flushes.XScuGic+ IPI ISR (XPAR_PSU_IPI_1_INT_ID = 65) wakes the R5 fromwfi. Server loop drains both vrings viaremoteproc_get_notification(RSC_NOTIFY_ID_ANY)then sleeps again. R5 idles near 0% CPU between TPM commands instead of busy-polling._boot): vector table at TCM0x0000_0000(where the R5 cold-reset vector fetches), TCM-resident reset stub clears stale CP15 MPU state before jumping to DDR_entry, IRQ stub saves caller-saved + lr-4 and dispatches to_irq_dispatchin C.pmu_eemi.cissuesPM_REQUEST_NODE(NODE_QSPI=0x2D)to PMUFW over the RPU0 IPI block beforeXQspiPsu_LookupConfig, so the controller's clock + power domain are up regardless of whether Linux'sspi-zynqmp-qspidriver is enabled in DTS. Build withmake elf EXTRA_CFLAGS=-DFWTPM_NV_QSPI. Default build uses the volatile RAM HAL.WOLFTPM_V185+WOLFSSL_HAVE_MLKEM+HAVE_DILITHIUM+ SHA3/SHAKE128/256 enabled inuser_settings.h; firmware advertises full ML-KEM 512/768/1024 and ML-DSA 44/65/87 via the v1.85PT_ML_PARAMETER_SETScapability.petalinux/): system-user.dtsi with the lock-step rproc + carveouts + IPI mailbox + qspi disabled; openamp.cfg kernel fragment forREMOTEPROC + RPMSG_CHAR + ZYNQMP_IPI_MBOX; recipes-bsp/fwtpm-r5 stages the R5 ELF in/lib/firmware/; recipes-apps/fwtpm-rpmsg-test installs the smoke client. SD root onmmcblk0p4.fwtpm_rpmsg_test.c— raw TPM2 wire-format smoke (Startup / SelfTest / GetRandom / GetCapability /--pqcaddsPT_ML_PARAMETER_SETSprobe).fwtpm_pqc_test.c+build-pqc-test.sh— wolfTPM2 wrapper round-trip (ML-KEM-768 encap/decap + ML-DSA-65 sign/verify) over an rpmsgTPM2_IoCb. Reproducible cross-build of static aarch64 binary against sibling wolfssl + wolftpm with the right--enable-pqc --disable-fwtpm --disable-autodetectcombo to keep the simple 5-argTPM2HalIoCbtypedef active.Shared design points
Both ports share the same wolfTPM fwTPM core:
WOLFCRYPT_ONLY,SINGLE_THREADED,NO_FILESYSTEM,WOLFSSL_USER_IO.WOLFSSL_SP,WOLFSSL_SP_MATH_ALL,WOLFSSL_SP_SMALL,SP_WORD_SIZE=32).HASH_DRBGseeded from a platform-specific entropy callback (STM32 RNG peripheral on H5; PMU CCNT jitter on R5 — replaceable with PSU CSU TRNG via PMU mailbox in a follow-on).src/fwtpm/layer dispatches TPM2 commands and routes to wolfCrypt for crypto.Test results
STM32H5 NUCLEO-H563ZI (single-MCU, TZEN=1)
Hardware-verified on
NUCLEO-H563ZIwithtest_uart_tpm.pyover USB-UART:TPM2_Startup(SU_CLEAR)TPM2_SelfTest(NO)TPM2_GetRandom(32)TPM2_GetCapability(MANUFACTURER)ZCU102 R5 lock-step (AMP fwTPM via OpenAMP RPMsg)
Hardware-verified on a stock ZCU102 (XCZU9EG-2FFVB1156I), 2026-05-15, lock-step R5 with custom PetaLinux 2025.2 image:
remoteproc startboots R5 ELF; trace ring shows bannergic_init done->FWTPM_Init done->rpmsg readyfwtpm_rpmsg_testbaseline 4 cmds (Startup/SelfTest/GetRandom/GetCap)fwtpm_rpmsg_test --pqcadvertisesPT_ML_PARAMETER_SETS = 0x3Fwfiserver loop responds to APU rpmsg writesmmcblk0p4rootfs)R5 trace ring (verbatim, sequential):