我的BIOS之行(9)-protocol的使用与创建

时间:2022-08-01 05:07:15

protocol简介

从语言上来看,protocol包含了属性和函数指针的结构体,从功能上看,protocoll是提供者与使用者对服务方式的一种约定。其实我们不难看出UEFI中的Protocol引入了面向对象的思想。

每一个protocol都必须要有一个唯一的GUID。如我的github上面的code一样,在github上我每章都有对应的提交,你可以通过我的提交看到我所完成的内容。如下就是我所定义的protocol的GUID。

#define EFI_HOMEWORKINTERFACE_PROTOCOL_GUID \
{ \
0x47590bea, 0x6178, 0x498d, {0xa9, 0x5, 0x3c, 0xe6, 0x63, 0xc3, 0x84, 0xd9} \
}

一般的protocol的服务会提供2种操作,一个是使用protocol,一个是产生protocol。这一篇均有介绍,一般来讲的话,产生protocol(相当于创建服务)与操作protocol(使用客户端)不在一个module里面的,我在这边为了讲解将所有的功能都集中到了同一个module中。

该文module简介

目前是创建protocol后并注册,以激活上一文讲到的event事件,通过callback进行调用protocol,并使用它的interface,其中一个会调用hob来读取setup的item值,后面一个是通过pcie读取相关device的device id并将值回填给setup的item,后面一个暂时还未实现。

protocol的使用

一般使用protocol有3个步骤
* 通过openprotocol(handleProtocol/LocateProtocol)找到protocol对象
* 使用这个protocol提供的服务
* 通过CloseProtocol关闭打开的protocol

在这里我就不细讲各个protocol的参数什么的了,这个完全可以自己到spec上去看。

一般有
* OpenProtocol(查询指定的Handle中是否支持指定的Protocol)
* HandleProtocol(由于OpenProtocol实在是用起来复杂,有时根本不要关心太对细节,所以有了这个接口)
* LocateProtocol(Open/Handle都是用来打开指定的protocol,但有时我们并不关心protocol在什么地方的时候就可以用这个函数了)
* CloseProtocol(用来关闭protocol)

实例

一般LocateProtocol找到了第一个实例后,因为没有相关的handle所以是无法关闭的,当然如果一定要关闭的话就需要用OpenProtocolInterface来获取handle来关闭它

在我写的code中

  EFI_HOMEWORKINTERFACE_PROTOCOL         *HomeworkProtocolinterface;
Status = pBS->LocateProtocol(&gEfiHomeWorkProtocolGuid,
NULL,
&HomeworkProtocolinterface );
if (!EFI_ERROR(Status))
{
Status = HomeworkProtocolinterface->HomeWorkHobread(1);
Status = HomeworkProtocolinterface->HomeWorkPciread(0,0,0);
}

我们可以看到通过LocateProtocol获取protocol后用它自己的protocol

在这里注意,一定要自己申请一个protocol的interface,不然会hang.原因是我在这边创建的protocol,可以直接用宏HomeworkProtocol,这个将会造成系统的误操作。

protocol的创造与注册

上面我们说明了咋么使用,在告诉你咋么用后,应该告诉你咋么创造。

设计接口

前面我就创建了HomeworkDxe.h一直没用,在此时就派上用处了。

typedef EFI_STATUS (* HOMEWORK_HOB_READ) (
IN UINT8 device

);
typedef EFI_STATUS (* HOMEWORK_PCI_READ) (
IN UINT8 busnum,
IN UINT8 devicenum,
IN UINT8 funcnum
);
struct _EFI_HOMEWORK_PROTOCOL{
HOMEWORK_HOB_READ HomeWorkHobread;
HOMEWORK_PCI_READ HomeWorkPciread;
};
typedef struct _EFI_HOMEWORK_PROTOCOL EFI_HOMEWORKINTERFACE_PROTOCOL;

注意这边EDK2是有命名规范的,Protocol结构体名字都是要在前面加“_”,所以这个接口为_EFI_HOMEWORK_PROTOCOL,然后它里面有哪些函数。

最好呢再重命名一下,那么我们再调用的时候会更加清晰

建立GUID

不要忘记将它extern出去,不然别人找不到你。

extern EFI_GUID gEfiHomeWorkProtocolGuid;
#define EFI_HOMEWORKINTERFACE_PROTOCOL_GUID \
{ \
0x47590bea, 0x6178, 0x498d, {0xa9, 0x5, 0x3c, 0xe6, 0x63, 0xc3, 0x84, 0xd9} \
}
EFI_GUID gEfiHomeWorkProtocolGuid = EFI_HOMEWORKINTERFACE_PROTOCOL_GUID;

Protocol服务的实现

此时你需要将上面写的接口进行实现

EFI_STATUS HomeWorkHobread(
IN UINT8 device
)
{
EFI_GUID GuidHob = HOB_LIST_GUID;
HOMEWORK_HOB *Homeworkhob=NULL;
EFI_GUID HomeworkHobGuid=HOMEWORK_HOB_GUID;
VOID *pHobList = NULL;
EFI_STATUS Status;
pHobList = GetEfiConfigurationTable(pST, &GuidHob);
if (!pHobList) return EFI_UNSUPPORTED;

Homeworkhob = (HOMEWORK_HOB*)pHobList;
while (!EFI_ERROR(Status = FindNextHobByType(EFI_HOB_TYPE_GUID_EXTENSION, &Homeworkhob)))
{
if (guidcmp(&(*Homeworkhob).EfiHobGuidType.Name, &HomeworkHobGuid) == 0)
break;
}
if (!EFI_ERROR(Status)){
OEM_TRACE("device=%d,data=%d\n",device,Homeworkhob->homeworkdata);
}
return Status;
}
EFI_STATUS HomeWorkPciread(
IN UINT8 busnum,
IN UINT8 devicenum,
IN UINT8 funcnum
)
{
OEM_TRACE("bus=%d,dev=%d,func=%d\n",busnum,devicenum,funcnum);
return EFI_SUCCESS;
}
EFI_HOMEWORKINTERFACE_PROTOCOL HomeworkProtocol = {
HomeWorkHobread,
HomeWorkPciread,
};

注册protocol

将protocol与event事件进行捆绑注册

 Status = pBS->RegisterProtocolNotify (
&gEfiHomeWorkProtocolGuid,
homeworkevent,
&Registration
);
if (EFI_ERROR(Status))
{
return Status;
}

用InstallProtocolInterface触发event,然后就看上面是咋么用的就可以了

 Status = pBS->InstallProtocolInterface(
&ImageHandle,
&gEfiHomeWorkProtocolGuid,
EFI_NATIVE_INTERFACE,
&HomeworkProtocol
);
if (EFI_ERROR(Status))
{
return Status;
}