UEFI中的Protocol使用方法

UEFI中的Protocol使用方法

前言

  啓動服務提供了豐富的服務供開發者操作Protocol,我們可以使用Protocol也可以開發Protocol。本文主要介紹如何使用Protocol。
  使用Protocol一般分爲下面三個步驟:
    1. 通過啓動服務找出Protocol對象;
    2. 使用這個Protocol提供的服務;
    3. 關閉打開的Protocol
  可以使用OpenProtocolInformation服務查看打開某個Protocol的所有設備。
  啓動服務,有四種,分別爲:OpenProtocol、HandleProtocol、LocateProtocol和LocalHandleBuffer。下面分別介紹它們。

一、OpenProtocol 服務

  OpenProtocol 用於查詢指定的Handle中是否支持指定的Protocol,如果支持,則打開該Protocol,否則返回錯誤代碼。
  BS(啓動時服務)中的OpenProtocol函數原型:

typedef
EFI_STATUS
(EFIAPI *EFI_OPEN_PROTOCOL) (
    IN EFI_HANDLE Handle,
    IN EFI_GUID *Protocol,
    OUT VOID **Interface ,  OPTIONAL
    IN EFI_HANDLE AgentHandle,
    IN EFI_HANDLE ControllerHandle,
    IN UINT32 Attributes
);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

返回值:

返回值 描述
EFI_SUCCESS The interface information for the specified protocol was returned.
EFI_UNSUPPORTED The device does not support the specified protocol.
EFI_INVALID_PARAMETER Handle is NULL.
EFI_INVALID_PARAMETER Protocol is NULL.
EFI_INVALID_PARAMETER Interface is NULL.
  • Handle:查詢次Handle提供的Protocol
  • *Protocol:要打開的Protocol(指向次Protocol GUID的指針)
  • **Interface:返回打開的Protocol的對象
  • AgentHandle:打開此Protocol的Image
  • ControllerHandle:打開此Protocol的控制器
  • Attributes:打開此Protocol的方式

      Handle是Protocol的提供者,如果Handle的Protocols鏈表中有該Protocol,則Protocol對象的指針寫到*Interface,並返回EFI_SUCCESS,否則返回EFI_UNSUPPORTED。
      如果在驅動中調用OpenProtocol,則ControllerHandle是擁有該驅動的控制器,也就是請求使用這個Protocol的控制器;AgentHandle是擁有該EFI_DRIVER_BINDING_PROTOCOL對象的Handle。EFI_DRIVER_BINDING_PROTOCOL是UEFI驅動開發一定會用到的一個Protocol,它負責驅動的安裝與卸載。
      如果調用OpenProtocol的是應用程序,那麼AgentHandle是該用用程序的Handle,也就是UefiMain的第一個參數,ControllerHandle此時可以忽略。
      Attribute值列表:
     這裏寫圖片描述
    注:截圖來源於UEFI Spec 2_6。關於這部分可以查閱UEFI Spec。

OpenHandle示例程序片斷:

