wolfTPM firmware TPM on PolarFire MPF300 (Mi-V RV32 soft core) - #5
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new bare‑metal example port demonstrating wolfTPM’s firmware TPM (fwTPM) running on a Mi‑V RV32 soft core in a Microchip PolarFire MPF300 FPGA design, including bring‑up firmware, a minimal HAL, host-side UART tooling, and detailed build/validation documentation.
Changes:
- Add a new
Microchip/miv-mpf300-splash/example with staged bring-up (hello-world, wolfCrypt test/benchmark, fwTPM over UART). - Implement a self-authored bare-metal HAL (UART/GPIO/timer/newlib retarget) plus System Controller mailbox access for TRNG (NRBG nonce) and persistent sNVM-backed NV.
- Add host-side Python clients to drive the UART transport (raw swtpm framing and mssim bridge) and document FPGA/platform setup and performance notes.
Reviewed changes
Copilot reviewed 32 out of 32 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| README.md | Adds top-level entry pointing to the new MPF300 Mi‑V fwTPM example. |
| Microchip/miv-mpf300-splash/README.md | Full documentation for staged bring-up, performance notes, TRNG/NV design, and usage. |
| Microchip/miv-mpf300-splash/fpga/README.md | Documents FPGA-side prerequisites and how to build/program the Mi‑V soft-core platform. |
| Microchip/miv-mpf300-splash/firmware/wolfcrypt-test/user_settings.h | wolfCrypt configuration for RV32 bare-metal, including PQC options and footprint tradeoffs. |
| Microchip/miv-mpf300-splash/firmware/wolfcrypt-test/miv-rv32-ram.ld | Linker script for wolfCrypt test/benchmark RAM execution from LSRAM. |
| Microchip/miv-mpf300-splash/firmware/wolfcrypt-test/Makefile | Build system for wolfcrypt_test + benchmark_test on the Mi‑V RV32 target. |
| Microchip/miv-mpf300-splash/firmware/wolfcrypt-test/main.c | Entry point wiring console/time/RNG hooks and running wolfcrypt_test/benchmark_test. |
| Microchip/miv-mpf300-splash/firmware/hello/miv-rv32-ram.ld | Linker script for the minimal hello-world image. |
| Microchip/miv-mpf300-splash/firmware/hello/Makefile | Build system for the hello-world firmware. |
| Microchip/miv-mpf300-splash/firmware/hello/main.c | Hello-world banner + LED heartbeat bring-up app. |
| Microchip/miv-mpf300-splash/firmware/fwtpm/user_settings.h | Combined wolfSSL/wolfTPM settings for fwTPM on Mi‑V RV32, including PQC toggle. |
| Microchip/miv-mpf300-splash/firmware/fwtpm/miv-rv32-ram.ld | Linker script for fwTPM RAM execution from LSRAM. |
| Microchip/miv-mpf300-splash/firmware/fwtpm/miv_sysserv.h | API for CoreSysServices_PF mailbox services (nonce + sNVM read/write). |
| Microchip/miv-mpf300-splash/firmware/fwtpm/miv_sysserv.c | Implementation of mailbox executor + nonce + sNVM page services. |
| Microchip/miv-mpf300-splash/firmware/fwtpm/Makefile | Build system for fwTPM firmware and its platform HAL integrations. |
| Microchip/miv-mpf300-splash/firmware/fwtpm/main.c | fwTPM server initialization, self-test, and UART command loop (raw swtpm + mssim). |
| Microchip/miv-mpf300-splash/firmware/fwtpm/host-client/swtpm_uart_bridge.py | Bridges TCP mssim frames to UART so standard wolfTPM examples can drive the device. |
| Microchip/miv-mpf300-splash/firmware/fwtpm/host-client/fwtpm_uart_test.py | Simple raw-swtpm UART test client (caps/PCR/random). |
| Microchip/miv-mpf300-splash/firmware/fwtpm/host-client/fwtpm_nv_persist_test.py | TPM-level NV persistence test script over UART. |
| Microchip/miv-mpf300-splash/firmware/fwtpm/fwtpm_rng_sysserv.c | TRNG seed hook feeding Hash-DRBG from System Controller nonce service. |
| Microchip/miv-mpf300-splash/firmware/fwtpm/fwtpm_nv_snvm.c | Persistent sNVM-backed NV HAL with RAM shadow and write-through flush. |
| Microchip/miv-mpf300-splash/firmware/fwtpm/fwtpm_clock_miv.c | fwTPM clock HAL using CoreTimer-based millisecond time. |
| Microchip/miv-mpf300-splash/firmware/common/startup.S | Minimal RV32 startup (gp/sp, bss/data init, trap, constructors, main). |
| Microchip/miv-mpf300-splash/firmware/common/retarget.c | Newlib syscall stubs and UART-backed stdout/stderr, plus simple heap. |
| Microchip/miv-mpf300-splash/firmware/common/miv_uart.h | CoreUARTapb minimal polled driver interface. |
| Microchip/miv-mpf300-splash/firmware/common/miv_uart.c | CoreUARTapb minimal polled driver implementation. |
| Microchip/miv-mpf300-splash/firmware/common/miv_time.h | CoreTimer-based timekeeping interface. |
| Microchip/miv-mpf300-splash/firmware/common/miv_time.c | CoreTimer-based monotonic ticks/millis and busy-wait delays. |
| Microchip/miv-mpf300-splash/firmware/common/miv_gpio.h | CoreGPIO minimal output driver interface. |
| Microchip/miv-mpf300-splash/firmware/common/miv_gpio.c | CoreGPIO minimal output driver implementation. |
| Microchip/miv-mpf300-splash/firmware/common/miv_board.h | Board/peripheral base addresses and clock configuration defaults. |
| Microchip/miv-mpf300-splash/.gitignore | Ignores firmware build artifacts and locally-generated FPGA project/job files. |
Comments suppressed due to low confidence (1)
Microchip/miv-mpf300-splash/firmware/fwtpm/miv_sysserv.c:228
- Same double-skip issue as the plaintext read:
rdWordOffset=4Uplusresp + 4means the authenticated sNVM read will not return the correct first bytes of the stored page data.
rc = miv_sysserv_exec(SS_CMD_SNVM_READ, (const uint8_t*)frame, 16U,
(uint8_t*)resp, 240U, 0U, 4U);
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
2d984ed to
facf0bd
Compare
facf0bd to
de1d679
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 32 out of 32 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (8)
Microchip/miv-mpf300-splash/firmware/fwtpm/fwtpm_nv_snvm.c:71
- When MIV_SNVM_NV_AUTH is enabled, the code falls back to a hard-coded default user secret key (MIV_SNVM_USK). This makes it easy to accidentally ship multiple images that all share the same sNVM authentication key, undermining the protection goal of authenticated-ciphertext mode.
#ifndef MIV_SNVM_USK
#define MIV_SNVM_USK { 0x77, 0x6F, 0x6C, 0x66, 0x54, 0x50, 0x4D, 0x4E, \
0x56, 0x6B, 0x65, 0x79 }
#endif
static const uint8_t g_nv_usk[MIV_SNVM_USK_LEN] = MIV_SNVM_USK;
Microchip/miv-mpf300-splash/firmware/fwtpm/fwtpm_nv_snvm.c:52
- The sNVM NV region geometry (MIV_SNVM_NV_BASE_PAGE + MIV_SNVM_NV_PAGES) is not compile-time validated against the device sNVM page count (221). If a user overrides these macros to an out-of-range region, the code will attempt invalid page accesses and fail at runtime.
#ifndef MIV_SNVM_NV_BASE_PAGE
#define MIV_SNVM_NV_BASE_PAGE 128U
#endif
#ifndef MIV_SNVM_NV_PAGES
#ifdef MIV_FWTPM_PQC
Microchip/miv-mpf300-splash/firmware/fwtpm/user_settings.h:41
- This bare-metal port doesn’t define XSLEEP_MS, while other bare-metal ports in this repo do. Without XSLEEP_MS, wolfTPM headers may fall back to nanosleep (which is typically unavailable on bare-metal/newlib and is a hard error on newer GCC), making the build toolchain-fragile.
/* ---- 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
Microchip/miv-mpf300-splash/firmware/fwtpm/miv_sysserv.c:38
- This file conditionally includes the wolfSSL source file wolfcrypt/src/misc.c to get ForceZero(). Since the build already compiles all wolfcrypt/src/*.c, including misc.c here risks duplicate symbol definitions (and makes the build more brittle). Prefer including the public header and linking the compiled object once.
#include <wolfssl/wolfcrypt/settings.h>
#ifdef NO_INLINE
#include <wolfssl/wolfcrypt/misc.h> /* ForceZero */
#else
#define WOLFSSL_MISC_INCLUDED
#include <wolfcrypt/src/misc.c> /* ForceZero (inline build) */
#endif
Microchip/miv-mpf300-splash/firmware/fwtpm/fwtpm_rng_sysserv.c:38
- This file conditionally includes the wolfSSL source file wolfcrypt/src/misc.c to get ForceZero(). Since the build already compiles wolfcrypt/src/*.c, including misc.c here can introduce duplicate definitions and makes the dependency on wolfSSL internals more fragile. Prefer including the public misc.h header.
#include <wolfssl/wolfcrypt/settings.h>
#include <wolfssl/wolfcrypt/error-crypt.h> /* RNG_FAILURE_E, BAD_FUNC_ARG */
#ifdef NO_INLINE
#include <wolfssl/wolfcrypt/misc.h> /* ForceZero */
#else
#define WOLFSSL_MISC_INCLUDED
#include <wolfcrypt/src/misc.c> /* ForceZero (inline build) */
#endif
Microchip/miv-mpf300-splash/firmware/fwtpm/Makefile:13
- The Makefile header says "GPLv2+", but the source headers in this directory (including user_settings.h) specify GPLv3+. This inconsistency can create licensing/compliance confusion; the Makefile header should match the actual licensing stated in the code it ships with.
# wolfTPM's tpm2_types.h XSLEEP_MS calls nanosleep, which GCC >= 14 rejects as an
# implicit declaration on bare-metal newlib, so SoftConsole is the default here.
#
# Copyright (C) 2006-2026 wolfSSL Inc. GPLv2+ (see source headers).
Microchip/miv-mpf300-splash/firmware/wolfcrypt-test/Makefile:23
- The Makefile header says "GPLv2+", but the newly added source headers under this example specify GPLv3+. Please align the Makefile’s stated license with the actual license used by the shipped source files to avoid compliance confusion.
# user_settings.h keeps GCC 15's stricter implicit-decl check happy.)
#
# Copyright (C) 2006-2026 wolfSSL Inc. GPLv2+ (see source headers).
Microchip/miv-mpf300-splash/firmware/hello/Makefile:12
- The Makefile header says "GPLv2+", but the source headers in this new example directory specify GPLv3+. Align the license statement in the Makefile header with the rest of the sources to avoid ambiguity.
# -march on GCC >= 12, hence ARCH = rv32imc_zicsr_zifencei. For the SoftConsole
# GCC 8.3.0 instead: make CROSS_COMPILE=riscv64-unknown-elf- ARCH=rv32imc
#
# Copyright (C) 2006-2026 wolfSSL Inc. GPLv2+ (see source headers).
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 32 out of 32 changed files in this pull request and generated 2 comments.
Suppressed comments (6)
Microchip/miv-mpf300-splash/firmware/fwtpm/miv_sysserv.c:229
- In authenticated sNVM mode,
miv_snvm_write_page_auth()copies the user secret key into the stackframebuffer and returns without scrubbing it. Since this code is part of the TPM’s trusted computing base, it’s better to zeroize the frame after the mailbox transaction completes.
((uint8_t*)frame)[0] = page;
memcpy((uint8_t*)frame + 4, data, MIV_SNVM_PAGE_DATA_AUTH);
memcpy((uint8_t*)frame + 4 + MIV_SNVM_PAGE_DATA_AUTH, usk, MIV_SNVM_USK_LEN);
return miv_sysserv_exec(SS_CMD_SNVM_WRITE_C, (const uint8_t*)frame, 252U,
Microchip/miv-mpf300-splash/firmware/fwtpm/miv_sysserv.c:252
- In authenticated sNVM mode,
miv_snvm_read_page_auth()places the user secret key into the stack request frame but does not scrub it (or the response buffer) before returning. Scrubbing these buffers reduces key exposure in memory dumps / post-mortem debug.
if (rc == 0) {
memcpy(data, (uint8_t*)resp + 4, MIV_SNVM_PAGE_DATA_AUTH);
}
return rc;
Microchip/miv-mpf300-splash/firmware/fwtpm/host-client/swtpm_uart_bridge.py:59
ser_read()usestime.time()for timeout measurement. Wall-clock time can jump (NTP, manual adjustments), which can cause spurious timeouts or hangs. Usetime.monotonic()for measuring elapsed time.
t0 = time.time()
while len(buf) < n:
c = ser.read(n - len(buf))
if c:
buf += c
Microchip/miv-mpf300-splash/firmware/hello/Makefile:11
- This Makefile header claims “GPLv2+ (see source headers)”, but the newly added source headers in this example use GPLv3-or-later wording. Please avoid hardcoding a conflicting license version in the Makefile comment (or update it to match the actual source headers).
# Copyright (C) 2006-2026 wolfSSL Inc. GPLv2+ (see source headers).
Microchip/miv-mpf300-splash/firmware/wolfcrypt-test/Makefile:22
- This Makefile header claims “GPLv2+ (see source headers)”, but the newly added source headers in this example use GPLv3-or-later wording. Please avoid hardcoding a conflicting license version in the Makefile comment (or update it to match the actual source headers).
# Copyright (C) 2006-2026 wolfSSL Inc. GPLv2+ (see source headers).
Microchip/miv-mpf300-splash/firmware/fwtpm/Makefile:12
- This Makefile header claims “GPLv2+ (see source headers)”, but the newly added source headers in this example use GPLv3-or-later wording. Please avoid hardcoding a conflicting license version in the Makefile comment (or update it to match the actual source headers).
# Copyright (C) 2006-2026 wolfSSL Inc. GPLv2+ (see source headers).
aidangarske
left a comment
There was a problem hiding this comment.
Skoll Code Review
Scan type: review-security
Overall recommendation: COMMENT
Findings: 2 total — 2 posted, 0 skipped
2 finding(s) posted as inline comments (see file-level comments below)
Posted findings
- [Medium] _sbrk() allows the heap cursor to move backwards on negative increments —
Microchip/miv-mpf300-splash/firmware/common/retarget.c:100-108 - [Medium] Authenticated sNVM mode ships with a shared default secret key —
Microchip/miv-mpf300-splash/firmware/fwtpm/fwtpm_nv_snvm.c:67-70
Review generated by Skoll
aidangarske
left a comment
There was a problem hiding this comment.
Skoll Code Review
Scan type: review-security
Overall 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] Default fwTPM NV storage is plaintext and unauthenticated —
Microchip/miv-mpf300-splash/firmware/fwtpm/fwtpm_nv_snvm.c:54-74,208-240 - [Medium] Authenticated NV mode has a shared hardcoded application key —
Microchip/miv-mpf300-splash/firmware/fwtpm/fwtpm_nv_snvm.c:63-71 - [Medium] UART framing can permanently stall the fwTPM —
Microchip/miv-mpf300-splash/firmware/fwtpm/main.c:106-117,224-248,299-318 - [Medium] NV shadow is committed before persistent page writes succeed —
Microchip/miv-mpf300-splash/firmware/fwtpm/fwtpm_nv_snvm.c:106-133,148-171 - [Low] Local bridge accepts unbounded command lengths —
Microchip/miv-mpf300-splash/firmware/fwtpm/host-client/swtpm_uart_bridge.py:89-97 - [Low] sNVM request and response buffers are not zeroized —
Microchip/miv-mpf300-splash/firmware/fwtpm/miv_sysserv.c:171-252 - [Low] Mailbox and persistence failure paths lack automated coverage —
Microchip/miv-mpf300-splash/firmware/fwtpm/miv_sysserv.c:55-153
Review generated by Skoll
aidangarske
left a comment
There was a problem hiding this comment.
Skoll Code Review
Scan type: review-securityOverall recommendation: REQUEST_CHANGES
Findings: 5 total — 5 posted, 0 skipped
Posted findings
- [High] Authenticated sNVM failures are treated as erased pages —
Microchip/miv-mpf300-splash/firmware/fwtpm/fwtpm_nv_snvm.c:79-82,120-126,304-323 - [High] Integrity-key read errors disable journal authentication —
Microchip/miv-mpf300-splash/firmware/fwtpm/fwtpm_nv_snvm.c:228-230,253-266,345-352 - [Medium] Journal HMAC cannot provide the claimed rollback detection —
Microchip/miv-mpf300-splash/firmware/fwtpm/fwtpm_nv_snvm.c:85-95,228-230,350-352; Microchip/miv-mpf300-splash/README.md:221-229 - [Low] Host clients accept unbounded device response lengths —
Microchip/miv-mpf300-splash/firmware/fwtpm/host-client/fwtpm_uart_test.py:49-54; Microchip/miv-mpf300-splash/firmware/fwtpm/host-client/fwtpm_nv_persist_test.py:59-69; Microchip/miv-mpf300-splash/firmware/fwtpm/host-client/swtpm_uart_bridge.py:117-120 - [Low] Boot-counter self-test overwrites state after any read error —
Microchip/miv-mpf300-splash/firmware/fwtpm/main.c:55-85
Review generated by Skoll
223011f to
eea7c00
Compare
…rtified TRNG) seeding the Hash-DRBG
Adds an example that runs wolfTPM's Firmware TPM (fTPM) in bare-metal on MIV_RV32 RISC-V soft-core built into the fabric of a Microchip PolarFire MPF300 FPGA. The Root of Trust, entropy, and secure storage all live in the FPGA design.
Included
Validation
Verified on MPF300 Splash Kit hardware: TRNG non-deterministic across cold boots, NV persistence across a real power cycle, and full
wolfcrypt_testPASS including ML-DSA/ML-KEM.Use case: a strong fit for aerospace and other harsh environments. A hardware root of trust that is part of the FPGA you already qualify, with no separate TPM to source or find board space for.