UEFI学习笔记(十四):UEFI 驱动概述

(一)UEFI的驱动分类1、UEFI_DRIVER:2、DXE_DRIVER:(1)服务型驱动(2)初始化驱动(3)根桥型驱动

(二)UEFI Driver模型一、定义二、如何加载三、Driver Binding Protocol1、Supported()2、Start()3、Stop()

(一)UEFI的驱动分类

在UEFI中,驱动分为两类:

1、UEFI_DRIVER:

符合UEFI驱动模型的驱动。包括总线驱动、设备驱动和混合驱动。通过实现 Driver Binding Protocol 来控制设备。这些驱动程序可以动态地启动、停止和管理设备。

2、DXE_DRIVER:

不遵循UEFI驱动模型的驱动。从功能上划分可以划分成:

(1)服务型驱动

不管理任何设备,不需要硬件支持,用来产生protocol提供功能服务。一般来说,服务是可以常驻内存的,应用程序不能常驻内存,只有驱动可以,所以用驱动的形式来提供服务,称之为服务型驱动。

(2)初始化驱动

不产生任何句柄,用来做一些初始化操作,执行完后就会从系统中卸载

(3)根桥型驱动

用来初始化平台上的根桥控制器,并产生一个设备地址Protocol,以及访问总线设备的Protocol。一般用来通过总线驱动访问设备。比如,使用的支持访问PCIE/PCI设备的EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL

UEFI Driver 主要用于管理 PCI 设备,采用分层架构,具有良好的模块化特性,层次结构清晰。相较之下,DXE Driver 主要负责平台的初始化工作以及一些功能服务。

(二)UEFI Driver模型

一、定义

Device Driver 在 DXE 阶段加载,而应用程序则在 BDS 阶段加载。通过在 BDS 启动时访问设备,可以提供相应的输入和输出设备。

引入 Device Driver 旨在实现更好的模块化管理,主要用于设备管理。它支持二进制发布,并可以集成到 Option ROM 中,同时提供清晰的外部接口,从而提升固件的扩展性。

从 UEFI 驱动程序模型的角度来看,总线驱动程序和设备驱动程序几乎是相同的。唯一的区别在于,总线驱动程序必须为其创建的每个子句柄安装协议接口。最低要求是它需要安装一个协议接口,为子控制器提供总线服务的 I/O 抽象。如果总线驱动程序创建的子句柄代表的是一个物理设备,则它还必须在子句柄上安装 Device Path Protocol 实例。

二、如何加载

Application:在 DXE 阶段加载,执行入口点(Entry Point),完成驱动初始化,安装相关协议到Handle Database,然后退出入口点,将控制权交给 UEFI Loader。

Driver:同样在 DXE 阶段加载,执行入口点,进行驱动初始化,安装协议,然后退出入口点,将控制权交给 UEFI Loader。驱动提供的服务会一直驻留在内存中,直到执行权转交给操作系统。

三、Driver Binding Protocol

它是UEFI必须要Install的Protocol。有下面三个API:

1、Supported()

该函数用于判断驱动程序是否支持特定的控制器。驱动程序在启动时调用此函数,以检查它是否能够处理指定的设备或控制器。此方法的返回值将决定后续的启动流程是否继续,确保只有兼容的驱动程序被加载。

typedef

EFI_STATUS

(EFIAPI *EFI_DRIVER_BINDING_PROTOCOL_SUPPORTED) (

IN EFI_DRIVER_BINDING_PROTOCOL *This,

IN EFI_HANDLE ControllerHandle,

IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL

);

例子:

extern EFI_GUID gEfiDriverBindingProtocolGuid;

EFI_HANDLE DriverImageHandle;

EFI_HANDLE ControllerHandle;

EFI_DRIVER_BINDING_PROTOCOL *DriverBinding;

EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath;

//

// Use the DriverImageHandle to get the Driver Binding protocol instance

//

Status = gBS->OpenProtocol (

DriverImageHandle,

&gEfiDriverBindingProtocolGuid,

&DriverBinding,

DriverImageHandle,

NULL,

EFI_OPEN_PROTOCOL_GET_PROTOCOL

);

if (EFI_ERROR (Status)) {

return Status;

}

//

// EXAMPLE #1

//

// Use the Driver Binding Protocol instance to test to see if the

// driver specified by DriverImageHandle supports the controller

// specified by ControllerHandle

//

Status = DriverBinding->Supported (

DriverBinding,

ControllerHandle,

NULL

);

return Status;

//

// EXAMPLE #2

//

// The RemainingDevicePath parameter can be used to initialize only

// the minimum devices required to boot. For example, maybe we only

// want to initialize 1 hard disk on a SCSI channel. If DriverImageHandle

// is a SCSI Bus Driver, and ControllerHandle is a SCSI Controller, and

// we only want to create a child handle for PUN=3 and LUN=0, then the

// RemainingDevicePath would be SCSI(3,0)/END. The following example

