ubuntu20.04下搭建EDK2开发环境

发布时间 2023-12-07 14:35:50作者: 闹闹爸爸

EDK2是UEFI应用程序的官方开发环境。它是由开源的Tianocore项目开发的,英特尔、惠普和微软是该项目的主要贡献者。虽然它可能比GNU-EFI大,但它有更多的功能,因此,一些操作系统开发人员可能更喜欢它而不是GNU-EFI。

什么是EDK2?

EDK2完全实现了UEFI规范。它包含开放虚拟机固件(OVMF) UEFI固件包,主要针对QEMU,许多操作系统开发人员使用它来测试他们的UEFI应用程序。它也有用于QEMU和各种板的ARM和AArch64固件包,以及用于各种HiFive板的RISC-V固件包。它还为UEFI驱动程序开发人员提供了工具。

EDK2的结构

EDK2由多个仓库组成,所有这些仓库都可以在https://github.com/tianocore上找到。主要有:

  • edk2,它包含构建系统、库、OVMF和ArmVirt固件。
  • edk2-platforms,其中包含用于各种真实硬件平台的固件,例如SiFive U450、Raspberry Pi等。
  • edk2-non-osi,它包含各种平台的专有二进制blob。

EDK2本身是用C语言和一些Python编写的,并在BSD 2 Clause +专利许可下获得许可。这些blobs都有自己的许可证。

目录结构

EDK2被分成多个子目录,每个子目录通常包含一个“包”。OvmfPkg子目录包含OVMF, MdePkg子目录包含UEFI库,等等。其中每一个都包含一个以.dsc结尾的文件。该文件描述文件夹内的包,并包含诸如依赖项、不同组件等内容。每个组件都有自己的文件,其中包含源、应用程序类型等。

构建EDK2基础

无论您尝试从EDK2构建什么,都必须完成这些步骤。首先,您必须决定是否只想在主要的EDK2仓库中构建东西,还是也想从EDK2平台中构建东西。注意,这些指南假定您使用的是Linux系统(Linux的Windows子系统也可以)。

使用edk2平台进行构建

确保你已经安装了所有的依赖项:

 apt install build-essential git python2 uuid-dev nasm acpica-tools 

如果您确实需要在真实硬件上运行的固件,那么您必须执行以下步骤。

首先,我们需要创建工作空间文件夹

mkdir edk2 && cd edk2
export WORKSPACE=$PWD

现在我们需要将源代码克隆到所有的仓库。edk2-non-osi是可选的,具体取决于您正在构建的平台,但我还是建议下载它

git clone https://github.com/tianocore/edk2.git -b"stable/202011"
git clone https://github.com/tianocore/edk2-platforms.git
git clone https://github.com/tianocore/edk2-non-osi.git
cd edk2 && git submodule update --init
cd ../edk2-platforms && git submodule update --init && cd ..

我们需要让EDK2的构建系统知道这些文件夹的位置。我们还将设置一些其他变量

export EDK_TOOLS_PATH="$PWD/edk2/BaseTools"
export PACKAGES_PATH="$PWD/edk2:$PWD/edk2-platforms:$PWD/edk2-non-osi"

我们现在必须设置环境

. edk2/edksetup.sh
make -C edk2/BaseTools

要在edk2-platforms中为某个平台构建,只需找到相关的.dsc文件。

构建EDK2 UEFI应用程序

一旦构建了EDK2的基础部分,构建UEFI应用程序就非常容易了。作为操作系统开发人员,主要的软件包将与MdeModulePkg一起工作,这是一个完整的UEFI封装库。
首先,我们必须创建一个包含应用程序的目录。我们将在主EDK2目录中创建它,如下所示:

cd edk2
mkdir MyEfiApp

现在,在MyEfiApp目录下创建一个名为MyEfiApp.inf的文件。内容如下:

[Defines]
  INF_VERSION = 1.25
  BASE_NAME = MyEfiApp
  FILE_GUID = # Get a GUID from guidgen.com
  MODULE_TYPE = UEFI_APPLICATION
  VERSION_STRING = 1.0
  ENTRY_POINT = UefiEntry
 
[Sources]
  UefiMain.c
 
[Packages]
  MdePkg/MdePkg.dec
 
[LibraryClasses]
  UefiApplicationEntryPoint
  UefiLib
 
[Guids]
 
[Ppis]
 
[Protocols]
 
[FeaturePcd]
 
[Pcd]

注意:上面的FILE_GUID不能空,否则编译时报错。具体填什么值可以从其他.inf文件中参考下。

在和MyEfiApp.inf同一个目录MyEfiApp中创建一个名为UefiMain.c的文件。并赋予它以下内容:

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h>
 
EFI_STATUS EFIAPI UefiEntry(IN EFI_HANDLE imgHandle, IN EFI_SYSTEM_TABLE* sysTable)
{
    gST = sysTable;
    gBS = sysTable->BootServices;
    gImageHandle = imgHandle;
    // UEFI apps automatically exit after 5 minutes. Stop that here
    gBS->SetWatchdogTimer(0, 0, 0, NULL);
    Print(L"Hello, world!\r\n");
    // Allocate a string
    CHAR16* str = NULL;
    gBS->AllocatePool(EfiLoaderData, 36, (VOID**)&str);
    // Copy over a string
    CHAR16* str2 = L"Allocated string\r\n";
    gBS->CopyMem((VOID*)str, (VOID*)str2, 36);
    Print(str);
    gBS->FreePool(str);
    return EFI_SUCCESS;
}

如果需要,现在运行 . edksetup.sh  。然后,在MdeModulePkg/MdeModulePkg.dsc找到“Components”部分,然后加上这个

[Components]
  ...
  MyEfiApp/MyEfiApp.inf

之后,像这样调用EDK2的构建系统来构建MyEfiApp:

build -a X64 -t GCC5 -p MdeModulePkg/MdeModulePkg.dsc

-a指定目标平台,可以指定为:IA32, X64, AARCH64, ARM 或者 RISCV64.

最后,在Build/MdeModule/DEBUG_GCC5/X64文件夹中,应该有一个名为MyEfiApp.efi的文件。

构建OVMF和ArmVirt

构建OVMF和ArmVirtPkg很容易,在设置EDK2之后,为OVMF运行以下命令:

build -a YOUR_ARCH -t GCC5 -p OvmfPkg/OvmfPkgYOUR_ARCH.dsc

对于OVMF, YOUR_ARCH只能是IA32或X64。对于ArmVirtPkg,运行这个:

build -a YOUR_ARCH -t GCC5 -p ArmVirtPkg/ArmVirtQemu.dsc

YOUR_ARCH只能是ARM或AARCH64。在此之后,固件将输出OVMF Build/OvmfYOUR_ARCH/DEBUG_GCC5/FV,或ArmVirtPkg Build/ArmVirtQemu-YOUR_ARCH/DEBUG_GCC5/FV。

本文参考:

https://wiki.osdev.org/EDK2

https://wiki.osdev.org/GNU-EFI

https://kcm.trellix.com/corporate/index?page=content&id=KB90801&locale=en_US