Skip to content

Commit 24c1291

Browse files
yamahatabonzini
authored andcommitted
KVM: TDX: Implement non-NMI interrupt injection
Implement non-NMI interrupt injection for TDX via posted interrupt. As CPU state is protected and APICv is enabled for the TDX guest, TDX supports non-NMI interrupt injection only by posted interrupt. Posted interrupt descriptors (PIDs) are allocated in shared memory, KVM can update them directly. If target vCPU is in non-root mode, send posted interrupt notification to the vCPU and hardware will sync PIR to vIRR atomically. Otherwise, kick it to pick up the interrupt from PID. To post pending interrupts in the PID, KVM can generate a self-IPI with notification vector prior to TD entry. Since the guest status of TD vCPU is protected, assume interrupt is always allowed. Ignore the code path for event injection mechanism or LAPIC emulation for TDX. 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> Reviewed-by: Paolo Bonzini <pbonzini@redhat.com> Message-ID: <20250222014757.897978-5-binbin.wu@linux.intel.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
1 parent 254e5dc commit 24c1291

File tree

6 files changed

+117
-19
lines changed

6 files changed

+117
-19
lines changed

arch/x86/kvm/vmx/main.c

Lines changed: 85 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,34 @@ static int vt_handle_exit(struct kvm_vcpu *vcpu,
191191
return vmx_handle_exit(vcpu, fastpath);
192192
}
193193

