Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

rust: pci: add config space read/write support

Drivers might need to access PCI config space for querying capability
structures and access the registers inside the structures.

For Rust drivers need to access PCI config space, the Rust PCI abstraction
needs to support it in a way that upholds Rust's safety principles.

Introduce a `ConfigSpace` wrapper in Rust PCI abstraction to provide safe
accessors for PCI config space. The new type implements the `Io` trait and
`IoCapable<T>` for u8, u16, and u32 to share offset validation and
bound-checking logic with other I/O backends.

The `ConfigSpace` type uses marker types (`Normal` and `Extended`) to
represent configuration space sizes at the type level.

Cc: Alexandre Courbot <acourbot@nvidia.com>
Cc: Danilo Krummrich <dakr@kernel.org>
Cc: Gary Guo <gary@garyguo.net>
Cc: Joel Fernandes <joelagnelf@nvidia.com>
Signed-off-by: Zhi Wang <zhiw@nvidia.com>
Reviewed-by: Gary Guo <gary@garyguo.net>
Link: https://lore.kernel.org/all/DFV4IJDQC2J6.1Q91JOAL6CJSG@kernel.org/ [1]
Link: https://patch.msgid.link/20260121202212.4438-5-zhiw@nvidia.com
[ Applied the diff from [1], considering subsequent comment; remove
#[expect(unused)] from define_{read,write}!(). - Danilo ]
Signed-off-by: Danilo Krummrich <dakr@kernel.org>

authored by

Zhi Wang and committed by
Danilo Krummrich
4dc0bacb 5981d03c

+204 -4
-2
rust/kernel/io.rs
··· 225 225 } 226 226 }; 227 227 } 228 - #[expect(unused)] 229 228 pub(crate) use define_read; 230 229 231 230 macro_rules! define_write { ··· 257 258 } 258 259 }; 259 260 } 260 - #[expect(unused)] 261 261 pub(crate) use define_write; 262 262 263 263 /// Checks whether an access of type `U` at the given `offset`
+7 -1
rust/kernel/pci.rs
··· 40 40 ClassMask, 41 41 Vendor, // 42 42 }; 43 - pub use self::io::Bar; 43 + pub use self::io::{ 44 + Bar, 45 + ConfigSpaceKind, 46 + ConfigSpaceSize, 47 + Extended, 48 + Normal, // 49 + }; 44 50 pub use self::irq::{ 45 51 IrqType, 46 52 IrqTypes,
+197 -1
rust/kernel/pci/io.rs
··· 8 8 device, 9 9 devres::Devres, 10 10 io::{ 11 + define_read, 12 + define_write, 13 + Io, 14 + IoCapable, 15 + IoKnownSize, 11 16 Mmio, 12 17 MmioRaw, // 13 18 }, 14 19 prelude::*, 15 20 sync::aref::ARef, // 16 21 }; 17 - use core::ops::Deref; 22 + use core::{ 23 + marker::PhantomData, 24 + ops::Deref, // 25 + }; 26 + 27 + /// Represents the size of a PCI configuration space. 28 + /// 29 + /// PCI devices can have either a *normal* (legacy) configuration space of 256 bytes, 30 + /// or an *extended* configuration space of 4096 bytes as defined in the PCI Express 31 + /// specification. 32 + #[repr(usize)] 33 + #[derive(Eq, PartialEq)] 34 + pub enum ConfigSpaceSize { 35 + /// 256-byte legacy PCI configuration space. 36 + Normal = 256, 37 + 38 + /// 4096-byte PCIe extended configuration space. 39 + Extended = 4096, 40 + } 41 + 42 + impl ConfigSpaceSize { 43 + /// Get the raw value of this enum. 44 + #[inline(always)] 45 + pub const fn into_raw(self) -> usize { 46 + // CAST: PCI configuration space size is at most 4096 bytes, so the value always fits 47 + // within `usize` without truncation or sign change. 48 + self as usize 49 + } 50 + } 51 + 52 + /// Marker type for normal (256-byte) PCI configuration space. 53 + pub struct Normal; 54 + 55 + /// Marker type for extended (4096-byte) PCIe configuration space. 56 + pub struct Extended; 57 + 58 + /// Trait for PCI configuration space size markers. 59 + /// 60 + /// This trait is implemented by [`Normal`] and [`Extended`] to provide 61 + /// compile-time knowledge of the configuration space size. 62 + pub trait ConfigSpaceKind { 63 + /// The size of this configuration space in bytes. 64 + const SIZE: usize; 65 + } 66 + 67 + impl ConfigSpaceKind for Normal { 68 + const SIZE: usize = 256; 69 + } 70 + 71 + impl ConfigSpaceKind for Extended { 72 + const SIZE: usize = 4096; 73 + } 74 + 75 + /// The PCI configuration space of a device. 76 + /// 77 + /// Provides typed read and write accessors for configuration registers 78 + /// using the standard `pci_read_config_*` and `pci_write_config_*` helpers. 79 + /// 80 + /// The generic parameter `S` indicates the maximum size of the configuration space. 81 + /// Use [`Normal`] for 256-byte legacy configuration space or [`Extended`] for 82 + /// 4096-byte PCIe extended configuration space (default). 83 + pub struct ConfigSpace<'a, S: ConfigSpaceKind = Extended> { 84 + pub(crate) pdev: &'a Device<device::Bound>, 85 + _marker: PhantomData<S>, 86 + } 87 + 88 + /// Internal helper macros used to invoke C PCI configuration space read functions. 89 + /// 90 + /// This macro is intended to be used by higher-level PCI configuration space access macros 91 + /// (define_read) and provides a unified expansion for infallible vs. fallible read semantics. It 92 + /// emits a direct call into the corresponding C helper and performs the required cast to the Rust 93 + /// return type. 94 + /// 95 + /// # Parameters 96 + /// 97 + /// * `$c_fn` – The C function performing the PCI configuration space write. 98 + /// * `$self` – The I/O backend object. 99 + /// * `$ty` – The type of the value to read. 100 + /// * `$addr` – The PCI configuration space offset to read. 101 + /// 102 + /// This macro does not perform any validation; all invariants must be upheld by the higher-level 103 + /// abstraction invoking it. 104 + macro_rules! call_config_read { 105 + (infallible, $c_fn:ident, $self:ident, $ty:ty, $addr:expr) => {{ 106 + let mut val: $ty = 0; 107 + // SAFETY: By the type invariant `$self.pdev` is a valid address. 108 + // CAST: The offset is cast to `i32` because the C functions expect a 32-bit signed offset 109 + // parameter. PCI configuration space size is at most 4096 bytes, so the value always fits 110 + // within `i32` without truncation or sign change. 111 + // Return value from C function is ignored in infallible accessors. 112 + let _ret = unsafe { bindings::$c_fn($self.pdev.as_raw(), $addr as i32, &mut val) }; 113 + val 114 + }}; 115 + } 116 + 117 + /// Internal helper macros used to invoke C PCI configuration space write functions. 118 + /// 119 + /// This macro is intended to be used by higher-level PCI configuration space access macros 120 + /// (define_write) and provides a unified expansion for infallible vs. fallible read semantics. It 121 + /// emits a direct call into the corresponding C helper and performs the required cast to the Rust 122 + /// return type. 123 + /// 124 + /// # Parameters 125 + /// 126 + /// * `$c_fn` – The C function performing the PCI configuration space write. 127 + /// * `$self` – The I/O backend object. 128 + /// * `$ty` – The type of the written value. 129 + /// * `$addr` – The configuration space offset to write. 130 + /// * `$value` – The value to write. 131 + /// 132 + /// This macro does not perform any validation; all invariants must be upheld by the higher-level 133 + /// abstraction invoking it. 134 + macro_rules! call_config_write { 135 + (infallible, $c_fn:ident, $self:ident, $ty:ty, $addr:expr, $value:expr) => { 136 + // SAFETY: By the type invariant `$self.pdev` is a valid address. 137 + // CAST: The offset is cast to `i32` because the C functions expect a 32-bit signed offset 138 + // parameter. PCI configuration space size is at most 4096 bytes, so the value always fits 139 + // within `i32` without truncation or sign change. 140 + // Return value from C function is ignored in infallible accessors. 141 + let _ret = unsafe { bindings::$c_fn($self.pdev.as_raw(), $addr as i32, $value) }; 142 + }; 143 + } 144 + 145 + // PCI configuration space supports 8, 16, and 32-bit accesses. 146 + impl<'a, S: ConfigSpaceKind> IoCapable<u8> for ConfigSpace<'a, S> {} 147 + impl<'a, S: ConfigSpaceKind> IoCapable<u16> for ConfigSpace<'a, S> {} 148 + impl<'a, S: ConfigSpaceKind> IoCapable<u32> for ConfigSpace<'a, S> {} 149 + 150 + impl<'a, S: ConfigSpaceKind> Io for ConfigSpace<'a, S> { 151 + const MIN_SIZE: usize = S::SIZE; 152 + 153 + /// Returns the base address of the I/O region. It is always 0 for configuration space. 154 + #[inline] 155 + fn addr(&self) -> usize { 156 + 0 157 + } 158 + 159 + /// Returns the maximum size of the configuration space. 160 + #[inline] 161 + fn maxsize(&self) -> usize { 162 + self.pdev.cfg_size().into_raw() 163 + } 164 + 165 + // PCI configuration space does not support fallible operations. 166 + // The default implementations from the Io trait are not used. 167 + 168 + define_read!(infallible, read8, call_config_read(pci_read_config_byte) -> u8); 169 + define_read!(infallible, read16, call_config_read(pci_read_config_word) -> u16); 170 + define_read!(infallible, read32, call_config_read(pci_read_config_dword) -> u32); 171 + 172 + define_write!(infallible, write8, call_config_write(pci_write_config_byte) <- u8); 173 + define_write!(infallible, write16, call_config_write(pci_write_config_word) <- u16); 174 + define_write!(infallible, write32, call_config_write(pci_write_config_dword) <- u32); 175 + } 176 + 177 + /// Marker trait indicating ConfigSpace has a known size at compile time. 178 + impl<'a, S: ConfigSpaceKind> IoKnownSize for ConfigSpace<'a, S> {} 18 179 19 180 /// A PCI BAR to perform I/O-Operations on. 20 181 /// ··· 304 143 name: &'a CStr, 305 144 ) -> impl PinInit<Devres<Bar>, Error> + 'a { 306 145 self.iomap_region_sized::<0>(bar, name) 146 + } 147 + 148 + /// Returns the size of configuration space. 149 + pub fn cfg_size(&self) -> ConfigSpaceSize { 150 + // SAFETY: `self.as_raw` is a valid pointer to a `struct pci_dev`. 151 + let size = unsafe { (*self.as_raw()).cfg_size }; 152 + match size { 153 + 256 => ConfigSpaceSize::Normal, 154 + 4096 => ConfigSpaceSize::Extended, 155 + _ => { 156 + // PANIC: The PCI subsystem only ever reports the configuration space size as either 157 + // `ConfigSpaceSize::Normal` or `ConfigSpaceSize::Extended`. 158 + unreachable!(); 159 + } 160 + } 161 + } 162 + 163 + /// Return an initialized normal (256-byte) config space object. 164 + pub fn config_space<'a>(&'a self) -> ConfigSpace<'a, Normal> { 165 + ConfigSpace { 166 + pdev: self, 167 + _marker: PhantomData, 168 + } 169 + } 170 + 171 + /// Return an initialized extended (4096-byte) config space object. 172 + pub fn config_space_extended<'a>(&'a self) -> Result<ConfigSpace<'a, Extended>> { 173 + if self.cfg_size() != ConfigSpaceSize::Extended { 174 + return Err(EINVAL); 175 + } 176 + 177 + Ok(ConfigSpace { 178 + pdev: self, 179 + _marker: PhantomData, 180 + }) 307 181 } 308 182 }