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 的实现,主要分为两步

  1. 从mProtocolDatabase查找有没有现成的ProtocolEntry,如果有匹配的GUID,就返回。
  2. 如果没有现成的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;
}


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部