UEFI开发探索42 – Protocol的使用1
(請保留-> 作者: 羅冰? ?https://blog.csdn.net/luobing4365)
雖然一直使用各種Protocol來實現需要的程序功能,但對其背后的原理、實現方法,一直都比較模糊。我奉行的是“先用再說”的實用主義,正好周末有點閑暇,探究一下對Protocol理解模糊的地方。
圖1 Protocol的構成
如圖為Protocol的結構圖,摘自于UEFI Spec 2.8 page 45。
我想弄清楚的問題如下:
1) 如何使用Protocol服務?
2) Protocol這種機制在UEFI中是如何實現的?
3) 如何實現一個Protocol?
1?函數學習
與Protocol處理有關的函數,在Spec中給出了列表:
圖2 相關的函數
如果只是使用Protocol,上述的很多函數都不需要關注。簡單描述下需要用到的幾個函數:
1-1 OpenProtocol()
typedef EFI_STATUS (EFIAPI *EFI_OPEN_PROTOCOL) (
IN EFI_HANDLE?Handle, ?//指定要打開此Handle安裝的Protocol接口
IN EFI_GUID?*Protocol,? //要打開的Protocol(指向該Protocol GUID的指針)
OUT VOID?**Interface?OPTIONAL,?//返回打開的Protocol對象,如果沒有則返回NULL
IN EFI_HANDLE?AgentHandle, //打開此Protocol的Image(對UEFI Application)
IN EFI_HANDLE?ControllerHandle,?//如果打開的Protocol是符合UEFI Driver Model的driver,則
??????????????????此參數為控制Protocol接口的控制器,否則為可選的,并且可能為NULL
IN UINT32?Attributes //打開Protocol的參數
);
函數描述:
上述的幾個參數,Handle是UEFI中設備的對象,它是Protocol的提供者。如果Handle的Protocol鏈表中有該Protocol,則Protocol對象的指針將寫到*Interface中(注意,Interface是個指向指針的指針)。
在驅動中調用OpenProtocol(),則ControllerHandle是擁有該驅動的控制器,即請求使用這個Protocol的控制器; AgentHandle是擁有該EFI_DRIVER_BINGDING_PROTOCOL對象的Handle。如果打開的是應用程序(UEFI Application),則AgentHandle是該程序的Handle,即UefiMain函數的第一個參數,ControllerHandle可以忽略。
該函數如果返回錯誤,有很多種可能性。具體可以參考Spec(UEFI spec 2.8 page 187)。
Attributes有6個值可以使用:
#define EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL 0x00000001
#define EFI_OPEN_PROTOCOL_GET_PROTOCOL 0x00000002
#define EFI_OPEN_PROTOCOL_TEST_PROTOCOL 0x00000004
#define EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER 0x00000008
#define EFI_OPEN_PROTOCOL_BY_DRIVER 0x00000010
#define EFI_OPEN_PROTOCOL_EXCLUSIVE 0x00000020
可根據實際情況自由選擇上述參數。
1-2 HandleProtocol()
typedef EFI_STATUS (EFIAPI *EFI_HANDLE_PROTOCOL) (
IN EFI_HANDLE?Handle,?//需要查詢的Handle,看是否支持指定的Protocol
IN EFI_GUID?*Protocol,?//發布的唯一Protocol標志,也即指向有效Protocol GUID的指針
OUT VOID?**Interface //返回待查詢的Protocol
);
函數描述:
它是OpenProtocol()的簡化版,不需要指定AgentHandle、ControllerHandle和Attributes的值(OpenProtocol()中的三個參數)。實際上,在函數內部中還是使用了OpenProtocol(),AgentHandle為gDxeCoreImageHandle,ControllerHandle使用NULL值,Attributes則使用EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL。
1-3 LocateHandleBuffer()
typedef EFI_STATUS (EFIAPI *EFI_LOCATE_HANDLE_BUFFER) (
IN EFI_LOCATE_SEARCH_TYPE?SearchType, //指定哪種handle返回,即設定查找方式
IN EFI_GUID?*Protocol?OPTIONAL,?//指定的Protocol(GUID),此參數只有在SearchType為
????????????????????????????? ByProtocol時才有效
IN VOID?*SearchKey?OPTIONAL,?//依SearchType不同,提供搜索的關鍵字
IN OUT UINTN?*NoHandles, //返回找到的Handle數量
OUT EFI_HANDLE?**Buffer //分配Handle數組并返回
);
函數描述:
此函數返回一個或多個Handle,以匹配SeatchType中規定的請求。參數Buffer所需要的內存,由函數使用EFI_BOOT_SERVICES.AllocatePool()分配,使用完后,調用者必須調用FreePool()將其釋放。
SearchType有三種類型:
AllHandles: 參數Protocol和SearchKey會被忽略,返回系統中的所有Handle;
ByRegisterNotify: 從RegisterProcotolNotify中找出匹配SearchKey的Handle,Protocol參數會被忽略;
ByProtocol: 從系統Handle數據庫中找出支持指定Protocol的Handle,SearchKey參數會被忽略。
1-4 LocateProcotrol()
typedef EFI_STATUS (EFIAPI *EFI_LOCATE_PROTOCOL) (
IN EFI_GUID?*Protocol, //待查詢的Protocol
IN VOID?*Registration?OPTIONAL,?//可選參數,從RegisterPtotocolNotify()獲得注冊Key
OUT VOID?**Interface //返回系統中第一個匹配到的Protocol接口
);
函數描述:
此函數找到第一個支持Protocol的設備Handle,并返回其Protocol接口(Protocol Interface)。調用時不用指定Handle,相比較OpenProtocol()和HandleProtocol(),更為簡便。
1-5 OpenProtocolInformation()
typedef EFI_STATUS (EFIAPI *EFI_OPEN_PROTOCOL_INFORMATION) (
IN EFI_HANDLE?Handle,?//已經查詢到的Protocol的設備句柄
IN EFI_GUID *Protocol,?//待查詢的Protocol(GUID)
OUT EFI_OPEN_PROTOCOL_INFORMATION_ENTRY **EntryBuffer,?//查詢到的信息從此參數返
????????????????????????????????????????????? ??????????回
OUT UINTN *EntryCount??//EntryBuffer數組元素個數
);
函數描述:
此函數用于獲得指定設備上指定Protocol的打開信息。它會為EntryBuffer分配內存,并將返回的信息存入其中,內存的釋放由調用者完成。EntryBuffer的數據結構如下:
typedef struct {
EFI_HANDLE?AgentHandle;
EFI_HANDLE?ControllerHandle;
UINT32?Attributes;
UINT32?OpenCount;
} EFI_OPEN_PROTOCOL_INFORMATION_ENTRY
返回的信息包括AgentHandle、ControllerHandle、打開的屬性和打開個數。很多時候,這個函數獲取的信息用來關閉打開的Protocol。
1-6 CloseProtocol()
typedef EFI_STATUS (EFIAPI *EFI_CLOSE_PROTOCOL) (
IN EFI_HANDLE?Handle, //之前已經打開的Protocol接口的設備句柄,準備關閉
IN EFI_GUID?*Protocol, //發布的唯一Protocol標志,也即指向有效Protocol GUID的指針
IN EFI_HANDLE?AgentHandle, //打開此Protocol的Image(對UEFI Application)
IN EFI_HANDLE?ControllerHandle,?//如果打開的Protocol是符合UEFI Driver Model的driver,則
??????????????????此參數為控制Protocol接口的控制器,否則為可選的,并且可能為NULL
);
函數描述:
Protocol使用完畢后要通過此函數來關閉。通過HandleProtocol()和LocateProtocol()打開的Protocol沒有指定AgentHandle,沒法直接關閉。需要調用OpenProtocolInformation()獲得AgentHandle和ControllerHandle,然后再關閉。
其他的一些函數不一一列舉了,需要的時候再去讀Spec,可在Spec 7.3 Protocol Handler Services中找到相應的內容。
2?使用Protocol
為了熟悉Protocol的使用,寫了一個小程序,用來判斷Protocol是否存在。
實際上,在使用各種Protocol的過程中,其中最重要的一點,就是UEFI下要提供相應的支持。如同之前在Legacy BIOS下的開發一樣,比如鼠標,如果BIOS對鼠標的中斷不支持,那希望在自己的代碼中支持鼠標,工作量太大了。
為了方便顯示,我把之前編寫的漢字顯示代碼(探索系列博客18)移植到了UefiMain為入口的代碼中。期間也遇到點小問題,在系列博客相關的問題集中也描述過,這里不再贅述。
代碼比較簡單。實際上,讀過上面函數的說明,代碼也就不難編寫了。
我主要的目的,對傳入的Protocol GUID進行定位,看系統中是否存在相應的Protocol,以及相關的Handle的數目。
具體來說,以串口為例,也就是找SerialProtocol是否存在,以及存在多少個串口。實際代碼都在本章例子TryProtocol.c的函數ListProtocolMsg()中,如下:
圖3 Protocol信息枚舉
另外,在平常的使用中,我處理Protocol的代碼都在Common.c中。需要注意的是,代碼中的一些細節還沒有處理好,比如內存的釋放、Protocol使用之后的關閉等。
商用化的時候再處理吧。
3?演示
我用上面的函數來檢查串口和隨機數生成器的Protocol,編譯之后,顯示如下:
圖4 程序演示
在TianCore的模擬環境中,有兩個串口、沒有提供隨機數生成器的Protocol,一如測試結果所示。
對于其他函數的使用,網上也有大量的例子可供參考,我也沒精力一個個試驗了。準備進入下一個問題,如何生成Protocol了。
Gitee地址:https://gitee.com/luobing4365/uefi-exolorer
項目代碼位于:/FF RobinPkg/ RobinPkg /Applications/ TryProtocol 下
總結
以上是生活随笔為你收集整理的UEFI开发探索42 – Protocol的使用1的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 计算机日期的格式怎么写,在excel表格
- 下一篇: 视频通信中的码率控制算法