blob: 6050dec093ea8051de7eaef7a955fbde0b9eef66 [file] [log] [blame]
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2017 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <assert.h>
#include <console/console.h>
#include <cpu/x86/msr.h>
#include <cpu/x86/mtrr.h>
#include <cpu/intel/microcode.h>
#include <intelblocks/mp_init.h>
#include <intelblocks/msr.h>
#include <intelblocks/sgx.h>
#include <intelblocks/systemagent.h>
#include <soc/cpu.h>
#include <soc/pci_devs.h>
#include <string.h>
static bool sgx_param_valid;
static struct sgx_param g_sgx_param;
static inline uint64_t sgx_resource(uint32_t low, uint32_t high)
{
uint64_t val;
val = (uint64_t)(high & SGX_RESOURCE_MASK_HI) << 32;
val |= low & SGX_RESOURCE_MASK_LO;
return val;
}
static const struct sgx_param *get_sgx_param(void)
{
if (sgx_param_valid)
return &g_sgx_param;
memset(&g_sgx_param, 0, sizeof(g_sgx_param));
if (soc_fill_sgx_param(&g_sgx_param) < 0) {
printk(BIOS_ERR, "SGX : Failed to get soc sgx param\n");
return NULL;
}
sgx_param_valid = true;
printk(BIOS_INFO, "SGX : param.enable = %d\n", g_sgx_param.enable);
return &g_sgx_param;
}
static int soc_sgx_enabled(void)
{
const struct sgx_param *sgx_param = get_sgx_param();
return sgx_param ? sgx_param->enable : 0;
}
static int is_sgx_supported(void)
{
struct cpuid_result cpuid_regs;
msr_t msr;
cpuid_regs = cpuid_ext(0x7, 0x0); /* EBX[2] is feature capability */
msr = rdmsr(MTRR_CAP_MSR); /* Bit 12 is PRMRR enablement */
return ((cpuid_regs.ebx & SGX_SUPPORTED) && (msr.lo & PRMRR_SUPPORTED));
}
void prmrr_core_configure(void)
{
union {
uint64_t data64;
struct {
uint32_t lo;
uint32_t hi;
} data32;
} prmrr_base, prmrr_mask;
msr_t msr;
if (!soc_sgx_enabled() || !is_sgx_supported())
return;
msr = rdmsr(PRMRR_PHYS_MASK_MSR);
/* If it is locked don't attempt to write PRMRR MSRs. */
if (msr.lo & PRMRR_PHYS_MASK_LOCK)
return;
/* PRMRR base and mask are read from the UNCORE PRMRR MSRs
* that are already set in FSP-M. */
if (soc_get_uncore_prmmr_base_and_mask(&prmrr_base.data64,
&prmrr_mask.data64) < 0) {
printk(BIOS_ERR, "SGX: Failed to get PRMRR base and mask\n");
return;
}
if (!prmrr_base.data32.lo) {
printk(BIOS_ERR, "SGX Error: Uncore PRMRR is not set!\n");
return;
}
printk(BIOS_INFO, "SGX: prmrr_base = 0x%llx", prmrr_base.data64);
printk(BIOS_INFO, "SGX: prmrr_mask = 0x%llx", prmrr_mask.data64);
/* Program core PRMRR MSRs.
* - Set cache writeback mem attrib in PRMRR base MSR
* - Clear the valid bit in PRMRR mask MSR
* - Lock PRMRR MASK MSR */
prmrr_base.data32.lo |= MTRR_TYPE_WRBACK;
wrmsr(PRMRR_PHYS_BASE_MSR, (msr_t) {.lo = prmrr_base.data32.lo,
.hi = prmrr_base.data32.hi});
prmrr_mask.data32.lo &= ~PRMRR_PHYS_MASK_VALID;
prmrr_mask.data32.lo |= PRMRR_PHYS_MASK_LOCK;
wrmsr(PRMRR_PHYS_MASK_MSR, (msr_t) {.lo = prmrr_mask.data32.lo,
.hi = prmrr_mask.data32.hi});
}
static int is_prmrr_set(void)
{
msr_t prmrr_base, prmrr_mask;
prmrr_base = rdmsr(PRMRR_PHYS_BASE_MSR);
prmrr_mask = rdmsr(PRMRR_PHYS_MASK_MSR);
/* If PRMRR base is zero and PRMRR mask is locked
* then PRMRR is not set */
if ((prmrr_base.hi == 0) && (prmrr_base.lo == 0)
&& (prmrr_mask.lo & PRMRR_PHYS_MASK_LOCK))
return 0;
return 1;
}
static void enable_sgx(void)
{
msr_t msr;
msr = rdmsr(IA32_FEATURE_CONTROL);
/* Only enable it when it is not locked */
if ((msr.lo & FEATURE_CONTROL_LOCK_BIT) == 0) {
msr.lo |= SGX_GLOBAL_ENABLE; /* Enable it */
wrmsr(IA32_FEATURE_CONTROL, msr);
}
}
static void lock_sgx(void)
{
msr_t msr;
msr = rdmsr(IA32_FEATURE_CONTROL);
/* If it is locked don't attempt to lock it again. */
if ((msr.lo & 1) == 0) {
msr.lo |= 1; /* Lock it */
wrmsr(IA32_FEATURE_CONTROL, msr);
}
}
static int owner_epoch_update(void)
{
/* TODO - the Owner Epoch update mechanism is not determined yet,
* for PoC just write '0's to the MSRs. */
msr_t msr = {0, 0};
wrmsr(MSR_SGX_OWNEREPOCH0, msr);
wrmsr(MSR_SGX_OWNEREPOCH1, msr);
return 0;
}
static void activate_sgx(void)
{
msr_t msr;
/* Activate SGX feature by writing 1b to MSR 0x7A on all threads.
* BIOS must ensure bit 0 is set prior to writing to it, then read it
* back and verify the bit is cleared to confirm SGX activation. */
msr = rdmsr(MSR_BIOS_UPGD_TRIG);
if (msr.lo & SGX_ACTIVATE_BIT) {
wrmsr(MSR_BIOS_UPGD_TRIG,
(msr_t) {.lo = SGX_ACTIVATE_BIT, .hi = 0});
/* Read back to verify it is activated */
msr = rdmsr(MSR_BIOS_UPGD_TRIG);
if (msr.lo & SGX_ACTIVATE_BIT)
printk(BIOS_ERR, "SGX activation failed.\n");
else
printk(BIOS_INFO, "SGX activation was successful.\n");
} else {
printk(BIOS_ERR, "SGX feature is deactivated.\n");
}
}
static int is_prmrr_approved(void)
{
msr_t msr;
msr = rdmsr(PRMRR_PHYS_MASK_MSR);
if (msr.lo & PRMRR_PHYS_MASK_VALID) {
printk(BIOS_INFO, "SGX: MCHECK approved SGX PRMRR\n");
return 1;
}
printk(BIOS_INFO, "SGX: MCHECK did not approve SGX PRMRR\n");
return 0;
}
void sgx_configure(void *unused)
{
const void *microcode_patch = intel_mp_current_microcode();
if (!soc_sgx_enabled() || !is_sgx_supported() || !is_prmrr_set()) {
printk(BIOS_ERR, "SGX: pre-conditions not met\n");
return;
}
/* Enable the SGX feature */
enable_sgx();
/* Update the owner epoch value */
if (owner_epoch_update() < 0)
return;
/* Ensure to lock memory before reload microcode patch */
cpu_lock_sgx_memory();
/* Reload the microcode patch */
intel_microcode_load_unlocked(microcode_patch);
/* Lock the SGX feature */
lock_sgx();
/* Activate the SGX feature, if PRMRR config was approved by MCHECK */
if (is_prmrr_approved())
activate_sgx();
}
void sgx_fill_gnvs(global_nvs_t *gnvs)
{
struct cpuid_result cpuid_regs;
if (!soc_sgx_enabled() || !is_sgx_supported()) {
printk(BIOS_DEBUG,
"SGX: not enabled or not supported. skip gnvs fill\n");
return;
}
/* Get EPC base and size.
* Intel SDM: Table 36-6. CPUID Leaf 12H, Sub-Leaf Index 2 or
* Higher for enumeration of SGX Resources. Same Table mentions
* about return values of the CPUID */
cpuid_regs = cpuid_ext(SGX_RESOURCE_ENUM_CPUID_LEAF,
SGX_RESOURCE_ENUM_CPUID_SUBLEAF);
if (cpuid_regs.eax & SGX_RESOURCE_ENUM_BIT) {
/* EPC section enumerated */
gnvs->ecps = 1;
gnvs->emna = sgx_resource(cpuid_regs.eax, cpuid_regs.ebx);
gnvs->elng = sgx_resource(cpuid_regs.ecx, cpuid_regs.edx);
}
printk(BIOS_DEBUG,
"SGX: gnvs ECP status = %d base = 0x%llx len = 0x%llx\n",
gnvs->ecps, gnvs->emna, gnvs->elng);
}