mmio/
lib.rs

1// Copyright 2025 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#![deny(missing_docs)]
5//! Safe Memory Mapped I/O for Fuchsia drivers.
6//!
7//! MMIO allows a program to interact with devices through operations on its address space. While
8//! the interface is similar to regular memory access, there are some key semantic differences.
9//! This crate provides safe interfaces for interacting with MMIO that are intended to be sound.
10//!
11//! # Memory semantics
12//!
13//! ## Safe Rust
14//! The semantics of a safe Rust program are determined by the Rust Abstract Machine. The Rust
15//! Abstract Machine notably does not provide certain guarantees that are required when interacting
16//! with devices:
17//!
18//! - Guaranteed execution: I/O operations may be elided if the elision would have no impact on the
19//! observable behavior of the executing thread (they may also be repeated).
20//! - Operation ordering: operations may be reordered if doing so does not impact the observable
21//! behavior of the executing thread.
22//!
23//! For memory that is under the control of the Rust Abstract Machine, the semantics of a safe Rust
24//! program can depend on memory not changing value without a corresponding write.
25//!
26//! ## Device Memory
27//! Device memory is not under the control of the Rust Abstract Machine. The guarantees around the
28//! state and behavior of device memory do not provide the guarantees required for safe Rust.
29//! Similarly, safe Rust cannot encode the semantics required to interact correctly with device
30//! memory.
31//!
32//! ### Side Effects
33//! Reading from or writing to device memory may have observable side effects, such as clearing a
34//! status flag, advancing a buffer, or changing device state in some other way.
35//!
36//! ### Value Volatility
37//! The value that would be returned when loading from a device memory address may change
38//! completely independently of the program's control. Two subsequent reads on the same thread with
39//! no interleaved write may return different values. A read of a just-written value is not
40//! guaranteed to return the just-written value.
41//!
42//! ### Atomicity
43//! A memory instruction which may be atomic when operating on regular memory is not guaranteed to
44//! be atomic when interfacing with a device. For example a memory operation with a width larger
45//! than the bus size supported by the device *must* be broken down into multiple operations.
46//!
47//! ### Elision and Spurious Accesses
48//! As mentioned already, memory operations may be ellided or duplicated when executing safe Rust
49//! provided they don't change the single-threaded semantics of the program. If a shared or mutable
50//! reference to an object is active, memory operations may be performed to the backing memory. For
51//! this reason accesses to a memory location via pointers should only be performed when there are
52//! no active references that may alias the same memory.
53//!
54//! ### Compiler Reordering and Volatile
55//! The Rust Compiler has some freedom to reorder instructions. For regular memory accesses the
56//! compiler may reorder instructions where a data dependency does not prevent the reordering.
57//!
58//! The compiler is not allowed to reorder `volatile` operations with respect to other `volatile`
59//! operations in the same thread. It is free to reorder `volatile` operations with other kinds of
60//! memory accesses.
61//!
62//! By themselves, `volatile` operations cannot provide any sequencing guarantees with respect to
63//! non-volatile operations on the same thread, or *any* kind of operation on another thread.
64//!
65//! # User Guide
66//!
67//! Users of this crate interact with device memory through the [Mmio] trait. This trait exposes
68//! the low-level load and store operations to safely interact with device memory. All stores
69//! performed through an [Mmio] instance are issued through a mutable reference.
70//!
71//! If an [Mmio] implementation also implements [MmioSplit], it is possible to split off
72//! independently owned sub-regions from it. This allows concurrent mutable access to disjoint
73//! MMIO regions.
74//!
75//! ## Operand Types
76//! The [Mmio] trait provides load and store operations for the following types:
77//!
78//! - `u8`
79//! - `u16`
80//! - `u32`
81//! - `u64`
82//!
83//! Conforming implementations of [Mmio] must perform these operations in the code order relative
84//! to each other on the same thread, and in 1:1 correspondence with calls to corresponding
85//! function.
86//!
87//! Where the operand type is larger than the bus size, an implementation should perform the
88//! sub-operations in increasing address order.
89//!
90//! ## Dyn Compatibility and Trait Extensions
91//! The [Mmio] trait is dyn compatible. Callers may want to also import the [MmioExt] trait which
92//! defines some useful utilities on top of [Mmio] that would otherwise break dyn compatibility.
93//! This trait is implemented automatically for any [Mmio].
94//!
95//! ## Alignment
96//! The [Mmio] trait exposes offsets instead of addresses. Operations must be performed at a
97//! suitable offset for the operand type. Valid alignment is not an intrinsic property of an offset.
98//! Callers can use [Mmio::align_offset] to determine the first offset within the MMIO region
99//! suitable for a given alignment.
100//!
101//! ## Usage Examples
102//! ### VmoMapping
103//! ```
104//! use mmio::{Mmio, MmioExt};
105//! use mmio::vmo::VmoMapping;
106//! const VMO_OFFSET: usize = 0;
107//! const VMO_LEN: usize = 1024;
108//! # let vmo = zx::Vmo::create(VMO_LEN as u64).unwrap();
109//! // Map the Vmo memory, returning an `MmioRegion`. The returned region implements `Mmio`.
110//! let mut mmio = VmoMapping::map(VMO_OFFSET, VMO_LEN, vmo).unwrap();
111//! // Load 4 bytes starting at offset 0.
112//! let _ = mmio.load32(0);
113//! // Store 2 bytes starting at offset 32.
114//! let _ = mmio.store16(32, 0x1234);
115//! ```
116//!
117//! ### Splittable VmoMapping
118//! ```
119//! use mmio::{Mmio, MmioExt};
120//! use mmio::vmo::VmoMapping;
121//! const VMO_LEN: usize = 1024;
122//! # let vmo = zx::Vmo::create(VMO_LEN as u64).unwrap();
123//! // Map the Vmo memory and convert it into a splittable `MmioRegion`.
124//! // The returned region implements `Mmio + MmioSplit + Send`. If you only need split and not
125//! // you can use `into_split` instead.
126//! let mut mmio = VmoMapping::map(0, VMO_LEN, vmo).unwrap().into_split_send();
127//!
128//! // Split off a number of regions which have exclusive ownership of their ranges.
129//! // reg1 owns the first 8 bytes, which start at offset 0 in the original mapping.
130//! let mut reg1 = mmio.split_off(8);
131//! // reg2 owns the next 4 bytes, which start at offset 8 in the original mapping.
132//! let mut reg2 = mmio.split_off(4);
133//! // reg3 owns the next 4 bytes, which start at offset 12 in the original mapping.
134//! let mut reg3 = mmio.split_off(4);
135//! // The original mmio owns the rest of the region, starting at offset 16 in the original
136//! // mapping.
137//!
138//! let _ = reg1.load64(0);
139//! reg1.store16(2, 0x1234);
140//!
141//! let _ = reg2.load32(0);
142//! reg2.store8(3, 0xff);
143//!
144//! let _ = reg3.load32(0);
145//! reg3.store16(0, 0xff00);
146//! ```
147//!
148//! ### Alignment and Capacity
149//! ```
150//! use mmio::{Mmio, MmioExt};
151//! use mmio::vmo::VmoMapping;
152//! const VMO_LEN: usize = 1024;
153//! # let vmo = zx::Vmo::create(VMO_LEN as u64).unwrap();
154//! // Map the Vmo memory. The returned mapping can be converted into an `MmioRegion`.
155//! let mut mmio = VmoMapping::map(0, VMO_LEN, vmo).unwrap().into_split();
156//!
157//! // Split off a number of regions which have exclusive ownership of their ranges.
158//! // reg1 owns the first 8 bytes, which start at offset 0 in the original mapping.
159//! let mut reg1 = mmio.split_off(8);
160//! // reg2 owns the next 4 bytes, which start at offset 8 in the original mapping.
161//! let mut reg2 = mmio.split_off(4);
162//! // reg3 owns the next 4 bytes, which start at offset 12 in the original mapping.
163//! let mut reg3 = mmio.split_off(4);
164//! // The original mmio owns the rest of the region, starting at offset 16 in the original
165//! // mapping.
166//!
167//! // reg1 owns the region starting at offset 0 in the Vmo, which is mapped to a page aligned
168//! // boundary. It is suitably aligned for any type with an alignment <= Fuchsia's page size. It
169//! // covers an 8 byte range, to can hold any `MmioOperand` type.
170//! reg1.check_suitable_for::<u64>().unwrap();
171//!
172//! // reg2 owns the region starting at offset 8. It is aligned for any `MmioOperand` type but
173//! // covers a 4 byte range.
174//! reg2.check_aligned_for::<u64>().unwrap();
175//! reg2.check_capacity_for::<u64>().expect_err("expect MmioError::OutOfRange");
176//!
177//! reg2.check_suitable_for::<u32>().unwrap();
178//!
179//! // reg3 owns the region starting at offset 12. It is aligned for types with an alignment <= 4
180//! // and covers a 4 byte range.
181//! reg3.check_aligned_for::<u64>().expect_err("expect MmioError::Unaligned");
182//! reg3.check_capacity_for::<u64>().expect_err("expect MmioError::OutOfRange");
183//!
184//! reg3.check_suitable_for::<u32>().unwrap();
185//!
186//! // The original mmio object owns the region starting at offset 16 and covering the rest of the
187//! // mapping.
188//! mmio.check_suitable_for::<u64>().unwrap();
189//! mmio.check_suitable_for::<[u64; 8]>().unwrap();
190//! ```
191//!
192//! ## Testing
193//! ### Regular Memory Implementation
194//! ```
195//! use mmio::memory::Memory;
196//! use std::mem::MaybeUninit;
197//!
198//! let mut mem = MaybeUninit<[u64; 8]>::uninit();
199//! // A MaybeUninit's memory can be borrowed as an Mmio region.
200//! let mmio = Memory::borrow_uninit(&mut mem);
201//!
202//! run_test(mmio);
203//!
204//! // Safety: any bit pattern is valid for [u64; 8].
205//! let mem = unsafe { mem.assume_init() };
206//!
207//! // Check that the memory is in the expected state.
208//! validate_mem(mem);
209//! ```
210//!
211//! # Implementers Guide
212//! Implementers of [Mmio] and [MmioSplit] are required to uphold some guarantees. These are
213//! discussed more thoroughly in the corresponding trait's documentation. These requirements are
214//! intended to guarantee the semantics required to interface with devices correctly, as discussed
215//! earlier.
216
217mod arch;
218mod memory;
219mod mmio;
220pub mod region;
221pub mod vmo;
222
223pub use mmio::*;