(06)ATF代码导读之BL31

代码导读

关于平台相关的代码和函数均以qemu实现解读。

BL31

在BL2中触发安全监控模式调用后会跳转到BL31中执行,同理复位的入口函数为bl31_entrypoint。BL31最主要的两个功能:作为启动流程,初始化硬件和加载BL32、BL31等;启动完成后常驻内存,处理各种SMC异常和路由到EL3中断。启动大致的函数执行流程如下。

bl1_entrypoint
el3_entrypoint_common
bl31_setup
bl31_main
clean_dcache_range
el3_exit

el3_entrypoint_common

同BL2,现将前面阶段传递过来的x0-x3参数保存起来。

/* ---------------------------------------------------------------
 * Stash the previous bootloader arguments x0 - x3 for later use.
 * ---------------------------------------------------------------
 */
mov	x20, x0
mov	x21, x1
mov	x22, x2
mov	x23, x3

接着同BL1一样,调用el3_entrypoint_common函数。这里根据RESET_TO_BL31是否定义分为两种情况:

  • RESET_TO_BL31=0:复位后不是从BL31开始启动,即会按照TF-A的标准启动方式,从BL1,BL2,到BL31启动,由于系统的基本配置已经设置完成,只需要设置C运行环境,异常向量表,地址无关大小。
  • RESET_TO_BL31=1:复位后直接从BL31开始启动,同BL1一样,需要初始化系统控制器,冷热启动处理,初始化内存,初始化C运行环境,异常向量表,地址无关大小。最后把x20-x23清零以便后续使用。
#if !RESET_TO_BL31
/* ---------------------------------------------------------------------
 * For !RESET_TO_BL31 systems, only the primary CPU ever reaches
 * bl31_entrypoint() during the cold boot flow, so the cold/warm boot
 * and primary/secondary CPU logic should not be executed in this case.
 *
 * Also, assume that the previous bootloader has already initialised the
 * SCTLR_EL3, including the endianness, and has initialised the memory.
 * ---------------------------------------------------------------------
 */
el3_entrypoint_common					\
    _init_sctlr=0					\
    _warm_boot_mailbox=0				\
    _secondary_cold_boot=0				\
    _init_memory=0					\
    _init_c_runtime=1				\
    _exception_vectors=runtime_exceptions		\
    _pie_fixup_size=BL31_LIMIT - BL31_BASE
#else

/* ---------------------------------------------------------------------
 * For RESET_TO_BL31 systems which have a programmable reset address,
 * bl31_entrypoint() is executed only on the cold boot path so we can
 * skip the warm boot mailbox mechanism.
 * ---------------------------------------------------------------------
 */
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=runtime_exceptions		\
    _pie_fixup_size=BL31_LIMIT - BL31_BASE

#if !RESET_TO_BL31_WITH_PARAMS
/* ---------------------------------------------------------------------
 * For RESET_TO_BL31 systems, BL31 is the first bootloader to run so
 * there's no argument to relay from a previous bootloader. Zero the
 * arguments passed to the platform layer to reflect that.
 * ---------------------------------------------------------------------
 */
mov	x20, 0
mov	x21, 0
mov	x22, 0
mov	x23, 0
#endif /* RESET_TO_BL31_WITH_PARAMS */
#endif /* RESET_TO_BL31 */

bl31_setup

同BL2一样,bl31_setup是BL31建立初始化,包括两个函数:bl31_early_platform_setup2()bl31_plat_arch_setup()

void bl31_setup(u_register_t arg0, u_register_t arg1, u_register_t arg2,
	       u_register_t arg3)
{
	/* Perform early platform-specific setup */
	bl31_early_platform_setup2(arg0, arg1, arg2, arg3);

	/* Perform late platform-specific setup */
	bl31_plat_arch_setup();
}
  1. bl31_early_platform_setup2

    首先初始化打印串口。接着获取存储在BL2安全RAM中,通过arg0传递过来的参数即镜像描述参数指针。最后遍历镜像链表,将BL32(若有)和BL33的跳转信息存储在全局变量中,并判断BL33跳转PC是否为空。

    void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1,
    				u_register_t arg2, u_register_t arg3)
    {
    	/* Initialize the console to provide early debug support */
    	qemu_console_init();
    
    	/*
    	 * Check params passed from BL2
    	 */
    	bl_params_t *params_from_bl2 = (bl_params_t *)arg0;
    
    	assert(params_from_bl2);
    	assert(params_from_bl2->h.type == PARAM_BL_PARAMS);
    	assert(params_from_bl2->h.version >= VERSION_2);
    
    	bl_params_node_t *bl_params = params_from_bl2->head;
    
    	/*
    	 * Copy BL33 and BL32 (if present), entry point information.
    	 * They are stored in Secure RAM, in BL2's address space.
    	 */
    	while (bl_params) {
    		if (bl_params->image_id == BL32_IMAGE_ID)
    			bl32_image_ep_info = *bl_params->ep_info;
    
    		if (bl_params->image_id == BL33_IMAGE_ID)
    			bl33_image_ep_info = *bl_params->ep_info;
    
    		bl_params = bl_params->next_params_info;
    	}
    
    	if (!bl33_image_ep_info.pc)
    		panic();
    }
    
  2. bl31_plat_arch_setup

    同BL1一样,配置MMU,建立MMU页表,使能dcache。

    void bl31_plat_arch_setup(void)
    {
    	qemu_configure_mmu_el3(BL31_BASE, (BL31_END - BL31_BASE),
    			      BL_CODE_BASE, BL_CODE_END,
    			      BL_RO_DATA_BASE, BL_RO_DATA_END,
    			      BL_COHERENT_RAM_BASE, BL_COHERENT_RAM_END);
    }
    

bl31_main

bl31_main主要执行平台初始化,运行时服务初始化,启动BL32和BL33镜像。

void bl31_main(void)
{
	NOTICE("BL31: %s\n", version_string);
	NOTICE("BL31: %s\n", build_message);

	/* Perform platform setup in BL31 */
	bl31_platform_setup();

	/* Initialise helper libraries */
	bl31_lib_init();

#if EL3_EXCEPTION_HANDLING
	INFO("BL31: Initialising Exception Handling Framework\n");
	ehf_init();
#endif

	/* Initialize the runtime services e.g. psci. */
	INFO("BL31: Initializing runtime services\n");
	runtime_svc_init();

	/*
	 * All the cold boot actions on the primary cpu are done. We now need to
	 * decide which is the next image and how to execute it.
	 * If the SPD runtime service is present, it would want to pass control
	 * to BL32 first in S-EL1. In that case, SPD would have registered a
	 * function to initialize bl32 where it takes responsibility of entering
	 * S-EL1 and returning control back to bl31_main. Similarly, if RME is
	 * enabled and a function is registered to initialize RMM, control is
	 * transferred to RMM in R-EL2. After RMM initialization, control is
	 * returned back to bl31_main. Once this is done we can prepare entry
	 * into BL33 as normal.
	 */

	/*
	 * If SPD had registered an init hook, invoke it.
	 */
	if (bl32_init != NULL) {
		INFO("BL31: Initializing BL32\n");

		int32_t rc = (*bl32_init)();

		if (rc == 0) {
			WARN("BL31: BL32 initialization failed\n");
		}
	}

	/*
	 * We are ready to enter the next EL. Prepare entry into the image
	 * corresponding to the desired security state after the next ERET.
	 */
	bl31_prepare_next_image_entry();

	console_flush();

	/*
	 * Perform any platform specific runtime setup prior to cold boot exit
	 * from BL31
	 */
	bl31_plat_runtime_setup();
}
  1. bl31_platform_setup:BL31平台初始化。

    void bl31_platform_setup(void)
    {
    	plat_qemu_gic_init();
    	qemu_gpio_init();
    }
    
    • plat_qemu_gic_init:初始化gic中断管理。

    • qemu_gpio_init:初始化qemu平台的pl061 gpio,设置基地址和回调函数。

  2. bl31_lib_init:初始化帮助库,调用了cm_init上下文管理初始化,但是里面是空函数。

  3. ehf_init:初始化异常处理框架。首先设置非安全路由到EL3的中断标志,然后设置bl31顶级中断处理函数。

    void __init ehf_init(void)
    {
    	unsigned int flags = 0;
    	int ret __unused;
    
    	/* Ensure EL3 interrupts are supported */
    	assert(plat_ic_has_interrupt_type(INTR_TYPE_EL3) != 0);
    
    	/*
    	 * Make sure that priority water mark has enough bits to represent the
    	 * whole priority array.
    	 */
    	assert(exception_data.num_priorities <= (sizeof(ehf_pri_bits_t) * 8U));
    
    	assert(exception_data.ehf_priorities != NULL);
    
    	/*
    	 * Bit 7 of GIC priority must be 0 for secure interrupts. This means
    	 * platforms must use at least 1 of the remaining 7 bits.
    	 */
    	assert((exception_data.pri_bits >= 1U) ||
    			(exception_data.pri_bits < 8U));
    
    	/* Route EL3 interrupts when in Non-secure. */
    	set_interrupt_rm_flag(flags, NON_SECURE);
    
    	/*
    	 * Route EL3 interrupts when in secure, only when SPMC is not present
    	 * in S-EL2.
    	 */
    #if !(defined(SPD_spmd) && (SPMD_SPM_AT_SEL2 == 1))
    	set_interrupt_rm_flag(flags, SECURE);
    #endif /* !(defined(SPD_spmd) && (SPMD_SPM_AT_SEL2 == 1)) */
    
    	/* Register handler for EL3 interrupts */
    	ret = register_interrupt_type_handler(INTR_TYPE_EL3,
    			ehf_el3_interrupt_handler, flags);
    	assert(ret == 0);
    }
    
  4. runtime_svc_init():运行时服务初始化,如前所述,BL31启动完成后会常驻内存,处理来自低异常等级的SMC异常,这些异常处理流程就是运行时服务,不同的服务C在调用之前需要先注册到BL31中,以optee服务,看下注册流程,在opteed_main.c通过DECLARE_RT_SVC定义了两个服务的SMC Call:一个用于快速SMC调用,其不需要执行完整的调度过程就能返回;另一个用于yielding SMC调用,其是阻塞的,需要完整的调度过程才能返回,处理速度较第一种慢。这样OPTEE可以根据不同的场景选择合适的SMC类型来提供服务。

    /*
     * Convenience macros to declare a service descriptor
     */
    #define DECLARE_RT_SVC(_name, _start, _end, _type, _setup, _smch)	\
    	static const rt_svc_desc_t __svc_desc_ ## _name			\
    		__section("rt_svc_descs") __used = {			\
    			.start_oen = (_start),				\
    			.end_oen = (_end),				\
    			.call_type = (_type),				\
    			.name = #_name,					\
    			.init = (_setup),				\
    			.handle = (_smch)				\
    		}
    

    DECLARE_RT_SVC宏定义被编译到镜像文件中的rt_svc_descs段中,这个段在链接脚本bl_common.ld.h中,如下。

    #define RT_SVC_DESCS					\
    	. = ALIGN(STRUCT_ALIGN);			\
    	__RT_SVC_DESCS_START__ = .;			\
    	KEEP(*(rt_svc_descs))				\
    	__RT_SVC_DESCS_END__ = .;
    

    DECLARE_RT_SVC宏定义的参数说明如下:

    • start_oen:服务的起始内部编号
    • end_oen:服务的末尾编号
    • call_type:调用的SMC的类型,包括上面的两种类型SMC_TYPE_FASTSMC_TYPE_YIELD
    • name:服务的名字
    • init:服务在执行之前需要被执行的初始化操作函数
    • handle:当触发SMC的调用时的请求处理函数
    /* Define an OPTEED runtime service descriptor for fast SMC calls */
    DECLARE_RT_SVC(
    	opteed_fast,
    
    	OEN_TOS_START,
    	OEN_TOS_END,
    	SMC_TYPE_FAST,
    	opteed_setup,
    	opteed_smc_handler
    );
    
    /* Define an OPTEED runtime service descriptor for yielding SMC calls */
    DECLARE_RT_SVC(
    	opteed_std,
    
    	OEN_TOS_START,
    	OEN_TOS_END,
    	SMC_TYPE_YIELD,
    	NULL,
    	opteed_smc_handler
    );
    

    回到bl31_main启动流程,runtime_svc_init完成对上面定义的各种服务初始化。通过遍历rt_svc_descs段,首先检验服务的有效性,然后调用对应服务的初始化函数,如optee服务即上面的opteed_setup函数,最后对于每个服务计算描述符索引,存储在rt_svc_descs_indices数组中,用于在处理服务时通过该索引获取到对应的服务。

    void __init runtime_svc_init(void)
    {
    	int rc = 0;
    	uint8_t index, start_idx, end_idx;
    	rt_svc_desc_t *rt_svc_descs;
    
    	/* Assert the number of descriptors detected are less than maximum indices */
    	assert((RT_SVC_DESCS_END >= RT_SVC_DESCS_START) &&
    			(RT_SVC_DECS_NUM < MAX_RT_SVCS));
    
    	/* If no runtime services are implemented then simply bail out */
    	if (RT_SVC_DECS_NUM == 0U)
    		return;
    
    	/* Initialise internal variables to invalid state */
    	(void)memset(rt_svc_descs_indices, -1, sizeof(rt_svc_descs_indices));
    
    	rt_svc_descs = (rt_svc_desc_t *) RT_SVC_DESCS_START;
    	for (index = 0U; index < RT_SVC_DECS_NUM; index++) {
    		rt_svc_desc_t *service = &rt_svc_descs[index];
    
    		/*
    		 * An invalid descriptor is an error condition since it is
    		 * difficult to predict the system behaviour in the absence
    		 * of this service.
    		 */
    		rc = validate_rt_svc_desc(service);
    		if (rc != 0) {
    			ERROR("Invalid runtime service descriptor %p\n",
    				(void *) service);
    			panic();
    		}
    
    		/*
    		 * The runtime service may have separate rt_svc_desc_t
    		 * for its fast smc and yielding smc. Since the service itself
    		 * need to be initialized only once, only one of them will have
    		 * an initialisation routine defined. Call the initialisation
    		 * routine for this runtime service, if it is defined.
    		 */
    		if (service->init != NULL) {
    			rc = service->init();
    			if (rc != 0) {
    				ERROR("Error initializing runtime service %s\n",
    						service->name);
    				continue;
    			}
    		}
    
    		/*
    		 * Fill the indices corresponding to the start and end
    		 * owning entity numbers with the index of the
    		 * descriptor which will handle the SMCs for this owning
    		 * entity range.
    		 */
    		start_idx = (uint8_t)get_unique_oen(service->start_oen,
    						    service->call_type);
    		end_idx = (uint8_t)get_unique_oen(service->end_oen,
    						  service->call_type);
    		assert(start_idx <= end_idx);
    		assert(end_idx < MAX_RT_SVCS);
    		for (; start_idx <= end_idx; start_idx++)
    			rt_svc_descs_indices[start_idx] = index;
    	}
    }
    

    看一下optee快速SMC服务的初始化,service->init()其会调用opteed_setup,该函数是启动optee的入口函数。

    static int32_t opteed_setup(void)
    {
    	entry_point_info_t *optee_ep_info;
    	uint32_t linear_id;
    	uint64_t opteed_pageable_part;
    	uint64_t opteed_mem_limit;
    	uint64_t dt_addr;
    
    	linear_id = plat_my_core_pos();
    
    	/*
    	 * Get information about the Secure Payload (BL32) image. Its
    	 * absence is a critical failure.  TODO: Add support to
    	 * conditionally include the SPD service
    	 */
    	optee_ep_info = bl31_plat_get_next_image_ep_info(SECURE);
    	if (!optee_ep_info) {
    		WARN("No OPTEE provided by BL2 boot loader, Booting device"
    			" without OPTEE initialization. SMC`s destined for OPTEE"
    			" will return SMC_UNK\n");
    		return 1;
    	}
    
    	/*
    	 * If there's no valid entry point for SP, we return a non-zero value
    	 * signalling failure initializing the service. We bail out without
    	 * registering any handlers
    	 */
    	if (!optee_ep_info->pc)
    		return 1;
    
    	opteed_rw = optee_ep_info->args.arg0;
    	opteed_pageable_part = optee_ep_info->args.arg1;
    	opteed_mem_limit = optee_ep_info->args.arg2;
    	dt_addr = optee_ep_info->args.arg3;
    
    	opteed_init_optee_ep_state(optee_ep_info,
    				opteed_rw,
    				optee_ep_info->pc,
    				opteed_pageable_part,
    				opteed_mem_limit,
    				dt_addr,
    				&opteed_sp_context[linear_id]);
    
    	/*
    	 * All OPTEED initialization done. Now register our init function with
    	 * BL31 for deferred invocation
    	 */
    	bl31_register_bl32_init(&opteed_init);
    
    	return 0;
    }
    
    • plat_my_core_pos:获取当前core的ID
    • bl31_plat_get_next_image_ep_info:获取BL32(OP-TEE)镜像的描述信息,如果返回NULL,说明BL2没有启动BL32,那么对于OPTEE 的SMC将会报错误
    • !optee_ep_info->pc:判断BL32 镜像的PC地址是否有效
    • opteed_init_optee_ep_state:初始化optee上下文和入口信息
    • bl31_register_bl32_init:注册opteed_init初始化函数给bl32_init变量,以备bl31调用
  5. (*bl32_init)():初始化BL32,即调用上面的opteed_init,完成optee的设置。

    static int32_t opteed_init(void)
    {
    	uint32_t linear_id = plat_my_core_pos();
    	optee_context_t *optee_ctx = &opteed_sp_context[linear_id];
    	entry_point_info_t *optee_entry_point;
    	uint64_t rc;
    
    	/*
    	 * Get information about the OPTEE (BL32) image. Its
    	 * absence is a critical failure.
    	 */
    	optee_entry_point = bl31_plat_get_next_image_ep_info(SECURE);
    	assert(optee_entry_point);
    
    	cm_init_my_context(optee_entry_point);
    
    	/*
    	 * Arrange for an entry into OPTEE. It will be returned via
    	 * OPTEE_ENTRY_DONE case
    	 */
    	rc = opteed_synchronous_sp_entry(optee_ctx);
    	assert(rc != 0);
    
    	return rc;
    }
    
    • plat_my_core_pos:获取当前core的ID

    • optee_ctx:获取执行的上下文

    • bl31_plat_get_next_image_ep_info:获取BL32(OP-TEE)镜像的描述信息

    • cm_init_my_context:初始化CPU的上下文

    • opteed_synchronous_sp_entry:同步进入跳转到optee执行。首先应用Seucre-EL1系统寄存器,然后调用opteed_enter_sp进入到optee。

      uint64_t opteed_synchronous_sp_entry(optee_context_t *optee_ctx)
      {
      	uint64_t rc;
      
      	assert(optee_ctx != NULL);
      	assert(optee_ctx->c_rt_ctx == 0);
      
      	/* Apply the Secure EL1 system register context and switch to it */
      	assert(cm_get_context(SECURE) == &optee_ctx->cpu_ctx);
      	cm_el1_sysregs_context_restore(SECURE);
      	cm_set_next_eret_context(SECURE);
      
      	rc = opteed_enter_sp(&optee_ctx->c_rt_ctx);
          
      	return rc;
      }
      

      由于BL31除了启动BL32,还要启动BL33,因此在跳转到BL32之前,需要保存当前上下文,用以返回到断点处继续执行。首先暂存EL3 callee-saved寄存器,然后调用el3_exit进入OPTEE执行。

      func opteed_enter_sp
      	/* Make space for the registers that we're going to save */
      	mov	x3, sp
      	str	x3, [x0, #0]
      	sub	sp, sp, #OPTEED_C_RT_CTX_SIZE
      
      	/* Save callee-saved registers on to the stack */
      	stp	x19, x20, [sp, #OPTEED_C_RT_CTX_X19]
      	stp	x21, x22, [sp, #OPTEED_C_RT_CTX_X21]
      	stp	x23, x24, [sp, #OPTEED_C_RT_CTX_X23]
      	stp	x25, x26, [sp, #OPTEED_C_RT_CTX_X25]
      	stp	x27, x28, [sp, #OPTEED_C_RT_CTX_X27]
      	stp	x29, x30, [sp, #OPTEED_C_RT_CTX_X29]
      
      	/* ---------------------------------------------
      	 * Everything is setup now. el3_exit() will
      	 * use the secure context to restore to the
      	 * general purpose and EL3 system registers to
      	 * ERET into OPTEE.
      	 * ---------------------------------------------
      	 */
      	b	el3_exit
      endfunc opteed_enter_sp
      

      当optee初始化完成后,由于其在Secure EL1下运行,其需要通过SMC方式重新进入到BL31,即通过TEESMC_OPTEED_RETURN_ENTRY_DONE进入到SMC处理流程,如下。opteed_smc_handler负责处理来自非安全状态的SMC调用的函数,它负责与安全负载进行通信,委派工作并将结果返回给非安全状态。这里会执行TEESMC_OPTEED_RETURN_ENTRY_DONE的分支,表明该SMC是从BL32执行完成的返回,然后调用opteed_synchronous_sp_exit恢复之前进入bl32之前保存的上下文,返回进行执行。

      static uintptr_t opteed_smc_handler(uint32_t smc_fid,
      			 u_register_t x1,
      			 u_register_t x2,
      			 u_register_t x3,
      			 u_register_t x4,
      			 void *cookie,
      			 void *handle,
      			 u_register_t flags)
      {
      	cpu_context_t *ns_cpu_context;
      	uint32_t linear_id = plat_my_core_pos();
      	optee_context_t *optee_ctx = &opteed_sp_context[linear_id];
      	uint64_t rc;
      	/*.....*/
      	/*
      	 * Returning from OPTEE
      	 */
      
      	switch (smc_fid) {
      	/*
      	 * OPTEE has finished initialising itself after a cold boot
      	 */
      	case TEESMC_OPTEED_RETURN_ENTRY_DONE:
      		/*
      		 * Stash the OPTEE entry points information. This is done
      		 * only once on the primary cpu
      		 */
      		assert(optee_vector_table == NULL);
      		optee_vector_table = (optee_vectors_t *) x1;
      
      		if (optee_vector_table) {
      			set_optee_pstate(optee_ctx->state, OPTEE_PSTATE_ON);
      
      			/*
      			 * OPTEE has been successfully initialized.
      			 * Register power management hooks with PSCI
      			 */
      			psci_register_spd_pm_hook(&opteed_pm);
      
      			/*
      			 * Register an interrupt handler for S-EL1 interrupts
      			 * when generated during code executing in the
      			 * non-secure state.
      			 */
      			flags = 0;
      			set_interrupt_rm_flag(flags, NON_SECURE);
      			rc = register_interrupt_type_handler(INTR_TYPE_S_EL1,
      						opteed_sel1_interrupt_handler,
      						flags);
      			if (rc)
      				panic();
      		}
      
      		/*
      		 * OPTEE reports completion. The OPTEED must have initiated
      		 * the original request through a synchronous entry into
      		 * OPTEE. Jump back to the original C runtime context.
      		 */
      		opteed_synchronous_sp_exit(optee_ctx, x1);
      		break;
      
      
      	/*
      	 * These function IDs is used only by OP-TEE to indicate it has
      	 * finished:
      	 * 1. turning itself on in response to an earlier psci
      	 *    cpu_on request
      	 * 2. resuming itself after an earlier psci cpu_suspend
      	 *    request.
      	 */
      	case TEESMC_OPTEED_RETURN_ON_DONE:
      	case TEESMC_OPTEED_RETURN_RESUME_DONE:
      	/* .... */
      	default:
      		panic();
      	}
      }
      

      首先恢复以前保存在x0的栈,然后恢复栈上的callee-saved寄存器,最后将x1作为x0,返回到断点处继续执行,即返回到bl32_init处。

      	/* ---------------------------------------------
      	 * This function is called 'x0' pointing to a C
      	 * runtime context saved in opteed_enter_sp().  It
      	 * restores the saved registers and jumps to
      	 * that runtime with 'x0' as the new sp. This
      	 * destroys the C runtime context that had been
      	 * built on the stack below the saved context by
      	 * the caller. Later the second parameter 'x1'
      	 * is passed as return value to the caller
      	 * ---------------------------------------------
      	 */
      	.global opteed_exit_sp
      func opteed_exit_sp
      	/* Restore the previous stack */
      	mov	sp, x0
      
      	/* Restore callee-saved registers on to the stack */
      	ldp	x19, x20, [x0, #(OPTEED_C_RT_CTX_X19 - OPTEED_C_RT_CTX_SIZE)]
      	ldp	x21, x22, [x0, #(OPTEED_C_RT_CTX_X21 - OPTEED_C_RT_CTX_SIZE)]
      	ldp	x23, x24, [x0, #(OPTEED_C_RT_CTX_X23 - OPTEED_C_RT_CTX_SIZE)]
      	ldp	x25, x26, [x0, #(OPTEED_C_RT_CTX_X25 - OPTEED_C_RT_CTX_SIZE)]
      	ldp	x27, x28, [x0, #(OPTEED_C_RT_CTX_X27 - OPTEED_C_RT_CTX_SIZE)]
      	ldp	x29, x30, [x0, #(OPTEED_C_RT_CTX_X29 - OPTEED_C_RT_CTX_SIZE)]
      
      	/* ---------------------------------------------
      	 * This should take us back to the instruction
      	 * after the call to the last opteed_enter_sp().
      	 * Place the second parameter to x0 so that the
      	 * caller will see it as a return value from the
      	 * original entry call
      	 * ---------------------------------------------
      	 */
      	mov	x0, x1
      	ret
      endfunc opteed_exit_sp
      
  6. bl31_prepare_next_image_entry:获取下一阶段需要被加载的镜像文件,配置运行环境。

    void __init bl31_prepare_next_image_entry(void)
    {
    	entry_point_info_t *next_image_info;
    	uint32_t image_type;
    
    	/* Determine which image to execute next */
    	image_type = bl31_get_next_image_type();
    
    	/* Program EL3 registers to enable entry into the next EL */
    	next_image_info = bl31_plat_get_next_image_ep_info(image_type);
    	assert(next_image_info != NULL);
    	assert(image_type == GET_SECURITY_STATE(next_image_info->h.attr));
    
    	INFO("BL31: Preparing for EL3 exit to %s world\n",
    		(image_type == SECURE) ? "secure" : "normal");
    	print_entry_point_info(next_image_info);
    	cm_init_my_context(next_image_info);
    
    	/*
    	* If we are entering the Non-secure world, use
    	* 'cm_prepare_el3_exit_ns' to exit.
    	*/
    	if (image_type == NON_SECURE) {
    		cm_prepare_el3_exit_ns();
    	} else {
    		cm_prepare_el3_exit(image_type);
    	}
    }
    
    • bl31_get_next_image_type:bl31进入下一阶段执行的镜像类型,即下一阶段BL33是非安全NON_SECURE
    • bl31_plat_get_next_image_ep_info:获取镜像跳转信息,即bl33_image_ep_info
    • cm_init_my_context:初始化CPU上下文
    • cm_prepare_el3_exit_ns:准备进入到非安全世界
  7. console_flush:刷新串口终端数据

  8. bl31_plat_runtime_setup:在退出bl31之前执行平台runtime建立,qemu是调用的弱函数,配置切换了console的状态,即console_switch_state(CONSOLE_FLAG_RUNTIME)

el3_eixt

bl31_main函数执行完成后,回到bl31_entrypoint继续执行,首先清零data和bss段,然后执行el3_eixt函数进入到bl33阶段。至此TF-A启动流程全部执行完毕,进入到uboot或者直接运行kernel。

/* --------------------------------------------------------------------
 * Clean the .data & .bss sections to main memory. This ensures
 * that any global data which was initialised by the primary CPU
 * is visible to secondary CPUs before they enable their data
 * caches and participate in coherency.
 * --------------------------------------------------------------------
 */
adrp	x0, __DATA_START__
add	x0, x0, :lo12:__DATA_START__
adrp	x1, __DATA_END__
add	x1, x1, :lo12:__DATA_END__
sub	x1, x1, x0
bl	clean_dcache_range

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	clean_dcache_range

b	el3_exit

欢迎关注“安全有理”微信公众号。

安全有理

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值