当前位置: 首页 > article >正文

SOC-ATF 安全启动BL1流程分析(1)

一、ATF 源码下载链接

1. ARM Trusted Firmware (ATF) 官方 GitHub 仓库

  • GitHub 地址: https://github.com/ARM-software/arm-trusted-firmware

  • 这是 ATF 的官方源码仓库,包含最新的代码、文档和示例。

下载方式:
  • 使用 Git 克隆仓库:

    git clone https://github.com/ARM-software/arm-trusted-firmware.git
  • 或者直接下载 ZIP 文件:在 GitHub 页面上点击 "Code" 按钮,然后选择 "Download ZIP"。


2. ATF 官方文档

  • 文档地址: Trusted Firmware-A Documentation — Trusted Firmware-A 2.12.0 documentation

  • 这里提供了详细的 ATF 使用指南、开发文档和 API 参考。


3. ATF 支持的平台

ATF 支持多种 ARM 平台,包括:

  • ARMv8-A 架构的处理器(如 Cortex-A53, Cortex-A57, Cortex-A72 等)。

  • 特定的 SoC 平台(如 NXP i.MX8, TI AM65x, Raspberry Pi 4 等)。

在 ATF 源码中,plat/ 目录下包含了针对不同平台的实现。


4. ATF 的主要功能

  • 安全启动:实现从 BL1(Boot Loader Stage 1)到 BL31(Runtime Firmware)的启动流程。

  • 可信执行环境(TEE):与 OP-TEE 等 TEE 实现集成。

  • 电源管理:支持 PSCI(Power State Coordination Interface)。

  • 异常处理:实现 ARMv8-A 的异常级别(EL3, EL2, EL1)管理。


5. ATF 的编译和使用

ATF 的编译通常需要交叉编译工具链(如 aarch64-none-elf-gcc)。以下是一个简单的编译示例:

安装依赖:
sudo apt-get install build-essential gcc-aarch64-linux-gnu
编译 ATF:
make CROSS_COMPILE=aarch64-none-elf- PLAT=<platform> all
  • 将 <platform> 替换为具体的平台名称(如 fvpqemurpi4 等)。

生成镜像:

编译完成后,生成的镜像文件(如 bl1.binfip.bin)会位于 build/<platform>/release/ 目录下。

二、BL1 启动流程

BL1是系统启动的第一阶段,其主要目的是初始化系统环境和启动第二阶段镜像BL2。借用网友的图,总体流程:仅供参考

展示一下,实际代码中的 el3_entrypoint_common 函数:

 其入口函数为bl1_entrypoint,它是由bl1/ bl1.ld.S通过ENTRY标号定义的:

OUTPUT_FORMAT(PLATFORM_LINKER_FORMAT)
OUTPUT_ARCH(PLATFORM_LINKER_ARCH)
ENTRY(bl1_entrypoint)

 它先初始化EL3环境,执行平台相关的初始化流程,然后加载下一阶段镜像、为其准备合适的参数,最后跳转到下一阶段镜像入口处运行。由于bl1的流程比较简单,接下来我们直接进入各个环节的具体实现吧。

(1)el3_entrypoint_common
  该函数是所有在EL3下执行镜像共享的,如BL1和BL31都会通过该函数初始化系统状态。该函数主要初始化系统的初始状态,执行一些必要的fixup操作,以及初始化c运行时环境和设置运行时的栈指针,为后续代码跳转到c语言执行准备条件

(2)bl_setup
  该函数主要执行一些平台相关的操作,如对于qemu平台会执行串口初始化、内存布局配置、MMU设置和data cache使能操作

(3)bl_main
  该函数主要用于bl2镜像加载以及跳转前的准备流程,如获取镜像参数、加载镜像内容、安全启动验签、bl2镜像跳转准备以及world switch上下文初始化等

(4)el3_exit
  该流程执行实际的上下文切换流程,包括保存当前EL3上下文以及跳转到bl2入口地址执行等。接下来我们将对以上流程进行更加详细的分析
  除了由硬件提供默认值的寄存器外,其它寄存器的值都处于不确定状态,因此在启动流程的初始阶段必须要先初始化这些寄存器,以将系统带到一个确定的运行状态。接下来需要设置cpu的异常处理程序和c运行时环境,为代码跳转到c语言做准备。最后,则需要加载BL2镜像,准备下一阶段启动所需的参数和跳转设置,并最终跳转到BL2的入口函数中执行。

 三、el3_entrypoint_common流程分析

该宏由所有需要在EL3下执行的镜像共享,如BL1和BL31都会入口处调用该函数,只是传入的参数有所区别。其主要完成的功能如下:
(1)初始化sctlr_el3寄存器,以初始化系统控制参数
(2)判断当前启动方式是冷启动还是热启动,并执行相应的处理
(3)pie相关的处理
(4)设置异常向量表
(5)特定cpu相关的reset处理
(6)架构相关el3的初始化
(7)冷启动时secondary cpu的处理
(8)c运行环境初始化
(9)初始化运行栈

3.1 sctlr_el3初始化

其代码流程如下:

