文章目录

  • 前言
  • BootService Table
    • gBS
    • EFI_BOOT_SERVICES
  • SystemTable
    • EFI_SYSTEM_TABLE
    • SystemTable
    • mBootServices
  • RunTimeService Table
  • 总结

前言

在实现一个基础的driver的文章中,在进行protocol安装的时候,我们用到了一个函数InstallProtocolInterface,同时也应该注意到了其使用的方式: gBS->InstallProtocolInterface,这里的gBS,在UEFI中可以说非常关键,在代码中处处可以看到他的身影。在介绍protocol相关的函数之前,我们先来整理一下gBS,还有与其重要性不相上下的gST,gRT.
既然我们是从gBS引出这些问题的,那么也从gBS开始逐步扩展进深入学习相关的内容。
介绍的过程中可能还会有一些我已经理解没有介绍、我没有理解但是需要的地方,没有介绍的会一一介绍,没有了解的慢慢学习。但是我觉得并不影响大局。

BootService Table

首先需要明确的是代码中你能看到的gBS,其指代的是UEFI 的BootService,就是UEFI提供的已经初始化完成的能够实现相应功能的服务或者说函数 被以表的形式被组织起来提供给你方便使用的东东。就是说你可以直接使用这个表中已有的函数来实现相应的功能。
上述这是我自己的理解,可以看一下书中给BootService的一个相对正式的解释说明:

BootService,即启动服务表,是UEFI的核心数据结构。系统进入DXE阶段时启动服务表被初始化,最终通过SystemTable指针将启动服务表传递给UEFI应用程序或者驱动程序。UEFI应用和驱动可以通过gST->BootService或者gBS访问这个表。1

抽象的说的太多意义不算大 还是在代码中一点一点看最有效。前面说到了gBS又说到了的BootService ,那么先看一下这两者在代码中是如何定义的,又是如何联系起来的?

gBS

gBS的初始定义在 UefiBootServicesTableLib.c 中找到,可以看到gBS初始的时候被定义为一个空指针,而指针的赋值在下面的函数 UefiBootServicesTableLibConstructor中可以看到

// UefiBootServiceTableLib.c
EFI_HANDLE         gImageHandle = NULL;
EFI_SYSTEM_TABLE   *gST         = NULL;
EFI_BOOT_SERVICES  *gBS         = NULL;EFI_STATUS
EFIAPI
UefiBootServicesTableLibConstructor (IN EFI_HANDLE        ImageHandle,IN EFI_SYSTEM_TABLE  *SystemTable)
{//// Cache the Image Handle//gImageHandle = ImageHandle;ASSERT (gImageHandle != NULL);//// Cache pointer to the EFI System Table//gST = SystemTable;ASSERT (gST != NULL);//// Cache pointer to the EFI Boot Services Table//gBS = SystemTable->BootServices;ASSERT (gBS != NULL);return EFI_SUCCESS;
}

可以看到,gBS被定义为一个全局的指针,一开始定义为NULL,指针的类型为EFI_BOOT_SERVICES
在函数UefiBootServicesTableLibConstructor里面,给gBS进行了一个赋值SystemTable->BootServices,我们继续追踪SystemTable这个结构体,就能看到其成员BootServices的类型也是 EFI_BOOT_SERVICES,此时gBS不再为空指针,这也就标志后续就可以使用对应的服务了。


同时这个文件中还定义了另一个指针gSTgST代表了就是SystemTable
这个文件定义了在UEFI中使用最为广泛的两个指针,这两个指针又都和SystemTable相关,说明这个指针也是非常重要的,不要着急马上就会说到 暂时先pass


现在我们知道了gBS指向的内容是什么了,接下来就出现了两个问题

  1. EFI_BOOT_SERVICES类型是什么样子的?
  2. SystemTable->BootServices是什么样子的?

EFI_BOOT_SERVICES

EFI_BOOT_SERVICES这个类型并不复杂,在文件 UefiSpec.h中可以找到其定义:

这是一个非常大的结构体,结构体中的成员都是重新定义过的。我们找一个熟悉成员InstallProtocolInterface来看一下具体的情况
可以看到这个成员的类型是EFI_INSTALL_PROTOCOL_INTERFACE,查看定义:

