那曲檬骨新材料有限公司

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

如何在UEFI環境下使用 UEFI規范提供的接口

Linux閱碼場 ? 來源:Linux閱碼場 ? 作者:Linux閱碼場 ? 2022-08-04 09:38 ? 次閱讀

目錄

7.1訪問PCI/PCIE 設備

7.1.1與PCI/PCIE設備通信的機制

7.1.2支持訪問PCI/PCIE設備的Protocol

7.1.3訪問PCI/PCIE設備示例

進行項目開發、構建產品框架的時候,最開始需要考慮的就是采用哪種通信方式讓軟件可以訪問外部設備(簡稱外設)。計算機經過多年的發展,提供了非常豐富的通信協議供程序員選擇。從古老的串口協議,到使用廣泛的 PCI/PCIE 協議,再到現在無處不在的 USB 協議等,可供選擇的方式實在太多。

在 Legacy BIOS 下,第三方開發者編寫訪問外設的代碼是件非常辛苦的事情。主要原因在于,Legacy BIOS 對很多協議支持得并不好。以筆者常用的 SMBus 協議為例,直到現在,也沒有 BIOS 廠家提供標準的中斷接口。大多數時候,只能通過閱讀主板芯片規格書,了解與 SMBus 協議相關的寄存器信息,再根據標準的 SMBus 總線讀寫協議編寫代碼。市場上主板芯片組太多,這種方法寫出來的代碼,兼容性、穩定性都不理想,并且工作量非常大。

UEFI BIOS 的出現,解決了上述這些問題。從 UEFI 標準和 EDK2 的源碼也可以看出,常用的總線協議如 PCI/PCIE、SMBus、串口等,UEFI 都已經提供了支持。對于依賴于 BIOS 接口進行產品開發的廠商來說,這是一個非常好的消息,產品的開發速度將大幅提高,穩定性和兼容性也能得到保障。

本章將介紹如何在 UEFI 環境下使用 UEFI 規范提供的接口(即各類 Protocol),通過 PCI/PCIE、SMBus 和串口訪問外設。

7.1 訪問 PCI/PCIE 設備

PCI(Peripheral Component Interconnect)是一種高速的局部總線。其主要目的是連接周邊設備,將低速的設備與高速的處理器結合起來,以解決用戶對數據傳輸速率越來越高的要求。PCIE 總線是在 PCI 總線上繼承發展來的,其將信號傳輸方式從并行改為了串行, 傳輸速率也突飛猛進,PCI 的理論帶寬為 133MB/s,而 PCIE4.0 x16 的帶寬達到了 64GB/s。

從硬件結構角度看,PCI 和 PCIE 有很大的不同。PCI 總線采用并行總線結構,而 PCIE 總線使用了高速差分總線結構,使用端到端的連接方式,這使得兩者采用的拓撲結構差異較大。隨著技術的發展,目前市場上 PCI 設備越來越少,很多主板現在只提供 PCIE 的接口了。

這些差異對在 UEFI 下進行編程影響不大。UEFI 系統已經屏蔽了這些差異,提供了一致的訪問接口,下面詳細介紹如何訪問 PCI/PCIE 設備。

7.1.1與 PCI/PCIE設備通信的機制

PCI 協議和 PCIE 協議經過多年的發展,其內容已經非常龐大。本書主要論述的是如何在 UEFI 下進行編程。站在軟件工程師的角度,在訪問 PCI/PCIE 設備時,實際上只要回答以下兩個問題就可以了。

?如何在系統中找到需要訪問的設備?

?找到設備后,如何訪問設備內的寄存器或其他資源?

UEFI 規范中,抽象了 PCI 的系統架構,典型的桌面系統的 PCI 架構如圖 7-1 所示。

a19e40d0-1389-11ed-ba43-dac502259ad0.png

圖 7-1 單 PCI Root Bridge 的桌面系統

一般的桌面系統只有一個 PCI Host Bus(PCI 主機總線),用于完成 CPU 與 PCI 設備之間的數據交換。PCI Root Bridge(PCI 根橋)一般也只有一個,它管理一個局部總線,下掛一棵 PCI 總線樹。我們所要訪問的 PCI 設備,就掛在這棵總線樹上,它們屬于同一總線空間,如圖 7-2 所示。

