高通Android UEFI XBL 代码流程分析

背景

之前学习的lk阶段点亮LCD的流程算是比较经典,但是高通已经推出了很多种基于UEFI方案的启动架构。

所以需要对这块比较新的技术进行学习。在学习之前,有必要了解一下高通UEFI启动流程。

原文(有删改):https://blog.csdn.net/Ciellee/article/details/113519478

参考文档:80_P2484_117_B_UEFI_With_XBL_On_MSM8998_SDM660_SDM

总览

先来看下SDM660芯片冷启动的流程。可以看出,在设备上电后,先跑的是 APPS PBL,接着运行XBL SEC、XBL Loader,通过Loader引出XBL CORE APPSBL,最后进入HLOS。

我们来看下这几个涉及的模块大概功能:

1、Application primary boot loader (APPS PBL)

PBL 启动时,CPU只开启了第一个核心 CPU Core 0,运行固件在ROM中,这部分是高通写死在芯片中的固件,外部开发人员是无法修改这部份的。

主要功能为:

(1)系统安全环境的初始化,以确保后续的XBL中的APPS 能够正常运行。

(2)根据boot gpio的配置选择从什么设备启动操作系统(如 Nand,USB等)。

(3)通过检测GPIO判断是否进入Emergency Download mode,用户可以通过FILE来下载完整的系统镜像。

(4)通过L2 TCM来加载XBL1 ELF,OCIMEM 和 RPM CodeRAM 代码。

2、Extensible boot loader (XBL)

从XBL开始,跑的就是我们编译下载进eMMC/UFS的系统镜像了,在XBL中主要是初始化相关的硬件环境,及代码安全环境。

(1)初始化 Buses、DDR、Clocks、CDT,启动QSEE,QHEE,RPM_FW, XBL core images。

(2)使能memory dump through USB and Sahara(系统死机时memory dump),看门狗,RAM dump to SD support等功能。

(3)初始化 USB驱动,USB充电功能,温升检测,PMIC驱动初始化,和 DDR training模块。

3、XBL core (UEFI or LK,ABL)

XBL core,就是之前的bootloader,主要功能就是初始化display驱动,提供fastboot功能,引导进入HLOS kernel操作系统。

注意,在ABL中,同样也只有CPU Core0在工作,其他的CPU核以是在进入HLOS Kernel后才开始初始化启用的。

本文中,我们重点关注的是Extensible boot loader (XBL),主要来学学UEFI XBL架构,及UEFI XBL代码流程。

一、UEFI XBL

代码目录分析

UEFI XBL代码路径位于:BOOT.XF.1.4\boot_images\

# BOOT.XF.1.4\boot_images

ArmPkg					----> ARM 架构相关的Protocols
ArmPlatformPkg ----> ARM 开发板相关的UEFI代码
BaseTools ----> 编译EDK和EDK2相关的工具,如
EmbeddedPkg ---->
FatPkg
IntelFrameworkModulePkg
IntelFrameworkPkg
MdeModulePkg
MdePkg
QcomPkg ----> 高通定制的相关pkg,如display和usb充电都在里面
ShellPkg ----> UEFI shell 环境

UEFI代码运行流程

从图中可以看出,UEFI代码运行流程为:

SEC(安全验证)--->PEI(EFI前期初始化)--->DXE(驱动执行环境)--->BDS(启动设备选择)--->UEFI Loader(操作系统加载前期)--->RT(Run Time)

接下来,我们根据这个流程来分析下UEFI代码。

SEC (安全验证)

SEC的汇编代码入口位于:

BOOT.XF.1.4\boot_images\QcomPkg\XBLCore\AARCH64\ModuleEntryPoint.masm_ModuleEntryPoint

入口汇编代码分析

分析看看ModuleEntryPoint.masm 这个文件

该汇编代码中,主要工作为:

1、关闭所有中断

2、关闭MMU和Caches

3、关闭TLB缓存表

4、获得当前运行的安全环境:EL1、EL2、EL3

5、初始化ELX 安全环境

