agent: fix OVMF nested PCI path resolution

Treat three-segment PCI paths as pxb-only when the bus indicates pxb,
preventing wrong root-bus lookup for nested OVMF hot-plug devices.

Signed-off-by: Fabiano Fidêncio <ffidencio@nvidia.com>
Assisted-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
Fabiano Fidêncio
2026-05-26 14:47:17 +02:00
parent c7e91684b8
commit e588764e31

View File

@@ -26,15 +26,29 @@ pub fn create_pci_root_bus_path(root_complex: &str) -> String {
format!("/devices/pci0000:{root_complex}")
}
/// Minimum guest PCI bus number used by pxb-pcie NUMA expander bridges in the
/// runtime (see `busNrSpacing` in createNUMAPCIeTopology).
const PXB_PCIE_ROOT_BUS_MIN: u8 = 0x20;
fn is_pxb_pcie_root_complex(segment: &str) -> bool {
u8::from_str_radix(segment, 16)
.map(|bus| bus >= PXB_PCIE_ROOT_BUS_MIN)
.unwrap_or(false)
}
// Parses a device tree path into a (root_complex, PCI path) pair.
//
// Supports two formats:
// - Full NUMA path: "root_complex/bus/device" (e.g. "10/00/02") where the
// first segment is the root complex and the rest form the PCI path.
// - Legacy path: "bus/device" (e.g. "00/02") which defaults to root complex "00".
// Supports three formats:
// - NUMA pxb-pcie path: "root_complex/bus/device" (e.g. "20/00/02") where
// the first segment is a pxb-pcie guest bus number (>= 0x20) and the rest
// form the PCI path under that root complex.
// - Nested hot-plug path on pci0000:00: "parent/bridge/device" (e.g.
// "02/00/01") used by OVMF Q35 networking; stays on root complex "00".
// - Legacy path: "bus/device" (e.g. "00/02") which defaults to root complex
// "00".
pub fn pcipath_from_dev_tree_path(dev_tree_path: &str) -> Result<(&str, pci::Path)> {
let segments: Vec<&str> = dev_tree_path.split('/').collect();
if segments.len() >= 3 {
if segments.len() >= 3 && is_pxb_pcie_root_complex(segments[0]) {
let root_complex = segments[0];
let pci_part = &dev_tree_path[root_complex.len() + 1..];
let pci_path = pci::Path::from_str(pci_part).with_context(|| {
@@ -141,3 +155,36 @@ pub const U_EVENT_SUB_SYSTEM: &str = "SUBSYSTEM";
pub const U_EVENT_SEQ_NUM: &str = "SEQNUM";
pub const U_EVENT_DEV_NAME: &str = "DEVNAME";
pub const U_EVENT_INTERFACE: &str = "INTERFACE";
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pcipath_from_dev_tree_path_legacy() {
let (root, path) = pcipath_from_dev_tree_path("02/01").unwrap();
assert_eq!(root, "00");
assert_eq!(path.len(), 2);
assert_eq!(path[0].slot(), 0x02);
assert_eq!(path[1].slot(), 0x01);
}
#[test]
fn test_pcipath_from_dev_tree_path_nested_ovmf() {
let (root, path) = pcipath_from_dev_tree_path("02/00/01").unwrap();
assert_eq!(root, "00");
assert_eq!(path.len(), 3);
assert_eq!(path[0].slot(), 0x02);
assert_eq!(path[1].slot(), 0x00);
assert_eq!(path[2].slot(), 0x01);
}
#[test]
fn test_pcipath_from_dev_tree_path_numa_pxb() {
let (root, path) = pcipath_from_dev_tree_path("20/00/02").unwrap();
assert_eq!(root, "20");
assert_eq!(path.len(), 2);
assert_eq!(path[0].slot(), 0x00);
assert_eq!(path[1].slot(), 0x02);
}
}