首页 > 其他分享 >ATF引导启动流程整理-Part2:BL1引导启动流程整理

ATF引导启动流程整理-Part2:BL1引导启动流程整理

时间:2025-01-19 16:20:38浏览次数:1  
标签:引导 初始化 BL1 流程 bl1 BL2 整理 el3 image

接上一章的介绍,本文详细整理一下 BL1 阶段的流程

Ch3: ATF启动流程

上面一章简单的介绍了 ATF的隔离和划分,下面就介绍一下使用 ATF 初始启动的流程。ARM v8的启动流程包含多个阶段,典型的官方定义的标志阶段包括 BL1、BL2、BL31、BL32、BL33,根据不同需求这些阶段可以添加或者裁剪。官网的启动过程示意图如下,可以初步建立一个简单印象。

蓝色箭头为启动顺序。

  1. BL1 - Trusted ROM,一般为Boot Rom(内置)
  2. BL2 - Trusted Boot Firmware,一般为Trusted Bootloader
  3. BL31 - EL3 Runtime Frimware,一般为SML,管理SMC执行处理和中断,运行在secure monitor中
  4. BL32 - Secure-EL1 Payload,一般为TEE OS Image
  5. BL33 - Non - Trusted Firemware,一般为uboot

在搜索信息时发现一个博主的绘图非常清晰,将EL等级和启动结合在一起,此处借用

  • BL1是启动的第一阶段,该镜像必须要存储在可直接执行的介质中。若芯片支持XIP启动方式,其可被存储在片外可直接执行的介质中(如norflash)。若不支持XIP,则需要存储在芯片的片内ROM中,此时在芯片出厂后该部分代码就将被固化,后续再也不能被修改和升级。若芯片要支持安全启动,则需要将bootrom作为启动时的信任根,此时除调试阶段外,SOC必须禁用XIP。
  • BL2镜像由BL1加载,此时DDR还没有被初始化,因此它需要被加载到片内的SRAM中执行,一般在这个阶段会完成DDR的初始化,因此后面的镜像都可以被加载到DDR中。从上图不难看出,BL31、BL32和BL33都是由BL2加载的,其中BL31和BL32是可选的,若系统不支持TRUST OS,则可去掉BL32,若不支持EL3异常等级及secure monitor,则可去掉BL31
  • BL33一般指uboot,一般通过它最终启动操作系统内核

下面整理并介绍 BL1、BL2、BL31、BL33(uboot)各个阶段的启动流程与代码实现。

3.1 BL1启动流程

BL1 又被称为Trusted Boot ROM,是启动最早的ROM,它不过它是在CPU的ROM里而不是和BIOS在一起,是一切的信任根。BL1是系统启动的第一阶段,其主要目的是初始化系统环境和启动第二阶段镜像BL2。它的代码定位在: /atf/bl1/aarch64/bl1.ld.S 。

bl1.ld.S文件为 BL1 阶段的第一个文件,定义了 BL1阶段使用的内存、代码段、数据段等信息,此外最重要的是通过ENTRY标号定义了BL1阶段的入口函数bl1_entrypoint,在此入口函数中调用完成了环境的初始化以及引导下一步启动的功能。

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

简单来看一下 BL1 阶段都做了哪些事情,关键看一下 bl1_entrypoint.S 文件(已去除无关代码)

	.globl	bl1_entrypoint
	.globl	bl1_run_bl2_in_root
func bl1_entrypoint
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
	bl	bl1_setup
	bl	bl1_main
b	el3_exit

  • el3_entrypoint_common

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

  • bl1_setup

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

  • bl1_main

该函数主要用于bl2镜像加载以及跳转前的准备流程,如获取镜像参数、加载镜像内容、安全启动验签、bl2镜像跳转准备以及world switch上下文初始化等。值得注意的是关于校验镜像的合法性、通过镜像 ID 获取下一阶段 bl2 镜像操作操作也在此处完成,如果适配是发现 bl1 阶段引导 bl2 阶段异常,可以定位此处实现。定位实现为 atf/bl1/bl1_main.c。

  • el3_exit

