[PATCH 10/10] kvm: vmx: handle VMEXIT from SGX Enclave

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



VMX adds new bit to both exit_reason and GUEST_INTERRUPT_STATE to indicate
whether VMEXIT happens in Enclave. Several instructions are also invalid or
behave differently in enclave according to SDM. This patch handles those
cases.

Signed-off-by: Kai Huang <kai.huang@xxxxxxxxxxxxxxx>
---
 arch/x86/include/asm/vmx.h      |   1 +
 arch/x86/include/uapi/asm/vmx.h |   1 +
 arch/x86/kvm/vmx.c              | 120 +++++++++++++++++++++++++++++++++-------
 3 files changed, 103 insertions(+), 19 deletions(-)

diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h
index 2f24290b7f9d..ec91f68f4511 100644
--- a/arch/x86/include/asm/vmx.h
+++ b/arch/x86/include/asm/vmx.h
@@ -351,6 +351,7 @@ enum vmcs_field {
 #define GUEST_INTR_STATE_MOV_SS		0x00000002
 #define GUEST_INTR_STATE_SMI		0x00000004
 #define GUEST_INTR_STATE_NMI		0x00000008
+#define GUEST_INTR_STATE_ENCLAVE_INTR	0x00000010
 
 /* GUEST_ACTIVITY_STATE flags */
 #define GUEST_ACTIVITY_ACTIVE		0
diff --git a/arch/x86/include/uapi/asm/vmx.h b/arch/x86/include/uapi/asm/vmx.h
index 2bcd967d5c83..6f18898c003d 100644
--- a/arch/x86/include/uapi/asm/vmx.h
+++ b/arch/x86/include/uapi/asm/vmx.h
@@ -26,6 +26,7 @@
 
 
 #define VMX_EXIT_REASONS_FAILED_VMENTRY         0x80000000
+#define VMX_EXIT_REASON_FROM_ENCLAVE		0x08000000
 
 #define EXIT_REASON_EXCEPTION_NMI       0
 #define EXIT_REASON_EXTERNAL_INTERRUPT  1
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index b5f37982e975..1022295ba925 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -2628,6 +2628,24 @@ static void vmx_set_interrupt_shadow(struct kvm_vcpu *vcpu, int mask)
 		vmcs_write32(GUEST_INTERRUPTIBILITY_INFO, interruptibility);
 }
 
+static bool vmx_exit_from_enclave(struct kvm_vcpu *vcpu)
+{
+	/*
+	 * We have 2 bits to indicate whether VMEXIT happens from enclave --
+	 * bit 27 in VM_EXIT_REASON, and bit 4 in GUEST_INTERRUPTIBILITY_INFO.
+	 * Currently use latter to check whether VMEXIT happens from enclave,
+	 * but note that we never clear this bit therefore we assume hardware
+	 * will clear this bit when VMEXIT happens not from enclave, which
+	 * should be the case.
+	 *
+	 * We can either do this via bit 27 in VM_EXIT_REASON, by adding a bool
+	 * in vmx and set it in vmx_handle_exit when above bit is set, and clear
+	 * the bool right before vmentry to guest.
+	 */
+	return vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) &
+		GUEST_INTR_STATE_ENCLAVE_INTR ? true : false;
+}
+
 static void skip_emulated_instruction(struct kvm_vcpu *vcpu)
 {
 	unsigned long rip;
@@ -5457,6 +5475,25 @@ static u32 vmx_secondary_exec_control(struct vcpu_vmx *vmx)
 	return exec_control;
 }
 
+static void vmcs_set_secondary_exec_control(u32 new_ctl)
+{
+	/*
+	 * These bits in the secondary execution controls field
+	 * are dynamic, the others are mostly based on the hypervisor
+	 * architecture and the guest's CPUID.  Do not touch the
+	 * dynamic bits.
+	 */
+	u32 mask =
+		SECONDARY_EXEC_SHADOW_VMCS |
+		SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE |
+		SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES;
+
+	u32 cur_ctl = vmcs_read32(SECONDARY_VM_EXEC_CONTROL);
+
+	vmcs_write32(SECONDARY_VM_EXEC_CONTROL,
+		     (new_ctl & ~mask) | (cur_ctl & mask));
+}
+
 static void ept_set_mmio_spte_mask(void)
 {
 	/*
@@ -6305,6 +6342,12 @@ static void vmx_set_dr7(struct kvm_vcpu *vcpu, unsigned long val)
 
 static int handle_cpuid(struct kvm_vcpu *vcpu)
 {
+	/* CPUID is invalid in enclave */
+	if (vmx_exit_from_enclave(vcpu)) {
+		kvm_inject_gp(vcpu, 0);
+		return 1;
+	}
+
 	return kvm_emulate_cpuid(vcpu);
 }
 
