Skip to content

Add wolfTPM fwTPM on AMD Zynq-7000 Cortex-A9 (ZC702) with wolfCrypt SRAM PUF - #7

Merged
aidangarske merged 1 commit into
mainfrom
zc702
Aug 24, 2026
Merged

Add wolfTPM fwTPM on AMD Zynq-7000 Cortex-A9 (ZC702) with wolfCrypt SRAM PUF#7
aidangarske merged 1 commit into
mainfrom
zc702

Conversation

@dgarske

@dgarske dgarske commented Aug 13, 2026

Copy link
Copy Markdown
Member

Summary

A bare-metal firmware-TPM 2.0 server (wolfTPM fwtpm) running on a single Cortex-A9 of an AMD Zynq-7000 (ZC702), driven from a host over UART with the same raw swtpm + Microsoft-simulator ("mssim") framing as the existing STM32H5 and Mi-V ports, so the stock wolfTPM swtpm client drives it unmodified.

Distinguishing feature: PUF-derived NV integrity key

The TPM NV-journal integrity key is a device-unique key derived from the Cortex-A9 on-chip-memory (OCM) SRAM power-on state, using wolfCrypt's configurable SRAM PUF (a BCH(127,k,t) fuzzy extractor plus HKDF). No root key is stored in flash - it is regenerated from silicon on each boot. Persistent NV and the PUF helper data live in QSPI flash (-DFWTPM_NV_QSPI).

What is included

  • Self-contained bare-metal A9 HAL (wolfSSL-authored, no vendor BSP): polled Cadence UART console, MPCore Global Timer time base and A9 cycle-counter entropy timer, SVC reset/startup with cache/VFP bring-up, and a flat MMU map (required for correct printf).
  • fwTPM HAL wiring: clock (Global Timer), NV backends (volatile RAM default, persistent QSPI opt-in), and the OCM SRAM PUF integrity-key source, plus a synthetic PUF regression (-DFWTPM_PUF_SELFTEST) with selectable BCH profile.
  • Host-side clients (swtpm bridge, caps/PCR/GetRandom, NV persistence check) and a hello sanity image.
  • Optional post-quantum build (-DFWTPM_ENABLE_PQC) for an ECC + ML-DSA / ML-KEM TPM instead of the default RSA + ECC.

Hardware validation

Validated on the ZC702 bench over JTAG (prebuilt FSBL does ps7_init, app loaded over the top): fwTPM boots, self-test passes, and the SRAM PUF regenerates a stable device-unique integrity key across boots.

Notes

Self-contained bare-metal server model (the dual-A9 second core and PetaLinux are not used; the coprocessor + Linux-client model is the separate ZCU102 R5 example). No vendor BSP is redistributed; wolfSSL and wolfTPM are consumed as sibling source trees.

@dgarske dgarske self-assigned this Aug 13, 2026
Copilot AI lite review requested due to automatic review settings August 13, 2026 20:06

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Adds a bare-metal wolfTPM fwTPM server port for AMD Zynq-7000 (ZC702) Cortex-A9, including a sibling “hello” bring-up image and host-side UART tooling, with optional QSPI-backed NV storage and an SRAM-PUF–derived NV integrity key.

Changes:

  • Introduces Zynq-7000 bare-metal HAL (startup, MMU, UART, timers, newlib retarget) plus a hello-world heartbeat image.
  • Adds fwTPM firmware (UART swtpm/mssim framing), SRAM PUF integration (incl. synthetic selftest), and NV backends (RAM default, QSPI optional).
  • Adds host-side Python utilities (swtpm TCP↔UART bridge, UART functional test, NV persistence test) and documentation.

Reviewed changes

