转自:http://blog.csdn.net/sevensevensevenday/article/details/71158858

一、前言

  对UEFI应用程序和驱动程序开发人员来讲,系统表是最重要的数据结构之一,它是用户空间通往内核空间的通道。有了它,UEFI应用程序和驱动才可以访问UEFI内核、硬件资源和I/O设备。
  (1)在应用程序和驱动中访问系统表
  计算机系统进入DXE阶段后系统表被初始化,因而系统表只能用于DXE阶段以及以后的应用程序和驱动中。系统表是UEFI内核的一个全局结构体,其指针作为程序映像(Image)入口函数的参数传递到用户空间。程序映像(包括UEFI应用程序、DXE驱动程序以及UEFI驱动程序)的入口函数有统一的格式,其函数原型如下:

typedef
EFI_STATUS
(EFIAPI *EFI_IMAGE_ENTRY_POINT) (IN EFI_HANDLE ImageHandle,        //程序映像(Image)的句柄IN EFI_SYSTEM_TABLE *SystemTable  //系统表指针
);
返回值 描述
EFI_SUCCESS The driver was initialized
EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources

  (2)系统表指针从内核传递到用户空间的过程
  通常,程序映像的入口函数是_ModuleEntryPoint(当一个Image被启动服务的StartImage服务启动时,执行的就是这个入口函数)。当应用程序或驱动加载到内存形成Image后(ImageHandle是这个Image的句柄),_ModuleEntryPoint函数地址被赋值给Image对象的EntryPoint,然后Image->EntryPoint(ImageHandle,SystemTable)会被执行,最终会从Image的入口函数_ModuleEntryPoint执行到模块的入口函数(模块的入口函数是通过.inf文件中ENTRY_POINT指定的那个函数)。

二、系统表的构成

  系统表可分为如下6部分:

  • 表头:包括表的版本号、表的CRC校验码等。
  • 固件信息:包括固件开发商名字的字符串和固件的版本号。
  • 标准输入控制台、标准输出控制台、标准错误控制台。
  • 启动服务表
  • 运行时服务表
  • 系统配置表

    typedef struct {EFI_TABLE_HEADER  Hdr; ///标准UEFI表头CHAR16  *FirmwareVendor; //固件提供商UINT32  FirmwareRevision; //固件版本号EFI_HANDLE  ConsoleInHandle; //输入控制台设备句柄EFI_SIMPLE_TEXT_INPUT_PROTOCOL  *ConIn;EFI_HANDLE  ConsoleOutHandle; //输出控制台设备句柄EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *ConOut;EFI_HANDLE  StandardErrorHandle; //标准错误控制台设备EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *StdErr;EFI_RUNTIME_SERVICES  *RuntimeServices; //运行时服务表EFI_BOOT_SERVICES  *BootServices; //启动时服务表UINTN  NumberOfTableEntries;  // CongratulationTable数组的大小EFI_CONFIGURATION_TABLE  *ConfigurationTable; //系统配置表数组
    } EFI_SYSTEM_TABLE;

      系统表数据结构:

  

typedef struct {EFI_TABLE_HEADER  Hdr; ///标准UEFI表头CHAR16  *FirmwareVendor; //固件提供商UINT32  FirmwareRevision; //固件版本号EFI_HANDLE  ConsoleInHandle; //输入控制台设备句柄EFI_SIMPLE_TEXT_INPUT_PROTOCOL  *ConIn;EFI_HANDLE  ConsoleOutHandle; //输出控制台设备句柄EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *ConOut;EFI_HANDLE  StandardErrorHandle; //标准错误控制台设备EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *StdErr;EFI_RUNTIME_SERVICES  *RuntimeServices; //运行时服务表EFI_BOOT_SERVICES  *BootServices; //启动时服务表UINTN  NumberOfTableEntries;  // CongratulationTable数组的大小EFI_CONFIGURATION_TABLE  *ConfigurationTable; //系统配置表数组
} EFI_SYSTEM_TABLE;

