[x86][hypervisor] Reinstate EPT invalidation
We used to invalidate EPT caches when launching a guest due to the use
of APIC addresses, which was removed when we moved to X2APIC. However,
we still need to invalidate the cache to cover the case where a guest is
destroyed immediately before a new guest with the same EPTP is started.
This was occurring randomly when running hypervisor-test in a loop and
causing triple faults.
PD-73 #done
Change-Id: Id250f52d7e3023daafcba78b0966667b5f9c78ad
diff --git a/kernel/arch/x86/hypervisor/vcpu.cpp b/kernel/arch/x86/hypervisor/vcpu.cpp
index 7b2522d..1b6bb6b 100644
--- a/kernel/arch/x86/hypervisor/vcpu.cpp
+++ b/kernel/arch/x86/hypervisor/vcpu.cpp
@@ -29,6 +29,19 @@
static constexpr uint32_t kInterruptTypeSoftwareException = 6u << 8;
static constexpr uint16_t kBaseProcessorVpid = 1;
+static zx_status_t invept(InvEpt invalidation, uint64_t eptp) {
+ uint8_t err;
+ uint64_t descriptor[] = { eptp, 0 };
+
+ __asm__ volatile(
+ "invept %[descriptor], %[invalidation];" VMX_ERR_CHECK(err)
+ : [err] "=r"(err)
+ : [descriptor] "m"(descriptor), [invalidation] "r"(invalidation)
+ : "cc");
+
+ return err ? ZX_ERR_INTERNAL : ZX_OK;
+}
+
static zx_status_t vmptrld(paddr_t pa) {
uint8_t err;
@@ -420,6 +433,11 @@
const auto eptp = ept_pointer(pml4_address);
vmcs.Write(VmcsField64::EPT_POINTER, eptp);
+ // From Volume 3, Section 28.3.3.4: Software can use an INVEPT with type all
+ // ALL_CONTEXT to prevent undesired retention of cached EPT information. Here,
+ // we only care about invalidating information associated with this EPTP.
+ invept(InvEpt::SINGLE_CONTEXT, eptp);
+
// Setup MSR handling.
vmcs.Write(VmcsField64::MSR_BITMAPS_ADDRESS, msr_bitmaps_address);
diff --git a/kernel/arch/x86/hypervisor/vcpu_priv.h b/kernel/arch/x86/hypervisor/vcpu_priv.h
index 5d9607c..51a737f 100644
--- a/kernel/arch/x86/hypervisor/vcpu_priv.h
+++ b/kernel/arch/x86/hypervisor/vcpu_priv.h
@@ -200,6 +200,12 @@
HOST_RIP = 0x6c16,
};
+// INVEPT invalidation types.
+enum class InvEpt : uint64_t {
+ SINGLE_CONTEXT = 1,
+ ALL_CONTEXT = 2,
+};
+
// clang-format on
// Loads a VMCS within a given scope.