diff options
author | Philipp Deppenwiese <zaolin.daisuki@gmail.com> | 2018-11-20 14:22:15 +0100 |
---|---|---|
committer | Philipp Deppenwiese <zaolin.daisuki@gmail.com> | 2020-07-31 16:02:54 +0000 |
commit | 5f9f77672d096a013094f3cad63cb138167dbf1b (patch) | |
tree | e3dd64faa6844b1e577651eff519a67fc1add1d8 /src/security/intel/txt/common.c | |
parent | a9eec2cc2f941d6e4035620e60c1a0ae93d5167e (diff) | |
download | coreboot-5f9f77672d096a013094f3cad63cb138167dbf1b.tar.xz |
security/intel/txt: Add Intel TXT support
Add TXT ramstage driver:
* Show startup errors
* Check for TXT reset
* Check for Secrets-in-memory
* Add assembly for GETSEC instruction
* Check platform state if GETSEC instruction is supported
* Configure TXT memory regions
* Lock TXT
* Protect TSEG using DMA protected regions
* Place SINIT ACM
* Print information about ACMs
Extend the `security_clear_dram_request()` function:
* Clear all DRAM if secrets are in memory
Add a config so that the code gets build-tested. Since BIOS and SINIT
ACM binaries are not available, use the STM binary as a placeholder.
Tested on OCP Wedge100s and Facebook Watson
* Able to enter a Measured Launch Environment using SINIT ACM and TBOOT
* Secrets in Memory bit is set on ungraceful shutdown
* Memory is cleared after ungraceful shutdown
Change-Id: Iaf4be7f016cc12d3971e1e1fe171e6665e44c284
Signed-off-by: Philipp Deppenwiese <zaolin@das-labor.org>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/37016
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Christian Walter <christian.walter@9elements.com>
Diffstat (limited to 'src/security/intel/txt/common.c')
-rw-r--r-- | src/security/intel/txt/common.c | 421 |
1 files changed, 421 insertions, 0 deletions
diff --git a/src/security/intel/txt/common.c b/src/security/intel/txt/common.c new file mode 100644 index 0000000000..d3e18376a5 --- /dev/null +++ b/src/security/intel/txt/common.c @@ -0,0 +1,421 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <arch/mmio.h> +#include <string.h> +#include <console/console.h> +#include <types.h> +#include <cbfs.h> +#include <cpu/x86/lapic.h> +#include <cpu/x86/cr.h> +#include <cpu/x86/mp.h> +#include <lib.h> +#include <smp/node.h> +#include <soc/intel/common/reset.h> +#include "txt.h" +#include "txt_register.h" +#include "txt_getsec.h" + +/** + * Dump the ACM error status bits. + * + * @param acm_error The status register to dump + * @return -1 on error (register is not valid) + * 0 on error (Class > 0 and Major > 0) + * 1 on success (Class == 0 and Major == 0 and progress > 0) + */ +int intel_txt_log_acm_error(const uint32_t acm_error) +{ + if (!(acm_error & ACMERROR_TXT_VALID)) + return -1; + + const uint8_t type = (acm_error & ACMERROR_TXT_TYPE_CODE) + >> ACMERROR_TXT_TYPE_SHIFT; + + switch (type) { + case ACMERROR_TXT_AC_MODULE_TYPE_BIOS: + printk(BIOS_ERR, "BIOSACM"); + break; + case ACMERROR_TXT_AC_MODULE_TYPE_SINIT: + printk(BIOS_ERR, "SINIT"); + break; + default: + printk(BIOS_ERR, "ACM"); + break; + } + printk(BIOS_ERR, ": Error code valid\n"); + + if (acm_error & ACMERROR_TXT_EXTERNAL) + printk(BIOS_ERR, " Caused by: External\n"); + else + printk(BIOS_ERR, " Caused by: Processor\n"); + + const uint32_t class = (acm_error & ACMERROR_TXT_CLASS_CODE) + >> ACMERROR_TXT_CLASS_SHIFT; + const uint32_t major = (acm_error & ACMERROR_TXT_MAJOR_CODE) + >> ACMERROR_TXT_MAJOR_SHIFT; + const uint32_t minor = (acm_error & ACMERROR_TXT_MINOR_CODE) + >> ACMERROR_TXT_MINOR_SHIFT; + const uint32_t progress = (acm_error & ACMERROR_TXT_PROGRESS_CODE) + >> ACMERROR_TXT_PROGRESS_SHIFT; + + if (!minor) { + if (class == 0 && major == 0 && progress > 0) { + printk(BIOS_ERR, " Execution successful\n"); + printk(BIOS_ERR, " Progress code 0x%x\n", progress); + } else { + printk(BIOS_ERR, " Error Class: %x\n", class); + printk(BIOS_ERR, " Error: %x.%x\n", major, progress); + } + } else { + printk(BIOS_ERR, " ACM didn't start\n"); + printk(BIOS_ERR, " Error Type: 0x%x\n", acm_error & 0xffffff); + return -1; + } + + return (acm_error & ACMERROR_TXT_EXTERNAL) && class == 0 && major == 0 && progress > 0; +} + +void intel_txt_log_spad(void) +{ + const uint64_t acm_status = read64((void *)TXT_SPAD); + + printk(BIOS_INFO, "TXT-STS: ACM verification "); + + if (acm_status & ACMSTS_VERIFICATION_ERROR) + printk(BIOS_INFO, "error\n"); + else + printk(BIOS_INFO, "successful\n"); + + printk(BIOS_INFO, "TXT-STS: IBB "); + + if (acm_status & ACMSTS_IBB_MEASURED) + printk(BIOS_INFO, "measured\n"); + else + printk(BIOS_INFO, "not measured\n"); + + printk(BIOS_INFO, "TXT-STS: TXT is "); + + if (acm_status & ACMSTS_TXT_DISABLED) + printk(BIOS_INFO, "disabled\n"); + else + printk(BIOS_INFO, "not disabled\n"); + + printk(BIOS_INFO, "TXT-STS: BIOS is "); + + if (acm_status & ACMSTS_BIOS_TRUSTED) + printk(BIOS_INFO, "trusted\n"); + else + printk(BIOS_INFO, "not trusted\n"); +} + +/* Returns true if secrets might be in memory */ +bool intel_txt_memory_has_secrets(void) +{ + bool ret; + if (!CONFIG(INTEL_TXT)) + return false; + + ret = (read8((void *)TXT_ESTS) & TXT_ESTS_WAKE_ERROR_STS) || + (read64((void *)TXT_E2STS) & TXT_E2STS_SECRET_STS); + + if (ret) + printk(BIOS_CRIT, "TXT-STS: Secrets in memory!\n"); + return ret; +} + +static struct acm_info_table *find_info_table(const void *ptr) +{ + const struct acm_header_v0 *acm_header = (struct acm_header_v0 *)ptr; + + return (struct acm_info_table *)(ptr + + (acm_header->header_len + acm_header->scratch_size) * sizeof(uint32_t)); +} + +/** + * Validate that the provided ACM is useable on this platform. + */ +static int validate_acm(const void *ptr) +{ + const struct acm_header_v0 *acm_header = (struct acm_header_v0 *)ptr; + uint32_t max_size_acm_area = 0; + + if (acm_header->module_type != CHIPSET_ACM) + return ACM_E_TYPE_NOT_MATCH; + + /* Seems inconsistent across generations. */ + if (acm_header->module_sub_type != 0 && acm_header->module_sub_type != 1) + return ACM_E_MODULE_SUB_TYPE_WRONG; + + if (acm_header->module_vendor != INTEL_ACM_VENDOR) + return ACM_E_MODULE_VENDOR_NOT_INTEL; + + if (((acm_header->header_len + acm_header->scratch_size) * sizeof(uint32_t) + + sizeof(struct acm_info_table)) > (acm_header->size & 0xffffff) * sizeof(uint32_t)) { + return ACM_E_SIZE_INCORRECT; + } + + if (!getsec_parameter(NULL, NULL, &max_size_acm_area, NULL, NULL, NULL)) + return ACM_E_CANT_CALL_GETSEC; + + /* + * Causes #GP if acm_header->size > processor internal authenticated + * code area capacity. + * SAFER MODE EXTENSIONS REFERENCE. + * Intel 64 and IA-32 Architectures Software Developer Manuals Vol 2D + */ + const size_t acm_len = 1UL << log2_ceil((acm_header->size & 0xffffff) << 2); + if (max_size_acm_area < acm_len) { + printk(BIOS_ERR, "TEE-TXT: BIOS ACM doesn't fit into AC execution region\n"); + return ACM_E_NOT_FIT_INTO_CPU_ACM_MEM; + } + + struct acm_info_table *info = find_info_table(ptr); + if (!info) + return ACM_E_NO_INFO_TABLE; + if (info->chipset_acm_type != BIOS) + return ACM_E_NOT_BIOS_ACM; + + static const u8 acm_uuid[] = { + 0xaa, 0x3a, 0xc0, 0x7f, 0xa7, 0x46, 0xdb, 0x18, + 0x2e, 0xac, 0x69, 0x8f, 0x8d, 0x41, 0x7f, 0x5a, + }; + if (memcmp(acm_uuid, info->uuid, sizeof(acm_uuid)) != 0) + return ACM_E_UUID_NOT_MATCH; + + if ((acm_header->flags & ACM_FORMAT_FLAGS_DEBUG) == + (read64((void *)TXT_VER_FSBIF) & TXT_VER_PRODUCTION_FUSED)) + return ACM_E_PLATFORM_IS_NOT_PROD; + + return 0; +} + +/* + * Test all bits for TXT execution. + * + * @return 0 on success + */ +int intel_txt_run_bios_acm(const u8 input_params) +{ + struct cbfsf file; + void *acm_data; + struct region_device acm; + size_t acm_len; + int ret; + + if (cbfs_boot_locate(&file, CONFIG_INTEL_TXT_CBFS_BIOS_ACM, NULL)) { + printk(BIOS_ERR, "TEE-TXT: Couldn't locate BIOS ACM in CBFS.\n"); + return -1; + } + + cbfs_file_data(&acm, &file); + acm_data = rdev_mmap_full(&acm); + acm_len = region_device_sz(&acm); + if (!acm_data || acm_len == 0) { + printk(BIOS_ERR, "TEE-TXT: Couldn't map BIOS ACM from CBFS.\n"); + return -1; + } + + /* + * CPU enforces only 4KiB alignment. + * Chapter A.1.1 + * Intel TXT Software Development Guide (Document: 315168-015) + */ + if (!IS_ALIGNED((uintptr_t)acm_data, 4096)) { + printk(BIOS_ERR, "TEE-TXT: BIOS ACM isn't mapped at page boundary.\n"); + rdev_munmap(&acm, acm_data); + return -1; + } + + /* + * Causes #GP if not multiple of 64. + * SAFER MODE EXTENSIONS REFERENCE. + * Intel 64 and IA-32 Architectures Software Developer Manuals Vol 2D + */ + if (!IS_ALIGNED(acm_len, 64)) { + printk(BIOS_ERR, "TEE-TXT: BIOS ACM size isn't multiple of 64.\n"); + rdev_munmap(&acm, acm_data); + return -1; + } + + /* + * The ACM should be aligned to it's size, but that's not possible, as + * some ACMs are not power of two. Use the next power of two for verification. + */ + if (!IS_ALIGNED((uintptr_t)acm_data, (1UL << log2_ceil(acm_len)))) { + printk(BIOS_ERR, "TEE-TXT: BIOS ACM isn't aligned to its size.\n"); + rdev_munmap(&acm, acm_data); + return -1; + } + + if (CONFIG(INTEL_TXT_LOGGING)) + txt_dump_acm_info(acm_data); + + ret = validate_acm(acm_data); + if (ret < 0) { + printk(BIOS_ERR, "TEE-TXT: Validation of ACM failed with: %d\n", ret); + rdev_munmap(&acm, acm_data); + return ret; + } + + /* Call into assembly which invokes the referenced ACM */ + getsec_enteraccs(input_params, (uintptr_t)acm_data, acm_len); + + rdev_munmap(&acm, acm_data); + + const uint64_t acm_status = read64((void *)TXT_SPAD); + if (acm_status & ACMERROR_TXT_VALID) { + printk(BIOS_ERR, "TEE-TXT: FATAL ACM launch error !\n"); + /* + * WARNING ! + * To clear TXT.BIOSACM.ERRORCODE you must issue a cold reboot! + */ + intel_txt_log_acm_error(read32((void *)TXT_BIOSACM_ERRORCODE)); + return -1; + } + if (intel_txt_log_acm_error(read32((void *)TXT_BIOSACM_ERRORCODE)) != 1) + return -1; + + return 0; +} + + /* Returns true if cond is not met */ +static bool check_precondition(const int cond) +{ + printk(BIOS_DEBUG, "%s\n", cond ? "true" : "false"); + return !cond; +} + +/* + * Test all bits that are required for Intel TXT. + * Enable SMX if available. + * + * @return 0 on success + */ +bool intel_txt_prepare_txt_env(void) +{ + bool failure = false; + uint32_t txt_feature_flags = 0; + + unsigned int ecx = cpuid_ecx(1); + + printk(BIOS_DEBUG, "TEE-TXT: CPU supports SMX: "); + failure |= check_precondition(ecx & CPUID_SMX); + + printk(BIOS_DEBUG, "TEE-TXT: CPU supports VMX: "); + failure |= check_precondition(ecx & CPUID_VMX); + + msr_t msr = rdmsr(IA32_FEATURE_CONTROL); + if (!(msr.lo & BIT(0))) { + printk(BIOS_ERR, "TEE-TXT: IA32_FEATURE_CONTROL is not locked\n"); + global_reset(); + } + + printk(BIOS_DEBUG, "TEE-TXT: IA32_FEATURE_CONTROL\n"); + printk(BIOS_DEBUG, " VMXON in SMX enable: "); + failure |= check_precondition(msr.lo & BIT(1)); + + printk(BIOS_DEBUG, " VMXON outside SMX enable: "); + failure |= check_precondition(msr.lo & FEATURE_ENABLE_VMX); + + printk(BIOS_DEBUG, " register is locked: "); + failure |= check_precondition(msr.lo & BIT(0)); + + /* IA32_FEATURE_CONTROL enables getsec instructions */ + printk(BIOS_DEBUG, " GETSEC (all instructions) is enabled: "); + failure |= check_precondition((msr.lo & 0xff00) == 0xff00); + + /* Prevent crash and opt out early */ + if (failure) + return true; + + uint32_t eax = 0; + /* + * GetSec[CAPABILITIES] + * SAFER MODE EXTENSIONS REFERENCE. + * Intel 64 and IA-32 Architectures Software Developer Manuals Vol 2D + * Must check BIT0 of TXT chipset has been detected by CPU. + */ + if (!getsec_capabilities(&eax)) + return true; + + printk(BIOS_DEBUG, "TEE-TXT: GETSEC[CAPABILITIES] returned:\n"); + printk(BIOS_DEBUG, " TXT capable chipset: %s\n", (eax & BIT(0)) ? "true" : "false"); + + printk(BIOS_DEBUG, " ENTERACCS available: %s\n", (eax & BIT(2)) ? "true" : "false"); + printk(BIOS_DEBUG, " EXITAC available: %s\n", (eax & BIT(3)) ? "true" : "false"); + printk(BIOS_DEBUG, " SENTER available: %s\n", (eax & BIT(4)) ? "true" : "false"); + printk(BIOS_DEBUG, " SEXIT available: %s\n", (eax & BIT(5)) ? "true" : "false"); + printk(BIOS_DEBUG, " PARAMETERS available: %s\n", (eax & BIT(6)) ? "true" : "false"); + + /* + * Causes #GP if function is not supported by getsec. + * SAFER MODE EXTENSIONS REFERENCE. + * Intel 64 and IA-32 Architectures Software Developer Manuals Vol 2D + * Order Number: 325383-060US + */ + if ((eax & 0x7d) != 0x7d) + failure = true; + + const uint64_t status = read64((void *)TXT_SPAD); + + if (status & ACMSTS_TXT_DISABLED) { + printk(BIOS_INFO, "TEE-TXT: TXT disabled by BIOS policy in FIT.\n"); + failure = true; + } + + /* + * Only the BSP must call getsec[ENTERACCS]. + * SAFER MODE EXTENSIONS REFERENCE. + * Intel 64 and IA-32 Architectures Software Developer Manuals Vol 2D + * Order Number: 325383-060US + */ + if (!boot_cpu()) { + printk(BIOS_ERR, "TEE-TXT: BSP flag not set in APICBASE_MSR.\n"); + failure = true; + } + + /* + * There must be no MCEs pending. + * Intel 64 and IA-32 Architectures Software Developer Manuals Vol 2D + * Order Number: 325383-060US + */ + msr = rdmsr(IA32_MCG_STATUS); + if (msr.lo & 0x4) { + printk(BIOS_ERR, "TEE-TXT: IA32_MCG_STATUS.MCIP is set.\n"); + failure = true; + } + + if (!getsec_parameter(NULL, NULL, NULL, NULL, NULL, &txt_feature_flags)) { + return true; + } else { + printk(BIOS_DEBUG, "TEE-TXT: Machine Check Register: "); + if (txt_feature_flags & GETSEC_PARAMS_TXT_EXT_MACHINE_CHECK) + printk(BIOS_DEBUG, "preserved\n"); + else + printk(BIOS_DEBUG, "must be clear\n"); + } + + if (!(txt_feature_flags & GETSEC_PARAMS_TXT_EXT_MACHINE_CHECK)) { + /* + * Make sure there are no uncorrectable MCE errors. + * Intel 64 and IA-32 Architectures Software Developer Manuals Vol 2D + */ + msr = rdmsr(IA32_MCG_CAP); + size_t max_mc_msr = msr.lo & MCA_BANKS_MASK; + for (size_t i = 0; i < max_mc_msr; i++) { + msr = rdmsr(IA32_MC0_STATUS + 4 * i); + if (!(msr.hi & MCA_STATUS_HI_UC)) + continue; + + printk(BIOS_ERR, "TEE-TXT: IA32_MC%zd_STATUS.UC is set.\n", i); + failure = true; + break; + } + } + + /* Need to park all APs. */ + if (CONFIG(PARALLEL_MP_AP_WORK)) + mp_park_aps(); + + return failure; +} |