UEFI 协议与句柄

简介

DxeCore 维护一个协议数据库,数据库中包含有句柄(Handle) , 协议(Protocol) 和接口(Interface).

协议以GUID 命名,由数据结构表示,数据结构可能为空,可能只包含数据,可能只包含函数指针,

也可能两者都包含。接口是协议的具体实现,与面向对象概念类比,协议等同于类,接口等同于类的

实例。

 

协议数据库

在UEFI 概念中,一个句柄(Handle) 上安装有一个或多个协议(Protocol) 的具体实现(Interface),

针对某个特定协议,一个句柄上只允许安装一个对应的接口。安装的操作通过

InstallMultipleProtocolInterfaces() , InstallProtocolInterface() 完成。 从句柄上卸载Interface 的操作

通过HandleProtocol() 可以查询指定句柄上是否安装有特定协议的接口,通过LocateHandle()

LocateHandleBuffer() 可以查询系统中安装特定协议的句柄列表。通过这些接口,我们实现了驱动

程序A安装某个协议的接口,驱动程序B查询并使用该接口。

 

UEFI 驱动模型

当驱动程序被DxeCore 加载入内存后,DxeCore 会为该驱动程序创建一个Image 句柄并在句柄上

安装LoadedImage 协议,然后DxeCore会调用该驱动程序的入口函数。不符合UEFI驱动模型的驱动

程序会在入口函数初始化硬件并退出。但符合UEFI 驱动模型的驱动程序不会在入口函数对硬件进行

编程,仅仅在Image 句柄上安装DriverBinding 协议。所有驱动程序在加载并且运行后,将等待

EFI 启动管理器(boot Manager) 通知它们管理一个或者多个硬件设备。EFI 启动管理器通过调用

ConnectController, ConnectController通过调用DriverBinding 的Supported 和Start 来连接驱动

程序到硬件设备。

全局变量

 

LIST_ENTRY mProtocolDatabase

存储系统所有协议的链表

 

LIST_ENTRY GhandleList

存储系统所有句柄的链表

 

EFI_LOCK gProtocolDatabaseLock

保护mProtocolDataBase 的锁

  //
  // Update the Key to show that the handle has been created/modified
  //
  gHandleDatabaseKey++;

 

gHandleDatabaseKey  每当有新的句柄被创建,句柄上的协议被卸载或者重新安装, 该值加1

 

DxeCore 的协议数据库可以用下图很清晰的表示。虚线双向箭头表示双向链表节点间的连接,

实线单向箭头表示指针与实际指向的数据间的连接。

例如 mProtocolDataBase为协议实体ProtocolEntry 链表的头,链表项是ProtocolEntry, 所有项由

ProtocolEntry.AllEntries 串连在一起。

 

安装卸载协议

CoreInstallMultipleProtocolInterfaces(...)

实现BootService的InstallMultipleProtocolInterfaces() 函数接口。该变参函数第一个参数为句柄的指针,

从第二个参数开始,接下来的每对参数分别分协议GUID 指针和协议接口指针,最后以空指针表示参数

列表的结束 。

 

