Skip to content

Commit d9a062b

Browse files
committed
aarch64_efi: measure kernel into firmware TPM via EFI_TCG2
Adds MEASURED_BOOT_TCG2: wolfBoot extends the verified kernel into the platform firmware TPM (PCR MEASURED_PCR_A) via EFI_TCG2_PROTOCOL HashLogExtendEvent before handoff, no wolfTPM transport. Validated on the NVIDIA Orin Nano fTPM (TPM present, SHA-256+SHA-384 banks, measured into PCR 9).
1 parent 1963856 commit d9a062b

4 files changed

Lines changed: 156 additions & 0 deletions

File tree

config/examples/aarch64_efi.config

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,10 @@ SPMATH=0
2727
# required for keytools
2828
WOLFBOOT_SECTOR_SIZE?=0x1000
2929
WOLFBOOT_NO_PARTITIONS=1
30+
# Measured boot: extend the verified kernel into the platform firmware TPM via
31+
# EFI_TCG2_PROTOCOL (PCR MEASURED_PCR_A) before handoff, using the firmware's
32+
# own TPM stack -- no wolfTPM transport. Best-effort: skips cleanly if the
33+
# firmware exposes no TCG2/TPM. Validated on the NVIDIA Orin Nano fTPM
34+
# (TPM present, SHA-256 + SHA-384 PCR banks).
35+
MEASURED_BOOT_TCG2=1
36+
MEASURED_PCR_A?=9

docs/Targets.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7953,6 +7953,19 @@ UEFI Secure Boot above is enforced by the edk2 firmware. On a production Jetson
79537953
79547954
These are burned with `odmfuse.sh`/`tegrasign` from `Linux_for_Tegra/` and are irreversible. They are the final production step and are intentionally NOT part of this port -- the development board stays in unfused/dev mode. Consult the NVIDIA Jetson Linux "Secure Boot" documentation for the current `odmfuse.sh` procedure for your module before burning anything. Once fused, the fused firmware enforces UEFI Secure Boot, which enforces `wolfboot.efi`, which enforces the kernel -- a complete hardware root of trust.
79557955
7956+
### Measured boot (firmware TPM via EFI_TCG2)
7957+
7958+
The Jetson firmware provides a TPM 2.0 (an OP-TEE fTPM) behind `EFI_TCG2_PROTOCOL` and already measures the early boot chain into PCRs. wolfBoot extends that chain to the OS: with `MEASURED_BOOT_TCG2=1` (default in `config/examples/aarch64_efi.config`), it measures the verified kernel image into PCR `MEASURED_PCR_A` (default 9) with `HashLogExtendEvent` just before handoff, appending a `wolfBoot kernel.img` record to the firmware event log. This is the same consumer pattern U-Boot uses -- the firmware / fTPM performs the hashing, PCR extend and log append, so wolfBoot needs no TPM transport driver of its own and pulls in no wolfTPM.
7959+
7960+
It is best-effort and does not disturb boot: wolfBoot logs the TPM capability and, if the platform exposes no TCG2 protocol or reports no TPM present, skips the measurement and continues. On the Orin Nano the console shows:
7961+
7962+
```
7963+
TCG2: TPM present=1 activeBanks=0x6 banks=2
7964+
TCG2: measured kernel (43091976 bytes) into PCR 9
7965+
```
7966+
7967+
`activeBanks=0x6` is the SHA-256 (0x2) + SHA-384 (0x4) PCR banks; the kernel is extended into PCR 9 in both. An attestation client can then read PCR 9 plus the TCG2 event log to confirm exactly which kernel wolfBoot verified and booted. Choose `MEASURED_PCR_A` to fit the platform's PCR allocation (0-7 are firmware-owned; 8-15 are for OS/loader use). Note the edk2 firmware separately measures the loaded `wolfboot.efi` image itself into its own PCRs via `LoadImage`, so the firmware-verifies-wolfBoot and wolfBoot-measures-kernel events are distinct entries in the log.
7968+
79567969
## Intel x86_64 with Intel FSP support
79577970
79587971
This setup is more complex than the UEFI approach described earlier, but allows

hal/aarch64_efi.c

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,127 @@ static void panic()
114114
while(1) {}
115115
}
116116

