1. 数据结构组织
Protocol主要的数据结构的组织如图所示,DXE CORE中对于Protocol的实现无非就是操作这些数据结构。
- mProtocolDatabase是一个链表用于记录PROTOCOL_ENTRY。
LIST_ENTRY mProtocolDatabase = INITIALIZE_LIST_HEAD_VARIABLE (mProtocolDatabase);
- PROTOCOL_ENTRY用于链接具体的Protocol实现和mProtocolDatabase,用于描述一个Protocol的节点。内部成员AllEntries用于链接mProtocolDatabase链表;Protocols是一个链表,用于链接PROTOCOL_ENTRY。
typedef struct {
UINTN Signature;
LIST_ENTRY AllEntries;
EFI_GUID ProtocolID;
LIST_ENTRY Protocols;
LIST_ENTRY Notify;
} PROTOCOL_ENTRY;
- PROTOCOL_INTERFACE是具体的Protocol对象,其中包含多个成员:ByProtocol用于链接PROTOCOL_ENTRY的Protocols链表;Protocol指向其对应的PROTOCOL_ENTRY;Interface就是具体实现的函数指针集;Handle指定了该Protocol所对应的Image Handle对象;Link用于链接在Protocol对应的IHANDLE的Protocols链表,一个Image可以有多个Protocol所以IHANDLE的Protocols是一个链表。
typedef struct {
UINTN Signature;
LIST_ENTRY Link;
IHANDLE *Handle;
LIST_ENTRY ByProtocol;
PROTOCOL_ENTRY *Protocol;
VOID *Interface;
LIST_ENTRY OpenList;
UINTN OpenListCount;
} PROTOCOL_INTERFACE;
- IHANDLE就是一个具体的USER Handle。其中的Protocol链表记录了所打开的Protocol Interface对象
typedef struct {
UINTN Signature;
LIST_ENTRY AllHandles;
LIST_ENTRY Protocols;
UINTN LocateRequest;
UINT64 Key;
} IHANDLE;
2. CoreInstallProtocolInterface
edk2/MdeModulePkg/Core/Dxe/Hand/Handle.c
这个函数是gBS->InstallProtocolInterface的实现,用于安装一个Protocol。它是一个wrapper函数,实际内部就是CoreInstallProtocolInterfaceNotify。
oreInstallProtocolInterface (
IN OUT EFI_HANDLE *UserHandle, --> Image自身的句柄
IN EFI_GUID *Protocol, --> Protocol的GUID
IN EFI_INTERFACE_TYPE InterfaceType, --> Interface类型
IN VOID *Interface --> 具体Interface对象的指针
)
{
return CoreInstallProtocolInterfaceNotify (
UserHandle,
Protocol,
InterfaceType,
Interface,
TRUE
);
}
CoreInstallProtocolInterfaceNotify实现如下
- CoreFindProtocolEntry查找于Protocol GUID对应的ProtEntry。这个函数实现后面讲,简单来说就是从mProtocolDatabase链表从找到对应ProtEntry。
- AllocateZeroPool分配一个PROTOCOL_INTERFACE对象。
- 根据UserHandle处理,如果UserHandle存在,那么就验证下这个UserHandle是否合法;如果不存在,就分配一个UserHandle并插入到gHandleList中。
- 建立各个数据结构的关系。
- 具体可看注释
EFI_STATUS
CoreInstallProtocolInterfaceNotify (
IN OUT EFI_HANDLE *UserHandle,
IN EFI_GUID *Protocol,
IN EFI_INTERFACE_TYPE InterfaceType,
IN VOID *Interface,
IN BOOLEAN Notify
)
{
PROTOCOL_INTERFACE *Prot;
PROTOCOL_ENTRY *ProtEntry;
IHANDLE *Handle;
EFI_STATUS Status;
VOID *ExistingInterface;
...
ProtEntry = CoreFindProtocolEntry (Protocol, TRUE);
...
Prot = AllocateZeroPool (sizeof (PROTOCOL_INTERFACE));
...
Handle = (IHANDLE *)*UserHandle;
if (Handle == NULL) {
Handle = AllocateZeroPool (sizeof (IHANDLE));
...
Handle->Signature = EFI_HANDLE_SIGNATURE;
InitializeListHead (&Handle->Protocols);
gHandleDatabaseKey++;
Handle->Key = gHandleDatabaseKey;
InsertTailList (&gHandleList, &Handle->AllHandles);
} else {
Status = CoreValidateHandle (Handle);
...
}
Prot->Signature = PROTOCOL_INTERFACE_SIGNATURE;
Prot->Handle = Handle;
Prot->Protocol = ProtEntry;
Prot->Interface = Interface;
InitializeListHead (&Prot->OpenList);
Prot->OpenListCount = 0;
InsertHeadList (&Handle->Protocols, &Prot->Link);
InsertTailList (&ProtEntry->Protocols, &Prot->ByProtocol);
if (Notify) {
CoreNotifyProtocolEntry (ProtEntry);
}
Status = EFI_SUCCESS;
...
return Status;
}
接下来来看一下CoreFindProtocolEntry 的实现,主要分为两步
- 从mProtocolDatabase查找有没有现成的ProtocolEntry,如果有匹配的GUID,就返回。
- 如果没有现成的ProtocolEntry,就从Memory Pool中分配一个,并插入到mProtocolDatabase链表中返回。
PROTOCOL_ENTRY *
CoreFindProtocolEntry (
IN EFI_GUID *Protocol,
IN BOOLEAN Create
)
{
LIST_ENTRY *Link;
PROTOCOL_ENTRY *Item;
PROTOCOL_ENTRY *ProtEntry;
ProtEntry = NULL;
for (Link = mProtocolDatabase.ForwardLink;
Link != &mProtocolDatabase;
Link = Link->ForwardLink)
{
Item = CR (Link, PROTOCOL_ENTRY, AllEntries, PROTOCOL_ENTRY_SIGNATURE);
if (CompareGuid (&Item->ProtocolID, Protocol)) {
ProtEntry = Item;
break;
}
}
if ((ProtEntry == NULL) && Create) {
ProtEntry = AllocatePool (sizeof (PROTOCOL_ENTRY));
if (ProtEntry != NULL) {
ProtEntry->Signature = PROTOCOL_ENTRY_SIGNATURE;
CopyGuid ((VOID *)&ProtEntry->ProtocolID, Protocol);
InitializeListHead (&ProtEntry->Protocols);
InitializeListHead (&ProtEntry->Notify);
InsertTailList (&mProtocolDatabase, &ProtEntry->AllEntries);
}
}
return ProtEntry;
}
3. CoreLocateProtocol
edk2/MdeModulePkg/Core/Dxe/Hand/Locate.c 这个函数是gBS->LocateHandle的实现,根据Protocol的GUID查找具体的Protocol并返回。·
- 如果Registration 参数为NULL,那么就调用CoreFindProtocolEntry 来获取PROTOCOL_ENTRY。这个函数之前已经讲过了,根据GUID从mProtocolDatabase链表中获取PROTOCOL_ENTRY对象示例。然后再根据CoreGetNextLocateByProtocol 函数来获取具体的Interface对象出来。
- 如果Registration 参数不为NULL, 那么就调用CoreGetNextLocateByRegisterNotify 来获取interface。
- 最后获取IHANDE对象,如果IHANDLE存在,那么就返回成功。否则就查不到Interface。
EFI_STATUS
EFIAPI
CoreLocateProtocol (
IN EFI_GUID *Protocol,
IN VOID *Registration OPTIONAL,
OUT VOID **Interface
)
{
EFI_STATUS Status;
LOCATE_POSITION Position;
PROTOCOL_NOTIFY *ProtNotify;
IHANDLE *Handle;
*Interface = NULL;
Status = EFI_SUCCESS;
Position.Protocol = Protocol;
Position.SearchKey = Registration;
Position.Position = &gHandleList;
mEfiLocateHandleRequest += 1;
if (Registration == NULL) {
Position.ProtEntry = CoreFindProtocolEntry (Protocol, FALSE);
if (Position.ProtEntry == NULL) {
Status = EFI_NOT_FOUND;
goto Done;
}
Position.Position = &Position.ProtEntry->Protocols;
Handle = CoreGetNextLocateByProtocol (&Position, Interface);
} else {
Handle = CoreGetNextLocateByRegisterNotify (&Position, Interface);
}
if (Handle == NULL) {
Status = EFI_NOT_FOUND;
} else if (Registration != NULL) {
ProtNotify = Registration;
ProtNotify->Position = ProtNotify->Position->ForwardLink;
}
...
}
CoreGetNextLocateByProtocol 根据获取的PROTOCOL_ENTRY找到PROTOCOL_INTERFACE, 从而拿到接口实例以及该PROTOCOL_INTERFACE所对应的image句柄对象。
IHANDLE *
CoreGetNextLocateByProtocol (
IN OUT LOCATE_POSITION *Position,
OUT VOID **Interface
)
{
IHANDLE *Handle;
LIST_ENTRY *Link;
PROTOCOL_INTERFACE *Prot;
Handle = NULL;
*Interface = NULL;
for ( ; ;) {
Link = Position->Position->ForwardLink;
Position->Position = Link;
if (Link == &Position->ProtEntry->Protocols) {
Handle = NULL;
break;
}
Prot = CR (Link, PROTOCOL_INTERFACE, ByProtocol, PROTOCOL_INTERFACE_SIGNATURE);
Handle = Prot->Handle;
*Interface = Prot->Interface;
...
}
return Handle;
}
4. CoreOpenProtocol
CoreOpenProtocol是gBs->OpenProtocol的接口实现。这个函数由于Attributes参数的存在,非常复杂,限于篇幅,不去细说每一个Attributes如何处理,这里就看最核心的地方,如何去获取到Protocol接口指针。
EFI_STATUS
EFIAPI
CoreOpenProtocol (
IN EFI_HANDLE UserHandle, --> Protocol对应的image句柄
IN EFI_GUID *Protocol, --> Protocol的GUID
OUT VOID **Interface OPTIONAL, --> Protocol接口指针
IN EFI_HANDLE ImageHandle, --> 当前image的句柄
IN EFI_HANDLE ControllerHandle, --> Controller句柄
IN UINT32 Attributes --> 选项参数
)
{
EFI_STATUS Status;
PROTOCOL_INTERFACE *Prot;
LIST_ENTRY *Link;
OPEN_PROTOCOL_DATA *OpenData;
BOOLEAN ByDriver;
BOOLEAN Exclusive;
BOOLEAN Disconnect;
BOOLEAN ExactMatch;
...
Status = CoreValidateHandle (UserHandle);
if (EFI_ERROR (Status)) {
goto Done;
}
...
Prot = CoreGetProtocolInterface (UserHandle, Protocol);
...
*Interface = Prot->Interface;
}
而CoreGetProtocolInterface这个函数做的非常简单,遍历IHANDE->Protocols链表,如果拿到GUID匹配的PROTOCOL_INTERFACE节点,则返回该PROTOCOL_INTERFACE节点。
PROTOCOL_INTERFACE *
CoreGetProtocolInterface (
IN EFI_HANDLE UserHandle,
IN EFI_GUID *Protocol
)
{
EFI_STATUS Status;
PROTOCOL_ENTRY *ProtEntry;
PROTOCOL_INTERFACE *Prot;
IHANDLE *Handle;
LIST_ENTRY *Link;
Status = CoreValidateHandle (UserHandle);
if (EFI_ERROR (Status)) {
return NULL;
}
Handle = (IHANDLE *)UserHandle;
for (Link = Handle->Protocols.ForwardLink; Link != &Handle->Protocols; Link = Link->ForwardLink) {
Prot = CR (Link, PROTOCOL_INTERFACE, Link, PROTOCOL_INTERFACE_SIGNATURE);
ProtEntry = Prot->Protocol;
if (CompareGuid (&ProtEntry->ProtocolID, Protocol)) {
return Prot;
}
}
return NULL;
}
|