ATF启动(三):BL2
前面BL1跳转到了BL2
BL2位于SRAM中,运行在Secure EL1主要工作有:
- 架构初始化:EL1/EL0使能浮点单元和ASMID。
- 平台初始化:控制台初始化、相关存储设备初始化、MMU、相关设备安全配置、
- SCP_BL2:系统控制核镜像加载,单独核处理系统功耗、时钟、复位等控制。
- 加载BL31镜像:BL2将控制权交给BL1;BL1关闭MMU并关cache;BL1将控制权交给BL31。
- 加载BL32镜像:BL32运行在安全世界,BL2依赖BL31将控制权交给BL32。SPSR通过Secure-EL1 Payload Dispatcher进行初始化。
- 加载BL33镜像:BL2依赖BL31将控制权交给BL33。
BL2 image将会为后续image的加载执行相关的初始化操作。
主要是内存,MMU,串口以及EL3软件运行环境的设置,并且加载bl3x的image到RAM中。
BL2的主要工作就是加载BL3x系列镜像,然后通过SMC进入BL1进而跳转到BL31运行。
bl2_entrypoint()是BL2的入口:
- 前半部分主要进行一系列初始化工作,
- 然后通过bl2_main()加载BL3x镜像到RAM中,
- 最后通过SMC调用执行BL1中指定的smc handler将CPU执行权交给BL31。
通过查看bl2.ld.S文件就可以发现,bl2 image的入口函数是bl2_entrypoint。该函数定义在bl2/aarch64/bl2_entrypoint.S文件中。
1、bl2_entrypoint()是BL2的入口初始化+通过bl2_main加载BL3x镜像,bl1中将CPU的控制权转交给bl31:bl2_entrypoint—>
2、bl2_main()主要实现将bl3x的image加载RAM中,并通过smc调用执行bl1中指定的smc handle将CPU的执行权交给bl31。:bl2_main—>
3、bl2_load_images是bl2_main中具体实现加载bl3x的image到RAM中,返回一个具有image入口信息的变量。smc handle根据该变量跳转到bl31进行执行:bl2_load_images—>
4、bl2_mem_params_descs该宏的执行将会初始化组成bl2加载bl3x image的列表使用到的重要全局变量:—>REGISTER_BL_IMAGE_DESCS
1.bl2_entrypoint
该函数的内容如下,该函数最终会出发smc操作,从bl1中将CPU的控制权转交给bl31:
func bl2_entrypoint
/*---------------------------------------------
* Save from x1 the extents of the tzram
* available to BL2 for future use.
* x0 is not currently used.
* ---------------------------------------------
*/
mov x20, x1
/* ---------------------------------------------
* Set the exception vector to something sane.
* ---------------------------------------------
*/
adr x0, early_exceptions
msr vbar_el1, x0-------------------------------------设定异常向量。
isb
/* ---------------------------------------------
* Enable the SError interrupt now that the
* exception vectors have been setup.
* ---------------------------------------------
*/
msr daifclr, #DAIF_ABT_BIT
/* ---------------------------------------------
* Enable the instruction cache, stack pointer
* and data access alignment checks
* ---------------------------------------------
*/
mov x1, #(SCTLR_I_BIT | SCTLR_A_BIT | SCTLR_SA_BIT)
mrs x0, sctlr_el1
orr x0, x0, x1
msr sctlr_el1, x0----------------------------------配置cache、内存对齐等属性。
isb
/* ---------------------------------------------
* Invalidate the RW memory used by the BL2
* 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.
* ---------------------------------------------
*/
adr x0, __RW_START__
adr x1, __RW_END__
sub x1, x1, x0
bl inv_dcache_range-------------------------------将BL2的__RW_START__和__RW_END__之间的内存刷回DDR中。
/* ---------------------------------------------
* Zero out NOBITS sections. There are 2 of them:
* - the .bss section;
* - the coherent memory section.
* ---------------------------------------------
*/
ldr x0, =__BSS_START__
ldr x1, =__BSS_SIZE__
bl zeromem16--------------------------------------初始化__BSS_START__开始,大小为__BSS_SIZE__内存为0。
#if USE_COHERENT_MEM
ldr x0, =__COHERENT_RAM_START__
ldr x1, =__COHERENT_RAM_UNALIGNED_SIZE__
bl zeromem16
#endif
/* --------------------------------------------
* 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
/* ---------------------------------------------
* Perform early platform setup & platform
* specific early arch. setup e.g. mmu setup
* ---------------------------------------------
*/
mov x0, x20
bl bl2_early_platform_setup
bl bl2_plat_arch_setup
/* ---------------------------------------------
* Jump to main function.
* ---------------------------------------------
*/
bl bl2_main------------------------------------跳转到BL2主函数执行,该函数加载BL3x镜像,并通过SMC调用BL1指定SMC函数将CPU执行权交给BL31。
/* ---------------------------------------------
* Should never reach this point.
* ---------------------------------------------
*/
no_ret plat_panic_handler
endfunc bl2_entrypoint
2.bl2_main()
bl2_main()主要加载BL3x镜像并验证,然后获取下一个要运行的镜像信息,通过SMC调用让BL1去启动。
该函数主要实现将bl3x的image加载RAM中,并通过smc调用执行bl1中指定的smc handle将CPU的使用权交给bl31。
/*******************************************************************************
* The only thing to do in BL2 is to load further images and pass control to
* next BL. The memory occupied by BL2 will be reclaimed by BL3x stages. BL2
* runs entirely in S-EL1.
******************************************************************************/
void bl2_main(void)
{
entry_point_info_t *next_bl_ep_info;
NOTICE("BL2: %s\n", version_string);
NOTICE("BL2: %s\n", build_message);
/* Perform remaining generic architectural setup in S-EL1 */
bl2_arch_setup();
#if TRUSTED_BOARD_BOOT
/* Initialize authentication module */
auth_mod_init();
#endif /* TRUSTED_BOARD_BOOT */
/* Load the subsequent bootloader images. */
next_bl_ep_info = bl2_load_images();
#ifdef AARCH32
/*
* For AArch32 state BL1 and BL2 share the MMU setup.
* Given that BL2 does not map BL1 regions, MMU needs
* to be disabled in order to go back to BL1.
*/
disable_mmu_icache_secure();
#endif /* AARCH32 */
/*
* Run next BL image via an SMC to BL1. Information on how to pass
* control to the BL32 (if present) and BL33 software images will
* be passed to next BL image as an argument.
*/
smc(BL1_SMC_RUN_IMAGE, (unsigned long)next_bl_ep_info, 0, 0, 0, 0, 0, 0);----------发起SMC异常启动BL31镜像,交给BL1 bl1_aarch32_smc_handler处理。
}
3.bl2_load_images
该函数用来加载bl3x的image到RAM中,返回一个具有image入口信息的变量。smc handle根据该变量跳转到bl31进行执行
entry_point_info_t *bl2_load_images(void)
{
bl_params_t *bl2_to_next_bl_params;
bl_load_info_t *bl2_load_info;
const bl_load_info_node_t *bl2_node_info;
int plat_setup_done = 0;
int err;
/*
* Get information about the images to load.
*/
/* 获取bl3x image的加载和入口信息 */
bl2_load_info = plat_get_bl_image_load_info();
/* 检查返回的bl2_load_info中的信息是否正确 */
assert(bl2_load_info);
assert(bl2_load_info->head);
assert(bl2_load_info->h.type == PARAM_BL_LOAD_INFO);
assert(bl2_load_info->h.version >= VERSION_2);
/* 将bl2_load_info中的head变量的值赋值为bl2_node_info,即将bl31 image的入口信息传递给bl2_node_info变量 */
bl2_node_info = bl2_load_info->head;
/* 进入loop循环, */
while (bl2_node_info) {
/*
* Perform platform setup before loading the image,
* if indicated in the image attributes AND if NOT
* already done before.
*/
/* 在加载特定的bl3x image到RAM之前先确定是否需要做平台的初始化 */
if (bl2_node_info->image_info->h.attr & IMAGE_ATTRIB_PLAT_SETUP) {
if (plat_setup_done) {
WARN("BL2: Platform setup already done!!\n");
} else {
INFO("BL2: Doing platform setup\n");
bl2_platform_setup();
plat_setup_done = 1;
}
}
/* 对bl3x image进行电子验签,如果通过则执行加载操作 */
if (!(bl2_node_info->image_info->h.attr & IMAGE_ATTRIB_SKIP_LOADING)) {
INFO("BL2: Loading image id %d\n", bl2_node_info->image_id);
err = load_auth_image(bl2_node_info->image_id,
bl2_node_info->image_info);
if (err) {
ERROR("BL2: Failed to load image (%i)\n", err);
plat_error_handler(err);
}
} else {
INFO("BL2: Skip loading image id %d\n", bl2_node_info->image_id);
}
/* Allow platform to handle image information. */
/* 可以根据实际需要更改,通过给定image ID来更改image的加载信息 */
err = bl2_plat_handle_post_image_load(bl2_node_info->image_id);
if (err) {
ERROR("BL2: Failure in post image load handling (%i)\n", err);
plat_error_handler(err);
}
/* Go to next image */
bl2_node_info = bl2_node_info->next_load_info;
}
/*
* Get information to pass to the next image.
*/
/* 获取下一个执行的Image的入口信息,并且将以后会被执行的image的入口信息组合成链表 ,t通过判断image des中的ep_info.h.attr的值是否为(EXECUTABLE|EP_FIRST_EX)来确定接下来第一个被执行的image*/
bl2_to_next_bl_params = plat_get_next_bl_params();
assert(bl2_to_next_bl_params);
assert(bl2_to_next_bl_params->head);
assert(bl2_to_next_bl_params->h.type == PARAM_BL_PARAMS);
assert(bl2_to_next_bl_params->h.version >= VERSION_2);
/* Flush the parameters to be passed to next image */
plat_flush_next_bl_params();
/* 返回下一个进入的image的入口信息,即bl31的入口信息 */
return bl2_to_next_bl_params->head->ep_info;
}
3.REGISTER_BL_IMAGE_DESCS(bl2_mem_params_descs)
bl2_mem_params_descs定义了BL2需要加载镜像的信息,通过REGISTER_BL_IMAGE_DESCS()将其和bl_mem_params_desc_ptr关联,并获取需要加载镜像数目bl_mem_params_desc_num。
该宏的执行将会初始化组成bl2加载bl3x image的列表使用到的重要全局变量,其中bl2_mem_params_descs变量的定义如下:
在该变量中规定了SCP_BL2, EL3_payload, bl32, bl33 image的相关信息,例如:
image的入口地址信息:ep_info
image在RAM中的基地址:image_base
image的基本信息:image_info
image的ID值:image_id
**bl2_mem_params_descs定义了BL2需要加载镜像的信息,**通过REGISTER_BL_IMAGE_DESCS()将其和bl_mem_params_desc_ptr关联,并获取需要加载镜像数目bl_mem_params_desc_num。
#define REGISTER_BL_IMAGE_DESCS(_img_desc) \
bl_mem_params_node_t *bl_mem_params_desc_ptr = &_img_desc[0]; \
unsigned int bl_mem_params_desc_num = ARRAY_SIZE(_img_desc);
static bl_mem_params_node_t bl2_mem_params_descs[] = {
...
#ifdef EL3_PAYLOAD_BASE
...
#else /* EL3_PAYLOAD_BASE */
/* Fill BL31 related information */
{
.image_id = BL31_IMAGE_ID,
SET_STATIC_PARAM_HEAD(ep_info, PARAM_EP,
VERSION_2, entry_point_info_t,
SECURE | EXECUTABLE | EP_FIRST_EXE),
.ep_info.pc = BL31_BASE,
.ep_info.spsr = SPSR_64(MODE_EL3, MODE_SP_ELX,
DISABLE_ALL_EXCEPTIONS),
#if DEBUG
.ep_info.args.arg1 = ARM_BL31_PLAT_PARAM_VAL,
#endif
SET_STATIC_PARAM_HEAD(image_info, PARAM_EP,
VERSION_2, image_info_t, IMAGE_ATTRIB_PLAT_SETUP),
.image_info.image_base = BL31_BASE,
.image_info.image_max_size = BL31_LIMIT - BL31_BASE,
# ifdef BL32_BASE
.next_handoff_image_id = BL32_IMAGE_ID,
# else
.next_handoff_image_id = BL33_IMAGE_ID,
# endif
},
# ifdef BL32_BASE
/* Fill BL32 related information */
{
.image_id = BL32_IMAGE_ID,
SET_STATIC_PARAM_HEAD(ep_info, PARAM_EP,
VERSION_2, entry_point_info_t, SECURE | EXECUTABLE),
.ep_info.pc = BL32_BASE,
SET_STATIC_PARAM_HEAD(image_info, PARAM_EP,
VERSION_2, image_info_t, 0),
.image_info.image_base = BL32_BASE,
.image_info.image_max_size = BL32_LIMIT - BL32_BASE,
.next_handoff_image_id = BL33_IMAGE_ID,
},
# endif /* BL32_BASE */
/* Fill BL33 related information */
{
.image_id = BL33_IMAGE_ID,
SET_STATIC_PARAM_HEAD(ep_info, PARAM_EP,
VERSION_2, entry_point_info_t, NON_SECURE | EXECUTABLE),
# ifdef PRELOADED_BL33_BASE
.ep_info.pc = PRELOADED_BL33_BASE,
SET_STATIC_PARAM_HEAD(image_info, PARAM_EP,
VERSION_2, image_info_t, IMAGE_ATTRIB_SKIP_LOADING),
# else
.ep_info.pc = PLAT_ARM_NS_IMAGE_OFFSET,
SET_STATIC_PARAM_HEAD(image_info, PARAM_EP,
VERSION_2, image_info_t, 0),
.image_info.image_base = PLAT_ARM_NS_IMAGE_OFFSET,
.image_info.image_max_size = ARM_DRAM1_SIZE,
# endif /* PRELOADED_BL33_BASE */
.next_handoff_image_id = INVALID_IMAGE_ID,
}
#endif /* EL3_PAYLOAD_BASE */
};
REGISTER_BL_IMAGE_DESCS(bl2_mem_params_descs)
/*******************************************************************************
* This function loads SCP_BL2/BL3x images and returns the ep_info for
* the next executable image.
******************************************************************************/
entry_point_info_t *bl2_load_images(void)
{
bl_params_t *bl2_to_next_bl_params;
bl_load_info_t *bl2_load_info;
const bl_load_info_node_t *bl2_node_info;
int plat_setup_done = 0;
int err;
/*
* Get information about the images to load.
*/
bl2_load_info = plat_get_bl_image_load_info();-----------------获取待加载镜像BL3x或SCP_EL2信息,然后遍历处理。
assert(bl2_load_info);
assert(bl2_load_info->head);
assert(bl2_load_info->h.type == PARAM_BL_LOAD_INFO);
assert(bl2_load_info->h.version >= VERSION_2);
bl2_node_info = bl2_load_info->head;---------------------------bl2_node_info指向镜像第一个对象。
while (bl2_node_info) {----------------------------------------循环遍历bl2_mem_params_descs成员并加载处理。
/*
* Perform platform setup before loading the image,
* if indicated in the image attributes AND if NOT
* already done before.
*/
if (bl2_node_info->image_info->h.attr & IMAGE_ATTRIB_PLAT_SETUP) {----确定加载前是否需要进行特定平台初始化。
if (plat_setup_done) {
WARN("BL2: Platform setup already done!!\n");
} else {
INFO("BL2: Doing platform setup\n");
bl2_platform_setup();
plat_setup_done = 1;
}
}
if (!(bl2_node_info->image_info->h.attr & IMAGE_ATTRIB_SKIP_LOADING)) {----确定是否需要跳过加载到RAM步骤;如否,则进行验证并加载。
INFO("BL2: Loading image id %d\n", bl2_node_info->image_id);
err = load_auth_image(bl2_node_info->image_id,
bl2_node_info->image_info);----------------------------------------将镜像加载到RAM,然后进行验证。
if (err) {
ERROR("BL2: Failed to load image (%i)\n", err);
plat_error_handler(err);
}
} else {
INFO("BL2: Skip loading image id %d\n", bl2_node_info->image_id);
}
/* Allow platform to handle image information. */
err = bl2_plat_handle_post_image_load(bl2_node_info->image_id);-------------修改特定镜像的加载信息。
if (err) {
ERROR("BL2: Failure in post image load handling (%i)\n", err);
plat_error_handler(err);
}
/* Go to next image */
bl2_node_info = bl2_node_info->next_load_info;------------------------------循环加载下一个镜像。
}
/*
* Get information to pass to the next image.
*/
bl2_to_next_bl_params = plat_get_next_bl_params();------------------------------获取下一个执行镜像入口信息,属性为EXECUTABLE和EP_FIRST_EXE,也即BL31。
assert(bl2_to_next_bl_params);
assert(bl2_to_next_bl_params->head);
assert(bl2_to_next_bl_params->h.type == PARAM_BL_PARAMS);
assert(bl2_to_next_bl_params->h.version >= VERSION_2);
/* Flush the parameters to be passed to next image */
plat_flush_next_bl_params();----------------------------------------------------将bl_mem_params_desc_ptr数据刷到DDR中,后面即将通过SMC跳转到BL1启动BL31,保持数据一致性。
return bl2_to_next_bl_params->head->ep_info;
}
参考资料:
https://www.cnblogs.com/arnoldlu/p/14175126.html#REGISTER_BL_IMAGE_DESCS
https://icyshuai.blog.csdn.net/article/details/72470044