// would return EFI_SUCCESS if the SCSI driver supports creating the

// child handle for PUN=3, LUN=0. Otherwise it would return an error.

//

Status = DriverBinding->Supported (

DriverBinding,

ControllerHandle,

RemainingDevicePath

);

2、Start()

此函数用于启动基于控制器的驱动程序。当驱动程序被加载并通过 Supported 检查后,则可以通过调用驱动程序的Start()函数将驱动程序连接到控制器。Start()函数实际上将额外的I/O协议添加到设备句柄中。 该函数负责执行以下任务:

1)初始化设备,配置必要的硬件资源。

2)安装所需的协议,以便其他组件或驱动程序可以访问设备的功能。

3)完成初始化后,Start 函数通常会返回一个成功状态,表明驱动程序已准备就绪。

typedef

EFI_STATUS

(EFIAPI *EFI_DRIVER_BINDING_PROTOCOL_START) (

IN EFI_DRIVER_BINDING_PROTOCOL *This,

IN EFI_HANDLE ControllerHandle,

IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL

);

例子:

extern EFI_GUID gEfiDriverBindingProtocolGuid;

EFI_HANDLE DriverImageHandle;

EFI_HANDLE ControllerHandle;

EFI_DRIVER_BINDING_PROTOCOL *DriverBinding;

EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath;

//

// Use the DriverImageHandle to get the Driver Binding Protocol instance

//

Status = gBS->OpenProtocol (

DriverImageHandle,

&gEfiDriverBindingProtocolGuid,

&DriverBinding,

DriverImageHandle,

NULL,

EFI_OPEN_PROTOCOL_GET_PROTOCOL

);

if (EFI_ERROR (Status)) {

return Status;

}

//

// EXAMPLE #1

//

// Use the Driver Binding Protocol instance to test to see if the

// driver specified by DriverImageHandle supports the controller

// specified by ControllerHandle

//

Status = DriverBinding->Supported (

DriverBinding,

ControllerHandle,

NULL

);

if (!EFI_ERROR (Status)) {

Status = DriverBinding->Start (

DriverBinding,

ControllerHandle,

NULL

);

}

return Status;

//

// EXAMPLE #2

//

// The RemainingDevicePath parameter can be used to initialize only

// the minimum devices required to boot. For example, maybe we only

// want to initialize 1 hard disk on a SCSI channel. If DriverImageHandle

// is a SCSI Bus Driver, and ControllerHandle is a SCSI Controller, and

// we only want to create a child handle for PUN=3 and LUN=0, then the

// RemainingDevicePath would be SCSI(3,0)/END. The following example

// would return EFI_SUCCESS if the SCSI driver supports creating the

// child handle for PUN=3, LUN=0. Otherwise it would return an error.

//

Status = DriverBinding->Supported (

DriverBinding,

ControllerHandle,

RemainingDevicePath

);

if (!EFI_ERROR (Status)) {

Status = DriverBinding->Start (

DriverBinding,

ControllerHandle,

RemainingDevicePath

);

}

3、Stop()

Stop 函数用于停止管理特定控制器的驱动程序,并在此过程中卸载相关的协议。当驱动程序不再需要时,Stop 函数被调用,以进行资源清理和释放。该函数执行以下操作:

1)停止对控制器的管理,确保设备处于安全状态。

2)卸载之前安装的协议,确保其他组件不再依赖该驱动程序。

3)返回状态以指示操作的成功与否,确保系统稳定性。

typedef

EFI_STATUS

(EFIAPI *EFI_DRIVER_BINDING_PROTOCOL_STOP) (

IN EFI_DRIVER_BINDING_PROTOCOL *This,

IN EFI_HANDLE ControllerHandle,

IN UINTN NumberOfChildren,

IN EFI_HANDLE *ChildHandleBuffer OPTIONAL

);

例子:

extern EFI_GUID gEfiDriverBindingProtocolGuid;

EFI_HANDLE DriverImageHandle;

EFI_HANDLE ControllerHandle;

EFI_HANDLE ChildHandle;

EFI_DRIVER_BINDING_PROTOCOL *DriverBinding;

//

// Use the DriverImageHandle to get the Driver Binding Protocol instance

//

Status = gBS->OpenProtocol (

DriverImageHandle,

&gEfiDriverBindingProtocolGuid,

&DriverBinding,

DriverImageHandle,

NULL,

EFI_OPEN_PROTOCOL_GET_PROTOCOL

);

if (EFI_ERROR (Status)) {

return Status;

}

//

// Use the Driver Binding Protocol instance to free the child

// specified by ChildHandle. Then, use the Driver Binding

// Protocol to stop ControllerHandle.

//

Status = DriverBinding->Stop (

DriverBinding,

ControllerHandle,

1,

&ChildHandle

);

Status = DriverBinding->Stop (

DriverBinding,

ControllerHandle,

0,

NULL

);