From e0f340054761086e4c329a2f66bb8f5b6e13d7c9 Mon Sep 17 00:00:00 2001 From: Keith Short Date: Tue, 5 Feb 2019 16:15:10 -0700 Subject: coreboot: check Cr50 PM mode on normal boot Under some scenarios the key ladder on the Cr50 can get disabled. If this state is detected, trigger a reboot of the Cr50 to restore full TPM functionality. BUG=b:121463033 BRANCH=none TEST=Built coreboot on sarien and grunt platforms. TEST=Ran 'gsctool -a -m disable' and reboot. Verified coreboot sends VENDOR_CC_IMMEDIATE_RESET command to Cr50 and that the Cr50 resets and then the platform boots normally. TEST=Performed Cr50 rollback to 0.0.22 which does not support the VENDOR_CC_TPM_MODE command, confirmed that platform boots normally and the coreboot log captures the unsupported command. Tested-by: Keith Short Change-Id: I70e012efaf1079d43890e909bc6b5015bef6835a Signed-off-by: Keith Short Reviewed-on: https://review.coreboot.org/c/31260 Tested-by: build bot (Jenkins) Reviewed-by: Julius Werner --- src/security/tpm/tss/tcg-2.0/tss_marshaling.c | 24 ++++++++++ src/security/tpm/tss/tcg-2.0/tss_structures.h | 1 + src/security/tpm/tss/vendor/cr50/cr50.c | 65 +++++++++++++++++++++++++-- src/security/tpm/tss/vendor/cr50/cr50.h | 48 ++++++++++++++++++++ src/security/tpm/tss_errors.h | 1 + 5 files changed, 136 insertions(+), 3 deletions(-) (limited to 'src/security') diff --git a/src/security/tpm/tss/tcg-2.0/tss_marshaling.c b/src/security/tpm/tss/tcg-2.0/tss_marshaling.c index f1c5a375e2..62bc6a9a35 100644 --- a/src/security/tpm/tss/tcg-2.0/tss_marshaling.c +++ b/src/security/tpm/tss/tcg-2.0/tss_marshaling.c @@ -266,6 +266,14 @@ static int marshal_cr50_vendor_command(struct obuf *ob, void *command_body) uint16_t *sub_command = command_body; switch (*sub_command) { + case TPM2_CR50_SUB_CMD_IMMEDIATE_RESET: + /* The 16-bit timeout parameter is optional for the + * IMMEDIATE_RESET command. However in coreboot, the timeout + * parameter must be specified. + */ + rc |= obuf_write_be16(ob, sub_command[0]); + rc |= obuf_write_be16(ob, sub_command[1]); + break; case TPM2_CR50_SUB_CMD_NVMEM_ENABLE_COMMITS: rc |= obuf_write_be16(ob, *sub_command); break; @@ -276,6 +284,18 @@ static int marshal_cr50_vendor_command(struct obuf *ob, void *command_body) case TPM2_CR50_SUB_CMD_GET_REC_BTN: rc |= obuf_write_be16(ob, *sub_command); break; + case TPM2_CR50_SUB_CMD_TPM_MODE: + /* The Cr50 TPM_MODE command supports an optional parameter. + * When the parameter is present the Cr50 will attempt to change + * the TPM state (enable or disable) and returns the new state + * in the response. When the parameter is absent, the Cr50 + * returns the current TPM state. + * + * coreboot currently only uses the TPM get capability and does + * not set a new TPM state with the Cr50. + */ + rc |= obuf_write_be16(ob, *sub_command); + break; default: /* Unsupported subcommand. */ printk(BIOS_WARNING, "Unsupported cr50 subcommand: 0x%04x\n", @@ -471,12 +491,16 @@ static int unmarshal_vendor_command(struct ibuf *ib, return -1; switch (vcr->vc_subcommand) { + case TPM2_CR50_SUB_CMD_IMMEDIATE_RESET: + break; case TPM2_CR50_SUB_CMD_NVMEM_ENABLE_COMMITS: break; case TPM2_CR50_SUB_CMD_TURN_UPDATE_ON: return ibuf_read_be8(ib, &vcr->num_restored_headers); case TPM2_CR50_SUB_CMD_GET_REC_BTN: return ibuf_read_be8(ib, &vcr->recovery_button_state); + case TPM2_CR50_SUB_CMD_TPM_MODE: + return ibuf_read_be8(ib, &vcr->tpm_mode); default: printk(BIOS_ERR, "%s:%d - unsupported vendor command %#04x!\n", 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 6952169108..991cbcf502 100644 --- a/src/security/tpm/tss/tcg-2.0/tss_structures.h +++ b/src/security/tpm/tss/tcg-2.0/tss_structures.h @@ -298,6 +298,7 @@ struct vendor_command_response { union { uint8_t num_restored_headers; uint8_t recovery_button_state; + uint8_t tpm_mode; }; }; diff --git a/src/security/tpm/tss/vendor/cr50/cr50.c b/src/security/tpm/tss/vendor/cr50/cr50.c index 450ad97fe5..1522ce6979 100644 --- a/src/security/tpm/tss/vendor/cr50/cr50.c +++ b/src/security/tpm/tss/vendor/cr50/cr50.c @@ -26,7 +26,7 @@ uint32_t tlcl_cr50_enable_nvcommits(void) if (response == NULL || (response && response->hdr.tpm_code)) { if (response) printk(BIOS_INFO, "%s: failed %x\n", __func__, - response->hdr.tpm_code); + response->hdr.tpm_code); else printk(BIOS_INFO, "%s: failed\n", __func__); return TPM_E_IOERROR; @@ -47,7 +47,7 @@ uint32_t tlcl_cr50_enable_update(uint16_t timeout_ms, response = tpm_process_command(TPM2_CR50_VENDOR_COMMAND, command_body); if (!response || response->hdr.tpm_code) - return TPM_E_INTERNAL_INCONSISTENCY; + return TPM_E_IOERROR; *num_restored_headers = response->vcr.num_restored_headers; return TPM_SUCCESS; @@ -63,8 +63,67 @@ uint32_t tlcl_cr50_get_recovery_button(uint8_t *recovery_button_state) response = tpm_process_command(TPM2_CR50_VENDOR_COMMAND, &sub_command); if (!response || response->hdr.tpm_code) - return TPM_E_INTERNAL_INCONSISTENCY; + return TPM_E_IOERROR; *recovery_button_state = response->vcr.recovery_button_state; return TPM_SUCCESS; } + +uint32_t tlcl_cr50_get_tpm_mode(uint8_t *tpm_mode) +{ + struct tpm2_response *response; + uint16_t mode_command = TPM2_CR50_SUB_CMD_TPM_MODE; + *tpm_mode = TPM_MODE_INVALID; + + printk(BIOS_INFO, "Reading cr50 TPM mode\n"); + + response = tpm_process_command(TPM2_CR50_VENDOR_COMMAND, &mode_command); + + if (!response) + return TPM_E_IOERROR; + + if (response->hdr.tpm_code == VENDOR_RC_INTERNAL_ERROR) { + /* + * The Cr50 returns VENDOR_RC_INTERNAL_ERROR iff the key ladder + * is disabled. The Cr50 requires a reboot to re-enable the key + * ladder. + */ + return TPM_E_MUST_REBOOT; + } + + if (response->hdr.tpm_code == VENDOR_RC_NO_SUCH_COMMAND) { + /* + * Explicitly inform caller when command is not supported + */ + return TPM_E_NO_SUCH_COMMAND; + } + + if (response->hdr.tpm_code) { + /* Unexpected return code from Cr50 */ + return TPM_E_IOERROR; + } + + /* TPM command completed without error */ + *tpm_mode = response->vcr.tpm_mode; + + return TPM_SUCCESS; +} + +uint32_t tlcl_cr50_immediate_reset(uint16_t timeout_ms) +{ + struct tpm2_response *response; + uint16_t reset_command_body[] = { + TPM2_CR50_SUB_CMD_IMMEDIATE_RESET, timeout_ms}; + + /* + * Issue an immediate reset to the Cr50. + */ + printk(BIOS_INFO, "Issuing cr50 reset\n"); + response = tpm_process_command(TPM2_CR50_VENDOR_COMMAND, + &reset_command_body); + + if (!response) + return TPM_E_IOERROR; + + return TPM_SUCCESS; +} diff --git a/src/security/tpm/tss/vendor/cr50/cr50.h b/src/security/tpm/tss/vendor/cr50/cr50.h index a1ab539c07..6a160e0a23 100644 --- a/src/security/tpm/tss/vendor/cr50/cr50.h +++ b/src/security/tpm/tss/vendor/cr50/cr50.h @@ -23,9 +23,35 @@ to extending generically because the marshaling code is assuming all knowledge of all commands. */ #define TPM2_CR50_VENDOR_COMMAND ((TPM_CC)(TPM_CC_VENDOR_BIT_MASK | 0)) +#define TPM2_CR50_SUB_CMD_IMMEDIATE_RESET (19) #define TPM2_CR50_SUB_CMD_NVMEM_ENABLE_COMMITS (21) #define TPM2_CR50_SUB_CMD_TURN_UPDATE_ON (24) #define TPM2_CR50_SUB_CMD_GET_REC_BTN (29) +#define TPM2_CR50_SUB_CMD_TPM_MODE (40) + +/* Cr50 vendor-specific error codes. */ +#define VENDOR_RC_ERR 0x00000500 +enum cr50_vendor_rc { + VENDOR_RC_INTERNAL_ERROR = (VENDOR_RC_ERR | 6), + VENDOR_RC_NO_SUCH_COMMAND = (VENDOR_RC_ERR | 127), +}; + +enum cr50_tpm_mode { + /* + * Default state: TPM is enabled, and may be set to either + * TPM_MODE_ENABLED or TPM_MODE_DISABLED. + */ + TPM_MODE_ENABLED_TENTATIVE = 0, + + /* TPM is enabled, and mode may not be changed. */ + TPM_MODE_ENABLED = 1, + + /* TPM is disabled, and mode may not be changed. */ + TPM_MODE_DISABLED = 2, + + TPM_MODE_INVALID, +}; + /** * CR50 specific tpm command to enable nvmem commits before internal timeout @@ -53,4 +79,26 @@ uint32_t tlcl_cr50_enable_update(uint16_t timeout_ms, */ uint32_t tlcl_cr50_get_recovery_button(uint8_t *recovery_button_state); +/** + * CR50 specific TPM command sequence to query the current TPM mode. + * + * Returns TPM_SUCCESS if TPM mode command completed, the Cr50 does not need a + * reboot, and the tpm_mode parameter is set to the current TPM mode. + * Returns TPM_E_MUST_REBOOT if TPM mode command completed, but the Cr50 + * requires a reboot. + * Returns TPM_E_NO_SUCH_COMMAND if the Cr50 does not support the command. + * Other returns value indicate a failure accessing the TPM. + */ +uint32_t tlcl_cr50_get_tpm_mode(uint8_t *tpm_mode); + +/** + * CR50 specific TPM command sequence to trigger an immediate reset to the Cr50 + * device after the specified timeout in milliseconds. A timeout of zero means + * "IMMEDIATE REBOOT". + * + * Return value indicates success or failure of accessing the TPM. + */ +uint32_t tlcl_cr50_immediate_reset(uint16_t timeout_ms); + + #endif /* CR50_TSS_STRUCTURES_H_ */ diff --git a/src/security/tpm/tss_errors.h b/src/security/tpm/tss_errors.h index 316661cd0a..ed6fc3d77c 100644 --- a/src/security/tpm/tss_errors.h +++ b/src/security/tpm/tss_errors.h @@ -42,5 +42,6 @@ #define TPM_E_NV_DEFINED ((uint32_t)0x0000500b) /* vboot local */ #define TPM_E_INVALID_ARG ((uint32_t)0x0000500c) #define TPM_E_HASH_ERROR ((uint32_t)0x0000500d) +#define TPM_E_NO_SUCH_COMMAND ((uint32_t)0x0000500e) #endif /* TSS_ERRORS_H_ */ -- cgit v1.2.3