* [PATCH 00/10] rust: io: make register bases typed and remove relative registers
@ 2026-07-21 16:54 Gary Guo
2026-07-21 16:54 ` [PATCH 01/10] rust: io: register: allow explicit base type specification Gary Guo
` (9 more replies)
0 siblings, 10 replies; 21+ messages in thread
From: Gary Guo @ 2026-07-21 16:54 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, Daniel Almeida, Miguel Ojeda,
Boqun Feng, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Trevor Gross, Tamir Duberstein, Alexandre Courbot,
Onur Özkan, David Airlie, Simona Vetter, Bjorn Helgaas,
Krzysztof Wilczyński
Cc: driver-core, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
linux-pci, Gary Guo
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.
Change this by requiring a base type for registers. `register!` can still
define registers on untyped `Region`s, although users would need to do so
explicitly and supply a concrete type.
This change makes it possible to use projection for relative registers;
relative registers can be implemented by defining new types for the I/O
subregions and just define registers for these subregion types like normal
registers.
This actually results in more ergnomic code for users of relative registers
(currently only nova-core), because non-array registers can be written to
with
subregion_proj(bar).read(REG)
subregion_proj(bar).write_reg(reg)
instead of
bar.read(REG::of::<Subregion>())
bar.write(WithBase::of::<Subregion>(), reg)
Signed-off-by: Gary Guo <gary@garyguo.net>
---
Gary Guo (10):
rust: io: register: allow explicit base type specification
gpu: nova-core: specify base type for registers
drm/tyr: specify base type for registers
samples: rust: pci: specify base type for registers
rust: io: register: make register have a typed base
rust: io: add static `cast()` method for views
rust: io: add subregion method with compile-time check
gpu: nova-core: use projection for PFALCON and PFALCON2 registers
gpu: nova-core: convert hshub0 from relative register to projection
rust: io: register: remove relative registers
drivers/gpu/drm/tyr/driver.rs | 1 +
drivers/gpu/drm/tyr/regs.rs | 42 ++
drivers/gpu/nova-core/driver.rs | 1 +
drivers/gpu/nova-core/falcon.rs | 158 +++---
drivers/gpu/nova-core/falcon/fsp.rs | 53 +-
drivers/gpu/nova-core/falcon/gsp.rs | 43 +-
drivers/gpu/nova-core/falcon/hal/ga102.rs | 39 +-
drivers/gpu/nova-core/falcon/hal/tu102.rs | 8 +-
drivers/gpu/nova-core/falcon/sec2.rs | 32 +-
drivers/gpu/nova-core/fb/hal/gb100.rs | 51 +-
drivers/gpu/nova-core/fb/regs.rs | 4 +
drivers/gpu/nova-core/firmware/fwsec/bootloader.rs | 11 +-
drivers/gpu/nova-core/gsp/regs.rs | 4 +
drivers/gpu/nova-core/regs.rs | 134 +++--
drivers/gpu/nova-core/vbios.rs | 11 +-
rust/kernel/io.rs | 67 +++
rust/kernel/io/register.rs | 539 +++------------------
samples/rust/rust_driver_pci.rs | 4 +
18 files changed, 461 insertions(+), 741 deletions(-)
---
base-commit: 3fe08b9796f36ef437ab9328e7dd1e5ff2d66603
change-id: 20260721-typed_register-176eab3abee7
Best regards,
--
Gary Guo <gary@garyguo.net>
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 01/10] rust: io: register: allow explicit base type specification
2026-07-21 16:54 [PATCH 00/10] rust: io: make register bases typed and remove relative registers Gary Guo
@ 2026-07-21 16:54 ` Gary Guo
2026-07-21 16:58 ` sashiko-bot
2026-07-21 16:54 ` [PATCH 02/10] gpu: nova-core: specify base type for registers Gary Guo
` (8 subsequent siblings)
9 siblings, 1 reply; 21+ messages in thread
From: Gary Guo @ 2026-07-21 16:54 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, Daniel Almeida, Miguel Ojeda,
Boqun Feng, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Trevor Gross, Tamir Duberstein, Alexandre Courbot,
Onur Özkan, David Airlie, Simona Vetter, Bjorn Helgaas,
Krzysztof Wilczyński
Cc: driver-core, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
linux-pci, Gary Guo
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@garyguo.net>
---
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
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 02/10] gpu: nova-core: specify base type for registers
2026-07-21 16:54 [PATCH 00/10] rust: io: make register bases typed and remove relative registers Gary Guo
2026-07-21 16:54 ` [PATCH 01/10] rust: io: register: allow explicit base type specification Gary Guo
@ 2026-07-21 16:54 ` Gary Guo
2026-07-21 16:59 ` sashiko-bot
2026-07-21 16:54 ` [PATCH 03/10] drm/tyr: " Gary Guo
` (7 subsequent siblings)
9 siblings, 1 reply; 21+ messages in thread
From: Gary Guo @ 2026-07-21 16:54 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, Daniel Almeida, Miguel Ojeda,
Boqun Feng, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Trevor Gross, Tamir Duberstein, Alexandre Courbot,
Onur Özkan, David Airlie, Simona Vetter, Bjorn Helgaas,
Krzysztof Wilczyński
Cc: driver-core, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
linux-pci, Gary Guo
All registers use the same base type, which is `<Bar0 as IO>::Target`. Thus
add the base parameter to `register!` invocation.
Signed-off-by: Gary Guo <gary@garyguo.net>
---
drivers/gpu/nova-core/driver.rs | 1 +
drivers/gpu/nova-core/fb/regs.rs | 4 ++++
drivers/gpu/nova-core/gsp/regs.rs | 4 ++++
drivers/gpu/nova-core/regs.rs | 42 ++++++++++++++++++++++++++++++++++++++-
drivers/gpu/nova-core/vbios.rs | 11 +++++++++-
5 files changed, 60 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs
index 48380ac15f68..fe82363a8201 100644
--- a/drivers/gpu/nova-core/driver.rs
+++ b/drivers/gpu/nova-core/driver.rs
@@ -37,6 +37,7 @@ pub(crate) struct NovaCore<'bound> {
const BAR0_SIZE: usize = SZ_16M;
pub(crate) type Bar0<'a> = &'a pci::Bar<'a, BAR0_SIZE>;
+pub(crate) type NovaRegisters = kernel::io::Region<BAR0_SIZE>;
kernel::pci_device_table!(
PCI_TABLE,
diff --git a/drivers/gpu/nova-core/fb/regs.rs b/drivers/gpu/nova-core/fb/regs.rs
index b2ec02f584be..4296bef5f262 100644
--- a/drivers/gpu/nova-core/fb/regs.rs
+++ b/drivers/gpu/nova-core/fb/regs.rs
@@ -2,9 +2,13 @@
use kernel::io::register;
+use crate::driver::NovaRegisters;
+
// PDISP
register! {
+ base: NovaRegisters;
+
pub(super) NV_PDISP_VGA_WORKSPACE_BASE(u32) @ 0x00625f04 {
/// VGA workspace base address divided by 0x10000.
31:8 addr;
diff --git a/drivers/gpu/nova-core/gsp/regs.rs b/drivers/gpu/nova-core/gsp/regs.rs
index a76dea3c3ab0..34ce8dd1f464 100644
--- a/drivers/gpu/nova-core/gsp/regs.rs
+++ b/drivers/gpu/nova-core/gsp/regs.rs
@@ -2,9 +2,13 @@
use kernel::io::register;
+use crate::driver::NovaRegisters;
+
// PGSP
register! {
+ base: NovaRegisters;
+
pub(super) NV_PGSP_QUEUE_HEAD(u32) @ 0x00110c00 {
31:0 address;
}
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index 397124f245ee..f2aadeb429b8 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -13,7 +13,7 @@
};
use crate::{
- driver::Bar0,
+ driver::{Bar0, NovaRegisters},
falcon::{
DmaTrfCmdSize,
FalconCoreRev,
@@ -37,6 +37,8 @@
// PMC
register! {
+ base: NovaRegisters;
+
/// Basic revision information about the GPU.
pub(crate) NV_PMC_BOOT_0(u32) @ 0x00000000 {
/// Lower bits of the architecture.
@@ -108,6 +110,8 @@ fn fmt(&self, f: &mut kernel::fmt::Formatter<'_>) -> kernel::fmt::Result {
// PBUS
register! {
+ base: NovaRegisters;
+
pub(crate) NV_PBUS_SW_SCRATCH(u32)[64] @ 0x00001400 {}
/// Scratch register 0xe used as FRTS firmware error code.
@@ -119,6 +123,8 @@ fn fmt(&self, f: &mut kernel::fmt::Formatter<'_>) -> kernel::fmt::Result {
// PFB
register! {
+ base: NovaRegisters;
+
/// Low bits of the physical system memory address used by the GPU to perform sysmembar
/// operations (see [`crate::fb::SysmemFlush`]).
pub(crate) NV_PFB_NISO_FLUSH_SYSMEM_ADDR(u32) @ 0x00100c10 {
@@ -154,6 +160,8 @@ fn fmt(&self, f: &mut kernel::fmt::Formatter<'_>) -> kernel::fmt::Result {
pub(crate) struct Hshub0Base(());
register! {
+ base: NovaRegisters;
+
// GB10x sysmem flush registers, relative to the HSHUB0 base. GB10x routes sysmembar
// through a primary and an EG (egress) pair that must both be programmed to the same
// address. Hardware ignores bits 7:0 of each LO register. The boot path uses a fixed
@@ -176,6 +184,8 @@ fn fmt(&self, f: &mut kernel::fmt::Formatter<'_>) -> kernel::fmt::Result {
}
register! {
+ base: NovaRegisters;
+
// GB20x FBHUB0 sysmem flush registers. Unlike the older
// NV_PFB_NISO_FLUSH_SYSMEM_ADDR registers, which encode the address with an
// 8-bit right-shift, these take the raw address split into lower and upper
@@ -190,6 +200,8 @@ fn fmt(&self, f: &mut kernel::fmt::Formatter<'_>) -> kernel::fmt::Result {
}
register! {
+ base: NovaRegisters;
+
/// Low bits of the physical system memory address used by the GPU to perform
/// sysmembar operations on Hopper.
///
@@ -253,6 +265,8 @@ pub(crate) fn is_wpr2_set(self) -> bool {
// number.
register! {
+ base: NovaRegisters;
+
/// Boot Sequence Interface (BSI) register used to determine
/// if GSP reload/resume has completed during the boot process.
pub(crate) NV_PGC6_BSI_SECURE_SCRATCH_14(u32) @ 0x001180f8 {
@@ -307,6 +321,8 @@ pub(crate) fn usable_fb_size(self) -> u64 {
pub(crate) const NV_FUSE_OPT_FPF_SIZE: usize = 16;
register! {
+ base: NovaRegisters;
+
pub(crate) NV_FUSE_OPT_FPF_NVDEC_UCODE1_VERSION(u32)[NV_FUSE_OPT_FPF_SIZE] @ 0x00824100 {
15:0 data => u16;
}
@@ -323,6 +339,8 @@ pub(crate) fn usable_fb_size(self) -> u64 {
// PFALCON
register! {
+ base: NovaRegisters;
+
pub(crate) NV_PFALCON_FALCON_IRQSCLR(u32) @ PFalconBase + 0x00000004 {
6:6 swgen0 => bool;
4:4 halt => bool;
@@ -524,6 +542,8 @@ pub(crate) fn mem_scrubbing_done(self) -> bool {
/* PFALCON2 */
register! {
+ base: NovaRegisters;
+
pub(crate) NV_PFALCON2_FALCON_MOD_SEL(u32) @ PFalcon2Base + 0x00000180 {
7:0 algo ?=> FalconModSelAlgo;
}
@@ -546,6 +566,8 @@ pub(crate) fn mem_scrubbing_done(self) -> bool {
// PRISCV
register! {
+ base: NovaRegisters;
+
/// RISC-V status register for debug (Turing and GA100 only).
/// Reflects current RISC-V core status.
pub(crate) NV_PRISCV_RISCV_CORE_SWITCH_RISCV_STATUS(u32) @ PFalcon2Base + 0x00000240 {
@@ -571,6 +593,8 @@ pub(crate) fn mem_scrubbing_done(self) -> bool {
// These registers manage falcon EMEM communication queues.
register! {
+ base: NovaRegisters;
+
pub(crate) NV_PFSP_QUEUE_HEAD(u32)[8] @ 0x008f2c00 {
31:0 address => u32;
}
@@ -594,9 +618,13 @@ pub(crate) fn mem_scrubbing_done(self) -> bool {
pub(crate) mod gm107 {
use kernel::io::register;
+ use crate::driver::NovaRegisters;
+
// FUSE
register! {
+ base: NovaRegisters;
+
pub(crate) NV_FUSE_STATUS_OPT_DISPLAY(u32) @ 0x00021c04 {
0:0 display_disabled => bool;
}
@@ -606,9 +634,13 @@ pub(crate) mod gm107 {
pub(crate) mod ga100 {
use kernel::io::register;
+ use crate::driver::NovaRegisters;
+
// FUSE
register! {
+ base: NovaRegisters;
+
pub(crate) NV_FUSE_STATUS_OPT_DISPLAY(u32) @ 0x00820c04 {
0:0 display_disabled => bool;
}
@@ -620,9 +652,13 @@ pub(crate) mod ga100 {
pub(crate) mod gh100 {
use kernel::io::register;
+ use crate::driver::NovaRegisters;
+
// PTHERM
register! {
+ base: NovaRegisters;
+
pub(crate) NV_THERM_I2CS_SCRATCH(u32) @ 0x000200bc {
31:0 data;
}
@@ -637,9 +673,13 @@ pub(crate) mod gh100 {
pub(crate) mod gb202 {
use kernel::io::register;
+ use crate::driver::NovaRegisters;
+
// PTHERM
register! {
+ base: NovaRegisters;
+
pub(crate) NV_THERM_I2CS_SCRATCH(u32) @ 0x00ad00bc {
31:0 data;
}
diff --git a/drivers/gpu/nova-core/vbios.rs b/drivers/gpu/nova-core/vbios.rs
index c03650ee5226..9c214b9f4dd9 100644
--- a/drivers/gpu/nova-core/vbios.rs
+++ b/drivers/gpu/nova-core/vbios.rs
@@ -16,7 +16,10 @@
};
use crate::{
- driver::Bar0,
+ driver::{
+ Bar0,
+ NovaRegisters, //
+ },
firmware::{
fwsec::Bcrt30Rsa3kSignature,
FalconUCodeDesc,
@@ -92,12 +95,16 @@ impl<'a> VbiosIterator<'a> {
fn rom_offset(dev: &device::Device, bar0: Bar0<'_>) -> Result<usize> {
// IFR Header in VBIOS.
register! {
+ base: NovaRegisters;
+
NV_PBUS_IFR_FMT_FIXED0(u32) @ 0x300000 {
31:0 signature;
}
}
register! {
+ base: NovaRegisters;
+
NV_PBUS_IFR_FMT_FIXED1(u32) @ 0x300004 {
30:16 fixed_data_size;
15:8 version => u8;
@@ -105,6 +112,8 @@ fn rom_offset(dev: &device::Device, bar0: Bar0<'_>) -> Result<usize> {
}
register! {
+ base: NovaRegisters;
+
NV_PBUS_IFR_FMT_FIXED2(u32) @ 0x300008 {
19:0 total_data_size;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 03/10] drm/tyr: specify base type for registers
2026-07-21 16:54 [PATCH 00/10] rust: io: make register bases typed and remove relative registers Gary Guo
2026-07-21 16:54 ` [PATCH 01/10] rust: io: register: allow explicit base type specification Gary Guo
2026-07-21 16:54 ` [PATCH 02/10] gpu: nova-core: specify base type for registers Gary Guo
@ 2026-07-21 16:54 ` Gary Guo
2026-07-21 17:10 ` sashiko-bot
2026-07-21 16:54 ` [PATCH 04/10] samples: rust: pci: " Gary Guo
` (6 subsequent siblings)
9 siblings, 1 reply; 21+ messages in thread
From: Gary Guo @ 2026-07-21 16:54 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, Daniel Almeida, Miguel Ojeda,
Boqun Feng, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Trevor Gross, Tamir Duberstein, Alexandre Courbot,
Onur Özkan, David Airlie, Simona Vetter, Bjorn Helgaas,
Krzysztof Wilczyński
Cc: driver-core, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
linux-pci, Gary Guo
All registers use the same base type, which is `<IoMem as IO>::Target`.
Thus add the base parameter to `register!` invocation.
Signed-off-by: Gary Guo <gary@garyguo.net>
---
drivers/gpu/drm/tyr/driver.rs | 1 +
drivers/gpu/drm/tyr/regs.rs | 42 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 43 insertions(+)
diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs
index 8348c6cd3929..401a4b321eaf 100644
--- a/drivers/gpu/drm/tyr/driver.rs
+++ b/drivers/gpu/drm/tyr/driver.rs
@@ -43,6 +43,7 @@
};
pub(crate) type IoMem<'a> = kernel::io::mem::IoMem<'a, SZ_2M>;
+pub(crate) type TyrRegisters = kernel::io::Region<SZ_2M>;
pub(crate) struct TyrDrmDriver;
diff --git a/drivers/gpu/drm/tyr/regs.rs b/drivers/gpu/drm/tyr/regs.rs
index 831357a8ef87..ef21d90dbbc4 100644
--- a/drivers/gpu/drm/tyr/regs.rs
+++ b/drivers/gpu/drm/tyr/regs.rs
@@ -55,7 +55,11 @@ pub(crate) mod gpu_control {
uapi, //
};
+ use crate::driver::TyrRegisters;
+
register! {
+ base: TyrRegisters;
+
/// GPU identification register.
pub(crate) GPU_ID(u32) @ 0x0 {
/// Status of the GPU release.
@@ -313,6 +317,8 @@ fn from(mode: FlushMode) -> Self {
}
register! {
+ base: TyrRegisters;
+
/// GPU command register.
///
/// Use the constructor methods to create commands:
@@ -378,6 +384,8 @@ pub(crate) fn clear_fault() -> Self {
}
register! {
+ base: TyrRegisters;
+
/// GPU status register. Read only.
pub(crate) GPU_STATUS(u32) @ 0x34 {
/// GPU active, a 1-bit boolean flag.
@@ -461,6 +469,8 @@ fn from(access: AccessType) -> Self {
}
register! {
+ base: TyrRegisters;
+
/// GPU fault status register. Read only.
pub(crate) GPU_FAULTSTATUS(u32) @ 0x3c {
/// Exception type.
@@ -766,6 +776,8 @@ fn from(mode: CoherencyMode) -> Self {
}
register! {
+ base: TyrRegisters;
+
/// Coherency enable. An index of which coherency protocols should be used.
/// This register only selects the protocol for coherency messages on the
/// interconnect. This is not to enable or disable coherency controlled by MMU.
@@ -806,6 +818,8 @@ fn from(mode: McuControlMode) -> Self {
}
register! {
+ base: TyrRegisters;
+
/// MCU control.
pub(crate) MCU_CONTROL(u32) @ 0x700 {
/// Request MCU state change.
@@ -847,6 +861,8 @@ fn from(status: McuStatus) -> Self {
}
register! {
+ base: TyrRegisters;
+
/// MCU status. Read only.
pub(crate) MCU_STATUS(u32) @ 0x704 {
/// Read current state of MCU.
@@ -860,7 +876,11 @@ fn from(status: McuStatus) -> Self {
pub(crate) mod job_control {
use kernel::register;
+ use crate::driver::TyrRegisters;
+
register! {
+ base: TyrRegisters;
+
/// Raw status of job interrupts.
///
/// Write to this register to trigger these interrupts.
@@ -910,7 +930,11 @@ pub(crate) mod job_control {
pub(crate) mod mmu_control {
use kernel::register;
+ use crate::driver::TyrRegisters;
+
register! {
+ base: TyrRegisters;
+
/// IRQ sources raw status.
///
/// This register contains the raw unmasked interrupt sources for MMU status and exception
@@ -965,6 +989,8 @@ pub(crate) mod mmu_as_control {
register, //
};
+ use crate::driver::TyrRegisters;
+
/// Maximum number of hardware address space slots.
/// The actual number of slots available is usually lower.
pub(crate) const MAX_AS: usize = 16;
@@ -973,6 +999,8 @@ pub(crate) mod mmu_as_control {
const STRIDE: usize = 0x40;
register! {
+ base: TyrRegisters;
+
/// Translation table base address. A 64-bit pointer.
///
/// This field contains the address of the top level of a translation table structure.
@@ -1100,6 +1128,8 @@ fn from(val: MemoryType) -> Self {
}
register! {
+ base: TyrRegisters;
+
/// Stage 1 memory attributes (8-bit bitfield).
///
/// This is not an actual register, but a bitfield definition used by the MEMATTR
@@ -1133,6 +1163,8 @@ fn from(val: MMU_MEMATTR_STAGE1) -> Self {
}
register! {
+ base: TyrRegisters;
+
/// Memory attributes.
///
/// Each address space can configure up to 8 different memory attribute profiles.
@@ -1220,6 +1252,8 @@ fn from(cmd: MmuCommand) -> Self {
}
register! {
+ base: TyrRegisters;
+
/// MMU command register for each address space. Write only.
pub(crate) COMMAND(u32)[MAX_AS, stride = STRIDE] @ 0x2418 {
7:0 command ?=> MmuCommand;
@@ -1347,6 +1381,8 @@ fn from(access: MmuAccessType) -> Self {
}
register! {
+ base: TyrRegisters;
+
/// Fault status register for each address space. Read only.
pub(crate) FAULTSTATUS(u32)[MAX_AS, stride = STRIDE] @ 0x241c {
/// Exception type.
@@ -1572,6 +1608,8 @@ fn from(sh: PtwShareability) -> Self {
}
register! {
+ base: TyrRegisters;
+
/// Translation configuration and control.
pub(crate) TRANSCFG(u64)[MAX_AS, stride = STRIDE] @ 0x2430 {
/// Address space mode.
@@ -1627,6 +1665,8 @@ fn from(sh: PtwShareability) -> Self {
pub(crate) mod doorbell_block {
use kernel::register;
+ use crate::driver::TyrRegisters;
+
/// Number of doorbells available.
pub(crate) const NUM_DOORBELLS: usize = 64;
@@ -1637,6 +1677,8 @@ pub(crate) mod doorbell_block {
const STRIDE: usize = 0x10000;
register! {
+ base: TyrRegisters;
+
/// Doorbell request register. Write-only.
pub(crate) DOORBELL(u32)[NUM_DOORBELLS, stride = STRIDE] @ 0x80000 {
/// Doorbell set. Writing 1 triggers the doorbell.
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 04/10] samples: rust: pci: specify base type for registers
2026-07-21 16:54 [PATCH 00/10] rust: io: make register bases typed and remove relative registers Gary Guo
` (2 preceding siblings ...)
2026-07-21 16:54 ` [PATCH 03/10] drm/tyr: " Gary Guo
@ 2026-07-21 16:54 ` Gary Guo
2026-07-21 17:03 ` sashiko-bot
2026-07-21 16:54 ` [PATCH 05/10] rust: io: register: make register have a typed base Gary Guo
` (5 subsequent siblings)
9 siblings, 1 reply; 21+ messages in thread
From: Gary Guo @ 2026-07-21 16:54 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, Daniel Almeida, Miguel Ojeda,
Boqun Feng, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Trevor Gross, Tamir Duberstein, Alexandre Courbot,
Onur Özkan, David Airlie, Simona Vetter, Bjorn Helgaas,
Krzysztof Wilczyński
Cc: driver-core, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
linux-pci, Gary Guo
The `register!` macro is going to require explicit base type, specify it
for both `register!` usages in PCI sample driver.
Signed-off-by: Gary Guo <gary@garyguo.net>
---
samples/rust/rust_driver_pci.rs | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/samples/rust/rust_driver_pci.rs b/samples/rust/rust_driver_pci.rs
index 1aa8197d8698..aca8af795785 100644
--- a/samples/rust/rust_driver_pci.rs
+++ b/samples/rust/rust_driver_pci.rs
@@ -23,6 +23,8 @@ mod regs {
use super::*;
register! {
+ base: kernel::io::Region<END>;
+
pub(super) TEST(u8) @ 0x0 {
7:0 index => TestIndex;
}
@@ -103,6 +105,8 @@ fn config_space(pdev: &pci::Device<Bound>) {
// Some PCI configuration space registers.
register! {
+ base: pci::Normal;
+
VENDOR_ID(u16) @ 0x0 {
15:0 vendor_id;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 05/10] rust: io: register: make register have a typed base
2026-07-21 16:54 [PATCH 00/10] rust: io: make register bases typed and remove relative registers Gary Guo
` (3 preceding siblings ...)
2026-07-21 16:54 ` [PATCH 04/10] samples: rust: pci: " Gary Guo
@ 2026-07-21 16:54 ` Gary Guo
2026-07-21 17:00 ` sashiko-bot
2026-07-21 16:54 ` [PATCH 06/10] rust: io: add static `cast()` method for views Gary Guo
` (4 subsequent siblings)
9 siblings, 1 reply; 21+ messages in thread
From: Gary Guo @ 2026-07-21 16:54 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, Daniel Almeida, Miguel Ojeda,
Boqun Feng, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Trevor Gross, Tamir Duberstein, Alexandre Courbot,
Onur Özkan, David Airlie, Simona Vetter, Bjorn Helgaas,
Krzysztof Wilczyński
Cc: driver-core, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
linux-pci, Gary Guo
Previously `register!` defined registers can be used on any untyped I/O
regions. With all users specifying their desired register type now,
propagate the specified type and restrict I/O access only when type
matches.
Signed-off-by: Gary Guo <gary@garyguo.net>
---
rust/kernel/io/register.rs | 111 ++++++++++++++++++---------------------------
1 file changed, 45 insertions(+), 66 deletions(-)
diff --git a/rust/kernel/io/register.rs b/rust/kernel/io/register.rs
index b0fcfce36c9c..df47c28ba4f4 100644
--- a/rust/kernel/io/register.rs
+++ b/rust/kernel/io/register.rs
@@ -121,10 +121,11 @@
io::IoLoc, //
};
-use super::Region;
-
/// Trait implemented by all registers.
pub trait Register: Sized {
+ /// Base type for this register.
+ type Base: ?Sized;
+
/// Backing primitive type of the register.
type Storage: Into<Self> + From<Self>;
@@ -139,9 +140,9 @@ pub trait FixedRegister: Register {}
/// Allows `()` to be used as the `location` parameter of [`Io::write`](super::Io::write) when
/// passing a [`FixedRegister`] value.
-impl<const SIZE: usize, T> IoLoc<Region<SIZE>, T> for ()
+impl<Base: ?Sized, T> IoLoc<Base, T> for ()
where
- T: FixedRegister,
+ T: FixedRegister<Base = Base>,
{
type IoType = T::Storage;
@@ -153,9 +154,9 @@ fn offset(self) -> usize {
/// A [`FixedRegister`] carries its location in its type. Thus `FixedRegister` values can be used
/// as an [`IoLoc`].
-impl<const SIZE: usize, T> IoLoc<Region<SIZE>, T> for T
+impl<Base: ?Sized, T> IoLoc<Base, T> for T
where
- T: FixedRegister,
+ T: FixedRegister<Base = Base>,
{
type IoType = T::Storage;
@@ -178,9 +179,9 @@ pub const fn new() -> Self {
}
}
-impl<const SIZE: usize, T> IoLoc<Region<SIZE>, T> for FixedRegisterLoc<T>
+impl<Base: ?Sized, T> IoLoc<Base, T> for FixedRegisterLoc<T>
where
- T: FixedRegister,
+ T: FixedRegister<Base = Base>,
{
type IoType = T::Storage;
@@ -249,9 +250,9 @@ const fn offset(self) -> usize {
}
}
-impl<const SIZE: usize, T, B> IoLoc<Region<SIZE>, T> for RelativeRegisterLoc<T, B>
+impl<SuperBase: ?Sized, T, B> IoLoc<SuperBase, T> for RelativeRegisterLoc<T, B>
where
- T: RelativeRegister,
+ T: RelativeRegister<Base = SuperBase>,
B: RegisterBase<T::BaseFamily> + ?Sized,
{
type IoType = T::Storage;
@@ -293,9 +294,9 @@ pub fn try_new(idx: usize) -> Option<Self> {
}
}
-impl<const SIZE: usize, T> IoLoc<Region<SIZE>, T> for RegisterArrayLoc<T>
+impl<Base: ?Sized, T> IoLoc<Base, T> for RegisterArrayLoc<T>
where
- T: RegisterArray,
+ T: RegisterArray<Base = Base>,
{
type IoType = T::Storage;
@@ -380,9 +381,9 @@ pub fn try_at(self, idx: usize) -> Option<RelativeRegisterArrayLoc<T, B>> {
}
}
-impl<const SIZE: usize, T, B> IoLoc<Region<SIZE>, T> for RelativeRegisterArrayLoc<T, B>
+impl<SuperBase: ?Sized, T, B> IoLoc<SuperBase, T> for RelativeRegisterArrayLoc<T, B>
where
- T: RelativeRegisterArray,
+ T: RelativeRegisterArray<Base = SuperBase>,
B: RegisterBase<T::BaseFamily> + ?Sized,
{
type IoType = T::Storage;
@@ -408,9 +409,9 @@ pub trait LocatedRegister<Base: ?Sized> {
fn into_io_op(self) -> (Self::Location, Self::Value);
}
-impl<const SIZE: usize, T> LocatedRegister<Region<SIZE>> for T
+impl<Base: ?Sized, T> LocatedRegister<Base> for T
where
- T: FixedRegister,
+ T: FixedRegister<Base = Base>,
{
type Location = FixedRegisterLoc<Self::Value>;
type Value = T;
@@ -850,37 +851,10 @@ macro_rules! register {
$(=> $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.
- (
- $(
- $(#[$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)?])?
+ @reg [$reg_base] $(#[$attr])* $vis $name ($storage) $([$size $(, stride = $stride)?])?
$(@ $($base +)? $offset)?
$(=> $alias $(+ $alias_offset)? $([$alias_idx])? )?
{ $($fields)* }
@@ -892,22 +866,22 @@ fn test_base(_: &$reg_base) {}
// Creates a register at a fixed offset of the MMIO space.
(
- @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) @ $offset:literal
+ @reg [$reg_base:ty] $(#[$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_base [$reg_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
+ @reg [$reg_base:ty] $(#[$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) @
+ @io_base [$reg_base] $name($storage) @
<$alias as $crate::io::register::Register>::OFFSET
);
$crate::register!(@io_fixed $(#[$attr])* $vis $name($storage));
@@ -915,44 +889,47 @@ fn test_base(_: &$reg_base) {}
// 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
+ @reg [$reg_base:ty]
+ $(#[$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_base [$reg_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
+ @reg [$reg_base:ty]
+ $(#[$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_base [$reg_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)
+ @reg [$reg_base:ty] $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
[ $size:expr, stride = $stride:expr ] @ $offset:literal { $($fields:tt)* }
) => {
$crate::build_assert::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_base [$reg_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
+ @reg [$reg_base:ty]
+ $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) [ $size:expr ] @ $offset:literal
{ $($fields:tt)* }
) => {
- $crate::register!(
+ $crate::register!(@reg [$reg_base]
$(#[$attr])* $vis $name($storage) [ $size, stride = ::core::mem::size_of::<$storage>() ]
@ $offset { $($fields)* }
);
@@ -960,7 +937,8 @@ fn test_base(_: &$reg_base) {}
// 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 ]
+ @reg [$reg_base:ty]
+ $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $alias:ident [ $idx:expr ]
{ $($fields:tt)* }
) => {
$crate::build_assert::static_assert!(
@@ -969,7 +947,7 @@ fn test_base(_: &$reg_base) {}
$crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
$crate::register!(
- @io_base $name($storage) @
+ @io_base [$reg_base] $name($storage) @
<$alias as $crate::io::register::Register>::OFFSET
+ $idx * <$alias as $crate::io::register::RegisterArray>::STRIDE
);
@@ -978,14 +956,14 @@ fn test_base(_: &$reg_base) {}
// Creates an array of registers at a relative offset from a base address provider.
(
- @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
+ @reg [$reg_base:ty] $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
[ $size:expr, stride = $stride:expr ]
@ $base:ident + $offset:literal { $($fields:tt)* }
) => {
$crate::build_assert::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_base [$reg_base] $name($storage) @ $offset);
$crate::register!(
@io_relative_array $vis $name($storage) [ $size, stride = $stride ] @ $base + $offset
);
@@ -993,10 +971,10 @@ fn test_base(_: &$reg_base) {}
// Shortcut for contiguous array of relative registers (stride == size of element).
(
- @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) [ $size:expr ]
+ @reg [$reg_base:ty] $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) [ $size:expr ]
@ $base:ident + $offset:literal { $($fields:tt)* }
) => {
- $crate::register!(
+ $crate::register!(@reg [$reg_base]
$(#[$attr])* $vis $name($storage) [ $size, stride = ::core::mem::size_of::<$storage>() ]
@ $base + $offset { $($fields)* }
);
@@ -1005,7 +983,7 @@ fn test_base(_: &$reg_base) {}
// 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)
+ @reg [$reg_base:ty] $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
=> $base:ident + $alias:ident [ $idx:expr ] { $($fields:tt)* }
) => {
$crate::build_assert::static_assert!(
@@ -1014,7 +992,7 @@ fn test_base(_: &$reg_base) {}
$crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
$crate::register!(
- @io_base $name($storage) @
+ @io_base [$reg_base] $name($storage) @
<$alias as $crate::io::register::Register>::OFFSET +
$idx * <$alias as $crate::io::register::RegisterArray>::STRIDE
);
@@ -1035,8 +1013,9 @@ fn test_base(_: &$reg_base) {}
};
// Implementations shared by all registers types.
- (@io_base $name:ident($storage:ty) @ $offset:expr) => {
+ (@io_base [$reg_base:ty] $name:ident($storage:ty) @ $offset:expr) => {
impl $crate::io::register::Register for $name {
+ type Base = $reg_base;
type Storage = $storage;
const OFFSET: usize = $offset;
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 06/10] rust: io: add static `cast()` method for views
2026-07-21 16:54 [PATCH 00/10] rust: io: make register bases typed and remove relative registers Gary Guo
` (4 preceding siblings ...)
2026-07-21 16:54 ` [PATCH 05/10] rust: io: register: make register have a typed base Gary Guo
@ 2026-07-21 16:54 ` Gary Guo
2026-07-21 17:08 ` sashiko-bot
2026-07-21 16:54 ` [PATCH 07/10] rust: io: add subregion method with compile-time check Gary Guo
` (3 subsequent siblings)
9 siblings, 1 reply; 21+ messages in thread
From: Gary Guo @ 2026-07-21 16:54 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, Daniel Almeida, Miguel Ojeda,
Boqun Feng, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Trevor Gross, Tamir Duberstein, Alexandre Courbot,
Onur Özkan, David Airlie, Simona Vetter, Bjorn Helgaas,
Krzysztof Wilczyński
Cc: driver-core, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
linux-pci, Gary Guo
Add a compile-time checked variant of `try_cast()` using the minimum size
and alignment information.
Signed-off-by: Gary Guo <gary@garyguo.net>
---
rust/kernel/io.rs | 39 +++++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
index c5f07c38e59e..0f8b166a6d3b 100644
--- a/rust/kernel/io.rs
+++ b/rust/kernel/io.rs
@@ -436,6 +436,45 @@ fn is_empty<T>(self) -> bool
self.len() == 0
}
+ /// Convert into a different typed I/O view.
+ ///
+ /// The target type must be known (statically) to be of the same or smaller size to current
+ /// type, and the current view is properly aligned for the target type.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use kernel::io::{
+ /// io_project,
+ /// Mmio,
+ /// Io,
+ /// Region,
+ /// };
+ /// #[derive(FromBytes, IntoBytes)]
+ /// #[repr(C)]
+ /// struct MyStruct { field: u32, }
+ ///
+ /// # fn test(mmio: &Mmio<'_, Region<0x1000>>) {
+ /// // let mmio: Mmio<'_, Region>;
+ /// let whole: Mmio<'_, MyStruct> = mmio.cast();
+ /// # }
+ /// ```
+ #[inline]
+ fn cast<U>(self) -> <Self::Backend as IoBackend>::View<'a, U>
+ where
+ Self::Target: FromBytes + IntoBytes,
+ U: FromBytes + IntoBytes,
+ {
+ let view = self.as_view();
+ let ptr = Self::Backend::as_ptr(view);
+
+ const_assert!(size_of::<U>() <= Self::Target::MIN_SIZE);
+ const_assert!(align_of::<U>() <= Self::Target::MIN_ALIGN.as_usize());
+
+ // SAFETY: We have checked bounds and alignment, so this is a valid projection.
+ unsafe { Self::Backend::project_view(view, ptr.cast()) }
+ }
+
/// Try to convert into a different typed I/O view.
///
/// A runtime check is performed to ensure that the target type is of same or smaller size to
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 07/10] rust: io: add subregion method with compile-time check
2026-07-21 16:54 [PATCH 00/10] rust: io: make register bases typed and remove relative registers Gary Guo
` (5 preceding siblings ...)
2026-07-21 16:54 ` [PATCH 06/10] rust: io: add static `cast()` method for views Gary Guo
@ 2026-07-21 16:54 ` Gary Guo
2026-07-21 17:05 ` sashiko-bot
2026-07-21 16:54 ` [PATCH 08/10] gpu: nova-core: use projection for PFALCON and PFALCON2 registers Gary Guo
` (2 subsequent siblings)
9 siblings, 1 reply; 21+ messages in thread
From: Gary Guo @ 2026-07-21 16:54 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, Daniel Almeida, Miguel Ojeda,
Boqun Feng, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Trevor Gross, Tamir Duberstein, Alexandre Courbot,
Onur Özkan, David Airlie, Simona Vetter, Bjorn Helgaas,
Krzysztof Wilczyński
Cc: driver-core, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
linux-pci, Gary Guo
Add a helper function that allows obtaining a subregion from region with
compile-time check. This can be used by drivers to obtaining a subregion
for relative register access.
Signed-off-by: Gary Guo <gary@garyguo.net>
---
rust/kernel/io.rs | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
index 0f8b166a6d3b..f31d37c8a3d5 100644
--- a/rust/kernel/io.rs
+++ b/rust/kernel/io.rs
@@ -80,6 +80,30 @@ pub fn ptr_try_from_raw_parts_mut(base: *mut u8, size: usize) -> Result<*mut Sel
Ok(Self::ptr_from_raw_parts_mut(base, size))
}
+
+ /// Create a subregion with provided offset and size.
+ #[inline]
+ pub fn subregion<'a, const OFFSET: usize, const NEW_SIZE: usize, IO>(
+ io: IO,
+ ) -> <IO::Backend as IoBackend>::View<'a, Region<NEW_SIZE>>
+ where
+ IO: IoBase<'a, Target = Self>,
+ {
+ const_assert!(
+ OFFSET + NEW_SIZE <= SIZE && OFFSET.is_multiple_of(4) && NEW_SIZE.is_multiple_of(4)
+ );
+
+ let view = io.as_view();
+ let ptr = IO::Backend::as_ptr(view) as *mut [u8];
+ let new_ptr = Region::ptr_from_raw_parts_mut(
+ ptr.cast::<u8>().wrapping_add(OFFSET),
+ ptr.len() - OFFSET,
+ );
+
+ // SAFETY: We have checked that the new region is a subregion and it is aligned, hence it is
+ // a valid projection.
+ unsafe { IO::Backend::project_view(view, new_ptr) }
+ }
}
impl<const SIZE: usize> KnownSize for Region<SIZE> {
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 08/10] gpu: nova-core: use projection for PFALCON and PFALCON2 registers
2026-07-21 16:54 [PATCH 00/10] rust: io: make register bases typed and remove relative registers Gary Guo
` (6 preceding siblings ...)
2026-07-21 16:54 ` [PATCH 07/10] rust: io: add subregion method with compile-time check Gary Guo
@ 2026-07-21 16:54 ` Gary Guo
2026-07-21 17:00 ` sashiko-bot
2026-07-21 16:54 ` [PATCH 09/10] gpu: nova-core: convert hshub0 from relative register to projection Gary Guo
2026-07-21 16:54 ` [PATCH 10/10] rust: io: register: remove relative registers Gary Guo
9 siblings, 1 reply; 21+ messages in thread
From: Gary Guo @ 2026-07-21 16:54 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, Daniel Almeida, Miguel Ojeda,
Boqun Feng, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Trevor Gross, Tamir Duberstein, Alexandre Courbot,
Onur Özkan, David Airlie, Simona Vetter, Bjorn Helgaas,
Krzysztof Wilczyński
Cc: driver-core, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
linux-pci, Gary Guo
Add fixed size region types for these and add projection methods that
project from `Bar0` into these. Update these registers to be registers on
`PFalconRegisters` and `PFalcon2Registers` and not relative registers on
`NovaRegisters`.
The use sites are updated mechanically; calls to the projection methods are
not extracted in this commit.
Signed-off-by: Gary Guo <gary@garyguo.net>
---
drivers/gpu/nova-core/falcon.rs | 158 +++++++++------------
drivers/gpu/nova-core/falcon/fsp.rs | 53 +++----
drivers/gpu/nova-core/falcon/gsp.rs | 43 +++---
drivers/gpu/nova-core/falcon/hal/ga102.rs | 39 ++---
drivers/gpu/nova-core/falcon/hal/tu102.rs | 8 +-
drivers/gpu/nova-core/falcon/sec2.rs | 32 +++--
drivers/gpu/nova-core/firmware/fwsec/bootloader.rs | 11 +-
drivers/gpu/nova-core/regs.rs | 81 ++++++-----
8 files changed, 193 insertions(+), 232 deletions(-)
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index 78948cc8bff3..3cd065019a66 100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -14,13 +14,12 @@
},
io::{
poll::read_poll_timeout,
- register::{
- RegisterBase,
- WithBase, //
- },
+ register::Array,
Io,
+ Mmio, //
},
prelude::*,
+ sizes::SZ_4K,
time::Delta,
};
@@ -165,18 +164,22 @@ pub(crate) enum FalconFbifMemType with From<Bounded<u32, 1>> {
}
}
-/// Type used to represent the `PFALCON` registers address base for a given falcon engine.
-pub(crate) struct PFalconBase(());
+/// Type used to represent the `PFALCON` registers.
+#[repr(align(4))]
+#[derive(FromBytes, IntoBytes)]
+pub(crate) struct PFalconRegisters([u8; SZ_4K]);
-/// Type used to represent the `PFALCON2` registers address base for a given falcon engine.
-pub(crate) struct PFalcon2Base(());
+/// Type used to represent the `PFALCON2` registers.
+#[repr(align(4))]
+#[derive(FromBytes, IntoBytes)]
+pub(crate) struct PFalcon2Registers([u8; SZ_4K]);
/// Trait defining the parameters of a given Falcon engine.
///
/// Each engine provides one base for `PFALCON` and `PFALCON2` registers.
-pub(crate) trait FalconEngine:
- Send + Sync + RegisterBase<PFalconBase> + RegisterBase<PFalcon2Base> + Sized
-{
+pub(crate) trait FalconEngine: Send + Sync + Sized {
+ fn pfalcon(io: Bar0<'_>) -> Mmio<'_, PFalconRegisters>;
+ fn pfalcon2(io: Bar0<'_>) -> Mmio<'_, PFalcon2Registers>;
}
/// Represents a portion of the firmware to be loaded into a particular memory (e.g. IMEM or DMEM)
@@ -376,14 +379,11 @@ pub(crate) fn new(
/// Resets DMA-related registers.
pub(crate) fn dma_reset(&self) {
- self.bar.update(regs::NV_PFALCON_FBIF_CTL::of::<E>(), |v| {
+ E::pfalcon(self.bar).update(regs::NV_PFALCON_FBIF_CTL, |v| {
v.with_allow_phys_no_ctx(true)
});
- self.bar.write(
- WithBase::of::<E>(),
- regs::NV_PFALCON_FALCON_DMACTL::zeroed(),
- );
+ E::pfalcon(self.bar).write_reg(regs::NV_PFALCON_FALCON_DMACTL::zeroed());
}
/// Reset the controller, select the falcon core, and wait for memory scrubbing to complete.
@@ -392,10 +392,9 @@ pub(crate) fn reset(&self) -> Result {
self.hal.select_core(self)?;
self.hal.reset_wait_mem_scrubbing(self)?;
- self.bar.write(
- WithBase::of::<E>(),
- regs::NV_PFALCON_FALCON_RM::from(self.bar.read(regs::NV_PMC_BOOT_0).into_raw()),
- );
+ E::pfalcon(self.bar).write_reg(regs::NV_PFALCON_FALCON_RM::from(
+ self.bar.read(regs::NV_PMC_BOOT_0).into_raw(),
+ ));
Ok(())
}
@@ -413,8 +412,8 @@ fn pio_wr_imem_slice(&self, load_offsets: FalconPioImemLoadTarget<'_>) -> Result
return Err(EINVAL);
}
- self.bar.write(
- WithBase::of::<E>().at(Self::PIO_PORT),
+ E::pfalcon(self.bar).write(
+ Array::at(Self::PIO_PORT),
regs::NV_PFALCON_FALCON_IMEMC::zeroed()
.with_secure(load_offsets.secure)
.with_aincw(true)
@@ -424,14 +423,14 @@ fn pio_wr_imem_slice(&self, load_offsets: FalconPioImemLoadTarget<'_>) -> Result
for (n, block) in load_offsets.data.chunks(MEM_BLOCK_ALIGNMENT).enumerate() {
let n = u16::try_from(n)?;
let tag: u16 = load_offsets.start_tag.checked_add(n).ok_or(ERANGE)?;
- self.bar.write(
- WithBase::of::<E>().at(Self::PIO_PORT),
+ E::pfalcon(self.bar).write(
+ Array::at(Self::PIO_PORT),
regs::NV_PFALCON_FALCON_IMEMT::zeroed().with_tag(tag),
);
for word in block.chunks_exact(4) {
let w = [word[0], word[1], word[2], word[3]];
- self.bar.write(
- WithBase::of::<E>().at(Self::PIO_PORT),
+ E::pfalcon(self.bar).write(
+ Array::at(Self::PIO_PORT),
regs::NV_PFALCON_FALCON_IMEMD::zeroed().with_data(u32::from_le_bytes(w)),
);
}
@@ -450,8 +449,8 @@ fn pio_wr_dmem_slice(&self, load_offsets: FalconPioDmemLoadTarget<'_>) -> Result
return Err(EINVAL);
}
- self.bar.write(
- WithBase::of::<E>().at(Self::PIO_PORT),
+ E::pfalcon(self.bar).write(
+ Array::at(Self::PIO_PORT),
regs::NV_PFALCON_FALCON_DMEMC::zeroed()
.with_aincw(true)
.with_offs(load_offsets.dst_start),
@@ -459,8 +458,8 @@ fn pio_wr_dmem_slice(&self, load_offsets: FalconPioDmemLoadTarget<'_>) -> Result
for word in load_offsets.data.chunks_exact(4) {
let w = [word[0], word[1], word[2], word[3]];
- self.bar.write(
- WithBase::of::<E>().at(Self::PIO_PORT),
+ E::pfalcon(self.bar).write(
+ Array::at(Self::PIO_PORT),
regs::NV_PFALCON_FALCON_DMEMD::zeroed().with_data(u32::from_le_bytes(w)),
);
}
@@ -473,14 +472,11 @@ pub(crate) fn pio_load<F: FalconFirmware<Target = E> + FalconPioLoadable>(
&self,
fw: &F,
) -> Result {
- self.bar.update(regs::NV_PFALCON_FBIF_CTL::of::<E>(), |v| {
+ E::pfalcon(self.bar).update(regs::NV_PFALCON_FBIF_CTL, |v| {
v.with_allow_phys_no_ctx(true)
});
- self.bar.write(
- WithBase::of::<E>(),
- regs::NV_PFALCON_FALCON_DMACTL::zeroed(),
- );
+ E::pfalcon(self.bar).write_reg(regs::NV_PFALCON_FALCON_DMACTL::zeroed());
if let Some(imem_ns) = fw.imem_ns_load_params() {
self.pio_wr_imem_slice(imem_ns)?;
@@ -492,10 +488,8 @@ pub(crate) fn pio_load<F: FalconFirmware<Target = E> + FalconPioLoadable>(
self.hal.program_brom(self, &fw.brom_params());
- self.bar.write(
- WithBase::of::<E>(),
- regs::NV_PFALCON_FALCON_BOOTVEC::zeroed().with_value(fw.boot_addr()),
- );
+ E::pfalcon(self.bar)
+ .write_reg(regs::NV_PFALCON_FALCON_BOOTVEC::zeroed().with_value(fw.boot_addr()));
Ok(())
}
@@ -563,16 +557,12 @@ fn dma_wr(
// Set up the base source DMA address.
- self.bar.write(
- WithBase::of::<E>(),
- regs::NV_PFALCON_FALCON_DMATRFBASE::zeroed().with_base(
- // CAST: `as u32` is used on purpose since we do want to strip the upper bits,
- // which will be written to `NV_PFALCON_FALCON_DMATRFBASE1`.
- (dma_start >> 8) as u32,
- ),
- );
- self.bar.write(
- WithBase::of::<E>(),
+ E::pfalcon(self.bar).write_reg(regs::NV_PFALCON_FALCON_DMATRFBASE::zeroed().with_base(
+ // CAST: `as u32` is used on purpose since we do want to strip the upper bits,
+ // which will be written to `NV_PFALCON_FALCON_DMATRFBASE1`.
+ (dma_start >> 8) as u32,
+ ));
+ E::pfalcon(self.bar).write_reg(
regs::NV_PFALCON_FALCON_DMATRFBASE1::zeroed().try_with_base(dma_start >> 40)?,
);
@@ -582,23 +572,21 @@ fn dma_wr(
for pos in (0..num_transfers).map(|i| i * DMA_LEN) {
// Perform a transfer of size `DMA_LEN`.
- self.bar.write(
- WithBase::of::<E>(),
+ E::pfalcon(self.bar).write_reg(
regs::NV_PFALCON_FALCON_DMATRFMOFFS::zeroed()
.try_with_offs(load_offsets.dst_start + pos)?,
);
- self.bar.write(
- WithBase::of::<E>(),
+ E::pfalcon(self.bar).write_reg(
regs::NV_PFALCON_FALCON_DMATRFFBOFFS::zeroed().with_offs(src_start + pos),
);
- self.bar.write(WithBase::of::<E>(), cmd);
+ E::pfalcon(self.bar).write_reg(cmd);
// Wait for the transfer to complete.
// TIMEOUT: arbitrarily large value, no DMA transfer to the falcon's small memories
// should ever take that long.
read_poll_timeout(
- || Ok(self.bar.read(regs::NV_PFALCON_FALCON_DMATRFCMD::of::<E>())),
+ || Ok(E::pfalcon(self.bar).read(regs::NV_PFALCON_FALCON_DMATRFCMD)),
|r| r.idle(),
Delta::ZERO,
Delta::from_secs(2),
@@ -630,11 +618,10 @@ fn dma_load<F: FalconFirmware<Target = E> + FalconDmaLoadable>(&self, fw: &F) ->
};
self.dma_reset();
- self.bar
- .update(regs::NV_PFALCON_FBIF_TRANSCFG::of::<E>().at(0), |v| {
- v.with_target(FalconFbifTarget::CoherentSysmem)
- .with_mem_type(FalconFbifMemType::Physical)
- });
+ E::pfalcon(self.bar).update(regs::NV_PFALCON_FBIF_TRANSCFG::at(0), |v| {
+ v.with_target(FalconFbifTarget::CoherentSysmem)
+ .with_mem_type(FalconFbifMemType::Physical)
+ });
self.dma_wr(&dma_obj, FalconMem::ImemSecure, fw.imem_sec_load_params())?;
self.dma_wr(&dma_obj, FalconMem::Dmem, fw.dmem_load_params())?;
@@ -642,10 +629,8 @@ fn dma_load<F: FalconFirmware<Target = E> + FalconDmaLoadable>(&self, fw: &F) ->
self.hal.program_brom(self, &fw.brom_params());
// Set `BootVec` to start of non-secure code.
- self.bar.write(
- WithBase::of::<E>(),
- regs::NV_PFALCON_FALCON_BOOTVEC::zeroed().with_value(fw.boot_addr()),
- );
+ E::pfalcon(self.bar)
+ .write_reg(regs::NV_PFALCON_FALCON_BOOTVEC::zeroed().with_value(fw.boot_addr()));
Ok(())
}
@@ -654,7 +639,7 @@ fn dma_load<F: FalconFirmware<Target = E> + FalconDmaLoadable>(&self, fw: &F) ->
pub(crate) fn wait_till_halted(&self) -> Result<()> {
// TIMEOUT: arbitrarily large value, firmwares should complete in less than 2 seconds.
read_poll_timeout(
- || Ok(self.bar.read(regs::NV_PFALCON_FALCON_CPUCTL::of::<E>())),
+ || Ok(E::pfalcon(self.bar).read(regs::NV_PFALCON_FALCON_CPUCTL)),
|r| r.halted(),
Delta::ZERO,
Delta::from_secs(2),
@@ -665,19 +650,14 @@ pub(crate) fn wait_till_halted(&self) -> Result<()> {
/// Start the falcon CPU.
pub(crate) fn start(&self) -> Result<()> {
- match self
- .bar
- .read(regs::NV_PFALCON_FALCON_CPUCTL::of::<E>())
+ match E::pfalcon(self.bar)
+ .read(regs::NV_PFALCON_FALCON_CPUCTL)
.alias_en()
{
- true => self.bar.write(
- WithBase::of::<E>(),
- regs::NV_PFALCON_FALCON_CPUCTL_ALIAS::zeroed().with_startcpu(true),
- ),
- false => self.bar.write(
- WithBase::of::<E>(),
- regs::NV_PFALCON_FALCON_CPUCTL::zeroed().with_startcpu(true),
- ),
+ true => E::pfalcon(self.bar)
+ .write_reg(regs::NV_PFALCON_FALCON_CPUCTL_ALIAS::zeroed().with_startcpu(true)),
+ false => E::pfalcon(self.bar)
+ .write_reg(regs::NV_PFALCON_FALCON_CPUCTL::zeroed().with_startcpu(true)),
}
Ok(())
@@ -686,31 +666,27 @@ pub(crate) fn start(&self) -> Result<()> {
/// Writes values to the mailbox registers if provided.
pub(crate) fn write_mailboxes(&self, mbox0: Option<u32>, mbox1: Option<u32>) {
if let Some(mbox0) = mbox0 {
- self.bar.write(
- WithBase::of::<E>(),
- regs::NV_PFALCON_FALCON_MAILBOX0::zeroed().with_value(mbox0),
- );
+ E::pfalcon(self.bar)
+ .write_reg(regs::NV_PFALCON_FALCON_MAILBOX0::zeroed().with_value(mbox0));
}
if let Some(mbox1) = mbox1 {
- self.bar.write(
- WithBase::of::<E>(),
- regs::NV_PFALCON_FALCON_MAILBOX1::zeroed().with_value(mbox1),
- );
+ E::pfalcon(self.bar)
+ .write_reg(regs::NV_PFALCON_FALCON_MAILBOX1::zeroed().with_value(mbox1));
}
}
/// Reads the value from `mbox0` register.
pub(crate) fn read_mailbox0(&self) -> u32 {
- self.bar
- .read(regs::NV_PFALCON_FALCON_MAILBOX0::of::<E>())
+ E::pfalcon(self.bar)
+ .read(regs::NV_PFALCON_FALCON_MAILBOX0)
.value()
}
/// Reads the value from `mbox1` register.
pub(crate) fn read_mailbox1(&self) -> u32 {
- self.bar
- .read(regs::NV_PFALCON_FALCON_MAILBOX1::of::<E>())
+ E::pfalcon(self.bar)
+ .read(regs::NV_PFALCON_FALCON_MAILBOX1)
.value()
}
@@ -765,9 +741,7 @@ pub(crate) fn load<F: FalconFirmware<Target = E> + FalconDmaLoadable>(&self, fw:
/// Write the application version to the OS register.
pub(crate) fn write_os_version(&self, app_version: u32) {
- self.bar.write(
- WithBase::of::<E>(),
- regs::NV_PFALCON_FALCON_OS::zeroed().with_value(app_version),
- );
+ E::pfalcon(self.bar)
+ .write_reg(regs::NV_PFALCON_FALCON_OS::zeroed().with_value(app_version));
}
}
diff --git a/drivers/gpu/nova-core/falcon/fsp.rs b/drivers/gpu/nova-core/falcon/fsp.rs
index 53b1079843ae..cf22992e6387 100644
--- a/drivers/gpu/nova-core/falcon/fsp.rs
+++ b/drivers/gpu/nova-core/falcon/fsp.rs
@@ -9,23 +9,21 @@
use kernel::{
io::{
poll::read_poll_timeout,
- register::{
- Array,
- RegisterBase,
- WithBase, //
- },
- Io, //
+ register::Array,
+ Io,
+ Mmio,
+ Region, //
},
prelude::*,
+ sizes::SZ_4K,
time::Delta,
};
use crate::{
+ driver::Bar0,
falcon::{
Falcon,
- FalconEngine,
- PFalcon2Base,
- PFalconBase, //
+ FalconEngine, //
},
num,
regs, //
@@ -37,16 +35,18 @@
/// Type specifying the `Fsp` falcon engine. Cannot be instantiated.
pub(crate) struct Fsp(());
-impl RegisterBase<PFalconBase> for Fsp {
- const BASE: usize = 0x8f2000;
-}
+impl FalconEngine for Fsp {
+ #[inline]
+ fn pfalcon(io: Bar0<'_>) -> Mmio<'_, super::PFalconRegisters> {
+ Region::subregion::<0x8f2000, SZ_4K, _>(io).cast()
+ }
-impl RegisterBase<PFalcon2Base> for Fsp {
- const BASE: usize = 0x8f3000;
+ #[inline]
+ fn pfalcon2(io: Bar0<'_>) -> Mmio<'_, super::PFalcon2Registers> {
+ Region::subregion::<0x8f3000, SZ_4K, _>(io).cast()
+ }
}
-impl FalconEngine for Fsp {}
-
impl<'a> Falcon<'a, Fsp> {
/// Writes `data` to FSP external memory at offset `0`.
///
@@ -58,19 +58,14 @@ fn write_emem(&mut self, data: &[u8]) -> Result {
}
// Begin a write burst at offset `0`, auto-incrementing on each write.
- self.bar.write(
- WithBase::of::<Fsp>(),
- regs::NV_PFALCON_FALCON_EMEMC::zeroed().with_aincw(true),
- );
+ Fsp::pfalcon(self.bar).write_reg(regs::NV_PFALCON_FALCON_EMEMC::zeroed().with_aincw(true));
for chunk in data.chunks_exact(4) {
let value = u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
// Write the next 32-bit `value`; hardware advances the offset.
- self.bar.write(
- WithBase::of::<Fsp>(),
- regs::NV_PFALCON_FALCON_EMEMD::zeroed().with_data(value),
- );
+ Fsp::pfalcon(self.bar)
+ .write_reg(regs::NV_PFALCON_FALCON_EMEMD::zeroed().with_data(value));
}
Ok(())
@@ -86,16 +81,12 @@ fn read_emem(&mut self, data: &mut [u8]) -> Result {
}
// Begin a read burst at offset `0`, auto-incrementing on each read.
- self.bar.write(
- WithBase::of::<Fsp>(),
- regs::NV_PFALCON_FALCON_EMEMC::zeroed().with_aincr(true),
- );
+ Fsp::pfalcon(self.bar).write_reg(regs::NV_PFALCON_FALCON_EMEMC::zeroed().with_aincr(true));
for chunk in data.chunks_exact_mut(4) {
// Read the next 32-bit word; hardware advances the offset.
- let value = self
- .bar
- .read(regs::NV_PFALCON_FALCON_EMEMD::of::<Fsp>())
+ let value = Fsp::pfalcon(self.bar)
+ .read(regs::NV_PFALCON_FALCON_EMEMD)
.data();
chunk.copy_from_slice(&value.to_le_bytes());
}
diff --git a/drivers/gpu/nova-core/falcon/gsp.rs b/drivers/gpu/nova-core/falcon/gsp.rs
index ae32f401aeb0..7308c596ad7b 100644
--- a/drivers/gpu/nova-core/falcon/gsp.rs
+++ b/drivers/gpu/nova-core/falcon/gsp.rs
@@ -3,22 +3,20 @@
use kernel::{
io::{
poll::read_poll_timeout,
- register::{
- RegisterBase,
- WithBase, //
- },
Io,
+ Mmio,
+ Region, //
},
prelude::*,
+ sizes::SZ_4K,
time::Delta, //
};
use crate::{
+ driver::Bar0,
falcon::{
Falcon,
- FalconEngine,
- PFalcon2Base,
- PFalconBase, //
+ FalconEngine, //
},
regs,
};
@@ -26,24 +24,24 @@
/// Type specifying the `Gsp` falcon engine. Cannot be instantiated.
pub(crate) struct Gsp(());
-impl RegisterBase<PFalconBase> for Gsp {
- const BASE: usize = 0x00110000;
-}
+impl FalconEngine for Gsp {
+ #[inline]
+ fn pfalcon<'a>(io: Bar0<'a>) -> Mmio<'a, super::PFalconRegisters> {
+ Region::subregion::<0x00110000, SZ_4K, _>(io).cast()
+ }
-impl RegisterBase<PFalcon2Base> for Gsp {
- const BASE: usize = 0x00111000;
+ #[inline]
+ fn pfalcon2<'a>(io: Bar0<'a>) -> Mmio<'a, super::PFalcon2Registers> {
+ Region::subregion::<0x00111000, SZ_4K, _>(io).cast()
+ }
}
-impl FalconEngine for Gsp {}
-
impl<'a> Falcon<'a, Gsp> {
/// Clears the SWGEN0 bit in the Falcon's IRQ status clear register to
/// allow GSP to signal CPU for processing new messages in message queue.
pub(crate) fn clear_swgen0_intr(&self) {
- self.bar.write(
- WithBase::of::<Gsp>(),
- regs::NV_PFALCON_FALCON_IRQSCLR::zeroed().with_swgen0(true),
- );
+ Gsp::pfalcon(self.bar)
+ .write_reg(regs::NV_PFALCON_FALCON_IRQSCLR::zeroed().with_swgen0(true));
}
/// Checks if GSP reload/resume has completed during the boot process.
@@ -59,8 +57,8 @@ pub(crate) fn check_reload_completed(&self, timeout: Delta) -> Result<bool> {
/// Returns whether the RISC-V branch privilege lockdown bit is set.
pub(crate) fn riscv_branch_privilege_lockdown(&self) -> bool {
- self.bar
- .read(regs::NV_PFALCON_FALCON_HWCFG2::of::<Gsp>())
+ Gsp::pfalcon(self.bar)
+ .read(regs::NV_PFALCON_FALCON_HWCFG2)
.riscv_br_priv_lockdown()
}
@@ -71,9 +69,8 @@ pub(crate) fn priv_target_mask_released(&self) -> bool {
const LOCKED_PATTERN: u32 = 0xbadf_4100;
const LOCKED_MASK: u32 = 0xffff_ff00;
- let hwcfg2 = self
- .bar
- .read(regs::NV_PFALCON_FALCON_HWCFG2::of::<Gsp>())
+ let hwcfg2 = Gsp::pfalcon(self.bar)
+ .read(regs::NV_PFALCON_FALCON_HWCFG2)
.into_raw();
hwcfg2 != 0 && (hwcfg2 & LOCKED_MASK) != LOCKED_PATTERN
diff --git a/drivers/gpu/nova-core/falcon/hal/ga102.rs b/drivers/gpu/nova-core/falcon/hal/ga102.rs
index fe821ded5fa1..c0a23a84aa69 100644
--- a/drivers/gpu/nova-core/falcon/hal/ga102.rs
+++ b/drivers/gpu/nova-core/falcon/hal/ga102.rs
@@ -6,10 +6,7 @@
device,
io::{
poll::read_poll_timeout,
- register::{
- Array,
- WithBase, //
- },
+ register::Array,
Io, //
},
prelude::*,
@@ -32,16 +29,15 @@
use super::FalconHal;
fn select_core_ga102<E: FalconEngine>(bar: Bar0<'_>) -> Result {
- let bcr_ctrl = bar.read(regs::NV_PRISCV_RISCV_BCR_CTRL::of::<E>());
+ let bcr_ctrl = E::pfalcon2(bar).read(regs::NV_PRISCV_RISCV_BCR_CTRL);
if bcr_ctrl.core_select() != PeregrineCoreSelect::Falcon {
- bar.write(
- WithBase::of::<E>(),
+ E::pfalcon2(bar).write_reg(
regs::NV_PRISCV_RISCV_BCR_CTRL::zeroed().with_core_select(PeregrineCoreSelect::Falcon),
);
// TIMEOUT: falcon core should take less than 10ms to report being enabled.
read_poll_timeout(
- || Ok(bar.read(regs::NV_PRISCV_RISCV_BCR_CTRL::of::<E>())),
+ || Ok(E::pfalcon2(bar).read(regs::NV_PRISCV_RISCV_BCR_CTRL)),
|r| r.valid(),
Delta::ZERO,
Delta::from_millis(10),
@@ -87,23 +83,19 @@ fn signature_reg_fuse_version_ga102(
}
fn program_brom_ga102<E: FalconEngine>(bar: Bar0<'_>, params: &FalconBromParams) {
- bar.write(
- WithBase::of::<E>().at(0),
+ E::pfalcon2(bar).write(
+ Array::at(0),
regs::NV_PFALCON2_FALCON_BROM_PARAADDR::zeroed().with_value(params.pkc_data_offset),
);
- bar.write(
- WithBase::of::<E>(),
+ E::pfalcon2(bar).write_reg(
regs::NV_PFALCON2_FALCON_BROM_ENGIDMASK::zeroed()
.with_value(u32::from(params.engine_id_mask)),
);
- bar.write(
- WithBase::of::<E>(),
+ E::pfalcon2(bar).write_reg(
regs::NV_PFALCON2_FALCON_BROM_CURR_UCODE_ID::zeroed().with_ucode_id(params.ucode_id),
);
- bar.write(
- WithBase::of::<E>(),
- regs::NV_PFALCON2_FALCON_MOD_SEL::zeroed().with_algo(FalconModSelAlgo::Rsa3k),
- );
+ E::pfalcon2(bar)
+ .write_reg(regs::NV_PFALCON2_FALCON_MOD_SEL::zeroed().with_algo(FalconModSelAlgo::Rsa3k));
}
pub(super) struct Ga102<E: FalconEngine>(PhantomData<E>);
@@ -133,16 +125,15 @@ fn program_brom(&self, falcon: &Falcon<'_, E>, params: &FalconBromParams) {
}
fn is_riscv_active(&self, falcon: &Falcon<'_, E>) -> bool {
- falcon
- .bar
- .read(regs::NV_PRISCV_RISCV_CPUCTL::of::<E>())
+ E::pfalcon2(falcon.bar)
+ .read(regs::NV_PRISCV_RISCV_CPUCTL)
.active_stat()
}
fn reset_wait_mem_scrubbing(&self, falcon: &Falcon<'_, E>) -> Result {
// TIMEOUT: memory scrubbing should complete in less than 20ms.
read_poll_timeout(
- || Ok(falcon.bar.read(regs::NV_PFALCON_FALCON_HWCFG2::of::<E>())),
+ || Ok(E::pfalcon(falcon.bar).read(regs::NV_PFALCON_FALCON_HWCFG2)),
|r| r.mem_scrubbing_done(),
Delta::ZERO,
Delta::from_millis(20),
@@ -153,12 +144,12 @@ fn reset_wait_mem_scrubbing(&self, falcon: &Falcon<'_, E>) -> Result {
fn reset_eng(&self, falcon: &Falcon<'_, E>) -> Result {
let bar = falcon.bar;
- let _ = bar.read(regs::NV_PFALCON_FALCON_HWCFG2::of::<E>());
+ let _ = E::pfalcon(bar).read(regs::NV_PFALCON_FALCON_HWCFG2);
// According to OpenRM's `kflcnPreResetWait_GA102` documentation, HW sometimes does not set
// RESET_READY so a non-failing timeout is used.
let _ = read_poll_timeout(
- || Ok(bar.read(regs::NV_PFALCON_FALCON_HWCFG2::of::<E>())),
+ || Ok(E::pfalcon(bar).read(regs::NV_PFALCON_FALCON_HWCFG2)),
|r| r.reset_ready(),
Delta::ZERO,
Delta::from_micros(150),
diff --git a/drivers/gpu/nova-core/falcon/hal/tu102.rs b/drivers/gpu/nova-core/falcon/hal/tu102.rs
index 34bf9f3f44c7..23ba22d6a7cf 100644
--- a/drivers/gpu/nova-core/falcon/hal/tu102.rs
+++ b/drivers/gpu/nova-core/falcon/hal/tu102.rs
@@ -5,7 +5,6 @@
use kernel::{
io::{
poll::read_poll_timeout,
- register::WithBase,
Io, //
},
prelude::*,
@@ -49,16 +48,15 @@ fn signature_reg_fuse_version(
fn program_brom(&self, _falcon: &Falcon<'_, E>, _params: &FalconBromParams) {}
fn is_riscv_active(&self, falcon: &Falcon<'_, E>) -> bool {
- falcon
- .bar
- .read(regs::NV_PRISCV_RISCV_CORE_SWITCH_RISCV_STATUS::of::<E>())
+ E::pfalcon2(falcon.bar)
+ .read(regs::NV_PRISCV_RISCV_CORE_SWITCH_RISCV_STATUS)
.active_stat()
}
fn reset_wait_mem_scrubbing(&self, falcon: &Falcon<'_, E>) -> Result {
// TIMEOUT: memory scrubbing should complete in less than 10ms.
read_poll_timeout(
- || Ok(falcon.bar.read(regs::NV_PFALCON_FALCON_DMACTL::of::<E>())),
+ || Ok(E::pfalcon(falcon.bar).read(regs::NV_PFALCON_FALCON_DMACTL)),
|r| r.mem_scrubbing_done(),
Delta::ZERO,
Delta::from_millis(10),
diff --git a/drivers/gpu/nova-core/falcon/sec2.rs b/drivers/gpu/nova-core/falcon/sec2.rs
index 91ec7d49c1f5..5acec63e6394 100644
--- a/drivers/gpu/nova-core/falcon/sec2.rs
+++ b/drivers/gpu/nova-core/falcon/sec2.rs
@@ -1,22 +1,30 @@
// SPDX-License-Identifier: GPL-2.0
-use kernel::io::register::RegisterBase;
+use kernel::{
+ io::{
+ Io,
+ Mmio,
+ Region, //
+ },
+ sizes::SZ_4K,
+};
-use crate::falcon::{
- FalconEngine,
- PFalcon2Base,
- PFalconBase, //
+use crate::{
+ driver::Bar0,
+ falcon::FalconEngine, //
};
/// Type specifying the `Sec2` falcon engine. Cannot be instantiated.
pub(crate) struct Sec2(());
-impl RegisterBase<PFalconBase> for Sec2 {
- const BASE: usize = 0x00840000;
-}
+impl FalconEngine for Sec2 {
+ #[inline]
+ fn pfalcon(io: Bar0<'_>) -> Mmio<'_, super::PFalconRegisters> {
+ Region::subregion::<0x00840000, SZ_4K, _>(io).cast()
+ }
-impl RegisterBase<PFalcon2Base> for Sec2 {
- const BASE: usize = 0x00841000;
+ #[inline]
+ fn pfalcon2(io: Bar0<'_>) -> Mmio<'_, super::PFalcon2Registers> {
+ Region::subregion::<0x00841000, SZ_4K, _>(io).cast()
+ }
}
-
-impl FalconEngine for Sec2 {}
diff --git a/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs b/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs
index d9fafd2eea5b..95fec6dd3cfd 100644
--- a/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs
+++ b/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs
@@ -12,7 +12,10 @@
Device, //
},
dma::Coherent,
- io::{register::WithBase, Io},
+ io::{
+ register::Array,
+ Io, //
+ },
prelude::*,
ptr::{
Alignable,
@@ -33,6 +36,7 @@
Falcon,
FalconBromParams,
FalconDmaLoadable,
+ FalconEngine,
FalconFbifMemType,
FalconFbifTarget,
FalconFirmware,
@@ -287,9 +291,8 @@ pub(crate) fn run(
.inspect_err(|e| dev_err!(dev, "Failed to load FWSEC firmware: {:?}\n", e))?;
// Configure DMA index for the bootloader to fetch the FWSEC firmware from system memory.
- bar.update(
- regs::NV_PFALCON_FBIF_TRANSCFG::of::<Gsp>()
- .try_at(usize::from_safe_cast(self.dmem_desc.ctx_dma))
+ Gsp::pfalcon(bar).update(
+ regs::NV_PFALCON_FBIF_TRANSCFG::try_at(usize::from_safe_cast(self.dmem_desc.ctx_dma))
.ok_or(EINVAL)?,
|v| {
v.with_target(FalconFbifTarget::CoherentSysmem)
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index f2aadeb429b8..7f7cb84b21e8 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -4,7 +4,6 @@
use kernel::{
io::{
register,
- register::WithBase,
Io, //
},
prelude::*,
@@ -24,8 +23,8 @@
FalconMem,
FalconModSelAlgo,
FalconSecurityModel,
- PFalcon2Base,
- PFalconBase,
+ PFalcon2Registers,
+ PFalconRegisters,
PeregrineCoreSelect, //
},
gpu::{
@@ -339,32 +338,32 @@ pub(crate) fn usable_fb_size(self) -> u64 {
// PFALCON
register! {
- base: NovaRegisters;
+ base: PFalconRegisters;
- pub(crate) NV_PFALCON_FALCON_IRQSCLR(u32) @ PFalconBase + 0x00000004 {
+ pub(crate) NV_PFALCON_FALCON_IRQSCLR(u32) @ 0x00000004 {
6:6 swgen0 => bool;
4:4 halt => bool;
}
- pub(crate) NV_PFALCON_FALCON_MAILBOX0(u32) @ PFalconBase + 0x00000040 {
+ pub(crate) NV_PFALCON_FALCON_MAILBOX0(u32) @ 0x00000040 {
31:0 value => u32;
}
- pub(crate) NV_PFALCON_FALCON_MAILBOX1(u32) @ PFalconBase + 0x00000044 {
+ pub(crate) NV_PFALCON_FALCON_MAILBOX1(u32) @ 0x00000044 {
31:0 value => u32;
}
/// Used to store version information about the firmware running
/// on the Falcon processor.
- pub(crate) NV_PFALCON_FALCON_OS(u32) @ PFalconBase + 0x00000080 {
+ pub(crate) NV_PFALCON_FALCON_OS(u32) @ 0x00000080 {
31:0 value => u32;
}
- pub(crate) NV_PFALCON_FALCON_RM(u32) @ PFalconBase + 0x00000084 {
+ pub(crate) NV_PFALCON_FALCON_RM(u32) @ 0x00000084 {
31:0 value => u32;
}
- pub(crate) NV_PFALCON_FALCON_HWCFG2(u32) @ PFalconBase + 0x000000f4 {
+ pub(crate) NV_PFALCON_FALCON_HWCFG2(u32) @ 0x000000f4 {
/// Signal indicating that reset is completed (GA102+).
31:31 reset_ready => bool;
/// RISC-V branch privilege lockdown bit.
@@ -374,17 +373,17 @@ pub(crate) fn usable_fb_size(self) -> u64 {
10:10 riscv => bool;
}
- pub(crate) NV_PFALCON_FALCON_CPUCTL(u32) @ PFalconBase + 0x00000100 {
+ pub(crate) NV_PFALCON_FALCON_CPUCTL(u32) @ 0x00000100 {
6:6 alias_en => bool;
4:4 halted => bool;
1:1 startcpu => bool;
}
- pub(crate) NV_PFALCON_FALCON_BOOTVEC(u32) @ PFalconBase + 0x00000104 {
+ pub(crate) NV_PFALCON_FALCON_BOOTVEC(u32) @ 0x00000104 {
31:0 value => u32;
}
- pub(crate) NV_PFALCON_FALCON_DMACTL(u32) @ PFalconBase + 0x0000010c {
+ pub(crate) NV_PFALCON_FALCON_DMACTL(u32) @ 0x0000010c {
7:7 secure_stat => bool;
6:3 dmaq_num;
2:2 imem_scrubbing => bool;
@@ -392,15 +391,15 @@ pub(crate) fn usable_fb_size(self) -> u64 {
0:0 require_ctx => bool;
}
- pub(crate) NV_PFALCON_FALCON_DMATRFBASE(u32) @ PFalconBase + 0x00000110 {
+ pub(crate) NV_PFALCON_FALCON_DMATRFBASE(u32) @ 0x00000110 {
31:0 base => u32;
}
- pub(crate) NV_PFALCON_FALCON_DMATRFMOFFS(u32) @ PFalconBase + 0x00000114 {
+ pub(crate) NV_PFALCON_FALCON_DMATRFMOFFS(u32) @ 0x00000114 {
23:0 offs;
}
- pub(crate) NV_PFALCON_FALCON_DMATRFCMD(u32) @ PFalconBase + 0x00000118 {
+ pub(crate) NV_PFALCON_FALCON_DMATRFCMD(u32) @ 0x00000118 {
16:16 set_dmtag;
14:12 ctxdma;
10:8 size ?=> DmaTrfCmdSize;
@@ -411,15 +410,15 @@ pub(crate) fn usable_fb_size(self) -> u64 {
0:0 full => bool;
}
- pub(crate) NV_PFALCON_FALCON_DMATRFFBOFFS(u32) @ PFalconBase + 0x0000011c {
+ pub(crate) NV_PFALCON_FALCON_DMATRFFBOFFS(u32) @ 0x0000011c {
31:0 offs => u32;
}
- pub(crate) NV_PFALCON_FALCON_DMATRFBASE1(u32) @ PFalconBase + 0x00000128 {
+ pub(crate) NV_PFALCON_FALCON_DMATRFBASE1(u32) @ 0x00000128 {
8:0 base;
}
- pub(crate) NV_PFALCON_FALCON_HWCFG1(u32) @ PFalconBase + 0x0000012c {
+ pub(crate) NV_PFALCON_FALCON_HWCFG1(u32) @ 0x0000012c {
/// Core revision subversion.
7:6 core_rev_subversion => FalconCoreRevSubversion;
/// Security model.
@@ -428,12 +427,12 @@ pub(crate) fn usable_fb_size(self) -> u64 {
3:0 core_rev ?=> FalconCoreRev;
}
- pub(crate) NV_PFALCON_FALCON_CPUCTL_ALIAS(u32) @ PFalconBase + 0x00000130 {
+ pub(crate) NV_PFALCON_FALCON_CPUCTL_ALIAS(u32) @ 0x00000130 {
1:1 startcpu => bool;
}
/// IMEM access control register. Up to 4 ports are available for IMEM access.
- pub(crate) NV_PFALCON_FALCON_IMEMC(u32)[4, stride = 16] @ PFalconBase + 0x00000180 {
+ pub(crate) NV_PFALCON_FALCON_IMEMC(u32)[4, stride = 16] @ 0x00000180 {
/// Access secure IMEM.
28:28 secure => bool;
/// Auto-increment on write.
@@ -444,17 +443,17 @@ pub(crate) fn usable_fb_size(self) -> u64 {
/// IMEM data register. Reading/writing this register accesses IMEM at the address
/// specified by the corresponding IMEMC register.
- pub(crate) NV_PFALCON_FALCON_IMEMD(u32)[4, stride = 16] @ PFalconBase + 0x00000184 {
+ pub(crate) NV_PFALCON_FALCON_IMEMD(u32)[4, stride = 16] @ 0x00000184 {
31:0 data;
}
/// IMEM tag register. Used to set the tag for the current IMEM block.
- pub(crate) NV_PFALCON_FALCON_IMEMT(u32)[4, stride = 16] @ PFalconBase + 0x00000188 {
+ pub(crate) NV_PFALCON_FALCON_IMEMT(u32)[4, stride = 16] @ 0x00000188 {
15:0 tag;
}
/// DMEM access control register. Up to 8 ports are available for DMEM access.
- pub(crate) NV_PFALCON_FALCON_DMEMC(u32)[8, stride = 8] @ PFalconBase + 0x000001c0 {
+ pub(crate) NV_PFALCON_FALCON_DMEMC(u32)[8, stride = 8] @ 0x000001c0 {
/// Auto-increment on write.
24:24 aincw => bool;
/// DMEM block and word offset.
@@ -463,29 +462,29 @@ pub(crate) fn usable_fb_size(self) -> u64 {
/// DMEM data register. Reading/writing this register accesses DMEM at the address
/// specified by the corresponding DMEMC register.
- pub(crate) NV_PFALCON_FALCON_DMEMD(u32)[8, stride = 8] @ PFalconBase + 0x000001c4 {
+ pub(crate) NV_PFALCON_FALCON_DMEMD(u32)[8, stride = 8] @ 0x000001c4 {
31:0 data;
}
/// Actually known as `NV_PSEC_FALCON_ENGINE` and `NV_PGSP_FALCON_ENGINE` depending on the
/// falcon instance.
- pub(crate) NV_PFALCON_FALCON_ENGINE(u32) @ PFalconBase + 0x000003c0 {
+ pub(crate) NV_PFALCON_FALCON_ENGINE(u32) @ 0x000003c0 {
0:0 reset => bool;
}
- pub(crate) NV_PFALCON_FBIF_TRANSCFG(u32)[8] @ PFalconBase + 0x00000600 {
+ pub(crate) NV_PFALCON_FBIF_TRANSCFG(u32)[8] @ 0x00000600 {
2:2 mem_type => FalconFbifMemType;
1:0 target ?=> FalconFbifTarget;
}
- pub(crate) NV_PFALCON_FBIF_CTL(u32) @ PFalconBase + 0x00000624 {
+ pub(crate) NV_PFALCON_FBIF_CTL(u32) @ 0x00000624 {
7:7 allow_phys_no_ctx => bool;
}
// Falcon EMEM PIO registers (used by FSP on Hopper/Blackwell).
// These provide the falcon external memory communication interface.
- pub(crate) NV_PFALCON_FALCON_EMEMC(u32) @ PFalconBase + 0x00000ac0 {
+ pub(crate) NV_PFALCON_FALCON_EMEMC(u32) @ 0x00000ac0 {
/// EMEM byte offset (4-byte aligned) within the block.
7:2 offs;
/// EMEM block to access.
@@ -496,7 +495,7 @@ pub(crate) fn usable_fb_size(self) -> u64 {
25:25 aincr => bool;
}
- pub(crate) NV_PFALCON_FALCON_EMEMD(u32) @ PFalconBase + 0x00000ac4 {
+ pub(crate) NV_PFALCON_FALCON_EMEMD(u32) @ 0x00000ac4 {
31:0 data => u32;
}
}
@@ -523,12 +522,12 @@ pub(crate) fn with_falcon_mem(self, mem: FalconMem) -> Self {
impl NV_PFALCON_FALCON_ENGINE {
/// Resets the falcon
pub(crate) fn reset_engine<E: FalconEngine>(bar: Bar0<'_>) {
- bar.update(Self::of::<E>(), |r| r.with_reset(true));
+ E::pfalcon(bar).update(NV_PFALCON_FALCON_ENGINE, |r| r.with_reset(true));
// TIMEOUT: falcon engine should not take more than 10us to reset.
time::delay::fsleep(time::Delta::from_micros(10));
- bar.update(Self::of::<E>(), |r| r.with_reset(false));
+ E::pfalcon(bar).update(NV_PFALCON_FALCON_ENGINE, |r| r.with_reset(false));
}
}
@@ -542,23 +541,23 @@ pub(crate) fn mem_scrubbing_done(self) -> bool {
/* PFALCON2 */
register! {
- base: NovaRegisters;
+ base: PFalcon2Registers;
- pub(crate) NV_PFALCON2_FALCON_MOD_SEL(u32) @ PFalcon2Base + 0x00000180 {
+ pub(crate) NV_PFALCON2_FALCON_MOD_SEL(u32) @ 0x00000180 {
7:0 algo ?=> FalconModSelAlgo;
}
- pub(crate) NV_PFALCON2_FALCON_BROM_CURR_UCODE_ID(u32) @ PFalcon2Base + 0x00000198 {
+ pub(crate) NV_PFALCON2_FALCON_BROM_CURR_UCODE_ID(u32) @ 0x00000198 {
7:0 ucode_id => u8;
}
- pub(crate) NV_PFALCON2_FALCON_BROM_ENGIDMASK(u32) @ PFalcon2Base + 0x0000019c {
+ pub(crate) NV_PFALCON2_FALCON_BROM_ENGIDMASK(u32) @ 0x0000019c {
31:0 value => u32;
}
/// OpenRM defines this as a register array, but doesn't specify its size and only uses its
/// first element. Be conservative until we know the actual size or need to use more registers.
- pub(crate) NV_PFALCON2_FALCON_BROM_PARAADDR(u32)[1] @ PFalcon2Base + 0x00000210 {
+ pub(crate) NV_PFALCON2_FALCON_BROM_PARAADDR(u32)[1] @ 0x00000210 {
31:0 value => u32;
}
}
@@ -566,23 +565,23 @@ pub(crate) fn mem_scrubbing_done(self) -> bool {
// PRISCV
register! {
- base: NovaRegisters;
+ base: PFalcon2Registers;
/// RISC-V status register for debug (Turing and GA100 only).
/// Reflects current RISC-V core status.
- pub(crate) NV_PRISCV_RISCV_CORE_SWITCH_RISCV_STATUS(u32) @ PFalcon2Base + 0x00000240 {
+ pub(crate) NV_PRISCV_RISCV_CORE_SWITCH_RISCV_STATUS(u32) @ 0x00000240 {
/// RISC-V core active/inactive status.
0:0 active_stat => bool;
}
/// GA102 and later.
- pub(crate) NV_PRISCV_RISCV_CPUCTL(u32) @ PFalcon2Base + 0x00000388 {
+ pub(crate) NV_PRISCV_RISCV_CPUCTL(u32) @ 0x00000388 {
7:7 active_stat => bool;
0:0 halted => bool;
}
/// GA102 and later.
- pub(crate) NV_PRISCV_RISCV_BCR_CTRL(u32) @ PFalcon2Base + 0x00000668 {
+ pub(crate) NV_PRISCV_RISCV_BCR_CTRL(u32) @ 0x00000668 {
8:8 br_fetch => bool;
4:4 core_select => PeregrineCoreSelect;
0:0 valid => bool;
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 09/10] gpu: nova-core: convert hshub0 from relative register to projection
2026-07-21 16:54 [PATCH 00/10] rust: io: make register bases typed and remove relative registers Gary Guo
` (7 preceding siblings ...)
2026-07-21 16:54 ` [PATCH 08/10] gpu: nova-core: use projection for PFALCON and PFALCON2 registers Gary Guo
@ 2026-07-21 16:54 ` Gary Guo
2026-07-21 17:18 ` sashiko-bot
2026-07-21 16:54 ` [PATCH 10/10] rust: io: register: remove relative registers Gary Guo
9 siblings, 1 reply; 21+ messages in thread
From: Gary Guo @ 2026-07-21 16:54 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, Daniel Almeida, Miguel Ojeda,
Boqun Feng, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Trevor Gross, Tamir Duberstein, Alexandre Courbot,
Onur Özkan, David Airlie, Simona Vetter, Bjorn Helgaas,
Krzysztof Wilczyński
Cc: driver-core, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
linux-pci, Gary Guo
Similar to the PFALCON and PFALCON2 conversion, the hshub0 relative access
can also be achieved cleanly with projection and a new base.
Signed-off-by: Gary Guo <gary@garyguo.net>
---
drivers/gpu/nova-core/fb/hal/gb100.rs | 51 ++++++++++++++++++-----------------
drivers/gpu/nova-core/regs.rs | 19 ++++++++-----
2 files changed, 38 insertions(+), 32 deletions(-)
diff --git a/drivers/gpu/nova-core/fb/hal/gb100.rs b/drivers/gpu/nova-core/fb/hal/gb100.rs
index 6e0eba101ca1..ec910e0a044b 100644
--- a/drivers/gpu/nova-core/fb/hal/gb100.rs
+++ b/drivers/gpu/nova-core/fb/hal/gb100.rs
@@ -5,11 +5,9 @@
use kernel::{
io::{
- register::{
- RegisterBase,
- WithBase, //
- },
- Io, //
+ Io,
+ Mmio,
+ Region, //
},
num::Bounded,
prelude::*,
@@ -29,17 +27,24 @@
struct Gb100;
-impl RegisterBase<regs::Hshub0Base> for Gb100 {
- const BASE: usize = 0x0087_0000;
+impl Gb100 {
+ #[inline]
+ fn hshub0(self, bar: Bar0<'_>) -> Mmio<'_, regs::Hshub0Registers> {
+ Region::subregion::<0x0087_0000, SZ_4K, _>(bar).cast()
+ }
}
fn read_sysmem_flush_page_gb100(bar: Bar0<'_>) -> u64 {
let lo = u64::from(
- bar.read(regs::NV_PFB_HSHUB_PCIE_FLUSH_SYSMEM_ADDR_LO::of::<Gb100>())
+ Gb100
+ .hshub0(bar)
+ .read(regs::NV_PFB_HSHUB_PCIE_FLUSH_SYSMEM_ADDR_LO)
.adr(),
);
let hi = u64::from(
- bar.read(regs::NV_PFB_HSHUB_PCIE_FLUSH_SYSMEM_ADDR_HI::of::<Gb100>())
+ Gb100
+ .hshub0(bar)
+ .read(regs::NV_PFB_HSHUB_PCIE_FLUSH_SYSMEM_ADDR_HI)
.adr(),
);
@@ -58,24 +63,20 @@ fn write_sysmem_flush_page_gb100(bar: Bar0<'_>, addr: Bounded<u64, 52>) {
// Write HI first. The hardware will trigger the flush on the LO write.
// Primary HSHUB pair.
- bar.write(
- regs::NV_PFB_HSHUB_PCIE_FLUSH_SYSMEM_ADDR_HI::of::<Gb100>(),
- regs::NV_PFB_HSHUB_PCIE_FLUSH_SYSMEM_ADDR_HI::zeroed().with_adr(addr_hi),
- );
- bar.write(
- regs::NV_PFB_HSHUB_PCIE_FLUSH_SYSMEM_ADDR_LO::of::<Gb100>(),
- regs::NV_PFB_HSHUB_PCIE_FLUSH_SYSMEM_ADDR_LO::zeroed().with_adr(addr_lo),
- );
+ Gb100
+ .hshub0(bar)
+ .write_reg(regs::NV_PFB_HSHUB_PCIE_FLUSH_SYSMEM_ADDR_HI::zeroed().with_adr(addr_hi));
+ Gb100
+ .hshub0(bar)
+ .write_reg(regs::NV_PFB_HSHUB_PCIE_FLUSH_SYSMEM_ADDR_LO::zeroed().with_adr(addr_lo));
// EG (egress) pair -- must match the primary pair.
- bar.write(
- regs::NV_PFB_HSHUB_EG_PCIE_FLUSH_SYSMEM_ADDR_HI::of::<Gb100>(),
- regs::NV_PFB_HSHUB_EG_PCIE_FLUSH_SYSMEM_ADDR_HI::zeroed().with_adr(addr_hi),
- );
- bar.write(
- regs::NV_PFB_HSHUB_EG_PCIE_FLUSH_SYSMEM_ADDR_LO::of::<Gb100>(),
- regs::NV_PFB_HSHUB_EG_PCIE_FLUSH_SYSMEM_ADDR_LO::zeroed().with_adr(addr_lo),
- );
+ Gb100
+ .hshub0(bar)
+ .write_reg(regs::NV_PFB_HSHUB_EG_PCIE_FLUSH_SYSMEM_ADDR_HI::zeroed().with_adr(addr_hi));
+ Gb100
+ .hshub0(bar)
+ .write_reg(regs::NV_PFB_HSHUB_EG_PCIE_FLUSH_SYSMEM_ADDR_LO::zeroed().with_adr(addr_lo));
}
pub(super) const fn pmu_reserved_size_gb100() -> u32 {
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index 7f7cb84b21e8..6bc8601f2176 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -7,7 +7,10 @@
Io, //
},
prelude::*,
- sizes::SizeConstants,
+ sizes::{
+ SizeConstants,
+ SZ_4K, //
+ },
time, //
};
@@ -156,28 +159,30 @@ fn fmt(&self, f: &mut kernel::fmt::Formatter<'_>) -> kernel::fmt::Result {
/// Base of the GB10x HSHUB0 register window (`NV_HSHUB0_PRIV_BASE` in Open RM).
///
/// The base is provided by the GB10x framebuffer HAL.
-pub(crate) struct Hshub0Base(());
+#[repr(align(4))]
+#[derive(FromBytes, IntoBytes)]
+pub(crate) struct Hshub0Registers([u8; SZ_4K]);
register! {
- base: NovaRegisters;
+ base: Hshub0Registers;
// GB10x sysmem flush registers, relative to the HSHUB0 base. GB10x routes sysmembar
// through a primary and an EG (egress) pair that must both be programmed to the same
// address. Hardware ignores bits 7:0 of each LO register. The boot path uses a fixed
// HSHUB0 base, so the multiple runtime-discovered HSHUB bases are not needed here.
- pub(crate) NV_PFB_HSHUB_PCIE_FLUSH_SYSMEM_ADDR_LO(u32) @ Hshub0Base + 0x00000e50 {
+ pub(crate) NV_PFB_HSHUB_PCIE_FLUSH_SYSMEM_ADDR_LO(u32) @ 0x00000e50 {
31:0 adr => u32;
}
- pub(crate) NV_PFB_HSHUB_PCIE_FLUSH_SYSMEM_ADDR_HI(u32) @ Hshub0Base + 0x00000e54 {
+ pub(crate) NV_PFB_HSHUB_PCIE_FLUSH_SYSMEM_ADDR_HI(u32) @ 0x00000e54 {
19:0 adr;
}
- pub(crate) NV_PFB_HSHUB_EG_PCIE_FLUSH_SYSMEM_ADDR_LO(u32) @ Hshub0Base + 0x000006c0 {
+ pub(crate) NV_PFB_HSHUB_EG_PCIE_FLUSH_SYSMEM_ADDR_LO(u32) @ 0x000006c0 {
31:0 adr => u32;
}
- pub(crate) NV_PFB_HSHUB_EG_PCIE_FLUSH_SYSMEM_ADDR_HI(u32) @ Hshub0Base + 0x000006c4 {
+ pub(crate) NV_PFB_HSHUB_EG_PCIE_FLUSH_SYSMEM_ADDR_HI(u32) @ 0x000006c4 {
19:0 adr;
}
}
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 10/10] rust: io: register: remove relative registers
2026-07-21 16:54 [PATCH 00/10] rust: io: make register bases typed and remove relative registers Gary Guo
` (8 preceding siblings ...)
2026-07-21 16:54 ` [PATCH 09/10] gpu: nova-core: convert hshub0 from relative register to projection Gary Guo
@ 2026-07-21 16:54 ` Gary Guo
2026-07-21 17:08 ` sashiko-bot
9 siblings, 1 reply; 21+ messages in thread
From: Gary Guo @ 2026-07-21 16:54 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, Daniel Almeida, Miguel Ojeda,
Boqun Feng, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Trevor Gross, Tamir Duberstein, Alexandre Courbot,
Onur Özkan, David Airlie, Simona Vetter, Bjorn Helgaas,
Krzysztof Wilczyński
Cc: driver-core, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
linux-pci, Gary Guo
Relative registers can be better served by projection to subregion instead
of ad-hoc handling in register macro. Projection composes better (e.g. it
natively allows relative registers of relative registers without needing
additional support).
Signed-off-by: Gary Guo <gary@garyguo.net>
---
rust/kernel/io/register.rs | 469 +--------------------------------------------
1 file changed, 3 insertions(+), 466 deletions(-)
diff --git a/rust/kernel/io/register.rs b/rust/kernel/io/register.rs
index df47c28ba4f4..88ead1c1874f 100644
--- a/rust/kernel/io/register.rs
+++ b/rust/kernel/io/register.rs
@@ -191,78 +191,6 @@ fn offset(self) -> usize {
}
}
-/// 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<SuperBase: ?Sized, T, B> IoLoc<SuperBase, T> for RelativeRegisterLoc<T, B>
-where
- T: RelativeRegister<Base = SuperBase>,
- 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.
@@ -327,73 +255,6 @@ fn try_at(idx: usize) -> Option<RegisterArrayLoc<Self>>
}
}
-/// 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<SuperBase: ?Sized, T, B> IoLoc<SuperBase, T> for RelativeRegisterArrayLoc<T, B>
-where
- T: RelativeRegisterArray<Base = SuperBase>,
- 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.
///
@@ -428,8 +289,7 @@ fn into_io_op(self) -> (FixedRegisterLoc<T>, T) {
/// 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.
+/// Registers can either be fixed offset registers or arrays of registers.
///
/// ## Fixed offset registers
///
@@ -514,122 +374,6 @@ fn into_io_op(self) -> (FixedRegisterLoc<T>, T) {
/// 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,
-/// Region,
-/// },
-/// };
-/// # 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! {
-/// base: Region<0x1000>;
-///
-/// /// CPU core control.
-/// pub CPU_CTL(u32) @ CpuCtlBase + 0x10 {
-/// 0:0 start;
-/// }
-/// }
-///
-/// # fn test(io: Mmio<'_, Region<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! {
-/// base: Region<0x1000>;
-///
-/// /// 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<'_, Region<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
@@ -725,118 +469,6 @@ fn into_io_op(self) -> (FixedRegisterLoc<T>, T) {
/// # 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,
-/// Region,
-/// },
-/// };
-/// # 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! {
-/// base: Region<0x1000>;
-///
-/// /// Per-CPU scratch registers.
-/// pub CPU_SCRATCH(u32)[64] @ CpuCtlBase + 0x00000080 {
-/// 31:0 value;
-/// }
-/// }
-///
-/// # fn test(io: Mmio<'_, Region<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! {
-/// base: Region<0x1000>;
-///
-/// /// 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! {
-/// base: Region<0x1000>;
-///
-/// /// 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<'_, Region<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.
@@ -847,7 +479,7 @@ macro_rules! register {
$(
$(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
$([ $size:expr $(, stride = $stride:expr)? ])?
- $(@ $($base:ident +)? $offset:literal)?
+ $(@ $offset:literal)?
$(=> $alias:ident $(+ $alias_offset:ident)? $([$alias_idx:expr])? )?
{ $($fields:tt)* }
)*
@@ -855,7 +487,7 @@ macro_rules! register {
$(
$crate::register!(
@reg [$reg_base] $(#[$attr])* $vis $name ($storage) $([$size $(, stride = $stride)?])?
- $(@ $($base +)? $offset)?
+ $(@ $offset)?
$(=> $alias $(+ $alias_offset)? $([$alias_idx])? )?
{ $($fields)* }
);
@@ -887,30 +519,6 @@ macro_rules! register {
$crate::register!(@io_fixed $(#[$attr])* $vis $name($storage));
};
- // Creates a register at a relative offset from a base address provider.
- (
- @reg [$reg_base:ty]
- $(#[$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 [$reg_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 [$reg_base:ty]
- $(#[$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 [$reg_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 [$reg_base:ty] $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
@@ -954,51 +562,6 @@ macro_rules! register {
$crate::register!(@io_fixed $(#[$attr])* $vis $name($storage));
};
- // Creates an array of registers at a relative offset from a base address provider.
- (
- @reg [$reg_base:ty] $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
- [ $size:expr, stride = $stride:expr ]
- @ $base:ident + $offset:literal { $($fields:tt)* }
- ) => {
- $crate::build_assert::static_assert!(::core::mem::size_of::<$storage>() <= $stride);
-
- $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
- $crate::register!(@io_base [$reg_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 [$reg_base:ty] $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) [ $size:expr ]
- @ $base:ident + $offset:literal { $($fields:tt)* }
- ) => {
- $crate::register!(@reg [$reg_base]
- $(#[$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 [$reg_base:ty] $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
- => $base:ident + $alias:ident [ $idx:expr ] { $($fields:tt)* }
- ) => {
- $crate::build_assert::static_assert!(
- $idx < <$alias as $crate::io::register::RegisterArray>::SIZE
- );
-
- $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
- $crate::register!(
- @io_base [$reg_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
@@ -1031,15 +594,6 @@ impl $crate::io::register::FixedRegister for $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 {}
@@ -1049,21 +603,4 @@ impl $crate::io::register::RegisterArray for $name {
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 {}
- };
}
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH 01/10] rust: io: register: allow explicit base type specification
2026-07-21 16:54 ` [PATCH 01/10] rust: io: register: allow explicit base type specification Gary Guo
@ 2026-07-21 16:58 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-07-21 16:58 UTC (permalink / raw)
To: Gary Guo; +Cc: linux-pci, Danilo Krummrich, dri-devel, Alexandre Courbot, ojeda
> 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@garyguo.net>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260721-typed_register-v1-0-452d72b60262@garyguo.net?part=1
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 02/10] gpu: nova-core: specify base type for registers
2026-07-21 16:54 ` [PATCH 02/10] gpu: nova-core: specify base type for registers Gary Guo
@ 2026-07-21 16:59 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-07-21 16:59 UTC (permalink / raw)
To: Gary Guo; +Cc: Alexandre Courbot, Danilo Krummrich, dri-devel, linux-pci, ojeda
> All registers use the same base type, which is `<Bar0 as IO>::Target`. Thus
> add the base parameter to `register!` invocation.
>
> Signed-off-by: Gary Guo <gary@garyguo.net>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260721-typed_register-v1-0-452d72b60262@garyguo.net?part=2
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 08/10] gpu: nova-core: use projection for PFALCON and PFALCON2 registers
2026-07-21 16:54 ` [PATCH 08/10] gpu: nova-core: use projection for PFALCON and PFALCON2 registers Gary Guo
@ 2026-07-21 17:00 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-07-21 17:00 UTC (permalink / raw)
To: Gary Guo; +Cc: dri-devel, Danilo Krummrich, Alexandre Courbot, ojeda, linux-pci
> Add fixed size region types for these and add projection methods that
> project from `Bar0` into these. Update these registers to be registers on
> `PFalconRegisters` and `PFalcon2Registers` and not relative registers on
> `NovaRegisters`.
>
> The use sites are updated mechanically; calls to the projection methods are
> not extracted in this commit.
>
> Signed-off-by: Gary Guo <gary@garyguo.net>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260721-typed_register-v1-0-452d72b60262@garyguo.net?part=8
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 05/10] rust: io: register: make register have a typed base
2026-07-21 16:54 ` [PATCH 05/10] rust: io: register: make register have a typed base Gary Guo
@ 2026-07-21 17:00 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-07-21 17:00 UTC (permalink / raw)
To: Gary Guo; +Cc: Danilo Krummrich, dri-devel, linux-pci, Alexandre Courbot, ojeda
> Previously `register!` defined registers can be used on any untyped I/O
> regions. With all users specifying their desired register type now,
> propagate the specified type and restrict I/O access only when type
> matches.
>
> Signed-off-by: Gary Guo <gary@garyguo.net>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260721-typed_register-v1-0-452d72b60262@garyguo.net?part=5
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 04/10] samples: rust: pci: specify base type for registers
2026-07-21 16:54 ` [PATCH 04/10] samples: rust: pci: " Gary Guo
@ 2026-07-21 17:03 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-07-21 17:03 UTC (permalink / raw)
To: Gary Guo; +Cc: linux-pci, dri-devel, ojeda, Danilo Krummrich, Alexandre Courbot
> The `register!` macro is going to require explicit base type, specify it
> for both `register!` usages in PCI sample driver.
>
> Signed-off-by: Gary Guo <gary@garyguo.net>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260721-typed_register-v1-0-452d72b60262@garyguo.net?part=4
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 07/10] rust: io: add subregion method with compile-time check
2026-07-21 16:54 ` [PATCH 07/10] rust: io: add subregion method with compile-time check Gary Guo
@ 2026-07-21 17:05 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-07-21 17:05 UTC (permalink / raw)
To: Gary Guo; +Cc: linux-pci, Danilo Krummrich, dri-devel, ojeda, Alexandre Courbot
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The `subregion` method incorrectly initializes the new subregion's runtime size using `ptr.len() - OFFSET` instead of `NEW_SIZE`, defeating encapsulation and enabling out-of-bounds MMIO accesses.
--
--- Patch [7]: [PATCH 07/10] rust: io: add subregion method with compile-time check ---
commit 3d97a4ac48567402f60659310de26681110a1c61
Author: Gary Guo <gary@garyguo.net>
rust: io: add subregion method with compile-time check
This commit adds a compile-time checked subregion helper function to Region.
This allows drivers to obtain a safely constrained subregion for relative
register access.
> diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
> index 0f8b166a6d3bb..f31d37c8a3d58 100644
> --- a/rust/kernel/io.rs
> +++ b/rust/kernel/io.rs
[ ... ]
> + /// Create a subregion with provided offset and size.
> + #[inline]
> + pub fn subregion<'a, const OFFSET: usize, const NEW_SIZE: usize, IO>(
> + io: IO,
> + ) -> <IO::Backend as IoBackend>::View<'a, Region<NEW_SIZE>>
> + where
> + IO: IoBase<'a, Target = Self>,
> + {
> + const_assert!(
> + OFFSET + NEW_SIZE <= SIZE && OFFSET.is_multiple_of(4) && NEW_SIZE.is_multiple_of(4)
> + );
> +
> + let view = io.as_view();
> + let ptr = IO::Backend::as_ptr(view) as *mut [u8];
> + let new_ptr = Region::ptr_from_raw_parts_mut(
> + ptr.cast::<u8>().wrapping_add(OFFSET),
> + ptr.len() - OFFSET,
[Severity: High]
Does this improperly initialize the new subregion's runtime size using
ptr.len() - OFFSET instead of NEW_SIZE?
If a parent driver creates a localized subregion specifying a NEW_SIZE and
delegates it to a sub-device driver, the sub-device could mistakenly execute
fallible MMIO operations beyond the intended NEW_SIZE.
Because the runtime dynamic bounds checking will evaluate against
ptr.len() - OFFSET instead of the constrained size, out-of-bounds accesses
would improperly succeed and could corrupt adjacent registers.
Should this be updated to pass NEW_SIZE to ptr_from_raw_parts_mut?
> + );
> +
> + // SAFETY: We have checked that the new region is a subregion and it is aligned, hence it is
> + // a valid projection.
> + unsafe { IO::Backend::project_view(view, new_ptr) }
> + }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260721-typed_register-v1-0-452d72b60262@garyguo.net?part=7
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 10/10] rust: io: register: remove relative registers
2026-07-21 16:54 ` [PATCH 10/10] rust: io: register: remove relative registers Gary Guo
@ 2026-07-21 17:08 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-07-21 17:08 UTC (permalink / raw)
To: Gary Guo; +Cc: dri-devel, Alexandre Courbot, ojeda, linux-pci, Danilo Krummrich
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Low] Broken intra-doc link for `WithBase` in module documentation.
- [Low] Leftover macro syntax for relative register aliases (`+ $alias_offset`) causes confusing internal macro errors.
--
--- Patch [10]: [PATCH 10/10] rust: io: register: remove relative registers ---
commit 9b1276cccc7f1b5ecb5c4e81e0c27d5b7d2f2492
Author: Gary Guo <gary@garyguo.net>
rust: io: register: remove relative registers
Relative registers can be better served by projection to subregion instead
of ad-hoc handling in register macro. Projection composes better (e.g. it
natively allows relative registers of relative registers without needing
additional support).
> diff --git a/rust/kernel/io/register.rs b/rust/kernel/io/register.rs
> index df47c28ba4f40..88ead1c1874fb 100644
> --- a/rust/kernel/io/register.rs
> +++ b/rust/kernel/io/register.rs
> @@ -191,78 +191,6 @@ fn offset(self) -> usize {
> }
> }
>
> -/// Trait providing a base address to be added to the offset of a relative register to obtain
> -/// its actual offset.
[ ... ]
> -/// Trait implemented by all registers that are relative to a base.
> -pub trait WithBase {
[Severity: Low]
With WithBase removed here, does the register.rs module documentation need
to be updated? The module doc still references it in its intra-doc links:
//! items you might want to import from this module are [`WithBase`] and [`Array`].
Since kernel rustdoc compilation is strict about broken intra-doc links, could
this cause the rustdoc build to fail?
[ ... ]
> @@ -847,7 +479,7 @@ macro_rules! register {
> $(
> $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
> $([ $size:expr $(, stride = $stride:expr)? ])?
> - $(@ $($base:ident +)? $offset:literal)?
> + $(@ $offset:literal)?
> $(=> $alias:ident $(+ $alias_offset:ident)? $([$alias_idx:expr])? )?
> { $($fields:tt)* }
> )*
> @@ -855,7 +487,7 @@ macro_rules! register {
> $(
> $crate::register!(
> @reg [$reg_base] $(#[$attr])* $vis $name ($storage) $([$size $(, stride = $stride)?])?
> - $(@ $($base +)? $offset)?
> + $(@ $offset)?
> $(=> $alias $(+ $alias_offset)? $([$alias_idx])? )?
[Severity: Low]
Does the macro entry point still need to parse $(+ $alias_offset:ident)? and
forward $(+ $alias_offset)? now that the internal @reg macro arms handling
relative alias syntax have been removed?
If someone accidentally defines a register using the removed relative alias
syntax (e.g., => Base + Alias), could this cause a confusing internal macro
matching error instead of a clean rejection?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260721-typed_register-v1-0-452d72b60262@garyguo.net?part=10
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 06/10] rust: io: add static `cast()` method for views
2026-07-21 16:54 ` [PATCH 06/10] rust: io: add static `cast()` method for views Gary Guo
@ 2026-07-21 17:08 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-07-21 17:08 UTC (permalink / raw)
To: Gary Guo; +Cc: Danilo Krummrich, ojeda, linux-pci, Alexandre Courbot, dri-devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [Low] The rustdoc example for `cast<U>` contains a visible code snippet that will fail to compile if uncommented and used, because it uses an unsized `Region`.
Pre-existing issues:
- [High] Clock prepare/enable state is leaked on error paths during probe.
--
--- Patch [6]: [PATCH 06/10] rust: io: add static `cast()` method for views ---
commit bc8b9f11b619bcfae6c2907cbb69af497e185e8b
Author: Gary Guo <gary@garyguo.net>
rust: io: add static cast() method for views
Add a compile-time checked variant of try_cast() using the minimum size
and alignment information.
Signed-off-by: Gary Guo <gary@garyguo.net>
> diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
> index c5f07c38e59e5..0f8b166a6d3bb 100644
> --- a/rust/kernel/io.rs
> +++ b/rust/kernel/io.rs
[ ... ]
> + /// # Examples
> + ///
> + /// ```no_run
> + /// use kernel::io::{
> + /// io_project,
> + /// Mmio,
> + /// Io,
> + /// Region,
> + /// };
> + /// #[derive(FromBytes, IntoBytes)]
> + /// #[repr(C)]
> + /// struct MyStruct { field: u32, }
> + ///
> + /// # fn test(mmio: &Mmio<'_, Region<0x1000>>) {
> + /// // let mmio: Mmio<'_, Region>;
[Severity: Low]
Does this rustdoc example compile successfully? Using an unsized Region here
might result in a compile-time assertion failure if a developer uncomments
and tries to use it.
> + /// let whole: Mmio<'_, MyStruct> = mmio.cast();
> + /// # }
> + /// ```
> + #[inline]
> + fn cast<U>(self) -> <Self::Backend as IoBackend>::View<'a, U>
[ ... ]
I also noticed a pre-existing issue in drivers/gpu/drm/tyr/driver.rs:
drivers/gpu/drm/tyr/driver.rs:TyrPlatformDriver::probe() {
[ ... ]
core_clk.prepare_enable()?;
stacks_clk.prepare_enable()?;
coregroup_clk.prepare_enable()?;
let mali_regulator = Regulator::<regulator::Enabled>::get(pdev.as_ref(), c"mali")?;
let sram_regulator = Regulator::<regulator::Enabled>::get(pdev.as_ref(), c"sram")?;
[ ... ]
}
[Severity: High]
This is a pre-existing issue, but does this leak the clock prepare/enable
state on error paths?
If operations like Regulator::get() fail and return an error like
-EPROBE_DEFER, the function returns early. The Clk instances are dropped,
but it doesn't seem to unprepare or disable the clocks before they are
wrapped in the Clocks struct later in the function.
Could this permanently lock the hardware clocks in the enabled state?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260721-typed_register-v1-0-452d72b60262@garyguo.net?part=6
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 03/10] drm/tyr: specify base type for registers
2026-07-21 16:54 ` [PATCH 03/10] drm/tyr: " Gary Guo
@ 2026-07-21 17:10 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-07-21 17:10 UTC (permalink / raw)
To: Gary Guo; +Cc: linux-pci, dri-devel, ojeda, Danilo Krummrich, Alexandre Courbot
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The `DOORBELL` register array exceeds the 2MB size of `TyrRegisters`, which will cause bounds-checking failures during access.
--
--- Patch [3]: [PATCH 03/10] drm/tyr: specify base type for registers ---
commit 0835e2ca0e8718a373eb876a88da9c8e609d5b61
Author: Gary Guo <gary@garyguo.net>
drm/tyr: specify base type for registers
This commit adds a base parameter to register definitions in the tyr drm
driver, associating them with a new TyrRegisters type bounded to a 2MB size.
> diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs
> index 8348c6cd39295..401a4b321eaf8 100644
> --- a/drivers/gpu/drm/tyr/driver.rs
> +++ b/drivers/gpu/drm/tyr/driver.rs
[ ... ]
> @@ -43,6 +43,7 @@
> };
>
> pub(crate) type IoMem<'a> = kernel::io::mem::IoMem<'a, SZ_2M>;
> +pub(crate) type TyrRegisters = kernel::io::Region<SZ_2M>;
>
> pub(crate) struct TyrDrmDriver;
>
[ ... ]
> diff --git a/drivers/gpu/drm/tyr/regs.rs b/drivers/gpu/drm/tyr/regs.rs
> index 831357a8ef87d..ef21d90dbbc41 100644
> --- a/drivers/gpu/drm/tyr/regs.rs
> +++ b/drivers/gpu/drm/tyr/regs.rs
[ ... ]
> @@ -1637,6 +1677,8 @@ pub(crate) mod doorbell_block {
> const STRIDE: usize = 0x10000;
>
> register! {
> + base: TyrRegisters;
> +
> /// Doorbell request register. Write-only.
> pub(crate) DOORBELL(u32)[NUM_DOORBELLS, stride = STRIDE] @ 0x80000 {
[Severity: Medium]
Will this result in bounds-checking failures during access?
The DOORBELL register array is defined with an offset of 0x80000, 64 elements,
and a stride of 0x10000. The final element is located at
0x80000 + (63 * 0x10000) = 0x470000, which is approximately 4.6MB.
However, this patch explicitly associates the DOORBELL array with the
TyrRegisters base type, which is constrained to SZ_2M (2MB).
When accessing DOORBELL with an index >= 24 via the TyrRegisters I/O region,
it will cause a compile-time build_assert! failure for static indices or a
failure for dynamic indices because the access is out of bounds.
Could the driver's mapped MMIO size of SZ_2M be undersized, or is the DOORBELL
register improperly modeled here?
> /// Doorbell set. Writing 1 triggers the doorbell.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260721-typed_register-v1-0-452d72b60262@garyguo.net?part=3
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 09/10] gpu: nova-core: convert hshub0 from relative register to projection
2026-07-21 16:54 ` [PATCH 09/10] gpu: nova-core: convert hshub0 from relative register to projection Gary Guo
@ 2026-07-21 17:18 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-07-21 17:18 UTC (permalink / raw)
To: Gary Guo; +Cc: ojeda, Danilo Krummrich, linux-pci, dri-devel, Alexandre Courbot
> Similar to the PFALCON and PFALCON2 conversion, the hshub0 relative access
> can also be achieved cleanly with projection and a new base.
>
> Signed-off-by: Gary Guo <gary@garyguo.net>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260721-typed_register-v1-0-452d72b60262@garyguo.net?part=9
^ permalink raw reply [flat|nested] 21+ messages in thread
end of thread, other threads:[~2026-07-21 17:18 UTC | newest]
Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-21 16:54 [PATCH 00/10] rust: io: make register bases typed and remove relative registers Gary Guo
2026-07-21 16:54 ` [PATCH 01/10] rust: io: register: allow explicit base type specification Gary Guo
2026-07-21 16:58 ` sashiko-bot
2026-07-21 16:54 ` [PATCH 02/10] gpu: nova-core: specify base type for registers Gary Guo
2026-07-21 16:59 ` sashiko-bot
2026-07-21 16:54 ` [PATCH 03/10] drm/tyr: " Gary Guo
2026-07-21 17:10 ` sashiko-bot
2026-07-21 16:54 ` [PATCH 04/10] samples: rust: pci: " Gary Guo
2026-07-21 17:03 ` sashiko-bot
2026-07-21 16:54 ` [PATCH 05/10] rust: io: register: make register have a typed base Gary Guo
2026-07-21 17:00 ` sashiko-bot
2026-07-21 16:54 ` [PATCH 06/10] rust: io: add static `cast()` method for views Gary Guo
2026-07-21 17:08 ` sashiko-bot
2026-07-21 16:54 ` [PATCH 07/10] rust: io: add subregion method with compile-time check Gary Guo
2026-07-21 17:05 ` sashiko-bot
2026-07-21 16:54 ` [PATCH 08/10] gpu: nova-core: use projection for PFALCON and PFALCON2 registers Gary Guo
2026-07-21 17:00 ` sashiko-bot
2026-07-21 16:54 ` [PATCH 09/10] gpu: nova-core: convert hshub0 from relative register to projection Gary Guo
2026-07-21 17:18 ` sashiko-bot
2026-07-21 16:54 ` [PATCH 10/10] rust: io: register: remove relative registers Gary Guo
2026-07-21 17:08 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox