[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.