6、使能 Cache

7、初始化栈

8、调用 CEntryPoint,传参 _StackBase(0x80C00000)、_StackSize(0x00040000)

#include <AsmMacroIoLibV8.h>
#include <Base.h>
#include <Library/PcdLib.h>
#include <AutoGen.h> AREA |.text|,ALIGN=8,CODE,READONLY # BOOT.XF.1.4\boot_images\QcomPkg\XBLCore\AARCH64\ModuleEntryPoint.masm
IMPORT CEntryPoint // 导入CEntryPoint()函数
EXPORT _ModuleEntryPoint // 输出 _ModuleEntryPoint段
IMPORT InitStackCanary // 导入InitStackCanary()函数 初始化栈 IMPORT ArmDisableInterrupts // 导入ArmDisableInterrupts()函数 禁用arm 中断
IMPORT ArmDisableCachesAndMmu // 导入ArmDisableCachesAndMmu()函数 禁用cache, mmu
IMPORT ArmWriteCptr
IMPORT ArmWriteHcr
IMPORT ArmWriteVBar EXPORT _StackBase // 输出栈起始地址,起始地址为:0x80C00000
EXPORT _StackSize // 输出栈大小,栈大小为 0x00040000,256k
EXPORT CNTFRQ // 输出时钟频率,19200000 //定义于: BOOT.XF.1.4\boot_images\QcomPkg\Sdm660Pkg\Common\Sdm660Pkg_Loader.dsc
_StackBase
dcq FixedPcdGet64(PcdPrePiStackBase) _StackSize
dcq FixedPcdGet64(PcdPrePiStackSize) CNTFRQ
dcq FixedPcdGet32(PcdArmArchTimerFreqInHz) _ModuleEntryPoint
mov x0, #0
// 1、关闭所有中断 /* First ensure all interrupts are disabled */
bl ArmDisableInterrupts // 2、关闭MMU和Caches /* Ensure that the MMU and caches are off */
bl ArmDisableCachesAndMmu // 3、关闭TLB缓存表 /* Invalidate Instruction Cache and TLB */
bl ArmInvalidateInstructionCache
bl ArmInvalidateTlb
// 4、获得当前运行的安全环境:EL1、EL2、EL3
/* Get current EL in x0 */
EL1_OR_EL2_OR_EL3(x0)
// CurrentEL : 0xC = EL3; 8 = EL2; 4 = EL1
// This only selects between EL1 and EL2 and EL3, else we die.
// Provide the Macro with a safe temp xreg to use.
//mrs x0, CurrentEL
cmp x0, #0xC // 比较 x0寄存器是否为 0xc,如果是跳转到 标签3
beq %F3
cmp x0, #0x8 // 比较 x0寄存器是否为 0x8,如果是跳转到 标签2
beq %F2
cmp x0, #0x4 // 比较 x0寄存器是否为 0x4
bne 、 // We should never get here
// EL1 code starts here 1 beq _Start
2 beq _Start // 如果当前是 EL2,直接跳转到_Start
/* Do not trap any access to Floating Point and Advanced SIMD in EL3、*/
/* Note this works only in EL3, x0 has current EL mode */
3 mov x0, #0
bl ArmWriteCptr // 如果当前是 EL3,直接跳转到ArmWriteCptr
// msr cptr_el3, x0 // EL3 Coprocessor Trap Reg (CPTR) // 5、初始化ELX 安全环境
_SetupELx
mov x0, #0x30 /* RES1 */ // x0 = 0x30
orr x0, x0, #(1 << 0) /* Non-secure bit */ // 使能第0位为1
orr x0, x0, #(1 << 8) /* HVC enable */ // 使能第8位为1
orr x0, x0, #(1 << 10) /* 64-bit EL2 */ // 使能第10位为1
msr scr_el3, x0 // 配置通用寄存器 scr_el3 为-
msr cptr_el3, xzr /* Disable copro、traps to EL3 */
ldr x0, CNTFRQ
//msr cntfrq_el0, x0
msr sctlr_el2, xzr
.......省略一部分代码.......
// 6、使能 Cache
_EnableCache
#ifdef PRE_SIL
LoadConstantToReg (FixedPcdGet32(PcdSkipEarlyCacheMaint), x0)
cmn x0, #0
b.ne _PrepareArguments
#endif
bl ArmInvalidateDataCache
bl ArmEnableInstructionCache
bl ArmEnableDataCache
// 7、初始化栈
_PrepareArguments
/* Initialize Stack Canary */
bl InitStackCanary
// 8、调用 CEntryPoint,传参 _StackBase(0x80C00000)、_StackSize(0x00040000)
/* x0 = _StackBase and x1 = _StackSize */
ldr x0, _StackBase /* Stack base arg0 */
ldr x1, _StackSize /* Stack size arg1 */
bl CEntryPoint

