From e50b05d93cb97ee982e93eab273c0a5ba458d712 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Fri, 1 Oct 2021 17:21:05 +1000 Subject: [PATCH] agent/pci: Add type to represent PCI addresses Add a new pci::Address type which represents a guest PCI address in DDDD:BB:SS.F form. fixes #2745 Signed-off-by: David Gibson --- src/agent/src/pci.rs | 86 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/src/agent/src/pci.rs b/src/agent/src/pci.rs index 3b2b7ef83b..25ad9a6a09 100644 --- a/src/agent/src/pci.rs +++ b/src/agent/src/pci.rs @@ -94,6 +94,53 @@ impl fmt::Display for SlotFn { } } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct Address { + domain: u16, + bus: u8, + slotfn: SlotFn, +} + +impl Address { + pub fn new(domain: u16, bus: u8, slotfn: SlotFn) -> Self { + Address { + domain, + bus, + slotfn, + } + } +} + +impl FromStr for Address { + type Err = anyhow::Error; + + fn from_str(s: &str) -> anyhow::Result { + let mut tokens = s.split(':').fuse(); + let domain = tokens.next(); + let bus = tokens.next(); + let slotfn = tokens.next(); + + if domain.is_none() || bus.is_none() || slotfn.is_none() || tokens.next().is_some() { + return Err(anyhow!( + "PCI address {} should have the format DDDD:BB:SS.F", + s + )); + } + + let domain = u16::from_str_radix(domain.unwrap(), 16)?; + let bus = u8::from_str_radix(bus.unwrap(), 16)?; + let slotfn = SlotFn::from_str(slotfn.unwrap())?; + + Ok(Address::new(domain, bus, slotfn)) + } +} + +impl fmt::Display for Address { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "{:04x}:{:02x}:{}", self.domain, self.bus, self.slotfn) + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub struct Path(Vec); @@ -202,6 +249,45 @@ mod tests { assert!(sf.is_err()); } + #[test] + fn test_address() { + // Valid addresses + let sf0_0 = SlotFn::new(0, 0).unwrap(); + let sf1f_7 = SlotFn::new(0x1f, 7).unwrap(); + + let addr = Address::new(0, 0, sf0_0); + assert_eq!(format!("{}", addr), "0000:00:00.0"); + let addr2 = Address::from_str("0000:00:00.0").unwrap(); + assert_eq!(addr, addr2); + + let addr = Address::new(0xffff, 0xff, sf1f_7); + assert_eq!(format!("{}", addr), "ffff:ff:1f.7"); + let addr2 = Address::from_str("ffff:ff:1f.7").unwrap(); + assert_eq!(addr, addr2); + + // Bad addresses + let addr = Address::from_str("10000:00:00.0"); + assert!(addr.is_err()); + + let addr = Address::from_str("0000:100:00.0"); + assert!(addr.is_err()); + + let addr = Address::from_str("0000:00:20.0"); + assert!(addr.is_err()); + + let addr = Address::from_str("0000:00:00.8"); + assert!(addr.is_err()); + + let addr = Address::from_str("xyz"); + assert!(addr.is_err()); + + let addr = Address::from_str("xyxy:xy:xy.z"); + assert!(addr.is_err()); + + let addr = Address::from_str("0000:00:00.0:00"); + assert!(addr.is_err()); + } + #[test] fn test_path() { let sf3_0 = SlotFn::new(0x03, 0).unwrap();