diff options
Diffstat (limited to 'src/security/intel/txt/romstage.c')
-rw-r--r-- | src/security/intel/txt/romstage.c | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/src/security/intel/txt/romstage.c b/src/security/intel/txt/romstage.c new file mode 100644 index 0000000000..7a12debd6d --- /dev/null +++ b/src/security/intel/txt/romstage.c @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <arch/mmio.h> +#include <cf9_reset.h> +#include <console/console.h> +#include <cpu/intel/common/common.h> +#include <cpu/x86/msr.h> +#include <southbridge/intel/common/pmbase.h> +#include <timer.h> +#include <types.h> + +#include <security/tpm/tis.h> + +#include "txt.h" +#include "txt_register.h" +#include "txt_getsec.h" + +static bool is_establishment_bit_asserted(void) +{ + struct stopwatch timer; + uint8_t access; + + /* Spec says no less than 30 milliseconds */ + stopwatch_init_msecs_expire(&timer, 50); + + while (true) { + access = read8((void *)TPM_ACCESS_REG); + + /* Register returns all ones if TPM is missing */ + if (access == 0xff) + return false; + + if (access & TPM_ACCESS_VALID) + break; + + /* On timeout, assume that the TPM is not working */ + if (stopwatch_expired(&timer)) + return false; + } + + /* This bit uses inverted logic: if cleared, establishment is asserted */ + return !(access & TPM_ACCESS_ESTABLISHMENT); +} + +static bool is_txt_cpu(void) +{ + const uint32_t ecx = cpu_get_feature_flags_ecx(); + + return (ecx & (CPUID_SMX | CPUID_VMX)) == (CPUID_SMX | CPUID_VMX); +} + +static bool is_txt_chipset(void) +{ + uint32_t eax; + + const bool success = getsec_capabilities(&eax); + + return success && eax & 1; +} + +/* Print the bad news */ +static void print_memory_is_locked(void) +{ + if (!CONFIG(INTEL_TXT_LOGGING)) + return; + + printk(BIOS_EMERG, "FATAL: Cannot run SCLEAN. Memory will remain locked.\n"); + printk(BIOS_EMERG, "\n"); + printk(BIOS_EMERG, "If you still want to boot, your options are:\n"); + printk(BIOS_EMERG, "\n"); + printk(BIOS_EMERG, " 1. Flash a coreboot image with a valid BIOS ACM.\n"); + printk(BIOS_EMERG, " Then, try again and hope it works this time.\n"); + printk(BIOS_EMERG, "\n"); + printk(BIOS_EMERG, " 2. If possible, remove the TPM from the system.\n"); + printk(BIOS_EMERG, " Reinstalling the TPM might lock memory again.\n"); + printk(BIOS_EMERG, "\n"); + printk(BIOS_EMERG, " 3. Disconnect all power sources, and RTC battery.\n"); + printk(BIOS_EMERG, " This may not work on all TXT-enabled platforms.\n"); + printk(BIOS_EMERG, "\n"); +} + +void intel_txt_romstage_init(void) +{ + /* Bail early if the CPU doesn't support TXT */ + if (!is_txt_cpu()) + return; + + /* We need to use GETSEC here, so enable it */ + enable_getsec_or_reset(); + + if (!is_txt_chipset()) + return; + + const uint8_t txt_ests = read8((void *)TXT_ESTS); + + const bool establishment = is_establishment_bit_asserted(); + const bool is_wake_error = !!(txt_ests & TXT_ESTS_WAKE_ERROR_STS); + + if (CONFIG(INTEL_TXT_LOGGING)) { + + printk(BIOS_INFO, "TEE-TXT: TPM established: %s\n", + establishment ? "true" : "false"); + } + + if (establishment && is_wake_error) { + + printk(BIOS_ERR, "TEE-TXT: Secrets remain in memory. SCLEAN is required.\n"); + + if (txt_ests & TXT_ESTS_TXT_RESET_STS) { + printk(BIOS_ERR, "TEE-TXT: TXT_RESET bit set, doing full reset!\n"); + full_reset(); + } + + /* FIXME: Clear SLP_TYP# */ + write_pmbase32(4, read_pmbase32(4) & ~(0x7 << 10)); + + intel_txt_run_sclean(); + + /* If running the BIOS ACM is impossible, manual intervention is required */ + print_memory_is_locked(); + + /* FIXME: vboot A/B could be used to recover, but has not been tested */ + die("Could not execute BIOS ACM to unlock the memory.\n"); + } +} |