UEFI源码学习3.2 - DXE Protocol实现
文章目录
- 1. 数据结构组织
- 2. CoreInstallProtocolInterface
- 3. CoreLocateProtocol
- 4. CoreOpenProtocol
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;/// Link Entry inserted to mProtocolDatabaseLIST_ENTRY AllEntries;/// ID of the protocolEFI_GUID ProtocolID;/// All protocol interfacesLIST_ENTRY Protocols;/// Registerd notification handlersLIST_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;/// Link on IHANDLE.ProtocolsLIST_ENTRY Link;/// Back pointerIHANDLE *Handle;/// Link on PROTOCOL_ENTRY.ProtocolsLIST_ENTRY ByProtocol;/// The protocol IDPROTOCOL_ENTRY *Protocol;/// The interface valueVOID *Interface;/// OPEN_PROTOCOL_DATA listLIST_ENTRY OpenList;UINTN OpenListCount;
} PROTOCOL_INTERFACE;
- IHANDLE就是一个具体的USER Handle。其中的Protocol链表记录了所打开的Protocol Interface对象
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;
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的GUIDIN 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;...//查找于Protocol GUID对应的ProtEntryProtEntry = CoreFindProtocolEntry (Protocol, TRUE);...//分配一个PROTOCOL_INTERFACE对象Prot = AllocateZeroPool (sizeof (PROTOCOL_INTERFACE));...Handle = (IHANDLE *)*UserHandle;if (Handle == NULL) {//UserHandle不存在,则分配一个HandleHandle = AllocateZeroPool (sizeof (IHANDLE));...//初始化Handle的成员变量Handle->Signature = EFI_HANDLE_SIGNATURE;InitializeListHead (&Handle->Protocols);gHandleDatabaseKey++;Handle->Key = gHandleDatabaseKey;//把Handle插入到gHandleList表中InsertTailList (&gHandleList, &Handle->AllHandles);} else {//验证以下Handle是否合法Status = CoreValidateHandle (Handle);...}Prot->Signature = PROTOCOL_INTERFACE_SIGNATURE;//建立Prot和Handle,ProtEntry和实现Interface的指针Prot->Handle = Handle;Prot->Protocol = ProtEntry;Prot->Interface = Interface;//初始化OpenlistInitializeListHead (&Prot->OpenList);Prot->OpenListCount = 0;//把Prot的Link节点插入到Handle的Protocols链表中InsertHeadList (&Handle->Protocols, &Prot->Link);//把ByProtocol节点插入到ProtEntry的Protocols链表中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;//遍历mProtocolDatabase链表for (Link = mProtocolDatabase.ForwardLink;Link != &mProtocolDatabase;Link = Link->ForwardLink){Item = CR (Link, PROTOCOL_ENTRY, AllEntries, PROTOCOL_ENTRY_SIGNATURE);//GUID匹配,返回if (CompareGuid (&Item->ProtocolID, Protocol)) {ProtEntry = Item;break;}}if ((ProtEntry == NULL) && Create) {//如果没有并且Create变量设上,则从memory pool中分配一个PROTOCOL_ENTRY对象。ProtEntry = AllocatePool (sizeof (PROTOCOL_ENTRY));if (ProtEntry != NULL) {//初始化成员变量ProtEntry->Signature = PROTOCOL_ENTRY_SIGNATURE;CopyGuid ((VOID *)&ProtEntry->ProtocolID, Protocol);InitializeListHead (&ProtEntry->Protocols);InitializeListHead (&ProtEntry->Notify);//把PROTOCOL_ENTRY插入到mProtocolDatabase链表中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 ( ; ;) {//遍历PROTOCOL_ENTRY的Protocls链表Link = Position->Position->ForwardLink;Position->Position = Link;//如果是链表头,则跳过。if (Link == &Position->ProtEntry->Protocols) {Handle = NULL;break;}//从链表节点ByProtocol拿到PROTOCOL_INTERFACEProt = CR (Link, PROTOCOL_INTERFACE, ByProtocol, PROTOCOL_INTERFACE_SIGNATURE);//从PROTOCOL_INTERFACE拿到image句柄Handle = Prot->Handle;//从PROTOCOL_INTERFACE拿到Interface对象*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的GUIDOUT 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;... //判断Protocol对应的UserHandle是否合法Status = CoreValidateHandle (UserHandle);if (EFI_ERROR (Status)) {goto Done;}...//根据UserHandle和Protocol GUID获取PROTOCOL_INTERFACE对象Prot = CoreGetProtocolInterface (UserHandle, Protocol);...//从PROTOCOL_INTERFACE中拿到Interface指针并返回*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;//判断UserHandle是否合法Status = CoreValidateHandle (UserHandle);if (EFI_ERROR (Status)) {return NULL;}//转换指针为IHANDLE类型Handle = (IHANDLE *)UserHandle;//遍历IHANDLE中的Protocols链表for (Link = Handle->Protocols.ForwardLink; Link != &Handle->Protocols; Link = Link->ForwardLink) {Prot = CR (Link, PROTOCOL_INTERFACE, Link, PROTOCOL_INTERFACE_SIGNATURE);ProtEntry = Prot->Protocol;//如果GUID匹配,那么就返回该节点if (CompareGuid (&ProtEntry->ProtocolID, Protocol)) {return Prot;}}return NULL;
}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
