@@ -43,7 +43,7 @@ fwTPM can replace a hardware TPM for:
4343| ------| ------|
4444| ` fwtpm_command.c ` | TPM 2.0 command processor and dispatch table (~ 9500 lines) |
4545| ` fwtpm_io.c ` | Transport layer -- SWTPM TCP socket protocol (default) |
46- | ` fwtpm_nv.c ` | NV storage -- file-based (default), HAL-abstracted for embedded |
46+ | ` fwtpm_nv.c ` | NV storage -- file-based (default); HAL-abstracted, with a built-in append-only mode for write-once flash |
4747| ` fwtpm_tis.c ` | TIS register state machine (transport-agnostic) |
4848| ` fwtpm_tis_shm.c ` | POSIX shared memory + semaphore TIS transport |
4949| ` fwtpm_main.c ` | Server entry point, CLI argument parsing |
120120| ` --enable-fwtpm ` | Build ` fwtpm_server ` binary (alongside client library) |
121121| ` --enable-fwtpm-only ` | Build only ` fwtpm_server ` (no client library, examples, or tests) |
122122| ` --enable-swtpm ` | Use SWTPM TCP socket transport (ports 2321/2322) |
123+ | ` --enable-fwtpm-nv-appendonly ` | Append-only NV journal for write-once flash ports (off by default) |
123124| ` --enable-debug ` | Enable debug logging |
124125
125126| Compile Define | Set By |
129130| ` WOLFTPM_FWTPM_HAL ` | ` --enable-fwtpm ` without ` --enable-swtpm ` |
130131| ` WOLFTPM_FWTPM_TIS ` | ` --enable-fwtpm ` without ` --enable-swtpm ` |
131132| ` WOLFTPM_ADV_IO ` | Set with ` WOLFTPM_FWTPM_HAL ` |
133+ | ` WOLFTPM_FWTPM_NV_APPEND_ONLY ` | ` --enable-fwtpm-nv-appendonly ` (CMake ` WOLFTPM_FWTPM_NV_APPEND_ONLY=yes ` ) |
132134
133135
134136## Usage
@@ -190,6 +192,51 @@ NV indices) in `fwtpm_nv.bin` (configurable via `FWTPM_NV_FILE`). On first
190192start, seeds are randomly generated and saved. Subsequent starts reload
191193existing state.
192194
195+ #### NV Storage HAL (embedded ports)
196+
197+ NV access is abstracted behind ` FWTPM_NV_HAL ` (` read ` /` write ` /` erase ` /` ctx ` /
198+ ` maxSize ` /` get_integrity_key ` ). The default backend is the file above; an
199+ embedded port supplies its own HAL and registers it before ` FWTPM_Init() ` .
200+
201+ The journal is log-structured. On a byte-addressable backend (the file
202+ default) it writes TLV entries at byte-granular offsets, rewrites the header in
203+ place, and rewrites a trailing integrity MAC after every append. Internal flash
204+ and NOR are write-once and program-granularity aligned, so they cannot service
205+ in-place rewrites. Build with ` --enable-fwtpm-nv-appendonly `
206+ (` -DWOLFTPM_FWTPM_NV_APPEND_ONLY ` ) and set ` appendOnly ` and ` writeAlign ` on the
207+ HAL; the journal then runs in append-only mode: the header is written only at
208+ compaction, ` writePos ` is derived by scanning on load, and each commit is
209+ sealed with an appended MAC-checkpoint entry padded up to ` writeAlign ` . The
210+ existing ` read ` /` write ` /` erase ` HAL is the integration point - no separate
211+ adapter.
212+
213+ ``` c
214+ /* Native flash HAL. In append-only mode the journal only ever calls write()
215+ * with writeAlign-aligned, forward, into-erased bytes, so write() is a simple
216+ * flash program; erase() erases the region (sector loop); read() reads raw. */
217+ FWTPM_NV_HAL hal;
218+ XMEMSET (&hal, 0 , sizeof (hal));
219+ hal.read = myRead; hal.write = myProgram; hal.erase = myErase;
220+ hal.ctx = myCtx; hal.maxSize = NV_SIZE ;
221+ hal.appendOnly = 1 ;
222+ hal.writeAlign = PROG_SIZE ; /* flash word size, e.g. 16 (STM32H5) */
223+ hal.get_integrity_key = myDeviceSecret;/* recommended on flash */
224+ FWTPM_NV_SetHAL (&ctx, &hal); /* before FWTPM_Init() * /
225+ ```
226+
227+ In append-only mode the journal buffers a pending program granule internally and
228+ flushes full, aligned granules through `write()`, so a programmed cell is never
229+ rewritten and a whole sector is erased only on compaction. The header sector is
230+ therefore not erased on every append, and a torn final commit (e.g. on power
231+ loss) is ignored on the next load while all previously committed state survives.
232+ A `get_integrity_key` callback is strongly recommended on flash so the MAC
233+ checkpoints authenticate the journal and reject a torn or tampered tail.
234+ `writeAlign <= 1` selects no buffering, so byte-writable NV (EEPROM/FRAM) works
235+ with a plain `write()`. Compaction still erases the whole region before
236+ rewriting, so a power loss during compaction itself remains a vulnerable window
237+ (a future two-region ping-pong layout would close it). See
238+ `src/fwtpm/ports/README.md` for the full porting guide.
239+
193240
194241## Supported TPM 2.0 Commands
195242
0 commit comments