* [RFC 0/2] Rust PCI capability infrastructure and SR-IOV support @ 2026-01-26 21:59 Zhi Wang 2026-01-26 21:59 ` [RFC 1/2] pci: Add fallible I/O methods to ConfigSpace Zhi Wang 2026-01-26 21:59 ` [RFC 2/2] pci: Add PCI capability infrastructure and SR-IOV capability support Zhi Wang 0 siblings, 2 replies; 6+ messages in thread From: Zhi Wang @ 2026-01-26 21:59 UTC (permalink / raw) To: rust-for-linux, linux-pci, linux-kernel Cc: dakr, aliceryhl, bhelgaas, kwilczynski, ojeda, alex.gaynor, boqun.feng, gary, bjorn3_gh, lossin, a.hindborg, tmgross, markus.probst, helgaas, cjia, smitra, ankita, aniketa, kwankhede, targupta, acourbot, joelagnelf, jhubbard, zhiwang, daniel.almeida, Zhi Wang This RFC series introduces PCI capability discovery and access infrastructure for Rust kernel drivers, with initial support for SR-IOV (Single Root I/O Virtualization) capabilities. Background ---------- Modern PCI devices expose advanced features through capability structures in configuration space. Rust drivers need type-safe, ergonomic APIs to discover and access these capabilities while maintaining the kernel's existing safety guarantees. An example can be found in RFC patch. [1] Overview -------- Patch 1 extends ConfigSpace with fallible I/O methods that properly handle PCI bus errors, providing the foundation for safe capability access with runtime-determined offsets. Patch 2 introduces the core capability infrastructure: - Generic Capability<S, K> struct with Io trait implementation - Capability size calculation using kernel's capability chaining - SriovCapability wrapper for accessing VF configuration registers - Support for reading VF Offset and VF BAR0/1/2 registers Feedback Requested ------------------ 1) The configuration cap doesn't have an fixed offset, thus it doesn't fit with the infallible accessors. But to have fallible accessors, it needs the config space backend to implement the fallible accessors as well. I tried different approaches, this seems the most reasonable one. Any thought? 2) Capability traits hierarchy I was thinking several solutions of arranging common functions for Normal/Extended caps, all the extended caps. It seems a complicated traits hierarchy again. Would like to hear more about this. [1] https://lore.kernel.org/all/20251206124208.305963-5-zhiw@nvidia.com/ Zhi Wang (2): pci: Add fallible I/O methods to ConfigSpace pci: Add PCI capability infrastructure and SR-IOV capability support rust/kernel/pci.rs | 9 ++ rust/kernel/pci/cap.rs | 274 +++++++++++++++++++++++++++++++++++++++++ rust/kernel/pci/io.rs | 34 ++++- 3 files changed, 314 insertions(+), 3 deletions(-) create mode 100644 rust/kernel/pci/cap.rs -- 2.51.0 ^ permalink raw reply [flat|nested] 6+ messages in thread
* [RFC 1/2] pci: Add fallible I/O methods to ConfigSpace 2026-01-26 21:59 [RFC 0/2] Rust PCI capability infrastructure and SR-IOV support Zhi Wang @ 2026-01-26 21:59 ` Zhi Wang 2026-01-26 21:59 ` [RFC 2/2] pci: Add PCI capability infrastructure and SR-IOV capability support Zhi Wang 1 sibling, 0 replies; 6+ messages in thread From: Zhi Wang @ 2026-01-26 21:59 UTC (permalink / raw) To: rust-for-linux, linux-pci, linux-kernel Cc: dakr, aliceryhl, bhelgaas, kwilczynski, ojeda, alex.gaynor, boqun.feng, gary, bjorn3_gh, lossin, a.hindborg, tmgross, markus.probst, helgaas, cjia, smitra, ankita, aniketa, kwankhede, targupta, acourbot, joelagnelf, jhubbard, zhiwang, daniel.almeida, Zhi Wang Rust PCI drivers might need to access device configuration space with runtime bound check. The existing ConfigSpace abstraction only provides infallible methods (read8/16/32) that use compile-time bounds checking via io_addr_assert, which cannot handle dynamic offsets. Add fallible I/O methods to ConfigSpace. Signed-off-by: Zhi Wang <zhiw@nvidia.com> --- rust/kernel/pci/io.rs | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/rust/kernel/pci/io.rs b/rust/kernel/pci/io.rs index 026e7a3b69bd..9fc9af0f9bfc 100644 --- a/rust/kernel/pci/io.rs +++ b/rust/kernel/pci/io.rs @@ -112,6 +112,17 @@ macro_rules! call_config_read { let _ret = unsafe { bindings::$c_fn($self.pdev.as_raw(), $addr as i32, &mut val) }; val }}; + + (fallible, $c_fn:ident, $self:ident, $ty:ty, $addr:expr) => {{ + let mut val: $ty = 0; + // SAFETY: By the type invariant `$self.pdev` is a valid address. + let ret = unsafe { bindings::$c_fn($self.pdev.as_raw(), $addr as i32, &mut val) }; + if ret != 0 { + Err(EIO) + } else { + Ok(val) + } + }}; } /// Internal helper macros used to invoke C PCI configuration space write functions. @@ -140,6 +151,16 @@ macro_rules! call_config_write { // Return value from C function is ignored in infallible accessors. let _ret = unsafe { bindings::$c_fn($self.pdev.as_raw(), $addr as i32, $value) }; }; + + (fallible, $c_fn:ident, $self:ident, $ty:ty, $addr:expr, $value:expr) => {{ + // SAFETY: By the type invariant `$self.pdev` is a valid address. + let ret = unsafe { bindings::$c_fn($self.pdev.as_raw(), $addr as i32, $value) }; + if ret != 0 { + Err(EIO) + } else { + Ok(()) + } + }}; } // PCI configuration space supports 8, 16, and 32-bit accesses. @@ -162,9 +183,7 @@ fn maxsize(&self) -> usize { self.pdev.cfg_size().into_raw() } - // PCI configuration space does not support fallible operations. - // The default implementations from the Io trait are not used. - + // Infallible methods with compile-time bounds checking define_read!(infallible, read8, call_config_read(pci_read_config_byte) -> u8); define_read!(infallible, read16, call_config_read(pci_read_config_word) -> u16); define_read!(infallible, read32, call_config_read(pci_read_config_dword) -> u32); @@ -172,6 +191,15 @@ fn maxsize(&self) -> usize { define_write!(infallible, write8, call_config_write(pci_write_config_byte) <- u8); define_write!(infallible, write16, call_config_write(pci_write_config_word) <- u16); define_write!(infallible, write32, call_config_write(pci_write_config_dword) <- u32); + + // Fallible methods with runtime bounds checking + define_read!(fallible, try_read8, call_config_read(pci_read_config_byte) -> u8); + define_read!(fallible, try_read16, call_config_read(pci_read_config_word) -> u16); + define_read!(fallible, try_read32, call_config_read(pci_read_config_dword) -> u32); + + define_write!(fallible, try_write8, call_config_write(pci_write_config_byte) <- u8); + define_write!(fallible, try_write16, call_config_write(pci_write_config_word) <- u16); + define_write!(fallible, try_write32, call_config_write(pci_write_config_dword) <- u32); } /// Marker trait indicating ConfigSpace has a known size at compile time. -- 2.51.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* [RFC 2/2] pci: Add PCI capability infrastructure and SR-IOV capability support 2026-01-26 21:59 [RFC 0/2] Rust PCI capability infrastructure and SR-IOV support Zhi Wang 2026-01-26 21:59 ` [RFC 1/2] pci: Add fallible I/O methods to ConfigSpace Zhi Wang @ 2026-01-26 21:59 ` Zhi Wang 2026-01-27 15:36 ` Gary Guo 1 sibling, 1 reply; 6+ messages in thread From: Zhi Wang @ 2026-01-26 21:59 UTC (permalink / raw) To: rust-for-linux, linux-pci, linux-kernel Cc: dakr, aliceryhl, bhelgaas, kwilczynski, ojeda, alex.gaynor, boqun.feng, gary, bjorn3_gh, lossin, a.hindborg, tmgross, markus.probst, helgaas, cjia, smitra, ankita, aniketa, kwankhede, targupta, acourbot, joelagnelf, jhubbard, zhiwang, daniel.almeida, Zhi Wang Rust drivers may need to access PCI capabilities (such as SR-IOV) to configure firmware metadata. Add a generic Capability<S, K> structure that: - Wraps capability discovery via kernel's pci_find_ext_capability() - Dynamically calculates capability size by reading next capability pointers (using 0xffc mask per PCI_EXT_CAP_NEXT for extended caps) - Implements fallible I/O via the Io trait with runtime bounds checking Add SR-IOV (Single Root I/O Virtualization) capability support: - SriovCapability wraps Capability<Extended, Extended> - read_vf_offset() reads PCI_SRIOV_VF_OFFSET register - read_vf_bar(n) reads PCI_SRIOV_BAR + n*4 (32-bit BAR) - read_vf_bar64(n) reads 64-bit BAR by combining two 32-bit reads Signed-off-by: Zhi Wang <zhiw@nvidia.com> --- rust/kernel/pci.rs | 9 ++ rust/kernel/pci/cap.rs | 274 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 283 insertions(+) create mode 100644 rust/kernel/pci/cap.rs diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index cd46ac12812c..2deb26fb3775 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -31,6 +31,7 @@ }, }; +mod cap; mod id; mod io; mod irq; @@ -42,6 +43,7 @@ }; pub use self::io::{ Bar, + ConfigSpace, ConfigSpaceKind, ConfigSpaceSize, Extended, @@ -52,6 +54,13 @@ IrqTypes, IrqVector, // }; +pub use self::cap::{ + Capability, + CapabilityId, + CapabilityKind, + ExtCapabilityId, + SriovCapability, // +}; /// An adapter for the registration of PCI drivers. pub struct Adapter<T: Driver>(T); diff --git a/rust/kernel/pci/cap.rs b/rust/kernel/pci/cap.rs new file mode 100644 index 000000000000..e3a903b79299 --- /dev/null +++ b/rust/kernel/pci/cap.rs @@ -0,0 +1,274 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! PCI Capability support. +//! +//! This module provides abstractions for discovering and accessing PCI capabilities. + +use super::{ + ConfigSpace, + ConfigSpaceKind, + Extended, + Normal, // +}; +use crate::{ + bindings, + io::{ + define_read, + define_write, + Io, + IoCapable, // + }, + prelude::*, +}; + +/// Internal helper macro to call ConfigSpace fallible methods from Capability. +macro_rules! call_cap_read { + (fallible, $method:ident, $self:ident, $ty:ty, $addr:expr) => { + $self.config_space.$method($self.offset() + $addr) + }; +} + +/// Internal helper macro to call ConfigSpace fallible write methods from Capability. +macro_rules! call_cap_write { + (fallible, $method:ident, $self:ident, $ty:ty, $addr:expr, $value:expr) => { + $self.config_space.$method($value, $self.offset() + $addr) + }; +} + +/// PCI Capability IDs for normal capabilities (in 256-byte config space). +/// +/// These are not currently implemented, but the enum is provided for API completeness. +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum CapabilityId { + /// Power Management + PM = bindings::PCI_CAP_ID_PM as u8, + /// Message Signalled Interrupts + MSI = bindings::PCI_CAP_ID_MSI as u8, + /// MSI-X + MSIX = bindings::PCI_CAP_ID_MSIX as u8, + /// PCI Express + Express = bindings::PCI_CAP_ID_EXP as u8, + /// Vendor Specific + VendorSpecific = bindings::PCI_CAP_ID_VNDR as u8, +} + +/// PCI Extended Capability IDs (in 4096-byte config space). +/// +/// Currently only SR-IOV is implemented. +#[repr(u16)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ExtCapabilityId { + /// Single Root I/O Virtualization + SRIOV = bindings::PCI_EXT_CAP_ID_SRIOV as u16, +} + +/// Trait for capability kinds (Normal or Extended). +pub trait CapabilityKind { + /// The capability ID type for this kind. + type IdType: Copy + PartialEq; + + /// Start offset for capability scanning. + const START_OFFSET: usize; +} + +/// Marker for normal (legacy) PCI capabilities. +impl CapabilityKind for Normal { + type IdType = u8; + const START_OFFSET: usize = bindings::PCI_CAPABILITY_LIST as usize; +} + +/// Marker for extended PCI capabilities. +impl CapabilityKind for Extended { + type IdType = u16; + const START_OFFSET: usize = bindings::PCI_CFG_SPACE_SIZE as usize; +} + +/// A PCI capability. +/// +/// This type represents a discovered PCI capability and provides safe access +/// to its registers. All I/O operations are relative to the capability's +/// base offset in configuration space. +pub struct Capability<'a, S: ConfigSpaceKind, K: CapabilityKind> { + config_space: &'a ConfigSpace<'a, S>, + offset: usize, + id: K::IdType, + size: usize, +} + +impl<'a, S: ConfigSpaceKind, K: CapabilityKind> Capability<'a, S, K> { + /// Creates a new capability handle. + fn new( + config_space: &'a ConfigSpace<'a, S>, + offset: usize, + id: K::IdType, + size: usize, + ) -> Self { + Self { + config_space, + offset, + id, + size, + } + } + + /// Returns the offset of this capability in configuration space. + pub fn offset(&self) -> usize { + self.offset + } + + /// Returns the capability ID. + pub fn id(&self) -> K::IdType { + self.id + } + + /// Returns the size of this capability in bytes. + pub fn size(&self) -> usize { + self.size + } +} + +// Implement IoCapable for Capability +impl<'a, S: ConfigSpaceKind, K: CapabilityKind> IoCapable<u8> for Capability<'a, S, K> {} +impl<'a, S: ConfigSpaceKind, K: CapabilityKind> IoCapable<u16> for Capability<'a, S, K> {} +impl<'a, S: ConfigSpaceKind, K: CapabilityKind> IoCapable<u32> for Capability<'a, S, K> {} + +// Implement Io trait for Capability using fallible methods (runtime checks) +impl<'a, S: ConfigSpaceKind, K: CapabilityKind> Io for Capability<'a, S, K> { + const MIN_SIZE: usize = S::SIZE; // Use config space size for fallible bounds checking + + #[inline] + fn addr(&self) -> usize { + 0 // Offsets are relative to capability base, not absolute + } + + #[inline] + fn maxsize(&self) -> usize { + self.size() + } + + // Only implement fallible methods (no IoKnownSize, so infallible methods not available) + define_read!(fallible, try_read8, call_cap_read(try_read8) -> u8); + define_read!(fallible, try_read16, call_cap_read(try_read16) -> u16); + define_read!(fallible, try_read32, call_cap_read(try_read32) -> u32); + + define_write!(fallible, try_write8, call_cap_write(try_write8) <- u8); + define_write!(fallible, try_write16, call_cap_write(try_write16) <- u16); + define_write!(fallible, try_write32, call_cap_write(try_write32) <- u32); +} + +impl<'a> ConfigSpace<'a, Extended> { + /// Finds a specific extended capability by ID using the kernel's `pci_find_ext_capability`. + pub fn find_ext_capability(&self, id: u16) -> Option<Capability<'_, Extended, Extended>> { + // SAFETY: pdev is valid by ConfigSpace invariants + let offset = unsafe { bindings::pci_find_ext_capability(self.pdev.as_raw(), id as i32) }; + + if offset == 0 { + return None; + } + + let size = self.calculate_ext_cap_size(offset as usize); + Some(Capability::new(self, offset as usize, id, size)) + } + + fn calculate_ext_cap_size(&self, offset: usize) -> usize { + // Extended capability header: [31:20] = next capability offset + // Use 0xffc mask (not 0xfff) to match kernel's PCI_EXT_CAP_NEXT macro + let header = self.try_read32(offset).unwrap_or(0); + let next_ptr = ((header >> 20) & 0xffc) as usize; + + if next_ptr == 0 { + // Last capability, size goes to end of config space + self.pdev.cfg_size().into_raw() - offset + } else { + // Size is distance to next capability + next_ptr - offset + } + } + + /// Finds the next occurrence of a specific extended capability starting from a given position. + pub fn find_next_ext_capability( + &self, + start_pos: u16, + id: u16, + ) -> Option<Capability<'_, Extended, Extended>> { + // SAFETY: pdev is valid by ConfigSpace invariants + let offset = unsafe { + bindings::pci_find_next_ext_capability(self.pdev.as_raw(), start_pos, id as i32) + }; + + if offset == 0 { + return None; + } + + // Calculate real capability size + let size = self.calculate_ext_cap_size(offset as usize); + Some(Capability::new(self, offset as usize, id, size)) + } +} + +/// SR-IOV register offsets (relative to the capability base). +mod sriov_offsets { + use crate::bindings; + + /// First VF Offset register offset + pub(super) const VF_OFFSET: usize = bindings::PCI_SRIOV_VF_OFFSET as usize; + /// VF BAR0 register offset (first of 6 VF BARs) + pub(super) const VF_BAR0: usize = bindings::PCI_SRIOV_BAR as usize; +} + +/// SR-IOV capability structure. +/// +/// This structure provides typed access to the SR-IOV extended capability +/// registers using the PCI configuration space backend. +pub struct SriovCapability<'a> { + cap: Capability<'a, Extended, Extended>, +} + +impl<'a> SriovCapability<'a> { + /// Creates a new SR-IOV capability from an extended capability. + pub fn new(cap: Capability<'a, Extended, Extended>) -> Result<Self> { + if cap.id() != ExtCapabilityId::SRIOV as u16 { + return Err(EINVAL); + } + Ok(Self { cap }) + } + + /// Tries to find and create an SR-IOV capability from a config space. + pub fn from_config_space(config_space: &'a ConfigSpace<'a, Extended>) -> Result<Self> { + let cap = config_space + .find_ext_capability(ExtCapabilityId::SRIOV as u16) + .ok_or(ENODEV)?; + Self::new(cap) + } + + /// Returns the offset of this capability in configuration space. + pub fn offset(&self) -> usize { + self.cap.offset() + } + + /// Reads the First VF Offset register. + pub fn read_vf_offset(&self) -> Result<u16> { + self.cap.try_read16(sriov_offsets::VF_OFFSET) + } + + /// Reads a VF BAR register (32-bit). + /// Returns the 32-bit value of the specified VF BAR register. + pub fn read_vf_bar(&self, bar_index: usize) -> Result<u32> { + if bar_index >= 6 { + return Err(EINVAL); + } + self.cap.try_read32(sriov_offsets::VF_BAR0 + bar_index * 4) + } + + /// Reads a 64-bit VF BAR register. + /// Returns the 64-bit address combining BAR[n] (low) and BAR[n+1] (high). + pub fn read_vf_bar64(&self, bar_index: usize) -> Result<u64> { + if bar_index >= 5 { + return Err(EINVAL); + } + let low = self.read_vf_bar(bar_index)?; + let high = self.read_vf_bar(bar_index + 1)?; + Ok((u64::from(high) << 32) | u64::from(low)) + } +} -- 2.51.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [RFC 2/2] pci: Add PCI capability infrastructure and SR-IOV capability support 2026-01-26 21:59 ` [RFC 2/2] pci: Add PCI capability infrastructure and SR-IOV capability support Zhi Wang @ 2026-01-27 15:36 ` Gary Guo 2026-02-05 11:57 ` Zhi Wang 0 siblings, 1 reply; 6+ messages in thread From: Gary Guo @ 2026-01-27 15:36 UTC (permalink / raw) To: Zhi Wang, rust-for-linux, linux-pci, linux-kernel Cc: dakr, aliceryhl, bhelgaas, kwilczynski, ojeda, alex.gaynor, boqun.feng, gary, bjorn3_gh, lossin, a.hindborg, tmgross, markus.probst, helgaas, cjia, smitra, ankita, aniketa, kwankhede, targupta, acourbot, joelagnelf, jhubbard, zhiwang, daniel.almeida On Mon Jan 26, 2026 at 9:59 PM GMT, Zhi Wang wrote: > Rust drivers may need to access PCI capabilities (such as SR-IOV) to > configure firmware metadata. > > Add a generic Capability<S, K> structure that: > - Wraps capability discovery via kernel's pci_find_ext_capability() > - Dynamically calculates capability size by reading next capability > pointers (using 0xffc mask per PCI_EXT_CAP_NEXT for extended caps) > - Implements fallible I/O via the Io trait with runtime bounds checking > > Add SR-IOV (Single Root I/O Virtualization) capability support: > - SriovCapability wraps Capability<Extended, Extended> > - read_vf_offset() reads PCI_SRIOV_VF_OFFSET register > - read_vf_bar(n) reads PCI_SRIOV_BAR + n*4 (32-bit BAR) > - read_vf_bar64(n) reads 64-bit BAR by combining two 32-bit reads > > Signed-off-by: Zhi Wang <zhiw@nvidia.com> > --- > rust/kernel/pci.rs | 9 ++ > rust/kernel/pci/cap.rs | 274 +++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 283 insertions(+) > create mode 100644 rust/kernel/pci/cap.rs > > diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs > index cd46ac12812c..2deb26fb3775 100644 > --- a/rust/kernel/pci.rs > +++ b/rust/kernel/pci.rs > @@ -31,6 +31,7 @@ > }, > }; > > +mod cap; > mod id; > mod io; > mod irq; > @@ -42,6 +43,7 @@ > }; > pub use self::io::{ > Bar, > + ConfigSpace, > ConfigSpaceKind, > ConfigSpaceSize, > Extended, > @@ -52,6 +54,13 @@ > IrqTypes, > IrqVector, // > }; > +pub use self::cap::{ > + Capability, > + CapabilityId, > + CapabilityKind, > + ExtCapabilityId, > + SriovCapability, // > +}; > > /// An adapter for the registration of PCI drivers. > pub struct Adapter<T: Driver>(T); > diff --git a/rust/kernel/pci/cap.rs b/rust/kernel/pci/cap.rs > new file mode 100644 > index 000000000000..e3a903b79299 > --- /dev/null > +++ b/rust/kernel/pci/cap.rs > @@ -0,0 +1,274 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +//! PCI Capability support. > +//! > +//! This module provides abstractions for discovering and accessing PCI capabilities. > + > +use super::{ > + ConfigSpace, > + ConfigSpaceKind, > + Extended, > + Normal, // > +}; > +use crate::{ > + bindings, > + io::{ > + define_read, > + define_write, > + Io, > + IoCapable, // > + }, > + prelude::*, > +}; > + > +/// Internal helper macro to call ConfigSpace fallible methods from Capability. > +macro_rules! call_cap_read { > + (fallible, $method:ident, $self:ident, $ty:ty, $addr:expr) => { > + $self.config_space.$method($self.offset() + $addr) > + }; > +} > + > +/// Internal helper macro to call ConfigSpace fallible write methods from Capability. > +macro_rules! call_cap_write { > + (fallible, $method:ident, $self:ident, $ty:ty, $addr:expr, $value:expr) => { > + $self.config_space.$method($value, $self.offset() + $addr) > + }; > +} > + > +/// PCI Capability IDs for normal capabilities (in 256-byte config space). > +/// > +/// These are not currently implemented, but the enum is provided for API completeness. > +#[repr(u8)] > +#[derive(Debug, Clone, Copy, PartialEq, Eq)] > +pub enum CapabilityId { > + /// Power Management > + PM = bindings::PCI_CAP_ID_PM as u8, > + /// Message Signalled Interrupts > + MSI = bindings::PCI_CAP_ID_MSI as u8, > + /// MSI-X > + MSIX = bindings::PCI_CAP_ID_MSIX as u8, > + /// PCI Express > + Express = bindings::PCI_CAP_ID_EXP as u8, > + /// Vendor Specific > + VendorSpecific = bindings::PCI_CAP_ID_VNDR as u8, > +} > + > +/// PCI Extended Capability IDs (in 4096-byte config space). > +/// > +/// Currently only SR-IOV is implemented. > +#[repr(u16)] > +#[derive(Debug, Clone, Copy, PartialEq, Eq)] > +pub enum ExtCapabilityId { > + /// Single Root I/O Virtualization > + SRIOV = bindings::PCI_EXT_CAP_ID_SRIOV as u16, > +} > + > +/// Trait for capability kinds (Normal or Extended). > +pub trait CapabilityKind { > + /// The capability ID type for this kind. > + type IdType: Copy + PartialEq; > + > + /// Start offset for capability scanning. > + const START_OFFSET: usize; > +} > + > +/// Marker for normal (legacy) PCI capabilities. > +impl CapabilityKind for Normal { > + type IdType = u8; > + const START_OFFSET: usize = bindings::PCI_CAPABILITY_LIST as usize; > +} > + > +/// Marker for extended PCI capabilities. > +impl CapabilityKind for Extended { > + type IdType = u16; > + const START_OFFSET: usize = bindings::PCI_CFG_SPACE_SIZE as usize; > +} > + > +/// A PCI capability. > +/// > +/// This type represents a discovered PCI capability and provides safe access > +/// to its registers. All I/O operations are relative to the capability's > +/// base offset in configuration space. > +pub struct Capability<'a, S: ConfigSpaceKind, K: CapabilityKind> { > + config_space: &'a ConfigSpace<'a, S>, > + offset: usize, > + id: K::IdType, > + size: usize, > +} Some thoughts about this: given that the IDs are different for PCI capability and PCI extended capabilities, it feels that we shouldn't overlap them into the same type. The `offset` and `size` feels like it can be something more generic, something like /// A subview into I/O. pub struct IoView<T> { io: T, offset: usize, size: usize, } impl<T: IO> IoView<T> { // .... } This is just like a slice, but act on arbitrary IO. The capability enumeration can just be something like fn capabilities(&self) -> impl Iterator<Item = (CapabilityId, IoView<&Self>)> and another method for capabilities_ext. If you want to add typed capabilities, an option is to have enum Capability<'a> { SpecificParsedCapability, Other(CapabilityId, IoView<&ConfigSpace<...>>), } Thoughts? Best, Gary > + > +impl<'a, S: ConfigSpaceKind, K: CapabilityKind> Capability<'a, S, K> { > + /// Creates a new capability handle. > + fn new( > + config_space: &'a ConfigSpace<'a, S>, > + offset: usize, > + id: K::IdType, > + size: usize, > + ) -> Self { > + Self { > + config_space, > + offset, > + id, > + size, > + } > + } > + > + /// Returns the offset of this capability in configuration space. > + pub fn offset(&self) -> usize { > + self.offset > + } > + > + /// Returns the capability ID. > + pub fn id(&self) -> K::IdType { > + self.id > + } > + > + /// Returns the size of this capability in bytes. > + pub fn size(&self) -> usize { > + self.size > + } > +} > + > +// Implement IoCapable for Capability > +impl<'a, S: ConfigSpaceKind, K: CapabilityKind> IoCapable<u8> for Capability<'a, S, K> {} > +impl<'a, S: ConfigSpaceKind, K: CapabilityKind> IoCapable<u16> for Capability<'a, S, K> {} > +impl<'a, S: ConfigSpaceKind, K: CapabilityKind> IoCapable<u32> for Capability<'a, S, K> {} > + > +// Implement Io trait for Capability using fallible methods (runtime checks) > +impl<'a, S: ConfigSpaceKind, K: CapabilityKind> Io for Capability<'a, S, K> { > + const MIN_SIZE: usize = S::SIZE; // Use config space size for fallible bounds checking > + > + #[inline] > + fn addr(&self) -> usize { > + 0 // Offsets are relative to capability base, not absolute > + } > + > + #[inline] > + fn maxsize(&self) -> usize { > + self.size() > + } > + > + // Only implement fallible methods (no IoKnownSize, so infallible methods not available) > + define_read!(fallible, try_read8, call_cap_read(try_read8) -> u8); > + define_read!(fallible, try_read16, call_cap_read(try_read16) -> u16); > + define_read!(fallible, try_read32, call_cap_read(try_read32) -> u32); > + > + define_write!(fallible, try_write8, call_cap_write(try_write8) <- u8); > + define_write!(fallible, try_write16, call_cap_write(try_write16) <- u16); > + define_write!(fallible, try_write32, call_cap_write(try_write32) <- u32); > +} > + > +impl<'a> ConfigSpace<'a, Extended> { > + /// Finds a specific extended capability by ID using the kernel's `pci_find_ext_capability`. > + pub fn find_ext_capability(&self, id: u16) -> Option<Capability<'_, Extended, Extended>> { > + // SAFETY: pdev is valid by ConfigSpace invariants > + let offset = unsafe { bindings::pci_find_ext_capability(self.pdev.as_raw(), id as i32) }; > + > + if offset == 0 { > + return None; > + } > + > + let size = self.calculate_ext_cap_size(offset as usize); > + Some(Capability::new(self, offset as usize, id, size)) > + } > + > + fn calculate_ext_cap_size(&self, offset: usize) -> usize { > + // Extended capability header: [31:20] = next capability offset > + // Use 0xffc mask (not 0xfff) to match kernel's PCI_EXT_CAP_NEXT macro > + let header = self.try_read32(offset).unwrap_or(0); > + let next_ptr = ((header >> 20) & 0xffc) as usize; > + > + if next_ptr == 0 { > + // Last capability, size goes to end of config space > + self.pdev.cfg_size().into_raw() - offset > + } else { > + // Size is distance to next capability > + next_ptr - offset > + } > + } > + > + /// Finds the next occurrence of a specific extended capability starting from a given position. > + pub fn find_next_ext_capability( > + &self, > + start_pos: u16, > + id: u16, > + ) -> Option<Capability<'_, Extended, Extended>> { > + // SAFETY: pdev is valid by ConfigSpace invariants > + let offset = unsafe { > + bindings::pci_find_next_ext_capability(self.pdev.as_raw(), start_pos, id as i32) > + }; > + > + if offset == 0 { > + return None; > + } > + > + // Calculate real capability size > + let size = self.calculate_ext_cap_size(offset as usize); > + Some(Capability::new(self, offset as usize, id, size)) > + } > +} > + > +/// SR-IOV register offsets (relative to the capability base). > +mod sriov_offsets { > + use crate::bindings; > + > + /// First VF Offset register offset > + pub(super) const VF_OFFSET: usize = bindings::PCI_SRIOV_VF_OFFSET as usize; > + /// VF BAR0 register offset (first of 6 VF BARs) > + pub(super) const VF_BAR0: usize = bindings::PCI_SRIOV_BAR as usize; > +} > + > +/// SR-IOV capability structure. > +/// > +/// This structure provides typed access to the SR-IOV extended capability > +/// registers using the PCI configuration space backend. > +pub struct SriovCapability<'a> { > + cap: Capability<'a, Extended, Extended>, > +} > + > +impl<'a> SriovCapability<'a> { > + /// Creates a new SR-IOV capability from an extended capability. > + pub fn new(cap: Capability<'a, Extended, Extended>) -> Result<Self> { > + if cap.id() != ExtCapabilityId::SRIOV as u16 { > + return Err(EINVAL); > + } > + Ok(Self { cap }) > + } > + > + /// Tries to find and create an SR-IOV capability from a config space. > + pub fn from_config_space(config_space: &'a ConfigSpace<'a, Extended>) -> Result<Self> { > + let cap = config_space > + .find_ext_capability(ExtCapabilityId::SRIOV as u16) > + .ok_or(ENODEV)?; > + Self::new(cap) > + } > + > + /// Returns the offset of this capability in configuration space. > + pub fn offset(&self) -> usize { > + self.cap.offset() > + } > + > + /// Reads the First VF Offset register. > + pub fn read_vf_offset(&self) -> Result<u16> { > + self.cap.try_read16(sriov_offsets::VF_OFFSET) > + } > + > + /// Reads a VF BAR register (32-bit). > + /// Returns the 32-bit value of the specified VF BAR register. > + pub fn read_vf_bar(&self, bar_index: usize) -> Result<u32> { > + if bar_index >= 6 { > + return Err(EINVAL); > + } > + self.cap.try_read32(sriov_offsets::VF_BAR0 + bar_index * 4) > + } > + > + /// Reads a 64-bit VF BAR register. > + /// Returns the 64-bit address combining BAR[n] (low) and BAR[n+1] (high). > + pub fn read_vf_bar64(&self, bar_index: usize) -> Result<u64> { > + if bar_index >= 5 { > + return Err(EINVAL); > + } > + let low = self.read_vf_bar(bar_index)?; > + let high = self.read_vf_bar(bar_index + 1)?; > + Ok((u64::from(high) << 32) | u64::from(low)) > + } > +} ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [RFC 2/2] pci: Add PCI capability infrastructure and SR-IOV capability support 2026-01-27 15:36 ` Gary Guo @ 2026-02-05 11:57 ` Zhi Wang 2026-02-05 13:10 ` Gary Guo 0 siblings, 1 reply; 6+ messages in thread From: Zhi Wang @ 2026-02-05 11:57 UTC (permalink / raw) To: Gary Guo Cc: rust-for-linux, linux-pci, linux-kernel, dakr, aliceryhl, bhelgaas, kwilczynski, ojeda, alex.gaynor, boqun.feng, bjorn3_gh, lossin, a.hindborg, tmgross, markus.probst, helgaas, cjia, smitra, ankita, aniketa, kwankhede, targupta, acourbot, joelagnelf, jhubbard, zhiwang, daniel.almeida On Tue, 27 Jan 2026 15:36:29 +0000 "Gary Guo" <gary@garyguo.net> wrote: snip > > +/// Marker for normal (legacy) PCI capabilities. > > +impl CapabilityKind for Normal { > > + type IdType = u8; > > + const START_OFFSET: usize = bindings::PCI_CAPABILITY_LIST as > > usize; +} > > + > > +/// Marker for extended PCI capabilities. > > +impl CapabilityKind for Extended { > > + type IdType = u16; > > + const START_OFFSET: usize = bindings::PCI_CFG_SPACE_SIZE as > > usize; +} > > + > > +/// A PCI capability. > > +/// > > +/// This type represents a discovered PCI capability and provides > > safe access +/// to its registers. All I/O operations are relative > > to the capability's +/// base offset in configuration space. > > +pub struct Capability<'a, S: ConfigSpaceKind, K: CapabilityKind> { > > + config_space: &'a ConfigSpace<'a, S>, > > + offset: usize, > > + id: K::IdType, > > + size: usize, > > +} > > Some thoughts about this: given that the IDs are different for PCI > capability and PCI extended capabilities, it feels that we shouldn't > overlap them into the same type. > Make sense. Sorry for the late reply as I was evaluating what would be the impact of IoCapable trait refinement to this patch. > The `offset` and `size` feels like it can be something more generic, > something like > > /// A subview into I/O. > pub struct IoView<T> { > io: T, > offset: usize, > size: usize, > } > > impl<T: IO> IoView<T> { > // .... > } > > This is just like a slice, but act on arbitrary IO. > > The capability enumeration can just be something like > > fn capabilities(&self) -> impl Iterator<Item = (CapabilityId, > IoView<&Self>)> > > and another method for capabilities_ext. > I actually had a version of that, but dropped it. The C side already handles finding capabilities by ID, so a Rust iterator seemed redundant for the common case. Unless we need to handle multiple capabilities with the same ID (which is rare). IMO, it might be better we add it when a rust driver really need this. > If you want to add typed capabilities, an option is to have > > enum Capability<'a> { > SpecificParsedCapability, > Other(CapabilityId, IoView<&ConfigSpace<...>>), > } > But given that the kernel's C API favors looking up by ID, usage of an enum might be slightly awkward here (e.g., calling find_capability(ID) and still having to unwrap an enum)? > Thoughts? > > Best, > Gary > > > > + > > +impl<'a, S: ConfigSpaceKind, K: CapabilityKind> Capability<'a, S, > > K> { > > + /// Creates a new capability handle. > > + fn new( > > + config_space: &'a ConfigSpace<'a, S>, > > + offset: usize, > > + id: K::IdType, > > + size: usize, > > + ) -> Self { > > + Self { > > + config_space, > > + offset, > > + id, > > + size, > > + } > > + } > > + > > + /// Returns the offset of this capability in configuration > > space. > > + pub fn offset(&self) -> usize { > > + self.offset > > + } > > + > > + /// Returns the capability ID. > > + pub fn id(&self) -> K::IdType { > > + self.id > > + } > > + > > + /// Returns the size of this capability in bytes. > > + pub fn size(&self) -> usize { > > + self.size > > + } > > +} > > + > > +// Implement IoCapable for Capability > > +impl<'a, S: ConfigSpaceKind, K: CapabilityKind> IoCapable<u8> for > > Capability<'a, S, K> {} +impl<'a, S: ConfigSpaceKind, K: > > CapabilityKind> IoCapable<u16> for Capability<'a, S, K> {} > > CapabilityKind> +impl<'a, S: ConfigSpaceKind, K: CapabilityKind> > > CapabilityKind> IoCapable<u32> for Capability<'a, S, K> {} > > + > > +// Implement Io trait for Capability using fallible methods > > (runtime checks) +impl<'a, S: ConfigSpaceKind, K: CapabilityKind> > > Io for Capability<'a, S, K> { > > + const MIN_SIZE: usize = S::SIZE; // Use config space size for > > fallible bounds checking + > > + #[inline] > > + fn addr(&self) -> usize { > > + 0 // Offsets are relative to capability base, not absolute > > + } > > + > > + #[inline] > > + fn maxsize(&self) -> usize { > > + self.size() > > + } > > + > > + // Only implement fallible methods (no IoKnownSize, so > > infallible methods not available) > > + define_read!(fallible, try_read8, call_cap_read(try_read8) -> > > u8); > > + define_read!(fallible, try_read16, call_cap_read(try_read16) > > -> u16); > > + define_read!(fallible, try_read32, call_cap_read(try_read32) > > -> u32); + > > + define_write!(fallible, try_write8, call_cap_write(try_write8) > > <- u8); > > + define_write!(fallible, try_write16, > > call_cap_write(try_write16) <- u16); > > + define_write!(fallible, try_write32, > > call_cap_write(try_write32) <- u32); +} > > + > > +impl<'a> ConfigSpace<'a, Extended> { > > + /// Finds a specific extended capability by ID using the > > kernel's `pci_find_ext_capability`. > > + pub fn find_ext_capability(&self, id: u16) -> > > Option<Capability<'_, Extended, Extended>> { > > + // SAFETY: pdev is valid by ConfigSpace invariants > > + let offset = unsafe { > > bindings::pci_find_ext_capability(self.pdev.as_raw(), id as i32) }; > > + > > + if offset == 0 { > > + return None; > > + } > > + > > + let size = self.calculate_ext_cap_size(offset as usize); > > + Some(Capability::new(self, offset as usize, id, size)) > > + } > > + > > + fn calculate_ext_cap_size(&self, offset: usize) -> usize { > > + // Extended capability header: [31:20] = next capability > > offset > > + // Use 0xffc mask (not 0xfff) to match kernel's > > PCI_EXT_CAP_NEXT macro > > + let header = self.try_read32(offset).unwrap_or(0); > > + let next_ptr = ((header >> 20) & 0xffc) as usize; > > + > > + if next_ptr == 0 { > > + // Last capability, size goes to end of config space > > + self.pdev.cfg_size().into_raw() - offset > > + } else { > > + // Size is distance to next capability > > + next_ptr - offset > > + } > > + } > > + > > + /// Finds the next occurrence of a specific extended > > capability starting from a given position. > > + pub fn find_next_ext_capability( > > + &self, > > + start_pos: u16, > > + id: u16, > > + ) -> Option<Capability<'_, Extended, Extended>> { > > + // SAFETY: pdev is valid by ConfigSpace invariants > > + let offset = unsafe { > > + > > bindings::pci_find_next_ext_capability(self.pdev.as_raw(), > > start_pos, id as i32) > > + }; > > + > > + if offset == 0 { > > + return None; > > + } > > + > > + // Calculate real capability size > > + let size = self.calculate_ext_cap_size(offset as usize); > > + Some(Capability::new(self, offset as usize, id, size)) > > + } > > +} > > + > > +/// SR-IOV register offsets (relative to the capability base). > > +mod sriov_offsets { > > + use crate::bindings; > > + > > + /// First VF Offset register offset > > + pub(super) const VF_OFFSET: usize = > > bindings::PCI_SRIOV_VF_OFFSET as usize; > > + /// VF BAR0 register offset (first of 6 VF BARs) > > + pub(super) const VF_BAR0: usize = bindings::PCI_SRIOV_BAR as > > usize; +} > > + > > +/// SR-IOV capability structure. > > +/// > > +/// This structure provides typed access to the SR-IOV extended > > capability +/// registers using the PCI configuration space backend. > > +pub struct SriovCapability<'a> { > > + cap: Capability<'a, Extended, Extended>, > > +} > > + > > +impl<'a> SriovCapability<'a> { > > + /// Creates a new SR-IOV capability from an extended > > capability. > > + pub fn new(cap: Capability<'a, Extended, Extended>) -> > > Result<Self> { > > + if cap.id() != ExtCapabilityId::SRIOV as u16 { > > + return Err(EINVAL); > > + } > > + Ok(Self { cap }) > > + } > > + > > + /// Tries to find and create an SR-IOV capability from a > > config space. > > + pub fn from_config_space(config_space: &'a ConfigSpace<'a, > > Extended>) -> Result<Self> { > > + let cap = config_space > > + .find_ext_capability(ExtCapabilityId::SRIOV as u16) > > + .ok_or(ENODEV)?; > > + Self::new(cap) > > + } > > + > > + /// Returns the offset of this capability in configuration > > space. > > + pub fn offset(&self) -> usize { > > + self.cap.offset() > > + } > > + > > + /// Reads the First VF Offset register. > > + pub fn read_vf_offset(&self) -> Result<u16> { > > + self.cap.try_read16(sriov_offsets::VF_OFFSET) > > + } > > + > > + /// Reads a VF BAR register (32-bit). > > + /// Returns the 32-bit value of the specified VF BAR register. > > + pub fn read_vf_bar(&self, bar_index: usize) -> Result<u32> { > > + if bar_index >= 6 { > > + return Err(EINVAL); > > + } > > + self.cap.try_read32(sriov_offsets::VF_BAR0 + bar_index * 4) > > + } > > + > > + /// Reads a 64-bit VF BAR register. > > + /// Returns the 64-bit address combining BAR[n] (low) and > > BAR[n+1] (high). > > + pub fn read_vf_bar64(&self, bar_index: usize) -> Result<u64> { > > + if bar_index >= 5 { > > + return Err(EINVAL); > > + } > > + let low = self.read_vf_bar(bar_index)?; > > + let high = self.read_vf_bar(bar_index + 1)?; > > + Ok((u64::from(high) << 32) | u64::from(low)) > > + } > > +} > ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [RFC 2/2] pci: Add PCI capability infrastructure and SR-IOV capability support 2026-02-05 11:57 ` Zhi Wang @ 2026-02-05 13:10 ` Gary Guo 0 siblings, 0 replies; 6+ messages in thread From: Gary Guo @ 2026-02-05 13:10 UTC (permalink / raw) To: Zhi Wang, Gary Guo Cc: rust-for-linux, linux-pci, linux-kernel, dakr, aliceryhl, bhelgaas, kwilczynski, ojeda, alex.gaynor, boqun.feng, bjorn3_gh, lossin, a.hindborg, tmgross, markus.probst, helgaas, cjia, smitra, ankita, aniketa, kwankhede, targupta, acourbot, joelagnelf, jhubbard, zhiwang, daniel.almeida On Thu Feb 5, 2026 at 11:57 AM GMT, Zhi Wang wrote: > On Tue, 27 Jan 2026 15:36:29 +0000 > "Gary Guo" <gary@garyguo.net> wrote: > > snip > >> > +/// Marker for normal (legacy) PCI capabilities. >> > +impl CapabilityKind for Normal { >> > + type IdType = u8; >> > + const START_OFFSET: usize = bindings::PCI_CAPABILITY_LIST as >> > usize; +} >> > + >> > +/// Marker for extended PCI capabilities. >> > +impl CapabilityKind for Extended { >> > + type IdType = u16; >> > + const START_OFFSET: usize = bindings::PCI_CFG_SPACE_SIZE as >> > usize; +} >> > + >> > +/// A PCI capability. >> > +/// >> > +/// This type represents a discovered PCI capability and provides >> > safe access +/// to its registers. All I/O operations are relative >> > to the capability's +/// base offset in configuration space. >> > +pub struct Capability<'a, S: ConfigSpaceKind, K: CapabilityKind> { >> > + config_space: &'a ConfigSpace<'a, S>, >> > + offset: usize, >> > + id: K::IdType, >> > + size: usize, >> > +} >> >> Some thoughts about this: given that the IDs are different for PCI >> capability and PCI extended capabilities, it feels that we shouldn't >> overlap them into the same type. >> > > Make sense. Sorry for the late reply as I was evaluating what would be > the impact of IoCapable trait refinement to this patch. There's a few discussions about this recently and I think we've determined a generic infrastrucutre for a subview of a I/O region is desired. I'm going to work on this in the upcoming weeks and would report my progress on Zulip. Let's coordinate there. > >> The `offset` and `size` feels like it can be something more generic, >> something like >> >> /// A subview into I/O. >> pub struct IoView<T> { >> io: T, >> offset: usize, >> size: usize, >> } >> >> impl<T: IO> IoView<T> { >> // .... >> } >> >> This is just like a slice, but act on arbitrary IO. >> >> The capability enumeration can just be something like >> >> fn capabilities(&self) -> impl Iterator<Item = (CapabilityId, >> IoView<&Self>)> >> >> and another method for capabilities_ext. >> > > I actually had a version of that, but dropped it. The C side already > handles finding capabilities by ID, so a Rust iterator seemed redundant > for the common case. Unless we need to handle multiple capabilities > with the same ID (which is rare). IMO, it might be better we add it when > a rust driver really need this. If we're not going to expose any API that support enumeration at all, then what you say makes sense. > >> If you want to add typed capabilities, an option is to have >> >> enum Capability<'a> { >> SpecificParsedCapability, >> Other(CapabilityId, IoView<&ConfigSpace<...>>), >> } >> > > But given that the kernel's C API favors looking up by ID, usage of an > enum might be slightly awkward here (e.g., calling find_capability(ID) > and still having to unwrap an enum)? Indeed. What you have there makes sense for typed capability lookup. Best, Gary ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-02-05 13:10 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-01-26 21:59 [RFC 0/2] Rust PCI capability infrastructure and SR-IOV support Zhi Wang 2026-01-26 21:59 ` [RFC 1/2] pci: Add fallible I/O methods to ConfigSpace Zhi Wang 2026-01-26 21:59 ` [RFC 2/2] pci: Add PCI capability infrastructure and SR-IOV capability support Zhi Wang 2026-01-27 15:36 ` Gary Guo 2026-02-05 11:57 ` Zhi Wang 2026-02-05 13:10 ` Gary Guo
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).