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