简要介绍系统表重要组成部分:
  (1) 表头 EFI_TABLE_HEADER
  UEFI中的表通常都是以EFI_TABLE_HEADER开头,EFI_TABLE_HEADER的数据结构如下:

typedef struct {UINT64 Signature;UINT32 Revision;UINT32 HeaderSize;UINT32 CRC32;UINT32 Reserved;
} EFI_TABLE_HEADER;

  UEFI中的Signature为64位的无符号整数,为了帮助开发者的使用,EDK2提供了宏SIGNATURE_64(A,B,C,D,E,F,G,H),它用于将ASCII码串转化为64位的无符号整数。例如,EFI_SYSTEM_TABLE的Signature为SIGNATURE_64(‘I’,’B’,’I’,’ ‘,’S’,’Y’,’S’,’T’)。
  HeaderSize是整个表的长度,对系统表来讲,就是sizeof(EFI_SYSTEM_TABLE)。
  CRC32是标的校验码。计算CRC32校验码时,首先将数据结构中CRC32域清零,然后计算整张表(表大小为HeaderSize)的CRC32码,计算后将校验码填充到CRC32域。
  (2)标准的输入控制台、标准的输出控制台和标准错误控制台。
  系统表还提供了三个控制台设备以及操作三个控制台的Protocol。ConIn用于从输入控制台ConsoleInHandle读取字符,通常输入控制台为键盘。ConOut用于向输出控制台ConsoleOutHandle输出字符串,通常输出控制台为屏幕。stdErr用于向标准错误控制台StandardErrorHandle输出字符串,通常这个标准错误控制台为屏幕。这三个控制台设备以及ConIn、ConOut、stdErr三个Protocol在驱动ConSplitterDxe中被初始化。
  (3)系统配置表
  ConfigurationTable是系统配置表,它指向EFI_CONFIGURATION_TABLE数组,数组中的每一项是一个表。
  EFI_CONFIGURATION_TABLE的数据结构:

typedef struct{EFI_GUID VendorGuid; //配置表标识符VOID *VendorTable;   //指向配置表的数据
} EFI_CONFIGURATION_TABLE;

  例如,ConfigurationTable通常会包含APCI(Advance Configuration and Power Interface)表,ACPI在系统配置表中可以表示为:{gEfiAcpiTableGuid,EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_POINTER*}

三、使用系统表

  系统表是UEFI内核的全局数据结构,应用程序运行在用户空间。那么用户空间是如何获得内核空间的系统表指针的?其实在UEFI中只有一个地址空间,所有的程序都运行在RING0优先级,应用程序地址空间占用UEFI地址空间的一部分。既然用户空间和内核空间是一个整体,在应用程序内也就可以直接使用内核空间的任何地址了,那么在应用程序内,只要得到了系统表的地址,就可以使用系统表了。
  (1)在用户空间使用系统表
  系统表的地址可以通过模块的入口函数的参数得到。

#include<Uefi.h>
EFI_Status
UefiMain(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE *SystemTable)
{EFI_STATUS Status;UINTN Index;EFI_INPUT_KEY Key;CHAR16 StrBuffer[3] = {0};SystemTable->BootServices->WaitForEvent(1,&SystemTable->ConIn->WaitForKey,&Index);Status = SystemTable->ConIn->ReadKeyStroke(SystemTable->ConIn,&Key);StrBuffer[0] = Key.UnicodeChar;StrBuffer[1] = '\n';SystemTable->ConOut->OutputString(SystemTable->ConOut,StrBuffer);return EFI_SUCCESS;
}

