From 3c2e31d717ac89c59e35e98a7910a6daa9f15a06 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Wed, 15 Oct 2025 20:14:30 +0200 Subject: rust: pci: move I/O infrastructure to separate file Move the PCI I/O infrastructure to a separate sub-module in order to keep things organized. Signed-off-by: Danilo Krummrich --- rust/kernel/pci/io.rs | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 rust/kernel/pci/io.rs (limited to 'rust/kernel/pci/io.rs') diff --git a/rust/kernel/pci/io.rs b/rust/kernel/pci/io.rs new file mode 100644 index 000000000000..65151a0a1a41 --- /dev/null +++ b/rust/kernel/pci/io.rs @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! PCI memory-mapped I/O infrastructure. + +use super::Device; +use crate::{ + bindings, device, + devres::Devres, + io::{Io, IoRaw}, + str::CStr, + sync::aref::ARef, +}; +use core::ops::Deref; +use kernel::prelude::*; + +/// A PCI BAR to perform I/O-Operations on. +/// +/// # Invariants +/// +/// `Bar` always holds an `IoRaw` inststance that holds a valid pointer to the start of the I/O +/// memory mapped PCI bar and its size. +pub struct Bar { + pdev: ARef, + io: IoRaw, + num: i32, +} + +impl Bar { + pub(super) fn new(pdev: &Device, num: u32, name: &CStr) -> Result { + let len = pdev.resource_len(num)?; + if len == 0 { + return Err(ENOMEM); + } + + // Convert to `i32`, since that's what all the C bindings use. + let num = i32::try_from(num)?; + + // SAFETY: + // `pdev` is valid by the invariants of `Device`. + // `num` is checked for validity by a previous call to `Device::resource_len`. + // `name` is always valid. + let ret = unsafe { bindings::pci_request_region(pdev.as_raw(), num, name.as_char_ptr()) }; + if ret != 0 { + return Err(EBUSY); + } + + // SAFETY: + // `pdev` is valid by the invariants of `Device`. + // `num` is checked for validity by a previous call to `Device::resource_len`. + // `name` is always valid. + let ioptr: usize = unsafe { bindings::pci_iomap(pdev.as_raw(), num, 0) } as usize; + if ioptr == 0 { + // SAFETY: + // `pdev` valid by the invariants of `Device`. + // `num` is checked for validity by a previous call to `Device::resource_len`. + unsafe { bindings::pci_release_region(pdev.as_raw(), num) }; + return Err(ENOMEM); + } + + let io = 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); + } + }; + + Ok(Bar { + pdev: pdev.into(), + io, + num, + }) + } + + /// # Safety + /// + /// `ioptr` must be a valid pointer to the memory mapped PCI bar number `num`. + unsafe fn do_release(pdev: &Device, ioptr: usize, num: i32) { + // SAFETY: + // `pdev` is valid by the invariants of `Device`. + // `ioptr` is valid by the safety requirements. + // `num` is valid by the safety requirements. + unsafe { + bindings::pci_iounmap(pdev.as_raw(), ioptr as *mut c_void); + bindings::pci_release_region(pdev.as_raw(), num); + } + } + + 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) }; + } +} + +impl Bar { + #[inline] + pub(super) 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 Drop for Bar { + fn drop(&mut self) { + self.release(); + } +} + +impl Deref for Bar { + type Target = Io; + + 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) } + } +} + +impl Device { + /// Mapps an entire PCI-BAR after performing a region-request on it. I/O operation bound checks + /// can be performed on compile time for offsets (plus the requested type size) < SIZE. + pub fn iomap_region_sized<'a, const SIZE: usize>( + &'a self, + bar: u32, + name: &'a CStr, + ) -> impl PinInit>, Error> + 'a { + Devres::new(self.as_ref(), Bar::::new(self, bar, name)) + } + + /// Mapps an entire PCI-BAR after performing a region-request on it. + pub fn iomap_region<'a>( + &'a self, + bar: u32, + name: &'a CStr, + ) -> impl PinInit, Error> + 'a { + self.iomap_region_sized::<0>(bar, name) + } +} -- cgit From 26c1a20bf7ce76e9afe4030f25bec20e3c63dcf8 Mon Sep 17 00:00:00 2001 From: Peter Colberg Date: Mon, 20 Oct 2025 17:02:23 +0000 Subject: rust: pci: normalise spelling of PCI BAR Consistently refer to PCI base address register as PCI BAR. Fix spelling mistake "Mapps" -> "Maps". Link: https://lore.kernel.org/rust-for-linux/20251015225827.GA960157@bhelgaas/ Link: https://github.com/Rust-for-Linux/linux/issues/1196 Suggested-by: Bjorn Helgaas Signed-off-by: Peter Colberg Signed-off-by: Danilo Krummrich --- rust/kernel/pci/io.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'rust/kernel/pci/io.rs') diff --git a/rust/kernel/pci/io.rs b/rust/kernel/pci/io.rs index 65151a0a1a41..3684276b326b 100644 --- a/rust/kernel/pci/io.rs +++ b/rust/kernel/pci/io.rs @@ -18,7 +18,7 @@ use kernel::prelude::*; /// # Invariants /// /// `Bar` always holds an `IoRaw` inststance that holds a valid pointer to the start of the I/O -/// memory mapped PCI bar and its size. +/// memory mapped PCI BAR and its size. pub struct Bar { pdev: ARef, io: IoRaw, @@ -78,7 +78,7 @@ impl Bar { /// # Safety /// - /// `ioptr` must be a valid pointer to the memory mapped PCI bar number `num`. + /// `ioptr` must be a valid pointer to the memory mapped PCI BAR number `num`. unsafe fn do_release(pdev: &Device, ioptr: usize, num: i32) { // SAFETY: // `pdev` is valid by the invariants of `Device`. @@ -120,7 +120,7 @@ impl Deref for Bar { } impl Device { - /// Mapps an entire PCI-BAR after performing a region-request on it. I/O operation bound checks + /// Maps an entire PCI BAR after performing a region-request on it. I/O operation bound checks /// can be performed on compile time for offsets (plus the requested type size) < SIZE. pub fn iomap_region_sized<'a, const SIZE: usize>( &'a self, @@ -130,7 +130,7 @@ impl Device { Devres::new(self.as_ref(), Bar::::new(self, bar, name)) } - /// Mapps an entire PCI-BAR after performing a region-request on it. + /// Maps an entire PCI BAR after performing a region-request on it. pub fn iomap_region<'a>( &'a self, bar: u32, -- cgit From d8407396f128d8bf4d06282b636df3f26db208c1 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Wed, 5 Nov 2025 13:03:28 +0100 Subject: rust: pci: use "kernel vertical" style for imports Convert all imports in the PCI Rust module to use "kernel vertical" style. With this subsequent patches neither introduce unrelated changes nor leave an inconsistent import pattern. While at it, drop unnecessary imports covered by prelude::*. Link: https://docs.kernel.org/rust/coding-guidelines.html#imports Reviewed-by: Zhi Wang Link: https://patch.msgid.link/20251105120352.77603-1-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/pci/io.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'rust/kernel/pci/io.rs') diff --git a/rust/kernel/pci/io.rs b/rust/kernel/pci/io.rs index 3684276b326b..0d55c3139b6f 100644 --- a/rust/kernel/pci/io.rs +++ b/rust/kernel/pci/io.rs @@ -4,14 +4,17 @@ use super::Device; use crate::{ - bindings, device, + bindings, + device, devres::Devres, - io::{Io, IoRaw}, - str::CStr, - sync::aref::ARef, + io::{ + Io, + IoRaw, // + }, + prelude::*, + sync::aref::ARef, // }; use core::ops::Deref; -use kernel::prelude::*; /// A PCI BAR to perform I/O-Operations on. /// -- cgit