我是 EDK2 的新手。
为了将 ekd2 固件移植到新的 ARM64 平台,最好先获得一个至少可以运行 UEFI Shell 的最小 edk2 端口,在此基础上逐步添加改进。
看起来第一步相当陡峭,例如,如何确定平台的最小“项目”集.dsc和文件?.fdf就我而言,我想.fd为我的平台构建 并将其视为 TF-A 的 BL33,实际上我想构建一个 edk2 固件来替换 u-boot。
网上好像很难找到这样的指南。我发现了一个旧版本的 edk2,其中包含一些指令,但显然它们已过时(最新master分支中不存在,而可以在 UDK 分支中找到,例如UDK2014),并且我不确定为什么这些文档从master分支中删除。
目前我可以构建.fdFVP ( edk2-platforms/Platform/ARM/VExpressPkg/ArmVExpress-FVP-AArch64.dsc),并且构建输出似乎FVP_AARCH64_EFI.fd应该被视为 BL33。理论上,这可能是我的新 ARM64 平台的原型,但对我来说,它太复杂了,无法开始:固件大小约为 2.5MiB(与 500K 的 u-boot 相比),所以我猜它远非“最小” “ 版本。但很难弄清楚要删除哪些功能(以及如何删除)。
我想知道是否有关于此类主题的详细指南...
经过 1 个月的反复试验,今天我成功地将我的 ARM64 平台引入 UEFI Shell 环境。我将其视为 EDK2 之旅的第一个里程碑。下面我将尝试总结我迄今为止采取的步骤,作为对我上面问题的初步回答。欢迎指导/更正/评论。
通过阅读书籍/规范/文章来熟悉 UEFI/PI 规范和 EDK2 实现。嗯,UEFI/PI 规范长达数千页……如何开始?我的主要阅读清单是:
获取一个参考平台,该平台可以正确启动从最新 EDK2 源构建的 FD 映像,并稍微使用一下启动管理器和 Shell 环境。就我而言,我选择了 RPi4B。对我来说,这一点非常重要,因为参考平台在整个过程中起到了扶手的作用,每当我遇到错误或有疑问时,我都会检查参考平台的源码/日志。这解决了我遇到的大部分问题。顺便说一句,总是为参考平台和目标平台生成“构建日志”和“构建报告”,因为这两个文件包含非常详细的信息用于比较和检查。请参阅 EDK2 构建规范,了解如何在构建过程中生成这两个文件。
我使用以下脚本为 RPi4B 平台构建:
#!/bin/bash
# https://github.com/tianocore/edk2-platforms#how-to-build-linux-environment
export WORKSPACE=/home/bruin/work/tianocore
export PACKAGES_PATH=$WORKSPACE/edk2:$WORKSPACE/edk2-platforms:$WORKSPACE/edk2-non-osi
pushd $WORKSPACE
rm -rf ./Build/RPi4
source edk2/edksetup.sh
echo "Building BaseTools..."
make -C edk2/BaseTools all
#sudo apt install acpica-tools # iasl
# pip install antlr4-python3-runtime # -Y EXECUTION_ORDER
echo "Building firmware for Pi4B..."
GCC5_AARCH64_PREFIX=aarch64-none-linux-gnu- build \
-n 4 \
-a AARCH64 \
-p Platform/RaspberryPi/RPi4/RPi4.dsc \
-t GCC5 \
-b NOOPT \
-v -d 9 -j RPi4-build.log \
-y RPi4-build-report.txt \
-Y PCD \
-Y LIBRARY \
-Y DEPEX \
-Y HASH \
-Y BUILD_FLAGS \
-Y FLASH \
-Y FIXED_ADDRESS \
-Y EXECUTION_ORDER \
all
Run Code Online (Sandbox Code Playgroud)
如何RPI_EFI.fd在RPi4B上使用构建结果,请参考以下内容:
edk2-platforms/Platform/RaspberryPi/RPi4/Readme.md
readme.md里面https://github.com/pftf/RPi4/releases/download/v1.17/RPi4_UEFI_Firmware_v1.32.zip。顺便说一句,我需要用 zip 文件中的内容替换原来的start4.elf和fixup4.dat,否则,RPi4 的启动将失败,抱怨如下:
RpiFirmwareGetClockRate: Get Clock Rate return: ClockRate=0 ClockId=C
ASSERT [ArasanMMCHost] /home/bruin/work/tianocore/edk2-platforms/Platform/RaspberryPi/
Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c(263): BaseFrequency != 0
Run Code Online (Sandbox Code Playgroud)
RPI_EFI.fd通过使用一些 UEFI 实用程序来对内容进行某种程度的分析是值得的。我主要使用GUI版本UEFITool的sudo apt install uefitool uefitool-cli. 还可以使用其他工具。阅读 EDK2 构建规范以检查对概念的理解时,剖析RPI_EFI.fd很有帮助。
RPI_EFI.fd是第一个 128K 是bl31.bin来自 ATF 的二进制。我猜这是由于 RPi 的特殊启动配置方法所致。对于我的平台,我不需要这种打包,我只需要构建 UEFI 映像MY.fd,该映像被视为 BL33 映像,并fip.bin通过 ATF 构建脚本与 BL2 和 BL31 映像打包在一起。.fd。这涉及到UEFI镜像的入口点(以及每个EDK2模块的入口点),以及解释BLAArch64的指令。基本上可以概括如下:第一个[Components]是RPI_EFI.fd,ArmPlatformPkg/PrePi/PeiUniCore.inf是MODULE_TYPE = SEC。
这个组件是什么:这是 RPi4 中的第一个(也是唯一的)SEC(安全)模块。名字PrePi和Pei含义是什么?
...PI 规范与 edk2 PEIM 无关,而且我不知道 EDKII PEI 模块是目前唯一“公认的”硅初始化环境。edk2 树本身似乎包含根本不使用 edk2 PEI 模块集的平台,但(IIRC)从 SEC 跳转到 DXE。我相信“ArmPlatformPkg/PrePi”和“ArmVirtPkg/PrePi”与此相关。
--- https://listman.redhat.com/archives/edk2-devel-archive/2020-November/msg00021.html
它的入口点:所有 UEFI 组件都有相同的入口点 ( _ModuleEntryPoint)。
.efi..efis是通过工具从ELF可执行文件( .dll)转换而来的GenFw:修改文件头。_ModuleEntryPoint”:
.dll构建报告(build -y <BUILD_REPORT_FILE>)中的生成命令行,我们有两个标志"aarch64-none-linux-gnu-gcc" -o xxx.dll -u _ModuleEntryPoint -Wl,-e,_ModuleEntryPoint ...:
-u:gcc --help -v|grep "undefined SYMBOL"给-u SYMBOL --undefined SYMBOL: star with undefined reference to SYMBOL.Wl,-e:ld --help|grep "entry"给-e ADDRESS, --entry ADDRESS Set start address..dll文件Entry point address == _ModuleEntryPoint:find . -type f -name "*.dll" -exec sh -c "readelf -a {} |grep -E 'Entry point address|_ModuleEntryPoint'" \;它的入口点是整个UEFI FD镜像的入口点(即从bl33_base_addr跳转到this _ModuleEntryPoint):
UEFI 固件文件的拓扑
UEFI 固件文件(实际上是 UEFI 固件设备 - FD 文件)是封装到单个映像中的 UEFI 二进制文件的集合。该映像的格式由平台初始化规范卷 3 定义。向量表位于该文件的底部。固件底部的“BL”分支指令(向量表中重置条目的位置)将跳转到 UEFI 固件映像的第一个“SEC”模块。
--- https://github.com/lzeng14/tianocore/wiki/ArmPkg-Debugging
为了验证上述说法:
反汇编生成的重置向量(即第一个字).FD(我们得到 offset= 0x360):
$ xxd -l 4 -e TEST.fd <== dump 4 bytes in little endian
00000000: 140000d8 <== BL {PC}+(0xd8<<2); offset=0x360
Run Code Online (Sandbox Code Playgroud)
检查入口点.dll(我们得到 offset= 0x240):
$ aarch64-none-elf-objdump -t ArmPlatformPrePiUniCore.dll|grep _ModuleEntryPoint
0000000000000240 g F .text 0000000000000000 _ModuleEntryPoint
$ readelf -h ArmPlatformPrePiUniCore.dll|grep Entry
Entry point address: 0x240
Run Code Online (Sandbox Code Playgroud)
比较不同偏移量的两个文件的内容(我们得到相同的内容):
$ xxd -s 0x360 -l 64 TEST.fd <== skip 0x360 bytes, dump 64 bytes
00000360: 901e 0094 050a 0094 ea03 00aa a1cd 0a58 ...............X
00000370: 0200 e0d2 2200 c0f2 0240 a0f2 0200 80f2 ...."....@......
00000380: c303 a0d2 e3ff 9ff2 6304 00d1 6300 028b ........c...c...
00000390: 0400 a1d2 0400 80f2 2000 03eb 8400 0054 ........ ......T
$ xxd -s 0x240 -l 64 ArmPlatformPrePiUniCore.dll <== skip 0x240 bytes
00000240: 901e 0094 050a 0094 ea03 00aa a1cd 0a58 ...............X
00000250: 0200 e0d2 2200 c0f2 0240 a0f2 0200 80f2 ...."....@......
00000260: c303 a0d2 e3ff 9ff2 6304 00d1 6300 028b ........c...c...
00000270: 0400 a1d2 0400 80f2 2000 03eb 8400 0054 ........ ......T
Run Code Online (Sandbox Code Playgroud)
准备一个空的pkg,并使其构建正常。主要目的是使用 EDK2 构建系统进行一些练习,并使用空 pkg 作为新平台的起点。
复制一份RaspberryPi.dec,全部gRaspberry改为gMyPlatform。
复制 和RPi4.dsc,RPi4.fdf并注释掉DSC和FDF文件中的所有内容。
// 替换文件中的所有 GUID DSC,FDF使用在线 GUID 生成器DEC生成新的 GUID 。
请注意,PCD 在文件中声明,DEC 文件由模块(文件)DEC引用。INF由于空包不包含任何模块,因此在FDF. 因此,为了成功构建空包,我们需要注释掉FDF.
构建NOOPT命令MyPlatform如下:
#!/bin/bash
export WORKSPACE=/home/bruin/work/tianocore
export PACKAGES_PATH=$WORKSPACE/edk2:$WORKSPACE/edk2-platforms:$WORKSPACE/edk2-non-osi
pushd $WORKSPACE
source edk2/edksetup.sh
echo "Building BaseTools..."
make -C edk2/BaseTools all
echo "Building UEFI firmware for MyPlatform..."
GCC5_AARCH64_PREFIX=aarch64-none-linux-gnu- build \
-n 4 \
-a AARCH64 \
-p Platform/MyCorp/MyPlatform/MyPlatform.dsc \
-t GCC5 \
-b NOOPT \
-v -d 9 -j MyPlatform-build.log \
-y MyPlatform-build-report.txt \
-Y EXECUTION_ORDER \
-Y PCD \
-Y LIBRARY \
-Y DEPEX \
-Y HASH \
-Y BUILD_FLAGS \
-Y FLASH \
-Y FIXED_ADDRESS \
all
popd
Run Code Online (Sandbox Code Playgroud)
添加第一个组件ArmPlatformPrePiUniCore。该组件用于为 DXE phae 准备 HOB。主要目的是使串行端口正常工作并正确配置内存。此步骤的另一个目的是熟悉添加组件/模块/lib 的步骤。以下是步骤的简要总结:
INF取消对DSC([Components]部分) 和FDF( [FV.FVMAIN_COMPACT])中模块的注释。Instance of library class [xxxLib] is not found通过[LibraryClasses]更新DSC.
ModuleEntryPoint.iiii:31: Error: immediate out of range:启用gArmTokenSpaceGuid.PcdFdBaseAddress并gArmTokenSpaceGuid.PcdFdSize在FDF。undefined reference to _gPcd_BinaryPatch_PcdSerialClockRate:在 中的部分PcdSerialClockRate中设置。修复我:为什么?参考号[PcdsPatchableInModule]DSCSerialPortLib: 通过.找到 lib-class 头文件 ( MdePkg/Include/Library/SerialPortLib.h) find edk2 -type f -name "*.dec" -exec grep -Hn SerialPortLib需要以下功能:
SerialPortInitialize()SerialPortWrite()SerialPortRead()SerialPortPoll()SerialPortSetControl(): RETURN_UNSUPPORTEDSerialPortGetControl(): RETURN_UNSUPPORTEDSerialPortSetAttributes(): RETURN_UNSUPPORTEDArmPlatformLib: 接口标题位于Include/Library/ArmPlatformLib.h. 需要以下功能:
ArmPlatformGetCorePosition():在给定 MPIDR 值的情况下返回集群中的 cpu idx。该函数用于_ModuleEntryPoint设置辅助核心的堆栈。现在假设一个集群。ArmPlatformIsPrimaryCore()ArmPlatformGetPrimaryCoreMpId()ArmPlatformGetBootMode()ArmPlatformPeiBootAction()ArmPlatformInitialize()ArmPlatformGetVirtualMemoryMap()ArmPlatformGetPlatformPpiList()逐个模块地取消注释 DSC/FDF 中的更多模块...对于 RPi 平台特定的驱动程序/库,我们可以:
edk2在/中搜索edk2-platform类似的驱动程序或 lib 实例,或者调试:我目前主要的调试方法是通过添加“printf()”,即edk2宏DEBUG((DEBUG_INFO,))。需要设置gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel为适当的值才能查看更多调试信息。
| 归档时间: |
|
| 查看次数: |
2083 次 |
| 最近记录: |