编写UEFI标准应用程序工程模块小结

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Kair_Wu/article/details/48343989


1.概述

在EDK2根目录下,有很多以*Pkg命名的文件夹,每一个这样的文件夹称为一个Package。“包”是一组模块及平台描述文件(.dsc文件)、包声明文件(.dec文件)组成的集合。模块是UEFI系统的一个特色。模块(可执行文件,即.efi文件)像插件一样可以动态地加载到UEFI内核中。对应到源文件,EDK2中的每个工程模块由元数据文件(.inf文件)和源文件(有些情况下也可以包含.efi文件)组成。

在Linux下编程,除了编写源代码之外,还要编写Makefile文件。在Windows下使用VS(Visual Studio)时通常要建立工程文件和源文件。与之相似,在EDK2环境下,我们除了要编写源文件外,还要为工程编写元数据文件(.inf)。.inf文件与VS的工程文件及Linux下的Makefile文件功能相似,用于自动编译源代码。包相当于VS中的项目,.dsc文件则相当于VS项目的.sln文件;模块相当于VS项目中的工程,.inf文件则相当于VS工程的.proj文件。

也就是说我们不但要编写一个C程序的源文件,还要编写一个.inf的工程文件。

2.源文件编写

2.1.C源文件编写

下面是一个简单模块的源程序,它仅有一个函数UefiMain。UefiMain就是这个模块的入口函数,实现功能是向标准输出设备输出字符串“HelloWorld”。

源码如下:

 

一般来说,标准应用程序至少要包含以下两个部分。

1)头文件:所有的UEFI程序都要包含头文件Uef?i.h。Uef?i.h定义了UEFI基本数据类型及核心数据结构。

2)入口函数:UEFI标准应用程序的入口函数通常是Uef?iMain。之所以说通常是Uef?iMain而不是说必须是Uef?iMain,是因为入口函数可有开发者指定。UefiMain只是一个约定俗成的函数名。入口函数由工程文件Uef?iMain.inf指定。虽然入口函数的函数名可以变化,但其函数签名(即返回值类型和参数列表类型)不能变化。

①入口函数的返回值类型是EFI_STATUS。

在UEFI程序中基本所有的返回值类型都是EFI_STATUS。它本质上是无符号长整数。

最高位为1时其值为错误代码,为0时表示非错误值。通过宏EFI_ERROR(Status)可以判断返回值Status是否为错误码。若Status为错误码,EFI_ERROR(Status)返回真,否则返回假。

EFI_SUCCESS为预定义常量,其值为0,表示没有错误的状态值或返回值。

②入口函数的参数ImageHandle和SystemTable。

.efi文件(UEFI应用程序或UEFI驱动程序)加载到内存后生成的对象称为Image(映像)。ImageHandle是Image对象的句柄,作为模块入口函数参数,它表示模块自身加载到内存后生成的Image对象。

SystemTable是程序同UEFI内核交互的桥梁,通过它可以获得UEFI提供的各种服务,如启动(BT)服务和运行时(RT)服务。SystemTable是UEFI内核中的一个全局结构体。

向标准输出设备打印字符串是通过SystemTable的ConOut提供的服务完成的。ConOut是EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL的一个实例。而EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL的主要功能是控制字符输出设备。向输出设备打印字符串是通过ConOut提供的OutputString服务完成的。该服务(函数)的第一个参数是This指针,指向EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL实例(此处为ConOut)本身;第二个参数是Unicode字符串。关于Protocol和This指针将在第4章详细介绍。简而言之,这条打印语句的意义就是通过SystemTable→ConOut→OutputString服务将字符串L“HelloWorld”打印到SystemTable→ConOut所控制的字符输出设备

2.2.工程文件编写

要想编译Main.c,还需要编写.inf(Module Information File)文件。.inf文件是模块的工程文件,其作用相当于Makefile文件或Visual Studio的.proj文件,用于指导EDK2编译工具自动编译模块。工程文件中,字符#后面的内容表示注释。

工程文件分为很多个块,每个块以“[块名]”开头,“[块名]”必须单独占一行。有些块是所有工程文件都必需的块,这些块包括[Defines]、[Sources]、[Packages]和[LibraryClasses]等。还有一些非必须的块,如Protocol,Guids,BuildOptions,Pcd等模块。

 

下面是程序的.inf文件

 

文件模块的说明:

1.[Defines]块

[Defines]块用于定义模块的属性和其他变量,块内定义的变量可被其他块引用。

(1)属性定义语法

属性名=属性值

(2)属性

块内必须定义的属性包括:

INF_VERSION:INF标准的版本号。EDK2的build会检查INF_VERSION的值并根据这个值解释.inf文件。最新的INF标准版本号为0x00010016,前半部分为主版本号,后半部分为次版本号。通常将INF_VERSION设置为0x00010005即可。

BASE_NAME:模块名字字符串,不能包含空格。它通常也是输出文件的名字。例如,Base_Name为UefiMain,则最终生成的文件为UefiMain.efi。

FILE_GUID:每个工程文件必须有一个8-4-4-4-12格式的GUID,用于生成固件。例如,FILE_GUID = 6987936E-ED34-ffdb-AE97-1FA5E4ED2117。

VERSION_STRING:模块的版本号字符串。例如,可以设置为VERSION_STRING= 1.0。

MODULE_TYPE:定义模块的模块类型,可以是SEC、PEI_CORE、PEIM、DXE_CORE、DXE_SAL_DRIVER、DXE_SMM_DRIVER、UEF_DRIVER 、DXE_DRIVER、DXE_RUNTIME_DRIVER、UEFI_APPLICATION、BASE中的一个。对标准应用程序工程模块来说,MODULE_TYPE的值为UEFI_APPLICATION。

ENTRY_POINT:定义模块的入口函数。在上文中我们提到UefiMain是模块的入口函数,就是在此处通过设置ENTRY_POINT的值为UefiMain得到的。

2.[Sources]块

[Sources]用于列出模块的所有源文件和资源文件。

(1)语法

块内每一行表示一个文件,文件使用相对路径,根路径是工程文件所在的目录。作为示例的标准应用程序工程模块仅含有一个源文件

(2)体系结构相关块

.$(Arch)是可选项,可以是IA32、X64、IPF、EBC、ARM中的一个,表示本块适用的体系结构。[Sources]块适用于任何体系结构。例如,编译32位模块时(即在build命令选项中使用了-a IA32选项),工程将包含[Sources]和[Sources.IA32]中的源文件;编译64位模块时,工程将包含[Sources]和[Sources.X64]中的源文件。

 3.[Packages]块

[Packages]列出本模块引用到的所有包的包声明文件(.dec文件)。

(1)语法

[Packages]块内每一行列出一个文件,文件使用相对路径,相对路径的根路径为EDK2的根目录。若[Sources]块内列出了源文件,则在[Packages]块必须列出MdePkg/MdePkg.dec,并将其放在本块的首行。

4.[LibraryClasses]

[LibraryClasses]块列出本模块要链接的库模块。

块内每一行声明一个需要链接的库(库定义在包的.dsc文件中)。常用库有:UefiApplicationEntryPoint库,驱动模块必须链接UefiDriverEntryPoint库。

5.[Protocols]块

[Protocols]列出模块中使用的Protocol,严格来说,列出的是Protocol对应的GUID。如果模块未使用任何Protocol,则此块为空。这里需要列出所有用到的GUID。

6.[BuildOptions]块

[BuildOptions]指定本模块的编译和连接选项。

编译器家族可以是MSFT(Visual Studio编译器家族)、INTEL(Intel编译器家族)、GCC(GCC编译器家族)和RVCT(ARM编译器家族)中的一个。Target是DEBUG、RELEASE和*中的一个,*为通配符,表示对DEBUG和RELEASE都有效。TOOL_CHAIN_TAG是编译器名字。编译器名字定义在Conf\tools_def.txt文件中,预定义的编译器名字有VS2003、VS2005、VS2008、VS2010、GCC44、GCC45、GCC46、CYGGCC、ICC等,*表示对指定编译器家族内的所有编译器都有效。Arch是体系结构,可以是IA32、X64、IPF、 EBC或 ARM,*表示对所有体系结构都有效。CC表示编译选项。DLINK表示连接选项。

= 表示选项附加到默认选项后面。 == 表示仅使用所定义的选项,弃用默认选项。

=或==号后面是编译选项或连接选项。

3.编译和运行

当源文件和工程文件编写好之后,就可以进行编译和运行了。

编译:将该工程文件的配置到相应的包里面,在包里面的.dsc文件中的[Compontents]部分里面添加应用程序模块的工程文件,也就是.inf文件。然后,运行编译指令。

运行:执行相应的运行指令。


最后的最后,你就可以看到那个熟悉的“Hello Word”了。Oh ,哈哈哈

 

 

猜你喜欢

转载自blog.csdn.net/Kair_Wu/article/details/48343989