IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> UEFI源码学习3.2 - DXE Protocol实现 -> 正文阅读

[数据结构与算法]UEFI源码学习3.2 - DXE Protocol实现

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 mProtocolDatabase
  LIST_ENTRY    AllEntries;
  /// ID of the protocol
  EFI_GUID      ProtocolID;
  /// All protocol interfaces
  LIST_ENTRY    Protocols;
  /// Registerd notification handlers
  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;
  /// Link on IHANDLE.Protocols
  LIST_ENTRY        Link;
  /// Back pointer
  IHANDLE           *Handle;
  /// Link on PROTOCOL_ENTRY.Protocols
  LIST_ENTRY        ByProtocol;
  /// The protocol ID
  PROTOCOL_ENTRY    *Protocol;
  /// The interface value
  VOID              *Interface;
  /// OPEN_PROTOCOL_DATA list
  LIST_ENTRY        OpenList;
  UINTN             OpenListCount;
} PROTOCOL_INTERFACE;
  • IHANDLE就是一个具体的USER Handle。其中的Protocol链表记录了所打开的Protocol Interface对象
typedef struct {
  UINTN         Signature;
  /// All handles list of IHANDLE
  LIST_ENTRY    AllHandles;
  /// List of PROTOCOL_INTERFACE's for this handle
  LIST_ENTRY    Protocols;
  UINTN         LocateRequest;
  /// The Handle Database Key value when this handle was last created or modified
  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;

  ...
  //查找于Protocol GUID对应的ProtEntry
  ProtEntry = CoreFindProtocolEntry (Protocol, TRUE);
  ...
  //分配一个PROTOCOL_INTERFACE对象
  Prot = AllocateZeroPool (sizeof (PROTOCOL_INTERFACE));
  ...
  Handle = (IHANDLE *)*UserHandle;
  if (Handle == NULL) {
    //UserHandle不存在,则分配一个Handle
    Handle = 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;
  //初始化Openlist
  InitializeListHead (&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_INTERFACE
    Prot       = 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的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;
  ...  
  //判断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;
}
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-05-08 08:20:57  更:2022-05-08 08:21:18 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 3:37:07-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码