From: Gary Guo <gary@garyguo.net>
To: "Greg Kroah-Hartman" <gregkh@linuxfoundation.org>,
"Rafael J. Wysocki" <rafael@kernel.org>,
"Danilo Krummrich" <dakr@kernel.org>,
"Miguel Ojeda" <ojeda@kernel.org>,
"Boqun Feng" <boqun@kernel.org>, "Gary Guo" <gary@garyguo.net>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
"Benno Lossin" <lossin@kernel.org>,
"Andreas Hindborg" <a.hindborg@kernel.org>,
"Alice Ryhl" <aliceryhl@google.com>,
"Trevor Gross" <tmgross@umich.edu>,
"Daniel Almeida" <daniel.almeida@collabora.com>,
"Bjorn Helgaas" <bhelgaas@google.com>,
"Krzysztof Wilczyński" <kwilczynski@kernel.org>,
"Abdiel Janulgue" <abdiel.janulgue@gmail.com>,
"Robin Murphy" <robin.murphy@arm.com>,
"Alexandre Courbot" <acourbot@nvidia.com>,
"David Airlie" <airlied@gmail.com>,
"Simona Vetter" <simona@ffwll.ch>
Cc: driver-core@lists.linux.dev, rust-for-linux@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org,
nouveau@lists.freedesktop.org, dri-devel@lists.freedesktop.org
Subject: [PATCH v2 05/11] rust: io: restrict untyped IO access and `register!` to `Region`
Date: Tue, 21 Apr 2026 15:56:16 +0100 [thread overview]
Message-ID: <20260421-io_projection-v2-5-4c251c692ef4@garyguo.net> (raw)
In-Reply-To: <20260421-io_projection-v2-0-4c251c692ef4@garyguo.net>
Currently the `Io` trait exposes a bunch of untyped IO accesses, but if the
`Io` region itself is typed, then it might be weird to have
let io: Mmio<u32> = /* ... */;
io.read8(1);
while not unsound, it is surely strange. Thus, restrict the untyped methods
and also the register macro to `Region` type only.
The way it is implemented is by adding a generic type to `IoLoc`. This also
paves the way to add typed register blocks in the future; for example, we
could use this mechanism to block driver A's `register!()` generated macro
from being used on driver B's MMIO. The same mechanism could be used for
relative IO registers. These are future opoortunities, and for this patch I
just restricted everything to require `IoLoc<Region<SIZE>, _>`.
Suggested-by: Alexandre Courbot <acourbot@nvidia.com>
Link: https://lore.kernel.org/rust-for-linux/DHLB3RO3OSF5.2R7F27U99BKLN@nvidia.com/
Signed-off-by: Gary Guo <gary@garyguo.net>
---
rust/kernel/io.rs | 47 +++++++++++++++++++++++++++++++---------------
rust/kernel/io/register.rs | 21 ++++++++++++---------
2 files changed, 44 insertions(+), 24 deletions(-)
diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
index c6d30c5b4e10..a13be8c5fd2d 100644
--- a/rust/kernel/io.rs
+++ b/rust/kernel/io.rs
@@ -238,15 +238,16 @@ pub trait IoCapable<T> {
/// (for primitive types like [`u32`]) and typed ones (like those generated by the [`register!`]
/// macro).
///
-/// An `IoLoc<T>` carries three pieces of information:
+/// An `IoLoc<Base, T>` carries the following pieces of information:
///
+/// - The valid `Base` to operate on. For most registers, this should be [`Region`].
/// - The offset to access (returned by [`IoLoc::offset`]),
/// - The width of the access (determined by [`IoLoc::IoType`]),
/// - The type `T` in which the raw data is returned or provided.
///
/// `T` and `IoLoc::IoType` may differ: for instance, a typed register has `T` = the register type
/// with its bitfields, and `IoType` = its backing primitive (e.g. `u32`).
-pub trait IoLoc<T> {
+pub trait IoLoc<Base: ?Sized, T> {
/// Size ([`u8`], [`u16`], etc) of the I/O performed on the returned [`offset`](IoLoc::offset).
type IoType: Into<T> + From<T>;
@@ -254,12 +255,12 @@ pub trait IoLoc<T> {
fn offset(self) -> usize;
}
-/// Implements [`IoLoc<$ty>`] for [`usize`], allowing [`usize`] to be used as a parameter of
-/// [`Io::read`] and [`Io::write`].
+/// Implements [`IoLoc<Region<SIZE>, $ty>`] for [`usize`], allowing [`usize`] to be used as a
+/// parameter of [`Io::read`] and [`Io::write`].
macro_rules! impl_usize_ioloc {
($($ty:ty),*) => {
$(
- impl IoLoc<$ty> for usize {
+ impl<const SIZE: usize> IoLoc<Region<SIZE>, $ty> for usize {
type IoType = $ty;
#[inline(always)]
@@ -328,6 +329,7 @@ fn io_addr<U>(&self, offset: usize) -> Result<*mut U> {
#[inline(always)]
fn try_read8(&self, offset: usize) -> Result<u8>
where
+ usize: IoLoc<Self::Type, u8, IoType = u8>,
Self: IoCapable<u8>,
{
self.try_read(offset)
@@ -337,6 +339,7 @@ fn try_read8(&self, offset: usize) -> Result<u8>
#[inline(always)]
fn try_read16(&self, offset: usize) -> Result<u16>
where
+ usize: IoLoc<Self::Type, u16, IoType = u16>,
Self: IoCapable<u16>,
{
self.try_read(offset)
@@ -346,6 +349,7 @@ fn try_read16(&self, offset: usize) -> Result<u16>
#[inline(always)]
fn try_read32(&self, offset: usize) -> Result<u32>
where
+ usize: IoLoc<Self::Type, u32, IoType = u32>,
Self: IoCapable<u32>,
{
self.try_read(offset)
@@ -355,6 +359,7 @@ fn try_read32(&self, offset: usize) -> Result<u32>
#[inline(always)]
fn try_read64(&self, offset: usize) -> Result<u64>
where
+ usize: IoLoc<Self::Type, u64, IoType = u64>,
Self: IoCapable<u64>,
{
self.try_read(offset)
@@ -364,6 +369,7 @@ fn try_read64(&self, offset: usize) -> Result<u64>
#[inline(always)]
fn try_write8(&self, value: u8, offset: usize) -> Result
where
+ usize: IoLoc<Self::Type, u8, IoType = u8>,
Self: IoCapable<u8>,
{
self.try_write(offset, value)
@@ -373,6 +379,7 @@ fn try_write8(&self, value: u8, offset: usize) -> Result
#[inline(always)]
fn try_write16(&self, value: u16, offset: usize) -> Result
where
+ usize: IoLoc<Self::Type, u16, IoType = u16>,
Self: IoCapable<u16>,
{
self.try_write(offset, value)
@@ -382,6 +389,7 @@ fn try_write16(&self, value: u16, offset: usize) -> Result
#[inline(always)]
fn try_write32(&self, value: u32, offset: usize) -> Result
where
+ usize: IoLoc<Self::Type, u32, IoType = u32>,
Self: IoCapable<u32>,
{
self.try_write(offset, value)
@@ -391,6 +399,7 @@ fn try_write32(&self, value: u32, offset: usize) -> Result
#[inline(always)]
fn try_write64(&self, value: u64, offset: usize) -> Result
where
+ usize: IoLoc<Self::Type, u64, IoType = u64>,
Self: IoCapable<u64>,
{
self.try_write(offset, value)
@@ -400,6 +409,7 @@ fn try_write64(&self, value: u64, offset: usize) -> Result
#[inline(always)]
fn read8(&self, offset: usize) -> u8
where
+ usize: IoLoc<Self::Type, u8, IoType = u8>,
Self: IoCapable<u8>,
{
self.read(offset)
@@ -409,6 +419,7 @@ fn read8(&self, offset: usize) -> u8
#[inline(always)]
fn read16(&self, offset: usize) -> u16
where
+ usize: IoLoc<Self::Type, u16, IoType = u16>,
Self: IoCapable<u16>,
{
self.read(offset)
@@ -418,6 +429,7 @@ fn read16(&self, offset: usize) -> u16
#[inline(always)]
fn read32(&self, offset: usize) -> u32
where
+ usize: IoLoc<Self::Type, u32, IoType = u32>,
Self: IoCapable<u32>,
{
self.read(offset)
@@ -427,6 +439,7 @@ fn read32(&self, offset: usize) -> u32
#[inline(always)]
fn read64(&self, offset: usize) -> u64
where
+ usize: IoLoc<Self::Type, u64, IoType = u64>,
Self: IoCapable<u64>,
{
self.read(offset)
@@ -436,6 +449,7 @@ fn read64(&self, offset: usize) -> u64
#[inline(always)]
fn write8(&self, value: u8, offset: usize)
where
+ usize: IoLoc<Self::Type, u8, IoType = u8>,
Self: IoCapable<u8>,
{
self.write(offset, value)
@@ -445,6 +459,7 @@ fn write8(&self, value: u8, offset: usize)
#[inline(always)]
fn write16(&self, value: u16, offset: usize)
where
+ usize: IoLoc<Self::Type, u16, IoType = u16>,
Self: IoCapable<u16>,
{
self.write(offset, value)
@@ -454,6 +469,7 @@ fn write16(&self, value: u16, offset: usize)
#[inline(always)]
fn write32(&self, value: u32, offset: usize)
where
+ usize: IoLoc<Self::Type, u32, IoType = u32>,
Self: IoCapable<u32>,
{
self.write(offset, value)
@@ -463,6 +479,7 @@ fn write32(&self, value: u32, offset: usize)
#[inline(always)]
fn write64(&self, value: u64, offset: usize)
where
+ usize: IoLoc<Self::Type, u64, IoType = u64>,
Self: IoCapable<u64>,
{
self.write(offset, value)
@@ -494,7 +511,7 @@ fn write64(&self, value: u64, offset: usize)
#[inline(always)]
fn try_read<T, L>(&self, location: L) -> Result<T>
where
- L: IoLoc<T>,
+ L: IoLoc<Self::Type, T>,
Self: IoCapable<L::IoType>,
{
let address = self.io_addr::<L::IoType>(location.offset())?;
@@ -529,7 +546,7 @@ fn try_read<T, L>(&self, location: L) -> Result<T>
#[inline(always)]
fn try_write<T, L>(&self, location: L, value: T) -> Result
where
- L: IoLoc<T>,
+ L: IoLoc<Self::Type, T>,
Self: IoCapable<L::IoType>,
{
let address = self.io_addr::<L::IoType>(location.offset())?;
@@ -576,8 +593,8 @@ fn try_write<T, L>(&self, location: L, value: T) -> Result
#[inline(always)]
fn try_write_reg<T, L, V>(&self, value: V) -> Result
where
- L: IoLoc<T>,
- V: LocatedRegister<Location = L, Value = T>,
+ L: IoLoc<Self::Type, T>,
+ V: LocatedRegister<Self::Type, Location = L, Value = T>,
Self: IoCapable<L::IoType>,
{
let (location, value) = value.into_io_op();
@@ -610,7 +627,7 @@ fn try_write_reg<T, L, V>(&self, value: V) -> Result
#[inline(always)]
fn try_update<T, L, F>(&self, location: L, f: F) -> Result
where
- L: IoLoc<T>,
+ L: IoLoc<Self::Type, T>,
Self: IoCapable<L::IoType>,
F: FnOnce(T) -> T,
{
@@ -650,7 +667,7 @@ fn try_update<T, L, F>(&self, location: L, f: F) -> Result
#[inline(always)]
fn read<T, L>(&self, location: L) -> T
where
- L: IoLoc<T>,
+ L: IoLoc<Self::Type, T>,
Self: IoCapable<L::IoType>,
{
let address = self.io_addr_assert::<L::IoType>(location.offset());
@@ -683,7 +700,7 @@ fn read<T, L>(&self, location: L) -> T
#[inline(always)]
fn write<T, L>(&self, location: L, value: T)
where
- L: IoLoc<T>,
+ L: IoLoc<Self::Type, T>,
Self: IoCapable<L::IoType>,
{
let address = self.io_addr_assert::<L::IoType>(location.offset());
@@ -727,8 +744,8 @@ fn write<T, L>(&self, location: L, value: T)
#[inline(always)]
fn write_reg<T, L, V>(&self, value: V)
where
- L: IoLoc<T>,
- V: LocatedRegister<Location = L, Value = T>,
+ L: IoLoc<Self::Type, T>,
+ V: LocatedRegister<Self::Type, Location = L, Value = T>,
Self: IoCapable<L::IoType>,
{
let (location, value) = value.into_io_op();
@@ -761,7 +778,7 @@ fn write_reg<T, L, V>(&self, value: V)
#[inline(always)]
fn update<T, L, F>(&self, location: L, f: F)
where
- L: IoLoc<T>,
+ L: IoLoc<Self::Type, T>,
Self: IoCapable<L::IoType>,
F: FnOnce(T) -> T,
{
diff --git a/rust/kernel/io/register.rs b/rust/kernel/io/register.rs
index 1a407fc35edc..5a7d44c51477 100644
--- a/rust/kernel/io/register.rs
+++ b/rust/kernel/io/register.rs
@@ -113,6 +113,8 @@
use kernel::build_assert;
+use super::Region;
+
/// Trait implemented by all registers.
pub trait Register: Sized {
/// Backing primitive type of the register.
@@ -129,7 +131,7 @@ pub trait FixedRegister: Register {}
/// Allows `()` to be used as the `location` parameter of [`Io::write`](super::Io::write) when
/// passing a [`FixedRegister`] value.
-impl<T> IoLoc<T> for ()
+impl<const SIZE: usize, T> IoLoc<Region<SIZE>, T> for ()
where
T: FixedRegister,
{
@@ -143,7 +145,7 @@ fn offset(self) -> usize {
/// A [`FixedRegister`] carries its location in its type. Thus `FixedRegister` values can be used
/// as an [`IoLoc`].
-impl<T> IoLoc<T> for T
+impl<const SIZE: usize, T> IoLoc<Region<SIZE>, T> for T
where
T: FixedRegister,
{
@@ -168,7 +170,7 @@ pub const fn new() -> Self {
}
}
-impl<T> IoLoc<T> for FixedRegisterLoc<T>
+impl<const SIZE: usize, T> IoLoc<Region<SIZE>, T> for FixedRegisterLoc<T>
where
T: FixedRegister,
{
@@ -239,7 +241,8 @@ const fn offset(self) -> usize {
}
}
-impl<T, B> IoLoc<T> for RelativeRegisterLoc<T, B>
+// FIXME: Make use of `Base` type parameter of `Region` directly.
+impl<const SIZE: usize, T, B> IoLoc<Region<SIZE>, T> for RelativeRegisterLoc<T, B>
where
T: RelativeRegister,
B: RegisterBase<T::BaseFamily> + ?Sized,
@@ -283,7 +286,7 @@ pub fn try_new(idx: usize) -> Option<Self> {
}
}
-impl<T> IoLoc<T> for RegisterArrayLoc<T>
+impl<const SIZE: usize, T> IoLoc<Region<SIZE>, T> for RegisterArrayLoc<T>
where
T: RegisterArray,
{
@@ -370,7 +373,7 @@ pub fn try_at(self, idx: usize) -> Option<RelativeRegisterArrayLoc<T, B>> {
}
}
-impl<T, B> IoLoc<T> for RelativeRegisterArrayLoc<T, B>
+impl<const SIZE: usize, T, B> IoLoc<Region<SIZE>, T> for RelativeRegisterArrayLoc<T, B>
where
T: RelativeRegisterArray,
B: RegisterBase<T::BaseFamily> + ?Sized,
@@ -387,18 +390,18 @@ fn offset(self) -> usize {
/// which to write it.
///
/// Implementors can be used with [`Io::write_reg`](super::Io::write_reg).
-pub trait LocatedRegister {
+pub trait LocatedRegister<Base: ?Sized> {
/// Register value to write.
type Value: Register;
/// Full location information at which to write the value.
- type Location: IoLoc<Self::Value>;
+ type Location: IoLoc<Base, Self::Value>;
/// Consumes `self` and returns a `(location, value)` tuple describing a valid I/O write
/// operation.
fn into_io_op(self) -> (Self::Location, Self::Value);
}
-impl<T> LocatedRegister for T
+impl<const SIZE: usize, T> LocatedRegister<Region<SIZE>> for T
where
T: FixedRegister,
{
--
2.51.2
next prev parent reply other threads:[~2026-04-21 14:56 UTC|newest]
Thread overview: 32+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-21 14:56 [PATCH v2 00/11] rust: I/O type generalization and projection Gary Guo
2026-04-21 14:56 ` [PATCH v2 01/11] rust: io: generalize `MmioRaw` to pointer to arbitrary type Gary Guo
2026-04-27 13:44 ` Andreas Hindborg
2026-04-21 14:56 ` [PATCH v2 02/11] rust: io: generalize `Mmio` " Gary Guo
2026-04-27 14:10 ` Andreas Hindborg
2026-04-21 14:56 ` [PATCH v2 03/11] rust: io: use pointer types instead of address Gary Guo
2026-04-27 14:20 ` Andreas Hindborg
2026-04-27 15:27 ` Gary Guo
2026-04-28 7:12 ` Andreas Hindborg
2026-04-21 14:56 ` [PATCH v2 04/11] rust: io: add missing safety requirement in `IoCapable` methods Gary Guo
2026-04-28 7:16 ` Andreas Hindborg
2026-04-21 14:56 ` Gary Guo [this message]
2026-04-28 9:02 ` [PATCH v2 05/11] rust: io: restrict untyped IO access and `register!` to `Region` Andreas Hindborg
2026-04-28 11:14 ` Gary Guo
2026-04-28 12:08 ` Andreas Hindborg
2026-04-28 12:55 ` Gary Guo
2026-04-28 14:41 ` Andreas Hindborg
2026-04-28 14:54 ` Danilo Krummrich
2026-04-29 8:04 ` Andreas Hindborg
2026-04-21 14:56 ` [PATCH v2 06/11] rust: io: add view type Gary Guo
2026-04-28 10:53 ` Andreas Hindborg
2026-04-28 11:20 ` Gary Guo
2026-04-21 14:56 ` [PATCH v2 07/11] rust: dma: add methods to unsafely create reference from subview Gary Guo
2026-04-28 12:10 ` Andreas Hindborg
2026-04-21 14:56 ` [PATCH v2 08/11] rust: io: add `read_val` and `write_val` function on I/O view Gary Guo
2026-04-28 12:53 ` Andreas Hindborg
2026-04-21 14:56 ` [PATCH v2 09/11] gpu: nova-core: use I/O projection for cleaner encapsulation Gary Guo
2026-04-21 14:56 ` [PATCH v2 10/11] rust: dma: drop `dma_read!` and `dma_write!` API Gary Guo
2026-04-28 11:16 ` Andreas Hindborg
2026-04-21 14:56 ` [PATCH v2 11/11] rust: io: add copying methods Gary Guo
2026-04-28 13:22 ` Andreas Hindborg
2026-04-28 14:08 ` Gary Guo
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260421-io_projection-v2-5-4c251c692ef4@garyguo.net \
--to=gary@garyguo.net \
--cc=a.hindborg@kernel.org \
--cc=abdiel.janulgue@gmail.com \
--cc=acourbot@nvidia.com \
--cc=airlied@gmail.com \
--cc=aliceryhl@google.com \
--cc=bhelgaas@google.com \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun@kernel.org \
--cc=dakr@kernel.org \
--cc=daniel.almeida@collabora.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=driver-core@lists.linux.dev \
--cc=gregkh@linuxfoundation.org \
--cc=kwilczynski@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=lossin@kernel.org \
--cc=nouveau@lists.freedesktop.org \
--cc=ojeda@kernel.org \
--cc=rafael@kernel.org \
--cc=robin.murphy@arm.com \
--cc=rust-for-linux@vger.kernel.org \
--cc=simona@ffwll.ch \
--cc=tmgross@umich.edu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox