boot: add LoaderTpm2ActivePcrBanks runtime variable

It turns out checking sysfs is not 100% reliable to figure out whether
the firmware had TPM2 support enabled or not. For example with EDK2 arm64, the
default upstream build config bundles TPM2 support with SecureBoot support,
so if the latter is disabled, TPM2 is also unavailable. But still, the ACPI
TPM2 table is created just as if it was enabled. So /sys/firmware/acpi/tables/TPM2
exists and looks correct, but there are no measurements, neither the firmware
nor the loader/stub can do them, and /sys/kernel/security/tpm0/binary_bios_measurements
does not exist.

The loader can use the apposite UEFI protocol to check, which is a more
definitive answer. Given userspace can also make use of this information, export
the bitmask with the list of active banks as-is. If it's not 0, then we can be
sure a working TPM2 was available in EFI mode.

Partially fixes https://github.com/systemd/systemd/issues/38071
This commit is contained in:
Luca Boccassi
2025-07-14 20:56:22 +01:00
parent 9120022587
commit 6eab4cd44c
10 changed files with 93 additions and 2 deletions
+1
View File
@@ -2849,6 +2849,7 @@ static void export_loader_variables(
EFI_LOADER_FEATURE_REPORT_URL |
EFI_LOADER_FEATURE_TYPE1_UKI |
EFI_LOADER_FEATURE_TYPE1_UKI_URL |
EFI_LOADER_FEATURE_TPM2_ACTIVE_PCR_BANKS |
0;
assert(loaded_image);
+9
View File
@@ -3,6 +3,7 @@
#include "device-path-util.h"
#include "efi-efivars.h"
#include "export-vars.h"
#include "measure.h"
#include "part-discovery.h"
#include "url-discovery.h"
#include "util.h"
@@ -51,4 +52,12 @@ void export_common_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) {
s = xasprintf("UEFI %u.%02u", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
efivar_set_str16(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareType", s, 0);
}
/* ditto for LoaderTpm2ActivePcrBanks */
if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderTpm2ActivePcrBanks", NULL, NULL) != EFI_SUCCESS) {
uint32_t active_pcr_banks = tpm_get_active_pcr_banks();
_cleanup_free_ char16_t *s = NULL;
s = xasprintf("0x%08x", active_pcr_banks);
efivar_set_str16(MAKE_GUID_PTR(LOADER), u"LoaderTpm2ActivePcrBanks", s, 0);
}
}
+18
View File
@@ -183,6 +183,24 @@ bool tpm_present(void) {
return tcg2_interface_check();
}
uint32_t tpm_get_active_pcr_banks(void) {
uint32_t active_pcr_banks = 0;
EFI_TCG2_PROTOCOL *tpm2;
EFI_STATUS err;
tpm2 = tcg2_interface_check();
if (!tpm2)
return 0;
err = tpm2->GetActivePcrBanks(tpm2, &active_pcr_banks);
if (err != EFI_SUCCESS) {
log_warning_status(err, "Failed to get TPM2 active PCR banks, assuming none: %m");
return 0;
}
return active_pcr_banks;
}
static EFI_STATUS tcg2_log_ipl_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, size_t buffer_size, const char16_t *description, bool *ret_measured) {
EFI_TCG2_PROTOCOL *tpm2;
EFI_STATUS err = EFI_SUCCESS;
+5
View File
@@ -6,6 +6,7 @@
#if ENABLE_TPM
bool tpm_present(void);
uint32_t tpm_get_active_pcr_banks(void);
/* Routines for boot-time TPM PCR measurement as well as submitting an event log entry about it. The latter
* can be done with two different event log record types. For old stuff we use EV_IPL (which is legacy, and
@@ -28,6 +29,10 @@ static inline bool tpm_present(void) {
return false;
}
static inline uint32_t tpm_get_active_pcr_banks(void) {
return 0;
}
static inline EFI_STATUS tpm_log_ipl_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, size_t buffer_size, const char16_t *description, bool *ret_measured) {
if (ret_measured)
*ret_measured = false;
+3 -1
View File
@@ -79,7 +79,9 @@ struct EFI_TCG2_PROTOCOL {
uint64_t DataToHashLen,
EFI_TCG2_EVENT *EfiTcgEvent);
void *SubmitCommand;
void *GetActivePcrBanks;
EFI_STATUS (EFIAPI *GetActivePcrBanks)(
EFI_TCG2_PROTOCOL *This,
uint32_t *ActivePcrBanks);
void *SetActivePcrBanks;
void *GetResultOfSetActivePcrBanks;
};