safe_mmio/
lib.rs

1// Copyright 2025 The safe-mmio Authors.
2// This project is dual-licensed under Apache 2.0 and MIT terms.
3// See LICENSE-APACHE and LICENSE-MIT for details.
4
5//! Types for safe MMIO device access, especially in systems with an MMU.
6
7#![no_std]
8#![deny(clippy::undocumented_unsafe_blocks)]
9#![deny(unsafe_op_in_unsafe_fn)]
10
11#[cfg(target_arch = "aarch64")]
12mod aarch64_mmio;
13pub mod fields;
14mod physical;
15#[cfg(not(target_arch = "aarch64"))]
16mod volatile_mmio;
17
18use crate::fields::{ReadOnly, ReadPure, ReadPureWrite, ReadWrite, WriteOnly};
19use core::{array, fmt::Debug, marker::PhantomData, ops::Deref, ptr, ptr::NonNull};
20pub use physical::PhysicalInstance;
21use zerocopy::{FromBytes, Immutable, IntoBytes};
22
23/// A unique owned pointer to the registers of some MMIO device.
24///
25/// It is guaranteed to be valid and unique; no other access to the MMIO space of the device may
26/// happen for the lifetime `'a`.
27///
28/// A `UniqueMmioPointer` may be created from a mutable reference, but this should only be used for
29/// testing purposes, as references should never be constructed for real MMIO address space.
30pub struct UniqueMmioPointer<'a, T: ?Sized>(SharedMmioPointer<'a, T>);
31
32// Implement Debug, Eq and PartialEq manually rather than deriving to avoid an unneccessary bound on
33// T.
34
35impl<T: ?Sized> Debug for UniqueMmioPointer<'_, T> {
36    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
37        f.debug_tuple("UniqueMmioPointer")
38            .field(&self.0.regs)
39            .finish()
40    }
41}
42
43impl<T: ?Sized> PartialEq for UniqueMmioPointer<'_, T> {
44    fn eq(&self, other: &Self) -> bool {
45        self.0 == other.0
46    }
47}
48
49impl<T: ?Sized> Eq for UniqueMmioPointer<'_, T> {}
50
51impl<T: ?Sized> UniqueMmioPointer<'_, T> {
52    /// Creates a new `UniqueMmioPointer` from a non-null raw pointer.
53    ///
54    /// # Safety
55    ///
56    /// `regs` must be a properly aligned and valid pointer to some MMIO address space of type T,
57    /// which is mapped as device memory and valid to read and write from any thread with volatile
58    /// operations. There must not be any other aliases which are used to access the same MMIO
59    /// region while this `UniqueMmioPointer` exists.
60    ///
61    /// If `T` contains any fields wrapped in [`ReadOnly`], [`WriteOnly`] or [`ReadWrite`] then they
62    /// must indeed be safe to perform MMIO reads or writes on.
63    pub unsafe fn new(regs: NonNull<T>) -> Self {
64        Self(SharedMmioPointer {
65            regs,
66            phantom: PhantomData,
67        })
68    }
69
70    /// Creates a new `UniqueMmioPointer` with the same lifetime as this one.
71    ///
72    /// This is used internally by the [`field!`] macro and shouldn't be called directly.
73    ///
74    /// # Safety
75    ///
76    /// `regs` must be a properly aligned and valid pointer to some MMIO address space of type T,
77    /// within the allocation that `self` points to.
78    pub unsafe fn child<U>(&mut self, regs: NonNull<U>) -> UniqueMmioPointer<U> {
79        UniqueMmioPointer(SharedMmioPointer {
80            regs,
81            phantom: PhantomData,
82        })
83    }
84
85    /// Returns a raw mut pointer to the MMIO registers.
86    pub fn ptr_mut(&mut self) -> *mut T {
87        self.0.regs.as_ptr()
88    }
89
90    /// Returns a `NonNull<T>` pointer to the MMIO registers.
91    pub fn ptr_nonnull(&mut self) -> NonNull<T> {
92        self.0.regs
93    }
94}
95
96impl<T: FromBytes + IntoBytes> UniqueMmioPointer<'_, ReadWrite<T>> {
97    /// Performs an MMIO read of the entire `T`.
98    pub fn read(&mut self) -> T {
99        // SAFETY: self.regs is always a valid and unique pointer to MMIO address space, and `T`
100        // being wrapped in `ReadWrite` implies that it is safe to read.
101        unsafe { self.read_unsafe().0 }
102    }
103}
104
105impl<T: Immutable + IntoBytes> UniqueMmioPointer<'_, ReadWrite<T>> {
106    /// Performs an MMIO write of the entire `T`.
107    pub fn write(&mut self, value: T) {
108        // SAFETY: self.regs is always a valid and unique pointer to MMIO address space, and `T`
109        // being wrapped in `ReadWrite` implies that it is safe to write.
110        unsafe {
111            self.write_unsafe(ReadWrite(value));
112        }
113    }
114}
115
116impl<T: Immutable + IntoBytes> UniqueMmioPointer<'_, ReadPureWrite<T>> {
117    /// Performs an MMIO write of the entire `T`.
118    pub fn write(&mut self, value: T) {
119        // SAFETY: self.regs is always a valid and unique pointer to MMIO address space, and `T`
120        // being wrapped in `ReadPureWrite` implies that it is safe to write.
121        unsafe {
122            self.write_unsafe(ReadPureWrite(value));
123        }
124    }
125}
126
127impl<T: FromBytes + IntoBytes> UniqueMmioPointer<'_, ReadOnly<T>> {
128    /// Performs an MMIO read of the entire `T`.
129    pub fn read(&mut self) -> T {
130        // SAFETY: self.regs is always a valid and unique pointer to MMIO address space, and `T`
131        // being wrapped in `ReadOnly` implies that it is safe to read.
132        unsafe { self.read_unsafe().0 }
133    }
134}
135
136impl<T: Immutable + IntoBytes> UniqueMmioPointer<'_, WriteOnly<T>> {
137    /// Performs an MMIO write of the entire `T`.
138    pub fn write(&mut self, value: T) {
139        // SAFETY: self.regs is always a valid and unique pointer to MMIO address space, and `T`
140        // being wrapped in `WriteOnly` implies that it is safe to write.
141        unsafe {
142            self.write_unsafe(WriteOnly(value));
143        }
144    }
145}
146
147impl<T> UniqueMmioPointer<'_, [T]> {
148    /// Returns a `UniqueMmioPointer` to an element of this slice, or `None` if the index is out of
149    /// bounds.
150    ///
151    /// # Example
152    ///
153    /// ```
154    /// use safe_mmio::{UniqueMmioPointer, fields::ReadWrite};
155    ///
156    /// let mut slice: UniqueMmioPointer<[ReadWrite<u32>]>;
157    /// # let mut fake = [ReadWrite(1), ReadWrite(2), ReadWrite(3)];
158    /// # slice = UniqueMmioPointer::from(fake.as_mut_slice());
159    /// let mut element = slice.get(1).unwrap();
160    /// element.write(42);
161    /// ```
162    pub fn get(&mut self, index: usize) -> Option<UniqueMmioPointer<T>> {
163        if index >= self.len() {
164            return None;
165        }
166        // SAFETY: self.ptr_mut() is guaranteed to return a pointer that is valid for MMIO and
167        // unique, as promised by the caller of `UniqueMmioPointer::new`.
168        let regs = NonNull::new(unsafe { &raw mut (*self.ptr_mut())[index] }).unwrap();
169        // SAFETY: We created regs from the raw slice in self.regs, so it must also be valid, unique
170        // and within the allocation of self.regs.
171        Some(unsafe { self.child(regs) })
172    }
173}
174
175impl<T, const LEN: usize> UniqueMmioPointer<'_, [T; LEN]> {
176    /// Splits a `UniqueMmioPointer` to an array into an array of `UniqueMmioPointer`s.
177    pub fn split(&mut self) -> [UniqueMmioPointer<T>; LEN] {
178        array::from_fn(|i| {
179            UniqueMmioPointer(SharedMmioPointer {
180                // SAFETY: self.regs is always unique and valid for MMIO access. We make sure the
181                // pointers we split it into don't overlap, so the same applies to each of them.
182                regs: NonNull::new(unsafe { &raw mut (*self.ptr_mut())[i] }).unwrap(),
183                phantom: PhantomData,
184            })
185        })
186    }
187
188    /// Returns a `UniqueMmioPointer` to an element of this array, or `None` if the index is out of
189    /// bounds.
190    ///
191    /// # Example
192    ///
193    /// ```
194    /// use safe_mmio::{UniqueMmioPointer, fields::ReadWrite};
195    ///
196    /// let mut slice: UniqueMmioPointer<[ReadWrite<u32>; 3]>;
197    /// # let mut fake = [ReadWrite(1), ReadWrite(2), ReadWrite(3)];
198    /// # slice = UniqueMmioPointer::from(&mut fake);
199    /// let mut element = slice.get(1).unwrap();
200    /// element.write(42);
201    /// ```
202    pub fn get(&mut self, index: usize) -> Option<UniqueMmioPointer<T>> {
203        if index >= LEN {
204            return None;
205        }
206        // SAFETY: self.ptr_mut() is guaranteed to return a pointer that is valid for MMIO and
207        // unique, as promised by the caller of `UniqueMmioPointer::new`.
208        let regs = NonNull::new(unsafe { &raw mut (*self.ptr_mut())[index] }).unwrap();
209        // SAFETY: We created regs from the raw array in self.regs, so it must also be valid, unique
210        // and within the allocation of self.regs.
211        Some(unsafe { self.child(regs) })
212    }
213}
214
215impl<'a, T: ?Sized> From<&'a mut T> for UniqueMmioPointer<'a, T> {
216    fn from(r: &'a mut T) -> Self {
217        Self(SharedMmioPointer {
218            regs: r.into(),
219            phantom: PhantomData,
220        })
221    }
222}
223
224impl<'a, T: ?Sized> Deref for UniqueMmioPointer<'a, T> {
225    type Target = SharedMmioPointer<'a, T>;
226
227    fn deref(&self) -> &Self::Target {
228        &self.0
229    }
230}
231
232/// A shared pointer to the registers of some MMIO device.
233///
234/// It is guaranteed to be valid but unlike [`UniqueMmioPointer`] may not be unique.
235pub struct SharedMmioPointer<'a, T: ?Sized> {
236    regs: NonNull<T>,
237    phantom: PhantomData<&'a T>,
238}
239
240// Implement Debug, Eq and PartialEq manually rather than deriving to avoid an unneccessary bound on
241// T.
242
243impl<T: ?Sized> Debug for SharedMmioPointer<'_, T> {
244    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
245        f.debug_tuple("SharedMmioPointer")
246            .field(&self.regs)
247            .finish()
248    }
249}
250
251impl<T: ?Sized> PartialEq for SharedMmioPointer<'_, T> {
252    fn eq(&self, other: &Self) -> bool {
253        ptr::eq(self.regs.as_ptr(), other.regs.as_ptr())
254    }
255}
256
257impl<T: ?Sized> Eq for SharedMmioPointer<'_, T> {}
258
259impl<T: ?Sized> Clone for SharedMmioPointer<'_, T> {
260    fn clone(&self) -> Self {
261        Self {
262            regs: self.regs.clone(),
263            phantom: self.phantom.clone(),
264        }
265    }
266}
267
268impl<T: ?Sized> SharedMmioPointer<'_, T> {
269    /// Creates a new `SharedMmioPointer` with the same lifetime as this one.
270    ///
271    /// This is used internally by the [`field_shared!`] macro and shouldn't be called directly.
272    ///
273    /// # Safety
274    ///
275    /// `regs` must be a properly aligned and valid pointer to some MMIO address space of type T,
276    /// within the allocation that `self` points to.
277    pub unsafe fn child<U>(&self, regs: NonNull<U>) -> SharedMmioPointer<U> {
278        SharedMmioPointer {
279            regs,
280            phantom: PhantomData,
281        }
282    }
283
284    /// Returns a raw const pointer to the MMIO registers.
285    pub fn ptr(&self) -> *const T {
286        self.regs.as_ptr()
287    }
288}
289
290// SAFETY: A `SharedMmioPointer` always originates either from a reference or from a
291// `UniqueMmioPointer`. The caller of `UniqueMmioPointer::new` promises that the MMIO registers can
292// be accessed from any thread.
293unsafe impl<T: ?Sized + Send + Sync> Send for SharedMmioPointer<'_, T> {}
294
295impl<'a, T: ?Sized> From<&'a T> for SharedMmioPointer<'a, T> {
296    fn from(r: &'a T) -> Self {
297        Self {
298            regs: r.into(),
299            phantom: PhantomData,
300        }
301    }
302}
303
304impl<'a, T: ?Sized> From<UniqueMmioPointer<'a, T>> for SharedMmioPointer<'a, T> {
305    fn from(unique: UniqueMmioPointer<'a, T>) -> Self {
306        unique.0
307    }
308}
309
310impl<T: FromBytes + IntoBytes> SharedMmioPointer<'_, ReadPure<T>> {
311    /// Performs an MMIO read of the entire `T`.
312    pub fn read(&self) -> T {
313        // SAFETY: self.regs is always a valid and unique pointer to MMIO address space, and `T`
314        // being wrapped in `ReadPure` implies that it is safe to read from a shared reference
315        // because doing so has no side-effects.
316        unsafe { self.read_unsafe().0 }
317    }
318}
319
320impl<T: FromBytes + IntoBytes> SharedMmioPointer<'_, ReadPureWrite<T>> {
321    /// Performs an MMIO read of the entire `T`.
322    pub fn read(&self) -> T {
323        // SAFETY: self.regs is always a valid pointer to MMIO address space, and `T`
324        // being wrapped in `ReadPureWrite` implies that it is safe to read from a shared reference
325        // because doing so has no side-effects.
326        unsafe { self.read_unsafe().0 }
327    }
328}
329
330impl<T> SharedMmioPointer<'_, [T]> {
331    /// Returns a `SharedMmioPointer` to an element of this slice, or `None` if the index is out of
332    /// bounds.
333    pub fn get(&self, index: usize) -> Option<SharedMmioPointer<T>> {
334        if index >= self.len() {
335            return None;
336        }
337        // SAFETY: self.regs is always unique and valid for MMIO access.
338        let regs = NonNull::new(unsafe { &raw mut (*self.regs.as_ptr())[index] }).unwrap();
339        // SAFETY: We created regs from the raw slice in self.regs, so it must also be valid, unique
340        // and within the allocation of self.regs.
341        Some(unsafe { self.child(regs) })
342    }
343
344    /// Returns the length of the slice.
345    pub const fn len(&self) -> usize {
346        self.regs.len()
347    }
348
349    /// Returns whether the slice is empty.
350    pub const fn is_empty(&self) -> bool {
351        self.regs.is_empty()
352    }
353}
354
355impl<T, const LEN: usize> SharedMmioPointer<'_, [T; LEN]> {
356    /// Splits a `SharedMmioPointer` to an array into an array of `SharedMmioPointer`s.
357    pub fn split(&self) -> [SharedMmioPointer<T>; LEN] {
358        array::from_fn(|i| SharedMmioPointer {
359            // SAFETY: self.regs is always unique and valid for MMIO access. We make sure the
360            // pointers we split it into don't overlap, so the same applies to each of them.
361            regs: NonNull::new(unsafe { &raw mut (*self.regs.as_ptr())[i] }).unwrap(),
362            phantom: PhantomData,
363        })
364    }
365
366    /// Returns a `SharedMmioPointer` to an element of this array, or `None` if the index is out of
367    /// bounds.
368    pub fn get(&self, index: usize) -> Option<SharedMmioPointer<T>> {
369        if index >= LEN {
370            return None;
371        }
372        // SAFETY: self.regs is always unique and valid for MMIO access.
373        let regs = NonNull::new(unsafe { &raw mut (*self.regs.as_ptr())[index] }).unwrap();
374        // SAFETY: We created regs from the raw array in self.regs, so it must also be valid, unique
375        // and within the allocation of self.regs.
376        Some(unsafe { self.child(regs) })
377    }
378}
379
380/// Gets a `UniqueMmioPointer` to a field of a type wrapped in a `UniqueMmioPointer`.
381#[macro_export]
382macro_rules! field {
383    ($mmio_pointer:expr, $field:ident) => {{
384        // Make sure $mmio_pointer is the right type.
385        let mmio_pointer: &mut $crate::UniqueMmioPointer<_> = &mut $mmio_pointer;
386        // SAFETY: ptr_mut is guaranteed to return a valid pointer for MMIO, so the pointer to the
387        // field must also be valid. MmioPointer::child gives it the same lifetime as the original
388        // pointer.
389        unsafe {
390            let child_pointer =
391                core::ptr::NonNull::new(&raw mut (*mmio_pointer.ptr_mut()).$field).unwrap();
392            mmio_pointer.child(child_pointer)
393        }
394    }};
395}
396
397/// Gets a `SharedMmioPointer` to a field of a type wrapped in a `SharedMmioPointer`.
398#[macro_export]
399macro_rules! field_shared {
400    ($mmio_pointer:expr, $field:ident) => {{
401        // Make sure $mmio_pointer is the right type.
402        let mmio_pointer: &$crate::SharedMmioPointer<_> = &$mmio_pointer;
403        // SAFETY: ptr_mut is guaranteed to return a valid pointer for MMIO, so the pointer to the
404        // field must also be valid. MmioPointer::child gives it the same lifetime as the original
405        // pointer.
406        unsafe {
407            let child_pointer =
408                core::ptr::NonNull::new((&raw const (*mmio_pointer.ptr()).$field).cast_mut())
409                    .unwrap();
410            mmio_pointer.child(child_pointer)
411        }
412    }};
413}
414
415#[cfg(test)]
416mod tests {
417    use super::*;
418
419    #[test]
420    fn fields() {
421        #[repr(C)]
422        struct Foo {
423            a: ReadWrite<u32>,
424            b: ReadOnly<u32>,
425            c: ReadPure<u32>,
426        }
427
428        let mut foo = Foo {
429            a: ReadWrite(1),
430            b: ReadOnly(2),
431            c: ReadPure(3),
432        };
433        let mut owned: UniqueMmioPointer<Foo> = UniqueMmioPointer::from(&mut foo);
434
435        let mut owned_a: UniqueMmioPointer<ReadWrite<u32>> = field!(owned, a);
436        assert_eq!(owned_a.read(), 1);
437        owned_a.write(42);
438        assert_eq!(owned_a.read(), 42);
439        field!(owned, a).write(44);
440        assert_eq!(field!(owned, a).read(), 44);
441
442        let mut owned_b: UniqueMmioPointer<ReadOnly<u32>> = field!(owned, b);
443        assert_eq!(owned_b.read(), 2);
444
445        let owned_c: UniqueMmioPointer<ReadPure<u32>> = field!(owned, c);
446        assert_eq!(owned_c.read(), 3);
447        assert_eq!(field!(owned, c).read(), 3);
448    }
449
450    #[test]
451    fn shared_fields() {
452        #[repr(C)]
453        struct Foo {
454            a: ReadPureWrite<u32>,
455            b: ReadPure<u32>,
456        }
457
458        let foo = Foo {
459            a: ReadPureWrite(1),
460            b: ReadPure(2),
461        };
462        let shared: SharedMmioPointer<Foo> = SharedMmioPointer::from(&foo);
463
464        let shared_a: SharedMmioPointer<ReadPureWrite<u32>> = field_shared!(shared, a);
465        assert_eq!(shared_a.read(), 1);
466        assert_eq!(field_shared!(shared, a).read(), 1);
467
468        let shared_b: SharedMmioPointer<ReadPure<u32>> = field_shared!(shared, b);
469        assert_eq!(shared_b.read(), 2);
470    }
471
472    #[test]
473    fn shared_from_unique() {
474        #[repr(C)]
475        struct Foo {
476            a: ReadPureWrite<u32>,
477            b: ReadPure<u32>,
478        }
479
480        let mut foo = Foo {
481            a: ReadPureWrite(1),
482            b: ReadPure(2),
483        };
484        let unique: UniqueMmioPointer<Foo> = UniqueMmioPointer::from(&mut foo);
485
486        let shared_a: SharedMmioPointer<ReadPureWrite<u32>> = field_shared!(unique, a);
487        assert_eq!(shared_a.read(), 1);
488
489        let shared_b: SharedMmioPointer<ReadPure<u32>> = field_shared!(unique, b);
490        assert_eq!(shared_b.read(), 2);
491    }
492
493    #[test]
494    fn restricted_fields() {
495        #[repr(C)]
496        struct Foo {
497            r: ReadOnly<u32>,
498            w: WriteOnly<u32>,
499            u: u32,
500        }
501
502        let mut foo = Foo {
503            r: ReadOnly(1),
504            w: WriteOnly(2),
505            u: 3,
506        };
507        let mut owned: UniqueMmioPointer<Foo> = UniqueMmioPointer::from(&mut foo);
508
509        let mut owned_r: UniqueMmioPointer<ReadOnly<u32>> = field!(owned, r);
510        assert_eq!(owned_r.read(), 1);
511
512        let mut owned_w: UniqueMmioPointer<WriteOnly<u32>> = field!(owned, w);
513        owned_w.write(42);
514
515        let mut owned_u: UniqueMmioPointer<u32> = field!(owned, u);
516        // SAFETY: 'u' is safe to read or write because it's just a fake.
517        unsafe {
518            assert_eq!(owned_u.read_unsafe(), 3);
519            owned_u.write_unsafe(42);
520            assert_eq!(owned_u.read_unsafe(), 42);
521        }
522    }
523
524    #[test]
525    fn array() {
526        let mut foo = [ReadWrite(1), ReadWrite(2), ReadWrite(3)];
527        let mut owned = UniqueMmioPointer::from(&mut foo);
528
529        let mut parts = owned.split();
530        assert_eq!(parts[0].read(), 1);
531        assert_eq!(parts[1].read(), 2);
532        assert_eq!(owned.split()[2].read(), 3);
533    }
534
535    #[test]
536    fn array_shared() {
537        let foo = [ReadPure(1), ReadPure(2), ReadPure(3)];
538        let shared = SharedMmioPointer::from(&foo);
539
540        let parts = shared.split();
541        assert_eq!(parts[0].read(), 1);
542        assert_eq!(parts[1].read(), 2);
543        assert_eq!(shared.split()[2].read(), 3);
544    }
545
546    #[test]
547    fn slice() {
548        let mut foo = [ReadWrite(1), ReadWrite(2), ReadWrite(3)];
549        let mut owned = UniqueMmioPointer::from(foo.as_mut_slice());
550
551        assert!(!owned.ptr().is_null());
552        assert!(!owned.ptr_mut().is_null());
553
554        assert!(!owned.is_empty());
555        assert_eq!(owned.len(), 3);
556
557        let mut first: UniqueMmioPointer<ReadWrite<i32>> = owned.get(0).unwrap();
558        assert_eq!(first.read(), 1);
559
560        let mut second: UniqueMmioPointer<ReadWrite<i32>> = owned.get(1).unwrap();
561        assert_eq!(second.read(), 2);
562
563        assert!(owned.get(3).is_none());
564    }
565
566    #[test]
567    fn slice_shared() {
568        let foo = [ReadPure(1), ReadPure(2), ReadPure(3)];
569        let shared = SharedMmioPointer::from(foo.as_slice());
570
571        assert!(!shared.ptr().is_null());
572
573        assert!(!shared.is_empty());
574        assert_eq!(shared.len(), 3);
575
576        let first: SharedMmioPointer<ReadPure<i32>> = shared.get(0).unwrap();
577        assert_eq!(first.read(), 1);
578
579        let second: SharedMmioPointer<ReadPure<i32>> = shared.get(1).unwrap();
580        assert_eq!(second.read(), 2);
581
582        assert!(shared.get(3).is_none());
583    }
584}