UEFI源码解析之UEFI_DRIVER

Dxe Driver可以视作UEFI中的一个服务,在entry中通过protocol安装自己的服务,在Bds等位置通过locate protocol使用该服务,不必依赖与具体的硬件。当需要封装某个设备/控制器或总线的时候,对应于具体的物理实现,需要用到UEFI Driver的模型实现。UEFI Driver模型实现设备/总线的检测,安装,卸载,更新,启动,停止等;一个Driver可能适配多个设备,一个设备可能连接管理多个子设备等,因此UEFI Driver还可以多次安装。以下从GraphicsOutput设备为入口来解读UEFI driver的源码。

1. UEFI Driver的实现

源码路径为MdeModulePkg\Universal\Console\GraphicsOutputDxe\GraphicsOutput.c。从driver的入口开始:

InitializeGraphicsOutput (
  IN EFI_HANDLE                        ImageHandle,
  IN EFI_SYSTEM_TABLE                  *SystemTable
  )
{
  // 从PEI阶段的HOB中获取Graphic的信息,如Edid等
  HobStart = GetFirstGuidHob (&gEfiGraphicsInfoHobGuid);
  // 将mGraphicsOutputDriverBinding 安装到Dxe环境里面
  Status = EfiLibInstallDriverBindingComponentName2 (
             ImageHandle,
             SystemTable,
             &mGraphicsOutputDriverBinding,
             ImageHandle,
             &mGraphicsOutputComponentName,
             &mGraphicsOutputComponentName2
             );

  return Status;
}

EfiLibInstallDriverBindingComponentName2()根据入参以及实际pcd的配置安装driver的protocol和名字等信息;

       Status = gBS->InstallMultipleProtocolInterfaces (
                       &DriverBinding->DriverBindingHandle,
                       &gEfiDriverBindingProtocolGuid, DriverBinding,
                       &gEfiComponentNameProtocolGuid, ComponentName,
                       &gEfiComponentName2ProtocolGuid, ComponentName2,
                       NULL
                       );

驱动模型的核心通过EFI_DRIVER_BINDING_PROTOCOL实现,通过该protocol判断一个driver是否支持某个控制器,以及启动和停止该控制器;

struct _EFI_DRIVER_BINDING_PROTOCOL {

  EFI_DRIVER_BINDING_SUPPORTED  Supported;  //判断driver是否支持该控制器

  EFI_DRIVER_BINDING_START      Start;  //安装driver到设备/总线控制器

  EFI_DRIVER_BINDING_STOP       Stop;  //卸载设备/总线控制器driver

  UINT32                        Version;

  EFI_HANDLE                    ImageHandle;   //当前driver的image handle

  EFI_HANDLE                    DriverBindingHandle;  //该protocol应该安装到的handle,大部分时候与ImageHandle相同,如果

};

graphicsOutput的实现为:

EFI_DRIVER_BINDING_PROTOCOL mGraphicsOutputDriverBinding = {
  GraphicsOutputDriverBindingSupported,
  GraphicsOutputDriverBindingStart,
  GraphicsOutputDriverBindingStop,
  0x10,
  NULL,
  NULL
};

EFI_COMPONENT_NAME_PROTOCOL/EFI_COMPONENT_NAME2_PROTOCOL 描述Driver的名字,提供获取driver名字和控制器名字的接口,支持的语言类型;

struct _EFI_COMPONENT_NAME2_PROTOCOL {

  EFI_COMPONENT_NAME2_GET_DRIVER_NAME      GetDriverName;

  EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME  GetControllerName;

  CHAR8                                    *SupportedLanguages;

};

graphicsOutput的实现为:

// EFI Component Name Protocol
GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL  mGraphicsOutputComponentName = {
  GraphicsOutputComponentNameGetDriverName,
  GraphicsOutputComponentNameGetControllerName,
  "eng"
};

// EFI Component Name 2 Protocol
GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL mGraphicsOutputComponentName2 = {
  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) GraphicsOutputComponentNameGetDriverName,
  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) GraphicsOutputComponentNameGetControllerName,
  "en"
};

