高通Android UEFI XBL 代碼流程分析


高通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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM