| /* SPDX-License-Identifier: GPL-2.0-only */ |
| |
| #include <arch/cpu.h> |
| #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"); |
| } |
| } |