菜单

kaoqi
kaoqi
发布于 2026-07-31 / 4 阅读
0
0

Protocol结构体

在EDK2项目中,Protocol结构体可以在/MdeModulePkg/Core/Dxe/Hand/Handle.h中找到:

typedef struct {
  UINTN    signature;  // 魔术字
  LIST_ENTRY    ALLEntries;  // 全局协议链表节点
  EFI_GUID    ProtocolID;  // GUID唯一标识
  LIST_ENTRY    Protocols;  // 协议实例链表头
  LIST_ENTRY    Notify;  // 通知事件链表头
} PROTOCOL_ENTRY;

Protocol的具体功能实现由BootService提供的API函数来完成。

在UEFI中,Protocol和Driver并认为是相似但不同的两个概念。驱动需要特定的硬件支持,服务则不需要。通常服务要能够常驻内存,但单纯的应用程序不可能常驻内存,因此需要包装为驱动的形式。

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

2、DXE驱动,如服务型驱动(不管理任何设备,用来产生protocol提供功能服务)、初始化驱动(不产生任何句柄,用来做一些初始化操作,执行完后就会从系统中卸载)、根桥型驱动(用来初始化平台上的根桥控制器,并产生一个设备地址Protocol,以及访问总线设备的Protocol)。

UEFI驱动的流程为:加载到内存中--》EntryPoint入口函数执行--》gBS->InstallProtocolInterface的系统函数boot service函数调用--》通过Driver Binding Protocol以及Component Name Protocol两个服务,安装驱动到自身Handle或者其他Handle上。

Hello Protocol项目

自定义一个Protocol,需要首先构建对应的数据结构、完成protocol与GUID的绑定、使用Install方法进行安装。

可以自定义一个protocol的结构体:(HelloProtocol.h)

#ifndef __HELLO_PROTOCOL_H
#define __HELLO_PROTOCOL_H

EFI_GUID_gEfiHelloProtocolGuid = {
  0x2b35952b, 0xa6dc, 0x4181, {0xa2, 0xab, 0x95, 0x89, 0xbe, 0xcf, 0x4c, 0xb3}
};  // 这里实际上是写一个8-4-4-4-12‌ 格式的 128 位数字,主要目的是同一台设备上不同的GUID不要撞号

typedef struct _EFI_HELLO_PROTOCOL EFI_HELLO_PROTOCOL;

// Protocol功能函数的定义
typedef EFI_STATUS (EFIAPI * PRINT_HELLO)(IN EFI_HELLO_PROTOCOL *this); // 按照UEFI驱动模型,第一个参数需要是指向自己的指针

// HelloProtocol结构体定义
struct _EFI_HELLO_PROTOCOL {
  UINTN  Data;
  PRINT_HELLO Hello;
};

#endif

然后编写Install文件:(MyHelloProtocol.c)

#include <Uefi.h>

#include <Library/UefiDriverEntryPoint.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/DebugLib.h>

#include <Protocol/HelloProtocol.h>

// 1、Protocol的功能函数
EFI_STATUS EFIAPI PrintHello(IN EFI_HELLO_PROTOCOL *this)
{
  DEBUG((EFI_D_ERROR, "[MyHelloProtocol] Hello Protocol!\r\n"));
  return EFI_SUCCESS;
}

// 入口函数
EFI_STATUS EFIAPI ProtocolServerEntry(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable)
{
  EFI STATUS Status;
  EFI_HELLO_PROTOCOL *Protocol;
  Status = EFI_SUCCESS;

  // 2、实例化protocol,分配相应的内存空间
  Protocol = AllocatePool(sizeof(EFI_HELLO_PROTOCOL));
  if (NULL == Protocol)
  {
    DEBUG((EFI_D_ERROR, "[MyHelloProtocol] Protocol Memory Allocate Failed!\r\n"));
    return EFI_OUT_OF_RESOURCES;
  }
  // 为Prorocol的成员赋值
  Protocol->Data = 0x01;
  Protocol->Hello = PrintHello;

  // 3、Install这个Protocol
  Status = gBS->InstallProtocolInterface(&ImageHandle,&gEfiHelloProtocolGuid,EFI_NATIVE_INTERFFACE,Protocol);
  // 安装失败的处理
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "[MyHelloProtocol] Install EFI_HELLO_PROTOCOL Failed! Code - %r\n", Status));
    FreePool (Protocol);
    Protocol = NULL;
    return Status;
  }
  return Status;
}

后续还需要编写inf文件并在dsc文件与fdf文件中完成添加。

本身的inf文件为:(MyHelloProtocolInstall.inf)

[Defines]
  INF_VERSION = 0X00010005
  BASE_NAME = MyHelloProtocolInstall
  FILE_GUID = b885710c-40f9-4a92-a52ce-022829746c5e
  MODULE_TYPE = UEFI_DRIVER
  VERSION_STRING = 1.0
  ENTRY_POINT = ProtocolServerEntry

[Sources.common]
  MyHelloProtocolInstall.c

[Packages]
  MdePkg/MdePkg.dec
  OvmfPkg/OvmfPkg.dec # 如果不包含自己的这个包,那么头文件就
                      # 需要写为 #include "../Include/Protocol/HelloProtocol.h"

[LibraryClasses]
  UefiDriverEntryPoint
  UefiBootServicesTableLib
  MemoryAllocationLib
  DebugLib

[Protocols]

[Depex]
  TRUE

在对应编译平台的dsc文件的[Components]中添加inf文件的路径,并在fdf文件的[FV.DXEFV]文件中指定inf文件路径,这样才会进行编译,并最终在生成的fd文件中挂上这个Hello的protocol。

很遗憾,笔者目前没有足够的时间完成这件事情,protocol的hello world下次文章再说吧


评论