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