Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
191 changes: 132 additions & 59 deletions Microchip/fwtpm-polarfire-miv/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ runs on harts 1-3 via HSS AMP. Build lives at `firmware/fwtpm-u54/`.
- HSS AMP boot: hart 0 (E51) runs HSS, harts 1–3 run Linux (S-mode under
OpenSBI), hart 4 runs the fwTPM payload at `0x91C00000` in M-mode with
`skip-opensbi: true`.
- Transport: mailbox + console ring + TIS register block in L2 LIM
(`0x08000000`, 16 KB used). Hart 4 polls `mbox.cmd_ready`; the full
Linux <-> hart 4 round-trip works under HSS AMP (verified on the Video
Kit). The backend is build-selectable (`FWTPM_XPORT`) -- see
"Transport options" below.
- Transport: mailbox + console ring + TIS register block in shared memory
(16 KB). Hart 4 polls `mbox.cmd_ready`. Cross-hart cache coherency is the
key constraint: the default `DDR_WCB` build places the mailbox in a
non-cached DDR window so it is coherent for hart 4. The server -> Linux
path is reliable; the interactive round-trip is being hardened -- see
"Transport and cache coherency" below. Backend is build-selectable
(`FWTPM_XPORT`).
- NV: 64 KB RAM-backed (volatile, lost on reset). FRAM driver swappable
via the same `FWTPM_NV_HAL` interface.
- Clock: CLINT `mtime` (RDTIME CSR is illegal in M-mode on this part).
Expand Down Expand Up @@ -58,44 +60,79 @@ scratchpad way count in HSS's `mss_sw_config.h` and rebuild — see
`hart-software-services/baremetal/polarfire-soc-bare-metal-library/src/platform/mpfs_hal/common/mss_l2_cache.c`
for the way-mask layout.

#### Linux <-> hart 4 round-trip (verified on HSS AMP)
#### Linux <-> hart 4 transport and cache coherency

Both directions work under HSS AMP. `fwtpm_smoke.py` Phase 1 (hart 4 ->
Linux read) and Phase 2 (Linux -> hart 4 command, with a nonce the server
echoes back) both pass with the default `LIM_CACHED` transport, verified
on the MPFS250T Video Kit and reproducible across runs.
The hard part of this port is cache coherency between cache-coherent Linux
(U54 harts 1-3 plus the shared L2) and the bare-metal hart 4. The U54 L1
data cache is write-back with **no cache-maintenance instruction** (the
core predates Zicbom), so a *cacheable* shared mailbox is not coherent for
hart 4: it stale-reads Linux's writes from its own L1d, and its writes do
not reliably reach the shared L2. The mailbox must live in genuinely
non-cached memory.

An earlier standalone bring-up (bare-metal `skip-opensbi`, no HSS) saw
Phase 2 time out: hart 4's write-through L1d held a stale `cmd_ready`, and
the U54 has no L1d invalidate (pre-Zicbom) so a fence could not fix it.
That setup never configured hart 4's PMP/PMA. Under HSS AMP, HSS sets up
hart 4 (`boot_service(u54_4) :: SetupPMP` in the boot log) and the
cacheable L2 LIM mailbox is coherent for hart 4 -- so the alternate
transports below are not needed in this configuration.
Observed on the MPFS250T Video Kit with the stock Yocto HSS:

##### Transport options (`FWTPM_XPORT`)
- **server -> Linux** (hart 4 writes, Linux reads): reliable through the
console ring and the mailbox status fields. The live demo uses this path.
- **Linux -> hart 4** (Linux writes a command, hart 4 reads it): reliable
only when the mailbox is non-cached for hart 4. The interactive TIS
round-trip (driving TPM commands from Linux) is still being
hardened over the write-combine DDR window and is not yet dependable.

`FWTPM_XPORT=DDR_WCB` (the default) puts the mailbox in the 0xC0000000
non-cached DDR window, which both hart 4 and Linux access uncached.
`LIM_CACHED` works only under an HSS that configures hart-4 PMA for the LIM
region (an earlier bring-up's HSS did -- its boot log showed
`boot_service(u54_4) :: SetupPMP`); the stock Yocto HSS does not.

The transport backend is build-selectable. Bench results on the Video Kit:
##### What works today: TPM 2.0 caps on hart 4

