Skip to content

virt_mshv_vtl: Cleanup cross-vtl interrupt checking #1380

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 7 additions & 11 deletions openhcl/virt_mshv_vtl/src/processor/hardware_cvm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1745,14 +1745,8 @@ impl<B: HardwareIsolatedBacking> UhProcessor<'_, B> {
}

/// Handle checking for cross-VTL interrupts, preempting VTL 0, and setting
/// VINA when appropriate. The `is_interrupt_pending` function should return
/// true if an interrupt of appropriate priority, or an NMI, is pending for
/// the given VTL. The boolean specifies whether RFLAGS.IF should be checked.
/// Returns true if interrupt reprocessing is required.
pub(crate) fn cvm_handle_cross_vtl_interrupts(
&mut self,
is_interrupt_pending: impl Fn(&mut Self, GuestVtl, bool) -> bool,
) -> Result<bool, UhRunVpError> {
/// VINA when appropriate. Returns true if interrupt reprocessing is required.
fn cvm_handle_cross_vtl_interrupts(&mut self, dev: &impl CpuIo) -> Result<bool, UhRunVpError> {
let cvm_state = self.backing.cvm_state();

// If VTL1 is not yet enabled, there is nothing to do.
Expand All @@ -1761,7 +1755,8 @@ impl<B: HardwareIsolatedBacking> UhProcessor<'_, B> {
}

// Check for VTL preemption - which ignores RFLAGS.IF
if cvm_state.exit_vtl == GuestVtl::Vtl0 && is_interrupt_pending(self, GuestVtl::Vtl1, false)
if cvm_state.exit_vtl == GuestVtl::Vtl0
&& B::is_interrupt_pending(self, GuestVtl::Vtl1, false, dev)
{
self.raise_vtl(GuestVtl::Vtl0, GuestVtl::Vtl1, HvVtlEntryReason::INTERRUPT);
}
Expand All @@ -1770,7 +1765,7 @@ impl<B: HardwareIsolatedBacking> UhProcessor<'_, B> {

// Check for VINA
if self.backing.cvm_state().exit_vtl == GuestVtl::Vtl1
&& is_interrupt_pending(self, GuestVtl::Vtl0, true)
&& B::is_interrupt_pending(self, GuestVtl::Vtl0, true, dev)
{
let hv = &mut self.backing.cvm_state_mut().hv[GuestVtl::Vtl1];
let vina = hv.synic.vina();
Expand Down Expand Up @@ -2342,7 +2337,8 @@ impl<B: HardwareIsolatedBacking> UhProcessor<'_, B> {
}
*first_scan_irr = false;

B::handle_cross_vtl_interrupts(self, dev).map_err(VpHaltReason::InvalidVmState)
self.cvm_handle_cross_vtl_interrupts(dev)
.map_err(VpHaltReason::InvalidVmState)
}

fn update_synic(&mut self, vtl: GuestVtl, untrusted_synic: bool) {
Expand Down
10 changes: 6 additions & 4 deletions openhcl/virt_mshv_vtl/src/processor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -446,12 +446,14 @@ trait HardwareIsolatedBacking: Backing {
/// Vector of the event that is pending injection into the guest state, if
/// valid.
fn pending_event_vector(this: &UhProcessor<'_, Self>, vtl: GuestVtl) -> Option<u8>;
/// Checks interrupt status for all VTLs, and handles cross VTL interrupt preemption and VINA.
/// Returns whether interrupt reprocessing is required.
fn handle_cross_vtl_interrupts(
/// Check if an interrupt of appropriate priority, or an NMI, is pending for
/// the given VTL. `check_rflags` specifies whether RFLAGS.IF should be checked.
fn is_interrupt_pending(
this: &mut UhProcessor<'_, Self>,
vtl: GuestVtl,
check_rflags: bool,
dev: &impl CpuIo,
) -> Result<bool, UhRunVpError>;
) -> bool;
/// Sets the pending exception for the guest state.
///
/// Note that this will overwrite any existing pending exception. It will
Expand Down
66 changes: 33 additions & 33 deletions openhcl/virt_mshv_vtl/src/processor/snp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,44 +320,44 @@ impl HardwareIsolatedBacking for SnpBacked {
.expect("setting intercept control succeeds");
}

fn handle_cross_vtl_interrupts(
fn is_interrupt_pending(
this: &mut UhProcessor<'_, Self>,
vtl: GuestVtl,
check_rflags: bool,
dev: &impl CpuIo,
) -> Result<bool, UhRunVpError> {
this.cvm_handle_cross_vtl_interrupts(|this, vtl, check_rflags| {
let vmsa = this.runner.vmsa_mut(vtl);
if vmsa.event_inject().valid()
&& vmsa.event_inject().interruption_type() == x86defs::snp::SEV_INTR_TYPE_NMI
{
return true;
}
) -> bool {
let vmsa = this.runner.vmsa_mut(vtl);
if vmsa.event_inject().valid()
&& vmsa.event_inject().interruption_type() == x86defs::snp::SEV_INTR_TYPE_NMI
{
return true;
}

let vmsa_priority = vmsa.v_intr_cntrl().priority() as u32;
let lapic = &mut this.backing.cvm.lapics[vtl].lapic;
let ppr = lapic
.access(&mut SnpApicClient {
partition: this.partition,
vmsa,
dev,
vmtime: &this.vmtime,
vtl,
})
.get_ppr();
let ppr_priority = ppr >> 4;
if vmsa_priority <= ppr_priority {
return false;
}
let vmsa_priority = vmsa.v_intr_cntrl().priority() as u32;
let lapic = &mut this.backing.cvm.lapics[vtl].lapic;
let ppr = lapic
.access(&mut SnpApicClient {
partition: this.partition,
vmsa,
dev,
vmtime: &this.vmtime,
vtl,
})
.get_ppr();
let ppr_priority = ppr >> 4;
if vmsa_priority <= ppr_priority {
return false;
}

let vmsa = this.runner.vmsa_mut(vtl);
if (check_rflags && !RFlags::from_bits(vmsa.rflags()).interrupt_enable())
|| vmsa.v_intr_cntrl().intr_shadow()
|| !vmsa.v_intr_cntrl().irq()
{
return false;
}
let vmsa = this.runner.vmsa_mut(vtl);
if (check_rflags && !RFlags::from_bits(vmsa.rflags()).interrupt_enable())
|| vmsa.v_intr_cntrl().intr_shadow()
|| !vmsa.v_intr_cntrl().irq()
{
return false;
}

true
})
true
}
}

Expand Down
96 changes: 47 additions & 49 deletions openhcl/virt_mshv_vtl/src/processor/tdx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -657,63 +657,61 @@ impl HardwareIsolatedBacking for TdxBacked {
);
}

fn handle_cross_vtl_interrupts(
fn is_interrupt_pending(
this: &mut UhProcessor<'_, Self>,
vtl: GuestVtl,
check_rflags: bool,
dev: &impl CpuIo,
) -> Result<bool, UhRunVpError> {
this.cvm_handle_cross_vtl_interrupts(|this, vtl, check_rflags| {
let backing_vtl = &this.backing.vtls[vtl];
if backing_vtl.interruption_information.valid()
&& backing_vtl.interruption_information.interruption_type() == INTERRUPT_TYPE_NMI
{
return true;
}
) -> bool {
let backing_vtl = &this.backing.vtls[vtl];
if backing_vtl.interruption_information.valid()
&& backing_vtl.interruption_information.interruption_type() == INTERRUPT_TYPE_NMI
{
return true;
}

let (vector, ppr) = if this.backing.cvm.lapics[vtl].lapic.is_offloaded() {
let vector = backing_vtl.private_regs.rvi;
let ppr = std::cmp::max(
backing_vtl.private_regs.svi.into(),
this.runner.tdx_apic_page(vtl).tpr.value,
);
(vector, ppr)
} else {
let lapic = &mut this.backing.cvm.lapics[vtl].lapic;
let vector = lapic.next_irr().unwrap_or(0);
let ppr = lapic
.access(&mut TdxApicClient {
partition: this.partition,
apic_page: this.runner.tdx_apic_page_mut(vtl),
dev,
vmtime: &this.vmtime,
vtl,
})
.get_ppr();
(vector, ppr)
};
let vector_priority = (vector as u32) >> 4;
let ppr_priority = ppr >> 4;
let (vector, ppr) = if this.backing.cvm.lapics[vtl].lapic.is_offloaded() {
let vector = backing_vtl.private_regs.rvi;
let ppr = std::cmp::max(
backing_vtl.private_regs.svi.into(),
this.runner.tdx_apic_page(vtl).tpr.value,
);
(vector, ppr)
} else {
let lapic = &mut this.backing.cvm.lapics[vtl].lapic;
let vector = lapic.next_irr().unwrap_or(0);
let ppr = lapic
.access(&mut TdxApicClient {
partition: this.partition,
apic_page: this.runner.tdx_apic_page_mut(vtl),
dev,
vmtime: &this.vmtime,
vtl,
})
.get_ppr();
(vector, ppr)
};
let vector_priority = (vector as u32) >> 4;
let ppr_priority = ppr >> 4;

if vector_priority <= ppr_priority {
return false;
}
if vector_priority <= ppr_priority {
return false;
}

if check_rflags
&& !RFlags::from_bits(backing_vtl.private_regs.rflags).interrupt_enable()
{
return false;
}
if check_rflags && !RFlags::from_bits(backing_vtl.private_regs.rflags).interrupt_enable() {
return false;
}

let interruptibility: Interruptibility = this
.runner
.read_vmcs32(vtl, VmcsField::VMX_VMCS_GUEST_INTERRUPTIBILITY)
.into();
let interruptibility: Interruptibility = this
.runner
.read_vmcs32(vtl, VmcsField::VMX_VMCS_GUEST_INTERRUPTIBILITY)
.into();

if interruptibility.blocked_by_sti() || interruptibility.blocked_by_movss() {
return false;
}
if interruptibility.blocked_by_sti() || interruptibility.blocked_by_movss() {
return false;
}

true
})
true
}
}

Expand Down