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
);
返回值:
返回值 | 描述 |
---|---|
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
)
- ProtocolGuid 是全局变量,变量定义在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
);
返回值 | 描述 |
---|---|
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
);
}
HandleProtocol使用示例:
Status = gBS->HandleProtocol(
ControllerHandle, //Handle
&gEfiBlockIoProtocolGuid, //Protocol
&BlockIo, //Interface
)
三、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
);
返回值 | 描述 |
---|---|
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
);
四、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数组并返回
);
返回值 | 描述 |
---|---|
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;
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
);
返回值 | 描述 |
---|---|
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
);
返回值 | 描述 |
---|---|
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
);
返回值 | 描述 |
---|---|
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;
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
);
返回值 | 描述 |
---|---|
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的数据结构。