Protocol

Protocol

2023-04-15
bios
  • 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语言代码。