Expose SGX to the guest if and only if KVM is enabled and supports virtualization of SGX. While the majority of ENCLS can be emulated to some degree, because SGX uses a hardware-based root of trust, the attestation aspects of SGX cannot be emulated in software, i.e. ultimately emulation will fail as software cannot generate a valid quote/report. The complexity of partially emulating SGX in Qemu far outweighs the value added, e.g. an SGX specific simulator for userspace applications can emulate SGX for development and testing purposes. Note, access to the PROVISIONKEY is not yet advertised to the guest as KVM blocks access to the PROVISIONKEY by default and requires userspace to provide additional credentials (via ioctl()) to expose PROVISIONKEY. Signed-off-by: Sean Christopherson <sean.j.christopherson@xxxxxxxxx> --- hw/i386/sgx-epc.c | 17 +++++++++ include/hw/i386/sgx-epc.h | 1 + target/i386/cpu.c | 76 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+) diff --git a/hw/i386/sgx-epc.c b/hw/i386/sgx-epc.c index 09aba1f8ea..7f26e9cb10 100644 --- a/hw/i386/sgx-epc.c +++ b/hw/i386/sgx-epc.c @@ -181,6 +181,23 @@ static void sgx_epc_register_types(void) type_init(sgx_epc_register_types) +int sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size) +{ + PCMachineState *pcms = PC_MACHINE(qdev_get_machine()); + SGXEPCDevice *epc; + + if (pcms->sgx_epc == NULL || pcms->sgx_epc->nr_sections <= section_nr) { + return 1; + } + + epc = pcms->sgx_epc->sections[section_nr]; + + *addr = epc->addr; + *size = memory_device_get_region_size(MEMORY_DEVICE(epc), &error_fatal); + + return 0; +} + static int sgx_epc_set_property(void *opaque, const char *name, const char *value, Error **errp) diff --git a/include/hw/i386/sgx-epc.h b/include/hw/i386/sgx-epc.h index 562c66148f..91ed4773e3 100644 --- a/include/hw/i386/sgx-epc.h +++ b/include/hw/i386/sgx-epc.h @@ -58,5 +58,6 @@ typedef struct SGXEPCState { extern int sgx_epc_enabled; void pc_machine_init_sgx_epc(PCMachineState *pcms); +int sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size); #endif diff --git a/target/i386/cpu.c b/target/i386/cpu.c index ab08c1765e..1bb9586230 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -57,6 +57,7 @@ #include "hw/xen/xen.h" #include "hw/i386/apic_internal.h" #include "hw/boards.h" +#include "hw/i386/sgx-epc.h" #endif #include "disas/capstone.h" @@ -4359,6 +4360,25 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *ecx |= CPUID_7_0_ECX_OSPKE; } *edx = env->features[FEAT_7_0_EDX]; /* Feature flags */ + + /* + * SGX cannot be emulated in software. If hardware does not + * support enabling SGX and/or SGX flexible launch control, + * then we need to update the VM's CPUID values accordingly. + */ + if ((*ebx & CPUID_7_0_EBX_SGX) && + (!kvm_enabled() || + !(kvm_arch_get_supported_cpuid(cs->kvm_state, 0x7, 0, R_EBX) & + CPUID_7_0_EBX_SGX))) { + *ebx &= ~CPUID_7_0_EBX_SGX; + } + + if ((*ecx & CPUID_7_0_ECX_SGX_LC) && + (!(*ebx & CPUID_7_0_EBX_SGX) || !kvm_enabled() || + !(kvm_arch_get_supported_cpuid(cs->kvm_state, 0x7, 0, R_ECX) & + CPUID_7_0_ECX_SGX_LC))) { + *ecx &= ~CPUID_7_0_ECX_SGX_LC; + } } else { *eax = 0; *ebx = 0; @@ -4488,6 +4508,62 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } break; } + case 0x12: +#ifndef CONFIG_USER_ONLY + if (!kvm_enabled() || + !(env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_SGX)) { + *eax = *ebx = *ecx = *edx = 0; + break; + } + + /* + * SGX sub-leafs CPUID.0x12.{0x2..N} enumerate EPC sections. Retrieve + * the EPC properties, e.g. confidentiality and integrity, from the + * host's first EPC section, i.e. assume there is one EPC section or + * that all EPC sections have the same security properties. + */ + if (count > 1) { + uint64_t epc_addr, epc_size; + + if (sgx_epc_get_section(count - 2, &epc_addr, &epc_size)) { + *eax = *ebx = *ecx = *edx = 0; + break; + } + host_cpuid(index, 2, eax, ebx, ecx, edx); + *eax = (uint32_t)(epc_addr & 0xfffff000) | 0x1; + *ebx = (uint32_t)(epc_addr >> 32); + *ecx = (uint32_t)(epc_size & 0xfffff000) | (*ecx & 0xf); + *edx = (uint32_t)(epc_size >> 32); + break; + } + + /* + * SGX sub-leafs CPUID.0x12.{0x0,0x1} are heavily dependent on hardware + * and KVM, i.e. QEMU cannot emulate features to override what KVM + * supports. Features can be further restricted by userspace, but not + * made more permissive. + */ + *eax = kvm_arch_get_supported_cpuid(cs->kvm_state, 0x12, count, R_EAX); + *ebx = kvm_arch_get_supported_cpuid(cs->kvm_state, 0x12, count, R_EBX); + *ecx = kvm_arch_get_supported_cpuid(cs->kvm_state, 0x12, count, R_ECX); + *edx = kvm_arch_get_supported_cpuid(cs->kvm_state, 0x12, count, R_EDX); + + if (count == 0) { + *eax &= env->features[FEAT_SGX_12_0_EAX]; + } else { + *eax &= env->features[FEAT_SGX_12_1_EAX]; + *ebx &= env->features[FEAT_SGX_12_1_EBX]; + *ecx &= env->features[FEAT_XSAVE_COMP_LO]; + *edx &= env->features[FEAT_XSAVE_COMP_HI]; + + /* FP and SSE are always allowed regardless of XSAVE/XCR0. */ + *ecx |= XSTATE_FP_MASK | XSTATE_SSE_MASK; + + /* Access to PROVISIONKEY requires additional credentials. */ + *eax &= ~(1U << 4); + } +#endif + break; case 0x14: { /* Intel Processor Trace Enumeration */ *eax = 0; -- 2.22.0