從圖 7-2 中可以看出,PCI 總線樹上包含 PCI 總線、PCI 橋和 PCI 設備。系統通過三段編碼的方式進行編碼,即通過 Bus Number(總線號)、Device Number(設備號)和 Function Number(功能號)來編碼,這種編碼一般簡稱為 BDF 碼。BDF 碼在 BIOS 進行 PCI 總線掃描和枚舉過程中確定,可以用來作為查找 PCI 設備的索引

a1b5677e-1389-11ed-ba43-dac502259ad0.png

圖 7-2 PCI 總線樹

找到 PCI 設備后,如何確定此設備就是自己要找的設備呢?每個 PCI 設備,除了主總線橋外,都會實現配置空間(主總線橋可以有選擇地實現),而在配置空間中,包含了設備廠商用來標志自身的 Vendor ID(供應商 ID)和 Device ID(設備 ID)。通過比對 PCI 設備的供應商 ID 和設備 ID,可以確定所找的設備是否為目標設備。

以 X86 平臺為例,可通過 CONFIG_ADDR 寄存器(0xCF8)和 CONFIG_DATA 寄存器(0xCFC),以 BDF 碼的形式訪問 PCI 設備,以得到設備的配置空間。圖 7-3 所示為 PCI設備的基本配置空間。

a1ca30c8-1389-11ed-ba43-dac502259ad0.png

圖 7-3 PCI 設備的配置空間

PCI 設備的基本配置空間由 64 字節組成,地址范圍為 0x00~0x3F,主要用來識別設備、定義主機訪問 PCI 卡的方式。從圖 7-3 中可以看出,最開始的兩個寄存器就是 Vendor ID 和 Device ID 寄存器,這是用來標志設備自身的寄存器,由 PCISIG 協會分配。比如Intel 集成顯卡 HD620,其 Vendor ID 為 0x8086,而 Device ID 為 0x5917。

在配置空間中,從地址 0x10至 0x24,包含了 6個 BaseAddressRegiste(r基址寄存器)。這組寄存器被稱為 BAR,保存了 PCI 設備使用的地址空間的基地址,也即該設備在 PCI 總線域中的地址。每個 PCI 設備最多可以有 6 個基址空間,但多數設備不會使用這么多,筆者以前常用的南京沁恒的 CH366 芯片,只使用了第一個 BAR。

BAR 可尋址 IO 地址空間或者 Memory 地址空間,其最低位是只讀位,顯示了可以訪問哪種地址空間。值為 0 表示寄存器是 Memory 地址譯碼,值為 1 表示寄存器是 IO 地址譯碼,如圖 7-4 所示。

a1dd53b0-1389-11ed-ba43-dac502259ad0.png

圖 7-4 基地址寄存器的位分配

那么如何訪問 PCI 設備內的寄存器和其他資源?答案是通過 BAR 寄存器,加上內部寄存器相對于 BAR 的偏移地址。芯片手冊中,會提供關于內部資源的使用說明,以 CH366 的芯片為例,其內部寄存器說明如圖 7-5 所示。

a1e92492-1389-11ed-ba43-dac502259ad0.png

圖 7-5 CH366 寄存器說明(節選自《CH366 中文手冊》)

CH366 的第一個 BAR 可以使用,它是 IO 地址譯碼的,其他 BAR 在芯片中是無效的。圖 7-4 表明,第一個 BAR 加上偏移地址,就可以成為芯片內部相應功能的寄存器。至于這些內部寄存器有什么作用,則需要詳細了解芯片手冊才能知道。

a20f9dde-1389-11ed-ba43-dac502259ad0.png

總結來說,訪問 PCI/PCIE 設備的過程如下。

1)掃描整個系統空間,通過 BDF獲取 PCI/PCIE設備的配置空間。同一總線域上(即存在一個主橋),PCIE一共支持 256個總線、32個設備、8個功能,也就是說總線號最大值為 255、設備號最大值為 31、功能號最大為 7。

2)讀取 PCI/PCIE設備配置空間中的 VendorID和 DeviceID,確定是否為需要訪問的設備。

3)找到 PCI/PCIE設備后,獲取其配置空間中的 BAR,參照芯片手冊,訪問設備的內部寄存器和資源。

