1use core::{fmt::Debug, marker::PhantomData, mem::ManuallyDrop, ops::Range};
4
5use super::util::{
6 alloc_kva, cvm_need_private_protection, prepare_dma, split_daddr, unprepare_dma,
7};
8use crate::{
9 arch::{irq, mm::can_sync_dma},
10 error::Error,
11 mm::{
12 Daddr, FrameAllocOptions, HasDaddr, HasPaddr, HasPaddrRange, HasSize, Infallible,
13 PAGE_SIZE, Paddr, Split, USegment, VmReader, VmWriter,
14 io::util::{HasVmReaderWriter, VmReaderWriterResult},
15 kspace::kvirt_area::KVirtArea,
16 paddr_to_vaddr,
17 },
18};
19
20pub trait DmaDirection: 'static + Debug + private::Sealed {
23 const CAN_READ_FROM_DEVICE: bool;
25 const CAN_WRITE_TO_DEVICE: bool;
27}
28
29mod private {
30 pub trait Sealed {}
34}
35
36#[derive(Debug)]
40pub enum ToDevice {}
41
42impl private::Sealed for ToDevice {}
43impl DmaDirection for ToDevice {
44 const CAN_READ_FROM_DEVICE: bool = false;
45 const CAN_WRITE_TO_DEVICE: bool = true;
46}
47
48#[derive(Debug)]
52pub enum FromDevice {}
53
54impl private::Sealed for FromDevice {}
55impl DmaDirection for FromDevice {
56 const CAN_READ_FROM_DEVICE: bool = true;
57 const CAN_WRITE_TO_DEVICE: bool = false;
58}
59
60#[derive(Debug)]
62pub enum FromAndToDevice {}
63
64impl private::Sealed for FromAndToDevice {}
65impl DmaDirection for FromAndToDevice {
66 const CAN_READ_FROM_DEVICE: bool = true;
67 const CAN_WRITE_TO_DEVICE: bool = true;
68}
69
70#[derive(Debug)]
81pub struct DmaStream<D: DmaDirection = FromAndToDevice> {
82 inner: Inner,
83 map_daddr: Option<Daddr>,
84 is_cache_coherent: bool,
85 _phantom: PhantomData<D>,
86}
87
88#[derive(Debug)]
89enum Inner {
90 Segment(USegment),
91 Kva(KVirtArea, Paddr),
92 Both(KVirtArea, Paddr, USegment),
93}
94
95impl<D: DmaDirection> DmaStream<D> {
96 pub fn alloc(nframes: usize, is_cache_coherent: bool) -> Result<Self, Error> {
118 const { assert!(D::CAN_WRITE_TO_DEVICE) };
119
120 Self::alloc_uninit(nframes, is_cache_coherent).and_then(|dma| {
121 dma.writer()?.fill_zeros(dma.size());
122 Ok(dma)
123 })
124 }
125
126 pub fn alloc_uninit(nframes: usize, is_cache_coherent: bool) -> Result<Self, Error> {
135 debug_assert!(irq::is_local_enabled());
136
137 let cvm = cvm_need_private_protection();
138
139 let (inner, paddr_range) = if (can_sync_dma() || is_cache_coherent) && !cvm {
140 let segment: USegment = FrameAllocOptions::new()
141 .zeroed(false)
142 .alloc_segment(nframes)?
143 .into();
144 let paddr_range = segment.paddr_range();
145
146 (Inner::Segment(segment), paddr_range)
147 } else {
148 let (kva, paddr) = alloc_kva(nframes, can_sync_dma() || is_cache_coherent)?;
149
150 (Inner::Kva(kva, paddr), paddr..paddr + nframes * PAGE_SIZE)
151 };
152
153 let map_daddr = unsafe { prepare_dma(&paddr_range) };
155
156 Ok(Self {
157 inner,
158 map_daddr,
159 is_cache_coherent,
160 _phantom: PhantomData,
161 })
162 }
163
164 pub fn map(segment: USegment, is_cache_coherent: bool) -> Result<Self, Error> {
173 debug_assert!(irq::is_local_enabled());
174
175 let cvm = cvm_need_private_protection();
176 let size = segment.size();
177
178 let (inner, paddr) = if (can_sync_dma() || is_cache_coherent) && !cvm {
179 let paddr = segment.paddr();
180
181 (Inner::Segment(segment), paddr)
182 } else {
183 let (kva, paddr) = alloc_kva(size / PAGE_SIZE, can_sync_dma() || is_cache_coherent)?;
184
185 (Inner::Both(kva, paddr, segment), paddr)
186 };
187
188 let paddr_range = paddr..paddr + size;
189
190 let map_daddr = unsafe { prepare_dma(&paddr_range) };
192
193 Ok(Self {
194 inner,
195 map_daddr,
196 is_cache_coherent,
197 _phantom: PhantomData,
198 })
199 }
200
201 pub fn sync_from_device(&self, byte_range: Range<usize>) -> Result<(), Error> {
210 const { assert!(D::CAN_READ_FROM_DEVICE) };
211
212 self.sync_impl(byte_range, true)
213 }
214
215 pub fn sync_to_device(&self, byte_range: Range<usize>) -> Result<(), Error> {
224 const { assert!(D::CAN_WRITE_TO_DEVICE) };
225
226 self.sync_impl(byte_range, false)
227 }
228
229 fn sync_impl(&self, byte_range: Range<usize>, is_from_device: bool) -> Result<(), Error> {
230 let size = self.size();
231 if byte_range.start > byte_range.end || byte_range.start > size {
232 return Err(Error::InvalidArgs);
233 }
234
235 if !is_from_device && let Inner::Both(kva, _, seg) = &self.inner {
236 self.sync_via_copying(byte_range.clone(), false, seg, kva);
237 }
238
239 unsafe { self.sync_cache(byte_range.clone()) };
241
242 if is_from_device && let Inner::Both(kva, _, seg) = &self.inner {
243 self.sync_via_copying(byte_range, true, seg, kva);
244 }
245
246 Ok(())
247 }
248
249 unsafe fn sync_cache(&self, byte_range: Range<usize>) {
253 if self.is_cache_coherent {
254 return;
255 }
256
257 let va_range = match &self.inner {
258 Inner::Segment(segment) => {
259 let pa_range = segment.paddr_range();
260 paddr_to_vaddr(pa_range.start)..paddr_to_vaddr(pa_range.end)
261 }
262 Inner::Kva(kva, _) | Inner::Both(kva, _, _) => {
263 if !can_sync_dma() {
264 return;
266 }
267 kva.range()
268 }
269 };
270 let range = va_range.start + byte_range.start..va_range.start + byte_range.end;
271
272 unsafe { crate::arch::mm::sync_dma_range::<D>(range) };
278 }
279
280 fn sync_via_copying(
281 &self,
282 byte_range: Range<usize>,
283 is_from_device: bool,
284 seg: &USegment,
285 kva: &KVirtArea,
286 ) {
287 let skip = byte_range.start;
288 let limit = byte_range.len();
289
290 let (mut reader, mut writer) = if is_from_device {
291 let kva_reader =
296 unsafe { VmReader::from_kernel_space(kva.start() as *const u8, kva.size()) };
297
298 (kva_reader, seg.writer())
299 } else {
300 let kva_writer =
305 unsafe { VmWriter::from_kernel_space(kva.start() as *mut u8, kva.size()) };
306
307 (seg.reader(), kva_writer)
308 };
309
310 writer
311 .skip(skip)
312 .limit(limit)
313 .write(reader.skip(skip).limit(limit));
314 }
315}
316
317impl<D: DmaDirection> Split for DmaStream<D> {
318 fn split(self, offset: usize) -> (Self, Self) {
319 assert!(offset.is_multiple_of(PAGE_SIZE));
320 assert!(0 < offset && offset < self.size());
321
322 let (inner, map_daddr, is_cache_coherent) = {
323 let this = ManuallyDrop::new(self);
324 (
325 unsafe { core::ptr::read(&this.inner as *const Inner) },
327 this.map_daddr,
328 this.is_cache_coherent,
329 )
330 };
331
332 let (inner1, inner2) = match inner {
333 Inner::Segment(segment) => {
334 let (s1, s2) = segment.split(offset);
335 (Inner::Segment(s1), Inner::Segment(s2))
336 }
337 Inner::Kva(kva, paddr) => {
338 let (kva1, kva2) = kva.split(offset);
339 let (paddr1, paddr2) = (paddr, paddr + offset);
340 (Inner::Kva(kva1, paddr1), Inner::Kva(kva2, paddr2))
341 }
342 Inner::Both(kva, paddr, segment) => {
343 let (kva1, kva2) = kva.split(offset);
344 let (paddr1, paddr2) = (paddr, paddr + offset);
345 let (s1, s2) = segment.split(offset);
346 (Inner::Both(kva1, paddr1, s1), Inner::Both(kva2, paddr2, s2))
347 }
348 };
349
350 let (daddr1, daddr2) = split_daddr(map_daddr, offset);
351
352 (
353 Self {
354 inner: inner1,
355 map_daddr: daddr1,
356 is_cache_coherent,
357 _phantom: PhantomData,
358 },
359 Self {
360 inner: inner2,
361 map_daddr: daddr2,
362 is_cache_coherent,
363 _phantom: PhantomData,
364 },
365 )
366 }
367}
368
369impl<D: DmaDirection> Drop for DmaStream<D> {
370 fn drop(&mut self) {
371 unsafe { unprepare_dma(&self.paddr_range(), self.map_daddr) };
373 }
374}
375
376impl<D: DmaDirection> HasPaddr for DmaStream<D> {
377 fn paddr(&self) -> Paddr {
378 match &self.inner {
379 Inner::Segment(segment) => segment.paddr(),
380 Inner::Kva(_, paddr) | Inner::Both(_, paddr, _) => *paddr, }
382 }
383}
384
385impl<D: DmaDirection> HasDaddr for DmaStream<D> {
386 fn daddr(&self) -> Daddr {
387 self.map_daddr.unwrap_or_else(|| self.paddr() as Daddr)
388 }
389}
390
391impl<D: DmaDirection> HasSize for DmaStream<D> {
392 fn size(&self) -> usize {
393 match &self.inner {
394 Inner::Segment(segment) => segment.size(),
395 Inner::Kva(kva, _) => kva.size(),
396 Inner::Both(kva, _, segment) => {
397 debug_assert_eq!(kva.size(), segment.size());
398 kva.size()
399 }
400 }
401 }
402}
403
404impl<D: DmaDirection> HasVmReaderWriter for DmaStream<D> {
405 type Types = VmReaderWriterResult;
406
407 fn reader(&self) -> Result<VmReader<'_, Infallible>, Error> {
408 if !D::CAN_READ_FROM_DEVICE {
409 return Err(Error::AccessDenied);
410 }
411 match &self.inner {
412 Inner::Segment(seg) | Inner::Both(_, _, seg) => Ok(seg.reader()),
413 Inner::Kva(kva, _) => {
414 unsafe {
420 Ok(VmReader::from_kernel_space(
421 kva.start() as *const u8,
422 kva.size(),
423 ))
424 }
425 }
426 }
427 }
428
429 fn writer(&self) -> Result<VmWriter<'_, Infallible>, Error> {
430 if !D::CAN_WRITE_TO_DEVICE {
431 return Err(Error::AccessDenied);
432 }
433 match &self.inner {
434 Inner::Segment(seg) | Inner::Both(_, _, seg) => Ok(seg.writer()),
435 Inner::Kva(kva, _) => {
436 unsafe {
442 Ok(VmWriter::from_kernel_space(
443 kva.start() as *mut u8,
444 kva.size(),
445 ))
446 }
447 }
448 }
449 }
450}