resources/
pci_address.rs

1// Copyright 2018 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::fmt;
6use std::fmt::Display;
7use std::path::Path;
8use std::str::FromStr;
9
10use remain::sorted;
11use serde::Deserialize;
12use serde::Deserializer;
13use serde::Serialize;
14use serde::Serializer;
15use thiserror::Error as ThisError;
16
17/// Identifies a single component of a [`PciAddress`].
18#[derive(Debug, PartialEq, Eq)]
19pub enum PciAddressComponent {
20    Domain,
21    Bus,
22    Device,
23    Function,
24}
25
26/// PCI address parsing and conversion errors.
27#[derive(ThisError, Debug, PartialEq, Eq)]
28#[sorted]
29pub enum Error {
30    /// The specified component was outside the valid range.
31    #[error("{0:?} out of range")]
32    ComponentOutOfRange(PciAddressComponent),
33    /// The specified component could not be parsed as a hexadecimal number.
34    #[error("{0:?} failed to parse as hex")]
35    InvalidHex(PciAddressComponent),
36    /// The given PCI device path is invalid
37    #[error("Invalid PCI device path")]
38    InvalidHostPath,
39    /// A delimiter (`:` or `.`) between the two specified components was missing or incorrect.
40    #[error("Missing delimiter between {0:?} and {1:?}")]
41    MissingDelimiter(PciAddressComponent, PciAddressComponent),
42    /// The PCI address contained more than the expected number of components.
43    #[error("Too many components in PCI address")]
44    TooManyComponents,
45}
46
47pub type Result<T> = std::result::Result<T, Error>;
48
49/// PCI Device Address, AKA Bus:Device.Function
50#[derive(Default, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
51pub struct PciAddress {
52    /// Bus number, in the range `0..=255`.
53    pub bus: u8,
54    /// Device number, in the range `0..=31`.
55    pub dev: u8,
56    /// Function number, in the range `0..=7`.
57    pub func: u8,
58}
59
60impl Serialize for PciAddress {
61    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
62    where
63        S: Serializer,
64    {
65        serializer.serialize_str(&self.to_string())
66    }
67}
68
69impl<'de> Deserialize<'de> for PciAddress {
70    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
71    where
72        D: Deserializer<'de>,
73    {
74        let s = String::deserialize(deserializer)?;
75        FromStr::from_str(&s).map_err(serde::de::Error::custom)
76    }
77}
78
79/// Convert `PciAddress` to a human-readable string format.
80///
81/// The display format will always include the domain component, even if it is zero.
82///
83/// # Example
84///
85/// ```
86/// use resources::PciAddress;
87///
88/// let pci_address = PciAddress::new(0x0000, 0x03, 0x14, 0x1)?;
89/// assert_eq!(pci_address.to_string(), "0000:03:14.1");
90/// # Ok::<(), resources::PciAddressError>(())
91/// ```
92impl Display for PciAddress {
93    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
94        let domain = 0;
95        write!(
96            f,
97            "{:04x}:{:02x}:{:02x}.{:0x}",
98            domain, self.bus, self.dev, self.func,
99        )
100    }
101}
102
103/// Construct `PciAddress` from string "\[domain:\]bus:device.function".
104/// Each component of the address is unprefixed hexadecimal.
105///
106/// # Example
107///
108/// ```
109/// use std::str::FromStr;
110/// use resources::PciAddress;
111///
112/// let pci_address = PciAddress::from_str("d7:15.4")?;
113/// assert_eq!(pci_address.bus, 0xd7);
114/// assert_eq!(pci_address.dev, 0x15);
115/// assert_eq!(pci_address.func, 0x4);
116/// # Ok::<(), resources::PciAddressError>(())
117/// ```
118impl FromStr for PciAddress {
119    type Err = Error;
120
121    fn from_str(address: &str) -> std::result::Result<Self, Self::Err> {
122        let (dev_bus_domain, func) = address.rsplit_once('.').ok_or(Error::MissingDelimiter(
123            PciAddressComponent::Device,
124            PciAddressComponent::Function,
125        ))?;
126        let func = u32::from_str_radix(func, 16)
127            .map_err(|_| Error::InvalidHex(PciAddressComponent::Function))?;
128
129        let (bus_domain, dev) = dev_bus_domain
130            .rsplit_once(':')
131            .ok_or(Error::MissingDelimiter(
132                PciAddressComponent::Bus,
133                PciAddressComponent::Device,
134            ))?;
135        let dev = u32::from_str_radix(dev, 16)
136            .map_err(|_| Error::InvalidHex(PciAddressComponent::Device))?;
137
138        // Domain is optional; if unspecified, the rest of the string is the bus, and domain
139        // defaults to 0.
140        let (domain, bus) = bus_domain.rsplit_once(':').unwrap_or(("0", bus_domain));
141        let bus = u32::from_str_radix(bus, 16)
142            .map_err(|_| Error::InvalidHex(PciAddressComponent::Bus))?;
143
144        if domain.contains(':') {
145            return Err(Error::TooManyComponents);
146        }
147
148        let domain = u32::from_str_radix(domain, 16)
149            .map_err(|_| Error::InvalidHex(PciAddressComponent::Domain))?;
150
151        Self::new(domain, bus, dev, func)
152    }
153}
154
155impl PciAddress {
156    #[doc(hidden)]
157    const BUS_MASK: u32 = 0x00ff;
158    #[doc(hidden)]
159    const DEVICE_BITS_NUM: usize = 5;
160    #[doc(hidden)]
161    const DEVICE_MASK: u32 = 0x1f;
162    #[doc(hidden)]
163    const FUNCTION_BITS_NUM: usize = 3;
164    #[doc(hidden)]
165    const FUNCTION_MASK: u32 = 0x07;
166    #[doc(hidden)]
167    const REGISTER_OFFSET: usize = 2;
168
169    /// Construct [`PciAddress`] from separate domain, bus, device, and function numbers.
170    ///
171    /// # Arguments
172    ///
173    /// * `domain` - The PCI domain number. Must be `0` in the current implementation.
174    /// * `bus` - The PCI bus number. Must be in the range `0..=255`.
175    /// * `dev` - The PCI device number. Must be in the range `0..=31`.
176    /// * `func` - The PCI function number. Must be in the range `0..=7`.
177    ///
178    /// # Errors
179    ///
180    /// If any component is out of the valid range, this function will return
181    /// [`Error::ComponentOutOfRange`].
182    pub fn new(domain: u32, bus: u32, dev: u32, func: u32) -> Result<Self> {
183        if bus > Self::BUS_MASK {
184            return Err(Error::ComponentOutOfRange(PciAddressComponent::Bus));
185        }
186
187        if dev > Self::DEVICE_MASK {
188            return Err(Error::ComponentOutOfRange(PciAddressComponent::Device));
189        }
190
191        if func > Self::FUNCTION_MASK {
192            return Err(Error::ComponentOutOfRange(PciAddressComponent::Function));
193        }
194
195        // PciAddress does not store domain for now, so disallow anything other than domain 0.
196        if domain > 0 {
197            return Err(Error::ComponentOutOfRange(PciAddressComponent::Domain));
198        }
199
200        Ok(PciAddress {
201            bus: bus as u8,
202            dev: dev as u8,
203            func: func as u8,
204        })
205    }
206
207    /// Decode a [`PciAddress`] and register index from a CONFIG_ADDRESS value.
208    ///
209    /// The configuration address should be in the format used with the PCI CAM or ECAM
210    /// configuration space access mechanisms, with the lowest bits encoding a register index and
211    /// the bits above that encoding the PCI function (3 bits), device (5 bits), and bus (8 bits).
212    /// The low two bits of the configuration address, which are technically part of the register
213    /// number, are ignored, since PCI configuration space accesses must be DWORD (4-byte) aligned.
214    ///
215    /// On success, returns a [`PciAddress`] and the extracted register index in DWORDs.
216    ///
217    /// # Arguments
218    ///
219    /// * `config_address` - The PCI configuration address.
220    /// * `register_bits_num` - The size of the register value in bits.
221    ///
222    /// # Example
223    ///
224    /// ```
225    /// use resources::PciAddress;
226    ///
227    /// let (pci_address, register_index) = PciAddress::from_config_address(0x32a354, 8);
228    /// assert_eq!(pci_address.bus, 0x32);
229    /// assert_eq!(pci_address.dev, 0x14);
230    /// assert_eq!(pci_address.func, 0x3);
231    /// assert_eq!(register_index, 0x15);
232    /// ```
233    pub fn from_config_address(config_address: u32, register_bits_num: usize) -> (Self, usize) {
234        let bus_offset = register_bits_num + Self::FUNCTION_BITS_NUM + Self::DEVICE_BITS_NUM;
235        let bus = ((config_address >> bus_offset) & Self::BUS_MASK) as u8;
236        let dev_offset = register_bits_num + Self::FUNCTION_BITS_NUM;
237        let dev = ((config_address >> dev_offset) & Self::DEVICE_MASK) as u8;
238        let func = ((config_address >> register_bits_num) & Self::FUNCTION_MASK) as u8;
239        let register_mask: u32 = (1_u32 << (register_bits_num - Self::REGISTER_OFFSET)) - 1;
240        let register = ((config_address >> Self::REGISTER_OFFSET) & register_mask) as usize;
241
242        (PciAddress { bus, dev, func }, register)
243    }
244
245    /// Construct [`PciAddress`] from a system PCI path
246    ///
247    /// # Arguments
248    ///
249    /// * `path` - The system PCI path. The file name of this path must be a valid PCI address.
250    ///
251    /// # Errors
252    ///
253    /// If the path given is invalid or filename is an invalid PCI address, this function will
254    /// return error.
255    pub fn from_path(path: &Path) -> Result<Self> {
256        let os_str = path.file_name().ok_or(Error::InvalidHostPath)?;
257        let pci_str = os_str.to_str().ok_or(Error::InvalidHostPath)?;
258        PciAddress::from_str(pci_str)
259    }
260
261    /// Encode [`PciAddress`] into CONFIG_ADDRESS value.
262    ///
263    /// See [`PciAddress::from_config_address()`] for details of the encoding.
264    ///
265    /// # Arguments
266    ///
267    /// * `register` - The register index in DWORDs.
268    /// * `register_bits_num` - The width of the register field, not including the two lowest bits.
269    ///
270    /// # Example
271    ///
272    /// ```
273    /// use resources::PciAddress;
274    ///
275    /// let pci_address = PciAddress::new(0x0000, 0x32, 0x14, 0x3)?;
276    /// let config_address = pci_address.to_config_address(0x15, 8);
277    /// assert_eq!(config_address, 0x32a354);
278    /// # Ok::<(), resources::PciAddressError>(())
279    /// ```
280    pub fn to_config_address(&self, register: usize, register_bits_num: usize) -> u32 {
281        let bus_offset = register_bits_num + Self::FUNCTION_BITS_NUM + Self::DEVICE_BITS_NUM;
282        let dev_offset = register_bits_num + Self::FUNCTION_BITS_NUM;
283        let register_mask: u32 = (1_u32 << (register_bits_num - Self::REGISTER_OFFSET)) - 1;
284        ((Self::BUS_MASK & self.bus as u32) << bus_offset)
285            | ((Self::DEVICE_MASK & self.dev as u32) << dev_offset)
286            | ((Self::FUNCTION_MASK & self.func as u32) << register_bits_num)
287            | ((register_mask & register as u32) << Self::REGISTER_OFFSET)
288    }
289
290    /// Convert B:D:F PCI address to unsigned 32 bit integer.
291    ///
292    /// The bus, device, and function numbers are packed into an integer as follows:
293    ///
294    /// | Bits 15-8 | Bits 7-3 | Bits 2-0 |
295    /// |-----------|----------|----------|
296    /// |    Bus    |  Device  | Function |
297    pub fn to_u32(&self) -> u32 {
298        ((Self::BUS_MASK & self.bus as u32) << (Self::FUNCTION_BITS_NUM + Self::DEVICE_BITS_NUM))
299            | ((Self::DEVICE_MASK & self.dev as u32) << Self::FUNCTION_BITS_NUM)
300            | (Self::FUNCTION_MASK & self.func as u32)
301    }
302
303    /// Convert D:F PCI address to a linux style devfn.
304    ///
305    /// The device and function numbers are packed into an u8 as follows:
306    ///
307    /// | Bits 7-3 | Bits 2-0 |
308    /// |----------|----------|
309    /// |  Device  | Function |
310    pub fn devfn(&self) -> u8 {
311        (self.dev << Self::FUNCTION_BITS_NUM) | self.func
312    }
313
314    /// Convert D:F PCI address to an ACPI _ADR.
315    ///
316    /// The device and function numbers are packed into an u32 as follows:
317    ///
318    /// | Bits 31-16 | Bits 15-0 |
319    /// |------------|-----------|
320    /// |   Device   | Function  |
321    pub fn acpi_adr(&self) -> u32 {
322        ((Self::DEVICE_MASK & self.dev as u32) << 16) | (Self::FUNCTION_MASK & self.func as u32)
323    }
324
325    /// Convert B:D:F PCI address to a PCI PME Requester ID.
326    ///
327    /// The output is identical to `to_u32()` except that only the lower 16 bits are needed
328    pub fn pme_requester_id(&self) -> u16 {
329        self.to_u32() as u16
330    }
331
332    /// Returns true if the address points to PCI root host-bridge.
333    ///
334    /// This is true if and only if this is the all-zero address (`00:0.0`).
335    pub fn is_root(&self) -> bool {
336        matches!(
337            &self,
338            PciAddress {
339                bus: 0,
340                dev: 0,
341                func: 0
342            }
343        )
344    }
345}
346
347#[cfg(test)]
348mod tests {
349    use super::*;
350
351    #[test]
352    fn from_string() {
353        assert_eq!(
354            PciAddress::from_str("0000:00:00.0").unwrap(),
355            PciAddress {
356                bus: 0,
357                dev: 0,
358                func: 0
359            }
360        );
361        assert_eq!(
362            PciAddress::from_str("00:00.0").unwrap(),
363            PciAddress {
364                bus: 0,
365                dev: 0,
366                func: 0
367            }
368        );
369        assert_eq!(
370            PciAddress::from_str("01:02.3").unwrap(),
371            PciAddress {
372                bus: 1,
373                dev: 2,
374                func: 3
375            }
376        );
377        assert_eq!(
378            PciAddress::from_str("ff:1f.7").unwrap(),
379            PciAddress {
380                bus: 0xff,
381                dev: 0x1f,
382                func: 7,
383            }
384        );
385    }
386
387    #[test]
388    fn from_string_missing_func_delim() {
389        assert_eq!(
390            PciAddress::from_str("1").expect_err("parse should fail"),
391            Error::MissingDelimiter(PciAddressComponent::Device, PciAddressComponent::Function)
392        );
393    }
394
395    #[test]
396    fn from_string_missing_dev_delim() {
397        assert_eq!(
398            PciAddress::from_str("2.1").expect_err("parse should fail"),
399            Error::MissingDelimiter(PciAddressComponent::Bus, PciAddressComponent::Device)
400        );
401    }
402
403    #[test]
404    fn from_string_extra_components() {
405        assert_eq!(
406            PciAddress::from_str("0:0:0:0.0").expect_err("parse should fail"),
407            Error::TooManyComponents
408        );
409    }
410
411    #[test]
412    fn from_string_invalid_func_hex() {
413        assert_eq!(
414            PciAddress::from_str("0000:00:00.g").expect_err("parse should fail"),
415            Error::InvalidHex(PciAddressComponent::Function)
416        );
417    }
418
419    #[test]
420    fn from_string_invalid_func_range() {
421        assert_eq!(
422            PciAddress::from_str("0000:00:00.8").expect_err("parse should fail"),
423            Error::ComponentOutOfRange(PciAddressComponent::Function)
424        );
425    }
426
427    #[test]
428    fn from_string_invalid_dev_hex() {
429        assert_eq!(
430            PciAddress::from_str("0000:00:gg.0").expect_err("parse should fail"),
431            Error::InvalidHex(PciAddressComponent::Device)
432        );
433    }
434
435    #[test]
436    fn from_string_invalid_dev_range() {
437        assert_eq!(
438            PciAddress::from_str("0000:00:20.0").expect_err("parse should fail"),
439            Error::ComponentOutOfRange(PciAddressComponent::Device)
440        );
441    }
442
443    #[test]
444    fn from_string_invalid_bus_hex() {
445        assert_eq!(
446            PciAddress::from_str("0000:gg:00.0").expect_err("parse should fail"),
447            Error::InvalidHex(PciAddressComponent::Bus)
448        );
449    }
450
451    #[test]
452    fn from_string_invalid_bus_range() {
453        assert_eq!(
454            PciAddress::from_str("0000:100:00.0").expect_err("parse should fail"),
455            Error::ComponentOutOfRange(PciAddressComponent::Bus)
456        );
457    }
458
459    #[test]
460    fn from_string_invalid_domain_hex() {
461        assert_eq!(
462            PciAddress::from_str("gggg:00:00.0").expect_err("parse should fail"),
463            Error::InvalidHex(PciAddressComponent::Domain)
464        );
465    }
466
467    #[test]
468    fn from_string_invalid_domain_range() {
469        assert_eq!(
470            PciAddress::from_str("0001:00:00.0").expect_err("parse should fail"),
471            Error::ComponentOutOfRange(PciAddressComponent::Domain)
472        );
473    }
474
475    #[test]
476    fn format_simple() {
477        assert_eq!(
478            PciAddress::new(0, 1, 2, 3).unwrap().to_string(),
479            "0000:01:02.3"
480        );
481    }
482
483    #[test]
484    fn format_max() {
485        assert_eq!(
486            PciAddress::new(0, 0xff, 0x1f, 7).unwrap().to_string(),
487            "0000:ff:1f.7"
488        );
489    }
490
491    #[test]
492    fn serialize_json() {
493        assert_eq!(
494            serde_json::to_string(&PciAddress::new(0, 0xa5, 0x1f, 3).unwrap()).unwrap(),
495            "\"0000:a5:1f.3\""
496        );
497    }
498
499    #[test]
500    fn deserialize_json() {
501        assert_eq!(
502            serde_json::from_str::<PciAddress>("\"0000:a5:1f.3\"").unwrap(),
503            PciAddress {
504                bus: 0xa5,
505                dev: 0x1f,
506                func: 3,
507            }
508        );
509    }
510}