blob: 6f7829a65d6479c8fd218d950a30679e6b8bd8ef [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* TPM setup an PCR-extend functions
*
* Copyright 2021 Google LLC
*/
#define LOG_CATEGORY LOGC_VBOOT
#include <common.h>
#include <tpm_api.h>
#include <cros/cros_common.h>
#include <cros/vboot.h>
#define TPM_PCR_BOOT_MODE "VBOOT: boot mode"
#define TPM_PCR_GBB_HWID_NAME "VBOOT: GBB HWID"
static u32 tpm_get_flags(struct udevice *dev, bool *disablep,
bool *deactivatedp, bool *nvlockedp)
{
struct tpm_permanent_flags pflags;
u32 ret = tpm1_get_permanent_flags(dev, &pflags);
if (ret == TPM_SUCCESS) {
if (disablep)
*disablep = pflags.disable;
if (deactivatedp)
*deactivatedp = pflags.deactivated;
if (nvlockedp)
*nvlockedp = pflags.nv_locked;
log_debug("TPM: flags disable=%d, deactivated=%d, nv_locked=%d\n",
pflags.disable, pflags.deactivated, pflags.nv_locked);
}
return ret;
}
static u32 tpm1_invoke_state_machine(struct vboot_info *vboot,
struct udevice *dev)
{
bool disable;
bool deactivated;
u32 ret = TPM_SUCCESS;
/* Check that the TPM is enabled and activated */
ret = tpm_get_flags(dev, &disable, &deactivated, NULL);
if (ret != TPM_SUCCESS) {
log_err("TPM: Can't read capabilities\n");
return ret;
}
if (!!deactivated != vboot->deactivate_tpm) {
log_info("TPM: Unexpected TPM deactivated state; toggling..\n");
ret = tpm_physical_set_deactivated(dev, !deactivated);
if (ret != TPM_SUCCESS) {
log_err("TPM: Can't toggle deactivated state\n");
return ret;
}
deactivated = !deactivated;
ret = TPM_E_MUST_REBOOT;
}
if (disable && !deactivated) {
log_info("TPM: disabled (%d). Enabling..\n", disable);
ret = tpm_physical_enable(dev);
if (ret != TPM_SUCCESS) {
log_err("TPM: Can't set enabled state\n");
return ret;
}
log_info("TPM: Must reboot to re-enable\n");
ret = TPM_E_MUST_REBOOT;
}
return ret;
}
/*
* This starts the TPM and establishes the root of trust for the
* anti-rollback mechanism. This can fail for three reasons. 1 A bug. 2 a
* TPM hardware failure. 3 An unexpected TPM state due to some attack. In
* general we cannot easily distinguish the kind of failure, so our strategy is
* to reboot in recovery mode in all cases. The recovery mode calls this code
* again, which executes (almost) the same sequence of operations. There is a
* good chance that, if recovery mode was entered because of a TPM failure, the
* failure will repeat itself. (In general this is impossible to guarantee
* because we have no way of creating the exact TPM initial state at the
* previous boot.) In recovery mode, we ignore the failure and continue, thus
* giving the recovery kernel a chance to fix things (that's why we don't set
* bGlobalLock). The choice is between a known-insecure device and a
* bricked device.
*
* As a side note, observe that we go through considerable hoops to avoid using
* the STCLEAR permissions for the index spaces. We do this to avoid writing
* to the TPM flashram at every reboot or wake-up, because of concerns about
* the durability of the NVRAM.
*
* Note: This function is called tpm_setup() in coreboot and is in tspi.c
*/
static u32 do_setup(struct vboot_info *vboot, bool s3flag)
{
u32 ret;
log(UCLASS_TPM, LOGL_DEBUG, "Setting up TPM (s3=%d):\n", s3flag);
ret = tpm_open(vboot->tpm);
if (ret != TPM_SUCCESS) {
log_err("TPM: Can't initialise\n");
goto out;
}
/* Handle special init for S3 resume path */
if (s3flag) {
ret = tpm_resume(vboot->tpm);
if (ret == TPM_INVALID_POSTINIT)
log_info("TPM: Already initialised\n");
return TPM_SUCCESS;
}
log(UCLASS_TPM, LOGL_DEBUG, "TPM startup:\n");
ret = tpm_startup(vboot->tpm, TPM_ST_CLEAR);
if (ret != TPM_SUCCESS) {
log_err("TPM: Can't run startup command\n");
goto out;
}
log(UCLASS_TPM, LOGL_DEBUG, "TPM presence:\n");
ret = tpm_tsc_physical_presence(vboot->tpm,
TPM_PHYSICAL_PRESENCE_PRESENT);
if (ret != TPM_SUCCESS) {
/*
* It is possible that the TPM was delivered with the physical
* presence command disabled. This tries enabling it, then
* tries asserting PP again.
*/
ret = tpm_tsc_physical_presence(vboot->tpm,
TPM_PHYSICAL_PRESENCE_CMD_ENABLE);
if (ret != TPM_SUCCESS) {
log_err("Can't enable physical presence command\n");
goto out;
}
ret = tpm_tsc_physical_presence(vboot->tpm,
TPM_PHYSICAL_PRESENCE_PRESENT);
if (ret != TPM_SUCCESS) {
log_err("Can't assert physical presence\n");
goto out;
}
}
if (tpm_get_version(vboot->tpm) == TPM_V1) {
if (!IS_ENABLED(CONFIG_TPM_V1))
return log_msg_ret("tpm_v1", -ENOSYS);
ret = tpm1_invoke_state_machine(vboot, vboot->tpm);
if (ret != TPM_SUCCESS)
return ret;
}
out:
if (ret != TPM_SUCCESS)
log_err("TPM: setup failed\n");
else
log_debug("TPM: setup succeeded\n");
return ret;
}
int vboot_setup_tpm(struct vboot_info *vboot)
{
struct vb2_context *ctx = vboot_get_ctx(vboot);
int ret;
ret = do_setup(vboot, ctx->flags & VB2_CONTEXT_S3_RESUME);
if (ret == TPM_E_MUST_REBOOT)
ctx->flags |= VB2_CONTEXT_SECDATA_WANTS_REBOOT;
return ret;
}
vb2_error_t vboot_extend_pcr(struct vboot_info *vboot, int pcr,
enum vb2_pcr_digest which_digest)
{
uint8_t buffer[VB2_PCR_DIGEST_RECOMMENDED_SIZE];
struct vb2_context *ctx = vboot_get_ctx(vboot);
u32 size = sizeof(buffer);
vb2_error_t rv;
int ret;
rv = vb2api_get_pcr_digest(ctx, which_digest, buffer, &size);
if (rv != VB2_SUCCESS)
return log_msg_retz("digest", rv);
if (size < TPM_PCR_MINIMUM_DIGEST_SIZE)
return log_msg_retz("size", VB2_ERROR_UNKNOWN);
/*
* On TPM 1.2, all PCRs are intended for use with SHA1. We truncate our
* SHA256 HWID hash to 20 bytes to make it fit. On TPM 2.0, we always
* want to use the SHA256 banks, even for the boot mode which is
* technically a SHA1 value for historical reasons. vboot has already
* zero-extended the buffer to 32 bytes for us, so we just take it like
* that and pretend it's a SHA256. In practice, this means we never care
* about the (*size) value returned from vboot (which indicates how many
* significant bytes vboot wrote, although it always extends zeroes up
* to the end of the buffer), we always use a hardcoded size instead.
*/
_Static_assert(sizeof(buffer) >= VB2_SHA256_DIGEST_SIZE,
"Buffer needs to be able to fit at least a SHA256");
enum vb2_hash_algorithm algo = tpm_is_v1(vboot->tpm) ? VB2_HASH_SHA1 :
VB2_HASH_SHA256;
switch (which_digest) {
/* SHA1 of (devmode|recmode|keyblock) bits */
case BOOT_MODE_PCR:
ret = tpm_pcr_extend(vboot->tpm, pcr, buffer,
vb2_digest_size(algo), buffer,
TPM_PCR_BOOT_MODE);
if (ret)
return log_msg_retz("boot", ret);
break;
/* SHA256 of HWID */
case HWID_DIGEST_PCR:
ret = tpm_pcr_extend(vboot->tpm, pcr, buffer,
vb2_digest_size(algo), buffer,
TPM_PCR_GBB_HWID_NAME);
if (ret)
return log_msg_retz("hwid", ret);
break;
default:
return log_msg_retz("none", VB2_ERROR_UNKNOWN);
}
return 0;
}
int vboot_extend_pcrs(struct vboot_info *vboot)
{
int ret;
ret = vboot_extend_pcr(vboot, 0, BOOT_MODE_PCR);
if (ret)
return log_msg_ret("boot", -EIO);
ret = vboot_extend_pcr(vboot, 1, HWID_DIGEST_PCR);
if (ret)
return log_msg_ret("hwid", -EIO);
return 0;
}