Skip to main content

regio/
lib.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#![cfg_attr(not(any(test, feature = "testing")), no_std)]
6
7pub mod arm64;
8mod mmio;
9pub mod riscv64;
10pub mod traits;
11pub mod x86;
12
13#[cfg(any(test, feature = "testing"))]
14pub mod testing;
15
16use core::marker::PhantomData;
17
18pub use mmio::{Mmio, MmioBank, MmioPtr, Offset};
19
20mod private {
21    pub trait Sealed {}
22}
23
24/// Base marker for register access permissions.
25///
26/// This trait is sealed and cannot be implemented outside of this crate,
27/// ensuring that permissions cannot be forged.
28pub trait Accessible: private::Sealed {}
29
30/// A tag for register read access.
31#[derive(Clone, Copy, Debug)]
32pub enum Read {}
33
34/// A tag for disallowed register read access.
35#[derive(Clone, Copy, Debug)]
36pub enum NoRead {}
37
38/// A tag for safe register write access: this is when writes to the register
39/// are statically known to be sound (e.g., in the cases of MMIO that lights up
40/// an LED or a performance counter system register).
41#[derive(Clone, Copy, Debug)]
42pub enum SafeWrite {}
43
44/// A tag for unsafe register write access: this is when writes to the register
45/// may contextually violate the memory model (e.g., system register that
46/// toggles the MMU or in modeling a page table entry as a register). In such
47/// cases, the user must attest to any given write being sound.
48#[derive(Clone, Copy, Debug)]
49pub enum UnsafeWrite {}
50
51/// A tag for disallowed register write access.
52#[derive(Clone, Copy, Debug)]
53pub enum NoWrite {}
54
55impl private::Sealed for (Read, NoWrite) {}
56impl private::Sealed for (Read, SafeWrite) {}
57impl private::Sealed for (Read, UnsafeWrite) {}
58impl private::Sealed for (NoRead, SafeWrite) {}
59impl private::Sealed for (NoRead, UnsafeWrite) {}
60
61impl Accessible for (Read, NoWrite) {}
62impl Accessible for (Read, SafeWrite) {}
63impl Accessible for (Read, UnsafeWrite) {}
64impl Accessible for (NoRead, SafeWrite) {}
65impl Accessible for (NoRead, UnsafeWrite) {}
66
67/// Marker for readable register access.
68pub trait Readable: Accessible {}
69impl Readable for (Read, NoWrite) {}
70impl Readable for (Read, SafeWrite) {}
71impl Readable for (Read, UnsafeWrite) {}
72
73/// Marker for writable register access.
74pub trait Writable: Accessible {}
75impl Writable for (Read, SafeWrite) {}
76impl Writable for (Read, UnsafeWrite) {}
77impl Writable for (NoRead, SafeWrite) {}
78impl Writable for (NoRead, UnsafeWrite) {}
79
80// Aliases for brevity.
81
82/// A tag for read-only register access.
83pub type Ro = (Read, NoWrite);
84
85/// A tag for read + safe-write register access.
86pub type RwSafe = (Read, SafeWrite);
87
88/// A tag for read + unsafe-write register access.
89pub type RwUnsafe = (Read, UnsafeWrite);
90
91/// A tag for safe-write-only register access.
92pub type WoSafe = (NoRead, SafeWrite);
93
94/// A tag for unsafe-write-only register access.
95pub type WoUnsafe = (NoRead, UnsafeWrite);
96
97/// Expresses how one set of access permissions can imply a narrower set. For
98/// example, read-writable should imply readable.
99pub trait AccessRestrictsTo<Access: Accessible>: Accessible {}
100
101// Any set of permissions restricts to itself.
102impl<Access: Accessible> AccessRestrictsTo<Access> for Access {}
103
104impl AccessRestrictsTo<(Read, NoWrite)> for (Read, SafeWrite) {}
105impl AccessRestrictsTo<(Read, NoWrite)> for (Read, UnsafeWrite) {}
106
107impl AccessRestrictsTo<(Read, UnsafeWrite)> for (Read, SafeWrite) {}
108
109impl AccessRestrictsTo<(NoRead, SafeWrite)> for (Read, SafeWrite) {}
110impl AccessRestrictsTo<(NoRead, UnsafeWrite)> for (Read, UnsafeWrite) {}
111
112impl AccessRestrictsTo<(NoRead, UnsafeWrite)> for (NoRead, SafeWrite) {}
113impl AccessRestrictsTo<(NoRead, UnsafeWrite)> for (Read, SafeWrite) {}
114
115/// A convenience supertrait for the traits expected of layout types over a
116/// specific base type. This is not intended to be implemented explicitly, only
117/// through its blanket implementation.
118pub trait LayoutOver<Base>: Copy + From<Base> + Into<Base> {}
119
120impl<Layout, Base> LayoutOver<Base> for Layout where Layout: Copy + From<Base> + Into<Base> {}
121
122/// Represents an abstracted means of register access.
123pub trait IoHandle {
124    /// The underlying base type of the register (assumed integral in
125    /// practice).
126    type Base: Copy;
127}
128
129/// Represents an abstracted means of register reads.
130pub trait ReadHandle: IoHandle {
131    /// Performs the read.
132    ///
133    /// # Safety
134    ///
135    /// The caller must guarantee that the implementation-specific
136    /// preconditions for a sound read are met. For example, in the case of a
137    /// pointer, that the instance is valid, properly aligned, and pointing to
138    /// initialized memory.
139    unsafe fn read_raw(&self) -> Self::Base;
140}
141
142/// Represents an abstracted means of register writes.
143pub trait WriteHandle: IoHandle {
144    /// Performs the write of the provided value.
145    ///
146    /// # Safety
147    ///
148    /// The caller must guarantee...
149    ///
150    /// * that the value-agnostic, implementation-specific preconditions for a
151    ///   sound write are met;
152    ///
153    /// * and further that the particular value will not cause undefined
154    ///   behaviour when written (in an implementation-specific way).
155    ///
156    /// For example, in the case of a pointer to an MMIO address, the
157    /// conditions would be that the pointer instance is valid and aligned, and
158    /// that the particular value to write does not misconfigure hardware in a
159    /// way that leads to undefined behavior (such as configuring DMA to
160    /// overwrite arbitrary memory).
161    unsafe fn write_raw(&self, value: Self::Base);
162}
163
164/// Represents an abstracted means of atomic read-modify-write register I/O.
165//
166// TODO(https://github.com/rust-lang/rust/issues/132980): Consider generic
167// versions of the bit-set and bit-clear methods when we can consider const
168// generics of layout values.
169pub trait AtomicIoHandle: ReadHandle + WriteHandle {
170    /// Atomically swaps the contents of the register with the provided value,
171    /// returning the original contents.
172    ///
173    /// # Safety
174    ///
175    /// The caller must guarantee...
176    ///
177    /// * that the value-agnostic, implementation-specific preconditions for a
178    ///   sound write are met;
179    ///
180    /// * and further that the particular value will not cause undefined
181    ///   behaviour when written (in an implementation-specific way).
182    unsafe fn atomic_swap_raw(&self, value: Self::Base) -> Self::Base;
183
184    /// Atomically sets the provided bits on the contents of the register,
185    /// returning the original contents.
186    ///
187    /// # Safety
188    ///
189    /// The caller must guarantee...
190    ///
191    /// * that the value-agnostic, implementation-specific preconditions for a
192    ///   sound write are met;
193    ///
194    /// * and further that the particular value will not cause undefined
195    ///   behaviour when written (in an implementation-specific way).
196    unsafe fn atomic_set_bits_raw(&self, bits: Self::Base) -> Self::Base;
197
198    /// Atomically clears the provided bits on the contents of the register,
199    /// returning the original contents.
200    ///
201    /// # Safety
202    ///
203    /// The caller must guarantee...
204    ///
205    /// * that the value-agnostic, implementation-specific preconditions for a
206    ///   sound write are met;
207    ///
208    /// * and further that the particular value will not cause undefined
209    ///   behaviour when written (in an implementation-specific way).
210    unsafe fn atomic_clear_bits_raw(&self, bits: Self::Base) -> Self::Base;
211}
212
213/// `Register` represents a structured register layout, and its access
214/// interface and permissions. This is the core abstraction of the crate.
215///
216/// If `Access` expresses unsafe-writability, then all write methods are marked
217/// as unsafe.
218#[derive(Clone, Copy, Debug)]
219pub struct Register<Layout, Access, Io>
220where
221    Io: IoHandle,
222    Layout: LayoutOver<<Io as IoHandle>::Base>,
223    Access: Accessible,
224{
225    io: Io,
226    _marker: PhantomData<(Layout, Access)>,
227}
228
229impl<Layout, Access, Io> Register<Layout, Access, Io>
230where
231    Io: IoHandle,
232    Layout: LayoutOver<<Io as IoHandle>::Base>,
233    Access: Accessible,
234{
235    /// Constructs a new register from an I/O handle.
236    ///
237    /// # Safety
238    ///
239    /// The caller must guarantee...
240    ///
241    /// * that the provided I/O handle meets the implementation-specific
242    ///   access safety preconditions for the duration of the lifetimes of the
243    ///   register instance and any copies of it (e.g., in the case of an MMIO
244    ///   pointer, that the pointer is aligned and the memory it points to
245    ///   remains mapped for those lifetimes);
246    ///
247    /// * and that `Access` correctly models the safe nature of access in this
248    ///   context.
249    pub const unsafe fn from_io(io: Io) -> Self {
250        Self { io, _marker: PhantomData }
251    }
252
253    pub const fn io(&self) -> &Io {
254        &self.io
255    }
256
257    // TODO(https://github.com/rust-lang/rust/issues/73255): Make this const
258    // when ergonomically easy. Ditto for the into_*() methods below.
259    pub fn into_io(self) -> Io {
260        self.io
261    }
262
263    /// Reads from the register, if the backend and register permit it.
264    #[inline]
265    pub fn read(&self) -> Layout
266    where
267        Access: Readable,
268        Io: ReadHandle,
269    {
270        // Safety: The I/O handle was attested as meeting the handle-specific
271        // preconditions for reading for the duration of our lifetime.
272        unsafe { self.io.read_raw().into() }
273    }
274
275    // Write subroutine consolidating the handle-specific safety justification
276    // of the access.
277    //
278    // # Safety
279    //
280    // The caller must guarantee that the particular value will not cause
281    // undefined behaviour when written.
282    #[inline(always)]
283    unsafe fn write_impl(&self, value: Layout)
284    where
285        Access: Writable,
286        Io: WriteHandle,
287    {
288        // Safety: The I/O handle was attested as meeting the
289        // handle-specific preconditions for writing for our lifetime; the
290        // value-specific preconditions are left to the caller to justify in
291        // the case of unsafe-writability. Moreover, the caller attested to the
292        // safeness/unsafeness of the write access in general.
293        unsafe { self.io.write_raw(value.into()) }
294    }
295
296    // Atomic swap subroutine consolidating the handle-specific safety
297    // justification of the access.
298    //
299    // # Safety
300    //
301    // The caller must guarantee that the particular value will not cause
302    // undefined behaviour when written.
303    #[inline(always)]
304    unsafe fn atomic_swap_impl(&self, value: Layout) -> Layout
305    where
306        Access: Readable + Writable,
307        Io: AtomicIoHandle,
308    {
309        // Safety: The I/O handle was attested as meeting the
310        // handle-specific preconditions for writing for our lifetime; the
311        // value-specific preconditions are left to the caller to justify in
312        // the case of unsafe-writability. Moreover, the caller attested to the
313        // safeness/unsafeness of the write access in general.
314        unsafe { self.io.atomic_swap_raw(value.into()).into() }
315    }
316
317    // Atomic bit-set subroutine consolidating the handle-specific safety
318    // justification of the access.
319    //
320    // # Safety
321    //
322    // The caller must guarantee that the particular value will not cause
323    // undefined behaviour when written.
324    #[inline(always)]
325    unsafe fn atomic_set_bits_impl(&self, value: Layout) -> Layout
326    where
327        Access: Readable + Writable,
328        Io: AtomicIoHandle,
329    {
330        // Safety: The I/O handle was attested as meeting the
331        // handle-specific preconditions for writing for our lifetime; the
332        // value-specific preconditions are left to the caller to justify in
333        // the case of unsafe-writability. Moreover, the caller attested to the
334        // safeness/unsafeness of the write access in general.
335        unsafe { self.io.atomic_set_bits_raw(value.into()).into() }
336    }
337
338    // Atomic bit-clear subroutine consolidating the handle-specific safety
339    // justification of the access.
340    //
341    // # Safety
342    //
343    // The caller must guarantee that the particular value will not cause
344    // undefined behaviour when written.
345    #[inline(always)]
346    unsafe fn atomic_clear_bits_impl(&self, value: Layout) -> Layout
347    where
348        Access: Readable + Writable,
349        Io: AtomicIoHandle,
350    {
351        // Safety: The I/O handle was attested as meeting the
352        // handle-specific preconditions for writing for our lifetime; the
353        // value-specific preconditions are left to the caller to justify in
354        // the case of unsafe-writability. Moreover, the caller attested to the
355        // safeness/unsafeness of the write access in general.
356        unsafe { self.io.atomic_clear_bits_raw(value.into()).into() }
357    }
358}
359
360// This will be used to stamp out write-related methods differing only in
361// safety, across the disjoint cases of safe- and unsafe-writability
362macro_rules! impl_writable {
363    ($write_kind:ident) => {
364        impl_writable!(@impl $write_kind);
365    };
366    ($write_kind:ident, unsafe) => {
367        impl_writable!(
368            @impl $write_kind,
369            unsafe,
370            ///
371            /// # Safety
372            ///
373            /// The caller must guarantee that the write does not result in
374            /// undefined behaviour.
375        );
376    };
377    (
378        @impl $write_kind:ident
379        $(, $unsafe:ident )?
380        $(, $(#[$safety_doc:meta])* )?
381    ) => {
382        impl<Layout, R, Io> Register<Layout, (R, $write_kind), Io>
383        where
384            Io: WriteHandle,
385            Layout: LayoutOver<<Io as IoHandle>::Base>,
386            (R, $write_kind): Writable,
387        {
388            /// Writes to the register, if the backend and register permit it.
389            $($(#[$safety_doc])*)?
390            #[inline]
391            pub $($unsafe)? fn write(&self, value: Layout) {
392                // Safety: In the case of unsafe-writability, this method is
393                // unsafe and the caller themselves must provide the
394                // justification.
395                #[allow(unused_unsafe)]
396                unsafe { self.write_impl(value) }
397            }
398
399            /// Modifies the contents of the register, if the register and backend admit
400            /// both reads and writes, returning whatever state the modification
401            /// callback wanted to forward.
402            $($(#[$safety_doc])*)?
403            #[inline]
404            pub $($unsafe)? fn modify<ModifyFn, Ret>(&self, cb: ModifyFn) -> Ret
405            where
406                (R, $write_kind): Readable,
407                Io: ReadHandle,
408                ModifyFn: FnOnce(&mut Layout) -> Ret,
409            {
410                let mut value = self.read();
411                let ret = cb(&mut value);
412
413                // Safety: In the case of unsafe-writability, this method is
414                // unsafe and the caller themselves must provide the
415                // justification.
416                #[allow(unused_unsafe)]
417                unsafe { self.write_impl(value) }
418                ret
419            }
420
421            /// Atomically swaps the contents of the register with the provided
422            /// value, returning the original contents.
423            $($(#[$safety_doc])*)?
424            #[inline]
425            pub $($unsafe)? fn atomic_swap(&self, value: Layout) -> Layout
426            where
427                (R, $write_kind): Readable,
428                Io: AtomicIoHandle,
429            {
430                // Safety: In the case of unsafe-writability, this method is
431                // unsafe and the caller themselves must provide the
432                // justification.
433                #[allow(unused_unsafe)]
434                unsafe { self.atomic_swap_impl(value) }
435            }
436
437            /// Atomically sets the provided bits on the contents of the
438            /// register, returning the original contents.
439            $($(#[$safety_doc])*)?
440            #[inline]
441            pub $($unsafe)? fn atomic_set_bits(&self, bits: Layout) -> Layout
442            where
443                (R, $write_kind): Readable,
444                Io: AtomicIoHandle,
445            {
446                // Safety: In the case of unsafe-writability, this method is
447                // unsafe and the caller themselves must provide the
448                // justification.
449                #[allow(unused_unsafe)]
450                unsafe { self.atomic_set_bits_impl(bits) }
451            }
452
453            /// Atomically clears the provided bits on the contents of the
454            /// register, returning the original contents.
455            $($(#[$safety_doc])*)?
456            #[inline]
457            pub $($unsafe)? fn atomic_clear_bits(&self, bits: Layout) -> Layout
458            where
459                (R, $write_kind): Readable,
460                Io: AtomicIoHandle,
461            {
462                // Safety: In the case of unsafe-writability, this method is
463                // unsafe and the caller themselves must provide the
464                // justification.
465                #[allow(unused_unsafe)]
466                unsafe { self.atomic_clear_bits_impl(bits) }
467            }
468        }
469    };
470}
471
472impl_writable!(SafeWrite);
473impl_writable!(UnsafeWrite, unsafe);