Copilot reviewed 27 out of 27 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
Xilinx/fwtpm-zc702-a9/firmware/hello/zynq7000-ddr.ld Adds DDR linker script for hello-world image.
Xilinx/fwtpm-zc702-a9/firmware/hello/main.c Implements UART banner + global-timer heartbeat sanity test.
Xilinx/fwtpm-zc702-a9/firmware/hello/Makefile Builds the hello-world image with the common HAL.
Xilinx/fwtpm-zc702-a9/firmware/fwtpm-a9/zynq7000-fwtpm.ld Adds DDR linker script sized for fwTPM (larger heap/stack).
Xilinx/fwtpm-zc702-a9/firmware/fwtpm-a9/user_settings.h Defines wolfSSL/wolfTPM build configuration for bare-metal + PUF/MemUse.
Xilinx/fwtpm-zc702-a9/firmware/fwtpm-a9/main.c Implements fwTPM init, self-test, and UART swtpm/mssim command server.
Xilinx/fwtpm-zc702-a9/firmware/fwtpm-a9/host-client/swtpm_uart_bridge.py Adds TCP mssim ↔ UART bridge for existing wolfTPM swtpm clients.
Xilinx/fwtpm-zc702-a9/firmware/fwtpm-a9/host-client/fwtpm_uart_test.py Adds direct UART functional test client (caps/PCR/random).
Xilinx/fwtpm-zc702-a9/firmware/fwtpm-a9/host-client/fwtpm_nv_persist_test.py Adds TPM NV persistence test over UART.
Xilinx/fwtpm-zc702-a9/firmware/fwtpm-a9/fwtpm_puf_selftest.c Adds on-target synthetic SRAM PUF regression (build-time opt-in).
Xilinx/fwtpm-zc702-a9/firmware/fwtpm-a9/fwtpm_puf.h Declares PUF init/key hook + helper persistence seam.
Xilinx/fwtpm-zc702-a9/firmware/fwtpm-a9/fwtpm_puf.c Implements OCM SRAM PUF enroll/reconstruct and integrity-key derivation.
Xilinx/fwtpm-zc702-a9/firmware/fwtpm-a9/fwtpm_nv_ram.c Implements volatile RAM-backed NV HAL.
Xilinx/fwtpm-zc702-a9/firmware/fwtpm-a9/fwtpm_nv_qspi.c Implements QSPI-backed NV HAL + persistent PUF helper store.
Xilinx/fwtpm-zc702-a9/firmware/fwtpm-a9/fwtpm_clock_zynq.c Implements fwTPM clock HAL + sleep shim + entropy timer.
Xilinx/fwtpm-zc702-a9/firmware/fwtpm-a9/Makefile Builds fwTPM image from sibling wolfSSL/wolfTPM source trees.
Xilinx/fwtpm-zc702-a9/firmware/common/zynq_uart.h Declares minimal polled Cadence UART API.
Xilinx/fwtpm-zc702-a9/firmware/common/zynq_uart.c Implements polled Cadence UART driver (+ optional baud configuration).
Xilinx/fwtpm-zc702-a9/firmware/common/zynq_time.h Declares Global Timer + PMU cycle-counter timing API.
Xilinx/fwtpm-zc702-a9/firmware/common/zynq_time.c Implements Global Timer ticks/ms/delays and PMU cycle counter enable/read.
Xilinx/fwtpm-zc702-a9/firmware/common/zynq7000.h Adds Zynq-7000 address/clock definitions (UART, timers, QSPI, OCM).
Xilinx/fwtpm-zc702-a9/firmware/common/startup.S Adds bare-metal A9 reset/startup: vectors, stacks, cache/MMU/VFP, BSS, main.
Xilinx/fwtpm-zc702-a9/firmware/common/retarget.c Adds newlib syscall stubs (stdio→UART, _sbrk heap, gettimeofday).
Xilinx/fwtpm-zc702-a9/firmware/common/mmu.c Adds minimal 1MB-section MMU mapping enabling Normal DDR for printf correctness.
Xilinx/fwtpm-zc702-a9/README.md Adds ZC702 fwTPM build/run docs and design notes.
Xilinx/fwtpm-zc702-a9/.gitignore Ignores build artifacts and Python cache files for the new port.
README.md Links the new ZC702 port from the repo root README.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread Xilinx/fwtpm-zc702-a9/firmware/common/zynq_time.c Outdated
Comment thread Xilinx/fwtpm-zc702-a9/firmware/fwtpm-a9/main.c
Comment thread Xilinx/fwtpm-zc702-a9/firmware/fwtpm-a9/main.c
Comment thread Xilinx/fwtpm-zc702-a9/firmware/fwtpm-a9/fwtpm_nv_qspi.c
Comment thread Xilinx/fwtpm-zc702-a9/firmware/fwtpm-a9/fwtpm_nv_qspi.c Outdated
Comment thread Xilinx/fwtpm-zc702-a9/README.md Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 31 out of 31 changed files in this pull request and generated no new comments.

Suppressed comments (4)

Xilinx/fwtpm-zc702-a9/firmware/fwtpm-a9/fwtpm_puf.c:136

  • In the enroll path, the return value of fwtpm_puf_helper_store() is ignored, so a flash/program failure can silently proceed with a derived NV integrity key that won’t be reconstructible on the next boot (breaking persistent NV and key stability). Propagate helper-store failures as an error and only report/enroll-success when the helper data is actually persisted.
        if (ret == 0) {
            (void)fwtpm_puf_helper_store(helper, (unsigned int)WC_PUF_HELPER_BYTES,
                (unsigned int)WC_PUF_PROFILE_ID);
        }
        didEnroll = 1;

Xilinx/fwtpm-zc702-a9/firmware/fwtpm-a9/fwtpm_puf.c:148

  • wc_PufDeriveKey() is passed sizeof(PUF_INFO), which includes the terminating NUL from the string literal. Using the NUL in HKDF info is easy to do accidentally and can lead to subtle mismatches if other implementations/tests use strlen() semantics. Prefer using (sizeof(PUF_INFO) - 1) to make the info length unambiguous.
    if (ret == 0) {
        ret = wc_PufDeriveKey(&ctx, PUF_INFO, (word32)sizeof(PUF_INFO),
            g_pufKey, sizeof(g_pufKey));

Xilinx/fwtpm-zc702-a9/firmware/fwtpm-a9/fwtpm_nv_qspi.c:167

  • This QSPI RX wait loop has no timeout. If the controller is misconfigured or the flash doesn’t respond, the firmware can hang forever inside qspi_xfer4(), potentially wedging the TPM server. Consider adding a bounded wait using zynq_millis() (and propagating an error up) similar to the WIP poll timeout used for erase/program.
    while (!(QSPI_ISR & ISR_RXNEMPTY)) {
        /* wait for the RX word */
    }

Xilinx/fwtpm-zc702-a9/firmware/bench/zynq7000-ddr.ld:5

  • This linker script header still says it is for the "hello-world" image, but it lives under firmware/bench and is used for the benchmark target. Updating the comment avoids confusion when grepping for the right linker script.
 * Linker script for the Zynq-7000 Cortex-A9 hello-world image, linked into DDR
 * at 0x04000000 (the FSBL brings DDR up via ps7_init; we load over the top with
 * JTAG). Vector table first so VBAR lands on it.

@aidangarske
aidangarske merged commit eaaf794 into main Aug 24, 2026
1 check passed
@aidangarske
aidangarske deleted the zc702 branch August 24, 2026 20:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants