Skip to content

Commit 540ffb5

Browse files
committed
x86 FSP: add TPM-NV anti-rollback with a persistent version floor and boot audit counters
1 parent 3c80a90 commit 540ffb5

16 files changed

Lines changed: 1005 additions & 18 deletions

File tree

‎.gitignore‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,8 @@ tools/unit-tests/unit-update-flash-enc
181181
tools/unit-tests/unit-update-flash-db
182182
tools/unit-tests/unit-update-flash-enc-full
183183
tools/unit-tests/unit-update-ram
184+
tools/unit-tests/unit-boot-state
185+
tools/unit-tests/unit-boot-state-strict
184186
tools/unit-tests/unit-boot-x86-fsp
185187
tools/unit-tests/unit-image-rsa
186188
tools/unit-tests/unit-multiboot

‎arch.mk‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2292,6 +2292,10 @@ ifeq ("${FSP}", "1")
22922292
OBJS += src/x86/fsp_s.o
22932293
UPDATE_OBJS := src/update_disk.o
22942294
CFLAGS+=-DWOLFBOOT_UPDATE_DISK
2295+
ifeq ($(ANTI_ROLLBACK),1)
2296+
OBJS += src/boot_state.o
2297+
OBJS += src/boot_state_tpm.o
2298+
endif
22952299
ifeq ($(64BIT),1)
22962300
LDFLAGS += -m elf_x86_64 --oformat elf64-x86-64
22972301
CFLAGS += -m64

‎include/boot_state.h‎

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/* boot_state.h
2+
*
3+
* Persistent anti-rollback reference and boot audit counters.
4+
*
5+
* Copyright (C) 2026 wolfSSL Inc.
6+
*
7+
* This file is part of wolfBoot.
8+
*
9+
* wolfBoot is free software; you can redistribute it and/or modify
10+
* it under the terms of the GNU General Public License as published by
11+
* the Free Software Foundation; either version 2 of the License, or
12+
* (at your option) any later version.
13+
*
14+
* wolfBoot is distributed in the hope that it will be useful,
15+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
* GNU General Public License for more details.
18+
*
19+
* You should have received a copy of the GNU General Public License
20+
* along with this program; if not, write to the Free Software
21+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
22+
*/
23+
24+
/*
25+
* Anti-rollback keeps two version numbers plus a small set of audit counters.
26+
*
27+
* anchor the highest image version ever successfully booted. Monotonically
28+
* non-decreasing. Booting anything below it is a rollback.
29+
* record the version booted most recently. Follows the image actually
30+
* booted, so unlike the anchor it can go down.
31+
*
32+
* The requirement is that a validly signed but older image still boots, and
33+
* only leaves a persistent audit event behind; the single condition that
34+
* halts the boot is a version reference that cannot be trusted. So a corrupt
35+
* anchor is fatal, while a rollback is merely recorded.
36+
*
37+
* The anchor is initialised trust-on-first-use: the first image that boots
38+
* successfully on an unprovisioned unit defines the floor. That keeps units
39+
* deployable with no provisioning step, at the cost that whatever runs first
40+
* sets the baseline -- if a unit is powered up with a downgraded image before
41+
* it is trusted, that lower version becomes the anchor. Provision before
42+
* first boot where that matters.
43+
*
44+
* Storage is deliberately compact: one record of counters rather than an
45+
* event log, because the v1 backend is TPM NV, which is small and has limited
46+
* write endurance. Full detail still goes to the boot console.
47+
*
48+
* The backend is reached only through boot_state_backend_{read,write}(), so
49+
* moving the reference off the TPM later -- for instance into the anti-rollback
50+
* registers of a root-of-trust device -- is a backend swap and not a change to
51+
* any of the policy above.
52+
*/
53+
54+
#ifndef BOOT_STATE_H
55+
#define BOOT_STATE_H
56+
57+
#include <stdint.h>
58+
59+
#define BOOT_STATE_MAGIC 0x42535431UL /* "BST1" */
60+
#define BOOT_STATE_VERSION 1
61+
62+
/* Result of loading the persisted state. */
63+
#define BOOT_STATE_OK 0 /* loaded and trustworthy */
64+
#define BOOT_STATE_UNPROVISIONED 1 /* nothing stored yet; not an error */
65+
#define BOOT_STATE_CORRUPT (-1) /* stored but unusable -- fatal */
66+
#define BOOT_STATE_UNAVAILABLE (-2) /* backend could not be reached */
67+
68+
/* Failure codes recorded in last_fail_code. */
69+
#define BOOT_FAIL_NONE 0
70+
#define BOOT_FAIL_NO_IMAGE 1
71+
#define BOOT_FAIL_VERIFY_PRIMARY 2
72+
#define BOOT_FAIL_VERIFY_BOTH 3
73+
#define BOOT_FAIL_ROLLBACK 4
74+
75+
/* On-storage layout. Packed and explicitly sized so the representation does
76+
* not shift between the bootloader and any provisioning tool. */
77+
struct boot_state {
78+
uint32_t magic;
79+
uint32_t anchor;
80+
uint32_t record;
81+
uint32_t rollback_count;
82+
uint32_t last_rollback_from;
83+
uint32_t last_rollback_to;
84+
uint32_t boot_fail_count;
85+
uint32_t last_fail_code;
86+
uint16_t struct_version;
87+
uint16_t reserved;
88+
uint32_t crc;
89+
} __attribute__((packed));
90+
91+
/* Backend, implemented per platform. v1 uses TPM NV (src/boot_state_tpm.c). */
92+
int boot_state_backend_read(uint8_t *buf, uint32_t len);
93+
int boot_state_backend_write(const uint8_t *buf, uint32_t len);
94+
/* Write-locks the record until the next TPM reset, so the booted OS cannot
95+
* rewrite it. */
96+
int boot_state_backend_lock(void);
97+
98+
/* Load the persisted state. Returns one of the BOOT_STATE_* codes above. */
99+
int boot_state_load(struct boot_state *st);
100+
101+
/* Called once an image has verified, immediately before handing off.
102+
* Applies the rollback check, updates anchor and record, and persists. */
103+
void boot_state_on_boot(uint32_t booted_version);
104+
105+
/* Called when the boot policy has decided no image may be booted. Records the
106+
* failure so the evidence survives the power cycle. */
107+
void boot_state_on_failure(uint32_t fail_code);
108+
109+
#endif /* BOOT_STATE_H */

