[RFC PATCH v5 092/104] KVM: TDX: Handle TDX PV HLT hypercall

From: isaku . yamahata
Date: Fri Mar 04 2022 - 14:58:48 EST


From: Isaku Yamahata <isaku.yamahata@xxxxxxxxx>

Wire up TDX PV HLT hypercall to the KVM backend function.

When the guest issues HLT, the hypercall instruction can be the right after
CLI instruction. Atomically unmask virtual interrupt and issue HLT
hypercall. The virtual interrupts can arrive right after CLI instruction
before switching back to VMM. In such a case, the VMM should return to the
guest without losing the interrupt. Check if interrupts arrived before the
TDX module switching to VMM. And return to the guest in such cases.

Signed-off-by: Isaku Yamahata <isaku.yamahata@xxxxxxxxx>
---
arch/x86/kvm/vmx/tdx.c | 45 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 44 insertions(+), 1 deletion(-)

diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
index f7c9170d596a..b0dcc2421649 100644
--- a/arch/x86/kvm/vmx/tdx.c
+++ b/arch/x86/kvm/vmx/tdx.c
@@ -917,6 +917,48 @@ static int tdx_emulate_cpuid(struct kvm_vcpu *vcpu)
return 1;
}

+static int tdx_emulate_hlt(struct kvm_vcpu *vcpu)
+{
+ bool interrupt_disabled = tdvmcall_p1_read(vcpu);
+ union tdx_vcpu_state_details details;
+
+ tdvmcall_set_return_code(vcpu, TDG_VP_VMCALL_SUCCESS);
+
+ if (!interrupt_disabled) {
+ /*
+ * Virtual interrupt can arrive after TDG.VM.VMCALL<HLT> during
+ * the TDX module executing. On the other hand, KVM doesn't
+ * know if vcpu was executing in the guest TD or the TDX module.
+ *
+ * CPU mode transition:
+ * TDG.VP.VMCALL<HLT> (SEAM VMX non-root mode) ->
+ * the TDX module (SEAM VMX root mode) ->
+ * KVM (Legacy VMX root mode)
+ *
+ * If virtual interrupt arrives to this vcpu
+ * - In the guest TD executing:
+ * KVM can handle it in the same way to the VMX case.
+ * - During the TDX module executing:
+ * The TDX modules switches to KVM with TDG.VM.VMCALL<HLT>
+ * exit reason. KVM thinks the guest was running. So KVM
+ * vcpu wake up logic doesn't kick in. Check if virtual
+ * interrupt is pending and resume vcpu without blocking vcpu.
+ * - KVM executing:
+ * The existing logic wakes up the target vcpu on injecting
+ * virtual interrupt in the same way to the VMX case.
+ *
+ * Check if the interrupt is already pending. If yes, resume
+ * vcpu from guest HLT without emulating hlt instruction.
+ */
+ details.full = td_state_non_arch_read64(
+ to_tdx(vcpu), TD_VCPU_STATE_DETAILS_NON_ARCH);
+ if (details.vmxip)
+ return 1;
+ }
+
+ return kvm_emulate_halt_noskip(vcpu);
+}
+
static int handle_tdvmcall(struct kvm_vcpu *vcpu)
{
struct vcpu_tdx *tdx = to_tdx(vcpu);
@@ -930,7 +972,8 @@ static int handle_tdvmcall(struct kvm_vcpu *vcpu)
switch (tdvmcall_exit_reason(vcpu)) {
case EXIT_REASON_CPUID:
return tdx_emulate_cpuid(vcpu);
-
+ case EXIT_REASON_HLT:
+ return tdx_emulate_hlt(vcpu);
default:
break;
}
--
2.25.1