def parse_device(bus_node, device_path): device_name = os.path.basename(device_path) cfg = parse_config_space(device_path) # 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. if cfg is None: return None if device_name == "0000:00:00.0": device_node = bus_node else: m = bdf_regex.match(device_name) device, function = int(m.group(3), base=16), int(m.group(4), base=16) adr = hex((device << 16) + function) device_node = get_node(bus_node, f"./device[@address='{adr}']") if device_node is None: device_node = add_child(bus_node, "device", None, address=adr) for cap in cfg.caps: # If the device is not in D0, power it on and reparse its configuration space. if cap.name == "Power Management" and cap.power_state != 0: logging.info(f"Try resuming {device_path}") try: with open(os.path.join(device_path, "power", "control"), "w") as f: f.write("on") cfg = parse_config_space(device_path) except Exception as e: 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) class_code = "0x{:06x}".format(cfg.header.class_code) if device_node.get("id") is None: device_node.set("id", device_id) add_child(device_node, "vendor", vendor_id) add_child(device_node, "identifier", device_id) add_child(device_node, "class", class_code) if cfg.header.header_type == 0: subvendor_id = "0x{:04x}".format(cfg.header.subsystem_vendor_id) subdevice_id = "0x{:04x}".format(cfg.header.subsystem_device_id) add_child(device_node, "subsystem_vendor", subvendor_id) 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." ) 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: idx += 1 # 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) 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) # Interrupt pin pin = cfg.header.interrupt_pin if pin > 0 and pin <= 4: pin_name = interrupt_pin_names[pin] res_node = add_child(device_node, "resource", type="interrupt_pin", pin=pin_name) prt_address = hex(int(device_node.get("address"), 16) | 0xffff) mapping = device_node.xpath( f"../interrupt_pin_routing/routing[@address='{prt_address}']/mapping[@pin='{pin_name}']" ) if len(mapping) > 0: res_node.set("source", mapping[0].get("source")) if "index" in mapping[0].keys(): res_node.set("index", mapping[0].get("index")) # Secondary bus if cfg.header.header_type == 1: # According to section 3.2.5.6, PCI to PCI Bridge Architecture Specification, the I/O Limit register contains a # value smaller than the I/O Base register if there are no I/O addresses on the secondary side. io_base = (cfg.header.io_base_upper_16_bits << 16) | ( (cfg.header.io_base >> 4) << 12) io_end = (cfg.header.io_limit_upper_16_bits << 16) | ( (cfg.header.io_limit >> 4) << 12) | 0xfff if io_base <= io_end: add_child(device_node, "resource", type="io_port", min=hex(io_base), max=hex(io_end), len=hex(io_end - io_base + 1)) # According to section 3.2.5.8, PCI to PCI Bridge Architecture Specification, the Memory Limit register contains # a value smaller than the Memory Base register if there are no memory-mapped I/O addresses on the secondary # side. if cfg.header.memory_base <= cfg.header.memory_limit: memory_base = (cfg.header.memory_base >> 4) << 20 memory_end = ((cfg.header.memory_limit >> 4) << 20) | 0xfffff add_child(device_node, "resource", type="memory", min=hex(memory_base), max=hex(memory_end), len=hex(memory_end - memory_base + 1)) secondary_bus_node = add_child(device_node, "bus", type="pci", address=hex( cfg.header.secondary_bus_number)) # If a PCI routing table is provided for the root port / switch, move the routing table down to the bus node, in # order to align the relative position of devices and routing tables. prt = device_node.find("interrupt_pin_routing") if prt is not None: device_node.remove(prt) secondary_bus_node.append(prt) return secondary_bus_node return device_node
def parse_device(bus_node, device_path): device_name = os.path.basename(device_path) if device_name == "0000:00:00.0": device_node = bus_node else: m = bdf_regex.match(device_name) device, function = int(m.group(3), base=16), int(m.group(4), base=16) adr = hex((device << 16) + function) device_node = get_node(bus_node, f"./device[@address='{adr}']") if device_node is None: device_node = add_child(bus_node, "device", None, address=adr) cfg = parse_config_space(device_path) # Device identifiers vendor_id = "0x{:04x}".format(cfg.header.vendor_id) device_id = "0x{:04x}".format(cfg.header.device_id) class_code = "0x{:06x}".format(cfg.header.class_code) if device_node.get("id") is None: device_node.set("id", device_id) add_child(device_node, "vendor", vendor_id) add_child(device_node, "identifier", device_id) add_child(device_node, "class", class_code) if cfg.header.header_type == 0: subvendor_id = "0x{:04x}".format(cfg.header.subsystem_vendor_id) subdevice_id = "0x{:04x}".format(cfg.header.subsystem_device_id) add_child(device_node, "subsystem_vendor", subvendor_id) 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): 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.error(f"Cannot detect the size of BAR {idx}") if isinstance(bar, MemoryBar64): idx += 2 else: idx += 1 # 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) 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) # Secondary bus if cfg.header.header_type == 1: # According to section 3.2.5.6, PCI to PCI Bridge Architecture Specification, the I/O Limit register contains a # value smaller than the I/O Base register if there are no I/O addresses on the secondary side. io_base = (cfg.header.io_base_upper_16_bits << 16) | ( (cfg.header.io_base >> 4) << 12) io_end = (cfg.header.io_limit_upper_16_bits << 16) | ( (cfg.header.io_limit >> 4) << 12) | 0xfff if io_base <= io_end: add_child(device_node, "resource", type="io_port", min=hex(io_base), max=hex(io_end), len=hex(io_end - io_base + 1)) # According to section 3.2.5.8, PCI to PCI Bridge Architecture Specification, the Memory Limit register contains # a value smaller than the Memory Base register if there are no memory-mapped I/O addresses on the secondary # side. if cfg.header.memory_base <= cfg.header.memory_limit: memory_base = (cfg.header.memory_base >> 4) << 20 memory_end = ((cfg.header.memory_limit >> 4) << 20) | 0xfffff add_child(device_node, "resource", type="memory", min=hex(memory_base), max=hex(memory_end), len=hex(memory_end - memory_base + 1)) secondary_bus_node = add_child(device_node, "bus", type="pci", address=hex( cfg.header.secondary_bus_number)) return secondary_bus_node return device_node