Skip to main content

dma_api/
array.rs

1use core::{alloc::Layout, marker::PhantomData, ops::Range, ptr::NonNull};
2
3use crate::{
4    DeviceDma, DmaAddr, DmaCoherency, DmaDirection, DmaDomainId, DmaError, DmaPod,
5    common::{AllocationKind, DmaAllocation},
6};
7
8pub struct CoherentArray<T: DmaPod> {
9    data: DmaAllocation,
10    _phantom: PhantomData<T>,
11}
12
13unsafe impl<T: DmaPod + Send> Send for CoherentArray<T> {}
14unsafe impl<T: DmaPod + Sync> Sync for CoherentArray<T> {}
15
16impl<T: DmaPod> CoherentArray<T> {
17    pub(crate) fn new_zero_with_align(
18        os: &DeviceDma,
19        len: usize,
20        align: usize,
21    ) -> Result<Self, DmaError> {
22        let layout = array_layout::<T>(len, align)?;
23        Ok(Self {
24            data: DmaAllocation::new_zero_coherent(os, layout)?,
25            _phantom: PhantomData,
26        })
27    }
28
29    pub(crate) fn new_zero(os: &DeviceDma, len: usize) -> Result<Self, DmaError> {
30        Self::new_zero_with_align(os, len, core::mem::align_of::<T>())
31    }
32
33    pub fn dma_addr(&self) -> DmaAddr {
34        self.data.handle().dma_addr()
35    }
36
37    pub fn len(&self) -> usize {
38        len_from_bytes::<T>(self.data.handle().size())
39    }
40
41    pub fn is_empty(&self) -> bool {
42        self.len() == 0
43    }
44
45    pub fn bytes_len(&self) -> usize {
46        self.data.handle().size()
47    }
48
49    pub fn read_cpu(&self, index: usize) -> Option<T> {
50        read_at(self.as_ptr(), self.len(), index)
51    }
52
53    pub fn set_cpu(&mut self, index: usize, value: T) {
54        write_at(self.as_ptr(), self.len(), index, value);
55    }
56
57    pub fn copy_from_slice_cpu(&mut self, src: &[T]) {
58        copy_from_slice(self.as_ptr(), self.len(), src);
59    }
60
61    pub fn iter_cpu(&self) -> ArrayCpuIter<'_, T, Self> {
62        ArrayCpuIter {
63            array: self,
64            index: 0,
65            _phantom: PhantomData,
66        }
67    }
68
69    pub fn write_with_cpu<R>(&mut self, len: usize, f: impl FnOnce(&mut [T]) -> R) -> R {
70        assert!(len <= self.len(), "range out of bounds");
71        let data = unsafe { self.as_mut_slice_cpu() };
72        f(&mut data[..len])
73    }
74
75    pub fn read_with_cpu<R>(&self, len: usize, f: impl FnOnce(&[T]) -> R) -> R {
76        assert!(len <= self.len(), "range out of bounds");
77        let data = unsafe { core::slice::from_raw_parts(self.as_ptr().as_ptr(), len) };
78        f(data)
79    }
80
81    pub fn as_ptr(&self) -> NonNull<T> {
82        self.data.handle().as_ptr().cast::<T>()
83    }
84
85    pub fn as_slice_cpu(&self) -> &[T] {
86        unsafe { core::slice::from_raw_parts(self.as_ptr().as_ptr(), self.len()) }
87    }
88
89    /// # Safety
90    ///
91    /// The caller must ensure the device is not concurrently accessing this
92    /// memory in a way that races with CPU writes.
93    pub unsafe fn as_mut_slice_cpu(&mut self) -> &mut [T] {
94        unsafe { core::slice::from_raw_parts_mut(self.as_ptr().as_ptr(), self.len()) }
95    }
96
97    pub fn try_release(mut self) -> Result<(), DmaError> {
98        self.data.try_release()
99    }
100}
101
102pub struct ContiguousArray<T: DmaPod> {
103    data: DmaAllocation,
104    _phantom: PhantomData<T>,
105}
106
107unsafe impl<T: DmaPod + Send> Send for ContiguousArray<T> {}
108unsafe impl<T: DmaPod + Sync> Sync for ContiguousArray<T> {}
109
110impl<T: DmaPod> ContiguousArray<T> {
111    pub(crate) fn new_zero_with_align(
112        os: &DeviceDma,
113        len: usize,
114        align: usize,
115        direction: DmaDirection,
116    ) -> Result<Self, DmaError> {
117        let layout = array_layout::<T>(len, align)?;
118        Ok(Self {
119            data: DmaAllocation::new_zero_contiguous(os, layout, direction)?,
120            _phantom: PhantomData,
121        })
122    }
123
124    pub(crate) fn new_zero(
125        os: &DeviceDma,
126        len: usize,
127        direction: DmaDirection,
128    ) -> Result<Self, DmaError> {
129        Self::new_zero_with_align(os, len, core::mem::align_of::<T>(), direction)
130    }
131
132    pub fn dma_addr(&self) -> DmaAddr {
133        self.data.handle().dma_addr()
134    }
135
136    pub fn len(&self) -> usize {
137        len_from_bytes::<T>(self.data.handle().size())
138    }
139
140    pub fn is_empty(&self) -> bool {
141        self.len() == 0
142    }
143
144    pub fn bytes_len(&self) -> usize {
145        self.data.handle().size()
146    }
147
148    pub fn domain_id(&self) -> DmaDomainId {
149        self.data.device.info().domain()
150    }
151
152    pub fn coherency(&self) -> DmaCoherency {
153        self.data.device.info().coherency()
154    }
155
156    pub fn direction(&self) -> DmaDirection {
157        match self.data.kind {
158            AllocationKind::Contiguous { direction } => direction,
159            AllocationKind::Coherent => unreachable!("ContiguousArray cannot hold coherent DMA"),
160        }
161    }
162
163    pub fn read_cpu(&self, index: usize) -> Option<T> {
164        read_at(self.as_ptr(), self.len(), index)
165    }
166
167    pub fn set_cpu(&mut self, index: usize, value: T) {
168        write_at(self.as_ptr(), self.len(), index, value);
169    }
170
171    pub fn copy_from_slice_cpu(&mut self, src: &[T]) {
172        copy_from_slice(self.as_ptr(), self.len(), src);
173    }
174
175    pub fn iter_cpu(&self) -> ArrayCpuIter<'_, T, Self> {
176        ArrayCpuIter {
177            array: self,
178            index: 0,
179            _phantom: PhantomData,
180        }
181    }
182
183    pub fn prepare_for_device(&self, range: Range<usize>) {
184        self.check_range(&range);
185        self.data.sync_for_device(range.start, range.len());
186    }
187
188    pub fn complete_for_cpu(&self, range: Range<usize>) {
189        self.check_range(&range);
190        self.data.sync_for_cpu(range.start, range.len());
191    }
192
193    pub fn write_for_device<R>(&mut self, len: usize, f: impl FnOnce(&mut [T]) -> R) -> R {
194        let ret = self.write_with_cpu(len, f);
195        self.prepare_for_device(0..len * core::mem::size_of::<T>());
196        ret
197    }
198
199    pub fn read_from_device<R>(&self, len: usize, f: impl FnOnce(&[T]) -> R) -> R {
200        let size = len * core::mem::size_of::<T>();
201        self.complete_for_cpu(0..size);
202        self.read_with_cpu(len, f)
203    }
204
205    pub fn copy_to_device_from_slice(&mut self, src: &[T]) {
206        self.copy_from_slice_cpu(src);
207        self.prepare_for_device(0..core::mem::size_of_val(src));
208    }
209
210    pub fn copy_from_device_to_slice(&self, dst: &mut [T]) {
211        self.read_from_device(dst.len(), |src| dst.copy_from_slice(src));
212    }
213
214    pub fn write_with_cpu<R>(&mut self, len: usize, f: impl FnOnce(&mut [T]) -> R) -> R {
215        assert!(len <= self.len(), "range out of bounds");
216        {
217            let data = unsafe { self.as_mut_slice_cpu() };
218            f(&mut data[..len])
219        }
220    }
221
222    pub fn read_with_cpu<R>(&self, len: usize, f: impl FnOnce(&[T]) -> R) -> R {
223        assert!(len <= self.len(), "range out of bounds");
224        let data = unsafe { core::slice::from_raw_parts(self.as_ptr().as_ptr(), len) };
225        f(data)
226    }
227
228    pub fn as_ptr(&self) -> NonNull<T> {
229        self.data.handle().as_ptr().cast::<T>()
230    }
231
232    pub fn as_slice_cpu(&self) -> &[T] {
233        unsafe { core::slice::from_raw_parts(self.as_ptr().as_ptr(), self.len()) }
234    }
235
236    /// # Safety
237    ///
238    /// The caller must ensure the device is not concurrently accessing this
239    /// memory in a way that races with CPU writes.
240    pub unsafe fn as_mut_slice_cpu(&mut self) -> &mut [T] {
241        unsafe { core::slice::from_raw_parts_mut(self.as_ptr().as_ptr(), self.len()) }
242    }
243
244    fn check_range(&self, range: &Range<usize>) {
245        assert!(
246            range.start <= range.end && range.end <= self.bytes_len(),
247            "range out of bounds, range: {:?}, bytes_len: {}",
248            range,
249            self.bytes_len()
250        );
251    }
252}
253
254pub trait DmaArrayCpuRead<T: DmaPod> {
255    fn len(&self) -> usize;
256    fn is_empty(&self) -> bool;
257    fn read_cpu(&self, index: usize) -> Option<T>;
258}
259
260impl<T: DmaPod> DmaArrayCpuRead<T> for CoherentArray<T> {
261    fn len(&self) -> usize {
262        CoherentArray::len(self)
263    }
264
265    fn is_empty(&self) -> bool {
266        CoherentArray::is_empty(self)
267    }
268
269    fn read_cpu(&self, index: usize) -> Option<T> {
270        CoherentArray::read_cpu(self, index)
271    }
272}
273
274impl<T: DmaPod> DmaArrayCpuRead<T> for ContiguousArray<T> {
275    fn len(&self) -> usize {
276        ContiguousArray::len(self)
277    }
278
279    fn is_empty(&self) -> bool {
280        ContiguousArray::is_empty(self)
281    }
282
283    fn read_cpu(&self, index: usize) -> Option<T> {
284        ContiguousArray::read_cpu(self, index)
285    }
286}
287
288pub struct ArrayCpuIter<'a, T: DmaPod, A: DmaArrayCpuRead<T>> {
289    array: &'a A,
290    index: usize,
291    _phantom: PhantomData<T>,
292}
293
294impl<'a, T: DmaPod, A: DmaArrayCpuRead<T>> Iterator for ArrayCpuIter<'a, T, A> {
295    type Item = T;
296
297    fn next(&mut self) -> Option<Self::Item> {
298        if self.index >= self.array.len() {
299            return None;
300        }
301        let value = self.array.read_cpu(self.index);
302        self.index += 1;
303        value
304    }
305}
306
307fn array_layout<T>(len: usize, align: usize) -> Result<Layout, DmaError> {
308    let size = len
309        .checked_mul(core::mem::size_of::<T>())
310        .ok_or(DmaError::LayoutError(
311            Layout::from_size_align(usize::MAX, 1).unwrap_err(),
312        ))?;
313    Ok(Layout::from_size_align(
314        size,
315        align.max(core::mem::align_of::<T>()),
316    )?)
317}
318
319fn len_from_bytes<T>(bytes: usize) -> usize {
320    if core::mem::size_of::<T>() == 0 {
321        0
322    } else {
323        bytes / core::mem::size_of::<T>()
324    }
325}
326
327fn read_at<T: DmaPod>(ptr: NonNull<T>, len: usize, index: usize) -> Option<T> {
328    if index >= len {
329        return None;
330    }
331    Some(unsafe { ptr.add(index).read() })
332}
333
334fn write_at<T: DmaPod>(ptr: NonNull<T>, len: usize, index: usize, value: T) {
335    assert!(
336        index < len,
337        "index out of range, index: {}, len: {}",
338        index,
339        len
340    );
341    unsafe { ptr.add(index).write(value) };
342}
343
344fn copy_from_slice<T: DmaPod>(ptr: NonNull<T>, len: usize, src: &[T]) {
345    assert!(
346        src.len() <= len,
347        "source slice is larger than DMA array, src len: {}, array len: {}",
348        src.len(),
349        len
350    );
351    unsafe {
352        ptr.as_ptr()
353            .copy_from_nonoverlapping(src.as_ptr(), src.len());
354    }
355}
356
357#[cfg(test)]
358mod tests {
359    use super::*;
360
361    fn array_helper_len_and_layout_rules_hold_for_test() -> bool {
362        // len_from_bytes: normal types
363        assert!(len_from_bytes::<u8>(100) == 100);
364        assert!(len_from_bytes::<u16>(100) == 50);
365        assert!(len_from_bytes::<u32>(100) == 25);
366        assert!(len_from_bytes::<u64>(100) == 12);
367
368        // array_layout: valid layout succeeds
369        let layout = array_layout::<u8>(100, 1);
370        assert!(layout.is_ok());
371        let l = layout.unwrap();
372        assert!(l.size() == 100);
373
374        // array_layout: overflow on size returns error
375        let overflow = array_layout::<u8>(usize::MAX, 1);
376        assert!(overflow.is_err());
377
378        // array_layout: alignment must be power of 2
379        let bad_align = array_layout::<u8>(10, 3); // 3 is not power of 2
380        assert!(bad_align.is_err());
381
382        // len_from_bytes: zero bytes returns 0
383        assert!(len_from_bytes::<u32>(0) == 0);
384
385        // array_layout: zero length is valid
386        let empty = array_layout::<u32>(0, 4);
387        assert!(empty.is_ok());
388        assert!(empty.unwrap().size() == 0);
389
390        // len_from_bytes: u16 with odd bytes
391        assert!(len_from_bytes::<u16>(5) == 2); // 5/2 = 2 (integer division)
392
393        // array_layout: large alignment
394        let large_align = array_layout::<u8>(16, 4096); // page-aligned
395        assert!(large_align.is_ok());
396        assert!(large_align.unwrap().align() == 4096);
397
398        true
399    }
400
401    fn array_contiguous_methods_hold_for_test() -> bool {
402        // Test ContiguousArray-specific methods that may not be covered
403        // These are tested indirectly but we verify the helpers exist
404        assert!(array_helper_len_and_layout_rules_hold_for_test());
405
406        // Test len_from_bytes with different types
407        assert!(len_from_bytes::<u8>(100) == 100);
408        assert!(len_from_bytes::<u16>(100) == 50);
409        assert!(len_from_bytes::<u32>(100) == 25);
410        assert!(len_from_bytes::<u64>(100) == 12);
411
412        true
413    }
414
415    fn array_read_at_write_at_helpers_hold_for_test() -> bool {
416        // Test that read_at and write_at helper functions exist
417        // These are tested through array operations but we verify basic logic
418        assert!(len_from_bytes::<u8>(0) == 0);
419        assert!(len_from_bytes::<u32>(0) == 0);
420
421        // Test array_layout with zero size
422        let empty = array_layout::<u8>(0, 1);
423        assert!(empty.is_ok());
424        assert!(empty.unwrap().size() == 0);
425
426        true
427    }
428
429    fn array_dma_array_cpu_read_trait_hold_for_test() -> bool {
430        // Test DmaArrayCpuRead trait methods exist
431        // These are tested through CoherentArray and ContiguousArray but we verify helpers
432        assert!(len_from_bytes::<u8>(100) == 100);
433        assert!(len_from_bytes::<u16>(50) == 25);
434
435        true
436    }
437
438    fn array_layout_edge_cases_comprehensive_hold_for_test() -> bool {
439        // Comprehensive edge case tests for array_layout and len_from_bytes
440
441        // len_from_bytes: zero-sized types return 0
442        assert!(len_from_bytes::<()>(100) == 0);
443
444        // array_layout: size=1, align=1
445        let tiny = array_layout::<u8>(1, 1);
446        assert!(tiny.is_ok());
447        assert_eq!(tiny.unwrap().size(), 1);
448
449        // array_layout: alignment must be power of 2 (3 is not)
450        assert!(array_layout::<u8>(10, 3).is_err());
451
452        // array_layout: alignment of 2 is valid
453        assert!(array_layout::<u16>(5, 2).is_ok());
454
455        // len_from_bytes: exact division
456        assert_eq!(len_from_bytes::<u32>(40), 10); // 40/4 = 10
457
458        // len_from_bytes: non-exact division truncates
459        assert_eq!(len_from_bytes::<u32>(42), 10); // 42/4 = 10 (truncated)
460
461        true
462    }
463
464    fn array_copy_from_slice_and_write_at_edge_hold_for_test() -> bool {
465        // Test copy_from_slice and write_at logic through helpers
466
467        // len_from_bytes with u8 (size 1, no truncation)
468        assert_eq!(len_from_bytes::<u8>(0), 0);
469        assert_eq!(len_from_bytes::<u8>(1), 1);
470        assert_eq!(len_from_bytes::<u8>(255), 255);
471
472        // len_from_bytes with u16 (size 2)
473        assert_eq!(len_from_bytes::<u16>(0), 0);
474        assert_eq!(len_from_bytes::<u16>(2), 1);
475        assert_eq!(len_from_bytes::<u16>(3), 1); // 3/2 = 1
476        assert_eq!(len_from_bytes::<u16>(4), 2);
477
478        // len_from_bytes with u64 (size 8)
479        assert_eq!(len_from_bytes::<u64>(0), 0);
480        assert_eq!(len_from_bytes::<u64>(7), 0); // 7/8 = 0
481        assert_eq!(len_from_bytes::<u64>(8), 1);
482        assert_eq!(len_from_bytes::<u64>(15), 1); // 15/8 = 1
483        assert_eq!(len_from_bytes::<u64>(16), 2);
484
485        // array_layout: various alignments
486        // align=1 always valid for any size
487        assert!(array_layout::<u8>(0, 1).is_ok());
488        assert!(array_layout::<u8>(1, 1).is_ok());
489        assert!(array_layout::<u8>(256, 1).is_ok());
490
491        // align=2: size must be valid
492        assert!(array_layout::<u16>(1, 2).is_ok()); // size=2, align=2
493        assert!(array_layout::<u16>(100, 2).is_ok()); // size=200, align=2
494
495        // align=4: for u32
496        assert!(array_layout::<u32>(10, 4).is_ok()); // size=40, align=4
497        assert!(array_layout::<u32>(0, 4).is_ok()); // size=0, align=4
498
499        // align=8: for u64
500        assert!(array_layout::<u64>(5, 8).is_ok()); // size=40, align=8
501
502        // Invalid alignments (not power of 2)
503        assert!(array_layout::<u8>(10, 3).is_err()); // 3 not power of 2
504        assert!(array_layout::<u8>(10, 5).is_err()); // 5 not power of 2
505        assert!(array_layout::<u8>(10, 6).is_err()); // 6 not power of 2
506        assert!(array_layout::<u8>(10, 7).is_err()); // 7 not power of 2
507        assert!(array_layout::<u8>(10, 9).is_err()); // 9 not power of 2
508
509        true
510    }
511
512    fn array_layout_overflow_and_size_align_hold_for_test() -> bool {
513        // Test array_layout overflow detection and size/align relationships
514
515        // Overflow: usize::MAX * size_of::<T>() overflows
516        let overflow_u8 = array_layout::<u8>(usize::MAX, 1);
517        assert!(overflow_u8.is_err());
518
519        let overflow_u16 = array_layout::<u16>(usize::MAX / 2 + 1, 2);
520        assert!(overflow_u16.is_err());
521
522        let overflow_u32 = array_layout::<u32>(usize::MAX / 4 + 1, 4);
523        assert!(overflow_u32.is_err());
524
525        // Valid large sizes (no overflow)
526        let large = array_layout::<u8>(1024 * 1024, 4096);
527        assert!(large.is_ok());
528        let l = large.unwrap();
529        assert_eq!(l.size(), 1024 * 1024);
530        assert_eq!(l.align(), 4096);
531
532        // Size 0 with any valid alignment
533        assert!(array_layout::<u8>(0, 1).is_ok());
534        assert!(array_layout::<u8>(0, 2).is_ok());
535        assert!(array_layout::<u8>(0, 4).is_ok());
536        assert!(array_layout::<u8>(0, 8).is_ok());
537        assert!(array_layout::<u8>(0, 16).is_ok());
538        assert!(array_layout::<u8>(0, 32).is_ok());
539        assert!(array_layout::<u8>(0, 64).is_ok());
540        assert!(array_layout::<u8>(0, 128).is_ok());
541        assert!(array_layout::<u8>(0, 256).is_ok());
542        assert!(array_layout::<u8>(0, 512).is_ok());
543        assert!(array_layout::<u8>(0, 1024).is_ok());
544        assert!(array_layout::<u8>(0, 2048).is_ok());
545        assert!(array_layout::<u8>(0, 4096).is_ok());
546
547        // align max uses max(align, align_of::<T>())
548        // For u8 (align 1), requested align is used
549        let a1 = array_layout::<u8>(10, 16).unwrap();
550        assert_eq!(a1.align(), 16);
551
552        // For u16 (align 2), requested align < 2 should use 2
553        let a2 = array_layout::<u16>(10, 1).unwrap();
554        assert_eq!(a2.align(), 2); // max(1, 2) = 2
555
556        true
557    }
558
559    #[test]
560    fn array_helper_len_and_layout_rules_hold() {
561        assert!(array_helper_len_and_layout_rules_hold_for_test());
562    }
563
564    #[test]
565    fn array_contiguous_methods_hold() {
566        assert!(array_contiguous_methods_hold_for_test());
567    }
568
569    #[test]
570    fn array_read_at_write_at_helpers_hold() {
571        assert!(array_read_at_write_at_helpers_hold_for_test());
572    }
573
574    #[test]
575    fn array_dma_array_cpu_read_trait_hold() {
576        assert!(array_dma_array_cpu_read_trait_hold_for_test());
577    }
578
579    #[test]
580    fn array_layout_edge_cases_comprehensive_hold() {
581        assert!(array_layout_edge_cases_comprehensive_hold_for_test());
582    }
583
584    #[test]
585    fn array_copy_from_slice_and_write_at_edge_hold() {
586        assert!(array_copy_from_slice_and_write_at_edge_hold_for_test());
587    }
588
589    #[test]
590    fn array_layout_overflow_and_size_align_hold() {
591        assert!(array_layout_overflow_and_size_align_hold_for_test());
592    }
593}