| `FWTPM_XPORT=` | What it does | Result |
|----------------|--------------|--------|
| `LIM_CACHED` (default) | mailbox in cacheable L2 LIM | **works** (Phase 1+2 pass) |
| `DDR_NONCACHED` | mailbox in the 0x1400000000 non-cached DDR alias; `startup.S` adds a PMP entry | **works** (Phase 1+2 pass; Linux reaches the alias via `/dev/mem`) |
| `L1D_OFF` | LIM mailbox, hart 4 L1d disabled via U54 CSR `0x7C1` | **non-functional** on this U54 -- the `csrw 0x7C1` halts hart 4 before boot (`progress=0`). Unnecessary since `LIM_CACHED` is coherent. |
| `IHC` | Microchip Inter-Hart Comm stub (bitstream-gated) | stub; Phase 2 skipped |
Because server -> Linux is reliable, hart 4 runs real TPM 2.0 commands
itself at startup -- `TPM2_Startup`, `TPM2_GetCapability`
(manufacturer/vendor), and `TPM2_GetRandom` (seeded by the System
Controller hardware TRNG) -- and prints the results to the console ring.
Read them from Linux:

```bash
make clean && make FWTPM_XPORT=DDR_NONCACHED
python3 ./linux-client/fwtpm_caps.py
```

The console-ring output includes, e.g.:

```
=== fTPM TPM2_GetCapability (executed on hart 4) ===
Manufacturer = 0x574f4c46 "WOLF"
VendorString = 0x776f6c66 "wolf"
FirmwareVer = 0x00000000
TPM2_GetRandom(16) = 1a 2b 3c ...
=== end capabilities ===
```

This is a genuine TPM 2.0 GetCapability -- the same query as
`wolftpm/examples/wrap/caps` -- executing inside the fTPM, observed live
from Linux, with no discrete TPM and no kernel driver. See
"Running the stock wolfTPM examples" below for the cross-compiled-client
path (pending a dependable interactive round-trip).

##### Transport options (`FWTPM_XPORT`)

| `FWTPM_XPORT=` | Mailbox location | Status (stock Yocto HSS) |
|----------------|------------------|--------------------------|
| `DDR_WCB` (default) | 0xC0000000 non-cached write-combine DDR | coherent for hart 4 + Linux; used by the caps demo. Interactive round-trip being hardened. |
| `LIM_CACHED` | cacheable L2 LIM | coherent only under an HSS that sets up hart-4 PMA for LIM; not on the stock HSS |
| `DDR_NONCACHED` | 0x1400000000 non-cached 64-bit DDR alias (+ PMP) | needs the non-cached PMA configured for hart 4 |
| `L1D_OFF` | LIM, hart 4 L1d off via U54 CSR `0x7C1` | non-functional: `csrw 0x7C1` halts hart 4 before boot (`progress=0`) |
| `IHC` | Microchip Inter-Hart Comm stub | bitstream-gated stub |

```bash
make clean && make FWTPM_XPORT=DDR_WCB
# ... flash, boot ...
sudo ./linux-client/fwtpm_smoke.py --xport ddr_noncached
python3 ./linux-client/fwtpm_caps.py
```

Phase 2 writes a rolling **nonce** into `cmd_ready` that the server echoes
into `mbox.echo_nonce`; a matching echo proves hart 4 observed the new
write (not a stale line). The boot/trap breadcrumbs always stay in L2 LIM
at `0x08000000` (read them with `fwtpm_mbox_dump.py`), even when
`DDR_NONCACHED` relocates the live mailbox, so a candidate that faults
before `main()` still reports its trap.
The boot/trap breadcrumbs always stay in L2 LIM at `0x08000000` (read with
`fwtpm_caps.py --dump`), even when the live mailbox relocates, so a candidate
that faults before `main()` still reports its trap.

### Files

Expand Down Expand Up @@ -175,20 +212,18 @@ Linux via `/dev/mem`.

### Reading server state from Linux

`linux-client/fwtpm_mbox_dump.py` (run as `root`) decodes the mailbox +
`linux-client/fwtpm_caps.py` (run as `root`) reads the mailbox +
boot-debug fields + console ring through `/dev/mem`. The L2 LIM region at
`0x08000000` is not part of Linux's System RAM map, so `/dev/mem` can map
it even under a stock `CONFIG_STRICT_DEVMEM=y` kernel -- no kernel rebuild
needed (verified on the Video Kit BSP). The only device-tree change
required is the cpu4-disable overlay so Linux releases hart 4.

