Dxe Driver可以视作UEFI中的一个服务,在entry中通过protocol安装自己的服务,在Bds等位置通过locate protocol使用该服务,不必依赖与具体的硬件。当需要封装某个设备/控制器或总线的时候,对应于具体的物理实现,需要用到UEFI Driver的模型实现。UEFI Driver模型实现设备/总线的检测,安装,卸载,更新,启动,停止等;一个Driver可能适配多个设备,一个设备可能连接管理多个子设备等,因此UEFI Driver还可以多次安装。以下从GraphicsOutput设备为入口来解读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》。
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));
}
源码在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;
}
}
Dxe Driver的分发,实现在CoreDispatcher,源码分析在将来《UEFI源码解析之D'x'e'Core》再详细描述;