.if \_init_sctlrmov_imm	x0, (SCTLR_RESET_VAL & ~(SCTLR_EE_BIT | SCTLR_WXN_BIT \| SCTLR_SA_BIT | SCTLR_A_BIT | SCTLR_DSSBS_BIT))msr	sctlr_el3, x0isb.endif 

 (1)sctlr_el3是EL3异常等级的控制寄存器,它控制了一些系统的重要行为,因此必须要在起始阶段就将其初始化到确定的状态
(2)这里主要设置了系统大小端(SCTLR_EE_BIT)、禁用了对齐错误(SCTLR_A_BIT)和栈对齐错误(SCTLR_SA_BIT)检查,以及禁止可写内存的执行权限(SCTLR_WXN_BIT)等
  而其它的值都沿用SCTLR_RESET_VAL的定义;

3.2 冷热启动处理

其代码如下:

.if \_warm_boot_mailboxbl	plat_get_my_entrypointcbz	x0, do_cold_bootbr	x0do_cold_boot:.endif

         冷启动和热启动的最大区别就是冷启动需要执行完整的系统初始化流程,而热启动因为在启动前保存了相关状态,因此可以跳过这些阶段,从而加快启动速度。因此,这段代码就很好理解了,它先通过plat_get_my_entrypoint从特定平台获取热启动地址,若地址获取成功,则直接跳转到该地址执行热启动流程。若地址获取失败,该函数会返回0,此时表明本次启动是冷启动,因此急需执行冷启动流程;

3.3 pie处理

        我们知道代码执行过程中可能需要跳转到某个位置,或者操作某个地址的数据,而在二进制代码中这些位置都需要通过地址来表示。因此,对于普通程序我们需要将其加载到与链接地址相同的位置执行,否则这些寻址操作就会失败。pie(地址无关可执行文件)就是为了解决该问题的,它的基本思路如下:
(1)程序中的函数调用和数据读写,若其可以转换为相对寻址的,则将其用相对寻址方式替换绝对地址。如armv8的adr指令,通过pc + offset的方式寻址,即以pc值为基地址,以offset为偏移量,从而计算得到新的地址。当然,这种寻址方式有一定的限制,如跳转范围有限等

(2)若该地址不能转换为相对寻址,则将其放到一个独立的段global descriptor table(GDT)中,并在镜像启动时通过实际加载地址调整这些地址值

因此,pie的实现需要编译和加载的共同配合完成,在构建时添加如下编译选项:
(1)编译时添加选项-fpie
(2)链接时添加选项-pie

在加载时需要对GDT表中的内容进行调整,这部分代码即是用于这一目的,其代码如下:

pie_fixup:ldr	x0, =pie_fixupand	x0, x0, #~(PAGE_SIZE_MASK)mov_imm	x1, \_pie_fixup_sizeadd	x1, x1, x0bl	fixup_gdt_reloc

具体的重定位流程位于fixup_gdt_reloc函数中,各位可以自行分析: 

路径:lib/aarch64/misc_helpers.S   +499

3.4 设置异常向量表

 这部分代码比较简单,就是将bl1的异常向量表设置到el3的向量表基地址寄存器中,其代码如下:

adr	x0, \_exception_vectorsmsr	vbar_el3, x0

 bl1异常向量表的定义位于bl1/aarch64/bl1_exceptions.S,从该异常向量表的定义我们可看到bl1只支持SMC异常的处理,其它的异常都是不合法的:func smc_handler64

3.5 reset_handler

该函数用于执行特定cpu相关的reset处理函数,这些处理函数在定义时会被放到一个特殊的段中,在执行reset_handler函数时就从该段中查找操作函数的函数指针,并执行相应的回调函数。以cortex-a53 为例,其cpu ops的定义流程如下:

lib/cpus/aarch64/cortex_a53.S:
declare_cpu_ops cortex_a53, CORTEX_A53_MIDR, \cortex_a53_reset_func, \cortex_a53_core_pwr_dwn, \cortex_a53_cluster_pwr_dwninclude/lib/cpus/aarch64/cpu_macros.S:
.macro declare_cpu_ops _name:req, _midr:req, _resetfunc:req, \_power_down_ops:varargdeclare_cpu_ops_base \_name, \_midr, \_resetfunc, 0, 0, 0, \\_power_down_ops
.endminclude/lib/cpus/aarch64/cpu_macros.S:
.macro declare_cpu_ops_base _name:req, _midr:req, _resetfunc:req, \_extra1:req, _extra2:req, _e_handler:req, _power_down_ops:vararg.section cpu_ops, "a".align 3.type cpu_ops_\_name, %object.quad \_midr
#if defined(IMAGE_AT_EL3).quad \_resetfunc
#endif.quad \_extra1.quad \_extra2.quad \_e_handler
…
.endm

其中cpu_ops段的地址定义在链接脚本头文件include/common/bl_common.ld.h中,即其位于__CPU_OPS_START__到__CPU_OPS_END__之间。

#define CPU_OPS                                         \. = ALIGN(STRUCT_ALIGN);                       \__CPU_OPS_START__ = .;                          \KEEP(*(cpu_ops))                                  \__CPU_OPS_END__ = .;

reset_handler的流程比较简单,就是查找__CPU_OPS_START__到__CPU_OPS_END__之间的cpu_ops结构体,并调用其reset_func回调函数,具体流程不再赘述。对于cortex-a53 平台,其reset函数定义如下,该流程主要是执行一些cpu相关的errata操作,以及使能SMP位(lib/cpus/aarch64/cortex_a53.S)。

func cortex_a53_reset_funcmov     x19, x30bl      cpu_get_rev_varmov     x18, x0
#if ERRATA_A53_826319mov     x0, x18bl      errata_a53_826319_wa
#endif
#if ERRATA_A53_836870mov     x0, x18bl      a53_disable_non_temporal_hint
#endif
#if ERRATA_A53_855873mov     x0, x18bl      errata_a53_855873_wa
#endifmrs     x0, CORTEX_A53_ECTLR_EL1orr     x0, x0, #CORTEX_A53_ECTLR_SMP_BITmsr     CORTEX_A53_ECTLR_EL1, x0isbret     x19
endfunc cortex_a53_reset_func

3.6 架构相关el3的初始化

该流程主要执行一些系统寄存器相关的配置,以设置系统的状态。其aarch64架构流程如下(include/arch/aarch64/el3_common_macro.S):

	.macro el3_arch_init_commonmov	x1, #(SCTLR_I_BIT | SCTLR_A_BIT | SCTLR_SA_BIT)                           (1)mrs	x0, sctlr_el3orr	x0, x0, x1msr	sctlr_el3, x0isb#ifdef IMAGE_BL31bl	init_cpu_data_ptr
#endif /* IMAGE_BL31 */mov_imm	x0, ((SCR_RESET_VAL | SCR_EA_BIT | SCR_SIF_BIT) \& ~(SCR_TWE_BIT | SCR_TWI_BIT | SCR_SMD_BIT))                      (2)
#if CTX_INCLUDE_PAUTH_REGSorr	x0, x0, #(SCR_API_BIT | SCR_APK_BIT)                                       (3)
#endifmsr	scr_el3, x0mov_imm	x0, ((MDCR_EL3_RESET_VAL | MDCR_SDD_BIT | \MDCR_SPD32(MDCR_SPD32_DISABLE) | MDCR_SCCD_BIT | \MDCR_MCCD_BIT) & ~(MDCR_SPME_BIT | MDCR_TDOSA_BIT | \MDCR_TDA_BIT | MDCR_TPM_BIT))                                         (4)msr	mdcr_el3, x0mov_imm	x0, ((PMCR_EL0_RESET_VAL | PMCR_EL0_LP_BIT | \PMCR_EL0_LC_BIT | PMCR_EL0_DP_BIT) & \~(PMCR_EL0_X_BIT | PMCR_EL0_D_BIT))                                     (5)msr	pmcr_el0, x0msr	daifclr, #DAIF_ABT_BIT                                              (6)mov_imm x0, (CPTR_EL3_RESET_VAL & ~(TCPAC_BIT | TTA_BIT | TFP_BIT))                 (7)         msr	cptr_el3, x0mrs	x0, id_aa64pfr0_el1ubfx	x0, x0, #ID_AA64PFR0_DIT_SHIFT, #ID_AA64PFR0_DIT_LENGTH                     (8)cmp	x0, #ID_AA64PFR0_DIT_SUPPORTEDbne	1fmov	x0, #DIT_BITmsr	DIT, x0
1:.endm

(1)使能指令cache、对齐错误和栈对齐错误检查

(2)secure寄存器相关设置,主要用于设置某些操作是否路由到EL3执行,如设置SCR_EA_BIT会将所有异常等级下的external abort和serror异常路由到EL3处理,清除SCR_TWE_BIT则不会使得低于EL3等级的WFE指令不会路由到EL3处理。其它位的含义基本类似,具体定义可查看armv8 spec

(3)它用于使能指针签名特性PAC。由于armv8虚拟地址没有完全使用,如对于48位虚拟地址,其高16位是空闲的,完全可以用于存储一些其它信息。因此arm支持了指针签名技术,它通过密钥和签名算法对指针进行签名,并将截断后的签名保存到虚拟地址的高位,在使用该指针时则对高签名进行验证,以确保其没有被篡改。它主要是用来保护栈中数据的安全性,防御ROP/JOP攻击。

(4)mdcr_el3寄存器用于设置debug和performance monitor相关的功能

(5)用于设置performance monitor配置,如一些性能事件计数器的行为

(6)用于使能serror异常,此后bl1将能接收serror异常,并处理smc调用

(7)设置一些特定事件是否要陷入EL3

(8)设置DIT特性,若使能了DIT,则DIT相关的指令执行时间与数据不相关。由于侧信道攻击可以利用某些敏感指令(如加解密指令)执行时间、功耗等的不同,来推测出数据内容,因此猜测该功能是用于防止侧信道攻击的

3.7 secondary cpu的处理

由于启动代码不支持并发,因此在smp系统中只有一个cpu(primary cpu)执行启动流程,而其它cpu(secondary cpu)需要将自身设置为一个安全的状态,待primary cpu启动完成后再通过spintable或psci等方式来启动它们。其流程如下:

bl	plat_is_my_cpu_primary                                            (1)cbnz	w0, do_primary_cold_boot                (2)bl	plat_secondary_cold_boot_setup      (3)bl	el3_panicdo_primary_cold_boot:

(1)当前cpu是否为primary cpu

(2)若其为primary cpu,继续执行cold boot流程

(3)若其为secondary cpu,执行平台定义的secondary cpu启动设置函数

这个和uboot 启动CPU和kernel 启动CPU 机制一样,先让primary cpu启动,初始化一定后,设置secondary cpu 的启动函数指针。

3.8 内存初始化

该函数执行平台相关的内存初始化函数platform_mem_init.

3.9 c运行环境初始化

c语言运行需要依赖于bss段和栈,因此在跳转到c函数之前需要下设置它们。而且由于bl1的镜像一般被烧写在rom中,因此需要将其可写数据段从rom重定位到ram中。以下为其主要代码实现:

		adrp	x0, __RW_START__add	x0, x0, :lo12:__RW_START__                                    (1)adrp	x1, __RW_END__add	x1, x1, :lo12:__RW_END__sub	x1, x1, x0                                                    (2)bl	inv_dcache_range                                              (3)…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                                                        (4)…
#if defined(IMAGE_BL1) || (defined(IMAGE_BL2) && BL2_AT_EL3 && BL2_IN_XIP_MEM)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                                                        (5)
#endif

(1)计算数据段的起始地址,由于adrp指令加载的地址值会将低bit mask掉,使其4k对齐。因此需要加上其低12位的数据,以恢复其原始值

(2)计算该段地址的长度

(3)失效这段sram内存的dcache

(4)获取bss段的起止地址,并计算其长度,然后清零该段内存的数据

(5)获取bl1可读写数据段在rom中的地址,以及其将要被重定位的ram地址,计算数据长度,并执行重定位操作

3.10 运行栈设置

C语言的函数调用返回地址,上层栈指针地址,局部变量以及参数传递都可能需要用到栈。本函数通过设置运行时栈指针为跳转到c语言执行做最后的准备,其代码如下:

msr	spsel, #0                                           (1)bl	plat_set_my_stack                   (2)
#if STACK_PROTECTOR_ENABLED.if \_init_c_runtimebl	update_stack_protector_canary               (3).endif
#endif

(1)使用sp_el0作为栈指针寄存器

(2)设置运行时栈,该函数会获取一个定义好的栈指针,并将其设置到当前栈指针寄存器sp中

(3)在栈顶设置一个canary值,用于检测栈溢出

四、bl_setup流程分析

4.1 bl1_early_platform_setup函数

以qemu平台的实现为例,其代码如下:plat/qemu/common/qemu_bl1_setup.c

void bl1_early_platform_setup(void)
{qemu_console_init();                                   (1)bl1_tzram_layout.total_base = BL_RAM_BASE;             (2)bl1_tzram_layout.total_size = BL_RAM_SIZE;
}

(1)控制台初始化

(2)设置secure sram内存的地址范围

4.2 bl1_plat_arch_setup函数

以qemu平台为例,其代码如下:

void bl1_plat_arch_setup(void)
{QEMU_CONFIGURE_BL1_MMU(bl1_tzram_layout.total_base,bl1_tzram_layout.total_size,BL_CODE_BASE, BL1_CODE_END,BL1_RO_DATA_BASE, BL1_RO_DATA_END,BL_COHERENT_RAM_BASE, BL_COHERENT_RAM_END);
}

        该函数用于为所有bl1需要访问的地址建立MMU页表,并且使能dcache。bl1中物理地址和虚拟地址映射的地址值是相等的,之所以要开启MMU主要是为了开启dcache,以加快后面BL2镜像加载的速度

五、bl_main流程分析

5.1 bl1的架构设置

        bl1的aarch64架构设置函数如下:

void bl1_arch_setup(void)
{write_scr_el3(read_scr_el3() | SCR_RW_BIT);
}

 该函数的作用为将下一个异常等级的执行状态设置为EL3;

5.2 secure boot初始化

        secure boot用于校验镜像的合法性,它通常需要一个包含镜像签名信息的镜像头。签名信息可在打包时完成,一般包括计算镜像的hash值,然后使用非对称算法(如RSA或ECDSA)对该hash值执行签名操作,并将签名信息保存到镜像头中。在系统启动时,需要校验该签名是否合法,若不合法表明镜像被破坏或被替换了,因此系统需要停止启动流程。

auth_mod_init函数用于初始化签名验证所需的模块,其代码如下:

void auth_mod_init(void)
{assert(cot_desc_ptr != NULL);crypto_mod_init();                       (1)img_parser_init();                        (2)
}

(1)初始化签名验证所需的密码库

(2)初始化获取镜像签名信息模块

5.3 bl1的平台初始化

        qemu平台的初始化实现接口如下:

void bl1_platform_setup(void)
{plat_qemu_io_setup();
}

        该函数用于初始化qemu可能使用的镜像加载驱动初始化。

5.4 获取下一阶段镜像id

 Bl1的下一阶段镜像通常为BL2,以下是通用的镜像id获取函数:

unsigned int bl1_plat_get_next_image_id(void)
{return BL2_IMAGE_ID;
}

5.5 bl2镜像加载

        镜像加载流程包含了镜像从storage中的加载以及镜像合法性验签两部分,由于secure boot计划在后面专门花一篇文章来介绍,因此这里只介绍镜像的加载流程。其代码主要流程如下:

desc = bl1_plat_get_image_desc(BL2_IMAGE_ID);                                           (1)
info = &desc->image_info;
err = bl1_plat_handle_pre_image_load(BL2_IMAGE_ID);                                     (2)
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);                                              (3)
if (err != 0) {ERROR("Failed to load BL2 firmware.\n");plat_error_handler(err);
}err = bl1_plat_handle_post_image_load(BL2_IMAGE_ID);                            (4)
if (err != 0) {ERROR("Failure in post image load handling of BL2 (%d)\n", err);plat_error_handler(err);
}

它主要包含以下几部分内容:

(1)获取待加载镜像描述信息
  在atf中,镜像描述信息主要包含镜像id、镜像加载器使用的信息image_info和镜像跳转时使用的信息ep_info,其结构如下:

bl1_plat_get_image_desc用于获取bl2镜像的信息。

(2)加载之前的处理
  它由平台函数bl1_plat_handle_pre_image_load处理,qemu平台未对其做任何处理

(3)从storage中加载镜像
  它会根据先前获取到的bl2镜像描述信息,从storage中将镜像数据加载到给定地址上。qemu支持fip和semihosting类型的加载方式

(4)加载之后的处理
  它主要用于设置bl1向bl2传递的参数,上面结构体中的args即用于该目的,它一共包括8个参数,在bl1跳转到bl2之前会分别被设置到x0 – x7寄存器中。bl1只需通过x1寄存器向bl2传送其可用的secure内存region即可。以下为其代码主体流程:

image_desc = bl1_plat_get_image_desc(BL2_IMAGE_ID);
ep_info = &image_desc->ep_info;                                           (a)
bl1_secram_layout = bl1_plat_sec_mem_layout();                            (b)
bl2_secram_layout = (meminfo_t *) bl1_secram_layout->total_base;
bl1_calc_bl2_mem_layout(bl1_secram_layout, bl2_secram_layout);            (c)
ep_info->args.arg1 = (uintptr_t)bl2_secram_layout; 

a 获取bl2的ep信息
b 获取bl1的secure内存region
c 将总的内存减去bl1已使用的sram内存,作为bl2的可用内存
d 将bl2的可用内存信息保存到参数传递信息中

5.6 下一阶段镜像启动准备流程

         在atf中定义了一个异常等级切换相关的cpu context结构体,该结构体包含了切换时所需的所有的信息,如gp寄存器的值,el1、el2系统寄存器以及el3状态的值等。由于armv8包含secure和non secure两种安全状态,因此在el3中为这两种状态分别保留了一份独立的上下文信息,我们在执行上下文切换准备工作时,实际上就是填充对应security状态的结构体内容。以下是该结构体的定义:

typedef struct cpu_context {gp_regs_t gpregs_ctx;el3_state_t el3state_ctx;el1_sysregs_t el1_sysregs_ctx;
#if CTX_INCLUDE_EL2_REGSel2_sysregs_t el2_sysregs_ctx;
#endif
#if CTX_INCLUDE_FPREGSfp_regs_t fpregs_ctx;
#endifcve_2018_3639_t cve_2018_3639_ctx;
#if CTX_INCLUDE_PAUTH_REGSpauth_t pauth_ctx;
#endif
} cpu_context_t;

        bl1_prepare_next_image的主要工作就是初始化primary cpu的cpu_context上下文,并填充该结构体的相关信息,其主要流程如下:

desc = bl1_plat_get_image_desc(image_id);next_bl_ep = &desc->ep_info;                                                              (1)security_state = GET_SECURITY_STATE(next_bl_ep->h.attr);                                  (2)if (cm_get_context(security_state) == NULL)cm_set_context(&bl1_cpu_context[security_state], security_state);                 (3)if ((security_state != SECURE) && (el_implemented(2) != EL_IMPL_NONE)) {                  (4)mode = MODE_EL2;}next_bl_ep->spsr = (uint32_t)SPSR_64((uint64_t) mode,(uint64_t)MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS);                                   (5)bl1_plat_set_ep_info(image_id, next_bl_ep);cm_init_my_context(next_bl_ep);                                                           (6)cm_prepare_el3_exit(security_state);                                                      (7)desc->state = IMAGE_STATE_EXECUTED;

(1)获取bl2的ep信息

(2)从bl2的ep信息中获取其security状态

(3)若context内存未分配,则为其分配内存

(4)默认的下一阶段镜像异常等级为其支持的最高等级,即若支持el2,则下一异常等级为EL2

(5)计算spsr的值,即异常等级为step 4计算的值,栈指针使用sp_elx,关闭所有DAIF异常

(6)该函数为待切换异常等级初始化上下文,如scr_el3,scr_el3,pc,spsr以及参数传递寄存器x0 – x7的值

(7)将context中参数设置到实际的寄存器中

5.7 console_flush函数

        在退出bl1之前将串口中的数据全部刷新掉。

六、el3_exit流程分析

路径:lib/el3_runtime/aarch64/context.S 

        该函数执行实际的异常等级切换流程,包括设置scr_el3,spsr_el3,elr_el3寄存器,以及执行eret指令跳转到elr_el3设定的bl2入口函数处执行。其定义如下:

func el3_exitmov	x17, sp                                                                    (1)msr	spsel, #MODE_SP_ELX                                                        (2)str	x17, [sp, #CTX_EL3STATE_OFFSET + CTX_RUNTIME_SP]                           (3)ldr	x18, [sp, #CTX_EL3STATE_OFFSET + CTX_SCR_EL3]ldp	x16, x17, [sp, #CTX_EL3STATE_OFFSET + CTX_SPSR_EL3]                         (4)msr	scr_el3, x18msr	spsr_el3, x16msr	elr_el3, x17                                                                (5)#if IMAGE_BL31ldp	x19, x20, [sp, #CTX_EL3STATE_OFFSET + CTX_CPTR_EL3]msr	cptr_el3, x19ands	x19, x19, #CPTR_EZ_BITbeq	sve_not_enabledisbmsr	S3_6_C1_C2_0, x20 /* zcr_el3 */
sve_not_enabled:
#endif#if IMAGE_BL31 && DYNAMIC_WORKAROUND_CVE_2018_3639ldr	x17, [sp, #CTX_CVE_2018_3639_OFFSET + CTX_CVE_2018_3639_DISABLE]cbz	x17, 1fblr	x17
1:
#endifrestore_ptw_el1_sys_regsbl	restore_gp_pmcr_pauth_regs                                                   (6)ldr	x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]#if IMAGE_BL31 && RAS_EXTENSIONesb
#elsedsb	sy
#endif
#ifdef IMAGE_BL31str	xzr, [sp, #CTX_EL3STATE_OFFSET + CTX_IS_IN_EL3]
#endifexception_return                                                                      (7)
endfunc el3_exit

(1)将sp_el0栈指针暂存到x17寄存器中

(2)将栈指针切换到sp_el3,其中sp_el3指向前面context的el3state_ctx指针,即它被用于保存el3的上下文

(3)将sp_el0的值保存的el3 context中

(4)从el3 context中加载scr_el3、spsr_el3和elr_el3寄存器的值

(5)设置scr_el3、spsr_el3和elr_el3寄存器

(6)恢复gp寄存器等寄存器的值

(7)执行eret指令,此后cpu将离开bl1跳转到bl2的入口处执行了

exception_return  的实现:路径include/arch/aarch64/asm_macros.S

相关文章:

SOC-ATF 安全启动BL1流程分析(1)

一、ATF 源码下载链接 1. ARM Trusted Firmware (ATF) 官方 GitHub 仓库 GitHub 地址: https://github.com/ARM-software/arm-trusted-firmware 这是 ATF 的官方源码仓库&#xff0c;包含最新的代码、文档和示例。 下载方式&#xff1a; 使用 Git 克隆仓库&#xff1a; git…...

QVariantList使用详解

QVariantList 1. 基本概念2. 使用场景3. 基本操作3.1 创建和初始化3.2 访问元素3.3 修改元素3.4 删除元素 4. 实际应用示例5. 总结其他QT文章推荐 QVariantList 是 Qt 框架中的一个类&#xff0c;用于存储和操作 QVariant 对象的列表。 QVariant 是 Qt 中用于封装各种类型的通…...

TDesign:Cascader 级联选择器(省市区三级联动)

Cascader 级联选择器API 参考官方示例代码 在自己的模板中使用&#xff1a;view import package:ducafe_ui_core/ducafe_ui_core.dart; import package:flutter/material.dart; import package:get/get.dart; import package:tdesign_flutter/tdesign_flutter.dart;import i…...

Ubuntu20.04下各类常用软件及库安装汇总

1.Miniconda的安装 Ubuntu 20.04版本快速安装 Miniconda&#xff08;宝宝级攻略&#xff09; 2.Nvidia显卡驱动/CUDA/cuDNN安装 Ubuntu 20.04安装nvidia显卡驱动/CUDA/cuDNN Ubuntu 20.04&#xff1a;CUDAcuDNN安装&卸载 Y9000P24款 win11ubuntu20.04 双系统教程 3.SGL…...

linux中安装部署Jenkins,成功构建springboot项目详细教程

参考别人配置Jenkins的git地址为https&#xff0c;无法连上github拉取项目&#xff0c;所以本章节介绍通过配置SSH地址来连github拉取项目 目录&#xff1a; 1、springboot项目 1.1 创建名为springcloudproject的springboot项目工程 1.2 已将工程上传到github中&#xff0c;g…...

2025系统架构师(一考就过):案例之四:架构复用、架构评估、特定架构(DSSA)、架构开发方法(ABSD)

二、软件架构复用 ◆软件产品线是指一组软件密集型系统&#xff0c;它们共享一个公共的、可管理的特性集&#xff0c;满足某个特定市场或任务的具体需要&#xff0c;是以规定的方式用公共的核心资产集成开发出来的。即围绕核心资产库进行管理复用、集成新的系统。 ◆软件架构…...

基于定制开发开源AI大模型S2B2C商城小程序的商品选品策略研究

摘要&#xff1a;随着电子商务的蓬勃发展和技术的不断进步&#xff0c;商品选品在电商领域中的重要性日益凸显。特别是在定制开发开源AI大模型S2B2C商城小程序的环境下&#xff0c;如何精准、高效地选择推广商品&#xff0c;成为商家面临的一大挑战。本文首先分析了商品选品的基…...

后端之JPA(EntityGraph+JsonView)

不同表之间的级联操作或者说关联查询是很多业务场景都会用到的。 对于这种需求最朴素的方法自然是手动写关联表&#xff0c;然后对被关联的表也是手动插入数据。但是手写容易最后写成一堆shit代码&#xff0c;而且修改起来也是非常麻烦的。 学会使用现成的工具还是非常有利的…...

linux--卡顿

1&#xff0c;swappiness swappiness是一个影响操作系统如何使用交换空间&#xff08;swap space&#xff09;的内核参数。它的值范围是从0到100&#xff0c;这个数值决定了系统将数据从物理内存&#xff08;RAM&#xff09;移动到交换分区或交换文件的倾向程度&#xff0c;交…...

【Python pro】函数

1、函数的定义及调用 1.1 为什么需要函数 提高代码复用性——封装将复杂问题分而治之——模块化利于代码的维护和管理 1.1.1 顺序式 n 5 res 1 for i in range(1, n1):res * i print(res) # 输出&#xff1a;1201.1.2 抽象成函数 def factorial(n):res 1for i in range(1…...

Docker 2025/2/24

用来快速构建、运行和管理应用的工具。帮助部署。 快速入门 代码略 解释 docker run :创建并运行一个容器&#xff0c;-d是让容器在后台运行 --name mysql :给容器起个名字&#xff0c;必须唯一 -p 3306:3306 :设置端口映射 -e KEYVALUE :是设置环境变量 mysql :指定运行的…...

4. 示例:创建带约束的随机地址生成器(范围0x1000-0xFFFF)

文章目录 前言代码示例&#xff1a;运行方法&#xff1a;查看结果&#xff1a;关键功能说明&#xff1a;扩展功能建议&#xff1a; 前言 以下是一个完整的SystemVerilog测试平台示例&#xff0c;包含约束随机地址生成、日志输出和波形生成功能&#xff1a; 代码示例&#xff1…...

爱普生SG-8101CE可编程晶振赋能智能手机的精准心脏

在智能手机高速迭代的今天&#xff0c;高性能、低功耗与小型化已成为核心诉求。智能手机作为人们生活中不可或缺的工具&#xff0c;需要在各种复杂场景下稳定运行。爱普生SG-8101CE可编程晶振凭借其卓越性能&#xff0c;成为智能手机中不可或缺的精密时钟源&#xff0c;为通信、…...

指针解剖学:穿透C/C++内存操作的核心密码与避坑指南

一、指针的本质与内存模型 指针是C/C的核心特性&#xff0c;本质是内存地址的变量化表示。每个变量在内存中占据连续的字节空间&#xff0c;地址是内存单元的唯一编号&#xff08;如0x0028FF40&#xff09;。指针变量存储的是目标数据的首地址&#xff0c;通过地址间接操作数据…...

Qt关于平滑滚动的使用QScroller及QScrollerProperties类说明

一、触控时代的滚动工具&#xff1a;QScroller类设计介绍 1.1 从机械滚轮到数字惯性 在触控设备普及前&#xff0c;滚动操作如同老式打字机的滚轴&#xff0c;只能通过鼠标滚轮或滚动条进行离散式控制。QScroller的出现如同给数字界面装上了"惯性飞轮"&#xff0c;…...

【音视频】编解码相关概念总结

NALU RTP PS流 三者总体关系 NALU在RTP中的应用&#xff1a;视频流的RTP传输通常将NALU作为基本的单元进行传输。每个RTP包携带一个或多个NALU&#xff0c;这些NALU包含了视频编码数据。RTP协议通过其头部信息&#xff08;如时间戳、序列号等&#xff09;帮助接收端重新排列和…...

Vue3 + Vite + TS,使用 配置项目别名属性:resolve

使用 resolve 配置全局项目路径别名 1.优化了开发中单页面引用其他模块的路径复杂性 2.妥妥解决了&#xff0c;组件复用当中提高开发效率 // 不使用配置 import { useStore } from ../../../stores // 使用配置 可根据开发者需求任意定义&#xff0c;较多 import { useStore…...

docker创建nginx

docker run -d -p 8080:80 --name my-nginx-container nginx docker&#xff1a;命令 run&#xff1a;命令 -d&#xff1a;在后台运行容器 -p&#xff1a;8080:80&#xff1a;将容器内部的80端口映射到宿主机的8080端口。 --name my-nginx-container&#xff1a;为容器指定一个…...

