diff --git a/man/systemd-boot.xml b/man/systemd-boot.xml index b6898ea8f0..52aa582c30 100644 --- a/man/systemd-boot.xml +++ b/man/systemd-boot.xml @@ -527,6 +527,18 @@ + + LoaderTpm2ActivePcrBanks + + Hexadecimal string representation of a bitmask with values defined by the TCG EFI + Protocol Specification for TPM 2.0 as EFI_TCG2_BOOT_HASH_ALG_*. If no TPM2 support or no active + banks were detected, will be set to 0. Set by the boot loader. Use + systemd-analyze1 + to view this data. + + + + LoaderImageIdentifier diff --git a/man/systemd-stub.xml b/man/systemd-stub.xml index dd709c2949..fb15861941 100644 --- a/man/systemd-stub.xml +++ b/man/systemd-stub.xml @@ -544,6 +544,18 @@ + + LoaderTpm2ActivePcrBanks + + Hexadecimal string representation of a bitmask with values defined by the TCG EFI + Protocol Specification for TPM 2.0 as EFI_TCG2_BOOT_HASH_ALG_*. If no TPM2 support or no active + banks were detected, will be set to 0. Set by the boot loader. Use + systemd-analyze1 + to view this data. + + + + LoaderImageIdentifier diff --git a/src/boot/boot.c b/src/boot/boot.c index 74b2884caf..ec779fa68d 100644 --- a/src/boot/boot.c +++ b/src/boot/boot.c @@ -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); diff --git a/src/boot/export-vars.c b/src/boot/export-vars.c index 25ca62065a..5c037bdd25 100644 --- a/src/boot/export-vars.c +++ b/src/boot/export-vars.c @@ -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); + } } diff --git a/src/boot/measure.c b/src/boot/measure.c index 590fed8ec2..5cf6156d62 100644 --- a/src/boot/measure.c +++ b/src/boot/measure.c @@ -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; diff --git a/src/boot/measure.h b/src/boot/measure.h index 9dde93b94d..4ceed0e0f8 100644 --- a/src/boot/measure.h +++ b/src/boot/measure.h @@ -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; diff --git a/src/boot/proto/tcg.h b/src/boot/proto/tcg.h index a85288178c..e99c01a4ad 100644 --- a/src/boot/proto/tcg.h +++ b/src/boot/proto/tcg.h @@ -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; }; diff --git a/src/bootctl/bootctl-status.c b/src/bootctl/bootctl-status.c index 9715c91d26..d8609d8ffd 100644 --- a/src/bootctl/bootctl-status.c +++ b/src/bootctl/bootctl-status.c @@ -413,6 +413,7 @@ int verb_status(int argc, char *argv[], void *userdata) { { EFI_LOADER_FEATURE_REPORT_URL, "Loader reports network boot URL" }, { EFI_LOADER_FEATURE_TYPE1_UKI, "Support Type #1 uki field" }, { EFI_LOADER_FEATURE_TYPE1_UKI_URL, "Support Type #1 uki-url field" }, + { EFI_LOADER_FEATURE_TPM2_ACTIVE_PCR_BANKS, "Loader reports TPM2 active PCR banks" }, }; static const struct { uint64_t flag; diff --git a/src/fundamental/efivars-fundamental.h b/src/fundamental/efivars-fundamental.h index d556e7718f..0f4670a37f 100644 --- a/src/fundamental/efivars-fundamental.h +++ b/src/fundamental/efivars-fundamental.h @@ -27,6 +27,7 @@ #define EFI_LOADER_FEATURE_REPORT_URL (UINT64_C(1) << 15) #define EFI_LOADER_FEATURE_TYPE1_UKI (UINT64_C(1) << 16) #define EFI_LOADER_FEATURE_TYPE1_UKI_URL (UINT64_C(1) << 17) +#define EFI_LOADER_FEATURE_TPM2_ACTIVE_PCR_BANKS (UINT64_C(1) << 18) /* Features of the stub, i.e. systemd-stub */ #define EFI_STUB_FEATURE_REPORT_BOOT_PARTITION (UINT64_C(1) << 0) diff --git a/src/shared/efi-api.c b/src/shared/efi-api.c index 11165cdb9a..73c09f66f6 100644 --- a/src/shared/efi-api.c +++ b/src/shared/efi-api.c @@ -10,6 +10,7 @@ #include "fd-util.h" #include "fileio.h" #include "log.h" +#include "parse-util.h" #include "sort-util.h" #include "stat-util.h" #include "stdio-util.h" @@ -516,6 +517,27 @@ int efi_get_boot_options(uint16_t **ret_options) { #endif } +#if ENABLE_EFI +static int loader_has_tpm2(void) { + _cleanup_free_ char *active_pcr_banks = NULL; + uint32_t active_pcr_banks_value; + int r; + + r = efi_get_variable_string(EFI_LOADER_VARIABLE_STR("LoaderTpm2ActivePcrBanks"), &active_pcr_banks); + if (r < 0) { + if (r != -ENOENT) + log_debug_errno(r, "Failed to read LoaderTpm2ActivePcrBanks variable: %m"); + return r; + } + + r = safe_atou32_full(active_pcr_banks, 16, &active_pcr_banks_value); + if (r < 0) + return log_debug_errno(r, "Failed to parse LoaderTpm2ActivePcrBanks variable: %m"); + + return active_pcr_banks_value != 0; +} +#endif + bool efi_has_tpm2(void) { #if ENABLE_EFI static int cache = -1; @@ -530,9 +552,17 @@ bool efi_has_tpm2(void) { if (!is_efi_boot()) return (cache = false); + /* Secondly, check if the loader told us, as that is the most accurate source of information + * regarding the firmware's setup */ + r = loader_has_tpm2(); + if (r >= 0) + return (cache = r); + /* Then, check if the ACPI table "TPM2" exists, which is the TPM2 event log table, see: * https://trustedcomputinggroup.org/wp-content/uploads/TCG_ACPIGeneralSpecification_v1.20_r8.pdf - * This table exists whenever the firmware knows ACPI and is hooked up to TPM2. */ + * This table exists whenever the firmware knows ACPI and is hooked up to TPM2. + * Note that in some cases, for example with EDK2 2025.2 with the default arm64 config, this ACPI + * table is present even if TPM2 support is not enabled in the firmware. */ if (access("/sys/firmware/acpi/tables/TPM2", F_OK) >= 0) return (cache = true); if (errno != ENOENT)