代码解析:

  • SystemTable->BootServices指向系统的启动服务表。
  • SystemTable->ConIn 指向安装在标准输入设备上的EFI_SIMPLE_TEXT_INPUT_PROTOCOL.
  • SystemTable->ConOut 指向安装在标准输出设备上的EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.
      该示例首先用WaitForEvent等待键盘事件,然后调用ConIn的ReadKeyStroke读取键盘,最后调用ConOut的OutputString服务将按键显示到屏幕。
      启动服务BootServices提供的EFI_STATUS WaitForEvent(IN UINTN NumberOfEvents, IN EFI_EVENT * Event,OUT UINTN *Index)服务用于等待Event数组中任一事件的发生。Event数组(NumberOfEvent是数组大小)中任一事件被触发时该函数返回,Index返回被触发事件在Event数组的下标。该函数是阻塞函数。
      EFI_STATUS ReadKeyStroke(EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,OUTEFI_INPUT_KEY *Key)用于读取按键,它是ConInProtocol的成员函数,第一个参数是This指针,第二个参数用于返回按键。
      EFI_STATUS OutputString(EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,CHAR16 String)用于向屏幕打印字符串,它是ConOut Protocol的成员函数,第一个参数同样是This指针,第二个参数是打印到屏幕的字符串。
      (2)在用户空间使用gST访问系统表
      上面(1)中的示例中的模块入口函数UefiMain中使用传入参数SystemTable访问系统表。EDK2为了方便开发者,提供了UefiBootServicesTableLib,在UefiLib定义了全局变量gST(指向SystemTable),gBS(指向SystemTable->BootServices)和gImageHandle(ImageHandle)。这三个全局变量在函数UefiBootServicesTableLibConstructor中被初始化,这个函数是库UefiBootServicesTableLib的构造函数,在AutoGen.c中的ProcessLibraryConstructorList被调用。而ProcessLibraryConstructorList是在UefiMain之前被调用的。
      构造函数UefiBootServicesTableLibConstructor源码:
///\EDK2\MdePkg\Library\UefiBootServicesTableLib\UefiBootServicesTableLib.c
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;
}

  gST变量定义在用户空间,它指向的系统表定义在UEFI内核中。在应用程序或驱动工程文件的[LibraryClasses]里引用UefiBootServicesTableLib后,就可以使用gST访问系统表了。

/// 示例:使用了gST
#include<Uefi.h>
#include<Library/UefiBootServicesTableLib.h>
EFI_Status
UefiMain(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE *SystemTable)
{EFI_STATUS Status;UINTN Index;EFI_INPUT_KEY Key;CHAR16 StrBuffer[4] = {0};gST -> ConOut -> OutputString(gST -> ConOut,L"Please enter any key\n");gBS->WaitForEvent(1,&gST->ConIn->WaitForKey,&Index);Status = gST->ConIn->ReadKeyStroke(gST->ConIn,&Key);StrBuffer[0] = Key.UnicodeChar;StrBuffer[1] = '\n';gST->ConOut->OutputString(gST->ConOut,StrBuffer);return EFI_SUCCESS;
}

  从代码上看,其实就是使用gST和gBS替换掉SystemTable和SystemTable -> BootServices。

四、总结

  本文主要系统表的构成、系统表从内核空间传递到用户空间的过程,以及在用户空间访问系统表的两种方法。得到系统表就可以在用户空间访问UEFI内核了,应用程序和驱动对内核的控制是通过两个核心成员BootServices和RuntimeServices。EDK2在用户空间分配了全局变量gBS和gRT指代这两个服务。

参考资料

1.《UEFI原理与编程》戴正华 著
2. UEFI Spec 2_6
3. EDK2 GitHub 

