赞
踩
一、目标
本报告分析ATF中bl1可信启动的源代码,源代码位于arm-trusted-firmware-master\bl1和arm-trusted-firmware-master\include\arch。
二、核心服务原理分析
1.bl1模块的运行原理
系统上电之后首先会运行ChipRom,之后会跳转到ATF的bl1中继续执行。bl1主要初始化CPU、设定异常向量、将bl2的镜像加载到安全RAM中,然后跳转到b2中开始运行。Bl1的主要代码存放在bl1目录中,bl1的链接文件是bl1/bl1.ld.s文件,该文件指定bl1的入口函数是bl1_entrypoint。AArch32的该函数定义在bl1/aarch32/bl1_entrypoint.S文件中,AArch64的该函数定义在bll/aarch64/bl1_entrypoint.S文件中。
2.与其他功能模块的关系
下图为在AArch64中,ATF的完整启动流程。
图2.2 AArch64模式的ATF启动流程
从图中可以看出bl1和bl2模块的联系:bl1执行后跳转到bl2执行。
在bl1完成了将bl2镜像文件加载到RAM中的操作、中断向量表的设定以及其他CPU相关设定后,bl1_main函数会解析出bl2镜像文件的描述信息,获取入口地址,并设定下一个阶段的cpu上下文。这些操作完成之后,调用el3_exit雨数来实现bl1到bl2的跳转进入bl2中开始执行。
三、源代码分析
1.bl1_entrypoint函数说明
bl1_entrypoint雨数主要完成ARMv8架构中EL3执行环境的基础初始化、设定异常向量表、加载bl2的镜像文件到内存中并进行跳转到bl2继续执行。该函数的内容如下:
func bl1_entrypoint
/* 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
/ BL1初始化 */
bl bl1_setup
#if ENABLE_PAUTH
/* 编程APIAKey_EL1并启用指针身份验证 */
bl pauth_init_enable_el3
#endif
/* 调用bl1_main函数,初始化验证模块,加载下一阶段的image到RAM中*/
bl bl1_main
#if ENABLE_PAUTH
/* 在跳转到下一个启动映像之前禁用指针身份验证 */
bl pauth_disable_el3
#endif
#if ENABLE_RME
/* 转换到下一个启动映像 */
b bl1_run_bl2_in_root
#else
/* 调用el3_exit函数,跳转到下一个image(bl2) */
b el3_exit
#endif
endfunc bl1_entrypoint
el3_entrypoint_common函数执行时带入的参数包括大小端标识、属于冷启动还是重启操作、是否是从核的启动、是否需要进行内存初始化、是否需要建立C语言运行环境(栈初始化)、异常向量表地址注册等。
2.el3_entrypoint_common功能说明
该函数以宏的方式被定义,主要用来完成EL3运行环境的设置和异常向量表的注册代码内容和注释如下:
.macro el3_entrypoint_common
_init_sctlr, _warm_boot_mailbox, _secondary_cold_boot,
_init_memory, _init_c_runtime, _exception_vectors,
_pie_fixup_size
/* 通过sctlr寄存器设定大小端 /
.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 */
#if DISABLE_MTPMU
bl mtpmu_disable
#endif
/* 判定是否需要调用do_cold_boot流程 */
.if _warm_boot_mailbox
bl plat_get_my_entrypoint
cbz x0, do_cold_boot
br x0
do_cold_boot:
.endif
.if _pie_fixup_size
#if ENABLE_PIE
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 */
/*
* 设定异常向量
*/
adr x0, _exception_vectors
msr vbar_el3, x0
isb
#if !(defined(IMAGE_BL2) && ENABLE_RME)
bl reset_handler //执行reset handle操作
#endif
el3_arch_init_common //初始化异常向量
/* 判定当前CPU是否是主CPU,如果是则执行主CPU的初始化 /
.if _secondary_cold_boot
/ 获取当前core的编号,判定当前是主核还是从核 /
bl plat_is_my_cpu_primary
/ 如果是主核,则调用do_primary_cold_boot执行主核启动 /
cbnz w0, do_primary_cold_boot
/ 如果是从核,则执行从核启动 */
bl plat_secondary_cold_boot_setup
bl el3_panic
do_primary_cold_boot:
.endif /* _secondary_cold_boot */
/* 初始化memory */
.if _init_memory
bl platform_mem_init
.endif /* _init_memory */
/* 初始化C语言的运行环境 /
.if _init_c_runtime
#if defined(IMAGE_BL31) || (defined(IMAGE_BL2) &&
((BL2_AT_EL3 && BL2_INV_DCACHE) || ENABLE_RME))
#if ENABLE_PIE
#if SEPARATE_CODE_AND_RODATA
adrp x0, TEXT_START //获取text的起始地址
add x0, x0, :lo12:TEXT_START
#else
adrp x0, RO_START //获取内存RO的起始地址
add x0, x0, :lo12:RO_START
#endif / SEPARATE_CODE_AND_RODATA /
#else
adrp x0, RW_START //获取内存RW的起始地址
add x0, x0, :lo12:RW_START
#endif / ENABLE_PIE */
adrp x1, RW_END //获取内存RW的末端地址
add x1, x1, :lo12:RW_END
sub x1, x1, x0 //RW的长度
bl inv_dcache_range //无效数据cache
#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
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) && BL2_AT_EL3 && BL2_IN_XIP_MEM)
adrp x0, DATA_RAM_START
add x0, x0, :lo12:DATA_RAM_START //将x0加到RAM的起始地址
adrp x1, DATA_ROM_START
add x1, x1, :lo12:DATA_ROM_START //将x1加到ROM的起始地址
adrp x2, DATA_RAM_END
add x2, x2, :lo12:DATA_RAM_END //将x2加到RAM的末端地址
sub x2, x2, x0
bl memcpy16
#endif
.endif /* _init_c_runtime */
bl plat_set_my_stack //设定堆栈
#if STACK_PROTECTOR_ENABLED
.if _init_c_runtime
bl update_stack_protector_canary
.endif /* _init_c_runtime */
#endif
.endm
#endif /* EL3_COMMON_MACROS_S */
el3_entpypoint_common函数主要完成C语言运行环境的搭建、异常向量表的注册、bl1镜像文件的复制、CPU安全运行环境的设定等。
3. bl1_setup函数
bl1_setup函数主要完成CPU中ARM核的早期初始化,包括内存、页表、外部设备以及ARM核状态的设定、其内容如下:
void bl1_setup(void)
{
/* 执行平台特定的早期设置 */
bl1_early_platform_setup();
/* 执行平台特定的后期设置 */
bl1_plat_arch_setup();
#if CTX_INCLUDE_PAUTH_REGS
/* 若在保存或恢复时ARMv8.3-PAuth寄存器不存在,则触发访问故障。*/
assert(is_armv8_3_pauth_present());
#endif
}
4.bl_main函数
bl_main函数主要完成bl2镜像文件的加载和b12运行环境的配置、如果使能了安全引导功能,则还需要对bl2镜像文件执行验签操作。该函数定义在/bl1/bl1_main.c文件中,主要内容和注释如下:
void bl1_main(void)
{
unsigned int image_id;
/* 宣布我们的到来 */
NOTICE(FIRMWARE_WELCOME_STR);
NOTICE("BL1: %s\n", 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;
/*
* 确保MMU/Caches and coherency 处于打开状态
/
#ifdef aarch64
val = read_sctlr_el3();
#else
val = read_sctlr();
#endif
assert((val & SCTLR_M_BIT) != 0);
assert((val & SCTLR_C_BIT) != 0);
assert((val & SCTLR_I_BIT) != 0);
/
检查CTR_EL0中的回写缓存颗粒(CWG)是否与提供的平台值匹配
/
val = (read_ctr_el0() >> CTR_CWG_SHIFT) & CTR_CWG_MASK;
/
如果CWG为零,则没有可用的CWG信息,但我们至少可以检查平台值是否小于架构最大值
*/
if (val != 0)
assert(CACHE_WRITEBACK_GRANULE == SIZE_FROM_LOG2_WORDS(val));
else
assert(CACHE_WRITEBACK_GRANULE <= MAX_CACHE_LINE_SIZE);
#endif
/* 设置bl2镜像运行时的EL级别 */
bl1_arch_setup();
crypto_mod_init();
/* 初始化image的验证模块 */
auth_mod_init();
/* 初始化测量的引导 */
bl1_plat_mboot_init();
/* 平台相关设置,主要是IO的设置 */
bl1_platform_setup();
#if ENABLE_PAUTH
/* 存储 APIAKey_EL1 钥匙 */
bl1_apiakey[0] = read_apiakeylo_el1();
bl1_apiakey[1] = read_apiakeyhi_el1();
#endif
/* 获取下一个阶段image的ID值。默认返回值为BL2_IMAGE_ID */
image_id = bl1_plat_get_next_image_id();
/*
目前,我们将BL2_IMAGE_ID以外的任何图像id都解释为固件更新的开始。
*/
if (image_id == BL2_IMAGE_ID)
bl1_load_bl2(); //将bl2 image加载到安全RAM中
else
NOTICE(“BL1-FWU: FWU Process Started\n”);
/* 拆卸已测量的引导驱动程序 */
bl1_plat_mboot_finish();
/* 获取bl2镜像的描述信息、包括名字、ID、entry point info等,并将这些信息保存到bl1_cpu_context的上下文中
/
bl1_prepare_next_image(image_id);
/ 刷新console */
console_flush();
}
5.bl1_prepare_next_image函数
Bl1_prepare_next_image函数用来获取bl2镜像的描述信息、bl2的入口地址信息、设定bl2的运行状态、以备跳转时使用,其内容和解释如下:
void bl1_prepare_next_image(unsigned int image_id)
{
/*以下数组将用于内容管理。
*有两个实例,分别用于安全内容和非安全内容。
*/
static cpu_context_t bl1_cpu_context[2];
unsigned int security_state, mode = MODE_EL1;
image_desc_t *desc;
entry_point_info_t *next_bl_ep;
#if CTX_INCLUDE_AARCH32_REGS
/*
确保在CPU上下文中保存AArch32系统寄存器的构建标志没有为只针对aarch64平台设置
*/
if (el_implemented(1) == EL_IMPL_A64ONLY) {
ERROR("EL1 supports AArch64-only. Please set build flag "
“CTX_INCLUDE_AARCH32_REGS = 0\n”);
panic();
}
#endif
/* 获取bl2 image的描述信息,主要包括入口地址、名字等信息 */
desc = bl1_plat_get_image_desc(image_id);
assert(desc != NULL);
/* 获取image的入口地址信息 */
next_bl_ep = &desc->ep_info;
/* 获取bl2 image的安全状态(判定该image是属于安全态的image还是非安全态的image) */
security_state = GET_SECURITY_STATE(next_bl_ep->h.attr);
/* 设定用于存放CPU context的变量 */
if (cm_get_context(security_state) == NULL)
cm_set_context(&bl1_cpu_context[security_state], security_state);
/* 为下个阶段的image准备好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);
/* 允许平台进行更改 */
bl1_plat_set_ep_info(image_id, next_bl_ep);
/* 使用获取到的bl2 image的entrypoint info数据来初始化cpu context /
cm_init_my_context(next_bl_ep);
/ 为进入到下个EL级别做准备 */
cm_prepare_el3_exit(security_state);
/* 设定image的执行状态 */
desc->state = IMAGE_STATE_EXECUTED;
/* 打印出bl2 image的入口信息 */
print_entry_point_info(next_bl_ep);
}
6.流程图
图3.6 bl1执行流程
四、分析结论
ARM可信任固件(ARM Trusted Firmware,ATF)是由ARM官方提供的底层固件该固件统一了ARM底层接口标准,如电源状态控制接口(Power Status Control interface,PSCI)、安全启动需求(Trusted Board Boot Requirements,TBBR)、安全世界状态(SWS)与正常世界状态(NWS)切换的安全监控模式调用(secure monitor call,smc)操作等。ATF旨在将ARM底层的操作统一使代码能够重用和便于移植。
ATF的源代码共分为bl1、bl2、bl31、bl32、bl33部分、其中bl1、bl2、bl31部分属于固定的固件、b1l2和bl33分别用于加载TEE OS和REE侧的镜像。整个加载过程可配置成安全启动的方式,每一个镜像文件在被加载之前都会验证镜像文件的电子签名是否合法。
ATF主要完成的功能如下:
① 初始化安全世界状态运行环境、异常向量、控制寄存器、中断控制器、配置平台的中断。
② 初始化ARM通用中断控制器(General Interrupt Controller,GIC)2.0版本和3.0版左的动初始化。
③ 执行ARM系统IP的标准初始化操作以及安全扩展组件的基本配置。
④ 安全监控模式调用(Secure Monitor Call,smc)请求的逻辑处理代码(Monitor模式/EL3)。
⑤ 实现可信板级引导功能,对引导过程中加载的镜像文件进行电子签名检查。
⑥ 支持自有固件的引导,开发者可根据具体需求将自有固件添加到ATF的引导流程中。
通过本实验,我了解了ATF可信启动的bl1阶段的执行流程,以及各个模块的作用,对ATF的原理有了更深刻的认识。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。