@@ -6378,6 +6421,16 @@ static int handle_vmcall(struct kvm_vcpu *vcpu)
 
 static int handle_invd(struct kvm_vcpu *vcpu)
 {
+	/*
+	 * SDM 39.6.5 INVD Handling when Enclaves Are Enabled.
+	 *
+	 * Spec says INVD causes #GP if EPC is enabled.
+	 */
+	if (vmx_exit_from_enclave(vcpu)) {
+		kvm_inject_gp(vcpu, 0);
+		return 1;
+	}
+
 	return emulate_instruction(vcpu, 0) == EMULATE_DONE;
 }
 
@@ -6399,6 +6452,18 @@ static int handle_rdpmc(struct kvm_vcpu *vcpu)
 
 static int handle_wbinvd(struct kvm_vcpu *vcpu)
 {
+	/*
+	 * SDM 39.6.5 INVD Handling when Enclaves Are Enabled.
+	 *
+	 * Spec says INVD causes #GP if EPC is enabled.
+	 *
+	 * FIXME: Does this also apply to WBINVD?
+	 */
+	if (vmx_exit_from_enclave(vcpu)) {
+		kvm_inject_gp(vcpu, 0);
+		return 1;
+	}
+
 	return kvm_emulate_wbinvd(vcpu);
 }
 
@@ -6977,6 +7042,31 @@ static __exit void hardware_unsetup(void)
  */
 static int handle_pause(struct kvm_vcpu *vcpu)
 {
+	/*
+	 * SDM 39.6.3 PAUSE Instruction.
+	 *
+	 * SDM suggests, if VMEXIT caused by 'PAUSE-loop exiting', VMM should
+	 * disable 'PAUSE-loop exiting' so PAUSE can be executed in Enclave
+	 * again without further PAUSE-looping VMEXIT.
+	 *
+	 * SDM suggests, if VMEXIT caused by 'PAUSE exiting', VMM should disable
+	 * 'PAUSE exiting' so PAUSE can be executed in Enclave again without
+	 * further PAUSE VMEXIT.
+	 */
+	if (vmx_exit_from_enclave(vcpu)) {
+		u32 exec_ctl, secondary_exec_ctl;
+
+		exec_ctl = vmx_exec_control(to_vmx(vcpu));
+		exec_ctl &= ~CPU_BASED_PAUSE_EXITING;
+		vmcs_write32(CPU_BASED_VM_EXEC_CONTROL, exec_ctl);
+
+		secondary_exec_ctl = vmx_secondary_exec_control(to_vmx(vcpu));
+		secondary_exec_ctl &= ~SECONDARY_EXEC_PAUSE_LOOP_EXITING;
+		vmcs_set_secondary_exec_control(secondary_exec_ctl);
+
+		return 1;
+	}
+
 	if (ple_gap)
 		grow_ple_window(vcpu);
 
@@ -8876,6 +8966,17 @@ static int vmx_handle_exit(struct kvm_vcpu *vcpu)
 		return 0;
 	}
 
+	/* Bit 27 of exit_reason will be set if VMEXT is from SGX enclave. */
+	if (exit_reason & VMX_EXIT_REASON_FROM_ENCLAVE) {
+		/*
+		 * Need to clear bit 27 otherwise further check of calling
+		 * kvm_vmx_exit_handlers would fail. We rely on bit 4 of
+		 * GUEST_INTERRUPTIBILITY_INFO to determine whether VMEXIT
+		 * is from enclave in the future.
+		 */
+		exit_reason &= ~VMX_EXIT_REASON_FROM_ENCLAVE;
+	}
+
 	/*
 	 * Note:
 	 * Do not try to fix EXIT_REASON_EPT_MISCONFIG if it caused by
@@ -9768,25 +9869,6 @@ static int vmx_get_lpage_level(void)
 		return PT_PDPE_LEVEL;
 }
 
-static void vmcs_set_secondary_exec_control(u32 new_ctl)
-{
-	/*
-	 * These bits in the secondary execution controls field
-	 * are dynamic, the others are mostly based on the hypervisor
-	 * architecture and the guest's CPUID.  Do not touch the
-	 * dynamic bits.
-	 */
-	u32 mask =
-		SECONDARY_EXEC_SHADOW_VMCS |
-		SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE |
-		SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES;
-
-	u32 cur_ctl = vmcs_read32(SECONDARY_VM_EXEC_CONTROL);
-
-	vmcs_write32(SECONDARY_VM_EXEC_CONTROL,
-		     (new_ctl & ~mask) | (cur_ctl & mask));
-}
-
 /*
  * Generate MSR_IA32_VMX_CR{0,4}_FIXED1 according to CPUID. Only set bits
  * (indicating "allowed-1") if they are supported in the guest's CPUID.
-- 
2.11.0




[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux