pci_driver/config/
ext_caps.rs1use 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
30pub trait ExtendedCapability<'a>:
34 PciRegion + AsPciSubregion<'a> + Clone + Copy + Debug + Sized
35{
36 fn backed_by(as_subregion: impl AsPciSubregion<'a>) -> io::Result<Option<Self>>;
43
44 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 capability_version @ 16--19 : RO u8,
54 next_capability_offset @ 20--31 : RO u16,
60 }
61}
62
63#[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 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 if config_space
92 .capabilities()?
93 .of_type::<PciExpressCapability>()?
94 .next()
95 .is_none()
96 {
97 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; 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 pub fn iter(&self) -> PciExtendedCapabilitiesIter<'a, UnspecifiedExtendedCapability<'a>> {
152 self.of_type().unwrap()
154 }
155
156 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
197pub 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
219pub 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
241macro_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 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 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
334pci_extended_capability! {
337 pub struct UnspecifiedExtendedCapability<'a> {
339 Length = |_cap| Ok(0x004),
340 Fields = {},
341 }
342}
343
344pci_extended_capability! {
347 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 },
356 }
357}
358
359pci_bit_field! {
360 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
368pci_extended_capability! {
371 pub struct NullExtendedCapability<'a> {
373 Id = 0x000b,
374 Length = |_cap| Ok(0x004),
375 Fields = {},
376 }
377}
378
379