UEFI编程入门-环境准备

环境准备

准备

需要安装Visual Studio 2019、Python 2.7+、IASL编译器、NASM编译器,并将后两者加入PATH。

其中IASL编译器下载地址https://www.intel.com/content/www/us/en/developer/topic-technology/open/acpica/download.html 。NASM编译器下载地址https://www.nasm.us/pub/nasm/releasebuilds/2.16.03/win64/

下载EDK2开发包:

1
2
git clone https://github.com/tianocore/edk2.git
git clone https://github.com/tianocore/edk2-libc.git

EDK2中通过Submodule方式提供了些必要的库文件和编译所需源文件,这里对他们进行初始化和更新,在edk2目录下:

1
git submodule update --init #以后再更新就不用加--init了

再在edk2目录下编译BaseTools,这里要打开Visual Studio的x86 Native Tools Command Prompt。编译好的BaseTools在BaseTools目录中。

1
edksetup.bat Rebuild

接着为了方便后续添加新开发包,这里编写一个设置开发工具路径的批处理。在edk2和edk2-libc同目录下,新建mybuild.bat,添加:

1
2
3
4
set WORKSPACE=%CD%
set EDK_TOOLS_PATH=%CD%\edk2\BaseTools
set CONF_PATH=%CD%\edk2\conf
set PACKAGES_PATH=%CD%\edk2;%CD%\edk2-libc

UEFI编译依赖edk2/Conf目录下的target.txt和tools_def.txt。分别指定编译默认参数和编译工具链。例如target.txt的设置:

1
2
3
4
5
6
ACTIVE_PLATFORM       = EmulatorPkg/EmulatorPkg.dsc #目前正在编译的包
TARGET = DEBUG #编译目标类型 可以是DEBUG、RELEASE、NOOPT
TARGET_ARCH = IA32 #程序运行目标架构 可以是IA32、IPF、X64、EBC、ARM、AARCH64
TOOL_CHAIN_CONF = Conf/tools_def.txt #编译链工具配置文件位置
TOOL_CHAIN_TAG = VS2019 #编译工具链
BUILD_RULE_CONF = Conf/build_rule.txt #编译规则文件

在tools_def.txt中有大量可用编译器,这个就不需要管了。

编译

先编译UEFI模拟器EmulatorPkg,在x86 Native Tools Command Prompt中:

1
2
3
mybuild.bat
edk2\edksetup.bat
build -p edk2\EmulatorPkg\EmulatorPkg.dsc -t VS2022 -a IA32 #-p对应ACTIVE_PLATFORM -t对应TOOL_CHAIN_TAG 64位则换成-aX64 这里不指定则用target.txt中参数编译

再编译UEFI程序,同样在x86 Native Tools Command Prompt中:

1
2
3
4
5
6
7
8
9
#例如编译edk2\-libc中的AppPkg包:
mybuild.bat
edk2\edksetup.bat
build -p edk2-libc\AppPkg\AppPkg.dsc -t VS2022 -a IA32

#例如编译MdeModulePkg包中的HelloWorld:
mybuild.bat
edk2\edksetup.bat
build -p edk2\MdeModulePkg\MdeModulePkg.dsc -m edk2\MdeModulePkg\Application\HelloWorld\HelloWorld.inf -t VS2022 -a IA32

编译出来的目标程序都在Build目录下。

模拟

运行WinHost:

1
2
cd Build\EmulatorIA32\DEBUG_VS2022\IA32
WinHost.exe

成功启动后进入UEFI Shell,显示挂载了一些设备,其中FS0为主机Build\EmulatorIA32\DEBUG_VS2022\IA32目录。UEFI Shell命令与DOS或Bash差不多,如lsdirpci等,用help -b查看帮助。这里运行helloworld:

1
2
fs0:
HelloWorld.efi

调试

Visual Studio 2022联动

这里对HelloWorld.efi进行调试。在edk2\MdeModulePkg\Application\HelloWorld目录下用Visual Studio 2022新建一个生成文件项目(Makefile Project),工程名随便起,这里叫x43dbg_vs,其中生成命令行、清除命令行和重新生成命令行先随便填,接下来要改,其他不用管。之后打开x32dbg_vs.vcxproj并修改对应DEBUG选项的NMakeBuildCommandLine标签内容:

