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

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…...

游戏引擎学习第127天

仓库:https://gitee.com/mrxiao_com/2d_game_3 为本周设定阶段 我们目前的渲染器已经实现了令人惊讶的优化&#xff0c;经过过去两周的优化工作后&#xff0c;渲染器在1920x1080分辨率下稳定地运行在60帧每秒。这个结果是意料之外的&#xff0c;因为我们没有预计会达到这样的…...

Grafana使用日志7--开启Sigv4

背景 在Grafana中&#xff0c;有些data source是需要开启sigv4认证的&#xff0c;例如OpenSearch&#xff0c;这个配置项默认是关闭的&#xff0c;这里我们介绍一下怎么开启 步骤 传统方式 如果我们想在Grafana中开启sigv4认证&#xff0c;我们需要在grafana.ini中修改一个…...

UWB人员定位:精准、高效、安全的智能管理解决方案

在现代企业管理、工业生产、安全监测等领域&#xff0c;UWB&#xff08;超宽带&#xff09;人员定位系统正逐步成为高精度定位技术的首选。相较于传统的GPS、Wi-Fi、蓝牙等定位方式&#xff0c;UWB具备厘米级高精度、低延迟、高安全性、抗干扰强等突出优势&#xff0c;能够实现…...

二、QT和驱动模块实现智能家居----2、编译支持QT的系统

因为我们的Linux内核文件不支持QT系统&#xff08;当然如果你的支持&#xff0c;完全跳过这篇文章&#xff09;&#xff0c;所以我们要从网上下载很多软件包&#xff0c;这里直接用百问网的软件包&#xff0c;非常方便。 一&#xff1a;Ubuntu 配置 1 设置交叉编译工具链 以…...

Windows上使用go-ios实现iOS17自动化

前言 在Windows上运行iOS的自动化&#xff0c;tidevice对于iOS17以上并不支持&#xff0c;原因是iOS 17 引入新通信协议 ‌RemoteXPCQUIC‌&#xff0c;改变了 XCUITest 的启动方式。 一、go-ios的安装 1、安装命令&#xff1a;npm i go-ios 2、安装完成后输入命令which io…...

越南SD-WAN跨境组网专线助力制造业访问国内 OA、ERP系统难题

近年来&#xff0c;随着全球制造业格局的不断调整&#xff0c;越来越多的制造业企业选择将工厂建立在越南。越南凭借其相对低廉的劳动力成本、优惠的政策以及优越的地理位置&#xff0c;吸引了大量的外资制造业企业入驻。然而&#xff0c;这些在越南设厂的企业却面临着一个棘手…...

【广度优先搜索】图像渲染 岛屿数量

文章目录 733. 图像渲染解题思路&#xff1a;BFS200. 岛屿数量解题思路&#xff1a;广度优先遍历 733. 图像渲染 733. 图像渲染 ​ 有一幅以 m x n 的二维整数数组表示的图画 image &#xff0c;其中 image[i][j] 表示该图画的像素值大小。 ​ 你也被给予三个整数 sr , sc 和…...

Rust学习总结之-枚举

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

Linux下用route命令修改默认网关,不用重启网络

在Linux系统中&#xff0c;可以使用route命令来修改默认网关&#xff0c;而不需要重启网络。 下面是使用route命令修改默认网关的步骤&#xff1a; 打开终端窗口&#xff0c;以root用户或拥有sudo权限的用户身份登录。 使用以下命令查看当前的路由表信息&#xff1a; route…...

Datawhale 数学建模导论二 笔记5 多模数据与智能模型

主要涉及到的知识点有&#xff1a; 数字图像处理与计算机视觉 计算语言学与自然语言处理 数字信号处理与智能感知 10.1 数字图像处理与计算机视觉 视觉信息是我们第一种非常规的数据模式&#xff0c;在Python当中可以使用opencv处理数字图像&#xff0c;并提取出视觉特征用…...

【练习】【贪心】力扣1005. K 次取反后最大化的数组和

题目 1005 K 次取反后最大化的数组和 给你一个整数数组 nums 和一个整数 k &#xff0c;按以下方法修改该数组&#xff1a; 选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。 重复这个过程恰好 k 次。可以多次选择同一个下标 i 。 以这种方式修改数组后&#xff0c;返回数组 可…...

python学习七

作用域&#xff1a; 在编程语言中定义变量的可见性和生命周期的规则集合。它决定了在程序中的哪些位置可以访问或引用某个变量 1.全局作用域&#xff1a; 全局作用域是指在整个程序中都可见的变量。在函数外 部定义的变量通常具有全局作用域&#xff0c;在任何地方都可以访问和…...

