Rust for Linux List
 help / color / mirror / Atom feed
* [PATCH v9 0/1] Rust PCI capability infrastructure and SR-IOV support
@ 2026-08-27 12:18 Zhi Wang
  2026-08-27 12:18 ` [PATCH v9 1/1] rust: pci: add extended capability " Zhi Wang
  0 siblings, 1 reply; 3+ messages in thread
From: Zhi Wang @ 2026-08-27 12:18 UTC (permalink / raw)
  To: rust-for-linux, linux-pci, linux-kernel
  Cc: dakr, aliceryhl, bhelgaas, kwilczynski, ojeda, boqun, gary,
	bjorn3_gh, lossin, a.hindborg, tmgross, markus.probst, cjia,
	smitra, ankita, aniketa, kwankhede, targupta, kjaju, alkumar,
	acourbot, jhubbard, zhiwang, Zhi Wang, joelagnelf, daniel.almeida,
	tamird, work

This is a follow-up to v8 [1].

The patch adds typed PCIe extended-capability lookup to the Rust PCI
abstraction and an SR-IOV layout used by the nova-core vGPU boot work.

This series is based on current mainline. That base provides the existing
num::casts helpers but not const_as!, so the const narrowing call uses the
temporary turbofish form discussed during review. It can be converted as
part of the subsequent const_as! migration before the old helper is
removed.

Changes since v8:
- Made all raw ExtSriovRegs fields private. (Danilo)
- Added a read-only first_vf_offset() accessor for the GSP ABI and
  documented why pci_iov_virtfn_devfn() cannot replace it. (Danilo, Zhi)
- Simplified VF BAR decoding using a slot iterator and removed
  ExtSriovVfBars. (Alexandre, Danilo)
- Explained why I/O-space VF BAR encodings are rejected. (Alexandre)
- Clarified that decoded BAR addresses are PCI bus addresses. (Zhi)
- Removed unused ExtCapId::new() and kept as_raw() private. (Danilo)
- Replaced documented integer casts with the num::casts helpers available
  in current mainline. (Alexandre)
- Rebased onto current mainline.

[1] https://lore.kernel.org/rust-for-linux/20260818084633.1673214-1-zhiw@nvidia.com/

Zhi Wang (1):
  rust: pci: add extended capability and SR-IOV support

 rust/helpers/pci.c     |   5 +
 rust/kernel/pci.rs     |   8 ++
 rust/kernel/pci/cap.rs | 290 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 303 insertions(+)
 create mode 100644 rust/kernel/pci/cap.rs


base-commit: 45c13f3f9e3bb15fd89ff2864c6f627a3b4b4229
-- 
2.53.0

^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH v9 1/1] rust: pci: add extended capability and SR-IOV support
  2026-08-27 12:18 [PATCH v9 0/1] Rust PCI capability infrastructure and SR-IOV support Zhi Wang
@ 2026-08-27 12:18 ` Zhi Wang
  2026-08-31 13:21   ` Ilpo Järvinen
  0 siblings, 1 reply; 3+ messages in thread
From: Zhi Wang @ 2026-08-27 12:18 UTC (permalink / raw)
  To: rust-for-linux, linux-pci, linux-kernel
  Cc: dakr, aliceryhl, bhelgaas, kwilczynski, ojeda, boqun, gary,
	bjorn3_gh, lossin, a.hindborg, tmgross, markus.probst, cjia,
	smitra, ankita, aniketa, kwankhede, targupta, kjaju, alkumar,
	acourbot, jhubbard, zhiwang, Zhi Wang, joelagnelf, daniel.almeida,
	tamird, work

Rust PCI drivers have no typed interface for locating and accessing PCIe
extended capabilities.

The SR-IOV extended capability describes VF topology and VF BARs. Expose
this information through the Rust PCI abstraction so drivers can use the
existing typed configuration-space accessors instead of raw bindings.

Define ExtCapability to associate a capability ID with a register layout,
and add ConfigSpace::find_ext_capability() to locate and project that
layout. Bound the view at the next capability or the end of extended
configuration space. Add ExtSriovRegs and a decoded VF BAR iterator that
reads and validates all six VF BAR register slots up front, yields decoded
BAR addresses and widths in logical order, and keeps the raw
configuration-space slot advancement internal. Since PCI_EXT_CAP_NEXT() is
a function-like macro, expose it through a Rust helper.

Keep raw SR-IOV registers private because PCI core owns their state. Expose
the current First VF Offset through a read-only accessor because the Nova
GSP ABI needs the raw 16-bit First VF Offset in Routing ID space. The
pci_iov_virtfn_devfn() helper instead returns the low eight bits of a
calculated VF Routing ID.

Link: https://lore.kernel.org/rust-for-linux/20260818084633.1673214-2-zhiw@nvidia.com/
Cc: Alexandre Courbot <acourbot@nvidia.com>
Cc: Gary Guo <gary@garyguo.net>
Signed-off-by: Zhi Wang <zhiw@nvidia.com>
---
 rust/helpers/pci.c     |   5 +
 rust/kernel/pci.rs     |   8 ++
 rust/kernel/pci/cap.rs | 290 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 303 insertions(+)
 create mode 100644 rust/kernel/pci/cap.rs

diff --git a/rust/helpers/pci.c b/rust/helpers/pci.c
index a714cc2bfb7a..502f56e02fcc 100644
--- a/rust/helpers/pci.c
+++ b/rust/helpers/pci.c
@@ -29,6 +29,11 @@ __rust_helper unsigned int rust_helper_pci_irq_type(struct pci_dev *pdev)
 	return pci_irq_type(pdev);
 }
 
+__rust_helper u32 rust_helper_pci_ext_cap_next(u32 header)
+{
+	return PCI_EXT_CAP_NEXT(header);
+}
+
 #ifndef CONFIG_PCI_IOV
 __rust_helper unsigned int
 rust_helper_pci_sriov_get_totalvfs(struct pci_dev *pdev)
diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
index 3ec897709e89..77c52fe59551 100644
--- a/rust/kernel/pci.rs
+++ b/rust/kernel/pci.rs
@@ -32,10 +32,18 @@
     },
 };
 
+mod cap;
 mod id;
 mod io;
 mod irq;
 
+pub use self::cap::{
+    ExtCapId,
+    ExtCapability,
+    ExtSriovCapability,
+    ExtSriovRegs,
+    ExtSriovVfBar, //
+};
 pub use self::id::{
     Class,
     ClassMask,
diff --git a/rust/kernel/pci/cap.rs b/rust/kernel/pci/cap.rs
new file mode 100644
index 000000000000..74cd9e50e919
--- /dev/null
+++ b/rust/kernel/pci/cap.rs
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! PCI extended capability support.
+
+use super::{
+    io::ConfigSpaceBackend,
+    ConfigSpace,
+    Extended, //
+};
+use crate::{
+    bindings,
+    io::{
+        Io,
+        IoBackend,
+        Region, //
+    },
+    num::{casts, Bounded},
+    prelude::*,
+};
+
+/// Number of VF BAR register slots in an SR-IOV capability.
+const NUM_VF_BARS: usize = casts::u32_as_usize(bindings::PCI_SRIOV_NUM_BARS);
+
+/// PCI extended capability IDs.
+#[repr(transparent)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct ExtCapId(u16);
+
+impl ExtCapId {
+    /// Single Root I/O Virtualization.
+    pub const SRIOV: Self = Self(casts::u32_into_u16::<{ bindings::PCI_EXT_CAP_ID_SRIOV }>());
+
+    /// Returns the raw PCIe extended capability ID.
+    #[inline]
+    const fn as_raw(self) -> u16 {
+        self.0
+    }
+}
+
+/// A typed PCI extended capability register layout.
+///
+/// Implementors describe the register layout of one extended capability. The layout must start at
+/// the extended capability header, and [`Self::ID`] must identify that layout.
+pub trait ExtCapability: FromBytes + IntoBytes {
+    /// PCI extended capability ID for this register layout.
+    const ID: ExtCapId;
+}
+
+impl<'a> ConfigSpace<'a, Extended> {
+    /// Finds and projects an extended capability into its typed register layout.
+    ///
+    /// Returns [`None`] if the device does not implement the capability.
+    ///
+    /// Returns an error if the capability is present but its register span is too small or
+    /// insufficiently aligned for `C`.
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// use kernel::{
+    ///     device::Bound,
+    ///     pci,
+    ///     prelude::*,
+    /// };
+    ///
+    /// fn probe_sriov(pdev: &pci::Device<Bound>) -> Result {
+    ///     let Some(sriov) = pdev
+    ///         .config_space_extended()?
+    ///         .find_ext_capability::<pci::ExtSriovRegs>()?
+    ///     else {
+    ///         return Ok(());
+    ///     };
+    ///
+    ///     let first_vf_offset = sriov.first_vf_offset();
+    ///     let mut vf_bars = sriov.vf_bars()?;
+    ///     let bar0 = vf_bars.next().ok_or(EINVAL)?;
+    ///     let bar1 = vf_bars.next().ok_or(EINVAL)?;
+    ///     let bar2 = vf_bars.next().ok_or(EINVAL)?;
+    ///
+    ///     Ok(())
+    /// }
+    /// ```
+    pub fn find_ext_capability<C: ExtCapability>(&self) -> Result<Option<ConfigSpace<'a, C>>> {
+        let offset = usize::from(
+            // SAFETY: `self.pdev` is valid by the type invariant of `ConfigSpace`.
+            unsafe {
+                bindings::pci_find_ext_capability(self.pdev.as_raw(), i32::from(C::ID.as_raw()))
+            },
+        );
+
+        if offset == 0 {
+            return Ok(None);
+        }
+
+        let size = self.calculate_ext_cap_size(offset)?;
+
+        let base = ConfigSpaceBackend::as_ptr(*self)
+            .cast::<u8>()
+            .wrapping_add(offset);
+        let ptr = Region::<0>::ptr_try_from_raw_parts_mut(base, size)?;
+
+        // SAFETY: `offset` was returned by `pci_find_ext_capability`, and
+        // `calculate_ext_cap_size` bounds `ptr` at the next capability or the end of the extended
+        // configuration space. `ptr_try_from_raw_parts_mut` verified the region layout.
+        let capability = unsafe { ConfigSpaceBackend::project_view(*self, ptr) };
+
+        capability.try_cast::<C>().map(Some)
+    }
+
+    /// Calculates the size of the extended capability at `offset`.
+    ///
+    /// The capability extends to the next extended capability, or to the end of the extended
+    /// configuration space if it is the last one. `offset` must be a DWORD-aligned offset within
+    /// the extended configuration space returned by `pci_find_ext_capability`. Returns an error if
+    /// the capability header is outside the extended configuration space.
+    fn calculate_ext_cap_size(&self, offset: usize) -> Result<usize> {
+        let header = self.try_read32(offset)?;
+        // SAFETY: Pure bit manipulation, no preconditions.
+        let next = casts::u32_as_usize(unsafe { bindings::pci_ext_cap_next(header) });
+
+        Ok(if next > offset {
+            next - offset
+        } else {
+            self.size() - offset
+        })
+    }
+}
+
+/// SR-IOV register layout per PCIe spec (64 bytes starting at cap offset).
+///
+/// The raw registers are private because PCI core owns SR-IOV state management. Drivers should use
+/// PCI core APIs for operations such as enabling VFs and querying their topology.
+#[repr(C)]
+#[derive(FromBytes, IntoBytes)]
+pub struct ExtSriovRegs {
+    /// Extended capability header.
+    _header: u32,
+    /// SR-IOV capabilities.
+    _cap: u32,
+    /// SR-IOV control.
+    _ctrl: u16,
+    /// SR-IOV status.
+    _status: u16,
+    /// Initial VFs.
+    _initial_vfs: u16,
+    /// Total VFs.
+    _total_vfs: u16,
+    /// Number of VFs.
+    _num_vfs: u16,
+    /// Function dependency link.
+    _func_dep_link: u8,
+    _reserved_0: u8,
+    /// First VF offset.
+    vf_offset: u16,
+    /// VF stride.
+    _vf_stride: u16,
+    _reserved_1: u16,
+    /// VF device ID.
+    _vf_device_id: u16,
+    /// Supported page sizes.
+    _supported_page_sizes: u32,
+    /// System page size.
+    _system_page_size: u32,
+    /// VF BARs (BAR0–BAR5).
+    vf_bar: [u32; NUM_VF_BARS],
+    /// VF migration state array offset.
+    _migration_state: u32,
+}
+
+impl ExtCapability for ExtSriovRegs {
+    const ID: ExtCapId = ExtCapId::SRIOV;
+}
+
+/// A typed view of an SR-IOV extended capability.
+pub type ExtSriovCapability<'a> = ConfigSpace<'a, ExtSriovRegs>;
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+enum VfBarMemoryType {
+    Bits32,
+    Bits64,
+}
+
+impl TryFrom<Bounded<u32, 2>> for VfBarMemoryType {
+    type Error = Error;
+
+    fn try_from(value: Bounded<u32, 2>) -> Result<Self> {
+        match value.get() {
+            0b00 => Ok(Self::Bits32),
+            0b10 => Ok(Self::Bits64),
+            _ => Err(EINVAL),
+        }
+    }
+}
+
+impl From<VfBarMemoryType> for Bounded<u32, 2> {
+    fn from(value: VfBarMemoryType) -> Self {
+        match value {
+            VfBarMemoryType::Bits32 => Self::new::<0b00>(),
+            VfBarMemoryType::Bits64 => Self::new::<0b10>(),
+        }
+    }
+}
+
+crate::bitfield! {
+    /// Low DWORD of an SR-IOV VF BAR.
+    struct VfBarLow(u32) {
+        /// Base address bits 31:4.
+        31:4 address;
+        /// Whether the address range is prefetchable.
+        3:3 prefetchable => bool;
+        /// Memory BAR type.
+        2:1 memory_type ?=> VfBarMemoryType;
+        /// Whether this is an I/O-space BAR.
+        0:0 io_space => bool;
+    }
+}
+
+/// A decoded VF BAR register encoding.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct ExtSriovVfBar {
+    /// The PCI bus address encoded by the BAR, without PCI attribute bits.
+    ///
+    /// This is not necessarily the CPU resource address.
+    pub address: u64,
+
+    /// Whether the BAR is 64-bit.
+    pub is_64bit: bool,
+}
+
+impl ExtSriovCapability<'_> {
+    /// Returns the current PCIe First VF Offset in Routing ID space.
+    ///
+    /// This value may change when NumVFs changes. Drivers should use PCI core helpers when they
+    /// need to calculate a VF BDF; this accessor is intended for interfaces that require the First
+    /// VF Offset itself.
+    #[inline]
+    pub fn first_vf_offset(&self) -> u16 {
+        crate::io_read!(*self, .vf_offset)
+    }
+
+    /// Returns an iterator over decoded VF BAR register encodings.
+    ///
+    /// All six raw VF BAR register slots are read and decoded up front. A 32-bit encoding yields
+    /// one entry; a 64-bit encoding combines two slots into one entry.
+    ///
+    /// A zero-valued low DWORD is yielded as a 32-bit BAR at address zero; this method does not
+    /// probe whether a BAR is implemented.
+    ///
+    /// Returns [`EINVAL`] and logs an error if a BAR low DWORD does not encode a 32-bit or 64-bit
+    /// memory BAR, or if a 64-bit encoding has no upper DWORD.
+    pub fn vf_bars(&self) -> Result<impl Iterator<Item = ExtSriovVfBar>> {
+        let slots: [u32; NUM_VF_BARS] =
+            core::array::from_fn(|slot| crate::io_read!(*self, .vf_bar[panic: slot]));
+        let mut slots = slots.into_iter();
+        let mut bars = [None; NUM_VF_BARS];
+        let mut count = 0;
+
+        let mut decode = || {
+            while let Some(low) = slots.next().map(VfBarLow::from) {
+                // SR-IOV VF BARs describe memory-space apertures; an I/O-space encoding is not
+                // valid for these registers.
+                if low.io_space() {
+                    return Err(EINVAL);
+                }
+
+                let low_address = u64::from(low.address()) << VfBarLow::ADDRESS_SHIFT;
+                let bar = match low.memory_type()? {
+                    VfBarMemoryType::Bits64 => ExtSriovVfBar {
+                        address: (u64::from(slots.next().ok_or(EINVAL)?) << 32) | low_address,
+                        is_64bit: true,
+                    },
+                    VfBarMemoryType::Bits32 => ExtSriovVfBar {
+                        address: low_address,
+                        is_64bit: false,
+                    },
+                };
+
+                bars[count] = Some(bar);
+                count += 1;
+            }
+
+            Ok(())
+        };
+
+        decode().inspect_err(|_| {
+            dev_err!(self.pdev, "invalid VF BAR encoding in SR-IOV capability\n");
+        })?;
+        Ok(bars.into_iter().flatten())
+    }
+}
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH v9 1/1] rust: pci: add extended capability and SR-IOV support
  2026-08-27 12:18 ` [PATCH v9 1/1] rust: pci: add extended capability " Zhi Wang
@ 2026-08-31 13:21   ` Ilpo Järvinen
  0 siblings, 0 replies; 3+ messages in thread
