当前位置:网站首页>UEFI学习01-ARM AARCH64编译、ArmPlatformPriPeiCore(SEC)
UEFI学习01-ARM AARCH64编译、ArmPlatformPriPeiCore(SEC)
2022-04-23 06:13:00 【MyeDy】
文章目录
1. AARCH64编译环境搭建
git clone https://github.com/tianocore/edk2-platforms.git
git clone https://github.com/acpica/acpica.git
git clone https://github.com/tianocore/edk2.git
cd edk2
git submodule update --init
cd ..
sudo apt-get install gcc-aarch64-linux-gnu
sudo apt-get install qemu-system-aarch64
export WORKSPACE=$PWD
export PACKAGES_PATH=$WORKSPACE/edk2:$WORKSPACE/edk2-platforms
export IASL_PREFIX=$WORKSPACE/acpica/generate/unix/bin/
export GCC5_AARCH64_PREFIX=/usr/bin/aarch64-linux-gnu-
source edk2/edksetup.sh
build -a AARCH64 -t GCC5 -p edk2/ArmVirtPkg/ArmVirtQemu.dsc -b DEBUG
编译完之后会生成UEFI文件:Build/ArmVirtQemu-AARCH64/DEBUG_GCC5/FV/QEMU_EFI.fd
运行命令如下
qemu-system-aarch64 -M virt -cpu cortex-a57 -bios Build/ArmVirtQemu-AARCH64/DEBUG_GCC5/FV/QEMU_EFI.fd -net none -serial stdio
2. ArmPlatformPriPeiCore
大部分教程都是用OVMF来做示例,OVMF中第一个运行的UEFI模块是SEC。但AARCH64中的SEC是这个ArmPlatformPriPeiCore。所以在edk2的AARCH64示例中,ArmPlatformPriPeiCore是第一个运行的模块。
2.1 QEMU_EFI.fd包含了什么
我们用UEFITool NE 打开QEMU_EFI.fd,可以看到如下图
- ArmPlatformPrePeiCore,充当的SEC core的模块。从0x1000存放
- PeiCore,PEI Core的代码
- 7个PEIM
PEIM 功能 PlatformPei 初始化SOC平台相关的代码 MemoryInit 初始化内存 CpuPei 初始化ARM cortex a57 cluster PcdPeim 提供动态Pcd PeiVariablePei 没用到 DxeIplPei 提供加载DXE的功能 - 最后是一个Volumn image,这个是一个压缩卷,里面包含PEI后面阶段的image,如DXE, BSD等等。需要在PEI中进行解压然后运行。
2.2 QEMU virt aarch64相关
- ROM的空间是0x00000000 - 0x3FFFFFFF
- RAM的空间在0x40000000 - 0x7FFFFFFF
- CPU第一条指令是在0地址运行,即在ROM上
- QEMU_EFI.fd文件存放在ROM上,即从0地址开始
2.3 从第一条指令到ArmPlatformPrePeiCore入口
从2.2中知道CPU第一条指令从0地址执行,那么QEMU_EFI.fd里的第一个word存放了什么东西?用二进制编辑器查看QEMU_EFI.fd可以看到在0地址存放了一个word:0x14000400。
这是一条跳转指令,根据armv8a的手册来看,这条指令是b pc+0x1000。CPU刚启动的时候,PC寄存器是0,所以这条指令会直接跳转到0x1000地址。
然后同样看一下0x1000地址的数据。又是一条跳转指令0x14000d16。解析出来就是b pc+0x3458,当前pc是0x1000,因此他就跳转到了0x4458。
那么0x4458存放的是什么东西?
首先通过反汇编ArmPlatformPrePeiCore.debug,可以得到0x3458是ArmPlatformPrePeiCore的_ModuleEntryPoint
然后我们查看QEMU_EFI.fd的0x4458的地址存放的数据,就是对应_ModuleEntryPoint的第一条指令。我们知道ArmPlatformPrePeiCore是从0x1000存放的,因此实际上0x4458就是ArmPlatformPrePeiCore的_ModuleEntryPoint。而ArmPlatformPrePeiCore编译出来的代码是位置无关代码,所以通过0地址和0x1000地址的两次跳转,最终就跳转到ArmPlatformPrePeiCore的_ModuleEntryPoint中。
2.4 ArmPlatformPrePeiCore做了点什么
ArmPlatformPrePeiCore非常简单,主要初始化CPU,设置栈指针, 初始化PEI阶段需要的参数SecCoreData最后跳转到PEI core中去。
函数调用栈如下:
_ModuleEntryPoint
_SetupPrimaryCoreStack
_PrepareArguments
CEntryPoint
PrimaryMain
(PeiCoreEntryPoint)(&SecCoreData, PpiList);
_ModuleEntryPoint
edk2/ArmPlatformPkg/PrePeiCore/AArch64/PrePeiCoreEntryPoint.S
这里面首先调用了ArmPlatformPeiBootAction,这个函数是个空实现,实际没什么用。接着调用SetupExceptionLevel1设置EL1的环境,然后跳转到MainEntryPoint。
ASM_FUNC(_ModuleEntryPoint)
// Do early platform specific actions
bl ASM_PFX(ArmPlatformPeiBootAction)
EL1_OR_EL2(x0)
1:bl ASM_PFX(SetupExceptionLevel1)
b ASM_PFX(MainEntryPoint)
MainEntryPoint里就读出CPU ID去配置一下栈指针,如果是primary core就设置primary 栈,如果是secondary core就设置secondary栈。后面都只讨论primary core。栈指针从FIX PCD中获取
ASM_PFX(MainEntryPoint):
MOV64 (x1, FixedPcdGet64(PcdCPUCoresStackBase) + FixedPcdGet32(PcdCPUCorePrimaryStackSize))
// x0 is equal to 1 if I am the primary core
cmp x0, #1
b.eq _SetupPrimaryCoreStack
_SetupPrimaryCoreStackz中主要配置了一下栈寄存器SP,然后跳转到_PrepareArguments
_SetupPrimaryCoreStack:
mov sp, x1
...
b _PrepareArguments
_PrepareArguments从PCD里拿到PEI CORE的entry,然后传给CEntryPoint,后面就是C代码了
_PrepareArguments:
// The PEI Core Entry Point has been computed by GenFV and stored in the second entry of the Reset Vector
MOV64 (x2, FixedPcdGet64(PcdFvBaseAddress))
ldr x1, [x2, #8]
// Move sec startup address into a data register
// Ensure we're jumping to FV version of the code (not boot remapped alias)
ldr x3, =ASM_PFX(CEntryPoint)
CEntryPoint
edk2/ArmPlatformPkg/PrePeiCore/PrePeiCore.c
CEntryPoint主要就初始化了一些ARM CPU的一些东西,关闭cache,打开VFP, 设置VBAR之类的,然后就跳转到 PrimaryMain中去了。
CEntryPoint (
IN UINTN MpId,
IN EFI_PEI_CORE_ENTRY_POINT PeiCoreEntryPoint
)
{
//关闭所有Dcache
ArmDisableDataCache ();
// Invalid所有ICache
ArmInvalidateInstructionCache ();
// 使能ICACHE
ArmEnableInstructionCache ();
// 刷下栈上的Dcache
InvalidateDataCacheRange (
(VOID *)(UINTN)PcdGet64 (PcdCPUCoresStackBase),
PcdGet32 (PcdCPUCorePrimaryStackSize)
);
//设置VBAR到PeiVectorTable --> PEI的异常向量表
ArmWriteVBar ((UINTN)PeiVectorTable);
//使能浮点单元
ArmEnableVFP ();
PrimaryMain (PeiCoreEntryPoint);
}
PrimaryMain
edk2/ArmPlatformPkg/PrePeiCore/MainMPCore.c
PrimaryMain里主要建立了SEC阶段传给PEI的PPI list,然后配置好SecCoreData结构体,跳转到PeiCore中去。
VOID
EFIAPI
PrimaryMain (
IN EFI_PEI_CORE_ENTRY_POINT PeiCoreEntryPoint
)
{
...
//创建SEC阶段的PPI
CreatePpiList (&PpiListSize, &PpiList);
//使能GIC
ArmGicEnableDistributor (PcdGet64 (PcdGicDistributorBase));
...
//从PCD中获取TempStack的base,TempStack在永久内存初始化之前的临时内存
TemporaryRamBase = (UINTN)PcdGet64 (PcdCPUCoresStackBase) + PpiListSize;
//从PCD中获取TempStack的大小
TemporaryRamSize = (UINTN)PcdGet32 (PcdCPUCorePrimaryStackSize) - PpiListSize;
SecCoreData.DataSize = sizeof (EFI_SEC_PEI_HAND_OFF);
//从PCD中获取PEI的FV地址和长度并存入SecCoreData中
SecCoreData.BootFirmwareVolumeBase = (VOID *)(UINTN)PcdGet64 (PcdFvBaseAddress);
SecCoreData.BootFirmwareVolumeSize = PcdGet32 (PcdFvSize);
SecCoreData.TemporaryRamBase = (VOID *)TemporaryRamBase; // We run on the primary core (and so we use the first stack)
SecCoreData.TemporaryRamSize = TemporaryRamSize;
SecCoreData.PeiTemporaryRamBase = SecCoreData.TemporaryRamBase;
SecCoreData.PeiTemporaryRamSize = ALIGN_VALUE (SecCoreData.TemporaryRamSize / 2, CPU_STACK_ALIGNMENT);
SecCoreData.StackBase = (VOID *)((UINTN)SecCoreData.TemporaryRamBase + SecCoreData.PeiTemporaryRamSize);
SecCoreData.StackSize = (TemporaryRamBase + TemporaryRamSize) - (UINTN)SecCoreData.StackBase;
// Jump to PEI core entry point
PeiCoreEntryPoint (&SecCoreData, PpiList);
}
我们在PeiCore的入口的地方加入了打印,把SecCoreData dump出来如下
DataSize:48
BootFirmwareVolumeBase:00000001000
BootFirmwareVolumeSize:000001FF000
TemporaryRamBase:0004007C030
TemporaryRamSize:00000003FD0
PeiTemporaryRamBase:0004007C030
PeiTemporaryRamSize:00000001FF0
StackBase:0004007E020
StackSize:00000001FE0
至此,SEC就运行完了,后面就是PEI阶段的执行了
版权声明
本文为[MyeDy]所创,转载请带上原文链接,感谢
https://blog.csdn.net/u011280717/article/details/124313785
边栏推荐
- Machine learning II: logistic regression classification based on Iris data set
- 多机多卡训练时的错误
- PyTorch 12. hook的用法
- PyTorch 19. PyTorch中相似操作的区别与联系
- The Cora dataset was trained and tested using the official torch GCN
- 【動態規劃】不同路徑2
- PyTorch训练一个网络的基本流程5步法
- Data class of kotlin journey
- Gephi教程【1】安装
- [8] Assertion failed: dims.nbDims == 4 || dims.nbDims == 5
猜你喜欢
【点云系列】Neural Opacity Point Cloud(NOPC)
PyTorch 10. 学习率
rearrange 和 einsum 真的优雅吗
Paddleocr image text extraction
[point cloud series] a rotation invariant framework for deep point cloud analysis
PyMySQL连接数据库
【点云系列】点云隐式表达相关论文概要
Use originpro express for free
[3D shape reconstruction series] implicit functions in feature space for 3D shape reconstruction and completion
【指标】Precision、Recall
随机推荐
How keras saves and loads the keras model
Solution to slow compilation speed of Xcode
Gee configuring local development environment
winform滚动条美化
树莓派:双色LED灯实验
Data class of kotlin journey
第2章 Pytorch基础2
torch. mm() torch. sparse. mm() torch. bmm() torch. Mul () torch The difference between matmul()
Chapter 2 pytoch foundation 2
常见的正则表达式
[point cloud series] a rotation invariant framework for deep point cloud analysis
微信小程序 使用wxml2canvas插件生成图片部分问题记录
Chapter 3 pytoch neural network toolbox
PyTorch中的一些常见数据类型转换方法,与list和np.ndarray的转换方法
PyMySQL连接数据库
使用 trt 的int8 量化和推断 onnx 模型
【点云系列】点云隐式表达相关论文概要
Pytorch model pruning example tutorial III. multi parameter and global pruning
【点云系列】PnP-3D: A Plug-and-Play for 3D Point Clouds
ThreadLocal, just look at me!