@@ -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);
@@ -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;
}
@@ -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
@@ -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,
@@ -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,
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(-)