/// 打開BlockIo
EFI_HANDLE ImageHandle;
EFI_DRIVER_BINDING_PROTOCOL *This;
IN EFI_HADNLE ControllerHandle;
extern EFI_GUID BlockIoProtocolGuid;
EFI_BLOCK_IO_PROTOCOL *BlockIo;
EFI_STATUS Status;
//在應用程序中使用OpenProtocol,一般ControllerHandle會設爲NULL
Status = gBS->OpenProtocol(
    ControllerHandle,                     //Handle
    &gEfiBlockIoProtocolGuid,             //Protocol
    &BlockIo,                             //Interface
    ImageHandle,                          //AgentHandle
    NULL,                                 //ControllerHandle
    EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL  //Attribute
)
//在驅動中使用,OpenProtocol, ControllerHandle是控制器的Handle
Status = gBS->OpenProtocol(
    ControllerHandle,                     //Handle
    &gEfiBlockIoProtocolGuid,             //Protocol
    &BlockIo,                             //Interface
    This->DriverBindingHandle,            //AgentHandle
    ControllerHandle,                     //ControllerHandle
    EFI_OPEN_PROTOCOL_GET_PROTOCOL        //Attribute
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

gEfiBlockIoProtocolGuid 是全局變量,變量定義在AutoGen.c 中。AutoGen.c 是由build工具根據 .inf 和 .dec 文件生成的,gEfiBlockIoProtocolGuid是EFI_GUID類型的變量,其EFI_GUID值定義在 .dec中。如果要在應用程序或驅動中使用gEfiBlockIoProtocolGuid,那麼必須在 .inf 文件的[Protocols]中聲明以便預處理時將其包含在AutoGen.c中。

二、HandleProtocol 服務

  OpenProtocol功能比較強大,但是使用比較複雜,需要提供Handle和Protocol的GUID,還要提供AgentHandle、ControllerHandle和Attributes。但是開發者大多時候只是想通過Protocol的GUID得到Protocol對象,並不關心打開方式細節。爲了方便開發者使用Protocol,啓動服務提供了HandleProtocol以簡化打開Protocol。
  HandleProtocol服務的函數原型:

typedef
EFI_STATUS
(EFIAPI *EFI_HANDLE_PROTOCOL) (
    IN EFI_HANDLE Handle,
    IN EFI_GUID *Protocol,
    OUT VOID **Interface
);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
返回值 描述
EFI_SUCCESS The interface information for the specified protocol was returned.
EFI_UNSUPPORTED The device does not support the specified protocol.
EFI_INVALID_PARAMETER Handle is NULL.
EFI_INVALID_PARAMETER Protocol is NULL.
EFI_INVALID_PARAMETER Interface is NULL.

HandleProtocol 是OpenProtocol的簡化版,在調用HandleProtocol時不必再傳入AgentHandle、ControllerHandle和Attribute。它的內部其實仍是調用了OpenProtocol。
  HandleProtocol 的內部實現:

EFI_STATUS
HandleProtocol (
    IN EFI_HANDLE Handle,
    IN EFI_GUID *Protocol,
    OUT VOID **Interface
)
{
    return OpenProtocol (
        Handle,
        Protocol,
        Interface,
        EfiCoreImageHandle,
        NULL,
        EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL
    );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

HandleProtocol使用示例:

Status = gBS->HandleProtocol(
    ControllerHandle,                     //Handle
    &gEfiBlockIoProtocolGuid,             //Protocol
    &BlockIo,                             //Interface   
)
  • 1
  • 2
  • 3
  • 4
  • 5

三、LocateProtocol 服務

  OpenProtocol和HandleProtocol用於打開指定設備上的某個Protocol,要使用這兩個函數,首先要得到這個設備的句柄 。有時開發者並不關心Protocol在哪個設備上,尤其是系統僅有一個該Protocol的實例時。啓動服務提供了LocateProtocol(…)服務,它可以從UEFI內核中找到指定Protocol的第一個實例。
  LocateProtocol服務函數原型:

typedef
EFI_STATUS
(EFIAPI *EFI_LOCATE_PROTOCOL) (
    IN EFI_GUID *Protocol,
    IN VOID *Registration,OPTIONAL
    OUT VOID **Interface
);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
返回值 描述
EFI_SUCCESS A protocol instance matching Protocol was found and returned in Interface.
EFI_INVALID_PARAMETER Interface is NULL.
EFI_NOT_FOUND No protocol instances were found that match Protocol and Registration.

  UEFI內核中某個Protocol的實例可能不止一個,例如,每個硬盤及每個分區都有一個EFI_DISK_IO_PROTOCOL實例。LocateProtocol順序搜索HANDLE鏈表,返回找到的第一個該Protocol的實例。
  下面的例子用於找出系統中第一個SIMPLE_FILE_SYSTEM_PROTOCOL實例。

EFI_SIMPLE_FILE_SYSTE_PROTOCOL* SimpleFs;
gBS->LocateProtocol(
    gEfiSimpleFileSystemProtocolGuid,
    NULL,
    &SimpleFs
);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

四、LocateProtocolBuffer服務

  前面三種是從設備上找出Protocol的方法。有時候開發者需要找出支持某個Protocol的所有設備,例如找出系統中所有安裝了BlockIo的設備。LocateHandle和LocateProtocolBuffer提供這個服務。
  LocateProtocolBuffer函數原型:

typedef
EFI_STATUS
(EFIAPI *EFI_LOCATE_HANDLE_BUFFER) (
    IN EFI_LOCATE_SEARCH_TYPE SearchType, //查找方式
    IN EFI_GUID *Protocol OPTIONAL,       //指定的Protocol
    IN VOID *SearchKey OPTIONAL,          //PROTOCOL_NOTIFY的類型
    IN OUT UINTN *NoHandles,              //返回找到的HANDLE數量
    OUT EFI_HANDLE **Buffer               //分配Handle數組並返回
);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
返回值 描述
EFI_SUCCESS The array of handles was returned.
EFI_NOT_FOUND No handles match the search.
EFI_BUFFER_TOO_SMALL The BufferSize is too small for the result. BufferSize has been updated with the size needed to complete the request.
EFI_INVALID_PARAMETER SearchType is not a member of EFI_LOCATE_SEARCH_TYPE
EFI_INVALID_PARAMETER SearchType is ByRegisterNotify and SearchKey is NULL.
EFI_INVALID_PARAMETER SearchType is ByProtocol and Protocol is NULL.
EFI_INVALID_PARAMETER One or more matches are found and BufferSize is NULL.
EFI_INVALID_PARAMETER BufferSize is large enough for the result and Buffer is NULL.

  SearchType有三種:AllHandles用於找出系統中所有的Handle;ByRegisterNotify用於從RegisterProtocolNotify中找出所有匹配SearchKey的Handle;ByProtocol用於從系統Handle數據庫中找到支持指定Protocol的HANDLE。
  SearchType的類型定義:

typedef enum{
    AllHandles,
    ByRegisterNotify,
    ByProtocol
} EFI_LOCATE_SEARCH_TYPE;
  • 1
  • 2
  • 3
  • 4
  • 5

LocateHandle服務函數原型:

typedef
EFI_STATUS
(EFIAPI *EFI_LOCATE_HANDLE) (
    IN EFI_LOCATE_SEARCH_TYPE SearchType,
    IN EFI_GUID *Protocol OPTIONAL,
    IN VOID *SearchKey OPTIONAL,
    IN OUT UINTN *BufferSize,
    OUT EFI_HANDLE *Buffer
);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
返回值 描述
EFI_SUCCESS The array of handles was returned.
EFI_NOT_FOUND No handles match the search.
EFI_BUFFER_TOO_SMALL The BufferSize is too small for the result. BufferSize has been updated with the size needed to complete the request.
EFI_INVALID_PARAMETER SearchType is not a member of EFI_LOCATE_SEARCH_TYPE
EFI_INVALID_PARAMETER SearchType is ByRegisterNotify and SearchKey is NULL.
EFI_INVALID_PARAMETER SearchType is ByProtocol and Protocol is NULL.
EFI_INVALID_PARAMETER One or more matches are found and BufferSize is NULL.
EFI_INVALID_PARAMETER BufferSize is large enough for the result and Buffer is NULL.

  LocateHandle服務與LocateHandleBuffer服務最大的不同是需要調用者負責管理Buffer數組佔用的內存。

五、其它Protocol 服務

  除了Protocol和根據Protocol找出設備這些常用服務,啓動服務關於使用Protocol的服務還有ProtocolsPerHandle和OpenProtocolInformation。

1. ProtocolsPerHandle

  ProtocolsPerHandle用於獲取指定設備所支持的所有Protocol。這些Protocol的GUID通過ProtocolBuffer返回給調用者,UEFI負責分配內存給ProtocolBuffer,調動者負責釋放該內存。
  
  ProtocolsPerHandle函數原型:

typedef
EFI_STATUS
(EFIAPI *EFI_PROTOCOLS_PER_HANDLE) (
    IN EFI_HANDLE Handle,
    OUT EFI_GUID ***ProtocolBuffer,
    OUT UINTN *ProtocolBufferCount
);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
返回值 描述
EFI_SUCCESS The list of protocol interface GUIDs installed on Handle was returned in ProtocolBuffer. The number of protocol interface GUIDs was returned in ProtocolBufferCount.
EFI_INVALID_PARAMETER Handle is NULL.
EFI_INVALID_PARAMETER ProtocolBuffer is NULL.
EFI_INVALID_PARAMETER ProtocolBufferCount is NULL.
EFI_OUT_OF_RESOURCES There is not enough pool memory to store the results

2. OpenProtocolInformation

  OpenProtocolInformation用於獲得指定設備上指定Protocol的打開信息。

  OpenProtocolInformation函數原型:

typedef
EFI_STATUS
(EFIAPI *EFI_OPEN_PROTOCOL_INFORMATION) (
    IN EFI_HANDLE Handle,
    IN EFI_GUID *Protocol,
    OUT EFI_OPEN_PROTOCOL_INFORMATION_ENTRY **EntryBuffer,
    OUT UINTN *EntryCount
);  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
返回值 描述
EFI_SUCCESS The open protocol information was returned in EntryBuffer,and the number of entries was returned EntryCount.
EFI_NOT_FOUND Handle does not support the protocol specified by Protocol.
EFI_OUT_OF_RESOURCES There are not enough resources available to allocate EntryBuffer.
//EFI_OPEN_PROTOCOL_INFORMATION_ENTRY數據結構
typedef struct {
    EFI_HANDLE AgentHandle;
    EFI_HANDLE ControllerHandle;
    UINT32 Attributes;
    UINT32 OpenCount;
} EFI_OPEN_PROTOCOL_INFORMATION_ENTRY;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

  Handle是這個設備的句柄,打開的信息存放在PROTOCOL_INTERDACE的OpenList列表中。設備上的同一Protocol可能被打開和關閉很多次。Protocol每一次被成功打開和關閉後都會更新OpenList列表。成功打開後,都會在設備句柄上添加一項EFI_OPEN_PROTOCOL_INFORMATION_ENTRY,若OpenList列表中已經存在一項與當前的(AgentHandle,ControllerHandle,Attributes)完全相同,則該項OpenCount加一。關閉Protocol則將OpenList對應項的OpenCount減一,OpenCount爲零時刪除對應項。

六、CloseProtocol 服務

  Protocol使用完畢之後需要通過CloseProtocol關閉打開的Protocol。
  CloseProtocol函數原型:

typedef
EFI_STATUS
(EFIAPI *EFI_CLOSE_PROTOCOL) (
    IN EFI_HANDLE Handle,
    IN EFI_GUID *Protocol,
    IN EFI_HANDLE AgentHandle,
    IN EFI_HANDLE ControllerHandle
);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
返回值 描述
EFI_SUCCESS The protocol instance was closed.
EFI_INVALID_PARAMETER Handle is NULL.
EFI_INVALID_PARAMETER AgentHandle is NULL.
EFI_INVALID_PARAMETER ControllerHandle is not NULL and ControllerHandle is NULL.
EFI_INVALID_PARAMETER Protocol is NULL.
EFI_NOT_FOUND Handle does not support the protocol specified by Protocol.
EFI_NOT_FOUND The protocol interface specified by Handle and Protocol is not currently open by AgentHandle and ControllerHandle.

  通過HandleProtocol和LocateProtocol打開的Protocol因爲沒有指定AgentHandle,所以無法關閉。如果一定要關閉它,則要調用OpenProtocolInformation()獲得AgentHandle和ControllerHandle,然後再關閉它。

七、總結

  本部分,不住要講解了Protocol的使用方法。Protocol提供了一種在UEFI應用程序以及UEFI驅動之間的通信方式。通過Protocol,用戶可以使用驅動提供的服務,以及系統提供的其他服務。要熟悉自己使用的Protocol的數據結構。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章