UEFI 中提供了兩個主要的模塊來支持 PCI 總線,一是 PCI Host Bridge(PCI 主橋)控制器驅動,另一個是 PCI 總線驅動。這兩個模塊是和特定的平臺硬件綁定的,在這種機制下,屏蔽了不同的 CPU 架構差異,為軟件開發者提供了比較一致的 Protocol 接口。下一節詳細介紹訪問 PCI/PCIE 設備的 Protocol。

7.1.2支持訪問 PCI/PCIE設備的 Protocol

UEFI標準中提供了兩類訪問 PCI/PCIE設備的 Protocol—EFI_PCI_ROOT_BRIDGE_ IO_PROTOCOL 和 EFI_PCI_IO_PROTOCOL。前者為 PCI 根橋提供了抽象的 IO 功能,它由 PCI Host Bus Controller(PCI 主總線驅動器)產生,一般由 PCI/PCIE 總線驅動用來枚舉設備、獲得 Option ROM、分配 PCI 設備資源等;后者由 PCI/PCIE 總線驅動為 PCI/PCIE 設備產生,一般由 PCI/PCIE 設備驅動用來訪問 PCI/PCIE 設備的 IO 空間、Memory 空間和配置空間。

這兩種 Protocol 的使用方法如下。

1.使用 EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL

EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL 中提供了基本的訪問接口,包括訪問 IO 空間、Memory 空間和配置空間的接口。該 Protocol 主要由 PCI/PCIE 總線驅動使用,當然, UEFI 應用也可以使用它來遍歷 PCI/PCIE 設備。

該 Protocol 中還提供了 DMA 接口,以支持總線驅動訪問系統內存。代碼清單 7-1 給出了 EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL 的函數接口。

代碼清單 7-1 EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL 函數接口