From: Ilpo Järvinen @ 2026-08-31 13:21 UTC (permalink / raw)
  To: Zhi Wang
  Cc: rust-for-linux, linux-pci, LKML, dakr, aliceryhl, bhelgaas,
	kwilczynski, ojeda, boqun, gary, bjorn3_gh, lossin, a.hindborg,
	tmgross, markus.probst, cjia, smitra, ankita, aniketa, kwankhede,
	targupta, kjaju, alkumar, acourbot, jhubbard, zhiwang, joelagnelf,
	daniel.almeida, tamird, work

[-- Attachment #1: Type: text/plain, Size: 14150 bytes --]

On Thu, 27 Aug 2026, Zhi Wang wrote:

> Rust PCI drivers have no typed interface for locating and accessing PCIe
> extended capabilities.
> 
> The SR-IOV extended capability describes VF topology and VF BARs. Expose
> this information through the Rust PCI abstraction so drivers can use the
> existing typed configuration-space accessors instead of raw bindings.
> 
> Define ExtCapability to associate a capability ID with a register layout,
> and add ConfigSpace::find_ext_capability() to locate and project that
> layout. Bound the view at the next capability or the end of extended
> configuration space. Add ExtSriovRegs and a decoded VF BAR iterator that
> reads and validates all six VF BAR register slots up front, yields decoded
> BAR addresses and widths in logical order, and keeps the raw
> configuration-space slot advancement internal. Since PCI_EXT_CAP_NEXT() is
> a function-like macro, expose it through a Rust helper.
> 
> Keep raw SR-IOV registers private because PCI core owns their state. Expose
> the current First VF Offset through a read-only accessor because the Nova
> GSP ABI needs the raw 16-bit First VF Offset in Routing ID space. The
> pci_iov_virtfn_devfn() helper instead returns the low eight bits of a
> calculated VF Routing ID.
> 
> Link: https://lore.kernel.org/rust-for-linux/20260818084633.1673214-2-zhiw@nvidia.com/
> Cc: Alexandre Courbot <acourbot@nvidia.com>
> Cc: Gary Guo <gary@garyguo.net>
> Signed-off-by: Zhi Wang <zhiw@nvidia.com>
> ---
>  rust/helpers/pci.c     |   5 +
>  rust/kernel/pci.rs     |   8 ++
>  rust/kernel/pci/cap.rs | 290 +++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 303 insertions(+)
>  create mode 100644 rust/kernel/pci/cap.rs
> 
> diff --git a/rust/helpers/pci.c b/rust/helpers/pci.c
> index a714cc2bfb7a..502f56e02fcc 100644
> --- a/rust/helpers/pci.c
> +++ b/rust/helpers/pci.c
> @@ -29,6 +29,11 @@ __rust_helper unsigned int rust_helper_pci_irq_type(struct pci_dev *pdev)
>  	return pci_irq_type(pdev);
>  }
>  
> +__rust_helper u32 rust_helper_pci_ext_cap_next(u32 header)
> +{
> +	return PCI_EXT_CAP_NEXT(header);
> +}
> +
>  #ifndef CONFIG_PCI_IOV
>  __rust_helper unsigned int
>  rust_helper_pci_sriov_get_totalvfs(struct pci_dev *pdev)
> diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
> index 3ec897709e89..77c52fe59551 100644
> --- a/rust/kernel/pci.rs
> +++ b/rust/kernel/pci.rs
> @@ -32,10 +32,18 @@
>      },
>  };
>  
> +mod cap;
>  mod id;
>  mod io;
>  mod irq;
>  
> +pub use self::cap::{
> +    ExtCapId,
> +    ExtCapability,
> +    ExtSriovCapability,
> +    ExtSriovRegs,
> +    ExtSriovVfBar, //
> +};
>  pub use self::id::{
>      Class,
>      ClassMask,
> diff --git a/rust/kernel/pci/cap.rs b/rust/kernel/pci/cap.rs
> new file mode 100644
> index 000000000000..74cd9e50e919
> --- /dev/null
> +++ b/rust/kernel/pci/cap.rs
> @@ -0,0 +1,290 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! PCI extended capability support.
> +
> +use super::{
> +    io::ConfigSpaceBackend,
> +    ConfigSpace,
> +    Extended, //
> +};
> +use crate::{
> +    bindings,
> +    io::{
> +        Io,
> +        IoBackend,
> +        Region, //
> +    },
> +    num::{casts, Bounded},
> +    prelude::*,
> +};
> +
> +/// Number of VF BAR register slots in an SR-IOV capability.
> +const NUM_VF_BARS: usize = casts::u32_as_usize(bindings::PCI_SRIOV_NUM_BARS);
> +
> +/// PCI extended capability IDs.
> +#[repr(transparent)]
> +#[derive(Debug, Clone, Copy, PartialEq, Eq)]
> +pub struct ExtCapId(u16);
> +
> +impl ExtCapId {
> +    /// Single Root I/O Virtualization.
> +    pub const SRIOV: Self = Self(casts::u32_into_u16::<{ bindings::PCI_EXT_CAP_ID_SRIOV }>());
> +
> +    /// Returns the raw PCIe extended capability ID.
> +    #[inline]
> +    const fn as_raw(self) -> u16 {
> +        self.0
> +    }
> +}
> +
> +/// A typed PCI extended capability register layout.
> +///
> +/// Implementors describe the register layout of one extended capability. The layout must start at
> +/// the extended capability header, and [`Self::ID`] must identify that layout.
> +pub trait ExtCapability: FromBytes + IntoBytes {
> +    /// PCI extended capability ID for this register layout.
> +    const ID: ExtCapId;
> +}
> +
> +impl<'a> ConfigSpace<'a, Extended> {
> +    /// Finds and projects an extended capability into its typed register layout.
> +    ///
> +    /// Returns [`None`] if the device does not implement the capability.
> +    ///
> +    /// Returns an error if the capability is present but its register span is too small or
> +    /// insufficiently aligned for `C`.
> +    ///
> +    /// # Examples
> +    ///
> +    /// ```no_run
> +    /// use kernel::{
> +    ///     device::Bound,
> +    ///     pci,
> +    ///     prelude::*,
> +    /// };
> +    ///
> +    /// fn probe_sriov(pdev: &pci::Device<Bound>) -> Result {
> +    ///     let Some(sriov) = pdev
> +    ///         .config_space_extended()?
> +    ///         .find_ext_capability::<pci::ExtSriovRegs>()?
> +    ///     else {
> +    ///         return Ok(());
> +    ///     };
> +    ///
> +    ///     let first_vf_offset = sriov.first_vf_offset();
> +    ///     let mut vf_bars = sriov.vf_bars()?;
> +    ///     let bar0 = vf_bars.next().ok_or(EINVAL)?;
> +    ///     let bar1 = vf_bars.next().ok_or(EINVAL)?;
> +    ///     let bar2 = vf_bars.next().ok_or(EINVAL)?;
> +    ///
> +    ///     Ok(())
> +    /// }
> +    /// ```
> +    pub fn find_ext_capability<C: ExtCapability>(&self) -> Result<Option<ConfigSpace<'a, C>>> {
> +        let offset = usize::from(
> +            // SAFETY: `self.pdev` is valid by the type invariant of `ConfigSpace`.
> +            unsafe {
> +                bindings::pci_find_ext_capability(self.pdev.as_raw(), i32::from(C::ID.as_raw()))
> +            },
> +        );
> +
> +        if offset == 0 {
> +            return Ok(None);
> +        }
> +
> +        let size = self.calculate_ext_cap_size(offset)?;
> +
> +        let base = ConfigSpaceBackend::as_ptr(*self)
> +            .cast::<u8>()
> +            .wrapping_add(offset);
> +        let ptr = Region::<0>::ptr_try_from_raw_parts_mut(base, size)?;
> +
> +        // SAFETY: `offset` was returned by `pci_find_ext_capability`, and
> +        // `calculate_ext_cap_size` bounds `ptr` at the next capability or the end of the extended
> +        // configuration space. `ptr_try_from_raw_parts_mut` verified the region layout.
> +        let capability = unsafe { ConfigSpaceBackend::project_view(*self, ptr) };
> +
> +        capability.try_cast::<C>().map(Some)
> +    }
> +
> +    /// Calculates the size of the extended capability at `offset`.
> +    ///
> +    /// The capability extends to the next extended capability, or to the end of the extended
> +    /// configuration space if it is the last one. `offset` must be a DWORD-aligned offset within
> +    /// the extended configuration space returned by `pci_find_ext_capability`. Returns an error if
> +    /// the capability header is outside the extended configuration space.
> +    fn calculate_ext_cap_size(&self, offset: usize) -> Result<usize> {
> +        let header = self.try_read32(offset)?;
> +        // SAFETY: Pure bit manipulation, no preconditions.
> +        let next = casts::u32_as_usize(unsafe { bindings::pci_ext_cap_next(header) });
> +
> +        Ok(if next > offset {
> +            next - offset
> +        } else {
> +            self.size() - offset
> +        })
> +    }
> +}
> +
> +/// SR-IOV register layout per PCIe spec (64 bytes starting at cap offset).
> +///
> +/// The raw registers are private because PCI core owns SR-IOV state management. Drivers should use
> +/// PCI core APIs for operations such as enabling VFs and querying their topology.
> +#[repr(C)]
> +#[derive(FromBytes, IntoBytes)]
> +pub struct ExtSriovRegs {
> +    /// Extended capability header.
> +    _header: u32,
> +    /// SR-IOV capabilities.
> +    _cap: u32,
> +    /// SR-IOV control.
> +    _ctrl: u16,
> +    /// SR-IOV status.
> +    _status: u16,
> +    /// Initial VFs.
> +    _initial_vfs: u16,
> +    /// Total VFs.
> +    _total_vfs: u16,
> +    /// Number of VFs.
> +    _num_vfs: u16,
> +    /// Function dependency link.
> +    _func_dep_link: u8,
> +    _reserved_0: u8,
> +    /// First VF offset.
> +    vf_offset: u16,
> +    /// VF stride.
> +    _vf_stride: u16,
> +    _reserved_1: u16,
> +    /// VF device ID.
> +    _vf_device_id: u16,
> +    /// Supported page sizes.
> +    _supported_page_sizes: u32,
> +    /// System page size.
> +    _system_page_size: u32,
> +    /// VF BARs (BAR0–BAR5).
> +    vf_bar: [u32; NUM_VF_BARS],
> +    /// VF migration state array offset.
> +    _migration_state: u32,
> +}
> +
> +impl ExtCapability for ExtSriovRegs {
> +    const ID: ExtCapId = ExtCapId::SRIOV;
> +}
> +
> +/// A typed view of an SR-IOV extended capability.
> +pub type ExtSriovCapability<'a> = ConfigSpace<'a, ExtSriovRegs>;
> +
> +#[derive(Debug, Clone, Copy, PartialEq, Eq)]
> +enum VfBarMemoryType {
> +    Bits32,
> +    Bits64,
> +}
> +
> +impl TryFrom<Bounded<u32, 2>> for VfBarMemoryType {
> +    type Error = Error;
> +
> +    fn try_from(value: Bounded<u32, 2>) -> Result<Self> {
> +        match value.get() {
> +            0b00 => Ok(Self::Bits32),
> +            0b10 => Ok(Self::Bits64),
> +            _ => Err(EINVAL),
> +        }
> +    }
> +}
> +
> +impl From<VfBarMemoryType> for Bounded<u32, 2> {
> +    fn from(value: VfBarMemoryType) -> Self {
> +        match value {
> +            VfBarMemoryType::Bits32 => Self::new::<0b00>(),
> +            VfBarMemoryType::Bits64 => Self::new::<0b10>(),
> +        }
> +    }
> +}
> +
> +crate::bitfield! {
> +    /// Low DWORD of an SR-IOV VF BAR.
> +    struct VfBarLow(u32) {

I admit not knowing that much about Rust but this caught my eye...

The PCIe spec says:

"These fields must define the VF’s Base Address Registers (BARs). These 
fields behave as normal PCI BARs, as described in § Section 7.5.1 ." 

So why doesn't Rust just define this as "normal" BARs (given your naming 
above, I'd expect simply "BarLow") but names it after "VF" specifically?
Or does Rust require duplicating things in cases like this for namespacing 
reasons?

"BarLow" doesn't seem to exist under mainline's rust/, maybe that hasn't 
been needed so far as C side code will likely be responsible for parsing 
them.

> +        /// Base address bits 31:4.
> +        31:4 address;
> +        /// Whether the address range is prefetchable.
> +        3:3 prefetchable => bool;
> +        /// Memory BAR type.
> +        2:1 memory_type ?=> VfBarMemoryType;
> +        /// Whether this is an I/O-space BAR.
> +        0:0 io_space => bool;
> +    }
> +}
> +
> +/// A decoded VF BAR register encoding.
> +#[derive(Debug, Clone, Copy, PartialEq, Eq)]
> +pub struct ExtSriovVfBar {
> +    /// The PCI bus address encoded by the BAR, without PCI attribute bits.
> +    ///
> +    /// This is not necessarily the CPU resource address.
> +    pub address: u64,
> +
> +    /// Whether the BAR is 64-bit.
> +    pub is_64bit: bool,
> +}

