1 介绍
在UEFI Spec的第七章可以看到,Boot Service中提供了丰富的服务供开发者操作Protocol,本文主要介绍如何使用Protocol。
使用Protocol一般分为三个步骤:
1、通过启动服务找出Protocol对象;
2、使用这个Protocol提供的服务;
3、关闭打开的Protocol。
先介绍一下一些相关的服务,有:HandleProtocol、OpenProtocol、LoacalHandleBuffer和LocateProtocol。
1.1 HandleProtocol
这个服务的作用是查询Handle以确定它是否支持某个Protocol。如果支持,那么在返回时,参数Interface指向一个相应Protocol Interface的指针。然后可以将Interface传递给任何protocol service,以标识请求的上下文。
函数原型为:
typedef
EFI_STATUS
(EFIAPI *EFI_HANDLE_PROTOCOL) (
IN EFI_HANDLE Handle,
IN EFI_GUID *Protocol,
OUT VOID **Interface
);
Note:所有新的applications和drivers推荐使用OpenProtocol()来代替HandleProtocol()。
1.2 OpenProtocol
OpenProtocol用于查询指定的Handle中是否支持指定的Protocol,如果支持,则打开该Protocol,否则返回错误代码。它是HandleProtocol的扩展版本。它与HandleProtocol的区别是,打开protocol interface的代理在EFI内部的handle数据库中被跟踪。UEFI驱动模型使用跟踪功能,也用于确定卸载或重新安装协议接口是否安全。
打开protocol interface的代理由AgentHandle、ControllerHandle和Attributes确定。如果protocol interface可以打开,AgentHandle、ControllerHandle和Attributes将会添加到使用Handle和Protocol指定的protocol interface的代理列表中。
这个函数调用可能返回错误的原因有很多。如果出错就仔细对照Spec吧。
函数原型为:
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.3 LocateHandleBuffer
在池中分配的缓冲区中返回支持请求的protocol的handle数组。这个函数返回一个或多个匹配参数SearchType要求的handle。
函数原型为:
typedef
EFI_STATUS
(EFIAPI *EFI_LOCATE_HANDLE_BUFFER) (
IN EFI_LOCATE_SEARCH_TYPE SearchType,
IN EFI_GUID *Protocol OPTIONAL,
IN VOID *SearchKey OPTIONAL,
OUT UINTN *NoHandles,
OUT EFI_HANDLE **Buffer
);
1.4 LocateProtocol
返回匹配给定protocol的第一个protocol实例。LocateProtocol()函数找到第一个支持Protocol的设备句柄,并在参数interface中返回一个指向protocol interface的指针,如果没有找到协议实例,则参数interface设置为NULL。
函数原型:
typedef
EFI_STATUS
(EFIAPI *EFI_LOCATE_PROTOCOL) (
IN EFI_GUID *Protocol,
IN VOID *Registration OPTIONAL,
OUT VOID **Interface
);
2 使用
以LocateProtocol为例,首先创建一个Module,新建MyUseProtocol.inf和MyUseProtocol.c。
在EmulatorPkg.dsc文件的[Components]下添加EmulatorPkg/Application/MyUseProtocol
/MyUseProtocol.inf。
注意inf文件中的FILE_GUID不能自己瞎编造一个,本来我以为只要满足这个格式就行,但发现编译不通过,可能GUID的生成还是有一些规则的。然后在inf文件的[Protocols]下加上我们需要使用的Protocol的guid。这里我使用的是gEfiDevicePathProtocolGuid。
MyUseProtocol.c文件内容为:
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/UefiApplicationEntryPoint.h>
EFI_STATUS
EFIAPI
UefiMain(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE *SystemTable)
{
EFI_STATUS Status;
VOID *Protocol;
Status = SystemTable->BootServices->LocateProtocol (
&gEfiDevicePathProtocolGuid,
NULL,
(VOID**) &Protocol
);
if (EFI_ERROR(Status))
{
SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Use Fail!\n");
} else {
SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Use Success!\n");
}
return EFI_SUCCESS;
}
编译之后生成efi文件在shell下运行的结果如下:
这里只是简单地使用了LocateProtocol服务,也没有使用匹配到的protocol的服务,如例子中的DevicePathProtocol。当然除了使用已有的protocol,我们也可以创建protocol,这个稍微复杂一点。