Skip to content

Commit 5cf7239

Browse files
yamahatabonzini
authored andcommitted
KVM: TDX: Handle TDX PV HLT hypercall
Handle TDX PV HLT hypercall and the interrupt status due to it. TDX guest status is protected, KVM can't get the interrupt status of TDX guest and it assumes interrupt is always allowed unless TDX guest calls TDVMCALL with HLT, which passes the interrupt blocked flag. If the guest halted with interrupt enabled, also query pending RVI by checking bit 0 of TD_VCPU_STATE_DETAILS_NON_ARCH field via a seamcall. Update vt_interrupt_allowed() for TDX based on interrupt blocked flag passed by HLT TDVMCALL. Do not wakeup TD vCPU if interrupt is blocked for VT-d PI. For NMIs, KVM cannot determine the NMI blocking status for TDX guests, so KVM always assumes NMIs are not blocked. In the unlikely scenario where a guest invokes the PV HLT hypercall within an NMI handler, this could result in a spurious wakeup. The guest should implement the PV HLT hypercall within a loop if it truly requires no interruptions, since NMI could be unblocked by an IRET due to an exception occurring before the PV HLT is executed in the NMI handler. Suggested-by: Sean Christopherson <seanjc@google.com> Signed-off-by: Isaku Yamahata <isaku.yamahata@intel.com> Co-developed-by: Binbin Wu <binbin.wu@linux.intel.com> Signed-off-by: Binbin Wu <binbin.wu@linux.intel.com> Message-ID: <20250227012021.1778144-7-binbin.wu@linux.intel.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
1 parent 3bf31b5 commit 5cf7239

File tree

5 files changed

+56
-6
lines changed

5 files changed

+56
-6
lines changed

arch/x86/kvm/vmx/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ static void vt_cancel_injection(struct kvm_vcpu *vcpu)
418418
static int vt_interrupt_allowed(struct kvm_vcpu *vcpu, bool for_injection)
419419
{
420420
if (is_td_vcpu(vcpu))
421-
return true;
421+
return tdx_interrupt_allowed(vcpu);
422422

423423
return vmx_interrupt_allowed(vcpu, for_injection);
424424
}

arch/x86/kvm/vmx/posted_intr.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,8 @@ void vmx_vcpu_pi_put(struct kvm_vcpu *vcpu)
203203
return;
204204

205205
if (kvm_vcpu_is_blocking(vcpu) &&
206-
(is_td_vcpu(vcpu) || !vmx_interrupt_blocked(vcpu)))
206+
((is_td_vcpu(vcpu) && tdx_interrupt_allowed(vcpu)) ||
207+
(!is_td_vcpu(vcpu) && !vmx_interrupt_blocked(vcpu))))
207208
pi_enable_wakeup_handler(vcpu);
208209

209210
/*

arch/x86/kvm/vmx/tdx.c

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -726,9 +726,39 @@ void tdx_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
726726
local_irq_enable();
727727
}
728728

729+
bool tdx_interrupt_allowed(struct kvm_vcpu *vcpu)
730+
{
731+
/*
732+
* KVM can't get the interrupt status of TDX guest and it assumes
733+
* interrupt is always allowed unless TDX guest calls TDVMCALL with HLT,
734+
* which passes the interrupt blocked flag.
735+
*/
736+
return vmx_get_exit_reason(vcpu).basic != EXIT_REASON_HLT ||
737+
!to_tdx(vcpu)->vp_enter_args.r12;
738+
}
739+
729740
bool tdx_protected_apic_has_interrupt(struct kvm_vcpu *vcpu)
730741
{
731-
return pi_has_pending_interrupt(vcpu);
742+
u64 vcpu_state_details;
743+
744+
if (pi_has_pending_interrupt(vcpu))
745+
return true;
746+
747+
/*
748+
* Only check RVI pending for HALTED case with IRQ enabled.
749+
* For non-HLT cases, KVM doesn't care about STI/SS shadows. And if the
750+
* interrupt was pending before TD exit, then it _must_ be blocked,
751+
* otherwise the interrupt would have been serviced at the instruction
752+
* boundary.
753+
*/
754+
if (vmx_get_exit_reason(vcpu).basic != EXIT_REASON_HLT ||
755+
to_tdx(vcpu)->vp_enter_args.r12)
756+
return false;
757+
758+
vcpu_state_details =
759+
td_state_non_arch_read64(to_tdx(vcpu), TD_VCPU_STATE_DETAILS_NON_ARCH);
760+
761+
return tdx_vcpu_state_details_intr_pending(vcpu_state_details);
732762
}
733763

