adds a `from_raw_cookie` function to the IoAccess trait. `from_raw_cookie` attempts to convert a iomem address that can be accessed by the ioread/iowrite family of C functions into either a `Io` or `MMIo`.
This is done so that devices that know what type of Io they are at compile time can give a hint about their type. Suggested-by: Danilo Krummrich <d...@kernel.org> Signed-off-by: Andrew Ballance <andrewjballa...@gmail.com> --- rust/kernel/io.rs | 73 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index 9445451f4b02..81b26602d3bc 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -6,6 +6,47 @@ use crate::error::{code::EINVAL, Result}; use crate::{bindings, build_assert}; +use io_backend::*; + +/// `io_backend` is private and implements the config specific logic for +/// `IoAccess::from_raw_cookie`. +#[cfg(all(CONFIG_X86, CONFIG_GENERIC_IOMAP))] +mod io_backend { + // if on x86, generic_iomap is enabled so copy the logic + // from IO_COND in `lib/iomap.c` + + // values copied from `lib/iomap.c` + const PIO_OFFSET: usize = 0x10000; + const PIO_RESERVED: usize = 0x40000; + + #[inline] + pub(super) fn is_mmio(addr: usize) -> bool { + addr >= PIO_RESERVED + } + + #[inline] + pub(super) fn is_portio(addr: usize) -> bool { + !is_mmio(addr) && addr > PIO_OFFSET + } +} +#[cfg(not(CONFIG_GENERIC_IOMAP))] +mod io_backend { + // for everyone who does not use generic iomap + // except for alpha and parisc, neither of which has a rust compiler, + // ioread/iowrite is defined in `include/asm-generic/io.h`. + // + // for these ioread/iowrite, maps to read/write. + // so allow any io to be converted because they use the same backend + #[inline] + pub(super) fn is_mmio(_addr: usize) -> bool { + true + } + + #[inline] + pub(super) fn is_portio(_addr: usize) -> bool { + false + } +} /// Private macro to define the [`IoAccess`] functions. macro_rules! define_io_access_function { @@ -162,6 +203,14 @@ pub unsafe trait IoAccess<const SIZE: usize = 0> { /// Returns the base address of the accessed IO area. fn addr(&self) -> usize; + /// Attempts to create a `Self` from a [`IoRaw`]. + /// + /// # Safety + /// `raw` should be a io cookie that can be accessed by the C `ioread`/`iowrite` functions + unsafe fn from_raw_cookie(raw: IoRaw<SIZE>) -> Result<Self> + where + Self: Sized; + define_io_access_function!(@read read8_unchecked, read8, try_read8, u8; read16_unchecked, read16, try_read16, u16; @@ -366,6 +415,18 @@ fn addr(&self) -> usize { self.0.addr() } + unsafe fn from_raw_cookie(raw: IoRaw<SIZE>) -> Result<Self> + where + Self: Sized, + { + if is_mmio(raw.addr()) { + // INVARIANT: `addr` is checked so it should be ok to access with read/write + Ok(Self(raw)) + } else { + Err(EINVAL) + } + } + impl_accessor_fn!( read8_unchecked, readb, write8_unchecked, writeb, u8; read16_unchecked, readw, write16_unchecked, writew, u16; @@ -474,6 +535,18 @@ fn maxsize(&self) -> usize { self.0.maxsize() } + unsafe fn from_raw_cookie(raw: IoRaw<SIZE>) -> Result<Self> + where + Self: Sized, + { + if is_mmio(raw.addr()) || is_portio(raw.addr()) { + // INVARIANT: `addr` is not touched so it should be able to be read with ioread/iowrite + Ok(Self(raw)) + } else { + Err(EINVAL) + } + } + impl_accessor_fn!( read8_unchecked, ioread8, write8_unchecked, iowrite8, u8; read16_unchecked, ioread16, write16_unchecked, iowrite16, u16; -- 2.49.0