mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
rust: io: add dynamically-sized Region type
Currently many I/O related structs carry a `SIZE` parameter to denote the minimum size of the I/O region, while they also carry a field indicating the actual size. Proliferation of the pattern creates a lot of duplicated code, and makes it hard to create typed views of I/O. Introduce a `Region` type that carries the `SIZE` parameter. It is a wrapper of `[u8]`, which makes it dynamically sized with a metadata of `usize`. This way, pointers to `Region` naturally carry size information. This type is required to be 4-byte aligned. Expose the minimum size information via `MIN_SIZE` constant of the `KnownSize` trait. Similarly, expose the minimum alignment information via `KnownSize::MIN_ALIGN`. With these changes, it is possible to add an associated type to `Io` trait to represent the type of I/O region. For untyped regions, this is the newly added `Region` type. Remove `IoKnownSize` as it is no longer necessary. Use the same mechanism to indicate minimum size of PCI config spaces. 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-1-72cd5d055d54@garyguo.net [ Add brief explanation on MIN_ALIGN. - Danilo ] Signed-off-by: Danilo Krummrich <dakr@kernel.org>
This commit is contained in:
committed by
Danilo Krummrich
parent
dc59e4fea9
commit
426c92ca1b
@@ -68,7 +68,6 @@ struct Inner<T> {
|
||||
/// devres::Devres,
|
||||
/// io::{
|
||||
/// Io,
|
||||
/// IoKnownSize,
|
||||
/// Mmio,
|
||||
/// MmioRaw,
|
||||
/// PhysAddr, //
|
||||
@@ -297,10 +296,7 @@ impl<T: Send + 'static> Devres<T> {
|
||||
/// use kernel::{
|
||||
/// device::Core,
|
||||
/// devres::Devres,
|
||||
/// io::{
|
||||
/// Io,
|
||||
/// IoKnownSize, //
|
||||
/// },
|
||||
/// io::Io,
|
||||
/// pci, //
|
||||
/// };
|
||||
///
|
||||
|
||||
+86
-44
@@ -6,7 +6,11 @@
|
||||
|
||||
use crate::{
|
||||
bindings,
|
||||
prelude::*, //
|
||||
prelude::*,
|
||||
ptr::{
|
||||
Alignment,
|
||||
KnownSize, //
|
||||
}, //
|
||||
};
|
||||
|
||||
pub mod mem;
|
||||
@@ -31,6 +35,58 @@ pub type PhysAddr = bindings::phys_addr_t;
|
||||
/// `CONFIG_PHYS_ADDR_T_64BIT`, and it can be a u64 even on 32-bit architectures.
|
||||
pub type ResourceSize = bindings::resource_size_t;
|
||||
|
||||
/// Untyped I/O region.
|
||||
///
|
||||
/// This type can be used when an I/O region without known type information has a compile-time known
|
||||
/// minimum size (and a runtime known actual size).
|
||||
///
|
||||
/// # Invariants
|
||||
///
|
||||
/// - 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))]
|
||||
pub struct Region<const SIZE: usize = 0> {
|
||||
inner: [u8],
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> Region<SIZE> {
|
||||
/// Create a raw mutable pointer from given base address and size.
|
||||
///
|
||||
/// `size` should be at least as large as the minimum size `SIZE`, and `base` and `size` should
|
||||
/// be 4-byte aligned to uphold the type invariant.
|
||||
///
|
||||
/// Just like other methods on raw pointers, it is not unsafe to create a raw pointer
|
||||
/// that does not uphold the type invariants. However such pointers are not valid.
|
||||
#[inline]
|
||||
pub fn ptr_from_raw_parts_mut(base: *mut u8, size: usize) -> *mut Self {
|
||||
core::ptr::slice_from_raw_parts_mut(base, size) as *mut Region<SIZE>
|
||||
}
|
||||
|
||||
/// Create a raw mutable pointer from given base address and size.
|
||||
///
|
||||
/// The alignment of `base` is checked, and `size` is checked against the minimum size specified
|
||||
/// via const generics.
|
||||
#[inline]
|
||||
pub fn ptr_try_from_raw_parts_mut(base: *mut u8, size: usize) -> Result<*mut Self> {
|
||||
if size < SIZE || base.align_offset(4) != 0 || !size.is_multiple_of(4) {
|
||||
return Err(EINVAL);
|
||||
}
|
||||
|
||||
Ok(Self::ptr_from_raw_parts_mut(base, size))
|
||||
}
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> KnownSize for Region<SIZE> {
|
||||
const MIN_SIZE: usize = SIZE;
|
||||
// Alignment of 4 is the most common; different base types can be added once required.
|
||||
const MIN_ALIGN: Alignment = Alignment::new::<4>();
|
||||
|
||||
#[inline(always)]
|
||||
fn size(p: *const Self) -> usize {
|
||||
(p as *const [u8]).len()
|
||||
}
|
||||
}
|
||||
|
||||
/// Raw representation of an MMIO region.
|
||||
///
|
||||
/// By itself, the existence of an instance of this structure does not provide any guarantees that
|
||||
@@ -85,7 +141,6 @@ impl<const SIZE: usize> MmioRaw<SIZE> {
|
||||
/// ffi::c_void,
|
||||
/// io::{
|
||||
/// Io,
|
||||
/// IoKnownSize,
|
||||
/// Mmio,
|
||||
/// MmioRaw,
|
||||
/// PhysAddr,
|
||||
@@ -241,12 +296,25 @@ impl_usize_ioloc!(u8, u16, u32, u64);
|
||||
/// For MMIO regions, all widths (u8, u16, u32, and u64 on 64-bit systems) are typically
|
||||
/// supported. For PCI configuration space, u8, u16, and u32 are supported but u64 is not.
|
||||
pub trait Io {
|
||||
/// Type of this I/O region. For untyped regions, [`Region`] can be used.
|
||||
type Target: ?Sized + KnownSize;
|
||||
|
||||
/// Returns the base address of this mapping.
|
||||
fn addr(&self) -> usize;
|
||||
|
||||
/// Returns the maximum size of this mapping.
|
||||
fn maxsize(&self) -> usize;
|
||||
|
||||
/// Returns the absolute I/O address for a given `offset`,
|
||||
/// performing compile-time bound checks.
|
||||
// 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));
|
||||
|
||||
self.addr() + offset
|
||||
}
|
||||
|
||||
/// Returns the absolute I/O address for a given `offset`,
|
||||
/// performing runtime bound checks.
|
||||
#[inline]
|
||||
@@ -336,7 +404,7 @@ pub trait Io {
|
||||
#[inline(always)]
|
||||
fn read8(&self, offset: usize) -> u8
|
||||
where
|
||||
Self: IoKnownSize + IoCapable<u8>,
|
||||
Self: IoCapable<u8>,
|
||||
{
|
||||
self.read(offset)
|
||||
}
|
||||
@@ -345,7 +413,7 @@ pub trait Io {
|
||||
#[inline(always)]
|
||||
fn read16(&self, offset: usize) -> u16
|
||||
where
|
||||
Self: IoKnownSize + IoCapable<u16>,
|
||||
Self: IoCapable<u16>,
|
||||
{
|
||||
self.read(offset)
|
||||
}
|
||||
@@ -354,7 +422,7 @@ pub trait Io {
|
||||
#[inline(always)]
|
||||
fn read32(&self, offset: usize) -> u32
|
||||
where
|
||||
Self: IoKnownSize + IoCapable<u32>,
|
||||
Self: IoCapable<u32>,
|
||||
{
|
||||
self.read(offset)
|
||||
}
|
||||
@@ -363,7 +431,7 @@ pub trait Io {
|
||||
#[inline(always)]
|
||||
fn read64(&self, offset: usize) -> u64
|
||||
where
|
||||
Self: IoKnownSize + IoCapable<u64>,
|
||||
Self: IoCapable<u64>,
|
||||
{
|
||||
self.read(offset)
|
||||
}
|
||||
@@ -372,7 +440,7 @@ pub trait Io {
|
||||
#[inline(always)]
|
||||
fn write8(&self, value: u8, offset: usize)
|
||||
where
|
||||
Self: IoKnownSize + IoCapable<u8>,
|
||||
Self: IoCapable<u8>,
|
||||
{
|
||||
self.write(offset, value)
|
||||
}
|
||||
@@ -381,7 +449,7 @@ pub trait Io {
|
||||
#[inline(always)]
|
||||
fn write16(&self, value: u16, offset: usize)
|
||||
where
|
||||
Self: IoKnownSize + IoCapable<u16>,
|
||||
Self: IoCapable<u16>,
|
||||
{
|
||||
self.write(offset, value)
|
||||
}
|
||||
@@ -390,7 +458,7 @@ pub trait Io {
|
||||
#[inline(always)]
|
||||
fn write32(&self, value: u32, offset: usize)
|
||||
where
|
||||
Self: IoKnownSize + IoCapable<u32>,
|
||||
Self: IoCapable<u32>,
|
||||
{
|
||||
self.write(offset, value)
|
||||
}
|
||||
@@ -399,7 +467,7 @@ pub trait Io {
|
||||
#[inline(always)]
|
||||
fn write64(&self, value: u64, offset: usize)
|
||||
where
|
||||
Self: IoKnownSize + IoCapable<u64>,
|
||||
Self: IoCapable<u64>,
|
||||
{
|
||||
self.write(offset, value)
|
||||
}
|
||||
@@ -582,7 +650,7 @@ pub trait Io {
|
||||
fn read<T, L>(&self, location: L) -> T
|
||||
where
|
||||
L: IoLoc<T>,
|
||||
Self: IoKnownSize + IoCapable<L::IoType>,
|
||||
Self: IoCapable<L::IoType>,
|
||||
{
|
||||
let address = self.io_addr_assert::<L::IoType>(location.offset());
|
||||
|
||||
@@ -614,7 +682,7 @@ pub trait Io {
|
||||
fn write<T, L>(&self, location: L, value: T)
|
||||
where
|
||||
L: IoLoc<T>,
|
||||
Self: IoKnownSize + IoCapable<L::IoType>,
|
||||
Self: IoCapable<L::IoType>,
|
||||
{
|
||||
let address = self.io_addr_assert::<L::IoType>(location.offset());
|
||||
let io_value = value.into();
|
||||
@@ -658,7 +726,7 @@ pub trait Io {
|
||||
where
|
||||
L: IoLoc<T>,
|
||||
V: LocatedRegister<Location = L, Value = T>,
|
||||
Self: IoKnownSize + IoCapable<L::IoType>,
|
||||
Self: IoCapable<L::IoType>,
|
||||
{
|
||||
let (location, value) = value.into_io_op();
|
||||
|
||||
@@ -690,7 +758,7 @@ pub trait Io {
|
||||
fn update<T, L, F>(&self, location: L, f: F)
|
||||
where
|
||||
L: IoLoc<T>,
|
||||
Self: IoKnownSize + IoCapable<L::IoType> + Sized,
|
||||
Self: IoCapable<L::IoType> + Sized,
|
||||
F: FnOnce(T) -> T,
|
||||
{
|
||||
let address = self.io_addr_assert::<L::IoType>(location.offset());
|
||||
@@ -704,28 +772,6 @@ pub trait Io {
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for types with a known size at compile time.
|
||||
///
|
||||
/// This trait is implemented by I/O backends that have a compile-time known size,
|
||||
/// enabling the use of infallible I/O accessors with compile-time bounds checking.
|
||||
///
|
||||
/// Types implementing this trait can use the infallible methods in [`Io`] trait
|
||||
/// (e.g., `read8`, `write32`), which require `Self: IoKnownSize` bound.
|
||||
pub trait IoKnownSize: Io {
|
||||
/// Minimum usable size of this region.
|
||||
const MIN_SIZE: usize;
|
||||
|
||||
/// Returns the absolute I/O address for a given `offset`,
|
||||
/// performing compile-time bound checks.
|
||||
// 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::MIN_SIZE));
|
||||
|
||||
self.addr() + offset
|
||||
}
|
||||
}
|
||||
|
||||
/// 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) => {
|
||||
@@ -758,6 +804,8 @@ impl_mmio_io_capable!(
|
||||
);
|
||||
|
||||
impl<const SIZE: usize> Io for Mmio<SIZE> {
|
||||
type Target = Region<SIZE>;
|
||||
|
||||
/// Returns the base address of this mapping.
|
||||
#[inline]
|
||||
fn addr(&self) -> usize {
|
||||
@@ -771,10 +819,6 @@ impl<const SIZE: usize> Io for Mmio<SIZE> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> IoKnownSize for Mmio<SIZE> {
|
||||
const MIN_SIZE: usize = SIZE;
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> Mmio<SIZE> {
|
||||
/// Converts an `MmioRaw` into an `Mmio` instance, providing the accessors to the MMIO mapping.
|
||||
///
|
||||
@@ -798,6 +842,8 @@ impl<const SIZE: usize> Mmio<SIZE> {
|
||||
pub struct RelaxedMmio<const SIZE: usize = 0>(Mmio<SIZE>);
|
||||
|
||||
impl<const SIZE: usize> Io for RelaxedMmio<SIZE> {
|
||||
type Target = Region<SIZE>;
|
||||
|
||||
#[inline]
|
||||
fn addr(&self) -> usize {
|
||||
self.0.addr()
|
||||
@@ -809,10 +855,6 @@ impl<const SIZE: usize> Io for RelaxedMmio<SIZE> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> IoKnownSize for RelaxedMmio<SIZE> {
|
||||
const MIN_SIZE: usize = SIZE;
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> Mmio<SIZE> {
|
||||
/// Returns a [`RelaxedMmio`] reference that performs relaxed I/O operations.
|
||||
///
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
// Please see https://github.com/Rust-for-Linux/linux/issues/2 for details on
|
||||
// the unstable features in use.
|
||||
//
|
||||
// Stable since Rust 1.87.0.
|
||||
#![feature(unsigned_is_multiple_of)]
|
||||
//
|
||||
// Stable since Rust 1.89.0.
|
||||
#![feature(generic_arg_infer)]
|
||||
//
|
||||
|
||||
@@ -43,7 +43,6 @@ pub use self::id::{
|
||||
pub use self::io::{
|
||||
Bar,
|
||||
ConfigSpace,
|
||||
ConfigSpaceKind,
|
||||
ConfigSpaceSize,
|
||||
Extended,
|
||||
Normal, //
|
||||
|
||||
+16
-24
@@ -10,11 +10,12 @@ use crate::{
|
||||
io::{
|
||||
Io,
|
||||
IoCapable,
|
||||
IoKnownSize,
|
||||
Mmio,
|
||||
MmioRaw, //
|
||||
MmioRaw,
|
||||
Region, //
|
||||
},
|
||||
prelude::*, //
|
||||
prelude::*,
|
||||
ptr::KnownSize, //
|
||||
};
|
||||
use core::{
|
||||
marker::PhantomData,
|
||||
@@ -46,28 +47,21 @@ impl ConfigSpaceSize {
|
||||
}
|
||||
}
|
||||
|
||||
/// Marker type for normal (256-byte) PCI configuration space.
|
||||
pub struct Normal;
|
||||
/// Alias for normal (256-byte) PCI configuration space.
|
||||
pub type Normal = Region<256>;
|
||||
|
||||
/// Marker type for extended (4096-byte) PCIe configuration space.
|
||||
pub struct Extended;
|
||||
/// Alias for extended (4096-byte) PCIe configuration space.
|
||||
pub type Extended = Region<4096>;
|
||||
|
||||
/// Trait for PCI configuration space size markers.
|
||||
///
|
||||
/// This trait is implemented by [`Normal`] and [`Extended`] to provide
|
||||
/// compile-time knowledge of the configuration space size.
|
||||
pub trait ConfigSpaceKind {
|
||||
/// The size of this configuration space in bytes.
|
||||
const SIZE: usize;
|
||||
}
|
||||
pub trait ConfigSpaceKind: KnownSize {}
|
||||
|
||||
impl ConfigSpaceKind for Normal {
|
||||
const SIZE: usize = 256;
|
||||
}
|
||||
impl ConfigSpaceKind for Normal {}
|
||||
|
||||
impl ConfigSpaceKind for Extended {
|
||||
const SIZE: usize = 4096;
|
||||
}
|
||||
impl ConfigSpaceKind for Extended {}
|
||||
|
||||
/// The PCI configuration space of a device.
|
||||
///
|
||||
@@ -77,7 +71,7 @@ impl ConfigSpaceKind for Extended {
|
||||
/// The generic parameter `S` indicates the maximum size of the configuration space.
|
||||
/// Use [`Normal`] for 256-byte legacy configuration space or [`Extended`] for
|
||||
/// 4096-byte PCIe extended configuration space (default).
|
||||
pub struct ConfigSpace<'a, S: ConfigSpaceKind = Extended> {
|
||||
pub struct ConfigSpace<'a, S: ?Sized + ConfigSpaceKind = Extended> {
|
||||
pub(crate) pdev: &'a Device<device::Bound>,
|
||||
_marker: PhantomData<S>,
|
||||
}
|
||||
@@ -85,7 +79,7 @@ pub struct ConfigSpace<'a, S: ConfigSpaceKind = Extended> {
|
||||
/// Implements [`IoCapable`] on [`ConfigSpace`] for `$ty` using `$read_fn` and `$write_fn`.
|
||||
macro_rules! impl_config_space_io_capable {
|
||||
($ty:ty, $read_fn:ident, $write_fn:ident) => {
|
||||
impl<'a, S: ConfigSpaceKind> IoCapable<$ty> for ConfigSpace<'a, S> {
|
||||
impl<'a, S: ?Sized + ConfigSpaceKind> IoCapable<$ty> for ConfigSpace<'a, S> {
|
||||
unsafe fn io_read(&self, address: usize) -> $ty {
|
||||
let mut val: $ty = 0;
|
||||
|
||||
@@ -118,7 +112,9 @@ impl_config_space_io_capable!(u8, pci_read_config_byte, pci_write_config_byte);
|
||||
impl_config_space_io_capable!(u16, pci_read_config_word, pci_write_config_word);
|
||||
impl_config_space_io_capable!(u32, pci_read_config_dword, pci_write_config_dword);
|
||||
|
||||
impl<'a, S: ConfigSpaceKind> Io for ConfigSpace<'a, S> {
|
||||
impl<'a, S: ?Sized + ConfigSpaceKind> Io for ConfigSpace<'a, S> {
|
||||
type Target = S;
|
||||
|
||||
/// Returns the base address of the I/O region. It is always 0 for configuration space.
|
||||
#[inline]
|
||||
fn addr(&self) -> usize {
|
||||
@@ -132,10 +128,6 @@ impl<'a, S: ConfigSpaceKind> Io for ConfigSpace<'a, S> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S: ConfigSpaceKind> IoKnownSize for ConfigSpace<'a, S> {
|
||||
const MIN_SIZE: usize = S::SIZE;
|
||||
}
|
||||
|
||||
/// A PCI BAR to perform I/O-Operations on.
|
||||
///
|
||||
/// I/O backend assumes that the device is little-endian and will automatically
|
||||
|
||||
@@ -235,11 +235,20 @@ impl_alignable_uint!(u8, u16, u32, u64, usize);
|
||||
///
|
||||
/// This is a generalization of [`size_of`] that works for dynamically sized types.
|
||||
pub trait KnownSize {
|
||||
/// Minimum size of this type known at compile-time.
|
||||
const MIN_SIZE: usize;
|
||||
|
||||
/// Minimum alignment of this type known at compile-time.
|
||||
const MIN_ALIGN: Alignment;
|
||||
|
||||
/// Get the size of an object of this type in bytes, with the metadata of the given pointer.
|
||||
fn size(p: *const Self) -> usize;
|
||||
}
|
||||
|
||||
impl<T> KnownSize for T {
|
||||
const MIN_SIZE: usize = size_of::<T>();
|
||||
const MIN_ALIGN: Alignment = Alignment::of::<T>();
|
||||
|
||||
#[inline(always)]
|
||||
fn size(_: *const Self) -> usize {
|
||||
size_of::<T>()
|
||||
@@ -247,6 +256,9 @@ impl<T> KnownSize for T {
|
||||
}
|
||||
|
||||
impl<T> KnownSize for [T] {
|
||||
const MIN_SIZE: usize = 0;
|
||||
const MIN_ALIGN: Alignment = Alignment::of::<T>();
|
||||
|
||||
#[inline(always)]
|
||||
fn size(p: *const Self) -> usize {
|
||||
p.len() * size_of::<T>()
|
||||
|
||||
Reference in New Issue
Block a user