Protocol 在 UEFI 内核中的表示
typedef VOID *EFI_HANDLE;
EFI_HANDLE
是指向某种对象的指针,UEFI 用它来表示某个对象。
UEFI 扫描总线后,会为每个设备建立一个 Controller
对象,用于控制设备,所有该设备的驱动以 Protocol
的形式安装到这个 Controller
中,这个 Controller
就是一个 EFI_HANDLE
对象。当我们将-个.ef
文件加载到内存中时,UEFI 也会为该文件建立一个Image
对象,这个 Image 对象也是一个EFI_HANDLE
对象。在 UEFI内部,EFI_HANDLE
被理解为IHANDLE
。
每个 IHANDLE
中都有一个Protocols
链表,存放属于自己的 Protocol
。所有的IHANDLE
通过AllHandles
链接起来。
IHANDLE
的Protocols
是一个双向链表,链表中每一个元素是PROTOCOL_INTERFACE
,通过PROTOCOL_INTERFACE
的Protocol
指针可以得到这个Protocol
的GUID
,通过Interface
指针可以得到这个Protocol
的实例。
使用 Protocol 服务
启动服务提供了丰富的服务供开发者操作 Protocol
,操作分两种,一种是使用 Protocol
另一种是产生 Protocol
。
要使用 Protocol
,首先要根据 GUID
找到 Protocol
对象,启动服务提供了 OpenProtocol(...)
、HandleProtocl(...)
和LocateProtocol(...)
三种服务用于找出指定的 Protocol
。
OpenProtocol
用于打开指定句柄上的 Protocol
;HandleProtocol
是 OpenProtocol
的简化版;
LocateProtocol
用于找出指定的 Potocol
在系统中的第一个实例。
使用完 Protocol
后还要关闭打开的 Protocol
,否则可能会造成内存泄漏。
除了打开和关闭 Protocol
,有时还需要找出能支持某个 Protocol
的所有设备,这时就要用到启动服务提供的LocalHandleBuffer
服务。
使用 Protocol 的三个步骤:
1)通过 gBS->OpenProtocol
(或 HandleProtocol
、LocateProtocol
)找出 Protocol
对象;
2)使用这个 Protocol
提供的服务;
3)通过 gBS->CloseProtocol
关闭打开的 Protocol
。
如果想知道某个 Protocol
被哪些设备打开了,那么可以使用OpenProtocolInformation
服务。
OpenProtocol 服务
OpenProtocol
用于查询指定的Handle
中是否支持指定的Protocol
,如果支持,则打开该Protocol
,否则返回错误代码。
Status = gBS->OpenProtocol(…)
Handle
是 Protocol
的提供者,如果 Handle
的 Protocols
链表中有该 Protocol
,则 Protocol
对象的指针写到 *Interface
,并返回EFI_SUCCESS
;否则返回EFI_UNSUPPORTED
。
如果在驱动中调用 OpenProtocol()
,则 ControllerHandle
是拥有该驱动的控制器,也就是请求使用这个 Protocol
的控制器;AgentHandle
是拥有该EFI_DRIVER_BINDING_PROTOCOL
对象的 HandIe
。
EFI_DRIVER_BINDING_PROTOCOL
是UEFI
驱动开发中一定会用到的一个Protocol
,它负责驱动的安装与卸载。
如果调用 OpenProtocol
的是应用程序,那么AgentHandle
是该应用程序的 Handle
,也就是 UefiMain
函数的第一个参数,ControllerHandle
此时可以忽略。
Attributes
可以取以下 6 种值:
HandleProtocol 服务
OpenProtocol
功能很强大,但使用起来也比较复杂,除了要提供Handle
和Protocol
的GUID
,还要提供 AgentHandle
、ControllerHandle
和Attributes
。
大部分情况下,开发者只是想通过 Protocol
的 GUID
得到 Protocol
对象,并不关心打开方式的细节。
HandleProtocol
是OpenProtocol
的简化版,在调用HandleProtocol
时不必再传入AgentHandle
、ControllerHandle
和 Attributes
的值。
AgentHandle
使用gDxeCorelmageHandle
,ControllerHandle
使用NULL
值,Attributes
则使用EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL
LocateProtocol 服务
LocateProtocol(...)
服务,它可以从 UEFI 内核中找出指定 Protocol
的第一个实例。
UEFI 内核中某个 Protocol
的实例可能不止一个。LocateProtocol
顺序搜索 HANDLE
链表,返回找到的第一个该 Protocol
的实例。
LocateHandleBuffer 服务
有时候需要找出支持某个 Protocol
的所有设备,例如找出系统中的所有安装了Blocklo
的设备,LocateHandleBuffer
和LocateHandle
服务提供了这个功能。
SearchType
有三种:
AllHandles
用于找出系统中的所有Handle
;
ByRegisterNotify
用于从 RegisterProtocolNotify
中找出匹配 SearchKey
的Handle
;
ByProtocol
用于从系统 Handle
数据库中找出支持指定Protocol
的HANDLE
。
LocateHandle 服务与 LocateHandleBuffer的最大区别是需有调用者负责管理 Buffer 数组占用的内存。
其他 Protocol 服务
-
ProtocolsPerHandle
用于获得指定设备所支持的所有Protocol
。这些Protocol
的GUID
通过ProtocolBuffer
返回给调用者,UEFI 负责分配内存给ProtocolBuffer
,调用者负责释放该内存; -
OpenProtocollnformation
用于获得指定设备上指定Protocol
的打开信息; -
Protocol
使用完毕后要通过CloseProtocol
关闭打开的Protocol
。- 通过
HandleProtocol
和LocateProtocol
打开的Protocol
因为没有指定 AgentHandle,所以无法关闭。如果一定要关闭它,则要调用OpenProtocolInformation()
获得AgentHandle
和ControllerHandle
,然后关闭它。
- 通过
总结
内容来自于《UEFI 原理与编程》。。。