您当前的位置:首页 > 计算机 > 精彩资源

EDK2 UEFI 固件学习笔记

时间:07-13来源:作者:点击数:

1.仓库地址

https://github.com/tianocore/edk2

https://github.com/tianocore/edk2-platforms

https://github.com/tianocore/edk2-non-osi

除此之外,还可以使用Linaro的efi编译脚本:

https://git.linaro.org/uefi/uefi-tools.git/

2.几个仓库的作用

  • EDK2 主仓库
  • edk2-non-osi 存放许可和主仓库不兼容的代码,比如某些厂商发布的非开源二进制等程序
  • edk2-platforms 存放和具体实际板子相关的代码
  • uefi-tools 辅助编译脚本,不用也可以,直接按照edk2的官网方法编译也是一样的,这个仓库的脚本也是调用的官网那些命令和配置。

3.编译方法

3.1 官网的编译过程

. edk2/edksetup.sh
make -C edk2/BaseTools
$ build -n $NUM_CPUS -a AARCH64 -t GCC5 -p Platform/ARM/JunoPkg/ArmJuno.dsc

主要过程就是加载编译的变量,然后编译好efi工具,efi工具用来编译固件(包含固件生成工具等等),最后就是使用build命令编译某个平台的pkg,build命令行的参数也可以在配置文件指定。具体的参考github了。

3.2 使用UEFI-TOOLS脚本

脚本更简单一些,一条命令类似于./uefi-tools/edk2-build.sh juno这样就好了,因为脚本里面有个配置文件uefi-tools/edk2-platforms.config写好了每一个板子的配置,当然也可以自己添加自己的板子配置进去。

3.目录及文件结构

几个仓库里面,基本上是以模块(module)包(pkg)这两种单位为基本结构,

  • 模块(module),代码里面最小的组织结构,一般会包含在某个pkg里面,比如edk2/MdeModulePkg/Universal/DriverSampleDxe这个模块,在MdeModulePkg/Universal里面。
    模块必须有一个.inf文件,类似于Makefile,定义了这个模块的变量,源码文件以及库等等编译信息,模块还包含的就是.c .h .vfr这些源码和窗体源码文件。
  • Pkg,包,包含的有模块和包的描述文件(.dsc),或者有包的声明文件(.dec),我们编译的实际Platform下的板子也是一个包。对于最终固件的包来说,还有一个flash 定义文件.fdf。

4. 实际笔记

飞腾E2000Q开发板的UEFI固件编译:

  • 默认efi的setup菜单还包含DriverSampleDxe了这个模块,要去掉。需要编辑两个文件
edk2-platforms/Platform/Phytium/CherryPkg/CherryPkg.dsc
#注释掉下面一行
MdeModulePkg/Universal/DriverSampleDxe/DriverSampleDxe.inf

edk2-platforms/Platform/Phytium/CherryPkg/CherryPkg.fdf
#注释掉下面一行
INF MdeModulePkg/Universal/DriverSampleDxe/DriverSampleDxe.inf

可以看出,其实edk2的编译和qt cmake makefile编译方法类似了。

其他未总结的,比如开机热键修改,BIOS菜单修改等等,未完待续(弄驱动这种我肯定是没能力也没精力去写的,改一改配置设置还行)。

4.1 ARM64编译虚拟机的UEFI固件
  • 编译arm64下kvm虚拟机的UEFI固件
    uefi-tool已经写好配置文件了,直接./uefi-tools/edk2-build.sh armvirtqemu64 -b DEBUG命令就编译完成了,编译完成后会生成2个文件
/home/user/Documents/e2000/e2000-v1.5_20230608/Build/ArmVirtQemu-AARCH64/DEBUG_GCC5/FV/QEMU_EFI.fd
/home/user/Documents/e2000/e2000-v1.5_20230608/Build/ArmVirtQemu-AARCH64/DEBUG_GCC5/FV/QEMU_VARS.fd

下面是日志


Fd File Name:QEMU_EFI (/home/user/Documents/e2000/e2000-v1.5_20230608/Build/ArmVirtQemu-AARCH64/DEBUG_GCC5/FV/QEMU_EFI.fd)

Generate Region at Offset 0x0
   Region Size = 0x1000
   Region Name = DATA

Generate Region at Offset 0x1000
   Region Size = 0x1FF000
   Region Name = FV

Generating FVMAIN_COMPACT FV

Generating FVMAIN FV
#######
Fd File Name:QEMU_VARS (/home/user/Documents/e2000/e2000-v1.5_20230608/Build/ArmVirtQemu-AARCH64/DEBUG_GCC5/FV/QEMU_VARS.fd)

Generate Region at Offset 0x0
   Region Size = 0x40000
   Region Name = DATA

Generate Region at Offset 0x40000
   Region Size = 0x40000
   Region Name = DATA

