pci_driver/config/
ext_caps.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! Provides facilities for accessing Extended Capabilities described in some PCI configuration
4//! space.
5//!
6//! For plain old non-extended Capabilities, see [`pci_driver::config::caps`](`super::caps`).
7//!
8//! The following table relates the section numbers and titles from the "PCI Express® Base
9//! Specification Revision 6.0" describing Extended Capabilities to the corresponding type:
10//!
11//! | Section number | Section title | Type |
12//! |-|-|-|
13//! | 7.9.5 | Vendor-Specific Extended Capability | [`VendorSpecificExtendedCapability`] |
14//! | 7.9.28 | Null Extended Capability | [`NullExtendedCapability`] |
15
16/* ---------------------------------------------------------------------------------------------- */
17
18use std::fmt::Debug;
19use std::io::{self, ErrorKind};
20use std::iter::{Flatten, FusedIterator};
21use std::marker::PhantomData;
22use std::ops::Range;
23use std::vec;
24
25use crate::config::caps::PciExpressCapability;
26use crate::config::PciConfig;
27use crate::pci_bit_field;
28use crate::regions::{AsPciSubregion, BackedByPciSubregion, PciRegion, PciSubregion};
29
30/* ---------------------------------------------------------------------------------------------- */
31
32/// Some specific type of PCI Extended Capability.
33pub trait ExtendedCapability<'a>:
34    PciRegion + AsPciSubregion<'a> + Clone + Copy + Debug + Sized
35{
36    /// Tries to create an instance of this `Capability` backed by the given [`AsPciSubregion`]. If
37    /// things like for instance the Capablity ID and Capability Version and possibly other factors
38    /// don't match what is expected for the present type, returns `Ok(None)`.
39    ///
40    /// Implementations should also make sure that the subregion is big enough, and fail with an
41    /// error if it isn't.
42    fn backed_by(as_subregion: impl AsPciSubregion<'a>) -> io::Result<Option<Self>>;
43
44    /// The header of the Extended Capability.
45    fn header(&self) -> ExtendedCapabilityHeader<'a>;
46}
47
48pci_bit_field! {
49    pub struct ExtendedCapabilityHeader<'a> : RO u32 {
50        capability_id          @  0--15 : RO u16,
51        /// This field is a PCI-SIG defined version number that indicates the version of the
52        /// Capability structure present.
53        capability_version     @ 16--19 : RO u8,
54        /// This field contains the offset to the next PCI Express Capability structure or 0x000 if
55        /// no other items exist in the linked list of Capabilities.
56        ///
57        /// You don't need to be using this directly. Use [`PciExtendedCapabilities`] to iterate
58        /// over capabilities instead.
59        next_capability_offset @ 20--31 : RO u16,
60    }
61}
62
63/* ---------------------------------------------------------------------------------------------- */
64
65/// Lets you inspect and manipulate the PCI Extended Capabilities defined in the configuration space
66/// of some PCI device.
67#[derive(Clone, Debug)]
68pub struct PciExtendedCapabilities<'a> {
69    cap_subregions: Box<[PciSubregion<'a>]>,
70}
71
72impl<'a> PciExtendedCapabilities<'a> {
73    pub fn backed_by(config_space: PciConfig<'a>) -> io::Result<Self> {
74        const CAP_RANGE: Range<usize> = 0x100..0x1000;
75
76        // Number of 2-byte words in extended config space
77        const ITERATIONS_UPPER_BOUND: usize = (CAP_RANGE.end - CAP_RANGE.start) / 2;
78
79        if config_space.len() < 0x1000 {
80            return Err(io::Error::new(
81                ErrorKind::InvalidInput,
82                format!(
83                    "Config space is 0x{:x} bytes long, expected at least 0x1000",
84                    config_space.len()
85                ),
86            ));
87        }
88
89        // This is somewhat expensive, but ensures we don't give unexpected results when the device
90        // is not PCI Express.
91        if config_space
92            .capabilities()?
93            .of_type::<PciExpressCapability>()?
94            .next()
95            .is_none()
96        {
97            // not a PCI Express device
98            return Ok(PciExtendedCapabilities {
99                cap_subregions: Box::new([]),
100            });
101        }
102
103        let mut cap_subregions = Vec::new();
104        let mut next_cap_offset = 0x100; // there's always at least one extended capability
105
106        while next_cap_offset != 0x000 {
107            if !CAP_RANGE.contains(&(next_cap_offset as usize)) {
108                return Err(io::Error::new(
109                    ErrorKind::InvalidInput,
110                    format!(
111                        "Extended Capability has offset 0x{:03x}, should be in [0x100, 0xfff]",
112                        next_cap_offset,
113                    ),
114                ));
115            }
116
117            if next_cap_offset % 2 != 0 {
118                return Err(io::Error::new(
119                    ErrorKind::InvalidInput,
120                    format!(
121                        "Extended Capability has offset 0x{:03x}, expected multiple of two",
122                        next_cap_offset,
123                    ),
124                ));
125            }
126
127            if cap_subregions.len() == ITERATIONS_UPPER_BOUND {
128                return Err(io::Error::new(
129                    ErrorKind::InvalidInput,
130                    format!(
131                        "Found more than {} Extended Capabilities, which implies a capability \
132                        list cycle",
133                        ITERATIONS_UPPER_BOUND,
134                    ),
135                ));
136            }
137
138            let cap_subregion = config_space.subregion(next_cap_offset.into()..0x1000);
139            let cap_header = ExtendedCapabilityHeader::backed_by(cap_subregion);
140
141            cap_subregions.push(cap_subregion);
142            next_cap_offset = cap_header.next_capability_offset().read()? & 0xfffc;
143        }
144
145        Ok(PciExtendedCapabilities {
146            cap_subregions: cap_subregions.into_boxed_slice(),
147        })
148    }
149
150    /// Returns an iterator over all extended capabilities.
151    pub fn iter(&self) -> PciExtendedCapabilitiesIter<'a, UnspecifiedExtendedCapability<'a>> {
152        // UnspecifiedExtendedCapability::backed_by() never fails, so we unwrap()
153        self.of_type().unwrap()
154    }
155
156    /// Returns an iterator over the capabilities that can be represented by `C`.
157    ///
158    /// This works by trying [`C::backed_by`](ExtendedCapability::backed_by) on every capability.
159    pub fn of_type<C: ExtendedCapability<'a>>(
160        &self,
161    ) -> io::Result<PciExtendedCapabilitiesIter<'a, C>> {
162        let iter = self
163            .cap_subregions
164            .iter()
165            .map(C::backed_by)
166            .collect::<io::Result<Vec<_>>>()?
167            .into_iter()
168            .flatten();
169
170        Ok(PciExtendedCapabilitiesIter {
171            iter,
172            phantom: PhantomData,
173        })
174    }
175}
176
177impl<'a> IntoIterator for PciExtendedCapabilities<'a> {
178    type Item = UnspecifiedExtendedCapability<'a>;
179    type IntoIter = PciExtendedCapabilitiesIntoIter<'a>;
180
181    fn into_iter(self) -> Self::IntoIter {
182        PciExtendedCapabilitiesIntoIter {
183            iter: Vec::from(self.cap_subregions).into_iter(),
184        }
185    }
186}
187
188impl<'a, 'b> IntoIterator for &'b PciExtendedCapabilities<'a> {
189    type Item = UnspecifiedExtendedCapability<'a>;
190    type IntoIter = PciExtendedCapabilitiesIter<'a, UnspecifiedExtendedCapability<'a>>;
191
192    fn into_iter(self) -> Self::IntoIter {
193        self.iter()
194    }
195}
196
197/* ---------------------------------------------------------------------------------------------- */
198
199/// An iterator over all PCI Extended Capabilities of a device.
200pub struct PciExtendedCapabilitiesIntoIter<'a> {
201    iter: vec::IntoIter<PciSubregion<'a>>,
202}
203
204impl<'a> Iterator for PciExtendedCapabilitiesIntoIter<'a> {
205    type Item = UnspecifiedExtendedCapability<'a>;
206
207    fn next(&mut self) -> Option<Self::Item> {
208        let subregion = self.iter.next()?;
209        UnspecifiedExtendedCapability::backed_by(subregion).unwrap()
210    }
211
212    fn size_hint(&self) -> (usize, Option<usize>) {
213        self.iter.size_hint()
214    }
215}
216
217impl FusedIterator for PciExtendedCapabilitiesIntoIter<'_> {}
218
219/* ---------------------------------------------------------------------------------------------- */
220
221/// An iterator over a device's PCI Extended Capabilities of a certain type.
222pub struct PciExtendedCapabilitiesIter<'a, C: ExtendedCapability<'a>> {
223    iter: Flatten<vec::IntoIter<Option<C>>>,
224    phantom: PhantomData<&'a ()>,
225}
226
227impl<'a, C: ExtendedCapability<'a>> Iterator for PciExtendedCapabilitiesIter<'a, C> {
228    type Item = C;
229
230    fn next(&mut self) -> Option<Self::Item> {
231        self.iter.next()
232    }
233
234    fn size_hint(&self) -> (usize, Option<usize>) {
235        self.iter.size_hint()
236    }
237}
238
239impl<'a, C: ExtendedCapability<'a>> FusedIterator for PciExtendedCapabilitiesIter<'a, C> {}
240
241/* ---------------------------------------------------------------------------------------------- */
242
243macro_rules! pci_extended_capability {
244    (
245        $(
246            $(#[$attr:meta])*
247            $vis:vis struct $name:ident<$lifetime:lifetime> {
248                $(Id = $id:literal,)?
249                $(MinVersion = $min_version:literal,)?
250                $(Matcher = $matcher:expr,)?
251                Length = $length:expr,
252                Fields = {
253                    $(
254                        $(#[$field_attr:meta])*
255                        $field_name:ident @ $field_offset:literal :
256                        $($field_type:ident)::+$(<$($field_generics:tt),+ $(,)?>)?
257                    ),* $(,)?
258                },
259            }
260        )*
261    ) => {
262        $(
263            $(#[$attr])*
264            #[derive(Clone, Copy)]
265            $vis struct $name<$lifetime> {
266                subregion: $crate::regions::PciSubregion<$lifetime>,
267            }
268
269            impl<'a> ExtendedCapability<'a> for $name<'a> {
270                fn backed_by(as_subregion: impl $crate::regions::AsPciSubregion<'a>) -> ::std::io::Result<Option<Self>> {
271                    let subregion = $crate::regions::AsPciSubregion::as_subregion(&as_subregion);
272
273                    $(
274                        let header = $crate::config::ext_caps::ExtendedCapabilityHeader::backed_by(subregion);
275                        if header.capability_id().read()? != $id {
276                            return ::std::io::Result::Ok(::std::option::Option::None);
277                        }
278                    )?
279
280                    $(
281                        let header = $crate::config::ext_caps::ExtendedCapabilityHeader::backed_by(subregion);
282                        if header.capability_version().read()? < $min_version {
283                            return ::std::io::Result::Ok(::std::option::Option::None);
284                        }
285                    )?
286
287                    // construct capability from a subregion that may be unnecessary long
288
289                    let cap = $name { subregion };
290
291                    $(
292                        let matcher_fn: fn(&Self) -> ::std::io::Result<()> = $matcher;
293                        if !matcher_fn(&cap)? {
294                            return ::std::io::Result::Ok(::std::option::Option::None);
295                        }
296                    )?
297
298                    let length_fn: fn(&Self) -> ::std::io::Result<u16> = $length;
299                    let length: u64 = length_fn(&cap)?.into();
300
301                    // construct new capability from a subregion with just the right size
302
303                    let cap = $name {
304                        subregion: subregion.subregion(..length),
305                    };
306
307                    ::std::io::Result::Ok(::std::option::Option::Some(cap))
308                }
309
310                fn header(&self) -> $crate::config::ext_caps::ExtendedCapabilityHeader<'a> {
311                    $crate::regions::BackedByPciSubregion::backed_by(self.subregion)
312                }
313            }
314
315            impl<'a> $crate::regions::AsPciSubregion<'a> for $name<'a> {
316                fn as_subregion(&self) -> $crate::regions::PciSubregion<'a> {
317                    self.subregion
318                }
319            }
320
321            $crate::_pci_struct_impl! {
322                impl $name<$lifetime> {
323                    $(
324                        $(#[$field_attr])*
325                        $field_name @ $field_offset :
326                        $($field_type)::+$(<$($field_generics),+>)?
327                    ),*
328                }
329            }
330        )*
331    };
332}
333
334/* ---------------------------------------------------------------------------------------------- */
335
336pci_extended_capability! {
337    /// Any PCI Extended Capability.
338    pub struct UnspecifiedExtendedCapability<'a> {
339        Length = |_cap| Ok(0x004),
340        Fields = {},
341    }
342}
343
344// 7.9.5 Vendor-Specific Extended Capability
345
346pci_extended_capability! {
347    /// Described in Section 7.9.5 of the "PCI Express® Base Specification Revision 6.0".
348    pub struct VendorSpecificExtendedCapability<'a> {
349        Id = 0x000b,
350        MinVersion = 0x1,
351        Length = |cap| cap.vendor_specific_header().vsec_length().read(),
352        Fields = {
353            vendor_specific_header @ 0x004 : VendorSpecificHeader,
354            // TODO
355        },
356    }
357}
358
359pci_bit_field! {
360    /// Described in Section 7.9.5.2 of the "PCI Express® Base Specification Revision 6.0".
361    pub struct VendorSpecificHeader<'a> : RO u32 {
362        vsec_id     @  0--15 : RO u16,
363        vsec_rev    @ 16--19 : RO u8,
364        vsec_length @ 20--31 : RO u16,
365    }
366}
367
368// 7.9.28 Null Extended Capability
369
370pci_extended_capability! {
371    /// Described in Section 7.9.28 of the "PCI Express® Base Specification Revision 6.0".
372    pub struct NullExtendedCapability<'a> {
373        Id = 0x000b,
374        Length = |_cap| Ok(0x004),
375        Fields = {},
376    }
377}
378
379/* ---------------------------------------------------------------------------------------------- */