赞
踩
关于平台相关的代码和函数均以qemu实现解读。
BL1是启动的第一阶段,复位的入口函数为bl1_entrypoint
,这可以从bl1.ld.S
链接脚本中ENTRY标号定义看出,即ENTRY(bl1_entrypoint)
。其主要执行BL1初始化,平台初始化,加载下一阶段镜像,以及跳转到下一阶段执行,大致的函数执行流程如下。
el3_entrypoint_common是定义的一个宏,它是EL3等级下执行的入口通用函数,其实现位于el3_common_macros.S
,主要完成C语言运行环境的搭建,异常向量表的注册,bl1镜像文件的复制,CPU安全运行环境的设定。
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
plat_get_my_entrypoint
函数返回非0地址,执行热启动直接跳转到该地址,否则执行冷启动。platform_mem_init
函数。bl1_exceptions.S
中,可以看出只支持SMC异常的处理smc_handler64
,其他都会panic。bl1_setup是BL1建立初始化,包括两个函数:bl1_early_platform_setup()
和bl1_plat_arch_setup()
。
bl1_early_platform_setup
void bl1_early_platform_setup(void)
{
/* Initialize the console to provide early debug support */
qemu_console_init();
/* Allow BL1 to see the whole Trusted RAM */
bl1_tzram_layout.total_base = BL_RAM_BASE;
bl1_tzram_layout.total_size = BL_RAM_SIZE;
}
bl1_plat_arch_setup
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);
}
该函数主要是建立MMU页表,并且使能dcache。QEMU_CONFIGURE_BL1_MMU
会调用到DEFINE_CONFIGURE_MMU_EL(el1)
,该宏的展开如下。
#define DEFINE_CONFIGURE_MMU_EL(_el) \ void qemu_configure_mmu_##_el(unsigned long total_base, \ unsigned long total_size, \ unsigned long code_start, \ unsigned long code_limit, \ unsigned long ro_start, \ unsigned long ro_limit, \ unsigned long coh_start, \ unsigned long coh_limit) \ { \ mmap_add_region(total_base, total_base, \ total_size, \ MT_MEMORY | MT_RW | MT_SECURE); \ mmap_add_region(code_start, code_start, \ code_limit - code_start, \ MT_CODE | MT_SECURE); \ mmap_add_region(ro_start, ro_start, \ ro_limit - ro_start, \ MT_RO_DATA | MT_SECURE); \ mmap_add_region(coh_start, coh_start, \ coh_limit - coh_start, \ MT_DEVICE | MT_RW | MT_SECURE); \ mmap_add(plat_qemu_mmap); \ init_xlat_tables(); \ \ enable_mmu_##_el(0); \ }
bl1_main函数主要完成bl2镜像的加载及其运行环境的配置,如果启用安全启动,还需要对bl2镜像进行验签操作。
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(); /* Perform remaining generic architectural setup from EL3 */ bl1_arch_setup(); crypto_mod_init(); /* Initialize authentication module */ auth_mod_init(); /* Initialize the measured boot */ bl1_plat_mboot_init(); /* Perform platform setup in BL1. */ bl1_platform_setup(); /* 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"); /* Teardown the measured boot driver */ bl1_plat_mboot_finish(); bl1_prepare_next_image(image_id); console_flush(); }
打印欢迎词,版本信息和编译时间。
bl1_arch_setup:执行EL3等级的通用架构初始化,实际调用write_scr_el3(read_scr_el3() | SCR_RW_BIT);
将下一异常等级设置为AArch64。
crypto_mod_init:初始化安全启动需要密码库,这里使用的mbedtls。
auth_mod_init:认证模块初始化,这里调用img_parser_init
初始化镜像解析模块,镜像完整性检查是通过X509v3证书来实现,其会存储镜像哈希和公钥。
bl1_plat_mboot_init:measured boot初始化,其是对各个软件组件的度量和验证。
bl1_platform_setup:平台初始化,这里是调用plat_qemu_io_setup
,主要是qemu存储驱动初始化,为后面加载镜像做准备。ATF抽象出一组IO接口,定义标准的打开、关闭、读取和写入等操作,关于存储抽象层,后面单独来讲。这里qemu的IO初始化注册了fip设备和memmap设备,所有的BLx镜像均来自FIP固件,而FIP又是来自memmap设备,对于真实的芯片是fip固件是存储在如nand、sd等介质中。另外这里qemu还注册了半主机设备,即可以通过读文件的方式加载镜像。
void plat_qemu_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); /* Register the additional IO devices on this platform */ io_result = register_io_dev_sh(&sh_dev_con); assert(io_result == 0); /* Open connections to devices and cache the handles */ io_result = io_dev_open(sh_dev_con, (uintptr_t)NULL, &sh_dev_handle); assert(io_result == 0); /* Ignore improbable errors in release builds */ (void)io_result; }
bl1_plat_get_next_image_id:获取下一阶段的镜像ID,这里ID是BL2_IMAGE_ID。
bl1_load_bl2:加载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"); }
bl1_plat_get_image_desc:获取BL2镜像描述信息,主要定义BL2镜像地址、镜像最大大小、镜像是安全状态并且需要执行,具体如下
#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, \ } // 展开如下 image_desc_t bl2_img_desc = { .image_id = BL2_IMAGE_ID, // 镜像ID .image_info.h.type = PARAM_EP, // 镜像信息类型 .image_info.h.version = VERSION_2, // 镜像信息版本 .image_info.h.size = sizeof(image_info_t), // 镜像信息大小 .image_info.h.attr = 0, // 镜像属性 .image_info.image_base = BL2_BASE, // 镜像地址 .image_info.image_max_size = BL2_LIMIT - BL2_BASE, // 镜像大小 .ep_info.h.type = PARAM_EP, // 跳转信息类型 .ep_info.h.version = VERSION_2, // 跳转信息版本 .ep_info.h.size = sizeof(entry_point_info_t), // 跳转信息大小 .ep_info.h.attr = SECURE | EXECUTABLE, // 跳转信息属性:镜像是安全状态并且可执行 }
bl1_plat_handle_pre_image_load:加载之前的镜像预处理,qemu未处理
load_auth_image:加载并认证镜像,主要是调用load_auth_image_internal
这个函数,对于安全启动会递归加载并认证镜像,否则直接加载镜像。
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(image_id, image_data);
}
load_image:普通启动加载镜像。首先获取镜像源,然后通过一些列IO操作完成对BL2镜像的加载。
static int load_image(unsigned int image_id, image_info_t *image_data) { uintptr_t dev_handle; uintptr_t image_handle; uintptr_t image_spec; uintptr_t image_base; size_t image_size; size_t bytes_read; int io_result; assert(image_data != NULL); assert(image_data->h.version >= VERSION_2); image_base = image_data->image_base; /* Obtain a reference to the image by querying the platform layer */ io_result = plat_get_image_source(image_id, &dev_handle, &image_spec); if (io_result != 0) { WARN("Failed to obtain reference to image id=%u (%i)\n", image_id, io_result); return io_result; } /* Attempt to access the image */ io_result = io_open(dev_handle, image_spec, &image_handle); if (io_result != 0) { WARN("Failed to access image id=%u (%i)\n", image_id, io_result); return io_result; } INFO("Loading image id=%u at address 0x%lx\n", image_id, image_base); /* Find the size of the image */ io_result = io_size(image_handle, &image_size); if ((io_result != 0) || (image_size == 0U)) { WARN("Failed to determine the size of the image id=%u (%i)\n", image_id, io_result); goto exit; } /* Check that the image size to load is within limit */ if (image_size > image_data->image_max_size) { WARN("Image id=%u size out of bounds\n", image_id); io_result = -EFBIG; goto exit; } /* * image_data->image_max_size is a uint32_t so image_size will always * fit in image_data->image_size. */ image_data->image_size = (uint32_t)image_size; /* We have enough space so load the image now */ /* TODO: Consider whether to try to recover/retry a partially successful read */ io_result = io_read(image_handle, image_base, image_size, &bytes_read); if ((io_result != 0) || (bytes_read < image_size)) { WARN("Failed to load image id=%u (%i)\n", image_id, io_result); goto exit; } INFO("Image id=%u loaded: 0x%lx - 0x%lx\n", image_id, image_base, (uintptr_t)(image_base + image_size)); exit: (void)io_close(image_handle); /* Ignore improbable/unrecoverable error in 'close' */ /* TODO: Consider maintaining open device connection from this bootloader stage */ (void)io_dev_close(dev_handle); /* Ignore improbable/unrecoverable error in 'dev_close' */ return io_result; }
load_auth_image_recursive:安全启动加载镜像,这里首先会获取BL2镜像的父镜像,这里即可信启动证书镜像,加载完证书镜像并对证书进行签名认证,然后才加载BL2镜像,最后对BL2镜像进行认证。关于认证方式及信任链再后续再单独来讲。
static int load_auth_image_recursive(unsigned int image_id, image_info_t *image_data, int is_parent_image) { int rc; unsigned int parent_id; /* Use recursion to authenticate parent images */ rc = auth_mod_get_parent_id(image_id, &parent_id); if (rc == 0) { rc = load_auth_image_recursive(parent_id, image_data, 1); if (rc != 0) { return rc; } } /* Load the image */ rc = load_image(image_id, image_data); if (rc != 0) { return rc; } /* Authenticate it */ rc = auth_mod_verify_img(image_id, (void *)image_data->image_base, image_data->image_size); if (rc != 0) { /* Authentication error, zero memory and flush it right away. */ zero_normalmem((void *)image_data->image_base, image_data->image_size); flush_dcache_range(image_data->image_base, image_data->image_size); return -EAUTH; } return 0; }
bl1_plat_handle_post_image_load:镜像平台后处理:获取BL2镜像描述符,获取跳转入口信息,计算BL2的可用内存,其等于总的内存减去BL1已使用的sram内存,最后将BL2的内存信息通过arg1参数传递给BL2。
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; /* Find out how much free trusted ram remains after BL1 load */ bl1_secram_layout = bl1_plat_sec_mem_layout(); /* * 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); ep_info->args.arg1 = (uintptr_t)bl2_secram_layout; VERBOSE("BL1: BL2 memory layout address = %p\n", (void *) bl2_secram_layout); return 0; }
bl1_plat_mboot_finish:卸载mesured boot驱动
bl1_prepare_next_image:下一阶段镜像启动准备,主要是准备安全世界和普通世界镜像的上下文,普通世界如果支持EL2,则镜像跳转到EL2,否则跳转到EL1
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; /* 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; /* Get the image security state. */ security_state = GET_SECURITY_STATE(next_bl_ep->h.attr); /* 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); /* Prepare the SPSR for the next BL image. */ 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. */ cm_init_my_context(next_bl_ep); cm_prepare_el3_exit(security_state); /* Indicate that image is in execution state. */ desc->state = IMAGE_STATE_EXECUTED; print_entry_point_info(next_bl_ep); }
console_flush:退出BL1前,刷新串口中的所有数据
该函数位于context.S
文件,执行实际的异常等级切换l流程。
func el3_exit /* ---------------------------------------------------------- * 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 msr spsel, #MODE_SP_ELX str x17, [sp, #CTX_EL3STATE_OFFSET + CTX_RUNTIME_SP] /* ---------------------------------------------------------- * 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] 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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。