blob: b78394add2c8ca66b7a49b72f00e663343868087 [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0-only */
#include <cbfs.h>
#include <console/console.h>
#include <cpu/x86/cr.h>
#include <cpu/x86/mp.h>
#include <cpu/x86/msr.h>
#include <cpu/x86/mtrr.h>
#include <device/mmio.h>
#include <lib.h>
#include <smp/node.h>
#include <string.h>
#include <types.h>
#if CONFIG(SOC_INTEL_COMMON_BLOCK_SA)
#include <soc/intel/common/reset.h>
#else
#if CONFIG(SOUTHBRIDGE_INTEL_COMMON_ME)
#include <southbridge/intel/common/me.h>
#endif
#include <cf9_reset.h>
#endif
#include "txt.h"
#include "txt_register.h"
#include "txt_getsec.h"
/* Usual security practice: if an unexpected error happens, reboot */
void __noreturn txt_reset_platform(void)
{
#if CONFIG(SOC_INTEL_COMMON_BLOCK_SA)
global_reset();
#else
#if CONFIG(SOUTHBRIDGE_INTEL_COMMON_ME)
set_global_reset(1);
#endif
full_reset();
#endif
}
/**
* 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;
}
bool intel_txt_chipset_is_production_fused(void)
{
/*
* Certain chipsets report production fused information in either
* TXT.VER.FSBIF or TXT.VER.EMIF/TXT.VER.QPIIF.
* Chapter B.1.7 and B.1.9
* Intel TXT Software Development Guide (Document: 315168-015)
*/
uint32_t reg = read32((void *)TXT_VER_FSBIF);
if (reg == 0 || reg == UINT32_MAX)
reg = read32((void *)TXT_VER_QPIIF);
return (reg & TXT_VER_PRODUCTION_FUSED) ? true : false;
}
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 usable 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->size == 0)
return ACM_E_SIZE_INCORRECT;
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;
const bool production_acm = !(acm_header->flags & ACM_FORMAT_FLAGS_DEBUG);
if (production_acm != intel_txt_chipset_is_production_fused())
return ACM_E_PLATFORM_IS_NOT_PROD;
return 0;
}
/*
* Prepare to run the BIOS ACM: mmap it from the CBFS and verify that it
* can be launched. Returns pointer to ACM on success, NULL on failure.
*/
static void *intel_txt_prepare_bios_acm(size_t *acm_len)
{
void *acm_data = NULL;
if (!acm_len)
return NULL;
acm_data = cbfs_map(CONFIG_INTEL_TXT_CBFS_BIOS_ACM, acm_len);
if (!acm_data) {
printk(BIOS_ERR, "TEE-TXT: Couldn't locate BIOS ACM in CBFS.\n");
return NULL;
}
/*
* 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");
cbfs_unmap(acm_data);
return NULL;
}
/*
* 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");
cbfs_unmap(acm_data);
return NULL;
}
/*
* 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");
cbfs_unmap(acm_data);
return NULL;
}
/*
* When setting up the MTRRs to cache the BIOS ACM, one must cache less than
* a page (4 KiB) of unused memory after the BIOS ACM. On Haswell, failure
* to do so will cause a TXT reset with Class Code 5, Major Error Code 2.
*/
if (popcnt(ALIGN_UP(*acm_len, 4096)) > get_var_mtrr_count()) {
printk(BIOS_ERR, "TEE-TXT: Not enough MTRRs to cache this BIOS ACM's size.\n");
cbfs_unmap(acm_data);
return NULL;
}
if (CONFIG(INTEL_TXT_LOGGING))
txt_dump_acm_info(acm_data);
const int ret = validate_acm(acm_data);
if (ret < 0) {
printk(BIOS_ERR, "TEE-TXT: Validation of ACM failed with: %d\n", ret);
cbfs_unmap(acm_data);
return NULL;
}
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)
{
size_t acm_len;
void *acm_data = intel_txt_prepare_bios_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");
cbfs_unmap(acm_data);
}
/*
* Test all bits for TXT execution.
*
* @return 0 on success
*/
int intel_txt_run_bios_acm(const u8 input_params)
{
size_t acm_len;
void *acm_data = intel_txt_prepare_bios_acm(&acm_len);
if (!acm_data)
return -1;
/* Call into assembly which invokes the referenced ACM */
getsec_enteraccs(input_params, (uintptr_t)acm_data, acm_len);
cbfs_unmap(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;
}
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");
txt_reset_platform();
}
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");
printk(BIOS_DEBUG, " SMCTRL available: %s\n", (eax & BIT(7)) ? "true" : "false");
printk(BIOS_DEBUG, " WAKEUP available: %s\n", (eax & BIT(8)) ? "true" : "false");
txt_dump_getsec_parameters();
/*
* 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;
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
*/
size_t max_mc_msr = mca_get_bank_count();
for (size_t i = 0; i < max_mc_msr; i++) {
msr = rdmsr(IA32_MC_STATUS(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;
}