diff mbox

[06/10] kvm: x86: add KVM_SET_CPUID2 SGX support

Message ID 20170508052434.3627-7-kai.huang@linux.intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Kai Huang May 8, 2017, 5:24 a.m. UTC
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 <kai.huang@linux.intel.com>
---
 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 mbox

Patch

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,