// UefiSpec.h
typedef
EFI_STATUS
(EFIAPI *EFI_INSTALL_PROTOCOL_INTERFACE)(IN OUT EFI_HANDLE               *Handle,IN     EFI_GUID                 *Protocol,IN     EFI_INTERFACE_TYPE       InterfaceType,IN     VOID                     *Interface);

可以看到这是一个函数指针类型的成员,也就是说最终这个指针会指向一个函数,输入参数为以上四个参数。
结构体中的其他成员都是同样的情况,类型均为函数指针(UEFI中的protocol都是这样的)想要知道实际上指向什么 ,下面就需要看一下SystemTable->BootServices的具体内容。
而想要知道具体这个实体是什么样子的,首先要追踪一下SystemTable是什么样子的。

SystemTable

前面说gBS的时候,说到了一个文件叫做UefiBootServiceTableLib.c,在这个文件中我们不仅可以看到gBS的初始化情况,同样的我们还能够看到gST初始化的具体情况。
一言以蔽之,和gBS情况几乎完全相同,首先定义了一个类型为EFI_SYSTEM_TABLE 的空指针gST,然后在函数中,将指针赋值为SystemTable,(类型仍为EFI_SYSTEM_TABLE)。
这里我们想要深入了解,同样产生了两个问题

  1. EFI_SYSTEM_TABLE类型是什么样子的?
  2. SystemTable是什么样子的?

EFI_SYSTEM_TABLE

上述的第一个问题是很容易回答的,在代码UefiSpec.h中稍加查找就能发现其原型

上面就是EFI_SYSTEM_TABLE的结构体,可以看到,这个结构体仍然是由多种类型的结构体成员组成的,其中有变量的形式还有指针的形式。还包括了我们之前讨论的BootServices和我们将要讨论的RuntimeServices。可以看到这两个都是指针。也就是最终我们将这个指针指向的位置就是结构体的实现位置。
(这个表中的内容是非常值得我们深入挖掘的,但是本篇文章主要讨论的就是 基础服务所以不对Console等做过多的讨论。)

SystemTable

讨论完结构体的定义之后,我们来到最最最关键的问题SystemTable是什么样子的?
之所以说他关键,

  1. 是因为第一这是这一节的关键
  2. 是之前讨论gBS的时候还遗留了一个问题SystemTable->BootServices是什么样子的?
  3. 如果我们后续要讨论gRT,也是与此相关的
    在之前的函数中,SystemTable似乎都是以一种入参的形式出现的(不信的话自己去看前面的HelloWorld Driver ,甚至在初始化’gST’的时候都是这样的),但是作为一个坚定的唯物主义战士,我坚信不可能有无缘无故的数据出现,只不过是我才疏学浅还没有解决SystemTable是从哪里来、怎么来的问题。
    为了解决这一问题,我详细的看了一下《UEFI编程与原理》1一书的第五章 UEFI基础服务。从这里找到了一些蛛丝马迹:

系统进入DXE阶段后系统表会被初始化,因而系统表只能用于DXE阶段以及以后的应用程序和驱动中。
这句话告诉我们一个重要的信息:SystemTable是在DXE阶段被初始化的。

DXE阶段,我们首先就应该去DxeMain函数里面去看有没有我们想要的东西,果不其然,在这个函数中我们看到这样一部分:


//MdeModulePkg\Core\Dxe\DxeMain\DxeMain.c
//
// DXE Core Global Variables for the EFI System Table, Boot Services Table,
// DXE Services Table, and Runtime Services Table
//EFI_SYSTEM_TABLE  *gDxeCoreST = NULL;
EFI_RUNTIME_SERVICES  *gDxeCoreRT         = &mEfiRuntimeServicesTableTemplate;// ...
// ...**/
VOID
EFIAPI
DxeMain (IN  VOID  *HobStart)
{//...// Allocate the EFI System Table and EFI Runtime Service Table from EfiRuntimeServicesData// Use the templates to initialize the contents of the EFI System Table and EFI Runtime Services Table//gDxeCoreST = AllocateRuntimeCopyPool (sizeof (EFI_SYSTEM_TABLE), &mEfiSystemTableTemplate);ASSERT (gDxeCoreST != NULL);gDxeCoreRT = AllocateRuntimeCopyPool (sizeof (EFI_RUNTIME_SERVICES), &mEfiRuntimeServicesTableTemplate);ASSERT (gDxeCoreRT != NULL);gDxeCoreST->RuntimeServices = gDxeCoreRT;//...
}