1
2
3
4
5
cd /D D:\tests\UEFItest
set WORKSPACE=D:\tests\UEFItest
call mybuild.bat
call edk2\edksetup.bat
call build.bat -p EmulatorPkg\EmulatorPkg.dsc -a IA32 -m MdeModulePkg\Application\HelloWorld\HelloWorld.inf -b DEBUG

修改NMakeCleanCommandLine标签内容:

1
2
3
4
5
cd /D D:\tests\UEFItest
set WORKSPACE=D:\tests\UEFItest
call mybuild.bat
call edk2\edksetup.bat
call build.bat -p EmulatorPkg\EmulatorPkg.dsc -a IA32 -m MdeModulePkg\Application\HelloWorld\HelloWorld.inf -b DEBUG clean

修改NMakeReBuildCommandLine标签内容:

1
2
3
4
5
6
cd /D D:\tests\UEFItest
set WORKSPACE=D:\tests\UEFItest
call mybuild.bat
call edk2\edksetup.bat
call build.bat -p EmulatorPkg\EmulatorPkg.dsc -a IA32 -m MdeModulePkg\Application\HelloWorld\HelloWorld.inf -b DEBUG clean
call build.bat -p EmulatorPkg\EmulatorPkg.dsc -a IA32 -m MdeModulePkg\Application\HelloWorld\HelloWorld.inf -b DEBUG

再手动把edk2\MdeModulePkg\Application\HelloWorld\HelloWorld.c导入源文件下,之后进行Build,发现下方输出窗口内容同上述手动编译过程。然后在项目属性中设置调试->工作目录为D:\tests\UEFItest\Build\EmulatorIA32\DEBUG_VS2022\IA32\,命令改为WinHost.exe。之后对源码打断点,并用F5启动调试。随即启动WinHost.exe,在其中运行HelloWorld.efi,发现在Visual Studio中命中断点。

QEMU+WinDBG联动

先下载QEMU和Intel UDK Debugger Tool,前者在https://qemu.weilnetz.de/w64/,后者在https://www.intel.com/content/www/us/en/developer/articles/tool/unified-extensible-firmware-interface.html 。Intel UDK Debugger Tool安装时选择Pipe类型,Port填qemu_pipe_dbg。WinDBG和Intel UDK Debugger Tool安装目录上不要有汉字。

接下来为QEMU创建VHD虚拟硬盘。打开Windows自带磁盘管理工具,选中某个磁盘创建VHD,200MB大小即可,命名为lbdbg.vhd,并设为动态增长模式。然后对刚生成的虚拟硬盘文件装载,格式化为FAT32分区,再弹出。

接着用OvmfPkg制作一个支持源码级调试的BIOS镜像,例如32位:

1
build -a IA32 -p edk2\OvmfPkg\OvmfPkgIa32.dsc -b NOOPT -D SOURCE_DEBUG_ENABLE #X64改为-a X64 OvmfPkgX64.dsc

编译出的文件位于Build\OvmfIa32\NOOPT_VS2022\FV目录下的OVMF.fd,并将HelloWorld.efi复制到lbdbg.vhd分区中,弹出。找个地方新建dbgOvmfIa32文件夹,将lbdbg.vhd和OVMF.fd复制到这里。打开开始菜单的Start Windbg With Intel UDK Debugger Tool,如果弹出不支持该内核调试方法,则自行启动管道调试。在dbgOvmfIa32目录打开命令行,运行:

1
qemu-system-i386 -L . -bios OVMF.fd -hdd lbdbg.vhd -serial pipe:qemu_pipe_dbg

运行后用下面命令下断点,并用g继续:

1
bu HelloWorld!UefiMain

制作启动盘

这里只讲针对UEFI BIOS制作的启动盘。启动文件通过编译ShellPkg得到:

1
2
3
mybuild.bat
edk2\edksetup.bat
build -a IA32 -a X64 -p ShellPkg\ShellPkg.dsc -t VS2022 -b RELEASE

在Build\Shell\RELEASE_VS2022下的IA32和X64目录下各有一个Shell.efi,所处目录类似于Build\Shell\RELEASE_VS2022\IA32\ShellPkg\Application\Shell\EA4BB293-2D7F-4456-A681-1F22F42CD0BC\OUTPUT,分别改名为bootx32.efi和bootx64.efi。之后将U盘格式化为FAT32格式,在U盘根目录下建立efi\boot目录,将bootx32.efi和bootx64.efi复制到该目录下。重启计算机,开机从U盘启动即可进入UEFI Shell。