该流程执行实际的上下文切换流程,包括保存当前EL3上下文以及跳转到bl2入口地址执行等。

以上就是 BL1 阶段主要完成的工作,结合上述的简单描述理解了 BL1阶段做了哪些事情,下面就结合函数实现详细整理一下各个函数主要进行了什么操作。

3.1.1 EL3系统状态初始化:el3_entrypoint_common

其中 el3_entrypoint_common 函数是一个初始化系统状态的接口,在 EL3 特权级均使用该接口进行初系统状态初始化,简单梳理此接口便于理解 ATF 的安全等级划分,下面简单看一下此函数接口都做了哪些事情。代码定位见:atf/include/arch/aarch64/el3_common_macros.S

	.macro el3_entrypoint_common					\
		_init_sctlr, _warm_boot_mailbox, _secondary_cold_boot,	\
		_init_memory, _init_c_runtime, _exception_vectors,	\
		_pie_fixup_size

此处执行的流程操作较多, 简单整理如下

(1) _init_sctlr:

初始化sctlr_el3寄存器,以初始化系统控制参数、架构相关el3的初始化实现见函数_init_sctlr,其中sctlr_el3初始化代码流程如下:

.if \_init_sctlr
	mov_imm	x0, (SCTLR_RESET_VAL & ~(SCTLR_EE_BIT | SCTLR_WXN_BIT \
			| SCTLR_SA_BIT | SCTLR_A_BIT | SCTLR_DSSBS_BIT))
	msr	sctlr_el3, x0
	isb
.endif /* _init_sctlr */

可以看到其核心是向 sctlr_el3 寄存器写入数据,结合搜索到的信息 sctlr_el3是EL3异常等级的控制寄存器,它控制了一些系统的重要行为,因此必须要在起始阶段就将其初始化到确定的状态。

那么这里结合宏定义可以看一下初始化定义了哪些信息

  • SCTLR_EE_BIT:定义系统大小端

  • SCTLR_WXN_BIT:可写内存执行权限

  • SCTLR_SA_BIT:栈对齐错误检查

  • SCTLR_A_BIT:对齐错误检查

  • SCTLR_DSSBS_BIT:随机存储器旁路检查

    结合上述信息我们可以看到在初始化系统寄存器时,禁用了上述功能;将计算所得数据写入到sctlr_el3控制寄存器,并调用指令同步屏障指令,确保对指令sctlr_el3的修改在后续指令执行前完成。

(2) _warm_boot_mailbox

判断当前启动方式是冷启动还是热启动(Reset、Reboot),并执行相应的处理, _warm_boot_mailbox 函数实现代码如下,根据 plat_get_my_entrypoint 获取热启动的地址,如果能正常获取地址则直接从热启动地址执行,如果不能则返回 0,表示此处启动为冷启动,机型执行冷启动流程。

	.if \_warm_boot_mailbox
		bl	plat_get_my_entrypoint
		cbz	x0, do_cold_boot
		br	x0

	do_cold_boot:
	.endif
(3) _secondary_cold_boot

确定当前的 CPU 是主 CPU 还是辅助CPU冷启动,如果是主 CPU 需要进行主 CPU 的初始化操作。根据注释信息:主CPU将进行平台配置,而辅助CPU将处于特定的状态,直到主CPU执行必要的操作将它们带出该状态并允许进入操作系统。(对此函数没有完全理解,认为是和硬件CPU划分安全环境相关,只将主CPU划分为 EL3安全等级)

后续结合 PSCI 了解得知和多核启动相关,ARM架构支持多核的热插拔已经支持指定辅助CPU重启,对应多核CPU集群启动场景,有兴趣的可以结合附件部分内容以及文档了解,此处不展开。

(4) _init_memory