For the default `LIM_CACHED` build the live mailbox and console ring sit
in LIM at `0x08000000`. Under `FWTPM_XPORT=DDR_NONCACHED` they relocate
to the non-cached DDR alias `0x1400000000`, while the boot/trap
breadcrumbs stay pinned in LIM; pass `--xport ddr_noncached` (or
`--base 0x1400000000`) to point the script at the live region, and it
still reads those breadcrumbs from LIM automatically. `fwtpm_smoke.py`
takes the same `--xport`/`--base` options.
For the default `DDR_WCB` build the live mailbox and console ring sit in
the non-cached DDR window at `0xC0000000`, while the boot/trap breadcrumbs
stay pinned in LIM at `0x08000000`. `fwtpm_caps.py` defaults to that base; for a different build pass
`--xport <backend>` (or `--base <addr>`) to point it at the live region,
and it still reads those breadcrumbs from LIM automatically.

### Boot debug fields

Expand Down Expand Up @@ -224,28 +259,66 @@ would drive a TIS register access by writing `regs->reg_addr`,
then setting `mbox->cmd_ready` to a nonzero nonce (the server echoes it
back in `mbox->echo_nonce`).

### Linux client smoke test
### Linux client: `fwtpm_caps.py`

`firmware/fwtpm-u54/linux-client/fwtpm_caps.py` is the single read-only
client, run on the target as `root`. It reads the mailbox status and the
console ring over `/dev/mem`, prints the hart-4 caps self-test output
(GetCapability manufacturer/vendor + GetRandom), and reports PASS/FAIL:

```
python3 fwtpm_caps.py # caps demo + transport/identity checks
python3 fwtpm_caps.py --dump # also print the full ring + breadcrumbs
```

It defaults to the `DDR_WCB` mailbox base; pass `--xport <backend>` or
`--base <addr>` to match a different `FWTPM_XPORT` build. This is the
working analogue of the cross-compiled `wolftpm/examples/wrap/caps`: the
caps run on hart 4 (the reliable server -> Linux path) rather than being
driven from Linux, since the interactive round-trip is still being
hardened.

`firmware/fwtpm-u54/linux-client/` contains two helper scripts to run
on the target as `root`:
### Running the stock wolfTPM examples (cross-compile)

The reliable capabilities demo today runs *inside the fTPM* on hart 4 (see
"What works today" above) and is read with `fwtpm_caps.py`. Running
the **stock** `wolftpm/examples/wrap/caps` binary *from Linux* against this
fTPM additionally needs the interactive Linux -> hart 4 round-trip plus a
wolfTPM IO callback that bridges TPM2 calls onto the shared-memory mailbox
(roadmap items 1-2). The cross-compile itself, for when that lands:

```bash
# RISC-V 64-bit Linux cross toolchain (Yocto SDK, or a distro
# riscv64-linux-gnu- gcc). Build wolfSSL, then wolfTPM:
export CROSS=riscv64-linux-gnu

cd wolfssl
./configure --host=$CROSS CC=$CROSS-gcc --enable-cryptocb \
--enable-rsa --enable-ecc --disable-examples --prefix=$PWD/../rv64-root
make && make install

cd ../wolftpm
./configure --host=$CROSS CC=$CROSS-gcc \
--with-wolfcrypt=$PWD/../rv64-root --prefix=$PWD/../rv64-root
make
file examples/wrap/caps # ELF 64-bit LSB executable, UCB RISC-V
```

- `fwtpm_mbox_dump.py` — decodes the mailbox + boot debug fields +
console ring buffer from `/dev/mem`. Diagnostic, always safe.
- `fwtpm_smoke.py` — Phase 1 reads `FWTPM_TIS_REGS.magic` + `did_vid`
(server -> Linux direction); Phase 2 drives a TIS read of `TPM_DID_VID`
via the mailbox (Linux -> server direction, with a nonce the server
echoes back). Both phases **pass** under HSS AMP on the default
`LIM_CACHED` build. Pass `--xport <backend>` to match the firmware's
`FWTPM_XPORT` build (e.g. `ddr_noncached` relocates the live mailbox).
Stage `examples/wrap/caps` onto the target (over the SD rootfs or `scp`).
Until the mailbox IO bridge exists it has no fTPM endpoint to talk to;
with a `/dev/tpmrm0` shim or a custom `TPM2_IoCb` over the mailbox it runs
unchanged. The on-hart-4 self-test in `main.c` (`fwtpm_caps_selftest`) is
the working stand-in until then.

## Roadmap

