/* * 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 #include #include #include #include #include #include #include #include #include #include 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(MSR_PRMRR_PHYS_MASK); /* 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\n", prmrr_base.data64); printk(BIOS_INFO, "SGX: prmrr_mask = 0x%llx\n", 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(MSR_PRMRR_PHYS_BASE, (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(MSR_PRMRR_PHYS_MASK, (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(MSR_PRMRR_PHYS_BASE); prmrr_mask = rdmsr(MSR_PRMRR_PHYS_MASK); /* 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(MSR_PRMRR_PHYS_MASK); 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); }