diff options
Diffstat (limited to 'src/security')
-rw-r--r-- | src/security/intel/txt/Makefile.inc | 2 | ||||
-rw-r--r-- | src/security/intel/txt/common.c | 42 | ||||
-rw-r--r-- | src/security/intel/txt/romstage.c | 125 | ||||
-rw-r--r-- | src/security/intel/txt/txt.h | 3 | ||||
-rw-r--r-- | src/security/intel/txt/txt_register.h | 10 |
5 files changed, 182 insertions, 0 deletions
diff --git a/src/security/intel/txt/Makefile.inc b/src/security/intel/txt/Makefile.inc index 762256f6e3..eab47b95f9 100644 --- a/src/security/intel/txt/Makefile.inc +++ b/src/security/intel/txt/Makefile.inc @@ -1,6 +1,8 @@ ifeq ($(CONFIG_INTEL_TXT),y) +romstage-y += romstage.c romstage-y += getsec_sclean.S +romstage-y += getsec.c romstage-y += common.c romstage-$(CONFIG_INTEL_TXT_LOGGING) += logging.c diff --git a/src/security/intel/txt/common.c b/src/security/intel/txt/common.c index 88e2b5dddb..4dd4ad3ddf 100644 --- a/src/security/intel/txt/common.c +++ b/src/security/intel/txt/common.c @@ -290,6 +290,48 @@ static void *intel_txt_prepare_bios_acm(struct region_device *acm, size_t *acm_l return acm_data; } +#define MCU_BASE_ADDR (TXT_BASE + 0x278) +#define BIOACM_ADDR (TXT_BASE + 0x27c) +#define APINIT_ADDR (TXT_BASE + 0x290) +#define SEMAPHORE (TXT_BASE + 0x294) + +/* Returns on failure, resets the computer on success */ +void intel_txt_run_sclean(void) +{ + struct region_device acm; + size_t acm_len; + + void *acm_data = intel_txt_prepare_bios_acm(&acm, &acm_len); + + if (!acm_data) + return; + + /* FIXME: Do we need to program these two? */ + //write32((void *)MCU_BASE_ADDR, 0xffe1a990); + //write32((void *)APINIT_ADDR, 0xfffffff0); + + write32((void *)BIOACM_ADDR, (uintptr_t)acm_data); + write32((void *)SEMAPHORE, 0); + + /* + * The time SCLEAN will take depends on the installed RAM size. + * On Haswell with 8 GiB of DDR3, it takes five or ten minutes. (rough estimate) + */ + printk(BIOS_ALERT, "TEE-TXT: Invoking SCLEAN. This can take several minutes.\n"); + + /* + * Invoke the BIOS ACM. If successful, the system will reset with memory unlocked. + */ + getsec_sclean((uintptr_t)acm_data, acm_len); + + /* + * However, if this function returns, the BIOS ACM could not be invoked. This is bad. + */ + printk(BIOS_CRIT, "TEE-TXT: getsec_sclean could not launch the BIOS ACM.\n"); + + rdev_munmap(&acm, acm_data); +} + /* * Test all bits for TXT execution. * 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"); + } +} diff --git a/src/security/intel/txt/txt.h b/src/security/intel/txt/txt.h index fc5c49e67e..976cc7458e 100644 --- a/src/security/intel/txt/txt.h +++ b/src/security/intel/txt/txt.h @@ -17,10 +17,13 @@ #define ACM_E_UUID_NOT_MATCH 0x09 #define ACM_E_PLATFORM_IS_NOT_PROD 0x10 +void intel_txt_romstage_init(void); + void intel_txt_log_bios_acm_error(void); int intel_txt_log_acm_error(const uint32_t acm_error); void intel_txt_log_spad(void); bool intel_txt_memory_has_secrets(void); +void intel_txt_run_sclean(void); int intel_txt_run_bios_acm(const u8 input_params); bool intel_txt_prepare_txt_env(void); diff --git a/src/security/intel/txt/txt_register.h b/src/security/intel/txt/txt_register.h index c19ec13799..bb735b6cfd 100644 --- a/src/security/intel/txt/txt_register.h +++ b/src/security/intel/txt/txt_register.h @@ -99,6 +99,16 @@ #define TXT_E2STS_SECRET_STS (1ull << 1) /* + * TCG PC Client Platform TPM Profile (PTP) Specification + * + * Note: Only locality 0 registers are publicly accessible. + */ + +#define TPM_BASE 0xfed40000UL + +#define TPM_ACCESS_REG (TPM_BASE + 0x00) + +/* * TXT Memory regions * Chapter 5.3 * Intel Trusted Execution Technology Lab Handout |