194+
static void vt_apicv_pre_state_restore(struct kvm_vcpu *vcpu)
195+
{
196+
struct pi_desc *pi = vcpu_to_pi_desc(vcpu);
197+
198+
pi_clear_on(pi);
199+
memset(pi->pir, 0, sizeof(pi->pir));
200+
}
201+
202+
static int vt_sync_pir_to_irr(struct kvm_vcpu *vcpu)
203+
{
204+
if (is_td_vcpu(vcpu))
205+
return -1;
206+
207+
return vmx_sync_pir_to_irr(vcpu);
208+
}
209+
210+
static void vt_deliver_interrupt(struct kvm_lapic *apic, int delivery_mode,
211+
int trig_mode, int vector)
212+
{
213+
if (is_td_vcpu(apic->vcpu)) {
214+
tdx_deliver_interrupt(apic, delivery_mode, trig_mode,
215+
vector);
216+
return;
217+
}
218+
219+
vmx_deliver_interrupt(apic, delivery_mode, trig_mode, vector);
220+
}
221+
194222
static void vt_flush_tlb_all(struct kvm_vcpu *vcpu)
195223
{
196224
if (is_td_vcpu(vcpu)) {
@@ -238,6 +266,54 @@ static void vt_load_mmu_pgd(struct kvm_vcpu *vcpu, hpa_t root_hpa,
238266
vmx_load_mmu_pgd(vcpu, root_hpa, pgd_level);
239267
}
240268

269+
static void vt_set_interrupt_shadow(struct kvm_vcpu *vcpu, int mask)
270+
{
271+
if (is_td_vcpu(vcpu))
272+
return;
273+
274+
vmx_set_interrupt_shadow(vcpu, mask);
275+
}
276+
277+
static u32 vt_get_interrupt_shadow(struct kvm_vcpu *vcpu)
278+
{
279+
if (is_td_vcpu(vcpu))
280+
return 0;
281+
282+
return vmx_get_interrupt_shadow(vcpu);
283+
}
284+
285+
static void vt_inject_irq(struct kvm_vcpu *vcpu, bool reinjected)
286+
{
287+
if (is_td_vcpu(vcpu))
288+
return;
289+
290+
vmx_inject_irq(vcpu, reinjected);
291+
}
292+
293+
static void vt_cancel_injection(struct kvm_vcpu *vcpu)
294+
{
295+
if (is_td_vcpu(vcpu))
296+
return;
297+
298+
vmx_cancel_injection(vcpu);
299+
}
300+
301+
static int vt_interrupt_allowed(struct kvm_vcpu *vcpu, bool for_injection)
302+
{
303+
if (is_td_vcpu(vcpu))
304+
return true;
305+
306+
return vmx_interrupt_allowed(vcpu, for_injection);
307+
}
308+
309+
static void vt_enable_irq_window(struct kvm_vcpu *vcpu)
310+
{
311+
if (is_td_vcpu(vcpu))
312+
return;
313+
314+
vmx_enable_irq_window(vcpu);
315+
}
316+
241317
static void vt_get_entry_info(struct kvm_vcpu *vcpu, u32 *intr_info, u32 *error_code)
242318
{
243319
*intr_info = 0;
@@ -359,31 +435,31 @@ struct kvm_x86_ops vt_x86_ops __initdata = {
359435
.handle_exit = vt_handle_exit,
360436
.skip_emulated_instruction = vmx_skip_emulated_instruction,
361437
.update_emulated_instruction = vmx_update_emulated_instruction,
362-
.set_interrupt_shadow = vmx_set_interrupt_shadow,
363-
.get_interrupt_shadow = vmx_get_interrupt_shadow,
438+
.set_interrupt_shadow = vt_set_interrupt_shadow,
439+
.get_interrupt_shadow = vt_get_interrupt_shadow,
364440
.patch_hypercall = vmx_patch_hypercall,
365-
.inject_irq = vmx_inject_irq,
441+
.inject_irq = vt_inject_irq,
366442
.inject_nmi = vmx_inject_nmi,
367443
.inject_exception = vmx_inject_exception,
368-
.cancel_injection = vmx_cancel_injection,
369-
.interrupt_allowed = vmx_interrupt_allowed,
444+
.cancel_injection = vt_cancel_injection,
445+
.interrupt_allowed = vt_interrupt_allowed,
370446
.nmi_allowed = vmx_nmi_allowed,
371447
.get_nmi_mask = vmx_get_nmi_mask,
372448
.set_nmi_mask = vmx_set_nmi_mask,
373449
.enable_nmi_window = vmx_enable_nmi_window,
374-
.enable_irq_window = vmx_enable_irq_window,
450+
.enable_irq_window = vt_enable_irq_window,
375451
.update_cr8_intercept = vmx_update_cr8_intercept,
376452

377453
.x2apic_icr_is_split = false,
378454
.set_virtual_apic_mode = vmx_set_virtual_apic_mode,
379455
.set_apic_access_page_addr = vmx_set_apic_access_page_addr,
380456
.refresh_apicv_exec_ctrl = vmx_refresh_apicv_exec_ctrl,
381457
.load_eoi_exitmap = vmx_load_eoi_exitmap,
382-
.apicv_pre_state_restore = vmx_apicv_pre_state_restore,
458+
.apicv_pre_state_restore = vt_apicv_pre_state_restore,
383459
.required_apicv_inhibits = VMX_REQUIRED_APICV_INHIBITS,
384460
.hwapic_isr_update = vmx_hwapic_isr_update,
385-
.sync_pir_to_irr = vmx_sync_pir_to_irr,
386-
.deliver_interrupt = vmx_deliver_interrupt,
461+
.sync_pir_to_irr = vt_sync_pir_to_irr,
462+
.deliver_interrupt = vt_deliver_interrupt,
387463
.dy_apicv_has_pending_interrupt = pi_has_pending_interrupt,
388464

389465
.set_tss_addr = vmx_set_tss_addr,

arch/x86/kvm/vmx/posted_intr.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ static DEFINE_PER_CPU(struct list_head, wakeup_vcpus_on_cpu);
3232
*/
3333
static DEFINE_PER_CPU(raw_spinlock_t, wakeup_vcpus_on_cpu_lock);
3434

35-
static inline struct pi_desc *vcpu_to_pi_desc(struct kvm_vcpu *vcpu)
35+
struct pi_desc *vcpu_to_pi_desc(struct kvm_vcpu *vcpu)
3636
{
3737
return &(to_vt(vcpu)->pi_desc);
3838
}

arch/x86/kvm/vmx/posted_intr.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#include <linux/bitmap.h>
66
#include <asm/posted_intr.h>
77

8+
struct pi_desc *vcpu_to_pi_desc(struct kvm_vcpu *vcpu);
9+
810
void vmx_vcpu_pi_load(struct kvm_vcpu *vcpu, int cpu);
911
void vmx_vcpu_pi_put(struct kvm_vcpu *vcpu);
1012
void pi_wakeup_handler(void);

arch/x86/kvm/vmx/tdx.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,9 @@ int tdx_vcpu_create(struct kvm_vcpu *vcpu)
671671
if ((kvm_tdx->xfam & XFEATURE_MASK_XTILE) == XFEATURE_MASK_XTILE)
672672
vcpu->arch.xfd_no_write_intercept = true;
673673

674+
tdx->vt.pi_desc.nv = POSTED_INTR_VECTOR;
675+
__pi_set_sn(&tdx->vt.pi_desc);
676+
674677
tdx->state = VCPU_TD_STATE_UNINITIALIZED;
675678

676679
return 0;
@@ -680,6 +683,7 @@ void tdx_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
680683
{
681684
struct vcpu_tdx *tdx = to_tdx(vcpu);
682685

686+
vmx_vcpu_pi_load(vcpu, cpu);
683687
if (vcpu->cpu == cpu || !is_hkid_assigned(to_kvm_tdx(vcpu->kvm)))
684688
return;
685689

@@ -965,6 +969,9 @@ fastpath_t tdx_vcpu_run(struct kvm_vcpu *vcpu, bool force_immediate_exit)
965969

966970
trace_kvm_entry(vcpu, force_immediate_exit);
967971

972+
if (pi_test_on(&vt->pi_desc))
973+
apic->send_IPI_self(POSTED_INTR_VECTOR);
974+
968975
tdx_vcpu_enter_exit(vcpu);
969976

970977
if (vt->host_debugctlmsr & ~TDX_DEBUGCTL_PRESERVED)
@@ -1628,6 +1635,18 @@ int tdx_sept_remove_private_spte(struct kvm *kvm, gfn_t gfn,
16281635
return tdx_sept_drop_private_spte(kvm, gfn, level, page);
16291636
}
16301637

1638+
void tdx_deliver_interrupt(struct kvm_lapic *apic, int delivery_mode,
1639+
int trig_mode, int vector)
1640+
{
1641+
struct kvm_vcpu *vcpu = apic->vcpu;
1642+
struct vcpu_tdx *tdx = to_tdx(vcpu);
1643+
1644+
/* TDX supports only posted interrupt. No lapic emulation. */
1645+
__vmx_deliver_posted_interrupt(vcpu, &tdx->vt.pi_desc, vector);
1646+
1647+
trace_kvm_apicv_accept_irq(vcpu->vcpu_id, delivery_mode, trig_mode, vector);
1648+
}
1649+
16311650
int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath)
16321651
{
16331652
struct vcpu_tdx *tdx = to_tdx(vcpu);
@@ -2571,6 +2590,10 @@ static int tdx_vcpu_init(struct kvm_vcpu *vcpu, struct kvm_tdx_cmd *cmd)
25712590
if (ret)
25722591
return ret;
25732592

2593+
td_vmcs_write16(tdx, POSTED_INTR_NV, POSTED_INTR_VECTOR);
2594+
td_vmcs_write64(tdx, POSTED_INTR_DESC_ADDR, __pa(&tdx->vt.pi_desc));
2595+
td_vmcs_setbit32(tdx, PIN_BASED_VM_EXEC_CONTROL, PIN_BASED_POSTED_INTR);
2596+
25742597
tdx->state = VCPU_TD_STATE_INITIALIZED;
25752598

25762599
return 0;

arch/x86/kvm/vmx/vmx.c

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6904,14 +6904,6 @@ void vmx_load_eoi_exitmap(struct kvm_vcpu *vcpu, u64 *eoi_exit_bitmap)
69046904
vmcs_write64(EOI_EXIT_BITMAP3, eoi_exit_bitmap[3]);
69056905
}
69066906

6907-
void vmx_apicv_pre_state_restore(struct kvm_vcpu *vcpu)
6908-
{
6909-
struct vcpu_vt *vt = to_vt(vcpu);
6910-
6911-
pi_clear_on(&vt->pi_desc);
6912-
memset(vt->pi_desc.pir, 0, sizeof(vt->pi_desc.pir));
6913-
}
6914-
69156907
void vmx_do_interrupt_irqoff(unsigned long entry);
69166908
void vmx_do_nmi_irqoff(void);
69176909

arch/x86/kvm/vmx/x86_ops.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ int vmx_check_intercept(struct kvm_vcpu *vcpu,
4646
bool vmx_apic_init_signal_blocked(struct kvm_vcpu *vcpu);
4747
void vmx_migrate_timers(struct kvm_vcpu *vcpu);
4848
void vmx_set_virtual_apic_mode(struct kvm_vcpu *vcpu);
49-
void vmx_apicv_pre_state_restore(struct kvm_vcpu *vcpu);
5049
void vmx_hwapic_isr_update(struct kvm_vcpu *vcpu, int max_isr);
5150
int vmx_sync_pir_to_irr(struct kvm_vcpu *vcpu);
5251
void vmx_deliver_interrupt(struct kvm_lapic *apic, int delivery_mode,
@@ -138,6 +137,9 @@ void tdx_vcpu_put(struct kvm_vcpu *vcpu);
138137
bool tdx_protected_apic_has_interrupt(struct kvm_vcpu *vcpu);
139138
int tdx_handle_exit(struct kvm_vcpu *vcpu,
140139
enum exit_fastpath_completion fastpath);
140+
141+
void tdx_deliver_interrupt(struct kvm_lapic *apic, int delivery_mode,
142+
int trig_mode, int vector);
141143
void tdx_get_exit_info(struct kvm_vcpu *vcpu, u32 *reason,
142144
u64 *info1, u64 *info2, u32 *intr_info, u32 *error_code);
143145

@@ -176,6 +178,9 @@ static inline void tdx_vcpu_put(struct kvm_vcpu *vcpu) {}
176178
static inline bool tdx_protected_apic_has_interrupt(struct kvm_vcpu *vcpu) { return false; }
177179
static inline int tdx_handle_exit(struct kvm_vcpu *vcpu,
178180
enum exit_fastpath_completion fastpath) { return 0; }
181+
182+
static inline void tdx_deliver_interrupt(struct kvm_lapic *apic, int delivery_mode,
183+
int trig_mode, int vector) {}
179184
static inline void tdx_get_exit_info(struct kvm_vcpu *vcpu, u32 *reason, u64 *info1,
180185
u64 *info2, u32 *intr_info, u32 *error_code) {}
181186

0 commit comments

Comments
 (0)