UEFI——保姆级教程的HelloWold Application

HelloWorld Application

  • 前言
  • 为什么是HelloWorld
  • Application又是什么
  • 代码部分
    • test_application.c文件
    • test_application.inf
    • 编译运行
  • 总结

前言

毕业之后工作开始接触UEFI,现在为止也不过短短的四个月,UEFI开发涉猎面广,知识体系庞杂,参考文献却相对少的可先,所以在此将自己学习过程中一些问题、成果、问题、经验进行一个记录,一来方便自己的复习,二来能给一些入坑的伙伴一点方向。
我自己磕磕绊绊,发现书、经验贴上的知识都是高屋建瓴,很少从基础一点一点说起,让我摸不到头脑,因此我从最简单的地方一点一点把初学者可能遇到的问题写下来,帮助大家少走弯路。

为什么是HelloWorld

我本人是从C++语言转到这个方向,不能说毫不相关,只能说关系不大。UEFI中主要使用的还是C语言,但这不是主要的问题,我觉得从我的角度出发,对于一个UEFI的初学者,理解UEFI整个过程中的概念、流程才是最难的。
通过网上的资料我们能够很容易的知道UEFI是OS前的一个过程,相当于传统BIOS的替代品或者说是优化版本。但是又有几个人能够详细的说明白传统BIOS在OS前是怎么工作的?可能对于大神很容易,但是对我,这个问题很难。所以我更加不能理解UEFI的详细内容。Lucky,经过这几个月,我想明白了,我可以慢慢的去理解具体的结构,转而把实践放在第一位,遇到问题之后再去解决研究可能效果更好。
在学习C、C++语言的时候,我们写的第一个程序一般就是HelloWorld,简单的一句HelloWorld,向我们展示了程序的魅力,更重要的是,直观的展现了Code与Cout的关系。听别人说代码很抽象,但是如果自己能够在代码上进行修改,比如把HelloWorld改成干啥呢 老铁,然后在屏幕上观察两次输出的不同就非常的直观。按照这个思路,我选择了HelloWorld作为自己学习UEFI的第一个小目标:在屏幕上输出自己的HelloWorld

Application又是什么

如果现在你一定要让我解释清楚什么是UEFI中的Application模块,那我只能说一声打扰了。对于Driver、Application这种划分,目前我自己也是迷迷糊糊,因此,也不敢在此胡言乱语。但是为了能够让初学的人理解,我还是想用我自己的方式介绍一下,此处纯属个人理解,不保证正确性,如有错误请见谅。
首先,前面已经说了UEFI的过程细节我并不全部了解,但为了不影响后续的学习和说明,我将整个UEFI过程进行了一句话概括:UEFI就是为OS创造完备运行环境的准备阶段。也就是说,UEFI运行完成之后没就算没有进入传统的OS比如Windows、Linux,也能够通过某种方式进行类似操作系统的各种操作——最简单的操作就是输入输出。
UEFI的大牛们已经贴心的为我们准备好了UEFI Shell(把他想象成基于UEFI的OS),我们就可以在UEFI Shell下面安装自己的Application,并且让自己的Application执行相应的功能。这样是不是容易理解了?UEFI Application 就是 基于UEFI Shell的能够执行任务的Application(相当于OS下的软件)。Application加载的过程就可以看成是软件运行的过程,Application运行的结果就是软件执行的结果。Ok?

注意:本节的解释只是为了容易理解,和实际UEFI的运行无关!!!

代码部分

说了这么多,可能你也是蒙圈的,是的,刚开始学的我也是,那既然不明白,不如直接上Code。前面已经说过了UEFI 是C语言主打,那么我们就从.C文件开始。

test_application.c文件

代码直接上

#include 

EFI_STATUS
EFIAPI
Test_Application_Entry (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
){
	SystemTable->ConOut->OutputString(SystemTable->ConOut,L"HelloWorld\n");
	return EFI_SUCCESS;
}

上面就是想要输出HelloWorld的最简单的C代码。代码很简单核心只有一行,但是对于啥也不懂的新人(比如我),刚刚开始学的时候可能就要发问了 EFI_SYSTEM_TABLE 表示什么啊?EFI_HANDLE表示什么啊?SystemTable是什么样子的啊?我现在告诉你,作为一个新人,你不需要知道的非常详细,你需要知道的就是如下几点:

  1. 这是一个入口函数,这种函数的开头和结尾是固定的格式,UEFI Application的入口函数都必须这么写。
    格式模板:
EFI_STATUS
EFIAPI
FunctionName(
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
){
	//Code Body
	return EFI_SUCCESS;
}
  1. UEFI Application的C代码都必须包含 文件。这个文件定义了最最基础的结构,是整个UEFI程序搭建的基础。
  2. SystemTable->ConOut->OutputString(SystemTable->ConOut,L"HelloWorld\n");是Application中输出的固定语句,将想要输出的内容进行替换即可。(替换哪里不需要说吧)

掌握了以上三点,本片文章C代码部分的精髓你就已经掌握了。

test_application.inf

在UEFI的开发中,会看到众多的文件格式,包括但是不限于:.c .h .sdl .inf .dec .dsc .fdf .sd,涉及到的我们在一个一个的忽悠你 解释。本篇文章中除了.c文件外需要自己编写的文件就是.inf文件。
.inf(Module Information File)文件是模块挖个坑,后面写文章总结这些个用语)的工程文件,其作用相当于Makefile文件或者VisualStudio的.proj文件1。一句话概括就是:.inf文件是告诉编译器模块可能使用的其他模块以及本模块的基本情况。
上代码:

[Defines]
  INF_VERSION                = 0x00010015
  BASE_NAME                  = Test_Application
  FILE_GUID                  = 16BEFBED-60DC-4EA2-8E81-A3430A6C2117
  MODULE_TYPE                = UEFI_APPLICATION
  VERSION_STRING             = 1.0
  ENTRY_POINT                = Test_Application_Entry

[Sources]
  test_application.c


[Packages]
  MdePkg/MdePkg.dec

[LibraryClasses]
  UefiApplicationEntryPoint

从上面的,Code可以看出,.inf[Name]为分界分成不同的块,[Name]表示块的名称,块名独占一行才能生效。上述四个块是每一个.inf必须的块,还有一些非必须的块,用到再说。先解释上面四个块的情况:

  1. Defines块
    下面是Application的Defines块模板。
[Defines]
  INF_VERSION                = 0x00010015      
  BASE_NAME                  = XXXXX
  FILE_GUID                  = XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX  (8-4-4-4-12格式)
  MODULE_TYPE                = UEFI_APPLICATION
  VERSION_STRING             = 1.0
  ENTRY_POINT                = XXXXX

INF_VERSION 版本号 ,不赘述
BASE_NAME 表示本模块的名字,最后输出的.efi文件也是这个名字
FILE_GUID GUID是一个唯一的标记,通过这一串唯一的标记可以找到对应的模块,想象成人的身份证就比较容易理解了。
MODULE_TYPE Application的时候必须这么写。其他情况遇到再说
VERSION_STRING 不重要 1.0就行
ENTRY_POINT 本模块的入口函数的名称,就是.c文件中函数体的名称

  1. Sources 块
    这部分相对简单,只需要写出本模块使用到的相关文件就行。

  2. Package 块
    使用相对路径写出使用到的包的对应.dec文件。
    我们用到了 文件,这个文件在MdePkg下,所以引用对应的 .dec文件。

  3. LibraryClasses块
    描述本模块使用的库。所有的Application都要连接UefiApplicationEntryPoint库 ,写上就完事。

以上涉及的很多名词三言两语解释不清楚,本篇文章需要的知识已经说明或者强加给了读者,详细规范的整理可能需要等我学习后写了。

编译运行

(如果你还没有完成基本的环境安装、配置等,可以参考安装教程先将环境进行配置。)
需要的基本代码已经编译完成,接下来就是如何在UEFI Shell上让这个Application运行起来。
在此之前还需要进行一步操作,将test_application.inf文件添加到MdeModulePkg的.dscComponent部分。
UEFI——保姆级教程的HelloWold Application_第1张图片
注意了,我添加在MdeModulePkg的.dsc是因为我将这个文件夹放在了MdeModulePkg下面,如果你不是,请在对应文件夹的.dsc文件夹下添加。

为了说明文件夹的关系,这里放个插图:
UEFI——保姆级教程的HelloWold Application_第2张图片

UEFI——保姆级教程的HelloWold Application_第3张图片
UEFI——保姆级教程的HelloWold Application_第4张图片
在这里插入图片描述

接下来就可以进行编译了。
我用的是Windows系统,具体的过程如下:

  1. 在命令行窗口打开Code所在文件夹,执行edksetup.bat --nt32
    UEFI——保姆级教程的HelloWold Application_第5张图片
    执行完毕结果如图(Warning忽略即可):
    UEFI——保姆级教程的HelloWold Application_第6张图片

  2. build编译
    UEFI——保姆级教程的HelloWold Application_第7张图片

  3. 启动模拟器 执行命令加载Application
    我的模拟器位置:D:\MyWorkSpace\Build\NT32IA32\DEBUG_VS2013x86\IA32\SecMain.exe
    请大家按照自己文件的位置找模拟器
    启动后就会出现如下三个界面:
    UEFI——保姆级教程的HelloWold Application_第8张图片
    加载中(两个一样的):
    UEFI——保姆级教程的HelloWold Application_第9张图片
    加载完成(两个一样的):
    UEFI——保姆级教程的HelloWold Application_第10张图片

  4. 执行命令 运行Application
    这个地方实际上是Shell加载了生成了.efi文件(文件名与BASE_NAME 一致),最后输出对应的结果。最终结果如图。
    UEFI——保姆级教程的HelloWold Application_第11张图片

总结

本片文章主要就是手把手的教你写一个基于UEFI的HelloWorld,以此打消新手对UEFI的恐惧,让菜鸟们知道UEFI 和其他的各种代码一样是从HelloWorld起步的,让小白不至于两眼一抹黑。第一次写博客难免有疏漏不足,欢迎指正,也希望有更多志同道合的伙伴一起交流。
文末了 ,我要着重感谢几个CSDN上的大佬 @Hi,Hubery @jiangwei0512,感谢他们在我学习道路上的帮助。最后 感谢@我是管小亮 一路以来的支持和陪伴


  1. 参考来源:UEFI原理与编程(戴正华著) ↩︎

你可能感兴趣的:(UEFI,uefi)