| /* SPDX-License-Identifier: GPL-2.0-only */ |
| |
| #include <mboot.h> |
| #include <assert.h> |
| #include <build.h> |
| #include <vb2_api.h> |
| #include <board_mboot.h> |
| |
| /* |
| * Get the list of currently active PCR banks in TPM. |
| * |
| * @retval A map of active PCR banks. |
| */ |
| EFI_TCG2_EVENT_ALGORITHM_BITMAP tpm2_get_active_pcrs(void) |
| { |
| int rc; |
| TPML_PCR_SELECTION Pcrs; |
| EFI_TCG2_EVENT_ALGORITHM_BITMAP tpmHashAlgorithmBitmap = 0; |
| uint32_t activePcrBanks = 0; |
| uint32_t index; |
| |
| rc = tpm2_get_capability_pcrs(&Pcrs); |
| if (rc != TPM_SUCCESS) { |
| tpmHashAlgorithmBitmap = EFI_TCG2_BOOT_HASH_ALG_SHA1; |
| activePcrBanks = EFI_TCG2_BOOT_HASH_ALG_SHA1; |
| } else { |
| for (index = 0; index < Pcrs.count; index++) { |
| switch (Pcrs.pcrSelections[index].hash) { |
| case TPM_ALG_SHA1: |
| tpmHashAlgorithmBitmap |= |
| EFI_TCG2_BOOT_HASH_ALG_SHA1; |
| if (!is_zero_buffer(Pcrs.pcrSelections[index].pcrSelect, |
| Pcrs.pcrSelections[index].sizeofSelect)) |
| activePcrBanks |= |
| EFI_TCG2_BOOT_HASH_ALG_SHA1; |
| break; |
| case TPM_ALG_SHA256: |
| tpmHashAlgorithmBitmap |= |
| EFI_TCG2_BOOT_HASH_ALG_SHA256; |
| if (!is_zero_buffer(Pcrs.pcrSelections[index].pcrSelect, |
| Pcrs.pcrSelections[index].sizeofSelect)) |
| activePcrBanks |= |
| EFI_TCG2_BOOT_HASH_ALG_SHA256; |
| break; |
| case TPM_ALG_SHA384: |
| case TPM_ALG_SHA512: |
| case TPM_ALG_SM3_256: |
| default: |
| printk(BIOS_DEBUG, "%s: unsupported algorithm " |
| "reported - 0x%x\n", __func__, |
| Pcrs.pcrSelections[index].hash); |
| break; |
| } |
| } |
| } |
| printk(BIOS_DEBUG, "Tcg2 Capability values from TPM\n"); |
| printk(BIOS_DEBUG, "tpmHashAlgorithmBitmap - 0x%08x\n", |
| tpmHashAlgorithmBitmap); |
| printk(BIOS_DEBUG, "activePcrBanks - 0x%08x\n", |
| activePcrBanks); |
| |
| return activePcrBanks; |
| } |
| |
| /* |
| * tpm2_get_capability_pcrs |
| * |
| * Return the TPM PCR information. |
| * |
| * This function parses the data got from tlcl_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. |
| */ |
| int tpm2_get_capability_pcrs(TPML_PCR_SELECTION *Pcrs) |
| { |
| TPMS_CAPABILITY_DATA TpmCap; |
| int rc; |
| int index; |
| |
| rc = tlcl_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 = 0x%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 |
| * |
| * Calculates the hash over the data and extends it in active PCR banks and |
| * then logs them in the event log. |
| * |
| * @param[in] flags flags associated with hash data. |
| * @param[in] hashData data to be hashed. |
| * @param[in] hashDataLen length of the data to be hashed. |
| * @param[in] newEventHdr event header in TCG_PCR_EVENT2 format. |
| * @param[in] eventLog description of the event. |
| * |
| * @retval TPM_SUCCESS Operation completed successfully. |
| * @retval TPM_IOERROR Unexpected device behavior. |
| */ |
| int mboot_hash_extend_log(uint64_t flags, uint8_t *hashData, uint32_t hashDataLen, |
| TCG_PCR_EVENT2_HDR *newEventHdr, uint8_t *eventLog) |
| { |
| TPMT_HA *digest = NULL; |
| |
| printk(BIOS_DEBUG, "%s: Hash Data Length: %zu bytes\n", __func__, (size_t)hashDataLen); |
| |
| /* Generate SHA256 */ |
| digest = &(newEventHdr->digest.digests[0]); |
| if (flags & MBOOT_HASH_PROVIDED) { |
| /* The hash is provided as data */ |
| memcpy(digest->digest.sha256, (void *)hashData, hashDataLen); |
| } else { |
| struct vb2_hash tmp; |
| if (vb2_hash_calculate(false, hashData, hashDataLen, VB2_HASH_SHA256, &tmp)) |
| return TPM_IOERROR; |
| memcpy(digest->digest.sha256, tmp.sha256, sizeof(tmp.sha256)); |
| } |
| |
| printk(BIOS_DEBUG, "%s: SHA256 Hash Digest:\n", __func__); |
| mboot_print_buffer(digest->digest.sha256, VB2_SHA256_DIGEST_SIZE); |
| |
| return (tlcl_extend(newEventHdr->pcrIndex, (uint8_t *)&(newEventHdr->digest), |
| VB2_HASH_SHA256)); |
| } |
| |
| /* |
| * invalidate_pcrs |
| * |
| * Invalidate PCRs 0-7 with extending 1 after tpm failure. |
| */ |
| void invalidate_pcrs(void) |
| { |
| int pcr; |
| int rc; |
| |
| TCG_PCR_EVENT2_HDR tcgEventHdr; |
| uint8_t invalidate = 1; |
| |
| for (pcr = 0; pcr < 8; pcr++) { |
| printk(BIOS_DEBUG, "%s: Invalidating PCR %d\n", __func__, pcr); |
| memset(&tcgEventHdr, 0, sizeof(tcgEventHdr)); |
| tcgEventHdr.pcrIndex = pcr; |
| tcgEventHdr.eventType = EV_NO_ACTION; |
| tcgEventHdr.eventSize = (uint32_t) sizeof(invalidate); |
| |
| rc = mboot_hash_extend_log(0, (uint8_t *)&invalidate, |
| tcgEventHdr.eventSize, &tcgEventHdr, |
| (uint8_t *)"Invalidate PCR"); |
| if (rc != TPM_SUCCESS) |
| printk(BIOS_DEBUG, "%s: invalidating pcr %d returned" |
| " 0x%x\n", __func__, pcr, rc); |
| } |
| } |
| |
| /* |
| * is_zero_buffer |
| * |
| * Check if buffer is all zero. |
| * |
| * @param[in] buffer Buffer to be checked. |
| * @param[in] size Size of buffer to be checked. |
| * |
| * @retval TRUE buffer is all zero. |
| * @retval FALSE buffer is not all zero. |
| */ |
| int is_zero_buffer(void *buffer, unsigned int size) |
| { |
| uint8_t *ptr; |
| |
| ptr = buffer; |
| while (size--) { |
| if (*(ptr++) != 0) |
| return false; |
| } |
| return true; |
| } |
| |
| /* |
| * Prints command or response buffer for debugging purposes. |
| * |
| * @param[in] Buffer Buffer to print. |
| * @param[in] BufferSize Buffer data length. |
| * |
| * @retval None |
| */ |
| void mboot_print_buffer(uint8_t *buffer, uint32_t bufferSize) |
| { |
| uint32_t index; |
| |
| printk(BIOS_DEBUG, "Buffer Address: 0x%08x, Size: 0x%08x, Value:\n", |
| (unsigned int)*buffer, bufferSize); |
| for (index = 0; index < bufferSize; index++) { |
| printk(BIOS_DEBUG, "%02x ", *(buffer + index)); |
| if ((index+1) % 16 == 0) |
| printk(BIOS_DEBUG, "\n"); |
| } |
| printk(BIOS_DEBUG, "\n"); |
| } |
| |
| /* |
| * measures and logs the specified cbfs file. |
| * |
| * @param[in] name name of the cbfs file to measure |
| * @param[in] type data type of the cbfs file. |
| * @param[in] pcr pcr to extend. |
| * @param[in] evenType tcg event type. |
| * @param[in] event_msg description of the event. |
| * |
| * @retval TPM_SUCCESS Operation completed successfully. |
| * @retval TPM_IOERROR Unexpected device behavior. |
| */ |
| int mb_measure_log_worker(const char *name, uint32_t type, uint32_t pcr, |
| TCG_EVENTTYPE eventType, const char *event_msg) |
| { |
| int rc; |
| TCG_PCR_EVENT2_HDR tcgEventHdr; |
| uint8_t *base; |
| size_t size; |
| |
| printk(BIOS_DEBUG, "%s: Measure %s\n", __func__, name); |
| base = cbfs_map(name, &size); |
| |
| if (base == NULL) { |
| printk(BIOS_DEBUG, "%s: CBFS locate fail: %s\n", __func__, name); |
| return VB2_ERROR_READ_FILE_OPEN; |
| } |
| |
| printk(BIOS_DEBUG, "%s: CBFS locate success: %s\n", __func__, name); |
| memset(&tcgEventHdr, 0, sizeof(tcgEventHdr)); |
| tcgEventHdr.pcrIndex = pcr; |
| tcgEventHdr.eventType = eventType; |
| if (event_msg) |
| tcgEventHdr.eventSize = (uint32_t) strlen(event_msg); |
| |
| rc = mboot_hash_extend_log(0, base, size, &tcgEventHdr, (uint8_t *)event_msg); |
| return rc; |
| } |
| |
| /* |
| * Called from early romstage |
| * |
| *mb_entry |
| * |
| * initializes measured boot mechanism, initializes the tpm library and starts the tpm called |
| * by mb_measure |
| * |
| * The function can be overridden at the mainboard level my simply creating a function with the |
| * same name there. |
| * |
| * @param[in] wake_from_s3 1 if we are waking from S3, 0 standard boot |
| * |
| * @retval TPM_SUCCESS Operation completed successfully. |
| * @retval TPM_IOERROR Unexpected device behavior. |
| **/ |
| |
| __weak int mb_entry(int wake_from_s3) |
| { |
| int rc; |
| |
| /* Initialize TPM driver. */ |
| printk(BIOS_DEBUG, "%s: tlcl_lib_init\n", __func__); |
| if (tlcl_lib_init() != VB2_SUCCESS) { |
| printk(BIOS_ERR, "%s: TPM driver initialization failed.\n", __func__); |
| return TPM_IOERROR; |
| } |
| |
| if (wake_from_s3) { |
| printk(BIOS_DEBUG, "%s: tlcl_resume\n", __func__); |
| rc = tlcl_resume(); |
| } else { |
| printk(BIOS_DEBUG, "%s: tlcl_startup\n", __func__); |
| rc = tlcl_startup(); |
| } |
| |
| if (rc) |
| printk(BIOS_ERR, "%s: StartUp failed 0x%x!\n", __func__, rc); |
| |
| return rc; |
| } |
| |
| /* |
| * |
| * mb_measure |
| * |
| * initial call to the measured boot mechanism, initializes the |
| * tpm library, starts the tpm and performs the measurements defined by |
| * the coreboot platform. |
| * |
| * The pcrs will be invalidated if the measurement fails |
| * |
| * The function can be overridden at the mainboard level my simply creating a |
| * function with the same name there. |
| * |
| * @param[in] wake_from_s3 1 if we are waking from S3, 0 standard boot |
| * |
| * @retval TPM_SUCCESS Operation completed successfully. |
| * @retval TPM_IOERROR Unexpected device behavior. |
| */ |
| |
| __weak int mb_measure(int wake_from_s3) |
| { |
| uint32_t rc; |
| |
| rc = mb_entry(wake_from_s3); |
| if (rc == TPM_SUCCESS) { |
| printk(BIOS_DEBUG, "%s: StartUp, successful!\n", __func__); |
| rc = mb_measure_log_start(); |
| if (rc == TPM_SUCCESS) { |
| printk(BIOS_DEBUG, "%s: Measuring, successful!\n", __func__); |
| } else { |
| invalidate_pcrs(); |
| printk(BIOS_ERR, "%s: Measuring returned 0x%x unsuccessful! PCRs invalidated.\n", |
| __func__, rc); |
| } |
| } else { |
| invalidate_pcrs(); |
| printk(BIOS_ERR, "%s: StartUp returned 0x%x, unsuccessful! PCRs invalidated.\n", __func__, |
| rc); |
| } |
| return rc; |
| } |
| |
| /* |
| * |
| * mb_measure_log_start |
| * |
| * performs the measurements defined by the board routines. |
| * |
| * The logging is defined by the mb_log_list structure |
| * |
| * These items need to be defined in the mainboard part of the mboot |
| * implementation |
| * |
| * The function can be overridden at the mainboard level my simply creating a |
| * function with the same name there. |
| * |
| * @param[in] none |
| * |
| * @retval TPM_SUCCESS Operation completed successfully. |
| * @retval TPM_IOERROR Unexpected device behavior. |
| */ |
| __weak int mb_measure_log_start(void) |
| { |
| int rc; |
| uint32_t i; |
| |
| if ((tpm2_get_active_pcrs() & EFI_TCG2_BOOT_HASH_ALG_SHA256) == 0x0) { |
| printk(BIOS_DEBUG, "%s: SHA256 PCR Bank not active in TPM.\n", |
| __func__); |
| return TPM_IOERROR; |
| } |
| |
| rc = mb_crtm(); |
| if (rc != TPM_SUCCESS) { |
| printk(BIOS_DEBUG, "%s: Fail! CRTM Version can't be measured." |
| " ABORTING!!!\n", __func__); |
| return rc; |
| } |
| printk(BIOS_DEBUG, "%s: Success! CRTM Version measured.\n", __func__); |
| |
| /* Log the items defined by the mainboard */ |
| for (i = 0; i < ARRAY_SIZE(mb_log_list); i++) { |
| rc = mb_measure_log_worker( |
| mb_log_list[i].cbfs_name, |
| mb_log_list[i].cbfs_type, mb_log_list[i].pcr, |
| mb_log_list[i].eventType, |
| mb_log_list[i].event_msg); |
| if (rc != TPM_SUCCESS) { |
| printk(BIOS_DEBUG, "%s: Fail! %s can't be measured." |
| "ABORTING!!!\n", __func__, |
| mb_log_list[i].cbfs_name); |
| return rc; |
| } |
| printk(BIOS_DEBUG, "%s: Success! %s measured to pcr" |
| "%d.\n", __func__, mb_log_list[i].cbfs_name, |
| mb_log_list[i].pcr); |
| } |
| return rc; |
| } |
| |
| static const uint8_t crtm_version[] = |
| CONFIG_VENDORCODE_ELTAN_CRTM_VERSION_STRING\ |
| COREBOOT_VERSION COREBOOT_EXTRA_VERSION " " COREBOOT_BUILD; |
| |
| /* |
| * |
| * mb_crtm |
| * |
| * measures the crtm version. this consists of a string than can be |
| * defined using make menuconfig and automatically generated version |
| * information. |
| * |
| * The function can be overridden at the mainboard level my simply creating a |
| * function with the same name there. |
| * |
| * @retval TPM_SUCCESS Operation completed successfully. |
| * @retval TPM_IOERROR Unexpected device behavior. |
| **/ |
| __weak int mb_crtm(void) |
| { |
| int rc; |
| TCG_PCR_EVENT2_HDR tcgEventHdr; |
| uint8_t hash[VB2_SHA256_DIGEST_SIZE]; |
| uint8_t *msgPtr; |
| |
| /* Use FirmwareVersion string to represent CRTM version. */ |
| printk(BIOS_DEBUG, "%s: Measure CRTM Version\n", __func__); |
| memset(&tcgEventHdr, 0, sizeof(tcgEventHdr)); |
| tcgEventHdr.pcrIndex = MBOOT_PCR_INDEX_0; |
| tcgEventHdr.eventType = EV_S_CRTM_VERSION; |
| tcgEventHdr.eventSize = sizeof(crtm_version); |
| printk(BIOS_DEBUG, "%s: EventSize - %u\n", __func__, |
| tcgEventHdr.eventSize); |
| |
| rc = mboot_hash_extend_log(0, (uint8_t *)crtm_version, tcgEventHdr.eventSize, |
| &tcgEventHdr, (uint8_t *)crtm_version); |
| if (rc) { |
| printk(BIOS_DEBUG, "Measure CRTM Version returned 0x%x\n", rc); |
| return rc; |
| } |
| |
| rc = get_intel_me_hash(hash); |
| if (rc) { |
| printk(BIOS_DEBUG, "get_intel_me_hash returned 0x%x\n", rc); |
| rc = TPM_IOERROR; |
| return rc; |
| } |
| |
| /* Add the me hash */ |
| printk(BIOS_DEBUG, "%s: Add the hash returned by the ME\n", |
| __func__); |
| memset(&tcgEventHdr, 0, sizeof(tcgEventHdr)); |
| tcgEventHdr.pcrIndex = MBOOT_PCR_INDEX_0; |
| tcgEventHdr.eventType = EV_S_CRTM_CONTENTS; |
| |
| msgPtr = NULL; |
| tcgEventHdr.eventSize = 0; |
| rc = mboot_hash_extend_log(MBOOT_HASH_PROVIDED, hash, sizeof(hash), &tcgEventHdr, |
| msgPtr); |
| if (rc) |
| printk(BIOS_DEBUG, "Add ME hash returned 0x%x\n", rc); |
| |
| return rc; |
| } |