(06)ATF代码导读之BL31

news2024/11/25 15:37:27

代码导读

关于平台相关的代码和函数均以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

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

安全有理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/720919.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

scratch 角色移动

scratch 角色移动 这是本人的第一个scratch程序。入坑scratch是因为希望体验一下图形化编程并制作以动画为主的图形化程序。用的软件是Mind。Mind是由scratch改编的开源IDE&#xff0c;可以图形化开发Arduino程序&#xff0c;本人使用Mind的另一个原因是Mind提供快捷地在OLED屏…

【数据库】MySQL安装(最新图文保姆级别超详细版本介绍)

1.总共两部分&#xff08;第二部可省略&#xff09; 安装mysql体验mysql环境变量配置 1.1安装mysql 1.输入官网地址https://www.mysql.com/ 下载完成后&#xff0c;我们双击打开我们的下载文件 打开后的界面&#xff0c;如图所示 我们选择custom&#xff0c;点击nex…

ModaHub魔搭社区:腾讯云向量数据库为什么以独立产品形式推出?

自今年大模型趋势发生以来&#xff0c;向量数据库领域备受关注。 今年3月以后&#xff0c;多家向量数据库厂商拿下最新融资&#xff0c;其中Pinecone更是获得高达1亿美元的B轮融资。 腾讯云当然也注意到了这一趋势。 腾讯云数据库副总经理罗云表示&#xff0c;当时内部已经开…

华为OD机试真题 Java 实现【网上商城优惠活动(一)】【2022 Q4 100分】,附详细解题思路

目录 一、题目描述二、输入描述三、输出描述四、补充说明五、Java算法源码六、效果展示1、输入2、输出3、说明 一、题目描述 某网上商城举办优惠活动&#xff0c;发布了满减、打折、无门槛3种优惠券&#xff0c;分别为&#xff1a; 1.每满100元优惠10元&#xff0c;无使用数限…

技术思维vs管理思维 程序员与项目经理5大思维差异

软件项目中&#xff0c;项目经理出身于程序员的情况较多&#xff0c;这样的项目经理在技术上拥有优势&#xff0c;但作为程序员的技术思维和作为项目经理的管理思维区别较大。因此如果不及时转换思维&#xff0c;往往造成过于纠结项目细节、忽视工期和成本压力&#xff0c;从而…

vmware虚拟机的12个使用技巧

1、增加虚拟机可用的物理内存 关闭虚拟机&#xff0c;设置内存&#xff1a; 2、硬件设备添加 一直选择下一步&#xff0c;直到这个界面进行磁盘大小分配&#xff1a; 3、控制权的切换 由于VMware的工作特点是在一台计算机中同时运行多个操作系统&#xff0c;因此这就存在一个…

排序算法-整理

1.【数据结构】带你玩转排序&#xff1a;堆排序、希尔排序、插入排序、选择排序、冒泡排序、快排(多版本)、归并排序 【数据结构】带你玩转排序&#xff1a;堆排序、希尔排序、插入排序、选择排序、冒泡排序、快排(多版本)、归并排序http://t.csdn.cn/fCXSo 2.十大基础算法 …

培训小程序首页开发

目录 1 定义变量2 欢迎语搭建3 分类导航搭建4 搭建底部导航总结 我们本篇来开发一下我们小程序的首页&#xff0c;先看一下原型 1 定义变量 因为我们首页展示的分类信息&#xff0c;现在分类信息已经存到了数据源里&#xff0c;我们要通过变量读取出来。 先打开我们创建的自…

Zabbix之部署

Zabbix 6.0 一.Zabbix介绍 1.zabbix 的含义 zabbix 是一个基于 Web 界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案zabbix 能监视各种网络参数&#xff0c;保证服务器系统的安全运营&#xff1b;并提供灵活的通知机制以让系统管理员快速定位/解决存在的各…

matlab用histfit画直方图+拟合曲线

matlab画直方图拟合曲线 成图效果1 数据格式2 绘制步骤3 后话 成图效果 1 数据格式 应该准备一个double的数组&#xff0c;如果是csv或者xlsx直接拖进matlab是table型&#xff0c;这是无法作为绘图参数的 如果是table型&#xff0c;可以使用table2array(data)进行转换 2 绘制…

广东程序员,今年28岁,4年经验月薪13K,仍一事无成

之前认识的一位工作很努力的广州程序员朋友&#xff0c;前天深夜微信拍了拍我——“在吗&#xff1f;播妞”。 他把对未来的迷茫一股脑的倒了出来&#xff1a; 对方拍了拍你的“隐形翅膀” 我在&#xff01; 长夜漫漫无心睡眠&#xff0c;我以为只有我睡不着觉&#xff0c;…

Unity 之 超级详细的隐私问题解决方案

Unity 之 助力游戏增长 -- 解决隐私问题 一&#xff0c;平台测试隐私问题二&#xff0c;解决方式一2.1 勾选自定义Mainifest2.2 修改自定义Mainifest2.3 隐私协议弹窗逻辑 三&#xff0c;解决方式二3.1 导出安卓工程3.2 创建上层Activity3.3 配置AndroidManifest 四&#xff0…

还在为PMO总结发愁?PMO工作总结应该怎么写及实例看这篇就够了

很多公司都要写总结了&#xff0c;作为PMO我们应该如何写工作总结呢&#xff1f;如何写总结能表现自己的价值还能让老板更喜欢看呢&#xff1f;过去一段时间的工作进行回顾总结&#xff0c;发掘问题、总结经验、进行规划等活动的过程。 今天分享给大家一个PMO写总结的方法和实…

Linux5.94 Zabbix服务配置与应用

文章目录 计算机系统5G云计算第四章 LINUX Zabbix服务配置与应用一、Zabbix服务概述1.zabbix 监控原理2.Zabbix 6.0 新特性1&#xff09;Zabbix server高可用防止硬件故障或计划维护期的停机2&#xff09;Zabbix 6.0 LTS新增Kubernetes监控功能&#xff0c;可以在Kubernetes系统…

PCB技巧(五)

一、问题及原因 问题&#xff1a; 对板子进行测试&#xff0c;发现引脚OPA电压不对&#xff0c;DAC输出电压没有问题&#xff0c;OPA接DAC输出应该和DAC电压大致差不多&#xff0c;但是电压差100mV。 原因&#xff1a; 经查找是下图中GND走线与旁边引脚OPO有接触。在未焊接…

香橙派刷机和系统登入

1.刷机 先打开刷机软件&#xff0c;再点击format&#xff0c;然后关闭 完成之后打开win32diskimager 选择映像文件后点击写入 等待写入 写入成功 2.登入香橙派 串口连接 默认登入账号密码为orangepi 密码不会显示

ChatGPT,你的智能助手,社交办公利器

ChatGPT&#xff0c;你的智能助手&#xff0c;社交办公利器&#xff01; 嗨&#xff0c;亲爱的小红书好友们&#xff01;我今天要向大家强力推荐一款让生活工作更轻松的神奇助手——ChatGPT&#xff01;无论是工作中的问题求解、日常生活的疑惑迷茫&#xff0c;还是灵感创作的启…

后台权限管理

1&#xff0c;页面级权限 通过后台接口控制页面级的权限&#xff0c;将数据保存在本地并且和路由匹配&#xff0c;左侧tabber 仅展示导航到拥有权限的页面。或者使用路由拦截的方式也可以。 2&#xff0c;按钮级权限 超级管理员有所有按钮的权限&#xff0c;普通管理员可能…

SpringBoot-Web 整合案例(图书管理系统)

SpringBoot-Web 整合案例讲解&#xff08;图书管理系统&#xff09; 0. 项目预览 1. 创建项目,添加依赖 <dependencies><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>…

Zabbix安装

Zabbix6.0 一&#xff1a;zabbix 是什么&#xff1f;二&#xff1a;Zabbix 6.0 新特性&#xff1a;1、Zabbix server高可用防止硬件故障或计划维护期的停机&#xff1a;2、Zabbix 6.0 LTS新增Kubernetes监控功能&#xff0c;可以在Kubernetes系统从多个维度采集指标&#xff1a…