UEFI源码学习01-ARM AARCH64编译、ArmPlatformPriPeiCore(SEC)
文章目录
- 1. AARCH64编译环境搭建
- 2. ArmPlatformPriPeiCore
- 2.1 QEMU_EFI.fd包含了什么
- 2.2 QEMU virt aarch64相关
- 2.3 从第一条指令到ArmPlatformPrePeiCore入口
- 2.4 ArmPlatformPrePeiCore做了点什么
- _ModuleEntryPoint
- CEntryPoint
- PrimaryMain
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.gitcd edk2
git submodule update --init
cd ..sudo apt-get install gcc-aarch64-linux-gnu
sudo apt-get install qemu-system-aarch64export 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_PrepareArgumentsCEntryPointPrimaryMain(PeiCoreEntryPoint)(&SecCoreData, PpiList);
_ModuleEntryPoint
edk2/ArmPlatformPkg/PrePeiCore/AArch64/PrePeiCoreEntryPoint.S
这里面首先调用了ArmPlatformPeiBootAction,这个函数是个空实现,实际没什么用。接着调用SetupExceptionLevel1设置EL1的环境,然后跳转到MainEntryPoint。
ASM_FUNC(_ModuleEntryPoint)// Do early platform specific actionsbl 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 corecmp x0, #1b.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 VectorMOV64 (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)
{//关闭所有DcacheArmDisableDataCache ();// Invalid所有ICacheArmInvalidateInstructionCache ();// 使能ICACHEArmEnableInstructionCache ();// 刷下栈上的DcacheInvalidateDataCacheRange ((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阶段的PPICreatePpiList (&PpiListSize, &PpiList);//使能GICArmGicEnableDistributor (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 pointPeiCoreEntryPoint (&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阶段的执行了
UEFI源码学习01-ARM AARCH64编译、ArmPlatformPriPeiCore(SEC)相关推荐
- UEFI源码学习3 - DXE
目录 UEFI源码学习3.1 - DXE Dispatcher UEFI源码学习3.2 - DXE Protocol实现 UEFI源码学习3.3 - DXE Event实现 UEFI源码学习3.4 - ...
- PostgreSQL源码学习(一)编译安装与GDB入门
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 PostgreSQL源码学习(一)编译安装与GDB入门 前言 一.安装PostgreSQL 1.获取源码 2.配置 3.编译 3.安装 ...
- 【iScroll源码学习01】准备阶段 - 叶小钗
[iScroll源码学习01]准备阶段 - 叶小钗 时间 2013-12-29 18:41:00 博客园-原创精华区 原文 http://www.cnblogs.com/yexiaochai/p/3 ...
- Dart源码学习01
1.Dart历史 2.dart设计基础思想 3.Dart的编译模式 Dart VM 支持五种编译模式: 1, :最常见的 JIT 模式,可以直接在虚拟机中执行 Dart 源码,像解释型语言一样使用.通 ...
- *迟来的爱*——《Foursquare》应用源码学习(一) 下载、编译、运行
做Android项目做到好几年的程序,发现技术进步很慢,逐渐往管理发展..于是, 要看开源项目,学习别人的成功经验,来解决项目中遇到的棘手问题. 于是看到了别人推荐的android的开源源码,找了一个 ...
- OBS源码学习(三)-编译updater工程
由于项目要求要将OBS更新功能迁到公司自己的服务器上,OBS默认的是不编译updater工程,现将OBS更新工程编译步骤如下: 一.下载解压并使用 CMake 生成编译 zlib https://do ...
- JDK11源码学习05 | HashMap类
JDK11源码学习05 | HashMap类 JDK11源码学习01 | Map接口 JDK11源码学习02 | AbstractMap抽象类 JDK11源码学习03 | Serializable接口 ...
- VUE源码学习第一篇--前言
一.目的 前端技术的发展,现在以vue,react,angular为代表的MVVM模式以成为主流,这三个框架大有三分天下之势.react和angular有facebook与谷歌背书,而vue是以一己之 ...
- 博通Broadcom SDK源码学习与开发1——SDK源码探究与Cable Modem 系统编译
声明:原创作品,严禁用于商业目的. 本系列文章将全面剖析以Bcm33xxx芯片开发Cablemodem产品的SDK源码为例,从编译系统到各个功能模块进行分析与探讨. 文章目录 0.写在前篇 1. 博通 ...
最新文章
- fond+html属性,JQuery 干货篇之选择元素
- python链表的创建_Python——新建链表
- DL之ShuffleNetV2:ShuffleNetV2算法的简介(论文介绍)、架构详解、案例应用等配图集合之详细攻略
- (转)怎样完全用 GNU/Linux 工作
- MySql中添加用户/删除用户
- php安装 pear,php pear / pecl 扩展工具的安装和使用
- C#获取电脑IP、MAC地址示例代码
- spring中context:property-placeholder标签详解
- Nitrozme Animation Studio Packages Mac(AE插件拓展包)
- Windows系统克隆***与防范
- mysql alert longtext_mysql数据类型介绍(含text,longtext,mediumtext说明)
- 第三章·MySQL版本区别及管理
- 深入浅出CChart 每日一课——快乐高四第九课 于无声处,CChart内置功能介绍之数据存取篇...
- AS-实践《第一行代码》中的出现的问题
- 电阻参数_电阻器的主要技术参数、分类和选用
- 电子密码锁的设计(Verilog HDL实现)
- 码农的奋斗之路 富爸爸穷爸爸系列--提高你的财商 读后感
- 今天准备出发去珠海横琴,去长隆海洋乐园看鲨鱼宝宝了
- 小米适配android o机型,小米公布部分机型安卓O/P适配进度及新适配计划
- 手把手教你制作自己的小程序