初始化C运行环境

前面汇编代码中主要目的是初始化C运行环境,初始化栈,以便可以调C代码运行。

SEC的C代码入口位于: BOOT.XF.1.4\boot_images\QcomPkg\XBLCore\Sec.cCEntryPoint

/**  Entry point
@param StackBase pointer to the stack base
@param StackSize stack size
**/
VOID CEntryPoint (IN VOID *StackBase,IN UINTN StackSize){
UefiDebugModeEntry(); // 如果支待jtag调试的话,此处会循环等待,直到debug指向的地址匹配
TargetEarlyInit()
Main (StackBase, StackSize); //进入main函数,传参 _StackBase(0x80C00000)、_StackSize(0x00040000)
}

接下来,我们进入 main函数分析下:

VOID Main (IN  VOID  *StackBase, IN  UINTN StackSize){
// 1、获得fdf文件所在的地址,fdf可以说是UEFI的配置文件,
// 在fdf文件中包含所有的inf文件所在路径,及相关的bmp图片资源路径,以及相关的cfg配置文件路径。
// ## FD Base offset (refer to .fdf for FD size)
UefiFdBase = FixedPcdGet64(PcdEmbeddedFdBaseAddress); // 0x80200000 SecHeapMemBase = UefiFdBase + SEC_HEAP_MEM_OFFSET; // 0x300000
HobStackSize = StackSize;
// 2、初始化栈
InitStackCanary();
// 3、启动定时器周期计数
StartCyclCounter ();
// 4、初始化UART,主要是serial port端口初始化,及 serial buffer初始化 /* Start UART debug output */
UartInit();
// 5、打印"UEFI Start" 串口信息
PrintUefiStartInfo();
// 6、初始化CPU异常处理入口
InitializeCpuExceptionHandlers (NULL);
// 7、打印从开机到现在的时间差
PrintTimerDelta();
// 8、如果支持的话,启动程序流预测 /* Enable program flow prediction, if supported */
ArmEnableBranchPrediction ();
// 9、/* Initialize Info Block */
UefiInfoBlkPtr = InitInfoBlock (UefiFdBase + UEFI_INFO_BLK_OFFSET);
UefiInfoBlkPtr->StackBase = StackBase;
UefiInfoBlkPtr->StackSize = StackSize;
// 10、初始化 RAM 分区表,起始地址0x80000000,内存大小512M,检查地址是否非法,是否可正常访问
InitRamPartitionTableLib ();
ValidateFdRegion(UefiFdBase); //TODO: Move this to ACPI-specific location
InitializeFBPT(); /* Get nibble from random value to adjust SEC heap */
SecHeapAslrVal = AslrAdjustRNGVal(ASLR_HEAP_RNG_BITS);
// 11、初始化hoblist,有关hob可参考:https://blog.csdn.net/z190814412/article/details/85330324
InitHobList(SecHeapMemBase,SEC_HEAP_MEM_SIZE - (SecHeapAslrVal*ASLR_HEAP_ALIGN), UefiInfoBlkPtr);
/* Add the FVs to the hob list */
BuildFvHob (PcdGet64(PcdFlashFvMainBase), PcdGet64(PcdFlashFvMainSize));
// 12、打印RAM 分区信息
/* Should be done after we have setup HOB for memory allocation */
PrintRamPartitions ();
// 13、初始化cache
Status = EarlyCacheInit (UefiFdBase, UEFI_FD_SIZE); // 14、加载并解析 uefiplat.cfg平台配置文件,/* Load and Parse platform cfg file, cache re-initialized per cfg file */
Status = LoadAndParsePlatformCfg(); // 15、更新系统内存区相关信息 /* Add information from all other memory banks */
Status = UpdateSystemMemoryRegions();
Status = InitCacheWithMemoryRegions(); // 16、初始化所有的共享库 /* All shared lib related initialization */
// 初始化的lib源码位于 BOOT.XF.1.4\boot_images\QcomPkg\Sdm660Pkg\Library\
// 配置文件位于 BOOT.XF.1.4\boot_images\QcomPkg\Sdm660Pkg\LA\Sdm660Pkg_Core.dsc
Status = InitSharedLibs();
InitDbiDump(); // 17、获得DXE Heap堆内存信息,/* Look for "DXE Heap" memory region in config file */
Status = GetMemRegionInfoByName("DXE Heap", &DxeHeapMemInfo);
/* Get nibble from random value to adjust DXE heap */
DxeHeapAslrVal = AslrAdjustRNGVal(ASLR_HEAP_RNG_BITS); /* Re-initialize HOB to point to the DXE Heap in CFG */
ReInitHobList(DxeHeapMemInfo.MemBase,
DxeHeapMemInfo.MemSize - (DxeHeapAslrVal*ASLR_HEAP_ALIGN),
UefiInfoBlkPtr);
// 18、初始化分页池缓存区
/* Now we have access to bigger pool, move pre-pi memory allocation pool to it */
ReInitPagePoolBuffer ();
// 19、创建Stack、CPU Hob信息
BuildStackHob ((EFI_PHYSICAL_ADDRESS)StackBase, HobStackSize);
BuildCpuHob (PcdGet8 (PcdPrePiCpuMemorySize), PcdGet8 (PcdPrePiCpuIoSize));
// 20、打印早期信息
DisplayEarlyInfo(); AddMemRegionHobs (); /* Start perf here, after timer init, start at current tick value */
InitPerf(); /* SEC phase needs to run library constructors by hand */
ExtractGuidedSectionLibConstructor ();
LzmaDecompressLibConstructor ();
ZlibDecompressLibConstructor (); /* Build HOBs to pass up our Version of stuff the DXE Core needs to save space */
BuildPeCoffLoaderHob ();
BuildExtractSectionHob (
&gLzmaCustomDecompressGuid,
LzmaGuidedSectionGetInfo,
LzmaGuidedSectionExtraction
); BuildExtractSectionHob (
&gZlibDecompressGuid,
ZlibGuidedSectionGetInfo,
ZlibGuidedSectionDecompress
);
/* Check PRODMODE flag */
ProdmodeInfo = PRODMODE_ENABLED;
/* Build HOB to pass up prodmode info for security applications */
BuildGuidDataHob (&gQcomProdmodeInfoGuid, &ProdmodeInfo, sizeof(BOOLEAN));
UefiStartTime = ConvertTimerCountms(gUEFIStartCounter);
BuildGuidDataHob (&gEfiStartTimeHobGuid, &UefiStartTime, sizeof(UINT32)); /* Assume the FV that contains the SEC (our code) also contains a compressed FV */
DecompressFirstFv (); /* Any non-critical initialization */
TargetLateInit(); /* Build memory allocation HOB for FV2 type
Need to remove for decompressed image */
BuildMemHobForFv(EFI_HOB_TYPE_FV2); /* Load the DXE Core and transfer control to it */
LoadDxeCoreFromFv (NULL, 0); /* DXE Core should always load and never return */
ASSERT (FALSE);
CpuDeadLoop();
}

