-
Notifications
You must be signed in to change notification settings - Fork 98
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
virt_mshv_vtl: Proxy irr filtering #609
base: main
Are you sure you want to change the base?
Changes from 8 commits
6c54673
ff5ca4a
5b2930c
e2d935b
2b3bfcb
5a0e6ca
583f17d
4ac1fe2
3515e60
1bae5f6
34bfcc4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,6 +26,8 @@ use crate::protocol::HCL_VMSA_GUEST_VSM_PAGE_OFFSET; | |
use crate::protocol::HCL_VMSA_PAGE_OFFSET; | ||
use crate::protocol::MSHV_APIC_PAGE_OFFSET; | ||
use crate::GuestVtl; | ||
use bitvec::prelude::BitArray; | ||
use bitvec::prelude::Lsb0; | ||
use hvdef::hypercall::AssertVirtualInterrupt; | ||
use hvdef::hypercall::HostVisibilityType; | ||
use hvdef::hypercall::HvGpaRange; | ||
|
@@ -1590,7 +1592,11 @@ impl HclVp { | |
isolation_type: IsolationType, | ||
) -> Result<Self, Error> { | ||
let fd = &hcl.mshv_vtl.file; | ||
let run = MappedPage::new(fd, vp as i64).map_err(|e| Error::MmapVp(e, None))?; | ||
let run: MappedPage<hcl_run> = | ||
MappedPage::new(fd, vp as i64).map_err(|e| Error::MmapVp(e, None))?; | ||
// SAFETY: Initializing `proxy_irr_blocked` to block all initially | ||
let proxy_irr_blocked = unsafe { &mut *addr_of_mut!((*run.0.as_ptr()).proxy_irr_blocked) }; | ||
proxy_irr_blocked.fill(0xFFFFFFFF); | ||
|
||
let backing = match isolation_type { | ||
IsolationType::None | IsolationType::Vbs => BackingState::Mshv { | ||
|
@@ -1857,6 +1863,20 @@ impl<'a, T: Backing> ProcessorRunner<'a, T> { | |
} | ||
} | ||
|
||
/// Update the `proxy_irr_blocked` in run page | ||
pub fn update_proxy_irr_filter(&mut self, irr_filter: &BitArray<[u32; 8], Lsb0>) { | ||
// SAFETY: `proxy_irr_blocked` is accessed in both user and kernel, but from current VP | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd suggest saying something like "SAFETY: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this is true. The kernel will access it in an interrupt context--only from the current VP yes, but we still need to use at least relaxed atomic reads/writes to access the values to satisfy the Rust and Linux kernel memory models. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ahh that's right, thanks for catching this... OpenVMM has U/K model, so yes I agree, same VP running on user context can be interrupted and then kernel may access this field. Will move to atomics, like the way it has been done while accessing |
||
// By default block all (i.e. set all), and only allow (unset) given vectors | ||
let proxy_irr_blocked = | ||
unsafe { &mut *addr_of_mut!((*self.run.as_ptr()).proxy_irr_blocked) }; | ||
proxy_irr_blocked.fill(0xFFFFFFFF); | ||
|
||
for irr_bit in irr_filter.iter_ones() { | ||
tracing::debug!(irr_bit, "update_proxy_irr_filter"); | ||
proxy_irr_blocked[irr_bit >> 5] &= !(1 << (irr_bit & 0x1F)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we get a comment explaining this math? Why are we shifting things? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The compiler can be relied upon to strength reduce a constant power-of-two division/remainder to a shift/and. So I'd probably write this as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Having said that, it seems like you can just directly store I'd also suggest just taking a |
||
} | ||
} | ||
|
||
/// Gets the proxy_irr_exit bitmask. This mask ensures that | ||
/// the masked interrupts always exit to user-space, and cannot | ||
/// be injected in the kernel. Interrupts matching this condition | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,9 @@ cfg_if::cfg_if!( | |
use processor::tdx::TdxBackedShared; | ||
use std::arch::x86_64::CpuidResult; | ||
use virt::CpuidLeaf; | ||
use bitvec::prelude::BitArray; | ||
use bitvec::prelude::Lsb0; | ||
type IrrBitmap = BitArray<[u32; 8], Lsb0>; | ||
} else if #[cfg(target_arch = "aarch64")] { // xtask-fmt allow-target-arch sys-crate | ||
pub use crate::processor::mshv::arm64::HypervisorBackedArm64 as HypervisorBacked; | ||
use hvdef::HvArm64RegisterName; | ||
|
@@ -223,6 +226,10 @@ struct UhPartitionInner { | |
no_sidecar_hotplug: AtomicBool, | ||
use_mmio_hypercalls: bool, | ||
backing_shared: BackingShared, | ||
#[inspect(skip)] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This feels like something we'd want wired up to inspect |
||
#[cfg(guest_arch = "x86_64")] | ||
// N.B For now, only one device vector table i.e. for VTL0 only | ||
device_vector_table: RwLock<IrrBitmap>, | ||
} | ||
|
||
#[derive(Inspect)] | ||
|
@@ -628,7 +635,8 @@ struct WakeReason { | |
message_queues: bool, | ||
hv_start_enable_vtl_vp: bool, | ||
intcon: bool, | ||
#[bits(28)] | ||
update_proxy_irr_filter: bool, | ||
#[bits(27)] | ||
_reserved: u32, | ||
} | ||
|
||
|
@@ -638,6 +646,8 @@ impl WakeReason { | |
const MESSAGE_QUEUES: Self = Self::new().with_message_queues(true); | ||
const HV_START_ENABLE_VP_VTL: Self = Self::new().with_hv_start_enable_vtl_vp(true); // StartVp/EnableVpVtl handling | ||
const INTCON: Self = Self::new().with_intcon(true); | ||
#[cfg(guest_arch = "x86_64")] | ||
const UPDATE_PROXY_IRR_FILTER: Self = Self::new().with_update_proxy_irr_filter(true); | ||
} | ||
|
||
/// Immutable access to useful bits of Partition state. | ||
|
@@ -749,6 +759,39 @@ impl UhPartitionInner { | |
self.vps.get(index.index() as usize) | ||
} | ||
|
||
/// For requester VP to issue `proxy_irr_blocked` update to other VPs | ||
#[cfg(guest_arch = "x86_64")] | ||
fn request_proxy_irr_filter_update(&self, vtl: GuestVtl, device_vector: u8, req_vp_index: u32) { | ||
tracing::debug!( | ||
?vtl, | ||
device_vector, | ||
req_vp_index, | ||
"request_proxy_irr_filter_update" | ||
); | ||
|
||
// Add given vector to partition global device vector table (VTL0 only for now) | ||
{ | ||
let mut device_vector_table = self.device_vector_table.write(); | ||
device_vector_table.set(device_vector as usize, true); | ||
} | ||
|
||
// Wake all other VPs for their `proxy_irr_blocked` filter update | ||
for vp in self.vps.iter() { | ||
if vp.cpu_index != req_vp_index { | ||
vp.wake(vtl, WakeReason::UPDATE_PROXY_IRR_FILTER); | ||
} | ||
} | ||
} | ||
|
||
/// Get current partition global device irr vectors (VTL0 for now) | ||
#[cfg(guest_arch = "x86_64")] | ||
fn get_device_vectors(&self, _vtl: GuestVtl, irr_vectors: &mut IrrBitmap) { | ||
let device_vector_table = self.device_vector_table.read(); | ||
for idx in device_vector_table.iter_ones() { | ||
irr_vectors.set(idx, true); | ||
} | ||
} | ||
|
||
fn inspect_extra(&self, resp: &mut inspect::Response<'_>) { | ||
let mut wake_vps = false; | ||
resp.field_mut( | ||
|
@@ -1609,6 +1652,8 @@ impl<'a> UhProtoPartition<'a> { | |
no_sidecar_hotplug: params.no_sidecar_hotplug.into(), | ||
use_mmio_hypercalls: params.use_mmio_hypercalls, | ||
backing_shared: BackingShared::new(isolation, BackingSharedParams { cvm_state })?, | ||
#[cfg(guest_arch = "x86_64")] | ||
device_vector_table: RwLock::new(IrrBitmap::new(Default::default())), | ||
}); | ||
|
||
if cfg!(guest_arch = "x86_64") { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,6 +23,8 @@ cfg_if::cfg_if! { | |
use virt::x86::MsrError; | ||
use virt_support_apic::LocalApic; | ||
use virt_support_x86emu::translate::TranslationRegisters; | ||
use bitvec::prelude::BitArray; | ||
use bitvec::prelude::Lsb0; | ||
} else if #[cfg(guest_arch = "aarch64")] { | ||
use hv1_hypercall::Arm64RegisterState; | ||
use hvdef::HvArm64RegisterName; | ||
|
@@ -934,6 +936,14 @@ impl<'a, T: Backing> UhProcessor<'a, T> { | |
} | ||
} | ||
} | ||
|
||
#[cfg(guest_arch = "x86_64")] | ||
if wake_reasons.update_proxy_irr_filter() | ||
&& self.partition.isolation.is_hardware_isolated() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this wake reason ever get set on non-isolated? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently we are only issuing this wake request from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to check that we're isolated in this if then? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I agree its duplicate; we can get rid of it. Was just making it more explicit. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can put an assertion if you want. |
||
{ | ||
// update `proxy_irr_blocked` filter | ||
self.update_proxy_irr_filter(vtl); | ||
} | ||
} | ||
|
||
Ok(wake_reasons_vtl.map(|w| w.intcon()).into()) | ||
|
@@ -966,9 +976,26 @@ impl<'a, T: Backing> UhProcessor<'a, T> { | |
#[cfg(guest_arch = "x86_64")] | ||
fn write_msr(&mut self, msr: u32, value: u64, vtl: GuestVtl) -> Result<(), MsrError> { | ||
if msr & 0xf0000000 == 0x40000000 { | ||
// If updated is Synic MSR, then check if its proxy or previous was proxy | ||
// in either case, we need to update the `proxy_irr_blocked` | ||
let mut irr_filter_update = false; | ||
if matches!(msr, hvdef::HV_X64_MSR_SINT0..=hvdef::HV_X64_MSR_SINT15) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we change the order of these ifs, can we then merge the two checking hv? |
||
if let Some(hv) = self.backing.hv(vtl).as_ref() { | ||
let sint_curr = | ||
HvSynicSint::from(hv.synic.sint((msr - hvdef::HV_X64_MSR_SINT0) as u8)); | ||
let sint_new = HvSynicSint::from(value); | ||
if sint_curr.proxy() || sint_new.proxy() { | ||
irr_filter_update = true; | ||
} | ||
} | ||
} | ||
if let Some(hv) = self.backing.hv_mut(vtl).as_mut() { | ||
let r = hv.msr_write(msr, value); | ||
if !matches!(r, Err(MsrError::Unknown)) { | ||
// SINT updated, check if proxy filter update was required | ||
if irr_filter_update { | ||
self.update_proxy_irr_filter(vtl); | ||
} | ||
return r; | ||
} | ||
} | ||
|
@@ -1128,6 +1155,28 @@ impl<'a, T: Backing> UhProcessor<'a, T> { | |
|
||
self.request_sint_notifications(vtl, pending_sints); | ||
} | ||
|
||
#[cfg(guest_arch = "x86_64")] | ||
fn update_proxy_irr_filter(&mut self, vtl: GuestVtl) { | ||
let mut irr_bits: BitArray<[u32; 8], Lsb0> = BitArray::new(Default::default()); | ||
|
||
// Get all not maksed && proxy SINT vectors | ||
if let Some(hv) = self.backing.hv(vtl).as_ref() { | ||
for sint in 0..NUM_SINTS as u8 { | ||
let sint_msr = hv.synic.sint(sint); | ||
let hv_sint = HvSynicSint::from(sint_msr); | ||
if hv_sint.proxy() && !hv_sint.masked() { | ||
irr_bits.set(hv_sint.vector() as usize, true); | ||
} | ||
} | ||
} | ||
|
||
// Get all device vectors | ||
self.partition.get_device_vectors(vtl, &mut irr_bits); | ||
|
||
// Update `proxy_irr_blocked` filter in run page | ||
self.runner.update_proxy_irr_filter(&irr_bits); | ||
} | ||
} | ||
|
||
fn signal_mnf(dev: &impl CpuIo, connection_id: u32) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's not how SAFETY comments work. SAFETY comments are intended to explain why the unsafe operation you're performing does not violate any of rust's safety rules. If you're interested I'd suggest reading chapter 1 of the nomicon (https://doc.rust-lang.org/nomicon/safe-unsafe-meaning.html). But for this case it's probably enough to look up other places where we touch the run page and mimic what they say.