Skip to main content

nvme/
pci.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! The NVMe 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::NvmeControllerClient;
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 guid::Guid;
32use inspect::Inspect;
33use inspect::InspectMut;
34use parking_lot::Mutex;
35use pci_core::capabilities::msix::MsixEmulator;
36use pci_core::capabilities::pci_express::PciExpressCapability;
37use pci_core::cfg_space_emu::BarMemoryKind;
38use pci_core::cfg_space_emu::ConfigSpaceType0Emulator;
39use pci_core::cfg_space_emu::DeviceBars;
40use pci_core::dma::DmaTarget;
41use pci_core::spec::hwid::ClassCode;
42use pci_core::spec::hwid::HardwareIds;
43use pci_core::spec::hwid::ProgrammingInterface;
44use pci_core::spec::hwid::Subclass;
45use std::sync::Arc;
46use vmcore::device_state::ChangeDeviceState;
47use vmcore::save_restore::SaveError;
48use vmcore::save_restore::SaveRestore;
49use vmcore::save_restore::SavedStateNotSupported;
50use vmcore::vm_task::VmTaskDriverSource;
51
52/// An NVMe controller.
53#[derive(InspectMut)]
54pub struct NvmeController {
55    cfg_space: ConfigSpaceType0Emulator,
56    #[inspect(skip)]
57    msix: MsixEmulator,
58
59    registers: RegState,
60    #[inspect(skip)]
61    qe_sizes: Arc<Mutex<IoQueueEntrySizes>>,
62    #[inspect(flatten, mut)]
63    workers: NvmeWorkers,
64}
65
66#[derive(Inspect)]
67struct RegState {
68    #[inspect(hex)]
69    interrupt_mask: u32,
70    cc: spec::Cc,
71    csts: spec::Csts,
72    aqa: spec::Aqa,
73    #[inspect(hex)]
74    asq: u64,
75    #[inspect(hex)]
76    acq: u64,
77}
78
79impl RegState {
80    fn new() -> Self {
81        Self {
82            interrupt_mask: 0,
83            cc: spec::Cc::new(),
84            csts: spec::Csts::new(),
85            aqa: spec::Aqa::new(),
86            asq: 0,
87            acq: 0,
88        }
89    }
90}
91
92const CAP: spec::Cap = spec::Cap::new()
93    .with_dstrd(DOORBELL_STRIDE_BITS - 2)
94    .with_mqes_z(MAX_QES - 1)
95    .with_cqr(true)
96    .with_css_nvm(true)
97    .with_to(!0);
98
99/// The NVMe controller's capabilities.
100#[derive(Debug, Copy, Clone)]
101pub struct NvmeControllerCaps {
102    /// The number of entries in the MSI-X table.
103    pub msix_count: u16,
104    /// The maximum number of IO submission and completion queues.
105    pub max_io_queues: u16,
106    /// The subsystem ID, used as part of the subnqn field of the identify
107    /// controller response.
108    pub subsystem_id: Guid,
109}
110
111impl NvmeController {
112    /// Creates a new NVMe controller.
113    pub fn new(
114        driver_source: &VmTaskDriverSource,
115        dma_target: &DmaTarget,
116        register_mmio: &mut dyn RegisterMmioIntercept,
117        caps: NvmeControllerCaps,
118    ) -> Self {
119        let msi_target = dma_target.msi_target();
120        let guest_memory = dma_target.guest_memory().clone();
121        let (msix, msix_cap) = MsixEmulator::new(4, caps.msix_count, msi_target);
122        let bars = DeviceBars::new()
123            .bar0(
124                BAR0_LEN,
125                BarMemoryKind::Intercept(register_mmio.new_io_region("bar0", BAR0_LEN)),
126            )
127            .bar4(
128                msix.bar_len(),
129                BarMemoryKind::Intercept(register_mmio.new_io_region("msix", msix.bar_len())),
130            );
131
132        let cfg_space = ConfigSpaceType0Emulator::new(
133            HardwareIds {
134                vendor_id: VENDOR_ID,
135                device_id: DEVICE_ID,
136                revision_id: 0,
137                prog_if: ProgrammingInterface::MASS_STORAGE_CONTROLLER_NON_VOLATILE_MEMORY_NVME,
138                sub_class: Subclass::MASS_STORAGE_CONTROLLER_NON_VOLATILE_MEMORY,
139                base_class: ClassCode::MASS_STORAGE_CONTROLLER,
140                type0_sub_vendor_id: 0,
141                type0_sub_system_id: 0,
142            },
143            vec![
144                Box::new(msix_cap),
145                Box::new(PciExpressCapability::new(
146                    pci_core::spec::caps::pci_express::DevicePortType::Endpoint,
147                    None,
148                )),
149            ],
150            Vec::new(),
151            bars,
152        );
153
154        let interrupts = (0..caps.msix_count)
155            .map(|i| msix.interrupt(i).unwrap())
156            .collect();
157
158        let qe_sizes = Arc::new(Default::default());
159        let admin = NvmeWorkers::new(
160            driver_source,
161            guest_memory,
162            interrupts,
163            caps.max_io_queues,
164            caps.max_io_queues,
165            Arc::clone(&qe_sizes),
166            caps.subsystem_id,
167        );
168
169        Self {
170            cfg_space,
171            msix,
172            registers: RegState::new(),
173            workers: admin,
174            qe_sizes,
175        }
176    }
177
178    /// Returns a client for manipulating the NVMe controller at runtime.
179    pub fn client(&self) -> NvmeControllerClient {
180        self.workers.client()
181    }
182
183    /// Reads from the virtual BAR 0.
184    pub fn read_bar0(&mut self, addr: u64, data: &mut [u8]) -> IoResult {
185        if data.len() < 4 {
186            return IoResult::Err(IoError::InvalidAccessSize);
187        }
188        if addr & (data.len() as u64 - 1) != 0 {
189            return IoResult::Err(IoError::UnalignedAccess);
190        }
191
192        // Check for 64-bit registers.
193        let d: Option<u64> = match spec::Register(addr & !7) {
194            spec::Register::CAP => Some(CAP.into()),
195            spec::Register::ASQ => Some(self.registers.asq),
196            spec::Register::ACQ => Some(self.registers.acq),
197            spec::Register::BPMBL => Some(0),
198            _ => None,
199        };
200        if let Some(d) = d {
201            if data.len() == 8 {
202                data.copy_from_slice(&d.to_ne_bytes());
203            } else if addr & 7 == 0 {
204                data.copy_from_slice(&(d as u32).to_ne_bytes());
205            } else {
206                data.copy_from_slice(&((d >> 32) as u32).to_ne_bytes());
207            }
208            return IoResult::Ok;
209        }
210
211        if data.len() != 4 {
212            return IoResult::Err(IoError::InvalidAccessSize);
213        }
214
215        // Handle 32-bit registers.
216        let d: u32 = match spec::Register(addr) {
217            spec::Register::VS => NVME_VERSION,
218            spec::Register::INTMS => self.registers.interrupt_mask,
219            spec::Register::INTMC => self.registers.interrupt_mask,
220            spec::Register::CC => self.registers.cc.into(),
221            spec::Register::RESERVED => 0,
222            spec::Register::CSTS => self.get_csts(),
223            spec::Register::NSSR => 0,
224            spec::Register::AQA => self.registers.aqa.into(),
225            spec::Register::CMBLOC => 0,
226            spec::Register::CMBSZ => 0,
227            spec::Register::BPINFO => 0,
228            spec::Register::BPRSEL => 0,
229            _ => return IoResult::Err(InvalidRegister),
230        };
231        data.copy_from_slice(&d.to_ne_bytes());
232        IoResult::Ok
233    }
234
235    /// Writes to the virtual BAR 0.
236    pub fn write_bar0(&mut self, addr: u64, data: &[u8]) -> IoResult {
237        if addr >= 0x1000 {
238            // Doorbell write.
239            let base = addr - 0x1000;
240            let db_id = base >> DOORBELL_STRIDE_BITS;
241            if (db_id << DOORBELL_STRIDE_BITS) != base {
242                return IoResult::Err(InvalidRegister);
243            }
244            let Ok(data) = data.try_into() else {
245                return IoResult::Err(IoError::InvalidAccessSize);
246            };
247            let value = u32::from_ne_bytes(data);
248            let db_id = match u16::try_from(db_id) {
249                Ok(id) => id,
250                Err(_) => return IoResult::Err(InvalidRegister),
251            };
252            self.workers.doorbell(db_id, value);
253            return IoResult::Ok;
254        }
255
256        if data.len() < 4 {
257            return IoResult::Err(IoError::InvalidAccessSize);
258        }
259        if addr & (data.len() as u64 - 1) != 0 {
260            return IoResult::Err(IoError::UnalignedAccess);
261        }
262
263        let update_reg = |x: u64| {
264            if data.len() == 8 {
265                u64::from_ne_bytes(data.try_into().unwrap())
266            } else {
267                let data = u32::from_ne_bytes(data.try_into().unwrap()) as u64;
268                if addr & 7 == 0 {
269                    (x & !(u32::MAX as u64)) | data
270                } else {
271                    (x & u32::MAX as u64) | (data << 32)
272                }
273            }
274        };
275
276        // Check for 64-bit registers.
277        let handled = match spec::Register(addr & !7) {
278            spec::Register::ASQ => {
279                if !self.registers.cc.en() {
280                    self.registers.asq = update_reg(self.registers.asq) & PAGE_MASK;
281                } else {
282                    tracelimit::warn_ratelimited!("attempt to set asq while enabled");
283                }
284                true
285            }
286            spec::Register::ACQ => {
287                if !self.registers.cc.en() {
288                    self.registers.acq = update_reg(self.registers.acq) & PAGE_MASK;
289                } else {
290                    tracelimit::warn_ratelimited!("attempt to set acq while enabled");
291                }
292                true
293            }
294            _ => false,
295        };
296        if handled {
297            return IoResult::Ok;
298        }
299
300        let Ok(data) = data.try_into() else {
301            return IoResult::Err(IoError::InvalidAccessSize);
302        };
303        let data = u32::from_ne_bytes(data);
304
305        // Handle 32-bit registers.
306        match spec::Register(addr) {
307            spec::Register::INTMS => self.registers.interrupt_mask |= data,
308            spec::Register::INTMC => self.registers.interrupt_mask &= !data,
309            spec::Register::CC => self.set_cc(data.into()),
310            spec::Register::AQA => self.registers.aqa = data.into(),
311            _ => return IoResult::Err(InvalidRegister),
312        }
313        IoResult::Ok
314    }
315
316    fn set_cc(&mut self, cc: spec::Cc) {
317        tracing::debug!(?cc, "set cc");
318
319        if cc.mps() != 0 {
320            tracelimit::warn_ratelimited!(
321                "This implementation only supports memory page sizes of 4K."
322            );
323            self.fatal_error();
324            return;
325        }
326
327        if cc.css() != 0 {
328            tracelimit::warn_ratelimited!("This implementation only supports the NVM command set.");
329            self.fatal_error();
330            return;
331        }
332
333        if let 2..=6 = cc.ams() {
334            tracelimit::warn_ratelimited!("Undefined arbitration mechanism.");
335            self.fatal_error();
336        }
337
338        let mask: u32 = u32::from(
339            spec::Cc::new()
340                .with_en(true)
341                .with_shn(0b11)
342                .with_iosqes(0b1111)
343                .with_iocqes(0b1111),
344        );
345        let mut cc: spec::Cc = (u32::from(cc) & mask).into();
346
347        if cc.shn() != 0 {
348            // It is unclear in the spec (to me) what guarantees a
349            // controller is supposed to make after shutdown. For now, just
350            // complete shutdown immediately.
351            self.registers.csts.set_shst(0b10);
352        }
353
354        if cc.en() != self.registers.cc.en() {
355            if cc.en() {
356                // Some drivers will write zeros to IOSQES and IOCQES, assuming that the defaults will work.
357                if cc.iocqes() == 0 {
358                    cc.set_iocqes(IOCQES);
359                } else if cc.iocqes() != IOCQES {
360                    tracelimit::warn_ratelimited!(
361                        "This implementation only supports CQEs of the default size."
362                    );
363                    self.fatal_error();
364                    return;
365                }
366
367                if cc.iosqes() == 0 {
368                    cc.set_iosqes(IOSQES);
369                } else if cc.iosqes() != IOSQES {
370                    tracelimit::warn_ratelimited!(
371                        "This implementation only supports SQEs of the default size."
372                    );
373                    self.fatal_error();
374                    return;
375                }
376
377                if self.registers.csts.rdy() {
378                    tracelimit::warn_ratelimited!("enabling during reset");
379                    return;
380                }
381                if cc.shn() == 0 {
382                    self.registers.csts.set_shst(0);
383                }
384
385                self.workers.enable(
386                    self.registers.asq,
387                    self.registers.aqa.asqs_z().max(1) + 1,
388                    self.registers.acq,
389                    self.registers.aqa.acqs_z().max(1) + 1,
390                );
391            } else if self.registers.csts.rdy() {
392                self.workers.controller_reset();
393            } else {
394                tracelimit::warn_ratelimited!("disabling while not ready");
395                return;
396            }
397        }
398
399        self.registers.cc = cc;
400        *self.qe_sizes.lock() = IoQueueEntrySizes {
401            sqe_bits: cc.iosqes(),
402            cqe_bits: cc.iocqes(),
403        };
404    }
405
406    fn get_csts(&mut self) -> u32 {
407        if !self.registers.cc.en() && self.registers.csts.rdy() {
408            // Keep trying to disable.
409            if self.workers.poll_controller_reset() {
410                // AQA, ASQ, and ACQ are not reset by controller reset.
411                self.registers.csts = 0.into();
412                self.registers.cc = 0.into();
413                self.registers.interrupt_mask = 0;
414            }
415        } else if self.registers.cc.en() && !self.registers.csts.rdy() {
416            if self.workers.poll_enabled() {
417                self.registers.csts.set_rdy(true);
418            }
419        }
420
421        let csts = self.registers.csts;
422        tracing::debug!(?csts, "get csts");
423        csts.into()
424    }
425
426    /// Sets the CFS bit in the controller status register (CSTS), indicating
427    /// that the controller has experienced "undefined" behavior.
428    pub fn fatal_error(&mut self) {
429        self.registers.csts.set_cfs(true);
430    }
431}
432
433impl ChangeDeviceState for NvmeController {
434    fn start(&mut self) {}
435
436    async fn stop(&mut self) {}
437
438    async fn reset(&mut self) {
439        let Self {
440            cfg_space,
441            msix: _,
442            registers,
443            qe_sizes,
444            workers,
445        } = self;
446        workers.reset().await;
447        cfg_space.reset();
448        *registers = RegState::new();
449        *qe_sizes.lock() = Default::default();
450    }
451}
452
453impl ChipsetDevice for NvmeController {
454    fn supports_mmio(&mut self) -> Option<&mut dyn MmioIntercept> {
455        Some(self)
456    }
457
458    fn supports_pci(&mut self) -> Option<&mut dyn PciConfigSpace> {
459        Some(self)
460    }
461}
462
463impl MmioIntercept for NvmeController {
464    fn mmio_read(&mut self, addr: u64, data: &mut [u8]) -> IoResult {
465        match self.cfg_space.find_bar(addr) {
466            Some((0, offset)) => self.read_bar0(offset, data),
467            Some((4, offset)) => {
468                read_as_u32_chunks(offset, data, |offset| self.msix.read_u32(offset));
469                IoResult::Ok
470            }
471            _ => IoResult::Err(InvalidRegister),
472        }
473    }
474
475    fn mmio_write(&mut self, addr: u64, data: &[u8]) -> IoResult {
476        match self.cfg_space.find_bar(addr) {
477            Some((0, offset)) => self.write_bar0(offset, data),
478            Some((4, offset)) => {
479                write_as_u32_chunks(offset, data, |offset, ty| match ty {
480                    ReadWriteRequestType::Read => Some(self.msix.read_u32(offset)),
481                    ReadWriteRequestType::Write(val) => {
482                        self.msix.write_u32(offset, val);
483                        None
484                    }
485                });
486                IoResult::Ok
487            }
488            _ => IoResult::Err(InvalidRegister),
489        }
490    }
491}
492
493impl PciConfigSpace for NvmeController {
494    fn pci_cfg_read(&mut self, offset: u16, value: ByteEnabledDwordRead<'_>) -> IoResult {
495        self.cfg_space.read_byte_enabled(offset, value)
496    }
497
498    fn pci_cfg_write(&mut self, offset: u16, value: ByteEnabledDwordWrite) -> IoResult {
499        self.cfg_space.write_byte_enabled(offset, value)
500    }
501}
502
503impl SaveRestore for NvmeController {
504    type SavedState = SavedStateNotSupported;
505
506    fn save(&mut self) -> Result<Self::SavedState, SaveError> {
507        Err(SaveError::NotSupported)
508    }
509
510    fn restore(
511        &mut self,
512        state: Self::SavedState,
513    ) -> Result<(), vmcore::save_restore::RestoreError> {
514        match state {}
515    }
516}