3. bl1_main
void bl1_main(void)
{
unsigned int image_id;
/* Announce our arrival */
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;
/*
* Ensure that MMU/Caches and coherency are turned on
*/
#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);
/*
* Check that Cache Writeback Granule (CWG) in CTR_EL0 matches the
* provided platform value
*/
val = (read_ctr_el0() >> CTR_CWG_SHIFT) & CTR_CWG_MASK;
/*
* If CWG is zero, then no CWG information is available but we can
* at least check the platform value is less than the architectural
* maximum.
*/
if (val != 0)
assert(CACHE_WRITEBACK_GRANULE == SIZE_FROM_LOG2_WORDS(val));
else
assert(CACHE_WRITEBACK_GRANULE <= MAX_CACHE_LINE_SIZE);
#endif /* ENABLE_ASSERTIONS */
/* Perform remaining generic architectural setup from EL3 */
bl1_arch_setup();
#if TRUSTED_BOARD_BOOT
/* Initialize authentication module */
auth_mod_init();
#endif /* TRUSTED_BOARD_BOOT */
/* Perform platform setup in BL1. */
bl1_platform_setup();
#if ENABLE_PAUTH
/* Store APIAKey_EL1 key */
bl1_apiakey[0] = read_apiakeylo_el1();
bl1_apiakey[1] = read_apiakeyhi_el1();
#endif /* ENABLE_PAUTH */
/* Get the image id of next image to load and run. */
image_id = bl1_plat_get_next_image_id();
/*
* We currently interpret any image id other than
* BL2_IMAGE_ID as the start of firmware update.
*/
if (image_id == BL2_IMAGE_ID)
bl1_load_bl2();
else
NOTICE("BL1-FWU: *******FWU Process Started*******\n");
bl1_prepare_next_image(image_id);
console_flush();
}
前边NOTICE是一些打印消息,然后如果使能ENABLE_ASSERTIONS,那么就检查一些sctlr_el3和ctr_el0的寄存器设置。
3.1 bl1_arch_setup
/*******************************************************************************
* Function that does the first bit of architectural setup that affects
* execution in the non-secure address space.
******************************************************************************/
void bl1_arch_setup(void)
{
/* Set the next EL to be AArch64 */
write_scr_el3(read_scr_el3() | SCR_RW_BIT);
}
将下一个异常等级的执行状态设置为aarch64
3.2 auth_mod_init
/*
* Initialize the different modules in the authentication framework
*/
void auth_mod_init(void)
{
/* Check we have a valid CoT registered */
assert(cot_desc_ptr != NULL);
/* Crypto module */
crypto_mod_init();
/* Image parser module */
img_parser_init();
}
(1)初始化签名验证所需的密码库
(2)初始化获取镜像签名信息模块
3.3 bl1_platform_setup
void marvell_bl1_platform_setup(void)
{
/* Initialise the IO layer and register platform IO devices */
plat_marvell_io_setup();
}
void bl1_platform_setup(void)
{
marvell_bl1_platform_setup();
}
void marvell_io_setup(void)
{
int io_result;
io_result = register_io_dev_fip(&fip_dev_con);
assert(io_result == 0);
io_result = register_io_dev_memmap(&memmap_dev_con);
assert(io_result == 0);
/* Open connections to devices and cache the handles */
io_result = io_dev_open(fip_dev_con, (uintptr_t)NULL,
&fip_dev_handle);
assert(io_result == 0);
io_result = io_dev_open(memmap_dev_con, (uintptr_t)NULL,
&memmap_dev_handle);
assert(io_result == 0);
/* Ignore improbable errors in release builds */
(void)io_result;
}
void plat_marvell_io_setup(void)
{
marvell_io_setup();
}
这一块主要是io的一些相关操作,这里又牵扯到atf对io的driver的抽象,所以暂时先不分析了。
3.4 bl1_plat_get_next_image_id
unsigned int bl1_plat_get_next_image_id(void)
{
/* BL2 load will be done by default. */
return BL2_IMAGE_ID;
}
#define BL2_IMAGE_ID U(1)
3.5 bl1_load_bl2
static void bl1_load_bl2(void)
{
image_desc_t *desc;
image_info_t *info;
int err;
/* Get the image descriptor */
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");
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);
}
/* Allow platform to handle image information. */
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");
}
3.5.1 bl1_plat_get_image_desc
struct image_desc *bl1_plat_get_image_desc(unsigned int image_id)
{
static image_desc_t bl2_img_desc = BL2_IMAGE_DESC;
return &bl2_img_desc;
}
#define BL2_IMAGE_DESC { \
.image_id = BL2_IMAGE_ID, \
SET_STATIC_PARAM_HEAD(image_info, PARAM_EP, \
VERSION_2, image_info_t, 0), \
.image_info.image_base = BL2_BASE, \
.image_info.image_max_size = BL2_LIMIT - BL2_BASE,\
SET_STATIC_PARAM_HEAD(ep_info, PARAM_EP, \
VERSION_2, entry_point_info_t, SECURE | EXECUTABLE),\
.ep_info.pc = BL2_BASE, \
}
在atf中,镜像描述信息主要包含镜像id、镜像加载器使用的信息image_info和镜像跳转时使用的信息ep_info,其结构如下:
bl1_plat_get_image_desc用于获取bl2镜像的信息
3.5.2 bl1_plat_handle_pre_image_load
这里没啥用,直接反返回了
int bl1_plat_handle_pre_image_load(unsigned int image_id)
{
return 0;
}
3.5.3 load_auth_image
从storage中加载镜像,它会根据先前获取到的bl2镜像描述信息,从storage中将镜像数据加载到给定地址上。qemu支持fip和semihosting类型的加载方式
int load_auth_image(unsigned int image_id, image_info_t *image_data)
{
int err;
do {
err = load_auth_image_internal(image_id, image_data);
} while ((err != 0) && (plat_try_next_boot_source() != 0));
return err;
}
static int load_auth_image_internal(unsigned int image_id,
image_info_t *image_data)
{
#if TRUSTED_BOARD_BOOT
if (dyn_is_auth_disabled() == 0) {
return load_auth_image_recursive(image_id, image_data, 0);
}
#endif
return load_image_flush(image_id, image_data);
}
static int load_image_flush(unsigned int image_id,
image_info_t *image_data)
{
int rc;
rc = load_image(image_id, image_data);
if (rc == 0) {
flush_dcache_range(image_data->image_base,
image_data->image_size);
}
return rc;
}
3.5.3.1 load_image
image_base = image_data->image_base;//获取image——base
io_result = io_open(dev_handle, image_spec, &image_handle);//连接设备
io_result = io_size(image_handle, &image_size);//读取镜像大小
if (image_size > image_data->image_max_size) //判断大小是否超出限制
io_result = io_read(image_handle, image_base, image_size, &bytes_read);//读镜像到image_base
3.5.3.2 flush_dcache_range
有可能读到的数据还在dcache里面,刷进sram里面。
/*
* This macro can be used for implementing various data cache operations `op`
*/
.macro do_dcache_maintenance_by_mva op
/* Exit early if size is zero */
cbz x1, exit_loop_\op
dcache_line_size x2, x3
add x1, x0, x1
sub x3, x2, #1
bic x0, x0, x3
loop_\op:
dc \op, x0
add x0, x0, x2
cmp x0, x1
b.lo loop_\op
dsb sy
exit_loop_\op:
ret
.endm
/* ------------------------------------------
* Clean+Invalidate from base address till
* size. 'x0' = addr, 'x1' = size
* ------------------------------------------
*/
func flush_dcache_range
do_dcache_maintenance_by_mva civac
endfunc flush_dcache_range
3.5.4 bl1_plat_handle_post_image_load
它主要用于设置bl1向bl2传递的参数,上面结构体中的args即用于该目的,它一共包括8个参数,在bl1跳转到bl2之前会分别被设置到x0 – x7寄存器中。bl1只需通过x1寄存器向bl2传送其可用的secure内存region即可。以下为其代码主体流程:
int bl1_plat_handle_post_image_load(unsigned int image_id)
{
meminfo_t *bl2_secram_layout;
meminfo_t *bl1_secram_layout;
image_desc_t *image_desc;
entry_point_info_t *ep_info;
if (image_id != BL2_IMAGE_ID)
return 0;
/* Get the image descriptor */
image_desc = bl1_plat_get_image_desc(BL2_IMAGE_ID);
assert(image_desc != NULL);
/* Get the entry point info */
ep_info = &image_desc->ep_info;//获取bl2的ep信息
/* Find out how much free trusted ram remains after BL1 load */
bl1_secram_layout = bl1_plat_sec_mem_layout();//获取bl1的secure内存region
/*
* Create a new layout of memory for BL2 as seen by BL1 i.e.
* tell it the amount of total and free memory available.
* This layout is created at the first free address visible
* to BL2. BL2 will read the memory layout before using its
* memory for other purposes.
*/
bl2_secram_layout = (meminfo_t *) bl1_secram_layout->total_base;
bl1_calc_bl2_mem_layout(bl1_secram_layout, bl2_secram_layout);//将总的内存减去bl1已使用的sram内存,作为bl2的可用内存
ep_info->args.arg1 = (uintptr_t)bl2_secram_layout;//将bl2的可用内存信息保存到参数传递信息中
VERBOSE("BL1: BL2 memory layout address = %p\n",
(void *) bl2_secram_layout);
return 0;
}
3.6 bl1_prepare_next_image
void bl1_prepare_next_image(unsigned int image_id)
{
/*
* Following array will be used for context management.
* There are 2 instances, for the Secure and Non-Secure contexts.
*/
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
/*
* Ensure that the build flag to save AArch32 system registers in CPU
* context is not set for AArch64-only platforms.
*/
if (el_implemented(1) == EL_IMPL_A64ONLY) {
ERROR("EL1 supports AArch64-only. Please set build flag "
"CTX_INCLUDE_AARCH32_REGS = 0\n");
panic();
}
#endif
/* Get the image descriptor. */
desc = bl1_plat_get_image_desc(image_id);
assert(desc != NULL);
/* Get the entry point info. */
next_bl_ep = &desc->ep_info;//获取bl2的ep信息
/* Get the image security state. */
security_state = GET_SECURITY_STATE(next_bl_ep->h.attr);//从bl2的ep信息中获取其security状态
/* Setup the Secure/Non-Secure context if not done already. */
if (cm_get_context(security_state) == NULL)
cm_set_context(&bl1_cpu_context[security_state], security_state);//若context内存未分配,则为其分配内存
/* Prepare the SPSR for the next BL image. */
if ((security_state != SECURE) && (el_implemented(2) != EL_IMPL_NONE)) {
mode = MODE_EL2; //默认的下一阶段镜像异常等级为其支持的最高等级,即若支持el2,则下一异常等级为EL2
}
//计算spsr的值,即异常等级为step 4计算的值,栈指针使用sp_elx,关闭所有DAIF异常
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. */
cm_init_my_context(next_bl_ep);//该函数为待切换异常等级初始化上下文,如scr_el3,scr_el3,pc,spsr以及参数传递寄存器x0 – x7的值
cm_prepare_el3_exit(security_state);//将context中参数设置到实际的寄存器中
/* Indicate that image is in execution state. */
desc->state = IMAGE_STATE_EXECUTED;
print_entry_point_info(next_bl_ep);
}
4.el3_exit
func el3_exit
#if ENABLE_ASSERTIONS
/* el3_exit assumes SP_EL0 on entry */
mrs x17, spsel
cmp x17, #MODE_SP_EL0
ASM_ASSERT(eq)
#endif
/* ----------------------------------------------------------
* Save the current SP_EL0 i.e. the EL3 runtime stack which
* will be used for handling the next SMC.
* Then switch to SP_EL3.
* ----------------------------------------------------------
*/
mov x17, sp //将sp_el0栈指针暂存到x17寄存器中
msr spsel, #MODE_SP_ELX //将栈指针切换到sp_el3,其中sp_el3指向前面context的el3state_ctx指针,即它被用于保存el3的上下文
str x17, [sp, #CTX_EL3STATE_OFFSET + CTX_RUNTIME_SP]//从el3 context中加载scr_el3、spsr_el3和elr_el3寄存器的值
/* ----------------------------------------------------------
* Restore SPSR_EL3, ELR_EL3 and SCR_EL3 prior to ERET
* ----------------------------------------------------------
*/
ldr x18, [sp, #CTX_EL3STATE_OFFSET + CTX_SCR_EL3]
ldp x16, x17, [sp, #CTX_EL3STATE_OFFSET + CTX_SPSR_EL3]
//设置scr_el3、spsr_el3和elr_el3寄存器
msr scr_el3, x18
msr spsr_el3, x16
msr elr_el3, x17
#if IMAGE_BL31 && DYNAMIC_WORKAROUND_CVE_2018_3639
/* ----------------------------------------------------------
* Restore mitigation state as it was on entry to EL3
* ----------------------------------------------------------
*/
ldr x17, [sp, #CTX_CVE_2018_3639_OFFSET + CTX_CVE_2018_3639_DISABLE]
cbz x17, 1f
blr x17
1:
#endif
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 //恢复gp寄存器等寄存器的值
ldr x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
#if IMAGE_BL31 && RAS_EXTENSION
/* ----------------------------------------------------------
* Issue Error Synchronization Barrier to synchronize SErrors
* before exiting EL3. We're running with EAs unmasked, so
* any synchronized errors would be taken immediately;
* therefore no need to inspect DISR_EL1 register.
* ----------------------------------------------------------
*/
esb
#else
dsb sy
#endif
#ifdef IMAGE_BL31
str xzr, [sp, #CTX_EL3STATE_OFFSET + CTX_IS_IN_EL3]
#endif
exception_return //执行eret指令,此后cpu将离开bl1跳转到bl2的入口处执行了
endfunc el3_exit