UEFI开发者通过注释告诉我们,这几行代码就是在为System TableRuntime Service Table分配内存,并且用临时的内容初始化了这两个表。
从代码中变量的名称其实可以推测出来,gDxeCoreST代表的就是System Table,它临时被mEfiSystemTableTemplate初始化了。

EFI_SYSTEM_TABLE  mEfiSystemTableTemplate = {{EFI_SYSTEM_TABLE_SIGNATURE,                                           // SignatureEFI_SYSTEM_TABLE_REVISION,                                            // Revisionsizeof (EFI_SYSTEM_TABLE),                                            // HeaderSize0,                                                                    // CRC320                                                                     // Reserved},NULL,                                                                   // FirmwareVendor0,                                                                      // FirmwareRevisionNULL,                                                                   // ConsoleInHandleNULL,                                                                   // ConInNULL,                                                                   // ConsoleOutHandleNULL,                                                                   // ConOutNULL,                                                                   // StandardErrorHandleNULL,                                                                   // StdErrNULL,                                                                   // RuntimeServices&mBootServices,                                                         // BootServices0,                                                                      // NumberOfConfigurationTableEntriesNULL                                                                    // ConfigurationTable
};

gDxeCoreRT代表Runtime Service Table,临时被mEfiRuntimeServicesTableTemplate初始化了。(这个部分后面在详细讨论) ^7dc49d


刚刚初始化完成的SystemTable成员大多是都为0或者为空指针,(BootServices指针除外 很重要!!!!后面会再来讨论这个问题)这就表示在代码中还有其他地方对SystemTable进行了赋值操作,这才有了我们经常使用的SystemTable
既然找到了初级形态的gDxeCoreST,那么只需要在整个代码中继续搜索gDxeCoreST,看看什么位置用到了他,就可以知道赋值操作到底发生在什么位置了。在代码中我们能够找到的使用这个参数的位置有很多 ,但是我们重点关注的应该是DxeMain.c函数中调用的的下面两个函数

  Status = CoreInitializeImageServices (HobStart);// ...CoreDispatcher ();

这两个函数分别实现了初始化ImageService和Dispatcher Driver,
如果一层一层的去查找这两个函数就会发现这样一条语句

  Image->Info.SystemTable = gDxeCoreST;

就这样,gDxeCoreST被赋值给了我们平时使用的SystemTable
(DxeMain函数我觉得可以单独拿出来说一下 这个函数我觉得还蛮重要的 包括SystemTable中的服务都是怎么具体实现的都是可以深挖的东西 等我搞明白再说 先挖坑…)
至此,我觉得我已经将SystemTable从哪里来的讲的大致清楚了。如果还要详细说,这中间还有几个问题没有说清楚:

  1. SystemTable中的服务是在哪里 、如何被一步步构建起来的
  2. Image->Info.SystemTable = gDxeCoreST中的Image具体代表的是什么?
  3. 为什么在执行ApplicationDriver的时候都会将Image作为参数传递进去?
    这几个问题我觉得可能大家都有自己的大致概念,我也是,但是想要准确的说出这些事情的来龙去脉可能真的有点难。这里我们还是先回归这边文章的重点继续沿着这条线向下

mBootServices

前面在第二节介绍SystemTable我们就已经看到,在初始化的时候,BootServices指针就已经被指向了一个结构体mBootServices。第一节中,我们推断过,SystemTable->BootServices就是gBS的实体。这二者结合我们就能够回答之前提出的问题:SystemTable->BootServices是什么样子的?
接下来就是最最激动人心的时刻了,一起来看一下mBootServices的庐山真面目(其实就是在DxeMain.c文件的开始部分)

在这里我们就可以看到每一个成员已经被初始化成了一个货真价实的函数了。找一下我们之前说过的InstallProtocolInterface的本体是什么样子的吧。

EFI_STATUS
EFIAPI
CoreInstallProtocolInterface (IN OUT EFI_HANDLE      *UserHandle,IN EFI_GUID            *Protocol,IN EFI_INTERFACE_TYPE  InterfaceType,IN VOID                *Interface)
{return CoreInstallProtocolInterfaceNotify (UserHandle,Protocol,InterfaceType,Interface,TRUE);
}

emm … 还是很简单的关于这个函数具体什么意思 下一篇在详细的去说啦。这一篇还是主要为了找清楚这些我们平时经常使用的函数是怎么来的。这不就找到了吗。这样下次想要知道想用的函数原型是什么样子的时候就不会再一脸懵逼。
反正我自己刚开始看UEFI代码的时候,我就是由于找不到函数原型一脸懵逼的 ,大佬肯定不会了
到这里 BootService的介绍基本完成。注意一下,我这里介绍的都是代码中能看到的,至于顺序是怎么样的,在哪一个阶段才能够使用,我想要留到介绍UEFI启动阶段的时候再说。但是这个阶段我自己现在还是没有完全搞得十分清楚,已经在学习了。。。有机会或者说下次一定?

RunTimeService Table

在最开始的时候,说了三个常用的重要的指针gBT,gST,gRT,已经说完了前两个了,还剩下最后一个gRT。现在就来说一下。
其实第二节在讨论SystemTable的时候已经说过gDxeCoreRT的初始化情况,如果不记得了再写一遍帮助你回想一下:

//MdeModulePkg\Core\Dxe\DxeMain\DxeMain.c
//
// DXE Core Global Variables for the EFI System Table, Boot Services Table,
// DXE Services Table, and Runtime Services Table
//EFI_SYSTEM_TABLE  *gDxeCoreST = NULL;
EFI_RUNTIME_SERVICES  *gDxeCoreRT         = &mEfiRuntimeServicesTableTemplate;// ...
// ...**/
VOID
EFIAPI
DxeMain (IN  VOID  *HobStart)
{//...// Allocate the EFI System Table and EFI Runtime Service Table from EfiRuntimeServicesData// Use the templates to initialize the contents of the EFI System Table and EFI Runtime Services Table//gDxeCoreST = AllocateRuntimeCopyPool (sizeof (EFI_SYSTEM_TABLE), &mEfiSystemTableTemplate);ASSERT (gDxeCoreST != NULL);gDxeCoreRT = AllocateRuntimeCopyPool (sizeof (EFI_RUNTIME_SERVICES), &mEfiRuntimeServicesTableTemplate);ASSERT (gDxeCoreRT != NULL);gDxeCoreST->RuntimeServices = gDxeCoreRT;//...
}

此时 gDxeCoreRT已经被初始化成为了mEfiRuntimeServicesTableTemplate ,接下来看一下mEfiRuntimeServicesTableTemplate的情况:

// DxeMain.c
EFI_RUNTIME_SERVICES  mEfiRuntimeServicesTableTemplate = {{EFI_RUNTIME_SERVICES_SIGNATURE,                               // SignatureEFI_RUNTIME_SERVICES_REVISION,                                // Revisionsizeof (EFI_RUNTIME_SERVICES),                                // HeaderSize0,                                                            // CRC320                                                             // Reserved},(EFI_GET_TIME)CoreEfiNotAvailableYetArg2,                       // GetTime(EFI_SET_TIME)CoreEfiNotAvailableYetArg1,                       // SetTime(EFI_GET_WAKEUP_TIME)CoreEfiNotAvailableYetArg3,                // GetWakeupTime(EFI_SET_WAKEUP_TIME)CoreEfiNotAvailableYetArg2,                // SetWakeupTime(EFI_SET_VIRTUAL_ADDRESS_MAP)CoreEfiNotAvailableYetArg4,        // SetVirtualAddressMap(EFI_CONVERT_POINTER)CoreEfiNotAvailableYetArg2,                // ConvertPointer(EFI_GET_VARIABLE)CoreEfiNotAvailableYetArg5,                   // GetVariable(EFI_GET_NEXT_VARIABLE_NAME)CoreEfiNotAvailableYetArg3,         // GetNextVariableName(EFI_SET_VARIABLE)CoreEfiNotAvailableYetArg5,                   // SetVariable(EFI_GET_NEXT_HIGH_MONO_COUNT)CoreEfiNotAvailableYetArg1,       // GetNextHighMonotonicCount(EFI_RESET_SYSTEM)CoreEfiNotAvailableYetArg4,                   // ResetSystem(EFI_UPDATE_CAPSULE)CoreEfiNotAvailableYetArg3,                 // UpdateCapsule(EFI_QUERY_CAPSULE_CAPABILITIES)CoreEfiNotAvailableYetArg4,     // QueryCapsuleCapabilities(EFI_QUERY_VARIABLE_INFO)CoreEfiNotAvailableYetArg4             // QueryVariableInfo
};

看起来似乎这个结构提已经被完全的初始化了,但是只要你动动手指,随便点一个函数进去,就能发现这个结构体里面的函数其实都空的!!!这是因为,运行时服务的内容是在之后DXE跑起来之后慢慢填充的,此时这些看起来正经的函数其实并不是正经函数,而是用来占位的。
具体是怎么初始化的,我拿时间相关的函数来举例:

//EmbeddedPkg\RealTimeClockRuntimeDxe\RealTimeClock.cEFI_STATUS
EFIAPI
InitializeRealTimeClock (IN EFI_HANDLE        ImageHandle,IN EFI_SYSTEM_TABLE  *SystemTable)
{EFI_STATUS  Status;UINTN       Size;Status = LibRtcInitialize (ImageHandle, SystemTable);if (EFI_ERROR (Status)) {return Status;}Size   = sizeof (mTimeSettings);Status = EfiGetVariable ((CHAR16 *)mTimeSettingsVariableName,&gEfiCallerIdGuid,NULL,&Size,(VOID *)&mTimeSettings);if (EFI_ERROR (Status) ||!IsValidTimeZone (mTimeSettings.TimeZone) ||!IsValidDaylight (mTimeSettings.Daylight)){DEBUG ((DEBUG_WARN,"%a: using default timezone/daylight settings\n",__FUNCTION__));mTimeSettings.TimeZone = EFI_UNSPECIFIED_TIMEZONE;mTimeSettings.Daylight = 0;}SystemTable->RuntimeServices->GetTime       = GetTime;SystemTable->RuntimeServices->SetTime       = SetTime;SystemTable->RuntimeServices->GetWakeupTime = GetWakeupTime;SystemTable->RuntimeServices->SetWakeupTime = SetWakeupTime;Status = gBS->InstallMultipleProtocolInterfaces (&mHandle,&gEfiRealTimeClockArchProtocolGuid,NULL,NULL);return Status;
}

这里才是真正初始化时间函数的位置。现在知道了函数初始化的位置。接下来就是怎么将RuntimeServicegRT联系起来。这个问题其实其实巨巨巨简单,看下面

//MdePkg\Library\UefiRuntimeServicesTableLib\UefiRuntimeServicesTableLib.cEFI_STATUS
EFIAPI
UefiRuntimeServicesTableLibConstructor (IN EFI_HANDLE        ImageHandle,IN EFI_SYSTEM_TABLE  *SystemTable)
{//// Cache pointer to the EFI Runtime Services Table//gRT = SystemTable->RuntimeServices;ASSERT (gRT != NULL);return EFI_SUCCESS;
}

看到没 就是这么轻松

总结

整体上从代码的角度介绍了常用的UEFI 基础服务的初始化以及赋值流程,为接下来的protocol函数深入研究铺垫了一下。

写笔记真的比学习都难 ,很佩服那些坚持记录的人,CSDN真的很不方便,最近正在研究有没有什么比较好用的软件进行记录。可能找到之后就上传图片或者PDF,这样也能省去调整格式的时间。


  1. UEFI原理与编程 ↩︎ ↩︎

UEFI——UEFI 基础服务相关推荐

  1. 【UEFI基础】UEFI变量基础2

    说明 之前已经写过一篇变量相关的文章[UEFI基础]UEFI变量基础,该文章使用的是模拟的变量,而本文更接近于实际的变量模块. 环境设置 为了测试BIOS的变量功能,需要修改QEMU的启动选项,如下所 ...

  2. 虚拟化基础架构Windows 2008篇之1-虚拟化基础服务概述

    看完文章,请顺手投我一票(王春海),谢谢 http://edu.51cto.com/activityvote/voteRanking 京东6.18结束了,IT人自己的6.18来了!!!51CTO学院3 ...

  3. 微信基础服务肯定不收费

    微信要收费了?连日来,发改委某专家的一句"如果微信不该收费,那短信为什么要收费呢?"激起了人们对于"微信是否会进入收费时代"的极大关注.3月31日,工信部部长苗 ...

  4. 阿里云Kubernetes实战2–搭建基础服务

    前言: 在系列的第一篇文章中,我已经介绍过如何在阿里云基于kubeasz搭建K8S集群,通过在K8S上部署gitlab并暴露至集群外来演示服务部署与发现的流程.文章写于4月,忙碌了小半年后,我才有时间 ...

  5. Nmap扫描教程之网络基础服务DHCP服务类

    Nmap扫描教程之网络基础服务DHCP服务类 Nmap网络基础服务 网络基础服务是网络正常工作的基石,常见的网络基础服务包括DHCP服务和DNS服务.其中,DHCP服务用来为计算机动态分配IP地址:D ...

  6. es用canals怎么和mysql同步_搬运基础服务到kubernetes,遇这3类大坑怎么破?

    工作中需要将原本部署在物理机或虚拟机上的一些基础服务搬到kubernetes中,在搬的过程中遇到了不少坑,笔者在此特别分享一下所遇到的问题及相应的解决方法~ 一.异常网络引起的问题 之前使用redis ...

  7. linux网络设置与基础服务命令(ifconfig、hostname、route、netstat、ss、ping、traceroute、nslookup、route)

    文章目录 linux网络设置与基础服务 前言 查看网络配置 使用ifconfig命令查看网络接口地址 查看指定网络接口信息 使用 hostname命令查看当前主机名称 使用route命令查看路由表条目 ...

  8. 【南京】.Net 开源基础服务线下技术交流会

    南京地区的.net开发人员对基础服务这块感兴趣的,欢迎大家参加及会后继续交流,踊跃参与!若对基础服务相关有深度技术交流的,后续交换联系方式,可一起深度合作. .NET技术行业落地分享交流会 邀请南京地 ...

  9. .Net 分布式云平台基础服务建设说明概要

    1)  背景 建设云平台的基础框架,用于支持各类云服务的业务的构建及发展. 2)  基础服务 根据目前对业务的理解和发展方向,总结抽象出以下几个基础服务,如图所示 3)  概要说明 基础服务的发展会根 ...

