summaryrefslogtreecommitdiff
path: root/rust/kernel
diff options
context:
space:
mode:
authorDanilo Krummrich <dakr@kernel.org>2026-03-17 20:11:03 +0100
committerDanilo Krummrich <dakr@kernel.org>2026-03-17 20:12:36 +0100
commitde25dc008ea74bc6f33b8d6e773e51a920813fdc (patch)
treee1ca61335676a2f5fd4bb603ef40f47d14398190 /rust/kernel
parentdc33ae50d32b509af5ae61030912fa20c79ef112 (diff)
parent79cf41692aadc3d0ac9b1d8e2c2f620ce2103918 (diff)
Merge tag 'rust_io-7.1-rc1' into driver-core-next
Register abstraction and I/O infrastructure improvements Introduce the register!() macro to define type-safe I/O register accesses. Refactor the IoCapable trait into a functional trait, which simplifies I/O backends and removes the need for overloaded Io methods. This is a stable tag for other trees to merge. Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Diffstat (limited to 'rust/kernel')
-rw-r--r--rust/kernel/cpufreq.rs1
-rw-r--r--rust/kernel/dma.rs114
-rw-r--r--rust/kernel/io.rs780
-rw-r--r--rust/kernel/io/mem.rs10
-rw-r--r--rust/kernel/io/register.rs1260
-rw-r--r--rust/kernel/lib.rs7
-rw-r--r--rust/kernel/num/bounded.rs70
-rw-r--r--rust/kernel/pci/io.rs99
-rw-r--r--rust/kernel/ptr.rs30
-rw-r--r--rust/kernel/ptr/projection.rs305
-rw-r--r--rust/kernel/str.rs4
11 files changed, 2248 insertions, 432 deletions
diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs
index 76faa1ac8501..f5adee48d40c 100644
--- a/rust/kernel/cpufreq.rs
+++ b/rust/kernel/cpufreq.rs
@@ -401,6 +401,7 @@ impl TableBuilder {
/// ```
/// use kernel::cpufreq::{DEFAULT_TRANSITION_LATENCY_NS, Policy};
///
+/// #[allow(clippy::double_parens, reason = "False positive before 1.92.0")]
/// fn update_policy(policy: &mut Policy) {
/// policy
/// .set_dvfs_possible_from_any_cpu(true)
diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs
index 909d56fd5118..a396f8435739 100644
--- a/rust/kernel/dma.rs
+++ b/rust/kernel/dma.rs
@@ -461,6 +461,19 @@ impl<T: AsBytes + FromBytes> CoherentAllocation<T> {
self.count * core::mem::size_of::<T>()
}
+ /// Returns the raw pointer to the allocated region in the CPU's virtual address space.
+ #[inline]
+ pub fn as_ptr(&self) -> *const [T] {
+ core::ptr::slice_from_raw_parts(self.cpu_addr.as_ptr(), self.count)
+ }
+
+ /// Returns the raw pointer to the allocated region in the CPU's virtual address space as
+ /// a mutable pointer.
+ #[inline]
+ pub fn as_mut_ptr(&self) -> *mut [T] {
+ core::ptr::slice_from_raw_parts_mut(self.cpu_addr.as_ptr(), self.count)
+ }
+
/// Returns the base address to the allocated region in the CPU's virtual address space.
pub fn start_ptr(&self) -> *const T {
self.cpu_addr.as_ptr()
@@ -581,23 +594,6 @@ impl<T: AsBytes + FromBytes> CoherentAllocation<T> {
Ok(())
}
- /// Returns a pointer to an element from the region with bounds checking. `offset` is in
- /// units of `T`, not the number of bytes.
- ///
- /// Public but hidden since it should only be used from [`dma_read`] and [`dma_write`] macros.
- #[doc(hidden)]
- pub fn item_from_index(&self, offset: usize) -> Result<*mut T> {
- if offset >= self.count {
- return Err(EINVAL);
- }
- // SAFETY:
- // - The pointer is valid due to type invariant on `CoherentAllocation`
- // and we've just checked that the range and index is within bounds.
- // - `offset` can't overflow since it is smaller than `self.count` and we've checked
- // that `self.count` won't overflow early in the constructor.
- Ok(unsafe { self.cpu_addr.as_ptr().add(offset) })
- }
-
/// Reads the value of `field` and ensures that its type is [`FromBytes`].
///
/// # Safety
@@ -670,6 +666,9 @@ unsafe impl<T: AsBytes + FromBytes + Send> Send for CoherentAllocation<T> {}
/// Reads a field of an item from an allocated region of structs.
///
+/// The syntax is of the form `kernel::dma_read!(dma, proj)` where `dma` is an expression evaluating
+/// to a [`CoherentAllocation`] and `proj` is a [projection specification](kernel::ptr::project!).
+///
/// # Examples
///
/// ```
@@ -684,36 +683,29 @@ unsafe impl<T: AsBytes + FromBytes + Send> Send for CoherentAllocation<T> {}
/// unsafe impl kernel::transmute::AsBytes for MyStruct{};
///
/// # fn test(alloc: &kernel::dma::CoherentAllocation<MyStruct>) -> Result {
-/// let whole = kernel::dma_read!(alloc[2]);
-/// let field = kernel::dma_read!(alloc[1].field);
+/// let whole = kernel::dma_read!(alloc, [2]?);
+/// let field = kernel::dma_read!(alloc, [1]?.field);
/// # Ok::<(), Error>(()) }
/// ```
#[macro_export]
macro_rules! dma_read {
- ($dma:expr, $idx: expr, $($field:tt)*) => {{
- (|| -> ::core::result::Result<_, $crate::error::Error> {
- let item = $crate::dma::CoherentAllocation::item_from_index(&$dma, $idx)?;
- // SAFETY: `item_from_index` ensures that `item` is always a valid pointer and can be
- // dereferenced. The compiler also further validates the expression on whether `field`
- // is a member of `item` when expanded by the macro.
- unsafe {
- let ptr_field = ::core::ptr::addr_of!((*item) $($field)*);
- ::core::result::Result::Ok(
- $crate::dma::CoherentAllocation::field_read(&$dma, ptr_field)
- )
- }
- })()
+ ($dma:expr, $($proj:tt)*) => {{
+ let dma = &$dma;
+ let ptr = $crate::ptr::project!(
+ $crate::dma::CoherentAllocation::as_ptr(dma), $($proj)*
+ );
+ // SAFETY: The pointer created by the projection is within the DMA region.
+ unsafe { $crate::dma::CoherentAllocation::field_read(dma, ptr) }
}};
- ($dma:ident [ $idx:expr ] $($field:tt)* ) => {
- $crate::dma_read!($dma, $idx, $($field)*)
- };
- ($($dma:ident).* [ $idx:expr ] $($field:tt)* ) => {
- $crate::dma_read!($($dma).*, $idx, $($field)*)
- };
}
/// Writes to a field of an item from an allocated region of structs.
///
+/// The syntax is of the form `kernel::dma_write!(dma, proj, val)` where `dma` is an expression
+/// evaluating to a [`CoherentAllocation`], `proj` is a
+/// [projection specification](kernel::ptr::project!), and `val` is the value to be written to the
+/// projected location.
+///
/// # Examples
///
/// ```
@@ -728,37 +720,31 @@ macro_rules! dma_read {
/// unsafe impl kernel::transmute::AsBytes for MyStruct{};
///
/// # fn test(alloc: &kernel::dma::CoherentAllocation<MyStruct>) -> Result {
-/// kernel::dma_write!(alloc[2].member = 0xf);
-/// kernel::dma_write!(alloc[1] = MyStruct { member: 0xf });
+/// kernel::dma_write!(alloc, [2]?.member, 0xf);
+/// kernel::dma_write!(alloc, [1]?, MyStruct { member: 0xf });
/// # Ok::<(), Error>(()) }
/// ```
#[macro_export]
macro_rules! dma_write {
- ($dma:ident [ $idx:expr ] $($field:tt)*) => {{
- $crate::dma_write!($dma, $idx, $($field)*)
- }};
- ($($dma:ident).* [ $idx:expr ] $($field:tt)* ) => {{
- $crate::dma_write!($($dma).*, $idx, $($field)*)
+ (@parse [$dma:expr] [$($proj:tt)*] [, $val:expr]) => {{
+ let dma = &$dma;
+ let ptr = $crate::ptr::project!(
+ mut $crate::dma::CoherentAllocation::as_mut_ptr(dma), $($proj)*
+ );
+ let val = $val;
+ // SAFETY: The pointer created by the projection is within the DMA region.
+ unsafe { $crate::dma::CoherentAllocation::field_write(dma, ptr, val) }
}};
- ($dma:expr, $idx: expr, = $val:expr) => {
- (|| -> ::core::result::Result<_, $crate::error::Error> {
- let item = $crate::dma::CoherentAllocation::item_from_index(&$dma, $idx)?;
- // SAFETY: `item_from_index` ensures that `item` is always a valid item.
- unsafe { $crate::dma::CoherentAllocation::field_write(&$dma, item, $val) }
- ::core::result::Result::Ok(())
- })()
+ (@parse [$dma:expr] [$($proj:tt)*] [.$field:tt $($rest:tt)*]) => {
+ $crate::dma_write!(@parse [$dma] [$($proj)* .$field] [$($rest)*])
+ };
+ (@parse [$dma:expr] [$($proj:tt)*] [[$index:expr]? $($rest:tt)*]) => {
+ $crate::dma_write!(@parse [$dma] [$($proj)* [$index]?] [$($rest)*])
+ };
+ (@parse [$dma:expr] [$($proj:tt)*] [[$index:expr] $($rest:tt)*]) => {
+ $crate::dma_write!(@parse [$dma] [$($proj)* [$index]] [$($rest)*])
};
- ($dma:expr, $idx: expr, $(.$field:ident)* = $val:expr) => {
- (|| -> ::core::result::Result<_, $crate::error::Error> {
- let item = $crate::dma::CoherentAllocation::item_from_index(&$dma, $idx)?;
- // SAFETY: `item_from_index` ensures that `item` is always a valid pointer and can be
- // dereferenced. The compiler also further validates the expression on whether `field`
- // is a member of `item` when expanded by the macro.
- unsafe {
- let ptr_field = ::core::ptr::addr_of_mut!((*item) $(.$field)*);
- $crate::dma::CoherentAllocation::field_write(&$dma, ptr_field, $val)
- }
- ::core::result::Result::Ok(())
- })()
+ ($dma:expr, $($rest:tt)*) => {
+ $crate::dma_write!(@parse [$dma] [] [$($rest)*])
};
}
diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
index e5fba6bf6db0..fcc7678fd9e3 100644
--- a/rust/kernel/io.rs
+++ b/rust/kernel/io.rs
@@ -11,10 +11,14 @@ use crate::{
pub mod mem;
pub mod poll;
+pub mod register;
pub mod resource;
+pub use crate::register;
pub use resource::Resource;
+use register::LocatedRegister;
+
/// Physical address type.
///
/// This is a type alias to either `u32` or `u64` depending on the config option
@@ -137,177 +141,6 @@ impl<const SIZE: usize> MmioRaw<SIZE> {
#[repr(transparent)]
pub struct Mmio<const SIZE: usize = 0>(MmioRaw<SIZE>);
-/// Internal helper macros used to invoke C MMIO read functions.
-///
-/// This macro is intended to be used by higher-level MMIO access macros (io_define_read) and
-/// provides a unified expansion for infallible vs. fallible read semantics. It emits a direct call
-/// into the corresponding C helper and performs the required cast to the Rust return type.
-///
-/// # Parameters
-///
-/// * `$c_fn` – The C function performing the MMIO read.
-/// * `$self` – The I/O backend object.
-/// * `$ty` – The type of the value to be read.
-/// * `$addr` – The MMIO address to read.
-///
-/// This macro does not perform any validation; all invariants must be upheld by the higher-level
-/// abstraction invoking it.
-macro_rules! call_mmio_read {
- (infallible, $c_fn:ident, $self:ident, $type:ty, $addr:expr) => {
- // SAFETY: By the type invariant `addr` is a valid address for MMIO operations.
- unsafe { bindings::$c_fn($addr as *const c_void) as $type }
- };
-
- (fallible, $c_fn:ident, $self:ident, $type:ty, $addr:expr) => {{
- // SAFETY: By the type invariant `addr` is a valid address for MMIO operations.
- Ok(unsafe { bindings::$c_fn($addr as *const c_void) as $type })
- }};
-}
-
-/// Internal helper macros used to invoke C MMIO write functions.
-///
-/// This macro is intended to be used by higher-level MMIO access macros (io_define_write) and
-/// provides a unified expansion for infallible vs. fallible write semantics. It emits a direct call
-/// into the corresponding C helper and performs the required cast to the Rust return type.
-///
-/// # Parameters
-///
-/// * `$c_fn` – The C function performing the MMIO write.
-/// * `$self` – The I/O backend object.
-/// * `$ty` – The type of the written value.
-/// * `$addr` – The MMIO address to write.
-/// * `$value` – The value to write.
-///
-/// This macro does not perform any validation; all invariants must be upheld by the higher-level
-/// abstraction invoking it.
-macro_rules! call_mmio_write {
- (infallible, $c_fn:ident, $self:ident, $ty:ty, $addr:expr, $value:expr) => {
- // SAFETY: By the type invariant `addr` is a valid address for MMIO operations.
- unsafe { bindings::$c_fn($value, $addr as *mut c_void) }
- };
-
- (fallible, $c_fn:ident, $self:ident, $ty:ty, $addr:expr, $value:expr) => {{
- // SAFETY: By the type invariant `addr` is a valid address for MMIO operations.
- unsafe { bindings::$c_fn($value, $addr as *mut c_void) };
- Ok(())
- }};
-}
-
-/// Generates an accessor method for reading from an I/O backend.
-///
-/// This macro reduces boilerplate by automatically generating either compile-time bounds-checked
-/// (infallible) or runtime bounds-checked (fallible) read methods. It abstracts the address
-/// calculation and bounds checking, and delegates the actual I/O read operation to a specified
-/// helper macro, making it generic over different I/O backends.
-///
-/// # Parameters
-///
-/// * `infallible` / `fallible` - Determines the bounds-checking strategy. `infallible` relies on
-/// `IoKnownSize` for compile-time checks and returns the value directly. `fallible` performs
-/// runtime checks against `maxsize()` and returns a `Result<T>`.
-/// * `$(#[$attr:meta])*` - Optional attributes to apply to the generated method (e.g.,
-/// `#[cfg(CONFIG_64BIT)]` or inline directives).
-/// * `$vis:vis` - The visibility of the generated method (e.g., `pub`).
-/// * `$name:ident` / `$try_name:ident` - The name of the generated method (e.g., `read32`,
-/// `try_read8`).
-/// * `$call_macro:ident` - The backend-specific helper macro used to emit the actual I/O call
-/// (e.g., `call_mmio_read`).
-/// * `$c_fn:ident` - The backend-specific C function or identifier to be passed into the
-/// `$call_macro`.
-/// * `$type_name:ty` - The Rust type of the value being read (e.g., `u8`, `u32`).
-#[macro_export]
-macro_rules! io_define_read {
- (infallible, $(#[$attr:meta])* $vis:vis $name:ident, $call_macro:ident($c_fn:ident) ->
- $type_name:ty) => {
- /// Read IO data from a given offset known at compile time.
- ///
- /// Bound checks are performed on compile time, hence if the offset is not known at compile
- /// time, the build will fail.
- $(#[$attr])*
- // Always inline to optimize out error path of `io_addr_assert`.
- #[inline(always)]
- $vis fn $name(&self, offset: usize) -> $type_name {
- let addr = self.io_addr_assert::<$type_name>(offset);
-
- // SAFETY: By the type invariant `addr` is a valid address for IO operations.
- $call_macro!(infallible, $c_fn, self, $type_name, addr)
- }
- };
-
- (fallible, $(#[$attr:meta])* $vis:vis $try_name:ident, $call_macro:ident($c_fn:ident) ->
- $type_name:ty) => {
- /// Read IO data from a given offset.
- ///
- /// Bound checks are performed on runtime, it fails if the offset (plus the type size) is
- /// out of bounds.
- $(#[$attr])*
- $vis fn $try_name(&self, offset: usize) -> Result<$type_name> {
- let addr = self.io_addr::<$type_name>(offset)?;
-
- // SAFETY: By the type invariant `addr` is a valid address for IO operations.
- $call_macro!(fallible, $c_fn, self, $type_name, addr)
- }
- };
-}
-pub use io_define_read;
-
-/// Generates an accessor method for writing to an I/O backend.
-///
-/// This macro reduces boilerplate by automatically generating either compile-time bounds-checked
-/// (infallible) or runtime bounds-checked (fallible) write methods. It abstracts the address
-/// calculation and bounds checking, and delegates the actual I/O write operation to a specified
-/// helper macro, making it generic over different I/O backends.
-///
-/// # Parameters
-///
-/// * `infallible` / `fallible` - Determines the bounds-checking strategy. `infallible` relies on
-/// `IoKnownSize` for compile-time checks and returns `()`. `fallible` performs runtime checks
-/// against `maxsize()` and returns a `Result`.
-/// * `$(#[$attr:meta])*` - Optional attributes to apply to the generated method (e.g.,
-/// `#[cfg(CONFIG_64BIT)]` or inline directives).
-/// * `$vis:vis` - The visibility of the generated method (e.g., `pub`).
-/// * `$name:ident` / `$try_name:ident` - The name of the generated method (e.g., `write32`,
-/// `try_write8`).
-/// * `$call_macro:ident` - The backend-specific helper macro used to emit the actual I/O call
-/// (e.g., `call_mmio_write`).
-/// * `$c_fn:ident` - The backend-specific C function or identifier to be passed into the
-/// `$call_macro`.
-/// * `$type_name:ty` - The Rust type of the value being written (e.g., `u8`, `u32`). Note the use
-/// of `<-` before the type to denote a write operation.
-#[macro_export]
-macro_rules! io_define_write {
- (infallible, $(#[$attr:meta])* $vis:vis $name:ident, $call_macro:ident($c_fn:ident) <-
- $type_name:ty) => {
- /// Write IO data from a given offset known at compile time.
- ///
- /// Bound checks are performed on compile time, hence if the offset is not known at compile
- /// time, the build will fail.
- $(#[$attr])*
- // Always inline to optimize out error path of `io_addr_assert`.
- #[inline(always)]
- $vis fn $name(&self, value: $type_name, offset: usize) {
- let addr = self.io_addr_assert::<$type_name>(offset);
-
- $call_macro!(infallible, $c_fn, self, $type_name, addr, value);
- }
- };
-
- (fallible, $(#[$attr:meta])* $vis:vis $try_name:ident, $call_macro:ident($c_fn:ident) <-
- $type_name:ty) => {
- /// Write IO data from a given offset.
- ///
- /// Bound checks are performed on runtime, it fails if the offset (plus the type size) is
- /// out of bounds.
- $(#[$attr])*
- $vis fn $try_name(&self, value: $type_name, offset: usize) -> Result {
- let addr = self.io_addr::<$type_name>(offset)?;
-
- $call_macro!(fallible, $c_fn, self, $type_name, addr, value)
- }
- };
-}
-pub use io_define_write;
-
/// Checks whether an access of type `U` at the given `offset`
/// is valid within this region.
#[inline]
@@ -320,14 +153,74 @@ const fn offset_valid<U>(offset: usize, size: usize) -> bool {
}
}
-/// Marker trait indicating that an I/O backend supports operations of a certain type.
+/// Trait indicating that an I/O backend supports operations of a certain type and providing an
+/// implementation for these operations.
///
/// Different I/O backends can implement this trait to expose only the operations they support.
///
/// For example, a PCI configuration space may implement `IoCapable<u8>`, `IoCapable<u16>`,
/// and `IoCapable<u32>`, but not `IoCapable<u64>`, while an MMIO region on a 64-bit
/// system might implement all four.
-pub trait IoCapable<T> {}
+pub trait IoCapable<T> {
+ /// Performs an I/O read of type `T` at `address` and returns the result.
+ ///
+ /// # Safety
+ ///
+ /// The range `[address..address + size_of::<T>()]` must be within the bounds of `Self`.
+ unsafe fn io_read(&self, address: usize) -> T;
+
+ /// Performs an I/O write of `value` at `address`.
+ ///
+ /// # Safety
+ ///
+ /// The range `[address..address + size_of::<T>()]` must be within the bounds of `Self`.
+ unsafe fn io_write(&self, value: T, address: usize);
+}
+
+/// Describes a given I/O location: its offset, width, and type to convert the raw value from and
+/// into.
+///
+/// This trait is the key abstraction allowing [`Io::read`], [`Io::write`], and [`Io::update`] (and
+/// their fallible [`try_read`](Io::try_read), [`try_write`](Io::try_write) and
+/// [`try_update`](Io::try_update) counterparts) to work uniformly with both raw [`usize`] offsets
+/// (for primitive types like [`u32`]) and typed ones (like those generated by the [`register!`]
+/// macro).
+///
+/// An `IoLoc<T>` carries three pieces of information:
+///
+/// - The offset to access (returned by [`IoLoc::offset`]),
+/// - The width of the access (determined by [`IoLoc::IoType`]),
+/// - The type `T` in which the raw data is returned or provided.
+///
+/// `T` and `IoLoc::IoType` may differ: for instance, a typed register has `T` = the register type
+/// with its bitfields, and `IoType` = its backing primitive (e.g. `u32`).
+pub trait IoLoc<T> {
+ /// Size ([`u8`], [`u16`], etc) of the I/O performed on the returned [`offset`](IoLoc::offset).
+ type IoType: Into<T> + From<T>;
+
+ /// Consumes `self` and returns the offset of this location.
+ fn offset(self) -> usize;
+}
+
+/// Implements [`IoLoc<$ty>`] for [`usize`], allowing [`usize`] to be used as a parameter of
+/// [`Io::read`] and [`Io::write`].
+macro_rules! impl_usize_ioloc {
+ ($($ty:ty),*) => {
+ $(
+ impl IoLoc<$ty> for usize {
+ type IoType = $ty;
+
+ #[inline(always)]
+ fn offset(self) -> usize {
+ self
+ }
+ }
+ )*
+ }
+}
+
+// Provide the ability to read any primitive type from a [`usize`].
+impl_usize_ioloc!(u8, u16, u32, u64);
/// Types implementing this trait (e.g. MMIO BARs or PCI config regions)
/// can perform I/O operations on regions of memory.
@@ -369,146 +262,445 @@ pub trait Io {
/// Fallible 8-bit read with runtime bounds check.
#[inline(always)]
- fn try_read8(&self, _offset: usize) -> Result<u8>
+ fn try_read8(&self, offset: usize) -> Result<u8>
where
Self: IoCapable<u8>,
{
- build_error!("Backend does not support fallible 8-bit read")
+ self.try_read(offset)
}
/// Fallible 16-bit read with runtime bounds check.
#[inline(always)]
- fn try_read16(&self, _offset: usize) -> Result<u16>
+ fn try_read16(&self, offset: usize) -> Result<u16>
where
Self: IoCapable<u16>,
{
- build_error!("Backend does not support fallible 16-bit read")
+ self.try_read(offset)
}
/// Fallible 32-bit read with runtime bounds check.
#[inline(always)]
- fn try_read32(&self, _offset: usize) -> Result<u32>
+ fn try_read32(&self, offset: usize) -> Result<u32>
where
Self: IoCapable<u32>,
{
- build_error!("Backend does not support fallible 32-bit read")
+ self.try_read(offset)
}
/// Fallible 64-bit read with runtime bounds check.
#[inline(always)]
- fn try_read64(&self, _offset: usize) -> Result<u64>
+ fn try_read64(&self, offset: usize) -> Result<u64>
where
Self: IoCapable<u64>,
{
- build_error!("Backend does not support fallible 64-bit read")
+ self.try_read(offset)
}
/// Fallible 8-bit write with runtime bounds check.
#[inline(always)]
- fn try_write8(&self, _value: u8, _offset: usize) -> Result
+ fn try_write8(&self, value: u8, offset: usize) -> Result
where
Self: IoCapable<u8>,
{
- build_error!("Backend does not support fallible 8-bit write")
+ self.try_write(offset, value)
}
/// Fallible 16-bit write with runtime bounds check.
#[inline(always)]
- fn try_write16(&self, _value: u16, _offset: usize) -> Result
+ fn try_write16(&self, value: u16, offset: usize) -> Result
where
Self: IoCapable<u16>,
{
- build_error!("Backend does not support fallible 16-bit write")
+ self.try_write(offset, value)
}
/// Fallible 32-bit write with runtime bounds check.
#[inline(always)]
- fn try_write32(&self, _value: u32, _offset: usize) -> Result
+ fn try_write32(&self, value: u32, offset: usize) -> Result
where
Self: IoCapable<u32>,
{
- build_error!("Backend does not support fallible 32-bit write")
+ self.try_write(offset, value)
}
/// Fallible 64-bit write with runtime bounds check.
#[inline(always)]
- fn try_write64(&self, _value: u64, _offset: usize) -> Result
+ fn try_write64(&self, value: u64, offset: usize) -> Result
where
Self: IoCapable<u64>,
{
- build_error!("Backend does not support fallible 64-bit write")
+ self.try_write(offset, value)
}
/// Infallible 8-bit read with compile-time bounds check.
#[inline(always)]
- fn read8(&self, _offset: usize) -> u8
+ fn read8(&self, offset: usize) -> u8
where
Self: IoKnownSize + IoCapable<u8>,
{
- build_error!("Backend does not support infallible 8-bit read")
+ self.read(offset)
}
/// Infallible 16-bit read with compile-time bounds check.
#[inline(always)]
- fn read16(&self, _offset: usize) -> u16
+ fn read16(&self, offset: usize) -> u16
where
Self: IoKnownSize + IoCapable<u16>,
{
- build_error!("Backend does not support infallible 16-bit read")
+ self.read(offset)
}
/// Infallible 32-bit read with compile-time bounds check.
#[inline(always)]
- fn read32(&self, _offset: usize) -> u32
+ fn read32(&self, offset: usize) -> u32
where
Self: IoKnownSize + IoCapable<u32>,
{
- build_error!("Backend does not support infallible 32-bit read")
+ self.read(offset)
}
/// Infallible 64-bit read with compile-time bounds check.
#[inline(always)]
- fn read64(&self, _offset: usize) -> u64
+ fn read64(&self, offset: usize) -> u64
where
Self: IoKnownSize + IoCapable<u64>,
{
- build_error!("Backend does not support infallible 64-bit read")
+ self.read(offset)
}
/// Infallible 8-bit write with compile-time bounds check.
#[inline(always)]
- fn write8(&self, _value: u8, _offset: usize)
+ fn write8(&self, value: u8, offset: usize)
where
Self: IoKnownSize + IoCapable<u8>,
{
- build_error!("Backend does not support infallible 8-bit write")
+ self.write(offset, value)
}
/// Infallible 16-bit write with compile-time bounds check.
#[inline(always)]
- fn write16(&self, _value: u16, _offset: usize)
+ fn write16(&self, value: u16, offset: usize)
where
Self: IoKnownSize + IoCapable<u16>,
{
- build_error!("Backend does not support infallible 16-bit write")
+ self.write(offset, value)
}
/// Infallible 32-bit write with compile-time bounds check.
#[inline(always)]
- fn write32(&self, _value: u32, _offset: usize)
+ fn write32(&self, value: u32, offset: usize)
where
Self: IoKnownSize + IoCapable<u32>,
{
- build_error!("Backend does not support infallible 32-bit write")
+ self.write(offset, value)
}
/// Infallible 64-bit write with compile-time bounds check.
#[inline(always)]
- fn write64(&self, _value: u64, _offset: usize)
+ fn write64(&self, value: u64, offset: usize)
where
Self: IoKnownSize + IoCapable<u64>,
{
- build_error!("Backend does not support infallible 64-bit write")
+ self.write(offset, value)
+ }
+
+ /// Generic fallible read with runtime bounds check.
+ ///
+ /// # Examples
+ ///
+ /// Read a primitive type from an I/O address:
+ ///
+ /// ```no_run
+ /// use kernel::io::{
+ /// Io,
+ /// Mmio,
+ /// };
+ ///
+ /// fn do_reads(io: &Mmio) -> Result {
+ /// // 32-bit read from address `0x10`.
+ /// let v: u32 = io.try_read(0x10)?;
+ ///
+ /// // 8-bit read from address `0xfff`.
+ /// let v: u8 = io.try_read(0xfff)?;
+ ///
+ /// Ok(())
+ /// }
+ /// ```
+ #[inline(always)]
+ fn try_read<T, L>(&self, location: L) -> Result<T>
+ where
+ L: IoLoc<T>,
+ Self: IoCapable<L::IoType>,
+ {
+ let address = self.io_addr::<L::IoType>(location.offset())?;
+
+ // SAFETY: `address` has been validated by `io_addr`.
+ Ok(unsafe { self.io_read(address) }.into())
+ }
+
+ /// Generic fallible write with runtime bounds check.
+ ///
+ /// # Examples
+ ///
+ /// Write a primitive type to an I/O address:
+ ///
+ /// ```no_run
+ /// use kernel::io::{
+ /// Io,
+ /// Mmio,
+ /// };
+ ///
+ /// fn do_writes(io: &Mmio) -> Result {
+ /// // 32-bit write of value `1` at address `0x10`.
+ /// io.try_write(0x10, 1u32)?;
+ ///
+ /// // 8-bit write of value `0xff` at address `0xfff`.
+ /// io.try_write(0xfff, 0xffu8)?;
+ ///
+ /// Ok(())
+ /// }
+ /// ```
+ #[inline(always)]
+ fn try_write<T, L>(&self, location: L, value: T) -> Result
+ where
+ L: IoLoc<T>,
+ Self: IoCapable<L::IoType>,
+ {
+ let address = self.io_addr::<L::IoType>(location.offset())?;
+ let io_value = value.into();
+
+ // SAFETY: `address` has been validated by `io_addr`.
+ unsafe { self.io_write(io_value, address) }
+
+ Ok(())
+ }
+
+ /// Generic fallible write of a fully-located register value.
+ ///
+ /// # Examples
+ ///
+ /// Tuples carrying a location and a value can be used with this method:
+ ///
+ /// ```no_run
+ /// use kernel::io::{
+ /// register,
+ /// Io,
+ /// Mmio,
+ /// };
+ ///
+ /// register! {
+ /// VERSION(u32) @ 0x100 {
+ /// 15:8 major;
+ /// 7:0 minor;
+ /// }
+ /// }
+ ///
+ /// impl VERSION {
+ /// fn new(major: u8, minor: u8) -> Self {
+ /// VERSION::zeroed().with_major(major).with_minor(minor)
+ /// }
+ /// }
+ ///
+ /// fn do_write_reg(io: &Mmio) -> Result {
+ ///
+ /// io.try_write_reg(VERSION::new(1, 0))
+ /// }
+ /// ```
+ #[inline(always)]
+ fn try_write_reg<T, L, V>(&self, value: V) -> Result
+ where
+ L: IoLoc<T>,
+ V: LocatedRegister<Location = L, Value = T>,
+ Self: IoCapable<L::IoType>,
+ {
+ let (location, value) = value.into_io_op();
+
+ self.try_write(location, value)
+ }
+
+ /// Generic fallible update with runtime bounds check.
+ ///
+ /// Note: this does not perform any synchronization. The caller is responsible for ensuring
+ /// exclusive access if required.
+ ///
+ /// # Examples
+ ///
+ /// Read the u32 value at address `0x10`, increment it, and store the updated value back:
+ ///
+ /// ```no_run
+ /// use kernel::io::{
+ /// Io,
+ /// Mmio,
+ /// };
+ ///
+ /// fn do_update(io: &Mmio<0x1000>) -> Result {
+ /// io.try_update(0x10, |v: u32| {
+ /// v + 1
+ /// })
+ /// }
+ /// ```
+ #[inline(always)]
+ fn try_update<T, L, F>(&self, location: L, f: F) -> Result
+ where
+ L: IoLoc<T>,
+ Self: IoCapable<L::IoType>,
+ F: FnOnce(T) -> T,
+ {
+ let address = self.io_addr::<L::IoType>(location.offset())?;
+
+ // SAFETY: `address` has been validated by `io_addr`.
+ let value: T = unsafe { self.io_read(address) }.into();
+ let io_value = f(value).into();
+
+ // SAFETY: `address` has been validated by `io_addr`.
+ unsafe { self.io_write(io_value, address) }
+
+ Ok(())
+ }
+
+ /// Generic infallible read with compile-time bounds check.
+ ///
+ /// # Examples
+ ///
+ /// Read a primitive type from an I/O address:
+ ///
+ /// ```no_run
+ /// use kernel::io::{
+ /// Io,
+ /// Mmio,
+ /// };
+ ///
+ /// fn do_reads(io: &Mmio<0x1000>) {
+ /// // 32-bit read from address `0x10`.
+ /// let v: u32 = io.read(0x10);
+ ///
+ /// // 8-bit read from the top of the I/O space.
+ /// let v: u8 = io.read(0xfff);
+ /// }
+ /// ```
+ #[inline(always)]
+ fn read<T, L>(&self, location: L) -> T
+ where
+ L: IoLoc<T>,
+ Self: IoKnownSize + IoCapable<L::IoType>,
+ {
+ let address = self.io_addr_assert::<L::IoType>(location.offset());
+
+ // SAFETY: `address` has been validated by `io_addr_assert`.
+ unsafe { self.io_read(address) }.into()
+ }
+
+ /// Generic infallible write with compile-time bounds check.
+ ///
+ /// # Examples
+ ///
+ /// Write a primitive type to an I/O address:
+ ///
+ /// ```no_run
+ /// use kernel::io::{
+ /// Io,
+ /// Mmio,
+ /// };
+ ///
+ /// fn do_writes(io: &Mmio<0x1000>) {
+ /// // 32-bit write of value `1` at address `0x10`.
+ /// io.write(0x10, 1u32);
+ ///
+ /// // 8-bit write of value `0xff` at the top of the I/O space.
+ /// io.write(0xfff, 0xffu8);
+ /// }
+ /// ```
+ #[inline(always)]
+ fn write<T, L>(&self, location: L, value: T)
+ where
+ L: IoLoc<T>,
+ Self: IoKnownSize + IoCapable<L::IoType>,
+ {
+ let address = self.io_addr_assert::<L::IoType>(location.offset());
+ let io_value = value.into();
+
+ // SAFETY: `address` has been validated by `io_addr_assert`.
+ unsafe { self.io_write(io_value, address) }
+ }
+
+ /// Generic infallible write of a fully-located register value.
+ ///
+ /// # Examples
+ ///
+ /// Tuples carrying a location and a value can be used with this method:
+ ///
+ /// ```no_run
+ /// use kernel::io::{
+ /// register,
+ /// Io,
+ /// Mmio,
+ /// };
+ ///
+ /// register! {
+ /// VERSION(u32) @ 0x100 {
+ /// 15:8 major;
+ /// 7:0 minor;
+ /// }
+ /// }
+ ///
+ /// impl VERSION {
+ /// fn new(major: u8, minor: u8) -> Self {
+ /// VERSION::zeroed().with_major(major).with_minor(minor)
+ /// }
+ /// }
+ ///
+ /// fn do_write_reg(io: &Mmio<0x1000>) {
+ /// io.write_reg(VERSION::new(1, 0));
+ /// }
+ /// ```
+ #[inline(always)]
+ fn write_reg<T, L, V>(&self, value: V)
+ where
+ L: IoLoc<T>,
+ V: LocatedRegister<Location = L, Value = T>,
+ Self: IoKnownSize + IoCapable<L::IoType>,
+ {
+ let (location, value) = value.into_io_op();
+
+ self.write(location, value)
+ }
+
+ /// Generic infallible update with compile-time bounds check.
+ ///
+ /// Note: this does not perform any synchronization. The caller is responsible for ensuring
+ /// exclusive access if required.
+ ///
+ /// # Examples
+ ///
+ /// Read the u32 value at address `0x10`, increment it, and store the updated value back:
+ ///
+ /// ```no_run
+ /// use kernel::io::{
+ /// Io,
+ /// Mmio,
+ /// };
+ ///
+ /// fn do_update(io: &Mmio<0x1000>) {
+ /// io.update(0x10, |v: u32| {
+ /// v + 1
+ /// })
+ /// }
+ /// ```
+ #[inline(always)]
+ fn update<T, L, F>(&self, location: L, f: F)
+ where
+ L: IoLoc<T>,
+ Self: IoKnownSize + IoCapable<L::IoType> + Sized,
+ F: FnOnce(T) -> T,
+ {
+ let address = self.io_addr_assert::<L::IoType>(location.offset());
+
+ // SAFETY: `address` has been validated by `io_addr_assert`.
+ let value: T = unsafe { self.io_read(address) }.into();
+ let io_value = f(value).into();
+
+ // SAFETY: `address` has been validated by `io_addr_assert`.
+ unsafe { self.io_write(io_value, address) }
}
}
@@ -534,14 +726,36 @@ pub trait IoKnownSize: Io {
}
}
-// MMIO regions support 8, 16, and 32-bit accesses.
-impl<const SIZE: usize> IoCapable<u8> for Mmio<SIZE> {}
-impl<const SIZE: usize> IoCapable<u16> for Mmio<SIZE> {}
-impl<const SIZE: usize> IoCapable<u32> for Mmio<SIZE> {}
+/// Implements [`IoCapable`] on `$mmio` for `$ty` using `$read_fn` and `$write_fn`.
+macro_rules! impl_mmio_io_capable {
+ ($mmio:ident, $(#[$attr:meta])* $ty:ty, $read_fn:ident, $write_fn:ident) => {
+ $(#[$attr])*
+ impl<const SIZE: usize> IoCapable<$ty> for $mmio<SIZE> {
+ unsafe fn io_read(&self, address: usize) -> $ty {
+ // SAFETY: By the trait invariant `address` is a valid address for MMIO operations.
+ unsafe { bindings::$read_fn(address as *const c_void) }
+ }
+
+ unsafe fn io_write(&self, value: $ty, address: usize) {
+ // SAFETY: By the trait invariant `address` is a valid address for MMIO operations.
+ unsafe { bindings::$write_fn(value, address as *mut c_void) }
+ }
+ }
+ };
+}
+// MMIO regions support 8, 16, and 32-bit accesses.
+impl_mmio_io_capable!(Mmio, u8, readb, writeb);
+impl_mmio_io_capable!(Mmio, u16, readw, writew);
+impl_mmio_io_capable!(Mmio, u32, readl, writel);
// MMIO regions on 64-bit systems also support 64-bit accesses.
-#[cfg(CONFIG_64BIT)]
-impl<const SIZE: usize> IoCapable<u64> for Mmio<SIZE> {}
+impl_mmio_io_capable!(
+ Mmio,
+ #[cfg(CONFIG_64BIT)]
+ u64,
+ readq,
+ writeq
+);
impl<const SIZE: usize> Io for Mmio<SIZE> {
/// Returns the base address of this mapping.
@@ -555,46 +769,6 @@ impl<const SIZE: usize> Io for Mmio<SIZE> {
fn maxsize(&self) -> usize {
self.0.maxsize()
}
-
- io_define_read!(fallible, try_read8, call_mmio_read(readb) -> u8);
- io_define_read!(fallible, try_read16, call_mmio_read(readw) -> u16);
- io_define_read!(fallible, try_read32, call_mmio_read(readl) -> u32);
- io_define_read!(
- fallible,
- #[cfg(CONFIG_64BIT)]
- try_read64,
- call_mmio_read(readq) -> u64
- );
-
- io_define_write!(fallible, try_write8, call_mmio_write(writeb) <- u8);
- io_define_write!(fallible, try_write16, call_mmio_write(writew) <- u16);
- io_define_write!(fallible, try_write32, call_mmio_write(writel) <- u32);
- io_define_write!(
- fallible,
- #[cfg(CONFIG_64BIT)]
- try_write64,
- call_mmio_write(writeq) <- u64
- );
-
- io_define_read!(infallible, read8, call_mmio_read(readb) -> u8);
- io_define_read!(infallible, read16, call_mmio_read(readw) -> u16);
- io_define_read!(infallible, read32, call_mmio_read(readl) -> u32);
- io_define_read!(
- infallible,
- #[cfg(CONFIG_64BIT)]
- read64,
- call_mmio_read(readq) -> u64
- );
-
- io_define_write!(infallible, write8, call_mmio_write(writeb) <- u8);
- io_define_write!(infallible, write16, call_mmio_write(writew) <- u16);
- io_define_write!(infallible, write32, call_mmio_write(writel) <- u32);
- io_define_write!(
- infallible,
- #[cfg(CONFIG_64BIT)]
- write64,
- call_mmio_write(writeq) <- u64
- );
}
impl<const SIZE: usize> IoKnownSize for Mmio<SIZE> {
@@ -612,44 +786,70 @@ impl<const SIZE: usize> Mmio<SIZE> {
// SAFETY: `Mmio` is a transparent wrapper around `MmioRaw`.
unsafe { &*core::ptr::from_ref(raw).cast() }
}
+}
+
+/// [`Mmio`] wrapper using relaxed accessors.
+///
+/// This type provides an implementation of [`Io`] that uses relaxed I/O MMIO operands instead of
+/// the regular ones.
+///
+/// See [`Mmio::relaxed`] for a usage example.
+#[repr(transparent)]
+pub struct RelaxedMmio<const SIZE: usize = 0>(Mmio<SIZE>);
+
+impl<const SIZE: usize> Io for RelaxedMmio<SIZE> {
+ #[inline]
+ fn addr(&self) -> usize {
+ self.0.addr()
+ }
- io_define_read!(infallible, pub read8_relaxed, call_mmio_read(readb_relaxed) -> u8);
- io_define_read!(infallible, pub read16_relaxed, call_mmio_read(readw_relaxed) -> u16);
- io_define_read!(infallible, pub read32_relaxed, call_mmio_read(readl_relaxed) -> u32);
- io_define_read!(
- infallible,
- #[cfg(CONFIG_64BIT)]
- pub read64_relaxed,
- call_mmio_read(readq_relaxed) -> u64
- );
-
- io_define_read!(fallible, pub try_read8_relaxed, call_mmio_read(readb_relaxed) -> u8);
- io_define_read!(fallible, pub try_read16_relaxed, call_mmio_read(readw_relaxed) -> u16);
- io_define_read!(fallible, pub try_read32_relaxed, call_mmio_read(readl_relaxed) -> u32);
- io_define_read!(
- fallible,
- #[cfg(CONFIG_64BIT)]
- pub try_read64_relaxed,
- call_mmio_read(readq_relaxed) -> u64
- );
-
- io_define_write!(infallible, pub write8_relaxed, call_mmio_write(writeb_relaxed) <- u8);
- io_define_write!(infallible, pub write16_relaxed, call_mmio_write(writew_relaxed) <- u16);
- io_define_write!(infallible, pub write32_relaxed, call_mmio_write(writel_relaxed) <- u32);
- io_define_write!(
- infallible,
- #[cfg(CONFIG_64BIT)]
- pub write64_relaxed,
- call_mmio_write(writeq_relaxed) <- u64
- );
-
- io_define_write!(fallible, pub try_write8_relaxed, call_mmio_write(writeb_relaxed) <- u8);
- io_define_write!(fallible, pub try_write16_relaxed, call_mmio_write(writew_relaxed) <- u16);
- io_define_write!(fallible, pub try_write32_relaxed, call_mmio_write(writel_relaxed) <- u32);
- io_define_write!(
- fallible,
- #[cfg(CONFIG_64BIT)]
- pub try_write64_relaxed,
- call_mmio_write(writeq_relaxed) <- u64
- );
+ #[inline]
+ fn maxsize(&self) -> usize {
+ self.0.maxsize()
+ }
+}
+
+impl<const SIZE: usize> IoKnownSize for RelaxedMmio<SIZE> {
+ const MIN_SIZE: usize = SIZE;
}
+
+impl<const SIZE: usize> Mmio<SIZE> {
+ /// Returns a [`RelaxedMmio`] reference that performs relaxed I/O operations.
+ ///
+ /// Relaxed accessors do not provide ordering guarantees with respect to DMA or memory accesses
+ /// and can be used when such ordering is not required.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use kernel::io::{
+ /// Io,
+ /// Mmio,
+ /// RelaxedMmio,
+ /// };
+ ///
+ /// fn do_io(io: &Mmio<0x100>) {
+ /// // The access is performed using `readl_relaxed` instead of `readl`.
+ /// let v = io.relaxed().read32(0x10);
+ /// }
+ ///
+ /// ```
+ pub fn relaxed(&self) -> &RelaxedMmio<SIZE> {
+ // SAFETY: `RelaxedMmio` is `#[repr(transparent)]` over `Mmio`, so `Mmio<SIZE>` and
+ // `RelaxedMmio<SIZE>` have identical layout.
+ unsafe { core::mem::transmute(self) }
+ }
+}
+
+// MMIO regions support 8, 16, and 32-bit accesses.
+impl_mmio_io_capable!(RelaxedMmio, u8, readb_relaxed, writeb_relaxed);
+impl_mmio_io_capable!(RelaxedMmio, u16, readw_relaxed, writew_relaxed);
+impl_mmio_io_capable!(RelaxedMmio, u32, readl_relaxed, writel_relaxed);
+// MMIO regions on 64-bit systems also support 64-bit accesses.
+impl_mmio_io_capable!(
+ RelaxedMmio,
+ #[cfg(CONFIG_64BIT)]
+ u64,
+ readq_relaxed,
+ writeq_relaxed
+);
diff --git a/rust/kernel/io/mem.rs b/rust/kernel/io/mem.rs
index 620022cff401..7dc78d547f7a 100644
--- a/rust/kernel/io/mem.rs
+++ b/rust/kernel/io/mem.rs
@@ -54,6 +54,7 @@ impl<'a> IoRequest<'a> {
/// use kernel::{
/// bindings,
/// device::Core,
+ /// io::Io,
/// of,
/// platform,
/// };
@@ -78,9 +79,9 @@ impl<'a> IoRequest<'a> {
/// let io = iomem.access(pdev.as_ref())?;
///
/// // Read and write a 32-bit value at `offset`.
- /// let data = io.read32_relaxed(offset);
+ /// let data = io.read32(offset);
///
- /// io.write32_relaxed(data, offset);
+ /// io.write32(data, offset);
///
/// # Ok(SampleDriver)
/// }
@@ -117,6 +118,7 @@ impl<'a> IoRequest<'a> {
/// use kernel::{
/// bindings,
/// device::Core,
+ /// io::Io,
/// of,
/// platform,
/// };
@@ -141,9 +143,9 @@ impl<'a> IoRequest<'a> {
///
/// let io = iomem.access(pdev.as_ref())?;
///
- /// let data = io.try_read32_relaxed(offset)?;
+ /// let data = io.try_read32(offset)?;
///
- /// io.try_write32_relaxed(data, offset)?;
+ /// io.try_write32(data, offset)?;
///
/// # Ok(SampleDriver)
/// }
diff --git a/rust/kernel/io/register.rs b/rust/kernel/io/register.rs
new file mode 100644
index 000000000000..abc49926abfe
--- /dev/null
+++ b/rust/kernel/io/register.rs
@@ -0,0 +1,1260 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Macro to define register layout and accessors.
+//!
+//! The [`register!`](kernel::io::register!) macro provides an intuitive and readable syntax for
+//! defining a dedicated type for each register and accessing it using [`Io`](super::Io). Each such
+//! type comes with its own field accessors that can return an error if a field's value is invalid.
+//!
+//! Note: most of the items in this module are public so they can be referenced by the macro, but
+//! most are not to be used directly by users. Outside of the `register!` macro itself, the only
+//! items you might want to import from this module are [`WithBase`] and [`Array`].
+//!
+//! # Simple example
+//!
+//! ```no_run
+//! use kernel::io::register;
+//!
+//! register! {
+//! /// Basic information about the chip.
+//! pub BOOT_0(u32) @ 0x00000100 {
+//! /// Vendor ID.
+//! 15:8 vendor_id;
+//! /// Major revision of the chip.
+//! 7:4 major_revision;
+//! /// Minor revision of the chip.
+//! 3:0 minor_revision;
+//! }
+//! }
+//! ```
+//!
+//! This defines a 32-bit `BOOT_0` type which can be read from or written to offset `0x100` of an
+//! `Io` region, with the described bitfields. For instance, `minor_revision` consists of the 4
+//! least significant bits of the type.
+//!
+//! Fields are instances of [`Bounded`](kernel::num::Bounded) and can be read by calling their
+//! getter method, which is named after them. They also have setter methods prefixed with `with_`
+//! for runtime values and `with_const_` for constant values. All setters return the updated
+//! register value.
+//!
+//! Fields can also be transparently converted from/to an arbitrary type by using the `=>` and
+//! `?=>` syntaxes.
+//!
+//! If present, doc comments above register or fields definitions are added to the relevant item
+//! they document (the register type itself, or the field's setter and getter methods).
+//!
+//! Note that multiple registers can be defined in a single `register!` invocation. This can be
+//! useful to group related registers together.
+//!
+//! Here is how the register defined above can be used in code:
+//!
+//!
+//! ```no_run
+//! use kernel::{
+//! io::{
+//! register,
+//! Io,
+//! IoLoc,
+//! },
+//! num::Bounded,
+//! };
+//! # use kernel::io::Mmio;
+//! # register! {
+//! # pub BOOT_0(u32) @ 0x00000100 {
+//! # 15:8 vendor_id;
+//! # 7:4 major_revision;
+//! # 3:0 minor_revision;
+//! # }
+//! # }
+//! # fn test(io: &Mmio<0x1000>) {
+//! # fn obtain_vendor_id() -> u8 { 0xff }
+//!
+//! // Read from the register's defined offset (0x100).
+//! let boot0 = io.read(BOOT_0);
+//! pr_info!("chip revision: {}.{}", boot0.major_revision().get(), boot0.minor_revision().get());
+//!
+//! // Update some fields and write the new value back.
+//! let new_boot0 = boot0
+//! // Constant values.
+//! .with_const_major_revision::<3>()
+//! .with_const_minor_revision::<10>()
+//! // Runtime value.
+//! .with_vendor_id(obtain_vendor_id());
+//! io.write_reg(new_boot0);
+//!
+//! // Or, build a new value from zero and write it:
+//! io.write_reg(BOOT_0::zeroed()
+//! .with_const_major_revision::<3>()
+//! .with_const_minor_revision::<10>()
+//! .with_vendor_id(obtain_vendor_id())
+//! );
+//!
+//! // Or, read and update the register in a single step.
+//! io.update(BOOT_0, |r| r
+//! .with_const_major_revision::<3>()
+//! .with_const_minor_revision::<10>()
+//! .with_vendor_id(obtain_vendor_id())
+//! );
+//!
+//! // Constant values can also be built using the const setters.
+//! const V: BOOT_0 = pin_init::zeroed::<BOOT_0>()
+//! .with_const_major_revision::<3>()
+//! .with_const_minor_revision::<10>();
+//! # }
+//! ```
+//!
+//! For more extensive documentation about how to define registers, see the
+//! [`register!`](kernel::io::register!) macro.
+
+use core::marker::PhantomData;
+
+use crate::io::IoLoc;
+
+use kernel::build_assert;
+
+/// Trait implemented by all registers.
+pub trait Register: Sized {
+ /// Backing primitive type of the register.
+ type Storage: Into<Self> + From<Self>;
+
+ /// Start offset of the register.
+ ///
+ /// The interpretation of this offset depends on the type of the register.
+ const OFFSET: usize;
+}
+
+/// Trait implemented by registers with a fixed offset.
+pub trait FixedRegister: Register {}
+
+/// Allows `()` to be used as the `location` parameter of [`Io::write`](super::Io::write) when
+/// passing a [`FixedRegister`] value.
+impl<T> IoLoc<T> for ()
+where
+ T: FixedRegister,
+{
+ type IoType = T::Storage;
+
+ #[inline(always)]
+ fn offset(self) -> usize {
+ T::OFFSET
+ }
+}
+
+/// A [`FixedRegister`] carries its location in its type. Thus `FixedRegister` values can be used
+/// as an [`IoLoc`].
+impl<T> IoLoc<T> for T
+where
+ T: FixedRegister,
+{
+ type IoType = T::Storage;
+
+ #[inline(always)]
+ fn offset(self) -> usize {
+ T::OFFSET
+ }
+}
+
+/// Location of a fixed register.
+pub struct FixedRegisterLoc<T: FixedRegister>(PhantomData<T>);
+
+impl<T: FixedRegister> FixedRegisterLoc<T> {
+ /// Returns the location of `T`.
+ #[inline(always)]
+ // We do not implement `Default` so we can be const.
+ #[expect(clippy::new_without_default)]
+ pub const fn new() -> Self {
+ Self(PhantomData)
+ }
+}
+
+impl<T> IoLoc<T> for FixedRegisterLoc<T>
+where
+ T: FixedRegister,
+{
+ type IoType = T::Storage;
+
+ #[inline(always)]
+ fn offset(self) -> usize {
+ T::OFFSET
+ }
+}
+
+/// Trait providing a base address to be added to the offset of a relative register to obtain
+/// its actual offset.
+///
+/// The `T` generic argument is used to distinguish which base to use, in case a type provides
+/// several bases. It is given to the `register!` macro to restrict the use of the register to
+/// implementors of this particular variant.
+pub trait RegisterBase<T> {
+ /// Base address to which register offsets are added.
+ const BASE: usize;
+}
+
+/// Trait implemented by all registers that are relative to a base.
+pub trait WithBase {
+ /// Family of bases applicable to this register.
+ type BaseFamily;
+
+ /// Returns the absolute location of this type when using `B` as its base.
+ #[inline(always)]
+ fn of<B: RegisterBase<Self::BaseFamily>>() -> RelativeRegisterLoc<Self, B>
+ where
+ Self: Register,
+ {
+ RelativeRegisterLoc::new()
+ }
+}
+
+/// Trait implemented by relative registers.
+pub trait RelativeRegister: Register + WithBase {}
+
+/// Location of a relative register.
+///
+/// This can either be an immediately accessible regular [`RelativeRegister`], or a
+/// [`RelativeRegisterArray`] that needs one additional resolution through
+/// [`RelativeRegisterLoc::at`].
+pub struct RelativeRegisterLoc<T: WithBase, B: ?Sized>(PhantomData<T>, PhantomData<B>);
+
+impl<T, B> RelativeRegisterLoc<T, B>
+where
+ T: Register + WithBase,
+ B: RegisterBase<T::BaseFamily> + ?Sized,
+{
+ /// Returns the location of a relative register or register array.
+ #[inline(always)]
+ // We do not implement `Default` so we can be const.
+ #[expect(clippy::new_without_default)]
+ pub const fn new() -> Self {
+ Self(PhantomData, PhantomData)
+ }
+
+ // Returns the absolute offset of the relative register using base `B`.
+ //
+ // This is implemented as a private const method so it can be reused by the [`IoLoc`]
+ // implementations of both [`RelativeRegisterLoc`] and [`RelativeRegisterArrayLoc`].
+ #[inline]
+ const fn offset(self) -> usize {
+ B::BASE + T::OFFSET
+ }
+}
+
+impl<T, B> IoLoc<T> for RelativeRegisterLoc<T, B>
+where
+ T: RelativeRegister,
+ B: RegisterBase<T::BaseFamily> + ?Sized,
+{
+ type IoType = T::Storage;
+
+ #[inline(always)]
+ fn offset(self) -> usize {
+ RelativeRegisterLoc::offset(self)
+ }
+}
+
+/// Trait implemented by arrays of registers.
+pub trait RegisterArray: Register {
+ /// Number of elements in the registers array.
+ const SIZE: usize;
+ /// Number of bytes between the start of elements in the registers array.
+ const STRIDE: usize;
+}
+
+/// Location of an array register.
+pub struct RegisterArrayLoc<T: RegisterArray>(usize, PhantomData<T>);
+
+impl<T: RegisterArray> RegisterArrayLoc<T> {
+ /// Returns the location of register `T` at position `idx`, with build-time validation.
+ #[inline(always)]
+ pub fn new(idx: usize) -> Self {
+ build_assert!(idx < T::SIZE);
+
+ Self(idx, PhantomData)
+ }
+
+ /// Attempts to return the location of register `T` at position `idx`, with runtime validation.
+ #[inline(always)]
+ pub fn try_new(idx: usize) -> Option<Self> {
+ if idx < T::SIZE {
+ Some(Self(idx, PhantomData))
+ } else {
+ None
+ }
+ }
+}
+
+impl<T> IoLoc<T> for RegisterArrayLoc<T>
+where
+ T: RegisterArray,
+{
+ type IoType = T::Storage;
+
+ #[inline(always)]
+ fn offset(self) -> usize {
+ T::OFFSET + self.0 * T::STRIDE
+ }
+}
+
+/// Trait providing location builders for [`RegisterArray`]s.
+pub trait Array {
+ /// Returns the location of the register at position `idx`, with build-time validation.
+ #[inline(always)]
+ fn at(idx: usize) -> RegisterArrayLoc<Self>
+ where
+ Self: RegisterArray,
+ {
+ RegisterArrayLoc::new(idx)
+ }
+
+ /// Returns the location of the register at position `idx`, with runtime validation.
+ #[inline(always)]
+ fn try_at(idx: usize) -> Option<RegisterArrayLoc<Self>>
+ where
+ Self: RegisterArray,
+ {
+ RegisterArrayLoc::try_new(idx)
+ }
+}
+
+/// Trait implemented by arrays of relative registers.
+pub trait RelativeRegisterArray: RegisterArray + WithBase {}
+
+/// Location of a relative array register.
+pub struct RelativeRegisterArrayLoc<
+ T: RelativeRegisterArray,
+ B: RegisterBase<T::BaseFamily> + ?Sized,
+>(RelativeRegisterLoc<T, B>, usize);
+
+impl<T, B> RelativeRegisterArrayLoc<T, B>
+where
+ T: RelativeRegisterArray,
+ B: RegisterBase<T::BaseFamily> + ?Sized,
+{
+ /// Returns the location of register `T` from the base `B` at index `idx`, with build-time
+ /// validation.
+ #[inline(always)]
+ pub fn new(idx: usize) -> Self {
+ build_assert!(idx < T::SIZE);
+
+ Self(RelativeRegisterLoc::new(), idx)
+ }
+
+ /// Attempts to return the location of register `T` from the base `B` at index `idx`, with
+ /// runtime validation.
+ #[inline(always)]
+ pub fn try_new(idx: usize) -> Option<Self> {
+ if idx < T::SIZE {
+ Some(Self(RelativeRegisterLoc::new(), idx))
+ } else {
+ None
+ }
+ }
+}
+
+/// Methods exclusive to [`RelativeRegisterLoc`]s created with a [`RelativeRegisterArray`].
+impl<T, B> RelativeRegisterLoc<T, B>
+where
+ T: RelativeRegisterArray,
+ B: RegisterBase<T::BaseFamily> + ?Sized,
+{
+ /// Returns the location of the register at position `idx`, with build-time validation.
+ #[inline(always)]
+ pub fn at(self, idx: usize) -> RelativeRegisterArrayLoc<T, B> {
+ RelativeRegisterArrayLoc::new(idx)
+ }
+
+ /// Returns the location of the register at position `idx`, with runtime validation.
+ #[inline(always)]
+ pub fn try_at(self, idx: usize) -> Option<RelativeRegisterArrayLoc<T, B>> {
+ RelativeRegisterArrayLoc::try_new(idx)
+ }
+}
+
+impl<T, B> IoLoc<T> for RelativeRegisterArrayLoc<T, B>
+where
+ T: RelativeRegisterArray,
+ B: RegisterBase<T::BaseFamily> + ?Sized,
+{
+ type IoType = T::Storage;
+
+ #[inline(always)]
+ fn offset(self) -> usize {
+ self.0.offset() + self.1 * T::STRIDE
+ }
+}
+
+/// Trait implemented by items that contain both a register value and the absolute I/O location at
+/// which to write it.
+///
+/// Implementors can be used with [`Io::write_reg`](super::Io::write_reg).
+pub trait LocatedRegister {
+ /// Register value to write.
+ type Value: Register;
+ /// Full location information at which to write the value.
+ type Location: IoLoc<Self::Value>;
+
+ /// Consumes `self` and returns a `(location, value)` tuple describing a valid I/O write
+ /// operation.
+ fn into_io_op(self) -> (Self::Location, Self::Value);
+}
+
+impl<T> LocatedRegister for T
+where
+ T: FixedRegister,
+{
+ type Location = FixedRegisterLoc<Self::Value>;
+ type Value = T;
+
+ #[inline(always)]
+ fn into_io_op(self) -> (FixedRegisterLoc<T>, T) {
+ (FixedRegisterLoc::new(), self)
+ }
+}
+
+/// Defines a dedicated type for a register, including getter and setter methods for its fields and
+/// methods to read and write it from an [`Io`](kernel::io::Io) region.
+///
+/// This documentation focuses on how to declare registers. See the [module-level
+/// documentation](mod@kernel::io::register) for examples of how to access them.
+///
+/// There are 4 possible kinds of registers: fixed offset registers, relative registers, arrays of
+/// registers, and relative arrays of registers.
+///
+/// ## Fixed offset registers
+///
+/// These are the simplest kind of registers. Their location is simply an offset inside the I/O
+/// region. For instance:
+///
+/// ```ignore
+/// register! {
+/// pub FIXED_REG(u16) @ 0x80 {
+/// ...
+/// }
+/// }
+/// ```
+///
+/// This creates a 16-bit register named `FIXED_REG` located at offset `0x80` of an I/O region.
+///
+/// These registers' location can be built simply by referencing their name:
+///
+/// ```no_run
+/// use kernel::{
+/// io::{
+/// register,
+/// Io,
+/// },
+/// };
+/// # use kernel::io::Mmio;
+///
+/// register! {
+/// FIXED_REG(u32) @ 0x100 {
+/// 16:8 high_byte;
+/// 7:0 low_byte;
+/// }
+/// }
+///
+/// # fn test(io: &Mmio<0x1000>) {
+/// let val = io.read(FIXED_REG);
+///
+/// // Write from an already-existing value.
+/// io.write(FIXED_REG, val.with_low_byte(0xff));
+///
+/// // Create a register value from scratch.
+/// let val2 = FIXED_REG::zeroed().with_high_byte(0x80);
+///
+/// // The location of fixed offset registers is already contained in their type. Thus, the
+/// // `location` argument of `Io::write` is technically redundant and can be replaced by `()`.
+/// io.write((), val2);
+///
+/// // Or, the single-argument `Io::write_reg` can be used.
+/// io.write_reg(val2);
+/// # }
+///
+/// ```
+///
+/// It is possible to create an alias of an existing register with new field definitions by using
+/// the `=> ALIAS` syntax. This is useful for cases where a register's interpretation depends on
+/// the context:
+///
+/// ```no_run
+/// use kernel::io::register;
+///
+/// register! {
+/// /// Scratch register.
+/// pub SCRATCH(u32) @ 0x00000200 {
+/// 31:0 value;
+/// }
+///
+/// /// Boot status of the firmware.
+/// pub SCRATCH_BOOT_STATUS(u32) => SCRATCH {
+/// 0:0 completed;
+/// }
+/// }
+/// ```
+///
+/// In this example, `SCRATCH_BOOT_STATUS` uses the same I/O address as `SCRATCH`, while providing
+/// its own `completed` field.
+///
+/// ## Relative registers
+///
+/// Relative registers can be instantiated several times at a relative offset of a group of bases.
+/// For instance, imagine the following I/O space:
+///
+/// ```text
+/// +-----------------------------+
+/// | ... |
+/// | |
+/// 0x100--->+------------CPU0-------------+
+/// | |
+/// 0x110--->+-----------------------------+
+/// | CPU_CTL |
+/// +-----------------------------+
+/// | ... |
+/// | |
+/// | |
+/// 0x200--->+------------CPU1-------------+
+/// | |
+/// 0x210--->+-----------------------------+
+/// | CPU_CTL |
+/// +-----------------------------+
+/// | ... |
+/// +-----------------------------+
+/// ```
+///
+/// `CPU0` and `CPU1` both have a `CPU_CTL` register that starts at offset `0x10` of their I/O
+/// space segment. Since both instances of `CPU_CTL` share the same layout, we don't want to define
+/// them twice and would prefer a way to select which one to use from a single definition.
+///
+/// This can be done using the `Base + Offset` syntax when specifying the register's address:
+///
+/// ```ignore
+/// register! {
+/// pub RELATIVE_REG(u32) @ Base + 0x80 {
+/// ...
+/// }
+/// }
+/// ```
+///
+/// This creates a register with an offset of `0x80` from a given base.
+///
+/// `Base` is an arbitrary type (typically a ZST) to be used as a generic parameter of the
+/// [`RegisterBase`] trait to provide the base as a constant, i.e. each type providing a base for
+/// this register needs to implement `RegisterBase<Base>`.
+///
+/// The location of relative registers can be built using the [`WithBase::of`] method to specify
+/// its base. All relative registers implement [`WithBase`].
+///
+/// Here is the above layout translated into code:
+///
+/// ```no_run
+/// use kernel::{
+/// io::{
+/// register,
+/// register::{
+/// RegisterBase,
+/// WithBase,
+/// },
+/// Io,
+/// },
+/// };
+/// # use kernel::io::Mmio;
+///
+/// // Type used to identify the base.
+/// pub struct CpuCtlBase;
+///
+/// // ZST describing `CPU0`.
+/// struct Cpu0;
+/// impl RegisterBase<CpuCtlBase> for Cpu0 {
+/// const BASE: usize = 0x100;
+/// }
+///
+/// // ZST describing `CPU1`.
+/// struct Cpu1;
+/// impl RegisterBase<CpuCtlBase> for Cpu1 {
+/// const BASE: usize = 0x200;
+/// }
+///
+/// // This makes `CPU_CTL` accessible from all implementors of `RegisterBase<CpuCtlBase>`.
+/// register! {
+/// /// CPU core control.
+/// pub CPU_CTL(u32) @ CpuCtlBase + 0x10 {
+/// 0:0 start;
+/// }
+/// }
+///
+/// # fn test(io: Mmio<0x1000>) {
+/// // Read the status of `Cpu0`.
+/// let cpu0_started = io.read(CPU_CTL::of::<Cpu0>());
+///
+/// // Stop `Cpu0`.
+/// io.write(WithBase::of::<Cpu0>(), CPU_CTL::zeroed());
+/// # }
+///
+/// // Aliases can also be defined for relative register.
+/// register! {
+/// /// Alias to CPU core control.
+/// pub CPU_CTL_ALIAS(u32) => CpuCtlBase + CPU_CTL {
+/// /// Start the aliased CPU core.
+/// 1:1 alias_start;
+/// }
+/// }
+///
+/// # fn test2(io: Mmio<0x1000>) {
+/// // Start the aliased `CPU0`, leaving its other fields untouched.
+/// io.update(CPU_CTL_ALIAS::of::<Cpu0>(), |r| r.with_alias_start(true));
+/// # }
+/// ```
+///
+/// ## Arrays of registers
+///
+/// Some I/O areas contain consecutive registers that share the same field layout. These areas can
+/// be defined as an array of identical registers, allowing them to be accessed by index with
+/// compile-time or runtime bound checking:
+///
+/// ```ignore
+/// register! {
+/// pub REGISTER_ARRAY(u8)[10, stride = 4] @ 0x100 {
+/// ...
+/// }
+/// }
+/// ```
+///
+/// This defines `REGISTER_ARRAY`, an array of 10 byte registers starting at offset `0x100`. Each
+/// register is separated from its neighbor by 4 bytes.
+///
+/// The `stride` parameter is optional; if unspecified, the registers are placed consecutively from
+/// each other.
+///
+/// A location for a register in a register array is built using the [`Array::at`] trait method.
+/// All arrays of registers implement [`Array`].
+///
+/// ```no_run
+/// use kernel::{
+/// io::{
+/// register,
+/// register::Array,
+/// Io,
+/// },
+/// };
+/// # use kernel::io::Mmio;
+/// # fn get_scratch_idx() -> usize {
+/// # 0x15
+/// # }
+///
+/// // Array of 64 consecutive registers with the same layout starting at offset `0x80`.
+/// register! {
+/// /// Scratch registers.
+/// pub SCRATCH(u32)[64] @ 0x00000080 {
+/// 31:0 value;
+/// }
+/// }
+///
+/// # fn test(io: &Mmio<0x1000>)
+/// # -> Result<(), Error>{
+/// // Read scratch register 0, i.e. I/O address `0x80`.
+/// let scratch_0 = io.read(SCRATCH::at(0)).value();
+///
+/// // Write scratch register 15, i.e. I/O address `0x80 + (15 * 4)`.
+/// io.write(Array::at(15), SCRATCH::from(0xffeeaabb));
+///
+/// // This is out of bounds and won't build.
+/// // let scratch_128 = io.read(SCRATCH::at(128)).value();
+///
+/// // Runtime-obtained array index.
+/// let idx = get_scratch_idx();
+/// // Access on a runtime index returns an error if it is out-of-bounds.
+/// let some_scratch = io.read(SCRATCH::try_at(idx).ok_or(EINVAL)?).value();
+///
+/// // Alias to a specific register in an array.
+/// // Here `SCRATCH[8]` is used to convey the firmware exit code.
+/// register! {
+/// /// Firmware exit status code.
+/// pub FIRMWARE_STATUS(u32) => SCRATCH[8] {
+/// 7:0 status;
+/// }
+/// }
+///
+/// let status = io.read(FIRMWARE_STATUS).status();
+///
+/// // Non-contiguous register arrays can be defined by adding a stride parameter.
+/// // Here, each of the 16 registers of the array is separated by 8 bytes, meaning that the
+/// // registers of the two declarations below are interleaved.
+/// register! {
+/// /// Scratch registers bank 0.
+/// pub SCRATCH_INTERLEAVED_0(u32)[16, stride = 8] @ 0x000000c0 {
+/// 31:0 value;
+/// }
+///
+/// /// Scratch registers bank 1.
+/// pub SCRATCH_INTERLEAVED_1(u32)[16, stride = 8] @ 0x000000c4 {
+/// 31:0 value;
+/// }
+/// }
+/// # Ok(())
+/// # }
+/// ```
+///
+/// ## Relative arrays of registers
+///
+/// Combining the two features described in the sections above, arrays of registers accessible from
+/// a base can also be defined:
+///
+/// ```ignore
+/// register! {
+/// pub RELATIVE_REGISTER_ARRAY(u8)[10, stride = 4] @ Base + 0x100 {
+/// ...
+/// }
+/// }
+/// ```
+///
+/// Like relative registers, they implement the [`WithBase`] trait. However the return value of
+/// [`WithBase::of`] cannot be used directly as a location and must be further specified using the
+/// [`at`](RelativeRegisterLoc::at) method.
+///
+/// ```no_run
+/// use kernel::{
+/// io::{
+/// register,
+/// register::{
+/// RegisterBase,
+/// WithBase,
+/// },
+/// Io,
+/// },
+/// };
+/// # use kernel::io::Mmio;
+/// # fn get_scratch_idx() -> usize {
+/// # 0x15
+/// # }
+///
+/// // Type used as parameter of `RegisterBase` to specify the base.
+/// pub struct CpuCtlBase;
+///
+/// // ZST describing `CPU0`.
+/// struct Cpu0;
+/// impl RegisterBase<CpuCtlBase> for Cpu0 {
+/// const BASE: usize = 0x100;
+/// }
+///
+/// // ZST describing `CPU1`.
+/// struct Cpu1;
+/// impl RegisterBase<CpuCtlBase> for Cpu1 {
+/// const BASE: usize = 0x200;
+/// }
+///
+/// // 64 per-cpu scratch registers, arranged as a contiguous array.
+/// register! {
+/// /// Per-CPU scratch registers.
+/// pub CPU_SCRATCH(u32)[64] @ CpuCtlBase + 0x00000080 {
+/// 31:0 value;
+/// }
+/// }
+///
+/// # fn test(io: &Mmio<0x1000>) -> Result<(), Error> {
+/// // Read scratch register 0 of CPU0.
+/// let scratch = io.read(CPU_SCRATCH::of::<Cpu0>().at(0));
+///
+/// // Write the retrieved value into scratch register 15 of CPU1.
+/// io.write(WithBase::of::<Cpu1>().at(15), scratch);
+///
+/// // This won't build.
+/// // let cpu0_scratch_128 = io.read(CPU_SCRATCH::of::<Cpu0>().at(128)).value();
+///
+/// // Runtime-obtained array index.
+/// let scratch_idx = get_scratch_idx();
+/// // Access on a runtime index returns an error if it is out-of-bounds.
+/// let cpu0_scratch = io.read(
+/// CPU_SCRATCH::of::<Cpu0>().try_at(scratch_idx).ok_or(EINVAL)?
+/// ).value();
+/// # Ok(())
+/// # }
+///
+/// // Alias to `SCRATCH[8]` used to convey the firmware exit code.
+/// register! {
+/// /// Per-CPU firmware exit status code.
+/// pub CPU_FIRMWARE_STATUS(u32) => CpuCtlBase + CPU_SCRATCH[8] {
+/// 7:0 status;
+/// }
+/// }
+///
+/// // Non-contiguous relative register arrays can be defined by adding a stride parameter.
+/// // Here, each of the 16 registers of the array is separated by 8 bytes, meaning that the
+/// // registers of the two declarations below are interleaved.
+/// register! {
+/// /// Scratch registers bank 0.
+/// pub CPU_SCRATCH_INTERLEAVED_0(u32)[16, stride = 8] @ CpuCtlBase + 0x00000d00 {
+/// 31:0 value;
+/// }
+///
+/// /// Scratch registers bank 1.
+/// pub CPU_SCRATCH_INTERLEAVED_1(u32)[16, stride = 8] @ CpuCtlBase + 0x00000d04 {
+/// 31:0 value;
+/// }
+/// }
+///
+/// # fn test2(io: &Mmio<0x1000>) -> Result<(), Error> {
+/// let cpu0_status = io.read(CPU_FIRMWARE_STATUS::of::<Cpu0>()).status();
+/// # Ok(())
+/// # }
+/// ```
+#[macro_export]
+macro_rules! register {
+ // Entry point for the macro, allowing multiple registers to be defined in one call.
+ // It matches all possible register declaration patterns to dispatch them to corresponding
+ // `@reg` rule that defines a single register.
+ (
+ $(
+ $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
+ $([ $size:expr $(, stride = $stride:expr)? ])?
+ $(@ $($base:ident +)? $offset:literal)?
+ $(=> $alias:ident $(+ $alias_offset:ident)? $([$alias_idx:expr])? )?
+ { $($fields:tt)* }
+ )*
+ ) => {
+ $(
+ $crate::register!(
+ @reg $(#[$attr])* $vis $name ($storage) $([$size $(, stride = $stride)?])?
+ $(@ $($base +)? $offset)?
+ $(=> $alias $(+ $alias_offset)? $([$alias_idx])? )?
+ { $($fields)* }
+ );
+ )*
+ };
+
+ // All the rules below are private helpers.
+
+ // Creates a register at a fixed offset of the MMIO space.
+ (
+ @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) @ $offset:literal
+ { $($fields:tt)* }
+ ) => {
+ $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
+ $crate::register!(@io_base $name($storage) @ $offset);
+ $crate::register!(@io_fixed $(#[$attr])* $vis $name($storage));
+ };
+
+ // Creates an alias register of fixed offset register `alias` with its own fields.
+ (
+ @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $alias:ident
+ { $($fields:tt)* }
+ ) => {
+ $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
+ $crate::register!(
+ @io_base $name($storage) @
+ <$alias as $crate::io::register::Register>::OFFSET
+ );
+ $crate::register!(@io_fixed $(#[$attr])* $vis $name($storage));
+ };
+
+ // Creates a register at a relative offset from a base address provider.
+ (
+ @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) @ $base:ident + $offset:literal
+ { $($fields:tt)* }
+ ) => {
+ $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
+ $crate::register!(@io_base $name($storage) @ $offset);
+ $crate::register!(@io_relative $vis $name($storage) @ $base);
+ };
+
+ // Creates an alias register of relative offset register `alias` with its own fields.
+ (
+ @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $base:ident + $alias:ident
+ { $($fields:tt)* }
+ ) => {
+ $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
+ $crate::register!(
+ @io_base $name($storage) @ <$alias as $crate::io::register::Register>::OFFSET
+ );
+ $crate::register!(@io_relative $vis $name($storage) @ $base);
+ };
+
+ // Creates an array of registers at a fixed offset of the MMIO space.
+ (
+ @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
+ [ $size:expr, stride = $stride:expr ] @ $offset:literal { $($fields:tt)* }
+ ) => {
+ ::kernel::static_assert!(::core::mem::size_of::<$storage>() <= $stride);
+
+ $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
+ $crate::register!(@io_base $name($storage) @ $offset);
+ $crate::register!(@io_array $vis $name($storage) [ $size, stride = $stride ]);
+ };
+
+ // Shortcut for contiguous array of registers (stride == size of element).
+ (
+ @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) [ $size:expr ] @ $offset:literal
+ { $($fields:tt)* }
+ ) => {
+ $crate::register!(
+ $(#[$attr])* $vis $name($storage) [ $size, stride = ::core::mem::size_of::<$storage>() ]
+ @ $offset { $($fields)* }
+ );
+ };
+
+ // Creates an alias of register `idx` of array of registers `alias` with its own fields.
+ (
+ @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $alias:ident [ $idx:expr ]
+ { $($fields:tt)* }
+ ) => {
+ ::kernel::static_assert!($idx < <$alias as $crate::io::register::RegisterArray>::SIZE);
+
+ $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
+ $crate::register!(
+ @io_base $name($storage) @
+ <$alias as $crate::io::register::Register>::OFFSET
+ + $idx * <$alias as $crate::io::register::RegisterArray>::STRIDE
+ );
+ $crate::register!(@io_fixed $(#[$attr])* $vis $name($storage));
+ };
+
+ // Creates an array of registers at a relative offset from a base address provider.
+ (
+ @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
+ [ $size:expr, stride = $stride:expr ]
+ @ $base:ident + $offset:literal { $($fields:tt)* }
+ ) => {
+ ::kernel::static_assert!(::core::mem::size_of::<$storage>() <= $stride);
+
+ $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
+ $crate::register!(@io_base $name($storage) @ $offset);
+ $crate::register!(
+ @io_relative_array $vis $name($storage) [ $size, stride = $stride ] @ $base + $offset
+ );
+ };
+
+ // Shortcut for contiguous array of relative registers (stride == size of element).
+ (
+ @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) [ $size:expr ]
+ @ $base:ident + $offset:literal { $($fields:tt)* }
+ ) => {
+ $crate::register!(
+ $(#[$attr])* $vis $name($storage) [ $size, stride = ::core::mem::size_of::<$storage>() ]
+ @ $base + $offset { $($fields)* }
+ );
+ };
+
+ // Creates an alias of register `idx` of relative array of registers `alias` with its own
+ // fields.
+ (
+ @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
+ => $base:ident + $alias:ident [ $idx:expr ] { $($fields:tt)* }
+ ) => {
+ ::kernel::static_assert!($idx < <$alias as $crate::io::register::RegisterArray>::SIZE);
+
+ $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
+ $crate::register!(
+ @io_base $name($storage) @
+ <$alias as $crate::io::register::Register>::OFFSET +
+ $idx * <$alias as $crate::io::register::RegisterArray>::STRIDE
+ );
+ $crate::register!(@io_relative $vis $name($storage) @ $base);
+ };
+
+ // Generates the bitfield for the register.
+ //
+ // `#[allow(non_camel_case_types)]` is added since register names typically use
+ // `SCREAMING_CASE`.
+ (
+ @bitfield $(#[$attr:meta])* $vis:vis struct $name:ident($storage:ty) { $($fields:tt)* }
+ ) => {
+ $crate::register!(@bitfield_core
+ #[allow(non_camel_case_types)]
+ $(#[$attr])* $vis $name $storage
+ );
+ $crate::register!(@bitfield_fields $vis $name $storage { $($fields)* });
+ };
+
+ // Implementations shared by all registers types.
+ (@io_base $name:ident($storage:ty) @ $offset:expr) => {
+ impl $crate::io::register::Register for $name {
+ type Storage = $storage;
+
+ const OFFSET: usize = $offset;
+ }
+ };
+
+ // Implementations of fixed registers.
+ (@io_fixed $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)) => {
+ impl $crate::io::register::FixedRegister for $name {}
+
+ $(#[$attr])*
+ $vis const $name: $crate::io::register::FixedRegisterLoc<$name> =
+ $crate::io::register::FixedRegisterLoc::<$name>::new();
+ };
+
+ // Implementations of relative registers.
+ (@io_relative $vis:vis $name:ident ($storage:ty) @ $base:ident) => {
+ impl $crate::io::register::WithBase for $name {
+ type BaseFamily = $base;
+ }
+
+ impl $crate::io::register::RelativeRegister for $name {}
+ };
+
+ // Implementations of register arrays.
+ (@io_array $vis:vis $name:ident ($storage:ty) [ $size:expr, stride = $stride:expr ]) => {
+ impl $crate::io::register::Array for $name {}
+
+ impl $crate::io::register::RegisterArray for $name {
+ const SIZE: usize = $size;
+ const STRIDE: usize = $stride;
+ }
+ };
+
+ // Implementations of relative array registers.
+ (
+ @io_relative_array $vis:vis $name:ident ($storage:ty) [ $size:expr, stride = $stride:expr ]
+ @ $base:ident + $offset:literal
+ ) => {
+ impl $crate::io::register::WithBase for $name {
+ type BaseFamily = $base;
+ }
+
+ impl $crate::io::register::RegisterArray for $name {
+ const SIZE: usize = $size;
+ const STRIDE: usize = $stride;
+ }
+
+ impl $crate::io::register::RelativeRegisterArray for $name {}
+ };
+
+ // Defines the wrapper `$name` type and its conversions from/to the storage type.
+ (@bitfield_core $(#[$attr:meta])* $vis:vis $name:ident $storage:ty) => {
+ $(#[$attr])*
+ #[repr(transparent)]
+ #[derive(Clone, Copy, PartialEq, Eq)]
+ $vis struct $name {
+ inner: $storage,
+ }
+
+ #[allow(dead_code)]
+ impl $name {
+ /// Creates a bitfield from a raw value.
+ #[inline(always)]
+ $vis const fn from_raw(value: $storage) -> Self {
+ Self{ inner: value }
+ }
+
+ /// Turns this bitfield into its raw value.
+ ///
+ /// This is similar to the [`From`] implementation, but is shorter to invoke in
+ /// most cases.
+ #[inline(always)]
+ $vis const fn into_raw(self) -> $storage {
+ self.inner
+ }
+ }
+
+ // SAFETY: `$storage` is `Zeroable` and `$name` is transparent.
+ unsafe impl ::pin_init::Zeroable for $name {}
+
+ impl ::core::convert::From<$name> for $storage {
+ #[inline(always)]
+ fn from(val: $name) -> $storage {
+ val.into_raw()
+ }
+ }
+
+ impl ::core::convert::From<$storage> for $name {
+ #[inline(always)]
+ fn from(val: $storage) -> $name {
+ Self::from_raw(val)
+ }
+ }
+ };
+
+ // Definitions requiring knowledge of individual fields: private and public field accessors,
+ // and `Debug` implementation.
+ (@bitfield_fields $vis:vis $name:ident $storage:ty {
+ $($(#[doc = $doc:expr])* $hi:literal:$lo:literal $field:ident
+ $(?=> $try_into_type:ty)?
+ $(=> $into_type:ty)?
+ ;
+ )*
+ }
+ ) => {
+ #[allow(dead_code)]
+ impl $name {
+ $(
+ $crate::register!(@private_field_accessors $vis $name $storage : $hi:$lo $field);
+ $crate::register!(
+ @public_field_accessors $(#[doc = $doc])* $vis $name $storage : $hi:$lo $field
+ $(?=> $try_into_type)?
+ $(=> $into_type)?
+ );
+ )*
+ }
+
+ $crate::register!(@debug $name { $($field;)* });
+ };
+
+ // Private field accessors working with the exact `Bounded` type for the field.
+ (
+ @private_field_accessors $vis:vis $name:ident $storage:ty : $hi:tt:$lo:tt $field:ident
+ ) => {
+ ::kernel::macros::paste!(
+ $vis const [<$field:upper _RANGE>]: ::core::ops::RangeInclusive<u8> = $lo..=$hi;
+ $vis const [<$field:upper _MASK>]: $storage =
+ ((((1 << $hi) - 1) << 1) + 1) - ((1 << $lo) - 1);
+ $vis const [<$field:upper _SHIFT>]: u32 = $lo;
+ );
+
+ ::kernel::macros::paste!(
+ fn [<__ $field>](self) ->
+ ::kernel::num::Bounded<$storage, { $hi + 1 - $lo }> {
+ // Left shift to align the field's MSB with the storage MSB.
+ const ALIGN_TOP: u32 = $storage::BITS - ($hi + 1);
+ // Right shift to move the top-aligned field to bit 0 of the storage.
+ const ALIGN_BOTTOM: u32 = ALIGN_TOP + $lo;
+
+ // Extract the field using two shifts. `Bounded::shr` produces the correctly-sized
+ // output type.
+ let val = ::kernel::num::Bounded::<$storage, { $storage::BITS }>::from(
+ self.inner << ALIGN_TOP
+ );
+ val.shr::<ALIGN_BOTTOM, { $hi + 1 - $lo } >()
+ }
+
+ const fn [<__with_ $field>](
+ mut self,
+ value: ::kernel::num::Bounded<$storage, { $hi + 1 - $lo }>,
+ ) -> Self
+ {
+ const MASK: $storage = <$name>::[<$field:upper _MASK>];
+ const SHIFT: u32 = <$name>::[<$field:upper _SHIFT>];
+
+ let value = value.get() << SHIFT;
+ self.inner = (self.inner & !MASK) | value;
+
+ self
+ }
+ );
+ };
+
+ // Public accessors for fields infallibly (`=>`) converted to a type.
+ (
+ @public_field_accessors $(#[doc = $doc:expr])* $vis:vis $name:ident $storage:ty :
+ $hi:literal:$lo:literal $field:ident => $into_type:ty
+ ) => {
+ ::kernel::macros::paste!(
+
+ $(#[doc = $doc])*
+ #[doc = "Returns the value of this field."]
+ #[inline(always)]
+ $vis fn $field(self) -> $into_type
+ {
+ self.[<__ $field>]().into()
+ }
+
+ $(#[doc = $doc])*
+ #[doc = "Sets this field to the given `value`."]
+ #[inline(always)]
+ $vis fn [<with_ $field>](self, value: $into_type) -> Self
+ {
+ self.[<__with_ $field>](value.into())
+ }
+
+ );
+ };
+
+ // Public accessors for fields fallibly (`?=>`) converted to a type.
+ (
+ @public_field_accessors $(#[doc = $doc:expr])* $vis:vis $name:ident $storage:ty :
+ $hi:tt:$lo:tt $field:ident ?=> $try_into_type:ty
+ ) => {
+ ::kernel::macros::paste!(
+
+ $(#[doc = $doc])*
+ #[doc = "Returns the value of this field."]
+ #[inline(always)]
+ $vis fn $field(self) ->
+ Result<
+ $try_into_type,
+ <$try_into_type as ::core::convert::TryFrom<
+ ::kernel::num::Bounded<$storage, { $hi + 1 - $lo }>
+ >>::Error
+ >
+ {
+ self.[<__ $field>]().try_into()
+ }
+
+ $(#[doc = $doc])*
+ #[doc = "Sets this field to the given `value`."]
+ #[inline(always)]
+ $vis fn [<with_ $field>](self, value: $try_into_type) -> Self
+ {
+ self.[<__with_ $field>](value.into())
+ }
+
+ );
+ };
+
+ // Public accessors for fields not converted to a type.
+ (
+ @public_field_accessors $(#[doc = $doc:expr])* $vis:vis $name:ident $storage:ty :
+ $hi:tt:$lo:tt $field:ident
+ ) => {
+ ::kernel::macros::paste!(
+
+ $(#[doc = $doc])*
+ #[doc = "Returns the value of this field."]
+ #[inline(always)]
+ $vis fn $field(self) ->
+ ::kernel::num::Bounded<$storage, { $hi + 1 - $lo }>
+ {
+ self.[<__ $field>]()
+ }
+
+ $(#[doc = $doc])*
+ #[doc = "Sets this field to the compile-time constant `VALUE`."]
+ #[inline(always)]
+ $vis const fn [<with_const_ $field>]<const VALUE: $storage>(self) -> Self {
+ self.[<__with_ $field>](
+ ::kernel::num::Bounded::<$storage, { $hi + 1 - $lo }>::new::<VALUE>()
+ )
+ }
+
+ $(#[doc = $doc])*
+ #[doc = "Sets this field to the given `value`."]
+ #[inline(always)]
+ $vis fn [<with_ $field>]<T>(
+ self,
+ value: T,
+ ) -> Self
+ where T: Into<::kernel::num::Bounded<$storage, { $hi + 1 - $lo }>>,
+ {
+ self.[<__with_ $field>](value.into())
+ }
+
+ $(#[doc = $doc])*
+ #[doc = "Tries to set this field to `value`, returning an error if it is out of range."]
+ #[inline(always)]
+ $vis fn [<try_with_ $field>]<T>(
+ self,
+ value: T,
+ ) -> ::kernel::error::Result<Self>
+ where T: ::kernel::num::TryIntoBounded<$storage, { $hi + 1 - $lo }>,
+ {
+ Ok(
+ self.[<__with_ $field>](
+ value.try_into_bounded().ok_or(::kernel::error::code::EOVERFLOW)?
+ )
+ )
+ }
+
+ );
+ };
+
+ // `Debug` implementation.
+ (@debug $name:ident { $($field:ident;)* }) => {
+ impl ::kernel::fmt::Debug for $name {
+ fn fmt(&self, f: &mut ::kernel::fmt::Formatter<'_>) -> ::kernel::fmt::Result {
+ f.debug_struct(stringify!($name))
+ .field("<raw>", &::kernel::prelude::fmt!("{:#x}", self.inner))
+ $(
+ .field(stringify!($field), &self.$field())
+ )*
+ .finish()
+ }
+ }
+ };
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 3da92f18f4ee..34b924819288 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -20,6 +20,7 @@
#![feature(generic_nonzero)]
#![feature(inline_const)]
#![feature(pointer_is_aligned)]
+#![feature(slice_ptr_len)]
//
// Stable since Rust 1.80.0.
#![feature(slice_flatten)]
@@ -37,6 +38,12 @@
#![feature(const_ptr_write)]
#![feature(const_refs_to_cell)]
//
+// Stable since Rust 1.84.0.
+#![feature(strict_provenance)]
+//
+// Stable since Rust 1.89.0.
+#![feature(generic_arg_infer)]
+//
// Expected to become stable.
#![feature(arbitrary_self_types)]
//
diff --git a/rust/kernel/num/bounded.rs b/rust/kernel/num/bounded.rs
index fa81acbdc8c2..bbab6bbcb315 100644
--- a/rust/kernel/num/bounded.rs
+++ b/rust/kernel/num/bounded.rs
@@ -379,6 +379,9 @@ where
/// Returns the wrapped value as the backing type.
///
+ /// This is similar to the [`Deref`] implementation, but doesn't enforce the size invariant of
+ /// the [`Bounded`], which might produce slightly less optimal code.
+ ///
/// # Examples
///
/// ```
@@ -387,8 +390,8 @@ where
/// let v = Bounded::<u32, 4>::new::<7>();
/// assert_eq!(v.get(), 7u32);
/// ```
- pub fn get(self) -> T {
- *self.deref()
+ pub const fn get(self) -> T {
+ self.0
}
/// Increases the number of bits usable for `self`.
@@ -473,6 +476,48 @@ where
// `N` bits, and with the same signedness.
unsafe { Bounded::__new(value) }
}
+
+ /// Right-shifts `self` by `SHIFT` and returns the result as a `Bounded<_, RES>`, where `RES >=
+ /// N - SHIFT`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use kernel::num::Bounded;
+ ///
+ /// let v = Bounded::<u32, 16>::new::<0xff00>();
+ /// let v_shifted: Bounded::<u32, 8> = v.shr::<8, _>();
+ ///
+ /// assert_eq!(v_shifted.get(), 0xff);
+ /// ```
+ pub fn shr<const SHIFT: u32, const RES: u32>(self) -> Bounded<T, RES> {
+ const { assert!(RES + SHIFT >= N) }
+
+ // SAFETY: We shift the value right by `SHIFT`, reducing the number of bits needed to
+ // represent the shifted value by as much, and just asserted that `RES >= N - SHIFT`.
+ unsafe { Bounded::__new(self.0 >> SHIFT) }
+ }
+
+ /// Left-shifts `self` by `SHIFT` and returns the result as a `Bounded<_, RES>`, where `RES >=
+ /// N + SHIFT`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use kernel::num::Bounded;
+ ///
+ /// let v = Bounded::<u32, 8>::new::<0xff>();
+ /// let v_shifted: Bounded::<u32, 16> = v.shl::<8, _>();
+ ///
+ /// assert_eq!(v_shifted.get(), 0xff00);
+ /// ```
+ pub fn shl<const SHIFT: u32, const RES: u32>(self) -> Bounded<T, RES> {
+ const { assert!(RES >= N + SHIFT) }
+
+ // SAFETY: We shift the value left by `SHIFT`, augmenting the number of bits needed to
+ // represent the shifted value by as much, and just asserted that `RES >= N + SHIFT`.
+ unsafe { Bounded::__new(self.0 << SHIFT) }
+ }
}
impl<T, const N: u32> Deref for Bounded<T, N>
@@ -1059,3 +1104,24 @@ where
unsafe { Self::__new(T::from(value)) }
}
}
+
+impl<T> Bounded<T, 1>
+where
+ T: Integer + Zeroable,
+{
+ /// Converts this [`Bounded`] into a [`bool`].
+ ///
+ /// This is a shorter way of writing `bool::from(self)`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use kernel::num::Bounded;
+ ///
+ /// assert_eq!(Bounded::<u8, 1>::new::<0>().into_bool(), false);
+ /// assert_eq!(Bounded::<u8, 1>::new::<1>().into_bool(), true);
+ /// ```
+ pub fn into_bool(self) -> bool {
+ self.into()
+ }
+}
diff --git a/rust/kernel/pci/io.rs b/rust/kernel/pci/io.rs
index fb6edab2aea7..ae78676c927f 100644
--- a/rust/kernel/pci/io.rs
+++ b/rust/kernel/pci/io.rs
@@ -8,8 +8,6 @@ use crate::{
device,
devres::Devres,
io::{
- io_define_read,
- io_define_write,
Io,
IoCapable,
IoKnownSize,
@@ -85,67 +83,41 @@ pub struct ConfigSpace<'a, S: ConfigSpaceKind = Extended> {
_marker: PhantomData<S>,
}
-/// Internal helper macros used to invoke C PCI configuration space read functions.
-///
-/// This macro is intended to be used by higher-level PCI configuration space access macros
-/// (io_define_read) and provides a unified expansion for infallible vs. fallible read semantics. It
-/// emits a direct call into the corresponding C helper and performs the required cast to the Rust
-/// return type.
-///
-/// # Parameters
-///
-/// * `$c_fn` – The C function performing the PCI configuration space write.
-/// * `$self` – The I/O backend object.
-/// * `$ty` – The type of the value to read.
-/// * `$addr` – The PCI configuration space offset to read.
-///
-/// This macro does not perform any validation; all invariants must be upheld by the higher-level
-/// abstraction invoking it.
-macro_rules! call_config_read {
- (infallible, $c_fn:ident, $self:ident, $ty:ty, $addr:expr) => {{
- let mut val: $ty = 0;
- // SAFETY: By the type invariant `$self.pdev` is a valid address.
- // CAST: The offset is cast to `i32` because the C functions expect a 32-bit signed offset
- // parameter. PCI configuration space size is at most 4096 bytes, so the value always fits
- // within `i32` without truncation or sign change.
- // Return value from C function is ignored in infallible accessors.
- let _ret = unsafe { bindings::$c_fn($self.pdev.as_raw(), $addr as i32, &mut val) };
- val
- }};
-}
+/// Implements [`IoCapable`] on [`ConfigSpace`] for `$ty` using `$read_fn` and `$write_fn`.
+macro_rules! impl_config_space_io_capable {
+ ($ty:ty, $read_fn:ident, $write_fn:ident) => {
+ impl<'a, S: ConfigSpaceKind> IoCapable<$ty> for ConfigSpace<'a, S> {
+ unsafe fn io_read(&self, address: usize) -> $ty {
+ let mut val: $ty = 0;
+
+ // Return value from C function is ignored in infallible accessors.
+ let _ret =
+ // SAFETY: By the type invariant `self.pdev` is a valid address.
+ // CAST: The offset is cast to `i32` because the C functions expect a 32-bit
+ // signed offset parameter. PCI configuration space size is at most 4096 bytes,
+ // so the value always fits within `i32` without truncation or sign change.
+ unsafe { bindings::$read_fn(self.pdev.as_raw(), address as i32, &mut val) };
+
+ val
+ }
-/// Internal helper macros used to invoke C PCI configuration space write functions.
-///
-/// This macro is intended to be used by higher-level PCI configuration space access macros
-/// (io_define_write) and provides a unified expansion for infallible vs. fallible read semantics.
-/// It emits a direct call into the corresponding C helper and performs the required cast to the
-/// Rust return type.
-///
-/// # Parameters
-///
-/// * `$c_fn` – The C function performing the PCI configuration space write.
-/// * `$self` – The I/O backend object.
-/// * `$ty` – The type of the written value.
-/// * `$addr` – The configuration space offset to write.
-/// * `$value` – The value to write.
-///
-/// This macro does not perform any validation; all invariants must be upheld by the higher-level
-/// abstraction invoking it.
-macro_rules! call_config_write {
- (infallible, $c_fn:ident, $self:ident, $ty:ty, $addr:expr, $value:expr) => {
- // SAFETY: By the type invariant `$self.pdev` is a valid address.
- // CAST: The offset is cast to `i32` because the C functions expect a 32-bit signed offset
- // parameter. PCI configuration space size is at most 4096 bytes, so the value always fits
- // within `i32` without truncation or sign change.
- // Return value from C function is ignored in infallible accessors.
- let _ret = unsafe { bindings::$c_fn($self.pdev.as_raw(), $addr as i32, $value) };
+ unsafe fn io_write(&self, value: $ty, address: usize) {
+ // Return value from C function is ignored in infallible accessors.
+ let _ret =
+ // SAFETY: By the type invariant `self.pdev` is a valid address.
+ // CAST: The offset is cast to `i32` because the C functions expect a 32-bit
+ // signed offset parameter. PCI configuration space size is at most 4096 bytes,
+ // so the value always fits within `i32` without truncation or sign change.
+ unsafe { bindings::$write_fn(self.pdev.as_raw(), address as i32, value) };
+ }
+ }
};
}
// PCI configuration space supports 8, 16, and 32-bit accesses.
-impl<'a, S: ConfigSpaceKind> IoCapable<u8> for ConfigSpace<'a, S> {}
-impl<'a, S: ConfigSpaceKind> IoCapable<u16> for ConfigSpace<'a, S> {}
-impl<'a, S: ConfigSpaceKind> IoCapable<u32> for ConfigSpace<'a, S> {}
+impl_config_space_io_capable!(u8, pci_read_config_byte, pci_write_config_byte);
+impl_config_space_io_capable!(u16, pci_read_config_word, pci_write_config_word);
+impl_config_space_io_capable!(u32, pci_read_config_dword, pci_write_config_dword);
impl<'a, S: ConfigSpaceKind> Io for ConfigSpace<'a, S> {
/// Returns the base address of the I/O region. It is always 0 for configuration space.
@@ -159,17 +131,6 @@ impl<'a, S: ConfigSpaceKind> Io for ConfigSpace<'a, S> {
fn maxsize(&self) -> usize {
self.pdev.cfg_size().into_raw()
}
-
- // PCI configuration space does not support fallible operations.
- // The default implementations from the Io trait are not used.
-
- io_define_read!(infallible, read8, call_config_read(pci_read_config_byte) -> u8);
- io_define_read!(infallible, read16, call_config_read(pci_read_config_word) -> u16);
- io_define_read!(infallible, read32, call_config_read(pci_read_config_dword) -> u32);
-
- io_define_write!(infallible, write8, call_config_write(pci_write_config_byte) <- u8);
- io_define_write!(infallible, write16, call_config_write(pci_write_config_word) <- u16);
- io_define_write!(infallible, write32, call_config_write(pci_write_config_dword) <- u32);
}
impl<'a, S: ConfigSpaceKind> IoKnownSize for ConfigSpace<'a, S> {
diff --git a/rust/kernel/ptr.rs b/rust/kernel/ptr.rs
index 5b6a382637fe..bdc2d79ff669 100644
--- a/rust/kernel/ptr.rs
+++ b/rust/kernel/ptr.rs
@@ -2,7 +2,13 @@
//! Types and functions to work with pointers and addresses.
-use core::mem::align_of;
+pub mod projection;
+pub use crate::project_pointer as project;
+
+use core::mem::{
+ align_of,
+ size_of, //
+};
use core::num::NonZero;
/// Type representing an alignment, which is always a power of two.
@@ -225,3 +231,25 @@ macro_rules! impl_alignable_uint {
}
impl_alignable_uint!(u8, u16, u32, u64, usize);
+
+/// Trait to represent compile-time known size information.
+///
+/// This is a generalization of [`size_of`] that works for dynamically sized types.
+pub trait KnownSize {
+ /// Get the size of an object of this type in bytes, with the metadata of the given pointer.
+ fn size(p: *const Self) -> usize;
+}
+
+impl<T> KnownSize for T {
+ #[inline(always)]
+ fn size(_: *const Self) -> usize {
+ size_of::<T>()
+ }
+}
+
+impl<T> KnownSize for [T] {
+ #[inline(always)]
+ fn size(p: *const Self) -> usize {
+ p.len() * size_of::<T>()
+ }
+}
diff --git a/rust/kernel/ptr/projection.rs b/rust/kernel/ptr/projection.rs
new file mode 100644
index 000000000000..140ea8e21617
--- /dev/null
+++ b/rust/kernel/ptr/projection.rs
@@ -0,0 +1,305 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Infrastructure for handling projections.
+
+use core::{
+ mem::MaybeUninit,
+ ops::Deref, //
+};
+
+use crate::prelude::*;
+
+/// Error raised when a projection is attempted on an array or slice out of bounds.
+pub struct OutOfBound;
+
+impl From<OutOfBound> for Error {
+ #[inline(always)]
+ fn from(_: OutOfBound) -> Self {
+ ERANGE
+ }
+}
+
+/// A helper trait to perform index projection.
+///
+/// This is similar to [`core::slice::SliceIndex`], but operates on raw pointers safely and
+/// fallibly.
+///
+/// # Safety
+///
+/// The implementation of `index` and `get` (if [`Some`] is returned) must ensure that, if provided
+/// input pointer `slice` and returned pointer `output`, then:
+/// - `output` has the same provenance as `slice`;
+/// - `output.byte_offset_from(slice)` is between 0 to
+/// `KnownSize::size(slice) - KnownSize::size(output)`.
+///
+/// This means that if the input pointer is valid, then pointer returned by `get` or `index` is
+/// also valid.
+#[diagnostic::on_unimplemented(message = "`{Self}` cannot be used to index `{T}`")]
+#[doc(hidden)]
+pub unsafe trait ProjectIndex<T: ?Sized>: Sized {
+ type Output: ?Sized;
+
+ /// Returns an index-projected pointer, if in bounds.
+ fn get(self, slice: *mut T) -> Option<*mut Self::Output>;
+
+ /// Returns an index-projected pointer; fail the build if it cannot be proved to be in bounds.
+ #[inline(always)]
+ fn index(self, slice: *mut T) -> *mut Self::Output {
+ Self::get(self, slice).unwrap_or_else(|| build_error!())
+ }
+}
+
+// Forward array impl to slice impl.
+//
+// SAFETY: Safety requirement guaranteed by the forwarded impl.
+unsafe impl<T, I, const N: usize> ProjectIndex<[T; N]> for I
+where
+ I: ProjectIndex<[T]>,
+{
+ type Output = <I as ProjectIndex<[T]>>::Output;
+
+ #[inline(always)]
+ fn get(self, slice: *mut [T; N]) -> Option<*mut Self::Output> {
+ <I as ProjectIndex<[T]>>::get(self, slice)
+ }
+
+ #[inline(always)]
+ fn index(self, slice: *mut [T; N]) -> *mut Self::Output {
+ <I as ProjectIndex<[T]>>::index(self, slice)
+ }
+}
+
+// SAFETY: `get`-returned pointer has the same provenance as `slice` and the offset is checked to
+// not exceed the required bound.
+unsafe impl<T> ProjectIndex<[T]> for usize {
+ type Output = T;
+
+ #[inline(always)]
+ fn get(self, slice: *mut [T]) -> Option<*mut T> {
+ if self >= slice.len() {
+ None
+ } else {
+ Some(slice.cast::<T>().wrapping_add(self))
+ }
+ }
+}
+
+// SAFETY: `get`-returned pointer has the same provenance as `slice` and the offset is checked to
+// not exceed the required bound.
+unsafe impl<T> ProjectIndex<[T]> for core::ops::Range<usize> {
+ type Output = [T];
+
+ #[inline(always)]
+ fn get(self, slice: *mut [T]) -> Option<*mut [T]> {
+ let new_len = self.end.checked_sub(self.start)?;
+ if self.end > slice.len() {
+ return None;
+ }
+ Some(core::ptr::slice_from_raw_parts_mut(
+ slice.cast::<T>().wrapping_add(self.start),
+ new_len,
+ ))
+ }
+}
+
+// SAFETY: Safety requirement guaranteed by the forwarded impl.
+unsafe impl<T> ProjectIndex<[T]> for core::ops::RangeTo<usize> {
+ type Output = [T];
+
+ #[inline(always)]
+ fn get(self, slice: *mut [T]) -> Option<*mut [T]> {
+ (0..self.end).get(slice)
+ }
+}
+
+// SAFETY: Safety requirement guaranteed by the forwarded impl.
+unsafe impl<T> ProjectIndex<[T]> for core::ops::RangeFrom<usize> {
+ type Output = [T];
+
+ #[inline(always)]
+ fn get(self, slice: *mut [T]) -> Option<*mut [T]> {
+ (self.start..slice.len()).get(slice)
+ }
+}
+
+// SAFETY: `get` returned the pointer as is, so it always has the same provenance and offset of 0.
+unsafe impl<T> ProjectIndex<[T]> for core::ops::RangeFull {
+ type Output = [T];
+
+ #[inline(always)]
+ fn get(self, slice: *mut [T]) -> Option<*mut [T]> {
+ Some(slice)
+ }
+}
+
+/// A helper trait to perform field projection.
+///
+/// This trait has a `DEREF` generic parameter so it can be implemented twice for types that
+/// implement [`Deref`]. This will cause an ambiguity error and thus block [`Deref`] types being
+/// used as base of projection, as they can inject unsoundness. Users therefore must not specify
+/// `DEREF` and should always leave it to be inferred.
+///
+/// # Safety
+///
+/// `proj` may only invoke `f` with a valid allocation, as the documentation of [`Self::proj`]
+/// describes.
+#[doc(hidden)]
+pub unsafe trait ProjectField<const DEREF: bool> {
+ /// Project a pointer to a type to a pointer of a field.
+ ///
+ /// `f` may only be invoked with a valid allocation so it can safely obtain raw pointers to
+ /// fields using `&raw mut`.
+ ///
+ /// This is needed because `base` might not point to a valid allocation, while `&raw mut`
+ /// requires pointers to be in bounds of a valid allocation.
+ ///
+ /// # Safety
+ ///
+ /// `f` must return a pointer in bounds of the provided pointer.
+ unsafe fn proj<F>(base: *mut Self, f: impl FnOnce(*mut Self) -> *mut F) -> *mut F;
+}
+
+// NOTE: in theory, this API should work for `T: ?Sized` and `F: ?Sized`, too. However, we cannot
+// currently support that as we need to obtain a valid allocation that `&raw const` can operate on.
+//
+// SAFETY: `proj` invokes `f` with valid allocation.
+unsafe impl<T> ProjectField<false> for T {
+ #[inline(always)]
+ unsafe fn proj<F>(base: *mut Self, f: impl FnOnce(*mut Self) -> *mut F) -> *mut F {
+ // Create a valid allocation to start projection, as `base` is not necessarily so. The
+ // memory is never actually used so it will be optimized out, so it should work even for
+ // very large `T` (`memoffset` crate also relies on this). To be extra certain, we also
+ // annotate `f` closure with `#[inline(always)]` in the macro.
+ let mut place = MaybeUninit::uninit();
+ let place_base = place.as_mut_ptr();
+ let field = f(place_base);
+ // SAFETY: `field` is in bounds from `base` per safety requirement.
+ let offset = unsafe { field.byte_offset_from(place_base) };
+ // Use `wrapping_byte_offset` as `base` does not need to be of valid allocation.
+ base.wrapping_byte_offset(offset).cast()
+ }
+}
+
+// SAFETY: Vacuously satisfied.
+unsafe impl<T: Deref> ProjectField<true> for T {
+ #[inline(always)]
+ unsafe fn proj<F>(_: *mut Self, _: impl FnOnce(*mut Self) -> *mut F) -> *mut F {
+ build_error!("this function is a guard against `Deref` impl and is never invoked");
+ }
+}
+
+/// Create a projection from a raw pointer.
+///
+/// The projected pointer is within the memory region marked by the input pointer. There is no
+/// requirement that the input raw pointer needs to be valid, so this macro may be used for
+/// projecting pointers outside normal address space, e.g. I/O pointers. However, if the input
+/// pointer is valid, the projected pointer is also valid.
+///
+/// Supported projections include field projections and index projections.
+/// It is not allowed to project into types that implement custom [`Deref`] or
+/// [`Index`](core::ops::Index).
+///
+/// The macro has basic syntax of `kernel::ptr::project!(ptr, projection)`, where `ptr` is an
+/// expression that evaluates to a raw pointer which serves as the base of projection. `projection`
+/// can be a projection expression of form `.field` (normally identifier, or numeral in case of
+/// tuple structs) or of form `[index]`.
+///
+/// If a mutable pointer is needed, the macro input can be prefixed with the `mut` keyword, i.e.
+/// `kernel::ptr::project!(mut ptr, projection)`. By default, a const pointer is created.
+///
+/// `ptr::project!` macro can perform both fallible indexing and build-time checked indexing.
+/// `[index]` form performs build-time bounds checking; if compiler fails to prove `[index]` is in
+/// bounds, compilation will fail. `[index]?` can be used to perform runtime bounds checking;
+/// `OutOfBound` error is raised via `?` if the index is out of bounds.
+///
+/// # Examples
+///
+/// Field projections are performed with `.field_name`:
+///
+/// ```
+/// struct MyStruct { field: u32, }
+/// let ptr: *const MyStruct = core::ptr::dangling();
+/// let field_ptr: *const u32 = kernel::ptr::project!(ptr, .field);
+///
+/// struct MyTupleStruct(u32, u32);
+///
+/// fn proj(ptr: *const MyTupleStruct) {
+/// let field_ptr: *const u32 = kernel::ptr::project!(ptr, .1);
+/// }
+/// ```
+///
+/// Index projections are performed with `[index]`:
+///
+/// ```
+/// fn proj(ptr: *const [u8; 32]) -> Result {
+/// let field_ptr: *const u8 = kernel::ptr::project!(ptr, [1]);
+/// // The following invocation, if uncommented, would fail the build.
+/// //
+/// // kernel::ptr::project!(ptr, [128]);
+///
+/// // This will raise an `OutOfBound` error (which is convertible to `ERANGE`).
+/// kernel::ptr::project!(ptr, [128]?);
+/// Ok(())
+/// }
+/// ```
+///
+/// If you need to match on the error instead of propagate, put the invocation inside a closure:
+///
+/// ```
+/// let ptr: *const [u8; 32] = core::ptr::dangling();
+/// let field_ptr: Result<*const u8> = (|| -> Result<_> {
+/// Ok(kernel::ptr::project!(ptr, [128]?))
+/// })();
+/// assert!(field_ptr.is_err());
+/// ```
+///
+/// For mutable pointers, put `mut` as the first token in macro invocation.
+///
+/// ```
+/// let ptr: *mut [(u8, u16); 32] = core::ptr::dangling_mut();
+/// let field_ptr: *mut u16 = kernel::ptr::project!(mut ptr, [1].1);
+/// ```
+#[macro_export]
+macro_rules! project_pointer {
+ (@gen $ptr:ident, ) => {};
+ // Field projection. `$field` needs to be `tt` to support tuple index like `.0`.
+ (@gen $ptr:ident, .$field:tt $($rest:tt)*) => {
+ // SAFETY: The provided closure always returns an in-bounds pointer.
+ let $ptr = unsafe {
+ $crate::ptr::projection::ProjectField::proj($ptr, #[inline(always)] |ptr| {
+ // Check unaligned field. Not all users (e.g. DMA) can handle unaligned
+ // projections.
+ if false {
+ let _ = &(*ptr).$field;
+ }
+ // SAFETY: `$field` is in bounds, and no implicit `Deref` is possible (if the
+ // type implements `Deref`, Rust cannot infer the generic parameter `DEREF`).
+ &raw mut (*ptr).$field
+ })
+ };
+ $crate::ptr::project!(@gen $ptr, $($rest)*)
+ };
+ // Fallible index projection.
+ (@gen $ptr:ident, [$index:expr]? $($rest:tt)*) => {
+ let $ptr = $crate::ptr::projection::ProjectIndex::get($index, $ptr)
+ .ok_or($crate::ptr::projection::OutOfBound)?;
+ $crate::ptr::project!(@gen $ptr, $($rest)*)
+ };
+ // Build-time checked index projection.
+ (@gen $ptr:ident, [$index:expr] $($rest:tt)*) => {
+ let $ptr = $crate::ptr::projection::ProjectIndex::index($index, $ptr);
+ $crate::ptr::project!(@gen $ptr, $($rest)*)
+ };
+ (mut $ptr:expr, $($proj:tt)*) => {{
+ let ptr: *mut _ = $ptr;
+ $crate::ptr::project!(@gen ptr, $($proj)*);
+ ptr
+ }};
+ ($ptr:expr, $($proj:tt)*) => {{
+ let ptr = <*const _>::cast_mut($ptr);
+ // We currently always project using mutable pointer, as it is not decided whether `&raw
+ // const` allows the resulting pointer to be mutated (see documentation of `addr_of!`).
+ $crate::ptr::project!(@gen ptr, $($proj)*);
+ ptr.cast_const()
+ }};
+}
diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
index fa87779d2253..3f8918764640 100644
--- a/rust/kernel/str.rs
+++ b/rust/kernel/str.rs
@@ -664,13 +664,13 @@ impl fmt::Write for Formatter<'_> {
///
/// * The first byte of `buffer` is always zero.
/// * The length of `buffer` is at least 1.
-pub(crate) struct NullTerminatedFormatter<'a> {
+pub struct NullTerminatedFormatter<'a> {
buffer: &'a mut [u8],
}
impl<'a> NullTerminatedFormatter<'a> {
/// Create a new [`Self`] instance.
- pub(crate) fn new(buffer: &'a mut [u8]) -> Option<NullTerminatedFormatter<'a>> {
+ pub fn new(buffer: &'a mut [u8]) -> Option<NullTerminatedFormatter<'a>> {
*(buffer.first_mut()?) = 0;
// INVARIANT: