From mboxrd@z Thu Jan 1 00:00:00 1970 From: Kai Huang Subject: [PATCH 06/10] kvm: x86: add KVM_SET_CPUID2 SGX support Date: Mon, 8 May 2017 17:24:29 +1200 Message-ID: <20170508052434.3627-7-kai.huang@linux.intel.com> References: <20170508052434.3627-1-kai.huang@linux.intel.com> To: pbonzini@redhat.com, rkrcmar@redhat.com, kvm@vger.kernel.org Return-path: Received: from mail-pg0-f67.google.com ([74.125.83.67]:36169 "EHLO mail-pg0-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751777AbdEHFZF (ORCPT ); Mon, 8 May 2017 01:25:05 -0400 Received: by mail-pg0-f67.google.com with SMTP id 64so1420609pgb.3 for ; Sun, 07 May 2017 22:25:05 -0700 (PDT) In-Reply-To: <20170508052434.3627-1-kai.huang@linux.intel.com> Sender: kvm-owner@vger.kernel.org List-ID: This patch adds SGX CPUID support for KVM_SET_CPUID2. Besides setting up guest's SGX CPUID, guest's SGX is initialized in KVM_SET_CPUID2 as well. This is because, to avoid adding a new IOCTL to set guest's EPC base & size, we need to get such info from KVM_SET_CPUID2 (where userspace will setup guest's EPC base & size), and guest's SGX can only be initialized after KVM knows such info. Initializing guest's SGX may fail, so kvm_x86_ops->cpuid_update is changed to return integer to reflect whether guest's SGX is initialized successfully or not. kvm_update_cpuid is also moved to be called before kvm_x86_ops->cpuid_update, as guest's SGX CPUID.0x12.1 needs to depends on vcpu->arch.guest_supported_xcr0 (which is set in kvm_update_cpuid). Also a new kvm_x86_ops->vm_destroy is added for VMX, in which guest's SGX is destroyed. Signed-off-by: Kai Huang --- arch/x86/include/asm/kvm_host.h | 2 +- arch/x86/kvm/cpuid.c | 8 ++- arch/x86/kvm/cpuid.h | 20 +++++++ arch/x86/kvm/svm.c | 6 ++- arch/x86/kvm/vmx.c | 113 +++++++++++++++++++++++++++++++++++++++- 5 files changed, 143 insertions(+), 6 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index d7254f36b17d..38cbb1eb652f 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -894,7 +894,7 @@ struct kvm_x86_ops { void (*hardware_unsetup)(void); /* __exit */ bool (*cpu_has_accelerated_tpr)(void); bool (*cpu_has_high_real_mode_segbase)(void); - void (*cpuid_update)(struct kvm_vcpu *vcpu); + int (*cpuid_update)(struct kvm_vcpu *vcpu); int (*vm_init)(struct kvm *kvm); void (*vm_destroy)(struct kvm *kvm); diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index d2c396b0b32f..11a13afef373 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c @@ -220,8 +220,10 @@ int kvm_vcpu_ioctl_set_cpuid(struct kvm_vcpu *vcpu, vcpu->arch.cpuid_nent = cpuid->nent; cpuid_fix_nx_cap(vcpu); kvm_apic_set_version(vcpu); - kvm_x86_ops->cpuid_update(vcpu); r = kvm_update_cpuid(vcpu); + if (r) + goto out; + r = kvm_x86_ops->cpuid_update(vcpu); out: vfree(cpuid_entries); @@ -243,8 +245,10 @@ int kvm_vcpu_ioctl_set_cpuid2(struct kvm_vcpu *vcpu, goto out; vcpu->arch.cpuid_nent = cpuid->nent; kvm_apic_set_version(vcpu); - kvm_x86_ops->cpuid_update(vcpu); r = kvm_update_cpuid(vcpu); + if (r) + goto out; + r = kvm_x86_ops->cpuid_update(vcpu); out: return r; } diff --git a/arch/x86/kvm/cpuid.h b/arch/x86/kvm/cpuid.h index de658f4fa1c6..7d10f2884779 100644 --- a/arch/x86/kvm/cpuid.h +++ b/arch/x86/kvm/cpuid.h @@ -155,6 +155,26 @@ static inline bool guest_cpuid_has_rdtscp(struct kvm_vcpu *vcpu) } /* + * Only checks CPUID.0x7.0x0:EBX.SGX. SDM says if this bit is 1, logical + * processor supports SGX CPUID 0x12. + */ +static inline bool guest_cpuid_has_sgx(struct kvm_vcpu *vcpu) +{ + struct kvm_cpuid_entry2 *best; + + best = kvm_find_cpuid_entry(vcpu, 0x7, 0); + return best && (best->ebx & bit(X86_FEATURE_SGX)); +} + +static inline bool guest_cpuid_has_sgx_launch_control(struct kvm_vcpu *vcpu) +{ + struct kvm_cpuid_entry2 *best; + + best = kvm_find_cpuid_entry(vcpu, 0x7, 0); + return best && (best->ecx & bit(X86_FEATURE_SGX_LAUNCH_CONTROL)); +} + +/* * NRIPS is provided through cpuidfn 0x8000000a.edx bit 3 */ #define BIT_NRIPS 3 diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c index 678b30d2a188..d5a5410ba623 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c @@ -4972,7 +4972,7 @@ static u64 svm_get_mt_mask(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio) return 0; } -static void svm_cpuid_update(struct kvm_vcpu *vcpu) +static int svm_cpuid_update(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); struct kvm_cpuid_entry2 *entry; @@ -4981,11 +4981,13 @@ static void svm_cpuid_update(struct kvm_vcpu *vcpu) svm->nrips_enabled = !!guest_cpuid_has_nrips(&svm->vcpu); if (!kvm_vcpu_apicv_active(vcpu)) - return; + return 0; entry = kvm_find_cpuid_entry(vcpu, 1, 0); if (entry) entry->ecx &= ~bit(X86_FEATURE_X2APIC); + + return 0; } static int svm_set_supported_cpuid(u32 func, u32 index, diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 31de95986dbd..3c1cc94e7e6d 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -9447,11 +9447,109 @@ static void nested_vmx_cr_fixed1_bits_update(struct kvm_vcpu *vcpu) #undef cr4_fixed1_update } -static void vmx_cpuid_update(struct kvm_vcpu *vcpu) +/* This should be called after vcpu's SGX CPUID has been properly setup */ +static void vmx_cpuid_get_sgx_cpuinfo(struct kvm_vcpu *vcpu, struct + sgx_cpuinfo *sgxinfo) +{ + struct kvm_cpuid_entry2 *best; + u64 base, size; + + BUG_ON(!sgxinfo); + + /* See comments in detect_sgx... */ + memset(sgxinfo, 0, sizeof(struct sgx_cpuinfo)); + + best = kvm_find_cpuid_entry(vcpu, 0x12, 0); + if (!best) + goto not_supported; + if (!(best->eax & SGX_CAP_SGX1)) + goto not_supported; + + sgxinfo->cap = best->eax; + sgxinfo->miscselect = best->ebx; + sgxinfo->max_enclave_size32 = best->edx & 0xff; + sgxinfo->max_enclave_size64 = (best->edx & 0xff00) >> 8; + + best = kvm_find_cpuid_entry(vcpu, 0x12, 1); + if (!best) + goto not_supported; + + sgxinfo->secs_attr_bitmask[0] = best->eax; + sgxinfo->secs_attr_bitmask[1] = best->ebx; + sgxinfo->secs_attr_bitmask[2] = best->ecx; + sgxinfo->secs_attr_bitmask[3] = best->edx; + + best = kvm_find_cpuid_entry(vcpu, 0x12, 2); + if (!(best->eax & 0x1) || !(best->ecx & 0x1)) + goto not_supported; + + base = (((u64)(best->ebx & 0xfffff)) << 32) | (best->eax & 0xfffff000); + size = (((u64)(best->edx & 0xfffff)) << 32) | (best->ecx & 0xfffff000); + if (!base || !size) + goto not_supported; + + sgxinfo->epc_base = base; + sgxinfo->epc_size = size; + + return; + +not_supported: + memset(sgxinfo, 0, sizeof(struct sgx_cpuinfo)); +} + +static int vmx_cpuid_update_sgx(struct kvm_vcpu *vcpu) +{ + struct kvm_cpuid_entry2 *best; + struct sgx_cpuinfo si; + int r; + + /* Nothing to check if SGX is not enabled for guest */ + if (!guest_cpuid_has_sgx(vcpu)) + return 0; + + /* + * Update CPUID.0x12.0x1 according to vcpu->arch.guest_supported_xcr0, + * which is calculated in kvm_update_cpuid. This is the reason we + * change the order of kvm_x86_ops->cpuid_update and kvm_update_cpuid. + */ + best = kvm_find_cpuid_entry(vcpu, 0x12, 0x1); + if (!best) + return -EFAULT; + best->ecx &= (unsigned int)(vcpu->arch.guest_supported_xcr0 & 0xffffffff); + best->ecx |= 0x3; + best->edx &= (unsigned int)(vcpu->arch.guest_supported_xcr0 >> 32); + + /* + * Make sure all SGX CPUIDs are properly set in KVM_SET_CPUID2 from + * userspace. vmx_cpuid_get_sgx_cpuinfo will report invalid SGX if + * any SGX CPUID is not properly setup in KVM_SET_CPUID2. + */ + vmx_cpuid_get_sgx_cpuinfo(vcpu, &si); + if (!(si.cap & SGX_CAP_SGX1)) + return -EFAULT; + + /* + * Initialize guest's SGX staff here. To avoid a new IOCTL to allow + * userspace to pass guest's (virtual) EPC base and size to, KVM gets + * such info from KVM_SET_CPUID2. Initializing guest's SGX here also + * provides a way for KVM to check whether userspace did everything + * right about SGX CPUID (ex, inconsistent SGX CPUID between vcpus + * is passed to KVM). In case of any error, we return error to reflect + * userspace did something wrong about CPUID. + */ + r = kvm_init_sgx(vcpu->kvm, &si); + if (r) + return r; + + return 0; +} + +static int vmx_cpuid_update(struct kvm_vcpu *vcpu) { struct kvm_cpuid_entry2 *best; struct vcpu_vmx *vmx = to_vmx(vcpu); u32 secondary_exec_ctl = vmx_secondary_exec_control(vmx); + int r = 0; if (vmx_rdtscp_supported()) { bool rdtscp_enabled = guest_cpuid_has_rdtscp(vcpu); @@ -9491,6 +9589,12 @@ static void vmx_cpuid_update(struct kvm_vcpu *vcpu) if (nested_vmx_allowed(vcpu)) nested_vmx_cr_fixed1_bits_update(vcpu); + + r = vmx_cpuid_update_sgx(vcpu); + if (r) + return r; + + return 0; } static int vmx_set_supported_cpuid(u32 func, u32 index, @@ -11589,6 +11693,11 @@ static void vmx_setup_mce(struct kvm_vcpu *vcpu) ~FEATURE_CONTROL_LMCE; } +static void vmx_vm_destroy(struct kvm *kvm) +{ + kvm_destroy_sgx(kvm); +} + static struct kvm_x86_ops vmx_x86_ops __ro_after_init = { .cpu_has_kvm_support = cpu_has_kvm_support, .disabled_by_bios = vmx_disabled_by_bios, @@ -11600,6 +11709,8 @@ static struct kvm_x86_ops vmx_x86_ops __ro_after_init = { .cpu_has_accelerated_tpr = report_flexpriority, .cpu_has_high_real_mode_segbase = vmx_has_high_real_mode_segbase, + .vm_destroy = vmx_vm_destroy, + .vcpu_create = vmx_create_vcpu, .vcpu_free = vmx_free_vcpu, .vcpu_reset = vmx_vcpu_reset, -- 2.11.0