安全运营的“黄金4小时“:如何突破告警疲劳困局

在当今复杂多变的网络安全环境中&#xff0c;安全团队面临着前所未有的挑战。尤其是面对高级持续性威胁&#xff08;APT&#xff09;时&#xff0c;最初的“黄金4小时”成为决定成败的关键窗口。在这段时间内&#xff0c;快速而准确地响应可以极大地降低损失&#xff0c;然而&a…...

本地部署Embedding模型API服务的实战教程

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于大模型算法的研究与应用。曾担任百度千帆大模型比赛、BPAA算法大赛评委,编写微软OpenAI考试认证指导手册。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。授权多项发明专利。对机器学…...

数据结构:二叉树的链式结构及相关算法详解

目录 一.链式结构的实现 1.二叉树结点基本结构&#xff0c;初始化与销毁&#xff1a; 二.链式结构二叉树的几种遍历算法 1.几种算法的简单区分&#xff1a; 2.前序遍历&#xff1a; 3.中序遍历&#xff1a; 4.后序遍历&#xff1a; 5.层序遍历&#xff08;广度优先遍历B…...

10.【线性代数】—— 四个基本子空间

十、 四个基本子空间 1. 列空间 C ( A ) C(A) C(A) in R m R^m Rm2. 零空间 N ( A ) N(A) N(A) in R n R^n Rn3. 行空间 C ( A T ) C(A^T) C(AT) in R n R^n Rn4. 左零空间 N ( A T ) N(A^T) N(AT) in R m R^m Rm综述5. 新的向量空间 讨论矩阵 A m ∗ n A_{m*n} Am∗n​…...

计算机黑皮书191本分享pdf

“黑皮书”通常指的是由机械工业出版社出版的计算机科学丛书。这些书籍的封面通常是黑色的&#xff0c;因此得名“黑皮书”。这些书籍涵盖了计算机科学的各个领域&#xff0c;包括操作系统、计算机网络、软件工程、编译原理、数据库等。 获取链接&#xff1a;链接:https://pan…...

MySQL Connector/J下载

MySQL Connector/J下载 下载mysql驱动jar包。 官网&#xff1a;https://downloads.mysql.com/archives/c-j/ 我下载的是8.0.33&#xff0c;下载的时候要注意与MySQL的版本对应。...

AIGC生图产品PM必须知道的Lora训练知识!

hihi&#xff0c;其实以前在方向AIGC生图技术原理和常见应用里面已经多次提到Lora的概念了&#xff0c;但是没有单独拿出来讲过&#xff0c;今天就耐心来一下&#xff01; &#x1f525; 一口气摸透AIGC文生图产品SD&#xff08;Stable Diffusion&#xff09;&#xff01; 一、…...

React Native 导航系统实战(React Navigation)

导航系统实战&#xff08;React Navigation&#xff09; React Navigation 是 React Native 应用中最常用的导航库之一&#xff0c;它提供了多种导航模式&#xff0c;如堆栈导航&#xff08;Stack Navigator&#xff09;、标签导航&#xff08;Tab Navigator&#xff09;和抽屉…...

练习(含atoi的模拟实现,自定义类型等练习)

一、结构体大小的计算及位段 &#xff08;结构体大小计算及位段 详解请看&#xff1a;自定义类型&#xff1a;结构体进阶-CSDN博客&#xff09; 1.在32位系统环境&#xff0c;编译选项为4字节对齐&#xff0c;那么sizeof(A)和sizeof(B)是多少&#xff1f; #pragma pack(4)st…...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

Pinocchio 库详解及其在足式机器人上的应用

Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库&#xff0c;专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性&#xff0c;并提供了一个通用的框架&…...

iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈

在日常iOS开发过程中&#xff0c;性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期&#xff0c;开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发&#xff0c;但背后往往隐藏着系统资源调度不当…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

MySQL 知识小结(一)

一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库&#xff0c;分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷&#xff0c;但是文件存放起来数据比较冗余&#xff0c;用二进制能够更好管理咱们M…...

vulnyx Blogger writeup

信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面&#xff0c;gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress&#xff0c;说明目标所使用的cms是wordpress&#xff0c;访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...

push [特殊字符] present

push &#x1f19a; present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中&#xff0c;push 和 present 是两种不同的视图控制器切换方式&#xff0c;它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...

基于PHP的连锁酒店管理系统

有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发&#xff0c;数据库mysql&#xff0c;前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...