Skip to main content

kernel/utilities/
dma_slice.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Leon Schuermann <leon@is.currently.online> 2026.
4// Copyright Tock Contributors 2026.
5
6//! Mechanism for sharing buffers with DMA peripherals.
7//!
8//! When implementing a chip peripheral driver using DMA, the driver must be
9//! careful to not introduce any undefined behavior. This module provides
10//! `DmaSlice` types, which drivers can use when passing a buffer to the DMA
11//! hardware. When used correctly, these types ensure that Rust's memory requirements
12//! are preserved when hardware is accessing memory in a way the Rust compiler
13//! cannot reason about.
14//!
15//! Tock provides multiple implementations of `DmaSlice` depending on the
16//! needs of the user. These include:
17//!
18//! - [`DmaSlice`]: For immutable buffers and read-only DMA operations.
19//! - [`DmaSliceMut`]: For mutable buffers and writable DMA operations.
20//! - [`DmaSubSlice`]: For immutable [`SubSlice`]s and read-only DMA
21//!   operations.
22//! - [`DmaSubSliceMut`]: For mutable [`SubSliceMut`]s and writable DMA
23//!   operations.
24//!
25//! Internally, all implementations of `DmaSlice` use an architecture or
26//! chip-provided implementation of [`DmaFence`] to ensure that the
27//! Rust compiler cannot assume the memory passed to the DMA hardware is not
28//! modified.
29//!
30//! Conceptually, a `DmaSlice` consumes a memory buffer. Once consumed, a
31//! pointer to that memory can then be safely provided to DMA hardware. When the
32//! buffer is consumed, the `DmaSlice` prevents the Rust compiler from making
33//! assumptions about the state of the memory accessed by DMA hardware that
34//! would be incorrect and introduce undefined behavior. Once the DMA operation
35//! finishes, the buffer must be extracted from the `DmaSlice`. Before
36//! extracting the buffer, the user must guarantee that the DMA hardware can no
37//! longer access the memory.
38//!
39//! # Usage
40//!
41//! This example shows how a developer might use the `DmaSlice` infrastructure
42//! in a driver. The [`DmaSliceMut::new`] operation can be replaced by the safe
43//! [`DmaSliceMut::new_static`] alternative if the provided slice reference has
44//! a `'static` lifetime.
45//!
46//! ```
47//! # use core::cell::Cell;
48//! # use kernel::platform::dma_fence::DmaFence;
49//! # use kernel::utilities::dma_slice::DmaSliceMut;
50//! #
51//! # #[derive(Debug, Copy, Clone)]
52//! # struct SomeDmaFence;
53//! # unsafe impl DmaFence for SomeDmaFence {
54//! #     fn release<T>(self, _buf: *mut [T]) {}
55//! #     fn acquire<T>(self, _buf: *mut [T]) {}
56//! # }
57//! #
58//! # enum DmaOp {
59//! #     Stop,
60//! # }
61//! #
62//! # struct Registers {
63//! #     dma_ptr: Cell<*mut u8>,
64//! #     dma_ctrl: Cell<DmaOp>,
65//! # }
66//! #
67//! # let regs = Registers {
68//! #     dma_ptr: Cell::new(core::ptr::null_mut()),
69//! #     dma_ctrl: Cell::new(DmaOp::Stop),
70//! # };
71//! #
72//! // Buffer that will be used by the DMA hardware.
73//! let mut buffer: [u8; 16] = [0_u8; 16];
74//!
75//! // Create the `DmaSlice` that can be provided to the DMA hardware.
76//! //
77//! // For a static slice, users can instead use the safe `new_static`
78//! // constructor.
79//! let dma_slice = unsafe { DmaSliceMut::new(&mut buffer, SomeDmaFence) };
80//!
81//! // Provide the pointer to the buffer to the DMA hardware registers.
82//! regs.dma_ptr.set(dma_slice.as_mut_ptr());
83//!
84//! // Wait for the DMA operation to finish...
85//!
86//! // Disable the DMA engine to ensure it cannot access the buffer.
87//! regs.dma_ctrl.set(DmaOp::Stop);
88//!
89//! // Extract the buffer to retrieve the Rust slice.
90//! let buffer = unsafe { dma_slice.take(SomeDmaFence) };
91//! ```
92
93use core::marker::PhantomData;
94use core::ops::Range;
95use core::ptr::{self, NonNull};
96
97use super::leasable_buffer::{SubSlice, SubSliceMut, SubSliceMutImmut};
98use crate::platform::dma_fence::DmaFence;
99
100/// An immutable buffer that can be safely used for read-only DMA operations.
101///
102/// The buffer can be a slice of any type which implements
103/// [`ImmutableFromIntoBytes`](immutable_from_into_bytes::ImmutableFromIntoBytes),
104/// such as `u8` or `u32`.
105///
106/// [`DmaSlice`] wraps an immutable slice. As such, its contents MUST NOT be
107/// modified by the DMA operation. For a DMA operation that may write to the
108/// supplied buffer, use [`DmaSliceMut`] instead.
109///
110/// # Use with DMA
111///
112/// Creating a [`DmaSlice`] over an immutable Rust slice ensures that all prior
113/// Rust writes to this slice are observable by any DMA operations initiated
114/// through an MMIO write operation, where that MMIO write is performed after
115/// constructing the [`DmaSlice`].
116///
117/// For this guarantee to hold, the [`DmaSlice`] instance must exist for the
118/// duration of the entire DMA operation, until the Rust program has observed
119/// that the operation is complete (such as by reading a status bit in memory or
120/// an MMIO register).
121///
122/// This struct uses a [`DmaFence`] implementation to ensure that all prior
123/// writes to `slice` are exposed to any DMA operations initiated by an MMIO
124/// read or write operation issued after this function returns, and which finish
125/// before the resulting [`DmaSlice`] is dropped.
126#[derive(Debug)]
127pub struct DmaSlice<'a, T: immutable_from_into_bytes::ImmutableFromIntoBytes> {
128    slice: &'a [T],
129}
130
131impl<'a, T: immutable_from_into_bytes::ImmutableFromIntoBytes> DmaSlice<'a, T> {
132    /// Create a [`DmaSlice`] from an immutable slice.
133    pub fn new(slice: &[T], fence: impl DmaFence) -> DmaSlice<'_, T> {
134        // Ensure that all prior writes to this slice are exposed to any DMA
135        // operations initiated by an MMIO read or write operation after this
136        // function returns.
137        let mut_slice_ptr: *mut [T] = ptr::from_ref(slice).cast_mut();
138        fence.release::<T>(mut_slice_ptr);
139
140        DmaSlice { slice }
141    }
142
143    /// Returns a pointer to the start of the slice.
144    pub fn as_ptr(&self) -> *const T {
145        self.slice.as_ptr()
146    }
147
148    /// Returns the address of the slice to pass to DMA hardware, without exposing provenance.
149    pub fn ptr_addr(&self) -> usize {
150        self.as_ptr().addr()
151    }
152
153    /// Returns the length of the slice.
154    pub fn len(&self) -> usize {
155        self.slice.len()
156    }
157
158    /// Retrieve the inner slice reference.
159    ///
160    /// This is safe, as the slice is immutable. Therefore, the DMA hardware and
161    /// software may read it concurrently.
162    pub fn get(&self) -> &'a [T] {
163        self.slice
164    }
165}
166
167/// A mutable buffer that can be safely used for DMA operations that read or
168/// write the buffer's contents.
169///
170/// The buffer can be a slice of any type which implements
171/// [`ImmutableFromIntoBytes`](immutable_from_into_bytes::ImmutableFromIntoBytes),
172/// such as `u8` or `u32`.
173///
174/// # Use with DMA
175///
176/// Creating a [`DmaSliceMut`] over a mutable Rust slice ensures that all prior
177/// Rust writes to this slice are observable by any DMA operations initiated
178/// through an MMIO write operation, where that MMIO write is performed
179/// **after** constructing the `DmaSliceMut`. All writes by the DMA operation
180/// will be observable by Rust when calling [`take`](Self::take) **after** the
181/// DMA operation is finished.
182///
183/// This struct uses a [`DmaFence`] implementation to ensure that all prior
184/// writes to `slice` are exposed to any DMA operations initiated by an MMIO
185/// read or write operation after this function returns, and which finish before
186/// calling [`take`](Self::take).
187///
188/// # Safety Considerations
189///
190/// Users **must** eventually call [`take`](Self::take) to retrieve the
191/// underlying buffer. The [`DmaSliceMut`] must exist for the entire duration of
192/// the DMA operation. Users must never drop a [`DmaSliceMut`] with a
193/// non-`'static` lifetime, as this could provide access to the underlying
194/// buffer without guaranteeing that the DMA operation has finished, and without
195/// issuing a DMA memory fence to ensure that writes by the DMA operation are
196/// visible to Rust.
197///
198/// [`take`](Self::take) must only be called after the DMA operation has been
199/// observed to be complete through an explicit memory or MMIO read. Callers
200/// must ensure that the hardware will not perform any further writes to the
201/// buffer at the point where [`take`](Self::take) is called.
202///
203/// Callers must further ensure that they start DMA operations through a memory
204/// or MMIO write only after constructing the [`DmaSliceMut`], and only in the
205/// memory region described by [`as_mut_ptr`](Self::as_mut_ptr) and
206/// [`len`](Self::len).
207///
208/// Users are responsible to ensure that, after the DMA operation completes and
209/// before calling [`take`](Self::take), every element in the underlying slice
210/// represents a well-initialized and valid instance of its type (with the
211/// exception of padding bytes). Concretely, all elements in the slice must meet
212/// the requirements of the
213/// [`ImmutableFromIntoBytes`](immutable_from_into_bytes::ImmutableFromIntoBytes)
214/// trait. See the [zerocopy crate](https://docs.rs/zerocopy/0.8.31/zerocopy/)
215/// for a more in-depth explanation of these requirements.
216#[derive(Debug)]
217pub struct DmaSliceMut<'a, T: immutable_from_into_bytes::ImmutableFromIntoBytes> {
218    slice_ptr: NonNull<[T]>,
219    _lt: PhantomData<&'a mut [T]>,
220}
221
222impl<'a, T: immutable_from_into_bytes::ImmutableFromIntoBytes> DmaSliceMut<'a, T> {
223    /// Create a [`DmaSliceMut`] from a static mutable slice.
224    pub fn new_static(slice: &'static mut [T], fence: impl DmaFence) -> DmaSliceMut<'static, T> {
225        // SAFETY: This operation is safe, as dropping or forgetting its return value is
226        // safe. This would merely leak memory and make the underlying slice
227        // inaccessible.
228        unsafe { Self::new(slice, fence) }
229    }
230
231    /// Create a [`DmaSliceMut`] from a mutable slice.
232    ///
233    /// # Safety
234    ///
235    /// Callers must ensure to not drop the returned [`DmaSliceMut`]. This could
236    /// provide access to the underlying buffer without guaranteeing that the
237    /// DMA operation has finished.  Users **must** eventually call
238    /// [`take`](Self::take) to retrieve the underlying buffer.
239    #[must_use]
240    pub unsafe fn new(slice: &mut [T], fence: impl DmaFence) -> DmaSliceMut<'_, T> {
241        let dma_slice_mut = DmaSliceMut {
242            slice_ptr: NonNull::from_mut(slice),
243            _lt: PhantomData,
244        };
245
246        // Ensure that all prior writes to this slice are exposed to any DMA
247        // operations initiated by an MMIO read or write operation after this
248        // function returns:
249        fence.release::<T>(dma_slice_mut.slice_ptr.as_ptr());
250
251        dma_slice_mut
252    }
253
254    /// Returns the pointer to the start of the slice.
255    pub fn as_mut_ptr(&self) -> *mut T {
256        // TODO: switch `.cast()` to `.as_mut_ptr()` on the slice pointer (`*mut
257        // [T]`) to obtain the "thin", raw pointer to its first element. This is
258        // blocked on the nightly `slice_ptr_get` feature.
259        self.slice_ptr.as_ptr().cast()
260    }
261
262    /// Returns the address of the slice to pass to DMA hardware, without exposing provenance.
263    pub fn ptr_addr(&self) -> usize {
264        self.as_mut_ptr().addr()
265    }
266
267    /// Returns the length of the slice.
268    pub fn len(&self) -> usize {
269        self.slice_ptr.len()
270    }
271
272    /// Recover the original mutable slice.
273    ///
274    /// The caller MUST ensure the hardware DMA will no longer write to the
275    /// buffer.
276    ///
277    /// # Safety
278    ///
279    /// Callers must guarantee no hardware DMA have access to the buffer before
280    /// calling `take()`. All DMA operations must have completed before calling
281    /// this function and the caller must ensure no future operations will occur
282    /// using the underlying buffer.
283    pub unsafe fn take(mut self, fence: impl DmaFence) -> &'a mut [T] {
284        // Ensure that any reads from Rust to the buffer described by
285        // `slice_ptr` issued _after_ this function returns reflect all writes
286        // made by DMA operations finished _before_ this function ran:
287        fence.acquire::<T>(self.slice_ptr.as_ptr());
288
289        // SAFETY: This pointer must be convertible to a reference. We know that
290        // it is because the only constructor for `self.slice_ptr` started with
291        // a valid Rust reference.
292        unsafe { self.slice_ptr.as_mut() }
293    }
294}
295
296/// A buffer that can be safely used for read-only DMA operations, backed by
297/// either a shared (immutable) or unique (mutable) Rust slice.
298///
299/// Creating a [`DmaSliceMutImmut`] over a Rust slice ensures that all prior
300/// Rust writes to this slice are observable by any DMA operations initiated
301/// through an MMIO write operation, where that MMIO write is performed after
302/// constructing the `DmaSliceMutImmut`.
303///
304/// For this guarantee to hold, the `DmaSliceMutImmut` instance must exist for
305/// the duration of the entire DMA operation, until the Rust program has
306/// observed that the operation is complete (such as by reading a status bit in
307/// memory or an MMIO register).
308///
309/// [`DmaSliceMutImmut`] may wrap an immutable, shared Rust slice
310/// reference. Furthermore, in contrast to `DmaSliceMut`, `DmaSliceMutImmut` may
311/// not expose writes performed by a DMA operation back to Rust. As such, its
312/// contents *must* not be modified by the DMA operation. For a DMA operation
313/// that may write to the supplied buffer, use [`DmaSliceMut`] instead.
314#[derive(Debug)]
315pub enum DmaSliceMutImmut<'a, T: immutable_from_into_bytes::ImmutableFromIntoBytes> {
316    Immutable(DmaSlice<'a, T>),
317    Mutable(DmaSliceMut<'a, T>),
318}
319
320impl<'a, T: immutable_from_into_bytes::ImmutableFromIntoBytes> DmaSliceMutImmut<'a, T> {
321    /// Create a [`DmaSliceMutImmut`] from a shared, immutable Rust slice.
322    ///
323    /// This function uses the supplied `fence` object to ensure that all prior
324    /// writes to `slice` are exposed to any DMA operations initiated by an MMIO
325    /// read or write operation after this function returns, and which finish
326    /// before the resulting [`DmaSliceMutImmut`] is dropped.
327    pub fn new(slice: &[T], fence: impl DmaFence) -> DmaSliceMutImmut<'_, T> {
328        DmaSliceMutImmut::Immutable(DmaSlice::new(slice, fence))
329    }
330
331    /// Create a [`DmaSliceMutImmut`] from a unique, mutable Rust slice.
332    ///
333    /// This function uses the supplied `fence` object to ensure that all prior
334    /// writes to `slice` are exposed to any DMA operations initiated by an MMIO
335    /// read or write operation after this function returns, and which finish
336    /// before the resulting [`DmaSliceMutImmut`] is dropped.
337    ///
338    /// Even though this method takes a unique, mutable Rust slice, DMA
339    /// operations must not modify the buffers contents.
340    pub fn new_mut(slice: &mut [T], fence: impl DmaFence) -> DmaSliceMutImmut<'_, T> {
341        // SAFETY: `DmaSliceMut::from_mut_slice_ref` is unsafe, as dropping its return
342        // value without calling `take` may make the underlying buffer
343        // accessible as a Rust slice, potentially before the DMA operation is
344        // complete, and without using `fence.acquire` to make DMA writes
345        // visible to Rust. However, this struct does not permit DMA operations
346        // which write to the slice, and hence it can be safely dropped without
347        // risk of concurrent modifications or incoherence.
348        DmaSliceMutImmut::Mutable(unsafe { DmaSliceMut::new(slice, fence) })
349    }
350
351    /// Returns the pointer to the first element of the wrapped slice reference.
352    pub fn as_ptr(&self) -> *const T {
353        match self {
354            DmaSliceMutImmut::Immutable(dma_slice) => dma_slice.as_ptr(),
355            DmaSliceMutImmut::Mutable(dma_slice_mut) => dma_slice_mut.as_mut_ptr().cast_const(),
356        }
357    }
358
359    /// Returns the address of the slice to pass to DMA hardware, without exposing provenance.
360    pub fn ptr_addr(&self) -> usize {
361        self.as_ptr().addr()
362    }
363
364    /// Returns the length of the wrapped slice reference.
365    pub fn len(&self) -> usize {
366        match self {
367            DmaSliceMutImmut::Immutable(dma_slice) => dma_slice.len(),
368            DmaSliceMutImmut::Mutable(dma_slice_mut) => dma_slice_mut.len(),
369        }
370    }
371
372    /// Retrieve the inner slice reference.
373    ///
374    /// This is safe, as [`DmaSliceMutImmut`] can only be used for read-only DMA
375    /// operations. The DMA hardware and software may read the underlying slice
376    /// concurrently.
377    pub fn get(&self) -> &'a [T] {
378        match self {
379            DmaSliceMutImmut::Immutable(dma_slice) => dma_slice.get(),
380            DmaSliceMutImmut::Mutable(dma_slice_mut) => {
381                // SAFETY: Over the duration that [`DmaSliceMutImmut`] the user
382                // guarantees that no DMA operation modifies the buffer (and
383                // doing so would require an MMIO write, which is itself
384                // unsafe). The `dma_slice_mut` is capturing a unique, mutable
385                // borrow of the underlying slice over its lifetime `'a`. As
386                // such, we can safely hand out immutable references over this
387                // slice, which are also bound to the lifetime `'a`.
388                unsafe {
389                    core::slice::from_raw_parts(
390                        dma_slice_mut.as_mut_ptr().cast_const(),
391                        dma_slice_mut.len(),
392                    )
393                }
394            }
395        }
396    }
397}
398
399/// An immutable buffer that can be safely used for read-only DMA operations,
400/// created from a `SubSlice` describing an active range in a larger buffer.
401///
402/// Creating a [`DmaSubSlice`] over a [`SubSlice`] ensures that all prior Rust
403/// writes to the active region of this slice are observable by any DMA
404/// operations initiated through an MMIO write operation, where that MMIO write
405/// is performed *after* constructing the `DmaSubSlice`.
406///
407/// For this guarantee to hold, the `DmaSubSlice` struct must exist for the
408/// duration of the entire DMA operation, until the Rust program has observed
409/// that the operation is complete (such as by reading a status bit in memory or
410/// an MMIO register).
411///
412/// [`DmaSubSlice`] wraps an immutable, shared Rust slice reference. As such,
413/// its contents must not be modified by the DMA operation. For a DMA operation
414/// that may write to the supplied buffer, use [`DmaSubSliceMut`] instead.
415#[derive(Debug)]
416pub struct DmaSubSlice<'a, T: immutable_from_into_bytes::ImmutableFromIntoBytes> {
417    sub_slice: SubSlice<'a, T>,
418}
419
420impl<'a, T: immutable_from_into_bytes::ImmutableFromIntoBytes> DmaSubSlice<'a, T> {
421    /// Create a [`DmaSubSlice`] from a shared, immutable Rust slice.
422    ///
423    /// This function uses the supplied `fence` object to ensure that all prior
424    /// writes to `slice` are exposed to any DMA operations initiated by an MMIO
425    /// read or write operation after this function returns, and which finish
426    /// before the resulting [`DmaSubSlice`] is dropped.
427    pub fn new(sub_slice: SubSlice<'_, T>, fence: impl DmaFence) -> DmaSubSlice<'_, T> {
428        // Ensure that all prior writes to this slice are exposed to any DMA
429        // operations initiated by an MMIO read or write operation after this
430        // function returns:
431        //
432        // Clippy says we should be using `.as_mut_ptr()` instead of `.as_ptr()
433        // as *mut T`, but that method doesn't exist. The cast doesn't matter
434        // here, `DmaFence::release` will not actually dereference the memory.
435        let sub_slice_ptr: *mut T = sub_slice.as_ptr().cast_mut();
436        #[allow(clippy::as_ptr_cast_mut)]
437        fence.release::<T>(ptr::slice_from_raw_parts_mut(
438            // `SubSlice::as_ptr()` returns a pointer to the currently
439            // accessible portion of the `SubSlice`.
440            sub_slice_ptr,
441            // `SubSlice::len()` returns the length of the currently accessible
442            // portion of the `SubSlice`.
443            sub_slice.len(),
444        ));
445
446        DmaSubSlice { sub_slice }
447    }
448
449    /// Returns the pointer to the first element of the currently accessible
450    /// portion of the wrapped `SubSlice`.
451    pub fn as_ptr(&self) -> *const T {
452        self.sub_slice.as_ptr()
453    }
454
455    /// Returns the address of the slice to pass to DMA hardware, without exposing provenance.
456    pub fn ptr_addr(&self) -> usize {
457        self.as_ptr().addr()
458    }
459
460    /// Returns the length of the currently accessible range of the wrapped
461    /// `SubSlice`.
462    pub fn len(&self) -> usize {
463        self.sub_slice.len()
464    }
465
466    /// Retrieve the wrapped `SubSlice`.
467    pub fn as_sub_slice(&self) -> SubSlice<'a, T> {
468        self.sub_slice
469    }
470}
471
472/// A mutable buffer that can be safely used for DMA operations that read or
473/// write the buffer's contents.
474///
475/// The buffer can be a slice of any type which implements
476/// [`ImmutableFromIntoBytes`](immutable_from_into_bytes::ImmutableFromIntoBytes),
477/// such as `u8` or `u32`.
478///
479/// # Use with DMA
480///
481/// Creating a [`DmaSubSliceMut`] over a [`SubSliceMut`] ensures that all prior
482/// Rust writes to the active range of this slice are observable by any DMA
483/// operations initiated through an MMIO write operation, where that MMIO write
484/// is performed **after** constructing the `DmaSubSliceMut`. All writes by the
485/// DMA operation will be observable by Rust when calling [`take`](Self::take)
486/// **after** the DMA operation is finished.
487///
488/// This struct uses a [`DmaFence`] implementation to ensure that all prior
489/// writes to `slice` are exposed to any DMA operations initiated by an MMIO
490/// read or write operation after this function returns, and which finish before
491/// calling [`take`](Self::take).
492///
493/// # Safety Considerations
494///
495/// Users **must** eventually call [`take`](Self::take) to retrieve the
496/// underlying buffer. The [`DmaSubSliceMut`] must exist for the entire duration
497/// of the DMA operation. Users must never drop a [`DmaSubSliceMut`] with a
498/// non-`'static` lifetime, as this could provide access to the underlying
499/// buffer without guaranteeing that the DMA operation has finished, and without
500/// issuing a DMA memory fence to ensure that writes by the DMA operation are
501/// visible to Rust.
502///
503/// [`take`](Self::take) must only be called after the DMA operation has been
504/// observed to be complete through an explicit memory or MMIO read. Callers
505/// must ensure that the hardware will not perform any further writes to the
506/// buffer at the point where [`take`](Self::take) is called.
507///
508/// Callers must further ensure that they start DMA operations through a memory
509/// or MMIO write only after constructing the [`DmaSubSliceMut`], and only in
510/// the memory region described by [`as_mut_ptr`](Self::as_mut_ptr) and
511/// [`len`](Self::len).
512///
513/// Users are responsible to ensure that, after the DMA operation completes and
514/// before calling [`take`](Self::take), every element in the underlying slice
515/// represents a well-initialized and valid instance of its type (with the
516/// exception of padding bytes). Concretely, all elements in the slice must meet
517/// the requirements of the
518/// [`ImmutableFromIntoBytes`](immutable_from_into_bytes::ImmutableFromIntoBytes)
519/// trait. See the [zerocopy crate](https://docs.rs/zerocopy/0.8.31/zerocopy/)
520/// for a more in-depth explanation of these requirements.
521#[derive(Debug)]
522pub struct DmaSubSliceMut<'a, T: immutable_from_into_bytes::ImmutableFromIntoBytes> {
523    internal_slice_ptr: NonNull<[T]>,
524    active_range: Range<usize>,
525    _lt: PhantomData<&'a mut [T]>,
526}
527
528impl<'a, T: immutable_from_into_bytes::ImmutableFromIntoBytes> DmaSubSliceMut<'a, T> {
529    /// Create a [`DmaSubSliceMut`] from a [`SubSliceMut`] with `'static`
530    /// lifetime.
531    pub fn new_static(
532        sub_slice: SubSliceMut<'static, T>,
533        fence: impl DmaFence,
534    ) -> DmaSubSliceMut<'static, T> {
535        // SAFETY: This operation is safe, as dropping or forgetting its return value is
536        // safe. This would merely leak memory and make the underlying slice
537        // inaccessible.
538        unsafe { Self::new(sub_slice, fence) }
539    }
540
541    /// Create a [`DmaSubSliceMut`] from a [`SubSliceMut`].
542    ///
543    /// # Safety
544    ///
545    /// Callers must ensure to not drop the returned [`DmaSubSliceMut`]. This
546    /// could provide access to the underlying buffer without guaranteeing that
547    /// the DMA operation has finished. Users **must** eventually call
548    /// [`take`](Self::take) to retrieve the underlying buffer.
549    #[must_use]
550    pub unsafe fn new(
551        sub_slice_mut: SubSliceMut<'_, T>,
552        fence: impl DmaFence,
553    ) -> DmaSubSliceMut<'_, T> {
554        let active_range = sub_slice_mut.active_range();
555        let internal_slice_ptr = sub_slice_mut.take();
556
557        // Store only a fat raw pointer to the inner slice:
558        let dma_sub_slice_mut = DmaSubSliceMut {
559            internal_slice_ptr: NonNull::from_mut(internal_slice_ptr),
560            active_range,
561            _lt: PhantomData,
562        };
563
564        // Ensure that all prior writes to the currently active portion of this
565        // SubSliceMut are exposed to any DMA operations initiated by an MMIO
566        // read or write operation after this function returns:
567        fence.release::<T>(ptr::slice_from_raw_parts_mut(
568            dma_sub_slice_mut.as_mut_ptr(),
569            dma_sub_slice_mut.len(),
570        ));
571
572        dma_sub_slice_mut
573    }
574
575    /// Returns the pointer to the first element of the active range of the
576    /// wrapped [`SubSliceMut`].
577    pub fn as_mut_ptr(&self) -> *mut T {
578        // TODO: switch `.cast()` to `.as_mut_ptr()` on the slice pointer (`*mut
579        // [T]`) to obtain the "thin", raw pointer to its first element. This is
580        // blocked on the nightly `slice_ptr_get` feature.
581        self.internal_slice_ptr.as_ptr().cast::<T>().wrapping_add(
582            if self.active_range.start >= self.internal_slice_ptr.len() {
583                // `range.start` is out of bounds, return a pointer that's one
584                // after the last byte in this buffer, and length `0`:
585                self.internal_slice_ptr.len()
586            } else {
587                // Start is in bounds:
588                self.active_range.start
589            },
590        )
591    }
592
593    /// Returns the address of the slice to pass to DMA hardware, without exposing provenance.
594    pub fn ptr_addr(&self) -> usize {
595        self.as_mut_ptr().addr()
596    }
597
598    /// Returns the length of the active range of the wrapped [`SubSliceMut`].
599    pub fn len(&self) -> usize {
600        core::cmp::min(
601            self.active_range
602                .end
603                .saturating_sub(self.active_range.start),
604            self.internal_slice_ptr.len(),
605        )
606    }
607
608    /// Recover the original [`SubSliceMut`] used to construct this
609    /// [`DmaSubSliceMut`] object.
610    ///
611    /// # Safety
612    ///
613    /// Callers must guarantee no hardware DMA have access to the buffer before
614    /// calling `take()`. All DMA operations must have completed before calling
615    /// this function and the caller must ensure no future operations will occur
616    /// using the underlying buffer.
617    pub unsafe fn take(mut self, fence: impl DmaFence) -> SubSliceMut<'a, T> {
618        // Ensure that any reads from Rust to the active range of the buffer
619        // (described by `self.as_mut_ptr()` and `self.len()`) _after_ this
620        // function returns reflect all writes made by DMA operations finished
621        // _before_ this function ran:
622        fence.acquire::<T>(ptr::slice_from_raw_parts_mut(self.as_mut_ptr(), self.len()));
623
624        // Restore the original `SubSliceMut` configuration:
625        //
626        // SAFETY: The `internal_slice_ptr` pointer must be convertible to a
627        // reference. We know that it is because the only constructor for
628        // `self.internal_slice_ptr` started with a valid Rust reference.
629        let mut sub_slice_mut = SubSliceMut::new(unsafe { self.internal_slice_ptr.as_mut() });
630        sub_slice_mut.slice(self.active_range);
631        sub_slice_mut
632    }
633
634    /// Recover the original [`SubSliceMut`] used to construct this
635    /// [`DmaSubSliceMut`] object, without performing an `acquire` fence.
636    ///
637    /// # Safety
638    ///
639    /// This function does not necessarily expose any writes to the underlying
640    /// buffer to Rust. It must only be used when the underlying buffer's
641    /// contents have not been modified by a DMA operation (i.e., any DMA
642    /// operations operating on the buffer while this `DmaSubSliceMut` existed
643    /// were read-only).
644    unsafe fn take_no_acquire(mut self) -> SubSliceMut<'a, T> {
645        // Restore the original `SubSliceMut` configuration:
646        //
647        // SAFETY: The `internal_slice_ptr` pointer must be convertible to a
648        // reference. We know that it is because the only constructor for
649        // `self.internal_slice_ptr` started with a valid Rust reference.
650        let mut sub_slice_mut = SubSliceMut::new(unsafe { self.internal_slice_ptr.as_mut() });
651        sub_slice_mut.slice(self.active_range);
652        sub_slice_mut
653    }
654}
655
656/// A buffer that can be safely used for read-only DMA operations, backed by
657/// either a [`SubSliceMutImmut`].
658///
659/// Creating a [`DmaSubSliceMutImmut`] over a [`SubSliceMutImmut`] ensures that
660/// all prior Rust writes to the active region of this slice are observable by
661/// any DMA operations initiated through an MMIO write operation, where that
662/// MMIO write is performed *after* constructing the `DmaSubSliceMutImmut`.
663///
664/// For this guarantee to hold, the `DmaSubSliceMutImmut` struct must exist for
665/// the duration of the entire DMA operation, until the Rust program has
666/// observed that the operation is complete (such as by reading a status bit in
667/// memory or an MMIO register).
668///
669/// [`DmaSliceMutImmut`] may wrap an immutable, shared Rust slice
670/// reference. Furthermore, in contrast to `DmaSubSliceMut`,
671/// `DmaSubSliceMutImmut` may not expose writes performed by a DMA operation
672/// back to Rust. As such, its contents *must* not be modified by the DMA
673/// operation. For a DMA operation that may write to the active range of the
674/// supplied sub slice, use [`DmaSubSliceMut`] instead.
675#[derive(Debug)]
676pub enum DmaSubSliceMutImmut<'a, T: immutable_from_into_bytes::ImmutableFromIntoBytes> {
677    Immutable(DmaSubSlice<'a, T>),
678    Mutable(DmaSubSliceMut<'a, T>),
679}
680
681impl<'a, T: immutable_from_into_bytes::ImmutableFromIntoBytes> DmaSubSliceMutImmut<'a, T> {
682    /// Create a [`DmaSubSliceMutImmut`] from a [`SubSliceMutImmut`].
683    ///
684    /// This function uses the supplied `fence` object to ensure that all prior
685    /// writes to `slice` are exposed to any DMA operations initiated by an MMIO
686    /// read or write operation after this function returns, and which finish
687    /// before the resulting [`DmaSubSlice`] is dropped.
688    pub fn new(
689        sub_slice: SubSliceMutImmut<'_, T>,
690        fence: impl DmaFence,
691    ) -> DmaSubSliceMutImmut<'_, T> {
692        match sub_slice {
693            SubSliceMutImmut::Immutable(sub_slice) => {
694                DmaSubSliceMutImmut::Immutable(DmaSubSlice::new(sub_slice, fence))
695            }
696            SubSliceMutImmut::Mutable(sub_slice_mut) => DmaSubSliceMutImmut::Mutable(
697                // SAFETY: `DmaSubSliceMut::new` is unsafe, as dropping its return value
698                // without calling `take` may make the underlying buffer
699                // accessible as a Rust slice, potentially before the DMA
700                // operation is complete, and without using `fence.acquire` to
701                // make DMA writes visible to Rust. However, this struct does
702                // not permit DMA operations which write to the slice, and hence
703                // it can be safely dropped without risk of concurrent
704                // modifications or incoherence.
705                unsafe { DmaSubSliceMut::new(sub_slice_mut, fence) },
706            ),
707        }
708    }
709
710    /// Returns the pointer to the first element of the currently accessible
711    /// portion of the wrapped `SubSlice`.
712    pub fn as_ptr(&self) -> *const T {
713        match self {
714            DmaSubSliceMutImmut::Immutable(dma_sub_slice) => dma_sub_slice.as_ptr(),
715            DmaSubSliceMutImmut::Mutable(dma_sub_slice_mut) => {
716                dma_sub_slice_mut.as_mut_ptr().cast_const()
717            }
718        }
719    }
720
721    /// Returns the address of the slice to pass to DMA hardware, without exposing provenance.
722    pub fn ptr_addr(&self) -> usize {
723        self.as_ptr().addr()
724    }
725
726    /// Returns the length of the currently accessible range of the wrapped
727    /// `SubSlice`.
728    pub fn len(&self) -> usize {
729        match self {
730            DmaSubSliceMutImmut::Immutable(dma_sub_slice) => dma_sub_slice.len(),
731            DmaSubSliceMutImmut::Mutable(dma_sub_slice_mut) => dma_sub_slice_mut.len(),
732        }
733    }
734
735    /// Reconstruct the original `SubSliceMutImmut`.
736    ///
737    /// This function must be called only when the Rust program has observed
738    /// that the DMA operation over the buffer is complete (such as by reading a
739    /// status bit in memory or an MMIO register). Otherwise, any future reads
740    /// by the DMA peripheral may not be consistent with the buffer contents.
741    pub fn take(self) -> SubSliceMutImmut<'a, T> {
742        // We don't need to perform an `acquire` fence, as any DMA operation on
743        // the underlying slice must not have changed its contents:
744        match self {
745            DmaSubSliceMutImmut::Immutable(dma_sub_slice) => {
746                SubSliceMutImmut::Immutable(dma_sub_slice.as_sub_slice())
747            }
748            DmaSubSliceMutImmut::Mutable(dma_sub_slice_mut) => SubSliceMutImmut::Mutable(
749                // SAFETY: The user guarantees that there has not been any DMA operation
750                // that changed the buffers contents while the
751                // `DmaSubSliceMutImmut` existed, and hence restoring a unique
752                // Rust slice through `take` is safe. No acquire-fence is
753                // needed, given the bufer contents have not been modified.
754                unsafe { dma_sub_slice_mut.take_no_acquire() },
755            ),
756        }
757    }
758}
759
760pub mod immutable_from_into_bytes {
761    /// Sealing module for [`ImmutableFromIntoBytes`]
762    mod private {
763        /// Sealing trait for [`ImmutableFromIntoBytes`](super::ImmutableFromIntoBytes)
764        pub trait Sealed {}
765    }
766
767    /// A type that is can be safely converted to an initialized sequence of
768    /// bytes, from an arbitrary initialized sequence of bytes, and does not
769    /// feature interior mutability.
770    ///
771    /// The requirements on implementors of this trait are effectively the same
772    /// as the combination of zerocopy's [`FromBytes`][zerocopy-frombytes],
773    /// [`IntoBytes`][zerocopy-intobytes], and [`Immutable`][zerocopy-immutable]
774    /// traits.
775    ///
776    /// This trait is only implemented for a few select primitives, intended to
777    /// be used for DMA operations. It is sealed; all extensions to future types
778    /// must ensure they conform to the above trait's requirements and are safe
779    /// for DMA operations.
780    ///
781    /// [zerocopy-frombytes]: https://docs.rs/zerocopy/0.8.42/zerocopy/trait.FromBytes.html
782    /// [zerocopy-intobytes]: https://docs.rs/zerocopy/0.8.42/zerocopy/trait.IntoBytes.html
783    /// [zerocopy-immutable]: https://docs.rs/zerocopy/0.8.42/zerocopy/trait.Immutable.html
784    ///
785    /// # Safety
786    ///
787    /// Types that implement `ImmutableFromIntoBytes` must:
788    ///
789    /// 1. no_uninit: Not contain any uninitialized bytes (such as padding)
790    /// 2. no_interior_mut: Not have any interior mutability
791    /// 3. any_init_pattern: Be valid for all initialized byte patterns of the
792    ///    correct length and alignment.
793    ///
794    /// These guarantees effectively allow `&mut [T]` references to be converted
795    /// into `&mut [u8]` references, mutated, and then converted back into `&mut
796    /// [T]` references.
797    pub unsafe trait ImmutableFromIntoBytes: private::Sealed {}
798
799    impl private::Sealed for u8 {}
800    // SAFETY: The primitive u8 type is always initialized, does not have
801    // interior mutability, and is always valid for any byte patterns.
802    unsafe impl ImmutableFromIntoBytes for u8 {}
803    impl private::Sealed for u16 {}
804    // SAFETY: The primitive u16 type is always initialized, does not have
805    // interior mutability, and is always valid for any byte patterns.
806    unsafe impl ImmutableFromIntoBytes for u16 {}
807    impl private::Sealed for u32 {}
808    // SAFETY: The primitive u32 type is always initialized, does not have
809    // interior mutability, and is always valid for any byte patterns.
810    unsafe impl ImmutableFromIntoBytes for u32 {}
811    impl private::Sealed for u64 {}
812    // SAFETY: The primitive u64 type is always initialized, does not have
813    // interior mutability, and is always valid for any byte patterns.
814    unsafe impl ImmutableFromIntoBytes for u64 {}
815    impl private::Sealed for u128 {}
816    // SAFETY: The primitive u128 type is always initialized, does not have
817    // interior mutability, and is always valid for any byte patterns.
818    unsafe impl ImmutableFromIntoBytes for u128 {}
819}
820
821#[cfg(test)]
822mod miri_tests {
823    use core::ptr;
824
825    use super::super::leasable_buffer::{SubSlice, SubSliceMut};
826    use super::{DmaSlice, DmaSliceMut, DmaSubSlice, DmaSubSliceMut};
827
828    /// A mock fence that does nothing, as Miri operations are sequential within
829    /// a single thread for this test.
830    #[derive(Debug, Clone, Copy)]
831    struct MockFence {
832        _private: (),
833    }
834
835    impl MockFence {
836        /// `MockFence` does not actually deliver any guarantees and is only
837        /// used for testing, thus make it unsafe to construct:
838        unsafe fn new() -> Self {
839            MockFence { _private: () }
840        }
841    }
842
843    unsafe impl super::DmaFence for MockFence {
844        fn release<T>(self, _buf: *mut [T]) {
845            // In a real system, this flushes caches. In Miri, memory is
846            // perfectly coherent, and our "DMA" reads are simply raw pointer
847            // reads.
848        }
849
850        fn acquire<T>(self, _buf: *mut [T]) {
851            // In a real system, this invalidates caches. In Miri, memory is
852            // perfectly coherent, and our "DMA" writes are simply raw pointer
853            // writes.
854        }
855    }
856
857    // Helper to simulate a DMA peripheral writing to memory. This writes to the
858    // memory using the pointer exposed by the DMA wrapper, which is legal
859    // because the wrapper owns the mutable borrow.
860    unsafe fn simulate_dma_write<T: Copy>(dst: *mut T, val: T, offset: usize) {
861        let target = unsafe { dst.add(offset) };
862        unsafe {
863            ptr::write(target, val);
864        }
865    }
866
867    #[test]
868    fn test_dma_slice_immut_basic() {
869        let fence = unsafe { MockFence::new() };
870
871        let data = [10u8, 20, 30, 40];
872
873        // 1. Create DmaSlice
874        let dma = DmaSlice::new(&data, fence);
875
876        // 2. Verify properties
877        assert_eq!(dma.len(), 4);
878        assert_eq!(dma.as_ptr(), data.as_ptr());
879
880        // 3. Simulate DMA Read (peripheral reads from host memory)
881        //
882        // In Miri, we just check if we can read via the raw pointer while the
883        // borrow is active.
884        let val = unsafe { ptr::read(dma.as_ptr().add(1)) };
885        assert_eq!(val, 20);
886
887        // 4. Drop (Safe)
888        drop(dma);
889    }
890
891    #[test]
892    fn test_dma_slice_mut_write_cycle() {
893        let fence = unsafe { MockFence::new() };
894
895        let mut data = [0u8; 4];
896        let data_ptr = data.as_mut_ptr();
897
898        // 1. Create DmaSliceMut
899        //
900        // SAFETY: We call `take` at the end.
901        let dma = unsafe { DmaSliceMut::new(&mut data, fence) };
902
903        // 2. Verify basic pointer integrity
904        assert_eq!(dma.as_mut_ptr(), data_ptr);
905        assert_eq!(dma.len(), 4);
906
907        // 3. Simulate DMA Write
908        //
909        // The peripheral writes `0xAA` to index 2.
910        unsafe {
911            simulate_dma_write(dma.as_mut_ptr(), 0xAA_u8, 2);
912        }
913
914        // 4. Restore
915        //
916        // SAFETY: DMA is "done".
917        let restored_slice = unsafe { dma.take(fence) };
918
919        // 5. Verify that the writes are reflected in the buffer:
920        assert_eq!(restored_slice, &[0, 0, 0xAA, 0]);
921    }
922
923    #[test]
924    fn test_dma_slice_mut_static() {
925        let fence = unsafe { MockFence::new() };
926
927        // Test specifically for the static constructor which is safe:
928        static mut BUFFER: [u32; 2] = [1, 2];
929
930        // 1. Create from static
931        //
932        // Note: access to static mut is unsafe, but the from_static_slice_ref
933        // call itself is safe
934        let dma = DmaSliceMut::new_static(unsafe { &mut *(&raw mut BUFFER) }, fence);
935
936        // 2. Simulate DMA Write
937        unsafe {
938            simulate_dma_write(dma.as_mut_ptr(), 99u32, 0);
939        }
940
941        // 3. Restore
942        let restored = unsafe { dma.take(fence) };
943
944        assert_eq!(restored[0], 99);
945        assert_eq!(restored[1], 2);
946    }
947
948    #[test]
949    fn test_dma_sub_slice_immut() {
950        let fence = unsafe { MockFence::new() };
951
952        let data = [100u8, 101, 102, 103, 104];
953
954        // Create a SubSlice with an active range of 1..=2
955        let mut sub = SubSlice::new(&data);
956        sub.slice(1..=2);
957
958        let dma = DmaSubSlice::new(sub, fence);
959        assert_eq!(dma.len(), 2);
960
961        unsafe {
962            assert_eq!(*dma.as_ptr(), 101);
963            assert_eq!(*dma.as_ptr().add(1), 102);
964        }
965    }
966
967    #[test]
968    fn test_dma_sub_slice_mut_offset() {
969        let fence = unsafe { MockFence::new() };
970
971        let mut data = [0u64, 1, 2, 3, 4]; // u64 to test stride sizes > 1 byte
972
973        // Create a SubSliceMut with an active range of 2..4
974        let mut sub = SubSliceMut::new(&mut data);
975        sub.slice(2..4);
976
977        let dma = unsafe { DmaSubSliceMut::new(sub, fence) };
978        assert_eq!(dma.len(), 2);
979
980        // Verify Pointer logic
981        //
982        // dma.as_ptr() should point to data[2]
983        unsafe {
984            // Write to index 0 of the DMA view (which is index 2 of underlying)
985            simulate_dma_write(dma.as_mut_ptr(), 0xFF_FF_FF_FF_u64, 0);
986
987            // Write to index 1 of the DMA view (index 3 of underlying)
988            simulate_dma_write(dma.as_mut_ptr(), 0xEE_EE_EE_EE_u64, 1);
989        }
990
991        // 3. Restore
992        let restored_sub = unsafe { dma.take(fence) };
993        let full_slice = restored_sub.take();
994
995        // 4. Validate
996        assert_eq!(full_slice[0], 0); // Untouched
997        assert_eq!(full_slice[1], 1); // Untouched
998        assert_eq!(full_slice[2], 0xFF_FF_FF_FF); // Written
999        assert_eq!(full_slice[3], 0xEE_EE_EE_EE); // Written
1000        assert_eq!(full_slice[4], 4); // Untouched
1001    }
1002
1003    #[test]
1004    fn test_dma_sub_slice_mut_edge_cases() {
1005        let fence = unsafe { MockFence::new() };
1006
1007        let mut data = [0u8; 10];
1008        let data_ptr = data.as_mut_ptr();
1009
1010        // Case A: Empty Range
1011        {
1012            let mut sub = SubSliceMut::new(&mut data);
1013            sub.slice(5..5);
1014
1015            let dma = unsafe { DmaSubSliceMut::new(sub, fence) };
1016            assert_eq!(dma.len(), 0);
1017
1018            // Verify we return the correct ptr, even if we shouldn't deref it
1019            assert_eq!(dma.as_mut_ptr(), data_ptr.wrapping_add(5));
1020            unsafe { dma.take(fence) };
1021        }
1022
1023        // Case B: Range at exact end
1024        {
1025            let base_addr = data.as_ptr() as usize;
1026
1027            let mut sub = SubSliceMut::new(&mut data);
1028            sub.slice(10..10); // End of buffer
1029
1030            let dma = unsafe { DmaSubSliceMut::new(sub, fence) };
1031
1032            // Pointer should point one past the end of the array
1033            let ptr_addr = dma.as_mut_ptr() as usize;
1034            assert_eq!(ptr_addr, base_addr + 10);
1035
1036            unsafe { dma.take(fence) };
1037        }
1038
1039        // Case C: Range out of bounds (Should be clamped by implementation)
1040        {
1041            let mut sub = SubSliceMut::new(&mut data);
1042            sub.slice(8..15); // End is past 10
1043
1044            let dma = unsafe { DmaSubSliceMut::new(sub, fence) };
1045
1046            // Length should be clamped to available (10 - 8 = 2)
1047            assert_eq!(dma.len(), 2);
1048
1049            unsafe {
1050                simulate_dma_write(dma.as_mut_ptr(), 99u8, 0); // index 8
1051                simulate_dma_write(dma.as_mut_ptr(), 88u8, 1); // index 9
1052            }
1053
1054            let res = unsafe { dma.take(fence) };
1055            let arr = res.take();
1056            assert_eq!(arr[8], 99);
1057            assert_eq!(arr[9], 88);
1058        }
1059    }
1060}