‎include/hal.h‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@ void hal_deinit();
5959

6060
void hal_init(void);
6161

62+
#ifdef WOLFBOOT_ANTI_ROLLBACK
63+
/* Halt the processor; never returns. Implemented by targets that enable
64+
* anti-rollback, and called when the stored version reference cannot be
65+
* trusted (see src/boot_state.c). */
66+
void hal_hold_in_reset(void);
67+
#endif
68+
6269
#ifdef WOLFBOOT_PARTITION_FILENAME
6370
/* Repoint the filesystem HAL's backing store at runtime, so one binary can
6471
* address several boot slots in turn. Implemented by hal/filesystem.c. */

‎include/tpm.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ extern WOLFTPM2_KEY wolftpm_srk;
4040
#ifndef WOLFBOOT_TPM_SEAL_NV_BASE
4141
#define WOLFBOOT_TPM_SEAL_NV_BASE 0x01400300
4242
#endif
43+
#ifndef WOLFBOOT_TPM_BOOT_STATE_NV_BASE
44+
#define WOLFBOOT_TPM_BOOT_STATE_NV_BASE 0x01400400
45+
#endif
4346
/* The PCR bank algorithm and its digest size must always agree. They used to
4447
* be defined together under a single #ifndef on the algorithm, which meant
4548
* overriding only WOLFBOOT_TPM_PCR_ALG left WOLFBOOT_TPM_PCR_DIG_SZ undefined

‎include/user_settings.h‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,11 @@ extern int tolower(int c);
183183
# endif
184184
#endif
185185

186-
#if defined(WOLFBOOT_TPM_KEYSTORE) || defined(WOLFBOOT_TPM_SEAL)
187-
/* TPM Parameter Encryption */
186+
#if defined(WOLFBOOT_TPM_KEYSTORE) || defined(WOLFBOOT_TPM_SEAL) || \
187+
(defined(WOLFBOOT_ANTI_ROLLBACK) && defined(WOLFBOOT_TPM) && \
188+
!defined(ARCH_SIM) && !defined(WOLFBOOT_TPM_NO_CHG_PLAT_AUTH))
189+
/* TPM Parameter Encryption. Anti-rollback needs it to send the random
190+
* platform auth it sets at handoff. */
188191
# define WOLFBOOT_TPM_PARMENC /* used in this file to gate features */
189192
#endif
190193

@@ -396,7 +399,7 @@ extern int tolower(int c);
396399
# if !defined(HAVE_ECC384)
397400
# define WOLFSSL_SP_NO_384
398401
# endif
399-
# if !defined(HAVE_ECC256)
402+
# if !defined(HAVE_ECC256) && !defined(WOLFBOOT_TPM_PARMENC)
400403
# define WOLFSSL_SP_NO_256
401404
# endif
402405
# endif

‎options.mk‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,21 @@ ifeq ($(ACPI),1)
5757
CFLAGS+=-D"WOLFBOOT_ACPI"
5858
endif
5959

60+
# Persistent anti-rollback reference plus boot audit counters, backed by TPM NV
61+
# (src/boot_state_tpm.c). A validly signed older image still boots and is
62+
# audited; a corrupt reference halts the boot. STRICT also halts when the
63+
# backend is unavailable or a state write fails.
64+
ifeq ($(ANTI_ROLLBACK),1)
65+
CFLAGS+=-D"WOLFBOOT_ANTI_ROLLBACK"
66+
# The v1 backend (src/boot_state_tpm.c) stores the version floor in TPM NV,
67+
# so anti-rollback has a hard wolfTPM dependency; force it on, as
68+
# MEASURED_BOOT does, rather than leave it to the example config.
69+
WOLFTPM:=1
70+
ifeq ($(ANTI_ROLLBACK_STRICT),1)
71+
CFLAGS+=-D"WOLFBOOT_ANTI_ROLLBACK_STRICT"
72+
endif
73+
endif
74+
6075
ifeq ($(USE_CLANG),1)
6176
ifeq ($(USE_GCC),1)
6277
$(error USE_CLANG=1 is incompatible with USE_GCC=1; set USE_GCC=0)

0 commit comments

Comments
 (0)