该函数实现初始化内存空间的功能,会直接执行平台相关的内存初始化函数platform_mem_init,注意此处的memory并非我们的DDR而是Trust RAM,检查手上的代码实现此处没有做任何操作。

(5) _init_c_runtime

此函数如名称,为c运行环境初始化。c语言运行需要依赖于bss段和栈,因此在跳转到c函数之前需要下设置它们。而且由于bl1的镜像一般被烧写在rom中,因此需要将其可写数据段从rom重定位到ram中。 C语言的函数调用返回地址,上层栈指针地址,局部变量以及参数传递都可能需要用到栈。在完成 通过设置运行时栈指针为跳转到c语言执行做最后的准备

具体实现可见下面逻辑

.if \_init_c_runtime
#if defined(IMAGE_BL31) || (defined(IMAGE_BL2) && \
	((RESET_TO_BL2 && BL2_INV_DCACHE) || ENABLE_RME))
		……
/*配置 RW start 、 RW end 地址空间,初始化 cache 空间*/
		adrp	x0, __RW_START__
		add	x0, x0, :lo12:__RW_START__
		adrp	x1, __RW_END__
		add	x1, x1, :lo12:__RW_END__
		sub	x1, x1, x0
		bl	inv_dcache_range
		……
/*配置 BSS start 、 BSS end 地址空间,清空BSS段空间*/
		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 defined(IMAGE_BL1) ||	\
	(defined(IMAGE_BL2) && RESET_TO_BL2 && 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
#endif
.endif /* _init_c_runtime */
/* 配置栈空间 */
	msr	spsel, #0
	bl	plat_set_my_stack
#if STACK_PROTECTOR_ENABLED
	.if \_init_c_runtime
	bl	update_stack_protector_canary
	.endif /* _init_c_runtime */
#endif
	.endm
(6) _exception_vectors

此函数接口实现功能为设置异常向量表,这部分代码比较清楚,就是将bl1的异常向量表设置到EL3的向量表基地址寄存器中,其代码如下:

	adr x0, \_exception_vectors
 	msr vbar_el3, x0

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

(7) _pie_fixup_size

Pie相关的处理。首先先明白 Pie(地址无关可执行环境)是什么。常见的在 gcc 编译时有选择-pie或者 -fpie选项,我们知道代码执行过程中可能需要跳转到某个位置,或者操作某个地址的数据,而在二进制代码中这些位置都需要通过地址来表示。因此,对于普通程序我们需要将其加载到与链接地址相同的位置执行,否则这些寻址操作就会失败。pie就是为了解决该问题的,它的基本思路如下:

  • Step1: 程序中的函数调用和数据读写,若其可以转换为相对寻址的,则将其用相对寻址方式替换绝对地址。如armv8的adr指令,通过pc + offset的方式寻址,即以pc值为基地址,以offset为偏移量,从而计算得到新的地址。当然,这种寻址方式有一定的限制,如跳转范围有限等

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

此处根据注释信息,如果配置 pie fixup则主 CPU 在冷启动路径上之后修复一次全局变量描述符(GDT),而根据之前的配置 _pie_fixup_size=0 此处代码不会执行。

继续了解为什么有此功能而不开启,搜索到的信息为: PIE 作为一种特殊的内存分配方式,支持在程序运行时可以为执行文件分配随机的内存位置,对于防止恶意软件攻击有很大作用,但是会有一定的性能开销,因此通常情况下 Arm 系统不会启用PIE,只有在安全性要求较高的领域,比如涉及到支付或者私密信息的系统时才会使用。

3.1.2 BL1阶段初始化:bl1_setup

下面进入 bl1 阶段的启动阶段,核心接口实现如下

/************************************************************************
 * Setup function for BL1.
 **********************************************************************/
void bl1_setup(void)
{
	/* Perform early platform-specific setup */
	/* 执行早期特定平台的配置 */
	bl1_early_platform_setup();

	/* Perform late platform-specific setup */
	/* 执行后期期特定平台的配置 */
	bl1_plat_arch_setup();

#if CTX_INCLUDE_PAUTH_REGS
	/*
	 * Assert that the ARMv8.3-PAuth registers are present or an access
	 * fault will be triggered when they are being saved or restored.
	 */
	assert(is_armv8_3_pauth_present());
#endif /* CTX_INCLUDE_PAUTH_REGS */
}

(1)bl1_early_platform_setup

以 arm 平台实现为例,实现代码和梳理如下,平台完成的工作包括:

  • 使能 watchdog、控制台初始化、设置secure SRAM内存的地址范围
  • 初始化外围设备
  • 使能外围设备,配置多个CPU核的内部通信,保证主CPU集群的互联一致性
void arm_bl1_early_platform_setup(void)
{

#if !ARM_DISABLE_TRUSTED_WDOG
	/* 使能 watchdog */
	plat_arm_secure_wdt_start();
#endif

	/* 初始化串口支持早期的 debug 打印 */
	arm_console_boot_init();

	/* Allow BL1 to see the whole Trusted RAM */
	bl1_tzram_layout.total_base = ARM_BL_RAM_BASE;
	bl1_tzram_layout.total_size = ARM_BL_RAM_SIZE;
}

void bl1_early_platform_setup(void)
{
	arm_bl1_early_platform_setup();

	/*
	 * Initialize Interconnect for this cluster during cold boot.
	 * No need for locks as no other CPU is active.
	 */
	plat_arm_interconnect_init();
	/*
	 * Enable Interconnect coherency for the primary CPU's cluster.
	 */
	plat_arm_interconnect_enter_coherency();
}
(2)bl1_plat_arch_setup

继续以ARM平台为例,检查后期初始化完成了哪些工作。通过下面代码可以看到,该函数用于为bl1需要访问的地址建立MMU页表。理解上bl1中物理地址和虚拟地址映射的地址值是相等的,此处开启MMU的原因并不明确,从 enable_mmu_el3 实现上来看, 结合相关 enable_mmu_el2、enable_mmu_el1 的实现,是为了统一接口,通过标记来配置 MMU,可以支持通过标识映射不同的内存地址。

void arm_bl1_early_platform_setup(void)
{

#if !ARM_DISABLE_TRUSTED_WDOG
	/* 使能 watchdog */
	plat_arm_secure_wdt_start();
#endif

	/* 初始化串口支持早期的 debug 打印 */
	arm_console_boot_init();

	/* Allow BL1 to see the whole Trusted RAM */
	bl1_tzram_layout.total_base = ARM_BL_RAM_BASE;
	bl1_tzram_layout.total_size = ARM_BL_RAM_SIZE;
}

void bl1_early_platform_setup(void)
{
	arm_bl1_early_platform_setup();

	/*
	 * Initialize Interconnect for this cluster during cold boot.
	 * No need for locks as no other CPU is active.
	 */
	plat_arm_interconnect_init();
	/*
	 * Enable Interconnect coherency for the primary CPU's cluster.
	 */
	plat_arm_interconnect_enter_coherency();
}

后续整理时意识到不是这样的,有些方案内部有独立的Trust RAM,因此对于安全区划分时自然的将Trust RAM 作为Trust Zone,所以物理地址和虚拟地址映射的地址值是相等;而对于通用框架,有些主芯片只有一个 RAM,那么就需要使用TZASC将 RAM 划分不同的区域,结合 MMU 标记区分不同的内存空间。

3.1.3 BL1加载验证BL2镜像:bl1_main

在完成上述的初始化操作后,基本的 CPU、串口等已完成初始化,且C语言环境也已经配置好,bl1主要功能中初始化系统环境已经完成,下面需要负责加载、验证Bl2镜像。

/* 执行剩余的 EL3 的通用架构配置 */
/* 根据注释信息,函数实现为将下一个异常等级的执行状态设置为aarch64 */
	bl1_arch_setup();

	/* 加密模块初始化,执行部分静态检查并调用库初始化函数 */
	crypto_mod_init();

	/* 初始化认证模块 */
	auth_mod_init();

	/* 初始化测量启动函数 */
	bl1_plat_mboot_init();

	/* 配置执行平台在 BL1. */
	bl1_platform_setup();

	/* 获取下一个加载和运行的镜像 id */
	image_id = bl1_plat_get_next_image_id();

	/* 判断 id 是否为 BL2 */
	if (image_id == BL2_IMAGE_ID)
		/*加载 bl2 镜像到安全的RAM */
		bl1_load_bl2();
	else
		NOTICE("BL1-FWU: *******FWU Process Started*******\n");

	/* 删除测量启动程序 */
	bl1_plat_mboot_finish();

	/* 获取Bl2 image的描述信息,将信息保存到 bl1_cpu_context的上下文中 */
	bl1_prepare_next_image(image_id);

	/* 退出 bl1 前将串口中的数据全部刷新掉 */
	console_flush();

这里不一一展开,我们核心关注的是 bl2 的镜像加载部分,镜像加载前我们获取了下一步镜像加载的 id,然后根据下一步引导的 ID 将数据加载到安全内存中,完成上述操作后将配置上下文切换的全局结构,配置数据和传参,实现如下

(1) bl1_load_bl2

此函数实现为 bl1 将 bl2 数据加载到 trust RAM中,整理函数功能如下:

  1. 获取待加载镜像描述信息,在ATF中,镜像描述信息主要包含镜像id、镜像加载器使用的信息image_info和镜像跳转时使用的信息ep_info

  2. 加载之前的处理(无操作);对于BL2 支持压缩可进行解压操作

  3. 加载镜像,根据先前获取到的bl2镜像描述信息,从storage中将镜像数据加载到给定地址上

  4. 加载之后的处理:主要用于设置bl1向bl2传递的参数,一共通过 args 传递八个参数,从汇编代码分别被设置到x0 – x7寄存器中。此过程核心包括计算 BL2 的可用内存(将总的内存减去bl1已使用的SRAM内存,作为bl2的可用内存)。

static void bl1_load_bl2(void)
{
	/* 获取bl2 image 的描述信息,主要包括入库地址,名字 */
	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");

	/* 载入前配置 BL1 和 BL2 存储器,实际实现 return 0 */
	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);
	}

	/* 填充BL2的默认参数,计算并配置 BL2的内存布局 */
	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");
}

