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}