一:以Graphics Output Protocol 为例来讲解如何调用一个Protocol
步骤如下:
1:搜索定位支持特定Protocol的设备,获取Handle。
有很多方式:此处选择gBs->LocateHandleBuffer,获得一个设备指针数组。会自己创建一个Buffer来存储结果,不用咱们操心。
2:打开Prototcol, 将内存中的Driver绑定到给定的ControllerHandler。
此处选择gBs->OpenProtocol,获得目标Protocol的指针。
先放代码!!!代码如下(包含task1.c和task1.inf文件),其中task1.c如下所示:
/*
使用的是EFI_GRAPHICS_OUTPUT_PROTOCAL
*/
#include<Uefi.h>
#include<Library/UefiLib.h>
#include<Library/UefiBootServicesTableLib.h>
EFI_STATUS
EFIAPI
UefiMain(
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
//搜索定位支持特定Protocol的设备,即获取Handle
EFI_STATUS Status = EFI_SUCCESS;
UINTN NoHandles = 0;
EFI_HANDLE *Buffer = NULL;
Status=gBS->LocateHandleBuffer(
ByProtocol,
&gEfiGraphicsOutputProtocolGuid,
NULL,
&NoHandles,
&Buffer
);
Print(L"Status=%d\n",Status);
if(EFI_ERROR(Status))
{
Print(L"Fail to use LocateHandlesBuff\n");
}
Print(L"NoHandles=%d\n",NoHandles);
//打开Protocol,将内存中的Drivers绑定到指定的ControllHandler
EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop;
Status=gBS->OpenProtocol(
Buffer[0],
&gEfiGraphicsOutputProtocolGuid,
(VOID**)&Gop,
ImageHandle,
NULL,
0x00000002 //EFI_OPEN_PROTPCOL_GET_PROTOCOL
);
Print(L"Status=%d\n",Status);
if(EFI_ERROR(Status))
{
Print(L"Fail to use OpenProtocol\n");
}
Print(L"Hello_tangrenhui_test\n");
return EFI_SUCCESS;
}
其中的task1.inf如下:
[Defines]
INF_VERSION = 0x00010005
BASE_NAME =task1
ENTRY_POINT =UefiMain
FILE_GUID = 39B12C61-1C87-4FC3-9375-6EF19FBBA137
VERSION_STRING = 1.0
MODULE_TYPE = UEFI_APPLICATION
[Sources]
task1.c
[Packages]
MdePkg/MdePkg.dec
[LibraryClasses]
UefiApplicationEntryPoint
UefiLib
编译后,在虚拟机运行task1.efi,结果如下:
在写task1.c时,注意事项有以下几点:
1:若要使用BootServers,要取得指向它具体的指针。
有两种方式:第一种是加头文件,第二种是通过UefiMain的第二个参数来访问。分别如下。之后就可以使用gBS这个全局变量来访问BootServers,上述代码使用第一种方式。
#include<Library/UefiBootServicesTableLib.h>
EFI_BOOT_SERVERS *gBS = SystemTable->BootServers;
2:使用LocateHandleBuffer时候,可以参考UEFI_Spec_2_10中相关部分,如下图所示:
如图所示,该Protocol共有五个参数,第一个参数为SearchType,有三种类型。第二个参数为EFI_GUID类型的*Protocol,即&gEfiGraphicsOutputProtocolGuid,第三个参数为*searchKey,由于第一个参数已经确定的原因,此参数为NULL。第一个参数三种类型,如下图所示。第四个参数为UINTN类型的 *BufferSize,定义一个同样类型的变量去接收它。第五个参数为EFI_HANDLE类型的*Buffer,定义一个同样类型的Buffer去接收它。
3:使用OpenProtocol的时候,可以参考如下图:
如图所示,该Protocol共有六个参数,第一个参数为上一步获得的Handle对象,即存在数组中的对象,如果上一步找到Handle,则一定会有一个,因此这里填写Buffer[0]。第二个参数为对应的Protocol的GUID,即&gEfiGraphicsOutputProtocolGuid。第三个参数为void**类型的Interface,目的是为了传出指针可以指向任意对象,因此定义一个变量去接收它,即EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop;同时要进行强制类型转换为(VOID**)。相应部分可参考UEFI_Spec如下图所示:
第四个参数为AgentHandle,就填写ImageHandle,第五个参数为ContrillerHandle,由于是应用程序所以此处写NULL,相应部分可参考下图:
第六个参数为Attributes,相应参考(UEFI_Spec)如下:此处选择0x00000002。
4:每次调用Protocol时候都要去判断是否调用成功,使用if语句判断EFI_ERROR(Status),注意:Status得到入口函数的返回值类型是EFI_STATUS。在UEFI程序中基本所有的返回值类型都是EFI_STATUS。它的本质是无符号长整数最高位为1时其值为错误代码,为0时表示非错误值。通过宏EFI_ERROR(Status)可以判断返回值Status时候为错误代码。若Status为错误代码EFI_ERROR(Status)返回值为真,否则为假。写到这里的时候就可以编译验证一下是否可以调用成功,如果Status返回等于0,表示调用Protocol成功。
5:在打印验证的时候Print中一定要加L,否则会报错。