(2)bl1_prepare_next_image

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

bl1_prepare_next_image的主要工作就是初始化primary cpu的cpu_context上下文,并填充该结构体的相关信息。整理实现如下

  1. 获取bl2的ep信息

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

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

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

  5. 计算SPSR寄存器的值,栈指针使用SP_ELX

AArch64状态下,SP对应的物理寄存器有4个SP_EL0、SP_EL1、SP_EL2、SP_EL3 也不难理解不同运行权限下使用不同的 SP寄存器。

  1. 切换异常等级初始化上下文,如scr_el3, pc,spsr以及参数传递寄存器x0 – x7的值

  2. 将context中参数设置到实际的寄存器中

void bl1_prepare_next_image(unsigned int image_id)
{
	……
	/* Get the image descriptor. */
	/* 获取bl2 image 的描述信息,主要包括入库地址,名字 */
	desc = bl1_plat_get_image_desc(image_id);
	assert(desc != NULL);

	/* Get the entry point info. */
	/* 获取 bl2 image 的入口地址信息 */
	next_bl_ep = &desc->ep_info;

	/* Get the image security state. */
	/* 获取 bl2 image的安全状态,用于判定该 image是否属于安全状态 */
	security_state = GET_SECURITY_STATE(next_bl_ep->h.attr);

	/* Setup the Secure/Non-Secure context if not done already. */
	/* 设置用于存放 CPU contest的变量 */
	if (cm_get_context(security_state) == NULL)
		cm_set_context(&bl1_cpu_context[security_state], security_state);

	/* Prepare the SPSR for the next BL image. */
	/* 为下一个阶段image配置运行 EL2参数,配置 SPSR寄存器(程序状态保存寄存器)	*/
	if ((security_state != SECURE) && (el_implemented(2) != EL_IMPL_NONE)) {
		mode = MODE_EL2;
	}

	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. */
	/* 通过获取到的 bl2 image 的入口地址数据来初始化 cpu context */
	cm_init_my_context(next_bl_ep);
	/* 为进入到下一个 EL等级做准备 */
	cm_prepare_el3_exit(security_state);

	/* Indicate that image is in execution state. */
	/* 设定image的执行状态 */
	desc->state = IMAGE_STATE_EXECUTED;

	/* 打印 bl2 image的入口信息 */
	print_entry_point_info(next_bl_ep);
}

3.1.4 上下文切换,转到BL2阶段:el3_exit

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

  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. cpu将离开bl1跳转到bl2的入口处执行了

func el3_exit

	/* 将SP_EL0指针暂存到 x17,此处记录EL3运行时的堆栈
 * 此堆栈将用于处理下一个 SMC 异常,然后将切换到 SP_EL3
*/
	mov	x17, sp
	msr	spsel, #MODE_SP_ELX
	str	x17, [sp, #CTX_EL3STATE_OFFSET + CTX_RUNTIME_SP]

	/* ----------------------------------------------------------
	 * Restore CPTR_EL3.
	 * ZCR is only restored if SVE is supported and enabled.
	 * Synchronization is required before zcr_el3 is addressed.
	 * ----------------------------------------------------------
	 */

	/* 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

	/* ----------------------------------------------------------
	 * Restore SPSR_EL3, ELR_EL3 and SCR_EL3 prior to ERET
	 * ----------------------------------------------------------
	 */
	/* 从之前存储的 CPU context 中加载scr_el3、 spsr_el3 、elr_el3的值*/
	ldr	x18, [sp, #CTX_EL3STATE_OFFSET + CTX_SCR_EL3]
	ldp	x16, x17, [sp, #CTX_EL3STATE_OFFSET + CTX_SPSR_EL3]
	msr	scr_el3, x18
	msr	spsr_el3, x16
	msr	elr_el3, x17

	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]

	exception_return

endfunc el3_exit

3.1.5 总结

BL1主要目的是建立Trusted SRAM、exception vector、初始化串口console等等。然后找到并验证BL2,然后跳过去。BL1位于ROM中,在EL3下从reset vector处开始运行,做的主要工作有:

  • 决定启动路径:冷启动还是热启动。

  • 架构初始化:异常向量、CPU复位处理函数配置、控制寄存器设置(SCRLR_EL3/SCR_EL3/CPTR_EL3)

  • 平台初始化:使能Trusted Watchdog、初始化控制台、配置硬件一致性互联、配置MMU、初始化相关存储设备。

  • 固件更新处理

  • BL2镜像加载和执行:

  • BL1输出“Booting Trusted Firmware"。

  • BL1加载BL2到SRAM;如果SRAM不够或者BL2镜像错误,输出“Failed to load BL2 firmware.”。(EL3)

  • BL1切换到Secure EL1并将执行权交给BL2.(EL3)

附录:

参考链接:

万字长文带你搞懂安全启动及ATF_atf是如何启动的-CSDN博客

【ATF】TF-A概述 - Emma1111 - 博客园

ATF启动(一):整体启动流程 - 知乎

ARM的安全启动—ATF/TF-A以及它与UEFI的互动_uefi arm-CSDN博客

标签:引导,初始化,BL1,流程,bl1,BL2,整理,el3,image
From: https://www.cnblogs.com/silas041301/p/18679660

相关文章

  • ATF引导启动流程整理-Part1:简介部分
    Ch1:背景与基础内容介绍1.1背景最近工作中使用U-boot进行内核引导启动调整,发现编译手册中对Uboot镜像编译流程和之前接触的不太一样,在完成U-boot编译流程后,需要单独再进行ATF编译。且ATF编译过程中需要使用U-boot.bin产物并且给出硬件配置。此工程的最终生成产物被分成......
  • 系统安全设计规范,代码管理机制,管理规范,接口集成规范,代码编写规范,程序设计规范(全文档整
    1.1安全建设原则1.2安全管理体系1.3安全管理规范1.4数据安全保障措施1.4.1数据库安全保障1.4.2操作系统安全保障1.4.3病毒防治1.5安全保障措施1.5.1实名认证保障1.5.2接口安全保障1.5.3加密传输保障1.5.4终端安全保障软件全套资料部分文档......
  • ASP.NET Core MVC的Areas使用整理
    一、ASP.NETCoreMVC区域Areas定义区域是一项MVC功能,用于将相关功能作为一个单独的组组织到一个组中:控制器操作的路由命名空间。视图的文件夹结构。通过使用区域,应用可以有多个名称相同的控制器,只要它们具有不同的区域。通过向controller和action添加另一个路由参......
  • java流程控制及其循环语句
    用户交互Scanner(IO流跟电脑打交道)基础语法:Scanners=newScanner(System.in);//表示系统数据输入​Scanners=newScanner(System.out);//表示系统数据输出​scanner.close:表示关闭数据流通过Scanner类的n......
  • C语言中的流程控制(for循环 while循环 do-while循环)
    什么是循环?循环---重复在执行循环语句for while do-while 1. for循环for(表达式1;表达式2;表达式3) {  语句} ①求解表达式1  ②判断表达式2  真 ③执行语句④求解表达式3  重复②-④直到表达式2为假    ①只会执行一次  ......
  • 【ESP 乐鑫相关】ESP32-S3启动流程
    转载自:https://blog.itpub.net/70040860/viewspace-3053923/ESP32-S3启动流程    本文将会介绍ESP32-S3从上电到运行app_main函数中间所经历的步骤(即启动流程)。从宏观上,该启动流程可分为如下3个步骤。    ①:一级引导程序,它被固化在ESP32-S3内部的ROM中,它会从flas......
  • Spring Boot 启动流程
    1.引导类(BootstrapClass)启动一个SpringBoot应用程序的第一步是编写引导类,该类包含了一个main方法,负责启动整个应用程序。例如:@SpringBootApplicationpublicclassApplication{publicstaticvoidmain(String[]args){SpringApplication.run(Applic......
  • 【大厂面试AI算法题中的知识点】方向涉及:ML/DL/CV/NLP/大数据...本篇介绍DERT中匈牙利
    【大厂面试AI算法题中的知识点】方向涉及:ML/DL/CV/NLP/大数据…本篇介绍DERT中匈牙利匹配算法的具体流程?【大厂面试AI算法题中的知识点】方向涉及:ML/DL/CV/NLP/大数据…本篇介绍DERT中匈牙利匹配算法的具体流程?文章目录【大厂面试AI算法题中的知识点】方向涉及:ML/DL/C......
  • 推荐2款无需安装实用软件,桌面图标整理设置,简真是Windows神器!
    聊一聊今天给大家推荐2款桌面美化小工具。为什么觉得要推荐这个小工具呢?因为最近帮一些人远程处理一些问题。感觉那电脑桌面,密密麻麻,全是小图标。我想找个东西都难,是太难了。我真恨不得上手整理。但又怕整理了,人家找不到东西。所以,今天给大家分享2款桌面美化小工具。......
  • Linux驱动开发:一文掌握 块设备VS字符设备开发流程全解!
    Linux驱动开发是嵌入式系统开发中的一个重要组成部分,它直接关系到硬件设备的功能实现和性能优化。在Linux系统中,驱动开发主要分为字符设备驱动、块设备驱动和网络设备驱动三大类。本文将重点介绍字符设备和块设备的基础知识,以及它们在驱动开发中的差异和开发流程。一、字符设......