从EfiLibInstallDriverBindingComponentName2接口的实现中发现, EFI_DRIVER_BINDING_PROTOCOL安装在gEfiDriverBindingProtocolGuid上,EFI_COMPONENT_NAME_PROTOCOL安装在gEfiComponentNameProtocolGuid上,EFI_COMPONENT_NAME2_PROTOCOL安装在gEfiComponentName2ProtocolGuid上。

详细安装流程参考《UEFI源码解析之PROTOCOL&HANDLE》。

2. UEFI Driver的使用

Driver的使用入口为gBS->ConnectController(), 将一个或多个driver链接到控制器;一般在Bds阶段的EfiBootManagerConnectAll ()接口中发起;其中调用BmConnectAllDriversToAllControllers ()实现;一般是Bds在load DriverOption或BootOption之前发起连接,因为实际Load启动项过程中会依赖具体的设备,如存储设备中读取启动文件等;

EfiBootManagerConnectAll ()
{
  // Connect the platform console first
  EfiBootManagerConnectAllDefaultConsoles ();

  // Generic way to connect all the drivers
  BmConnectAllDriversToAllControllers ();

  // Here we have the assumption that we have already had
  // platform default console
  EfiBootManagerConnectAllDefaultConsoles ();
}

BmConnectAllDriversToAllControllers实现为:

BmConnectAllDriversToAllControllers ()
{
  do {
    // Connect All EFI 1.10 drivers following EFI 1.10 algorithm
    gBS->LocateHandleBuffer (
           AllHandles,
           NULL,
           NULL,
           &HandleCount,
           &HandleBuffer
           );
    // 遍历说有的image handle,链接该handle上的driver
    for (Index = 0; Index < HandleCount; Index++) {
      gBS->ConnectController (HandleBuffer[Index], NULL, NULL, TRUE);
    }

    // dispatch过程中可能或出现新的Dxe Driver,因此要多次链接
    Status = gDS->Dispatch ();

  } while (!EFI_ERROR (Status));
}

3. ConnectController的实现

源码在MdeModulePkg\Core\Dxe\Hand\DriverSupport.c的CoreConnectController();

CoreConnectController (

  IN  EFI_HANDLE                ControllerHandle,  //driver需要连接的控制器handle

  IN  EFI_HANDLE                *DriverImageHandle   // 支持该driver的handle 可选

  IN  EFI_DEVICE_PATH_PROTOCOL  *RemainingDevicePath // controller设备路径,可选

  IN  BOOLEAN                   Recursive  // 是否允许递归调用,因为可能存在子设备

  )

