renames `Bar` to `RawBar` and makes it generic over `IoAccess`. a user can give a compile time suggestion when mapping a bar so that the type of io can be known.
Suggested-by: Danilo Krummrich <d...@kernel.org> Signed-off-by: Andrew Ballance <andrewjballa...@gmail.com> --- rust/kernel/pci.rs | 88 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 69 insertions(+), 19 deletions(-) diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index c97d6d470b28..7e592db99073 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -11,8 +11,7 @@ devres::Devres, driver, error::{to_result, Result}, - io::Io, - io::IoRaw, + io::{Io, IoAccess, IoRaw, MMIo, PortIo}, str::CStr, types::{ARef, ForeignOwnable, Opaque}, ThisModule, @@ -259,15 +258,25 @@ pub struct Device<Ctx: device::DeviceContext = device::Normal>( /// /// # Invariants /// -/// `Bar` always holds an `IoRaw` inststance that holds a valid pointer to the start of the I/O +/// `Bar` always holds an `I` inststance that holds a valid pointer to the start of the I/O /// memory mapped PCI bar and its size. -pub struct Bar<const SIZE: usize = 0> { +pub struct RawBar<const SIZE: usize = 0, I: IoAccess<SIZE> = Io<SIZE>> { pdev: ARef<Device>, - io: IoRaw<SIZE>, + original_ioptr: usize, + io: I, num: i32, } -impl<const SIZE: usize> Bar<SIZE> { +/// a pci bar that can be either PortIo or MMIo +pub type IoBar<const SIZE: usize = 0> = RawBar<SIZE, Io<SIZE>>; + +/// a pci bar that maps a [`PortIo`]. +pub type MMIoBar<const SIZE: usize = 0> = RawBar<SIZE, MMIo<SIZE>>; + +/// a pci bar that maps a [`MMIo`]. +pub type PIoBar<const SIZE: usize = 0> = RawBar<SIZE, PortIo<SIZE>>; + +impl<const SIZE: usize, I: IoAccess<SIZE>> RawBar<SIZE, I> { fn new(pdev: &Device, num: u32, name: &CStr) -> Result<Self> { let len = pdev.resource_len(num)?; if len == 0 { @@ -299,7 +308,22 @@ fn new(pdev: &Device, num: u32, name: &CStr) -> Result<Self> { return Err(ENOMEM); } - let io = match IoRaw::new(ioptr, len as usize) { + let raw = match IoRaw::new(ioptr, len as usize) { + Ok(io) => io, + Err(err) => { + // SAFETY: + // `pdev` is valid by the invariants of `Device`. + // `ioptr` is guaranteed to be the start of a valid I/O mapped memory region. + // `num` is checked for validity by a previous call to `Device::resource_len`. + unsafe { Self::do_release(pdev, ioptr, num) }; + return Err(err); + } + }; + + // SAFETY: + // - `raw` is from `pci_iomap` + // - addresses from `pci_iomap` should be accesed through ioread/iowrite + let io = match unsafe { I::from_raw_cookie(raw) } { Ok(io) => io, Err(err) => { // SAFETY: @@ -311,8 +335,9 @@ fn new(pdev: &Device, num: u32, name: &CStr) -> Result<Self> { } }; - Ok(Bar { + Ok(RawBar { pdev: pdev.into(), + original_ioptr: ioptr, io, num, }) @@ -334,29 +359,28 @@ unsafe fn do_release(pdev: &Device, ioptr: usize, num: i32) { fn release(&self) { // SAFETY: The safety requirements are guaranteed by the type invariant of `self.pdev`. - unsafe { Self::do_release(&self.pdev, self.io.addr(), self.num) }; + unsafe { Self::do_release(&self.pdev, self.original_ioptr, self.num) }; } } -impl Bar { +impl RawBar { fn index_is_valid(index: u32) -> bool { // A `struct pci_dev` owns an array of resources with at most `PCI_NUM_RESOURCES` entries. index < bindings::PCI_NUM_RESOURCES } } -impl<const SIZE: usize> Drop for Bar<SIZE> { +impl<const SIZE: usize, I: IoAccess<SIZE>> Drop for RawBar<SIZE, I> { fn drop(&mut self) { self.release(); } } -impl<const SIZE: usize> Deref for Bar<SIZE> { - type Target = Io<SIZE>; +impl<const SIZE: usize, I: IoAccess<SIZE>> Deref for RawBar<SIZE, I> { + type Target = I; fn deref(&self) -> &Self::Target { - // SAFETY: By the type invariant of `Self`, the MMIO range in `self.io` is properly mapped. - unsafe { Io::from_raw(&self.io) } + &self.io } } @@ -379,7 +403,7 @@ pub fn device_id(&self) -> u16 { /// Returns the size of the given PCI bar resource. pub fn resource_len(&self, bar: u32) -> Result<bindings::resource_size_t> { - if !Bar::index_is_valid(bar) { + if !RawBar::index_is_valid(bar) { return Err(EINVAL); } @@ -395,17 +419,43 @@ pub fn iomap_region_sized<const SIZE: usize>( &self, bar: u32, name: &CStr, - ) -> Result<Devres<Bar<SIZE>>> { - let bar = Bar::<SIZE>::new(self, bar, name)?; + ) -> Result<Devres<IoBar<SIZE>>> { + let bar = RawBar::<SIZE, _>::new(self, bar, name)?; let devres = Devres::new(self.as_ref(), bar, GFP_KERNEL)?; Ok(devres) } /// Mapps an entire PCI-BAR after performing a region-request on it. - pub fn iomap_region(&self, bar: u32, name: &CStr) -> Result<Devres<Bar>> { + pub fn iomap_region(&self, bar: u32, name: &CStr) -> Result<Devres<IoBar>> { self.iomap_region_sized::<0>(bar, name) } + + /// Maps an entire PCI-BAR after performing a region-request` where the + /// type of Io backend is known at compile time. + pub fn iomap_region_hint<I: IoAccess>( + &self, + bar: u32, + name: &CStr, + ) -> Result<Devres<RawBar<0, I>>> { + let bar = RawBar::<0, I>::new(self, bar, name)?; + let devres = Devres::new(self.as_ref(), bar, GFP_KERNEL)?; + + Ok(devres) + } + /// Maps an entire PCI-BAR after performing a region-request` where the + /// type of Io backend is known at compile time. I/O operation bound checks + /// can be performed on compile time for offsets (plus the requested type size) < SIZE. + pub fn iomap_region_sized_hint<const SIZE: usize, I: IoAccess<SIZE>>( + &self, + bar: u32, + name: &CStr, + ) -> Result<Devres<RawBar<SIZE, I>>> { + let bar = RawBar::<SIZE, I>::new(self, bar, name)?; + let devres = Devres::new(self.as_ref(), bar, GFP_KERNEL)?; + + Ok(devres) + } } impl Device<device::Core> { -- 2.49.0