In order to support I/O projection, we are splitting I/O types into two categories: owned objects and views. Owned objects have a specific type that is related to setting up and tearing down, while views can have their type changed with I/O projection.
Things like `IoMem` or `Bar` are owned objects, which requires setting up mapping and cleaning up on drop. On the other side, `ConfigSpace` is really just a view, as the resource is associated with the `pci::Device`. Remove the `ConfigSpaceKind` bound on `ConfigSpace` and make it a generic view. This means that `ConfigSpace` object now represents a subregion and therefore encodes offset (as address of pointers) and size (as metadata of pointers) itself. The full region case is still supported with offset 0 and size of `cfg_size`. Signed-off-by: Gary Guo <[email protected]> --- rust/kernel/pci/io.rs | 58 ++++++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/rust/kernel/pci/io.rs b/rust/kernel/pci/io.rs index e0acb62f58a2..a4cfa1ec6e62 100644 --- a/rust/kernel/pci/io.rs +++ b/rust/kernel/pci/io.rs @@ -18,7 +18,6 @@ ptr::KnownSize, // }; use core::{ - marker::PhantomData, ops::Deref, // }; @@ -53,33 +52,36 @@ pub const fn into_raw(self) -> usize { /// 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: KnownSize {} - -impl ConfigSpaceKind for Normal {} - -impl ConfigSpaceKind for Extended {} - -/// The PCI configuration space of a device. +/// A view of PCI configuration space of a device. /// /// Provides typed read and write accessors for configuration registers /// using the standard `pci_read_config_*` and `pci_write_config_*` helpers. /// -/// 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: ?Sized + ConfigSpaceKind = Extended> { +/// The generic parameter `T` is the type of the view. The full configuration space is also a +/// special type of view; in such cases, `T` can be [`Normal`] for 256-byte legacy configuration +/// space or [`Extended`] for 4096-byte PCIe extended configuration space (default). +/// +/// # Invariants +/// +/// `ptr` is aligned and range `ptr..ptr + KnownSize::size(ptr)` is within +/// `0..pdev.cfg_size().into_raw()`. +pub struct ConfigSpace<'a, T: ?Sized = Extended> { pub(crate) pdev: &'a Device<device::Bound>, - _marker: PhantomData<S>, + ptr: *mut T, +} + +impl<T: ?Sized> Copy for ConfigSpace<'_, T> {} +impl<T: ?Sized> Clone for ConfigSpace<'_, T> { + #[inline] + fn clone(&self) -> Self { + *self + } } /// 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: ?Sized + ConfigSpaceKind> IoCapable<$ty> for &ConfigSpace<'a, S> { + impl<'a, T: ?Sized> IoCapable<$ty> for ConfigSpace<'a, T> { unsafe fn io_read(self, address: usize) -> $ty { let mut val: $ty = 0; @@ -112,19 +114,17 @@ unsafe fn io_write(self, value: $ty, address: usize) { 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: ?Sized + ConfigSpaceKind> Io for &ConfigSpace<'a, S> { - type Target = S; +impl<'a, T: ?Sized + KnownSize> Io for ConfigSpace<'a, T> { + type Target = T; - /// Returns the base address of the I/O region. It is always 0 for configuration space. #[inline] fn addr(self) -> usize { - 0 + self.ptr.addr() } - /// Returns the maximum size of the configuration space. #[inline] fn maxsize(self) -> usize { - self.pdev.cfg_size().into_raw() + KnownSize::size(self.ptr) } } @@ -281,23 +281,25 @@ pub fn cfg_size(&self) -> ConfigSpaceSize { } } - /// Return an initialized normal (256-byte) config space object. + /// Return a view of the normal (256-byte) config space. pub fn config_space<'a>(&'a self) -> ConfigSpace<'a, Normal> { + // INVARIANT: null is aligned and the range is within config space. ConfigSpace { pdev: self, - _marker: PhantomData, + ptr: Normal::ptr_from_raw_parts_mut(core::ptr::null_mut(), self.cfg_size().into_raw()), } } - /// Return an initialized extended (4096-byte) config space object. + /// Return a view of the extended (4096-byte) config space. pub fn config_space_extended<'a>(&'a self) -> Result<ConfigSpace<'a, Extended>> { if self.cfg_size() != ConfigSpaceSize::Extended { return Err(EINVAL); } + // INVARIANT: null is aligned and we just checked the `cfg_size`. Ok(ConfigSpace { pdev: self, - _marker: PhantomData, + ptr: Extended::ptr_from_raw_parts_mut(core::ptr::null_mut(), 4096), }) } } -- 2.54.0
