blob: 966fd9f6abeb41a97934d66ddd873ca0ff2fe893 [file] [log] [blame]
// Copyright 2018 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::fmt;
use std::fmt::Display;
use std::path::Path;
use std::str::FromStr;
use remain::sorted;
use serde::Deserialize;
use serde::Deserializer;
use serde::Serialize;
use serde::Serializer;
use thiserror::Error as ThisError;
/// Identifies a single component of a [`PciAddress`].
#[derive(Debug, PartialEq, Eq)]
pub enum PciAddressComponent {
Domain,
Bus,
Device,
Function,
}
/// PCI address parsing and conversion errors.
#[derive(ThisError, Debug, PartialEq, Eq)]
#[sorted]
pub enum Error {
/// The specified component was outside the valid range.
#[error("{0:?} out of range")]
ComponentOutOfRange(PciAddressComponent),
/// The specified component could not be parsed as a hexadecimal number.
#[error("{0:?} failed to parse as hex")]
InvalidHex(PciAddressComponent),
/// The given PCI device path is invalid
#[error("Invalid PCI device path")]
InvalidHostPath,
/// A delimiter (`:` or `.`) between the two specified components was missing or incorrect.
#[error("Missing delimiter between {0:?} and {1:?}")]
MissingDelimiter(PciAddressComponent, PciAddressComponent),
/// The PCI address contained more than the expected number of components.
#[error("Too many components in PCI address")]
TooManyComponents,
}
pub type Result<T> = std::result::Result<T, Error>;
/// PCI Device Address, AKA Bus:Device.Function
#[derive(Default, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PciAddress {
/// Bus number, in the range `0..=255`.
pub bus: u8,
/// Device number, in the range `0..=31`.
pub dev: u8,
/// Function number, in the range `0..=7`.
pub func: u8,
}
impl Serialize for PciAddress {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl<'de> Deserialize<'de> for PciAddress {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
FromStr::from_str(&s).map_err(serde::de::Error::custom)
}
}
/// Convert `PciAddress` to a human-readable string format.
///
/// The display format will always include the domain component, even if it is zero.
///
/// # Example
///
/// ```
/// use devices::PciAddress;
///
/// let pci_address = PciAddress::new(0x0000, 0x03, 0x14, 0x1)?;
/// assert_eq!(pci_address.to_string(), "0000:03:14.1");
/// # Ok::<(), devices::PciAddressError>(())
/// ```
impl Display for PciAddress {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let domain = 0;
write!(
f,
"{:04x}:{:02x}:{:02x}.{:0x}",
domain, self.bus, self.dev, self.func,
)
}
}
/// Construct `PciAddress` from string "\[domain:\]bus:device.function".
/// Each component of the address is unprefixed hexadecimal.
///
/// # Example
///
/// ```
/// use std::str::FromStr;
/// use devices::PciAddress;
///
/// let pci_address = PciAddress::from_str("d7:15.4")?;
/// assert_eq!(pci_address.bus, 0xd7);
/// assert_eq!(pci_address.dev, 0x15);
/// assert_eq!(pci_address.func, 0x4);
/// # Ok::<(), devices::PciAddressError>(())
/// ```
impl FromStr for PciAddress {
type Err = Error;
fn from_str(address: &str) -> std::result::Result<Self, Self::Err> {
let (dev_bus_domain, func) = address.rsplit_once('.').ok_or(Error::MissingDelimiter(
PciAddressComponent::Device,
PciAddressComponent::Function,
))?;
let func = u32::from_str_radix(func, 16)
.map_err(|_| Error::InvalidHex(PciAddressComponent::Function))?;
let (bus_domain, dev) = dev_bus_domain
.rsplit_once(':')
.ok_or(Error::MissingDelimiter(
PciAddressComponent::Bus,
PciAddressComponent::Device,
))?;
let dev = u32::from_str_radix(dev, 16)
.map_err(|_| Error::InvalidHex(PciAddressComponent::Device))?;
// Domain is optional; if unspecified, the rest of the string is the bus, and domain
// defaults to 0.
let (domain, bus) = bus_domain.rsplit_once(':').unwrap_or(("0", bus_domain));
let bus = u32::from_str_radix(bus, 16)
.map_err(|_| Error::InvalidHex(PciAddressComponent::Bus))?;
if domain.contains(':') {
return Err(Error::TooManyComponents);
}
let domain = u32::from_str_radix(domain, 16)
.map_err(|_| Error::InvalidHex(PciAddressComponent::Domain))?;
Self::new(domain, bus, dev, func)
}
}
impl PciAddress {
#[doc(hidden)]
const BUS_MASK: u32 = 0x00ff;
#[doc(hidden)]
const DEVICE_BITS_NUM: usize = 5;
#[doc(hidden)]
const DEVICE_MASK: u32 = 0x1f;
#[doc(hidden)]
const FUNCTION_BITS_NUM: usize = 3;
#[doc(hidden)]
const FUNCTION_MASK: u32 = 0x07;
#[doc(hidden)]
const REGISTER_OFFSET: usize = 2;
/// Construct [`PciAddress`] from separate domain, bus, device, and function numbers.
///
/// # Arguments
///
/// * `domain` - The PCI domain number. Must be `0` in the current implementation.
/// * `bus` - The PCI bus number. Must be in the range `0..=255`.
/// * `dev` - The PCI device number. Must be in the range `0..=31`.
/// * `func` - The PCI function number. Must be in the range `0..=7`.
///
/// # Errors
///
/// If any component is out of the valid range, this function will return
/// [`Error::ComponentOutOfRange`].
pub fn new(domain: u32, bus: u32, dev: u32, func: u32) -> Result<Self> {
if bus > Self::BUS_MASK {
return Err(Error::ComponentOutOfRange(PciAddressComponent::Bus));
}
if dev > Self::DEVICE_MASK {
return Err(Error::ComponentOutOfRange(PciAddressComponent::Device));
}
if func > Self::FUNCTION_MASK {
return Err(Error::ComponentOutOfRange(PciAddressComponent::Function));
}
// PciAddress does not store domain for now, so disallow anything other than domain 0.
if domain > 0 {
return Err(Error::ComponentOutOfRange(PciAddressComponent::Domain));
}
Ok(PciAddress {
bus: bus as u8,
dev: dev as u8,
func: func as u8,
})
}
/// Decode a [`PciAddress`] and register index from a CONFIG_ADDRESS value.
///
/// The configuration address should be in the format used with the PCI CAM or ECAM
/// configuration space access mechanisms, with the lowest bits encoding a register index and
/// the bits above that encoding the PCI function (3 bits), device (5 bits), and bus (8 bits).
/// The low two bits of the configuration address, which are technically part of the register
/// number, are ignored, since PCI configuration space accesses must be DWORD (4-byte) aligned.
///
/// On success, returns a [`PciAddress`] and the extracted register index in DWORDs.
///
/// # Arguments
///
/// * `config_address` - The PCI configuration address.
/// * `register_bits_num` - The size of the register value in bits.
///
/// # Example
///
/// ```
/// use devices::PciAddress;
///
/// let (pci_address, register_index) = PciAddress::from_config_address(0x32a354, 8);
/// assert_eq!(pci_address.bus, 0x32);
/// assert_eq!(pci_address.dev, 0x14);
/// assert_eq!(pci_address.func, 0x3);
/// assert_eq!(register_index, 0x15);
/// ```
pub fn from_config_address(config_address: u32, register_bits_num: usize) -> (Self, usize) {
let bus_offset = register_bits_num + Self::FUNCTION_BITS_NUM + Self::DEVICE_BITS_NUM;
let bus = ((config_address >> bus_offset) & Self::BUS_MASK) as u8;
let dev_offset = register_bits_num + Self::FUNCTION_BITS_NUM;
let dev = ((config_address >> dev_offset) & Self::DEVICE_MASK) as u8;
let func = ((config_address >> register_bits_num) & Self::FUNCTION_MASK) as u8;
let register_mask: u32 = (1_u32 << (register_bits_num - Self::REGISTER_OFFSET)) - 1;
let register = ((config_address >> Self::REGISTER_OFFSET) & register_mask) as usize;
(PciAddress { bus, dev, func }, register)
}
/// Construct [`PciAddress`] from a system PCI path
///
/// # Arguments
///
/// * `path` - The system PCI path. The file name of this path must be a valid PCI address.
///
/// # Errors
///
/// If the path given is invalid or filename is an invalid PCI address, this function will
/// return error.
pub fn from_path(path: &Path) -> Result<Self> {
let os_str = path.file_name().ok_or(Error::InvalidHostPath)?;
let pci_str = os_str.to_str().ok_or(Error::InvalidHostPath)?;
PciAddress::from_str(pci_str)
}
/// Encode [`PciAddress`] into CONFIG_ADDRESS value.
///
/// See [`PciAddress::from_config_address()`] for details of the encoding.
///
/// # Arguments
///
/// * `register` - The register index in DWORDs.
/// * `register_bits_num` - The width of the register field, not including the two lowest bits.
///
/// # Example
///
/// ```
/// use devices::PciAddress;
///
/// let pci_address = PciAddress::new(0x0000, 0x32, 0x14, 0x3)?;
/// let config_address = pci_address.to_config_address(0x15, 8);
/// assert_eq!(config_address, 0x32a354);
/// # Ok::<(), devices::PciAddressError>(())
/// ```
pub fn to_config_address(&self, register: usize, register_bits_num: usize) -> u32 {
let bus_offset = register_bits_num + Self::FUNCTION_BITS_NUM + Self::DEVICE_BITS_NUM;
let dev_offset = register_bits_num + Self::FUNCTION_BITS_NUM;
let register_mask: u32 = (1_u32 << (register_bits_num - Self::REGISTER_OFFSET)) - 1;
((Self::BUS_MASK & self.bus as u32) << bus_offset)
| ((Self::DEVICE_MASK & self.dev as u32) << dev_offset)
| ((Self::FUNCTION_MASK & self.func as u32) << register_bits_num)
| ((register_mask & register as u32) << Self::REGISTER_OFFSET)
}
/// Convert B:D:F PCI address to unsigned 32 bit integer.
///
/// The bus, device, and function numbers are packed into an integer as follows:
///
/// | Bits 15-8 | Bits 7-3 | Bits 2-0 |
/// |-----------|----------|----------|
/// | Bus | Device | Function |
pub fn to_u32(&self) -> u32 {
((Self::BUS_MASK & self.bus as u32) << (Self::FUNCTION_BITS_NUM + Self::DEVICE_BITS_NUM))
| ((Self::DEVICE_MASK & self.dev as u32) << Self::FUNCTION_BITS_NUM)
| (Self::FUNCTION_MASK & self.func as u32)
}
/// Convert D:F PCI address to a linux style devfn.
///
/// The device and function numbers are packed into an u8 as follows:
///
/// | Bits 7-3 | Bits 2-0 |
/// |----------|----------|
/// | Device | Function |
pub fn devfn(&self) -> u8 {
(self.dev << Self::FUNCTION_BITS_NUM) | self.func
}
/// Convert D:F PCI address to an ACPI _ADR.
///
/// The device and function numbers are packed into an u32 as follows:
///
/// | Bits 31-16 | Bits 15-0 |
/// |------------|-----------|
/// | Device | Function |
pub fn acpi_adr(&self) -> u32 {
((Self::DEVICE_MASK & self.dev as u32) << 16) | (Self::FUNCTION_MASK & self.func as u32)
}
/// Convert B:D:F PCI address to a PCI PME Requester ID.
///
/// The output is identical to `to_u32()` except that only the lower 16 bits are needed
pub fn pme_requester_id(&self) -> u16 {
self.to_u32() as u16
}
/// Returns true if the address points to PCI root host-bridge.
///
/// This is true if and only if this is the all-zero address (`00:0.0`).
pub fn is_root(&self) -> bool {
matches!(
&self,
PciAddress {
bus: 0,
dev: 0,
func: 0
}
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_string() {
assert_eq!(
PciAddress::from_str("0000:00:00.0").unwrap(),
PciAddress {
bus: 0,
dev: 0,
func: 0
}
);
assert_eq!(
PciAddress::from_str("00:00.0").unwrap(),
PciAddress {
bus: 0,
dev: 0,
func: 0
}
);
assert_eq!(
PciAddress::from_str("01:02.3").unwrap(),
PciAddress {
bus: 1,
dev: 2,
func: 3
}
);
assert_eq!(
PciAddress::from_str("ff:1f.7").unwrap(),
PciAddress {
bus: 0xff,
dev: 0x1f,
func: 7,
}
);
}
#[test]
fn from_string_missing_func_delim() {
assert_eq!(
PciAddress::from_str("1").expect_err("parse should fail"),
Error::MissingDelimiter(PciAddressComponent::Device, PciAddressComponent::Function)
);
}
#[test]
fn from_string_missing_dev_delim() {
assert_eq!(
PciAddress::from_str("2.1").expect_err("parse should fail"),
Error::MissingDelimiter(PciAddressComponent::Bus, PciAddressComponent::Device)
);
}
#[test]
fn from_string_extra_components() {
assert_eq!(
PciAddress::from_str("0:0:0:0.0").expect_err("parse should fail"),
Error::TooManyComponents
);
}
#[test]
fn from_string_invalid_func_hex() {
assert_eq!(
PciAddress::from_str("0000:00:00.g").expect_err("parse should fail"),
Error::InvalidHex(PciAddressComponent::Function)
);
}
#[test]
fn from_string_invalid_func_range() {
assert_eq!(
PciAddress::from_str("0000:00:00.8").expect_err("parse should fail"),
Error::ComponentOutOfRange(PciAddressComponent::Function)
);
}
#[test]
fn from_string_invalid_dev_hex() {
assert_eq!(
PciAddress::from_str("0000:00:gg.0").expect_err("parse should fail"),
Error::InvalidHex(PciAddressComponent::Device)
);
}
#[test]
fn from_string_invalid_dev_range() {
assert_eq!(
PciAddress::from_str("0000:00:20.0").expect_err("parse should fail"),
Error::ComponentOutOfRange(PciAddressComponent::Device)
);
}
#[test]
fn from_string_invalid_bus_hex() {
assert_eq!(
PciAddress::from_str("0000:gg:00.0").expect_err("parse should fail"),
Error::InvalidHex(PciAddressComponent::Bus)
);
}
#[test]
fn from_string_invalid_bus_range() {
assert_eq!(
PciAddress::from_str("0000:100:00.0").expect_err("parse should fail"),
Error::ComponentOutOfRange(PciAddressComponent::Bus)
);
}
#[test]
fn from_string_invalid_domain_hex() {
assert_eq!(
PciAddress::from_str("gggg:00:00.0").expect_err("parse should fail"),
Error::InvalidHex(PciAddressComponent::Domain)
);
}
#[test]
fn from_string_invalid_domain_range() {
assert_eq!(
PciAddress::from_str("0001:00:00.0").expect_err("parse should fail"),
Error::ComponentOutOfRange(PciAddressComponent::Domain)
);
}
#[test]
fn format_simple() {
assert_eq!(
PciAddress::new(0, 1, 2, 3).unwrap().to_string(),
"0000:01:02.3"
);
}
#[test]
fn format_max() {
assert_eq!(
PciAddress::new(0, 0xff, 0x1f, 7).unwrap().to_string(),
"0000:ff:1f.7"
);
}
#[test]
fn serialize_json() {
assert_eq!(
serde_json::to_string(&PciAddress::new(0, 0xa5, 0x1f, 3).unwrap()).unwrap(),
"\"0000:a5:1f.3\""
);
}
#[test]
fn deserialize_json() {
assert_eq!(
serde_json::from_str::<PciAddress>("\"0000:a5:1f.3\"").unwrap(),
PciAddress {
bus: 0xa5,
dev: 0x1f,
func: 3,
}
);
}
}