UEFI原理与编程(3)protocol介绍

UEFI中的protocol

简介

1.我们先看protocol的翻译,protocol:礼仪;外交礼节;条约草案;议定书;(协议或条约的)附件;(数据传递的)协议,规程,规约。我把它理解为协议,作为服务端和客户端约定好的协议,服务端和客户端按照协议互通信息,使用服务的是客户端,提供服务的是服务端。

UEFI的英文翻译过来应该叫“可扩展固件接口”。这个名词中最重要的事实上是“可扩展”这三个字。为了两个参与操作的进程之间的操作以及代码的保密性,类似于做交易的双方,为了代码的安全,双方约定好交易地点时间(接口),双方遵循这个协议进行交易。而这种事先的约定的接口就是protocol的定义。而这种定义尤以.h文件的形式加以实现。

2.在语言方面c语言作为一门面向过程的语言为了也能使用对象这个概念,用struct来模拟class,成员变量存入函数指针模拟成员函数,此函数的第一参数必须指向Protocol的指针,用来模拟this指针。

3.Protocol不是UEFI BIOS一开始就可以用的。我们知道UEFI BIOS启动时分为不同的阶段,SEC-PEI-DXE-BDS等等。而Protocol需要等到DXE阶段才可以使用(不需要特别在意DXE阶段的哪个点开始,基本上我们开发时写的DXE模块都可以使用)

4.GUID是唯一的id,一个唯一的标识符,方便使用这个唯一的标识符操控对应的protocol。如果要在应用程序或者驱动中使用这个GUID(如gEfiBlockIoProtocolGuid),那么必须要在.inf文件的[Protocols]中什么以便预处理时将其包含在生成的AutoGen.c中供全局使用。
protocol结构

以下是以块设备的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

5.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连接起来。

DECLARE_HANDLE(HDC);
展开
#define DECLARE_HANDLE(name) struct name##__ { int unused; };

用HDC替换上面的name来看看,我们就得到了下面的一段代码:

struct HDC__

{

int unused;

};

typedef struct HDC__ *HDC
typedef struct name##__ *name

handle与遥控器类似,可以通过遥控器操控换台,但是内部的结构怎么做到的没有权限接触,不会通过遥控器伤害或者了解电视内部的结构。

HANDLE不过是一个指向void型,即无类型的指针,嗯,目前的指针是32位的吧.其实也不能说HANDLE是一种指针,它只充当一种索引的作用.HANDLE是固定的,它不会变,但是对象的地址会变,当对象在内存中的位置发生改变后,我们不能通过之前的对象指针找到对象,怎么办呢?嗯,这时候HANDLE的用处就出来了,HANDLE就是用来记录对象的最新地址的。 在UEFI内部,EFI_HANDLE被理解为IHANDLE,这个IHANDLE包含了Protocols的链表,存放属于自己的Protocol,然后ALLHANDLE将所有的IHANDLE连接起来。

Windows是一个以虚拟内存为基础的操作系统。在这种系统环境下,Windows内存管理器经常在内存中来回移动对象,依此来满足各种应用程序的内存需要。对象被移动之后,我们还可以通过指针来操作对象吗?显然是不能的,为此我们使用一个第三方变量,既然地址不是唯一不变的,我们将这个变动的值赋值给一个变量,当地址变动,变量的值也相应改变,所以我们只需要调动这个变量即可。Windows内存管理器在移动对象在内存中的位置后,把对象新的地址告知这个句柄地址来保存。这样我们只需记住这个句柄地址就可以间接地知道对象具体在内存中的哪个位置。这个地址是在对象装载(Load)时由系统分配给的,当系统卸载时(Unload)又释放给系统。句柄地址(稳定)→记载着对象在内存中的地址→对象在内存中的地址(不稳定)→实际对象。但是,必须注意的是程序每次从新启动,系统不能保证分配给这个程序的句柄还是原来的那个句柄,而且绝大多数情况的确不一样的。

使用步骤

使用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此时可以忽略.

2.HandleProtocol服务

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

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

3、LocateProtocol服务

Typedef EFI_STATUS LocateProtocol (

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

这个服务是从内核中找出指定Protocol的第一个实例,它会按顺序搜索HANDLE链表,返回找到的第一个该Protocol的实例。以下是LocateProtocol的服务的函数原型:
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的控制器
);
根据提供的引用内容,可以得知UEFI编程实践的相关PDF资料是: 2. UEFI原理编程.pdf 3. UEFI Spec 2.8B May 2020.pdf 4. PI_Spec_1_7_A_final_May1.pdf 6. edk-ii-dec-specification.pdf 7. edk-ii-dsc-specification.pdf 8. Driver Writer’s Guide.pdf 可以参考这些资料来了解UEFI编程的实践方面的知识。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [2020-UEFI docs packages-all.7z](https://download.youkuaiyun.com/download/xiaopangzi313/12571477)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [UEFI应用编程--Handle&EFI_DEVICE_PATH_TO_TEXT_PROTOCOL](https://blog.youkuaiyun.com/qyqcs/article/details/79497079)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [UEFI开发之应用开发](https://blog.youkuaiyun.com/u014492377/article/details/43570859)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值