总述

使用 HOB 的原因是因为,在 PEI 阶段内存尚未完全初始化,到了 DXE 阶段才完整初始化了内存,所以无法通过简单的内存地址传递数据,并且我们仍然有一些对于内存空间存储的需求,因此发明了 HOB 机制。

HOBs may be relocated in system memory by the HOB consumer phase. HOBs must not contain pointers to other data in the HOB list, including that in other HOBs. The table must be able to be copied without requiring internal pointer adjustment.[1]

也就是需要注意 PEI 的 HOB 内存空间在 DXE 阶段会被DxeCore relocation,所以不要用 HOB 来存放内存地址。[2]

UEFI 提供了 HOB(Hand-Off-Block)机制,即在 PEI 阶段将数据打包成数据块存放在一段连续的内存中,数据块的标识为 GUID,DXE 阶段可以通过该 GUID 在 HOB 中找到对应数据块。内存中一系列的 HOB 组成了 HOB List

如下图所示:

HOB的结构列表 HOB List

HOB 是非常重要数据结构,在 CAR 时期将会初始化好,UEFI 的早期的堆栈都基于HOB,

PEI 阶段会创建大量的 HOB,包含板级数据(UBA), silicon Hob(Memmap, RC resource), Setup,当进入PEI PostMem phase后,CAR 里的 HOB 会 shadow 到 Mem, 当进入 DXE (也就是PeiMain 调用了TempPtr.DxeIpl->Entry) 后,这些 HOB 会被 DXE driver 大量采用, 用来创建 Protocol 或者其他初始化。

由 PEI 阶段进入 DXE 阶段,PeiCore 中传递 HOB 的代码

HOB 在 PEI 阶段会被修改(写),在 DXE 和 SMM 中会被使用,原则上不推荐SMM修改 HOB。创建的单个HOB Size最大为 64K

ASSERT (DataLength <= (0xFFF8 - sizeof (EFI_HOB_GUID_TYPE)));
// Make sure that data length is not too long.

需要大数据量的HOB需要注意。

HOB 在PEI到 DXE 传送信息的过程遵循 one Producer to one Consumer 的模式,即在PEI阶段,一个 PEIM 创建一个 HOB,在 DXE 阶段,一个 DXE Driver 使用那个 HOB 并且把 HOB 相关的信息传送给其他的需要这些信息的 DXE 组件。

因此,只有 PEI Phase 才允许增加或改动这些 HOBs,当 HOB List 被传送给 DXE Phase 之后,他们就是只读的(Read Only)。

一个只读的 HOB List 的延伸就是 Handoff 信息,比如 Boot Mode,必须以别的方式来处理。比如,DXE Phase 想要产生一个 Recovery 条件,它不能 update Boot Mode,而是通过使用特殊方式的 reset call 来实现。

HOB list 是在 PEI Phase 被建立的,它存在于已经 present,initialized,tested 的Memory 中。一旦最初的 HOB List 被创建,物理内存就不能被 remapped, interleaved, 或者被后来的程序 moved

第一次进入 PeiCore 的时候,还是 CAR 阶段,内存没有初始化,InitializeMemoryServices 中调用 PeiCoreBuildHobHandoffInfoTable 开启 HOB 创建;位置在Car中;

PeiCore() 进入 InitializeMemoryServices()

InitializeMemoryServices() 进入 PeiCoreBuildHobHandoffInfoTable()

网上会找到很多资料说:

如何增加一个新的HOB到HOB List中?

PEI Phase(HOB Producer Phase)肯定包含一个指向PHIT HOB(这是HOB List的开始)的指针,然后遵循以下的步骤:

1. 确定NewHobSize,即确定要创建的HOB的大小(以Byte为单位)

2. 确定是否有足够的空闲内存分配给新的HOB(NewHobSize <= (PHIT->EfiFreeMemoryTop - PHIT->EfiFreeMemoryBottom))

3. 在(PHIT->EfiFreeMemoryBottom)处构建HOB

4. 设置PHIT->EfiFreeMemoryBottom = PHIT->EfiFreeMemoryBottom + NewHobSize~~

但是在开发中,其实我们不必操作 PHIT 中的各个堆栈指针,已经做好的有很多轮子。

代码

说了那么多,我们肯定是面向 API 编程,不用费劲心思去自己对HOB List中 PHIT的各种操作。

  1. 在PEI阶段中,需要写一个PEIM,来创建一个HOB。HOB不建议放内存地址,所以这里没用字符数组。

