Skip to content

Commit 90cfe14

Browse files
sean-jcbonzini
authored andcommitted
KVM: TDX: Add support for find pending IRQ in a protected local APIC
Add flag and hook to KVM's local APIC management to support determining whether or not a TDX guest has a pending IRQ. For TDX vCPUs, the virtual APIC page is owned by the TDX module and cannot be accessed by KVM. As a result, registers that are virtualized by the CPU, e.g. PPR, cannot be read or written by KVM. To deliver interrupts for TDX guests, KVM must send an IRQ to the CPU on the posted interrupt notification vector. And to determine if TDX vCPU has a pending interrupt, KVM must check if there is an outstanding notification. Return "no interrupt" in kvm_apic_has_interrupt() if the guest APIC is protected to short-circuit the various other flows that try to pull an IRQ out of the vAPIC, the only valid operation is querying _if_ an IRQ is pending, KVM can't do anything based on _which_ IRQ is pending. Intentionally omit sanity checks from other flows, e.g. PPR update, so as not to degrade non-TDX guests with unnecessary checks. A well-behaved KVM and userspace will never reach those flows for TDX guests, but reaching them is not fatal if something does go awry. For the TD exits not due to HLT TDCALL, skip checking RVI pending in tdx_protected_apic_has_interrupt(). Except for the guest being stupid (e.g., non-HLT TDCALL in an interrupt shadow), it's not even possible to have an interrupt in RVI that is fully unmasked. There is no any CPU flows that modify RVI in the middle of instruction execution. I.e. if RVI is non-zero, then either the interrupt has been pending since before the TD exit, or the instruction caused the TD exit is in an STI/SS shadow. KVM doesn't care about STI/SS shadows outside of the HALTED case. And if the interrupt was pending before TD exit, then it _must_ be blocked, otherwise the interrupt would have been serviced at the instruction boundary. For the HLT TDCALL case, it will be handled in a future patch when HLT TDCALL is supported. Signed-off-by: Sean Christopherson <seanjc@google.com> Signed-off-by: Isaku Yamahata <isaku.yamahata@intel.com> Signed-off-by: Binbin Wu <binbin.wu@linux.intel.com> Message-ID: <20250222014757.897978-2-binbin.wu@linux.intel.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
1 parent bb723be commit 90cfe14

File tree

8 files changed

+19
-0
lines changed

8 files changed

+19
-0
lines changed

arch/x86/include/asm/kvm-x86-ops.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ KVM_X86_OP_OPTIONAL(pi_start_assignment)
116116
KVM_X86_OP_OPTIONAL(apicv_pre_state_restore)
117117
KVM_X86_OP_OPTIONAL(apicv_post_state_restore)
118118
KVM_X86_OP_OPTIONAL_RET0(dy_apicv_has_pending_interrupt)
119+
KVM_X86_OP_OPTIONAL(protected_apic_has_interrupt)
119120
KVM_X86_OP_OPTIONAL(set_hv_timer)
120121
KVM_X86_OP_OPTIONAL(cancel_hv_timer)
121122
KVM_X86_OP(setup_mce)

arch/x86/include/asm/kvm_host.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1842,6 +1842,7 @@ struct kvm_x86_ops {
18421842
void (*apicv_pre_state_restore)(struct kvm_vcpu *vcpu);
18431843
void (*apicv_post_state_restore)(struct kvm_vcpu *vcpu);
18441844
bool (*dy_apicv_has_pending_interrupt)(struct kvm_vcpu *vcpu);
1845+
bool (*protected_apic_has_interrupt)(struct kvm_vcpu *vcpu);
18451846

18461847
int (*set_hv_timer)(struct kvm_vcpu *vcpu, u64 guest_deadline_tsc,
18471848
bool *expired);

arch/x86/kvm/irq.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ int kvm_cpu_has_interrupt(struct kvm_vcpu *v)
100100
if (kvm_cpu_has_extint(v))
101101
return 1;
102102

103+
if (lapic_in_kernel(v) && v->arch.apic->guest_apic_protected)
104+
return kvm_x86_call(protected_apic_has_interrupt)(v);
105+
103106
return kvm_apic_has_interrupt(v) != -1; /* LAPIC */
104107
}
105108
EXPORT_SYMBOL_GPL(kvm_cpu_has_interrupt);