{

// 确保控制器handle有效

CoreValidateHandle (ControllerHandle);

//如果需要签名验证,如Secure Boot开启,则做签名检查

 if (gSecurity2 != NULL) {

    Status = CoreHandleProtocol (ControllerHandle, &gEfiDevicePathProtocolGuid, (VOID **)&HandleFilePath);

    if (!EFI_ERROR (Status)) {

      FilePath     = HandleFilePath;

      if (RemainingDevicePath != NULL && !Recursive) {

        HandleFilePathSize      = GetDevicePathSize (HandleFilePath) - sizeof (EFI_DEVICE_PATH_PROTOCOL);

        RemainingDevicePathSize = GetDevicePathSize (RemainingDevicePath);

        TempFilePath = AllocateZeroPool (HandleFilePathSize + RemainingDevicePathSize);

        ASSERT (TempFilePath != NULL);

        CopyMem (TempFilePath, HandleFilePath, HandleFilePathSize);

        CopyMem ((UINT8 *) TempFilePath + HandleFilePathSize, RemainingDevicePath, RemainingDevicePathSize);

        FilePath = TempFilePath;

      }

      Status = gSecurity2->FileAuthentication (

                            gSecurity2,

                            FilePath,

                            NULL,

                            0,

                            FALSE

                            );

    }

  }

// 实际连接操作,如果返回EFI_NOT_READY,表示binding handle个数有变化,需要重新连接

do {

    ReturnStatus = CoreConnectSingleController (

                     ControllerHandle,

                     DriverImageHandle,

                     AlignedRemainingDevicePath

                     );

  } while (ReturnStatus == EFI_NOT_READY);

//子设备递归处理

1. 统计子设备个数,每次打开一个记录在ControllerHandle的OpenData中,被子设备打开一次增加一个记录,见 《UEFI源码解析之PROTOCOL&HANDLE》opendata描述;

    for (Link = Handle->Protocols.ForwardLink, ChildHandleCount = 0; Link != &Handle->Protocols; Link = Link->ForwardLink) {

      Prot = CR(Link, PROTOCOL_INTERFACE, Link, PROTOCOL_INTERFACE_SIGNATURE);

      for (ProtLink = Prot->OpenList.ForwardLink;

          ProtLink != &Prot->OpenList;

          ProtLink = ProtLink->ForwardLink) {

        OpenData = CR (ProtLink, OPEN_PROTOCOL_DATA, Link, OPEN_PROTOCOL_DATA_SIGNATURE);

        if ((OpenData->Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) {

          ChildHandleCount++;

        }

      }

2. 获取子设备的handle

for (Link = Handle->Protocols.ForwardLink, ChildHandleCount = 0; Link != &Handle->Protocols; Link = Link->ForwardLink) {

      Prot = CR(Link, PROTOCOL_INTERFACE, Link, PROTOCOL_INTERFACE_SIGNATURE);

      for (ProtLink = Prot->OpenList.ForwardLink;

          ProtLink != &Prot->OpenList;

          ProtLink = ProtLink->ForwardLink) {

        OpenData = CR (ProtLink, OPEN_PROTOCOL_DATA, Link, OPEN_PROTOCOL_DATA_SIGNATURE);

        if ((OpenData->Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) {

          ChildHandleBuffer[ChildHandleCount] = OpenData->ControllerHandle;

          ChildHandleCount++;

        }

      }

    }

    }

3. 递归连接每个子设备

for (Index = 0; Index < ChildHandleCount; Index++) {

      CoreConnectController (

        ChildHandleBuffer[Index],

        NULL,

        NULL,

        TRUE

        );

    }

}

CoreConnectSingleController ()的实现:

1. 从前面实现阶段描述可知,所有binding handles安装在一个protocol(gEfiDriverBindingProtocolGuid)上,因此可以一次性获取所有binding handle;

Status = CoreLocateHandleBuffer (

             ByProtocol,

             &gEfiDriverBindingProtocolGuid,

             NULL,

             &DriverBindingHandleCount,

             &DriverBindingHandleBuffer

             );

2. AddSortedDriverBindingProtocol()主要完成BindingProtocol的过滤和排序,将入参中handle描述支持的binding以及平台重写的handle中支持的binding加入最终的binding列表SortedDriverBindingProtocols中;该接口逻辑和实现背景还需要深入研究,当前还不是很清楚。

Status = CoreHandleProtocol(
             DriverBindingHandle,
             &gEfiDriverBindingProtocolGuid,
             (VOID **) &DriverBinding
             );
  //
  // If DriverBindingHandle does not support the Driver Binding Protocol then return
  //
  if (EFI_ERROR (Status) || DriverBinding == NULL) {
    return;
  }

  //
  // See if DriverBinding is already in the sorted list
  //
  for (Index = 0; Index < *NumberOfSortedDriverBindingProtocols && Index < DriverBindingHandleCount; Index++) {
    if (DriverBinding == SortedDriverBindingProtocols[Index]) {
      return;
    }
  }

  //
  // Add DriverBinding to the end of the list
  //
  if (*NumberOfSortedDriverBindingProtocols < DriverBindingHandleCount) {
    SortedDriverBindingProtocols[*NumberOfSortedDriverBindingProtocols] = DriverBinding;
  }
  *NumberOfSortedDriverBindingProtocols = *NumberOfSortedDriverBindingProtocols + 1;

  //
  // Mark the cooresponding handle in DriverBindingHandleBuffer as used
  //
  for (Index = 0; Index < DriverBindingHandleCount; Index++) {
    if (DriverBindingHandleBuffer[Index] == DriverBindingHandle) {
      DriverBindingHandleBuffer[Index] = NULL;
    }
  }

3. 如果binding 数量增加,说明调用connect过程中有新的binding进来,返回EFI_NOT_READY,使得上层可以重新进入connnect执行;

// If the number of Driver Binding Protocols has increased since this function started, then return
  // EFI_NOT_READY, so it will be restarted
  //
  Status = CoreLocateHandleBuffer (
             ByProtocol,
             &gEfiDriverBindingProtocolGuid,
             NULL,
             &NewDriverBindingHandleCount,
             &NewDriverBindingHandleBuffer
             );
  CoreFreePool (NewDriverBindingHandleBuffer);
  if (NewDriverBindingHandleCount > DriverBindingHandleCount) {
    //
    // Free any buffers that were allocated with AllocatePool()
    //
    CoreFreePool (SortedDriverBindingProtocols);

    return EFI_NOT_READY;
  }

4. binding protocol根据版本排序;  为什么要排序??

// Sort the remaining DriverBinding Protocol based on their Version field from
  // highest to lowest.
  //
  for ( ; SortIndex < NumberOfSortedDriverBindingProtocols; SortIndex++) {
    HighestVersion = SortedDriverBindingProtocols[SortIndex]->Version;
    HighestIndex   = SortIndex;
    for (Index = SortIndex + 1; Index < NumberOfSortedDriverBindingProtocols; Index++) {
      if (SortedDriverBindingProtocols[Index]->Version > HighestVersion) {
        HighestVersion = SortedDriverBindingProtocols[Index]->Version;
        HighestIndex   = Index;
      }
    }
    if (SortIndex != HighestIndex) {
      DriverBinding = SortedDriverBindingProtocols[SortIndex];
      SortedDriverBindingProtocols[SortIndex] = SortedDriverBindingProtocols[HighestIndex];
      SortedDriverBindingProtocols[HighestIndex] = DriverBinding;
    }
  }

5. 遍历所有排序后的binding protocol,一次调用support检查是否该binding支持controller,如果support成功则设置DriverFound找到标记,标记该controller的Driver已找到,然后调用start加载driver,设置OneStart标记表示有一个驱动已经加载;否则返回EFI_NOT_FOUND;

OneStarted = FALSE;
  do {

    //
    // Loop through the sorted Driver Binding Protocol Instances in order, and see if
    // any of the Driver Binding Protocols support the controller specified by
    // ControllerHandle.
    //
    DriverBinding = NULL;
    DriverFound = FALSE;
    for (Index = 0; (Index < NumberOfSortedDriverBindingProtocols) && !DriverFound; Index++) {
      if (SortedDriverBindingProtocols[Index] != NULL) {
        DriverBinding = SortedDriverBindingProtocols[Index];
        PERF_DRIVER_BINDING_SUPPORT_BEGIN (DriverBinding->DriverBindingHandle, ControllerHandle);
        Status = DriverBinding->Supported(
                                  DriverBinding,
                                  ControllerHandle,
                                  RemainingDevicePath
                                  );
        PERF_DRIVER_BINDING_SUPPORT_END (DriverBinding->DriverBindingHandle, ControllerHandle);
        if (!EFI_ERROR (Status)) {
          SortedDriverBindingProtocols[Index] = NULL;
          DriverFound = TRUE;

          //
          // A driver was found that supports ControllerHandle, so attempt to start the driver
          // on ControllerHandle.
          //
          PERF_DRIVER_BINDING_START_BEGIN (DriverBinding->DriverBindingHandle, ControllerHandle);
          Status = DriverBinding->Start (
                                    DriverBinding,
                                    ControllerHandle,
                                    RemainingDevicePath
                                    );
          PERF_DRIVER_BINDING_START_END (DriverBinding->DriverBindingHandle, ControllerHandle);

          if (!EFI_ERROR (Status)) {
            //
            // The driver was successfully started on ControllerHandle, so set a flag
            //
            OneStarted = TRUE;
          }
        }
      }
    }
  } while (DriverFound);

6. 如果没有驱动被加载且时结尾设备节点,返回成功;

  // If no drivers started and RemainingDevicePath is an End Device Path Node, then return EFI_SUCCESS
  //
  if (RemainingDevicePath != NULL) {
    if (IsDevicePathEnd (RemainingDevicePath)) {
      return EFI_SUCCESS;
    }
  }

4. gDS->Dispatch ()实现

Dxe Driver的分发,实现在CoreDispatcher,源码分析在将来《UEFI源码解析之D'x'e'Core》再详细描述;

  • 2
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值