diff options
Diffstat (limited to 'rust/kernel/io.rs')
| -rw-r--r-- | rust/kernel/io.rs | 176 |
1 files changed, 135 insertions, 41 deletions
diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index ec4ac42aa25d..081b4613bc12 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -4,6 +4,10 @@ //! //! C header: [`include/asm-generic/io.h`](srctree/include/asm-generic/io.h) +use core::{ + marker::PhantomData, // +}; + use crate::{ bindings, prelude::*, @@ -538,10 +542,11 @@ pub trait Io: Copy { /// ```no_run /// use kernel::io::{ /// Io, - /// MmioOwned, + /// Mmio, + /// Region, /// }; /// - /// fn do_reads(io: &MmioOwned) -> Result { + /// fn do_reads(io: Mmio<'_, Region>) -> Result { /// // 32-bit read from address `0x10`. /// let v: u32 = io.try_read(0x10)?; /// @@ -572,10 +577,11 @@ pub trait Io: Copy { /// ```no_run /// use kernel::io::{ /// Io, - /// MmioOwned, + /// Mmio, + /// Region, /// }; /// - /// fn do_writes(io: &MmioOwned) -> Result { + /// fn do_writes(io: Mmio<'_, Region>) -> Result { /// // 32-bit write of value `1` at address `0x10`. /// io.try_write(0x10, 1u32)?; /// @@ -610,7 +616,8 @@ pub trait Io: Copy { /// use kernel::io::{ /// register, /// Io, - /// MmioOwned, + /// Mmio, + /// Region, /// }; /// /// register! { @@ -626,7 +633,7 @@ pub trait Io: Copy { /// } /// } /// - /// fn do_write_reg(io: &MmioOwned) -> Result { + /// fn do_write_reg(io: Mmio<'_, Region>) -> Result { /// /// io.try_write_reg(VERSION::new(1, 0)) /// } @@ -655,10 +662,11 @@ pub trait Io: Copy { /// ```no_run /// use kernel::io::{ /// Io, - /// MmioOwned, + /// Mmio, + /// Region, /// }; /// - /// fn do_update(io: &MmioOwned<0x1000>) -> Result { + /// fn do_update(io: Mmio<'_, Region<0x1000>>) -> Result { /// io.try_update(0x10, |v: u32| { /// v + 1 /// }) @@ -692,10 +700,11 @@ pub trait Io: Copy { /// ```no_run /// use kernel::io::{ /// Io, - /// MmioOwned, + /// Mmio, + /// Region, /// }; /// - /// fn do_reads(io: &MmioOwned<0x1000>) { + /// fn do_reads(io: Mmio<'_, Region<0x1000>>) { /// // 32-bit read from address `0x10`. /// let v: u32 = io.read(0x10); /// @@ -724,10 +733,11 @@ pub trait Io: Copy { /// ```no_run /// use kernel::io::{ /// Io, - /// MmioOwned, + /// Mmio, + /// Region, /// }; /// - /// fn do_writes(io: &MmioOwned<0x1000>) { + /// fn do_writes(io: Mmio<'_, Region<0x1000>>) { /// // 32-bit write of value `1` at address `0x10`. /// io.write(0x10, 1u32); /// @@ -758,7 +768,8 @@ pub trait Io: Copy { /// use kernel::io::{ /// register, /// Io, - /// MmioOwned, + /// Mmio, + /// Region, /// }; /// /// register! { @@ -774,7 +785,7 @@ pub trait Io: Copy { /// } /// } /// - /// fn do_write_reg(io: &MmioOwned<0x1000>) { + /// fn do_write_reg(io: Mmio<'_, Region<0x1000>>) { /// io.write_reg(VERSION::new(1, 0)); /// } /// ``` @@ -802,10 +813,11 @@ pub trait Io: Copy { /// ```no_run /// use kernel::io::{ /// Io, - /// MmioOwned, + /// Mmio, + /// Region, /// }; /// - /// fn do_update(io: &MmioOwned<0x1000>) { + /// fn do_update(io: Mmio<'_, Region<0x1000>>) { /// io.update(0x10, |v: u32| { /// v + 1 /// }) @@ -829,16 +841,72 @@ pub trait Io: Copy { } } +/// A view of memory-mapped I/O region. +/// +/// # Invariant +/// +/// `ptr` points to a valid and aligned memory-mapped I/O region for the duration lifetime `'a`. +pub struct Mmio<'a, T: ?Sized> { + ptr: *mut T, + phantom: PhantomData<&'a ()>, +} + +impl<T: ?Sized> Copy for Mmio<'_, T> {} +impl<T: ?Sized> Clone for Mmio<'_, T> { + #[inline] + fn clone(&self) -> Self { + *self + } +} + +impl<'a, T: ?Sized> Mmio<'a, T> { + /// Create a `Mmio`, providing the accessors to the MMIO mapping. + /// + /// # Safety + /// + /// `raw` represents a valid and aligned memory-mapped I/O region while `'a` is alive. + #[inline] + pub unsafe fn from_raw(raw: MmioRaw<T>) -> Self { + // INVARIANT: Per safety requirement. + Self { + ptr: raw.ptr, + phantom: PhantomData, + } + } +} + +// SAFETY: `Mmio<'_, T>` is conceptually `&T` but in I/O memory. +unsafe impl<T: ?Sized + Sync> Send for Mmio<'_, T> {} + +// SAFETY: `Mmio<'_, T>` is conceptually `&T` but in I/O memory. +unsafe impl<T: ?Sized + Sync> Sync for Mmio<'_, T> {} + +impl<T: ?Sized + KnownSize> Io for Mmio<'_, T> { + type Target = T; + + #[inline] + fn addr(self) -> usize { + self.ptr.addr() + } + + #[inline] + fn maxsize(self) -> usize { + KnownSize::size(self.ptr) + } +} + /// Implements [`IoCapable`] on `$mmio` for `$ty` using `$read_fn` and `$write_fn`. macro_rules! impl_mmio_io_capable { ($mmio:ident, $(#[$attr:meta])* $ty:ty, $read_fn:ident, $write_fn:ident) => { $(#[$attr])* - impl<const SIZE: usize> IoCapable<$ty> for &$mmio<SIZE> { + impl<T: ?Sized> IoCapable<$ty> for $mmio<'_, T> { + #[inline] unsafe fn io_read(self, address: usize) -> $ty { // SAFETY: By the trait invariant `address` is a valid address for MMIO operations. unsafe { bindings::$read_fn(address as *const c_void) } } + #[inline] unsafe fn io_write(self, value: $ty, address: usize) { // SAFETY: By the trait invariant `address` is a valid address for MMIO operations. unsafe { bindings::$write_fn(value, address as *mut c_void) } @@ -848,17 +916,12 @@ macro_rules! impl_mmio_io_capable { } // MMIO regions support 8, 16, and 32-bit accesses. -impl_mmio_io_capable!(MmioOwned, u8, readb, writeb); -impl_mmio_io_capable!(MmioOwned, u16, readw, writew); -impl_mmio_io_capable!(MmioOwned, u32, readl, writel); +impl_mmio_io_capable!(Mmio, u8, readb, writeb); +impl_mmio_io_capable!(Mmio, u16, readw, writew); +impl_mmio_io_capable!(Mmio, u32, readl, writel); // MMIO regions on 64-bit systems also support 64-bit accesses. -impl_mmio_io_capable!( - MmioOwned, - #[cfg(CONFIG_64BIT)] - u64, - readq, - writeq -); +#[cfg(CONFIG_64BIT)] +impl_mmio_io_capable!(Mmio, u64, readq, writeq); impl<'a, const SIZE: usize> Io for &'a MmioOwned<SIZE> { type Target = Region<SIZE>; @@ -876,6 +939,23 @@ impl<'a, const SIZE: usize> Io for &'a MmioOwned<SIZE> { } } +impl<'a, const SIZE: usize, T> IoCapable<T> for &'a MmioOwned<SIZE> +where + Mmio<'a, Region<SIZE>>: IoCapable<T>, +{ + #[inline] + unsafe fn io_read(self, address: usize) -> T { + // SAFETY: Per safety requirement. + unsafe { self.as_view().io_read(address) } + } + + #[inline] + unsafe fn io_write(self, value: T, address: usize) { + // SAFETY: Per safety requirement. + unsafe { self.as_view().io_write(value, address) } + } +} + impl<const SIZE: usize> MmioOwned<SIZE> { /// Converts an `MmioRaw` into an `MmioOwned` instance, providing the accessors to the MMIO /// mapping. @@ -888,19 +968,33 @@ impl<const SIZE: usize> MmioOwned<SIZE> { // SAFETY: `MmioOwned` is a transparent wrapper around `MmioRaw`. unsafe { &*core::ptr::from_ref(raw).cast() } } + + /// Return a view that covers the full region. + #[inline] + pub fn as_view(&self) -> Mmio<'_, Region<SIZE>> { + // SAFETY: `Mmio` has same invariant as `MmioOwned`. + unsafe { Mmio::from_raw(self.0) } + } } -/// [`MmioOwned`] wrapper using relaxed accessors. +/// [`Mmio`] but using relaxed accessors. /// /// This type provides an implementation of [`Io`] that uses relaxed I/O MMIO operands instead of /// the regular ones. /// -/// See [`MmioOwned::relaxed`] for a usage example. -#[repr(transparent)] -pub struct RelaxedMmio<const SIZE: usize = 0>(MmioOwned<SIZE>); +/// See [`Mmio::relaxed`] for a usage example. +pub struct RelaxedMmio<'a, T: ?Sized>(Mmio<'a, T>); -impl<'a, const SIZE: usize> Io for &'a RelaxedMmio<SIZE> { - type Target = Region<SIZE>; +impl<T: ?Sized> Copy for RelaxedMmio<'_, T> {} +impl<T: ?Sized> Clone for RelaxedMmio<'_, T> { + #[inline] + fn clone(&self) -> Self { + *self + } +} + +impl<T: ?Sized + KnownSize> Io for RelaxedMmio<'_, T> { + type Target = T; #[inline] fn addr(self) -> usize { @@ -913,8 +1007,8 @@ impl<'a, const SIZE: usize> Io for &'a RelaxedMmio<SIZE> { } } -impl<const SIZE: usize> MmioOwned<SIZE> { - /// Returns a [`RelaxedMmio`] reference that performs relaxed I/O operations. +impl<'a, T: ?Sized> Mmio<'a, T> { + /// Returns a [`RelaxedMmio`] that performs relaxed I/O operations. /// /// Relaxed accessors do not provide ordering guarantees with respect to DMA or memory accesses /// and can be used when such ordering is not required. @@ -924,20 +1018,20 @@ impl<const SIZE: usize> MmioOwned<SIZE> { /// ```no_run /// use kernel::io::{ /// Io, - /// MmioOwned, + /// Mmio, + /// Region, /// RelaxedMmio, /// }; /// - /// fn do_io(io: &MmioOwned<0x100>) { + /// fn do_io(io: Mmio<'_, Region<0x100>>) { /// // The access is performed using `readl_relaxed` instead of `readl`. /// let v = io.relaxed().read32(0x10); /// } /// /// ``` - pub fn relaxed(&self) -> &RelaxedMmio<SIZE> { - // SAFETY: `RelaxedMmio` is `#[repr(transparent)]` over `MmioOwned`, so `MmioOwned<SIZE>` - // and `RelaxedMmio<SIZE>` have identical layout. - unsafe { core::mem::transmute(self) } + #[inline] + pub fn relaxed(self) -> RelaxedMmio<'a, T> { + RelaxedMmio(self) } } |