StableDiffusion打包 项目迁移 项目分发 1

文章目录 SD项目迁移前置知识webui-user.batwebui.batlaunch_utils.py 下一篇开始实践 SD项目迁移 显卡驱动更新&#xff1a;https://www.nvidia.cn/geforce/drivers/ 下载安装三个程序&#xff1a; python3.10.6: https://www.python.org/downloads/release/python-3106/gi…...

【数据结构进阶】哈希表

&#x1f31f;&#x1f31f;作者主页&#xff1a;ephemerals__ &#x1f31f;&#x1f31f;所属专栏&#xff1a;数据结构 目录 前言 一、哈希表的概念 二、哈希函数的实现方法 1. 直接定址法 2. 除留余数法 三、哈希冲突 1. 开放定址法&#xff08;闭散列&#xff0…...

【蓝桥杯嵌入式】各模块学习总结

系列文章目录 留空 文章目录 系列文章目录前言一、LED模块1.1 赛题要求1.2 模块原理图1.3 编写代码1.4 赛题实战 二、LCD模块2.1 赛题要求2.2 模块原理图2.3 编写代码2.4 赛题实战 三、按键模块3.1 赛题要求3.2 模块原理图3.3 编写代码3.4 赛题实战 四、串口模块4.1 赛题要求4…...

Rust学习总结之-枚举

枚举是一个很多语言都有的功能&#xff0c;不过不同语言中其功能各不相同但是要表达的意思是一致的&#xff0c;枚举就是对于一个事物可以穷举出所有可能得值。比如说人的性别就可以用枚举&#xff0c;男人和女人两种。下面我们来学习Rust中的枚举。 一&#xff1a;枚举定义 …...

Linux系统管理(十七)——配置英伟达驱动、Cuda、cudnn、Conda、Pytorch、Pycharm等Python深度学习环境

文章目录 前言安装驱动下载安装Cuda编辑环境变量安装Cudnn安装conda验证安装成功配置conda镜像退出conda环境创建python环境查看当前conda环境激活环境安装python包安装pytorch 安装pycharm安装jupyter notebook 前言 深度学习和大语言模型的部署不免会用到Linux系统&#xff…...

SLAM算法工程师的技术图谱和学习路径

SLAM(Simultaneous Localization and Mapping)算法工程师是负责开发和实现用于机器人、自动驾驶车辆等领域的SLAM算法的专业人士。下面是SLAM算法工程师需要掌握的基础理论知识: 机器人运动学和动力学:理解机器人在空间中的运动方式和控制方法,包括轮式、蜘蛛腿、飞行器等…...

【第三天】零基础学习量化基础代码分析-持续更新

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 记录量化基础代码总览引言基本概念量化投资 伪代码示例&#xff1a;量化投资模型框架总结 每日-往期回看 第一天零基础学量化基础知识点总览-持续更新 第二天零基础…...

深入了解 Python 中的 MRO(方法解析顺序)

文章目录 深入了解 Python 中的 MRO&#xff08;方法解析顺序&#xff09;什么是 MRO&#xff1f;如何计算 MRO&#xff1f;C3 算法的合并规则C3 算法的合并步骤示例&#xff1a;合并过程解析 MRO 解析失败的场景使用 mro() 方法查看 MRO示例 1&#xff1a;基本用法 菱形继承与…...

如何防止 Instagram 账号被盗用:安全设置与注意事项

如何防止 Instagram 账号被盗用&#xff1a;安全设置与注意事项 在这个数字化时代&#xff0c;社交媒体平台如 Instagram 已成为我们日常生活的一部分。然而&#xff0c;随着网络犯罪的增加&#xff0c;保护我们的在线账户安全变得尤为重要。以下是一些关键的安全设置和注意事…...

采样算法二:去噪扩散隐式模型(DDIM)采样算法详解教程

参考 https://arxiv.org/pdf/2010.02502 一、背景与动机 去噪扩散隐式模型&#xff08;DDIM&#xff09; 是对DDPM的改进&#xff0c;旨在加速采样过程同时保持生成质量。DDPM虽然生成效果优异&#xff0c;但其采样需迭代数百至数千次&#xff0c;效率较低。DDIM通过以下关键…...

各种类型网络安全竞赛有哪些 网络安全大赛的简称

本文是对入门学习的一些概念了解和一些常规场景记录 1.CTF&#xff08;capture the flag&#xff09;是夺旗赛的意思。 是网络安全技术人员之间进行攻防的比赛。 起源1996年DEFCON全球黑客大会&#xff0c;替代之前真实攻击的技术比拼。 (DEFCON极客大会诞生1993&#xff0c;…...

包子凑数——蓝桥杯真题Python

包子凑数 输入输出样例 示例 1 输入 2 4 5输出 6样例说明 凑不出的数目包括&#xff1a;1, 2, 3, 6, 7, 11。 示例 2 输入 2 4 6输出 INF样例说明 所有奇数都凑不出来&#xff0c;所以有无限多个 运行限制 最大运行时间&#xff1a;1s最大运行内存: 256M 最大公约数 最大公…...