117+
#ifdef WOLFBOOT_MEASURED_BOOT_EFI_TCG2
118+
/* EFI TCG2 protocol (TCG EFI Protocol Specification) -- not provided by gnu-efi.
119+
* Used to measure the wolfBoot-verified kernel into a firmware-TPM PCR via
120+
* HashLogExtendEvent before StartImage, extending the platform's measured-boot
121+
* chain to the OS. This is the same consumer pattern U-Boot uses: the platform
122+
* firmware / fTPM performs the hashing, PCR extend and event-log append behind
123+
* EFI_TCG2_PROTOCOL, so wolfBoot needs no TPM transport driver of its own. */
124+
#define WOLFBOOT_TCG2_PROTOCOL_GUID \
125+
{ 0x607f766c, 0x7455, 0x42be, {0x93,0x0b,0xe4,0xd7,0x6d,0xb2,0x72,0x0f} }
126+
127+
#define WOLFBOOT_TCG2_EV_IPL 0x0000000DUL /* boot-loader / OS measurement */
128+
129+
typedef struct {
130+
UINT8 Major;
131+
UINT8 Minor;
132+
} WOLFBOOT_TCG2_VERSION;
133+
134+
typedef struct {
135+
UINT8 Size;
136+
WOLFBOOT_TCG2_VERSION StructureVersion;
137+
WOLFBOOT_TCG2_VERSION ProtocolVersion;
138+
UINT32 HashAlgorithmBitmap;
139+
UINT32 SupportedEventLogs;
140+
BOOLEAN TPMPresentFlag;
141+
UINT16 MaxCommandSize;
142+
UINT16 MaxResponseSize;
143+
UINT32 ManufacturerID;
144+
UINT32 NumberOfPcrBanks;
145+
UINT32 ActivePcrBanks;
146+
} WOLFBOOT_TCG2_CAPABILITY;
147+
148+
typedef struct {
149+
UINT32 HeaderSize;
150+
UINT16 HeaderVersion;
151+
UINT32 PCRIndex;
152+
UINT32 EventType;
153+
} __attribute__((packed)) WOLFBOOT_TCG2_EVENT_HEADER;
154+
155+
typedef struct {
156+
UINT32 Size;
157+
WOLFBOOT_TCG2_EVENT_HEADER Header;
158+
UINT8 Event[1];
159+
} __attribute__((packed)) WOLFBOOT_TCG2_EVENT;
160+
161+
struct wolfboot_tcg2_protocol;
162+
typedef struct wolfboot_tcg2_protocol WOLFBOOT_TCG2_PROTOCOL;
163+
struct wolfboot_tcg2_protocol {
164+
EFI_STATUS (EFIAPI *GetCapability)(WOLFBOOT_TCG2_PROTOCOL *This,
165+
WOLFBOOT_TCG2_CAPABILITY *Cap);
166+
void *GetEventLog;
167+
EFI_STATUS (EFIAPI *HashLogExtendEvent)(WOLFBOOT_TCG2_PROTOCOL *This,
168+
UINT64 Flags,
169+
EFI_PHYSICAL_ADDRESS DataToHash,
170+
UINT64 DataToHashLen,
171+
WOLFBOOT_TCG2_EVENT *Event);
172+
void *SubmitCommand;
173+
void *GetActivePcrBanks;
174+
void *SetActivePcrBanks;
175+
void *GetResultOfSetActivePcrBanks;
176+
};
177+
178+
/* Measure the verified kernel image into the firmware TPM (PCR
179+
* WOLFBOOT_MEASURED_PCR_A) via EFI_TCG2_PROTOCOL, before handing off. Best
180+
* effort: if the firmware exposes no TCG2 protocol or reports no TPM present,
181+
* this logs and returns without failing the boot. */
182+
static void tcg2_measure_kernel(void *addr, uint32_t size)
183+
{
184+
EFI_GUID guid = WOLFBOOT_TCG2_PROTOCOL_GUID;
185+
WOLFBOOT_TCG2_PROTOCOL *tcg2 = NULL;
186+
WOLFBOOT_TCG2_CAPABILITY cap;
187+
EFI_STATUS status;
188+
static const char desc[] = "wolfBoot kernel.img";
189+
uint8_t buf[sizeof(WOLFBOOT_TCG2_EVENT) + sizeof(desc)];
190+
WOLFBOOT_TCG2_EVENT *evt = (WOLFBOOT_TCG2_EVENT *)buf;
191+
192+
status = uefi_call_wrapper(BS->LocateProtocol, 3, &guid, NULL,
193+
(void **)&tcg2);
194+
if (status != EFI_SUCCESS || tcg2 == NULL) {
195+
wolfBoot_printf("TCG2: protocol not found (0x%lx); measure skipped\n",
196+
(unsigned long)status);
197+
return;
198+
}
199+
200+
ZeroMem(&cap, sizeof(cap));
201+
cap.Size = (UINT8)sizeof(cap);
202+
status = uefi_call_wrapper(tcg2->GetCapability, 2, tcg2, &cap);
203+
if (status != EFI_SUCCESS) {
204+
wolfBoot_printf("TCG2: GetCapability failed (0x%lx)\n",
205+
(unsigned long)status);
206+
return;
207+
}
208+
wolfBoot_printf("TCG2: TPM present=%d activeBanks=0x%x banks=%d\n",
209+
(int)cap.TPMPresentFlag, (unsigned)cap.ActivePcrBanks,
210+
(int)cap.NumberOfPcrBanks);
211+
if (!cap.TPMPresentFlag) {
212+
wolfBoot_printf("TCG2: no firmware TPM present; measure skipped\n");
213+
return;
214+
}
215+
216+
ZeroMem(buf, sizeof(buf));
217+
evt->Size = (UINT32)sizeof(buf);
218+
evt->Header.HeaderSize = (UINT32)sizeof(WOLFBOOT_TCG2_EVENT_HEADER);
219+
evt->Header.HeaderVersion = 1;
220+
evt->Header.PCRIndex = (UINT32)WOLFBOOT_MEASURED_PCR_A;
221+
evt->Header.EventType = (UINT32)WOLFBOOT_TCG2_EV_IPL;
222+
CopyMem(evt->Event, (void *)desc, sizeof(desc));
223+
224+
status = uefi_call_wrapper(tcg2->HashLogExtendEvent, 5, tcg2,
225+
(UINT64)0,
226+
(EFI_PHYSICAL_ADDRESS)(uintptr_t)addr,
227+
(UINT64)size, evt);
228+
if (status != EFI_SUCCESS) {
229+
wolfBoot_printf("TCG2: HashLogExtendEvent failed (0x%lx)\n",
230+
(unsigned long)status);
231+
return;
232+
}
233+
wolfBoot_printf("TCG2: measured kernel (%u bytes) into PCR %d\n",
234+
size, (int)WOLFBOOT_MEASURED_PCR_A);
235+
}
236+
#endif /* WOLFBOOT_MEASURED_BOOT_EFI_TCG2 */
237+
117238
void RAMFUNCTION aarch64_efi_do_boot(uint32_t *boot_addr)
118239
{
119240
uint32_t *size;
@@ -136,6 +257,11 @@ void RAMFUNCTION aarch64_efi_do_boot(uint32_t *boot_addr)
136257

137258
SetDevicePathEndNode(&mem_path_device[1].Header);
138259

260+
#ifdef WOLFBOOT_MEASURED_BOOT_EFI_TCG2
261+
/* Extend the verified kernel into the firmware TPM before handoff. */
262+
tcg2_measure_kernel((void*)boot_addr, *size);
263+
#endif
264+
139265
wolfBoot_printf("Staging kernel at address %p, size: %u\n", (void*)boot_addr, *size);
140266
status = uefi_call_wrapper(gSystemTable->BootServices->LoadImage,
141267
6,

options.mk

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,16 @@ ifeq ($(MEASURED_BOOT),1)
102102
endif
103103
endif
104104

105+
## Measured boot via the platform firmware TPM behind EFI_TCG2_PROTOCOL, for
106+
## UEFI-application targets (e.g. aarch64_efi on NVIDIA Jetson). wolfBoot
107+
## measures the verified next-stage image into a PCR with HashLogExtendEvent;
108+
## the firmware / fTPM owns the TPM, so this pulls in NO wolfTPM transport.
109+
ifeq ($(MEASURED_BOOT_TCG2),1)
110+
MEASURED_PCR_A ?= 9
111+
CFLAGS+=-D"WOLFBOOT_MEASURED_BOOT_EFI_TCG2"
112+
CFLAGS+=-D"WOLFBOOT_MEASURED_PCR_A=$(MEASURED_PCR_A)"
113+
endif
114+
105115
## TPM keystore
106116
ifeq ($(WOLFBOOT_TPM_KEYSTORE),1)
107117
WOLFTPM:=1

0 commit comments

Comments
 (0)