diff options
-rw-r--r-- | configs/config.purism_librem15_v4.txt_build_test | 8 | ||||
-rw-r--r-- | src/security/intel/txt/Kconfig | 28 | ||||
-rw-r--r-- | src/security/intel/txt/Makefile.inc | 19 | ||||
-rw-r--r-- | src/security/intel/txt/common.c | 421 | ||||
-rw-r--r-- | src/security/intel/txt/getsec.c | 117 | ||||
-rw-r--r-- | src/security/intel/txt/getsec_enteraccs.S | 319 | ||||
-rw-r--r-- | src/security/intel/txt/logging.c | 241 | ||||
-rw-r--r-- | src/security/intel/txt/ramstage.c | 372 | ||||
-rw-r--r-- | src/security/intel/txt/txt.h | 27 | ||||
-rw-r--r-- | src/security/intel/txt/txt_getsec.h | 21 | ||||
-rw-r--r-- | src/security/intel/txt/txt_register.h | 267 | ||||
-rw-r--r-- | src/security/memory/memory.c | 6 |
12 files changed, 1837 insertions, 9 deletions
diff --git a/configs/config.purism_librem15_v4.txt_build_test b/configs/config.purism_librem15_v4.txt_build_test new file mode 100644 index 0000000000..f2de8bc59f --- /dev/null +++ b/configs/config.purism_librem15_v4.txt_build_test @@ -0,0 +1,8 @@ +# Not meant for actual use. Exercises Intel TXT code. Since BIOS +# and SINIT ACM blobs are missing, use something else as placeholder. +CONFIG_VENDOR_PURISM=y +CONFIG_BOARD_PURISM_LIBREM15_V4=y +CONFIG_INTEL_TXT=y +CONFIG_INTEL_TXT_BIOSACM_FILE="3rdparty/blobs/cpu/intel/stm/stm.bin" +CONFIG_INTEL_TXT_SINITACM_FILE="3rdparty/blobs/cpu/intel/stm/stm.bin" +CONFIG_INTEL_TXT_LOGGING=y diff --git a/src/security/intel/txt/Kconfig b/src/security/intel/txt/Kconfig index 732f3d3c67..edb13a51b5 100644 --- a/src/security/intel/txt/Kconfig +++ b/src/security/intel/txt/Kconfig @@ -19,7 +19,7 @@ config INTEL_TXT_BIOSACM_FILE string "BIOS ACM file" default "3rdparty/blobs/soc/intel/skylake/biosacm.bin" if SOC_INTEL_COMMON_SKYLAKE_BASE help - Intel TXT BIOS ACM file. This file can be obtained by privileged + Intel TXT BIOS ACM file. This file can be obtained through privileged access to Intel resources. Or for some platforms found inside the blob repository. @@ -27,16 +27,34 @@ config INTEL_TXT_SINITACM_FILE string "SINIT ACM file" default "3rdparty/blobs/soc/intel/skylake/sinitacm.bin" if SOC_INTEL_COMMON_SKYLAKE_BASE help - Intel TXT SINIT ACM file. This file can be obtained by privileged + Intel TXT SINIT ACM file. This file can be obtained through privileged access to Intel resources. Or for some platforms found inside the blob repository. +config INTEL_TXT_LOGGING + bool "Enable verbose logging" + help + Print more TXT related debug output. + Use in pre-production environments only! + config INTEL_TXT_BIOSACM_ALIGNMENT hex - default 0x20000 # 128KB + default 0x20000 # 128 KiB help - Exceptions are Ivy- and Sandy Bridge with 64KB and Purely with 256KB - alignment size. Please overwrite it SoC specific. + Exceptions are Ivy and Sandy Bridge with 64 KiB and Purley with 256 KiB + alignment size. If necessary, override from platform-specific Kconfig. + +config INTEL_TXT_CBFS_BIOS_POLICY + string + default "txt_bios_policy.bin" + +config INTEL_TXT_CBFS_BIOS_ACM + string + default "txt_bios_acm.bin" + +config INTEL_TXT_CBFS_SINIT_ACM + string + default "txt_sinit_acm.bin" endmenu # Intel diff --git a/src/security/intel/txt/Makefile.inc b/src/security/intel/txt/Makefile.inc index 38eb65d69c..39c3ad1dff 100644 --- a/src/security/intel/txt/Makefile.inc +++ b/src/security/intel/txt/Makefile.inc @@ -1,5 +1,14 @@ ifeq ($(CONFIG_INTEL_TXT),y) +romstage-y += common.c +romstage-$(CONFIG_INTEL_TXT_LOGGING) += logging.c + +ramstage-y += common.c +ramstage-y += getsec.c +ramstage-y += getsec_enteraccs.S +ramstage-y += ramstage.c +ramstage-$(CONFIG_INTEL_TXT_LOGGING) += logging.c + cbfs-files-y += txt_bios_acm.bin txt_bios_acm.bin-file := $(CONFIG_INTEL_TXT_BIOSACM_FILE) txt_bios_acm.bin-type := raw @@ -13,6 +22,8 @@ txt_sinit_acm.bin-align := 0x10 txt_sinit_acm.bin-compression := lzma endif +ifeq ($(CONFIG_CPU_INTEL_FIRMWARE_INTERFACE_TABLE),y) + INTERMEDIATE+=add_acm_fit add_acm_fit: $(obj)/coreboot.pre $(IFITTOOL) $(IFITTOOL) -r COREBOOT -a -n txt_bios_acm.bin -t 2 -s $(CONFIG_CPU_INTEL_NUM_FIT_ENTRIES) -f $< @@ -26,7 +37,9 @@ ibb-files += bootblock INTERMEDIATE+=add_ibb_fit add_ibb_fit: $(obj)/coreboot.pre $(IFITTOOL) - $(foreach file, $(ibb-files), $(shell $(IFITTOOL) -f $< -a -n $(file) -t 7 -s $(CONFIG_CPU_INTEL_NUM_FIT_ENTRIES) \ - -r COREBOOT)) true + $(foreach file, $(ibb-files), $(shell $(IFITTOOL) -f $< -a -n $(file) -t 7 \ + -s $(CONFIG_CPU_INTEL_NUM_FIT_ENTRIES) -r COREBOOT)) true -endif +endif # CPU_INTEL_FIRMWARE_INTERFACE_TABLE + +endif # INTEL_TXT 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; +} diff --git a/src/security/intel/txt/getsec.c b/src/security/intel/txt/getsec.c new file mode 100644 index 0000000000..a42607dccc --- /dev/null +++ b/src/security/intel/txt/getsec.c @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <stdint.h> +#include <cpu/x86/lapic.h> +#include <cpu/x86/cr.h> +#include <cpu/x86/cache.h> +#include <cpu/x86/mp.h> + +#include "txt_register.h" +#include "txt_getsec.h" + +/** + * Check for SMX support and enable it if possible. + * + * Returns false on error, true on success. + */ +static bool getsec_enabled(void) +{ + unsigned int ecx = cpuid_ecx(1); + /* + * Check if SMX and VMX is supported by CPU. + */ + if (!(ecx & CPUID_SMX) || !(ecx & CPUID_VMX)) + return false; + + /* + * Check if SMX, VMX and GetSec instructions haven't been disabled. + */ + msr_t msr = rdmsr(IA32_FEATURE_CONTROL); + if ((msr.lo & 0xff07) != 0xff07) + return false; + + /* + * Enable SMX. Required to execute GetSec instruction. + * Chapter 2.2.4.3 + * Intel TXT Software Development Guide (Document: 315168-015) + */ + write_cr4(read_cr4() | CR4_SMXE); + + return true; +} + +/** + * Get information as returned by getsec[PARAMETER]. + * Arguments can be set to NULL if not needed. + * + * Returns false on error, true on success. + */ +bool getsec_parameter(uint32_t *version_mask, + uint32_t *version_numbers_supported, + uint32_t *max_size_acm_area, + uint32_t *memory_type_mask, + uint32_t *senter_function_disable, + uint32_t *txt_feature_flags) +{ + uint32_t i, eax, ebx, ecx; + + if (!getsec_enabled()) + return false; + + /* + * SAFER MODE EXTENSIONS REFERENCE. + * Intel 64 and IA-32 Architectures Software Developer Manuals Vol 2D + */ + for (i = 0; i < 0x1f; i++) { + /* Getsec[PARAMETERS] */ + asm volatile ("getsec\n" + : "=a" (eax), "=b" (ebx), "=c" (ecx) + : "a" (IA32_GETSEC_PARAMETERS), "b" (i) :); + switch (eax & 0x1f) { + case 0: /* NULL - Exit marker */ + return true; + case 1: /* Supported AC module versions */ + if (version_mask) + *version_mask = ebx; + if (version_numbers_supported) + *version_numbers_supported = ecx; + break; + case 2: /* Max size of authenticated code execution area */ + if (max_size_acm_area) + *max_size_acm_area = eax & ~0x1f; + break; + case 3: /* External memory types supported during AC mode */ + if (memory_type_mask) + *memory_type_mask = eax; + break; + case 4: /* Selective SENTER functionality control */ + if (senter_function_disable) + *senter_function_disable = eax & (0x3f00); + break; + case 5: /* TXT extensions support */ + if (txt_feature_flags) + *txt_feature_flags = eax & (0x60); + break; + } + } + + return true; +} + +/** + * Get capabilities as returned by getsec[CAPABILITIES]. + * + * Returns false on error, true on success. + */ + +bool getsec_capabilities(uint32_t *eax) +{ + if (!getsec_enabled()) + return false; + + asm volatile ("getsec\n" + : "=a" (*eax) + : "a" (IA32_GETSEC_CAPABILITIES), "b" (0) :); + + return true; +} diff --git a/src/security/intel/txt/getsec_enteraccs.S b/src/security/intel/txt/getsec_enteraccs.S new file mode 100644 index 0000000000..0dd5237e7a --- /dev/null +++ b/src/security/intel/txt/getsec_enteraccs.S @@ -0,0 +1,319 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <cpu/x86/mtrr.h> +#include <cpu/x86/cr.h> +#include <cpu/x86/msr.h> + +#define MTRR_HIGH_MASK $((1 << (CONFIG_CPU_ADDR_BITS - 32)) - 1) + +.macro PUSH_MSR x + movl $(\x), %ecx + rdmsr + push %eax + push %edx +.endm + +.macro POP_MSR x + movl $(\x), %ecx + pop %edx + pop %eax + wrmsr +.endm + +.macro CLEAR_MSR x + movl $(\x), %ecx + xorl %edx, %edx + xorl %eax, %eax + wrmsr +.endm + +/* Variable MTRR index is passed through %ebx */ +.macro PUSH_VAR_MTRR + movl %ebx, %ecx + shll %ecx + addl MTRR_PHYS_BASE(0), %ecx + rdmsr + push %eax + push %edx + incl %ecx /* MTRR_PHYS_MASK */ + rdmsr + push %eax + push %edx +.endm + +.macro POP_VAR_MTRR + movl %ebx, %ecx + shll %ecx + addl MTRR_PHYS_MASK(0), %ecx + pop %edx + pop %eax + wrmsr + decl %ecx /* MTRR_PHYS_BASE */ + pop %edx + pop %eax + wrmsr +.endm + +.macro CLEAR_VAR_MTRR + movl %ebx, %ecx + shll %ecx + addl MTRR_PHYS_BASE(0), %ecx + xorl %edx, %edx + xorl %eax, %eax + wrmsr + incl %ecx /* MTRR_PHYS_MASK */ + xorl %edx, %edx + xorl %eax, %eax + wrmsr +.endm + +.align 4 +.text + +/* + * See "SAFER MODE EXTENSIONS REFERENCE." + * Chapter "GETSEC[ENTERACCS] - Execute Authenticated Chipset Code" for reference. + * Intel 64 and IA-32 Architectures Software Developer Manuals Vol 2D + * + * void getsec_enteraccs(uint32_t esi, + * uint32_t acm_base, + * uint32_t acm_size); + */ +.global getsec_enteraccs +getsec_enteraccs: + + /* Backup current register state */ + pushl %ebp + movl %esp, %ebp + + pushal + + movl %cr0, %eax + pushl %eax + movl %cr4, %eax + pushl %eax + + /* Pushed 10 32bit registers */ + + /* Reserve space on stack for GDT */ + subl $8, %esp + + PUSH_MSR IA32_MISC_ENABLE + PUSH_MSR MTRR_FIX_64K_00000 + PUSH_MSR MTRR_FIX_16K_80000 + PUSH_MSR MTRR_FIX_16K_A0000 + PUSH_MSR MTRR_FIX_4K_C0000 + PUSH_MSR MTRR_FIX_4K_C8000 + PUSH_MSR MTRR_FIX_4K_D0000 + PUSH_MSR MTRR_FIX_4K_D8000 + PUSH_MSR MTRR_FIX_4K_E0000 + PUSH_MSR MTRR_FIX_4K_F0000 + PUSH_MSR MTRR_FIX_4K_F8000 + + /* Push variable MTRRs in ascending order */ + xorl %ebx, %ebx + jmp cond_push_var_mtrrs + +body_push_var_mtrrs: + + PUSH_VAR_MTRR + incl %ebx + +cond_push_var_mtrrs: + + movl $(MTRR_CAP_MSR), %ecx + rdmsr + andl $(0xff), %eax + cmp %ebx, %eax + jge body_push_var_mtrrs + + /* + * Disable cache. + * Chapter 2.2.4.3 + * Intel TXT Software Development Guide (Document: 315168-015) + */ + movl %cr0, %eax + orl $(CR0_CD | CR0_NW), %eax + movl %eax, %cr0 + + /* Disable fixed MTRRs */ + movl $(MTRR_DEF_TYPE_MSR), %ecx + rdmsr + andl $(~MTRR_DEF_TYPE_FIX_EN), %eax + wrmsr + + /* + * Clear fixed MTRRs. + * Chapter 2.2.5.1 + * Intel TXT Software Development Guide (Document: 315168-015) + */ + CLEAR_MSR MTRR_FIX_64K_00000 + CLEAR_MSR MTRR_FIX_16K_80000 + CLEAR_MSR MTRR_FIX_16K_A0000 + CLEAR_MSR MTRR_FIX_4K_C0000 + CLEAR_MSR MTRR_FIX_4K_C8000 + CLEAR_MSR MTRR_FIX_4K_D0000 + CLEAR_MSR MTRR_FIX_4K_D8000 + CLEAR_MSR MTRR_FIX_4K_E0000 + CLEAR_MSR MTRR_FIX_4K_F0000 + CLEAR_MSR MTRR_FIX_4K_F8000 + + /* + * Clear variable MTRRs + * Chapter 2.2.5.1 + * Intel TXT Software Development Guide (Document: 315168-015) + */ + movl $(MTRR_CAP_MSR), %ecx + rdmsr + andl $(0xff), %eax + movl %eax, %ebx + +body_clear_var_mtrrs: + + CLEAR_VAR_MTRR + decl %ebx + jnz body_clear_var_mtrrs + + /* + * Setup BIOS ACM as WB + * Chapter A.1.1 + * Intel TXT Software Development Guide (Document: 315168-015) + */ + movl $(MTRR_PHYS_BASE(0)), %ecx + movl 12(%ebp), %eax /* %eax = acmbase */ + orl $(6), %eax /* MTRR_TYPE_WB */ + movl $0, %edx + wrmsr + + /* Round acmsize to next power of two. Required for MTRR programming. */ + movl $1, %ebx + movl 16(%ebp), %ecx /* %ebx = acmsize */ + dec %ecx + bsr %ecx, %ecx /* find MSB */ + inc %ecx + shl %cl, %ebx + movl $(MTRR_PHYS_MASK(0)), %ecx + xorl %eax, %eax + subl %ebx, %eax /* %eax = 4GIB - log2_ceil(ACM SIZE) */ + orl $((1 << 11)), %eax /* MTRR_PHYS_MASK_VALID */ + movl MTRR_HIGH_MASK, %edx + wrmsr + + /* Enable cache - GPF# if not done */ + movl %cr0, %eax + andl $(~(CR0_CD | CR0_NW)), %eax + movl %eax, %cr0 + + /* Enable Numeric error - GPE# if not done */ + movl %cr0, %eax + orl $(CR0_NE), %eax + movl %eax, %cr0 + + /* Enable SMX and FXSTORE - for getsec */ + movl %cr4, %eax + orl $(CR4_SMXE | CR4_OSFXSR), %eax + movl %eax, %cr4 + + /* + * Save GDT + * Chapter A.1.2 + * Intel TXT Software Development Guide (Document: 315168-015) + */ + sgdt -48(%ebp) + + /* Backup stack pointer */ + movd %esp, %xmm0 + movd %ebp, %xmm1 + + /* + * Get function arguments. + * It's important to pass the exact ACM size as it's used by getsec to verify + * the integrity of ACM. Unlike the size for MTRR programming, which needs to + * be power of two. + * + * The following assembly code is based on tboot's tboot/include/txt/smx.h. + */ + movl 8(%ebp), %esi /* flags */ + movl 12(%ebp), %ebx /* acm_base */ + movl 16(%ebp), %ecx /* acm_size */ + + movl $0, %edx /* reserved, must be zero */ + movl $0, %edi /* must be zero */ + movl $2, %eax /* GetSec[ENTERACCS] */ + + getsec + + /* Restore stack pointer */ + movd %xmm0, %esp + movd %xmm1, %ebp + + /* Reload GDT */ + lgdt -48(%ebp) + + /* Set cs */ + ljmp $0x10, $1f +1: + /* Fix segment registers */ + movl $0x18, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %ss + movl %eax, %fs + movl %eax, %gs + + /* Disable cache */ + movl %cr0, %eax + orl $(CR0_CD | CR0_NW), %eax + movl %eax, %cr0 + + /* Restore MTTRs */ + + /* Pop variable MTRRs in descending order */ + movl $(MTRR_CAP_MSR), %ecx + rdmsr + andl $(0xff), %eax + movl %eax, %ebx + +body_pop_var_mtrrs: + + POP_VAR_MTRR + decl %ebx + jnz body_pop_var_mtrrs + + POP_MSR MTRR_FIX_4K_F8000 + POP_MSR MTRR_FIX_4K_F0000 + POP_MSR MTRR_FIX_4K_E0000 + POP_MSR MTRR_FIX_4K_D8000 + POP_MSR MTRR_FIX_4K_D0000 + POP_MSR MTRR_FIX_4K_C8000 + POP_MSR MTRR_FIX_4K_C0000 + POP_MSR MTRR_FIX_16K_A0000 + POP_MSR MTRR_FIX_16K_80000 + POP_MSR MTRR_FIX_64K_00000 + POP_MSR IA32_MISC_ENABLE + + /* Enable fixed MTRRs */ + movl $(MTRR_DEF_TYPE_MSR), %ecx + rdmsr + orl $(MTRR_DEF_TYPE_FIX_EN), %eax + wrmsr + + /* Enable cache */ + movl %cr0, %eax + andl $(~(CR0_CD | CR0_NW)), %eax + movl %eax, %cr0 + + /* Pop GDT */ + addl $8, %esp + + popl %eax + movl %eax, %cr4 + popl %eax + movl %eax, %cr0 + + popal + + movl %ebp, %esp + popl %ebp + + ret diff --git a/src/security/intel/txt/logging.c b/src/security/intel/txt/logging.c new file mode 100644 index 0000000000..cf14b55df9 --- /dev/null +++ b/src/security/intel/txt/logging.c @@ -0,0 +1,241 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <console/console.h> +#if CONFIG(SOC_INTEL_COMMON_BLOCK_SA) +#include <intelblocks/systemagent.h> +#endif + +#include <arch/mmio.h> +#include <string.h> + +#include "txt.h" +#include "txt_register.h" + +/** + * Logs microcode or SINIT ACM errors. + * Does not log SBIOS ACM errors. + */ +static void log_txt_error(const char *phase) +{ + const uint64_t txt_error = read64((void *)TXT_ERROR); + + if (txt_error & ACMERROR_TXT_VALID) { + printk(BIOS_ERR, "%s: Error occurred\n", phase); + + if (txt_error & ACMERROR_TXT_EXTERNAL) + printk(BIOS_ERR, " Caused by: External\n"); + else + printk(BIOS_ERR, " Caused by: Processor\n"); + + printk(BIOS_ERR, " Type: "); + + switch (txt_error & TXT_ERROR_MASK) { + case 0: + printk(BIOS_ERR, "Legacy Shutdown\n"); + break; + case 5: + printk(BIOS_ERR, "Load memory type error in ACM area\n"); + break; + case 6: + printk(BIOS_ERR, "Unrecognized ACM format\n"); + break; + case 7: + printk(BIOS_ERR, "Failure to authenticate\n"); + break; + case 8: + printk(BIOS_ERR, "Invalid ACM format\n"); + break; + case 9: + printk(BIOS_ERR, "Unexpected Snoop hit\n"); + break; + case 10: + printk(BIOS_ERR, "Invalid event\n"); + break; + case 11: + printk(BIOS_ERR, "Invalid MLE\n"); + break; + case 12: + printk(BIOS_ERR, "Machine check event\n"); + break; + case 13: + printk(BIOS_ERR, "VMXAbort\n"); + break; + case 14: + printk(BIOS_ERR, "AC memory corruption\n"); + break; + case 15: + printk(BIOS_ERR, "Illegal voltage/bus ratio\n"); + break; + default: + printk(BIOS_ERR, "unknown\n"); + break; + } + } +} + +/** + * Dump useful informaation about the BIOS ACM state. + * Should run right after console_init() in romstage. + * Resets the platform if TXT reset is active and MLE cannot be established. + **/ +void intel_txt_log_bios_acm_error(void) +{ + uint32_t bios_acm_error; + uint64_t acm_status; + uint64_t txt_error; + + printk(BIOS_INFO, "TEE-TXT: State of ACM and ucode update:\n"); + + bios_acm_error = read32((void *)TXT_BIOSACM_ERRORCODE); + acm_status = read64((void *)TXT_SPAD); + txt_error = read64((void *)TXT_ERROR); + + /* Errors by BIOS ACM or FIT */ + if ((txt_error & ACMERROR_TXT_VALID) && + (acm_status & ACMERROR_TXT_VALID)) { + intel_txt_log_acm_error(read32((void *)TXT_BIOSACM_ERRORCODE)); + log_txt_error("FIT MICROCODE"); + } + /* Errors by SINIT */ + if ((txt_error & ACMERROR_TXT_VALID) && + !(acm_status & ACMERROR_TXT_VALID)) { + intel_txt_log_acm_error(txt_error); + log_txt_error("SINIT"); + } + + /* Check for fatal ACM error and TXT reset */ + uint8_t error = read8((void *)TXT_ESTS); + if (error & TXT_ESTS_TXT_RESET_STS) { + printk(BIOS_CRIT, "TXT-STS: Intel TXT reset detected\n"); + intel_txt_log_acm_error(read32((void *)TXT_ERROR)); + } +} + +/** + * Dump information about the provided ACM. + */ +void txt_dump_acm_info(const struct acm_header_v0 *acm_header) +{ + const struct acm_info_table *info = NULL; + if (!acm_header) + return; + + printk(BIOS_INFO, "ACM @ %p\n", acm_header); + + const size_t acm_size = (acm_header->size & 0xffffff) << 2; + const size_t info_off = (acm_header->header_len + acm_header->scratch_size) * 4; + + if (acm_size > (info_off + sizeof(struct acm_info_table))) + info = (const struct acm_info_table *) + ((const unsigned char *)acm_header + info_off); + + printk(BIOS_INFO, " ACM: Binary Info\n"); + if (acm_header->module_type == CHIPSET_ACM) + printk(BIOS_INFO, " Type: Chipset ACM\n"); + + if (acm_header->module_sub_type == 0) + printk(BIOS_INFO, " Subtype: undefined\n"); + else if (acm_header->module_sub_type == 1) + printk(BIOS_INFO, " Subtype: Run at reset\n"); + + printk(BIOS_INFO, " Header: v%u.%u\n", acm_header->header_version[0], + acm_header->header_version[1]); + + printk(BIOS_INFO, " Chipset: %u\n", acm_header->chipset_id); + printk(BIOS_INFO, " Size: %zu\n", acm_size); + + switch (acm_header->flags) { + case ACM_FORMAT_FLAGS_PW: + printk(BIOS_INFO, " Flags: PW signed (Production Worthy)\n"); + break; + case ACM_FORMAT_FLAGS_NPW: + printk(BIOS_INFO, " Flags: NPW signed (Non Production Worthy)\n"); + break; + case ACM_FORMAT_FLAGS_DEBUG: + printk(BIOS_INFO, " Flags: Debug signed\n"); + break; + } + + if (acm_header->module_vendor == INTEL_ACM_VENDOR) + printk(BIOS_INFO, " Vendor: Intel Corporation\n"); + + printk(BIOS_INFO, " Date: %x\n", acm_header->date); + + switch (acm_header->size) { + case ACM_FORMAT_SIZE_64KB: + printk(BIOS_INFO, " Size: 64KB\n"); + printk(BIOS_INFO, " CBnT: no\n"); + break; + case ACM_FORMAT_SIZE_128KB: + printk(BIOS_INFO, " Size: 128KB\n"); + printk(BIOS_INFO, " CBnT: no\n"); + break; + case ACM_FORMAT_SIZE_256KB: + printk(BIOS_INFO, " Size: 256KB\n"); + printk(BIOS_INFO, " CBnT: yes\n"); + break; + default: + printk(BIOS_INFO, " Size: 0x%08x\n", acm_header->size); + + break; + } + + printk(BIOS_INFO, " TXT SVN: %u\n", acm_header->txt_svn); + printk(BIOS_INFO, " SE SVN: %u\n", acm_header->se_svn); + + if (!info) + return; + printk(BIOS_INFO, " Table info:\n"); + printk(BIOS_INFO, " UUID: "); + for (size_t i = 0; i < sizeof(info->uuid); i++) + printk(BIOS_INFO, "%02X ", info->uuid[i]); + printk(BIOS_INFO, "\n"); + printk(BIOS_INFO, " Chipset acm type: 0x%x\n", info->chipset_acm_type); + printk(BIOS_INFO, " Capabilities: 0x%x\n", info->capabilities); +} + +/** + * Dump information about the chipset's TXT capabilities. + */ +void txt_dump_chipset_info(void) +{ + printk(BIOS_INFO, "TEE-TXT: Chipset Key Hash 0x"); + for (int i = 0; i < TXT_ACM_KEY_HASH_LEN; i++) { + printk(BIOS_INFO, "%llx", read64((void *)TXT_ACM_KEY_HASH + + (i * sizeof(uint64_t)))); + } + printk(BIOS_INFO, "\n"); + + printk(BIOS_INFO, "TEE-TXT: DIDVID 0x%x\n", read32((void *)TXT_DIDVID)); + printk(BIOS_INFO, "TEE-TXT: production fused chipset: %s\n", + (read64((void *)TXT_VER_FSBIF) & TXT_VER_PRODUCTION_FUSED) ? "true" : "false"); +} + +void txt_dump_regions(void) +{ + struct txt_biosdataregion *bdr = NULL; + uintptr_t tseg = 0; + uint64_t reg64; + + reg64 = read64((void *)TXT_HEAP_BASE); + if ((reg64 != 0 && reg64 != ~0UL) && + (read64((void *)(uintptr_t)reg64) >= (sizeof(*bdr) + sizeof(uint64_t)))) + bdr = (void *)((uintptr_t)reg64 + sizeof(uint64_t)); + + printk(BIOS_DEBUG, "TEE-TXT: TSEG 0x%lx\n", tseg * MiB); + printk(BIOS_DEBUG, "TEE-TXT: TXT.HEAP.BASE 0x%llx\n", read64((void *)TXT_HEAP_BASE)); + printk(BIOS_DEBUG, "TEE-TXT: TXT.HEAP.SIZE 0x%llx\n", read64((void *)TXT_HEAP_SIZE)); + printk(BIOS_DEBUG, "TEE-TXT: TXT.SINIT.BASE 0x%llx\n", read64((void *)TXT_SINIT_BASE)); + printk(BIOS_DEBUG, "TEE-TXT: TXT.SINIT.SIZE 0x%llx\n", read64((void *)TXT_SINIT_SIZE)); + printk(BIOS_DEBUG, "TEE-TXT: TXT.MSEG.BASE 0x%llx\n", read64((void *)TXT_MSEG_BASE)); + printk(BIOS_DEBUG, "TEE-TXT: TXT.MSEG.SIZE 0x%llx\n", read64((void *)TXT_MSEG_SIZE)); + + if (bdr) { + printk(BIOS_DEBUG, "TEE-TXT: BiosDataRegion.bios_sinit_size 0x%x\n", + bdr->bios_sinit_size); + printk(BIOS_DEBUG, "TEE-TXT: BiosDataRegion.lcp_pd_size 0x%llx\n", + bdr->lcp_pd_size); + printk(BIOS_DEBUG, "TEE-TXT: BiosDataRegion.lcp_pd_base 0x%llx\n", + bdr->lcp_pd_base); + } +} diff --git a/src/security/intel/txt/ramstage.c b/src/security/intel/txt/ramstage.c new file mode 100644 index 0000000000..8627a2b5c9 --- /dev/null +++ b/src/security/intel/txt/ramstage.c @@ -0,0 +1,372 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <arch/mmio.h> +#include <acpi/acpi.h> +#include <bootstate.h> +#include <bootmem.h> +#include <console/console.h> +#include <stdint.h> +#include <cbfs.h> +#include <cpu/intel/common/common.h> +#include <cpu/x86/msr.h> + +#include <lib.h> +#include <device/pci_ops.h> + +#include "txt.h" +#include "txt_register.h" +#include "txt_getsec.h" + +/* FIXME: Seems to work only on some platforms */ +static void log_ibb_measurements(void) +{ + const uint64_t mseg_size = read64((void *)TXT_MSEG_SIZE); + uint64_t mseg_base = read64((void *)TXT_MSEG_BASE); + + if (!mseg_size || !mseg_base || mseg_size <= mseg_base) + return; + /* + * MSEG SIZE and MSEG BASE might contain random values. + * Assume below 4GiB and 8byte aligned. + */ + if (mseg_base & ~0xfffffff8ULL || mseg_size & ~0xfffffff8ULL) + return; + + printk(BIOS_INFO, "TEE-TXT: IBB Hash 0x"); + for (; mseg_base < mseg_size; mseg_base++) + printk(BIOS_INFO, "%02X", read8((void *)(uintptr_t)mseg_base)); + + printk(BIOS_INFO, "\n"); +} + +void bootmem_platform_add_ranges(void) +{ + uint64_t status = read64((void *)TXT_SPAD); + + if (status & ACMSTS_TXT_DISABLED) + return; + + /* Chapter 5.5.5 Intel TXT reserved memory */ + bootmem_add_range(TXT_RESERVED_SPACE, + TXT_RESERVED_SPACE_SIZE, + BM_MEM_RESERVED); + + /* Intel TPM decode memory */ + bootmem_add_range(TXT_TPM_DECODE_AREA, + TXT_RESERVED_SPACE - TXT_TPM_DECODE_AREA, + BM_MEM_RESERVED); + + /* Intel TXT public space memory */ + bootmem_add_range(TXT_PUBLIC_SPACE, + TXT_TPM_DECODE_AREA - TXT_PUBLIC_SPACE, + BM_MEM_RESERVED); + + /* Intel TXT private space memory */ + bootmem_add_range(TXT_PRIVATE_SPACE, + TXT_PUBLIC_SPACE - TXT_PRIVATE_SPACE, + BM_MEM_RESERVED); + + const uint32_t txt_dev_memory = read32((void *)TXT_DPR) & + (TXT_DPR_TOP_ADDR_MASK << TXT_DPR_TOP_ADDR_SHIFT); + const uint32_t txt_dev_size = + (read32((void *)TXT_DPR) >> TXT_DPR_LOCK_SIZE_SHIFT) & + TXT_DPR_LOCK_SIZE_MASK; + + /* Chapter 5.5.6 Intel TXT Device Memory */ + bootmem_add_range(txt_dev_memory - txt_dev_size * MiB, + txt_dev_size * MiB, + BM_MEM_RESERVED); +} + +static bool get_wake_error_status(void) +{ + const uint8_t error = read8((void *)TXT_ESTS); + return !!(error & TXT_ESTS_WAKE_ERROR_STS); +} + +static void check_secrets_txt(void *unused) +{ + uint64_t status = read64((void *)TXT_SPAD); + + if (status & ACMSTS_TXT_DISABLED) + return; + + /* Check for fatal ACM error and TXT reset */ + if (get_wake_error_status()) { + /* + * Check if secrets bit needs to be reset. Only platforms that support + * CONFIG(PLATFORM_HAS_DRAM_CLEAR) will be able to run this code. + * Assume all memory really was cleared. + * + * TXT will issue a platform reset to come up sober. + */ + if (intel_txt_memory_has_secrets()) { + printk(BIOS_INFO, "TEE-TXT: Wiping TEE...\n"); + intel_txt_run_bios_acm(ACMINPUT_CLEAR_SECRETS); + + /* Should never reach this point ... */ + intel_txt_log_acm_error(read32((void *)TXT_BIOSACM_ERRORCODE)); + die("Waiting for platform reset...\n"); + } + } +} + +BOOT_STATE_INIT_ENTRY(BS_POST_DEVICE, BS_ON_ENTRY, check_secrets_txt, NULL); + +/** + * Log TXT startup errors, check all bits for TXT, run BIOSACM using + * GETSEC[ENTERACCS]. + * + * If a "TXT reset" is detected or "memory had secrets" is set, then do nothing as + * 1. Running ACMs will cause a TXT-RESET + * 2. Memory will be scrubbed in BS_DEV_INIT + * 3. TXT-RESET will be issued by code above later + * + */ +static void init_intel_txt(void *unused) +{ + const uint64_t status = read64((void *)TXT_SPAD); + + if (status & ACMSTS_TXT_DISABLED) + return; + + printk(BIOS_INFO, "TEE-TXT: Initializing TEE...\n"); + + intel_txt_log_spad(); + + if (CONFIG(INTEL_TXT_LOGGING)) { + intel_txt_log_bios_acm_error(); + txt_dump_chipset_info(); + } + + printk(BIOS_INFO, "TEE-TXT: Validate TEE...\n"); + + if (intel_txt_prepare_txt_env()) { + printk(BIOS_ERR, "TEE-TXT: Failed to prepare TXT environment\n"); + return; + } + + /* Check for fatal ACM error and TXT reset */ + if (get_wake_error_status()) { + /* Can't run ACMs with TXT_ESTS_WAKE_ERROR_STS set */ + printk(BIOS_ERR, "TEE-TXT: Fatal BIOS ACM error reported\n"); + return; + } + + printk(BIOS_INFO, "TEE-TXT: Testing BIOS ACM calling code...\n"); + + /* + * Test BIOS ACM code. + * ACM should do nothing on reserved functions, and return an error code + * in TXT_BIOSACM_ERRORCODE. Tests showed that this is not true. + * Use special function "NOP" that does 'nothing'. + */ + if (intel_txt_run_bios_acm(ACMINPUT_NOP) < 0) { + printk(BIOS_ERR, "TEE-TXT: Error calling BIOS ACM with NOP function.\n"); + return; + } + + if (status & (ACMSTS_BIOS_TRUSTED | ACMSTS_IBB_MEASURED)) { + log_ibb_measurements(); + + int s3resume = acpi_is_wakeup_s3(); + if (!s3resume) { + printk(BIOS_INFO, "TEE-TXT: Scheck...\n"); + if (intel_txt_run_bios_acm(ACMINPUT_SCHECK) < 0) { + printk(BIOS_ERR, "TEE-TXT: Error calling BIOS ACM.\n"); + return; + } + } + } +} + +BOOT_STATE_INIT_ENTRY(BS_DEV_INIT, BS_ON_EXIT, init_intel_txt, NULL); + +static void push_sinit_heap(u8 **heap_ptr, void *data, size_t data_length) +{ + /* Push size */ + const uint64_t tmp = data_length + 8; + memcpy(*heap_ptr, &tmp, 8); + *heap_ptr += 8; + + if (data_length) { + /* Push data */ + memcpy(*heap_ptr, data, data_length); + *heap_ptr += data_length; + } +} + +/** + * Finalize the TXT device. + * + * - Lock TXT register. + * - Protect TSEG using DMA protected regions. + * - Setup TXT regions. + * - Place SINIT ACM in TXT_SINIT memory segment. + * - Fill TXT BIOSDATA region. + */ +static void lockdown_intel_txt(void *unused) +{ + const uint64_t status = read64((void *)TXT_SPAD); + uintptr_t tseg = 0; + + if (status & ACMSTS_TXT_DISABLED) + return; + + printk(BIOS_INFO, "TEE-TXT: Locking TEE...\n"); + + /* Lock TXT config, unlocks TXT_HEAP_BASE */ + if (intel_txt_run_bios_acm(ACMINPUT_LOCK_CONFIG) < 0) { + printk(BIOS_ERR, "TEE-TXT: Failed to lock registers.\n"); + printk(BIOS_ERR, "TEE-TXT: SINIT won't be supported.\n"); + return; + } + + /* + * Document Number: 558294 + * Chapter 5.5.6.1 DMA Protection Memory Region + */ + + const u8 dpr_capable = !!(read64((void *)TXT_CAPABILITIES) & + TXT_CAPABILITIES_DPR); + printk(BIOS_INFO, "TEE-TXT: DPR capable %x\n", dpr_capable); + if (dpr_capable) { + + /* Protect 3 MiB below TSEG and lock register */ + write64((void *)TXT_DPR, (TXT_DPR_TOP_ADDR(tseg) | + TXT_DPR_LOCK_SIZE(3) | + TXT_DPR_LOCK_MASK)); + + // DPR TODO: implement SA_ENABLE_DPR in the intelblocks + + printk(BIOS_INFO, "TEE-TXT: TXT.DPR 0x%08x\n", + read32((void *)TXT_DPR)); + } + + /* + * Document Number: 558294 + * Chapter 5.5.6.3 Intel TXT Heap Memory Region + */ + write64((void *)TXT_HEAP_SIZE, 0xE0000); + write64((void *)TXT_HEAP_BASE, + ALIGN_DOWN((tseg * MiB) - read64((void *)TXT_HEAP_SIZE), 4096)); + + /* + * Document Number: 558294 + * Chapter 5.5.6.2 SINIT Memory Region + */ + write64((void *)TXT_SINIT_SIZE, 0x20000); + write64((void *)TXT_SINIT_BASE, + ALIGN_DOWN(read64((void *)TXT_HEAP_BASE) - + read64((void *)TXT_SINIT_SIZE), 4096)); + + /* + * BIOS Data Format + * Chapter C.2 + * Intel TXT Software Development Guide (Document: 315168-015) + */ + struct { + struct txt_biosdataregion bdr; + struct txt_heap_acm_element heap_acm; + struct txt_extended_data_element_header end; + } __packed data = {0}; + + /* TPM2.0 requires version 6 of BDT */ + if (CONFIG(TPM2)) + data.bdr.version = 6; + else + data.bdr.version = 5; + + data.bdr.no_logical_procs = dev_count_cpu(); + + void *sinit_base = (void *)(uintptr_t)read64((void *)TXT_SINIT_BASE); + data.bdr.bios_sinit_size = cbfs_boot_load_file(CONFIG_INTEL_TXT_CBFS_SINIT_ACM, + sinit_base, + read64((void *)TXT_SINIT_SIZE), + CBFS_TYPE_RAW); + + if (data.bdr.bios_sinit_size) { + printk(BIOS_INFO, "TEE-TXT: Placing SINIT ACM in memory.\n"); + if (CONFIG(INTEL_TXT_LOGGING)) + txt_dump_acm_info(sinit_base); + } else { + printk(BIOS_ERR, "TEE-TXT: Couldn't locate SINIT ACM in CBFS.\n"); + /* Clear memory */ + memset(sinit_base, 0, read64((void *)TXT_SINIT_SIZE)); + } + + struct cbfsf file; + /* The following have been removed from BIOS Data Table in version 6 */ + if (!cbfs_boot_locate(&file, CONFIG_INTEL_TXT_CBFS_BIOS_POLICY, NULL)) { + struct region_device policy; + + cbfs_file_data(&policy, &file); + void *policy_data = rdev_mmap_full(&policy); + size_t policy_len = region_device_sz(&policy); + + if (policy_data && policy_len) { + /* Point to FIT Type 9 entry in flash */ + data.bdr.lcp_pd_base = (uintptr_t)policy_data; + data.bdr.lcp_pd_size = (uint64_t)policy_len; + rdev_munmap(&policy, policy_data); + } else { + printk(BIOS_ERR, "TEE-TXT: Couldn't map LCP PD Policy from CBFS.\n"); + } + } else { + printk(BIOS_ERR, "TEE-TXT: Couldn't locate LCP PD Policy in CBFS.\n"); + } + + data.bdr.support_acpi_ppi = 0; + data.bdr.platform_type = 0; + + /* Extended elements - ACM addresses */ + data.heap_acm.header.type = HEAP_EXTDATA_TYPE_ACM; + data.heap_acm.header.size = sizeof(data.heap_acm); + if (data.bdr.bios_sinit_size) { + data.heap_acm.num_acms = 2; + data.heap_acm.acm_addrs[1] = (uintptr_t)sinit_base; + } else { + data.heap_acm.num_acms = 1; + } + data.heap_acm.acm_addrs[0] = + (uintptr_t)cbfs_boot_map_with_leak(CONFIG_INTEL_TXT_CBFS_BIOS_ACM, + CBFS_TYPE_RAW, + NULL); + /* Extended elements - End marker */ + data.end.type = HEAP_EXTDATA_TYPE_END; + data.end.size = sizeof(data.end); + + /* Fill TXT.HEAP.BASE with 4 subregions */ + u8 *heap_struct = (void *)((uintptr_t)read64((void *)TXT_HEAP_BASE)); + + /* BiosData */ + push_sinit_heap(&heap_struct, &data, sizeof(data)); + + /* OsMLEData */ + /* FIXME: Does firmware need to write this? */ + push_sinit_heap(&heap_struct, NULL, 0); + + /* OsSinitData */ + /* FIXME: Does firmware need to write this? */ + push_sinit_heap(&heap_struct, NULL, 0); + + /* SinitMLEData */ + /* FIXME: Does firmware need to write this? */ + push_sinit_heap(&heap_struct, NULL, 0); + + /* + * FIXME: Server-TXT capable platforms need to install an STM in SMM and set up MSEG. + */ + + /** + * Chapter 5.10.1 SMM in the Intel TXT for Servers Environment + * Disable MSEG. + */ + write64((void *)TXT_MSEG_SIZE, 0); + write64((void *)TXT_MSEG_BASE, 0); + + if (CONFIG(INTEL_TXT_LOGGING)) + txt_dump_regions(); +} + +BOOT_STATE_INIT_ENTRY(BS_POST_DEVICE, BS_ON_EXIT, lockdown_intel_txt, NULL); diff --git a/src/security/intel/txt/txt.h b/src/security/intel/txt/txt.h new file mode 100644 index 0000000000..fc5c49e67e --- /dev/null +++ b/src/security/intel/txt/txt.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef SECURITY_INTEL_TXT_H_ +#define SECURITY_INTEL_TXT_H_ + +#include <types.h> + +/* Error codes */ +#define ACM_E_TYPE_NOT_MATCH 0x01 +#define ACM_E_MODULE_SUB_TYPE_WRONG 0x02 +#define ACM_E_MODULE_VENDOR_NOT_INTEL 0x03 +#define ACM_E_SIZE_INCORRECT 0x04 +#define ACM_E_CANT_CALL_GETSEC 0x05 +#define ACM_E_NOT_FIT_INTO_CPU_ACM_MEM 0x06 +#define ACM_E_NO_INFO_TABLE 0x07 +#define ACM_E_NOT_BIOS_ACM 0x08 +#define ACM_E_UUID_NOT_MATCH 0x09 +#define ACM_E_PLATFORM_IS_NOT_PROD 0x10 + +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); +int intel_txt_run_bios_acm(const u8 input_params); +bool intel_txt_prepare_txt_env(void); + +#endif /* SECURITY_INTEL_TXT_H_ */ diff --git a/src/security/intel/txt/txt_getsec.h b/src/security/intel/txt/txt_getsec.h new file mode 100644 index 0000000000..7c4a1a4283 --- /dev/null +++ b/src/security/intel/txt/txt_getsec.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef SECURITY_INTEL_TXT_GETSEC_H_ +#define SECURITY_INTEL_TXT_GETSEC_H_ + +#include <stdint.h> + +bool getsec_parameter(uint32_t *version_mask, + uint32_t *version_numbers_supported, + uint32_t *max_size_acm_area, + uint32_t *memory_type_mask, + uint32_t *senter_function_disable, + uint32_t *txt_feature_flags); + +bool getsec_capabilities(uint32_t *eax); + +void getsec_enteraccs(const uint32_t esi, + const uint32_t acm_base, + const uint32_t acm_size); + +#endif /* SECURITY_INTEL_TXT_REGISTER_H_ */ diff --git a/src/security/intel/txt/txt_register.h b/src/security/intel/txt/txt_register.h new file mode 100644 index 0000000000..70bb309f15 --- /dev/null +++ b/src/security/intel/txt/txt_register.h @@ -0,0 +1,267 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef SECURITY_INTEL_TXT_REGISTER_H_ +#define SECURITY_INTEL_TXT_REGISTER_H_ + +#include <types.h> +#include <stddef.h> + +/* + * Document: 315168-016 + * Intel Trusted Execution Technology (Intel TXT) + * Software Development Guide + * Chapter B + */ +#define TXT_BASE 0xfed30000UL + +#define TXT_STS (TXT_BASE + 0) +#define TXT_ESTS (TXT_BASE + 8) +#define TXT_ESTS_TXT_RESET_STS (1 << 0) +/* + * Chapter 6 + * Intel Trusted Execution Technology Lab Handout + */ +#define TXT_ESTS_WAKE_ERROR_STS (1 << 6) + +#define TXT_ERROR (TXT_BASE + 0x30) +#define ACMERROR_TXT_VALID (1ul << 31) +#define ACMERROR_TXT_EXTERNAL (1ul << 30) + +#define ACMERROR_TXT_PROGRESS_SHIFT 16 +#define ACMERROR_TXT_MINOR_SHIFT 15 +#define ACMERROR_TXT_MAJOR_SHIFT 10 +#define ACMERROR_TXT_CLASS_SHIFT 4 +#define ACMERROR_TXT_TYPE_SHIFT 0 + +#define ACMERROR_TXT_PROGRESS_CODE (0xffull << ACMERROR_TXT_PROGRESS_SHIFT) +#define ACMERROR_TXT_MINOR_CODE (0x01ull << ACMERROR_TXT_MINOR_SHIFT) +#define ACMERROR_TXT_MAJOR_CODE (0x1full << ACMERROR_TXT_MAJOR_SHIFT) +#define ACMERROR_TXT_CLASS_CODE (0x3full << ACMERROR_TXT_CLASS_SHIFT) +#define ACMERROR_TXT_TYPE_CODE (0x0full << ACMERROR_TXT_TYPE_SHIFT) + +#define ACMERROR_TXT_AC_MODULE_TYPE_BIOS 0 +#define ACMERROR_TXT_AC_MODULE_TYPE_SINIT 1 + +#define TXT_ERROR_MASK (0x3ff << 0) + +#define TXT_CMD_RESET (TXT_BASE + 0x38) +#define TXT_CMD_CLOSE_PRIVATE (TXT_BASE + 0x48) + +/* Present in Document Number: 315168-016. */ +#define TXT_SPAD (TXT_BASE + 0xa0) +#define ACMSTS_IBB_MEASURED (1ull << 63) +#define ACMSTS_VERIFICATION_ERROR (1ull << 62) +#define ACMSTS_BG_STARTUP_ERROR (1ull << 61) /* CBnT platforms only */ +#define ACMSTS_TXT_DISABLED (1ull << 60) /* disabled by FIT type 0xA record */ +#define ACMSTS_BIOS_TRUSTED (1ull << 59) +#define ACMSTS_MEM_CLEAR_POWER_DOWN (1ull << 47) +#define ACMSTS_TXT_STARTUP_SUCCESS (1ull << 30) + +#define TXT_VER_FSBIF (TXT_BASE + 0x100) +#define TXT_VER_PRODUCTION_FUSED (1ull << 31) + +#define TXT_DIDVID (TXT_BASE + 0x110) + +/* + * Chapter 6 + * Intel Trusted Execution Technology Lab Handout + */ +#define TXT_CAPABILITIES (TXT_BASE + 0x200) +#define TXT_CAPABILITIES_DPR (1ull << 26) +#define TXT_CAPABILITIES_PMRC (1ull << 19) + +#define TXT_VER_QPIIF (TXT_BASE + 0x200) + +#define TXT_SINIT_BASE (TXT_BASE + 0x270) +#define TXT_SINIT_SIZE (TXT_BASE + 0x278) +#define TXT_MLE_JOIN (TXT_BASE + 0x290) + +#define TXT_HEAP_BASE (TXT_BASE + 0x300) +#define TXT_HEAP_SIZE (TXT_BASE + 0x308) +/* + * Chapter 6 + * Intel Trusted Execution Technology Lab Handout + */ +#define TXT_MSEG_BASE (TXT_BASE + 0x310) +#define TXT_MSEG_SIZE (TXT_BASE + 0x318) + +/* + * Chapter 5.4.2.1 + * Intel Trusted Execution Technology Lab Handout + */ +#define TXT_BIOSACM_ERRORCODE (TXT_BASE + 0x328) + +#define TXT_DPR (TXT_BASE + 0x330) +#define TXT_DPR_LOCK_SHIFT 0 +#define TXT_DPR_LOCK_SIZE_SHIFT 4 +#define TXT_DPR_LOCK_SIZE_MASK 0xff +#define TXT_DPR_TOP_ADDR_SHIFT 20 +#define TXT_DPR_TOP_ADDR_MASK 0xfff + +#define TXT_DPR_LOCK_MASK (1 << TXT_DPR_LOCK_SHIFT) +#define TXT_DPR_LOCK_SIZE(x) ((x) << TXT_DPR_LOCK_SIZE_SHIFT) +#define TXT_DPR_TOP_ADDR(x) ((x) << TXT_DPR_TOP_ADDR_SHIFT) + +#define TXT_ACM_KEY_HASH (TXT_BASE + 0x400) +#define TXT_ACM_KEY_HASH_LEN 0x4 + +#define TXT_E2STS (TXT_BASE + 0x8f0) +#define TXT_E2STS_SECRET_STS (1ull << 1) + +/* + * TXT Memory regions + * Chapter 5.3 + * Intel Trusted Execution Technology Lab Handout + */ +#define TXT_PRIVATE_SPACE 0xfed20000UL +#define TXT_PUBLIC_SPACE 0xfed30000UL +#define TXT_TPM_DECODE_AREA 0xfed40000UL +#define TXT_RESERVED_SPACE 0xfed50000UL + +#define TXT_RESERVED_SPACE_SIZE 0x3ffff + +/* ESI flags for GETSEC[ENTERACCS] see Reference Number: 323372-017 */ +#define ACMINPUT_RESET_TPM_AUXILIARY_INDICIES 2 +#define ACMINPUT_NOP 3 +#define ACMINPUT_SCHECK 4 +#define ACMINPUT_CLEAR_SECRETS 5 +#define ACMINPUT_LOCK_CONFIG 6 + +/* + * GetSec EAX value. + * SAFER MODE EXTENSIONS REFERENCE. + * Intel 64 and IA-32 Architectures Software Developer Manuals Vol 2 + * Order Number: 325383-060US + */ +#define IA32_GETSEC_CAPABILITIES 0 +#define IA32_GETSEC_ENTERACCS 2 +#define IA32_GETSEC_SENTER 4 +#define IA32_GETSEC_SEXIT 5 +#define IA32_GETSEC_PARAMETERS 6 +#define IA32_GETSEC_SMCTRL 7 +#define IA32_GETSEC_WAKEUP 8 + +#define GETSEC_PARAMS_TXT_EXT (1ul << 5) +#define GETSEC_PARAMS_TXT_EXT_CRTM_SUPPORT (1ul << 1) +#define GETSEC_PARAMS_TXT_EXT_MACHINE_CHECK (1ul << 6) + +/* ACM defines */ +#define INTEL_ACM_VENDOR 0x00008086 + +#define ACM_FORMAT_FLAGS_PW 0x00000000 +#define ACM_FORMAT_FLAGS_NPW (1 << 14) +#define ACM_FORMAT_FLAGS_DEBUG (1 << 15) + +/* Old ACMs are power of two aligned, newer ACMs are not */ +#define ACM_FORMAT_SIZE_64KB (64 * KiB / 4) +#define ACM_FORMAT_SIZE_128KB (128 * KiB / 4) +#define ACM_FORMAT_SIZE_256KB (256 * KiB / 4) + +/* MSRs */ +#define IA32_MCG_STATUS 0x17a + +typedef enum { + CHIPSET_ACM = 2, +} acm_module_type; + +typedef enum { + BIOS = 0, + SINIT = 1, +} acm_module_sub_type; + +/* + * ACM Header v0.0 without dynamic part + * Chapter A.1 + * Intel TXT Software Development Guide (Document: 315168-015) + */ +struct __packed acm_header_v0 { + uint16_t module_type; + uint16_t module_sub_type; + uint32_t header_len; + uint16_t header_version[2]; + uint16_t chipset_id; + uint16_t flags; + uint32_t module_vendor; + uint32_t date; + uint32_t size; + uint16_t txt_svn; + uint16_t se_svn; + uint32_t code_control; + uint32_t error_entry_point; + uint32_t gdt_limit; + uint32_t gdt_ptr; + uint32_t seg_sel; + uint32_t entry_point; + uint8_t reserved2[64]; + uint32_t key_size; + uint32_t scratch_size; + uint8_t rsa2048_pubkey[256]; + uint32_t pub_exp; + uint8_t rsa2048_sig[256]; + uint32_t scratch[143]; + uint8_t user_area[]; +}; + +struct __packed acm_info_table { + uint8_t uuid[16]; + uint8_t chipset_acm_type; + uint8_t version; + uint16_t length; + uint32_t chipset_id_list; + uint32_t os_sinit_data_ver; + uint32_t min_mle_hdr_ver; + uint32_t capabilities; + uint8_t acm_ver; + uint8_t reserved[3]; +}; + +/* + * Extended Data Elements + * Chapter C.1 + * Intel TXT Software Development Guide (Document: 315168-015) + */ +struct __packed txt_extended_data_element_header { + uint32_t type; + uint32_t size; + uint8_t data[0]; +}; + +#define HEAP_EXTDATA_TYPE_END 0 +#define HEAP_EXTDATA_TYPE_BIOS_SPEC_VER 1 +#define HEAP_EXTDATA_TYPE_ACM 2 +#define HEAP_EXTDATA_TYPE_CUSTOM 4 + +struct __packed txt_heap_acm_element { + struct txt_extended_data_element_header header; + uint32_t num_acms; // must greater 0, smaller than 3 + uint64_t acm_addrs[2]; +}; + +/* + * BIOS Data Format + * Chapter C.2 + * Intel TXT Software Development Guide (Document: 315168-015) + */ +struct __packed txt_biosdataregion { + uint32_t version; + uint32_t bios_sinit_size; + uint64_t lcp_pd_base; + uint64_t lcp_pd_size; + uint32_t no_logical_procs; + uint32_t sinit_flags; + union { + uint32_t mle_flags; + struct { + uint32_t support_acpi_ppi : 1; + uint32_t platform_type : 2; + }; + }; + u8 extended_data_elements[0]; +}; + + +void txt_dump_regions(void); +void txt_dump_chipset_info(void); +void txt_dump_acm_info(const struct acm_header_v0 *acm_header); + +#endif /* SECURITY_INTEL_TXT_REGISTER_H_ */ diff --git a/src/security/memory/memory.c b/src/security/memory/memory.c index 664e7c17a3..ff4cd1581b 100644 --- a/src/security/memory/memory.c +++ b/src/security/memory/memory.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ -#include <types.h> +#include <stdint.h> +#include <security/intel/txt/txt.h> #include "memory.h" /** @@ -13,6 +14,9 @@ bool security_clear_dram_request(void) if (CONFIG(SECURITY_CLEAR_DRAM_ON_REGULAR_BOOT)) return true; + if (CONFIG(INTEL_TXT) && intel_txt_memory_has_secrets()) + return true; + /* TODO: Add TEE environments here */ return false; |