我的BIOS之行7-protocol的使用与创建

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;
    }
posted @ 2023-08-15 14:31  King_Alex  阅读(227)  评论(0编辑  收藏  举报