1#![expect(unsafe_code)]
12
13use crate::MshvPartitionInner;
14use anyhow::Context;
15use headervec::HeaderVec;
16use mshv_bindings::MSHV_IRQFD_BIT_DEASSIGN;
17use mshv_bindings::mshv_user_irq_entry;
18use mshv_bindings::mshv_user_irqfd;
19use pal_event::Event;
20use parking_lot::Mutex;
21use std::os::fd::AsFd;
22use std::os::fd::AsRawFd;
23use std::sync::Arc;
24use virt::irqfd::IrqFd;
25use virt::irqfd::IrqFdRoute;
26
27pub(crate) const NUM_GSIS: usize = 2048;
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub(crate) enum GsiState {
32 Unallocated,
34 Disabled,
36 Enabled(MsiRoute),
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub(crate) struct MsiRoute {
43 address_lo: u32,
44 address_hi: u32,
45 data: u32,
46}
47
48impl MshvPartitionInner {
49 fn alloc_gsi(&self) -> Option<u32> {
51 let mut states = self.gsi_states.lock();
52 let gsi = states
53 .iter()
54 .position(|state| matches!(state, GsiState::Unallocated))?;
55 states[gsi] = GsiState::Disabled;
56 Some(gsi as u32)
57 }
58
59 fn free_gsi(&self, gsi: u32) {
61 self.gsi_states.lock()[gsi as usize] = GsiState::Unallocated;
62 }
63
64 fn set_gsi_route(&self, gsi: u32, route: Option<MsiRoute>) -> anyhow::Result<()> {
67 let mut states = self.gsi_states.lock();
68 let state = &mut states[gsi as usize];
69 anyhow::ensure!(
70 !matches!(state, GsiState::Unallocated),
71 "cannot set route for unallocated GSI {gsi}"
72 );
73 let new_state = match route {
74 Some(r) => GsiState::Enabled(r),
75 None => GsiState::Disabled,
76 };
77 if *state == new_state {
78 return Ok(());
79 }
80 let old_state = *state;
81 *state = new_state;
82
83 if let Err(e) = Self::push_routing_table(&self.vmfd, &states) {
84 states[gsi as usize] = old_state;
86 return Err(e);
87 }
88 Ok(())
89 }
90
91 fn push_routing_table(
93 vmfd: &mshv_ioctls::VmFd,
94 states: &[GsiState; NUM_GSIS],
95 ) -> anyhow::Result<()> {
96 let entries: Vec<mshv_user_irq_entry> = states
97 .iter()
98 .enumerate()
99 .filter_map(|(gsi, state)| match state {
100 GsiState::Enabled(route) => Some(mshv_user_irq_entry {
101 gsi: gsi as u32,
102 address_lo: route.address_lo,
103 address_hi: route.address_hi,
104 data: route.data,
105 }),
106 _ => None,
107 })
108 .collect();
109
110 set_msi_routing_ioctl(vmfd, &entries).context("failed to set MSI routing")
111 }
112
113 unsafe fn register_irqfd(&self, event: &Event, gsi: u32) -> anyhow::Result<()> {
120 let irqfd_arg = mshv_user_irqfd {
121 fd: event.as_fd().as_raw_fd(),
122 resamplefd: 0,
123 gsi,
124 flags: 0,
125 };
126 let ret = unsafe {
131 libc::ioctl(
132 self.vmfd.as_raw_fd(),
133 mshv_ioctls::MSHV_IRQFD() as _,
134 std::ptr::from_ref(&irqfd_arg),
135 )
136 };
137 if ret < 0 {
138 return Err(std::io::Error::last_os_error()).context("MSHV_IRQFD register failed");
139 }
140 Ok(())
141 }
142
143 unsafe fn unregister_irqfd(&self, event: &Event, gsi: u32) -> anyhow::Result<()> {
150 let irqfd_arg = mshv_user_irqfd {
151 fd: event.as_fd().as_raw_fd(),
152 resamplefd: 0,
153 gsi,
154 flags: 1 << MSHV_IRQFD_BIT_DEASSIGN,
155 };
156 let ret = unsafe {
160 libc::ioctl(
161 self.vmfd.as_raw_fd(),
162 mshv_ioctls::MSHV_IRQFD() as _,
163 std::ptr::from_ref(&irqfd_arg),
164 )
165 };
166 if ret < 0 {
167 return Err(std::io::Error::last_os_error()).context("MSHV_IRQFD unregister failed");
168 }
169 Ok(())
170 }
171}
172
173pub(crate) struct MshvIrqFd {
179 partition: Arc<MshvPartitionInner>,
180}
181
182impl MshvIrqFd {
183 pub fn new(partition: Arc<MshvPartitionInner>) -> Self {
184 Self { partition }
185 }
186}
187
188impl IrqFd for MshvIrqFd {
189 fn new_irqfd_route(&self) -> anyhow::Result<Box<dyn IrqFdRoute>> {
190 let gsi = self
191 .partition
192 .alloc_gsi()
193 .context("no free GSIs available for irqfd")?;
194
195 let event = Event::new();
196 if let Err(e) = unsafe { self.partition.register_irqfd(&event, gsi) } {
199 self.partition.free_gsi(gsi);
200 return Err(e);
201 }
202
203 Ok(Box::new(MshvIrqFdRoute {
204 partition: self.partition.clone(),
205 gsi,
206 event,
207 last_route: Mutex::new(None),
208 }))
209 }
210}
211
212struct MshvIrqFdRoute {
216 partition: Arc<MshvPartitionInner>,
217 gsi: u32,
218 event: Event,
219 last_route: Mutex<Option<MsiRoute>>,
222}
223
224impl IrqFdRoute for MshvIrqFdRoute {
225 fn event(&self) -> &Event {
226 &self.event
227 }
228
229 fn set_msi(&self, address: u64, data: u32) -> anyhow::Result<()> {
230 let route = MsiRoute {
231 address_lo: address as u32,
232 address_hi: (address >> 32) as u32,
233 data,
234 };
235 self.partition.set_gsi_route(self.gsi, Some(route))?;
236 *self.last_route.lock() = Some(route);
237 Ok(())
238 }
239
240 fn clear_msi(&self) -> anyhow::Result<()> {
241 self.partition.set_gsi_route(self.gsi, None)?;
242 *self.last_route.lock() = None;
243 Ok(())
244 }
245
246 fn mask(&self) -> anyhow::Result<()> {
247 self.partition.set_gsi_route(self.gsi, None)
252 }
253
254 fn unmask(&self) -> anyhow::Result<()> {
255 let route = *self.last_route.lock();
257 if let Some(route) = route {
258 self.partition.set_gsi_route(self.gsi, Some(route))?;
259 }
260 Ok(())
261 }
262}
263
264impl Drop for MshvIrqFdRoute {
265 fn drop(&mut self) {
266 self.partition
267 .set_gsi_route(self.gsi, None)
268 .expect("failed to clear GSI route on drop");
269
270 unsafe {
273 self.partition
274 .unregister_irqfd(&self.event, self.gsi)
275 .expect("failed to unregister irqfd on drop");
276 }
277
278 self.partition.free_gsi(self.gsi);
279 }
280}
281
282#[repr(C)]
286#[derive(Debug, Copy, Clone)]
287struct MsiRoutingHeader {
288 nr: u32,
289 rsvd: u32,
290}
291
292fn set_msi_routing_ioctl(
297 vmfd: &mshv_ioctls::VmFd,
298 entries: &[mshv_user_irq_entry],
299) -> anyhow::Result<()> {
300 let mut buf = HeaderVec::<MsiRoutingHeader, mshv_user_irq_entry, 0>::new(MsiRoutingHeader {
301 nr: entries.len() as u32,
302 rsvd: 0,
303 });
304 buf.extend_tail_from_slice(entries);
305
306 let ret = unsafe {
311 libc::ioctl(
312 vmfd.as_raw_fd(),
313 mshv_ioctls::MSHV_SET_MSI_ROUTING() as _,
314 buf.as_ptr(),
315 )
316 };
317 if ret < 0 {
318 return Err(std::io::Error::last_os_error()).context("MSHV_SET_MSI_ROUTING ioctl failed");
319 }
320
321 Ok(())
322}