- UEFI 基础教程 (二) - 运行第一个APP HelloWorld
一.代码编写:
1.edk2/OvmfPkg/HelloWorld/HelloWorld.c:
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/UefiBootServicesTableLib.h>
//ShellCEntryLib call user interface ShellAppMain
EFI_STATUS
EFIAPI
HelloWorldEntry(
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status = EFI_SUCCESS;
Print (L"[Console] HelloWorldEntry Start..\n");
Print (L"[Console] HelloWorldEntry End ... \n");
return Status;
}
2.OvmfPkg/HelloWorld/HelloWorld.inf:
[Defines]
INF_VERSION = 0x00010007
BASE_NAME = HelloWorld
FILE_GUID = 69A6DE6D-FA9F-485E-9A4E-EA70FDCFD82F
MODULE_TYPE = UEFI_APPLICATION
VERSION_STRING = 1.0
ENTRY_POINT = HelloWorldEntry
[Sources]
HelloWorld.c
[Packages]
MdePkg/MdePkg.dec
[LibraryClasses]
UefiApplicationEntryPoint
UefiLib
3.OvmfPkg/OvmfPkgX64.dsc:
diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
index c9235e4..c42708a 100644
--- a/OvmfPkg/OvmfPkgX64.dsc
+++ b/OvmfPkg/OvmfPkgX64.dsc
@@ -661,6 +661,7 @@
################################################################################
[Components]
OvmfPkg/ResetVector/ResetVector.inf
+ OvmfPkg/HelloWorld/HelloWorld.inf
二.编译生成EFI文件
1.编译
$ build -a X64 -p OvmfPkg/OvmfPkgX64.dsc -D DEBUG_ON_SERIAL_PORT
2.检查efi文件
Build/OvmfX64/DEBUG_GCC5/X64/HelloWorld.efi
三.运行HelloWorld.efi (参考:https://github.com/emvivre/uefi_hello_world)
git clone https://github.com/emvivre/uefi_hello_world.git
将文件build_and_test.sh拷贝到edk2根目录下,然后修改:
#!/bin/sh
set -e
dd if=/dev/zero of=uefi.img bs=512 count=93750
sgdisk -n 1 -t 1:ef00 -c 1:"EFI System Partition" uefi.img
dd if=/dev/zero of=part.img bs=512 count=91669
mformat -i part.img -h 32 -t 32 -n 64 -c 1
mcopy -i part.img HelloWorld.efi ::
dd if=part.img of=uefi.img bs=512 count=91669 seek=2048 conv=notrunc
sgdisk -p uefi.img
#qemu-system-x86_64 -cpu qemu64 -bios /usr/share/ovmf/OVMF.fd -drive file=uefi.img,if=ide -net none
qemu-system-x86_64 -cpu qemu64 -bios Build/OvmfX64/DEBUG_GCC5/FV/OVMF.fd -drive file=uefi.img,if=ide -net none
执行./build_and_test.sh
三.标准应用程序加载过程
编译过程:
- HelloWorld.c 首先被编译成目标文件 HelloWorld.obj
- 连接器将目标文件HelloWorld.c 和其它库连接成HelloWorld.dll
- GenFw 工具将HelloWorld.dll 转化成 HelloWorld.efi
上述过程由 build 命令自动完成,连接器在生成HelloWorld.dll时使用了/dll/entry:_ModuleEntryPoint。.efi是遵循了PE32格式的二进制文件,_ModuleEntryPoint便是这个二进制文件的入口函数。下面探讨应用程序加载过程,主要看_ModuleEntryPoint和源文件中入口函数UefiMain的关系。
-
将HelloWorld.efi 文件加载到内存
当shell中执行HelloWorld.efi时,shell首先用gBS->LoadImage()将HelloWorld.efi文件加载到内存生成Image对象,然后调用gBS->StartImag(Image)启动这个Image对象。gBS->StartImage()是一个函数指针,它实际指向的是CoreStartImage()。 -
进入映像入口函数
CoreStartImage()的主要作用是调用映像入口函数,在gBS->StartImage 的核心是Image->EntryPoint(···),它就是程序映像的入口函数,对应程序来说就是_ModuleEntryPoint 函数。进入 _ModuleEntryPoint 后,控制权才转交给应用程序(HelloWorld.efi)。
_ModuleEntryPoint主要处理三件事:
1. 初始化:初始化函数ProcessLibraryConstructorList中调用一系列构造函数
2. 调用本模块的入口函数 : ProcessModuleEntryPointList 中调用的是工程模块定义的入口函数
3. 析构:ProcessLibraryDestructorList 中调用一系列析构函数。
这三个对应的函数AutoGen.h,AutoGen.c中。 -
进入模块入口函数
在ProcessModuleEntryPointList函数中调用了工程模块的真正入口函数UefiMain。