The current safety comment on `io_read`/`io_write` does not cover the topic about alignment. Add it so it can be relied on by implementor of `IoCapable`.
Expand the check `Io` by taking `self.addr()` into consideration when checking if `offset` is aligned. For the compile-time `io_addr_assert` check, check using the known minimum alignment of `IO::Target` and the accessed type. Signed-off-by: Gary Guo <[email protected]> --- rust/kernel/io.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index dcf3b40ffa48..a58fb9a21dca 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -197,13 +197,15 @@ pub fn maxsize(&self) -> usize { #[repr(transparent)] pub struct Mmio<const SIZE: usize = 0>(MmioRaw<SIZE>); -/// 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<U>(offset: usize, size: usize) -> bool { +const fn offset_valid<U>(base: usize, 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 + end <= size && (base.wrapping_add(offset) % type_size == 0) } else { false } @@ -222,14 +224,16 @@ pub trait IoCapable<T> { /// /// # Safety /// - /// The range `[address..address + size_of::<T>()]` must be within the bounds of `Self`. + /// - The range `[address..address + size_of::<T>()]` 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::<T>()]` must be within the bounds of `Self`. + /// - The range `[address..address + size_of::<T>()]` must be within the bounds of `Self`. + /// - `address` must be aligned. unsafe fn io_write(&self, value: T, address: usize); } @@ -311,7 +315,11 @@ pub trait Io { // Always inline to optimize out error path of `build_assert`. #[inline(always)] fn io_addr_assert<U>(&self, offset: usize) -> usize { - build_assert!(offset_valid::<U>(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::<U>().as_usize() <= Self::Target::MIN_ALIGN.as_usize()); + build_assert!(offset_valid::<U>(0, offset, Self::Target::MIN_SIZE)); self.addr() + offset } @@ -320,7 +328,7 @@ fn io_addr_assert<U>(&self, offset: usize) -> usize { /// performing runtime bound checks. #[inline] fn io_addr<U>(&self, offset: usize) -> Result<usize> { - if !offset_valid::<U>(offset, self.maxsize()) { + if !offset_valid::<U>(self.addr(), offset, self.maxsize()) { return Err(EINVAL); } -- 2.54.0
