mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 04:34:03 +02:00
rust: io: add projection macro and methods
Add an `io_project!()` macro to allow projection from `Io` to a subview of it, using the pointer projection mechanism to perform compile-time checks. For cases where type-casting is required, the `try_cast()` function may be used where the size and alignment checks are performed at runtime. Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Signed-off-by: Gary Guo <gary@garyguo.net> Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com> Link: https://patch.msgid.link/20260706-io_projection-v6-13-72cd5d055d54@garyguo.net Signed-off-by: Danilo Krummrich <dakr@kernel.org>
This commit is contained in:
parent
9b36c13cbd
commit
1f989555fe
|
|
@ -49,6 +49,7 @@
|
|||
/// - Size of the region is at least as large as the `SIZE` generic parameter.
|
||||
/// - Size of the region is multiple of 4.
|
||||
#[repr(C, align(4))]
|
||||
#[derive(FromBytes)]
|
||||
pub struct Region<const SIZE: usize = 0> {
|
||||
inner: [u8],
|
||||
}
|
||||
|
|
@ -91,6 +92,19 @@ fn size(p: *const Self) -> usize {
|
|||
}
|
||||
}
|
||||
|
||||
// SAFETY:
|
||||
// - Values read from I/O are always treated as initialized.
|
||||
// - Per type invariant the size is multiple of 4 and the type is 4-byte aligned, so it is padding
|
||||
// free.
|
||||
//
|
||||
// This cannot be derived as `derive(IntoBytes)` as the padding free property comes from type
|
||||
// invariant which the macro does not know.
|
||||
unsafe impl<const SIZE: usize> IntoBytes for Region<SIZE> {
|
||||
#[inline]
|
||||
#[allow(unused)] // Rust 1.87+ stops requiring this and will emit unused warnings.
|
||||
fn only_derive_is_allowed_to_implement_this_trait() {}
|
||||
}
|
||||
|
||||
/// Raw representation of an MMIO region.
|
||||
///
|
||||
/// `MmioRaw<T>` is equivalent to `T __iomem *` in C.
|
||||
|
|
@ -340,6 +354,51 @@ fn size(self) -> usize {
|
|||
KnownSize::size(Self::Backend::as_ptr(self.as_view()))
|
||||
}
|
||||
|
||||
/// Try to convert into a different typed I/O view.
|
||||
///
|
||||
/// A runtime check is performed to ensure that the target type is of same or smaller size to
|
||||
/// current type, and the current view is properly aligned for the target type. Returns
|
||||
/// `Err(EINVAL)` if the runtime check fails.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use kernel::io::{
|
||||
/// io_project,
|
||||
/// Mmio,
|
||||
/// Io,
|
||||
/// Region,
|
||||
/// };
|
||||
/// #[derive(FromBytes, IntoBytes)]
|
||||
/// #[repr(C)]
|
||||
/// struct MyStruct { field: u32, }
|
||||
///
|
||||
/// # fn test(mmio: &Mmio<'_, Region>) -> Result {
|
||||
/// // let mmio: Mmio<'_, Region>;
|
||||
/// let whole: Mmio<'_, MyStruct> = mmio.try_cast()?;
|
||||
/// # Ok::<(), Error>(()) }
|
||||
/// ```
|
||||
#[inline]
|
||||
fn try_cast<U>(self) -> Result<<Self::Backend as IoBackend>::View<'a, U>>
|
||||
where
|
||||
Self::Target: FromBytes + IntoBytes,
|
||||
U: FromBytes + IntoBytes,
|
||||
{
|
||||
let view = self.as_view();
|
||||
let ptr = Self::Backend::as_ptr(view);
|
||||
|
||||
if size_of::<U>() > KnownSize::size(ptr) {
|
||||
return Err(EINVAL);
|
||||
}
|
||||
|
||||
if ptr.addr() % align_of::<U>() != 0 {
|
||||
return Err(EINVAL);
|
||||
}
|
||||
|
||||
// SAFETY: We have checked bounds and alignment, so this is a valid projection.
|
||||
Ok(unsafe { Self::Backend::project_view(view, ptr.cast()) })
|
||||
}
|
||||
|
||||
/// Fallible 8-bit read with runtime bounds check.
|
||||
#[inline(always)]
|
||||
fn try_read8(self, offset: usize) -> Result<u8>
|
||||
|
|
@ -982,3 +1041,72 @@ pub fn relaxed(self) -> RelaxedMmio<'a, T> {
|
|||
// MMIO regions on 64-bit systems also support 64-bit accesses.
|
||||
#[cfg(CONFIG_64BIT)]
|
||||
impl_mmio_io_capable!(RelaxedMmioBackend, u64, readq_relaxed, writeq_relaxed);
|
||||
|
||||
// This helper turns associated functions to methods so it can be invoked in macro.
|
||||
// Used by `io_project!()` only.
|
||||
#[doc(hidden)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct ProjectHelper<T>(pub T);
|
||||
|
||||
impl<'a, T> ProjectHelper<T>
|
||||
where
|
||||
T: Io<'a, Backend: IoBackend<View<'a, T::Target> = T>>,
|
||||
{
|
||||
// These helper methods must not have symbols present in the binary to avoid confusion.
|
||||
#[inline(always)]
|
||||
pub fn as_ptr(self) -> *mut T::Target {
|
||||
T::Backend::as_ptr(self.0)
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// Same as `IoBackend::project_view`
|
||||
#[inline(always)]
|
||||
pub unsafe fn project_view<U: ?Sized + KnownSize>(
|
||||
self,
|
||||
ptr: *mut U,
|
||||
) -> <T::Backend as IoBackend>::View<'a, U> {
|
||||
// SAFETY: Per safety requirement.
|
||||
unsafe { T::Backend::project_view::<T::Target, _>(self.0, ptr) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Project an I/O type to a subview of it.
|
||||
///
|
||||
/// The syntax is of form `io_project!(io, proj)` where `io` is an expression to a type that
|
||||
/// implements [`Io`] and `proj` is a [projection specification](kernel::ptr::project!).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use kernel::io::{
|
||||
/// io_project,
|
||||
/// Mmio,
|
||||
/// };
|
||||
/// #[repr(C)]
|
||||
/// struct MyStruct { field: u32, }
|
||||
///
|
||||
/// # fn test(mmio: Mmio<'_, [MyStruct]>) -> Result {
|
||||
/// // let mmio: Mmio<[MyStruct]>;
|
||||
/// let field: Mmio<'_, u32> = io_project!(mmio, [try: 1].field);
|
||||
/// let whole: Mmio<'_, MyStruct> = io_project!(mmio, [try: 2]);
|
||||
/// let nested: Mmio<'_, u32> = io_project!(whole, .field);
|
||||
/// # Ok::<(), Error>(()) }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! io_project {
|
||||
($io:expr, $($proj:tt)*) => {{
|
||||
#[allow(unused)]
|
||||
use $crate::io::IoBase as _;
|
||||
let view = $crate::io::ProjectHelper($io.as_view());
|
||||
let ptr = $crate::ptr::project!(
|
||||
mut view.as_ptr(), $($proj)*
|
||||
);
|
||||
#[allow(unused_unsafe)]
|
||||
// SAFETY: `ptr` is a projection.
|
||||
unsafe { view.project_view(ptr) }
|
||||
}};
|
||||
}
|
||||
#[doc(inline)]
|
||||
pub use crate::io_project;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user