#include <uefi.h>
#include <Library/UefiLib.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/UefiDriverEntryPoint.h>
#include <Library/PeimEntryPoint.h>
#include <Library/PeiServicesLib.h>
#include <Library/PeiServicesTablePointerLib.h>
#include <Pi/PiHob.h>
#include <Pi/PiPeiCis.h> EFI_GUID gEfiMyPeimHobGuid = {0x34ad055e, 0x1c21, 0x4b06, {0x80, 0x2f, 0xcd, 0x8a, 0x34, 0x2b, 0xa8, 0xc9}}; // define a data struct for HOB Create
typedef struct _MY_PEIM_HOB
{
EFI_HOB_GUID_TYPE EfiHobGuidType;
CHAR8 c0;
CHAR8 c1;
CHAR8 c2;
CHAR8 c3;
CHAR8 c4;
CHAR8 c5;
CHAR8 c6;
CHAR8 c7;
CHAR8 c8;
CHAR8 c9;
} MY_PEIM_HOB; // PEI Module Entry Function
EFI_STATUS
EFIAPI
MyPeimHobEntry(
IN EFI_PEI_FILE_HANDLE FileHandle,
IN CONST EFI_PEI_SERVICES **PeiServices
)
{
EFI_STATUS Status;
DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] MyPeimHobEntry Start..\n")); MY_PEIM_HOB *Hob = NULL; // create HOB by PeiServices
Status = PeiServicesCreateHob (
EFI_HOB_TYPE_GUID_EXTENSION,
sizeof(MY_PEIM_HOB),
&Hob); // fill the HOB data [Guid + 'Hello HOB!']
if (!EFI_ERROR(Status))
{
Hob->EfiHobGuidType.Name = gEfiMyPeimHobGuid;
Hob->c0 = 'H';
Hob->c1 = 'e';
Hob->c2 = 'l';
Hob->c3 = 'l';
Hob->c4 = 'o';
Hob->c5 = ' ';
Hob->c6 = 'H';
Hob->c7 = 'O';
Hob->c8 = 'B';
Hob->c9 = '!'; } DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] MyPeimHobEntry End..\n"));
return EFI_SUCCESS;
}
[Defines]
INF_VERSION = 0x00010006
BASE_NAME = MyPeimHob
FILE_GUID = e1223900-9e9a-4a7d-8835-ff4f54f5cff9
MODULE_TYPE = PEIM
VERSION_STRING = 1.0
ENTRY_POINT = MyPeimHobEntry [Sources]
MyPeimHob.c [Packages]
MdePkg/MdePkg.dec
ShellPkg/ShellPkg.dec
MdeModulePkg/MdeModulePkg.dec [LibraryClasses]
BaseLib
PeimEntryPoint
BaseMemoryLib
DebugLib
PeiServicesLib [depex]
TRUE
  1. 在DXE阶段中,需要写一个DXE Driver,来获得之前创建好的HOB,并打印出来。

#include <uefi.h>
#include <Library/UefiLib.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/UefiDriverEntryPoint.h>
#include <Pi/PiBootMode.h>
#include <Pi/PiHob.h>
#include <Library/HobLib.h>
#include <Library/UefiBootServicesTableLib.h> EFI_GUID gEfiMyPeimHobGuid = {0x34ad055e, 0x1c21, 0x4b06, {0x80, 0x2f, 0xcd, 0x8a, 0x34, 0x2b, 0xa8, 0xc9}}; typedef struct _MY_PEIM_HOB
{
EFI_HOB_GUID_TYPE EfiHobGuidType;
CHAR8 c0;
CHAR8 c1;
CHAR8 c2;
CHAR8 c3;
CHAR8 c4;
CHAR8 c5;
CHAR8 c6;
CHAR8 c7;
CHAR8 c8;
CHAR8 c9;
} MY_PEIM_HOB; EFI_STATUS
EFIAPI
MyDxeHobEntry(
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status = EFI_SUCCESS;
DEBUG ((EFI_D_ERROR , "[MyHelloWorldHob] MyDxeHobEntry Start..\n"));
MY_PEIM_HOB *Hob; // get the first Hob connected with [gEfiMyPeimHobGuid] by Guid.
Hob = GetFirstGuidHob (&gEfiMyPeimHobGuid); // parse the hob and print debug info
if ( Hob != NULL)
{
DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] MyDxeHob Guid:%g\n", Hob->EfiHobGuidType.Name));
DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] %c \n", Hob->c0));
DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] %c \n", Hob->c1));
DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] %c \n", Hob->c2));
DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] %c \n", Hob->c3));
DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] %c \n", Hob->c4));
DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] %c \n", Hob->c5));
DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] %c \n", Hob->c6));
DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] %c \n", Hob->c7));
DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] %c \n", Hob->c8));
DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] %c \n", Hob->c9));
} DEBUG ((EFI_D_ERROR , "[MyHelloWorldHob] MyDxeHobEntry End..\n"));
return Status;
}
[Defines]
INF_VERSION = 0x00010006
BASE_NAME = MyDxeHob
FILE_GUID = 0760c3a1-a98e-4d86-8102-160d49e29e51
MODULE_TYPE = UEFI_DRIVER
VERSION_STRING = 1.0
ENTRY_POINT = MyDxeHobEntry [Sources]
MyDxeHob.c [Packages]
MdePkg/MdePkg.dec
ShellPkg/ShellPkg.dec
MdeModulePkg/MdeModulePkg.dec [LibraryClasses]
UefiDriverEntryPoint
BaseLib
BaseMemoryLib
DebugLib
PrintLib
DevicePathLib
UefiBootServicesTableLib
MemoryAllocationLib
UefiLib
HobLib [depex]
TRUE
  1. 根据PEIM和DXE Driver的类型,填写OvmfPkgX64.fdf和OvmfPkgX64.dsc,来编译写好的两个模块,具体

    OvmfPkgX64.fdf

