【技术】UEFI: Protocol浅谈

转自:http://blog.csdn.net/chris_leeyc/article/details/47088177

一、Protocol是什么

Protocol是服务器端和客户端之间的一种约定,在软件编程上称为接口,服务器端和客户端通过这个约定信息的互通。服务器端和客户端在UEFI中都是可执行的二进制文件,为了实现这些二进制文件之间的互通,C/S双方共同作出的让步,使用Protocol进行双方的交互。

二、Protocol是干什么的,为什么要有Protocol

UEFI的中文含义是“可扩展固件接口”,所谓可扩展的含义就是可以在系统完成后(编译为binary)之后,再次为系统增加新的功能,而不用重新rebuild整个系统。为了支持不同二进制组件运行时相互通信,不同组件可以相互调用之间的功能,同时各个组件相互之间调用时的统一编程接口以便方便组件厂商对于组件的开发等要求。Protocol用以实现这些功能的同时满足服务器端和客户端之间的通信。而通信至少要具备以下几种功能:

(1)互操作。A组件自由可以调用B组件实现的函数。反过来也一样。

(2)数据传递。双方可以通过某种方法(sharedmemory, pipes and etc)互相交换数据。

(3)可探测。某组件必须具备探测另一个组件是否存在的能力。

(4)组件的开发必须是独立的。开发A组件不需要B组件的源代码,反之亦然。

三、Protocol的组成

Protocol其实就是一个大的结构类型,它包含有很多数据信息。最主要的是GUID,每一个Protocol都有一个定义好的、唯一的标识符,这个标识符用GUID来表达。这样程序员可以方便的通过指明不同的GUID来得到不同的Protocol。其次是一个指向GUID的全局指针变量,比如EFI_BLOCK_IO_PROTOCOL,就有一个gEfiBlockIoProtocolGuid的全局指针,他是EFI_GUID *结构的。这样做事实上完全是为了方便,他的变量名的命名规则是统一的,即g + Efi + Protocol 名称 + ProtocolGuid。这样一来,程序员就不需记住相应GUID具体的值,而只在需要GUID作为参数的时候,使用这个全局变量就行了。如果要在应用程序或者驱动中使用这个GUID(如gEfiBlockIoProtocolGuid),那么必须要在.inf文件的[Protocols]中什么以便预处理时将其包含在生成的AutoGen.c中供全局使用。以下是以块设备的Protocol为例描述protocol的数据结构中的成员变量,通过这个Protocol可以控制块设备。

struct _EFI_BLOCK_IO_PROTOCOL {
 UINT64              Revision;  //必须保证向后兼容的Protocol版本号,若木有向后兼容的话就必须给未来的版本号定义一个新的GUID,相当于定义了一个新的Protocol
  EFI_BLOCK_IO_MEDIA  *Media; //指针指向这个设备
  EFI_BLOCK_RESET     Reset;  //重置复位信号
  EFI_BLOCK_READ      ReadBlocks;  //读Protocol服务
  EFI_BLOCK_WRITE     WriteBlocks;  //写Protocol服务
  EFI_BLOCK_FLUSH     FlushBlocks;  //清除缓存服务
};
extern EFI_GUID gEfiBlockIoProtocolGuid;  //导出该Protocol

四、Protocol在内核中的表示

了解Protocol在什么位置之前我们要先了解下EFI_HANDLE。

typedef    VOID      *EFI_HANDLE;  //相关接口的一个集合

这个EFI_HANDLE是指向某种对象的指针,UEFI用其来表示某个对象的。UEFI通过扫描总线,为每一个设备建立一个Controller对象,用于控制各个设备,所有该设备的驱动都是以Protocol的形式安装在这个Controller中的,这个Controller就是一个EFI_HANDLE指针指向的对象。 当我们将一个.efi文件加载到内存中,UEFI也会为该文件建立一个Image对象,这个Image对象也是一个EFI_HANDLE对象。 在UEFI内部,EFI_HANDLE被理解为IHANDLE,这个IHANDLE包含了Protocols的链表,存放属于自己的Protocol,然后ALLHANDLE将所有的IHANDLE连接起来。

五、Protocol服务的使用

要使用Protocol服务,首先要根据GUID找到Protocol对象,Boot Service中提供了几种Protocol服务,如下表所示:

表1 Boot Service中提供的Protocol服务

OpenProtocol

打开Protocol

HandleProtocol

打开Protocol,PenProtocol的简化版本

LocateProtocol

找出系统中指定Protocol的第一个实例

LocateHandleBuffer

找出支持指定Protocol的左右Handle。系统负责分配内存,调用者负责释放内存

LocateHandle

找出支持指定Protocol的左右Handle,调用者负责分配和释放内存

OpenProtocolInformation

返回指定Protocol的打开信息

ProtocolsPerHandle

找出指定Handle上安装的所有Protocol

CloseProtocol

关闭Protocol

介绍完了上述几种Protocol服务,接下来我们了解一下使用Protocol服务的几个操作步骤,使用protocol服务的一般有以下三步:

第一步:通过gBS->OpenProtocol(或者HandleProtocol、LocateProtocol)找出Protocol的对象。

第二步:使用这个Protocol提供的服务。

第三步:通过gBS->CloseProtocol关闭打开的Protocol。

1、OpenProtocol服务

OpenProtocol用于查询指定的Handle中是否支持指定的Protocol,如果支持的话就打开,不支持的话就返回错误的代码。OpenProtocol服务的函数原型如下所示:

EFI_STATUS (EFIAPI*EFI_OPEN_PROTOCOL)(

  IN  EFI_HANDLE       Handle,  //指定的Handle,将查询和打开此Handle中安装的Protocol

  IN  EFI_GUID           *Protocol,  //这个是要打开的Protocol对象(指向Protocol GUID)

  OUT VOID               **Interface, OPTIONAL,  //返回打开的Protocol对象

  IN  EFI_HANDLE       AgentHandle,  //打开此Protocol的Image

  IN  EFI_HANDLE       ControllerHandle,  //使用此Protocol的控制器

  IN  UINT32              Attributes //打开Protocol的方式

);

Handle是Protocol的提供者,Handle是Protocol的提供者,如果Handle的Protocols链表中有该Potocol,Protocol对象的指针写到*Interface,并返回EFI_SUCCESS;否则 返回EFI_UNSUPPORTED。
    如果在驱动中调用OpenProtocol(),就是请求使用Protocol的控制器ControllerHandle。AgentHandle是拥有负责驱动安装和卸载的EFI_DRIVER_BINDING_PROTOCOL对象的Handle。如果调用OpenProtocol的是应用程序,那么AgentHandle是该应用对应的Handle,也就main函数的第一个参数,ControllerHandle此时可以忽略。

对于Attributes可以取以下5种值,即有五种打开Protocol的方式:

#define EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL  0x00000001
#define EFI_OPEN_PROTOCOL_GET_PROTOCOL            0x00000002
#define EFI_OPEN_PROTOCOL_TEST_PROTOCOL           0x00000004
#define EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER   0x00000008
#define EFI_OPEN_PROTOCOL_BY_DRIVER                  0x00000010  //若已经打开,则被同一控制器再次打开的时候将会失败
#define EFI_OPEN_PROTOCOL_EXCLUSIVE                  0x00000020  //若Protocol已经打开,则再次打开就会失败

2、HandleProtocol服务

这也是一个打开Protocol的服务,只是这是一个简化版本的OpenProtocol,它只含有三个参数,木有提供AgentHandle、ControllerHandle和Attributes,在调用的时候AgentHandle使用gDxeCoreImageHandle,ControllerHandle使用NULL值,Attribute使用EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL。以下是HandleProtocol的服务的函数原型:

EFI_STATUS EFIAPI CoreHandleProtocol (
  IN EFI_HANDLE       UserHandle,//查询该Handle是否支持Protocol
  IN EFI_GUID         *Protocol,  //带查询的Protocol
  OUT VOID            **Interface  //返回带查询的Protocol
  )

3、LocateProtocol服务

这个服务是从内核中找出指定Protocol的第一个实例,它会按顺序搜索HANDLE链表,返回找到的第一个该Protocol的实例。以下是LocateProtocol的服务的函数原型:

Typedef EFI_STATUS LocateProtocol (

IN EFI_GUID *Protocol,  //带查询的Protocol
IN VOID       *Registration OPTIONAL, //可选参数,从RegisterProtocolNotify中获得的Key
OUT VOID     **Interface  //返回系统中第一个匹配到的Protocol实例
);

4、LocateHandleBuffer服务

这个服务的功能是找出支持某个Protocol的所有设备。该服务的函数原型如下:

typedef  EFI_STATUS LocateHandleBuffer (
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数组并返回
);

上述的SearchType有三种:AllHandles(查找系统中所有HANDLE), ByRegisterNotify(从RegisterProtocolNotify中找出匹配SearchKey的Handle), ByProtocol(从系统Handle数据库中查找支持指定Protocol的HANDLE)。NoHandles是找到的HANDLE的数量, Buffer数组由UEFI复杂分配,由用户负责释放。

5、LocateHandle服务

这个服务的功能是找出支持某个Protocol的所有设备,它的作用于上述的LocateHandleBuffer提供的服务是一样的,两者之间最大的区别在于LocateHandle需有调用者负责管理Buffer数组占用的内存。该服务的函数原型如下:

typedef  EFI_STATUS LocateHandleBuffer (
IN EFI_LOCATE_SEARCH_TYPE SearchType, //查找方法
IN EFI_GUID                        *Protocol OPTIONAL,  //指定的Protocol
IN VOID *SearchKey OPTIONAL, //PROTOCOL_NOTIFY类型
IN OUT UINTN                      *Buffersize, //返回找到的Handle的Buffer大小
OUT EFI_HANDLE                  **Buffer  //返回找到的Handle
);

6、其他使用Protocol的服务

(1)、ProtocolsPerHandle服务

这个服务是用于获得指定设备所支持的所有Protocol。这些Protocol的GUID通过ProtocolBuffer返回给调用者,UEFI负责分配内存给ProtocolBuffer,调用者负责释放该内存。其函数原型如下:

Typedef EFI_STATUS ProtocolsPerHandle (
IN EFI_HANDLE Handle,  //找出这个Handle上所有的Protocol
OUT EFI_GUID  ***ProtocolBuffer,  //返回Protocol GUID的数组
OUT UINTN      *ProtocolBufferCount  //返回Protocol的数目
);

(2)、OpenProtocolInformation服务

这个服务用于获得指定设备上指定Protocol的打开信息,其函数原型如下:

Typedef  EFI_STATUS(EFIAPI *EFI_OPEN_PROTOCOL_INFORMATION) (
IN EFI_HANDLE Handle,  //设备句柄
IN EFI_GUID    *Protocol,  //带查询的protocol
OUT EFI_OPEN_PROTOCOL_INFORMATION_ENTRY **EntryBuffer,  //打开信息通过此数组返回
OUT UINTN     *EntryCount //EntryBuffer数组元素个数
);

7、Close Protocol服务

这个服务的作用是关闭打开的Protocol。通过HandleProtocol和LocateHandle打开的Protocol因为没有指定AgentHandle,所以无法关闭。若一定要关闭,则要调用OpenProtocolInformation获得AgentHandle和ControllerHandle,然后才能进行关闭。它的函数原型如下:

Typedef  EFI_STATUS(EFIAPI *EFI_CLOSE_PROTOCOL) (
IN EFI_HANDLE Handle,  //设备句柄
IN EFI_GUID *Protocol,  //这个是要关闭的Protocol对象
IN EFI_HANDLE AgentHandle,  //关闭此Protocol的Image
IN EFI_HANDLE ControllerHandle //使用此protocol的控制器
);


  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值