arch/x86/kvm/lapic.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2967,6 +2967,9 @@ int kvm_apic_has_interrupt(struct kvm_vcpu *vcpu)
29672967
if (!kvm_apic_present(vcpu))
29682968
return -1;
29692969

2970+
if (apic->guest_apic_protected)
2971+
return -1;
2972+
29702973
__apic_update_ppr(apic, &ppr);
29712974
return apic_has_interrupt_for_ppr(apic, ppr);
29722975
}

arch/x86/kvm/lapic.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ struct kvm_lapic {
6565
bool sw_enabled;
6666
bool irr_pending;
6767
bool lvt0_in_nmi_mode;
68+
/* Select registers in the vAPIC cannot be read/written. */
69+
bool guest_apic_protected;
6870
/* Number of bits set in ISR. */
6971
s16 isr_count;
7072
/* The highest vector set in ISR; if -1 - invalid, must scan ISR. */

arch/x86/kvm/vmx/main.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ static __init int vt_hardware_setup(void)
6262
vt_x86_ops.set_external_spte = tdx_sept_set_private_spte;
6363
vt_x86_ops.free_external_spt = tdx_sept_free_private_spt;
6464
vt_x86_ops.remove_external_spte = tdx_sept_remove_private_spte;
65+
vt_x86_ops.protected_apic_has_interrupt = tdx_protected_apic_has_interrupt;
6566
}
6667

6768
return 0;

arch/x86/kvm/vmx/tdx.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,7 @@ int tdx_vcpu_create(struct kvm_vcpu *vcpu)
649649
return -EINVAL;
650650

651651
fpstate_set_confidential(&vcpu->arch.guest_fpu);
652+
vcpu->arch.apic->guest_apic_protected = true;
652653

653654
vcpu->arch.efer = EFER_SCE | EFER_LME | EFER_LMA | EFER_NX;
654655

@@ -695,6 +696,11 @@ void tdx_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
695696
local_irq_enable();
696697
}
697698

699+
bool tdx_protected_apic_has_interrupt(struct kvm_vcpu *vcpu)
700+
{
701+
return pi_has_pending_interrupt(vcpu);
702+
}
703+
698704
/*
699705
* Compared to vmx_prepare_switch_to_guest(), there is not much to do
700706
* as SEAMCALL/SEAMRET calls take care of most of save and restore.

arch/x86/kvm/vmx/x86_ops.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ int tdx_vcpu_pre_run(struct kvm_vcpu *vcpu);
135135
fastpath_t tdx_vcpu_run(struct kvm_vcpu *vcpu, bool force_immediate_exit);
136136
void tdx_prepare_switch_to_guest(struct kvm_vcpu *vcpu);
137137
void tdx_vcpu_put(struct kvm_vcpu *vcpu);
138+
bool tdx_protected_apic_has_interrupt(struct kvm_vcpu *vcpu);
138139
int tdx_handle_exit(struct kvm_vcpu *vcpu,
139140
enum exit_fastpath_completion fastpath);
140141
void tdx_get_exit_info(struct kvm_vcpu *vcpu, u32 *reason,
@@ -172,6 +173,7 @@ static inline fastpath_t tdx_vcpu_run(struct kvm_vcpu *vcpu, bool force_immediat
172173
}
173174
static inline void tdx_prepare_switch_to_guest(struct kvm_vcpu *vcpu) {}
174175
static inline void tdx_vcpu_put(struct kvm_vcpu *vcpu) {}
176+
static inline bool tdx_protected_apic_has_interrupt(struct kvm_vcpu *vcpu) { return false; }
175177
static inline int tdx_handle_exit(struct kvm_vcpu *vcpu,
176178
enum exit_fastpath_completion fastpath) { return 0; }
177179
static inline void tdx_get_exit_info(struct kvm_vcpu *vcpu, u32 *reason, u64 *info1,

0 commit comments

Comments
 (0)