From 02f5525589961da3c674fdd809fd2ce47d4256fd Mon Sep 17 00:00:00 2001 From: Sergii Dmytruk Date: Mon, 1 Dec 2025 00:51:50 +0200 Subject: [PATCH 1/9] security/tpm/tss/tcg-2.0/tss: move tpm2_get_capability_pcrs() here Out of vendorcode/eltan/security/mboot/mboot.c to not duplicate the implementation. Upstream-Status: Pending Signed-off-by: Sergii Dmytruk --- src/security/tpm/tss/tcg-2.0/tss.c | 28 +++++++++++++++ src/security/tpm/tss2.h | 15 ++++++++ src/vendorcode/eltan/security/mboot/mboot.c | 40 +-------------------- 3 files changed, 44 insertions(+), 39 deletions(-) diff --git a/src/security/tpm/tss/tcg-2.0/tss.c b/src/security/tpm/tss/tcg-2.0/tss.c index 68d50f4f194..9a2077b8c1e 100644 --- a/src/security/tpm/tss/tcg-2.0/tss.c +++ b/src/security/tpm/tss/tcg-2.0/tss.c @@ -494,3 +494,31 @@ tpm_result_t tlcl2_get_capability(TPM_CAP capability, uint32_t property, memcpy(capability_data, &response->gc.cd, sizeof(TPMS_CAPABILITY_DATA)); return TPM_SUCCESS; } + +tpm_result_t tlcl2_get_capability_pcrs(TPML_PCR_SELECTION *pcrs) +{ + TPMS_CAPABILITY_DATA TpmCap; + tpm_result_t rc; + int index; + + rc = tlcl2_get_capability(TPM_CAP_PCRS, 0, 1, &TpmCap); + if (rc != TPM_SUCCESS) + return rc; + + pcrs->count = TpmCap.data.assignedPCR.count; + printk(BIOS_DEBUG, "%s(): pcrs->count = %d\n", __func__, pcrs->count); + + for (index = 0; index < pcrs->count; index++) { + pcrs->pcrSelections[index].hash = + swab16(TpmCap.data.assignedPCR.pcrSelections[index].hash); + printk(BIOS_DEBUG, "%s(): pcrs->pcrSelections[%d].hash = %#x\n", + __func__, index, pcrs->pcrSelections[index].hash); + pcrs->pcrSelections[index].sizeofSelect = + TpmCap.data.assignedPCR.pcrSelections[index].sizeofSelect; + memcpy(pcrs->pcrSelections[index].pcrSelect, + TpmCap.data.assignedPCR.pcrSelections[index].pcrSelect, + pcrs->pcrSelections[index].sizeofSelect); + } + + return TPM_SUCCESS; +} diff --git a/src/security/tpm/tss2.h b/src/security/tpm/tss2.h index 9bb562a2349..384fa72fca1 100644 --- a/src/security/tpm/tss2.h +++ b/src/security/tpm/tss2.h @@ -42,6 +42,21 @@ tpm_result_t tlcl2_get_capability(TPM_CAP capability, uint32_t property, uint32_t property_count, TPMS_CAPABILITY_DATA *capability_data); +/* + * tlcl2_get_capability_pcrs + * + * Return the TPM PCR information. + * + * This function parses the data got from tlcl2_get_capability and returns the + * PcrSelection. + * + * @param[out] pcrs The Pcr Selection + * + * @retval TPM_SUCCESS Operation completed successfully. + * @retval TPM_IOERROR The command was unsuccessful. + */ +tpm_result_t tlcl2_get_capability_pcrs(TPML_PCR_SELECTION *pcrs); + /* Issue TPM2_NV_SetBits command */ tpm_result_t tlcl2_set_bits(uint32_t index, uint64_t bits); diff --git a/src/vendorcode/eltan/security/mboot/mboot.c b/src/vendorcode/eltan/security/mboot/mboot.c index 9cdd0de2fe8..67ec41e2c6c 100644 --- a/src/vendorcode/eltan/security/mboot/mboot.c +++ b/src/vendorcode/eltan/security/mboot/mboot.c @@ -19,7 +19,7 @@ EFI_TCG2_EVENT_ALGORITHM_BITMAP tpm2_get_active_pcrs(void) uint32_t activePcrBanks = 0; uint32_t index; - rc = tpm2_get_capability_pcrs(&Pcrs); + rc = tlcl2_get_capability_pcrs(&Pcrs); if (rc != TPM_SUCCESS) { tpmHashAlgorithmBitmap = EFI_TCG2_BOOT_HASH_ALG_SHA1; activePcrBanks = EFI_TCG2_BOOT_HASH_ALG_SHA1; @@ -62,44 +62,6 @@ EFI_TCG2_EVENT_ALGORITHM_BITMAP tpm2_get_active_pcrs(void) return activePcrBanks; } -/* - * tpm2_get_capability_pcrs - * - * Return the TPM PCR information. - * - * This function parses the data got from tlcl2_get_capability and returns the - * PcrSelection. - * - * @param[out] Pcrs The Pcr Selection - * - * @retval TPM_SUCCESS Operation completed successfully. - * @retval TPM_IOERROR The command was unsuccessful. - */ -tpm_result_t tpm2_get_capability_pcrs(TPML_PCR_SELECTION *Pcrs) -{ - TPMS_CAPABILITY_DATA TpmCap; - tpm_result_t rc; - int index; - - rc = tlcl2_get_capability(TPM_CAP_PCRS, 0, 1, &TpmCap); - if (rc == TPM_SUCCESS) { - Pcrs->count = TpmCap.data.assignedPCR.count; - printk(BIOS_DEBUG, "Pcrs->count = %d\n", Pcrs->count); - for (index = 0; index < Pcrs->count; index++) { - Pcrs->pcrSelections[index].hash = - swab16(TpmCap.data.assignedPCR.pcrSelections[index].hash); - printk(BIOS_DEBUG, "Pcrs->pcrSelections[%d].hash = %#x\n", index, - Pcrs->pcrSelections[index].hash); - Pcrs->pcrSelections[index].sizeofSelect = - TpmCap.data.assignedPCR.pcrSelections[index].sizeofSelect; - memcpy(Pcrs->pcrSelections[index].pcrSelect, - TpmCap.data.assignedPCR.pcrSelections[index].pcrSelect, - Pcrs->pcrSelections[index].sizeofSelect); - } - } - return rc; -} - /* * mboot_hash_extend_log * From 4b3c51bcc29922fe13e21f1ebcdce731112b45a8 Mon Sep 17 00:00:00 2001 From: Sergii Dmytruk Date: Tue, 2 Dec 2025 01:25:40 +0200 Subject: [PATCH 2/9] security/tpm: prepare API for multiple active banks No functional changes are intended, merely changing interface and how places that make use of it. A few places got extra error checks, but their conditions shouldn't be satisfied at this point. Upstream-Status: Pending Signed-off-by: Sergii Dmytruk --- src/security/intel/cbnt/measurement.c | 43 ++++++++++++++++++++------- src/security/tpm/tspi.h | 32 ++++++++------------ src/security/tpm/tspi/crtm.c | 18 ++++++----- src/security/tpm/tspi/log-tpm1.c | 26 +++++++++------- src/security/tpm/tspi/log-tpm2.c | 25 +++++++--------- src/security/tpm/tspi/log.c | 34 +++++++++++++-------- src/security/tpm/tspi/logs.h | 23 +++++--------- src/security/tpm/tspi/tspi.c | 18 +++++++---- src/security/tpm/tss.h | 13 ++++++-- src/security/tpm/tss/tcg-1.2/tss.c | 9 +++--- src/security/tpm/tss/tcg-2.0/tss.c | 13 +++----- src/security/tpm/tss1.h | 6 ++-- src/security/tpm/tss2.h | 6 ++-- src/security/vboot/tpm_common.c | 14 +++++---- 14 files changed, 155 insertions(+), 125 deletions(-) diff --git a/src/security/intel/cbnt/measurement.c b/src/security/intel/cbnt/measurement.c index 2e9aa956dc8..4e540cd0483 100644 --- a/src/security/intel/cbnt/measurement.c +++ b/src/security/intel/cbnt/measurement.c @@ -617,9 +617,13 @@ static enum cb_err cap_pcrs(union cbnt_biosacm_policy biosacm_sts) return CB_ERR; } + const struct tpm_digest cap_digests[] = { + { .hash_type = hash.algo, .hash = hash.raw }, + { .hash_type = VB2_HASH_INVALID } + }; + for (int pcr = 0; pcr < 8; pcr++) { - tpm_log_add_table_entry("CBnT hit TPM failure during boot", pcr, hash.algo, - hash.raw, vb2_digest_size(hash.algo)); + tpm_log_add_table_entry("CBnT hit TPM failure during boot", pcr, cap_digests); printk(BIOS_INFO, "CBnT: capped PCR-%d\n", pcr); } return CB_SUCCESS; @@ -722,8 +726,11 @@ void intel_cbnt_inject_ibg_measurements(void) } /* Per BWG this should be logged with EV_S_CRTM_CONTENTS type. */ - tpm_log_add_table_entry(CBNT_EVENT_LOG_MESSAGE, 0, hash.algo, hash.raw, - vb2_digest_size(hash.algo)); + const struct tpm_digest pcr0_digests[] = { + { .hash_type = hash.algo, .hash = hash.raw }, + { .hash_type = VB2_HASH_INVALID } + }; + tpm_log_add_table_entry(CBNT_EVENT_LOG_MESSAGE, 0, pcr0_digests); /* Optionally making and hashing PCR-7 data (not supported since MTL). */ if (auth_measure) { @@ -740,8 +747,11 @@ void intel_cbnt_inject_ibg_measurements(void) /* Per BWG this should be logged with EV_EFI_VARIABLE_DRIVER_CONFIG type and event name should be a Unicode string. */ - tpm_log_add_table_entry(CBNT_EVENT_LOG_MESSAGE, 7, hash.algo, hash.raw, - vb2_digest_size(hash.algo)); + const struct tpm_digest pcr7_digests[] = { + { .hash_type = hash.algo, .hash = hash.raw }, + { .hash_type = VB2_HASH_INVALID } + }; + tpm_log_add_table_entry(CBNT_EVENT_LOG_MESSAGE, 7, pcr7_digests); printk(BIOS_INFO, "CBnT: reconstructed PCR-7 measurement\n"); } @@ -774,8 +784,11 @@ void intel_cbnt_inject_ibg_measurements(void) return; } /* Per BWG this should be logged with EV_S_CRTM_VERSION type. */ - tpm_log_add_table_entry(crtm_version_str, 0, hash.algo, hash.raw, - vb2_digest_size(hash.algo)); + const struct tpm_digest crtm_digests[] = { + { .hash_type = hash.algo, .hash = hash.raw }, + { .hash_type = VB2_HASH_INVALID } + }; + tpm_log_add_table_entry(crtm_version_str, 0, crtm_digests); if (copy_ibb_hash(&data_ob, tpm2_alg_from_vb2_hash(tpm_log_alg())) != CB_SUCCESS) { @@ -783,8 +796,11 @@ void intel_cbnt_inject_ibg_measurements(void) return; } /* Per BWG this should be logged with EV_POST_CODE type. */ - tpm_log_add_table_entry("Boot Guard Measured IBB", 0, tpm_log_alg(), data, - obuf_nr_written(&data_ob)); + const struct tpm_digest post_code_digests[] = { + { .hash_type = tpm_log_alg(), .hash = data }, + { .hash_type = VB2_HASH_INVALID } + }; + tpm_log_add_table_entry("Boot Guard Measured IBB", 0, post_code_digests); /* * struct { @@ -815,8 +831,13 @@ void intel_cbnt_inject_ibg_measurements(void) printk(BIOS_ERR, "CBnT: failed to hash POLICY_DATA\n"); return; } + /* Per BWG this should be logged with EV_POST_CODE type. */ - if (tpm_extend_pcr(0, hash.algo, hash.raw, vb2_digest_size(hash.algo), + const struct tpm_digest policy_digests[] = { + { .hash_type = hash.algo, .hash = hash.raw }, + { .hash_type = VB2_HASH_INVALID } + }; + if (tpm_extend_pcr(0, policy_digests, "BIOS Measured Boot Guard Policy") != TPM_SUCCESS) { printk(BIOS_ERR, "CBnT: failed to extend POLICY_DATA\n"); return; diff --git a/src/security/tpm/tspi.h b/src/security/tpm/tspi.h index f79b2cf043e..d102f44684d 100644 --- a/src/security/tpm/tspi.h +++ b/src/security/tpm/tspi.h @@ -132,15 +132,15 @@ static inline void tpm_log_copy_entries(const void *from, void *to) /** * Retrieves an entry from a log. Returns non-zero on invalid index or error. */ -static inline int tpm_log_get(int entry_idx, int *pcr, const uint8_t **digest_data, - enum vb2_hash_algorithm *digest_algo, const char **event_name) +static inline int tpm_log_get(int entry_idx, int *pcr, struct tpm_digest *digests, + const char **event_name) { if (CONFIG(TPM_LOG_CB)) - return tpm_cb_log_get(entry_idx, pcr, digest_data, digest_algo, event_name); + return tpm_cb_log_get(entry_idx, pcr, digests, event_name); if (tpm_log_use_tpm1_format()) - return tpm1_log_get(entry_idx, pcr, digest_data, digest_algo, event_name); + return tpm1_log_get(entry_idx, pcr, digests, event_name); if (tpm_log_use_tpm2_format()) - return tpm2_log_get(entry_idx, pcr, digest_data, digest_algo, event_name); + return tpm2_log_get(entry_idx, pcr, digests, event_name); return 1; } @@ -148,21 +148,17 @@ static inline int tpm_log_get(int entry_idx, int *pcr, const uint8_t **digest_da * Add table entry for cbmem TPM log. * @param name Name of the hashed data * @param pcr PCR used to extend hashed data - * @param diget_algo sets the digest algorithm - * @param digest sets the hash extended into the tpm - * @param digest_len the length of the digest + * @param digests An array of digests terminated by an entry with VB2_HASH_NONE */ static inline void tpm_log_add_table_entry(const char *name, const uint32_t pcr, - enum vb2_hash_algorithm digest_algo, - const uint8_t *digest, - const size_t digest_len) + const struct tpm_digest *digests) { if (CONFIG(TPM_LOG_CB)) - tpm_cb_log_add_table_entry(name, pcr, digest_algo, digest, digest_len); + tpm_cb_log_add_table_entry(name, pcr, digests); else if (tpm_log_use_tpm1_format()) - tpm1_log_add_table_entry(name, pcr, digest_algo, digest, digest_len); + tpm1_log_add_table_entry(name, pcr, digests); else if (tpm_log_use_tpm2_format()) - tpm2_log_add_table_entry(name, pcr, digest_algo, digest, digest_len); + tpm2_log_add_table_entry(name, pcr, digests); } /** @@ -191,15 +187,11 @@ static inline void tpm_log_dump(void *unused) /** * Ask vboot for a digest and extend a TPM PCR with it. * @param pcr sets the pcr index - * @param diget_algo sets the digest algorithm - * @param digest sets the hash to extend into the tpm - * @param digest_len the length of the digest + * @param digests An array of digests terminated by an entry with VB2_HASH_NONE * @param name sets additional info where the digest comes from * @return TPM_SUCCESS on success. If not a tpm error is returned */ -tpm_result_t tpm_extend_pcr(int pcr, enum vb2_hash_algorithm digest_algo, - const uint8_t *digest, size_t digest_len, - const char *name); +tpm_result_t tpm_extend_pcr(int pcr, const struct tpm_digest *digests, const char *name); /** * Issue a TPM_Clear and re-enable/reactivate the TPM. diff --git a/src/security/tpm/tspi/crtm.c b/src/security/tpm/tspi/crtm.c index c870a90198e..a904b3dcdbd 100644 --- a/src/security/tpm/tspi/crtm.c +++ b/src/security/tpm/tspi/crtm.c @@ -132,10 +132,14 @@ tpm_result_t tspi_cbfs_measurement(const char *name, uint32_t type, const struct break; } + struct tpm_digest digests[] = { + { .hash_type = hash->algo, .hash = hash->raw }, + { .hash_type = VB2_HASH_INVALID } + }; + snprintf(tpm_log_metadata, TPM_CB_LOG_PCR_HASH_NAME, "CBFS: %s", name); - return tpm_extend_pcr(pcr_index, hash->algo, hash->raw, vb2_digest_size(hash->algo), - tpm_log_metadata); + return tpm_extend_pcr(pcr_index, digests, tpm_log_metadata); } void *tpm_log_init(void) @@ -163,8 +167,7 @@ tpm_result_t tspi_measure_cache_to_pcr(void) int i; int pcr; const char *event_name; - const uint8_t *digest_data; - enum vb2_hash_algorithm digest_algo; + struct tpm_digest digests[2]; /* This means the table is empty. */ if (!tspi_tpm_log_available()) @@ -177,7 +180,7 @@ tpm_result_t tspi_measure_cache_to_pcr(void) printk(BIOS_DEBUG, "TPM: Write digests cached in TPM log to PCR\n"); i = 0; - while (!tpm_log_get(i++, &pcr, &digest_data, &digest_algo, &event_name)) { + while (!tpm_log_get(i++, &pcr, digests, &event_name)) { /* * Skip log entries that coreboot synthesized to account for PCR extends * performed by hardware S-CRTM. @@ -193,11 +196,12 @@ tpm_result_t tspi_measure_cache_to_pcr(void) continue; printk(BIOS_DEBUG, "TPM: Write digest for %s into PCR %d\n", event_name, pcr); - tpm_result_t rc = tlcl_extend(pcr, digest_data, digest_algo); + + tpm_result_t rc = tlcl_extend(pcr, digests); if (rc != TPM_SUCCESS) { printk(BIOS_ERR, "TPM: Writing digest of %s into PCR failed with error %d\n", - event_name, rc); + event_name, rc); return rc; } } diff --git a/src/security/tpm/tspi/log-tpm1.c b/src/security/tpm/tspi/log-tpm1.c index 0fbfa5ab503..7052425a9e0 100644 --- a/src/security/tpm/tspi/log-tpm1.c +++ b/src/security/tpm/tspi/log-tpm1.c @@ -83,10 +83,7 @@ void tpm1_log_dump(void) printk(BIOS_INFO, "\n"); } -void tpm1_log_add_table_entry(const char *name, const uint32_t pcr, - enum vb2_hash_algorithm digest_algo, - const uint8_t *digest, - const size_t digest_len) +void tpm1_log_add_table_entry(const char *name, uint32_t pcr, const struct tpm_digest *digests) { struct tpm_1_log_table *tclt; struct tpm_1_log_entry *tce; @@ -102,8 +99,14 @@ void tpm1_log_add_table_entry(const char *name, const uint32_t pcr, return; } - if (digest_algo != VB2_HASH_SHA1) { - printk(BIOS_WARNING, "TPM LOG: unsupported hash algorithm\n"); + if (digests[0].hash_type != VB2_HASH_SHA1) { + printk(BIOS_WARNING, "TPM LOG: digest is of unsupported type: %s\n", + vb2_get_hash_algorithm_name(digests[0].hash_type)); + return; + } + + if (digests[1].hash_type != VB2_HASH_INVALID) { + printk(BIOS_WARNING, "TPM LOG: TPM 1 can't handle multiple banks\n"); return; } @@ -118,7 +121,7 @@ void tpm1_log_add_table_entry(const char *name, const uint32_t pcr, tce->pcr = htole32(pcr); tce->event_type = htole32(EV_ACTION); - memcpy(tce->digest, digest, digest_len); + memcpy(tce->digest, digests[0].hash, TPM_1_LOG_DIGEST_MAX_LENGTH); tce->data_length = htole32(TPM_1_LOG_DATA_MAX_LENGTH); strncpy((char *)tce->data, name, sizeof(tce->data) - 1); @@ -137,8 +140,7 @@ void tpm1_preram_log_clear(void) tclt->vendor.num_entries = htole16(0); } -int tpm1_log_get(int entry_idx, int *pcr, const uint8_t **digest_data, - enum vb2_hash_algorithm *digest_algo, const char **event_name) +int tpm1_log_get(int entry_idx, int *pcr, struct tpm_digest *digests, const char **event_name) { struct tpm_1_log_table *tclt; struct tpm_1_log_entry *tce; @@ -152,9 +154,11 @@ int tpm1_log_get(int entry_idx, int *pcr, const uint8_t **digest_data, tce = &tclt->entries[entry_idx]; + digests[0].hash_type = VB2_HASH_SHA1; + digests[0].hash = tce->digest; + digests[1].hash_type = VB2_HASH_INVALID; + *pcr = le32toh(tce->pcr); - *digest_data = tce->digest; - *digest_algo = VB2_HASH_SHA1; *event_name = (char *)tce->data; return 0; } diff --git a/src/security/tpm/tspi/log-tpm2.c b/src/security/tpm/tspi/log-tpm2.c index 3e26970a880..0eb36e7c1a9 100644 --- a/src/security/tpm/tspi/log-tpm2.c +++ b/src/security/tpm/tspi/log-tpm2.c @@ -104,10 +104,7 @@ void tpm2_log_dump(void) printk(BIOS_INFO, "\n"); } -void tpm2_log_add_table_entry(const char *name, const uint32_t pcr, - enum vb2_hash_algorithm digest_algo, - const uint8_t *digest, - const size_t digest_len) +void tpm2_log_add_table_entry(const char *name, uint32_t pcr, const struct tpm_digest *digests) { struct tpm_2_log_table *tclt; struct tpm_2_log_entry *tce; @@ -123,15 +120,14 @@ void tpm2_log_add_table_entry(const char *name, const uint32_t pcr, return; } - if (digest_algo != tpm_log_alg()) { + if (digests[0].hash_type != tpm_log_alg()) { printk(BIOS_WARNING, "TPM LOG: digest is of unsupported type: %s\n", - vb2_get_hash_algorithm_name(digest_algo)); + vb2_get_hash_algorithm_name(digests[0].hash_type)); return; } - if (digest_len != vb2_digest_size(tpm_log_alg())) { - printk(BIOS_WARNING, "TPM LOG: digest has invalid length: %d\n", - (int)digest_len); + if (digests[1].hash_type != VB2_HASH_INVALID) { + printk(BIOS_WARNING, "TPM LOG: can't handle multiple banks\n"); return; } @@ -148,7 +144,7 @@ void tpm2_log_add_table_entry(const char *name, const uint32_t pcr, tce->digest_count = htole32(1); tce->digest_type = htole16(tpm2_alg_from_vb2_hash(tpm_log_alg())); - memcpy(tce->digest, digest, vb2_digest_size(tpm_log_alg())); + memcpy(tce->digest, digests[0].hash, vb2_digest_size(tpm_log_alg())); tce->data_length = htole32(sizeof(tce->data)); strncpy((char *)tce->data, name, sizeof(tce->data) - 1); @@ -196,8 +192,7 @@ void tpm2_log_startup_locality(int locality) memcpy(tce->data, &locality_event, sizeof(locality_event)); } -int tpm2_log_get(int entry_idx, int *pcr, const uint8_t **digest_data, - enum vb2_hash_algorithm *digest_algo, const char **event_name) +int tpm2_log_get(int entry_idx, int *pcr, struct tpm_digest *digests, const char **event_name) { struct tpm_2_log_table *tclt; struct tpm_2_log_entry *tce; @@ -211,9 +206,11 @@ int tpm2_log_get(int entry_idx, int *pcr, const uint8_t **digest_data, tce = &tclt->entries[entry_idx]; + digests[0].hash_type = tpm_log_alg(); /* We validate algorithm on addition */ + digests[0].hash = tce->digest; + digests[1].hash_type = VB2_HASH_INVALID; + *pcr = le32toh(tce->pcr); - *digest_data = tce->digest; - *digest_algo = tpm_log_alg(); /* We validate algorithm on addition */ *event_name = (char *)tce->data; return 0; } diff --git a/src/security/tpm/tspi/log.c b/src/security/tpm/tspi/log.c index a04f4070bf9..70482cf29d0 100644 --- a/src/security/tpm/tspi/log.c +++ b/src/security/tpm/tspi/log.c @@ -58,9 +58,7 @@ void tpm_cb_log_dump(void) } void tpm_cb_log_add_table_entry(const char *name, const uint32_t pcr, - enum vb2_hash_algorithm digest_algo, - const uint8_t *digest, - const size_t digest_len) + const struct tpm_digest *digests) { struct tpm_cb_log_table *tclt = tpm_log_init(); if (!tclt) { @@ -78,22 +76,32 @@ void tpm_cb_log_add_table_entry(const char *name, const uint32_t pcr, return; } + if (digests[0].hash_type == VB2_HASH_INVALID) { + printk(BIOS_WARNING, "TPM LOG: no digests provided\n"); + return; + } + + if (digests[1].hash_type != VB2_HASH_INVALID) { + printk(BIOS_WARNING, "TPM LOG: coreboot log can't handle multiple banks\n"); + return; + } + struct tpm_cb_log_entry *tce = &tclt->entries[tclt->num_entries++]; strncpy(tce->name, name, TPM_CB_LOG_PCR_HASH_NAME - 1); tce->name[TPM_CB_LOG_PCR_HASH_NAME - 1] = '\0'; tce->pcr = pcr; - if (digest_len > TPM_CB_LOG_DIGEST_MAX_LENGTH) { + if (vb2_digest_size(digests[0].hash_type) > TPM_CB_LOG_DIGEST_MAX_LENGTH) { printk(BIOS_WARNING, "TPM LOG: PCR digest too long for log entry\n"); return; } strncpy(tce->digest_type, - vb2_get_hash_algorithm_name(digest_algo), + vb2_get_hash_algorithm_name(digests[0].hash_type), TPM_CB_LOG_PCR_HASH_LEN - 1); - tce->digest_length = digest_len; - memcpy(tce->digest, digest, tce->digest_length); + tce->digest_length = vb2_digest_size(digests[0].hash_type); + memcpy(tce->digest, digests[0].hash, tce->digest_length); } void tpm_cb_preram_log_clear(void) @@ -104,8 +112,7 @@ void tpm_cb_preram_log_clear(void) tclt->num_entries = 0; } -int tpm_cb_log_get(int entry_idx, int *pcr, const uint8_t **digest_data, - enum vb2_hash_algorithm *digest_algo, const char **event_name) +int tpm_cb_log_get(int entry_idx, int *pcr, struct tpm_digest *digests, const char **event_name) { struct tpm_cb_log_table *tclt; struct tpm_cb_log_entry *tce; @@ -121,16 +128,19 @@ int tpm_cb_log_get(int entry_idx, int *pcr, const uint8_t **digest_data, tce = &tclt->entries[entry_idx]; *pcr = tce->pcr; - *digest_data = tce->digest; *event_name = tce->name; - *digest_algo = VB2_HASH_INVALID; + digests[0].hash = tce->digest; + digests[0].hash_type = VB2_HASH_INVALID; for (algo = VB2_HASH_INVALID; algo != VB2_HASH_ALG_COUNT; ++algo) { if (strcmp(tce->digest_type, vb2_hash_names[algo]) == 0) { - *digest_algo = algo; + digests[0].hash_type = algo; break; } } + + digests[1].hash_type = VB2_HASH_INVALID; + return 0; } diff --git a/src/security/tpm/tspi/logs.h b/src/security/tpm/tspi/logs.h index 3bfd0909175..52632ee7ef4 100644 --- a/src/security/tpm/tspi/logs.h +++ b/src/security/tpm/tspi/logs.h @@ -7,6 +7,8 @@ #include #include +struct tpm_digest; + /* coreboot-specific TPM log format */ void *tpm_cb_log_init(void); @@ -14,12 +16,9 @@ void *tpm_cb_log_cbmem_init(void); void tpm_cb_preram_log_clear(void); uint16_t tpm_cb_log_get_size(const void *log_table); void tpm_cb_log_copy_entries(const void *from, void *to); -int tpm_cb_log_get(int entry_idx, int *pcr, const uint8_t **digest_data, - enum vb2_hash_algorithm *digest_algo, const char **event_name); +int tpm_cb_log_get(int entry_idx, int *pcr, struct tpm_digest *digests, const char **event_name); void tpm_cb_log_add_table_entry(const char *name, const uint32_t pcr, - enum vb2_hash_algorithm digest_algo, - const uint8_t *digest, - const size_t digest_len); + const struct tpm_digest *digests); void tpm_cb_log_dump(void); /* TPM 1.2 log format */ @@ -29,12 +28,9 @@ void *tpm1_log_cbmem_init(void); void tpm1_preram_log_clear(void); uint16_t tpm1_log_get_size(const void *log_table); void tpm1_log_copy_entries(const void *from, void *to); -int tpm1_log_get(int entry_idx, int *pcr, const uint8_t **digest_data, - enum vb2_hash_algorithm *digest_algo, const char **event_name); +int tpm1_log_get(int entry_idx, int *pcr, struct tpm_digest *digests, const char **event_name); void tpm1_log_add_table_entry(const char *name, const uint32_t pcr, - enum vb2_hash_algorithm digest_algo, - const uint8_t *digest, - const size_t digest_len); + const struct tpm_digest *digests); void tpm1_log_dump(void); /* TPM 2.0 log format */ @@ -44,12 +40,9 @@ void *tpm2_log_cbmem_init(void); void tpm2_preram_log_clear(void); uint16_t tpm2_log_get_size(const void *log_table); void tpm2_log_copy_entries(const void *from, void *to); -int tpm2_log_get(int entry_idx, int *pcr, const uint8_t **digest_data, - enum vb2_hash_algorithm *digest_algo, const char **event_name); +int tpm2_log_get(int entry_idx, int *pcr, struct tpm_digest *digests, const char **event_name); void tpm2_log_add_table_entry(const char *name, const uint32_t pcr, - enum vb2_hash_algorithm digest_algo, - const uint8_t *digest, - const size_t digest_len); + const struct tpm_digest *digests); void tpm2_log_startup_locality(int locality); void tpm2_log_dump(void); diff --git a/src/security/tpm/tspi/tspi.c b/src/security/tpm/tspi/tspi.c index 8c951506f29..f4ce0036f1a 100644 --- a/src/security/tpm/tspi/tspi.c +++ b/src/security/tpm/tspi/tspi.c @@ -219,13 +219,14 @@ tpm_result_t tpm_clear_and_reenable(void) return TPM_SUCCESS; } -tpm_result_t tpm_extend_pcr(int pcr, enum vb2_hash_algorithm digest_algo, - const uint8_t *digest, size_t digest_len, const char *name) +tpm_result_t tpm_extend_pcr(int pcr, const struct tpm_digest *digests, const char *name) { tpm_result_t rc; - if (!digest) + if (digests[0].hash_type == VB2_HASH_INVALID) { + /* This may be a sign of an absent TPM. */ return TPM_IOERROR; + } if (!tspi_tpm_log_available()) { rc = tspi_init_crtm(); @@ -245,7 +246,7 @@ tpm_result_t tpm_extend_pcr(int pcr, enum vb2_hash_algorithm digest_algo, } printk(BIOS_DEBUG, "TPM: Extending digest for `%s` into PCR %d\n", name, pcr); - rc = tlcl_extend(pcr, digest, digest_algo); + rc = tlcl_extend(pcr, digests); if (rc != TPM_SUCCESS) { printk(BIOS_ERR, "TPM Error (%#x): Extending hash for `%s` into PCR %d failed.\n", rc, name, pcr); @@ -254,7 +255,7 @@ tpm_result_t tpm_extend_pcr(int pcr, enum vb2_hash_algorithm digest_algo, } if (CONFIG(TPM_MEASURED_BOOT)) - tpm_log_add_table_entry(name, pcr, digest_algo, digest, digest_len); + tpm_log_add_table_entry(name, pcr, digests); printk(BIOS_DEBUG, "TPM: Digest of `%s` to PCR %d %s\n", name, pcr, tspi_tpm_is_setup() ? "measured" : "logged"); @@ -303,6 +304,11 @@ tpm_result_t tpm_measure_region(const struct region_device *rdev, uint8_t pcr, printk(BIOS_ERR, "TPM: Error finalizing hash.\n"); return TPM_CB_HASH_ERROR; } - return tpm_extend_pcr(pcr, tpm_log_alg(), digest, digest_len, rname); + + struct tpm_digest digests[] = { + { .hash_type = tpm_log_alg(), .hash = digest }, + { .hash_type = VB2_HASH_INVALID } + }; + return tpm_extend_pcr(pcr, digests, rname); } #endif /* VBOOT_LIB */ diff --git a/src/security/tpm/tss.h b/src/security/tpm/tss.h index c9aec082620..ac77d3b50d3 100644 --- a/src/security/tpm/tss.h +++ b/src/security/tpm/tss.h @@ -10,6 +10,7 @@ #define TSS_H_ #include +#include #include #include @@ -20,6 +21,13 @@ #include #include +struct tpm_digest { + /* Pointer to an array of length vb2_digest_size(hash_type) or bigger */ + const uint8_t *hash; + /* VB2_HASH_NONE/VB2_HASH_INVALID here marks the end of a digests list */ + enum vb2_hash_algorithm hash_type; +}; + /* * Operations that are applicable to both TPM versions have wrappers which * pick the implementation based on version determined during initialization via @@ -153,10 +161,9 @@ static inline tpm_result_t tlcl_force_clear(void) /** * Perform a TPM_Extend. */ -static inline tpm_result_t tlcl_extend(int pcr_num, const uint8_t *digest_data, - enum vb2_hash_algorithm digest_algo) +static inline tpm_result_t tlcl_extend(int pcr_num, const struct tpm_digest *digests) { - TLCL_CALL(extend, pcr_num, digest_data, digest_algo); + TLCL_CALL(extend, pcr_num, digests); } extern tis_sendrecv_fn tlcl_tis_sendrecv; diff --git a/src/security/tpm/tss/tcg-1.2/tss.c b/src/security/tpm/tss/tcg-1.2/tss.c index 04a02682a98..d050aa6b83e 100644 --- a/src/security/tpm/tss/tcg-1.2/tss.c +++ b/src/security/tpm/tss/tcg-1.2/tss.c @@ -318,18 +318,17 @@ tpm_result_t tlcl1_set_global_lock(void) return tlcl1_write(TPM_NV_INDEX0, NULL, 0); } -tpm_result_t tlcl1_extend(int pcr_num, const uint8_t *digest_data, - enum vb2_hash_algorithm digest_algo) +tpm_result_t tlcl1_extend(int pcr_num, const struct tpm_digest *digests) { struct s_tpm_extend_cmd cmd; uint8_t response[kTpmResponseHeaderLength + kPcrDigestLength]; - if (digest_algo != VB2_HASH_SHA1) - return TPM_CB_INVALID_ARG; + if (digests[0].hash_type != VB2_HASH_SHA1 || digests[1].hash_type != VB2_HASH_INVALID) + return TPM_CB_HASH_ERROR; memcpy(&cmd, &tpm_extend_cmd, sizeof(cmd)); to_tpm_uint32(cmd.buffer + tpm_extend_cmd.pcrNum, pcr_num); - memcpy(cmd.buffer + cmd.inDigest, digest_data, kPcrDigestLength); + memcpy(cmd.buffer + cmd.inDigest, digests[0].hash, kPcrDigestLength); return tlcl1_send_receive(cmd.buffer, response, sizeof(response)); } diff --git a/src/security/tpm/tss/tcg-2.0/tss.c b/src/security/tpm/tss/tcg-2.0/tss.c index 9a2077b8c1e..7decbe26ec2 100644 --- a/src/security/tpm/tss/tcg-2.0/tss.c +++ b/src/security/tpm/tss/tcg-2.0/tss.c @@ -140,18 +140,13 @@ static TPM_ALG_ID tpmalg_from_vb2_hash(enum vb2_hash_algorithm hash_type) } } -/* - * The caller will provide the digest in a 32 byte buffer, let's consider it a - * sha256 digest. - */ -tpm_result_t tlcl2_extend(int pcr_num, const uint8_t *digest_data, - enum vb2_hash_algorithm digest_type) +tpm_result_t tlcl2_extend(int pcr_num, const struct tpm_digest *digests) { struct tpm2_pcr_extend_cmd pcr_ext_cmd; struct tpm2_response *response; TPM_ALG_ID alg; - alg = tpmalg_from_vb2_hash(digest_type); + alg = tpmalg_from_vb2_hash(digests[0].hash_type); if (alg == TPM_ALG_ERROR) return TPM_CB_HASH_ERROR; @@ -159,8 +154,8 @@ tpm_result_t tlcl2_extend(int pcr_num, const uint8_t *digest_data, pcr_ext_cmd.digests.count = 1; pcr_ext_cmd.digests.digests[0].hashAlg = alg; /* Always copying to sha512 as it's the largest one */ - memcpy(pcr_ext_cmd.digests.digests[0].digest.sha512, digest_data, - vb2_digest_size(digest_type)); + memcpy(pcr_ext_cmd.digests.digests[0].digest.sha512, digests[0].hash, + vb2_digest_size(digests[0].hash_type)); response = tlcl2_process_command(TPM2_PCR_Extend, &pcr_ext_cmd); diff --git a/src/security/tpm/tss1.h b/src/security/tpm/tss1.h index 9894e80b200..1f6c48c7287 100644 --- a/src/security/tpm/tss1.h +++ b/src/security/tpm/tss1.h @@ -4,7 +4,6 @@ #define TSS1_H_ #include -#include #include #include @@ -78,6 +77,8 @@ tpm_result_t tlcl1_get_permissions(uint32_t index, uint32_t *permissions); * based on TPM family. */ +struct tpm_digest; + tpm_result_t tlcl1_save_state(void); tpm_result_t tlcl1_resume(void); tpm_result_t tlcl1_startup(void); @@ -88,7 +89,6 @@ tpm_result_t tlcl1_assert_physical_presence(void); tpm_result_t tlcl1_physical_presence_cmd_enable(void); tpm_result_t tlcl1_finalize_physical_presence(void); tpm_result_t tlcl1_force_clear(void); -tpm_result_t tlcl1_extend(int pcr_num, const uint8_t *digest_data, - enum vb2_hash_algorithm digest_algo); +tpm_result_t tlcl1_extend(int pcr_num, const struct tpm_digest *digests); #endif /* TSS1_H_ */ diff --git a/src/security/tpm/tss2.h b/src/security/tpm/tss2.h index 384fa72fca1..6e6a52faf4d 100644 --- a/src/security/tpm/tss2.h +++ b/src/security/tpm/tss2.h @@ -4,7 +4,6 @@ #define TSS2_H_ #include -#include #include #include @@ -89,6 +88,8 @@ tpm_result_t tlcl2_disable_platform_hierarchy(void); * based on TPM family. */ +struct tpm_digest; + tpm_result_t tlcl2_save_state(void); tpm_result_t tlcl2_resume(void); tpm_result_t tlcl2_startup(void); @@ -99,7 +100,6 @@ tpm_result_t tlcl2_assert_physical_presence(void); tpm_result_t tlcl2_physical_presence_cmd_enable(void); tpm_result_t tlcl2_finalize_physical_presence(void); tpm_result_t tlcl2_force_clear(void); -tpm_result_t tlcl2_extend(int pcr_num, const uint8_t *digest_data, - enum vb2_hash_algorithm digest_algo); +tpm_result_t tlcl2_extend(int pcr_num, const struct tpm_digest *digests); #endif /* TSS2_H_ */ diff --git a/src/security/vboot/tpm_common.c b/src/security/vboot/tpm_common.c index dedf8dfebcc..1f7bd3c1729 100644 --- a/src/security/vboot/tpm_common.c +++ b/src/security/vboot/tpm_common.c @@ -47,19 +47,21 @@ tpm_result_t vboot_extend_pcr(struct vb2_context *ctx, int pcr, enum vb2_hash_algorithm algo = tlcl_get_family() == TPM_1 ? VB2_HASH_SHA1 : VB2_HASH_SHA256; + struct tpm_digest digests[] = { + { .hash_type = algo, .hash = buffer }, + { .hash_type = VB2_HASH_INVALID } + }; + switch (which_digest) { /* SHA1 of (devmode|recmode|keyblock) bits */ case BOOT_MODE_PCR: - return tpm_extend_pcr(pcr, algo, buffer, vb2_digest_size(algo), - TPM_PCR_BOOT_MODE); + return tpm_extend_pcr(pcr, digests, TPM_PCR_BOOT_MODE); /* SHA256 of HWID */ case HWID_DIGEST_PCR: - return tpm_extend_pcr(pcr, algo, buffer, vb2_digest_size(algo), - TPM_PCR_GBB_HWID_NAME); + return tpm_extend_pcr(pcr, digests, TPM_PCR_GBB_HWID_NAME); /* firmware version */ case FIRMWARE_VERSION_PCR: - return tpm_extend_pcr(pcr, algo, buffer, vb2_digest_size(algo), - TPM_PCR_FIRMWARE_VERSION); + return tpm_extend_pcr(pcr, digests, TPM_PCR_FIRMWARE_VERSION); default: return TPM_CB_FAIL; } From f914cfe0d8ea66b1d651037c611fab6ef34c3b2e Mon Sep 17 00:00:00 2001 From: Sergii Dmytruk Date: Fri, 12 Dec 2025 21:20:37 +0200 Subject: [PATCH 3/9] security/tpm/tss/tcg-2.0: enable extending multiple PCR banks The marshaling code was already there. This change only increases maximum number of hashes and initializes `tpm2_pcr_extend_cmd` with all digests that were passed in. Upstream-Status: Pending Signed-off-by: Sergii Dmytruk --- src/security/tpm/tss/tcg-2.0/tss.c | 23 ++++++++++++------- src/security/tpm/tss/tcg-2.0/tss_structures.h | 2 +- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/security/tpm/tss/tcg-2.0/tss.c b/src/security/tpm/tss/tcg-2.0/tss.c index 7decbe26ec2..5701541d120 100644 --- a/src/security/tpm/tss/tcg-2.0/tss.c +++ b/src/security/tpm/tss/tcg-2.0/tss.c @@ -144,18 +144,25 @@ tpm_result_t tlcl2_extend(int pcr_num, const struct tpm_digest *digests) { struct tpm2_pcr_extend_cmd pcr_ext_cmd; struct tpm2_response *response; - TPM_ALG_ID alg; + int i; - alg = tpmalg_from_vb2_hash(digests[0].hash_type); - if (alg == TPM_ALG_ERROR) + for (i = 0; digests[i].hash_type != VB2_HASH_INVALID; ++i) { + TPM_ALG_ID alg = tpmalg_from_vb2_hash(digests[i].hash_type); + if (alg == TPM_ALG_ERROR) + return TPM_CB_HASH_ERROR; + + pcr_ext_cmd.digests.digests[i].hashAlg = alg; + + /* Always copying to sha512 as it's the largest one. */ + memcpy(pcr_ext_cmd.digests.digests[i].digest.sha512, digests[i].hash, + vb2_digest_size(digests[i].hash_type)); + } + + if (i == 0) return TPM_CB_HASH_ERROR; pcr_ext_cmd.pcrHandle = HR_PCR + pcr_num; - pcr_ext_cmd.digests.count = 1; - pcr_ext_cmd.digests.digests[0].hashAlg = alg; - /* Always copying to sha512 as it's the largest one */ - memcpy(pcr_ext_cmd.digests.digests[0].digest.sha512, digests[0].hash, - vb2_digest_size(digests[0].hash_type)); + pcr_ext_cmd.digests.count = i; response = tlcl2_process_command(TPM2_PCR_Extend, &pcr_ext_cmd); diff --git a/src/security/tpm/tss/tcg-2.0/tss_structures.h b/src/security/tpm/tss/tcg-2.0/tss_structures.h index 5893ffdb68d..50b23317a9e 100644 --- a/src/security/tpm/tss/tcg-2.0/tss_structures.h +++ b/src/security/tpm/tss/tcg-2.0/tss_structures.h @@ -17,7 +17,7 @@ #define TPM2_RC_SUCCESS 0 #define TPM2_RC_NV_DEFINED 0x14c -#define HASH_COUNT 2 /* SHA-1 and SHA-256 are supported */ +#define HASH_COUNT 4 /* SHA-1, SHA-256, SHA384, SHA512 */ /* Basic TPM2 types. */ typedef uint16_t TPM_SU; From 362ed5a3210cfd89cd0a66e407eba5239c1a2f09 Mon Sep 17 00:00:00 2001 From: Sergii Dmytruk Date: Sat, 13 Dec 2025 16:47:57 +0200 Subject: [PATCH 4/9] security/tpm: stop using fixed-size entries in TPM 2.0 log * Update vendor data to drop entry size and maximum count and add offset to the first unused byte and total number of available bytes. This bumps its major version because the update breaks compatibility. * Represent log table as two structures, tpm_2_log_table and tpm_2_log_bottom, separate by a list of digests in the header. * Remove tpm_2_log_entry. * Add functions to store and parse log entries and use them instead of array operations. * cbmem tool doesn't need an update because it already parses the log as an agile format rather than an array of entries of the same size. Change-Id: I13cebe2a40c220375cc14124ef9b13ea7ee0207f Upstream-Status: Pending Signed-off-by: Sergii Dmytruk --- src/security/tpm/tpm2_log_serialized.h | 64 ++--- src/security/tpm/tspi/log-tpm2.c | 329 ++++++++++++++++--------- 2 files changed, 231 insertions(+), 162 deletions(-) diff --git a/src/security/tpm/tpm2_log_serialized.h b/src/security/tpm/tpm2_log_serialized.h index aaaf576eca3..cc655b4b9f8 100644 --- a/src/security/tpm/tpm2_log_serialized.h +++ b/src/security/tpm/tpm2_log_serialized.h @@ -9,62 +9,36 @@ #define TPM_20_LOG_DATA_MAX_LENGTH 50 #define TPM_20_LOG_VI_MAGIC 0x32544243 /* "CBT2" in LE */ -#define TPM_20_LOG_VI_MAJOR 1 +#define TPM_20_LOG_VI_MAJOR 2 #define TPM_20_LOG_VI_MINOR 0 -/* - * TPM2.0 log entries can't be generally represented as C structures due to - * varying number of digests and their sizes. However, it works as long as - * we're only using single kind of digests. - */ -#if CONFIG(TPM_LOG_TCG) || CONFIG(TPM_LOG_TPM2) -# if CONFIG(TPM_HASH_SHA1) -# define TPM_20_LOG_DIGEST_MAX_LENGTH SHA1_DIGEST_SIZE -# endif -# if CONFIG(TPM_HASH_SHA256) -# define TPM_20_LOG_DIGEST_MAX_LENGTH SHA256_DIGEST_SIZE -# endif -# if CONFIG(TPM_HASH_SHA384) -# define TPM_20_LOG_DIGEST_MAX_LENGTH SHA384_DIGEST_SIZE -# endif -# if CONFIG(TPM_HASH_SHA512) -# define TPM_20_LOG_DIGEST_MAX_LENGTH SHA512_DIGEST_SIZE -# endif +#define TPM_20_VENDOR_INFO_SIZE (sizeof(struct tpm_2_log_bottom) - sizeof(uint8_t)) -# ifndef TPM_20_LOG_DIGEST_MAX_LENGTH -# error "Misconfiguration: failed to determine TPM hashing algorithm" -# endif -#else -# define TPM_20_LOG_DIGEST_MAX_LENGTH 1 /* To avoid compilation error */ -#endif - -/* TCG_PCR_EVENT2 */ -struct tpm_2_log_entry { - uint32_t pcr; - uint32_t event_type; - uint32_t digest_count; /* Always 1 in current implementation */ - uint16_t digest_type; - uint8_t digest[TPM_20_LOG_DIGEST_MAX_LENGTH]; - uint32_t data_length; - uint8_t data[TPM_20_LOG_DATA_MAX_LENGTH]; +/* This log table has two variable-sized portions one of which is in the middle. For this + * reason the declaration is split in two each ending on a variable-sized portion. */ +struct tpm_2_log_table { + struct tcg_efi_spec_id_event header; /* TCG_PCR_EVENT actually */ + // struct tpm_digest_sizes digest_sizes[header.num_of_algorithms]; + // struct tpm_2_log_bottom bottom; } __packed; -struct tpm_2_vendor { +/* The bottom part of the log which follows its first variable-sized portion (list of digest + sizes). */ +struct tpm_2_log_bottom { + /* Size of the following set of fields (doesn't include the size field). */ + uint8_t vendor_info_size; + + /* This is vendor info/data. */ uint8_t reserved; uint8_t version_major; uint8_t version_minor; uint32_t magic; - uint16_t max_entries; uint16_t num_entries; - uint32_t entry_size; -} __packed; + uint16_t next_offset; /* Offset within `events` array */ + uint16_t max_offset; /* Maximum offset within `events` array */ -struct tpm_2_log_table { - struct tcg_efi_spec_id_event header; /* TCG_PCR_EVENT actually */ - struct tpm_digest_sizes digest_sizes[1]; - uint8_t vendor_info_size; - struct tpm_2_vendor vendor; - struct tpm_2_log_entry entries[]; /* Variable number of entries */ + /* Events follow. */ + uint8_t events[]; } __packed; #endif diff --git a/src/security/tpm/tspi/log-tpm2.c b/src/security/tpm/tspi/log-tpm2.c index 0eb36e7c1a9..38160c5cfc6 100644 --- a/src/security/tpm/tspi/log-tpm2.c +++ b/src/security/tpm/tspi/log-tpm2.c @@ -12,6 +12,7 @@ */ #include +#include #include #include #include @@ -22,15 +23,43 @@ #include #include -#define TPM_LOG_SIZE (64 * KiB) -#define MAX_TPM_LOG_ENTRIES ((TPM_LOG_SIZE - sizeof(struct tpm_2_log_table)) / \ - sizeof(struct tpm_2_log_entry)) +#define TPM_LOG_SIZE (64 * KiB) + +struct log_event { + uint32_t pcr; + uint32_t event_type; + uint32_t digest_count; + struct tpm_digest digests[1]; + uint32_t name_len; + const char *name; +}; struct startup_locality_event { char signature[16]; /* "StartupLocality" (NUL-terminated) */ uint8_t startup_locality; /* 0 or 3 */ } __packed; +/* Assumes tclt->header.num_of_algorithms is already set to its final value. */ +static struct tpm_2_log_bottom *get_log_bottom(const struct tpm_2_log_table *tclt) +{ + uint8_t *p; + + /* Start at the first variable-sized part of the header. */ + p = (uint8_t *)tclt->header.digest_sizes; + /* Skip over it. */ + p += le32toh(tclt->header.num_of_algorithms) * sizeof(tclt->header.digest_sizes[0]); + /* `p` points at `uint8_t vendor_info_size` here. */ + return (struct tpm_2_log_bottom *)p; +} + +static uint16_t get_log_footprint(const struct tpm_2_log_table *tclt) +{ + return sizeof(*tclt) + + le32toh(tclt->header.num_of_algorithms) * sizeof(tclt->header.digest_sizes[0]) + + sizeof(struct tpm_2_log_bottom) + + le16toh(get_log_bottom(tclt)->next_offset); +} + void *tpm2_log_cbmem_init(void) { static struct tpm_2_log_table *tclt; @@ -39,6 +68,7 @@ void *tpm2_log_cbmem_init(void) if (ENV_HAS_CBMEM) { struct tcg_efi_spec_id_event *hdr; + struct tpm_2_log_bottom *bottom; tclt = cbmem_find(CBMEM_ID_TPM2_TCG_LOG); if (tclt) @@ -52,173 +82,246 @@ void *tpm2_log_cbmem_init(void) hdr = &tclt->header; hdr->event_type = htole32(EV_NO_ACTION); - hdr->event_size = htole32(33 + sizeof(tclt->vendor)); + hdr->event_size = htole32(28 + + 1 * sizeof(hdr->digest_sizes[0]) + + 1 + + TPM_20_VENDOR_INFO_SIZE); strcpy((char *)hdr->signature, TPM_20_SPEC_ID_EVENT_SIGNATURE); hdr->platform_class = htole32(0x00); // client platform hdr->spec_version_minor = 0x00; hdr->spec_version_major = 0x02; hdr->spec_errata = 0x00; hdr->uintn_size = 0x02; // 64-bit UINT + hdr->num_of_algorithms = htole32(1); hdr->digest_sizes[0].alg_id = htole16(tpm2_alg_from_vb2_hash(tpm_log_alg())); hdr->digest_sizes[0].digest_size = htole16(vb2_digest_size(tpm_log_alg())); - tclt->vendor_info_size = sizeof(tclt->vendor); - tclt->vendor.reserved = 0; - tclt->vendor.version_major = TPM_20_LOG_VI_MAJOR; - tclt->vendor.version_minor = TPM_20_LOG_VI_MINOR; - tclt->vendor.magic = htole32(TPM_20_LOG_VI_MAGIC); - tclt->vendor.max_entries = htole16(MAX_TPM_LOG_ENTRIES); - tclt->vendor.num_entries = htole16(0); - tclt->vendor.entry_size = htole32(sizeof(struct tpm_2_log_entry)); + bottom = get_log_bottom(tclt); + bottom->vendor_info_size = TPM_20_VENDOR_INFO_SIZE; + bottom->reserved = 0; + bottom->version_major = TPM_20_LOG_VI_MAJOR; + bottom->version_minor = TPM_20_LOG_VI_MINOR; + bottom->magic = htole32(TPM_20_LOG_VI_MAGIC); + bottom->num_entries = 0; + bottom->next_offset = 0; + bottom->max_offset = htole16(TPM_LOG_SIZE - get_log_footprint(tclt)); } return tclt; } +/* The function assumes input buffer includes a complete event. */ +static void read_log_event(struct ibuf *ib, struct log_event *ev) +{ + ibuf_read_le32(ib, &ev->pcr); + ibuf_read_le32(ib, &ev->event_type); + ibuf_read_le32(ib, &ev->digest_count); + + uint32_t i; + for (i = 0; i < ev->digest_count; ++i) { + uint16_t alg; + ibuf_read_le16(ib, &alg); + + ev->digests[i].hash_type = tpm2_alg_to_vb2_hash(alg); + ev->digests[i].hash = ibuf_oob_drain(ib, + vb2_digest_size(ev->digests[i].hash_type)); + } + + ibuf_read_le32(ib, &ev->name_len); + ev->name = ibuf_oob_drain(ib, ev->name_len); +} + +/* Returns true if an event was parsed successfully. */ +static bool parse_log_event(const struct tpm_2_log_bottom *bottom, + struct log_event *ev, + uint16_t *offset) +{ + if (*offset == le16toh(bottom->next_offset)) + return false; + + struct ibuf ib; + ibuf_init(&ib, &bottom->events[*offset], le16toh(bottom->next_offset) - *offset); + + read_log_event(&ib, ev); + + *offset += ibuf_nr_read(&ib); + return true; +} + void tpm2_log_dump(void) { - int i, j; + uint16_t offset; + struct log_event ev; struct tpm_2_log_table *tclt; - int hash_size; - const char *alg_name; + const struct tpm_2_log_bottom *bottom; tclt = tpm_log_init(); if (!tclt) return; - hash_size = vb2_digest_size(tpm_log_alg()); - alg_name = vb2_get_hash_algorithm_name(tpm_log_alg()); + bottom = get_log_bottom(tclt); + + offset = 0; + while (parse_log_event(bottom, &ev, &offset)) { + uint32_t i; - printk(BIOS_INFO, "coreboot TPM 2.0 measurements:\n\n"); - for (i = 0; i < le16toh(tclt->vendor.num_entries); i++) { - struct tpm_2_log_entry *tce = &tclt->entries[i]; + printk(BIOS_INFO, " PCR-%u [%s]:\n", ev.pcr, ev.name); - printk(BIOS_INFO, " PCR-%u ", le32toh(tce->pcr)); + for (i = 0; i < ev.digest_count; ++i) { + enum vb2_hash_algorithm hash_type; + int digest_size, j; - for (j = 0; j < hash_size; j++) - printk(BIOS_INFO, "%02x", tce->digest[j]); + hash_type = ev.digests[i].hash_type; + digest_size = vb2_digest_size(hash_type); - printk(BIOS_INFO, " %s [%s]\n", alg_name, tce->data); + printk(BIOS_INFO, " %6s: ", vb2_get_hash_algorithm_name(hash_type)); + for (j = 0; j < digest_size; ++j) + printk(BIOS_INFO, "%02x", ev.digests[i].hash[j]); + printk(BIOS_INFO, "\n"); + } } printk(BIOS_INFO, "\n"); } -void tpm2_log_add_table_entry(const char *name, uint32_t pcr, const struct tpm_digest *digests) +/* The function assumes output buffer has enough space for the new event. */ +static void write_log_event(struct obuf *ob, + const void *data, + size_t data_len, + uint32_t pcr, + uint32_t type, + const struct tpm_digest *digests) { - struct tpm_2_log_table *tclt; - struct tpm_2_log_entry *tce; + uint32_t digest_count = 0; + while (digests[digest_count].hash_type != VB2_HASH_INVALID) + ++digest_count; - tclt = tpm_log_init(); - if (!tclt) { - printk(BIOS_WARNING, "TPM LOG: non-existent!\n"); - return; - } + obuf_write_le32(ob, pcr); + obuf_write_le32(ob, type); + obuf_write_le32(ob, digest_count); - if (!name) { - printk(BIOS_WARNING, "TPM LOG: entry name not set\n"); - return; - } + int i; + for (i = 0; digests[i].hash_type != VB2_HASH_INVALID; ++i) { + int hash_size = vb2_digest_size(digests[i].hash_type); - if (digests[0].hash_type != tpm_log_alg()) { - printk(BIOS_WARNING, "TPM LOG: digest is of unsupported type: %s\n", - vb2_get_hash_algorithm_name(digests[0].hash_type)); - return; + obuf_write_le16(ob, tpm2_alg_from_vb2_hash(digests[i].hash_type)); + obuf_write(ob, digests[i].hash, hash_size); } - if (digests[1].hash_type != VB2_HASH_INVALID) { - printk(BIOS_WARNING, "TPM LOG: can't handle multiple banks\n"); - return; - } + obuf_write_le32(ob, data_len); + obuf_write(ob, data, data_len); +} + +static void add_log_table_entry(struct tpm_2_log_table *tclt, + const void *data, + size_t data_len, + uint32_t pcr, + uint32_t type, + const struct tpm_digest *digests) +{ + int i; + + uint16_t needed_size = 4 * sizeof(uint32_t) + data_len; + for (i = 0; digests[i].hash_type != VB2_HASH_INVALID; ++i) + needed_size += sizeof(uint16_t) + vb2_digest_size(digests[i].hash_type); - if (le16toh(tclt->vendor.num_entries) >= le16toh(tclt->vendor.max_entries)) { - printk(BIOS_WARNING, "TPM LOG: log table is full\n"); + struct tpm_2_log_bottom *bottom = get_log_bottom(tclt); + if (le16toh(bottom->next_offset) + needed_size > le16toh(bottom->max_offset)) { + printk(BIOS_WARNING, "TPM LOG: log is full: %u/%u (need %u)\n", + le16toh(bottom->next_offset), le16toh(bottom->max_offset), needed_size); return; } - tce = &tclt->entries[le16toh(tclt->vendor.num_entries)]; - tclt->vendor.num_entries = htole16(le16toh(tclt->vendor.num_entries) + 1); + struct obuf ob; + obuf_init(&ob, &bottom->events[le16toh(bottom->next_offset)], + le16toh(bottom->max_offset) - le16toh(bottom->next_offset)); - tce->pcr = htole32(pcr); - tce->event_type = htole32(EV_ACTION); + write_log_event(&ob, data, data_len, pcr, type, digests); - tce->digest_count = htole32(1); - tce->digest_type = htole16(tpm2_alg_from_vb2_hash(tpm_log_alg())); - memcpy(tce->digest, digests[0].hash, vb2_digest_size(tpm_log_alg())); - - tce->data_length = htole32(sizeof(tce->data)); - strncpy((char *)tce->data, name, sizeof(tce->data) - 1); - tce->data[sizeof(tce->data) - 1] = '\0'; + bottom->next_offset = htole16(le16toh(bottom->next_offset) + needed_size); + bottom->num_entries = htole16(le16toh(bottom->num_entries) + 1); } -void tpm2_log_startup_locality(int locality) +void tpm2_log_add_table_entry(const char *name, uint32_t pcr, const struct tpm_digest *digests) { - _Static_assert(sizeof(struct startup_locality_event) <= TPM_20_LOG_DATA_MAX_LENGTH, - "Data field of TCG log for TPM2 is too short for Startup Locality.\n"); - - struct tpm_2_log_table *tclt; - struct tpm_2_log_entry *tce; - struct startup_locality_event locality_event; - - tclt = tpm_log_init(); + struct tpm_2_log_table *tclt = tpm_log_init(); if (!tclt) { printk(BIOS_WARNING, "TPM LOG: non-existent!\n"); return; } - if (le16toh(tclt->vendor.num_entries) >= le16toh(tclt->vendor.max_entries)) { - printk(BIOS_WARNING, "TPM LOG: log table is full\n"); + if (!name) { + printk(BIOS_WARNING, "TPM LOG: entry name not set\n"); return; } - tce = &tclt->entries[le16toh(tclt->vendor.num_entries)]; - tclt->vendor.num_entries = htole16(le16toh(tclt->vendor.num_entries) + 1); + add_log_table_entry(tclt, name, strlen(name) + 1, pcr, EV_ACTION, digests); +} - /* EV_NO_ACTION events use PCR-0 by default. */ - tce->pcr = htole32(0); - tce->event_type = htole32(EV_NO_ACTION); +void tpm2_log_startup_locality(int locality) +{ + struct tpm_2_log_table *tclt = tpm_log_init(); + if (!tclt) { + printk(BIOS_WARNING, "TPM LOG: non-existent!\n"); + return; + } /* EV_NO_ACTION events use zeroes for digest(s). */ - tce->digest_count = htole32(1); - tce->digest_type = htole16(tpm2_alg_from_vb2_hash(tpm_log_alg())); - memset(tce->digest, 0, vb2_digest_size(tpm_log_alg())); - - tce->data_length = htole32(sizeof(tce->data)); + struct vb2_hash zero_hash = {0}; + const struct tpm_digest digests[] = { + { .hash_type = tpm_log_alg(), .hash = zero_hash.raw }, + { .hash_type = VB2_HASH_INVALID } + }; - strcpy(locality_event.signature, "StartupLocality"); - locality_event.startup_locality = locality; + struct startup_locality_event event_data; + strcpy(event_data.signature, "StartupLocality"); + event_data.startup_locality = locality; - memset(tce->data, 0, sizeof(tce->data)); - memcpy(tce->data, &locality_event, sizeof(locality_event)); + /* EV_NO_ACTION events use PCR-0 by default. */ + add_log_table_entry(tclt, &event_data, sizeof(event_data), 0, EV_NO_ACTION, digests); } int tpm2_log_get(int entry_idx, int *pcr, struct tpm_digest *digests, const char **event_name) { + uint16_t offset; + struct log_event ev; + int idx; struct tpm_2_log_table *tclt; - struct tpm_2_log_entry *tce; + struct tpm_2_log_bottom *bottom; tclt = tpm_log_init(); if (!tclt) return 1; - if (entry_idx < 0 || entry_idx >= le16toh(tclt->vendor.num_entries)) + bottom = get_log_bottom(tclt); + if (entry_idx < 0 || entry_idx >= le16toh(bottom->num_entries)) return 1; - tce = &tclt->entries[entry_idx]; + offset = 0; + idx = 0; + while (parse_log_event(bottom, &ev, &offset)) { + if (idx != entry_idx) { + ++idx; + continue; + } + + int i; + for (i = 0; i < ev.digest_count; ++i) + digests[i] = ev.digests[i]; + digests[ev.digest_count].hash_type = VB2_HASH_INVALID; - digests[0].hash_type = tpm_log_alg(); /* We validate algorithm on addition */ - digests[0].hash = tce->digest; - digests[1].hash_type = VB2_HASH_INVALID; + *pcr = ev.pcr; + *event_name = ev.name; + return 0; + } - *pcr = le32toh(tce->pcr); - *event_name = (char *)tce->data; - return 0; + return 1; } uint16_t tpm2_log_get_size(const void *log_table) { const struct tpm_2_log_table *tclt = log_table; - return le16toh(tclt->vendor.num_entries); + return le16toh(get_log_bottom(tclt)->num_entries); } void tpm2_preram_log_clear(void) @@ -226,37 +329,29 @@ void tpm2_preram_log_clear(void) printk(BIOS_INFO, "TPM LOG: clearing the log\n"); /* * Pre-RAM log is only for internal use and isn't exported anywhere, hence it's header - * is not initialized. + * is not fully initialized. */ struct tpm_2_log_table *tclt = (struct tpm_2_log_table *)_tpm_log; - tclt->vendor.max_entries = htole16(MAX_TPM_LOG_ENTRIES); - tclt->vendor.num_entries = htole16(0); + tclt->header.num_of_algorithms = htole32(1); + + struct tpm_2_log_bottom *bottom = get_log_bottom(tclt); + bottom->num_entries = 0; + bottom->next_offset = 0; + bottom->max_offset = htole16((_etpm_log - _tpm_log) - get_log_footprint(tclt)); } void tpm2_log_copy_entries(const void *from, void *to) { - const struct tpm_2_log_table *from_log = from; - struct tpm_2_log_table *to_log = to; - int i; - - for (i = 0; i < le16toh(from_log->vendor.num_entries); i++) { - if (le16toh(to_log->vendor.num_entries) >= le16toh(to_log->vendor.max_entries)) { - printk(BIOS_WARNING, "TPM LOG: log table is full\n"); - return; - } - - struct tpm_2_log_entry *tce = - &to_log->entries[le16toh(to_log->vendor.num_entries)]; - to_log->vendor.num_entries = htole16(le16toh(to_log->vendor.num_entries) + 1); + const struct tpm_2_log_bottom *from_bottom = get_log_bottom(from); + struct tpm_2_log_bottom *to_bottom = get_log_bottom(to); - tce->pcr = from_log->entries[i].pcr; - tce->event_type = from_log->entries[i].event_type; - - tce->digest_count = from_log->entries[i].digest_count; - tce->digest_type = from_log->entries[i].digest_type; - memcpy(tce->digest, from_log->entries[i].digest, sizeof(tce->digest)); - - tce->data_length = from_log->entries[i].data_length; - memcpy(tce->data, from_log->entries[i].data, sizeof(tce->data)); + if (le16toh(to_bottom->max_offset) < le16toh(from_bottom->next_offset)) { + printk(BIOS_WARNING, + "TPM LOG: not enough space at destination to copy event log entries!\n"); + return; } + + memcpy(to_bottom->events, from_bottom->events, le16toh(from_bottom->next_offset)); + to_bottom->num_entries = from_bottom->num_entries; + to_bottom->next_offset = from_bottom->next_offset; } From 943f1b6a801b11c03a67aaee82716e5513a219f1 Mon Sep 17 00:00:00 2001 From: Sergii Dmytruk Date: Sun, 14 Dec 2025 20:52:30 +0200 Subject: [PATCH 5/9] security/intel/cbnt/measurement.c: extract measurement functions Get rid of the large if-else statement in intel_cbnt_inject_ibg_measurements() by moving code from branches into separate functions. Upstream-Status: Pending Change-Id: I892c56d37abac1b43c68ac761d428c3560007246 Signed-off-by: Sergii Dmytruk --- src/security/intel/cbnt/measurement.c | 350 +++++++++++++------------- 1 file changed, 178 insertions(+), 172 deletions(-) diff --git a/src/security/intel/cbnt/measurement.c b/src/security/intel/cbnt/measurement.c index 4e540cd0483..abdc96a9ccd 100644 --- a/src/security/intel/cbnt/measurement.c +++ b/src/security/intel/cbnt/measurement.c @@ -38,6 +38,12 @@ #define KM_HASH_USAGE_SDEV (1 << 3) #define KM_HASH_USAGE_PFR_CPLD (1 << 4) +/* + * Allow for 4 8192-bit digests in PCR-0 measurements which is an unlikely situation, so this + * buffer should be enough to not run out of space (data measured into PCR-7 is smaller). + */ +#define MAX_DATA_SIZE (sizeof(uint64_t) + sizeof(uint16_t) + 4 * KiB) + /* * SRTM version is an upper-case hex dump of ACM's "Date" (offset 20) and "TXT SVN" (offset 28) * fields as a NUL-terminated UTF16 string. @@ -636,6 +642,173 @@ static char hex_digit(int nibble) return 'A' + nibble - 10; } +static void measure_intel_tgl_style(uint64_t biosacm_policy, bool auth_measure) +{ + uint8_t data[MAX_DATA_SIZE]; + struct obuf data_ob; + obuf_init(&data_ob, data, sizeof(data)); + + /* + * Making and hashing PCR-0 data. + * + * Pseudo-code of the data to be measured into PCR-0 for TigerLake and newer + * (older hardware isn't supported yet): + * + * struct { + * uint64_t ACM_POLICY_STATUS; + * uint16_t ACM.Header.SVN; + * uint8_t ACM.Signature[ACM signature size]; + * uint8_t KM.Signature[KM signature size]; + * uint8_t BPM.Signature[BPM signature size]; + * uint8_t IBB.Digest[IBB digest size]; + * } PCR0_DATA; + */ + + /* ACM_POLICY_STATUS (won't run out of space on this one) */ + (void)obuf_write_le64(&data_ob, biosacm_policy); + + if (fill_pcr0_acm_fields(&data_ob) != CB_SUCCESS) { + printk(BIOS_ERR, "CBnT: failed to fill ACM fields of PCR-0 measurement data\n"); + return; + } + + if (copy_km_signature(&data_ob) != CB_SUCCESS) { + printk(BIOS_ERR, "CBnT: failed to copy KM signature for PCR-0 measurement\n"); + return; + } + + if (copy_bpm_signature(&data_ob) != CB_SUCCESS) { + printk(BIOS_ERR, "CBnT: failed to copy BPM signature for PCR-0 measurement\n"); + return; + } + + struct vb2_hash hash; + if (vb2_hash_calculate(vboot_hwcrypto_allowed(), data, obuf_nr_written(&data_ob), + tpm_log_alg(), &hash)) { + printk(BIOS_ERR, "CBnT: failed to hash PCR-0 measurement data\n"); + return; + } + + /* Per BWG this should be logged with EV_S_CRTM_CONTENTS type. */ + const struct tpm_digest pcr0_digests[] = { + { .hash_type = hash.algo, .hash = hash.raw }, + { .hash_type = VB2_HASH_INVALID } + }; + tpm_log_add_table_entry(CBNT_EVENT_LOG_MESSAGE, 0, pcr0_digests); + + /* Optionally making and hashing PCR-7 data (not supported since MTL). */ + if (!auth_measure) + return; + + /* Reuse the first 2 fields of PCR-0 data which are identical in both cases. */ + obuf_init(&data_ob, data, sizeof(data)); + (void)obuf_oob_fill(&data_ob, sizeof(uint64_t) + sizeof(uint16_t)); + + if (!make_pcr7_hash(&data_ob, &hash)) { + printk(BIOS_ERR, "CBnT: failed to build and hash PCR-7 measurement data\n"); + return; + } + + /* Per BWG this should be logged with EV_EFI_VARIABLE_DRIVER_CONFIG type + and event name should be a Unicode string. */ + const struct tpm_digest pcr7_digests[] = { + { .hash_type = hash.algo, .hash = hash.raw }, + { .hash_type = VB2_HASH_INVALID } + }; + tpm_log_add_table_entry(CBNT_EVENT_LOG_MESSAGE, 7, pcr7_digests); + printk(BIOS_INFO, "CBnT: reconstructed PCR-7 measurement\n"); +} + +static void measure_tcg_style(uint64_t biosacm_policy) +{ + uint8_t data[MAX_DATA_SIZE]; + struct obuf data_ob; + obuf_init(&data_ob, data, sizeof(data)); + + size_t acm_len; + const struct acm_header_v3 *acm = find_in_fit(FIT_ENTRY_TYPE_SACM, &acm_len); + if (acm == NULL) { + printk(BIOS_ERR, "CBnT: failed to find SACM\n"); + return; + } + + uint8_t crtm_version[6]; + memcpy(&crtm_version[0], &acm->date, 4); + memcpy(&crtm_version[4], &acm->txt_svn, 2); + + char crtm_version_str[SCRTM_VERSION_LENGTH] = {0}; + uint16_t crtm_version_utf16[SCRTM_VERSION_LENGTH] = {0}; + for (int i = 0; i < sizeof(crtm_version); ++i) { + crtm_version_str[i*2 + 0] = hex_digit(crtm_version[i] >> 4); + crtm_version_str[i*2 + 1] = hex_digit(crtm_version[i] & 0xf); + + crtm_version_utf16[i*2 + 0] = crtm_version_str[i*2 + 0]; + crtm_version_utf16[i*2 + 1] = crtm_version_str[i*2 + 1]; + } + + struct vb2_hash hash; + if (vb2_hash_calculate(vboot_hwcrypto_allowed(), crtm_version_utf16, + sizeof(crtm_version_utf16), tpm_log_alg(), &hash)) { + printk(BIOS_ERR, "CBnT: failed to hash CRTM version\n"); + return; + } + /* Per BWG this should be logged with EV_S_CRTM_VERSION type. */ + const struct tpm_digest crtm_digests[] = { + { .hash_type = hash.algo, .hash = hash.raw }, + { .hash_type = VB2_HASH_INVALID } + }; + tpm_log_add_table_entry(crtm_version_str, 0, crtm_digests); + + if (copy_ibb_hash(&data_ob, tpm2_alg_from_vb2_hash(tpm_log_alg())) != CB_SUCCESS) { + printk(BIOS_ERR, "CBnT: failed to obtain IBB digest\n"); + return; + } + /* Per BWG this should be logged with EV_POST_CODE type. */ + const struct tpm_digest post_code_digests[] = { + { .hash_type = tpm_log_alg(), .hash = data }, + { .hash_type = VB2_HASH_INVALID } + }; + tpm_log_add_table_entry("Boot Guard Measured IBB", 0, post_code_digests); + + /* + * struct { + * uint64_t ACM_POLICY_STATUS; + * uint8_t KM.Signature[KM signature size]; + * uint8_t BPM.Signature[BPM signature size]; + * } POLICY_DATA; + */ + obuf_init(&data_ob, data, sizeof(data)); + + /* ACM_POLICY_STATUS (won't run out of space on this one) */ + (void)obuf_write_le64(&data_ob, biosacm_policy); + if (copy_acm_signature(&data_ob) != CB_SUCCESS) { + printk(BIOS_ERR, "CBnT: failed to copy ACM signature\n"); + return; + } + if (copy_km_signature(&data_ob) != CB_SUCCESS) { + printk(BIOS_ERR, "CBnT: failed to copy KM signature\n"); + return; + } + if (copy_bpm_signature(&data_ob) != CB_SUCCESS) { + printk(BIOS_ERR, "CBnT: failed to copy BPM signature for PCR-0 measurement\n"); + return; + } + if (vb2_hash_calculate(vboot_hwcrypto_allowed(), data, obuf_nr_written(&data_ob), + tpm_log_alg(), &hash)) { + printk(BIOS_ERR, "CBnT: failed to hash POLICY_DATA\n"); + return; + } + + /* Per BWG this should be logged with EV_POST_CODE type. */ + const struct tpm_digest policy_digests[] = { + { .hash_type = hash.algo, .hash = hash.raw }, + { .hash_type = VB2_HASH_INVALID } + }; + if (tpm_extend_pcr(0, policy_digests, + "BIOS Measured Boot Guard Policy") != TPM_SUCCESS) + printk(BIOS_ERR, "CBnT: failed to extend POLICY_DATA\n"); +} + void intel_cbnt_inject_ibg_measurements(void) { const union cbnt_biosacm_policy biosacm_sts = { @@ -671,178 +844,11 @@ void intel_cbnt_inject_ibg_measurements(void) return; } - /* - * Allow for 4 8192-bit digests in PCR-0 measurements which is an unlikely situation, - * so this buffer should be enough to not run out of space (data measured into PCR-7 is - * smaller). - */ - uint8_t data[sizeof(uint64_t) + sizeof(uint16_t) + 4 * KiB]; - struct obuf data_ob; - obuf_init(&data_ob, data, sizeof(data)); - - if (conventional_measurements) { - /* - * Making and hashing PCR-0 data. - * - * Pseudo-code of the data to be measured into PCR-0 for TigerLake and newer - * (older hardware isn't supported yet): - * - * struct { - * uint64_t ACM_POLICY_STATUS; - * uint16_t ACM.Header.SVN; - * uint8_t ACM.Signature[ACM signature size]; - * uint8_t KM.Signature[KM signature size]; - * uint8_t BPM.Signature[BPM signature size]; - * uint8_t IBB.Digest[IBB digest size]; - * } PCR0_DATA; - */ - - /* ACM_POLICY_STATUS (won't run out of space on this one) */ - (void)obuf_write_le64(&data_ob, biosacm_sts.raw & biosacm_sts_mask); - - if (fill_pcr0_acm_fields(&data_ob) != CB_SUCCESS) { - printk(BIOS_ERR, - "CBnT: failed to fill ACM fields of PCR-0 measurement data\n"); - return; - } - - if (copy_km_signature(&data_ob) != CB_SUCCESS) { - printk(BIOS_ERR, - "CBnT: failed to copy KM signature for PCR-0 measurement\n"); - return; - } - - if (copy_bpm_signature(&data_ob) != CB_SUCCESS) { - printk(BIOS_ERR, - "CBnT: failed to copy BPM signature for PCR-0 measurement\n"); - return; - } - - struct vb2_hash hash; - if (vb2_hash_calculate(vboot_hwcrypto_allowed(), data, - obuf_nr_written(&data_ob), tpm_log_alg(), &hash)) { - printk(BIOS_ERR, "CBnT: failed to hash PCR-0 measurement data\n"); - return; - } - - /* Per BWG this should be logged with EV_S_CRTM_CONTENTS type. */ - const struct tpm_digest pcr0_digests[] = { - { .hash_type = hash.algo, .hash = hash.raw }, - { .hash_type = VB2_HASH_INVALID } - }; - tpm_log_add_table_entry(CBNT_EVENT_LOG_MESSAGE, 0, pcr0_digests); - - /* Optionally making and hashing PCR-7 data (not supported since MTL). */ - if (auth_measure) { - /* Reuse the first 2 fields of PCR-0 data which are identical in both - cases. */ - obuf_init(&data_ob, data, sizeof(data)); - (void)obuf_oob_fill(&data_ob, sizeof(uint64_t) + sizeof(uint16_t)); - - if (!make_pcr7_hash(&data_ob, &hash)) { - printk(BIOS_ERR, - "CBnT: failed to build and hash PCR-7 measurement data\n"); - return; - } - - /* Per BWG this should be logged with EV_EFI_VARIABLE_DRIVER_CONFIG type - and event name should be a Unicode string. */ - const struct tpm_digest pcr7_digests[] = { - { .hash_type = hash.algo, .hash = hash.raw }, - { .hash_type = VB2_HASH_INVALID } - }; - tpm_log_add_table_entry(CBNT_EVENT_LOG_MESSAGE, 7, pcr7_digests); - printk(BIOS_INFO, "CBnT: reconstructed PCR-7 measurement\n"); - } - - } else { - size_t acm_len; - const struct acm_header_v3 *acm = find_in_fit(FIT_ENTRY_TYPE_SACM, &acm_len); - if (acm == NULL) { - printk(BIOS_ERR, "CBnT: failed to find SACM\n"); - return; - } - - uint8_t crtm_version[6]; - memcpy(&crtm_version[0], &acm->date, 4); - memcpy(&crtm_version[4], &acm->txt_svn, 2); - - char crtm_version_str[SCRTM_VERSION_LENGTH] = {0}; - uint16_t crtm_version_utf16[SCRTM_VERSION_LENGTH] = {0}; - for (int i = 0; i < sizeof(crtm_version); ++i) { - crtm_version_str[i*2 + 0] = hex_digit(crtm_version[i] >> 4); - crtm_version_str[i*2 + 1] = hex_digit(crtm_version[i] & 0xf); - - crtm_version_utf16[i*2 + 0] = crtm_version_str[i*2 + 0]; - crtm_version_utf16[i*2 + 1] = crtm_version_str[i*2 + 1]; - } - - struct vb2_hash hash; - if (vb2_hash_calculate(vboot_hwcrypto_allowed(), crtm_version_utf16, - sizeof(crtm_version_utf16), tpm_log_alg(), &hash)) { - printk(BIOS_ERR, "CBnT: failed to hash CRTM version\n"); - return; - } - /* Per BWG this should be logged with EV_S_CRTM_VERSION type. */ - const struct tpm_digest crtm_digests[] = { - { .hash_type = hash.algo, .hash = hash.raw }, - { .hash_type = VB2_HASH_INVALID } - }; - tpm_log_add_table_entry(crtm_version_str, 0, crtm_digests); - - if (copy_ibb_hash(&data_ob, tpm2_alg_from_vb2_hash(tpm_log_alg())) != - CB_SUCCESS) { - printk(BIOS_ERR, "CBnT: failed to obtain IBB digest\n"); - return; - } - /* Per BWG this should be logged with EV_POST_CODE type. */ - const struct tpm_digest post_code_digests[] = { - { .hash_type = tpm_log_alg(), .hash = data }, - { .hash_type = VB2_HASH_INVALID } - }; - tpm_log_add_table_entry("Boot Guard Measured IBB", 0, post_code_digests); - - /* - * struct { - * uint64_t ACM_POLICY_STATUS; - * uint8_t KM.Signature[KM signature size]; - * uint8_t BPM.Signature[BPM signature size]; - * } POLICY_DATA; - */ - obuf_init(&data_ob, data, sizeof(data)); - - /* ACM_POLICY_STATUS (won't run out of space on this one) */ - (void)obuf_write_le64(&data_ob, biosacm_sts.raw & biosacm_sts_mask); - if (copy_acm_signature(&data_ob) != CB_SUCCESS) { - printk(BIOS_ERR, "CBnT: failed to copy ACM signature\n"); - return; - } - if (copy_km_signature(&data_ob) != CB_SUCCESS) { - printk(BIOS_ERR, "CBnT: failed to copy KM signature\n"); - return; - } - if (copy_bpm_signature(&data_ob) != CB_SUCCESS) { - printk(BIOS_ERR, - "CBnT: failed to copy BPM signature for PCR-0 measurement\n"); - return; - } - if (vb2_hash_calculate(vboot_hwcrypto_allowed(), data, - obuf_nr_written(&data_ob), tpm_log_alg(), &hash)) { - printk(BIOS_ERR, "CBnT: failed to hash POLICY_DATA\n"); - return; - } - - /* Per BWG this should be logged with EV_POST_CODE type. */ - const struct tpm_digest policy_digests[] = { - { .hash_type = hash.algo, .hash = hash.raw }, - { .hash_type = VB2_HASH_INVALID } - }; - if (tpm_extend_pcr(0, policy_digests, - "BIOS Measured Boot Guard Policy") != TPM_SUCCESS) { - printk(BIOS_ERR, "CBnT: failed to extend POLICY_DATA\n"); - return; - } - } + uint64_t biosacm_policy = biosacm_sts.raw & biosacm_sts_mask; + if (conventional_measurements) + measure_intel_tgl_style(biosacm_policy, auth_measure); + else + measure_tcg_style(biosacm_policy); /* * BWG suggests to Perform reconstruction at this point, but we are likely to continue From 27ebc1b37eb23d3cd57ed36ce3da1faf00703b2d Mon Sep 17 00:00:00 2001 From: Sergii Dmytruk Date: Tue, 16 Dec 2025 00:44:15 +0200 Subject: [PATCH 6/9] security/intel/cbnt/measurement.c: support multiple PCR banks Updates the code to support hashing for more than one digest algorithm when that's available, the code should work as before at this point. Change-Id: I4e0ea97946e6c8cafbc21a6418b8cb5e7d087df0 Upstream-Status: Pending Signed-off-by: Sergii Dmytruk --- src/security/intel/cbnt/measurement.c | 237 +++++++++++++++++--------- src/security/tpm/tspi.h | 48 ++++++ src/security/tpm/tspi/tspi.c | 30 ++++ 3 files changed, 230 insertions(+), 85 deletions(-) diff --git a/src/security/intel/cbnt/measurement.c b/src/security/intel/cbnt/measurement.c index abdc96a9ccd..8a34f5f8581 100644 --- a/src/security/intel/cbnt/measurement.c +++ b/src/security/intel/cbnt/measurement.c @@ -477,7 +477,23 @@ static enum cb_err fill_pcr7_km_fields(struct obuf *ob) return CB_SUCCESS; } -static enum cb_err copy_ibb_hash(struct obuf *ob, uint16_t tpm2_alg) +static const struct hash_struct *find_ibb_digest(const struct bpm_ibbs *ibbs, + enum vb2_hash_algorithm alg) +{ + unsigned int i; + const struct hash_struct *ibb_hash = ibbs->digest_list.hashes; + uint16_t tpm2_alg = tpm2_alg_from_vb2_hash(alg); + for (i = 0; i < ibbs->digest_list.count; ++i) { + if (ibb_hash->alg == tpm2_alg) + return ibb_hash; + + ibb_hash = (const void *)&ibb_hash->data[ibb_hash->size]; + } + + return NULL; +} + +static enum cb_err copy_ibb_hash(struct obuf *ob, enum vb2_hash_algorithm alg) { size_t bpm_len; const struct bpm_header *bpm = find_in_fit(FIT_ENTRY_TYPE_BPM, &bpm_len); @@ -486,16 +502,9 @@ static enum cb_err copy_ibb_hash(struct obuf *ob, uint16_t tpm2_alg) return CB_ERR; } - unsigned int i; const struct bpm_ibbs *ibbs = (const void *)bpm->se_element; - const struct hash_struct *ibb_hash = ibbs->digest_list.hashes; - for (i = 0; i < ibbs->digest_list.count; ++i) { - if (ibb_hash->alg == tpm2_alg) - break; - ibb_hash = (const void *)&ibb_hash->data[ibb_hash->size]; - } - - if (i == ibbs->digest_list.count) { + const struct hash_struct *ibb_hash = find_ibb_digest(ibbs, alg); + if (ibb_hash == NULL) { printk(BIOS_ERR, "CBnT: failed to find matching IBB digest\n"); return CB_ERR; } @@ -509,7 +518,7 @@ static enum cb_err copy_ibb_hash(struct obuf *ob, uint16_t tpm2_alg) return CB_SUCCESS; } -static enum cb_err copy_bpm_signature(struct obuf *ob) +static enum cb_err copy_bpm_signature(struct obuf *ob, enum vb2_hash_algorithm alg) { size_t bpm_len; const struct bpm_header *bpm = find_in_fit(FIT_ENTRY_TYPE_BPM, &bpm_len); @@ -528,7 +537,7 @@ static enum cb_err copy_bpm_signature(struct obuf *ob) return CB_ERR; } - return copy_ibb_hash(ob, tpm2_alg_from_vb2_hash(tpm_log_alg())); + return copy_ibb_hash(ob, alg); } /* @@ -545,9 +554,9 @@ static enum cb_err copy_bpm_signature(struct obuf *ob) * * Applies only for TPM2.0. * - * Returns true and initializes *hash on success. + * Returns true on success. */ -static bool make_pcr7_hash(struct obuf *ob, struct vb2_hash *hash) +static bool make_pcr7_data(struct obuf *ob) { /* * ACM.KeyHash[32] @@ -566,13 +575,6 @@ static bool make_pcr7_hash(struct obuf *ob, struct vb2_hash *hash) return false; } - size_t size; - const void *data = obuf_contents(ob, &size); - if (vb2_hash_calculate(vboot_hwcrypto_allowed(), data, size, tpm_log_alg(), hash)) { - printk(BIOS_ERR, "CBnT: failed to hash PCR-7 measurement data\n"); - return false; - } - return true; } @@ -593,15 +595,8 @@ int intel_cbnt_get_locality(void) return biosacm_sts.status.tpm_startup_locality == 0 ? 3 : 0; } -static enum cb_err cap_pcrs(union cbnt_biosacm_policy biosacm_sts) +static enum cb_err cap_pcrs_for_alg(enum vb2_hash_algorithm alg) { - /* TODO: when adding support of all active PCR banks to Measured Boot, implement - capping those PCRs for which there is no corresponding IBB digest in BPM. */ - - /* Nothing to do if there was no TPM failure. */ - if (biosacm_sts.status.tpm_success) - return CB_SUCCESS; - size_t bpm_len; const struct bpm_header *bpm = find_in_fit(FIT_ENTRY_TYPE_BPM, &bpm_len); if (bpm == NULL) { @@ -617,8 +612,8 @@ static enum cb_err cap_pcrs(union cbnt_biosacm_policy biosacm_sts) const uint32_t separator = 0x00000001; struct vb2_hash hash; - if (vb2_hash_calculate(vboot_hwcrypto_allowed(), &separator, sizeof(separator), - tpm_log_alg(), &hash)) { + if (vb2_hash_calculate(vboot_hwcrypto_allowed(), &separator, sizeof(separator), alg, + &hash)) { printk(BIOS_ERR, "CBnT: failed to hash PCR separator\n"); return CB_ERR; } @@ -635,6 +630,41 @@ static enum cb_err cap_pcrs(union cbnt_biosacm_policy biosacm_sts) return CB_SUCCESS; } +static enum cb_err cap_pcrs(union cbnt_biosacm_policy biosacm_sts) +{ + size_t bpm_len; + const struct bpm_header *bpm = find_in_fit(FIT_ENTRY_TYPE_BPM, &bpm_len); + if (bpm == NULL) { + printk(BIOS_ERR, "CBnT: failed to find BPM\n"); + return CB_ERR; + } + + const struct bpm_ibbs *ibbs = (const void *)bpm->se_element; + + int i; + for (i = 0; i < ENABLED_TPM_ALGS_NUM; ++i) { + enum vb2_hash_algorithm alg = enabled_tpm_algs[i]; + if (!tpm_log_alg_active(alg)) + continue; + + /* + * If there was a TPM failure, cap all active banks. Otherwise, cap those + * active banks for which there is no corresponding IBB digests in BPM. + */ + if (biosacm_sts.status.tpm_success && find_ibb_digest(ibbs, alg) != NULL) + continue; + + enum cb_err err = cap_pcrs_for_alg(alg); + if (err != CB_SUCCESS) { + printk(BIOS_WARNING, "CBnT: failed to cap %s bank\n", + vb2_get_hash_algorithm_name(alg)); + return err; + } + } + + return CB_SUCCESS; +} + static char hex_digit(int nibble) { if (nibble < 10) @@ -677,54 +707,65 @@ static void measure_intel_tgl_style(uint64_t biosacm_policy, bool auth_measure) return; } - if (copy_bpm_signature(&data_ob) != CB_SUCCESS) { - printk(BIOS_ERR, "CBnT: failed to copy BPM signature for PCR-0 measurement\n"); - return; - } + int i, j; + struct tpm_digests pcr0_digests; + for (i = 0, j = 0; i < ENABLED_TPM_ALGS_NUM; ++i) { + enum vb2_hash_algorithm alg = enabled_tpm_algs[i]; + if (!tpm_log_alg_active(alg)) + continue; - struct vb2_hash hash; - if (vb2_hash_calculate(vboot_hwcrypto_allowed(), data, obuf_nr_written(&data_ob), - tpm_log_alg(), &hash)) { - printk(BIOS_ERR, "CBnT: failed to hash PCR-0 measurement data\n"); - return; + struct obuf local_ob = data_ob; + if (copy_bpm_signature(&local_ob, alg) != CB_SUCCESS) { + printk(BIOS_ERR, + "CBnT: failed to copy BPM signature for PCR-0 measurement\n"); + return; + } + + if (vb2_hash_calculate(vboot_hwcrypto_allowed(), data, + obuf_nr_written(&data_ob), alg, + &pcr0_digests.hashes[i])) { + printk(BIOS_ERR, "CBnT: failed to hash PCR-0 measurement data\n"); + return; + } + + pcr0_digests.values[j].hash_type = alg; + pcr0_digests.values[j].hash = pcr0_digests.hashes[i].raw; + ++j; } + pcr0_digests.values[j].hash_type = VB2_HASH_INVALID; + /* Per BWG this should be logged with EV_S_CRTM_CONTENTS type. */ - const struct tpm_digest pcr0_digests[] = { - { .hash_type = hash.algo, .hash = hash.raw }, - { .hash_type = VB2_HASH_INVALID } - }; - tpm_log_add_table_entry(CBNT_EVENT_LOG_MESSAGE, 0, pcr0_digests); + tpm_log_add_table_entry(CBNT_EVENT_LOG_MESSAGE, 0, pcr0_digests.values); /* Optionally making and hashing PCR-7 data (not supported since MTL). */ if (!auth_measure) return; + struct tpm_digests pcr7_digests; + /* Reuse the first 2 fields of PCR-0 data which are identical in both cases. */ obuf_init(&data_ob, data, sizeof(data)); (void)obuf_oob_fill(&data_ob, sizeof(uint64_t) + sizeof(uint16_t)); - if (!make_pcr7_hash(&data_ob, &hash)) { - printk(BIOS_ERR, "CBnT: failed to build and hash PCR-7 measurement data\n"); + if (!make_pcr7_data(&data_ob)) { + printk(BIOS_ERR, "CBnT: failed to build PCR-7 measurement data\n"); + return; + } + + if (!tpm_make_digests(data, obuf_nr_written(&data_ob), NULL, &pcr7_digests)) { + printk(BIOS_ERR, "CBnT: failed to hash PCR-7 measurement data\n"); return; } /* Per BWG this should be logged with EV_EFI_VARIABLE_DRIVER_CONFIG type and event name should be a Unicode string. */ - const struct tpm_digest pcr7_digests[] = { - { .hash_type = hash.algo, .hash = hash.raw }, - { .hash_type = VB2_HASH_INVALID } - }; - tpm_log_add_table_entry(CBNT_EVENT_LOG_MESSAGE, 7, pcr7_digests); + tpm_log_add_table_entry(CBNT_EVENT_LOG_MESSAGE, 7, pcr7_digests.values); printk(BIOS_INFO, "CBnT: reconstructed PCR-7 measurement\n"); } static void measure_tcg_style(uint64_t biosacm_policy) { - uint8_t data[MAX_DATA_SIZE]; - struct obuf data_ob; - obuf_init(&data_ob, data, sizeof(data)); - size_t acm_len; const struct acm_header_v3 *acm = find_in_fit(FIT_ENTRY_TYPE_SACM, &acm_len); if (acm == NULL) { @@ -746,29 +787,39 @@ static void measure_tcg_style(uint64_t biosacm_policy) crtm_version_utf16[i*2 + 1] = crtm_version_str[i*2 + 1]; } - struct vb2_hash hash; - if (vb2_hash_calculate(vboot_hwcrypto_allowed(), crtm_version_utf16, - sizeof(crtm_version_utf16), tpm_log_alg(), &hash)) { + struct tpm_digests crtm_digests; + if (!tpm_make_digests(crtm_version_utf16, sizeof(crtm_version_utf16), NULL, + &crtm_digests)) { printk(BIOS_ERR, "CBnT: failed to hash CRTM version\n"); return; } /* Per BWG this should be logged with EV_S_CRTM_VERSION type. */ - const struct tpm_digest crtm_digests[] = { - { .hash_type = hash.algo, .hash = hash.raw }, - { .hash_type = VB2_HASH_INVALID } - }; - tpm_log_add_table_entry(crtm_version_str, 0, crtm_digests); + tpm_log_add_table_entry(crtm_version_str, 0, crtm_digests.values); - if (copy_ibb_hash(&data_ob, tpm2_alg_from_vb2_hash(tpm_log_alg())) != CB_SUCCESS) { - printk(BIOS_ERR, "CBnT: failed to obtain IBB digest\n"); - return; + int i, j; + + struct tpm_digests post_code_digests; + for (i = 0, j = 0; i < ENABLED_TPM_ALGS_NUM; ++i) { + enum vb2_hash_algorithm alg = enabled_tpm_algs[i]; + if (!tpm_log_alg_active(alg)) + continue; + + struct obuf data_ob; + obuf_init(&data_ob, post_code_digests.hashes[j].sha512, + sizeof(post_code_digests.hashes[j].sha512)); + + if (copy_ibb_hash(&data_ob, alg) != CB_SUCCESS) { + printk(BIOS_ERR, "CBnT: failed to obtain IBB digest\n"); + return; + } + + ++j; } + + post_code_digests.values[j].hash_type = VB2_HASH_INVALID; + /* Per BWG this should be logged with EV_POST_CODE type. */ - const struct tpm_digest post_code_digests[] = { - { .hash_type = tpm_log_alg(), .hash = data }, - { .hash_type = VB2_HASH_INVALID } - }; - tpm_log_add_table_entry("Boot Guard Measured IBB", 0, post_code_digests); + tpm_log_add_table_entry("Boot Guard Measured IBB", 0, post_code_digests.values); /* * struct { @@ -777,6 +828,8 @@ static void measure_tcg_style(uint64_t biosacm_policy) * uint8_t BPM.Signature[BPM signature size]; * } POLICY_DATA; */ + uint8_t data[MAX_DATA_SIZE]; + struct obuf data_ob; obuf_init(&data_ob, data, sizeof(data)); /* ACM_POLICY_STATUS (won't run out of space on this one) */ @@ -789,22 +842,36 @@ static void measure_tcg_style(uint64_t biosacm_policy) printk(BIOS_ERR, "CBnT: failed to copy KM signature\n"); return; } - if (copy_bpm_signature(&data_ob) != CB_SUCCESS) { - printk(BIOS_ERR, "CBnT: failed to copy BPM signature for PCR-0 measurement\n"); - return; - } - if (vb2_hash_calculate(vboot_hwcrypto_allowed(), data, obuf_nr_written(&data_ob), - tpm_log_alg(), &hash)) { - printk(BIOS_ERR, "CBnT: failed to hash POLICY_DATA\n"); - return; + + struct tpm_digests policy_digests; + for (i = 0, j = 0; i < ENABLED_TPM_ALGS_NUM; ++i) { + enum vb2_hash_algorithm alg = enabled_tpm_algs[i]; + if (!tpm_log_alg_active(alg)) + continue; + + struct obuf local_ob = data_ob; + if (copy_bpm_signature(&local_ob, alg) != CB_SUCCESS) { + printk(BIOS_ERR, + "CBnT: failed to copy BPM signature for PCR-0 measurement\n"); + return; + } + + if (vb2_hash_calculate(vboot_hwcrypto_allowed(), data, + obuf_nr_written(&data_ob), alg, + &policy_digests.hashes[i])) { + printk(BIOS_ERR, "CBnT: failed to hash PCR-0 measurement data\n"); + return; + } + + policy_digests.values[j].hash_type = alg; + policy_digests.values[j].hash = policy_digests.hashes[i].raw; + ++j; } + policy_digests.values[j].hash_type = VB2_HASH_INVALID; + /* Per BWG this should be logged with EV_POST_CODE type. */ - const struct tpm_digest policy_digests[] = { - { .hash_type = hash.algo, .hash = hash.raw }, - { .hash_type = VB2_HASH_INVALID } - }; - if (tpm_extend_pcr(0, policy_digests, + if (tpm_extend_pcr(0, policy_digests.values, "BIOS Measured Boot Guard Policy") != TPM_SUCCESS) printk(BIOS_ERR, "CBnT: failed to extend POLICY_DATA\n"); } diff --git a/src/security/tpm/tspi.h b/src/security/tpm/tspi.h index d102f44684d..2041e18e372 100644 --- a/src/security/tpm/tspi.h +++ b/src/security/tpm/tspi.h @@ -67,6 +67,14 @@ static inline enum vb2_hash_algorithm tpm_log_alg(void) return VB2_HASH_INVALID; } +/** + * Checks whether a PCR banks corresponding to a hash algorithm is active. + */ +static inline bool tpm_log_alg_active(enum vb2_hash_algorithm alg) +{ + return alg == tpm_log_alg(); +} + /** * Get the pointer to the single instance of global * TPM log data, and initialize it when necessary @@ -216,4 +224,44 @@ tpm_result_t tpm_setup(int s3flag); tpm_result_t tpm_measure_region(const struct region_device *rdev, uint8_t pcr, const char *rname); +/* + * Arrays of tpm_digest structures should generally have `ENABLED_TPM_ALGS_NUM + 1` entries. + * The extra entry terminates the list. Functions that fill such arrays rely on this + * assumption. + */ +#define ENABLED_TPM_ALGS_NUM ARRAY_SIZE(enabled_tpm_algs) + +/* + * This is a list of supported digests to be used in loops. + */ +static const enum vb2_hash_algorithm enabled_tpm_algs[] __maybe_unused = { +#if CONFIG(TPM_HASH_SHA1) || CONFIG(TPM_LOG_CB) + VB2_HASH_SHA1, +#endif +#if CONFIG(TPM_HASH_SHA256) || CONFIG(TPM_LOG_CB) + VB2_HASH_SHA256, +#endif +#if CONFIG(TPM_HASH_SHA384) + VB2_HASH_SHA384, +#endif +#if CONFIG(TPM_HASH_SHA512) + VB2_HASH_SHA512, +#endif +}; + +_Static_assert(ENABLED_TPM_ALGS_NUM <= HASH_COUNT, + "Marshalling code of TSS 2.0 can't accommodate all enabled hashes."); + +struct tpm_digests { + struct vb2_hash hashes[ENABLED_TPM_ALGS_NUM]; + struct tpm_digest values[ENABLED_TPM_ALGS_NUM + 1]; +}; + +/** + * Compute all supported hashes of a buffer. hash_hint can be passed in to avoid recomputing + * the digest if it's already known. + */ +bool tpm_make_digests(const void *buffer, size_t size, const struct vb2_hash *hash_hint, + struct tpm_digests *digests); + #endif /* TSPI_H_ */ diff --git a/src/security/tpm/tspi/tspi.c b/src/security/tpm/tspi/tspi.c index f4ce0036f1a..f71c0f583ac 100644 --- a/src/security/tpm/tspi/tspi.c +++ b/src/security/tpm/tspi/tspi.c @@ -311,4 +311,34 @@ tpm_result_t tpm_measure_region(const struct region_device *rdev, uint8_t pcr, }; return tpm_extend_pcr(pcr, digests, rname); } + +bool tpm_make_digests(const void *buffer, size_t size, const struct vb2_hash *hash_hint, + struct tpm_digests *digests) +{ + int i, j; + for (i = 0, j = 0; i < ENABLED_TPM_ALGS_NUM; ++i) { + enum vb2_hash_algorithm alg = enabled_tpm_algs[i]; + if (!tpm_log_alg_active(alg)) + continue; + + digests->values[j].hash_type = alg; + + if (hash_hint != NULL && hash_hint->algo == alg) { + digests->values[j++].hash = hash_hint->raw; + continue; + } + + if (vb2_hash_calculate(vboot_hwcrypto_allowed(), buffer, size, + alg, &digests->hashes[i])) { + printk(BIOS_ERR, "%s: failed to compute %s hash.\n", __func__, + vb2_get_hash_algorithm_name(alg)); + return false; + } + + digests->values[j++].hash = digests->hashes[i].raw; + } + + digests->values[j].hash_type = VB2_HASH_INVALID; + return true; +} #endif /* VBOOT_LIB */ From e688fc4946749af0a06597d29e74e2cf3c749753 Mon Sep 17 00:00:00 2001 From: Sergii Dmytruk Date: Mon, 15 Dec 2025 21:34:14 +0200 Subject: [PATCH 7/9] security/vboot/tpm_common.c: support multiple banks Updates the code to support hashing for more than one digest algorithm when that's available, the code should work as before at this point. Change-Id: I243531e699d927896278df2822e80c69db2715dd Upstream-Status: Pending Signed-off-by: Sergii Dmytruk --- src/security/vboot/tpm_common.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/security/vboot/tpm_common.c b/src/security/vboot/tpm_common.c index 1f7bd3c1729..04a7dc31805 100644 --- a/src/security/vboot/tpm_common.c +++ b/src/security/vboot/tpm_common.c @@ -25,7 +25,7 @@ tpm_result_t vboot_setup_tpm(struct vb2_context *ctx) tpm_result_t vboot_extend_pcr(struct vb2_context *ctx, int pcr, enum vb2_pcr_digest which_digest) { - uint8_t buffer[VB2_PCR_DIGEST_RECOMMENDED_SIZE]; + uint8_t buffer[VB2_MAX_DIGEST_SIZE]; uint32_t size = sizeof(buffer); if (vb2api_get_pcr_digest(ctx, which_digest, buffer, &size) != VB2_SUCCESS) @@ -44,13 +44,28 @@ tpm_result_t vboot_extend_pcr(struct vb2_context *ctx, int pcr, */ _Static_assert(sizeof(buffer) >= VB2_SHA256_DIGEST_SIZE, "Buffer needs to be able to fit at least a SHA256"); - enum vb2_hash_algorithm algo = tlcl_get_family() == TPM_1 ? - VB2_HASH_SHA1 : VB2_HASH_SHA256; - struct tpm_digest digests[] = { - { .hash_type = algo, .hash = buffer }, - { .hash_type = VB2_HASH_INVALID } - }; + int i, j; + struct tpm_digest digests[ENABLED_TPM_ALGS_NUM + 1]; + for (i = 0, j = 0; i < ENABLED_TPM_ALGS_NUM; ++i) { + enum vb2_hash_algorithm alg = enabled_tpm_algs[i]; + if (!tpm_log_alg_active(alg)) + continue; + + if (vb2_digest_size(alg) > sizeof(buffer)) { + printk(BIOS_WARNING, + "vboot: Not extending %s digest (buffer in %s is too small).\n", + vb2_get_hash_algorithm_name(alg), __func__); + continue; + } + + /* Extending the same data to all banks. It either gets truncated, fits + perfectly or is padded with zeroes. */ + digests[j].hash = buffer; + digests[j].hash_type = alg; + ++j; + } + digests[j].hash_type = VB2_HASH_INVALID; switch (which_digest) { /* SHA1 of (devmode|recmode|keyblock) bits */ From 2bbe938ecca50a6aac0eda82434397f4bdc7169c Mon Sep 17 00:00:00 2001 From: Sergii Dmytruk Date: Tue, 16 Dec 2025 01:02:48 +0200 Subject: [PATCH 8/9] lib/cbfs.c,security/tpm/tspi: support multiple banks Updates the code to support hashing for more than one digest algorithm when that's available, the code should work as before at this point. Change-Id: I6a89d8d430986bda7ee77053ca3768a292e1b53b Upstream-Status: Pending Signed-off-by: Sergii Dmytruk --- src/lib/cbfs.c | 21 ++++-------- src/security/tpm/tspi/crtm.c | 16 +++++---- src/security/tpm/tspi/crtm.h | 6 ++-- src/security/tpm/tspi/tspi.c | 66 ++++++++++++++++++++---------------- 4 files changed, 55 insertions(+), 54 deletions(-) diff --git a/src/lib/cbfs.c b/src/lib/cbfs.c index df34594a53c..f63770e78f5 100644 --- a/src/lib/cbfs.c +++ b/src/lib/cbfs.c @@ -186,21 +186,12 @@ static bool cbfs_file_hash_mismatch(const void *buffer, size_t size, } if (CONFIG(TPM_MEASURED_BOOT) && !ENV_SMM) { - struct vb2_hash calculated_hash; - - /* No need to re-hash file if we already have it from verification. */ - if (!hash || hash->algo != tpm_log_alg()) { - if (vb2_hash_calculate(vboot_hwcrypto_allowed(), buffer, size, - tpm_log_alg(), &calculated_hash)) - hash = NULL; - else - hash = &calculated_hash; - } - - if (!hash || - tspi_cbfs_measurement(mdata->h.filename, be32toh(mdata->h.type), hash)) - ERROR("failed to measure '%s' into TPM log\n", mdata->h.filename); - /* We intentionally continue to boot on measurement errors. */ + tpm_result_t rc = tspi_cbfs_measurement(mdata->h.filename, buffer, size, + be32toh(mdata->h.type), hash); + if (rc != TPM_SUCCESS) + ERROR("failed to measure '%s' into TPM log, error %#x\n", + mdata->h.filename, rc); + /* We intentionally continue to boot on measurement errors. */ } return false; diff --git a/src/security/tpm/tspi/crtm.c b/src/security/tpm/tspi/crtm.c index a904b3dcdbd..d4bdca0424f 100644 --- a/src/security/tpm/tspi/crtm.c +++ b/src/security/tpm/tspi/crtm.c @@ -105,7 +105,8 @@ static bool is_runtime_data(const char *name) return !strcmp(allowlist, name); } -tpm_result_t tspi_cbfs_measurement(const char *name, uint32_t type, const struct vb2_hash *hash) +tpm_result_t tspi_cbfs_measurement(const char *name, const void *buffer, size_t size, + uint32_t type, const struct vb2_hash *hash_hint) { uint32_t pcr_index; char tpm_log_metadata[TPM_CB_LOG_PCR_HASH_NAME]; @@ -132,14 +133,15 @@ tpm_result_t tspi_cbfs_measurement(const char *name, uint32_t type, const struct break; } - struct tpm_digest digests[] = { - { .hash_type = hash->algo, .hash = hash->raw }, - { .hash_type = VB2_HASH_INVALID } - }; + struct tpm_digests digests; + if (!tpm_make_digests(buffer, size, hash_hint, &digests)) { + printk(BIOS_ERR, "%s: failed to fill digests!\n", __func__); + return TPM_FAIL; + } snprintf(tpm_log_metadata, TPM_CB_LOG_PCR_HASH_NAME, "CBFS: %s", name); - return tpm_extend_pcr(pcr_index, digests, tpm_log_metadata); + return tpm_extend_pcr(pcr_index, digests.values, tpm_log_metadata); } void *tpm_log_init(void) @@ -167,7 +169,7 @@ tpm_result_t tspi_measure_cache_to_pcr(void) int i; int pcr; const char *event_name; - struct tpm_digest digests[2]; + struct tpm_digest digests[ENABLED_TPM_ALGS_NUM + 1]; /* This means the table is empty. */ if (!tspi_tpm_log_available()) diff --git a/src/security/tpm/tspi/crtm.h b/src/security/tpm/tspi/crtm.h index cd20e81b646..04ee46a75fd 100644 --- a/src/security/tpm/tspi/crtm.h +++ b/src/security/tpm/tspi/crtm.h @@ -34,9 +34,11 @@ tpm_result_t tspi_init_crtm(void); tpm_result_t tspi_measure_cache_to_pcr(void); /** - * Extend a measurement hash taken for a CBFS file into the appropriate PCR. + * Extend a measurement hash of a CBFS file into the appropriate PCR. hash_hint can be passed + * in to avoid recomputing the digest if it's already known. */ -tpm_result_t tspi_cbfs_measurement(const char *name, uint32_t type, const struct vb2_hash *hash); +tpm_result_t tspi_cbfs_measurement(const char *name, const void *buffer, size_t size, + uint32_t type, const struct vb2_hash *hash_hint); /* * Provide a function on SoC level to measure the bootblock for cases where bootblock is diff --git a/src/security/tpm/tspi/tspi.c b/src/security/tpm/tspi/tspi.c index f71c0f583ac..7fb513be208 100644 --- a/src/security/tpm/tspi/tspi.c +++ b/src/security/tpm/tspi/tspi.c @@ -267,49 +267,55 @@ tpm_result_t tpm_extend_pcr(int pcr, const struct tpm_digest *digests, const cha tpm_result_t tpm_measure_region(const struct region_device *rdev, uint8_t pcr, const char *rname) { - uint8_t digest[TPM_PCR_MAX_LEN], digest_len; uint8_t buf[HASH_DATA_CHUNK_SIZE]; uint32_t offset; + int i, j; size_t len; struct vb2_digest_context ctx; + struct tpm_digests digests; if (!rdev || !rname) return TPM_CB_INVALID_ARG; - digest_len = vb2_digest_size(tpm_log_alg()); - assert(digest_len <= sizeof(digest)); - if (vb2_digest_init(&ctx, vboot_hwcrypto_allowed(), tpm_log_alg(), - region_device_sz(rdev))) { - printk(BIOS_ERR, "TPM: Error initializing hash.\n"); - return TPM_CB_HASH_ERROR; - } - /* - * Though one can mmap the full needed region on x86 this is not the - * case for e.g. ARM. In order to make this code as universal as - * possible across different platforms read the data to hash in chunks. - */ - for (offset = 0; offset < region_device_sz(rdev); offset += len) { - len = MIN(sizeof(buf), region_device_sz(rdev) - offset); - if (rdev_readat(rdev, buf, offset, len) < 0) { - printk(BIOS_ERR, "TPM: Not able to read region %s.\n", - rname); - return TPM_CB_READ_FAILURE; + for (i = 0, j = 0; i < ENABLED_TPM_ALGS_NUM; ++i) { + enum vb2_hash_algorithm alg = enabled_tpm_algs[i]; + if (!tpm_log_alg_active(alg)) + continue; + + if (vb2_digest_init(&ctx, vboot_hwcrypto_allowed(), alg, + region_device_sz(rdev))) { + printk(BIOS_ERR, "TPM: Error initializing hash.\n"); + return TPM_CB_HASH_ERROR; } - if (vb2_digest_extend(&ctx, buf, len)) { - printk(BIOS_ERR, "TPM: Error extending hash.\n"); + /* + * Though one can mmap the full needed region on x86 this is not the + * case for e.g. ARM. In order to make this code as universal as + * possible across different platforms read the data to hash in chunks. + */ + for (offset = 0; offset < region_device_sz(rdev); offset += len) { + len = MIN(sizeof(buf), region_device_sz(rdev) - offset); + if (rdev_readat(rdev, buf, offset, len) < 0) { + printk(BIOS_ERR, "TPM: Not able to read region %s.\n", + rname); + return TPM_CB_READ_FAILURE; + } + if (vb2_digest_extend(&ctx, buf, len)) { + printk(BIOS_ERR, "TPM: Error extending hash.\n"); + return TPM_CB_HASH_ERROR; + } + } + if (vb2_digest_finalize(&ctx, digests.hashes[j].raw, vb2_digest_size(alg))) { + printk(BIOS_ERR, "TPM: Error finalizing hash.\n"); return TPM_CB_HASH_ERROR; } - } - if (vb2_digest_finalize(&ctx, digest, digest_len)) { - printk(BIOS_ERR, "TPM: Error finalizing hash.\n"); - return TPM_CB_HASH_ERROR; + + digests.values[j].hash = digests.hashes[j].raw; + digests.values[j].hash_type = alg; + ++j; } - struct tpm_digest digests[] = { - { .hash_type = tpm_log_alg(), .hash = digest }, - { .hash_type = VB2_HASH_INVALID } - }; - return tpm_extend_pcr(pcr, digests, rname); + digests.values[j].hash_type = VB2_HASH_INVALID; + return tpm_extend_pcr(pcr, digests.values, rname); } bool tpm_make_digests(const void *buffer, size_t size, const struct vb2_hash *hash_hint, From 69aee15ab38c6f4dec05d62b97e059bd0441cef0 Mon Sep 17 00:00:00 2001 From: Sergii Dmytruk Date: Tue, 16 Dec 2025 01:05:58 +0200 Subject: [PATCH 9/9] security/tpm: detect and use all active PCR banks All of the client has already been updated to permit use of multiple banks, but at most one was ever enabled. TPM 2 log was also updated to permit handling of multiple digests, but similarly only one was in use. From now on, it's possible to configure more than one digest (only SHA1 and SHA256 are selected by default). This changes previous TSPI API of `tpm_log_alg()` (single hash) to `tpm_log_alg_active(enum vb2_hash_algorithm)` coupled with `enabled_tpm_algs` array (multiple hashes). The bulk of the code here is for dealing with the set of banks of TPM: - querying it from the device to know what digests should be used - synchronizing set of digests in the log with the actual set of active banks The latter is needed in case TPM is initialized in ramstage while measurements are accumulated starting from the bootblock. An alternative was to require initializing TPM in the bootblock, but bootblock may not have enough space for the extra code required for TPM, hence a different approach was taken: take all supported hashes before TPM is initialized, trim unnecessary digests after the initialization. Change-Id: Ia326b22869c4983fc4e02e150461e7a9ff94dc4e Upstream-Status: Pending Signed-off-by: Sergii Dmytruk --- configs/config.emulation_qemu_x86_q35_uefi | 1 - ...nfig.emulation_qemu_x86_q35_uefi_all_menus | 1 - src/security/tpm/Kconfig | 18 +- src/security/tpm/tspi.h | 39 +-- src/security/tpm/tspi/crtm.c | 7 + src/security/tpm/tspi/log-tpm2.c | 274 +++++++++++++++++- src/security/tpm/tspi/logs.h | 2 + 7 files changed, 297 insertions(+), 45 deletions(-) diff --git a/configs/config.emulation_qemu_x86_q35_uefi b/configs/config.emulation_qemu_x86_q35_uefi index 0be46de9698..7c3693ea0f1 100644 --- a/configs/config.emulation_qemu_x86_q35_uefi +++ b/configs/config.emulation_qemu_x86_q35_uefi @@ -14,7 +14,6 @@ CONFIG_DRIVERS_EFI_MAIN_FW_LSV=0x00020101 CONFIG_DRIVERS_EFI_UPDATE_CAPSULES=y CONFIG_TPM1=y CONFIG_TPM2=y -CONFIG_TPM_HASH_SHA256=y CONFIG_DEFAULT_CONSOLE_LOGLEVEL_0=y # CONFIG_CONSOLE_USE_LOGLEVEL_PREFIX is not set # CONFIG_CONSOLE_USE_ANSI_ESCAPES is not set diff --git a/configs/config.emulation_qemu_x86_q35_uefi_all_menus b/configs/config.emulation_qemu_x86_q35_uefi_all_menus index a720d7970c4..76261c43ef5 100644 --- a/configs/config.emulation_qemu_x86_q35_uefi_all_menus +++ b/configs/config.emulation_qemu_x86_q35_uefi_all_menus @@ -9,7 +9,6 @@ CONFIG_UDK_202005_BINDING=y CONFIG_DRIVERS_EFI_VARIABLE_STORE=y CONFIG_TPM1=y CONFIG_TPM2=y -CONFIG_TPM_HASH_SHA256=y CONFIG_DEFAULT_CONSOLE_LOGLEVEL_0=y # CONFIG_CONSOLE_USE_LOGLEVEL_PREFIX is not set # CONFIG_CONSOLE_USE_ANSI_ESCAPES is not set diff --git a/src/security/tpm/Kconfig b/src/security/tpm/Kconfig index 3ffbb8a8337..3e2a736e287 100644 --- a/src/security/tpm/Kconfig +++ b/src/security/tpm/Kconfig @@ -108,22 +108,20 @@ config TPM_LOG_TPM2 endchoice -choice - prompt "TPM2 hashing algorithm" - depends on TPM_MEASURED_BOOT && (TPM_LOG_TCG || TPM_LOG_TPM2) - default TPM_HASH_SHA1 if TPM1 - default TPM_HASH_SHA256 if TPM2 +if TPM_MEASURED_BOOT && (TPM_LOG_TCG || TPM_LOG_TPM2) config TPM_HASH_SHA1 - bool "SHA1" + bool "SHA1 PCR hashing" + default y if TPM1 || TPM2 config TPM_HASH_SHA256 - bool "SHA256" + bool "SHA256 PCR hashing" + default y if TPM2 config TPM_HASH_SHA384 - bool "SHA384" + bool "SHA384 PCR hashing" config TPM_HASH_SHA512 - bool "SHA512" + bool "SHA512 PCR hashing" -endchoice +endif config TPM_MEASURED_BOOT_INIT_BOOTBLOCK bool diff --git a/src/security/tpm/tspi.h b/src/security/tpm/tspi.h index 2041e18e372..4ec2d2859a9 100644 --- a/src/security/tpm/tspi.h +++ b/src/security/tpm/tspi.h @@ -43,36 +43,20 @@ static inline bool tpm_log_use_tpm2_format(void) } /** - * Retrieves hash algorithm used by TPM event log or VB2_HASH_INVALID. + * Checks whether a PCR banks corresponding to a hash algorithm is active. */ -static inline enum vb2_hash_algorithm tpm_log_alg(void) +static inline bool tpm_log_alg_active(enum vb2_hash_algorithm alg) { if (CONFIG(TPM_LOG_CB)) - return (tlcl_get_family() == TPM_1 ? VB2_HASH_SHA1 : VB2_HASH_SHA256); + return alg == (tlcl_get_family() == TPM_1 ? VB2_HASH_SHA1 : VB2_HASH_SHA256); if (tpm_log_use_tpm1_format()) - return VB2_HASH_SHA1; - - if (tpm_log_use_tpm2_format()) { - if (CONFIG(TPM_HASH_SHA1)) - return VB2_HASH_SHA1; - if (CONFIG(TPM_HASH_SHA256)) - return VB2_HASH_SHA256; - if (CONFIG(TPM_HASH_SHA384)) - return VB2_HASH_SHA384; - if (CONFIG(TPM_HASH_SHA512)) - return VB2_HASH_SHA512; - } + return alg == VB2_HASH_SHA1; - return VB2_HASH_INVALID; -} + if (tpm_log_use_tpm2_format()) + return tpm2_log_alg_active(alg); -/** - * Checks whether a PCR banks corresponding to a hash algorithm is active. - */ -static inline bool tpm_log_alg_active(enum vb2_hash_algorithm alg) -{ - return alg == tpm_log_alg(); + return false; } /** @@ -179,6 +163,15 @@ static inline void tpm_log_startup_locality(int locality) tpm2_log_startup_locality(locality); } +/** + * Align TPM log with the TPM if necessary. + */ +static inline void tpm_log_align_with_tpm(void) +{ + if (tpm_log_use_tpm2_format()) + tpm2_log_align_with_tpm(); +} + /** * Dump TPM log entries on console */ diff --git a/src/security/tpm/tspi/crtm.c b/src/security/tpm/tspi/crtm.c index d4bdca0424f..0d77369e318 100644 --- a/src/security/tpm/tspi/crtm.c +++ b/src/security/tpm/tspi/crtm.c @@ -180,6 +180,13 @@ tpm_result_t tspi_measure_cache_to_pcr(void) return TPM_CB_FAIL; } + /* + * At this point TPM has been initialized, but none of coreboot's measurements have been + * submitted to it yet. Before extending cached digests, invoke a log-specific function + * to do modifications based on the information queried from a TPM. + */ + tpm_log_align_with_tpm(); + printk(BIOS_DEBUG, "TPM: Write digests cached in TPM log to PCR\n"); i = 0; while (!tpm_log_get(i++, &pcr, digests, &event_name)) { diff --git a/src/security/tpm/tspi/log-tpm2.c b/src/security/tpm/tspi/log-tpm2.c index 38160c5cfc6..a9352175565 100644 --- a/src/security/tpm/tspi/log-tpm2.c +++ b/src/security/tpm/tspi/log-tpm2.c @@ -29,7 +29,7 @@ struct log_event { uint32_t pcr; uint32_t event_type; uint32_t digest_count; - struct tpm_digest digests[1]; + struct tpm_digest digests[ENABLED_TPM_ALGS_NUM]; uint32_t name_len; const char *name; }; @@ -39,6 +39,45 @@ struct startup_locality_event { uint8_t startup_locality; /* 0 or 3 */ } __packed; +struct pcr_banks_info { + int active_count; /* Number is_active entries set to true. */ + bool is_active[ENABLED_TPM_ALGS_NUM]; /* Set of active banks. */ +}; + +static int find_alg_index(enum vb2_hash_algorithm alg) +{ + unsigned int i; + for (i = 0; i < ENABLED_TPM_ALGS_NUM; i++) { + if (enabled_tpm_algs[i] == alg) + return i; + } + + return -1; +} + +static bool is_all_zeroes(const void *buffer, size_t size) +{ + const uint8_t *p = buffer; + while (size-- != 0) { + if (*p++ != 0) + return false; + } + return true; +} + +static bool supports_digest(const struct tpm_2_log_table *tclt, uint16_t alg_id) +{ + const struct tcg_efi_spec_id_event *hdr = &tclt->header; + + int i; + for (i = 0; i < le32toh(hdr->num_of_algorithms); ++i) { + if (hdr->digest_sizes[i].alg_id == htole16(alg_id)) + return true; + } + + return false; +} + /* Assumes tclt->header.num_of_algorithms is already set to its final value. */ static struct tpm_2_log_bottom *get_log_bottom(const struct tpm_2_log_table *tclt) { @@ -52,6 +91,78 @@ static struct tpm_2_log_bottom *get_log_bottom(const struct tpm_2_log_table *tcl return (struct tpm_2_log_bottom *)p; } +/* See comment on tpm2_log_align_with_tpm(). */ +static const struct pcr_banks_info *get_pcr_banks_info(void) +{ + static bool initialized; + static struct pcr_banks_info info; + + if (initialized) + return &info; + + /* Start by pretending that all supported banks are active in case there will be an + error. */ + unsigned int i; + for (i = 0; i < ENABLED_TPM_ALGS_NUM; ++i) + info.is_active[i] = true; + info.active_count = ENABLED_TPM_ALGS_NUM; + + if (CONFIG(TPM_INIT_RAMSTAGE) && !ENV_RAMSTAGE && !ENV_POSTCAR && !ENV_PAYLOAD_LOADER) { + /* TPM initialization is postponed until ramstage, no use poking it. */ + initialized = true; + return &info; + } + + tpm_result_t rc = tlcl_lib_init(); + if (rc != TPM_SUCCESS) { + printk(BIOS_WARNING, "TPM LOG: failed to initialize TLCL: %d\n", rc); + return &info; + } + + TPML_PCR_SELECTION pcrs; + rc = tlcl2_get_capability_pcrs(&pcrs); + if (rc != TPM_SUCCESS) { + printk(BIOS_WARNING, "TPM LOG: failed to query PCR capabilities: %d\n", rc); + return &info; + } + + if (pcrs.count == 0) { + /* TPM likely just hasn't been set up yet. */ + return &info; + } + + info.active_count = 0; + memset(info.is_active, 0, sizeof(info.is_active)); + + for (i = 0; i < pcrs.count; i++) { + TPMS_PCR_SELECTION *selection = &pcrs.pcrSelections[i]; + + enum vb2_hash_algorithm alg = tpm2_alg_to_vb2_hash(selection->hash); + if (alg == VB2_HASH_INVALID) { + printk(BIOS_INFO, "TPM LOG: unsupported PCR bank: %#x\n", + selection->hash); + continue; + } + + int alg_index = find_alg_index(alg); + if (alg_index < 0) { + printk(BIOS_INFO, + "TPM LOG: skipping PCR bank disabled at build-time: %s\n", + vb2_get_hash_algorithm_name(alg)); + continue; + } + + bool is_active = !is_all_zeroes(selection->pcrSelect, selection->sizeofSelect); + if (is_active) { + info.is_active[alg_index] = true; + ++info.active_count; + } + } + + initialized = true; + return &info; +} + static uint16_t get_log_footprint(const struct tpm_2_log_table *tclt) { return sizeof(*tclt) + @@ -67,7 +178,9 @@ void *tpm2_log_cbmem_init(void) return tclt; if (ENV_HAS_CBMEM) { + int i, j; struct tcg_efi_spec_id_event *hdr; + const struct pcr_banks_info *pcr_banks_info; struct tpm_2_log_bottom *bottom; tclt = cbmem_find(CBMEM_ID_TPM2_TCG_LOG); @@ -78,12 +191,14 @@ void *tpm2_log_cbmem_init(void) if (!tclt) return NULL; + pcr_banks_info = get_pcr_banks_info(); + memset(tclt, 0, TPM_LOG_SIZE); hdr = &tclt->header; hdr->event_type = htole32(EV_NO_ACTION); hdr->event_size = htole32(28 + - 1 * sizeof(hdr->digest_sizes[0]) + + pcr_banks_info->active_count * sizeof(hdr->digest_sizes[0]) + 1 + TPM_20_VENDOR_INFO_SIZE); strcpy((char *)hdr->signature, TPM_20_SPEC_ID_EVENT_SIGNATURE); @@ -93,9 +208,17 @@ void *tpm2_log_cbmem_init(void) hdr->spec_errata = 0x00; hdr->uintn_size = 0x02; // 64-bit UINT - hdr->num_of_algorithms = htole32(1); - hdr->digest_sizes[0].alg_id = htole16(tpm2_alg_from_vb2_hash(tpm_log_alg())); - hdr->digest_sizes[0].digest_size = htole16(vb2_digest_size(tpm_log_alg())); + hdr->num_of_algorithms = htole32(pcr_banks_info->active_count); + for (i = 0, j = -1; i < le32toh(hdr->num_of_algorithms); ++i) { + /* Find the next active bank. */ + while (!pcr_banks_info->is_active[++j]) + continue; + + hdr->digest_sizes[i].alg_id = + htole16(tpm2_alg_from_vb2_hash(enabled_tpm_algs[j])); + hdr->digest_sizes[i].digest_size = + htole16(vb2_digest_size(enabled_tpm_algs[j])); + } bottom = get_log_bottom(tclt); bottom->vendor_info_size = TPM_20_VENDOR_INFO_SIZE; @@ -184,6 +307,15 @@ void tpm2_log_dump(void) printk(BIOS_INFO, "\n"); } +bool tpm2_log_alg_active(enum vb2_hash_algorithm alg) +{ + int alg_index = find_alg_index(alg); + if (alg_index < 0) + return false; + + return get_pcr_banks_info()->is_active[alg_index]; +} + /* The function assumes output buffer has enough space for the new event. */ static void write_log_event(struct obuf *ob, const void *data, @@ -266,12 +398,22 @@ void tpm2_log_startup_locality(int locality) return; } + const struct pcr_banks_info *pcr_banks_info = get_pcr_banks_info(); + /* EV_NO_ACTION events use zeroes for digest(s). */ struct vb2_hash zero_hash = {0}; - const struct tpm_digest digests[] = { - { .hash_type = tpm_log_alg(), .hash = zero_hash.raw }, - { .hash_type = VB2_HASH_INVALID } - }; + struct tpm_digest digests[ENABLED_TPM_ALGS_NUM + 1]; + + int i, j; + for (i = 0, j = 0; i < ARRAY_SIZE(pcr_banks_info->is_active); ++i) { + if (!pcr_banks_info->is_active[i]) + continue; + + digests[j].hash_type = enabled_tpm_algs[i]; + digests[j].hash = zero_hash.raw; + ++j; + } + digests[j].hash_type = VB2_HASH_INVALID; struct startup_locality_event event_data; strcpy(event_data.signature, "StartupLocality"); @@ -332,7 +474,7 @@ void tpm2_preram_log_clear(void) * is not fully initialized. */ struct tpm_2_log_table *tclt = (struct tpm_2_log_table *)_tpm_log; - tclt->header.num_of_algorithms = htole32(1); + tclt->header.num_of_algorithms = htole32(get_pcr_banks_info()->active_count); struct tpm_2_log_bottom *bottom = get_log_bottom(tclt); bottom->num_entries = 0; @@ -355,3 +497,115 @@ void tpm2_log_copy_entries(const void *from, void *to) to_bottom->num_entries = from_bottom->num_entries; to_bottom->next_offset = from_bottom->next_offset; } + +/* + * Events can be logged before TPM is initialized but the set of active PCR banks can be queried + * only after its initialization. For this reason get_pcr_banks_info() reports all supported + * banks as active if TPM is unavailable, collecting all possible digests in the log itself. + * Once TPM is initialized, tspi_measure_cache_to_pcr() is called which invokes this function. + * The purpose of the function is to trim down the log by removing unnecessary digests. + * Normally, this function gets called in the ramstage, but if TPM gets initialized in the + * bootblock, no trimming (and no unnecessary hashing) will occur. + */ +void tpm2_log_align_with_tpm(void) +{ + /* There is no point doing anything if exactly one digest algorithm is enabled. */ + if (ENABLED_TPM_ALGS_NUM == 1) + return; + + struct tpm_2_log_table *tclt = tpm_log_init(); + if (tclt == NULL) { + printk(BIOS_WARNING, "TPM LOG: can't adjust a non-existent log!\n"); + return; + } + + struct tcg_efi_spec_id_event *hdr = &tclt->header; + const int old_alg_count = le32toh(hdr->num_of_algorithms); + + /* Filter-out unsupported digest algorithms in place. */ + int i; + int new_alg_count = 0; + for (i = 0; i < old_alg_count; ++i) { + uint16_t alg_id = le16toh(hdr->digest_sizes[i].alg_id); + if (tpm2_log_alg_active(tpm2_alg_to_vb2_hash(alg_id))) + hdr->digest_sizes[new_alg_count++] = hdr->digest_sizes[i]; + } + + if (new_alg_count == old_alg_count) { + printk(BIOS_INFO, "TPM LOG: none of %d digests were filtered\n", old_alg_count); + return; + } + + if (new_alg_count == 0) { + printk(BIOS_WARNING, "TPM LOG: can't filter-out all digests, leaving as is\n"); + return; + } + + printk(BIOS_INFO, "TPM LOG: filtering-out digests of inactive banks: %d -> %d\n", + old_alg_count, new_alg_count); + + /* + * The following code excludes inactive hashes in place. This allows using it in a + * bootblock which lacks dynamic memory allocation and avoid reserving memory for a + * temporary copy. Updating in place is safe to do and success is guaranteed because + * the data is merely shifted. The only possible concern is that obuf_write() uses + * memcpy() which per standard doesn't deal with overlapping ranges but that should be + * fine in this environment which has its own memcpy(). + * + * How the update is performed: + * 1. Remember current range of event data. + * 2. Unsupported digest algorithms inside log's header are already gone, merely need + * to update their count. + * 3. Increase size of log's header (it got shorter). + * 4. Rediscover bottom part of the log (where vendor data is) whose location depends + * on the number of digest algorithms. + * 5. Reinsert all log entries while skipping inactive banks. + * 6. Fill bottom part with correct values and update offsets there. + * 7. Clear the remainder of the log. + */ + + /* Save original bottom part on the stack. */ + struct tpm_2_log_bottom *bottom = get_log_bottom(tclt); + const struct tpm_2_log_bottom old_bottom = *bottom; + + /* Set up input buffer before bottom gets moved (updates to the header above don't + hurt anything). */ + struct ibuf ib; + ibuf_init(&ib, bottom->events, le16toh(bottom->next_offset)); + + size_t size_diff = + (old_alg_count - new_alg_count) * sizeof(tclt->header.digest_sizes[0]); + + hdr->num_of_algorithms = htole32(new_alg_count); + hdr->event_size = htole32(le32toh(hdr->event_size) - size_diff); + + /* Updating `hdr->num_of_algorithms` above shifted location of the bottom part. */ + bottom = get_log_bottom(tclt); + + /* Set up output buffer at the new location. */ + struct obuf ob; + obuf_init(&ob, bottom->events, le16toh(old_bottom.max_offset) + size_diff); + + for (i = 0; i < le16toh(old_bottom.num_entries); ++i) { + struct log_event ev; + read_log_event(&ib, &ev); + + int j, k = 0; + struct tpm_digest digests[ENABLED_TPM_ALGS_NUM + 1]; + for (j = 0; j < ev.digest_count; ++j) { + uint16_t alg = tpm2_alg_from_vb2_hash(ev.digests[j].hash_type); + if (supports_digest(tclt, alg)) + digests[k++] = ev.digests[j]; + } + digests[k].hash_type = VB2_HASH_INVALID; + + write_log_event(&ob, ev.name, ev.name_len, ev.pcr, ev.event_type, digests); + } + + *bottom = old_bottom; + bottom->max_offset = htole16(le16toh(old_bottom.max_offset) + size_diff); + bottom->next_offset = htole16(obuf_nr_written(&ob)); + + memset(&bottom->events[obuf_nr_written(&ob)], 0, + le16toh(bottom->max_offset) - obuf_nr_written(&ob)); +} diff --git a/src/security/tpm/tspi/logs.h b/src/security/tpm/tspi/logs.h index 52632ee7ef4..f8b537576f6 100644 --- a/src/security/tpm/tspi/logs.h +++ b/src/security/tpm/tspi/logs.h @@ -44,7 +44,9 @@ int tpm2_log_get(int entry_idx, int *pcr, struct tpm_digest *digests, const char void tpm2_log_add_table_entry(const char *name, const uint32_t pcr, const struct tpm_digest *digests); void tpm2_log_startup_locality(int locality); +void tpm2_log_align_with_tpm(void); void tpm2_log_dump(void); +bool tpm2_log_alg_active(enum vb2_hash_algorithm alg); static inline uint16_t tpm2_alg_from_vb2_hash(enum vb2_hash_algorithm hash_type) {