From: Daniel Almeida <daniel.almeida@collabora.com>
To: "Danilo Krummrich" <dakr@kernel.org>,
"Abdiel Janulgue" <abdiel.janulgue@gmail.com>,
"Robin Murphy" <robin.murphy@arm.com>,
"Andreas Hindborg" <a.hindborg@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>,
"Alice Ryhl" <aliceryhl@google.com>,
"Trevor Gross" <tmgross@umich.edu>,
"Tamir Duberstein" <tamird@kernel.org>,
"Alexandre Courbot" <acourbot@nvidia.com>,
"Onur Özkan" <work@onurozkan.dev>,
"Bjorn Helgaas" <bhelgaas@google.com>,
"Krzysztof Wilczyński" <kwilczynski@kernel.org>,
"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,
dri-devel@lists.freedesktop.org,
Daniel Almeida <daniel.almeida@collabora.com>
Subject: [PATCH 5/7] rust: io: let I/O locations carry an access mode
Date: Thu, 15 Oct 2026 09:39:09 -0300 [thread overview]
Message-ID: <20261015-io-access-modes-v1-5-93611b84b5e3@collabora.com> (raw)
In-Reply-To: <20261015-io-access-modes-v1-0-93611b84b5e3@collabora.com>
`Io::read`, `Io::write`, `Io::update` and their fallible variants
project the view to the given `IoLoc` and access it there. A location
has no say in which accesses are allowed, so a register cannot be
declared read-only or write-only.
Add the `io::access` module with the `Mode` trait and the `ReadWrite`,
`ReadOnly` and `WriteOnly` modes, and add an `Access` associated type to
`IoLoc`. Projecting through a location applies its mode: `ReadWrite`
leaves the view as it is, while `ReadOnly` and `WriteOnly` wrap it in the
`ReadOnly` and `WriteOnly` views. The bounds of the generic accessors now
apply to the backend of the restricted view, so a write to a read-only
location, or a read from a write-only one, does not compile.
Raw offsets are `ReadWrite`. `OffsetLoc` takes the mode as a type
parameter that defaults to `ReadWrite`, and `RegisterArray` gets an
`Access` type, which `register!` sets to `ReadWrite` for now.
No existing location changes its mode, so there is no functional change.
A following patch lets `register!` declare read-only and write-only
registers.
Signed-off-by: Daniel Almeida <daniel.almeida@collabora.com>
---
rust/kernel/io.rs | 42 +++++++++++++++++--------
rust/kernel/io/access.rs | 76 ++++++++++++++++++++++++++++++++++++++++++++++
rust/kernel/io/register.rs | 18 +++++++++--
rust/macros/io/register.rs | 1 +
4 files changed, 121 insertions(+), 16 deletions(-)
diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
index 4be8c761c556..71bcd0d77ce9 100644
--- a/rust/kernel/io.rs
+++ b/rust/kernel/io.rs
@@ -22,6 +22,7 @@
}, //
};
+pub mod access;
#[cfg(CONFIG_HAS_IOMEM)]
pub mod mem;
pub mod poll;
@@ -377,12 +378,16 @@ fn copy_write<T: IntoBytes>(view: Self::View<'_, T>, value: T) {
///
/// - The valid `Base` to operate on. For most registers, this should be [`Region`].
/// - The offset to access (returned by [`IoLoc::offset`]),
-/// - The type `T` in which the data is returned or provided.
+/// - The type `T` in which the data is returned or provided,
+/// - The access mode ([`IoLoc::Access`]) applied to the views projected through it.
///
/// `T` is not necessarily the type for underlying I/O operation. Methods that take `IoLoc` have `T:
/// AsRepr` bound and the `<T as AsRepr>::Repr` type would be used to perform I/O and converted to
/// `T` instead.
pub trait IoLoc<Base: ?Sized, T> {
+ /// Access mode applied to the views projected through this location.
+ type Access: access::Mode;
+
/// Consumes `self` and returns the offset of this location.
fn offset(self) -> usize;
}
@@ -393,6 +398,8 @@ macro_rules! impl_usize_ioloc {
($($ty:ty),*) => {
$(
impl<const SIZE: usize> IoLoc<Region<SIZE>, $ty> for usize {
+ type Access = access::ReadWrite;
+
#[inline(always)]
fn offset(self) -> usize {
self
@@ -961,7 +968,7 @@ fn try_read<T, L>(self, location: L) -> Result<T>
where
T: AsReprMut,
L: IoLoc<Self::Target, T>,
- Self::Backend: IoReadable<<T as AsRepr>::Repr>,
+ <L::Access as access::Mode>::Backend<Self::Backend>: IoReadable<<T as AsRepr>::Repr>,
{
Ok(io_read!(self, try: location))
}
@@ -994,7 +1001,7 @@ fn try_write<T, L>(self, location: L, value: T) -> Result
where
T: AsRepr,
L: IoLoc<Self::Target, T>,
- Self::Backend: IoWritable<<T as AsRepr>::Repr>,
+ <L::Access as access::Mode>::Backend<Self::Backend>: IoWritable<<T as AsRepr>::Repr>,
{
io_write!(self, try: location, value);
Ok(())
@@ -1040,7 +1047,7 @@ fn try_write_reg<T, L, V>(self, value: V) -> Result
T: AsRepr,
L: IoLoc<Self::Target, T>,
V: LocatedRegister<Self::Target, Location = L, Value = T>,
- Self::Backend: IoWritable<<T as AsRepr>::Repr>,
+ <L::Access as access::Mode>::Backend<Self::Backend>: IoWritable<<T as AsRepr>::Repr>,
{
let (location, value) = value.into_io_op();
@@ -1074,7 +1081,8 @@ fn try_update<T, L, F>(self, location: L, f: F) -> Result
where
T: AsReprMut,
L: IoLoc<Self::Target, T>,
- Self::Backend: IoReadable<<T as AsRepr>::Repr> + IoWritable<<T as AsRepr>::Repr>,
+ <L::Access as access::Mode>::Backend<Self::Backend>:
+ IoReadable<<T as AsRepr>::Repr> + IoWritable<<T as AsRepr>::Repr>,
F: FnOnce(T) -> T,
{
let view = io_project!(self, try: location);
@@ -1108,7 +1116,7 @@ fn read<T, L>(self, location: L) -> T
where
T: AsReprMut,
L: IoLoc<Self::Target, T>,
- Self::Backend: IoReadable<<T as AsRepr>::Repr>,
+ <L::Access as access::Mode>::Backend<Self::Backend>: IoReadable<<T as AsRepr>::Repr>,
{
io_read!(self, build: location)
}
@@ -1139,7 +1147,7 @@ fn write<T, L>(self, location: L, value: T)
where
T: AsRepr,
L: IoLoc<Self::Target, T>,
- Self::Backend: IoWritable<<T as AsRepr>::Repr>,
+ <L::Access as access::Mode>::Backend<Self::Backend>: IoWritable<<T as AsRepr>::Repr>,
{
io_write!(self, build: location, value);
}
@@ -1183,7 +1191,7 @@ fn write_reg<T, L, V>(self, value: V)
T: AsRepr,
L: IoLoc<Self::Target, T>,
V: LocatedRegister<Self::Target, Location = L, Value = T>,
- Self::Backend: IoWritable<<T as AsRepr>::Repr>,
+ <L::Access as access::Mode>::Backend<Self::Backend>: IoWritable<<T as AsRepr>::Repr>,
{
let (location, value) = value.into_io_op();
@@ -1217,7 +1225,8 @@ fn update<T, L, F>(self, location: L, f: F)
where
T: AsReprMut,
L: IoLoc<Self::Target, T>,
- Self::Backend: IoReadable<<T as AsRepr>::Repr> + IoWritable<<T as AsRepr>::Repr>,
+ <L::Access as access::Mode>::Backend<Self::Backend>:
+ IoReadable<<T as AsRepr>::Repr> + IoWritable<<T as AsRepr>::Repr>,
F: FnOnce(T) -> T,
{
let view = io_project!(self, build: location);
@@ -1909,19 +1918,26 @@ pub unsafe fn project_view<U: ?Sized + KnownSize>(
pub fn try_project_loc<U, L>(
self,
location: L,
- ) -> Result<<T::Backend as IoBackend>::View<'a, U>>
+ ) -> Result<access::RestrictedView<'a, L::Access, T::Backend, U>>
where
L: IoLoc<T::Target, U>,
{
- io_view::<_, U>(self.0, location.offset())
+ let view = io_view::<_, U>(self.0, location.offset())?;
+
+ Ok(<L::Access as access::Mode>::restrict::<T::Backend, U>(view))
}
#[inline(always)]
- pub fn project_loc<U, L>(self, location: L) -> <T::Backend as IoBackend>::View<'a, U>
+ pub fn project_loc<U, L>(
+ self,
+ location: L,
+ ) -> access::RestrictedView<'a, L::Access, T::Backend, U>
where
L: IoLoc<T::Target, U>,
{
- io_view_assert::<_, U>(self.0, location.offset())
+ let view = io_view_assert::<_, U>(self.0, location.offset());
+
+ <L::Access as access::Mode>::restrict::<T::Backend, U>(view)
}
}
diff --git a/rust/kernel/io/access.rs b/rust/kernel/io/access.rs
new file mode 100644
index 000000000000..caec21dd943b
--- /dev/null
+++ b/rust/kernel/io/access.rs
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Access modes of I/O locations.
+//!
+//! An I/O location ([`IoLoc`](super::IoLoc)) has an access mode, which applies to the views
+//! projected through it: a [`ReadWrite`] location gives the view as it is, a [`ReadOnly`] location
+//! gives a [`ReadOnly`](super::ReadOnly) view, and a [`WriteOnly`] location gives a
+//! [`WriteOnly`](super::WriteOnly) view.
+
+use crate::ptr::KnownSize;
+
+use super::{
+ IoBackend,
+ ReadOnlyBackend,
+ WriteOnlyBackend, //
+};
+
+/// An access mode of an I/O location.
+pub trait Mode {
+ /// Backend of the views this mode gives for views of backend `B`.
+ type Backend<B: IoBackend>: IoBackend;
+
+ /// Applies this mode to `view`.
+ fn restrict<'a, B: IoBackend, T: ?Sized + KnownSize>(
+ view: B::View<'a, T>,
+ ) -> <Self::Backend<B> as IoBackend>::View<'a, T>;
+}
+
+/// View of `T` that access mode `M` gives for views of backend `B`.
+pub type RestrictedView<'a, M, B, T> = <<M as Mode>::Backend<B> as IoBackend>::View<'a, T>;
+
+/// Read-write access mode.
+///
+/// Views are given as they are.
+pub struct ReadWrite;
+
+impl Mode for ReadWrite {
+ type Backend<B: IoBackend> = B;
+
+ #[inline(always)]
+ fn restrict<'a, B: IoBackend, T: ?Sized + KnownSize>(view: B::View<'a, T>) -> B::View<'a, T> {
+ view
+ }
+}
+
+/// Read-only access mode.
+///
+/// Views are wrapped in [`ReadOnly`](super::ReadOnly).
+pub struct ReadOnly;
+
+impl Mode for ReadOnly {
+ type Backend<B: IoBackend> = ReadOnlyBackend<B>;
+
+ #[inline(always)]
+ fn restrict<'a, B: IoBackend, T: ?Sized + KnownSize>(
+ view: B::View<'a, T>,
+ ) -> super::ReadOnly<B::View<'a, T>> {
+ super::ReadOnly(view)
+ }
+}
+
+/// Write-only access mode.
+///
+/// Views are wrapped in [`WriteOnly`](super::WriteOnly).
+pub struct WriteOnly;
+
+impl Mode for WriteOnly {
+ type Backend<B: IoBackend> = WriteOnlyBackend<B>;
+
+ #[inline(always)]
+ fn restrict<'a, B: IoBackend, T: ?Sized + KnownSize>(
+ view: B::View<'a, T>,
+ ) -> super::WriteOnly<B::View<'a, T>> {
+ super::WriteOnly(view)
+ }
+}
diff --git a/rust/kernel/io/register.rs b/rust/kernel/io/register.rs
index b6513fa0f412..9218dc66017b 100644
--- a/rust/kernel/io/register.rs
+++ b/rust/kernel/io/register.rs
@@ -118,7 +118,10 @@
use crate::{
build_assert::build_assert,
- io::IoLoc, //
+ io::{
+ access,
+ IoLoc, //
+ },
};
/// Allows `()` to be used as the `location` parameter of [`Io::write`](super::Io::write) when
@@ -127,6 +130,8 @@ impl<Base: ?Sized, T> IoLoc<Base, T> for ()
where
T: FixedIoLoc<Base>,
{
+ type Access = <T::Location as IoLoc<Base, T>>::Access;
+
#[inline(always)]
fn offset(self) -> usize {
T::LOCATION.offset()
@@ -135,7 +140,7 @@ fn offset(self) -> usize {
// Provides a `IoLoc` impl that for a fixed offset.
#[doc(hidden)]
-pub struct OffsetLoc<Base: ?Sized, T>(usize, PhantomData<(T, Base)>);
+pub struct OffsetLoc<Base: ?Sized, T, A = access::ReadWrite>(usize, PhantomData<(T, A, Base)>);
impl<Base: ?Sized, T> OffsetLoc<Base, T> {
#[inline]
@@ -149,7 +154,9 @@ pub const fn const_offset(self) -> usize {
}
}
-impl<Base: ?Sized, T> IoLoc<Base, T> for OffsetLoc<Base, T> {
+impl<Base: ?Sized, T, A: access::Mode> IoLoc<Base, T> for OffsetLoc<Base, T, A> {
+ type Access = A;
+
#[inline(always)]
fn offset(self) -> usize {
self.0
@@ -161,6 +168,9 @@ pub trait RegisterArray: Sized {
/// Base type for this register.
type Base: ?Sized;
+ /// Access mode of the registers in this array.
+ type Access: access::Mode;
+
/// Start offset of the register.
///
/// The interpretation of this offset depends on the type of the register.
@@ -198,6 +208,8 @@ impl<Base: ?Sized, T> IoLoc<Base, T> for RegisterArrayLoc<T>
where
T: RegisterArray<Base = Base>,
{
+ type Access = T::Access;
+
#[inline(always)]
fn offset(self) -> usize {
T::OFFSET + self.0 * T::STRIDE
diff --git a/rust/macros/io/register.rs b/rust/macros/io/register.rs
index 420c3ad052b8..2d92c7f41447 100644
--- a/rust/macros/io/register.rs
+++ b/rust/macros/io/register.rs
@@ -283,6 +283,7 @@ impl ::kernel::io::register::Array for #name {}
impl ::kernel::io::register::RegisterArray for #name {
type Base = #base;
+ type Access = ::kernel::io::access::ReadWrite;
const OFFSET: usize = #offset;
const SIZE: usize = #size;
const STRIDE: usize = #stride;
--
2.56.0
next prev parent reply other threads:[~2026-10-15 12:40 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-15 12:39 [PATCH 0/7] rust: io: add read-only and write-only views and registers Daniel Almeida
2026-10-15 12:39 ` [PATCH 1/7] rust: io: split IoCapable into IoReadable and IoWritable Daniel Almeida
2026-10-20 12:04 ` Gary Guo
2026-10-20 14:01 ` Vishal Moola (Fractile)
2026-10-15 12:39 ` [PATCH 2/7] rust: io: split IoCopyable into IoCopyReadable and IoCopyWritable Daniel Almeida
2026-10-15 12:39 ` [PATCH 3/7] rust: io: add read-only and write-only views Daniel Almeida
2026-10-15 12:39 ` [PATCH 4/7] rust: io: project primitive accessors directly Daniel Almeida
2026-10-15 12:39 ` Daniel Almeida [this message]
2026-10-20 12:23 ` [PATCH 5/7] rust: io: let I/O locations carry an access mode Gary Guo
2026-10-15 12:39 ` [PATCH 6/7] rust: io: register: add `#[ro]` and `#[wo]` attributes Daniel Almeida
2026-10-15 12:39 ` [PATCH 7/7] drm/tyr: mark read-only and write-only registers Daniel Almeida
2026-10-20 8:01 ` [PATCH 0/7] rust: io: add read-only and write-only views and registers Alice Ryhl
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=20261015-io-access-modes-v1-5-93611b84b5e3@collabora.com \
--to=daniel.almeida@collabora.com \
--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=dri-devel@lists.freedesktop.org \
--cc=driver-core@lists.linux.dev \
--cc=gary@garyguo.net \
--cc=kwilczynski@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=lossin@kernel.org \
--cc=ojeda@kernel.org \
--cc=robin.murphy@arm.com \
--cc=rust-for-linux@vger.kernel.org \
--cc=simona@ffwll.ch \
--cc=tamird@kernel.org \
--cc=tmgross@umich.edu \
--cc=work@onurozkan.dev \
/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
all inboxes | Powered by JetHome®