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::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/// An NVMe controller.
60#[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    /// The NVMe fault controller is repurposed for use in TDISP tests.
75    #[inspect(skip)]
76    tdisp_interface: Option<Box<dyn TdispHostDeviceTarget>>,
77    /// The MMIO ranges TDISP currently allows the guest to reach. Empty, and so
78    /// blocking every range, on a controller that is not a TDISP device.
79    #[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/// The NVMe controller's capabilities.
117#[derive(Debug, Copy, Clone)]
118pub struct NvmeFaultControllerCaps {
119    /// The number of entries in the MSI-X table.
120    pub msix_count: u16,
121    /// The maximum number of IO submission and completion queues.
122    pub max_io_queues: u16,
123    /// The subsystem ID, used as part of the subnqn field of the identify
124    /// controller response.
125    pub subsystem_id: Guid,
126}
127
128impl NvmeFaultController {
129    /// Creates a new NVMe controller.
130    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        // The fault controller is repurposed as an emulated TDISP device. Its
142        // own TDISP interface reports the BARs below and records which of them
143        // the guest has been allowed to reach.
144        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        // Apply any hardware-config fault overrides for the IDs reported in
164        // PCI configuration space, falling back to the real values when no
165        // override is configured.
166        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    /// Returns a client for manipulating the NVMe controller at runtime.
227    pub fn client(&self) -> NvmeFaultControllerClient {
228        self.workers.client()
229    }
230
231    /// Reads from the virtual BAR 0.
232    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        // Check for 64-bit registers.
241        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        // Handle 32-bit registers.
270        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    /// Writes to the virtual BAR 0.
290    pub fn write_bar0(&mut self, addr: u64, data: &[u8]) -> IoResult {
291        if addr >= 0x1000 {
292            // Doorbell write.
293            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        // Check for 64-bit registers.
330        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        // Handle 32-bit registers.
359        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            // It is unclear in the spec (to me) what guarantees a
402            // controller is supposed to make after shutdown. For now, just
403            // complete shutdown immediately.
404            self.registers.csts.set_shst(0b10);
405        }
406
407        if cc.en() != self.registers.cc.en() {
408            if cc.en() {
409                // If any fault was configured for cc.en() process it here
410                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                // Some drivers will write zeros to IOSQES and IOCQES, assuming that the defaults will work.
425                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            // Keep trying to disable.
477            if self.workers.poll_controller_reset() {
478                // AQA, ASQ, and ACQ are not reset by controller reset.
479                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    /// Sets the CFS bit in the controller status register (CSTS), indicating
495    /// that the controller has experienced "undefined" behavior.
496    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    /// The NVMe fault controller is repurposed for use in TDISP tests.
535    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    /// Whether the guest may reach the register BAR right now.
550    ///
551    /// On a TDISP device the register BAR holds TEE memory, so it stays dark
552    /// until the guest has attested the TDI and accepted the range. A
553    /// controller that is not acting as a TDISP device has no such restriction.
554    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                // Read as an undecoded window rather than an error, so the
564                // caller sees the same all-ones a real device gives when
565                // nothing answers.
566                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}