The existing `Io<SIZE>` implementation embedded utility methods such
as `addr()`, `maxsize()`, and offset validation helpers directly on the
struct.
To allow sharing of I/O region check logic across different region types
(e.g. MMIO BARs, PCI config space), move common bound-checking and
address arithmetic into a reusable trait.
No functional change intended.
Signed-off-by: Zhi Wang <zhiw@nvidia.com>
---
rust/kernel/io.rs | 65 ++++++++++++++++++++++++++++++++---------------
1 file changed, 44 insertions(+), 21 deletions(-)
diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
index 03b467722b86..f4727f3b954e 100644
--- a/rust/kernel/io.rs
+++ b/rust/kernel/io.rs
@@ -171,32 +171,24 @@ pub fn $try_name(&self, value: $type_name, offset: usize) -> Result {
};
}
-impl<const SIZE: usize> Io<SIZE> {
- /// Converts an `IoRaw` into an `Io` 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: &IoRaw<SIZE>) -> &Self {
- // SAFETY: `Io` is a transparent wrapper around `IoRaw`.
- unsafe { &*core::ptr::from_ref(raw).cast() }
- }
-
+/// Represents a region of I/O space of a fixed size.
+///
+/// Provides common helpers for offset validation and address
+/// calculation on top of a base address and maximum size.
+///
+/// Types implementing this trait (e.g. MMIO BARs or PCI config
+/// regions) can share the same accessors.
+pub trait IoRegion<const SIZE: usize> {
/// Returns the base address of this mapping.
- #[inline]
- pub fn addr(&self) -> usize {
- self.0.addr()
- }
+ fn addr(&self) -> usize;
/// Returns the maximum size of this mapping.
- #[inline]
- pub fn maxsize(&self) -> usize {
- self.0.maxsize()
- }
+ fn maxsize(&self) -> usize;
+ /// Checks whether an access of type `U` at the given `offset`
+ /// is valid within this region.
#[inline]
- const fn offset_valid<U>(offset: usize, size: usize) -> bool {
+ fn offset_valid<U>(offset: usize, size: usize) -> bool {
let type_size = core::mem::size_of::<U>();
if let Some(end) = offset.checked_add(type_size) {
end <= size && offset % type_size == 0
@@ -205,6 +197,8 @@ const fn offset_valid<U>(offset: usize, size: usize) -> bool {
}
}
+ /// Returns the absolute I/O address for a given `offset`.
+ /// Performs runtime bounds checks using [`offset_valid`]
#[inline]
fn io_addr<U>(&self, offset: usize) -> Result<usize> {
if !Self::offset_valid::<U>(offset, self.maxsize()) {
@@ -216,12 +210,41 @@ fn io_addr<U>(&self, offset: usize) -> Result<usize> {
self.addr().checked_add(offset).ok_or(EINVAL)
}
+ /// Returns the absolute I/O address for a given `offset`,
+ /// performing compile-time bound checks.
#[inline]
fn io_addr_assert<U>(&self, offset: usize) -> usize {
build_assert!(Self::offset_valid::<U>(offset, SIZE));
self.addr() + offset
}
+}
+
+impl<const SIZE: usize> IoRegion<SIZE> for Io<SIZE> {
+ /// Returns the base address of this mapping.
+ #[inline]
+ fn addr(&self) -> usize {
+ self.0.addr()
+ }
+
+ /// Returns the maximum size of this mapping.
+ #[inline]
+ fn maxsize(&self) -> usize {
+ self.0.maxsize()
+ }
+}
+
+impl<const SIZE: usize> Io<SIZE> {
+ /// Converts an `IoRaw` into an `Io` 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: &IoRaw<SIZE>) -> &Self {
+ // SAFETY: `Io` is a transparent wrapper around `IoRaw`.
+ unsafe { &*core::ptr::from_ref(raw).cast() }
+ }
define_read!(read8, try_read8, readb -> u8);
define_read!(read16, try_read16, readw -> u16);
--
2.47.3