Generate Region at Offset 0x80000
   Region Size = 0x40000
   Region Name = None

GUID cross reference file can be found at /home/user/Documents/e2000/e2000-v1.5_20230608/Build/ArmVirtQemu-AARCH64/DEBUG_GCC5/FV/Guid.xref

FV Space Information
FVMAIN [99%Full] 6865920 (0x68c400) total, 6865880 (0x68c3d8) used, 40 (0x28) free
FVMAIN_COMPACT [58%Full] 2093056 (0x1ff000) total, 1217000 (0x1291e8) used, 876056 (0xd5e18) free

- Done -
Build end time: 14:38:52, Jun.17 2023
Build total time: 00:03:00

------------------------------------------------------------
                        ArmVirtPkg QEMU (AARCH64) DEBUG pass
------------------------------------------------------------
pass    1
fail    0

然后要使用qemu+kvm测试还需要把这两个文件坐下扩容,不然启动失败

qemu-img resize -f raw QEMU_VARS.fd 64M
qemu-img resize -f raw QEMU_EFI.fd 64M

测试命令

sudo qemu-system-aarch64 -m 4096M \
-machine virt-3.1,accel=kvm,usb=off,dump-guest-core=off,gic-version=3 \
-cpu host \
-drive file=/home/user/temp/QEMU_EFI.fd,if=pflash,format=raw,unit=0,readonly=on \
-drive file=/home/user/temp/QEMU_VARS.fd,if=pflash,format=raw,unit=1  \
-device pcie-root-port,port=0x8,chassis=1,id=pci.1,bus=pcie.0,multifunction=on,addr=0x1 \
-device pcie-root-port,port=0x9,chassis=2,id=pci.2,bus=pcie.0,addr=0x1.0x1 \
-device pcie-root-port,port=0xa,chassis=3,id=pci.3,bus=pcie.0,addr=0x1.0x2 \
-device pcie-root-port,port=0xb,chassis=4,id=pci.4,bus=pcie.0,addr=0x1.0x3 \
-device pcie-root-port,port=0xc,chassis=5,id=pci.5,bus=pcie.0,addr=0x1.0x4 \
-device pcie-root-port,port=0xd,chassis=6,id=pci.6,bus=pcie.0,addr=0x1.0x5 \
-device virtio-gpu-pci,id=video0,max_outputs=1,bus=pci.5,addr=0x0 \
-netdev tap,id=hostnet0,vhost=on \
-device virtio-net-pci,netdev=hostnet0,id=net0,mac=52:54:00:54:fb:35,bus=pci.1,addr=0x0 \
-device qemu-xhci,p2=15,p3=15,id=usb,bus=pci.2,addr=0x0 \
-device usb-kbd,id=input0,bus=usb.0,port=1 \
-device usb-mouse,id=input1,bus=usb.0,port=2 

可以直接图形和串口2个窗口查看启动过程。

  • 在UOS上本身也有edk2的源码包,可以apt source qemu-efi-aarch64下载后查看debian/rules可以观察到debian是如何build的, rules基本上就是个Makefile,最主要就是这条命令
build-qemu-efi-aarch64:
        $(MAKE) -f debian/rules build-qemu-efi EDK2_ARCH_DIR=AArch64 EDK2_HOST_ARCH=AARCH64 FW_NAME=AAVMF
build-qemu-efi: setup-build
        mkdir -p ShellBinPkg/UefiShell/$(EDK2_ARCH_DIR) FatBinPkg/EnhancedFatDxe/$(EDK2_ARCH_DIR)
        set -e; . ./edksetup.sh; \
                build -a $(EDK2_HOST_ARCH) -p ShellPkg/ShellPkg.dsc \
                        -b RELEASE -t $(EDK2_TOOLCHAIN); \
                cp -a Build/Shell/RELEASE_$(EDK2_TOOLCHAIN)/$(EDK2_HOST_ARCH)/Shell.efi \
                        ShellBinPkg/UefiShell/$(EDK2_ARCH_DIR)/Shell.efi; \
                build -a $(EDK2_HOST_ARCH) -p FatPkg/FatPkg.dsc \
                        -m FatPkg/EnhancedFatDxe/Fat.inf \
                        -t $(EDK2_TOOLCHAIN) -b RELEASE; \
                cp -a Build/Fat/RELEASE_$(EDK2_TOOLCHAIN)/$(EDK2_HOST_ARCH)/Fat.efi \
                        FatBinPkg/EnhancedFatDxe/$(EDK2_ARCH_DIR)/Fat.efi; \
                build -a $(EDK2_HOST_ARCH) \
                        -t $(EDK2_TOOLCHAIN) \
                        -p ArmVirtPkg/ArmVirtQemu.dsc \
                        $(AAVMF_FLAGS) -b RELEASE
        dd if=/dev/zero of=Build/ArmVirtQemu-$(EDK2_HOST_ARCH)/RELEASE_$(EDK2_TOOLCHAIN)/FV/$(FW_NAME)_CODE.fd bs=1M seek=64 count=0
        dd if=Build/ArmVirtQemu-$(EDK2_HOST_ARCH)/RELEASE_$(EDK2_TOOLCHAIN)/FV/QEMU_EFI.fd of=Build/ArmVirtQemu-$(EDK2_HOST_ARCH)/RELEASE_$(EDK2_TOOLCHAIN)/FV/$(FW_NAME)_CODE.fd conv=notrunc
        dd if=/dev/zero of=Build/ArmVirtQemu-$(EDK2_HOST_ARCH)/RELEASE_$(EDK2_TOOLCHAIN)/FV/$(FW_NAME)_VARS.fd bs=1M seek=64 count=0