1、获得fdf文件所在的地址,

在fdf文件中包含所有的inf文件所在路径,及相关的bmp图片资源路径,以及相关的cfg配置文件路径。

fdf可以说是UEFI的配置文件。

2、初始化栈

3、启动定时器周期计数

4、初始化UART,主要是serial port端口初始化,及 serial buffer初始化

5、打印"UEFI Start" 串口信息

6、初始化CPU异常处理入口

7、打印从开机到现在的时间差

8、如果支持的话,启动程序流预测 /* Enable program flow prediction, if supported */

9、Initialize Info Block

10、初始化 RAM 分区表,起始地址0x80000000,内存大小512M,检查地址是否非法,是否可正常访问

11、初始化hoblist,有关hob可参考:https://blog.csdn.net/z190814412/article/details/85330324

12、打印RAM 分区信息

13、初始化cache

14、加载并解析 uefiplat.cfg平台配置文件

15、更新系统内存区相关信息 /* Add information from all other memory banks */

16、初始化所有的共享库 /* All shared lib related initialization */

初始化的lib源码位于 BOOT.XF.1.4\boot_images\QcomPkg\Sdm660Pkg\Library

配置文件位于 BOOT.XF.1.4\boot_images\QcomPkg\Sdm660Pkg\LA\Sdm660Pkg_Core.dsc

17、获得DXE Heap堆内存信息,/* Look for “DXE Heap” memory region in config file */

18、初始化分页池缓存区

19、创建Stack、CPU Hob信息

20、打印早期信息

PEI (EFI前期初始化)

BOOT.XF.1.4\boot_images\MdeModulePkg\Core\Pei\PeiMain\PeiMain.cPeiCore 函数中

DXE (驱动执行环境)

DXE的加载位置在:

BOOT.XF.1.4\boot_images\EmbeddedPkg\Library\PrePiLib\PrePiLib.cLoadDxeCoreFromFv

DXE的入口代码位于:

BOOT.XF.1.4\boot_images\MdeModulePkg\Core\Dxe\DxeMain\DxeMain.cDxeMain

BDS (启动设备选择)

代码位于:

BOOT.XF.1.4\boot_images\QcomPkg\Drivers\BdsDxe\BdsEntry.cBdsEntry

代码位于:

BOOT.XF.1.4\boot_images\QcomPkg\Drivers\QcomBds\QcomBds.cBdsEntry

XBL Loader

代码位于:

BOOT.XF.1.4\boot_images\QcomPkg\XBLLoader\boot_loader.c

RT(Run Time)

代码位于:

BOOT.XF.1.4\boot_images\MdeModulePkg\Core\RuntimeDxe\Runtime.cRuntimeDriverInitialize

高通Android UEFI XBL 代码流程分析的更多相关文章

  1. 高通Android display架构分析

    目录(?)[-] Kernel Space Display架构介绍 函数和数据结构介绍 函数和数据结构介绍 函数和数据结构介绍 数据流分析 初始化过程分析 User Space display接口 K ...

  2. 高通Android display分析【转】

    本文转载自:http://blog.csdn.net/zhangchiytu/article/details/6777039 高通7系列硬件架构分析 如上图,高通7系列 Display的硬件部分主要由 ...

  3. 高通Android平台硬件调试之Camera篇

    之前一段时间有幸在高通android平台上调试2款camera sensor,一款是OV的5M YUV sensor,支持jpeg out,同时也支持AF,调试比较比较简单,因为别的项目已经在使用了, ...

  4. Gradle之Android Gradle Plugin 主要流程分析(二)

    [Android 修炼手册]Gradle 篇 -- Android Gradle Plugin 主要流程分析 预备知识 理解 gradle 的基本开发 了解 gradle task 和 plugin ...

  5. nova start 虚机的代码流程分析

    nova start 虚机的代码流程分析,以ocata版本为分析基础1.nova api服务接受用户下发的 nova start启动虚机请求其对应的http restfull api接口为post / ...

  6. 高通spi 屏幕 -lk代码分析

    lk SPI驱动 1. 初始化时钟 在lk中,我们是从kmain开始执行下来的,而执行顺序则是先初始化时钟,也就是在platform_early_init函数中开始执行的: 在这里我们需要修改这个函数 ...

  7. Android 4.4KitKat AudioRecord 流程分析

    Android是架构分为三层: 底层      Linux Kernel 中间层  主要由C++实现 (Android 60%源码都是C++实现) 应用层  主要由JAVA开发的应用程序 应用程序执行 ...

  8. Cocos2d-x3.3RC0的Android编译Activity启动流程分析

    本文将从引擎源代码Jni分析Cocos2d-x3.3RC0的Android Activity的启动流程,以下是具体分析. 1.引擎源代码Jni.部分Java层和C++层代码分析 watermark/2 ...

  9. 高通android开发摘要

    一部分是开源的,可以从codeaurora.org上下载,还有一部分是高通产权的,需要从高通的网站上下载. 将高通产权的代码放到:vendor/qcom/proprietary 1. 设置bms一些参 ...

  10. u-boot移植(三)---修改前工作:代码流程分析2

    一.vectors.S 1.1 代码地址 vectors.S (arch\arm\lib) 1.2 流程跳转 跳转符号 B 为 start.S 中的 reset 执行代码,暂且先不看,先看看 vect ...

