在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下次文章再说吧