在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

如何在UEFI環(huán)境下使用 UEFI規(guī)范提供的接口

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

目錄

7.1訪問PCI/PCIE 設(shè)備

7.1.1與PCI/PCIE設(shè)備通信的機(jī)制

7.1.2支持訪問PCI/PCIE設(shè)備的Protocol

7.1.3訪問PCI/PCIE設(shè)備示例

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

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

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

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

7.1 訪問 PCI/PCIE 設(shè)備

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

從硬件結(jié)構(gòu)角度看,PCI 和 PCIE 有很大的不同。PCI 總線采用并行總線結(jié)構(gòu),而 PCIE 總線使用了高速差分總線結(jié)構(gòu),使用端到端的連接方式,這使得兩者采用的拓?fù)浣Y(jié)構(gòu)差異較大。隨著技術(shù)的發(fā)展,目前市場上 PCI 設(shè)備越來越少,很多主板現(xiàn)在只提供 PCIE 的接口了。

這些差異對在 UEFI 下進(jìn)行編程影響不大。UEFI 系統(tǒng)已經(jīng)屏蔽了這些差異,提供了一致的訪問接口,下面詳細(xì)介紹如何訪問 PCI/PCIE 設(shè)備。

7.1.1與 PCI/PCIE設(shè)備通信的機(jī)制

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

?如何在系統(tǒng)中找到需要訪問的設(shè)備?

?找到設(shè)備后,如何訪問設(shè)備內(nèi)的寄存器或其他資源?

UEFI 規(guī)范中,抽象了 PCI 的系統(tǒng)架構(gòu),典型的桌面系統(tǒng)的 PCI 架構(gòu)如圖 7-1 所示。

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

圖 7-1 單 PCI Root Bridge 的桌面系統(tǒng)

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

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

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

圖 7-2 PCI 總線樹

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

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

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

圖 7-3 PCI 設(shè)備的配置空間

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

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

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

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

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

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

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

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

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

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

總結(jié)來說,訪問 PCI/PCIE 設(shè)備的過程如下。

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

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

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

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

7.1.2支持訪問 PCI/PCIE設(shè)備的 Protocol

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

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

1.使用 EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL

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

該 Protocol 中還提供了 DMA 接口,以支持總線驅(qū)動訪問系統(tǒng)內(nèi)存。代碼清單 7-1 給出了 EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL 的函數(shù)接口。

代碼清單 7-1 EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL 函數(shù)接口

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 設(shè)備的 3 種空間。

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

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

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

typedef struct {EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_IO_MEM Read;  //讀數(shù)據(jù)EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_IO_MEM Write;  //寫數(shù)據(jù)} 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,  //讀寫的數(shù)據(jù)個數(shù),單位為讀寫寬度WidthIN OUT VOID *Buffer //對讀操作,這是目的緩沖區(qū);對寫操作,這是要寫的數(shù)據(jù)緩沖區(qū));

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

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

#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 空間而言,參數(shù) Address 是指 PCI 設(shè)備 IO 空間的 IO 地址;對 Memory 空間而言,參數(shù) Address 是指 PCI 設(shè)備 Memory 空間的 Memory 地址。參考 4.2.1 節(jié)可知,IO 地址和 Memory 地址是由 BAR 和偏移決定的,每個地址的作用還需要查看對應(yīng)芯片的說明手冊。

2.使用 EFI_PCI_IO_PROTOCOL

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

代碼清單 7-3 EFI_PCI_IO_PROTOCOL 函數(shù)接口

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 空間和配置空間時,使用了兩種類型來區(qū)分。其中, 訪問 Memory 空間和 IO 空間使用的類型是 EFI_PCI_IO_PROTOCOL_ACCESS,如代碼清單 7-4 所示。

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

typedefstruct{EFI_PCI_IO_PROTOCOL_IO_MEM Read;  //讀數(shù)據(jù)EFI_PCI_IO_PROTOCOL_IO_MEM Write; //寫數(shù)據(jù)} 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,  //偏移寄存器,用來進(jìn)行IO空間/Memory空間讀寫IN UINTN Count,  //讀寫的數(shù)據(jù)個數(shù),單位為讀寫寬度WidthINOUTVOID*Buffer//對讀操作,這是目的緩沖區(qū);對寫操作,這是要寫的數(shù)據(jù)緩沖區(qū));

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

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

訪問配置空間使用的數(shù)據(jù)結(jié)構(gòu)為 EFI_PCI_IO_PROTOCOL_CONFIG_ACCESS,其接口說明如代碼清單 7-5 所示。

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

typedefstruct{EFI_PCI_IO_PROTOCOL_CONFIG Read; //讀數(shù)據(jù)EFI_PCI_IO_PROTOCOL_CONFIG Write; //寫數(shù)據(jù)} 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,  //偏移,在配置空間內(nèi)的偏移地址IN UINTN Count,  //讀寫的個數(shù),以Width為單位INOUTVOID*Buffer//對讀操作,這是目的緩沖區(qū);對寫操作,這是要寫的數(shù)據(jù)緩沖區(qū));

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

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

這些配置空間的信息,都可以通過 EFI_PCI_IO_PROTOCOL 獲取。至于配置空間內(nèi)寄存器的具體含義,讀者可以參考 PCI 標(biāo)準(zhǔn)和 PCIE 標(biāo)準(zhǔn)進(jìn)行深入學(xué)習(xí)。

7.1.3訪問 PCI/PCIE設(shè)備示例

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

使用 EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL,通過 BDF,遍歷所有 PCI/PCIE設(shè)備,打印出設(shè)備的相關(guān)信息。

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

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

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

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

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++){//判斷設(shè)備是否存在Status = PciDevicePresent(gPCIRootBridgeIO,&Pci, (UINT8)k,(UINT8)i,(UINT8)j);if (Status == EFI_SUCCESS)  //找到了設(shè)備{++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;}

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

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

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

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;}

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

可參照 2.1.3 節(jié)的方法,設(shè)置編譯的環(huán)境變量,并使用如下命令編譯程序:

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

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

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

圖 7-6 測試 ListPCIMsg 程序

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

    關(guān)注

    33

    文章

    8669

    瀏覽量

    151551
  • 軟件
    +關(guān)注

    關(guān)注

    69

    文章

    4987

    瀏覽量

    87825
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4809

    瀏覽量

    68826
  • UEFI
    +關(guān)注

    關(guān)注

    0

    文章

    53

    瀏覽量

    11863

原文標(biāo)題:《UEFI編程實踐》選載之訪問 PCI/PCIE 設(shè)備

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

收藏 人收藏

    評論

    相關(guān)推薦

    ARM系統(tǒng)預(yù)引導(dǎo)固件的新機(jī)遇-UEFI

    是作為固件在UEFI規(guī)范的界面層之下提供計算機(jī)系統(tǒng)初始化服務(wù)。PI的實現(xiàn)不是UEFI規(guī)范的必須。  UE
    發(fā)表于 08-23 09:06

    uefi的標(biāo)準(zhǔn)哪里可以下載呀?

    uefi的標(biāo)準(zhǔn)哪里可以下載呀?
    發(fā)表于 11-06 16:49

    UEFI有什么定義?

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

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

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

    Embedded SIG | 樹莓派的UEFI支持和網(wǎng)絡(luò)啟動

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

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

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

    簡化安全、基于 UEFI 的物聯(lián)網(wǎng) 固件更新

    簡化安全、基于 UEFI 的物聯(lián)網(wǎng) 固件更新
    發(fā)表于 09-04 17:22 ?8次下載
    簡化安全、基于 <b class='flag-5'>UEFI</b> 的物聯(lián)網(wǎng) 固件更新

    基于UEFI固件的操作系統(tǒng)完整性度量機(jī)制

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

    基于UEFI固件的攻擊檢測系統(tǒng)的設(shè)計與實現(xiàn)

    計算機(jī)基礎(chǔ)硬件和系統(tǒng)軟件的橋梁。UEFI (Unified Extensible Firmware Interface,統(tǒng)一可擴(kuò)展固件接口)是新的固件標(biāo)準(zhǔn),目前被業(yè)界廣泛使用。UEFI為固件和操作系統(tǒng)之間的
    發(fā)表于 11-30 17:31 ?0次下載
    基于<b class='flag-5'>UEFI</b>固件的攻擊檢測系統(tǒng)的設(shè)計與實現(xiàn)

    uefi 嵌入式Linux,面向嵌入式平臺的高級UEFI開發(fā)環(huán)境.PDF

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

    BIOS+UEFI引導(dǎo)修復(fù)工具

    BIOS+UEFI引導(dǎo)修復(fù)工具
    發(fā)表于 11-19 14:54 ?4次下載

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

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

    P7固件 FCODE BIOS UEFI

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

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

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

    UEFIRC:運行于UEFI環(huán)境的IRC聊天室

    據(jù)悉,開源開發(fā)者Phillip Tennen展示了基于UEFI的沉浸式IRC網(wǎng)絡(luò)聊天室:UREFIRC原型設(shè)計。該設(shè)計無需進(jìn)入操作系統(tǒng),僅在UEFI環(huán)境內(nèi)運行。
    的頭像 發(fā)表于 04-08 16:16 ?609次閱讀
    主站蜘蛛池模板: 国产精品久久久久久久免费| 18性夜影院午夜寂寞影院免费| 国产乱码精品一区二区三区四川人| 国产一区二区三区四卡| 国产色播| 97av在线| 天天拍天天色| 激情综合激情| 好爽好紧好大的免费视频国产| 日本aaaa毛片在线看| 一区二区三区四区免费视频| 五月婷丁香| 欧美色视频日本片免费高清| 很黄很色的网站| 91亚洲免费视频| 亚洲一区中文| 亚洲欧美啪啪| 18满xo影院视频免费体验区| 视频一区视频二区在线观看| 一区二区三区精品国产欧美| 天堂中文字幕在线观看| 你懂的 在线观看| 国产gaysexchina男同men1068| 一本到中文字幕高清不卡在线| 欧美黄色录象| 69xxxxtube日本免费| 一区二区视频在线| 欧美69视频在线| 337p亚洲精品色噜噜狠狠| 久久99精品福利久久久| 一区二区三区视频观看| 午夜肉伦伦影院在线观看| 美女色黄一男一女| 亚洲黄色网址在线观看| 成 人 a v黄 色| 成人国产精品一级毛片了| 色噜噜狠狠色综合久| 国产精品欧美一区二区三区| 欧美成人免费午夜全| 亚洲国产网址| 日本四虎影院|