mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-08-08 03:35:14 +00:00
board_inspector: extract PF and VF info
This patch adds logic to the extractors to fetch the following information. 1. All the details of an SR-IOV capability, which are reported in the SR-IOV extended capability structure. 2. Correctly report the vendor ID, device ID and BAR addresses of VFs. 3. Refer each VF back to the corresponding PF. Use XPATH to search for all the VFs enabled by a PF. Tracked-On: #7301 Signed-off-by: Junjie Mao <junjie.mao@intel.com>
This commit is contained in:
parent
93ccc0f473
commit
46fab9e8a0
@ -31,7 +31,7 @@ def collect_hostbridge_resources(bus_node, bus_number):
|
||||
begin, end = tuple(map(lambda x: int(f"0x{x}", base=16), fields[0].split("-")))
|
||||
add_child(bus_node, "resource", type="memory", min=hex(begin), max=hex(end), len=hex(end - begin + 1))
|
||||
|
||||
def parse_msi(cap_node, cap_struct):
|
||||
def parse_msi(cap_node, cap_struct, **kwargs):
|
||||
add_child(cap_node, "count", str(1 << cap_struct.multiple_message_capable))
|
||||
if cap_struct.multiple_message_capable > 0:
|
||||
add_child(cap_node, "capability", id="multiple-message")
|
||||
@ -42,21 +42,61 @@ def parse_msi(cap_node, cap_struct):
|
||||
if cap_struct.per_vector_masking_capable:
|
||||
add_child(cap_node, "capability", id="per-vector masking")
|
||||
|
||||
def parse_msix(cap_node, cap_struct):
|
||||
def parse_msix(cap_node, cap_struct, **kwargs):
|
||||
add_child(cap_node, "table_size", str(cap_struct.table_size))
|
||||
add_child(cap_node, "table_bir", str(cap_struct.table_bir))
|
||||
add_child(cap_node, "table_offset", hex(cap_struct.table_offset_z))
|
||||
add_child(cap_node, "pba_bir", str(cap_struct.pba_bir))
|
||||
add_child(cap_node, "pba_offset", hex(cap_struct.pba_offset_z))
|
||||
|
||||
def parse_sriov(cap_node, cap_struct, **kwargs):
|
||||
device_node = cap_node.getparent()
|
||||
bus_node = device_node.getparent()
|
||||
assert bus_node.tag == "bus" and device_node.tag == "device"
|
||||
|
||||
pf_bus_id = int(bus_node.get("address"), 16)
|
||||
pf_dev_id = int(device_node.get("address"), 16) >> 16
|
||||
pf_func_id = int(device_node.get("address"), 16) & 0xffff
|
||||
pf_bdf = (pf_bus_id << 8) | (pf_dev_id << 3) | pf_func_id
|
||||
|
||||
if cap_struct.vf_enable == 0 or cap_struct.num_vfs == 0:
|
||||
logging.warning(f"The PCI device at {pf_bus_id:02x}:{pf_dev_id:02x}.{pf_func_id} has SR-IOV capability which is currently disabled.")
|
||||
logging.warning(f"If that device is intended to provide virtual functions in your scenario, write (as root) the number of VFs to {kwargs['device_path']}/sriov_numvfs and rerun the board inspector.")
|
||||
|
||||
if cap_struct.vf_migration_capable:
|
||||
vf_migration_cap = add_child(cap_node, "capability", id="vf_migration")
|
||||
add_child(vf_migration_cap, "vf_migration_interrupt_message_number", str(cap_struct.vf_migration_interrupt_message_number))
|
||||
|
||||
if cap_struct.ari_capable_hierarchy_preserved:
|
||||
add_child(cap_node, "capability", id="ari_capable_hierarchy_preserved")
|
||||
|
||||
if cap_struct.vf_10_bit_tag_requester_supported:
|
||||
add_child(cap_node, "capability", id="vf_10_bit_tag_requester_supported")
|
||||
|
||||
add_child(cap_node, "initial_vfs", str(cap_struct.initial_vfs))
|
||||
add_child(cap_node, "total_vfs", str(cap_struct.total_vfs))
|
||||
add_child(cap_node, "function_dependency_link", str(cap_struct.function_dependency_link))
|
||||
|
||||
first_vf_bdf_node = add_child(cap_node, "first_vf_bdf")
|
||||
first_vf_bdf = pf_bdf + cap_struct.first_vf_offset
|
||||
add_child(first_vf_bdf_node, "bus", hex(first_vf_bdf >> 8))
|
||||
add_child(first_vf_bdf_node, "device", hex((first_vf_bdf >> 3) & 0x1f))
|
||||
add_child(first_vf_bdf_node, "function", hex(first_vf_bdf & 0x7))
|
||||
|
||||
add_child(cap_node, "vf_stride", str(cap_struct.vf_stride))
|
||||
add_child(cap_node, "vf_device_id", hex(cap_struct.vf_device_id))
|
||||
add_child(cap_node, "supported_page_sizes", hex(cap_struct.supported_page_sizes))
|
||||
|
||||
cap_parsers = {
|
||||
"MSI": parse_msi,
|
||||
"MSI-X": parse_msix,
|
||||
"SR-IOV": parse_sriov,
|
||||
}
|
||||
|
||||
def parse_device(bus_node, device_path):
|
||||
device_name = os.path.basename(device_path)
|
||||
cfg = parse_config_space(device_path)
|
||||
physfn_cfg = parse_config_space(os.path.join(device_path, "physfn"))
|
||||
|
||||
# There are cases where Linux creates device-like nodes without a file named "config", e.g. when there is a PCIe
|
||||
# non-transparent bridge (NTB) on the physical platform.
|
||||
@ -85,8 +125,8 @@ def parse_device(bus_node, device_path):
|
||||
logging.info(f"Resuming {device_path} failed: {str(e)}")
|
||||
|
||||
# Device identifiers
|
||||
vendor_id = "0x{:04x}".format(cfg.header.vendor_id)
|
||||
device_id = "0x{:04x}".format(cfg.header.device_id)
|
||||
vendor_id = "0x{:04x}".format(cfg.header.vendor_id if physfn_cfg is None else physfn_cfg.header.vendor_id)
|
||||
device_id = "0x{:04x}".format(cfg.header.device_id if physfn_cfg is None else physfn_cfg.get_cap("SR-IOV").vf_device_id)
|
||||
class_code = "0x{:06x}".format(cfg.header.class_code)
|
||||
if device_node.get("id") is None:
|
||||
device_node.set("id", device_id)
|
||||
@ -100,43 +140,85 @@ def parse_device(bus_node, device_path):
|
||||
add_child(device_node, "subsystem_identifier", subdevice_id)
|
||||
|
||||
# BARs
|
||||
idx = 0
|
||||
for bar in cfg.header.bars:
|
||||
resource_path = os.path.join(device_path, f"resource{idx}")
|
||||
resource_type = bar.resource_type
|
||||
base = bar.base
|
||||
if os.path.exists(resource_path):
|
||||
if bar.base == 0:
|
||||
logging.debug(f"PCI {device_name}: BAR {idx} exists but is programmed with all 0. This device cannot be passed through to any VM.")
|
||||
if physfn_cfg is None:
|
||||
idx = 0
|
||||
for bar in cfg.header.bars:
|
||||
resource_path = os.path.join(device_path, f"resource{idx}")
|
||||
resource_type = bar.resource_type
|
||||
base = bar.base
|
||||
if os.path.exists(resource_path):
|
||||
if bar.base == 0:
|
||||
logging.debug(f"PCI {device_name}: BAR {idx} exists but is programmed with all 0. This device cannot be passed through to any VM.")
|
||||
else:
|
||||
resource_node = get_node(device_node, f"./resource[@type = '{resource_type}' and @min = '{hex(base)}']")
|
||||
if resource_node is None:
|
||||
size = os.path.getsize(resource_path)
|
||||
resource_node = add_child(device_node, "resource", None, type=resource_type, min=hex(base), max=hex(base + size - 1), len=hex(size))
|
||||
resource_node.set("id", f"bar{idx}")
|
||||
if isinstance(bar, MemoryBar32):
|
||||
resource_node.set("width", "32")
|
||||
resource_node.set("prefetchable", str(bar.prefetchable))
|
||||
elif isinstance(bar, MemoryBar64):
|
||||
resource_node.set("width", "64")
|
||||
resource_node.set("prefetchable", str(bar.prefetchable))
|
||||
elif bar.base != 0:
|
||||
logging.debug(f"PCI {device_name}: Cannot detect the size of BAR {idx}")
|
||||
if isinstance(bar, MemoryBar64):
|
||||
idx += 2
|
||||
else:
|
||||
resource_node = get_node(device_node, f"./resource[@type = '{resource_type}' and @min = '{hex(base)}']")
|
||||
if resource_node is None:
|
||||
idx += 1
|
||||
else:
|
||||
pf_path = os.path.realpath(os.path.join(device_path, "physfn"))
|
||||
pf_name = os.path.basename(pf_path)
|
||||
m = bdf_regex.match(pf_name)
|
||||
pf_bus, pf_device, pf_function = int(m.group(2), base=16), int(m.group(3), base=16), int(m.group(4), base=16)
|
||||
pf_bdf = (pf_bus << 8) | (pf_device << 3) | pf_function
|
||||
|
||||
m = bdf_regex.match(device_name)
|
||||
vf_bus, vf_device, vf_function = int(m.group(2), base=16), int(m.group(3), base=16), int(m.group(4), base=16)
|
||||
vf_bdf = (vf_bus << 8) | (vf_device << 3) | vf_function
|
||||
first_vf_offset = physfn_cfg.get_cap("SR-IOV").first_vf_offset
|
||||
vf_stride = physfn_cfg.get_cap("SR-IOV").vf_stride
|
||||
|
||||
vf_id = (vf_bdf - (pf_bdf + first_vf_offset)) // vf_stride
|
||||
|
||||
idx = 0
|
||||
for bar in physfn_cfg.get_cap("SR-IOV").vf_bars:
|
||||
resource_path = os.path.join(device_path, f"resource{idx}")
|
||||
resource_type = bar.resource_type
|
||||
if os.path.exists(resource_path):
|
||||
if bar.base == 0:
|
||||
logging.debug(f"PCI {device_name}: BAR {idx} exists but is programmed with all 0. This device cannot be passed through to any VM.")
|
||||
else:
|
||||
size = os.path.getsize(resource_path)
|
||||
base = bar.base + vf_id * size
|
||||
resource_node = add_child(device_node, "resource", None, type=resource_type, min=hex(base), max=hex(base + size - 1), len=hex(size))
|
||||
resource_node.set("id", f"bar{idx}")
|
||||
if isinstance(bar, MemoryBar32):
|
||||
resource_node.set("width", "32")
|
||||
resource_node.set("prefetchable", str(bar.prefetchable))
|
||||
elif isinstance(bar, MemoryBar64):
|
||||
resource_node.set("width", "64")
|
||||
resource_node.set("prefetchable", str(bar.prefetchable))
|
||||
elif bar.base != 0:
|
||||
logging.debug(f"PCI {device_name}: Cannot detect the size of BAR {idx}")
|
||||
if isinstance(bar, MemoryBar64):
|
||||
idx += 2
|
||||
else:
|
||||
idx += 1
|
||||
resource_node.set("id", f"bar{idx}")
|
||||
if isinstance(bar, MemoryBar32):
|
||||
resource_node.set("width", "32")
|
||||
resource_node.set("prefetchable", str(bar.prefetchable))
|
||||
elif isinstance(bar, MemoryBar64):
|
||||
resource_node.set("width", "64")
|
||||
resource_node.set("prefetchable", str(bar.prefetchable))
|
||||
elif bar.base != 0:
|
||||
logging.debug(f"PCI {device_name}: Cannot detect the size of BAR {idx}")
|
||||
if isinstance(bar, MemoryBar64):
|
||||
idx += 2
|
||||
else:
|
||||
idx += 1
|
||||
|
||||
add_child(device_node, "physfn", bus=hex(pf_bus), address=hex((pf_device << 16) | pf_function))
|
||||
|
||||
# Capabilities
|
||||
for cap in cfg.caps:
|
||||
cap_node = add_child(device_node, "capability", id=cap.name)
|
||||
if cap.name in cap_parsers:
|
||||
cap_parsers[cap.name](cap_node, cap)
|
||||
cap_parsers[cap.name](cap_node, cap, device_path=device_path)
|
||||
|
||||
for cap in cfg.extcaps:
|
||||
cap_node = add_child(device_node, "capability", id=cap.name)
|
||||
if cap.name in cap_parsers:
|
||||
cap_parsers[cap.name](cap_node, cap)
|
||||
cap_parsers[cap.name](cap_node, cap, device_path=device_path)
|
||||
|
||||
# Interrupt pin
|
||||
pin = cfg.header.interrupt_pin
|
||||
|
@ -28,7 +28,7 @@ def getkey(child):
|
||||
|
||||
tags = ["vendor", "identifier", "subsystem_vendor", "subsystem_identifier", "class",
|
||||
"acpi_object", "compatible_id", "acpi_uid", "aml_template", "status",
|
||||
"resource", "capability", "interrupt_pin_routing", "dependency", "bus", "device"]
|
||||
"resource", "capability", "interrupt_pin_routing", "dependency", "bus", "device", "physfn"]
|
||||
|
||||
if child.tag == "resource":
|
||||
return (tags.index(child.tag), child.get("type"), resource_subkey(child))
|
||||
|
Loading…
Reference in New Issue
Block a user