2023-04-15
- Protocol是UEFI提供的接口函数。
- 每个Protocol都包含GUID、Protocol接口的结构体、和Protocol接口函数(Protocol服务)。
- 3个使用Protocol的函数OpenProtocol、HandleProtocol、LocateProtocol找到Protocol。
- .efi文件(如EFI_DRIVER)加载到内存后被称为Image,ImageHandle是Image的句柄。一般模块的入口函数会有一个ImageHandle入参,该参数指向了内存中的Image。
- UEFI扫描总线后,会为每个设备建立一个ControllerHandle句柄。即ControllerHandle指向了某个硬件。
- 每个Handle(对应的结构体为IHANDLE)都会有一个Protocols链表,存放自己的Protocol。所有的IHANDLE通过AllHandles链接起来。
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;
OpenProtocol()
#
typedef
EFI_STATUS
(EFIAPI *EFI_OPEN_PROTOCOL)(
IN EFI_HANDLE Handle, //安装了此Protocol的Handle
IN EFI_GUID *Protocol, //要打开的Protocol的GUID
OUT VOID **Interface OPTIONAL, //返回打开的Protocol
IN EFI_HANDLE AgentHandle, //使用此Protocol的Image(存疑)
IN EFI_HANDLE ControllerHandle, //如果打开的是Protocol是符合UEFI驱动模型的驱动,则此参数为控制Protocol接口的控制器,否则为可选的,可能为NULL
IN UINT32 Attributes //打开Protocol的参数
);
- 对于符合UEFI驱动模型的UEFI驱动而言,ControllerHandle是拥有该驱动的控制器,AgentHandle是拥有该EFI_DRIVER_BINGDING_PROTOCOL实例的句柄;
- 对于UEFI应用而言,ControllerHandle可以忽略,AgentHandle是该程序的句柄,即UefiMain函数的第一个参数。
HandleProtocol()
#
- HandleProtocol不需要提供AgentHandle、ControllerHandle和Attributes。
- HandleProtocol的AgentHandle为gDxeCoreImageHandle,ControllerHandle为NULL,Attributes为EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL。
/**
@retval EFI_SUCCESS 成功返回指定的Protocol
@retval EFI_UNSUPPORTED 此Handle没有安装此Protocol
**/
typedef
EFI_STATUS
(EFIAPI *EFI_HANDLE_PROTOCOL)(
IN EFI_HANDLE Handle, //安装了此Protocol的Handle
IN EFI_GUID *Protocol, //此Protocol的GUID
OUT VOID **Interface //返回此Protocol的实例
);
LocateProtocol()
#
- 当仅有一个Handle安装了某个Protocol时,可以使用LocateProtocol,该函数不需要提供安装了该Protocol的Handle。
- 如果多个Handle都安装了某个Protocol,就会顺序遍历HANDLE链表,找到第一个该Protocol实例。
typedef
EFI_STATUS
(EFIAPI *EFI_LOCATE_PROTOCOL)(
IN EFI_GUID *Protocol, //此Protocol的GUID
IN VOID *Registration OPTIONAL, //不知道干啥用的,描述时从RegisterProtocolNotify()获得的key
OUT VOID **Interface //返回此Protocol的实例
);
EFI Driver Binding Protocol
#
- 从功能上划分,UEFI驱动分为以下类别:
- 符合UEFI驱动模型的驱动(UEFI Driver Model):包括总线驱动(Bus Drivers)、设备驱动(Device Drivers)和混合驱动(Hybrid Drivers),一般用来驱动对应的硬件设备。
- 服务型驱动(Service Drivers):这类驱动不管理任何设备,一般用来产生Protocol。
- 初始化驱动(Initializing Drivers):不会产生任何句柄,也不会增加任何Protocol到系统数据库,主要用来初始化一些操作,执行完后就从系统内存中卸载。
- 根桥型驱动(Root Bridge Drivers):用来初始化平台上的根桥控制器,并产生一个设备地址Protocol,以及访问总线设备的Protocol,一般用来通过总线驱动访问设备。
- 一个完整的符合UEFI驱动模型的驱动程序,大致可分为EFI Driver Binding Protocol和驱动本身提供的服务。前者用来管理驱动,后者才是用户需要使用提供的部分。
- 所提供的服务一般为多个Protocol。
- 为了方便用户使用,驱动程序一般还会包括EFI Component Name Protocol,这个Protocol用来显示驱动信息。
- EFI_DRIVER_BINDING_PROTOCOL的结构体如下:
struct _EFI_DRIVER_BINDING_PROTOCOL {
EFI_DRIVER_BINDING_SUPPORTED Supported; //检查设备控制器是否支持驱动
EFI_DRIVER_BINDING_START Start; //安装驱动并启动设备
EFI_DRIVER_BINDING_STOP Stop; //停止设备并卸载驱动
UINT32 Version; //版本
EFI_HANDLE ImageHandle; //镜像句柄
EFI_HANDLE DriverBindingHandle;
};
- 接口变量ImageHandle是产生此Protocol实例的镜像句柄,DriverBindingHandle是安装了Protocol实例的句柄。
Supported()接口函数
#
- Supported()接口函数用来检测给定的设备控制器是否支持某驱动,函数原型如下:
typedef
EFI_STATUS
(EFIAPI *EFI_DRIVER_BINDING_SUPPORTED)(
//Protocol实例
IN EFI_DRIVER_BINDING_PROTOCOL *This,
//设备控制器句柄
IN EFI_HANDLE ControllerHandle,
//此参数对设备型驱动无效
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
);
Start()接口函数
#
- 用来将驱动安装到设备上,并启动硬件设备。一般在此函数中使用InstallProtocolInterface()或InstallMultipleProtocolInterface()函数进行安装。
typedef
EFI_STATUS
(EFIAPI *EFI_DRIVER_BINDING_START)(
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
);
Stop()接口函数
#
typedef
EFI_STATUS
(EFIAPI *EFI_DRIVER_BINDING_STOP)(
IN EFI_DRIVER_BINDING_PROTOCOL *This, //Protocol实例
IN EFI_HANDLE ControllerHandle, //停止此控制器句柄上对应的驱动
IN UINTN NumberOfChildren, //子控制器数量
IN EFI_HANDLE *ChildHandleBuffer OPTIONAL //子控制器数组
);
EFI Component Name Protocol
#
- 为了方便用户使用,UEFI驱动通常会提供名字,以便向用户显示驱动信息。
- 此功能由EFI_COMPONENT_NAME_PROTOCOL和EFI_COMPONENT_NAME2_PROTOCOL实现,这两种Protocol的功能相同,结构体相同,仅语言代码的格式不同,前者使用的是ISO 639-2语言代码,后者使用的是RFC4646语言代码。