首页 > 基础资料 博客日记

UEFI Application 如何调用 Protocol

2026-04-08 12:00:02基础资料围观1

本篇文章分享UEFI Application 如何调用 Protocol,对你有帮助的话记得收藏一下,看极客资料网收获更多编程知识

如何在 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 中的两个关键函数:

  1. 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);  // 别忘了释放
    
  2. OpenProtocol

    OpenProtocol 用于获取协议接口函数,打开一个已安装的协议,获得其函数指针或数据结构的访问权。

    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             // 简单获取
);

OpenProtocolHandleProtocol / 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;
}

参考:https://www.bilibili.com/video/BV1a34y197YF?spm_id_from=333.788.videopod.sections&vd_source=2ee7caa81fced5c94d0d863e82c6acae


Steady Progress!


文章来源:https://www.cnblogs.com/ayuan01/p/19834530
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!

标签:

相关文章

本站推荐

标签云