lspci -vt Style PCI Topology Viewer with EfiPy2 (PciTree.py)程式碼解析:如何使用 EfiPy2 編寫 PciTree.py 繪製 PCIe 樹狀拓樸
PciTree.py is a UEFI Shell Python script built on EfiPy2 that enumerates the PCI/PCIe hierarchy via ECAM (Enhanced Configuration Access Mechanism) MMIO and displays it in an enhanced Linux lspci -vt tree format—complete with Segment/Bus ranges (-[Segment:StartBus-EndBus]-), full B:D.F device addresses, Bridge secondary-to-subordinate bus ranges (-[Sec-Sub]--), and human-readable Vendor/Device names from pciids.
PciTree.py 是一支運行於 UEFI Shell 環境下的 EfiPy2 腳本。它透過 ACPI MCFG 表與 ECAM (Enhanced Configuration Access Mechanism) 記憶體映射機制,直接存取 PCIe 配置空間(Configuration Space),並將系統上所有的 Host Bridge、PCI-to-PCI Bridge(Root Port / Switch)與末端裝置(Endpoint),以類似 Linux lspci -vt 的樹狀結構搭配 pciids 可讀裝置名稱完整呈現出來。
Below, we walk through the implementation of PciTree.py function by function, explaining both the underlying PCIe/ACPI specifications and how EfiPy2 simplifies low-level hardware access in Python.
以下我們依照 PciTree.py 的程式結構,一步步解析每個函式的底層 PCIe/ACPI 規範原理與 EfiPy2 實作細節。
1. Execution Flow Overview (整體執行流程)
The script's workflow is divided into four core stages:
整支程式的運作邏輯分為四個核心階段:
- Parse the ACPI
MCFGTable (__main__): Retrieve the ECAM MMIO base address (BaseAddress), the PCI Segment Group number (PciSegmentGroupNumber), and the decoded bus range (StartBusNumbertoEndBusNumber).
解析 ACPIMCFG表(__main__):取得 ECAM MMIO 基址(BaseAddress)、PciSegmentGroupNumber以及StartBusNumber~EndBusNumber。 - Dynamically Overlay PCI Headers (
BuildPciHeader): Inspect the common header to determine whether a function is an Endpoint (Type 00) or a Bridge (Type 01), then overlay the corresponding EDK2Structuredirectly onto the ECAM MMIO address.
動態映射 PCI Header(BuildPciHeader):根據裝置是 Endpoint(Type 00)還是 Bridge(Type 01),動態建立對應的 EDK2Structure並直接映射(Overlay)到 ECAM 記憶體位址。 - Recursively Collect Bus Topology (
CollectBusDevices&GetDeviceDesc): Scan all Devices and Functions on a given Bus, resolve human-readable names viapciids, and recursively followSecondaryBuswhenever a PCI-to-PCI Bridge is encountered.
遞迴收集 Bus 拓樸(CollectBusDevices&GetDeviceDesc):掃描指定 Bus 上的所有 Device/Function,查詢pciids名稱;若遇到 PCI-to-PCI Bridge,則順著SecondaryBus遞迴向下掃描。 - Render the ASCII Tree (
PrintBusTree): Calculate indentation, branch connectors (+-,\-,|), and bus range tags to print a clean hierarchical tree.
格式化輸出樹狀圖(PrintBusTree):根據收集到的裝置樹,自動計算縮排、連接線(+-、\-、|)與匯流排範圍標籤並印出結果。
2. Entry Point: Parsing the ACPI MCFG Table (__main__)
程式進入點:解析 ACPI MCFG 取得 ECAM 資訊
if __name__ == '__main__':
AcpiRaw = ExtractTable (b'MCFG', 0)
AcpiObj, _ = AcpiMcfgParser (AcpiRaw)
PciCfgBase = AcpiObj.McfgDesc.BaseAddress
PciSegment = AcpiObj.McfgDesc.PciSegmentGroupNumber
StartBus = AcpiObj.McfgDesc.StartBusNumber
EndBus = AcpiObj.McfgDesc.EndBusNumber
ScanBus (StartBus, EndBus, PciSegment)
In modern PCIe systems, the 4 KB configuration space of every PCI function is linearly mapped into physical memory (MMIO) via ECAM. To discover where this MMIO region resides, firmware and operating systems consult the ACPI MCFG (Memory Mapped Configuration Space Access) table:
在現代 PCIe 系統中,每個 PCI Function 的 4 KB 配置空間都被線性映射到一段連續的實體記憶體(MMIO)中,稱為 ECAM。為了得知這段記憶體的實體位址,韌體與作業系統會查詢 ACPI 的 MCFG (Memory Mapped Configuration Space Access) 表:
ExtractTable(b'MCFG', 0):
Extracts the raw binary payload of the firstMCFGtable from the UEFI System Table's ACPI Configuration Tables.
從 UEFI System Table 的 ACPI Configuration Table 中取出原始的MCFG二進位資料。AcpiMcfgParser(AcpiRaw):
Casts the raw bytes into EfiPy2'sEFIPY_ACPI_MEMORY_MAPPED_CONFIGURATION_SPACE_ACCESS_DESCRIPTION_TABLEstructure.
將二進位資料轉型為 EfiPy2 定義的EFIPY_ACPI_MEMORY_MAPPED_CONFIGURATION_SPACE_ACCESS_DESCRIPTION_TABLE結構體。AcpiObj.McfgDesc:
Corresponds toEFI_ACPI_MEMORY_MAPPED_ENHANCED_CONFIGURATION_SPACE_BASE_ADDRESS_ALLOCATION_STRUCTUREdefined in the PCI Firmware Specification, providing four essential fields:
對應 PCI Firmware Specification 中的EFI_ACPI_MEMORY_MAPPED_ENHANCED_CONFIGURATION_SPACE_BASE_ADDRESS_ALLOCATION_STRUCTURE,直接提供四個關鍵欄位:BaseAddress: The physical starting address of the ECAM MMIO region (stored in the globalPciCfgBase).
ECAM MMIO 的起始實體位址(存入全域變數PciCfgBase)。PciSegmentGroupNumber: The 16-bit PCI Segment / Domain number (e.g.,0x0000).
16-bit 的 PCI Segment / Domain 編號(例如0x0000)。StartBusNumber/EndBusNumber: The starting and ending Bus numbers decoded by this ECAM allocation (e.g.,0x00to0xFF).
此 Segment 管理的起始與結束 Bus 編號(例如0x00~0xFF)。
3. Mapping ECAM Memory to EDK2 Structures (BuildPciHeader)
動態建立與映射 PCI Configuration Header
def BuildPciHeader (EcamBase: int, EcamOffset: int) -> None:
PciCfgAddress = EcamBase + EcamOffset
PciCfg = pci.PCI_TYPE00.from_address (PciCfgAddress)
PciTypeFields = [
('Hdr', pci.PCI_DEVICE_INDEPENDENT_REGION),
]
#
# Build TYPE 00/01 header
#
if pci.IS_PCI_BRIDGE (PciCfg) or pci.IS_CARDBUS_BRIDGE (PciCfg):
# Type 1
PciTypeFields.append (('Cfg', pci.PCI_BRIDGE_CONTROL_REGISTER))
else:
# Type 0
PciTypeFields.append (('Cfg', pci.PCI_DEVICE_HEADER_TYPE_REGION))
class PciStruct (EFIPY_INDUSTRY_STRUCTURE):
_fields_ = PciTypeFields
PciCfg = PciStruct.from_address (PciCfgAddress)
return PciCfg, PciStruct
BuildPciHeader showcases one of EfiPy2's most powerful capabilities: overlaying EDK2 C structures from MdePkg.IndustryStandard.Pci directly onto physical MMIO addresses via .from_address(PciCfgAddress).
BuildPciHeader 展示了 EfiPy2 最核心的優勢:直接將 EDK2 MdePkg.IndustryStandard.Pci 的 C 結構體映射到硬體 MMIO 位址上(.from_address(PciCfgAddress)):
- Reading the Common Header (
PCI_DEVICE_INDEPENDENT_REGION):
Every PCI function shares the exact same 16-byte layout at offsets0x00to0x0F, containingVendorId,DeviceId,Command,Status,RevisionID,ClassCode[3], andHeaderType.
讀取共用標頭(PCI_DEVICE_INDEPENDENT_REGION):所有 PCI 裝置的前 16 bytes(Offset0x00~0x0F)格式完全固定,包含VendorId、DeviceId、Command、Status、RevisionID、ClassCode[3]與HeaderType。 - Distinguishing Header Types (Type 00 vs. Type 01):
- Using the EDK2 helper macro
pci.IS_PCI_BRIDGE(PciCfg), the script checks whether(PciCfg.Hdr.HeaderType & 0x7F) == 0x01. - If the function is a Type 01 (PCI-to-PCI Bridge), offsets
0x10to0x3Fare mapped aspci.PCI_BRIDGE_CONTROL_REGISTER, which exposesPrimaryBus,SecondaryBus, andSubordinateBus. - Otherwise, for a Type 00 (Endpoint Device), offsets
0x10to0x3Fare mapped aspci.PCI_DEVICE_HEADER_TYPE_REGION.
判斷 Header Type(Type 00 vs. Type 01): - 透過 EDK2 巨集函式
pci.IS_PCI_BRIDGE(PciCfg)檢查(PciCfg.Hdr.HeaderType & 0x7F) == 0x01。 - 如果是 Type 01 (PCI-to-PCI Bridge),Offset
0x10~0x3F會掛載pci.PCI_BRIDGE_CONTROL_REGISTER(內含建立樹狀圖所需的PrimaryBus、SecondaryBus與SubordinateBus)。 - 如果是 Type 00 (一般 Endpoint 裝置),則掛載
pci.PCI_DEVICE_HEADER_TYPE_REGION。
- Using the EDK2 helper macro
- Zero-Copy Register Access:
By callingPciStruct.from_address(PciCfgAddress), any subsequent attribute access likePciCfg.Hdr.VendorIdorPciCfg.Cfg.SecondaryBusreads directly from the memory-mapped hardware register without manual bit-shifting or buffer copying.
Zero-Copy 暫存器存取:最後透過PciStruct.from_address(PciCfgAddress)回傳映射物件PciCfg。後續存取PciCfg.Hdr.VendorId或PciCfg.Cfg.SecondaryBus時,底層會直接從 ECAM MMIO 位址取值,不需手動複製記憶體或做位移運算。
4. Resolving Vendor and Device Names (GetDeviceDesc)
透過 pciids 轉換可讀的廠商與裝置名稱
def GetDeviceDesc (VendorId: int, DeviceId: int) -> str:
VendorName, DeviceDict = pciids.PciIds.get (VendorId, (f"Vendor {VendorId:04x}", {}))
DeviceName, _ = DeviceDict.get (DeviceId, (f"Device {DeviceId:04x}", {}))
return f"{VendorName} {DeviceName} [{VendorId:04x}:{DeviceId:04x}]"
To combine human-readable device names (like lspci -v) with raw hexadecimal IDs (like lspci -nn), GetDeviceDesc queries the pciids.PciIds dictionary:
為了讓輸出的樹狀圖同時具備人類可讀的裝置名稱(如 lspci -v)與十六進位識別碼(如 lspci -nn),GetDeviceDesc 會查詢 pciids.PciIds 字典:
- First-level lookup (
VendorId): Retrieves(VendorName, DeviceDict)frompciids.PciIds, falling back toVendor xxxxif the vendor ID is unknown.
第一層查詢(VendorId):以VendorId為 key 取出(VendorName, DeviceDict);若資料庫中無此廠商,則預設顯示Vendor xxxx。 - Second-level lookup (
DeviceId): RetrievesDeviceNamefromDeviceDict, falling back toDevice xxxxif the device ID is not listed.
第二層查詢(DeviceId):在DeviceDict中以DeviceId為 key 取出DeviceName;若找不到則預設顯示Device xxxx。 - Formatted Output: Combines both names and the
[vid:did]tag into{VendorName} {DeviceName} [{VendorId:04x}:{DeviceId:04x}](for example,Red Hat, Inc. QEMU PCIe Root port [1b36:000c]).
格式化輸出:最後組合成{VendorName} {DeviceName} [{VendorId:04x}:{DeviceId:04x}](例如:Red Hat, Inc. QEMU PCIe Root port [1b36:000c])。
5. Recursive Bus & Bridge Discovery (CollectBusDevices)
遞迴收集匯流排與 Bridge 子樹
def CollectBusDevices (Bus: int) -> list:
devices = []
for Dev in range (pci.PCI_MAX_DEVICE + 1):
for Func in range (pci.PCI_MAX_FUNC + 1):
PciCfgOffset = pci.PCI_ECAM_ADDRESS (Bus, Dev, Func, 0)
PciObj, _ = BuildPciHeader (PciCfgBase, PciCfgOffset)
VendorId = PciObj.Hdr.VendorId
if VendorId in (0xFFFF, 0x0000) and Func == 0:
break
if VendorId in (0xFFFF, 0x0000):
continue
DeviceId = PciObj.Hdr.DeviceId
IsMulti = pci.IS_PCI_MULTI_FUNC (PciObj)
IsBridge = pci.IS_PCI_BRIDGE (PciObj) or pci.IS_PCI_P2P (PciObj)
dev_entry = {
"bus": Bus,
"dev": Dev,
"func": Func,
"vid": VendorId,
"did": DeviceId,
"desc": GetDeviceDesc (VendorId, DeviceId),
"is_bridge": IsBridge,
"sec_bus": PciObj.Cfg.SecondaryBus if IsBridge else 0,
"sub_bus": PciObj.Cfg.SubordinateBus if IsBridge else 0,
"children": [],
}
if IsBridge and dev_entry["sec_bus"] > Bus:
dev_entry["children"] = CollectBusDevices (dev_entry["sec_bus"])
devices.append (dev_entry)
if Func == 0 and not IsMulti:
break
return devices
CollectBusDevices iterates through all 32 Devices (0..31) and 8 Functions (0..7) on a given Bus to build a hierarchical list of device nodes:
CollectBusDevices 負責走訪單一 Bus 上的所有 Device(0..31)與 Function(0..7),並建構出樹狀資料結構:
- Calculating the ECAM Offset (
pci.PCI_ECAM_ADDRESS):
Uses the EDK2 macro to compute the byte offset in ECAM MMIO:((Bus << 20) | (Dev << 15) | (Func << 12) | Reg).
計算 ECAM Offset(pci.PCI_ECAM_ADDRESS):使用 EDK2 標準巨集將(Bus, Dev, Func, Reg)轉換為 ECAM 的記憶體位移量:((Bus << 20) | (Dev << 15) | (Func << 12) | Reg)。 - Checking Device Presence (
VendorId):- When an unpopulated slot is read, the host bridge returns all ones (
0xFFFF). IfFunc == 0returns0xFFFF(or0x0000), no device is present at thisDevnumber, so webreakout of theFuncloop immediately. - If
Func > 0returns0xFFFF, that specific function is unpopulated on a multi-function device, so wecontinueto the nextFunc.
檢查裝置是否存在(VendorId): - 當讀取空插槽時,Host Bridge 會回傳
0xFFFF。若Func == 0讀出0xFFFF(或0x0000),代表該Dev沒有裝置,直接break跳過其餘 Function。 - 若在
Func > 0讀到0xFFFF,代表多功能裝置的該 Function 未實作,則continue檢查下一個 Function。
- When an unpopulated slot is read, the host bridge returns all ones (
- Recursive Traversal Across Bridges:
- If
IsBridgeisTrue(pci.IS_PCI_BRIDGE(PciObj)orpci.IS_PCI_P2P(PciObj)), the script readsSecondaryBus(the bus immediately downstream of the bridge) andSubordinateBus(the highest bus number behind the bridge subtree). - When
sec_bus > Bus, it recursively callsCollectBusDevices(dev_entry["sec_bus"])and attaches the returned list todev_entry["children"].
遞迴深入 PCI-to-PCI Bridge: - 若
IsBridge為True,則從 Type 01 Header 的PciObj.Cfg讀出SecondaryBus(Bridge 下游緊鄰的 Bus 號)與SubordinateBus(該 Bridge 子樹下最大的 Bus 號)。 - 當
sec_bus > Bus時,遞迴呼叫CollectBusDevices(dev_entry["sec_bus"]),將下游 Bus 的所有裝置存入dev_entry["children"]。
- If
- Multi-Function Check (
pci.IS_PCI_MULTI_FUNC):if Func == 0 and not IsMulti: breakchecks Bit 7 (0x80) ofHeaderTypeon Function 0.- Single-function devices often ignore the Function number bits during configuration cycles; if we do not stop after
Func == 0, the same device could be duplicated acrossFunc 1..7.
Multi-Function 檢查(pci.IS_PCI_MULTI_FUNC): if Func == 0 and not IsMulti: break會檢查Func 0的HeaderTypeBit 7 (0x80)。- 單一功能裝置(Single-Function Device)通常不會解碼
Func 1..7;若不在此break,Func 1..7可能會重複映射出與Func 0相同的內容。
6. Rendering the Tree View (PrintBusTree & ScanBus)
繪製樹狀結構與對齊排版
Why do we collect the entire bus list in CollectBusDevices before printing in PrintBusTree?
Because drawing an accurate ASCII tree requires knowing whether the current node is the last device on its bus (is_last = (idx == total - 1)):
- If it is NOT the last device (
not is_last): Its branch connector is+-, and any child lines printed below it must preserve a vertical continuation bar|in the current bus's column to connect to subsequent sibling devices. - If it IS the last device (
is_last): Its branch connector is\-, and its child lines use blank spacesin the current bus's column.
為什麼要先在 CollectBusDevices 把整條 Bus 的裝置收集完,再交給 PrintBusTree 印出?
因為在繪製 ASCII 樹狀圖時,必須預先知道目前節點是不是該條 Bus 上的最後一個裝置(is_last = (idx == total - 1)):
- 若不是最後一個裝置(
not is_last):分支符號用+-,且在展開它的子樹時,左方必須保留垂直延續線|,以連接同層級後面的兄弟裝置。 - 若是最後一個裝置(
is_last):分支符號用\-,且展開子樹時左方留白,不再畫垂直線。
def PrintBusTree (devices: list, line_prefix: str, cont_prefix: str, is_root: bool = False) -> None:
if not devices:
if is_root:
print (line_prefix)
return
total = len (devices)
for idx, dev in enumerate (devices):
is_first = (idx == 0)
is_last = (idx == total - 1)
conn = "\\-" if is_last else "+-"
dev_line_prefix = (line_prefix if (is_first and is_root) else cont_prefix) + conn
branch_bar = " " if is_last else "| "
dev_cont_prefix = cont_prefix + branch_bar
bdf_str = f"{dev['bus']:02x}:{dev['dev']:02x}.{dev['func']:x}"
if dev["is_bridge"]:
sec = dev["sec_bus"]
sub = dev["sub_bus"]
bus_tag = f"-[{sec:02x}-{sub:02x}]--"
print (f"{dev_line_prefix}{bdf_str}{bus_tag} {dev['desc']}")
if dev["children"]:
child_cont_prefix = dev_cont_prefix + " " * (len (bdf_str) + len (bus_tag))
PrintBusTree (dev["children"], child_cont_prefix, child_cont_prefix, is_root=False)
else:
print (f"{dev_line_prefix}{bdf_str} {dev['desc']}")
def ScanBus (StartBus: int = 0, EndBus: int = 0xFF, Segment: int = 0) -> None:
root_prefix = f"-[{Segment:04x}:{StartBus:02x}-{EndBus:02x}]-"
root_cont = " " * len (root_prefix)
devices = CollectBusDevices (StartBus)
PrintBusTree (devices, root_prefix, root_cont, is_root=True)
Formatting Highlights (排版設計重點)
- Root Prefix (
-[Segment:StartBus-EndBus]-):
InScanBus,root_prefixis formatted as-[0000:00-ff]-. The very first device on the Root Bus is printed on the same line immediately afterroot_prefix(is_first and is_root), while all subsequent devices start on a new line aligned withroot_cont = " " * len(root_prefix).
根節點前綴(-[Segment:StartBus-EndBus]-):在ScanBus中,root_prefix格式為-[0000:00-ff]-。Root Bus 的第一個裝置會直接印在同一行後方(is_first and is_root),而第二個以後的裝置則換行並以等長空白root_cont = " " * len(root_prefix)對齊。 - Full
B:D.F& Bridge Bus Range (-[sec-sub]--):
Every node prints its completeB:D.F(bdf_str = f"{dev['bus']:02x}:{dev['dev']:02x}.{dev['func']:x}"). If the node is a Bridge, it appendsbus_tag = f"-[{sec:02x}-{sub:02x}]--"and prints the Bridge's own vendor/device description on that line—unlike standard Linuxlspci -vt, which hides the Bridge's name when child devices are present.
完整B:D.F與 Bridge 次級匯流排範圍(-[sec-sub]--):每個節點皆顯示完整的B:D.F(如00:02.0)。當節點為 Bridge 時,後方會緊接印出它管理的次級匯流排範圍-[{sec:02x}-{sub:02x}]--(如-[01-01]--)以及 Bridge 本身的裝置名稱,解決了標準 Linuxlspci -vt在有子裝置時會省略 Bridge 名稱的問題。 - Automatic Child Indentation:
When a Bridge has downstream devices (if dev["children"]:),child_cont_prefixappends" " * (len(bdf_str) + len(bus_tag))todev_cont_prefix, ensuring the child branch connectors (+-/\-) align right after the parent Bridge's-[sec-sub]--tag.
子匯流排自動縮排對齊:當 Bridge 下游有子裝置時(if dev["children"]:),下一層的縮排字串child_cont_prefix會自動加上len(bdf_str) + len(bus_tag)個空白字元,讓子裝置的+-/\-精準對齊在父層 Bridge 的-[sec-sub]--正後方。
7. Sample Output (實際執行輸出)
Running python PciTree.py in the UEFI Shell on a QEMU Q35 virtual machine produces the following output:
在 QEMU Q35 虛擬機的 UEFI Shell 中執行 python PciTree.py,輸出結果如下:
FS0:\Efi\Apps\EfiPy2Sample\> python PciTree.py
-[0000:00-ff]-+-00:00.0 Intel Corporation 82G33/G31/P35/P31 Express DRAM Controller [8086:29c0]
+-00:01.0 Red Hat, Inc. QXL paravirtual graphic card [1b36:0100]
+-00:02.0-[01-01]-- Red Hat, Inc. QEMU PCIe Root port [1b36:000c]
| \-01:00.0 Red Hat, Inc. Virtio 1.0 network device [1af4:1041]
+-00:02.1-[02-02]-- Red Hat, Inc. QEMU PCIe Root port [1b36:000c]
| \-02:00.0 Red Hat, Inc. QEMU XHCI Host Controller [1b36:000d]
+-00:02.2-[03-03]-- Red Hat, Inc. QEMU PCIe Root port [1b36:000c]
| \-03:00.0 Red Hat, Inc. Virtio 1.0 console [1af4:1043]
+-00:02.3-[04-04]-- Red Hat, Inc. QEMU PCIe Root port [1b36:000c]
| \-04:00.0 Red Hat, Inc. Virtio 1.0 SCSI [1af4:1048]
+-00:02.4-[05-05]-- Red Hat, Inc. QEMU PCIe Root port [1b36:000c]
| \-05:00.0 Red Hat, Inc. Virtio 1.0 balloon [1af4:1045]
+-00:02.5-[06-06]-- Red Hat, Inc. QEMU PCIe Root port [1b36:000c]
| \-06:00.0 Red Hat, Inc. Virtio 1.0 RNG [1af4:1044]
+-00:02.6-[07-07]-- Red Hat, Inc. QEMU PCIe Root port [1b36:000c]
+-00:02.7-[08-08]-- Red Hat, Inc. QEMU PCIe Root port [1b36:000c]
+-00:1b.0 Intel Corporation 82801I (ICH9 Family) HD Audio Controller [8086:293e]
+-00:1f.0 Intel Corporation 82801IB (ICH9) LPC Interface Controller [8086:2918]
+-00:1f.2 Intel Corporation 82801IR/IO/IH (ICH9R/DO/DH) 6 port SATA Controller [AHCI mode] [8086:2922]
\-00:1f.3 Intel Corporation 82801I (ICH9 Family) SMBus Controller [8086:2930]
From this output, we can immediately observe:
從上方輸出可以一目了然地看出:
- Segment & Bus Range: The system operates on PCI Segment
0000with root bus range00-ff.
Segment 與匯流排範圍:系統的 PCI Segment 為0000,主匯流排範圍為00-ff。 - PCIe Root Ports (
00:02.0to00:02.7): Eight multifunction QEMU PCIe Root Ports ([1b36:000c]) bridge Root Bus00to Secondary Buses01through08.
PCIe Root Ports(00:02.0~00:02.7):8 個多功能 QEMU PCIe Root Ports([1b36:000c])分別將 Bus00橋接至次級匯流排 Bus01~08。 - Downstream Endpoints vs. Empty Slots: Buses
01through06each host a downstream Virtio or XHCI Endpoint device (01:00.0to06:00.0), while Root Ports00:02.6([07-07]) and00:02.7([08-08]) are empty slots with no downstream devices attached.
下游裝置與空插槽:Bus01~06下游各自掛載了一個 Virtio 或 XHCI 裝置(01:00.0~06:00.0),而00:02.6([07-07])與00:02.7([08-08])則是尚未接上子裝置的空 Root Port。

No comments:
Post a Comment