**/
EFI_STATUS
EFIAPI
CoreInstallMultipleProtocolInterfaces (
  IN OUT EFI_HANDLE           *Handle,
  ...
  )
{
  VA_LIST                   Args;
  EFI_STATUS                Status;
  EFI_GUID                  *Protocol;
  VOID                      *Interface;
  EFI_TPL                   OldTpl;
  UINTN                     Index;
  EFI_HANDLE                OldHandle;
  EFI_HANDLE                DeviceHandle;
  EFI_DEVICE_PATH_PROTOCOL  *DevicePath;

  if (Handle == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Syncronize with notifcations.
  //
  OldTpl = CoreRaiseTpl (TPL_NOTIFY);
  OldHandle = *Handle;

  //
  // Check for duplicate device path and install the protocol interfaces
  //
  VA_START (Args, Handle);
  for (Index = 0, Status = EFI_SUCCESS; !EFI_ERROR (Status); Index++) {
    //
    // If protocol is NULL, then it's the end of the list
    //
    Protocol = VA_ARG (Args, EFI_GUID *);
    if (Protocol == NULL) {
      break;
    }

    Interface = VA_ARG (Args, VOID *);

    //
    // Make sure you are installing on top a device path that has already been added.
    //
    if (CompareGuid (Protocol, &gEfiDevicePathProtocolGuid)) {
      DeviceHandle = NULL;
      DevicePath   = Interface;
      Status = CoreLocateDevicePath (&gEfiDevicePathProtocolGuid, &DevicePath, &DeviceHandle);
      if (!EFI_ERROR (Status) && (DeviceHandle != NULL) && IsDevicePathEnd(DevicePath)) {
        Status = EFI_ALREADY_STARTED;
        continue;
      }
    }

    //
    // Install it
    //
    Status = CoreInstallProtocolInterface (Handle, Protocol, EFI_NATIVE_INTERFACE, Interface);
  }
  VA_END (Args);

提升TPL 到NOTIFY 级别

记录下*Handle 的初始值,以备错误恢复。

开始循环,Index 记录已经安装的协议数量。

  //
  // If there was an error, remove all the interfaces that were installed without any errors
  //
  if (EFI_ERROR (Status)) {
    //
    // Reset the va_arg back to the first argument.
    //
    VA_START (Args, Handle);
    for (; Index > 1; Index--) {
      Protocol = VA_ARG (Args, EFI_GUID *);
      Interface = VA_ARG (Args, VOID *);
      CoreUninstallProtocolInterface (*Handle, Protocol, Interface);
    }
    VA_END (Args);

    *Handle = OldHandle;
  }

  //
  // Done
  //
  CoreRestoreTpl (OldTpl);
  return Status;

 

在安装协议过程中有错误发生,将已经安装的协议逐个从*Handle 上卸载。

恢复*Handle 的初始值。

TPL 从NOTIFY 级恢复。

CoreInstallProtocolInterfaceNotify

 

该函数安装协议Protocol 的实例Interface 到*Userhandle上。如果*UserHandle 为空,新的句柄会被

创建并返回。

  //
  // returns EFI_INVALID_PARAMETER if InterfaceType is invalid.
  // Also added check for invalid UserHandle and Protocol pointers.
  //
  if (UserHandle == NULL || Protocol == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (InterfaceType != EFI_NATIVE_INTERFACE) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Print debug message
  //
  DEBUG((DEBUG_INFO, "InstallProtocolInterface: %g %p\n", Protocol, Interface));

  Status = EFI_OUT_OF_RESOURCES;
  Prot = NULL;
  Handle = NULL;

  if (*UserHandle != NULL) {
    Status = CoreHandleProtocol (*UserHandle, Protocol, (VOID **)&ExistingInterface);
    if (!EFI_ERROR (Status)) {
      return EFI_INVALID_PARAMETER;
    }
  }

  //
  // Lock the protocol database
  //
  CoreAcquireProtocolLock ();

  //
  // Lookup the Protocol Entry for the requested protocol
  //
  ProtEntry = CoreFindProtocolEntry (Protocol, TRUE);
  if (ProtEntry == NULL) {
    goto Done;
  }

  //
  // Allocate a new protocol interface structure
  //
  Prot = AllocateZeroPool (sizeof(PROTOCOL_INTERFACE));
  if (Prot == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto Done;
  }

  //
  // If caller didn't supply a handle, allocate a new one
  //
  Handle = (IHANDLE *)*UserHandle;
  if (Handle == NULL) {
    Handle = AllocateZeroPool (sizeof(IHANDLE));
    if (Handle == NULL) {
      Status = EFI_OUT_OF_RESOURCES;
      goto Done;
    }

    //
    // Initialize new handler structure
    //
    Handle->Signature = EFI_HANDLE_SIGNATURE;
    InitializeListHead (&Handle->Protocols);

    //
    // Initialize the Key to show that the handle has been created/modified
    //
    gHandleDatabaseKey++;
    Handle->Key = gHandleDatabaseKey;

    //
    // Add this handle to the list global list of all handles
    // in the system
    //
    InsertTailList (&gHandleList, &Handle->AllHandles);
  } else {
    Status = CoreValidateHandle (Handle);
    if (EFI_ERROR (Status)) {
      DEBUG((DEBUG_ERROR, "InstallProtocolInterface: input handle at 0x%x is invalid\n", Handle));
      goto Done;
    }
  }

如果Handle 上安装有Protocol协议且实例就是Interface, CoreFindProtocolInterface() 返回对应的ProtocolInterface。

当前正要安装该实例,所以找不到才是我们所期待的。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值