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