`Io::read`, `Io::write`, `Io::update` and their fallible variants project the view to the given `IoLoc` and access it there. A location has no say in which accesses are allowed, so a register cannot be declared read-only or write-only.
Add the `io::access` module with the `Mode` trait and the `ReadWrite`, `ReadOnly` and `WriteOnly` modes, and add an `Access` associated type to `IoLoc`. Projecting through a location applies its mode: `ReadWrite` leaves the view as it is, while `ReadOnly` and `WriteOnly` wrap it in the `ReadOnly` and `WriteOnly` views. The bounds of the generic accessors now apply to the backend of the restricted view, so a write to a read-only location, or a read from a write-only one, does not compile. Raw offsets are `ReadWrite`. `OffsetLoc` takes the mode as a type parameter that defaults to `ReadWrite`, and `RegisterArray` gets an `Access` type, which `register!` sets to `ReadWrite` for now. No existing location changes its mode, so there is no functional change. A following patch lets `register!` declare read-only and write-only registers. Signed-off-by: Daniel Almeida <[email protected]> --- rust/kernel/io.rs | 42 +++++++++++++++++-------- rust/kernel/io/access.rs | 76 ++++++++++++++++++++++++++++++++++++++++++++++ rust/kernel/io/register.rs | 18 +++++++++-- rust/macros/io/register.rs | 1 + 4 files changed, 121 insertions(+), 16 deletions(-) diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index 4be8c761c556..71bcd0d77ce9 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -22,6 +22,7 @@ }, // }; +pub mod access; #[cfg(CONFIG_HAS_IOMEM)] pub mod mem; pub mod poll; @@ -377,12 +378,16 @@ fn copy_write<T: IntoBytes>(view: Self::View<'_, T>, value: T) { /// /// - The valid `Base` to operate on. For most registers, this should be [`Region`]. /// - The offset to access (returned by [`IoLoc::offset`]), -/// - The type `T` in which the data is returned or provided. +/// - The type `T` in which the data is returned or provided, +/// - The access mode ([`IoLoc::Access`]) applied to the views projected through it. /// /// `T` is not necessarily the type for underlying I/O operation. Methods that take `IoLoc` have `T: /// AsRepr` bound and the `<T as AsRepr>::Repr` type would be used to perform I/O and converted to /// `T` instead. pub trait IoLoc<Base: ?Sized, T> { + /// Access mode applied to the views projected through this location. + type Access: access::Mode; + /// Consumes `self` and returns the offset of this location. fn offset(self) -> usize; } @@ -393,6 +398,8 @@ macro_rules! impl_usize_ioloc { ($($ty:ty),*) => { $( impl<const SIZE: usize> IoLoc<Region<SIZE>, $ty> for usize { + type Access = access::ReadWrite; + #[inline(always)] fn offset(self) -> usize { self @@ -961,7 +968,7 @@ fn try_read<T, L>(self, location: L) -> Result<T> where T: AsReprMut, L: IoLoc<Self::Target, T>, - Self::Backend: IoReadable<<T as AsRepr>::Repr>, + <L::Access as access::Mode>::Backend<Self::Backend>: IoReadable<<T as AsRepr>::Repr>, { Ok(io_read!(self, try: location)) } @@ -994,7 +1001,7 @@ fn try_write<T, L>(self, location: L, value: T) -> Result where T: AsRepr, L: IoLoc<Self::Target, T>, - Self::Backend: IoWritable<<T as AsRepr>::Repr>, + <L::Access as access::Mode>::Backend<Self::Backend>: IoWritable<<T as AsRepr>::Repr>, { io_write!(self, try: location, value); Ok(()) @@ -1040,7 +1047,7 @@ fn try_write_reg<T, L, V>(self, value: V) -> Result T: AsRepr, L: IoLoc<Self::Target, T>, V: LocatedRegister<Self::Target, Location = L, Value = T>, - Self::Backend: IoWritable<<T as AsRepr>::Repr>, + <L::Access as access::Mode>::Backend<Self::Backend>: IoWritable<<T as AsRepr>::Repr>, { let (location, value) = value.into_io_op(); @@ -1074,7 +1081,8 @@ fn try_update<T, L, F>(self, location: L, f: F) -> Result where T: AsReprMut, L: IoLoc<Self::Target, T>, - Self::Backend: IoReadable<<T as AsRepr>::Repr> + IoWritable<<T as AsRepr>::Repr>, + <L::Access as access::Mode>::Backend<Self::Backend>: + IoReadable<<T as AsRepr>::Repr> + IoWritable<<T as AsRepr>::Repr>, F: FnOnce(T) -> T, { let view = io_project!(self, try: location); @@ -1108,7 +1116,7 @@ fn read<T, L>(self, location: L) -> T where T: AsReprMut, L: IoLoc<Self::Target, T>, - Self::Backend: IoReadable<<T as AsRepr>::Repr>, + <L::Access as access::Mode>::Backend<Self::Backend>: IoReadable<<T as AsRepr>::Repr>, { io_read!(self, build: location) } @@ -1139,7 +1147,7 @@ fn write<T, L>(self, location: L, value: T) where T: AsRepr, L: IoLoc<Self::Target, T>, - Self::Backend: IoWritable<<T as AsRepr>::Repr>, + <L::Access as access::Mode>::Backend<Self::Backend>: IoWritable<<T as AsRepr>::Repr>, { io_write!(self, build: location, value); } @@ -1183,7 +1191,7 @@ fn write_reg<T, L, V>(self, value: V) T: AsRepr, L: IoLoc<Self::Target, T>, V: LocatedRegister<Self::Target, Location = L, Value = T>, - Self::Backend: IoWritable<<T as AsRepr>::Repr>, + <L::Access as access::Mode>::Backend<Self::Backend>: IoWritable<<T as AsRepr>::Repr>, { let (location, value) = value.into_io_op(); @@ -1217,7 +1225,8 @@ fn update<T, L, F>(self, location: L, f: F) where T: AsReprMut, L: IoLoc<Self::Target, T>, - Self::Backend: IoReadable<<T as AsRepr>::Repr> + IoWritable<<T as AsRepr>::Repr>, + <L::Access as access::Mode>::Backend<Self::Backend>: + IoReadable<<T as AsRepr>::Repr> + IoWritable<<T as AsRepr>::Repr>, F: FnOnce(T) -> T, { let view = io_project!(self, build: location); @@ -1909,19 +1918,26 @@ pub unsafe fn project_view<U: ?Sized + KnownSize>( pub fn try_project_loc<U, L>( self, location: L, - ) -> Result<<T::Backend as IoBackend>::View<'a, U>> + ) -> Result<access::RestrictedView<'a, L::Access, T::Backend, U>> where L: IoLoc<T::Target, U>, { - io_view::<_, U>(self.0, location.offset()) + let view = io_view::<_, U>(self.0, location.offset())?; + + Ok(<L::Access as access::Mode>::restrict::<T::Backend, U>(view)) } #[inline(always)] - pub fn project_loc<U, L>(self, location: L) -> <T::Backend as IoBackend>::View<'a, U> + pub fn project_loc<U, L>( + self, + location: L, + ) -> access::RestrictedView<'a, L::Access, T::Backend, U> where L: IoLoc<T::Target, U>, { - io_view_assert::<_, U>(self.0, location.offset()) + let view = io_view_assert::<_, U>(self.0, location.offset()); + + <L::Access as access::Mode>::restrict::<T::Backend, U>(view) } } diff --git a/rust/kernel/io/access.rs b/rust/kernel/io/access.rs new file mode 100644 index 000000000000..caec21dd943b --- /dev/null +++ b/rust/kernel/io/access.rs @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Access modes of I/O locations. +//! +//! An I/O location ([`IoLoc`](super::IoLoc)) has an access mode, which applies to the views +//! projected through it: a [`ReadWrite`] location gives the view as it is, a [`ReadOnly`] location +//! gives a [`ReadOnly`](super::ReadOnly) view, and a [`WriteOnly`] location gives a +//! [`WriteOnly`](super::WriteOnly) view. + +use crate::ptr::KnownSize; + +use super::{ + IoBackend, + ReadOnlyBackend, + WriteOnlyBackend, // +}; + +/// An access mode of an I/O location. +pub trait Mode { + /// Backend of the views this mode gives for views of backend `B`. + type Backend<B: IoBackend>: IoBackend; + + /// Applies this mode to `view`. + fn restrict<'a, B: IoBackend, T: ?Sized + KnownSize>( + view: B::View<'a, T>, + ) -> <Self::Backend<B> as IoBackend>::View<'a, T>; +} + +/// View of `T` that access mode `M` gives for views of backend `B`. +pub type RestrictedView<'a, M, B, T> = <<M as Mode>::Backend<B> as IoBackend>::View<'a, T>; + +/// Read-write access mode. +/// +/// Views are given as they are. +pub struct ReadWrite; + +impl Mode for ReadWrite { + type Backend<B: IoBackend> = B; + + #[inline(always)] + fn restrict<'a, B: IoBackend, T: ?Sized + KnownSize>(view: B::View<'a, T>) -> B::View<'a, T> { + view + } +} + +/// Read-only access mode. +/// +/// Views are wrapped in [`ReadOnly`](super::ReadOnly). +pub struct ReadOnly; + +impl Mode for ReadOnly { + type Backend<B: IoBackend> = ReadOnlyBackend<B>; + + #[inline(always)] + fn restrict<'a, B: IoBackend, T: ?Sized + KnownSize>( + view: B::View<'a, T>, + ) -> super::ReadOnly<B::View<'a, T>> { + super::ReadOnly(view) + } +} + +/// Write-only access mode. +/// +/// Views are wrapped in [`WriteOnly`](super::WriteOnly). +pub struct WriteOnly; + +impl Mode for WriteOnly { + type Backend<B: IoBackend> = WriteOnlyBackend<B>; + + #[inline(always)] + fn restrict<'a, B: IoBackend, T: ?Sized + KnownSize>( + view: B::View<'a, T>, + ) -> super::WriteOnly<B::View<'a, T>> { + super::WriteOnly(view) + } +} diff --git a/rust/kernel/io/register.rs b/rust/kernel/io/register.rs index b6513fa0f412..9218dc66017b 100644 --- a/rust/kernel/io/register.rs +++ b/rust/kernel/io/register.rs @@ -118,7 +118,10 @@ use crate::{ build_assert::build_assert, - io::IoLoc, // + io::{ + access, + IoLoc, // + }, }; /// Allows `()` to be used as the `location` parameter of [`Io::write`](super::Io::write) when @@ -127,6 +130,8 @@ impl<Base: ?Sized, T> IoLoc<Base, T> for () where T: FixedIoLoc<Base>, { + type Access = <T::Location as IoLoc<Base, T>>::Access; + #[inline(always)] fn offset(self) -> usize { T::LOCATION.offset() @@ -135,7 +140,7 @@ fn offset(self) -> usize { // Provides a `IoLoc` impl that for a fixed offset. #[doc(hidden)] -pub struct OffsetLoc<Base: ?Sized, T>(usize, PhantomData<(T, Base)>); +pub struct OffsetLoc<Base: ?Sized, T, A = access::ReadWrite>(usize, PhantomData<(T, A, Base)>); impl<Base: ?Sized, T> OffsetLoc<Base, T> { #[inline] @@ -149,7 +154,9 @@ pub const fn const_offset(self) -> usize { } } -impl<Base: ?Sized, T> IoLoc<Base, T> for OffsetLoc<Base, T> { +impl<Base: ?Sized, T, A: access::Mode> IoLoc<Base, T> for OffsetLoc<Base, T, A> { + type Access = A; + #[inline(always)] fn offset(self) -> usize { self.0 @@ -161,6 +168,9 @@ pub trait RegisterArray: Sized { /// Base type for this register. type Base: ?Sized; + /// Access mode of the registers in this array. + type Access: access::Mode; + /// Start offset of the register. /// /// The interpretation of this offset depends on the type of the register. @@ -198,6 +208,8 @@ impl<Base: ?Sized, T> IoLoc<Base, T> for RegisterArrayLoc<T> where T: RegisterArray<Base = Base>, { + type Access = T::Access; + #[inline(always)] fn offset(self) -> usize { T::OFFSET + self.0 * T::STRIDE diff --git a/rust/macros/io/register.rs b/rust/macros/io/register.rs index 420c3ad052b8..2d92c7f41447 100644 --- a/rust/macros/io/register.rs +++ b/rust/macros/io/register.rs @@ -283,6 +283,7 @@ impl ::kernel::io::register::Array for #name {} impl ::kernel::io::register::RegisterArray for #name { type Base = #base; + type Access = ::kernel::io::access::ReadWrite; const OFFSET: usize = #offset; const SIZE: usize = #size; const STRIDE: usize = #stride; -- 2.56.0