typedefstruct_EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL{EFI_HANDLEParentHandle;//Protocol的父句柄EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_POLL_IO_MEM PollMem; EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_POLL_IO_MEM PollIo;EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ACCESS Mem; //讀寫Memory空間EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ACCESS Io; //讀寫IO空間EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ACCESS Pci; //讀寫配置空間EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_COPY_MEM CopyMem; EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_MAP Map; EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_UNMAP Unmap;EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ALLOCATE_BUFFER AllocateBuffer; EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_FREE_BUFFER FreeBuffer; EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_FLUSH Flush; EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_GET_ATTRIBUTES GetAttributes; EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_SET_ATTRIBUTES SetAttributes; EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_CONFIGURATION Con?guration;UINT32 SegmentNumber;}EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL;

從代碼清單 7-1 中可以看出,此 Protocol 提供了非常豐富的訪問接口。由于篇幅所限, 無法將所有接口都介紹清楚,這里主要介紹如何讀寫 PCI/PCIE 設備的 3 種空間。

從 7.1.1 節的介紹中我們知道,PCI/PCIE 設備能訪問的空間包括 Memory 空間、IO 空間和配置空間。對于這 3 種空間,每個 PCI/PCIE 設備必須實現配置空間,而 Memory 空間和 IO 空間的功能,則不一定實現。7.1.1 節介紹的 PCIE 芯片 CH366 就只支持 IO 空間的訪問。

EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL 提供了訪問 Memory 空間的接口 Mem、訪問 IO 空間的接口 Io 和訪問配置空間的接口 Pci。這 3 個接口的參數類型都是一樣的,均為EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ACCESS,詳見代碼清單 7-2。

代碼清單 7-2 訪問 IO 空間、Memory 空間和配置空間的接口

typedef struct {EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_IO_MEM Read;  //讀數據EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_IO_MEM Write;  //寫數據} EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ACCESS;typedef EFI_STATUS (EFIAPI *EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_IO_MEM) (IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *This,  //實例IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH Width,  //讀寫寬度IN UINT64 Address, //IO空間/Memory空間/配置空間的地址IN UINTN Count,  //讀寫的數據個數,單位為讀寫寬度WidthIN OUT VOID *Buffer //對讀操作,這是目的緩沖區;對寫操作,這是要寫的數據緩沖區);

訪問 3 種空間的接口都包含讀數據和寫數據兩種操作,并且使用了同樣的數據結構EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_IO_MEM。在此結構中,Width 是指讀寫寬度, 其值由枚舉類型 EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH 給出,一般包括 8 位、16 位、32 位和 64 位幾種。

需要注意的是,參數 Address 在訪問 IO 空間、Memory 空間和配置空間時,其含義是不同的。對配置空間而言,Address 由 BDF 地址和 Register 偏移決定,即總線號、設備號、功能號和 Register 共同給出的尋址用索引。這是一個 64 位長的參數,一般使用宏 EFI_ PCI_ADDRESS 來組合 BDF 和 Register 偏移。在 EDK2 中,這個宏定義于頭文件 MdePkg IncludeProtocolPciRootBridgeIo.h 中,其內容如下。

#de?ne EFI_PCI_ADDRESS(bus, dev, func, reg) (UINT64) ( (((UINTN) bus) << 24) |  (((UINTN) dev) << 16) |  (((UINTN) func) << 8) | (((UINTN) (reg)) < 256 ? ((UINTN) (reg)): (UINT64) (LShiftU64 ((UINT64) (reg), 32))))

對 IO 空間而言,參數 Address 是指 PCI 設備 IO 空間的 IO 地址;對 Memory 空間而言,參數 Address 是指 PCI 設備 Memory 空間的 Memory 地址。參考 4.2.1 節可知,IO 地址和 Memory 地址是由 BAR 和偏移決定的,每個地址的作用還需要查看對應芯片的說明手冊。

2.使用 EFI_PCI_IO_PROTOCOL

在 PCI/PCIE 設備驅動中,一般使用 EFI_PCI_IO_PROTOCOL 來訪問設備的內部資源, Protocol 掛載在 PCI/PCIE 控制器上,運行在 EFI 啟動服務環境中,對 PCI/PCIE 設備進行Memory 空間和 IO 空間訪問。其函數接口如代碼清單 7-3 所示。

代碼清單 7-3 EFI_PCI_IO_PROTOCOL 函數接口

typedef struct _EFI_PCI_IO_PROTOCOL { EFI_PCI_IO_PROTOCOL_POLL_IO_MEM PollMem; EFI_PCI_IO_PROTOCOL_POLL_IO_MEM PollIo;EFI_PCI_IO_PROTOCOL_ACCESS Mem; //讀寫Memory空間EFI_PCI_IO_PROTOCOL_ACCESS Io; //讀寫IO空間EFI_PCI_IO_PROTOCOL_CONFIG_ACCESS Pci; //讀寫配置空間EFI_PCI_IO_PROTOCOL_COPY_MEM CopyMem; EFI_PCI_IO_PROTOCOL_MAP Map; EFI_PCI_IO_PROTOCOL_UNMAP Unmap;EFI_PCI_IO_PROTOCOL_ALLOCATE_BUFFER AllocateBuffer; EFI_PCI_IO_PROTOCOL_FREE_BUFFER FreeBuffer; EFI_PCI_IO_PROTOCOL_FLUSH Flush;EFI_PCI_IO_PROTOCOL_GET_LOCATION GetLocation; EFI_PCI_IO_PROTOCOL_ATTRIBUTES Attributes; EFI_PCI_IO_PROTOCOL_GET_BAR_ATTRIBUTES GetBarAttributes; EFI_PCI_IO_PROTOCOL_SET_BAR_ATTRIBUTES SetBarAttributes; UINT64 RomSize;VOID*RomImage;} EFI_PCI_IO_PROTOCOL;
與EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL 不 同,EFI_PCI_IO_PROTOCOL 在 處理訪問 PCI/PCIE 的 Memory 空間、IO 空間和配置空間時,使用了兩種類型來區分。其中, 訪問 Memory 空間和 IO 空間使用的類型是 EFI_PCI_IO_PROTOCOL_ACCESS,如代碼清單 7-4 所示。

代碼清單7-4訪問IO空間和Memory空間的接口

typedefstruct{EFI_PCI_IO_PROTOCOL_IO_MEM Read;  //讀數據EFI_PCI_IO_PROTOCOL_IO_MEM Write; //寫數據} EFI_PCI_IO_PROTOCOL_ACCESS;typedef EFI_STATUS (EFIAPI *EFI_PCI_IO_PROTOCOL_IO_MEM) (IN EFI_PCI_IO_PROTOCOL *This,  //EFI_PCI_IO_PROTOCOL實例IN EFI_PCI_IO_PROTOCOL_WIDTH Width, //讀寫寬度,8位、16位、32位、64位IN UINT8 BarIndex, //在配置空間中的BAR索引值IN UINT64 Offset,  //偏移寄存器,用來進行IO空間/Memory空間讀寫IN UINTN Count,  //讀寫的數據個數,單位為讀寫寬度WidthINOUTVOID*Buffer//對讀操作,這是目的緩沖區;對寫操作,這是要寫的數據緩沖區);

上述代碼中的參數 This 指向的是與 PCI/PCIE 設備本身相關的 EFI_PCI_IO_PROTOCOL實例,因此,在訪問設備時比較直接,不需要通過 BDF 等方式給出設備的地址。

IO 空間讀寫使用的函數為 Io.Read() 和 Io.Write() ;Memory 空間讀寫使用的函數為Mem.Read() 和 Mem.Write()。讀寫數據的時候,所需要訪問的地址由 BarIndex 和 Offset 共同規定。圖 7-3 所示為 PCI/PCIE 設備的配置空間,從圖中可知,BAR 總共有6 個, BarIndex 值的范圍為 0 至 5。最終訪問的地址,等于 BarIndex 所指向的 BAR 加上 Offset。至于此地址的含義,仍舊得查看 PCI/PCIE 芯片廠家提供的說明手冊。

訪問配置空間使用的數據結構為 EFI_PCI_IO_PROTOCOL_CONFIG_ACCESS,其接口說明如代碼清單 7-5 所示。

代碼清單7-5訪問配置空間的接口

typedefstruct{EFI_PCI_IO_PROTOCOL_CONFIG Read; //讀數據EFI_PCI_IO_PROTOCOL_CONFIG Write; //寫數據} EFI_PCI_IO_PROTOCOL_CONFIG_ACCESS;typedef EFI_STATUS (EFIAPI *EFI_PCI_IO_PROTOCOL_CONFIG) (IN EFI_PCI_IO_PROTOCOL *This,  //EFI_PCI_IO_PROTOCOL實例IN EFI_PCI_IO_PROTOCOL_WIDTH Width, //讀寫寬度,8位、16位、32位、64位IN UINT32 Offset,  //偏移,在配置空間內的偏移地址IN UINTN Count,  //讀寫的個數,以Width為單位INOUTVOID*Buffer//對讀操作,這是目的緩沖區;對寫操作,這是要寫的數據緩沖區);

Pci.Read() 和 Pci.Write() 函數用來訪問 PCI/PCIE 設備的配置空間。參數 Offset 用來指定在配置空間內的偏移地址,比如 Offset=0x10 時,是指 BAR0 寄存器。

PCI 設備的基本配置空間是由 64 字節(0x00~0x3F)組成的,這是所有 PCI/PCIE 設備必須支持的。此外,PCI/PCIE 設備還擴展了 0x40~0xFF 這段配置空間,主要用來存放于MSI 中斷機制和電源管理相關的 Capability 結構。另外,PCIE 設備還支持 0x100~0xFFF 這段配置空間,這段配置空間用于存放 PCIE 設備獨有的 Capability 結構。

這些配置空間的信息,都可以通過 EFI_PCI_IO_PROTOCOL 獲取。至于配置空間內寄存器的具體含義,讀者可以參考 PCI 標準和 PCIE 標準進行深入學習。

7.1.3訪問 PCI/PCIE設備示例

本節準備了相應的示例,演示如何使用 7.1.2 節介紹的兩種 Protocol 來遍歷系統內的PCI/PCIE 設備。大多數的機器上,只存在一個 PCI 總線域(PCISegment),即一個主橋。因此,在使用 EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL 的時候,應該只會找到一個實例。我們設計的程序,其主要功能如下。

使用 EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL,通過 BDF,遍歷所有 PCI/PCIE設備,打印出設備的相關信息。

尋找所有的 EFI_PCI_IO_PROTOCOL 實例,直接訪問每個實例的配置空間,將其信息打印出來。

本節提供的示例程序位于隨書代碼的 RobinPkgApplicationsListPCIMsg 目錄下。示例

7-1 演示了如何獲取兩類 Protocol 的實例。

【示例 7-1】獲取 Protocol 的實例。

EFI_STATUS LocatePCIRootBridgeIO(void){EFI_STATUS  Status;EFI_HANDLE  *PciHandleBuffer = NULL;UINTN  HandleIndex = 0;UINTN  HandleCount = 0;//獲取PciRootBridgeIOProtocol的所有句柄Status = gBS->LocateHandleBuffer(ByProtocol, &gE?PciRootBridgeIoProtocolGuid, NULL,&HandleCount, &PciHandleBuffer);if (EFI_ERROR(Status))  return Status;Print(L"Find PCI Root Bridge I/O Protocol: %d
",HandleCount);//獲取PciRootBridgeIOProtocol實例for(HandleIndex=0;HandleIndexHandleProtocol( PciHandleBuffer[HandleIndex], &gE?PciRootBridgeIoProtocolGuid, (VOID**)&gPCIRootBridgeIO);if (EFI_ERROR(Status))  continue; elsereturn EFI_SUCCESS;}return Status;}EFI_STATUS LocatePCIIO(void){EFI_STATUS  Status;EFI_HANDLE  *PciHandleBuffer = NULL;UINTN  HandleIndex = 0;UINTN  HandleCount = 0;//獲取PciIoProtocol的所有句柄 Status = gBS->LocateHandleBuffer(ByProtocol, &gE?PciIoProtocolGuid, NULL,&HandleCount, &PciHandleBuffer);if (EFI_ERROR(Status))  return Status;  //unsupport gPCIIO_Count = HandleCount;Print(L"Find PCI I/O Protocol: %d
",HandleCount);//獲取PciIoProtocol實例,并存儲在全局變量gPCIIOArray中for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++){Status = gBS->HandleProtocol( PciHandleBuffer[HandleIndex], &gE?PciIoProtocolGuid, (VOID**)&(gPCIIOArray[HandleIndex]));}return Status;}

示例 7-1 中提供了兩個函數—LocatePCIRootBridgeIO() 和 LocatePCIIO(),用來獲取需要測試的兩類 Protocol 的實例。獲取實例的方法在 3.5 節中已經介紹過了,本節的例程用了同樣的方法。EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL 的實例,在大部分辦公用的個人電腦中只存在一個,因此直接用全局指針變量 gPCIRootBridgeIO 存儲;而 EFI_PCI_IO_ PROTOCOL 的實例存在多個,一般有多少個 PCI/PCIE 設備,就存在多少個實例,因此使用全局指針數組 gPCIIOArray[256] 來存儲這些實例。

為遍歷全部的 PCI/PCIE 設備,可以使用 gPCIRootBridgeIO 和 BDF 碼,循環查找掛載總線上的設備,代碼如示例 7-2 所示。

【示例 7-2】使用 EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL 遍歷 PCI/PCIE 設備。

EFI_STATUS ListPCIMessage1(void){EFI_STATUS Status=EFI_SUCCESS; PCI_TYPE00 Pci;UINT16 i,j,k,count=0; for(k=0;k<=PCI_MAX_BUS;k++)for(i=0;i<=PCI_MAX_DEVICE;i++) for(j=0;j<=PCI_MAX_FUNC;j++){//判斷設備是否存在Status = PciDevicePresent(gPCIRootBridgeIO,&Pci, (UINT8)k,(UINT8)i,(UINT8)j);if (Status == EFI_SUCCESS)  //找到了設備{++count;Print(L"%02d. Bus-%02x Dev-%02x Func-%02x: ", count,(UINT8)k,(UINT8)i,(UINT8)j);Print(L"VendorID-%x DeviceID-%x ClassCode-%x", Pci.Hdr.VendorId,Pci.Hdr.DeviceId,Pci.Hdr.ClassCode[0]);Print(L"
");}}return EFI_SUCCESS;}

從代碼中可以看出,函數使用了 3 個 for 循環調用函數 PciDevicePresent(),依次尋找PCI/PCIE 設備是否存在。如果存在,則取出已經讀取到的配置空間的數據,將設備的一些信息打印出來。

使用 EFI_PCI_IO_PROTOCOL 遍歷設備則比較簡單,因為之前所得到的此 Protocol 的實例,就是為 PCI/PCIE 設備產生的,實際上相當于找到了設備,只需要將設備的信息打印出來即可。相應的代碼見示例 7-3 所示。

【示例 7-3】使用 EFI_PCI_IO_PROTOCOL 遍歷 PCI/PCIE 設備。

EFI_STATUS ListPCIMessage2(void){UINTN i,count=0;PCI_TYPE00 Pci;for(i=0;iPci.Read(gPCIIOArray[i],E?PciWidthUint32,0,sizeof (PCI_TYPE00) / sizeof (UINT32),&Pci);++count;Print(L"%02d. VendorID-%x DeviceID-%x ClassCode-%x", count,Pci.Hdr.VendorId,Pci.Hdr.DeviceId,Pci.Hdr.ClassCode[0]);Print(L"
");}return EFI_SUCCESS;}

本節所準備的示例,主要是為了演示如何使用與 PCI/PCIE 相關的兩個 Protocol。代碼本身還有許多不完善的地方,比如對多個總線域情況的處理、內存的釋放、Protocol 的關閉等,都沒有考慮。本書的代碼,包括本節的代碼在內,建議讀者只用來學習使用,如果想商用,則應該在代碼中將所有情況考慮到。

可參照 2.1.3 節的方法,設置編譯的環境變量,并使用如下命令編譯程序:

C:UEFIWorkspaceedk2uild -p RobinPkgRobinPkg.dsc -m RobinPkgApplicationsListPCIMsg ListPCIMsg.inf -a X64

所編譯的程序最好在實際的機器上測試運行。筆者使用 2.2.2 節搭建的 QEMU 環境來運行編譯好的 64 位 UEFI 程序,程序運行的結果如圖 7-6 所示。

a232f18a-1389-11ed-ba43-dac502259ad0.png

圖 7-6 測試 ListPCIMsg 程序

審核編輯:彭靜
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 接口
    +關注

    關注

    33

    文章

    8694

    瀏覽量

    151931
  • 軟件
    +關注

    關注

    69

    文章

    5013

    瀏覽量

    88089
  • 代碼
    +關注

    關注

    30

    文章

    4828

    瀏覽量

    69063
  • UEFI
    +關注

    關注

    0

    文章

    53

    瀏覽量

    11871

原文標題:《UEFI編程實踐》選載之訪問 PCI/PCIE 設備

文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    ARM系統預引導固件的新機遇-UEFI

    是作為固件在UEFI規范的界面層之下提供計算機系統初始化服務。PI的實現不是UEFI規范的必須。  UE
    發表于 08-23 09:06

    uefi的標準哪里可以下載呀?

    uefi的標準哪里可以下載呀?
    發表于 11-06 16:49

    UEFI有什么定義?

     EFI的出現第一次被正式提出,是在2000年的Intel春季IDF上,經過幾次修訂,現在已經到了EFI 1.10版,而2.0版正由UEFI這個組織制定中(故EFI也現稱為UEFI)。
    發表于 10-30 09:12

    如何切換BIOS啟動與UEFI啟動 bios與uefi切換方法

    就修改,沒有就跳過:1)切換到Boot,選擇UEFI Boot回車設置為Enabled。2)有些電腦在Startup,把UEFI/Legacy Boot設置為UEFI Only。3)
    發表于 06-05 11:54

    Embedded SIG | 樹莓派的UEFI支持和網絡啟動

    。使用 openEuler Embedded UEFI+GRUB 的樹莓派鏡像openEuler Embedded 的樹莓派鏡像集成了基于樹梅派 4B 的混合部署環境依賴,因此建議直接使用 openEuler
    發表于 09-07 15:22

    使用VisionFive 2適配UEFI有哪些資料可以參考?

    使用VisionFive 2適配UEFI,有哪些資料可以參考?
    發表于 09-12 06:52

    簡化安全、基于 UEFI 的物聯網 固件更新

    簡化安全、基于 UEFI 的物聯網 固件更新
    發表于 09-04 17:22 ?8次下載
    簡化安全、基于 <b class='flag-5'>UEFI</b> 的物聯網 固件更新

    基于UEFI固件的操作系統完整性度量機制

    Extensible Firmware Interface)是Intel聯合業界采用開源方式共同制定推出的規范吲。UEFI BIOS定義了操作系統和平臺固件之間的接口標準,是運行在操作系統和硬件之間的一個新的模型。
    發表于 11-30 17:27 ?0次下載
    基于<b class='flag-5'>UEFI</b>固件的操作系統完整性度量機制

    基于UEFI固件的攻擊檢測系統的設計與實現

    計算機基礎硬件和系統軟件的橋梁。UEFI (Unified Extensible Firmware Interface,統一可擴展固件接口)是新的固件標準,目前被業界廣泛使用。UEFI為固件和操作系統之間的
    發表于 11-30 17:31 ?0次下載
    基于<b class='flag-5'>UEFI</b>固件的攻擊檢測系統的設計與實現

    uefi 嵌入式Linux,面向嵌入式平臺的高級UEFI開發環境.PDF

    面向嵌入式平臺的高級UEFI開發環境面向嵌入式平臺的高級 UEFI 開發環境晉磊, 技術市場工程師, 英特爾周鵬程, 開發經理, 百敖軟件*姜波, 首席技術官, 盛博科技*PTAS00
    發表于 11-02 13:06 ?14次下載
    <b class='flag-5'>uefi</b> 嵌入式Linux,面向嵌入式平臺的高級<b class='flag-5'>UEFI</b>開發<b class='flag-5'>環境</b>.PDF

    BIOS+UEFI引導修復工具

    BIOS+UEFI引導修復工具
    發表于 11-19 14:54 ?4次下載

    龍芯LoongArch獲國際主流固件接口組織UEFI全面支持

    。 LoongArch基礎代碼被TianoCore EDK2合并進主線 UEFI(Unified Extensible Firmware Interface)即統一可擴展固件接口,是一種個人電腦系統規格,可擴展固件接口、負責加電
    的頭像 發表于 10-20 18:14 ?1181次閱讀
    龍芯LoongArch獲國際主流固件<b class='flag-5'>接口</b>組織<b class='flag-5'>UEFI</b>全面支持

    P7固件 FCODE BIOS UEFI

    電子發燒友網站提供《P7固件 FCODE BIOS UEFI.zip》資料免費下載
    發表于 08-08 11:24 ?1次下載
    P7固件 FCODE BIOS <b class='flag-5'>UEFI</b>

    研華SSD與Phoenix合作開發基于UEFI安全解決方案

    ?研華與BIOS固件專家Phoenix合作推出基于UEFI(Unified Extensible Firmware Interface統一可擴展固件接口)的SSD安全軟件工具-- SQErase
    發表于 09-06 13:44 ?1007次閱讀
    研華SSD與Phoenix合作開發基于<b class='flag-5'>UEFI</b>安全解決方案

    UEFIRC:運行于UEFI環境的IRC聊天室

    據悉,開源開發者Phillip Tennen展示了基于UEFI的沉浸式IRC網絡聊天室:UREFIRC原型設計。該設計無需進入操作系統,僅在UEFI環境內運行。
    的頭像 發表于 04-08 16:16 ?628次閱讀
    百家乐官网庄闲必胜打| 伯爵百家乐娱乐城| 伟德亚洲| 国际百家乐规则| 百家乐官网管理启发书| 大发888客服| 百家乐官网定位胆技巧| 利高百家乐官网现金网| 大发888如何注册送58| 游戏机百家乐下载| 24山辅星水法分阴阳| 百家乐官网游戏作弊| 百家乐官网高人玩法| 澳门顶级赌场国际| 百家乐平注常赢规则| 百家乐群柏拉图软件| 电子百家乐官网规则| 网上百家乐官网赌博出| 澳门百家乐官网娱乐城注册 | 伟易博百家乐的玩法技巧和规则 | 百家乐官网破战| 百家乐官网龙虎斗扎金花| 澳门赌场老板| 棋牌游戏平台排名| 大发888娱乐城dafa888dafa8| 百家乐龙虎扑克牌游戏技巧打| 百家乐h游戏怎么玩| 百家乐几点开奖| 百家乐看牌技巧| 百家乐官网统计软件| 博彩网百家乐官网的玩法技巧和规则| 洪泽县| 紫金县| 至尊百家乐官网规则| 百家乐官网出庄概率| 临漳县| 百家乐官网实战技术| 新河县| 镇江市| 现金百家乐官网攻略| 百家乐官网博赌城|