Skip to main content

nvme_test/
pci.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! The NVMe (Fault Injection) PCI device implementation.
5
6use 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/// An NVMe controller.
57#[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    /// The NVMe fault controller is repurposed for use in TDISP tests.
72    #[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/// The NVMe controller's capabilities.
110#[derive(Debug, Copy, Clone)]
111pub struct NvmeFaultControllerCaps {
112    /// The number of entries in the MSI-X table.
113    pub msix_count: u16,
114    /// The maximum number of IO submission and completion queues.
115    pub max_io_queues: u16,
116    /// The subsystem ID, used as part of the subnqn field of the identify
117    /// controller response.
118    pub subsystem_id: Guid,
119}
120
121impl NvmeFaultController {
122    /// Creates a new NVMe controller.
123    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        // Apply any hardware-config fault overrides for the IDs reported in
144        // PCI configuration space, falling back to the real values when no
145        // override is configured.
146        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    /// Returns a client for manipulating the NVMe controller at runtime.
206    pub fn client(&self) -> NvmeFaultControllerClient {
207        self.workers.client()
208    }
209
210    /// Reads from the virtual BAR 0.
211    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        // Check for 64-bit registers.
220        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        // Handle 32-bit registers.
249        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    /// Writes to the virtual BAR 0.
269    pub fn write_bar0(&mut self, addr: u64, data: &[u8]) -> IoResult {
270        if addr >= 0x1000 {
271            // Doorbell write.
272            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        // Check for 64-bit registers.
309        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        // Handle 32-bit registers.
338        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            // It is unclear in the spec (to me) what guarantees a
381            // controller is supposed to make after shutdown. For now, just
382            // complete shutdown immediately.
383            self.registers.csts.set_shst(0b10);
384        }
385
386        if cc.en() != self.registers.cc.en() {
387            if cc.en() {
388                // If any fault was configured for cc.en() process it here
389                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                // Some drivers will write zeros to IOSQES and IOCQES, assuming that the defaults will work.
404                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            // Keep trying to disable.
456            if self.workers.poll_controller_reset() {
457                // AQA, ASQ, and ACQ are not reset by controller reset.
458                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    /// Sets the CFS bit in the controller status register (CSTS), indicating
474    /// that the controller has experienced "undefined" behavior.
475    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    /// The NVMe fault controller is repurposed for use in TDISP tests.
513    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}