diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index b4cfa3588098..c9597065a776 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -196,13 +196,14 @@ pub fn maxsize(&self) -> usize { #[repr(transparent)] pub struct Mmio(MmioRaw); -/// Checks whether an access of type `U` at the given `offset` +/// Checks whether an access of type `U` at the given `base` and the given `offset` /// is valid within this region. +/// +/// The `base` is used for alignment checking only. This can be set to 0 to skip the check. #[inline] -const fn offset_valid(offset: usize, size: usize) -> bool { - let type_size = core::mem::size_of::(); - if let Some(end) = offset.checked_add(type_size) { - end <= size && offset % type_size == 0 +const fn offset_valid(base: usize, offset: usize, size: usize) -> bool { + if let Some(end) = offset.checked_add(size_of::()) { + end <= size && (base.wrapping_add(offset) % align_of::() == 0) } else { false } @@ -221,14 +222,16 @@ pub trait IoCapable { /// /// # Safety /// - /// The range `[address..address + size_of::()]` must be within the bounds of `Self`. + /// - The range `[address..address + size_of::()]` must be within the bounds of `Self`. + /// - `address` must be aligned. unsafe fn io_read(&self, address: usize) -> T; /// Performs an I/O write of `value` at `address`. /// /// # Safety /// - /// The range `[address..address + size_of::()]` must be within the bounds of `Self`. + /// - The range `[address..address + size_of::()]` must be within the bounds of `Self`. + /// - `address` must be aligned. unsafe fn io_write(&self, value: T, address: usize); } @@ -310,7 +313,11 @@ pub trait Io { // Always inline to optimize out error path of `build_assert`. #[inline(always)] fn io_addr_assert(&self, offset: usize) -> usize { - build_assert!(offset_valid::(offset, Self::Target::MIN_SIZE)); + // We cannot check alignment with `offset_valid` using `self.addr()`. So set 0 for it and + // ensure alignment by checking that the alignment of `U` is smaller or equal to the + // alignment of `Self::Target`. + const_assert!(Alignment::of::().as_usize() <= Self::Target::MIN_ALIGN.as_usize()); + build_assert!(offset_valid::(0, offset, Self::Target::MIN_SIZE)); self.addr() + offset } @@ -319,7 +326,7 @@ fn io_addr_assert(&self, offset: usize) -> usize { /// performing runtime bound checks. #[inline] fn io_addr(&self, offset: usize) -> Result { - if !offset_valid::(offset, self.maxsize()) { + if !offset_valid::(self.addr(), offset, self.maxsize()) { return Err(EINVAL); }