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);