【技术】UEFI基础服务:系统表相关推荐

  1. UEFI——UEFI 基础服务

    文章目录 前言 BootService Table gBS EFI_BOOT_SERVICES SystemTable EFI_SYSTEM_TABLE SystemTable mBootServic ...

  2. ORACLE基础及系统表

    1)oracle启动顺序 启动: (1)登录ORACLE用户,启动监听 lsnrctl start (2)登录数据库,并启动(mount ,oppen(实例)) sqlplus as sysdba s ...

  3. UEFI——protocol服务详解

    文章目录 InstallProtocolInterface CoreInstallProtocolInterfaceNotify 输入参数 入参判断 CoreHandleProtocol CoreOp ...

  4. 【计算机毕业设计】542医疗服务系统

    一.系统截图(需要演示视频可以私聊) 摘  要 随着社会的发展,社会的各行各业都在利用信息化时代的优势.计算机的优势和普及使得各种信息系统的开发成为必需. 医疗服务系统,主要的模块包括查看管理员:首页 ...

  5. 如何从0设计一套实用可靠的支付服务系统?

    从产品分类.模块功能和业务流程,了解支付产品服务的设计 支付产品模块是按照支付场景来为业务方提供支付服务.这个模块一般位于支付网关之后,支付渠道之前. 它根据支付能力将不同的支付渠道封装成统一的接口, ...

  6. [系统安全] 四十四.APT系列(9)Metasploit技术之基础用法万字详解及防御机理

    您可能之前看到过我写的类似文章,为什么还要重复撰写呢?只是想更好地帮助初学者了解病毒逆向分析和系统安全,更加成体系且不破坏之前的系列.因此,我重新开设了这个专栏,准备系统整理和深入学习系统安全.逆向分 ...

  7. 网络技术入门(一):网络技术基础知识系统归结

    <网络技术入门>系列文章,分别从宏观硬件和微观数据传输角度说明网络传输过程.请期待后续系列文章. 自己写文章习惯性在每一个小节上做总结,有时候方便理解可以先看总结的内容. 本章主要是总结性 ...

  8. winsever服务系统基础教程(随堂笔记)

    本文草稿链接https://www.yuque.com/docs/share/000c4611-fa69-4881-90f4-d35ace0f9d26?# <winsever服务系统基础教程(随 ...

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

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

  10. 计算机php开发技术,基于PHP技术的计算机基础考试系统的开发

    余攀 摘 要 对于这个系统相关的开发,主要是针对于我们学校的计算机基础考试来进行的,从对于考试进行计时,先自动的进行分管卷子,然后进行试题管理等等方面进行相关的研究,这样的话可以更好地进行相应的研究, ...

最新文章

  1. 一天一个Java基础——对象和类
  2. 目标检测的常用数据处理方法!
  3. OGG目标端复制Sequence时Hang住的问题
  4. The 'Microsoft Jet OLEDB 4.0 Provider' is not registered on the local machine
  5. linux系统实用脚本,常用linux系统命令及简单小脚本
  6. oracle fiscal year,Version 0 is not defined for fiscal year 2007.
  7. ExtJs + .NET MVC 不分页处理大数据
  8. C# winform如何设置ListBox背景图或者透明背景及边框色
  9. C++编写程序:输入三角形的三边,判断三角形的类型。
  10. jrtplib使用笔记
  11. 目标检测 3—— 人脸检测
  12. UVa 106 Fermat vs. Pythagoras(毕达哥拉斯定理)
  13. 绝对受用的求职经验分享
  14. 挪威是这样养三文鱼的--转帖
  15. Freeswitch和微信小程序对接
  16. python对txt文本文件边读边写,同时读取和写入的方式修改文件
  17. pycharm新建项目环境设置详解
  18. 有关input输入框内容改变后的触发事件
  19. 磁盘显示设备未就绪,要怎么找到资料
  20. Redis第二讲【Redis基本命令和五大数据结构】

热门文章

  1. 城市信息学其五-空间经济学、城市信息学、和交通便利
  2. 31省份RD经费内部支出、全时当量、专利数、技术市场成交额(1997-2019年)
  3. 2017最新App Store 审核指南中文版
  4. 十大Excel函数(一)
  5. 数字滤波器原理及应用 借助matlab,数字滤波器原理及应用(借助MATLAB)
  6. EasyRecovery2022数据恢复绿色版
  7. MySQL学习 --- 嵌套查询
  8. python根据文件名筛选文件_Python-实现筛选出文件夹下含有特定名字的文件
  9. 常见的贴片LED封装尺寸规格表
  10. 邮件传输协议 SMTP 、POP3 、IMAP 和 Exchange 比较及联系?