-- 
 i.

> +impl ExtSriovCapability<'_> {
> +    /// Returns the current PCIe First VF Offset in Routing ID space.
> +    ///
> +    /// This value may change when NumVFs changes. Drivers should use PCI core helpers when they
> +    /// need to calculate a VF BDF; this accessor is intended for interfaces that require the First
> +    /// VF Offset itself.
> +    #[inline]
> +    pub fn first_vf_offset(&self) -> u16 {
> +        crate::io_read!(*self, .vf_offset)
> +    }
> +
> +    /// Returns an iterator over decoded VF BAR register encodings.
> +    ///
> +    /// All six raw VF BAR register slots are read and decoded up front. A 32-bit encoding yields
> +    /// one entry; a 64-bit encoding combines two slots into one entry.
> +    ///
> +    /// A zero-valued low DWORD is yielded as a 32-bit BAR at address zero; this method does not
> +    /// probe whether a BAR is implemented.
> +    ///
> +    /// Returns [`EINVAL`] and logs an error if a BAR low DWORD does not encode a 32-bit or 64-bit
> +    /// memory BAR, or if a 64-bit encoding has no upper DWORD.
> +    pub fn vf_bars(&self) -> Result<impl Iterator<Item = ExtSriovVfBar>> {
> +        let slots: [u32; NUM_VF_BARS] =
> +            core::array::from_fn(|slot| crate::io_read!(*self, .vf_bar[panic: slot]));
> +        let mut slots = slots.into_iter();
> +        let mut bars = [None; NUM_VF_BARS];
> +        let mut count = 0;
> +
> +        let mut decode = || {
> +            while let Some(low) = slots.next().map(VfBarLow::from) {
> +                // SR-IOV VF BARs describe memory-space apertures; an I/O-space encoding is not
> +                // valid for these registers.
> +                if low.io_space() {
> +                    return Err(EINVAL);
> +                }
> +
> +                let low_address = u64::from(low.address()) << VfBarLow::ADDRESS_SHIFT;
> +                let bar = match low.memory_type()? {
> +                    VfBarMemoryType::Bits64 => ExtSriovVfBar {
> +                        address: (u64::from(slots.next().ok_or(EINVAL)?) << 32) | low_address,
> +                        is_64bit: true,
> +                    },
> +                    VfBarMemoryType::Bits32 => ExtSriovVfBar {
> +                        address: low_address,
> +                        is_64bit: false,
> +                    },
> +                };
> +
> +                bars[count] = Some(bar);
> +                count += 1;
> +            }
> +
> +            Ok(())
> +        };
> +
> +        decode().inspect_err(|_| {
> +            dev_err!(self.pdev, "invalid VF BAR encoding in SR-IOV capability\n");
> +        })?;
> +        Ok(bars.into_iter().flatten())
> +    }
> +}
> 

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-08-31 13:21 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-27 12:18 [PATCH v9 0/1] Rust PCI capability infrastructure and SR-IOV support Zhi Wang
2026-08-27 12:18 ` [PATCH v9 1/1] rust: pci: add extended capability " Zhi Wang
2026-08-31 13:21   ` Ilpo Järvinen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox