[PATCH 01/10] rust: io: register: allow explicit base type specification
From: Gary Guo
Date: Tue Jul 21 2026 - 13:02:07 EST
Currently registers work for all untyped I/O regions, which is not ideal.
It allows registers defined for device A to work for another device B and
there is no safeguarding at all.
All users of the `register!` macro know what type it will be operating on,
and that type is consistent across the driver. Therefore, add a `base`
parameter to `register!`.
Currently this parameter is unused in the generated code; it will be used
when all users of `register!` is converted to gain the parameter.
Signed-off-by: Gary Guo <gary@xxxxxxxxxxx>
---
rust/kernel/io.rs | 4 +++
rust/kernel/io/register.rs | 79 ++++++++++++++++++++++++++++++++++++++++++----
2 files changed, 76 insertions(+), 7 deletions(-)
diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
index 95f46bb75f9e..c5f07c38e59e 100644
--- a/rust/kernel/io.rs
+++ b/rust/kernel/io.rs
@@ -883,6 +883,8 @@ fn try_write<T, L>(self, location: L, value: T) -> Result
/// };
///
/// register! {
+ /// base: Region;
+ ///
/// VERSION(u32) @ 0x100 {
/// 15:8 major;
/// 7:0 minor;
@@ -1028,6 +1030,8 @@ fn write<T, L>(self, location: L, value: T)
/// };
///
/// register! {
+ /// base: Region<0x1000>;
+ ///
/// VERSION(u32) @ 0x100 {
/// 15:8 major;
/// 7:0 minor;
diff --git a/rust/kernel/io/register.rs b/rust/kernel/io/register.rs
index 6cb07fc92cc3..b0fcfce36c9c 100644
--- a/rust/kernel/io/register.rs
+++ b/rust/kernel/io/register.rs
@@ -13,9 +13,14 @@
//! # Simple example
//!
//! ```no_run
-//! use kernel::io::register;
+//! use kernel::io::{
+//! register,
+//! Region,
+//! };
//!
//! register! {
+//! base: Region<0x1000>;
+//!
//! /// Basic information about the chip.
//! pub BOOT_0(u32) @ 0x00000100 {
//! /// Vendor ID.
@@ -55,11 +60,14 @@
//! register,
//! Io,
//! IoLoc,
+//! Region,
//! },
//! num::Bounded,
//! };
-//! # use kernel::io::{Mmio, Region};
+//! # use kernel::io::Mmio;
//! # register! {
+//! # base: Region<0x1000>;
+//! #
//! # pub BOOT_0(u32) @ 0x00000100 {
//! # 15:8 vendor_id;
//! # 7:4 major_revision;
@@ -444,11 +452,14 @@ fn into_io_op(self) -> (FixedRegisterLoc<T>, T) {
/// io::{
/// register,
/// Io,
+/// Region,
/// },
/// };
-/// # use kernel::io::{Mmio, Region};
+/// # use kernel::io::Mmio;
///
/// register! {
+/// base: Region<0x1000>;
+///
/// FIXED_REG(u32) @ 0x100 {
/// 15:8 high_byte;
/// 7:0 low_byte;
@@ -479,9 +490,14 @@ fn into_io_op(self) -> (FixedRegisterLoc<T>, T) {
/// the context:
///
/// ```no_run
-/// use kernel::io::register;
+/// use kernel::io::{
+/// register,
+/// Region,
+/// };
///
/// register! {
+/// base: Region<0x1000>;
+///
/// /// Scratch register.
/// pub SCRATCH(u32) @ 0x00000200 {
/// 31:0 value;
@@ -531,6 +547,7 @@ fn into_io_op(self) -> (FixedRegisterLoc<T>, T) {
///
/// ```ignore
/// register! {
+/// ...
/// pub RELATIVE_REG(u32) @ Base + 0x80 {
/// ...
/// }
@@ -557,9 +574,10 @@ fn into_io_op(self) -> (FixedRegisterLoc<T>, T) {
/// WithBase,
/// },
/// Io,
+/// Region,
/// },
/// };
-/// # use kernel::io::{Mmio, Region};
+/// # use kernel::io::Mmio;
///
/// // Type used to identify the base.
/// pub struct CpuCtlBase;
@@ -578,6 +596,8 @@ fn into_io_op(self) -> (FixedRegisterLoc<T>, T) {
///
/// // This makes `CPU_CTL` accessible from all implementors of `RegisterBase<CpuCtlBase>`.
/// register! {
+/// base: Region<0x1000>;
+///
/// /// CPU core control.
/// pub CPU_CTL(u32) @ CpuCtlBase + 0x10 {
/// 0:0 start;
@@ -594,6 +614,8 @@ fn into_io_op(self) -> (FixedRegisterLoc<T>, T) {
///
/// // Aliases can also be defined for relative register.
/// register! {
+/// base: Region<0x1000>;
+///
/// /// Alias to CPU core control.
/// pub CPU_CTL_ALIAS(u32) => CpuCtlBase + CPU_CTL {
/// /// Start the aliased CPU core.
@@ -636,15 +658,18 @@ fn into_io_op(self) -> (FixedRegisterLoc<T>, T) {
/// register,
/// register::Array,
/// Io,
+/// Region,
/// },
/// };
-/// # use kernel::io::{Mmio, Region};
+/// # use kernel::io::Mmio;
/// # fn get_scratch_idx() -> usize {
/// # 0x15
/// # }
///
/// // Array of 64 consecutive registers with the same layout starting at offset `0x80`.
/// register! {
+/// base: Region<0x1000>;
+///
/// /// Scratch registers.
/// pub SCRATCH(u32)[64] @ 0x00000080 {
/// 31:0 value;
@@ -670,6 +695,8 @@ fn into_io_op(self) -> (FixedRegisterLoc<T>, T) {
/// // Alias to a specific register in an array.
/// // Here `SCRATCH[8]` is used to convey the firmware exit code.
/// register! {
+/// base: Region<0x1000>;
+///
/// /// Firmware exit status code.
/// pub FIRMWARE_STATUS(u32) => SCRATCH[8] {
/// 7:0 status;
@@ -682,6 +709,8 @@ fn into_io_op(self) -> (FixedRegisterLoc<T>, T) {
/// // 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! {
+/// base: Region<0x1000>;
+///
/// /// Scratch registers bank 0.
/// pub SCRATCH_INTERLEAVED_0(u32)[16, stride = 8] @ 0x000000c0 {
/// 31:0 value;
@@ -703,6 +732,7 @@ fn into_io_op(self) -> (FixedRegisterLoc<T>, T) {
///
/// ```ignore
/// register! {
+/// ...
/// pub RELATIVE_REGISTER_ARRAY(u8)[10, stride = 4] @ Base + 0x100 {
/// ...
/// }
@@ -722,9 +752,10 @@ fn into_io_op(self) -> (FixedRegisterLoc<T>, T) {
/// WithBase,
/// },
/// Io,
+/// Region,
/// },
/// };
-/// # use kernel::io::{Mmio, Region};
+/// # use kernel::io::Mmio;
/// # fn get_scratch_idx() -> usize {
/// # 0x15
/// # }
@@ -746,6 +777,8 @@ fn into_io_op(self) -> (FixedRegisterLoc<T>, T) {
///
/// // 64 per-cpu scratch registers, arranged as a contiguous array.
/// register! {
+/// base: Region<0x1000>;
+///
/// /// Per-CPU scratch registers.
/// pub CPU_SCRATCH(u32)[64] @ CpuCtlBase + 0x00000080 {
/// 31:0 value;
@@ -773,6 +806,8 @@ fn into_io_op(self) -> (FixedRegisterLoc<T>, T) {
///
/// // Alias to `SCRATCH[8]` used to convey the firmware exit code.
/// register! {
+/// base: Region<0x1000>;
+///
/// /// Per-CPU firmware exit status code.
/// pub CPU_FIRMWARE_STATUS(u32) => CpuCtlBase + CPU_SCRATCH[8] {
/// 7:0 status;
@@ -783,6 +818,8 @@ fn into_io_op(self) -> (FixedRegisterLoc<T>, T) {
/// // 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! {
+/// base: Region<0x1000>;
+///
/// /// Scratch registers bank 0.
/// pub CPU_SCRATCH_INTERLEAVED_0(u32)[16, stride = 8] @ CpuCtlBase + 0x00000d00 {
/// 31:0 value;
@@ -801,6 +838,34 @@ fn into_io_op(self) -> (FixedRegisterLoc<T>, T) {
/// ```
#[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.
+ (
+ base: $reg_base:ty;
+ $(
+ $(#[$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)* }
+ )*
+ ) => {
+ const _: () = {
+ #[allow(unused)]
+ fn test_base(_: &$reg_base) {}
+ };
+
+ $(
+ $crate::register!(
+ @reg $(#[$attr])* $vis $name ($storage) $([$size $(, stride = $stride)?])?
+ $(@ $($base +)? $offset)?
+ $(=> $alias $(+ $alias_offset)? $([$alias_idx])? )?
+ { $($fields)* }
+ );
+ )*
+ };
+
// 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.
--
2.54.0