Skip to content

Conversation

@jennagoddard
Copy link
Contributor

When running an SNP guest VP, test and set the guest busy bit atomically. Handle cases where the VP is not able to be restarted due to access issues with the guest APIC backing page. The guest busy bit is used for both secure AVIC and alternate injection scenarios.

@jennagoddard jennagoddard requested a review from a team as a code owner January 26, 2026 18:30
Copilot AI review requested due to automatic review settings January 26, 2026 18:30
@github-actions github-actions bot added the unsafe Related to unsafe code label Jan 26, 2026
@github-actions
Copy link

github-actions bot commented Jan 26, 2026

⚠️ Unsafe Code Detected

This PR modifies files containing unsafe Rust code. Extra scrutiny is required during review.

For more on why we check whole files, instead of just diffs, check out the Rustonomicon #Closed

}

/// Gets an atomic reference to the v_intr_cntrl field.
pub fn v_intr_cntrl_atomic(&self) -> &AtomicU64 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible for a caller to get a non-atomic ref to this field? Can we make that not be possible?

Does this function need to be unsafe? Does it need to be pub?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes the goal of this is to allow that

Copy link
Contributor

@smalis-msft smalis-msft Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to be careful here, the Rust aliasing requirements state that if code holds a shared reference (&) to some memory then that memory can not change out from under it. This code as written allows such a change to happen, as we can hold an &u64 and &AtomicU64 to the same memory, which could allow for the value pointed to by said &u64 to change out from under it, which would be Undefined Behavior.

Copy link
Contributor

@smalis-msft smalis-msft Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest either requiring &mut self for this (to ensure aliasing isn't possible (although that might not be sufficient if a caller can create a new &u64 to this field after calling this method and keeping the &AtomicU64 around)), or requiring all accesses to this field to go through this new Atomic, and prevent doing it through regular accesses. And then mentioning whichever you go with as justification in the SAFETY comment

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses the need to atomically test and set the guest busy bit when running SNP guest VPs, and adds error handling for cases where the VP cannot be restarted due to issues with the guest APIC backing page.

Changes:

  • Added not_restartable field to SevNpfInfo structure and NOT_RESTARTABLE exit code to handle non-restartable errors
  • Implemented atomic test-and-set operation for the guest busy bit in v_intr_cntrl field
  • Refactored event injection logic to use atomic operations and handle NOT_RESTARTABLE errors by triggering triple fault

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
vm/x86/x86defs/src/snp.rs Added not_restartable bit to SevNpfInfo structure (bit 63) and NOT_RESTARTABLE exit code (0x406)
openhcl/hcl/src/vmsa.rs Added atomic operations for v_intr_cntrl field including guest_busy_bit_test_and_set() method using fetch_or with SeqCst ordering
openhcl/virt_mshv_vtl/src/processor/snp/mod.rs Refactored to use atomic test-and-set for guest_busy bit, added error handling for NOT_RESTARTABLE cases, and made exit_int_info validation conditional instead of asserting

Comment on lines +170 to +176
pub fn guest_busy_bit_test_and_set(&mut self) -> bool {
const VINTR_GUEST_BUSYBIT_MASK: u64 = 1u64 << 63;
let prev = self
.v_intr_cntrl_atomic()
.fetch_or(VINTR_GUEST_BUSYBIT_MASK, Ordering::SeqCst);
(prev & VINTR_GUEST_BUSYBIT_MASK) != 0
}
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The atomic test-and-set operation mixes with non-atomic accesses to the same v_intr_cntrl field (e.g., in processor/snp/mod.rs at lines 1233, 1324, 1334). While the SAFETY comment acknowledges the caller's responsibility for synchronization, and the current sequential code structure ensures safety, consider whether all call sites properly maintain this invariant. If future changes introduce concurrent access patterns, this could become undefined behavior. Consider documenting this requirement more explicitly in the function's doc comment.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this needs to be fixed up, we should not make it so easy for callers to get things wrong here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the vintr control field is on the VMSA which is per-VP per-VTL. As VPs don't run on multiple processors at the same time there is a limited need for synchronization on the vintr control field in a performance sensitive path

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is useful context that should be in the SAFETY comment to explain why the unsafe code in question is safe.

// The guest APIC backing page is not validated in the RMP.
return Err(VpHaltReason::TripleFault {
vtl: entered_from_vtl.into(),
});
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to set bugcheck params?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should use dev.fatal_error instead of returning a TripleFault


/// Gets an atomic reference to the v_intr_cntrl field.
pub fn v_intr_cntrl_atomic(&self) -> &AtomicU64 {
// SAFETY: caller responsible for synchronizing with any non-atomic access.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SAFETY comments are meant to explain why a piece of unsafe code is impossible to misuse. Simply stating that the responsibility is on the caller is not sufficient, unless the function is also marked unsafe and contains its own Safety comment explaining what the caller needs to do to maintain safety.

_ => Some(exit_int_info),
};

vmsa.set_exit_int_info(0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't remember this code well enough, but is it safe to clear the exit_int_info here? Looks like it's used below

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

unsafe Related to unsafe code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants