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