blob: 4d3dade2eacd016e15bcc9e2fd648cdf47722f6e [file] [log] [blame]
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::fmt::{self, Display};
use std::str::FromStr;
use remain::sorted;
use serde::{Deserialize, Serialize};
use thiserror::Error as ThisError;
#[derive(Debug, PartialEq)]
pub enum PciAddressComponent {
Domain,
Bus,
Device,
Function,
}
#[derive(ThisError, Debug, PartialEq)]
#[sorted]
pub enum Error {
#[error("{0:?} out of range")]
ComponentOutOfRange(PciAddressComponent),
#[error("{0:?} failed to parse as hex")]
InvalidHex(PciAddressComponent),
#[error("Missing delimiter between {0:?} and {1:?}")]
MissingDelimiter(PciAddressComponent, PciAddressComponent),
#[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(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct PciAddress {
pub bus: u8,
pub dev: u8, /* u5 */
pub func: u8, /* u3 */
}
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".
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 {
const BUS_MASK: u32 = 0x00ff;
const DEVICE_BITS_NUM: usize = 5;
const DEVICE_MASK: u32 = 0x1f;
const FUNCTION_BITS_NUM: usize = 3;
const FUNCTION_MASK: u32 = 0x07;
const REGISTER_OFFSET: usize = 2;
/// Construct PciAddress from separate domain, bus, device, and function numbers.
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,
})
}
/// Construct PciAddress and register tuple from CONFIG_ADDRESS value.
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)
}
/// Encode PciAddress into CONFIG_ADDRESS value.
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
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)
}
/// Returns true if the address points to PCI root host-bridge.
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"
);
}
}