最新文章

  1. 近期活动盘点:基于雷达图像预测未来降水参赛经验分享、大数据基础设施讲座、药品行业分析及大数据应用思享会(11.22-11.29)
  2. LVS的DR模式配置
  3. .团队组建及项目启动
  4. 完美解决NV4_disp.dll已正常停止工作”蓝屏问题
  5. QT+FFMPEG实现视频播放
  6. mysql workbench 从model建库_使用MySQL Workbench进行数据库设计——MySQL Workbench用法总结...
  7. Ubuntu查看CPU和MEM的使用率
  8. PAI算法组件详解:PLDA
  9. 【log4net】配置文件
  10. 莫比乌斯函数之和(51nod 1244)
  11. spring 整合struts
  12. 微信小程序 点击复制文本到剪贴板
  13. 6.3创建自己执行的二进制文件
  14. anaconda conda 切换为清华源
  15. 公安如何通过大数据破案?知识图谱实现公安情报分析(人工智能大数据公司)
  16. Yalmip最优化求解器+matlab | 教程(一)
  17. pentaho资源库迁移-MySQL
  18. linux配置路由器命令手册,Linux配置路由器
  19. Android学习视频推荐
  20. 石墨烯散热科技视频发布,华为Mate20系列将带来极致游戏体验

热门文章

  1. Android组件化原理
  2. iOS 多线程 swift5 GCD 自己消化的
  3. 初学JAVA 基础(二)JVM、JRE、JDK之间的关系
  4. 大数据下报警运营重铸“安全防范”新思维
  5. 1094 谷歌的招聘 (20 分)
  6. struts2综合漏洞扫描工具
  7. 如何优雅的获取 Mac OS 系统 IP 地址?
  8. 大家信夫:推动城市信用建设提升企业发展
  9. 真正的程序员 (转)
  10. linux 安装xdebug