1. **Linux <-> hart 4 transport** — DONE under HSS AMP. The full
round-trip works on the default `LIM_CACHED` transport (verified on
the Video Kit); `DDR_NONCACHED` also works as a fully-uncached
alternative. The earlier "Phase 2 times out" limitation was specific
to the standalone `skip-opensbi` bring-up that did not set up hart 4's
PMP/PMA. Optional future enhancements:
1. **Reliable interactive transport** — the server -> Linux path works
(used by the on-hart-4 caps demo); the Linux -> hart 4 round-trip needs
the mailbox in genuinely non-cached memory. `DDR_WCB` (0xC0000000) is
coherent for both hart 4 and Linux, but the write-combine ordering for
the interactive path is still being hardened. The clean fix is an HSS
that configures hart-4 coherency (an earlier bring-up's HSS did, via
`boot_service(u54_4) :: SetupPMP`). Related directions:
- `IHC` (Microchip Inter-Hart Communication) for an interrupt-driven
mailbox -- gated on the Libero bitstream containing the IHC IP.
- A kernel doorbell ringing `CLINT_MSIP[4]` so hart 4 can WFI between
Expand All @@ -255,7 +328,7 @@ on the target as `root`:
in uncached DTIM (`0x01000000`) / non-cached DDR (SEG1) and an SBI
vendor-EID doorbell available; tracked as a parallel investigation.
(`FWTPM_XPORT=L1D_OFF` is a dead end: the U54 `csrw 0x7C1` halts
hart 4 before boot, and it is unnecessary since LIM is coherent here.)
hart 4 before boot.)
2. **Linux userspace TIS HAL** — once (1) lands, wrap the mailbox
behind wolfTPM's HAL callbacks so the `wolftpm/examples/` tree
runs unchanged on the target.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ build/
*.elf
*.map
*.dtbo
payload.bin
payload*.bin
u-boot.bin

# Python client bytecode
Expand Down
23 changes: 13 additions & 10 deletions Microchip/fwtpm-polarfire-miv/firmware/fwtpm-u54/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,18 @@ DEFS = -DWOLFSSL_USER_SETTINGS
DEFS += -DWOLFTPM_USER_SETTINGS
DEFS += -DMPFS250T_U54_FWTPM

# Transport backend for the shared TIS mailbox.
# LIM_CACHED default -- cacheable L2 LIM; full round-trip works
# under HSS AMP (verified on the Video Kit)
# DDR_NONCACHED mailbox in 0x1400000000 non-cached DDR alias (+ PMP);
# also works (fully-uncached alternative)
# Transport backend for the shared TIS mailbox. The U54 L1d is write-back
# with no cache-maintenance instruction, so the mailbox must live in
# genuinely non-cached memory to be coherent for hart 4 under the stock HSS.
# DDR_WCB default -- mailbox in the 0xC0000000 non-cached DDR window;
# coherent for hart 4 and Linux (used by the demo)
# LIM_CACHED cacheable L2 LIM; coherent only under an HSS that sets up
# hart-4 PMA for LIM (not the stock Yocto HSS)
# DDR_NONCACHED 0x1400000000 non-cached 64-bit DDR alias (+ PMP)
# L1D_OFF dead end: csrw 0x7C1 halts hart 4 (see startup.S)
# IHC Microchip Inter-Hart Comm stub (bitstream-gated)
# Build a candidate with e.g.: make clean && make FWTPM_XPORT=DDR_NONCACHED
FWTPM_XPORT ?= LIM_CACHED
# Build a candidate with e.g.: make clean && make FWTPM_XPORT=LIM_CACHED
FWTPM_XPORT ?= DDR_WCB
XPORT_DEF = -DFWTPM_XPORT_$(FWTPM_XPORT)
DEFS += $(XPORT_DEF)

Expand Down Expand Up @@ -206,9 +209,9 @@ help:
@echo " make clean"
@echo ""
@echo "Variables:"
@echo " FWTPM_XPORT= Transport backend (coherency spike):"
@echo " LIM_CACHED (default) | DDR_NONCACHED |"
@echo " L1D_OFF | IHC"
@echo " FWTPM_XPORT= Transport backend:"
@echo " DDR_WCB (default) | LIM_CACHED |"
@echo " DDR_NONCACHED | L1D_OFF | IHC"
@echo " FWTPM_RNG= DRBG seed source:"
@echo " SCB_NONCE (default, HW TRNG) | JITTER (dev-only)"
@echo " FWTPM_DEV_INSECURE_RNG=1 acknowledge the JITTER dev seed (else #error)"
Expand Down
Loading