From 426c92ca1bdd33dcbc01d6d66bb5bb4a356f2c54 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 6 Jul 2026 13:44:14 +0100 Subject: rust: io: add dynamically-sized `Region` type Currently many I/O related structs carry a `SIZE` parameter to denote the minimum size of the I/O region, while they also carry a field indicating the actual size. Proliferation of the pattern creates a lot of duplicated code, and makes it hard to create typed views of I/O. Introduce a `Region` type that carries the `SIZE` parameter. It is a wrapper of `[u8]`, which makes it dynamically sized with a metadata of `usize`. This way, pointers to `Region` naturally carry size information. This type is required to be 4-byte aligned. Expose the minimum size information via `MIN_SIZE` constant of the `KnownSize` trait. Similarly, expose the minimum alignment information via `KnownSize::MIN_ALIGN`. With these changes, it is possible to add an associated type to `Io` trait to represent the type of I/O region. For untyped regions, this is the newly added `Region` type. Remove `IoKnownSize` as it is no longer necessary. Use the same mechanism to indicate minimum size of PCI config spaces. Reviewed-by: Alexandre Courbot Signed-off-by: Gary Guo Reviewed-by: Daniel Almeida Link: https://patch.msgid.link/20260706-io_projection-v6-1-72cd5d055d54@garyguo.net [ Add brief explanation on MIN_ALIGN. - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/devres.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'rust/kernel/devres.rs') diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs index 11ce500e9b76..ed30ccc6e68e 100644 --- a/rust/kernel/devres.rs +++ b/rust/kernel/devres.rs @@ -68,7 +68,6 @@ struct Inner { /// devres::Devres, /// io::{ /// Io, -/// IoKnownSize, /// Mmio, /// MmioRaw, /// PhysAddr, // @@ -297,10 +296,7 @@ impl Devres { /// use kernel::{ /// device::Core, /// devres::Devres, - /// io::{ - /// Io, - /// IoKnownSize, // - /// }, + /// io::Io, /// pci, // /// }; /// -- cgit v1.2.3 From 9734e905119c5f7d7af9dd3e483f9a0d9ee12187 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 6 Jul 2026 13:44:18 +0100 Subject: rust: io: generalize `MmioRaw` to pointer to arbitrary type Conceptually, `MmioRaw` is just `__iomem *`, so it should work for any types. Update the existing use case where it represents a region of compile-time known minimum size and run-time known actual size to use the dynamic-sized type `Region` instead. Rename `maxsize` method to reflect that it is the actual size (not a bound) of the region. Implement `Clone` and `Copy` manually, which cannot be derived due to the generic parameter. The use of raw pointers also cause the `Send` and `Sync` auto trait implementation to be lost, so add them back by manual implementation. Reviewed-by: Alexandre Courbot Signed-off-by: Gary Guo Reviewed-by: Daniel Almeida Suggested-by: Danilo Krummrich Link: https://rust-for-linux.zulipchat.com/#narrow/channel/288089-General/topic/Generic.20I.2FO.20backends/near/571198078 Link: https://patch.msgid.link/20260706-io_projection-v6-5-72cd5d055d54@garyguo.net Signed-off-by: Danilo Krummrich --- rust/kernel/devres.rs | 7 +++--- rust/kernel/io.rs | 67 +++++++++++++++++++++++++++++++++++++-------------- rust/kernel/io/mem.rs | 5 ++-- rust/kernel/pci/io.rs | 4 +-- 4 files changed, 57 insertions(+), 26 deletions(-) (limited to 'rust/kernel/devres.rs') diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs index ed30ccc6e68e..d0c677fd7932 100644 --- a/rust/kernel/devres.rs +++ b/rust/kernel/devres.rs @@ -70,14 +70,15 @@ struct Inner { /// Io, /// Mmio, /// MmioRaw, -/// PhysAddr, // +/// PhysAddr, +/// Region, // /// }, /// prelude::*, /// }; /// use core::ops::Deref; /// /// // See also [`pci::Bar`] for a real example. -/// struct IoMem(MmioRaw); +/// struct IoMem(MmioRaw>); /// /// impl IoMem { /// /// # Safety @@ -92,7 +93,7 @@ struct Inner { /// return Err(ENOMEM); /// } /// -/// Ok(IoMem(MmioRaw::new(addr as usize, SIZE)?)) +/// Ok(IoMem(MmioRaw::new_region(addr as usize, SIZE)?)) /// } /// } /// diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index a2aa6173ce87..3013d8cf39e4 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -89,37 +89,67 @@ impl KnownSize for Region { /// Raw representation of an MMIO region. /// +/// `MmioRaw` is equivalent to `T __iomem *` in C. +/// /// By itself, the existence of an instance of this structure does not provide any guarantees that /// the represented MMIO region does exist or is properly mapped. /// /// Instead, the bus specific MMIO implementation must convert this raw representation into an /// `Mmio` instance providing the actual memory accessors. Only by the conversion into an `Mmio` /// structure any guarantees are given. -pub struct MmioRaw { - addr: usize, - maxsize: usize, +pub struct MmioRaw { + /// Pointer is in I/O address space. + /// + /// The provenance does not matter, only the address and metadata do. + ptr: *mut T, } -impl MmioRaw { - /// Returns a new `MmioRaw` instance on success, an error otherwise. - pub fn new(addr: usize, maxsize: usize) -> Result { - if maxsize < SIZE { - return Err(EINVAL); +impl Copy for MmioRaw {} +impl Clone for MmioRaw { + #[inline] + fn clone(&self) -> Self { + *self + } +} + +// SAFETY: `MmioRaw` is just an address, so is thread-safe. +unsafe impl Send for MmioRaw {} +// SAFETY: `MmioRaw` is just an address, so is thread-safe. +unsafe impl Sync for MmioRaw {} + +impl MmioRaw { + /// Create a `MmioRaw` from address. + #[inline] + pub fn new(addr: usize) -> Self { + Self { + ptr: core::ptr::without_provenance_mut(addr), } + } +} - Ok(Self { addr, maxsize }) +impl MmioRaw> { + /// Create a `MmioRaw` representing a I/O region with given size. + /// + /// The size is checked against the minimum size specified via const generics. + #[inline] + pub fn new_region(addr: usize, size: usize) -> Result { + Ok(Self { + ptr: Region::ptr_try_from_raw_parts_mut(core::ptr::without_provenance_mut(addr), size)?, + }) } +} +impl MmioRaw { /// Returns the base address of the MMIO region. #[inline] pub fn addr(&self) -> usize { - self.addr + self.ptr.addr() } - /// Returns the maximum size of the MMIO region. + /// Returns the size of the MMIO region. #[inline] - pub fn maxsize(&self) -> usize { - self.maxsize + pub fn size(&self) -> usize { + KnownSize::size(self.ptr) } } @@ -144,12 +174,13 @@ impl MmioRaw { /// Mmio, /// MmioRaw, /// PhysAddr, +/// Region, /// }, /// }; /// use core::ops::Deref; /// /// // See also `pci::Bar` for a real example. -/// struct IoMem(MmioRaw); +/// struct IoMem(MmioRaw>); /// /// impl IoMem { /// /// # Safety @@ -164,7 +195,7 @@ impl MmioRaw { /// return Err(ENOMEM); /// } /// -/// Ok(IoMem(MmioRaw::new(addr as usize, SIZE)?)) +/// Ok(IoMem(MmioRaw::new_region(addr as usize, SIZE)?)) /// } /// } /// @@ -194,7 +225,7 @@ impl MmioRaw { /// # } /// ``` #[repr(transparent)] -pub struct Mmio(MmioRaw); +pub struct Mmio(MmioRaw>); /// Checks whether an access of type `U` at the given `base` and the given `offset` /// is valid within this region. @@ -841,7 +872,7 @@ impl<'a, const SIZE: usize> Io for &'a Mmio { /// Returns the maximum size of this mapping. #[inline] fn maxsize(self) -> usize { - self.0.maxsize() + self.0.size() } } @@ -852,7 +883,7 @@ impl Mmio { /// /// Callers must ensure that `addr` is the start of a valid I/O mapped memory region of size /// `maxsize`. - pub unsafe fn from_raw(raw: &MmioRaw) -> &Self { + pub unsafe fn from_raw(raw: &MmioRaw>) -> &Self { // SAFETY: `Mmio` is a transparent wrapper around `MmioRaw`. unsafe { &*core::ptr::from_ref(raw).cast() } } diff --git a/rust/kernel/io/mem.rs b/rust/kernel/io/mem.rs index fc2a3e24f8d5..9e15bc8fde78 100644 --- a/rust/kernel/io/mem.rs +++ b/rust/kernel/io/mem.rs @@ -229,7 +229,7 @@ impl Deref for ExclusiveIoMem<'_, SIZE> { /// start of the I/O memory mapped region. pub struct IoMem<'a, const SIZE: usize = 0> { dev: &'a Device, - io: MmioRaw, + io: MmioRaw>, } impl<'a, const SIZE: usize> IoMem<'a, SIZE> { @@ -264,8 +264,7 @@ impl<'a, const SIZE: usize> IoMem<'a, SIZE> { return Err(ENOMEM); } - let io = MmioRaw::new(addr as usize, size)?; - + let io = MmioRaw::new_region(addr as usize, size)?; Ok(IoMem { dev, io }) } diff --git a/rust/kernel/pci/io.rs b/rust/kernel/pci/io.rs index 505305cd9b86..42f840d64a6f 100644 --- a/rust/kernel/pci/io.rs +++ b/rust/kernel/pci/io.rs @@ -139,7 +139,7 @@ impl<'a, S: ?Sized + ConfigSpaceKind> Io for &ConfigSpace<'a, S> { /// memory mapped PCI BAR and its size. pub struct Bar<'a, const SIZE: usize = 0> { pdev: &'a Device, - io: MmioRaw, + io: MmioRaw>, num: i32, } @@ -179,7 +179,7 @@ impl<'a, const SIZE: usize> Bar<'a, SIZE> { return Err(ENOMEM); } - let io = match MmioRaw::new(ioptr, len as usize) { + let io = match MmioRaw::new_region(ioptr, len as usize) { Ok(io) => io, Err(err) => { // SAFETY: -- cgit v1.2.3 From 691c75967d44bef006e4d4e783baa88470b33ea5 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 6 Jul 2026 13:44:19 +0100 Subject: rust: io: rename `Mmio` to `MmioOwned` Most users would more commonly reach out to a view of `Mmio` rather than an owned instance of `Mmio`. Only implementor of `Io` like `Bar` or `IoMem` would need the owned version. Thus, rename `Mmio` to `MmioOwned` so that the name `Mmio` can be used for the view type instead. Reviewed-by: Alexandre Courbot Signed-off-by: Gary Guo Reviewed-by: Daniel Almeida Suggested-by: Danilo Krummrich Link: https://rust-for-linux.zulipchat.com/#narrow/channel/288089-General/topic/Generic.20I.2FO.20backends/near/571198078 Link: https://patch.msgid.link/20260706-io_projection-v6-6-72cd5d055d54@garyguo.net Signed-off-by: Danilo Krummrich --- rust/kernel/devres.rs | 6 ++-- rust/kernel/io.rs | 77 +++++++++++++++++++++++----------------------- rust/kernel/io/mem.rs | 8 ++--- rust/kernel/io/poll.rs | 8 ++--- rust/kernel/io/register.rs | 24 +++++++-------- rust/kernel/pci/io.rs | 6 ++-- 6 files changed, 65 insertions(+), 64 deletions(-) (limited to 'rust/kernel/devres.rs') diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs index d0c677fd7932..aed0c994fd30 100644 --- a/rust/kernel/devres.rs +++ b/rust/kernel/devres.rs @@ -68,7 +68,7 @@ struct Inner { /// devres::Devres, /// io::{ /// Io, -/// Mmio, +/// MmioOwned, /// MmioRaw, /// PhysAddr, /// Region, // @@ -105,11 +105,11 @@ struct Inner { /// } /// /// impl Deref for IoMem { -/// type Target = Mmio; +/// type Target = MmioOwned; /// /// fn deref(&self) -> &Self::Target { /// // SAFETY: The memory range stored in `self` has been properly mapped in `Self::new`. -/// unsafe { Mmio::from_raw(&self.0) } +/// unsafe { MmioOwned::from_raw(&self.0) } /// } /// } /// # fn no_run(dev: &Device) -> Result<(), Error> { diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index 3013d8cf39e4..ec4ac42aa25d 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -95,8 +95,8 @@ impl KnownSize for Region { /// the represented MMIO region does exist or is properly mapped. /// /// Instead, the bus specific MMIO implementation must convert this raw representation into an -/// `Mmio` instance providing the actual memory accessors. Only by the conversion into an `Mmio` -/// structure any guarantees are given. +/// `MmioOwned` instance providing the actual memory accessors. Only by the conversion into an +/// `MmioOwned` structure any guarantees are given. pub struct MmioRaw { /// Pointer is in I/O address space. /// @@ -171,7 +171,7 @@ impl MmioRaw { /// ffi::c_void, /// io::{ /// Io, -/// Mmio, +/// MmioOwned, /// MmioRaw, /// PhysAddr, /// Region, @@ -207,11 +207,11 @@ impl MmioRaw { /// } /// /// impl Deref for IoMem { -/// type Target = Mmio; +/// type Target = MmioOwned; /// /// fn deref(&self) -> &Self::Target { /// // SAFETY: The memory range stored in `self` has been properly mapped in `Self::new`. -/// unsafe { Mmio::from_raw(&self.0) } +/// unsafe { MmioOwned::from_raw(&self.0) } /// } /// } /// @@ -225,7 +225,7 @@ impl MmioRaw { /// # } /// ``` #[repr(transparent)] -pub struct Mmio(MmioRaw>); +pub struct MmioOwned(MmioRaw>); /// Checks whether an access of type `U` at the given `base` and the given `offset` /// is valid within this region. @@ -538,10 +538,10 @@ pub trait Io: Copy { /// ```no_run /// use kernel::io::{ /// Io, - /// Mmio, + /// MmioOwned, /// }; /// - /// fn do_reads(io: &Mmio) -> Result { + /// fn do_reads(io: &MmioOwned) -> Result { /// // 32-bit read from address `0x10`. /// let v: u32 = io.try_read(0x10)?; /// @@ -572,10 +572,10 @@ pub trait Io: Copy { /// ```no_run /// use kernel::io::{ /// Io, - /// Mmio, + /// MmioOwned, /// }; /// - /// fn do_writes(io: &Mmio) -> Result { + /// fn do_writes(io: &MmioOwned) -> Result { /// // 32-bit write of value `1` at address `0x10`. /// io.try_write(0x10, 1u32)?; /// @@ -610,7 +610,7 @@ pub trait Io: Copy { /// use kernel::io::{ /// register, /// Io, - /// Mmio, + /// MmioOwned, /// }; /// /// register! { @@ -626,7 +626,7 @@ pub trait Io: Copy { /// } /// } /// - /// fn do_write_reg(io: &Mmio) -> Result { + /// fn do_write_reg(io: &MmioOwned) -> Result { /// /// io.try_write_reg(VERSION::new(1, 0)) /// } @@ -655,10 +655,10 @@ pub trait Io: Copy { /// ```no_run /// use kernel::io::{ /// Io, - /// Mmio, + /// MmioOwned, /// }; /// - /// fn do_update(io: &Mmio<0x1000>) -> Result { + /// fn do_update(io: &MmioOwned<0x1000>) -> Result { /// io.try_update(0x10, |v: u32| { /// v + 1 /// }) @@ -692,10 +692,10 @@ pub trait Io: Copy { /// ```no_run /// use kernel::io::{ /// Io, - /// Mmio, + /// MmioOwned, /// }; /// - /// fn do_reads(io: &Mmio<0x1000>) { + /// fn do_reads(io: &MmioOwned<0x1000>) { /// // 32-bit read from address `0x10`. /// let v: u32 = io.read(0x10); /// @@ -724,10 +724,10 @@ pub trait Io: Copy { /// ```no_run /// use kernel::io::{ /// Io, - /// Mmio, + /// MmioOwned, /// }; /// - /// fn do_writes(io: &Mmio<0x1000>) { + /// fn do_writes(io: &MmioOwned<0x1000>) { /// // 32-bit write of value `1` at address `0x10`. /// io.write(0x10, 1u32); /// @@ -758,7 +758,7 @@ pub trait Io: Copy { /// use kernel::io::{ /// register, /// Io, - /// Mmio, + /// MmioOwned, /// }; /// /// register! { @@ -774,7 +774,7 @@ pub trait Io: Copy { /// } /// } /// - /// fn do_write_reg(io: &Mmio<0x1000>) { + /// fn do_write_reg(io: &MmioOwned<0x1000>) { /// io.write_reg(VERSION::new(1, 0)); /// } /// ``` @@ -802,10 +802,10 @@ pub trait Io: Copy { /// ```no_run /// use kernel::io::{ /// Io, - /// Mmio, + /// MmioOwned, /// }; /// - /// fn do_update(io: &Mmio<0x1000>) { + /// fn do_update(io: &MmioOwned<0x1000>) { /// io.update(0x10, |v: u32| { /// v + 1 /// }) @@ -848,19 +848,19 @@ macro_rules! impl_mmio_io_capable { } // MMIO regions support 8, 16, and 32-bit accesses. -impl_mmio_io_capable!(Mmio, u8, readb, writeb); -impl_mmio_io_capable!(Mmio, u16, readw, writew); -impl_mmio_io_capable!(Mmio, u32, readl, writel); +impl_mmio_io_capable!(MmioOwned, u8, readb, writeb); +impl_mmio_io_capable!(MmioOwned, u16, readw, writew); +impl_mmio_io_capable!(MmioOwned, u32, readl, writel); // MMIO regions on 64-bit systems also support 64-bit accesses. impl_mmio_io_capable!( - Mmio, + MmioOwned, #[cfg(CONFIG_64BIT)] u64, readq, writeq ); -impl<'a, const SIZE: usize> Io for &'a Mmio { +impl<'a, const SIZE: usize> Io for &'a MmioOwned { type Target = Region; /// Returns the base address of this mapping. @@ -876,27 +876,28 @@ impl<'a, const SIZE: usize> Io for &'a Mmio { } } -impl Mmio { - /// Converts an `MmioRaw` into an `Mmio` instance, providing the accessors to the MMIO mapping. +impl MmioOwned { + /// Converts an `MmioRaw` into an `MmioOwned` instance, providing the accessors to the MMIO + /// mapping. /// /// # Safety /// /// Callers must ensure that `addr` is the start of a valid I/O mapped memory region of size /// `maxsize`. pub unsafe fn from_raw(raw: &MmioRaw>) -> &Self { - // SAFETY: `Mmio` is a transparent wrapper around `MmioRaw`. + // SAFETY: `MmioOwned` is a transparent wrapper around `MmioRaw`. unsafe { &*core::ptr::from_ref(raw).cast() } } } -/// [`Mmio`] wrapper using relaxed accessors. +/// [`MmioOwned`] wrapper using relaxed accessors. /// /// This type provides an implementation of [`Io`] that uses relaxed I/O MMIO operands instead of /// the regular ones. /// -/// See [`Mmio::relaxed`] for a usage example. +/// See [`MmioOwned::relaxed`] for a usage example. #[repr(transparent)] -pub struct RelaxedMmio(Mmio); +pub struct RelaxedMmio(MmioOwned); impl<'a, const SIZE: usize> Io for &'a RelaxedMmio { type Target = Region; @@ -912,7 +913,7 @@ impl<'a, const SIZE: usize> Io for &'a RelaxedMmio { } } -impl Mmio { +impl MmioOwned { /// Returns a [`RelaxedMmio`] reference that performs relaxed I/O operations. /// /// Relaxed accessors do not provide ordering guarantees with respect to DMA or memory accesses @@ -923,19 +924,19 @@ impl Mmio { /// ```no_run /// use kernel::io::{ /// Io, - /// Mmio, + /// MmioOwned, /// RelaxedMmio, /// }; /// - /// fn do_io(io: &Mmio<0x100>) { + /// fn do_io(io: &MmioOwned<0x100>) { /// // The access is performed using `readl_relaxed` instead of `readl`. /// let v = io.relaxed().read32(0x10); /// } /// /// ``` pub fn relaxed(&self) -> &RelaxedMmio { - // SAFETY: `RelaxedMmio` is `#[repr(transparent)]` over `Mmio`, so `Mmio` and - // `RelaxedMmio` have identical layout. + // SAFETY: `RelaxedMmio` is `#[repr(transparent)]` over `MmioOwned`, so `MmioOwned` + // and `RelaxedMmio` have identical layout. unsafe { core::mem::transmute(self) } } } diff --git a/rust/kernel/io/mem.rs b/rust/kernel/io/mem.rs index 9e15bc8fde78..8f6c257c5b8e 100644 --- a/rust/kernel/io/mem.rs +++ b/rust/kernel/io/mem.rs @@ -16,7 +16,7 @@ use crate::{ Region, Resource, // }, - Mmio, + MmioOwned, MmioRaw, // }, prelude::*, @@ -211,7 +211,7 @@ impl<'a, const SIZE: usize> ExclusiveIoMem<'a, SIZE> { } impl Deref for ExclusiveIoMem<'_, SIZE> { - type Target = Mmio; + type Target = MmioOwned; fn deref(&self) -> &Self::Target { &self.iomem @@ -291,10 +291,10 @@ impl Drop for IoMem<'_, SIZE> { } impl Deref for IoMem<'_, SIZE> { - type Target = Mmio; + type Target = MmioOwned; fn deref(&self) -> &Self::Target { // SAFETY: Safe as by the invariant of `IoMem`. - unsafe { Mmio::from_raw(&self.io) } + unsafe { MmioOwned::from_raw(&self.io) } } } diff --git a/rust/kernel/io/poll.rs b/rust/kernel/io/poll.rs index 75d1b3e8596c..79828a8006b5 100644 --- a/rust/kernel/io/poll.rs +++ b/rust/kernel/io/poll.rs @@ -47,14 +47,14 @@ use crate::{ /// ```no_run /// use kernel::io::{ /// Io, -/// Mmio, +/// MmioOwned, /// poll::read_poll_timeout, // /// }; /// use kernel::time::Delta; /// /// const HW_READY: u16 = 0x01; /// -/// fn wait_for_hardware(io: &Mmio) -> Result { +/// fn wait_for_hardware(io: &MmioOwned) -> Result { /// read_poll_timeout( /// // The `op` closure reads the value of a specific status register. /// || io.try_read16(0x1000), @@ -134,14 +134,14 @@ where /// ```no_run /// use kernel::io::{ /// Io, -/// Mmio, +/// MmioOwned, /// poll::read_poll_timeout_atomic, // /// }; /// use kernel::time::Delta; /// /// const HW_READY: u16 = 0x01; /// -/// fn wait_for_hardware(io: &Mmio) -> Result { +/// fn wait_for_hardware(io: &MmioOwned) -> Result { /// read_poll_timeout_atomic( /// // The `op` closure reads the value of a specific status register. /// || io.try_read16(0x1000), diff --git a/rust/kernel/io/register.rs b/rust/kernel/io/register.rs index 3122b17098ee..43284d9fba96 100644 --- a/rust/kernel/io/register.rs +++ b/rust/kernel/io/register.rs @@ -58,7 +58,7 @@ //! }, //! num::Bounded, //! }; -//! # use kernel::io::Mmio; +//! # use kernel::io::MmioOwned; //! # register! { //! # pub BOOT_0(u32) @ 0x00000100 { //! # 15:8 vendor_id; @@ -66,7 +66,7 @@ //! # 3:0 minor_revision; //! # } //! # } -//! # fn test(io: &Mmio<0x1000>) { +//! # fn test(io: &MmioOwned<0x1000>) { //! # fn obtain_vendor_id() -> u8 { 0xff } //! //! // Read from the register's defined offset (0x100). @@ -446,7 +446,7 @@ where /// Io, /// }, /// }; -/// # use kernel::io::Mmio; +/// # use kernel::io::MmioOwned; /// /// register! { /// FIXED_REG(u32) @ 0x100 { @@ -455,7 +455,7 @@ where /// } /// } /// -/// # fn test(io: &Mmio<0x1000>) { +/// # fn test(io: &MmioOwned<0x1000>) { /// let val = io.read(FIXED_REG); /// /// // Write from an already-existing value. @@ -559,7 +559,7 @@ where /// Io, /// }, /// }; -/// # use kernel::io::Mmio; +/// # use kernel::io::MmioOwned; /// /// // Type used to identify the base. /// pub struct CpuCtlBase; @@ -584,7 +584,7 @@ where /// } /// } /// -/// # fn test(io: Mmio<0x1000>) { +/// # fn test(io: MmioOwned<0x1000>) { /// // Read the status of `Cpu0`. /// let cpu0_started = io.read(CPU_CTL::of::()); /// @@ -601,7 +601,7 @@ where /// } /// } /// -/// # fn test2(io: Mmio<0x1000>) { +/// # fn test2(io: MmioOwned<0x1000>) { /// // Start the aliased `CPU0`, leaving its other fields untouched. /// io.update(CPU_CTL_ALIAS::of::(), |r| r.with_alias_start(true)); /// # } @@ -638,7 +638,7 @@ where /// Io, /// }, /// }; -/// # use kernel::io::Mmio; +/// # use kernel::io::MmioOwned; /// # fn get_scratch_idx() -> usize { /// # 0x15 /// # } @@ -651,7 +651,7 @@ where /// } /// } /// -/// # fn test(io: &Mmio<0x1000>) +/// # fn test(io: &MmioOwned<0x1000>) /// # -> Result<(), Error>{ /// // Read scratch register 0, i.e. I/O address `0x80`. /// let scratch_0 = io.read(SCRATCH::at(0)).value(); @@ -724,7 +724,7 @@ where /// Io, /// }, /// }; -/// # use kernel::io::Mmio; +/// # use kernel::io::MmioOwned; /// # fn get_scratch_idx() -> usize { /// # 0x15 /// # } @@ -752,7 +752,7 @@ where /// } /// } /// -/// # fn test(io: &Mmio<0x1000>) -> Result<(), Error> { +/// # fn test(io: &MmioOwned<0x1000>) -> Result<(), Error> { /// // Read scratch register 0 of CPU0. /// let scratch = io.read(CPU_SCRATCH::of::().at(0)); /// @@ -794,7 +794,7 @@ where /// } /// } /// -/// # fn test2(io: &Mmio<0x1000>) -> Result<(), Error> { +/// # fn test2(io: &MmioOwned<0x1000>) -> Result<(), Error> { /// let cpu0_status = io.read(CPU_FIRMWARE_STATUS::of::()).status(); /// # Ok(()) /// # } diff --git a/rust/kernel/pci/io.rs b/rust/kernel/pci/io.rs index 42f840d64a6f..e0acb62f58a2 100644 --- a/rust/kernel/pci/io.rs +++ b/rust/kernel/pci/io.rs @@ -10,7 +10,7 @@ use crate::{ io::{ Io, IoCapable, - Mmio, + MmioOwned, MmioRaw, Region, // }, @@ -242,11 +242,11 @@ impl Drop for Bar<'_, SIZE> { } impl Deref for Bar<'_, SIZE> { - type Target = Mmio; + type Target = MmioOwned; fn deref(&self) -> &Self::Target { // SAFETY: By the type invariant of `Self`, the MMIO range in `self.io` is properly mapped. - unsafe { Mmio::from_raw(&self.io) } + unsafe { MmioOwned::from_raw(&self.io) } } } -- cgit v1.2.3 From bed01ca9e9cf8f8fea5352c07fa206cc3c106045 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 6 Jul 2026 13:44:24 +0100 Subject: rust: io: remove `MmioOwned` `Io` trait is now very easy to implement. Thus, implement it on `Bar` and `IoMem` directly and remove the `MmioOwned` struct. Reviewed-by: Alexandre Courbot Signed-off-by: Gary Guo Reviewed-by: Daniel Almeida Suggested-by: Danilo Krummrich Link: https://rust-for-linux.zulipchat.com/#narrow/channel/288089-General/topic/Generic.20I.2FO.20backends/near/571198078 Link: https://patch.msgid.link/20260706-io_projection-v6-11-72cd5d055d54@garyguo.net Signed-off-by: Danilo Krummrich --- rust/kernel/devres.rs | 12 +++--- rust/kernel/io.rs | 103 +------------------------------------------------- rust/kernel/io/mem.rs | 26 +++++++------ rust/kernel/pci/io.rs | 16 ++++---- 4 files changed, 32 insertions(+), 125 deletions(-) (limited to 'rust/kernel/devres.rs') diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs index aed0c994fd30..3545ffc5345d 100644 --- a/rust/kernel/devres.rs +++ b/rust/kernel/devres.rs @@ -68,8 +68,9 @@ struct Inner { /// devres::Devres, /// io::{ /// Io, -/// MmioOwned, +/// Mmio, /// MmioRaw, +/// MmioBackend, /// PhysAddr, /// Region, // /// }, @@ -104,12 +105,13 @@ struct Inner { /// } /// } /// -/// impl Deref for IoMem { -/// type Target = MmioOwned; +/// impl<'a, const SIZE: usize> Io<'a> for &'a IoMem { +/// type Backend = MmioBackend; +/// type Target = Region; /// -/// fn deref(&self) -> &Self::Target { +/// fn as_view(self) -> Mmio<'a, Region> { /// // SAFETY: The memory range stored in `self` has been properly mapped in `Self::new`. -/// unsafe { MmioOwned::from_raw(&self.0) } +/// unsafe { Mmio::from_raw(self.0) } /// } /// } /// # fn no_run(dev: &Device) -> Result<(), Error> { diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index 82a3369ae110..729b64a385c3 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -99,8 +99,8 @@ impl KnownSize for Region { /// the represented MMIO region does exist or is properly mapped. /// /// Instead, the bus specific MMIO implementation must convert this raw representation into an -/// `MmioOwned` instance providing the actual memory accessors. Only by the conversion into an -/// `MmioOwned` structure any guarantees are given. +/// `Mmio` instance providing the actual memory accessors. Only by the conversion into an `Mmio` +/// structure any guarantees are given. pub struct MmioRaw { /// Pointer is in I/O address space. /// @@ -157,80 +157,6 @@ impl MmioRaw { } } -/// IO-mapped memory region. -/// -/// The creator (usually a subsystem / bus such as PCI) is responsible for creating the -/// mapping, performing an additional region request etc. -/// -/// # Invariant -/// -/// `addr` is the start and `maxsize` the length of valid I/O mapped memory region of size -/// `maxsize`. -/// -/// # Examples -/// -/// ```no_run -/// use kernel::{ -/// bindings, -/// ffi::c_void, -/// io::{ -/// Io, -/// MmioOwned, -/// MmioRaw, -/// PhysAddr, -/// Region, -/// }, -/// }; -/// use core::ops::Deref; -/// -/// // See also `pci::Bar` for a real example. -/// struct IoMem(MmioRaw>); -/// -/// impl IoMem { -/// /// # Safety -/// /// -/// /// [`paddr`, `paddr` + `SIZE`) must be a valid MMIO region that is mappable into the CPUs -/// /// virtual address space. -/// unsafe fn new(paddr: usize) -> Result{ -/// // SAFETY: By the safety requirements of this function [`paddr`, `paddr` + `SIZE`) is -/// // valid for `ioremap`. -/// let addr = unsafe { bindings::ioremap(paddr as PhysAddr, SIZE) }; -/// if addr.is_null() { -/// return Err(ENOMEM); -/// } -/// -/// Ok(IoMem(MmioRaw::new_region(addr as usize, SIZE)?)) -/// } -/// } -/// -/// impl Drop for IoMem { -/// fn drop(&mut self) { -/// // SAFETY: `self.0.addr()` is guaranteed to be properly mapped by `Self::new`. -/// unsafe { bindings::iounmap(self.0.addr() as *mut c_void); }; -/// } -/// } -/// -/// impl Deref for IoMem { -/// type Target = MmioOwned; -/// -/// fn deref(&self) -> &Self::Target { -/// // SAFETY: The memory range stored in `self` has been properly mapped in `Self::new`. -/// unsafe { MmioOwned::from_raw(&self.0) } -/// } -/// } -/// -///# fn no_run() -> Result<(), Error> { -/// // SAFETY: Invalid usage for example purposes. -/// let iomem = unsafe { IoMem::<{ core::mem::size_of::() }>::new(0xBAAAAAAD)? }; -/// iomem.write32(0x42, 0x0); -/// assert!(iomem.try_write32(0x42, 0x0).is_ok()); -/// assert!(iomem.try_write32(0x42, 0x4).is_err()); -/// # Ok(()) -/// # } -/// ``` -#[repr(transparent)] -pub struct MmioOwned(MmioRaw>); - /// Checks whether an access of type `U` at the given `base` and the given `offset` /// is valid within this region. /// @@ -958,31 +884,6 @@ impl_mmio_io_capable!(MmioBackend, u32, readl, writel); #[cfg(CONFIG_64BIT)] impl_mmio_io_capable!(MmioBackend, u64, readq, writeq); -impl<'a, const SIZE: usize> Io<'a> for &'a MmioOwned { - type Backend = MmioBackend; - type Target = Region; - - #[inline] - fn as_view(self) -> Mmio<'a, Self::Target> { - // SAFETY: `Mmio` has same invariant as `MmioOwned` - unsafe { Mmio::from_raw(self.0) } - } -} - -impl MmioOwned { - /// Converts an `MmioRaw` into an `MmioOwned` instance, providing the accessors to the MMIO - /// mapping. - /// - /// # Safety - /// - /// Callers must ensure that `addr` is the start of a valid I/O mapped memory region of size - /// `maxsize`. - pub unsafe fn from_raw(raw: &MmioRaw>) -> &Self { - // SAFETY: `MmioOwned` is a transparent wrapper around `MmioRaw`. - unsafe { &*core::ptr::from_ref(raw).cast() } - } -} - /// [`Mmio`] but using relaxed accessors. /// /// This type provides an implementation of [`Io`] that uses relaxed I/O MMIO operands instead of diff --git a/rust/kernel/io/mem.rs b/rust/kernel/io/mem.rs index 8f6c257c5b8e..d9b3189d09b4 100644 --- a/rust/kernel/io/mem.rs +++ b/rust/kernel/io/mem.rs @@ -2,8 +2,6 @@ //! Generic memory-mapped IO. -use core::ops::Deref; - use crate::{ device::{ Bound, @@ -16,7 +14,9 @@ use crate::{ Region, Resource, // }, - MmioOwned, + Io, + Mmio, + MmioBackend, MmioRaw, // }, prelude::*, @@ -210,11 +210,13 @@ impl<'a, const SIZE: usize> ExclusiveIoMem<'a, SIZE> { } } -impl Deref for ExclusiveIoMem<'_, SIZE> { - type Target = MmioOwned; +impl<'a, const SIZE: usize> Io<'a> for &'a ExclusiveIoMem<'_, SIZE> { + type Backend = MmioBackend; + type Target = super::Region; - fn deref(&self) -> &Self::Target { - &self.iomem + #[inline] + fn as_view(self) -> Mmio<'a, Self::Target> { + self.iomem.as_view() } } @@ -290,11 +292,13 @@ impl Drop for IoMem<'_, SIZE> { } } -impl Deref for IoMem<'_, SIZE> { - type Target = MmioOwned; +impl<'a, const SIZE: usize> Io<'a> for &'a IoMem<'_, SIZE> { + type Backend = MmioBackend; + type Target = super::Region; - fn deref(&self) -> &Self::Target { + #[inline] + fn as_view(self) -> Mmio<'a, Self::Target> { // SAFETY: Safe as by the invariant of `IoMem`. - unsafe { MmioOwned::from_raw(&self.io) } + unsafe { Mmio::from_raw(self.io) } } } diff --git a/rust/kernel/pci/io.rs b/rust/kernel/pci/io.rs index e67c1e3694fb..4be33ecb4192 100644 --- a/rust/kernel/pci/io.rs +++ b/rust/kernel/pci/io.rs @@ -11,16 +11,14 @@ use crate::{ Io, IoBackend, IoCapable, - MmioOwned, + Mmio, + MmioBackend, MmioRaw, Region, // }, prelude::*, ptr::KnownSize, // }; -use core::{ - ops::Deref, // -}; /// Represents the size of a PCI configuration space. /// @@ -269,12 +267,14 @@ impl Drop for Bar<'_, SIZE> { } } -impl Deref for Bar<'_, SIZE> { - type Target = MmioOwned; +impl<'a, const SIZE: usize> Io<'a> for &'a Bar<'_, SIZE> { + type Backend = MmioBackend; + type Target = crate::io::Region; - fn deref(&self) -> &Self::Target { + #[inline] + fn as_view(self) -> Mmio<'a, Self::Target> { // SAFETY: By the type invariant of `Self`, the MMIO range in `self.io` is properly mapped. - unsafe { MmioOwned::from_raw(&self.io) } + unsafe { Mmio::from_raw(self.io) } } } -- cgit v1.2.3 From 9b36c13cbd4fb761212b1ea9a2e89f7df2d3c9f8 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 6 Jul 2026 13:44:25 +0100 Subject: rust: io: move `Io` methods to extension trait `Io` trait now has a single required method with many more provided methods. Provided methods may want to rely on their implementations to not be arbitrarily overridden by implementers for correctness or soundness. A good example is the `size` method, it may be relied by unsafe code and thus must be consistent with the metadata obtained from `as_ptr`. Thus, create a new trait to host `size` method, extract existing provided methods to the new trait, and provide a blanket implementation. This pattern is used extensively in userspace Rust libraries e.g. `tokio` where `AsyncRead` has minimum methods and `AsyncReadExt` is what users mostly interact with. To avoid changing all user imports, the base trait is renamed to `IoBase` and the newly added trait takes the existing `Io` name. Reviewed-by: Alexandre Courbot Suggested-by: Danilo Krummrich Signed-off-by: Gary Guo Reviewed-by: Daniel Almeida Link: https://patch.msgid.link/20260706-io_projection-v6-12-72cd5d055d54@garyguo.net [ Add comment explaining the purpose of the Io blanket implementation. - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/devres.rs | 3 ++- rust/kernel/io.rs | 36 ++++++++++++++++++++++++++---------- rust/kernel/io/mem.rs | 6 +++--- rust/kernel/pci/io.rs | 6 +++--- 4 files changed, 34 insertions(+), 17 deletions(-) (limited to 'rust/kernel/devres.rs') diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs index 3545ffc5345d..6e0b845b229b 100644 --- a/rust/kernel/devres.rs +++ b/rust/kernel/devres.rs @@ -68,6 +68,7 @@ struct Inner { /// devres::Devres, /// io::{ /// Io, +/// IoBase, /// Mmio, /// MmioRaw, /// MmioBackend, @@ -105,7 +106,7 @@ struct Inner { /// } /// } /// -/// impl<'a, const SIZE: usize> Io<'a> for &'a IoMem { +/// impl<'a, const SIZE: usize> IoBase<'a> for &'a IoMem { /// type Backend = MmioBackend; /// type Target = Region; /// diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index 729b64a385c3..a9ff9e2c9f5c 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -224,7 +224,7 @@ fn io_view<'a, IO: Io<'a>, U>( /// operation. pub trait IoBackend { /// View type for this I/O backend. - type View<'a, T: ?Sized + KnownSize>: Io<'a, Backend = Self, Target = T>; + type View<'a, T: ?Sized + KnownSize>: IoBase<'a, Backend = Self, Target = T>; /// Convert a `view` to a raw pointer for projection. /// @@ -310,15 +310,12 @@ impl_usize_ioloc!(u8, u16, u32, u64); /// Types implementing this trait (e.g. MMIO BARs or PCI config regions) /// can perform I/O operations on regions of memory. /// -/// The [`Io`] trait provides: -/// - Method to convert into [`IoBackend::View`]. -/// - Helper methods for offset validation and address calculation -/// - Fallible (runtime checked) accessors for different data widths -/// -/// Which I/O methods are available depends on the associated [`IoBackend`] implementation. +/// This trait defines which backend shall be used for I/O operations and provides a method to +/// convert into [`IoBackend::View`]. Users should use the [`Io`] trait which provides the actual +/// methods to perform I/O operations. /// /// This should be implemented on cheaply copyable handles, such as references or view types. -pub trait Io<'a>: Copy { +pub trait IoBase<'a>: Copy { /// Type that defines all I/O operations. type Backend: IoBackend; @@ -327,6 +324,21 @@ pub trait Io<'a>: Copy { /// Return a view that covers the full region. fn as_view(self) -> ::View<'a, Self::Target>; +} + +/// Extension trait to provide I/O operation methods to types that implement [`IoBase`]. +/// +/// This trait provides: +/// - Helper methods for offset validation and address calculation +/// - Fallible (runtime checked) accessors for different data widths +/// +/// Which I/O methods are available depends on the associated [`IoBackend`] implementation. +pub trait Io<'a>: IoBase<'a> { + /// Returns the size of this I/O region. + #[inline] + fn size(self) -> usize { + KnownSize::size(Self::Backend::as_ptr(self.as_view())) + } /// Fallible 8-bit read with runtime bounds check. #[inline(always)] @@ -780,6 +792,10 @@ pub trait Io<'a>: Copy { } } +// Blanket implementation ensures that provided methods cannot be arbitrarily overridden by +// implementers, which is relied upon for correctness and soundness. +impl<'a, T: IoBase<'a>> Io<'a> for T {} + /// A view of memory-mapped I/O region. /// /// # Invariant @@ -820,7 +836,7 @@ unsafe impl Send for Mmio<'_, T> {} // SAFETY: `Mmio<'_, T>` is conceptually `&T` but in I/O memory. unsafe impl Sync for Mmio<'_, T> {} -impl<'a, T: ?Sized + KnownSize> Io<'a> for Mmio<'a, T> { +impl<'a, T: ?Sized + KnownSize> IoBase<'a> for Mmio<'a, T> { type Backend = MmioBackend; type Target = T; @@ -921,7 +937,7 @@ impl IoBackend for RelaxedMmioBackend { } } -impl<'a, T: ?Sized + KnownSize> Io<'a> for RelaxedMmio<'a, T> { +impl<'a, T: ?Sized + KnownSize> IoBase<'a> for RelaxedMmio<'a, T> { type Backend = RelaxedMmioBackend; type Target = T; diff --git a/rust/kernel/io/mem.rs b/rust/kernel/io/mem.rs index d9b3189d09b4..e95b769ebe47 100644 --- a/rust/kernel/io/mem.rs +++ b/rust/kernel/io/mem.rs @@ -14,7 +14,7 @@ use crate::{ Region, Resource, // }, - Io, + IoBase, Mmio, MmioBackend, MmioRaw, // @@ -210,7 +210,7 @@ impl<'a, const SIZE: usize> ExclusiveIoMem<'a, SIZE> { } } -impl<'a, const SIZE: usize> Io<'a> for &'a ExclusiveIoMem<'_, SIZE> { +impl<'a, const SIZE: usize> IoBase<'a> for &'a ExclusiveIoMem<'_, SIZE> { type Backend = MmioBackend; type Target = super::Region; @@ -292,7 +292,7 @@ impl Drop for IoMem<'_, SIZE> { } } -impl<'a, const SIZE: usize> Io<'a> for &'a IoMem<'_, SIZE> { +impl<'a, const SIZE: usize> IoBase<'a> for &'a IoMem<'_, SIZE> { type Backend = MmioBackend; type Target = super::Region; diff --git a/rust/kernel/pci/io.rs b/rust/kernel/pci/io.rs index 4be33ecb4192..4d1d0afdc491 100644 --- a/rust/kernel/pci/io.rs +++ b/rust/kernel/pci/io.rs @@ -8,8 +8,8 @@ use crate::{ device, devres::Devres, io::{ - Io, IoBackend, + IoBase, IoCapable, Mmio, MmioBackend, @@ -144,7 +144,7 @@ impl_config_space_io_capable!(u8, pci_read_config_byte, pci_write_config_byte); impl_config_space_io_capable!(u16, pci_read_config_word, pci_write_config_word); impl_config_space_io_capable!(u32, pci_read_config_dword, pci_write_config_dword); -impl<'a, T: ?Sized + KnownSize> Io<'a> for ConfigSpace<'a, T> { +impl<'a, T: ?Sized + KnownSize> IoBase<'a> for ConfigSpace<'a, T> { type Backend = ConfigSpaceBackend; type Target = T; @@ -267,7 +267,7 @@ impl Drop for Bar<'_, SIZE> { } } -impl<'a, const SIZE: usize> Io<'a> for &'a Bar<'_, SIZE> { +impl<'a, const SIZE: usize> IoBase<'a> for &'a Bar<'_, SIZE> { type Backend = MmioBackend; type Target = crate::io::Region; -- cgit v1.2.3