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