OvmfPkgX64.dsc

最后build一下,然后再在qemu目录下创建setup-qemu-x64.bat文件,里面内容

"D:\Program Files\qemu\qemu-system-x86_64.exe" -bios "D:\edk2\edk2\Build\OvmfX64\DEBUG_VS2019\FV\OVMF.fd" -M "pc" -m 256 -cpu "qemu64" -boot order=dc -serial stdio -hda fat:rw:D:\edk2\ovmf\esp -net none

其中...\ovmf\esp文件夹需要自己创建,或者改成自己想挂载的路径下。

在qemu目录下执行qemu>setup-qemu-x64.bat | findstr MyHelloWorldHob 查看结果

在这个例子里用GUID直接找HOB,实际开发中,会使用GetHobList()找到HOB头,然后循环调用GetNextHob()来找对应类型的HOB,可以参考EDK2中的原生代码,全局搜索GetNextHob()这个函数就能找到。篇幅原因,这里先Todo.......

后记:HOB 是如何跨阶段传递的?

1. 在 Cache-As-RAM 中创建

PEI 阶段在 DRAM 初始化之前运行,因此它使用 Cache-As-RAM(CAR) 作为临时内存。

HOB 就是在这个 CAR 中被创建的。

在早期 PEI 阶段,HOB 是保存在这个临时的栈空间里的。

2. 内存初始化之后,迁移 HOB

当 DRAM 初始化完成之后,PEI Core 会分配一块真实的内存区域作为 永久内存池(Permanent Memory)

此时,PEI Core 会把已经存在于 CAR 中的 HOB 数据复制到新分配的 DRAM 区域。

在 PeiMain 中,PeiDispatcher() 会调用一个专门用于初始化永久内存的PEIM,这个PEIM根据你所编译的Pkg不同而不同。这个PEIM会进行内存初始化。

这一步叫做 HOB 的重定位(HOB Relocation)。

内核会更新 HOB list 的起始地址指针。

3. DXE 阶段读取 HOB

当转交控制权给 DXE Loader 时,PEI 会通过 EFI_PEI_HOB_POINTER 将 HOB list 的新地址传递过去。

DXE Core 通过这个地址获取 HOB 列表,从而继续处理。


  1. UEFI Platform Initialization Specification

  2. UEFI 基础教程 (七) - HOB 简单使用

【UEFI】HOB 从概念到代码的更多相关文章

  1. 第三章--Win32程序的执行单元(部分概念及代码讲解)(上 -- 多线程)

    学习<Windows程序设计>记录 概念贴士: 1. 线程描述了进程内代码的执行路径. 2. _stdcall是新标准C/C++函数的调用方法.从底层来说,使用这种调用方法参数的进栈顺序和 ...

  2. 第二章--Win32程序运行原理 (部分概念及代码讲解)

    学习<Windows程序设计>记录 概念贴士: 1. 每个进程都有赋予它自己的私有地址空间.当进程内的线程运行时,该线程仅仅能够访问属于它的进程的内存,而属于其他进程的内存被屏蔽了起来,不 ...

  3. bluetooth(蓝牙) AVRCP协议概念及代码流程解析

    一 概念 AVRCP全称:The Audio/Video Remote Control Profile (AVRCP) 翻译成中文就是:音视频远程控制协议.概念:AVRCP定义了蓝牙设备之间的音视频传 ...

  4. flume原理及代码实现

    转载标明出处:http://www.cnblogs.com/adealjason/p/6240122.html 最近想玩一下流计算,先看了flume的实现原理及源码 源码可以去apache 官网下载 ...

  5. 面向对象---java代码块

    概念:代码块是指用{}括起来的一段代码. 根据位置及声明的关键字不同,代码块可分为普通代码块.构造块.静态代码块.同步代码块4种. 1.普通代码块: 直接在方法中或在语句中定义 public clas ...

  6. 我读<代码整洁之道>--读书笔记整理

    第一章 整洁代码 "我可以列出我留意到的整洁代码的所有特点,但其中有一条是根本性的,整洁的代码总是看起来像是某位特别在意他的人写的.几乎没有改进的余地,代码作者设么都想到了,如果你企图改进它 ...

  7. UEFI EVENT 全解

    Event和Timer在UEFI当中是怎么实现的以及原理,我们先从Timer开始,然后细细的拨开隐藏在底层的实现. 先说Timer,那什么是Timer呢?其实在中文里面我们把它叫做定时/计数器,但是我 ...

  8. 压缩跟踪(CT)代码具体学习_模块1(样本的採集和扩充)

    本章主要具体解释的是compressive tracking框架中的第一部分:样本的採集和扩充部分. 在開始代码学习的前面,你须要知道的理论知识參见论文:Real-time Compressive T ...

  9. IOS开发之----代码块的使用(二)

    iOS4引入了一个新特性,支持代码块的使用,这将从根本上改变你的编程方式.代码块是对C语言的一个扩展,因此在Objective-C中完全支持.如果你学过Ruby,Python或Lisp编程语言,那么你 ...

  10. 【软件构造】第八章第三节 代码调优的设计模式和I/O

    第八章第三节 代码调优的设计模式和I/O 本节学习如何通过对代码的修改,消除性能瓶颈,提高系统性能?——代码调优.面向性 能的设计模式 Outline Java调优 代码调优的概念 单例模式(Sing ...

随机推荐

  1. Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!

    在众多开源项目中,高颜值.功能强大且部署简单的项目往往更能俘获开发者的心.然而,实际部署 Web 应用时,面对数据库.缓存.消息队列等复杂的依赖关系,常常令人头疼.Docker 的开源为我们普及了容器 ...

  2. 【文献阅读】 PVDF &阻尼&有限元建模

    1. 压电Damper原理 Piezoelectric Composite Materials - ScienceDirect 当振动传递到压电材料时,振动能量通过压电效应转化为电能,产生交流电压.所 ...

  3. kubernetes安装配置使用vGPU

    前言 AI 落地时,在某些场景下 AI 模型在训练或者是推理时,其算力要求不需要占用整卡的 GPU,比如只需要0.5卡 GPU 即可满足需求. 在这种情况下,可以使用 GPU 虚拟化技术来解决这个问题 ...

  4. 如何编写Kubernetes的YAML(一)

    什么是API对象 作为一个集群操作系统,Kubernetes 归纳总结了 Google 多年的经验,在理论层面抽象出了很多个概念,用来描述系统的管理运维工作,这些概念就叫做"API 对象&q ...

  5. Docker镜像的内部机制

    Docker镜像的内部机制 镜像就是一个打包文件,里面包含了应用程序还有它运行所依赖的环境,例如文件系统.环境变量.配置参数等等. 环境变量.配置参数这些东西还是比较简单的,随便用一个 manifes ...

  6. python 函数与方法的区别

    函数与方法的区别 并不是类中的调用都叫方法 1.函数要手动传self,方法不用传self. 2.如果是一个函数,用类名去调用,如果是一个方法,用对象去调用. class Foo(object): de ...

  7. Laravel admin 用户头像显示不出的原因及解决方法

    已经使用命令 php artisan storage:link 创建过软链接了,头像仍然不显示. 发现链接显示的是 http://localhost/storage/images/b6f0ad07b9 ...

  8. 【VMware VCF】解决 VCF 环境中组件用户密码过期问题。

    由于长时间没有启动 VCF 环境,现在在启动 SDDC Manager 组件后,UI 一直处于如下图所示的"初始化"状态.当时第一直觉就认为肯定是 VCF 环境组件的用户密码过期了 ...

  9. breach1靶机渗透学习

    靶机下载 https://www.vulnhub.com/ 找Breach-1.0,不过这个是老靶机了,可以看网上其他博客附带有靶机下载的网盘链接,靶机官网访问很慢而且网站好像出问题了排版很乱,不打也 ...

  10. Ceph-集群内分布式存储解决方案及基于Docker的部署

    打造集群高可用分布式存储Ceph很早以前在玩集群的时候就折腾过分布式存储服务来作为跨节点的数据共享和可靠存储,以前尝试过GlusterFS,但是由于读写速度实在是太低,就放弃了.见基于GlusterF ...