734764
/*
@@ -845,6 +875,7 @@ static __always_inline u32 tdcall_to_vmx_exit_reason(struct kvm_vcpu *vcpu)
845875
{
846876
switch (tdvmcall_leaf(vcpu)) {
847877
case EXIT_REASON_CPUID:
878+
case EXIT_REASON_HLT:
848879
case EXIT_REASON_IO_INSTRUCTION:
849880
return tdvmcall_leaf(vcpu);
850881
case EXIT_REASON_EPT_VIOLATION:
@@ -1129,9 +1160,7 @@ static int tdx_complete_vmcall_map_gpa(struct kvm_vcpu *vcpu)
11291160
/*
11301161
* Stop processing the remaining part if there is a pending interrupt,
11311162
* which could be qualified to deliver. Skip checking pending RVI for
1132-
* TDVMCALL_MAP_GPA.
1133-
* TODO: Add a comment to link the reason when the target function is
1134-
* implemented.
1163+
* TDVMCALL_MAP_GPA, see comments in tdx_protected_apic_has_interrupt().
11351164
*/
11361165
if (kvm_vcpu_has_events(vcpu)) {
11371166
tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_RETRY);
@@ -1934,6 +1963,8 @@ int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath)
19341963
return 1;
19351964
case EXIT_REASON_CPUID:
19361965
return tdx_emulate_cpuid(vcpu);
1966+
case EXIT_REASON_HLT:
1967+
return kvm_emulate_halt_noskip(vcpu);
19371968
case EXIT_REASON_TDCALL:
19381969
return handle_tdvmcall(vcpu);
19391970
case EXIT_REASON_VMCALL:

arch/x86/kvm/vmx/tdx.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ static __always_inline void tdvps_vmcs_check(u32 field, u8 bits)
123123
}
124124

125125
static __always_inline void tdvps_management_check(u64 field, u8 bits) {}
126+
static __always_inline void tdvps_state_non_arch_check(u64 field, u8 bits) {}
126127

127128
#define TDX_BUILD_TDVPS_ACCESSORS(bits, uclass, lclass) \
128129
static __always_inline u##bits td_##lclass##_read##bits(struct vcpu_tdx *tdx, \
@@ -170,11 +171,15 @@ static __always_inline void td_##lclass##_clearbit##bits(struct vcpu_tdx *tdx, \
170171
tdh_vp_wr_failed(tdx, #uclass, " &= ~", field, bit, err);\
171172
}
172173

174+
175+
bool tdx_interrupt_allowed(struct kvm_vcpu *vcpu);
176+
173177
TDX_BUILD_TDVPS_ACCESSORS(16, VMCS, vmcs);
174178
TDX_BUILD_TDVPS_ACCESSORS(32, VMCS, vmcs);
175179
TDX_BUILD_TDVPS_ACCESSORS(64, VMCS, vmcs);
176180

177181
TDX_BUILD_TDVPS_ACCESSORS(8, MANAGEMENT, management);
182+
TDX_BUILD_TDVPS_ACCESSORS(64, STATE_NON_ARCH, state_non_arch);
178183

179184
#else
180185
static inline int tdx_bringup(void) { return 0; }
@@ -190,6 +195,8 @@ struct vcpu_tdx {
190195
struct kvm_vcpu vcpu;
191196
};
192197

198+
static inline bool tdx_interrupt_allowed(struct kvm_vcpu *vcpu) { return false; }
199+
193200
#endif
194201

195202
#endif

arch/x86/kvm/vmx/tdx_arch.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,17 @@ enum tdx_tdcs_execution_control {
3737
TD_TDCS_EXEC_TSC_MULTIPLIER = 11,
3838
};
3939

40+
enum tdx_vcpu_guest_other_state {
41+
TD_VCPU_STATE_DETAILS_NON_ARCH = 0x100,
42+
};
43+
44+
#define TDX_VCPU_STATE_DETAILS_INTR_PENDING BIT_ULL(0)
45+
46+
static inline bool tdx_vcpu_state_details_intr_pending(u64 vcpu_state_details)
47+
{
48+
return !!(vcpu_state_details & TDX_VCPU_STATE_DETAILS_INTR_PENDING);
49+
}
50+
4051
/* @field is any of enum tdx_tdcs_execution_control */
4152
#define TDCS_EXEC(field) BUILD_TDX_FIELD(TD_CLASS_EXECUTION_CONTROLS, (field))
4253

0 commit comments

Comments
 (0)