首页 > 基础资料 博客日记
UEFI Application 如何调用 Protocol
2026-04-08 12:00:02基础资料围观1次
如何在 UEFI 应用程序中调用 Protocol
前面我们介绍过 Protocol 的本质是一个包含一系列函数指针的 C 语言结构体。例如:
typedef struct {
EFI_TEXT_RESET Reset;
EFI_TEXT_OUTPUT_STRING OutputString;
EFI_TEXT_TEST_STRING TestString;
EFI_TEXT_QUERY_MODE QueryMode;
// ... 更多函数指针
EFI_SIMPLE_TEXT_OUTPUT_MODE *Mode; // 这是一个数据指针,指向协议的状态信息
} EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL;
本文旨在说明 Protocol 中定义的函数在 UEFI 应用程序中该如何调用。
示例程序
我们以图形输出协议 GOP 为例,编写代码演示应用程序如何查找指定协议。
我们知道,Handle 表示某个对象的实例,Protocol 通常挂在对应的 Handle 上,表示这个 Handle 所具备的功能。比如硬盘这个 Handle,就需要有块读写的功能,对应 Block IO Protocol。
本示例代码的逻辑是,首先根据协议的 ProtocolGuid 查找支持这个协议的 Handle,注意可能有多个 Handle 支持这个协议。拿到句柄之后,需要获取具体的协议接口随后调用函数。
我们介绍本文会用到的 Boot Service 中的两个关键函数:
-
LocateHandleBuffer/LocateHandle这两个函数都用于在 UEFI 系统中查找句柄,区别在于内存管理方式。
typedef EFI_STATUS (EFIAPI *EFI_LOCATE_HANDLE)( IN EFI_LOCATE_SEARCH_TYPE SearchType, // 搜索方式 IN EFI_GUID *Protocol OPTIONAL, // 要查找的协议GUID IN VOID *SearchKey OPTIONAL, // 搜索键(与SearchType相关) IN OUT UINTN *BufferSize, // 输入:缓冲区大小,输出:所需大小 OUT EFI_HANDLE *Buffer // 输出缓冲区 ); typedef EFI_STATUS (EFIAPI *EFI_LOCATE_HANDLE_BUFFER)( IN EFI_LOCATE_SEARCH_TYPE SearchType, IN EFI_GUID *Protocol OPTIONAL, IN VOID *SearchKey OPTIONAL, OUT UINTN *NoHandles, // 输出:找到的句柄数量 OUT EFI_HANDLE **Buffer // 输出:分配的缓冲区指针 );LocateHandle调用者自己分配缓冲区,使用流程为:-
第一次调用:传小BufferSize,函数返回
EFI_BUFFER_TOO_SMALL并填充所需大小 -
你根据返回的大小手动分配内存
-
第二次调用:传入足够大的缓冲区,获取句柄列表
UINTN BufferSize = 0; EFI_HANDLE *Buffer; // 第一次调用获取大小 LocateHandle(ByProtocol, &gEfiDriverBindingProtocolGuid, NULL, &BufferSize, NULL); // 分配内存 Buffer = AllocatePool(BufferSize); // 第二次调用获取实际句柄 LocateHandle(ByProtocol, &gEfiDriverBindingProtocolGuid, NULL, &BufferSize, Buffer);LocateHandleBuffer函数内部自动分配缓冲区- 直接返回
NoHandles(句柄数量)和Buffer(已分配好的缓冲区指针) - 使用完毕后需要调用
FreePool()释放
UINTN NoHandles; EFI_HANDLE *Buffer; // 一次调用完成 LocateHandleBuffer(ByProtocol, &gEfiDriverBindingProtocolGuid, NULL, &NoHandles, &Buffer); // 使用Buffer... FreePool(Buffer); // 别忘了释放 -
-
OpenProtocolOpenProtocol 用于获取协议接口函数,打开一个已安装的协议,获得其函数指针或数据结构的访问权。
typedef EFI_STATUS (EFIAPI *EFI_OPEN_PROTOCOL)( IN EFI_HANDLE Handle, // 协议所在的句柄 IN EFI_GUID *Protocol, // 协议的GUID OUT VOID **Interface OPTIONAL, // 输出:协议接口指针 IN EFI_HANDLE AgentHandle, // 请求者句柄(驱动映像句柄) IN EFI_HANDLE ControllerHandle, // 控制器句柄 IN UINT32 Attributes // 打开模式 );
Attributes(打开模式):
| 模式 | 含义 | 典型场景 |
|---|---|---|
EFI_OPEN_PROTOCOL_BY_DRIVER |
驱动独占控制 | 驱动开始管理设备时 |
| `EFI_OPEN_PROTOCOL_BY_DRIVER | EFI_OPEN_PROTOCOL_EXCLUSIVE` | 强制独占 |
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER |
子控制器打开 | Bus驱动为子设备打开 |
EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL |
简单获取(不建立关系) | 临时读取信息 |
EFI_OPEN_PROTOCOL_GET_PROTOCOL |
仅获取(不注册到协议数据库) | 内部查询 |
简单使用示例:
EFI_STATUS Status;
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut;
// 从系统表句柄打开Console输出协议
Status = gBS->OpenProtocol(
gST->ConsoleOutHandle, // 句柄
&gEfiSimpleTextOutProtocolGuid, // 协议GUID
(VOID **)&ConOut, // 输出接口
gImageHandle, // 本驱动句柄
NULL, // 无控制器
EFI_OPEN_PROTOCOL_GET_PROTOCOL // 简单获取
);
OpenProtocol 与 HandleProtocol / LocateProtocol 的关系:
HandleProtocol:是OpenProtocol的简化包装,使用EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL模式。LocateProtocol:先查找句柄,再打开协议(两步合并)。直接查找系统中第一个支持指定 Protocol 的 Handle 并打开它。OpenProtocol:最底层,可精确控制驱动与设备的关系(谁打开了谁),用于驱动的启动/停止管理。
OpenProtocol 的重要机制:协议引用计数。每次成功 OpenProtocol 会增加协议的引用计数,必须配对调用 CloseProtocol 来减少计数,当计数归零时协议才能被卸载。
示例代码:
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h>
EFI_STATUS
EFIAPI
UefiMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
) {
EFI_STATUS Status;
UINTN NoHandles;
EFI_HANDLE *Buffer;
EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput;
// Locate all handles that support the Graphics Output Protocol
Status = gBS->LocateHandleBuffer(
ByProtocol,
&gEfiGraphicsOutputProtocolGuid,
NULL,
&NoHandles,
&Buffer
);
Print(L"Status of LocateHandleBuffer: %r\n", Status);
if (EFI_ERROR(Status))
{
Print(L"Failed to locate handles for Graphics Output Protocol: %r\n", Status);
return Status;
}
Print(L"Hello, Protocol!\n");
Print(L"Number of handles that support Graphics Output Protocol: %d\n", NoHandles);
// Open the Graphics Output Protocol on the first handle found
Status = gBS->OpenProtocol(
Buffer[0],
&gEfiGraphicsOutputProtocolGuid,
(VOID**)&GraphicsOutput,
ImageHandle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
Print(L"Status of OpenProtocol: %r\n", Status);
if (EFI_ERROR(Status)) {
Print(L"Failed to open Graphics Output Protocol: %r\n", Status);
return Status;
}
return EFI_SUCCESS;
}
Steady Progress!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!
标签:
相关文章
最新发布
- 筑基期:掌握Odoo基础核心知识点01
- 统计学WebApp实验体系:从概率直觉到AI赋能的能力进阶
- 一个命令救命:GitHub 爆火项目 thefuck,真把我笑服了
- SDD 之外是 Harness 吗?
- 嵌套 H5 的跨端通信:iOS / Android / 小程序 / 浏览器
- UEFI Application 如何调用 Protocol
- 企业微信服务商争源数字科技谈中小企业如何用好数字化工具
- 【生产避坑】Flink CDC + SQL Server 无增量?5分钟定位,直接抄解决方案
- Python网络请求库,从 requests 到 httpx
- 使用 Python 将 Excel 数据批量导入到数据库中(SQLite)

