U-Boot EFI Measured Boot: A Full Event Log Can Leave PCR State Ahead of the Record

An EFI measurement operation reports that the event log is full. It is tempting to interpret that result as “the measurement did not happen” and retry. In U-Boot v2025.01’s HashLogExtendEvent path, the TPM extension comes before the event-log append. A capacity failure can therefore leave PCR state changed even though the explanatory record was not stored in the normal log.

This review is pinned to commit 6d41f0a39d6423c8e57e92ebbe9f8c0333a63f72 and the EFI TCG2 implementation. It does not claim the same behavior for every measured-boot entry point or firmware version. No TPM command, EFI application, attestation exchange, or destructive recovery action was executed.

Keep measurement state separate from its explanation

A PCR extension and an event-log record serve related but different roles. The extension updates the TPM’s measurement state. The event record identifies the measurement and carries the digest information needed by a consumer interpreting that state. They are not one atomic storage object.

The versioned EFI TCG2 implementation validates the HashLogExtendEvent inputs, obtains a TPM, and prepares digests. It has different hashing paths for ordinary buffers and PE/COFF images. After successful digest preparation, it calls tcg2_pcr_extend before handling the optional event-log append.

An extension error exits with EFI_DEVICE_ERROR. A successful extension proceeds to the logging decision. This order is the key fact when interpreting a later EFI_VOLUME_FULL result: the failure is not evidence that no earlier state transition occurred.

Source-derived sequence showing digest preparation, successful PCR extension, optional log append, and a full-log error after extension rather than an atomic rollback.

Locate the capacity failure precisely

For a normal logging request, HashLogExtendEvent calculates the event payload length and calls tcg2_agile_log_append. Before ExitBootServices, that helper checks whether the normal log is already marked truncated or whether the new event would exceed TPM2_EVENT_LOG_SIZE.

Either condition sets the truncated flag and returns EFI_VOLUME_FULL without appending that event to the normal buffer. In the calling operation, however, the PCR extension has already succeeded. The log’s previous final entry remains a record of an earlier event, not proof that the TPM stopped advancing there.

This is not a reason to hide the capacity error. It is a reason to preserve the exact operation, selected PCR, digest context, flags, and returned status together. Reducing the report to “logging failed” loses the fact that the measurement operation may have changed the attestation state.

Do not present the available log prefix as a complete explanation of all measurements. Whether a verifier can accept incomplete evidence depends on its policy and protocol; the bootloader cannot repair that omission by relabeling a truncated buffer as complete.

Understand why a blind retry is not a repair

Repeating the same request enters the hashing and extension path again. The implementation does not reserve normal-log space first or roll back a previous extension when appending fails. A repeated call can therefore add another extension while the normal log is still unable to accept the record.

The proposed application response is to stop treating EFI_VOLUME_FULL as an ordinary retryable write failure. Mark the evidence as incomplete, retain diagnostics outside the measurement transcript where appropriate, and follow a product-defined recovery policy. Do not invent a generic command to clear PCRs or reset the TPM: those operations have separate platform, security, and lifecycle requirements.

A bounded early-boot measurement workload should instead budget for event sizes and the selected digest algorithms before deployment. The append helper accounts for the event payload plus the agile-event structure. A count of event labels alone is not a reliable byte budget.

Treat EXTEND_ONLY as an intentional different contract

When EFI_TCG2_EXTEND_ONLY is set, the function does not append the event. That branch still follows the successful PCR extension. If the normal log is already marked truncated, it returns EFI_VOLUME_FULL; otherwise it can return success without producing a log entry.

Therefore, even a successful call does not always imply a corresponding record. A diagnostic harness must preserve the flags used for each request. Without them, an intentionally unlogged extension and a capacity-lost record can look identical to an observer examining only the buffer.

If a product uses extend-only operations, its evidence and verifier design need to account for them explicitly. Do not assume that an ordinary event-log replay alone will explain all PCR changes in such a design. This is a contract decision, not a formatting issue in the log reader.

Track the normal log and final-events buffer independently

The implementation also maintains a final-events buffer. Calling GetEventLog sets a flag after reporting the normal log location, its last-entry address, and the normal truncated state. Subsequent appended events are also added to the final-events buffer when that flag is set.

Before ExitBootServices, the helper first updates the normal log, then considers the final-events copy. If the latter lacks space, it returns EFI_VOLUME_FULL after the normal append already occurred. That is a different partial outcome from normal-log exhaustion: the normal record can exist while the final-events addition fails.

The final-buffer capacity check does not set the normal log’s truncated flag. Consequently, reading only that one flag is not a complete check of every logging result. Preserve call statuses and each buffer’s relevant state rather than flattening two independent capacity boundaries into one boolean.

Qualify the failure model without inventing target results

A proposed fixture can provide a mock TPM extension callback and a deliberately constrained log buffer. Record callback count, normal-log position, last-event size, truncation, final-event count, and final position before and after each request. Assert the exact partial state rather than only the EFI status.

Include normal-log exhaustion, a later call after truncation, extend-only with and without prior truncation, and final-buffer exhaustion after GetEventLog. Keep the pre-ExitBootServices boundary explicit for the normal-then-final append case. These tests are proposals, not results reported from a firmware run.

The release criterion is an interpretable measurement transcript with an explicit response to incomplete evidence. A full-buffer error must never be translated automatically into “nothing happened,” and a retry must never be assumed to restore a missing record without changing the measurement history again.

Similar Posts