README.md
1# safe-mmio
2
3[](https://crates.io/crates/safe-mmio)
4[](https://docs.rs/safe-mmio)
5
6This crate provides types for safe MMIO device access, especially in systems with an MMU.
7
8This is not an officially supported Google product.
9
10## Usage
11
12### UniqueMmioPointer
13
14The main type provided by this crate is `UniqueMmioPointer`. A `UniqueMmioPointer<T>` is roughly
15equivalent to an `&mut T` for a memory-mapped IO device. Suppose you want to construct a pointer to
16the data register of some UART device, and write some character to it:
17
18```rust
19use core::ptr::NonNull;
20use safe_mmio::UniqueMmioPointer;
21
22let mut data_register =
23 unsafe { UniqueMmioPointer::<u8>::new(NonNull::new(0x900_0000 as _).unwrap()) };
24unsafe {
25 data_register.write_unsafe(b'x');
26}
27```
28
29Depending on your platform this will either use `write_volatile` or some platform-dependent inline
30assembly to perform the MMIO write.
31
32### Safe MMIO methods
33
34If you know that a particular MMIO field is safe to access, you can use the appropriate wrapper type
35to mark that. In this case, suppose that the UART data register should only be written to:
36
37```rust
38use core::ptr::NonNull;
39use safe_mmio::{fields::WriteOnly, UniqueMmioPointer};
40
41let mut data_register: UniqueMmioPointer<WriteOnly<u8>> =
42 unsafe { UniqueMmioPointer::new(NonNull::new(0x900_0000 as _).unwrap()) };
43data_register.write(b'x');
44```
45
46### Grouping registers with a struct
47
48In practice, most devices have more than one register. To model this, you can create a struct, and
49then use the `field!` macro to project from a `UniqueMmioPointer` to the struct to a pointer to one
50of its fields:
51
52```rust
53use core::ptr::NonNull;
54use safe_mmio::{
55 field,
56 fields::{ReadOnly, ReadPure, ReadWrite, WriteOnly},
57 UniqueMmioPointer,
58};
59
60#[repr(C)]
61struct UartRegisters {
62 data: ReadWrite<u8>,
63 status: ReadPure<u8>,
64 pending_interrupt: ReadOnly<u8>,
65}
66
67let mut uart_registers: UniqueMmioPointer<UartRegisters> =
68 unsafe { UniqueMmioPointer::new(NonNull::new(0x900_0000 as _).unwrap()) };
69field!(uart_registers, data).write(b'x');
70```
71
72Methods are also provided to go from a `UniqueMmioPointer` to an array or slice to its elements.
73
74### Pure reads vs. side-effects
75
76We distinguish between fields which for which MMIO reads may have side effects (e.g. popping a byte
77from the UART's receive FIFO or clearing an interrupt status) and those for which reads are 'pure'
78with no side-effects. Reading from a `ReadOnly` or `ReadWrite` field is assumed to have
79side-effects, whereas reading from a `ReadPure` or `ReadPureWrite` must not. Reading from a
80`ReadOnly` or `ReadWrite` field requires an `&mut UniqueMmioPointer` (the same as writing), whereas
81reading from a `ReadPure` or `ReadPureWrite` field can be done with an `&UniqueMmioPointer` or
82`&SharedMmioPointer`.
83
84### Physical addresses
85
86`UniqueMmioPointer` (and `SharedMmioPointer`) is used for a pointer to a device which is mapped into
87the page table and accessible, i.e. a virtual address. Sometimes you may want to deal with the
88physical address of a device, which may or may not be mapped in. For this you can use the
89`PhysicalInstance` type. A `PhysicalInstance` doesn't let you do anything other than get the
90physical address and size of the device's MMIO region, but is intended to convey ownership. There
91should never be more than one `PhysicalInstance` pointer to the same device. This way your page
92table management code can take a `PhysicalInstance<T>` and return a `UniqueMmioPointer<T>` when a
93device is mapped into the page table.
94
95## Comparison with other MMIO crates
96
97There are a number of things that distinguish this crate from other crates providing abstractions
98for MMIO in Rust.
99
1001. We avoid creating references to MMIO address space. The Rust compiler is free to dereference
101 references whenever it likes, so constructing references to MMIO address space (even temporarily)
102 can lead to undefined behaviour. See https://github.com/rust-embedded/volatile-register/issues/10
103 for more background on this.
1042. We distinguish between MMIO reads which have side-effects (e.g. clearing an interrupt status, or
105 popping from a queue) and those which don't (e.g. just reading some status). A read which has
106 side-effects should be treated like a write and only be allowed from a unique pointer (passed via
107 &mut) whereas a read without side-effects can safely be done via a shared pointer (passed via
108 '&'), e.g. simultaneously from multiple threads.
1093. On most platforms MMIO reads and writes can be done via `read_volatile` and `write_volatile`, but
110 on aarch64 this may generate instructions which can't be virtualised. This is arguably
111 [a bug in rustc](https://github.com/rust-lang/rust/issues/131894), but in the meantime we work
112 around this by using inline assembly to generate the correct instructions for MMIO reads and
113 writes on aarch64.
114
115| Crate name | Last release | Version | Avoids references | Distinguishes reads with side-effects | Works around aarch64 volatile bug | Model | Field projection | Notes |
116| --------------------------------------------------------------- | -------------- | ------- | ----------------- | ------------------------------------- | --------------------------------- | ----------------------------------- | ------------------------------------ | --------------------------------------------------------------------------------- |
117| safe-mmio | March 2025 | 0.2.1 | ✅ | ✅ | ✅ | struct with field wrappers | macro |
118| [derive-mmio](https://crates.io/crates/derive-mmio) | February 2025 | 0.3.0 | ✅ | ❌ | ❌ | struct with derive macro | only one level, through derive macro |
119| [volatile](https://crates.io/crates/volatile) | June 2024 | 0.6.1 | ✅ | ❌ | ❌ | struct with derive macro | macro or generated methods |
120| [volatile-register](https://crates.io/crates/volatile-register) | October 2023 | 0.2.2 | ❌ | ❌ | ❌ | struct with field wrappers | manual (references) |
121| [tock-registers](https://crates.io/crates/tock-registers) | September 2023 | 0.9.0 | ❌ | ❌ | ❌ | macros to define fields and structs | manual (references) | Also covers CPU registers, and bitfields |
122| [mmio](https://crates.io/crates/mmio) | May 2021 | 2.1.0 | ✅ | ❌ | ❌ | only deals with individual fields | ❌ |
123| [rumio](https://crates.io/crates/rumio) | March 2021 | 0.2.0 | ✅ | ❌ | ❌ | macros to define fields and structs | generated methods | Also covers CPU registers, and bitfields |
124| [vcell](https://crates.io/crates/vcell) | January 2021 | 0.1.3 | ❌ | ❌ | ❌ | plain struct | manual (references) |
125| [register](https://crates.io/crates/register) | January 2021 | 1.0.2 | ❌ | ❌ | ❌ | macros to define fields and structs | manual (references) | Deprecated in favour of tock-registers. Also covers CPU registers, and bitfields. |
126
127## License
128
129Licensed under either of
130
131- Apache License, Version 2.0
132 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
133- MIT license
134 ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
135
136at your option.
137
138## Contributing
139
140If you want to contribute to the project, see details of
141[how we accept contributions](CONTRIBUTING.md).
142