随机推荐

  1. 几个ABAP FREE面试问题

    Text. Text. Text. Text. Text. 电话面试,有几个问题没有回答上.有些问题是此前完全不了解的,有些是学过但因为好久不用已经忘记.这里试着重新回答一下. 1,如何创建bapi? ...

  2. 01 go-zero入门--单体服务demo

    目录 官方文档: 视频地址: 系统环境: 配置环境: 1. golang安装 2. go module配置 3. goctl安装 4.protoc,protoc-gen-go,protoc-gen-g ...

  3. linux下的开机启动

    使用systemctl命令,systemctl命令是系统服务管理器指令,它实际上将 service 和 chkconfig 这两个命令组合到一起. 据说在CentOS7.0后,不再使用service, ...

  4. webstorm上的Element提示插件

    https://zhuanlan.zhihu.com/p/34735529 首先说明下,这个插件是用idea开发的,在idea上同样适用.有兴趣的朋友可以直接git下来开发自己的小插件. 插件基于的w ...

  5. ansible系列(29)--ansible的Jinja2语法及应用

    目录 1. Ansible Jinja2 1.1 jinja2语法结构 1.2 jinja2中{{ }}中的运算符 1.3 jinja2中for循环和if判断示例 1.4 Jinja2管理Nginx负 ...

  6. 远程控制软件 TeamViewer 的局限性和替代方案

    TeamViewer 公司创建于2005年,总部位于德国,客户遍及全球,其中企业用户居多,其各方面性能都很不错,但价格却非常贵.针对个人用户,TeamViewer 提供免费版软件,但时不时会提示&qu ...

  7. apisix~升级原始插件的方法

    扩展apisix原始插件 当apisix提供的插件不能满足我们要求时,我们可能需要将它的plugin进行个性化扩展,例如一个jwt认证插件jwt-auth,它本身具有验证jwt有效性功能,支持rs25 ...

  8. 初识上位机(上):搭建PLC模拟仿真环境

    大家好,我是Edison. 作为一个工业自动化领域的程序员,不懂点PLC和上位机,貌似有点说不过去.这里我用两篇小文带你快速进入上位机开发领域.后续,我会考虑再出一个系列文章一起玩工控上位机. 什么是 ...

  9. mybatis-plus id在高并发下出现重复

    mybaits-plus ASSIGN_ID生成 id生成策略 在分布式高并发环境下出现重复id https://github.com/baomidou/mybatis-plus/issues/307 ...

  10. MySQL慢查询及优化

    最近做一个CRM系统,发现了慢查询日志里记载了许多的慢sql,于是就对其进行了sql优化.在优化的过程中,自己也归纳整理了一些sql优化的方案.今天就来和大家聊聊. **1.慢查询的分析** 常见的分 ...