【UEFI实战】EFI System Table中的输入输出

本文详细介绍了UEFI中输出机制的实现原理,包括DEBUG、Print等常用输出函数的工作方式及内部实现细节。重点讲解了EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL协议及其如何在不同设备上实现输出。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

UEFI中的输入输出

UEFI下面有如下的输出函数:

1. DEBUG,它一般通过串口输出,这个是调试的时候最常用的;

2. Print,这个一般会输出到串口和显示器;

3. gST->ConOut->OutputString,这个一般也是输出到串口和显示器,其实Print也是调用的这里:

InternalPrint (Format, gST->ConOut, Marker);

4. 还有一个输出是Setup界面,但是目前还不知道它是怎么输出的;

DEBUG已经在之前的文章中写过:

【UEFI实战】DEBUG

这里主要讲的是gST下的ConOut(ConIn跟它类似,再这里不多讲)。

本文讲到的代码是edk2017版本的。

EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL

这个Protocol的结构如下:

///
/// The SIMPLE_TEXT_OUTPUT protocol is used to control text-based output devices. 
/// It is the minimum required protocol for any handle supplied as the ConsoleOut 
/// or StandardError device. In addition, the minimum supported text mode of such 
/// devices is at least 80 x 25 characters.
///
struct _EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL {
  EFI_TEXT_RESET                Reset;

  EFI_TEXT_STRING               OutputString;
  EFI_TEXT_TEST_STRING          TestString;

  EFI_TEXT_QUERY_MODE           QueryMode;
  EFI_TEXT_SET_MODE             SetMode;
  EFI_TEXT_SET_ATTRIBUTE        SetAttribute;

  EFI_TEXT_CLEAR_SCREEN         ClearScreen;
  EFI_TEXT_SET_CURSOR_POSITION  SetCursorPosition;
  EFI_TEXT_ENABLE_CURSOR        EnableCursor;

  ///
  /// Pointer to SIMPLE_TEXT_OUTPUT_MODE data.
  ///
  EFI_SIMPLE_TEXT_OUTPUT_MODE   *Mode;
};

gST->ConOut在不同的地方会初始化。

下面是在ConSplitter.c的ConSplitterDriverEntry()函数中:

  //
  // Create virtual device handle for ConOut Splitter
  //
  Status = ConSplitterTextOutConstructor (&mConOut);
  if (!EFI_ERROR (Status)) {
    Status = gBS->InstallMultipleProtocolInterfaces (
                    &mConOut.VirtualHandle,
                    &gEfiSimpleTextOutProtocolGuid,
                    &mConOut.TextOut,
                    NULL
                    );
    if (!EFI_ERROR (Status)) {
      //
      // Update the EFI System Table with new virtual console
      // and Update the pointer to Text Output protocol.
      //
      gST->ConsoleOutHandle = mConOut.VirtualHandle;
      gST->ConOut           = &mConOut.TextOut;
    }
  }

另外还有在BdsConsole.c的BdsLibConnectAllDefaultConsoles():

  if (UpdateSystemTableConsole (L"ConOut", &gEfiSimpleTextOutProtocolGuid, &gST->ConsoleOutHandle, (VOID **) &gST->ConOut)) {
    SystemTableUpdated = TRUE;
  }

gST->ConOut还会跟gST->ConsoleOutHandle对应,最终跟一个gEfiSimpleTextOutProtocolGuid对应。

gEfiSimpleTextOutProtocolGuid的安装对应到不同的情况,比如对于串口就需要有gEfiSerialIoProtocolGuid,对于显示器就需要依赖于gEfiUgaDrawProtocolGuid或者gEfiGraphicsOutputProtocolGuid等与图形输出有关的protocols。

而gST->ConOut->OutputString()最终会调用这些protocols来实现输出。

这里有一个问题,即为什么一个gST->ConOut->OutputString()可以在多个位置输出,以ConSplitter.c里面为例,在ConSplitterTextOutOutputString()中有如下的代码:

  //
  // return the worst status met
  //
  for (Index = 0, ReturnStatus = EFI_SUCCESS; Index < Private->CurrentNumberOfConsoles; Index++) {
    Status = Private->TextOutList[Index].TextOut->OutputString (
                                                    Private->TextOutList[Index].TextOut,
                                                    WString
                                                    );
    if (EFI_ERROR (Status)) {
      ReturnStatus = Status;
    }
  }

这里有一个循环,可以起到多个位置打印的效果,而ConSplitterTextOutAddDevice()可以增加输出位置。

输出Protocol路线图

UEFI下为了要有输出,并且具有好的兼容性,需要有一套比较复杂的流程,要安装很多的protocols,最简单的一个形式如下:

另外还有从USB输出的,这里没有画出来。

### UefiLibConstructor 函数的用途 `UefiLibConstructor` 是 EDK II 中的一个常见库构造函数,用于初始化特定模块所需的资源或环境设置。它通常是一个静态链接的库函数,在模块加载时自动调用。以下是关于 `UefiLibConstructor` 的详细说明: #### 初始化功能 `UefiLibConstructor` 主要负责完成以下任务: - 设置全局变量:例如,将 `gST` 和 `gBS` 指向当前系统的 EFI 系统表 (`EFI_SYSTEM_TABLE`) 和引导服务 (`EFI_BOOT_SERVICES`) [^1]。 - 配置默认参数:如果某些模块依赖于固定的配置项,则可以在该函数中进行预设。 #### 实现方式 在 EDK II 编程框架下,`UefiLibConstructor` 被定义为一个标准的 C 构造器函数,其签名如下所示: ```c EFI_STATUS EFIAPI UefiLibConstructor ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ); ``` 其中: - **ImageHandle**: 表示当前镜像实例句柄,类型为 `EFI_HANDLE`,即指向任意有效内存地址的一般指针 。 - **SystemTable**: 提供访问整个平台固件接口的能力,包含了所有可用的服务和协议列表 [^2]。 当此函数被执行时,UEFI 运行时环境已经完成了 DXE(Driver Execution Environment)阶段的核心初始化工作,包括分配并填充了系统表结构体等内容 。 #### 解决编译错误的方法 针对与 `ImageHandle` 或者 `SystemTable` 相关的编译问题,可以尝试以下几个方向来排查原因: 1. 确认头文件是否正确引入; - 对应的 `.h` 文件应该被包含进来以便获取必要的数据声明。 2. 检查宏定义是否存在冲突; - 如果项目中有重复定义或者条件编译影响到了这些基本类型的解析过程,则可能导致无法识别的情况发生。 3. 审视链接选项是否有误; - 当缺少某个外部符号引用的时候(比如找不到实际实现),就需要核查目标平台上所使用的工具链以及相应的库路径设置是否恰当。 通过上述分析可以看出,理解好 `UefiLibConstructor` 及其上下文中涉及到的关键概念对于顺利开展基于EDKII的应用开发至关重要。 ```c // Example Code Snippet showing how a typical constructor might look like. #include <Uefi.h> #include <Library/UefiLib.h> EFI_STATUS EFIAPI MyCustomLibConstructor(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable){ // Initialize global variables here using passed parameters gST = SystemTable; gBS = SystemTable->BootServices; return EFI_SUCCESS; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值