1use crate::BAR0_LEN;
7use crate::DEVICE_ID;
8use crate::DOORBELL_STRIDE_BITS;
9use crate::IOCQES;
10use crate::IOSQES;
11use crate::MAX_QES;
12use crate::NVME_VERSION;
13use crate::NvmeFaultControllerClient;
14use crate::PAGE_MASK;
15use crate::VENDOR_ID;
16use crate::spec;
17use crate::tdisp::BAR0_RANGE_ID;
18use crate::tdisp::TdispMmioRanges;
19use crate::tdisp::new_tdisp_interface;
20use crate::workers::IoQueueEntrySizes;
21use crate::workers::NvmeWorkers;
22use chipset_device::ChipsetDevice;
23use chipset_device::io::IoError;
24use chipset_device::io::IoError::InvalidRegister;
25use chipset_device::io::IoResult;
26use chipset_device::mmio::MmioIntercept;
27use chipset_device::mmio::RegisterMmioIntercept;
28use chipset_device::pci::ByteEnabledDwordRead;
29use chipset_device::pci::ByteEnabledDwordWrite;
30use chipset_device::pci::PciConfigSpace;
31use device_emulators::ReadWriteRequestType;
32use device_emulators::read_as_u32_chunks;
33use device_emulators::write_as_u32_chunks;
34use guestmem::GuestMemory;
35use guid::Guid;
36use inspect::Inspect;
37use inspect::InspectMut;
38use nvme_resources::fault::FaultConfiguration;
39use nvme_resources::fault::PciFaultBehavior;
40use nvme_resources::fault::PciFaultConfig;
41use parking_lot::Mutex;
42use pci_core::capabilities::msix::MsixEmulator;
43use pci_core::cfg_space_emu::BarMemoryKind;
44use pci_core::cfg_space_emu::ConfigSpaceType0Emulator;
45use pci_core::cfg_space_emu::DeviceBars;
46use pci_core::msi::MsiTarget;
47use pci_core::spec::hwid::ClassCode;
48use pci_core::spec::hwid::HardwareIds;
49use pci_core::spec::hwid::ProgrammingInterface;
50use pci_core::spec::hwid::Subclass;
51use std::sync::Arc;
52use tdisp::TdispHostDeviceTarget;
53use vmcore::device_state::ChangeDeviceState;
54use vmcore::save_restore::SaveError;
55use vmcore::save_restore::SaveRestore;
56use vmcore::save_restore::SavedStateNotSupported;
57use vmcore::vm_task::VmTaskDriverSource;
58
59#[derive(InspectMut)]
61pub struct NvmeFaultController {
62 cfg_space: ConfigSpaceType0Emulator,
63 #[inspect(skip)]
64 msix: MsixEmulator,
65 registers: RegState,
66 #[inspect(skip)]
67 qe_sizes: Arc<Mutex<IoQueueEntrySizes>>,
68 #[inspect(flatten, mut)]
69 workers: NvmeWorkers,
70 #[inspect(skip)]
71 pci_fault_config: PciFaultConfig,
72 #[inspect(skip)]
73 fault_active: mesh::Cell<bool>,
74 #[inspect(skip)]
76 tdisp_interface: Option<Box<dyn TdispHostDeviceTarget>>,
77 #[inspect(skip)]
80 tdisp_mmio_ranges: TdispMmioRanges,
81}
82
83#[derive(Inspect)]
84struct RegState {
85 #[inspect(hex)]
86 interrupt_mask: u32,
87 cc: spec::Cc,
88 csts: spec::Csts,
89 aqa: spec::Aqa,
90 #[inspect(hex)]
91 asq: u64,
92 #[inspect(hex)]
93 acq: u64,
94}
95
96impl RegState {
97 fn new() -> Self {
98 Self {
99 interrupt_mask: 0,
100 cc: spec::Cc::new(),
101 csts: spec::Csts::new(),
102 aqa: spec::Aqa::new(),
103 asq: 0,
104 acq: 0,
105 }
106 }
107}
108
109const CAP: spec::Cap = spec::Cap::new()
110 .with_dstrd(DOORBELL_STRIDE_BITS - 2)
111 .with_mqes_z(MAX_QES - 1)
112 .with_cqr(true)
113 .with_css_nvm(true)
114 .with_to(!0);
115
116#[derive(Debug, Copy, Clone)]
118pub struct NvmeFaultControllerCaps {
119 pub msix_count: u16,
121 pub max_io_queues: u16,
123 pub subsystem_id: Guid,
126}
127
128impl NvmeFaultController {
129 pub fn new(
131 driver_source: &VmTaskDriverSource,
132 guest_memory: GuestMemory,
133 msi_target: &MsiTarget,
134 register_mmio: &mut dyn RegisterMmioIntercept,
135 caps: NvmeFaultControllerCaps,
136 mut fault_configuration: FaultConfiguration,
137 enable_tdisp: bool,
138 ) -> Self {
139 let (msix, msix_cap) = MsixEmulator::new(4, caps.msix_count, msi_target);
140
141 let (tdisp_interface, tdisp_mmio_ranges) = if enable_tdisp {
145 let (emulator, ranges) = new_tdisp_interface("fault-controller-test", msix.bar_len());
146 (
147 Some(Box::new(emulator) as Box<dyn TdispHostDeviceTarget>),
148 ranges,
149 )
150 } else {
151 (None, TdispMmioRanges::default())
152 };
153 let bars = DeviceBars::new()
154 .bar0(
155 BAR0_LEN,
156 BarMemoryKind::Intercept(register_mmio.new_io_region("bar0", BAR0_LEN)),
157 )
158 .bar4(
159 msix.bar_len(),
160 BarMemoryKind::Intercept(register_mmio.new_io_region("msix", msix.bar_len())),
161 );
162
163 let hardware_config_fault = fault_configuration.hardware_config_fault.take();
167 let vendor_id = hardware_config_fault
168 .and_then(|f| f.vendor_id)
169 .unwrap_or(VENDOR_ID);
170 let device_id = hardware_config_fault
171 .and_then(|f| f.device_id)
172 .unwrap_or(DEVICE_ID);
173
174 let cfg_space = ConfigSpaceType0Emulator::new(
175 HardwareIds {
176 vendor_id,
177 device_id,
178 revision_id: 0,
179 prog_if: ProgrammingInterface::MASS_STORAGE_CONTROLLER_NON_VOLATILE_MEMORY_NVME,
180 sub_class: Subclass::MASS_STORAGE_CONTROLLER_NON_VOLATILE_MEMORY,
181 base_class: ClassCode::MASS_STORAGE_CONTROLLER,
182 type0_sub_vendor_id: 0,
183 type0_sub_system_id: 0,
184 },
185 vec![Box::new(msix_cap)],
186 Vec::new(),
187 bars,
188 );
189
190 let interrupts = (0..caps.msix_count)
191 .map(|i| msix.interrupt(i).unwrap())
192 .collect();
193
194 let pci_fault_config = fault_configuration
195 .pci_fault
196 .take()
197 .unwrap_or(PciFaultConfig::new());
198
199 let fault_active = fault_configuration.fault_active.clone();
200
201 let qe_sizes = Arc::new(Default::default());
202 let admin = NvmeWorkers::new(
203 driver_source,
204 guest_memory,
205 interrupts,
206 caps.max_io_queues,
207 caps.max_io_queues,
208 Arc::clone(&qe_sizes),
209 caps.subsystem_id,
210 fault_configuration,
211 );
212
213 Self {
214 cfg_space,
215 msix,
216 registers: RegState::new(),
217 workers: admin,
218 qe_sizes,
219 pci_fault_config,
220 fault_active,
221 tdisp_interface,
222 tdisp_mmio_ranges,
223 }
224 }
225
226 pub fn client(&self) -> NvmeFaultControllerClient {
228 self.workers.client()
229 }
230
231 pub fn read_bar0(&mut self, addr: u64, data: &mut [u8]) -> IoResult {
233 if data.len() < 4 {
234 return IoResult::Err(IoError::InvalidAccessSize);
235 }
236 if addr & (data.len() as u64 - 1) != 0 {
237 return IoResult::Err(IoError::UnalignedAccess);
238 }
239
240 let d: Option<u64> = match spec::Register(addr & !7) {
242 spec::Register::CAP => {
243 if let Some(mqes) = self.pci_fault_config.max_queue_size {
244 Some(CAP.with_mqes_z(mqes - 1).into())
245 } else {
246 Some(CAP.into())
247 }
248 }
249 spec::Register::ASQ => Some(self.registers.asq),
250 spec::Register::ACQ => Some(self.registers.acq),
251 spec::Register::BPMBL => Some(0),
252 _ => None,
253 };
254 if let Some(d) = d {
255 if data.len() == 8 {
256 data.copy_from_slice(&d.to_ne_bytes());
257 } else if addr & 7 == 0 {
258 data.copy_from_slice(&(d as u32).to_ne_bytes());
259 } else {
260 data.copy_from_slice(&((d >> 32) as u32).to_ne_bytes());
261 }
262 return IoResult::Ok;
263 }
264
265 if data.len() != 4 {
266 return IoResult::Err(IoError::InvalidAccessSize);
267 }
268
269 let d: u32 = match spec::Register(addr) {
271 spec::Register::VS => NVME_VERSION,
272 spec::Register::INTMS => self.registers.interrupt_mask,
273 spec::Register::INTMC => self.registers.interrupt_mask,
274 spec::Register::CC => self.registers.cc.into(),
275 spec::Register::RESERVED => 0,
276 spec::Register::CSTS => self.get_csts(),
277 spec::Register::NSSR => 0,
278 spec::Register::AQA => self.registers.aqa.into(),
279 spec::Register::CMBLOC => 0,
280 spec::Register::CMBSZ => 0,
281 spec::Register::BPINFO => 0,
282 spec::Register::BPRSEL => 0,
283 _ => return IoResult::Err(InvalidRegister),
284 };
285 data.copy_from_slice(&d.to_ne_bytes());
286 IoResult::Ok
287 }
288
289 pub fn write_bar0(&mut self, addr: u64, data: &[u8]) -> IoResult {
291 if addr >= 0x1000 {
292 let base = addr - 0x1000;
294 let db_id = base >> DOORBELL_STRIDE_BITS;
295 if (db_id << DOORBELL_STRIDE_BITS) != base {
296 return IoResult::Err(InvalidRegister);
297 }
298 let Ok(db_id) = u16::try_from(db_id) else {
299 return IoResult::Err(InvalidRegister);
300 };
301 let Ok(data) = data.try_into() else {
302 return IoResult::Err(IoError::InvalidAccessSize);
303 };
304 let value = u32::from_ne_bytes(data);
305 self.workers.doorbell(db_id, value);
306 return IoResult::Ok;
307 }
308
309 if data.len() < 4 {
310 return IoResult::Err(IoError::InvalidAccessSize);
311 }
312 if addr & (data.len() as u64 - 1) != 0 {
313 return IoResult::Err(IoError::UnalignedAccess);
314 }
315
316 let update_reg = |x: u64| {
317 if data.len() == 8 {
318 u64::from_ne_bytes(data.try_into().unwrap())
319 } else {
320 let data = u32::from_ne_bytes(data.try_into().unwrap()) as u64;
321 if addr & 7 == 0 {
322 (x & !(u32::MAX as u64)) | data
323 } else {
324 (x & u32::MAX as u64) | (data << 32)
325 }
326 }
327 };
328
329 let handled = match spec::Register(addr & !7) {
331 spec::Register::ASQ => {
332 if !self.registers.cc.en() {
333 self.registers.asq = update_reg(self.registers.asq) & PAGE_MASK;
334 } else {
335 tracelimit::warn_ratelimited!("attempt to set asq while enabled");
336 }
337 true
338 }
339 spec::Register::ACQ => {
340 if !self.registers.cc.en() {
341 self.registers.acq = update_reg(self.registers.acq) & PAGE_MASK;
342 } else {
343 tracelimit::warn_ratelimited!("attempt to set acq while enabled");
344 }
345 true
346 }
347 _ => false,
348 };
349 if handled {
350 return IoResult::Ok;
351 }
352
353 let Ok(data) = data.try_into() else {
354 return IoResult::Err(IoError::InvalidAccessSize);
355 };
356 let data = u32::from_ne_bytes(data);
357
358 match spec::Register(addr) {
360 spec::Register::INTMS => self.registers.interrupt_mask |= data,
361 spec::Register::INTMC => self.registers.interrupt_mask &= !data,
362 spec::Register::CC => self.set_cc(data.into()),
363 spec::Register::AQA => self.registers.aqa = data.into(),
364 _ => return IoResult::Err(InvalidRegister),
365 }
366 IoResult::Ok
367 }
368
369 fn set_cc(&mut self, cc: spec::Cc) {
370 tracing::debug!(?cc, "set cc");
371
372 if cc.mps() != 0 {
373 tracelimit::warn_ratelimited!(
374 "This implementation only supports memory page sizes of 4K."
375 );
376 self.fatal_error();
377 return;
378 }
379
380 if cc.css() != 0 {
381 tracelimit::warn_ratelimited!("This implementation only supports the NVM command set.");
382 self.fatal_error();
383 return;
384 }
385
386 if let 2..=6 = cc.ams() {
387 tracelimit::warn_ratelimited!("Undefined arbitration mechanism.");
388 self.fatal_error();
389 }
390
391 let mask: u32 = u32::from(
392 spec::Cc::new()
393 .with_en(true)
394 .with_shn(0b11)
395 .with_iosqes(0b1111)
396 .with_iocqes(0b1111),
397 );
398 let mut cc: spec::Cc = (u32::from(cc) & mask).into();
399
400 if cc.shn() != 0 {
401 self.registers.csts.set_shst(0b10);
405 }
406
407 if cc.en() != self.registers.cc.en() {
408 if cc.en() {
409 if self.fault_active.get() {
411 match &mut self.pci_fault_config.controller_management_fault_enable {
412 PciFaultBehavior::Delay(duration) => {
413 std::thread::sleep(*duration);
414 }
415 PciFaultBehavior::Default => {}
416 PciFaultBehavior::Verify(send) => {
417 if let Some(send) = send.take() {
418 send.send(());
419 }
420 }
421 }
422 }
423
424 if cc.iocqes() == 0 {
426 cc.set_iocqes(IOCQES);
427 } else if cc.iocqes() != IOCQES {
428 tracelimit::warn_ratelimited!(
429 "This implementation only supports CQEs of the default size."
430 );
431 self.fatal_error();
432 return;
433 }
434
435 if cc.iosqes() == 0 {
436 cc.set_iosqes(IOSQES);
437 } else if cc.iosqes() != IOSQES {
438 tracelimit::warn_ratelimited!(
439 "This implementation only supports SQEs of the default size."
440 );
441 self.fatal_error();
442 return;
443 }
444
445 if self.registers.csts.rdy() {
446 tracelimit::warn_ratelimited!("enabling during reset");
447 return;
448 }
449 if cc.shn() == 0 {
450 self.registers.csts.set_shst(0);
451 }
452
453 self.workers.enable(
454 self.registers.asq,
455 self.registers.aqa.asqs_z().max(1) + 1,
456 self.registers.acq,
457 self.registers.aqa.acqs_z().max(1) + 1,
458 );
459 } else if self.registers.csts.rdy() {
460 self.workers.controller_reset();
461 } else {
462 tracelimit::warn_ratelimited!("disabling while not ready");
463 return;
464 }
465 }
466
467 self.registers.cc = cc;
468 *self.qe_sizes.lock() = IoQueueEntrySizes {
469 sqe_bits: cc.iosqes(),
470 cqe_bits: cc.iocqes(),
471 };
472 }
473
474 fn get_csts(&mut self) -> u32 {
475 if !self.registers.cc.en() && self.registers.csts.rdy() {
476 if self.workers.poll_controller_reset() {
478 self.registers.csts = 0.into();
480 self.registers.cc = 0.into();
481 self.registers.interrupt_mask = 0;
482 }
483 } else if self.registers.cc.en() && !self.registers.csts.rdy() {
484 if self.workers.poll_enabled() {
485 self.registers.csts.set_rdy(true);
486 }
487 }
488
489 let csts = self.registers.csts;
490 tracing::debug!(?csts, "get csts");
491 csts.into()
492 }
493
494 pub fn fatal_error(&mut self) {
497 self.registers.csts.set_cfs(true);
498 }
499}
500
501impl ChangeDeviceState for NvmeFaultController {
502 fn start(&mut self) {}
503
504 async fn stop(&mut self) {}
505
506 async fn reset(&mut self) {
507 let Self {
508 cfg_space,
509 msix: _,
510 registers,
511 qe_sizes,
512 workers,
513 pci_fault_config: _,
514 fault_active: _,
515 tdisp_interface: _,
516 tdisp_mmio_ranges: _,
517 } = self;
518 workers.reset().await;
519 cfg_space.reset();
520 *registers = RegState::new();
521 *qe_sizes.lock() = Default::default();
522 }
523}
524
525impl ChipsetDevice for NvmeFaultController {
526 fn supports_mmio(&mut self) -> Option<&mut dyn MmioIntercept> {
527 Some(self)
528 }
529
530 fn supports_pci(&mut self) -> Option<&mut dyn PciConfigSpace> {
531 Some(self)
532 }
533
534 fn supports_tdisp_host(&mut self) -> Option<&mut dyn TdispHostDeviceTarget> {
536 tracing::debug!(
537 supported = self.tdisp_interface.is_some(),
538 "fault controller TDISP support in ChipsetDevice"
539 );
540
541 match &mut self.tdisp_interface {
542 Some(tdisp) => Some(tdisp.as_mut()),
543 None => None,
544 }
545 }
546}
547
548impl NvmeFaultController {
549 fn bar0_reachable(&self) -> bool {
555 self.tdisp_interface.is_none() || self.tdisp_mmio_ranges.is_unblocked(BAR0_RANGE_ID)
556 }
557}
558
559impl MmioIntercept for NvmeFaultController {
560 fn mmio_read(&mut self, addr: u64, data: &mut [u8]) -> IoResult {
561 match self.cfg_space.find_bar(addr) {
562 Some((0, _)) if !self.bar0_reachable() => {
563 tracelimit::warn_ratelimited!(
567 addr,
568 "read of a TDISP register BAR whose range is blocked"
569 );
570 data.fill(!0);
571 IoResult::Ok
572 }
573 Some((0, offset)) => self.read_bar0(offset, data),
574 Some((4, offset)) => {
575 read_as_u32_chunks(offset, data, |offset| self.msix.read_u32(offset));
576 IoResult::Ok
577 }
578 _ => IoResult::Err(InvalidRegister),
579 }
580 }
581
582 fn mmio_write(&mut self, addr: u64, data: &[u8]) -> IoResult {
583 match self.cfg_space.find_bar(addr) {
584 Some((0, _)) if !self.bar0_reachable() => {
585 tracelimit::warn_ratelimited!(
586 addr,
587 "write to a TDISP register BAR whose range is blocked"
588 );
589 IoResult::Ok
590 }
591 Some((0, offset)) => self.write_bar0(offset, data),
592 Some((4, offset)) => {
593 write_as_u32_chunks(offset, data, |offset, ty| match ty {
594 ReadWriteRequestType::Read => Some(self.msix.read_u32(offset)),
595 ReadWriteRequestType::Write(val) => {
596 self.msix.write_u32(offset, val);
597 None
598 }
599 });
600 IoResult::Ok
601 }
602 _ => IoResult::Err(InvalidRegister),
603 }
604 }
605}
606
607impl PciConfigSpace for NvmeFaultController {
608 fn pci_cfg_read(&mut self, offset: u16, value: ByteEnabledDwordRead<'_>) -> IoResult {
609 self.cfg_space.read_byte_enabled(offset, value)
610 }
611
612 fn pci_cfg_write(&mut self, offset: u16, value: ByteEnabledDwordWrite) -> IoResult {
613 self.cfg_space.write_byte_enabled(offset, value)
614 }
615}
616
617impl SaveRestore for NvmeFaultController {
618 type SavedState = SavedStateNotSupported;
619
620 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
621 Err(SaveError::NotSupported)
622 }
623
624 fn restore(
625 &mut self,
626 state: Self::SavedState,
627 ) -> Result<(), vmcore::save_restore::RestoreError> {
628 match state {}
629 }
630}