##1.ImageHandle
每个uefi module都是一个image,而每个image对应都有一个ImageHandle,其实ImageHandle的类型就是EFI_HANDLE
typedef VOID *EFI_HANDLE;
因此实际上每个ImageHandle是一个void*指针,那么也就是说任何结构体都可以传给他,其实通过代码可知在uefi中,传入ImageHandle中的数据结构只会是IHANDLE:
///
/// IHANDLE - contains a list of protocol handles
///
typedef struct {UINTN Signature;/// All handles list of IHANDLELIST_ENTRY AllHandles;/// List of PROTOCOL_INTERFACE's for this handleLIST_ENTRY Protocols; UINTN LocateRequest;/// The Handle Database Key value when this handle was last created or modifiedUINT64 Key;
} IHANDLE;
由此可知,每个ImageHandle其实就对应一个IHANDLE结构,这个结构就是用于描述此image的,我们可以在ImageHandle上安装一系列的接口(就是Protocol),比如在DXE和UEFI驱动中,这一系列接口通过Protocols这个成员连接在一起,它还有AllHandles链表,也就是说系统中所有的IHANDLE也都是连接在一起的,这就会在整个UEFI运行过程中组成一个EFI_HANDLE数据库。
因此在某个模块中实现的接口(Protocol),可以通过一系列的函数,先找到ImageHandle,再通过它找到对应的Protocol,然后在其它的地方调用这些Protocol。UEFI中所有的IHANDLE是通过AllHandles连接在一起的,所有的protocol是通过PROTOCOL_ENTRY结构连接在一起的。
关于ImageHandle和Procotol之间的关系可以参考下图:
##2.ControllerHandle
看了一些书,也在网上搜索了一些Controller的概念,总结了一下我的理解。其实ControllerHandle和上文中介绍的ImageHandle本质上并没有区别,两者都是EFI_HANDLE对象,也就是IHANDLE数据结构。Controller控制器更多的含义应该是逻辑意义上的,它代表着当前IHANDLE对象的父对象,这样对我来说可能更好理解一些,子对象的实现是依赖于父对象之上的,这就更像是linux kernel中,bus总线和device设备之间的关系。
比如说我们系统中要实现一个AC97音频驱动,那么可能会有两个UEFI Driver,一个是PCI驱动,一个是AC97音频驱动提供音频操作的函数,那么我们可以认为PCI驱动是AC97音频驱动的Controller控制器,而AC97音频驱动的实现依赖PCI驱动。
Controller在UEFI中的体现就是函数和参数的命名,比如我们常用到的操作:
gBS->ConnectController()
gBS->DisconnectController()
gBS->ConnectController()这个函数就是用来把我们的驱动按照到父对象上,也就是Controller上,而gBS->DisconnectController()是把我们驱动从对应的Controller上断开。实际进一步跟下去,他们的实现是这样的:
EFI_STATUSEFIAPICoreConnectController (IN EFI_HANDLE ControllerHandle,IN EFI_HANDLE *DriverImageHandle OPTIONAL,IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL,IN BOOLEAN Recursive)
EFI_STATUS
EFIAPI
CoreDisconnectController (IN EFI_HANDLE ControllerHandle,IN EFI_HANDLE DriverImageHandle OPTIONAL,IN EFI_HANDLE ChildHandle OPTIONAL)
举例说明这个函数如何使用,我们可以这样使用:
EFI_STATUS
EFIAPI
BdsLibConnectAllEfi (VOID)
{EFI_STATUS Status;UINTN HandleCount;EFI_HANDLE *HandleBuffer;UINTN Index;Status = gBS->LocateHandleBuffer (AllHandles,NULL,NULL,&HandleCount,&HandleBuffer);if (EFI_ERROR (Status)) {return Status;}for (Index = 0; Index < HandleCount; Index++) {Status = gBS->ConnectController (HandleBuffer[Index], NULL, NULL, TRUE);}if (HandleBuffer != NULL) {FreePool (HandleBuffer);}return EFI_SUCCESS;
}
ControllerHandle和ChildHandle是有依赖的,并且我们知道驱动之前的联系都是通过Procotol来进行了,所以这种依赖也是通过Procotol来进行的。那么我们在Connect和Disconnect的时候其实只需要查找Controller中的Procotol对应的OpenData和attribute私有数据区就可以找到对应的ChildHandle了。
绑定操作的基本原理是利用Driver Binding Protocol,这是UEFI driver中的一个概念了,在ConnectController中,可以不用指定DriverImageHandle和ChildHandle,因为它内部都可以通过Driver Binding Protocol来查找到对应的绑定信息。在开发UEFI driver时,在driver entry加载时都会利用Driver Binding Protocol进行一些操作。
本文图片摘自《UEFI原理与编程》戴正华