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 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 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}