Skip to main content

nvme_driver/
nvme.rs

1use alloc::vec::Vec;
2use core::ptr::NonNull;
3
4use dma_api::{CoherentArray, DeviceDma, DmaDirection, DmaOp};
5use log::{debug, info};
6use mmio_api::{Mmio, MmioAddr, MmioOp};
7
8use crate::{
9    command::{
10        self, ControllerInfo, Feature, Identify, IdentifyActiveNamespaceList, IdentifyController,
11        IdentifyNamespaceDataStructure,
12    },
13    err::*,
14    queue::{CommandSet, NvmeQueue},
15    registers::NvmeReg,
16};
17
18pub struct Nvme {
19    bar: NonNull<NvmeReg>,
20    _mmio: Option<Mmio>,
21    dma: DeviceDma,
22    admin_queue: NvmeQueue,
23    io_queues: Vec<Option<NvmeQueue>>,
24    num_ns: usize,
25    sqes: u32,
26    cqes: u32,
27    page_size: usize,
28    io_queue_interrupts: bool,
29    interrupt_vector: u32,
30}
31
32#[derive(Debug, Clone, Copy)]
33pub struct Config {
34    pub page_size: usize,
35    pub io_queue_pair_count: usize,
36    pub io_queue_interrupts: bool,
37    pub interrupt_vector: u32,
38}
39
40impl Config {
41    pub const fn new(page_size: usize, io_queue_pair_count: usize) -> Self {
42        Self {
43            page_size,
44            io_queue_pair_count,
45            io_queue_interrupts: false,
46            interrupt_vector: 0,
47        }
48    }
49
50    pub const fn with_intx_irq(mut self) -> Self {
51        self.io_queue_interrupts = true;
52        self.interrupt_vector = 0;
53        self
54    }
55}
56
57impl Nvme {
58    pub fn new(
59        bar_addr: impl Into<MmioAddr>,
60        bar_size: usize,
61        dma_mask: u64,
62        dma_op: &'static dyn DmaOp,
63        mmio_op: &'static dyn MmioOp,
64        config: Config,
65    ) -> Result<Self> {
66        mmio_api::init(mmio_op);
67        let mmio = mmio_api::ioremap(bar_addr.into(), bar_size)?;
68        let dma = DeviceDma::new(dma_mask, dma_op);
69        Self::new_mmio(mmio, dma, config)
70    }
71
72    fn new_mmio(mmio: Mmio, dma: DeviceDma, config: Config) -> Result<Self> {
73        let bar = NonNull::new(mmio.as_ptr()).expect("mmio mapping must not be null");
74        Self::new_with_bar(bar.cast(), Some(mmio), dma, config)
75    }
76
77    fn new_with_bar(
78        bar: NonNull<NvmeReg>,
79        mmio: Option<Mmio>,
80        dma: DeviceDma,
81        config: Config,
82    ) -> Result<Self> {
83        let admin_queue = NvmeQueue::new(0, bar, &dma, config.page_size, 64, 64)?;
84
85        assert!(config.io_queue_pair_count > 0);
86
87        let mut s = Self {
88            bar,
89            _mmio: mmio,
90            dma,
91            admin_queue,
92            io_queues: Vec::new(),
93            num_ns: 0,
94            sqes: 6,
95            cqes: 4,
96            page_size: config.page_size,
97            io_queue_interrupts: config.io_queue_interrupts,
98            interrupt_vector: config.interrupt_vector,
99        };
100
101        let version = s.version();
102
103        info!(
104            "NVME @{bar:?} init begin, version: {}.{}.{} ",
105            version.0, version.1, version.2
106        );
107
108        s.init(config)?;
109
110        Ok(s)
111    }
112
113    pub fn dma_mask(&self) -> u64 {
114        self.dma.dma_mask()
115    }
116
117    fn reset(&mut self) {
118        self.reg().reset();
119    }
120
121    fn reset_and_setup_controller_info(&mut self) -> Result<ControllerInfo> {
122        self.reset();
123        self.nvme_configure_admin_queue();
124        self.reg().ready_for_read_controller_info();
125
126        self.get_identfy(IdentifyController::new())
127    }
128
129    fn init(&mut self, config: Config) -> Result {
130        let controller = self.reset_and_setup_controller_info()?;
131
132        debug!("Controller: {:?}", controller);
133
134        self.sqes = controller.sqes_min as _;
135        self.cqes = controller.cqes_min as _;
136        self.reset();
137        self.nvme_configure_admin_queue();
138        self.reg().setup_cc(self.sqes, self.cqes);
139        let controller = self.get_identfy(IdentifyController::new())?;
140
141        debug!("Controller: {:?}", controller);
142
143        self.num_ns = controller.number_of_namespaces as _;
144        if config.io_queue_interrupts {
145            self.mask_interrupt_vector(config.interrupt_vector);
146        }
147        self.config_io_queue(config)?;
148
149        debug!("IO queue ok.");
150        loop {
151            let ns = self.get_identfy(IdentifyNamespaceDataStructure::new(1))?;
152            if let Some(ns) = ns {
153                debug!("Namespace: {:?}", ns);
154                break;
155            }
156        }
157        debug!("Namespace ok.");
158        Ok(())
159    }
160
161    pub fn namespace_list(&mut self) -> Result<Vec<Namespace>> {
162        let id_list = self.get_identfy(IdentifyActiveNamespaceList::new())?;
163        let mut out = Vec::new();
164
165        for id in id_list {
166            let ns = self
167                .get_identfy(IdentifyNamespaceDataStructure::new(id))?
168                .unwrap();
169
170            out.push(Namespace {
171                id,
172                lba_size: ns.lba_size as _,
173                lba_count: ns.namespace_size as _,
174                metadata_size: ns.metadata_size as _,
175            });
176        }
177
178        Ok(out)
179    }
180
181    // config admin queue
182    // 1. set admin queue(cq && sq) size
183    // 2. set admin queue(cq && sq) dma address
184    // 3. enable ctrl
185    fn nvme_configure_admin_queue(&mut self) {
186        self.reg().set_admin_submission_and_completion_queue_size(
187            self.admin_queue.sq_len(),
188            self.admin_queue.cq_len(),
189        );
190
191        self.reg()
192            .set_admin_submission_queue_base_address(self.admin_queue.sq_bus_addr());
193
194        self.reg()
195            .set_admin_completion_queue_base_address(self.admin_queue.cq_bus_addr());
196    }
197
198    fn config_io_queue(&mut self, config: Config) -> Result {
199        let num = config.io_queue_pair_count;
200        // 设置 io queue 数量
201        let cmd = CommandSet::set_features(Feature::NumberOfQueues {
202            nsq: num as u32 - 1,
203            ncq: num as u32 - 1,
204        });
205        self.admin_queue.command_sync(cmd)?;
206
207        for i in 0..num {
208            let id = (i + 1) as u32;
209            let io_queue = NvmeQueue::new(
210                id,
211                self.bar,
212                &self.dma,
213                config.page_size,
214                2usize.pow(self.sqes as _),
215                2usize.pow(self.cqes as _),
216            )?;
217
218            let data = CommandSet::create_io_completion_queue(
219                io_queue.qid,
220                io_queue.cq_len() as _,
221                io_queue.cq_bus_addr(),
222                true,
223                config.io_queue_interrupts,
224                config.interrupt_vector,
225            );
226            self.admin_queue.command_sync(data)?;
227
228            let data = CommandSet::create_io_submission_queue(
229                io_queue.qid,
230                io_queue.sq_len() as _,
231                io_queue.sq_bus_addr(),
232                true,
233                0,
234                io_queue.qid,
235                0,
236            );
237
238            self.admin_queue.command_sync(data)?;
239
240            self.io_queues.push(Some(io_queue));
241        }
242
243        Ok(())
244    }
245
246    pub fn io_queue_count(&self) -> usize {
247        self.io_queues.len()
248    }
249
250    pub fn page_size(&self) -> usize {
251        self.page_size
252    }
253
254    pub fn io_queue_interrupts_enabled(&self) -> bool {
255        self.io_queue_interrupts
256    }
257
258    pub fn interrupt_vector(&self) -> u32 {
259        self.interrupt_vector
260    }
261
262    pub fn mask_interrupt_vector(&mut self, vector: u32) {
263        self.reg().mask_interrupt_vector(vector);
264    }
265
266    pub fn unmask_interrupt_vector(&mut self, vector: u32) {
267        self.reg().unmask_interrupt_vector(vector);
268    }
269
270    pub(crate) fn take_io_queue(&mut self, index: usize) -> Option<NvmeQueue> {
271        self.io_queues.get_mut(index)?.take()
272    }
273
274    pub(crate) fn alloc_prp_list(&self) -> Result<CoherentArray<u64>> {
275        self.dma
276            .coherent_array_zero_with_align(
277                self.page_size / core::mem::size_of::<u64>(),
278                self.page_size,
279            )
280            .map_err(Into::into)
281    }
282
283    pub fn get_identfy<T: Identify>(&mut self, mut want: T) -> Result<T::Output> {
284        let cmd = want.command_set_mut();
285
286        cmd.cdw0 = CommandSet::cdw0_from_opcode(command::Opcode::IDENTIFY);
287        cmd.cdw10 = T::CNS;
288
289        let buff = self.dma.contiguous_array_zero_with_align::<u8>(
290            0x1000,
291            0x1000,
292            DmaDirection::FromDevice,
293        )?;
294        cmd.prp1 = buff.dma_addr().as_u64();
295
296        self.admin_queue.command_sync(*cmd)?;
297
298        let data = buff.read_from_device(buff.len(), |data| data.to_vec());
299        let res = want.parse(&data);
300        Ok(res)
301    }
302
303    pub fn block_write_sync(
304        &mut self,
305        ns: &Namespace,
306        block_start: u64,
307        buff: &[u8],
308    ) -> Result<()> {
309        assert!(
310            buff.len().is_multiple_of(ns.lba_size),
311            "buffer size must be multiple of lba size"
312        );
313
314        let mut dma_buff = self.dma.contiguous_array_zero_with_align::<u8>(
315            buff.len(),
316            ns.lba_size,
317            DmaDirection::ToDevice,
318        )?;
319        dma_buff.copy_to_device_from_slice(buff);
320
321        let blk_num = dma_buff.len() / ns.lba_size;
322
323        let cmd = CommandSet::nvm_cmd_write(
324            ns.id,
325            dma_buff.dma_addr().as_u64(),
326            block_start,
327            blk_num as _,
328        );
329
330        self.io_queues
331            .get_mut(0)
332            .and_then(Option::as_mut)
333            .ok_or(Error::Unknown("missing IO queue"))?
334            .command_sync(cmd)?;
335
336        Ok(())
337    }
338
339    pub fn block_read_sync(
340        &mut self,
341        ns: &Namespace,
342        block_start: u64,
343        buff: &mut [u8],
344    ) -> Result<()> {
345        assert!(
346            buff.len().is_multiple_of(ns.lba_size),
347            "buffer size must be multiple of lba size"
348        );
349
350        let dma_buff = self.dma.contiguous_array_zero_with_align::<u8>(
351            buff.len(),
352            ns.lba_size,
353            DmaDirection::FromDevice,
354        )?;
355
356        let blk_num = dma_buff.len() / ns.lba_size;
357
358        let cmd = CommandSet::nvm_cmd_read(
359            ns.id,
360            dma_buff.dma_addr().as_u64(),
361            block_start,
362            blk_num as _,
363        );
364
365        self.io_queues
366            .get_mut(0)
367            .and_then(Option::as_mut)
368            .ok_or(Error::Unknown("missing IO queue"))?
369            .command_sync(cmd)?;
370        dma_buff.copy_from_device_to_slice(buff);
371        Ok(())
372    }
373
374    pub fn version(&self) -> (usize, usize, usize) {
375        self.reg().version()
376    }
377
378    fn reg(&self) -> &NvmeReg {
379        unsafe { self.bar.as_ref() }
380    }
381}
382
383unsafe impl Send for Nvme {}
384
385#[derive(Debug, Clone, Copy)]
386pub struct Namespace {
387    pub id: u32,
388    pub lba_size: usize,
389    pub lba_count: usize,
390    pub metadata_size: usize,
391}
392
393#[cfg(test)]
394mod tests {
395    use super::Config;
396
397    #[test]
398    fn config_defaults_to_polling_and_can_enable_intx() {
399        let config = Config::new(4096, 1);
400        assert!(!config.io_queue_interrupts);
401        assert_eq!(config.interrupt_vector, 0);
402
403        let irq_config = config.with_intx_irq();
404        assert!(irq_config.io_queue_interrupts);
405        assert_eq!(irq_config.interrupt_vector, 0);
406    }
407}