最后dd那两下差点没看懂,仔细想了下,才发现是直接把文件增大到64M(seek是dd对目标文件跳过多少个bs开始写),debian的这个rules这里发现两个文件也需要扩容到64M。

4.2 UEFI启动过程
在这里插入图片描述

这里摘抄一张大神书里面的图片,整个启动过程还是比较清晰的,对我目前来说,我就是想改个LOGO,然后BIOS配置界面

稍微做点文字上的自定义(功能就算了,精力跟不上,开发基础也不够)

于是乎,最有用的应该就是BDS阶段,BDS作为一个特殊的DEX加载后,应该就是我的切入点。

查看edk2/ArmVirtPkg/ArmVirtQemu.dsc固件的包描述文件 Bds部分。

  #
  # Bds
  #
  MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf {
    <LibraryClasses>
      DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
      PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
  }
  MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngineDxe.inf
  MdeModulePkg/Universal/SetupBrowserDxe/SetupBrowserDxe.inf
  MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.inf
  MdeModulePkg/Universal/BdsDxe/BdsDxe.inf
  MdeModulePkg/Logo/LogoDxe.inf
  MdeModulePkg/Application/UiApp/UiApp.inf {
    <LibraryClasses>
      NULL|MdeModulePkg/Library/DeviceManagerUiLib/DeviceManagerUiLib.inf
      NULL|MdeModulePkg/Library/BootManagerUiLib/BootManagerUiLib.inf
      NULL|MdeModulePkg/Library/BootMaintenanceManagerUiLib/BootMaintenanceManagerUiLib.inf
  }
  OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf {
    <LibraryClasses>
      NULL|OvmfPkg/Library/BlobVerifierLibNull/BlobVerifierLibNull.inf
  }

盲猜这部分我需要去查看的有

 MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngineDxe.inf
  MdeModulePkg/Universal/SetupBrowserDxe/SetupBrowserDxe.inf
  MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.inf
  MdeModulePkg/Universal/BdsDxe/BdsDxe.inf
  MdeModulePkg/Logo/LogoDxe.inf
  MdeModulePkg/Application/UiApp/UiApp.inf

这几个模块,通过启动过程的debug日志字符串查找

edk2/MdeModulePkg/Universal/BdsDxe/BdsEntry.c这个文件在UEFI显卡界面显示LOGO并走进度条的时候串口打印

DEBUG ((DEBUG_INFO, "[Bds]BdsWait(%d)..Zzzz...\n", (UINTN)TimeoutRemain));

对于BIOS配置界面,盲猜应该在edk2/MdeModulePkg/Application/UiApp这个模块内,具体看了下代码,dsc文件定义了入口函数是ENTRY_POINT = InitializeUserInterface直接在edk2/MdeModulePkg/Application/UiApp/FrontPage.c文件搜索,这个函数内的UiEntry函数就有 InitializeFrontPage(); 来初始化配置页面,继续跟下去,InitializeFrontPage里面UpdateFrontPageBannerStrings里面读取了smibios来配置bios配置界面的顶部banner,就是下面这个图里面的 CPU 内存

在这里插入图片描述

查看文件,发现BIOS配置界面总共有5行,第四第五行都是空的,前面几行都是从SMBIOS接口获取的数据,动态,暂时不研究咋修改,尝试给第四行的空字符串加上自定义的,在文件edk2/MdeModulePkg/Application/UiApp/FrontPageStrings.uni中修改字符串如下:

#string STR_CUSTOMIZE_BANNER_LINE4_LEFT  #language en-US  "Made by Actionchen"
                                         #language fr-FR  ""
#string STR_CUSTOMIZE_BANNER_LINE4_RIGHT #language en-US  "2023-06-07"

重新编译固件后再测试,已经显示为自定义的字符串了。

在这里插入图片描述

这个其他部分有时间页面慢慢研究,没有开发能力折腾这个是缓慢的。

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门