3 BL1

3.1 bl1_entrypoint

bl1的源码如下

img

  • bl1.ld.S为链接文件
  • bl1.mk为编译文件

bl1.ld.S中定义了bl1的入口函数为bl1_entrypoint以及内存布局,ROM的内存为可读可执行,RAM的内存可读可写可执行

ENTRY(bl1_entrypoint)

MEMORY {
ROM (rx): ORIGIN = BL1_RO_BASE, LENGTH = BL1_RO_LIMIT - BL1_RO_BASE
RAM (rwx): ORIGIN = BL1_RW_BASE, LENGTH = BL1_RW_LIMIT - BL1_RW_BASE
}

关于BL1_RO_BASEBL1_RW_BASE的值,这个是和平台息息相关的,比如qemu平台上:

/*
* Partition memory into secure ROM, non-secure DRAM, secure "SRAM",
* and secure DRAM.
*/
#define SEC_ROM_BASE 0x00000000
#define SEC_ROM_SIZE 0x00020000
#define SEC_SRAM_BASE 0x0e000000
#define SEC_SRAM_SIZE 0x00100000

/*
* ARM-TF lives in SRAM, partition it here
*/
#define SHARED_RAM_BASE SEC_SRAM_BASE
#define SHARED_RAM_SIZE 0x00001000
#define BL_RAM_BASE (SHARED_RAM_BASE + SHARED_RAM_SIZE)
#define BL_RAM_SIZE (SEC_SRAM_SIZE - SHARED_RAM_SIZE)

/*
* BL1 specific defines.
*
* BL1 RW data is relocated from ROM to RAM at runtime so we need 2 sets of
* addresses.
* Put BL1 RW at the top of the Secure SRAM. BL1_RW_BASE is calculated using
* the current BL1 RW debug size plus a little space for growth.
*/
#define BL1_RO_BASE SEC_ROM_BASE
#define BL1_RO_LIMIT (SEC_ROM_BASE + SEC_ROM_SIZE)
#define BL1_RW_BASE (BL1_RW_LIMIT - 0x12000)
#define BL1_RW_LIMIT (BL_RAM_BASE + BL_RAM_SIZE)

bl1_entrypoint函数定义在bl1\aarch64\bl1_entrypoint.S中:

    .globl  bl1_entrypoint
.globl bl1_run_bl2_in_root

/* -----------------------------------------------------
* bl1_entrypoint() is the entry point into the trusted
* firmware code when a cpu is released from warm or
* cold reset.
* -----------------------------------------------------
*/

func bl1_entrypoint
/* ---------------------------------------------------------------------
* If the reset address is programmable then bl1_entrypoint() is
* executed only on the cold boot path. Therefore, we can skip the warm
* boot mailbox mechanism.
* ---------------------------------------------------------------------
*/
// EL3级别运行环境的初始化,该函数定义在 include/common/aarch64/el3_common_macros.S文件中
el3_entrypoint_common \
_init_sctlr=1 \
_warm_boot_mailbox=!PROGRAMMABLE_RESET_ADDRESS \
_secondary_cold_boot=!COLD_BOOT_SINGLE_CPU \
_init_memory=1 \
_init_c_runtime=1 \
_exception_vectors=bl1_exceptions \
_pie_fixup_size=0

/* --------------------------------------------------------------------
* Initialize platform and jump to our c-entry point
* for this type of reset.
* --------------------------------------------------------------------
*/
bl bl1_main

/* --------------------------------------------------
* Do the transition to next boot image.
* --------------------------------------------------
*/
#if ENABLE_RME
b bl1_run_bl2_in_root
#else
b el3_exit
#endif
endfunc bl1_entrypoint

func bl1_run_bl2_in_root
/* read bl2_ep_info */
adrp x20, bl2_ep_info
add x20, x20, :lo12:bl2_ep_info
ldr x20, [x20]

/* ---------------------------------------------
* MMU needs to be disabled because BL2 executes
* in EL3. It will initialize the address space
* according to its own requirements.
* ---------------------------------------------
*/
bl disable_mmu_icache_el3
tlbi alle3

ldp x0, x1, [x20, #ENTRY_POINT_INFO_PC_OFFSET]
msr elr_el3, x0
msr spsr_el3, x1

ldp x6, x7, [x20, #(ENTRY_POINT_INFO_ARGS_OFFSET + 0x30)]
ldp x4, x5, [x20, #(ENTRY_POINT_INFO_ARGS_OFFSET + 0x20)]
ldp x2, x3, [x20, #(ENTRY_POINT_INFO_ARGS_OFFSET + 0x10)]
ldp x0, x1, [x20, #(ENTRY_POINT_INFO_ARGS_OFFSET + 0x0)]
exception_return
endfunc bl1_run_bl2_in_root
  • bl1_entrypoint函数进来首先会掉用el3_entrypoint_common此宏,此宏做了很多事情,会根据传入的参数来进行操作:

    • 参数 含义(是否需要执行该步骤) 典型使用场景 备注
      _init_sctlr 是否初始化 SCTLR_EL3(含 endian 设置) 几乎总是需要 通常传 1
      _warm_boot_mailbox 是否检查 warm boot mailbox 并跳转 BL31 通常需要,BL1 通常不需要 决定是否要读平台 entrypoint
      _secondary_cold_boot 是否区分 primary/secondary CPU 并处理 secondary 冷启动多核平台几乎都要 可设 0 跳过(已知只 primary)
      _init_memory 是否执行平台内存初始化(platform_mem_init) 通常 primary CPU 冷启动需要
      _init_c_runtime 是否初始化 C 运行时环境(清 BSS、COHERENT_RAM 等) 几乎所有进入 C 代码前都要
      _exception_vectors 要设置的 VBAR_EL3 向量表地址 必填,通常是 el3_vectors 或类似
      _pie_fixup_size PIE(位置无关可执行)需要修复的 GDT 大小 开启 PIE 时非 0 通常是映像大小
  • 接着通过bl bl1_main跳转到bl1_main函数中执行,执行完毕bl1_main后会接着判断是否启用ENABLE_RME,关于RME是什么,可以参考如下的文章,这是ARM-V9引入的新的特性,我们暂且不关注https://zhuanlan.zhihu.com/p/25848029106。这是一种应用于ARM机密计算(CCA)的一种新架构

img

  • 最后调用el3_exit函数进入下一阶段运行

3.2 el3_entrypoint_common

/* -----------------------------------------------------------------------------
* This is the super set of actions that need to be performed during a cold boot
* or a warm boot in EL3. This code is shared by BL1 and BL31.
*
* This macro will always perform reset handling, architectural initialisations
* and stack setup. The rest of the actions are optional because they might not
* be needed, depending on the context in which this macro is called. This is
* why this macro is parameterised ; each parameter allows to enable/disable
* some actions.
*
* _init_sctlr:
* Whether the macro needs to initialise SCTLR_EL3, including configuring
* the endianness of data accesses.
*
* _warm_boot_mailbox:
* Whether the macro needs to detect the type of boot (cold/warm). The
* detection is based on the platform entrypoint address : if it is zero
* then it is a cold boot, otherwise it is a warm boot. In the latter case,
* this macro jumps on the platform entrypoint address.
*
* _secondary_cold_boot:
* Whether the macro needs to identify the CPU that is calling it: primary
* CPU or secondary CPU. The primary CPU will be allowed to carry on with
* the platform initialisations, while the secondaries will be put in a
* platform-specific state in the meantime.
*
* If the caller knows this macro will only be called by the primary CPU
* then this parameter can be defined to 0 to skip this step.
*
* _init_memory:
* Whether the macro needs to initialise the memory.
*
* _init_c_runtime:
* Whether the macro needs to initialise the C runtime environment.
*
* _exception_vectors:
* Address of the exception vectors to program in the VBAR_EL3 register.
*
* _pie_fixup_size:
* Size of memory region to fixup Global Descriptor Table (GDT).
*
* A non-zero value is expected when firmware needs GDT to be fixed-up.
*
* -----------------------------------------------------------------------------
*/
.macro el3_entrypoint_common \
_init_sctlr, _warm_boot_mailbox, _secondary_cold_boot, \
_init_memory, _init_c_runtime, _exception_vectors, \
_pie_fixup_size

.if \_init_sctlr
/* -------------------------------------------------------------
* This is the initialisation of SCTLR_EL3 and so must ensure
* that all fields are explicitly set rather than relying on hw.
* Some fields reset to an IMPLEMENTATION DEFINED value and
* others are architecturally UNKNOWN on reset.
*
* SCTLR.EE: Set the CPU endianness before doing anything that
* might involve memory reads or writes. Set to zero to select
* Little Endian.
*
* SCTLR_EL3.WXN: For the EL3 translation regime, this field can
* force all memory regions that are writeable to be treated as
* XN (Execute-never). Set to zero so that this control has no
* effect on memory access permissions.
*
* SCTLR_EL3.SA: Set to zero to disable Stack Alignment check.
*
* SCTLR_EL3.A: Set to zero to disable Alignment fault checking.
*
* SCTLR.DSSBS: Set to zero to disable speculation store bypass
* safe behaviour upon exception entry to EL3.
* -------------------------------------------------------------
*/
mov_imm x0, (SCTLR_RESET_VAL & ~(SCTLR_EE_BIT | SCTLR_WXN_BIT \
| SCTLR_SA_BIT | SCTLR_A_BIT | SCTLR_DSSBS_BIT))
#if ENABLE_FEAT_RAS
/* If FEAT_RAS is present assume FEAT_IESB is also present */
orr x0, x0, #SCTLR_IESB_BIT
#endif
msr sctlr_el3, x0
isb
.endif /* _init_sctlr */
/* 判定是否需要调用do_cold_boot流程 */
.if \_warm_boot_mailbox
/* -------------------------------------------------------------
* This code will be executed for both warm and cold resets.
* Now is the time to distinguish between the two.
* Query the platform entrypoint address and if it is not zero
* then it means it is a warm boot so jump to this address.
* -------------------------------------------------------------
*/
bl plat_get_my_entrypoint
cbz x0, do_cold_boot
br x0

do_cold_boot:
.endif /* _warm_boot_mailbox */

.if \_pie_fixup_size
#if ENABLE_PIE
/*
* ------------------------------------------------------------
* If PIE is enabled fixup the Global descriptor Table only
* once during primary core cold boot path.
*
* Compile time base address, required for fixup, is calculated
* using "pie_fixup" label present within first page.
* ------------------------------------------------------------
*/
pie_fixup:
ldr x0, =pie_fixup
and x0, x0, #~(PAGE_SIZE_MASK)
mov_imm x1, \_pie_fixup_size
add x1, x1, x0
bl fixup_gdt_reloc
#endif /* ENABLE_PIE */
.endif /* _pie_fixup_size */

/* ---------------------------------------------------------------------
* Set the exception vectors.
* 初始化异常向量表
* ---------------------------------------------------------------------
*/
adr x0, \_exception_vectors
msr vbar_el3, x0
isb

call_reset_handler

el3_arch_init_common

/* ---------------------------------------------------------------------
* Set the el3 execution context(i.e. root_context).
* ---------------------------------------------------------------------
*/
setup_el3_execution_context
/* 判定当前CPU是否是主CPU,如果是则做主CPU的初始化 */
.if \_secondary_cold_boot
/* -------------------------------------------------------------
* Check if this is a primary or secondary CPU cold boot.
* The primary CPU will set up the platform while the
* secondaries are placed in a platform-specific state until the
* primary CPU performs the necessary actions to bring them out
* of that state and allows entry into the OS.
* -------------------------------------------------------------
*/
bl plat_is_my_cpu_primary
cbnz w0, do_primary_cold_boot

/* This is a cold boot on a secondary CPU */
bl plat_secondary_cold_boot_setup
/* plat_secondary_cold_boot_setup() is not supposed to return */
bl el3_panic

do_primary_cold_boot:
.endif /* _secondary_cold_boot */

/* ---------------------------------------------------------------------
* Initialize memory now. Secondary CPU initialization won't get to this
* point.
* ---------------------------------------------------------------------
*/
/* 初始化memory */
.if \_init_memory
bl platform_mem_init
.endif /* _init_memory */

/* ---------------------------------------------------------------------
* Init C runtime environment:
* - Zero-initialise the NOBITS sections. There are 2 of them:
* - the .bss section;
* - the coherent memory section (if any).
* - Relocate the data section from ROM to RAM, if required.
* ---------------------------------------------------------------------
*/
.if \_init_c_runtime
#if defined(IMAGE_BL31) || (defined(IMAGE_BL2) && \
((RESET_TO_BL2 && BL2_INV_DCACHE) || ENABLE_RME))
/* -------------------------------------------------------------
* Invalidate the RW memory used by the BL31 image. This
* includes the data and NOBITS sections. This is done to
* safeguard against possible corruption of this memory by
* dirty cache lines in a system cache as a result of use by
* an earlier boot loader stage. If PIE is enabled however,
* RO sections including the GOT may be modified during
* pie fixup. Therefore, to be on the safe side, invalidate
* the entire image region if PIE is enabled.
* -------------------------------------------------------------
*/
#if ENABLE_PIE
#if SEPARATE_CODE_AND_RODATA
adrp x0, __TEXT_START__
add x0, x0, :lo12:__TEXT_START__
#else
adrp x0, __RO_START__
add x0, x0, :lo12:__RO_START__
#endif /* SEPARATE_CODE_AND_RODATA */
#else
adrp x0, __RW_START__
add x0, x0, :lo12:__RW_START__
#endif /* ENABLE_PIE */
adrp x1, __RW_END__
add x1, x1, :lo12:__RW_END__
sub x1, x1, x0
bl inv_dcache_range
#if defined(IMAGE_BL31) && SEPARATE_NOBITS_REGION
adrp x0, __NOBITS_START__
add x0, x0, :lo12:__NOBITS_START__
adrp x1, __NOBITS_END__
add x1, x1, :lo12:__NOBITS_END__
sub x1, x1, x0
bl inv_dcache_range
#endif
#if defined(IMAGE_BL2) && SEPARATE_BL2_NOLOAD_REGION
adrp x0, __BL2_NOLOAD_START__
add x0, x0, :lo12:__BL2_NOLOAD_START__
adrp x1, __BL2_NOLOAD_END__
add x1, x1, :lo12:__BL2_NOLOAD_END__
sub x1, x1, x0
bl inv_dcache_range
#endif
#endif
#if defined(IMAGE_BL31)
adrp x0, __PER_CPU_START__
add x0, x0, :lo12:__PER_CPU_START__
adrp x1, __PER_CPU_END__
add x1, x1, :lo12:__PER_CPU_END__
sub x1, x1, x0
#if (PLATFORM_NODE_COUNT > 1)
mov x9, x1
#endif /* (PLATFORM_NODE_COUNT > 1) */
bl zeromem
#if (PLATFORM_NODE_COUNT > 1)
/*
* Zero-initialize per-cpu sections defined by the platform.
* Care must be taken to preserve and retain the clobbered
* registers. A standard around the container for per-cpu nodes
* is not yet defined.
*/
mov x10, #1
mov x11, #PLATFORM_NODE_COUNT

1:
cmp x10, x11
b.hs 2f

mov x0, x10
bl plat_per_cpu_node_base
cmn x0, #1
b.eq 3f

/* x1 contains size param */
mov x1, x9
bl zeromem

3:
add x10, x10, #1
b 1b

2:
#endif /* (PLATFORM_NODE_COUNT > 1) */
#endif /* defined(IMAGE_BL31) */

adrp x0, __BSS_START__
add x0, x0, :lo12:__BSS_START__

adrp x1, __BSS_END__
add x1, x1, :lo12:__BSS_END__
sub x1, x1, x0
bl zeromem

#if USE_COHERENT_MEM
adrp x0, __COHERENT_RAM_START__
add x0, x0, :lo12:__COHERENT_RAM_START__
adrp x1, __COHERENT_RAM_END_UNALIGNED__
add x1, x1, :lo12: __COHERENT_RAM_END_UNALIGNED__
sub x1, x1, x0
bl zeromem
#endif

#if defined(IMAGE_BL1) || \
(defined(IMAGE_BL2) && RESET_TO_BL2 && BL2_IN_XIP_MEM) || \
(defined(IMAGE_BL31) && SEPARATE_RWDATA_REGION)

adrp x0, __DATA_RAM_START__
add x0, x0, :lo12:__DATA_RAM_START__
adrp x1, __DATA_ROM_START__
add x1, x1, :lo12:__DATA_ROM_START__
adrp x2, __DATA_RAM_END__
add x2, x2, :lo12:__DATA_RAM_END__
sub x2, x2, x0
bl memcpy16
#endif
.endif /* _init_c_runtime */

/* ---------------------------------------------------------------------
* Use SP_EL0 for the C runtime stack.
* ---------------------------------------------------------------------
*/
msr spsel, #0

/* ---------------------------------------------------------------------
* Allocate a stack whose memory will be marked as Normal-IS-WBWA when
* the MMU is enabled. There is no risk of reading stale stack memory
* after enabling the MMU as only the primary CPU is running at the
* moment.
* ---------------------------------------------------------------------
*/
bl plat_set_my_stack

#if STACK_PROTECTOR_ENABLED
.if \_init_c_runtime
bl update_stack_protector_canary
.endif /* _init_c_runtime */
#endif
.endm

.macro apply_at_speculative_wa
#if ERRATA_SPECULATIVE_AT
/*
* This function expects x30 has been saved.
* Also, save x29 which will be used in the called function.
*/
str x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X29]
bl save_and_update_ptw_el1_sys_regs
ldr x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X29]
#endif
.endm

.macro restore_ptw_el1_sys_regs
#if ERRATA_SPECULATIVE_AT
/* -----------------------------------------------------------
* In case of ERRATA_SPECULATIVE_AT, must follow below order
* to ensure that page table walk is not enabled until
* restoration of all EL1 system registers. TCR_EL1 register
* should be updated at the end which restores previous page
* table walk setting of stage1 i.e.(TCR_EL1.EPDx) bits. ISB
* ensures that CPU does below steps in order.
*
* 1. Ensure all other system registers are written before
* updating SCTLR_EL1 using ISB.
* 2. Restore SCTLR_EL1 register.
* 3. Ensure SCTLR_EL1 written successfully using ISB.
* 4. Restore TCR_EL1 register.
* -----------------------------------------------------------
*/
isb
ldp x28, x29, [sp, #CTX_ERRATA_SPEC_AT_OFFSET + CTX_ERRATA_SPEC_AT_SCTLR_EL1]
msr sctlr_el1, x28
isb
msr tcr_el1, x29
#endif
.endm

总体流程如下:

1. (可选)初始化 SCTLR_EL3

2. (可选)检查 warm boot mailbox → 如果是 warm boot 直接跳转
↓ (否则继续冷启动流程)
3. (可选)PIE 全局指针表修复(只 primary 冷启动做一次)

4. 设置 VBAR_EL3(异常向量表)

5. 调用平台 reset handler(call_reset_handler)

6. 架构公共初始化(el3_arch_init_common)

7. 设置 EL3 执行上下文(setup_el3_execution_context)

8. (可选)判断 primary / secondary CPU
secondary → 进入平台特定的等待逻辑(通常死循环或 WFI)
primary → 继续往下走

9. (可选)平台内存初始化(platform_mem_init)

10.(可选)初始化 C 运行时环境
• 失效脏 cache(inv_dcache_range)
• 清零 .bss / coherent / per-cpu 区域
• 必要时从 ROM 拷贝 .data 到 RAM

11. 切换到 SP_EL0(C 语言栈)

12. 设置当前 CPU 的栈(plat_set_my_stack)

13. (可选)更新栈保护 canary

进入后续 C 代码(bl bl31_main / bl1_main 等)

3.2.1 SCTLR_EL3寄存器

此寄存器全名为:System Control Register for Exception Level 3

img

各个bit的解释如下:

名称 含义(简要中文) 取值解释 复位值(PE 复位到 EL3 时) TF-A 典型做法(el3_entrypoint_common) 是否常见扩展特性
[63:45] 保留,RES0
44 DSSBS 禁用投机存储旁路安全(Speculative Store Bypass Safe) 0:异常进入 EL3 时 PSTATE.SSBS = 0(启用 SSBS 防护) 1:PSTATE.SSBS = 1(禁用防护) IMPLEMENTATION DEFINED 强制清0(安全优先) ARMv8.0-SSBS
43 ATA EL3 分配标签访问控制(Allocation Tag Access) 0:禁止访问 Allocation Tags 1:允许访问 UNKNOWN 通常不设(依赖平台) ARMv8.5-MemTag
42 保留,RES0
[41:40] TCF EL3 Tag Check Fault 处理方式 00:无影响 01:同步异常 10:异步累积 11:保留 UNKNOWN 通常不设 ARMv8.5-MemTag
[39:38] 保留,RES0
37 ITFSB Tag Check Fault 异步异常进入 EL3 时是否自动同步到 TFSR 0:不同步 1:同步 UNKNOWN 通常不设 ARMv8.5-MemTag
36 BT PAC 分支类型兼容性(Branch Type compatibility) 0:PAC*ASP 与 BTYPE=0b11 兼容 1:不兼容 UNKNOWN 通常不设 ARMv8.5-BTI
[35:32] 保留,RES0
31 EnIA 使用 APIAKey_EL1 对指令地址进行 PAC(Pointer Authentication) 0:禁用 1:启用 UNKNOWN 通常不设(或平台决定) ARMv8.3-PAuth
30 EnIB 使用 APIBKey_EL1 对指令地址进行 PAC 同上 UNKNOWN 同上 ARMv8.3-PAuth
[29:28] 保留,RES1 必须写1
27 EnDA 使用 APDAKey_EL1 对数据地址进行 PAC 0:禁用 1:启用 UNKNOWN 通常不设 ARMv8.3-PAuth
26 保留,RES0
25 EE EL3 数据访问 + Stage-1 页表走访 的 Endianness 0:Little-endian(小端) 1:Big-endian(大端) IMPLEMENTATION DEFINED 强制 0(现代系统全 LE) 基础特性
24 保留,RES0
23 保留,RES1 必须写1
22 EIS 异常进入 EL3 是否为上下文同步事件(Context Synchronizing) 0:不是 1:是 UNKNOWN 通常不设(默认0) ARMv8.5-CSEH
21 IESB 隐式错误同步屏障(Implicit Error Synchronization Barrier) 0:禁用 1:启用(异常进入/ERET 前加隐式 ESB) UNKNOWN 如果 ENABLE_FEAT_RAS 则设1 ARMv8.2-IESB
20 保留,RES0
19 WXN Write 权限隐含 XN(Writeable implies eXecute-Never) 0:无影响 1:EL3 可写区域强制不可执行 UNKNOWN 强制 0(避免限制) 基础特性
18 保留,RES1 必须写1
17 保留,RES0
16 保留,RES1 必须写1
[15:14] 保留,RES0
13 EnDB 使用 APDBKey_EL1 对数据地址进行 PAC 0:禁用 1:启用 UNKNOWN 通常不设 ARMv8.3-PAuth
12 I EL3 指令缓存控制 0:指令 Non-cacheable 1:正常缓存策略 0 通常后续设1(开启 I-cache) 基础特性
11 EOS 异常返回(ERET)是否为上下文同步事件 0:不是 1:是 UNKNOWN 通常不设 ARMv8.5-CSEH
[10:7] 保留,RES0
6 nAA 非对齐访问控制(针对某些 Acquire/Release 指令) 0:强制 16 字节对齐,否则 Alignment fault 1:允许非对齐 UNKNOWN 通常不设(默认0较安全) ARMv8.4-LSE
[5:4] 保留,RES1 必须写1
3 SA SP(栈指针)对齐检查 0:禁用 1:SP 必须 16 字节对齐,否则异常 UNKNOWN 强制 0(方便栈操作) 基础特性
2 C EL3 数据缓存控制 0:数据 Non-cacheable 1:正常缓存策略 0 通常后续设1(开启 D-cache) 基础特性
1 A 常规内存访问对齐检查 0:禁用对齐检查 1:启用(未对齐访问 → Alignment fault) UNKNOWN 强制 0(避免不必要故障) 基础特性
0 M EL3 Stage-1 MMU 使能 0:关闭地址翻译 1:开启 0 早期通常0,后续视平台设1 基础特性

el3_entrypoint_common中做了如下设置:

mov_imm x0, (SCTLR_RESET_VAL & ~(SCTLR_EE_BIT | SCTLR_WXN_BIT \
| SCTLR_SA_BIT | SCTLR_A_BIT | SCTLR_DSSBS_BIT))
#if ENABLE_FEAT_RAS
/* If FEAT_RAS is present assume FEAT_IESB is also present */
orr x0, x0, #SCTLR_IESB_BIT
#endif
msr sctlr_el3, x0
isb
  • SCTLR_RESET_VAL:硬件复位后的默认值(平台定义,但通常包含未知位)
  • 强制 EE=0(小端)
  • 强制 WXN=0(不强制写=不可执行)
  • 强制 SA=0(栈不对齐也行)
  • 强制 A=0(普通访问不对齐也行)
  • 强制 DSSBS=0(启用 SSBS 防护)

3.2.2 EL3异常向量表设置

img

Bits **[63:11]**:真正的向量基地址(Vector Base Address)

  • 必須對齊到 2KB(因為最低 11 bits 是保留的)

Bits **[10:0]**:RES0(保留,必須為 0)

/* ---------------------------------------------------------------------
* Set the exception vectors.
* ---------------------------------------------------------------------
*/
adr x0, \_exception_vectors
msr vbar_el3, x0
isb
  • 设置EL3的异常向量表为bl1_exceptions
/* -----------------------------------------------------------------------------
* Very simple stackless exception handlers used by BL1.
* -----------------------------------------------------------------------------
*/
.globl bl1_exceptions

vector_base bl1_exceptions

/* -----------------------------------------------------
* Current EL with SP0 : 0x0 - 0x200
* -----------------------------------------------------
*/
vector_entry SynchronousExceptionSP0
mov x0, #SYNC_EXCEPTION_SP_EL0
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry SynchronousExceptionSP0

vector_entry IrqSP0
mov x0, #IRQ_SP_EL0
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry IrqSP0

vector_entry FiqSP0
mov x0, #FIQ_SP_EL0
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry FiqSP0

vector_entry SErrorSP0
mov x0, #SERROR_SP_EL0
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry SErrorSP0

/* -----------------------------------------------------
* Current EL with SPx: 0x200 - 0x400
* -----------------------------------------------------
*/
vector_entry SynchronousExceptionSPx
mov x0, #SYNC_EXCEPTION_SP_ELX
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry SynchronousExceptionSPx

vector_entry IrqSPx
mov x0, #IRQ_SP_ELX
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry IrqSPx

vector_entry FiqSPx
mov x0, #FIQ_SP_ELX
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry FiqSPx

vector_entry SErrorSPx
mov x0, #SERROR_SP_ELX
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry SErrorSPx

/* -----------------------------------------------------
* Lower EL using AArch64 : 0x400 - 0x600
* -----------------------------------------------------
*/
vector_entry SynchronousExceptionA64
/* Enable the SError interrupt */
msr daifclr, #DAIF_ABT_BIT

str x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]

/* Expect only SMC exceptions */
mrs x30, esr_el3
ubfx x30, x30, #ESR_EC_SHIFT, #ESR_EC_LENGTH
cmp x30, #EC_AARCH64_SMC
b.ne unexpected_sync_exception

b smc_handler64
end_vector_entry SynchronousExceptionA64

vector_entry IrqA64
mov x0, #IRQ_AARCH64
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry IrqA64

vector_entry FiqA64
mov x0, #FIQ_AARCH64
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry FiqA64

vector_entry SErrorA64
mov x0, #SERROR_AARCH64
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry SErrorA64

/* -----------------------------------------------------
* Lower EL using AArch32 : 0x600 - 0x800
* -----------------------------------------------------
*/
vector_entry SynchronousExceptionA32
mov x0, #SYNC_EXCEPTION_AARCH32
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry SynchronousExceptionA32

vector_entry IrqA32
mov x0, #IRQ_AARCH32
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry IrqA32

vector_entry FiqA32
mov x0, #FIQ_AARCH32
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry FiqA32

vector_entry SErrorA32
mov x0, #SERROR_AARCH32
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry SErrorA32

bl1的异常向量表从 bl1_exceptions 开始,2KB 对齐,总共 0x800 字节,分成 4 个 0x200 字节的块:

偏移 异常来源 BL1 处理方式 预期异常类型
0x000 Current EL with SP0 全部 panic 不应该发生
0x200 Current EL with SPx (EL3 用自己的 SP) 全部 panic 不应该发生
0x400 Lower EL 使用 AArch64 只允许 SMC,其余 panic 只允许 SMC
0x600 Lower EL 使用 AArch32 全部 panic 不支持 AArch32

plat_report_exception是一个和平台相关的自定义函数,例如:

    /* -----------------------------------------------------
* Placeholder function which should be redefined by
* each platform.
* -----------------------------------------------------
*/
func plat_report_exception
ret
endfunc plat_report_exception
.globl plat_report_exception

/* ---------------------------------------------
* void plat_report_exception(unsigned int type)
* Function to report an unhandled exception
* with platform-specific means.
* On FVP platform, it updates the LEDs
* to indicate where we are
* ---------------------------------------------
*/
func plat_report_exception
mrs x1, CurrentEl
lsr x1, x1, #MODE_EL_SHIFT
lsl x1, x1, #V2M_SYS_LED_EL_SHIFT
lsl x0, x0, #V2M_SYS_LED_EC_SHIFT
mov x2, #(SECURE << V2M_SYS_LED_SS_SHIFT)
orr x0, x0, x2
orr x0, x0, x1
mov x1, #V2M_SYSREGS_BASE
add x1, x1, #V2M_SYS_LED
str w0, [x1]
ret
endfunc plat_report_exception

对于smc_handler64的处理,函数逻辑如下:

func smc_handler64
/* ----------------------------------------------
* Detect if this is a RUN_IMAGE or other SMC.
* ----------------------------------------------
*/
mov x30, #BL1_SMC_RUN_IMAGE
cmp x30, x0
b.ne smc_handler

/* ------------------------------------------------
* Make sure only Secure world reaches here.
* ------------------------------------------------
*/
mrs x30, scr_el3
tst x30, #SCR_NS_BIT
b.ne unexpected_sync_exception

/* ----------------------------------------------
* Handling RUN_IMAGE SMC. First switch back to
* SP_EL0 for the C runtime stack.
* ----------------------------------------------
*/
ldr x30, [sp, #CTX_EL3STATE_OFFSET + CTX_RUNTIME_SP]
msr spsel, #MODE_SP_EL0
mov sp, x30

/* ---------------------------------------------------------------------
* Pass EL3 control to next BL image.
* Here it expects X1 with the address of a entry_point_info_t
* structure describing the next BL image entrypoint.
* ---------------------------------------------------------------------
*/
mov x20, x1

mov x0, x20
bl bl1_print_next_bl_ep_info

ldp x0, x1, [x20, #ENTRY_POINT_INFO_PC_OFFSET]
msr elr_el3, x0
msr spsr_el3, x1
ubfx x0, x1, #MODE_EL_SHIFT, #2
cmp x0, #MODE_EL3
b.ne unexpected_sync_exception

bl disable_mmu_icache_el3
tlbi alle3
dsb ish /* ERET implies ISB, so it is not needed here */

#if SPIN_ON_BL1_EXIT
bl print_debug_loop_message
debug_loop:
b debug_loop
#endif

mov x0, x20
bl bl1_plat_prepare_exit

ldp x6, x7, [x20, #(ENTRY_POINT_INFO_ARGS_OFFSET + 0x30)]
ldp x4, x5, [x20, #(ENTRY_POINT_INFO_ARGS_OFFSET + 0x20)]
ldp x2, x3, [x20, #(ENTRY_POINT_INFO_ARGS_OFFSET + 0x10)]
ldp x0, x1, [x20, #(ENTRY_POINT_INFO_ARGS_OFFSET + 0x0)]
exception_return

smc_handler:
bl prepare_el3_entry

/* -----------------------------------------------------
* Go to BL1 SMC handler.
* -----------------------------------------------------
*/
bl bl1_smc_wrapper_aarch64

/* -----------------------------------------------------
* Do the transition to next BL image.
* -----------------------------------------------------
*/
b el3_exit
endfunc smc_handler64

smc_handler64

├─ 如果 x0 == BL1_SMC_RUN_IMAGE

│ └─ 走「RUN_IMAGE 快速路径」→ 直接跳转下一个 BL

└─ 否则

​ └─ 走「普通 SMC 路径」

​ → bl1_smc_wrapper_aarch64

​ → el3_exit

3.2.3 CPU复位

.macro call_reset_handler
#if defined(IMAGE_BL1) || defined(IMAGE_BL31) || (defined(IMAGE_BL2) && RESET_TO_BL2)
/* ---------------------------------------------------------------------
* It is a cold boot.
* Perform any processor specific actions upon reset e.g. cache, TLB
* invalidations etc.
* ---------------------------------------------------------------------
*/
/* The plat_reset_handler can clobber x0 - x18, x30 */
/* 调用平台级 reset handler */
bl plat_reset_handler

/* Get the matching cpu_ops pointer */
/* x0 = struct cpu_ops */
bl get_cpu_ops_ptr

/* Get the cpu_ops reset handler */
/* x2 = cpu_ops->reset_func */
ldr x2, [x0, #CPU_RESET_FUNC]

/* The cpu_ops reset handler can clobber x0 - x19, x30 */
blr x2
#endif
.endm
#endif /* ASM_MACROS_S */

3.2.4 公共初始化

    /*
* Helper macro to initialise EL3 registers we care about.
*/
.macro el3_arch_init_common
/* ---------------------------------------------------------------------
* SCTLR_EL3 has already been initialised - read current value before
* modifying.
*
* SCTLR_EL3.I: Enable the instruction cache.
*
* SCTLR_EL3.SA: Enable Stack Alignment check. A SP alignment fault
* exception is generated if a load or store instruction executed at
* EL3 uses the SP as the base address and the SP is not aligned to a
* 16-byte boundary.
*
* SCTLR_EL3.A: Enable Alignment fault checking. All instructions that
* load or store one or more registers have an alignment check that the
* address being accessed is aligned to the size of the data element(s)
* being accessed.
*
* SCTLR_EL3.BT: PAuth instructions are compatible with bti jc
* ---------------------------------------------------------------------
*/
mov_imm x1, (SCTLR_I_BIT | SCTLR_A_BIT | SCTLR_SA_BIT)
mrs x0, sctlr_el3
#if ENABLE_BTI
bic x0, x0, #SCTLR_BT_BIT
#endif
orr x0, x0, x1
msr sctlr_el3, x0
isb

#if ENABLE_FEAT_SCTLR2
#if ENABLE_FEAT_SCTLR2 > 1
is_feat_sctlr2_present_asm x1
beq feat_sctlr2_not_supported\@
#endif
mov x1, #SCTLR2_RESET_VAL
msr SCTLR2_EL3, x1
feat_sctlr2_not_supported\@:
#endif

#ifdef IMAGE_BL31
/* ---------------------------------------------------------------------
* Initialise the per-cpu framework to utilize tpidr_el3.
*
* This is done early to enable crash reporting to have access to crash
* stack. Since crash reporting depends on cpu_data to report the
* unhandled exception, not doing so can lead to recursive exceptions
* due to a NULL TPIDR_EL3.
* ---------------------------------------------------------------------
*/
per_cpu_init
#endif /* IMAGE_BL31 */

/* ---------------------------------------------------------------------
* Initialise SCR_EL3, setting all fields rather than relying on hw.
* All fields are architecturally UNKNOWN on reset. The following fields
* do not change during the TF lifetime. The remaining fields are set to
* zero here but are updated ahead of transitioning to a lower EL in the
* function cm_init_context_common().
*
* SCR_EL3.EEL2: Set to one if S-EL2 is present and enabled.
*
* NOTE: Modifying EEL2 bit along with EA bit ensures that we mitigate
* against ERRATA_V2_3099206.
* ---------------------------------------------------------------------
*/
mov_imm x0, SCR_RESET_VAL
#if IMAGE_BL31 && defined(SPD_spmd) && SPMD_SPM_AT_SEL2
mrs x1, id_aa64pfr0_el1
and x1, x1, #(ID_AA64PFR0_SEL2_MASK << ID_AA64PFR0_SEL2_SHIFT)
cbz x1, 1f
orr x0, x0, #SCR_EEL2_BIT
#endif
1:
msr scr_el3, x0

/* ---------------------------------------------------------------------
* Initialise MDCR_EL3, setting all fields rather than relying on hw.
* Some fields are architecturally UNKNOWN on reset.
*/
mov_imm x0, MDCR_EL3_RESET_VAL
msr mdcr_el3, x0

/* ---------------------------------------------------------------------
* Initialise CPTR_EL3, setting all fields rather than relying on hw.
* All fields are architecturally UNKNOWN on reset.
* ---------------------------------------------------------------------
*/
mov_imm x0, CPTR_EL3_RESET_VAL
msr cptr_el3, x0

.endm

3.2.5 恢复EL3执行环境

  • EL3 会在 Secure / Non-secure / Realm 等世界之间来回切换
  • lower EL 可能修改:
    • 中断屏蔽
    • debug / PMU 状态
    • CPTR / SCR 等寄存器的某些位

👉 如果不“每次回来都重置”,EL3 的行为就会被下层世界污染

/*-----------------------------------------------------------------------------
* Helper macro to configure EL3 registers we care about, while executing
* at EL3/Root world. Root world has its own execution environment and
* needs to have its settings configured to be independent of other worlds.
* -----------------------------------------------------------------------------
*/
.macro setup_el3_execution_context

/* ---------------------------------------------------------------------
* The following registers need to be part of separate root context
* as their values are of importance during EL3 execution.
* Hence these registers are overwritten to their intital values,
* irrespective of whichever world they return from to ensure EL3 has a
* consistent execution context throughout the lifetime of TF-A.
*
* DAIF.A: Enable External Aborts and SError Interrupts at EL3.
*
* MDCR_EL3.SDD: Set to one to disable AArch64 Secure self-hosted debug.
* Debug exceptions, other than Breakpoint Instruction exceptions, are
* disabled from all ELs in Secure state.
*
* SCR_EL3.EA: Set to one to enable SError interrupts at EL3.
*
* SCR_EL3.SIF: Set to one to disable instruction fetches from
* Non-secure memory.
*
* PMCR_EL0.DP: Set to one so that the cycle counter,
* PMCCNTR_EL0 does not count when event counting is prohibited.
* Necessary on PMUv3 <= p7 where MDCR_EL3.{SCCD,MCCD} are not
* available.
*
* CPTR_EL3.EZ: Set to one so that accesses to ZCR_EL3 do not trap
* CPTR_EL3.ESM: Set to one so that SME related registers don't trap
*
* PSTATE.DIT: Set to one to enable the Data Independent Timing (DIT)
* functionality, if implemented in EL3.
* ---------------------------------------------------------------------
*/
msr daifclr, #DAIF_ABT_BIT

mrs x15, mdcr_el3
orr x15, x15, #MDCR_SDD_BIT
msr mdcr_el3, x15

mrs x15, scr_el3
orr x15, x15, #SCR_EA_BIT
orr x15, x15, #SCR_SIF_BIT
bic x15, x15, #SCR_TRNDR_BIT
msr scr_el3, x15

mrs x15, pmcr_el0
orr x15, x15, #PMCR_EL0_DP_BIT
msr pmcr_el0, x15

mrs x15, cptr_el3
orr x15, x15, #CPTR_EZ_BIT
orr x15, x15, #ESM_BIT
msr cptr_el3, x15

#if ENABLE_FEAT_DIT
#if ENABLE_FEAT_DIT > 1
mrs x15, id_aa64pfr0_el1
ubfx x15, x15, #ID_AA64PFR0_DIT_SHIFT, #ID_AA64PFR0_DIT_LENGTH
cbz x15, 1f
#endif
mov x15, #DIT_BIT
msr DIT, x15
1:
#endif

isb
.endm

#endif /* EL3_COMMON_MACROS_S */

3.3 bl1_main

/*******************************************************************************
* Setup function for BL1.
* Also perform late architectural and platform specific initialization.
* It also queries the platform to load and run next BL image. Only called
* by the primary cpu after a cold boot.
******************************************************************************/
void __no_pauth bl1_main(void)
{
unsigned int image_id;

/* Enable early console if EARLY_CONSOLE flag is enabled */
plat_setup_early_console();

/* 早期和平台相关的初始化 */
bl1_early_platform_setup();
/* 晚期平台架构相关设置 */
bl1_plat_arch_setup();

/* 初始化 EL3 级别的架构扩展(如 SVE、RAS、MTE 等)*/
cm_manage_extensions_el3(plat_my_core_pos());

/*如果 BL2 不跑在 EL3(常见情况),初始化 per-world(Secure/Non-secure)上下文*/
#if !BL2_RUNS_AT_EL3
/* Init per-world context registers. */
cm_manage_extensions_per_world();
#endif

/* 性能/时间戳记录 */
#if ENABLE_RUNTIME_INSTRUMENTATION
PMF_CAPTURE_TIMESTAMP(bl_svc, BL1_ENTRY, PMF_CACHE_MAINT);
#endif

/* 启动信息输出 */
NOTICE(FIRMWARE_WELCOME_STR);
NOTICE("BL1: %s\n", build_version_string);
NOTICE("BL1: %s\n", build_message);

INFO("BL1: RAM %p - %p\n", (void *)BL1_RAM_BASE, (void *)BL1_RAM_LIMIT);

print_errata_status();

#if ENABLE_ASSERTIONS
u_register_t val;
/*
* Ensure that MMU/Caches and coherency are turned on
*/
#ifdef __aarch64__
val = read_sctlr_el3();
#else
val = read_sctlr();
#endif
assert((val & SCTLR_M_BIT) != 0); /* MMU 打开 */
assert((val & SCTLR_C_BIT) != 0); /* D-Cache 打开 */
assert((val & SCTLR_I_BIT) != 0); /* I-Cache 打开 */
/*
* Check that Cache Writeback Granule (CWG) in CTR_EL0 matches the
* provided platform value
*/
val = (read_ctr_el0() >> CTR_CWG_SHIFT) & CTR_CWG_MASK;
/*
* If CWG is zero, then no CWG information is available but we can
* at least check the platform value is less than the architectural
* maximum.
*/
if (val != 0)
assert(CACHE_WRITEBACK_GRANULE == SIZE_FROM_LOG2_WORDS(val));
else
assert(CACHE_WRITEBACK_GRANULE <= MAX_CACHE_LINE_SIZE);
#endif /* ENABLE_ASSERTIONS */

/* Perform remaining generic architectural setup from EL3 */
bl1_arch_setup();
/* 加密模块 */
crypto_mod_init();

/* 认证模块 */
auth_mod_init();

/* Initialize the measured boot */
bl1_plat_mboot_init();

/* Perform platform setup in BL1. */
bl1_platform_setup();

/* Get the image id of next image to load and run. */
image_id = bl1_plat_get_next_image_id();

/*
* We currently interpret any image id other than
* BL2_IMAGE_ID as the start of firmware update.
*/
if (image_id == BL2_IMAGE_ID)
bl1_load_bl2();
else
NOTICE("BL1-FWU: *******FWU Process Started*******\n");

/* Teardown the measured boot driver */
bl1_plat_mboot_finish();

crypto_mod_finish();

bl1_prepare_next_image(image_id);

#if ENABLE_RUNTIME_INSTRUMENTATION
PMF_CAPTURE_TIMESTAMP(bl_svc, BL1_EXIT, PMF_CACHE_MAINT);
#endif

console_flush();

/* Disable pointer authentication before jumping to next boot image. */
if (is_feat_pauth_supported()) {
pauth_disable_el3();
}
}

3.3.1 加载验证BL2镜像

主函数在加载BL2镜像时会根据板级配置拿到BL2镜像的大小、地址等信息,然后将其从存储介质(如 Flash 或 FIP 容器)读取 BL2 镜像到可信 SRAM,同时还会执行执行加密校验(哈希、签名验证),确保镜像完整性和来源可信。

    /* Get the image id of next image to load and run. */
image_id = bl1_plat_get_next_image_id();

/*
* We currently interpret any image id other than
* BL2_IMAGE_ID as the start of firmware update.
*/
if (image_id == BL2_IMAGE_ID)
bl1_load_bl2();
else
NOTICE("BL1-FWU: *******FWU Process Started*******\n");



/*******************************************************************************
* This function locates and loads the BL2 raw binary image in the trusted SRAM.
* Called by the primary cpu after a cold boot.
* TODO: Add support for alternative image load mechanism e.g using virtio/elf
* loader etc.
******************************************************************************/
static void bl1_load_bl2(void)
{
image_desc_t *desc;
image_info_t *info;
int err;

/* Get the image descriptor */
desc = bl1_plat_get_image_desc(BL2_IMAGE_ID);
assert(desc != NULL);

/* Get the image info */
info = &desc->image_info;
INFO("BL1: Loading BL2\n");

err = bl1_plat_handle_pre_image_load(BL2_IMAGE_ID);
if (err != 0) {
ERROR("Failure in pre image load handling of BL2 (%d)\n", err);
plat_error_handler(err);
}

err = load_auth_image(BL2_IMAGE_ID, info);
if (err != 0) {
ERROR("Failed to load BL2 firmware.\n");
plat_error_handler(err);
}

/* Allow platform to handle image information. */
err = bl1_plat_handle_post_image_load(BL2_IMAGE_ID);
if (err != 0) {
ERROR("Failure in post image load handling of BL2 (%d)\n", err);
plat_error_handler(err);
}

NOTICE("BL1: Booting BL2\n");
}

3.3.2 跳转前的准备

/***************************************************************************
* This structure provides version information and the size of the
* structure, attributes for the structure it represents
***************************************************************************/
typedef struct param_header {
uint8_t type; /* type of the structure */
uint8_t version; /* version of this structure */
uint16_t size; /* size of this structure in bytes */
uint32_t attr; /* attributes: unused bits SBZ */
} param_header_t;


typedef struct entry_point_info {
param_header_t h;
uintptr_t pc;
uint32_t spsr;
#ifdef __aarch64__
aapcs64_params_t args;
#else
uintptr_t lr_svc;
aapcs32_params_t args;
#endif
} entry_point_info_t;

对应的SPSR_EL3寄存器如下:

img

SPSR_EL3寄存器是ARMv8-A (AArch64 执行状态) 中的 PSTATE(Processor State)的描述,主要关注最后几位:

Bits **[4:0]M[4:0]**(Mode bits,执行模式)

  • nRW(bit 4):0 = AArch64 执行状态,1 = AArch32 执行状态。
  • **M[3:0]**(bit [3:0]):异常级别 + SP 选择
    • 常见编码:
      • 0b0000:EL0t(EL0,使用 SP_EL0)
      • 0b0100:EL1t(EL1,使用 SP_EL0)
      • 0b0101:EL1h(EL1,使用 SP_EL1)
      • 0b1000:EL2t / EL2h 等
      • 0b1100:EL3t / EL3h
  • M 字段决定当前异常级别(EL)和使用的栈指针(SP)。

异常进入 EL3(如 SMC / IRQ / reset)

PSTATE  ──保存──▶  SPSR_EL3 ``PC      ──保存──▶  ELR_EL3

异常返回(ERET)

PSTATE  ◀──恢复──  SPSR_EL3 ``PC      ◀──恢复──  ELR_EL3
/*******************************************************************************
* This function prepares the context for Secure/Normal world images.
* Normal world images are transitioned to EL2(if supported) else EL1.
******************************************************************************/
void bl1_prepare_next_image(unsigned int image_id)
{
unsigned int security_state, mode = MODE_EL1;
image_desc_t *desc;
entry_point_info_t *next_bl_ep;

#if CTX_INCLUDE_AARCH32_REGS
/*
* Ensure that the build flag to save AArch32 system registers in CPU
* context is not set for AArch64-only platforms.
*/
if (el_implemented(1) == EL_IMPL_A64ONLY) {
ERROR("EL1 supports AArch64-only. Please set build flag "
"CTX_INCLUDE_AARCH32_REGS = 0\n");
panic();
}
#endif

/* Get the image descriptor. */
desc = bl1_plat_get_image_desc(image_id);
assert(desc != NULL);

/* Get the entry point info. */
next_bl_ep = &desc->ep_info;

/* Get the image security state. */
security_state = GET_SECURITY_STATE(next_bl_ep->h.attr);

/* 决定 BL2 运行在哪个 EL, BL2运行在secure EL1 */
if ((security_state != SECURE) && (el_implemented(2) != EL_IMPL_NONE)) {
mode = MODE_EL2;
}

/* 构造 SPSR */
next_bl_ep->spsr = (uint32_t)SPSR_64((uint64_t) mode,
(uint64_t)MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS);

/* Allow platform to make change */
bl1_plat_set_ep_info(image_id, next_bl_ep);

/* Prepare the context for the next BL image. */
cm_init_my_context(next_bl_ep);
cm_prepare_el3_exit(security_state);

/* Indicate that image is in execution state. */
desc->state = IMAGE_STATE_EXECUTED;

print_entry_point_info(next_bl_ep);
}

3.4 el3_exit

/* ----------------lib\el3_runtime\aarch64\context.S--------------------------------------------------
* This routine assumes that the SP_EL3 is pointing to a valid
* context structure from where the gp regs and other special
* registers can be retrieved.
* ------------------------------------------------------------------
*/
func el3_exit
#if ENABLE_ASSERTIONS
/* el3_exit assumes SP_EL0 on entry */
mrs x17, spsel
cmp x17, #MODE_SP_EL0
ASM_ASSERT(eq)
#endif /* ENABLE_ASSERTIONS */

/* ----------------------------------------------------------
* Save the current SP_EL0 i.e. the EL3 runtime stack which
* will be used for handling the next SMC.
* Then switch to SP_EL3.
* ----------------------------------------------------------
*/
mov x17, sp
msr spsel, #MODE_SP_ELX
str x17, [sp, #CTX_EL3STATE_OFFSET + CTX_RUNTIME_SP]

/* ----------------------------------------------------------
* Restore CPTR_EL3.
* ---------------------------------------------------------- */

/* The address of the per_world context is stored in x9 */
get_per_world_context x9

ldp x19, x20, [x9, #CTX_CPTR_EL3]
msr cptr_el3, x19

#if IMAGE_BL31
restore_mpam3_el3

#endif /* IMAGE_BL31 */

#if IMAGE_BL31 && DYNAMIC_WORKAROUND_CVE_2018_3639
/* ----------------------------------------------------------
* Restore mitigation state as it was on entry to EL3
* ----------------------------------------------------------
*/
ldr x17, [sp, #CTX_CVE_2018_3639_OFFSET + CTX_CVE_2018_3639_DISABLE]
cbz x17, 1f
blr x17
1:
#endif /* IMAGE_BL31 && DYNAMIC_WORKAROUND_CVE_2018_3639 */

#if IMAGE_BL31
synchronize_errors
#endif /* IMAGE_BL31 */

/* --------------------------------------------------------------
* Restore MDCR_EL3, SPSR_EL3, ELR_EL3 and SCR_EL3 prior to ERET
* --------------------------------------------------------------
*/
ldp x16, x17, [sp, #CTX_EL3STATE_OFFSET + CTX_SPSR_EL3]
ldr x18, [sp, #CTX_EL3STATE_OFFSET + CTX_SCR_EL3]
ldr x19, [sp, #CTX_EL3STATE_OFFSET + CTX_MDCR_EL3]
msr spsr_el3, x16
msr elr_el3, x17
msr scr_el3, x18
msr mdcr_el3, x19

restore_ptw_el1_sys_regs

/* ----------------------------------------------------------
* Restore general purpose (including x30), PMCR_EL0 and
* ARMv8.3-PAuth registers.
* Exit EL3 via ERET to a lower exception level.
* ----------------------------------------------------------
*/
bl restore_gp_pmcr_pauth_regs
ldr x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]

#ifdef IMAGE_BL31
/* Clear the EL3 flag as we are exiting el3 */
str xzr, [sp, #CTX_EL3STATE_OFFSET + CTX_NESTED_EA_FLAG]
#endif /* IMAGE_BL31 */

exception_return

endfunc el3_exit

所以整体的BL1流程大概如下:

img

虽然ARM提供了一个BL1,但是基本上各自的厂商都会实现自己的BL1,并且固化在芯片的ROM中。