1use super::PciCapability;
7use crate::msi::MsiInterrupt;
8use crate::msi::RegisterMsi;
9use crate::spec::caps::CapabilityId;
10use crate::spec::caps::msix::MsixCapabilityHeader;
11use crate::spec::caps::msix::MsixTableEntryIdx;
12use inspect::Inspect;
13use inspect::InspectMut;
14use parking_lot::Mutex;
15use std::fmt::Debug;
16use std::sync::Arc;
17use vmcore::interrupt::Interrupt;
18
19#[derive(Debug, Inspect)]
20struct MsiTableLocation {
21 #[inspect(hex)]
22 offset: u32,
23 bar: u8,
24}
25
26impl MsiTableLocation {
27 fn new(bar: u8, offset: u32) -> Self {
28 assert!(bar < 6);
29 assert!(offset & 7 == 0);
30 Self { offset, bar }
31 }
32
33 fn read_u32(&self) -> u32 {
34 self.offset | self.bar as u32
35 }
36}
37
38#[derive(Inspect)]
39struct MsixCapability {
40 count: u16,
41 #[inspect(with = "|x| inspect::adhoc(|req| x.lock().inspect_mut(req))")]
42 state: Arc<Mutex<MsixState>>,
43 config_table_location: MsiTableLocation,
44 pending_bits_location: MsiTableLocation,
45}
46
47impl PciCapability for MsixCapability {
48 fn label(&self) -> &str {
49 "msi-x"
50 }
51
52 fn len(&self) -> usize {
53 12
54 }
55
56 fn read_u32(&self, offset: u16) -> u32 {
57 match MsixCapabilityHeader(offset) {
58 MsixCapabilityHeader::CONTROL_CAPS => {
59 CapabilityId::MSIX.0 as u32
60 | ((self.count as u32 - 1) | if self.state.lock().enabled { 0x8000 } else { 0 })
61 << 16
62 }
63 MsixCapabilityHeader::OFFSET_TABLE => self.config_table_location.read_u32(),
64 MsixCapabilityHeader::OFFSET_PBA => self.pending_bits_location.read_u32(),
65 _ => panic!("Unreachable read offset {}", offset),
66 }
67 }
68
69 fn write_u32(&mut self, offset: u16, val: u32) {
70 match MsixCapabilityHeader(offset) {
71 MsixCapabilityHeader::CONTROL_CAPS => {
72 let enabled = val & 0x80000000 != 0;
73 let mut state = self.state.lock();
74 let was_enabled = state.enabled;
75 state.enabled = enabled;
76 if was_enabled && !enabled {
77 for entry in &mut state.vectors {
78 if entry.is_enabled(true) {
79 entry.msi.disable();
80 }
81 }
82 } else if enabled && !was_enabled {
83 for entry in &mut state.vectors {
84 if entry.is_enabled(true) {
85 entry.msi.enable(
86 entry.state.address,
87 entry.state.data,
88 entry.state.is_pending,
89 );
90 entry.state.is_pending = false;
91 }
92 }
93 }
94 }
95 MsixCapabilityHeader::OFFSET_TABLE | MsixCapabilityHeader::OFFSET_PBA => {
96 tracelimit::warn_ratelimited!(
97 "Unexpected write offset {:?}",
98 MsixCapabilityHeader(offset)
99 )
100 }
101 _ => panic!("Unreachable write offset {}", offset),
102 }
103 }
104
105 fn reset(&mut self) {
106 let mut state = self.state.lock();
107 state.enabled = false;
108 for vector in &mut state.vectors {
109 vector.state = EntryState::new();
110 }
111 }
112}
113
114struct MsixMessageTableEntry {
115 msi: MsiInterrupt,
116 state: EntryState,
117}
118
119impl InspectMut for MsixMessageTableEntry {
120 fn inspect_mut(&mut self, req: inspect::Request<'_>) {
121 req.respond()
122 .hex("address", self.state.address)
123 .hex("data", self.state.data)
124 .hex("control", self.state.control)
125 .field("enabled", self.state.control & 1 == 0)
126 .field("is_pending", self.check_is_pending(true));
127 }
128}
129
130#[derive(Debug)]
131struct EntryState {
132 address: u64,
133 data: u32,
134 control: u32,
135 is_pending: bool,
136}
137
138impl EntryState {
139 fn new() -> Self {
140 Self {
141 address: 0,
142 data: 0,
143 control: 1,
144 is_pending: false,
145 }
146 }
147}
148
149impl MsixMessageTableEntry {
150 fn new(msi: MsiInterrupt) -> Self {
151 Self {
152 msi,
153 state: EntryState::new(),
154 }
155 }
156
157 fn read_u32(&self, offset: u16) -> u32 {
158 match MsixTableEntryIdx(offset) {
159 MsixTableEntryIdx::MSG_ADDR_LO => self.state.address as u32,
160 MsixTableEntryIdx::MSG_ADDR_HI => (self.state.address >> 32) as u32,
161 MsixTableEntryIdx::MSG_DATA => self.state.data,
162 MsixTableEntryIdx::VECTOR_CTL => self.state.control,
163 _ => panic!("Unexpected read offset {}", offset),
164 }
165 }
166
167 fn write_u32(&mut self, offset: u16, val: u32) {
168 match MsixTableEntryIdx(offset) {
169 MsixTableEntryIdx::MSG_ADDR_LO => {
170 self.state.address = (self.state.address & 0xffffffff00000000) | val as u64
171 }
172 MsixTableEntryIdx::MSG_ADDR_HI => {
173 self.state.address = (val as u64) << 32 | self.state.address & 0xffffffff
174 }
175 MsixTableEntryIdx::MSG_DATA => self.state.data = val,
176 MsixTableEntryIdx::VECTOR_CTL => self.state.control = val,
177 _ => panic!("Unexpected write offset {}", offset),
178 }
179 }
180
181 fn is_enabled(&self, global_enabled: bool) -> bool {
182 global_enabled && self.state.control & 1 == 0
183 }
184
185 fn check_is_pending(&mut self, global_enabled: bool) -> bool {
186 if !self.state.is_pending && !self.is_enabled(global_enabled) {
187 self.state.is_pending = self.msi.drain_pending();
188 }
189 self.state.is_pending
190 }
191}
192
193#[derive(InspectMut)]
194struct MsixState {
195 enabled: bool,
196 #[inspect(mut, with = "inspect_entries")]
197 vectors: Vec<MsixMessageTableEntry>,
198}
199
200fn inspect_entries(entries: &mut [MsixMessageTableEntry]) -> impl '_ + InspectMut {
201 inspect::adhoc_mut(|req| {
202 let mut resp = req.respond();
203 for (i, entry) in entries.iter_mut().enumerate() {
204 resp.field_mut(&i.to_string(), entry);
205 }
206 })
207}
208
209#[derive(Clone)]
212pub struct MsixEmulator {
213 state: Arc<Mutex<MsixState>>,
214 pending_bits_offset: u16,
215 pending_bits_dword_count: u16,
216}
217
218impl MsixEmulator {
219 pub fn new(
233 bar: u8,
234 count: u16,
235 register_msi: &mut dyn RegisterMsi,
236 ) -> (Self, impl PciCapability + use<>) {
237 let state = MsixState {
238 enabled: false,
239 vectors: (0..count)
240 .map(|_| MsixMessageTableEntry::new(register_msi.new_msi()))
241 .collect(),
242 };
243 let state = Arc::new(Mutex::new(state));
244 let pending_bits_offset = count * 16;
245 (
246 Self {
247 state: state.clone(),
248 pending_bits_offset,
249 pending_bits_dword_count: count.div_ceil(32),
250 },
251 MsixCapability {
252 count,
253 state,
254 config_table_location: MsiTableLocation::new(bar, 0),
255 pending_bits_location: MsiTableLocation::new(bar, pending_bits_offset.into()),
256 },
257 )
258 }
259
260 pub fn bar_len(&self) -> u64 {
262 (self.pending_bits_offset + self.pending_bits_dword_count * 4).into()
263 }
264
265 pub fn read_u32(&self, offset: u16) -> u32 {
267 let mut state = self.state.lock();
268 let state: &mut MsixState = &mut state;
269 if offset < self.pending_bits_offset {
270 let index = offset / 16;
271 if let Some(entry) = state.vectors.get(index as usize) {
272 return entry.read_u32(offset & 0xf);
273 }
274 } else {
275 let dword = (offset - self.pending_bits_offset) / 4;
276 let start = dword as usize * 32;
277 if start < state.vectors.len() {
278 let end = (start + 32).min(state.vectors.len());
279 let mut val = 0u32;
280 for (i, entry) in state.vectors[start..end].iter_mut().enumerate() {
281 if entry.check_is_pending(state.enabled) {
282 val |= 1 << i;
283 }
284 }
285 return val;
286 }
287 }
288 tracelimit::warn_ratelimited!(offset, "Unexpected read offset");
289 0
290 }
291
292 pub fn write_u32(&mut self, offset: u16, val: u32) {
294 let mut state = self.state.lock();
295 if offset < self.pending_bits_offset {
296 let index = offset / 16;
297 let global = state.enabled;
298 if let Some(entry) = state.vectors.get_mut(index as usize) {
299 let was_enabled = entry.is_enabled(global);
300 entry.write_u32(offset & 0xf, val);
301 let is_enabled = entry.is_enabled(global);
302 if is_enabled && !was_enabled {
303 entry.msi.enable(
304 entry.state.address,
305 entry.state.data,
306 entry.state.is_pending,
307 );
308 entry.state.is_pending = false;
309 } else if was_enabled && !is_enabled {
310 entry.msi.disable();
311 }
312 return;
313 }
314 } else if offset - self.pending_bits_offset < self.pending_bits_dword_count * 4 {
315 return;
316 }
317 tracelimit::warn_ratelimited!(offset, "Unexpected write offset");
318 }
319
320 pub fn interrupt(&self, index: u16) -> Option<Interrupt> {
323 Some(
324 self.state
325 .lock()
326 .vectors
327 .get_mut(index as usize)?
328 .msi
329 .interrupt(),
330 )
331 }
332
333 #[cfg(test)]
334 fn clear_pending_bit(&self, index: u8) {
335 let mut state = self.state.lock();
336 state.vectors[index as usize].state.is_pending = false;
337 }
338
339 #[cfg(test)]
340 fn set_pending_bit(&self, index: u8) {
341 let mut state = self.state.lock();
342 state.vectors[index as usize].state.is_pending = true;
343 }
344}
345
346mod save_restore {
347 use super::*;
348 use thiserror::Error;
349 use vmcore::save_restore::RestoreError;
350 use vmcore::save_restore::SaveError;
351 use vmcore::save_restore::SaveRestore;
352
353 mod state {
354 use mesh::payload::Protobuf;
355 use vmcore::save_restore::SavedStateRoot;
356
357 #[derive(Debug, Protobuf)]
358 #[mesh(package = "pci.caps.msix")]
359 pub struct SavedMsixMessageTableEntryState {
360 #[mesh(1)]
361 pub address: u64,
362 #[mesh(2)]
363 pub data: u32,
364 #[mesh(3)]
365 pub control: u32,
366 #[mesh(4)]
367 pub is_pending: bool,
368 }
369
370 #[derive(Debug, Protobuf, SavedStateRoot)]
371 #[mesh(package = "pci.caps.msix")]
372 pub struct SavedState {
373 #[mesh(2)]
374 pub enabled: bool,
375 #[mesh(3)]
376 pub vectors: Vec<SavedMsixMessageTableEntryState>,
377 }
378 }
379
380 #[derive(Debug, Error)]
381 enum MsixRestoreError {
382 #[error("mismatched vector lengths: current:{0}, saved:{1}")]
383 MismatchedTableLengths(usize, usize),
384 }
385
386 impl SaveRestore for MsixCapability {
387 type SavedState = state::SavedState;
388
389 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
390 let state = self.state.lock();
391 let saved_state = state::SavedState {
392 enabled: state.enabled,
393 vectors: {
394 state
395 .vectors
396 .iter()
397 .map(|vec| {
398 let EntryState {
399 address,
400 data,
401 control,
402 is_pending,
403 } = vec.state;
404
405 state::SavedMsixMessageTableEntryState {
406 address,
407 data,
408 control,
409 is_pending,
410 }
411 })
412 .collect()
413 },
414 };
415 Ok(saved_state)
416 }
417
418 fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
419 let state::SavedState { enabled, vectors } = state;
420
421 let mut state = self.state.lock();
422 state.enabled = enabled;
423
424 if vectors.len() != state.vectors.len() {
425 return Err(RestoreError::InvalidSavedState(
426 MsixRestoreError::MismatchedTableLengths(vectors.len(), state.vectors.len())
427 .into(),
428 ));
429 }
430
431 for (new_vec, vec) in vectors.into_iter().zip(state.vectors.iter_mut()) {
432 vec.state = EntryState {
433 address: new_vec.address,
434 data: new_vec.data,
435 control: new_vec.control,
436 is_pending: new_vec.is_pending,
437 }
438 }
439
440 Ok(())
441 }
442 }
443}
444
445#[cfg(test)]
446mod tests {
447 use super::*;
448 use crate::msi::MsiInterruptSet;
449 use crate::test_helpers::TestPciInterruptController;
450
451 #[test]
452 fn msix_check() {
453 let mut set = MsiInterruptSet::new();
454 let (mut msix, mut cap) = MsixEmulator::new(2, 64, &mut set);
455 let msi_controller = TestPciInterruptController::new();
456 set.connect(&msi_controller);
457 assert_eq!(cap.read_u32(0), 0x3f0011);
459 assert_eq!(cap.read_u32(4), 2);
460 assert_eq!(cap.read_u32(8), 0x402);
461 cap.write_u32(0, 0xffffffff);
462 assert_eq!(cap.read_u32(0), 0x803f0011);
463 assert_eq!(msix.read_u32(0), 0);
466 assert_eq!(msix.read_u32(4), 0);
467 assert_eq!(msix.read_u32(8), 0);
468 assert_eq!(msix.read_u32(12), 1);
469 msix.write_u32(0, 0x12345678);
470 msix.write_u32(4, 0x9abcdef0);
471 msix.write_u32(8, 0x123);
472 msix.write_u32(12, 0x456);
473 assert_eq!(msix.read_u32(0), 0x12345678);
474 assert_eq!(msix.read_u32(4), 0x9abcdef0);
475 assert_eq!(msix.read_u32(8), 0x123);
476 assert_eq!(msix.read_u32(12), 0x456);
477 assert_eq!(msix.read_u32(0x3f0), 0);
479 assert_eq!(msix.read_u32(0x3f4), 0);
480 assert_eq!(msix.read_u32(0x3f8), 0);
481 assert_eq!(msix.read_u32(0x3fc), 1);
482 msix.write_u32(0x3f0, 0x12345678);
483 msix.write_u32(0x3f4, 0x9abcdef0);
484 msix.write_u32(0x3f8, 0x123);
485 msix.write_u32(0x3fc, 0x456);
486 assert_eq!(msix.read_u32(0x3f0), 0x12345678);
487 assert_eq!(msix.read_u32(0x3f4), 0x9abcdef0);
488 assert_eq!(msix.read_u32(0x3f8), 0x123);
489 assert_eq!(msix.read_u32(0x3fc), 0x456);
490 assert_eq!(msix.read_u32(0x400), 0);
492 assert_eq!(msix.read_u32(0x404), 0);
493 msix.set_pending_bit(1);
494 assert_eq!(msix.read_u32(0x400), 2);
495 assert_eq!(msix.read_u32(0x404), 0);
496 msix.set_pending_bit(33);
497 assert_eq!(msix.read_u32(0x400), 2);
498 assert_eq!(msix.read_u32(0x404), 2);
499 msix.set_pending_bit(63);
500 msix.set_pending_bit(31);
501 assert_eq!(msix.read_u32(0x400), 0x80000002);
502 assert_eq!(msix.read_u32(0x404), 0x80000002);
503 msix.clear_pending_bit(1);
504 assert_eq!(msix.read_u32(0x400), 0x80000000);
505 assert_eq!(msix.read_u32(0x404), 0x80000002);
506 }
507}