(05)ATF代码导读之BL2

代码导读

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

BL2

BL2是启动的第二阶段,同理复位的入口函数为bl2_entrypoint。BL2的加载流程同BL1类似,只是需要加载更多的镜像,如BL31、BL32或者BL33等,大致的函数执行流程如下。

bl1_entrypoint
env_init
plat_set_my_stack
bl2_setup
bl2_main

env_init

基础环境初始化。

  • 保存BL1传递过来的x0-x3参数,BL1通过x0-x1寄存器向BL2传递参数,当前BL1只通过x1传递过来BL2可用的安全内存区域。

    /*---------------------------------------------
     * Save arguments x0 - x3 from BL1 for future
     * use.
     * ---------------------------------------------
     */
    mov	x20, x0
    mov	x21, x1
    mov	x22, x2
    mov	x23, x3
    
    # 使用gdb调试看下寄存器值
    (gdb) info reg x0 x1 x2 x3
    x0             0x0                 0
    x1             0xe001000           234885120
    x2             0x0                 0
    x3             0x0                 0
    
  • 设定异常向量:将EL1异常向量表基地址设置到VBAR寄存器,向量表定义位于early_exceptions.S,可以看出BL2捕获异常后不会做处理,只是打印异常相关的信息,然后直接进入panic。

    /* ---------------------------------------------
     * Set the exception vector to something sane.
     * ---------------------------------------------
     */
    adr	x0, early_exceptions
    msr	vbar_el1, x0
    isb
    
  • 使能SError中断,用来屏蔽系统错误

    /* ---------------------------------------------
     * Enable the SError interrupt now that the
     * exception vectors have been setup.
     * ---------------------------------------------
     */
    msr	daifclr, #DAIF_ABT_BIT
    
  • EL1等级下,使能ICACHE和栈对齐、数据对齐检查,关闭预取

    /* ---------------------------------------------
     * Enable the instruction cache, stack pointer
     * and data access alignment checks and disable
     * speculative loads.
     * ---------------------------------------------
     */
    mov	x1, #(SCTLR_I_BIT | SCTLR_A_BIT | SCTLR_SA_BIT)
    mrs	x0, sctlr_el1
    orr	x0, x0, x1
    bic	x0, x0, #SCTLR_DSSBS_BIT
    msr	sctlr_el1, x0
    isb
    
  • C运行环境初始化,将ROM中的数据段重定位到RAM和清零BSS段

    /* ---------------------------------------------
     * 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
    
    /* ---------------------------------------------
     * Zero out NOBITS sections. There are 2 of them:
     *   - the .bss section;
     *   - the coherent memory section.
     * ---------------------------------------------
     */
    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	zeromem
    

plat_set_my_stack

分配bl2运行时栈

func plat_set_my_stack
	get_up_stack platform_coherent_stacks, PLATFORM_STACK_SIZE
	mov sp, x0
	ret
endfunc plat_set_my_stack

bl2_setup

bl2_setup是BL2建立初始化,包括两个函数:bl2_early_platform_setup2()bl2_plat_arch_setup()

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

	/* Perform late platform-specific setup */
	bl2_plat_arch_setup();
}
  1. bl2_early_platform_setup2

    同BL1类似,该函数会初始化串口、设置BL2内存区域和qemu的存储驱动初始化

    void bl2_early_platform_setup2(u_register_t arg0, u_register_t arg1,
    			       u_register_t arg2, u_register_t arg3)
    {
    	meminfo_t *mem_layout = (void *)arg1;
    
    	/* Initialize the console to provide early debug support */
    	qemu_console_init();
    
    	/* Setup the BL2 memory layout */
    	bl2_tzram_layout = *mem_layout;
    
    	plat_qemu_io_setup();
    }
    
  2. bl2_plat_arch_setup

    同BL1类似,该函数会配置MMU,启动DCACHe。

    void bl2_plat_arch_setup(void)
    {
    	QEMU_CONFIGURE_BL2_MMU(bl2_tzram_layout.total_base,
    			      bl2_tzram_layout.total_size,
    			      BL_CODE_BASE, BL_CODE_END,
    			      BL_RO_DATA_BASE, BL_RO_DATA_END,
    			      BL_COHERENT_RAM_BASE, BL_COHERENT_RAM_END);
    }
    

bl2_main

bl2_main主要完成了bl2阶段的主要操作, 包括对下一个阶段镜像文件的解析、 获取入口地址和镜像文件大小等信息, 然后对镜像文件进行验签和加载操作。 将bl31加载到内存中后会触发安全监控模式调用(smc) 将CPU权限转交给bl31。

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();

	crypto_mod_init();

	/* Initialize authentication module */
	auth_mod_init();

	/* Initialize the Measured Boot backend */
	bl2_plat_mboot_init();

	/* Initialize boot source */
	bl2_plat_preload_setup();

	/* Load the subsequent bootloader images. */
	next_bl_ep_info = bl2_load_images();

	/* Teardown the Measured Boot backend */
	bl2_plat_mboot_finish();

	console_flush();

	/*
	 * 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);
}
  1. bl2_arch_setup:开启FP/SIMD寄存器访问权限。

    void bl2_arch_setup(void)
    {
    	/* Give access to FP/SIMD registers */
    	write_cpacr(CPACR_EL1_FPEN(CPACR_EL1_FP_TRAP_NONE));
    }
    
  2. crypto_mod_init:初始化安全启动需要密码库,与BL1相同。

  3. auth_mod_init:认证模块初始化,与BL1相同。

  4. bl1_plat_mboot_init:measured boot初始化,与BL1相同。

  5. bl2_plat_preload_setup:qemu为空函数

  6. bl2_load_images:加载bl3x镜像,并返回bl31的入口地址。

    struct entry_point_info *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();
    	assert(bl2_load_info != NULL);
    	assert(bl2_load_info->head != NULL);
    	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;
    
    	while (bl2_node_info != NULL) {
    		/*
    		 * 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) != 0U) {
    			if (plat_setup_done != 0) {
    				WARN("BL2: Platform setup already done!!\n");
    			} else {
    				INFO("BL2: Doing platform setup\n");
    				bl2_platform_setup();
    				plat_setup_done = 1;
    			}
    		}
    
    		err = bl2_plat_handle_pre_image_load(bl2_node_info->image_id);
    		if (err != 0) {
    			ERROR("BL2: Failure in pre image load handling (%i)\n", err);
    			plat_error_handler(err);
    		}
    
    		if ((bl2_node_info->image_info->h.attr &
    		    IMAGE_ATTRIB_SKIP_LOADING) == 0U) {
    			INFO("BL2: Loading image id %u\n", bl2_node_info->image_id);
    			err = load_auth_image(bl2_node_info->image_id,
    				bl2_node_info->image_info);
    			if (err != 0) {
    				ERROR("BL2: Failed to load image id %u (%i)\n",
    				      bl2_node_info->image_id, err);
    				plat_error_handler(err);
    			}
    		} else {
    			INFO("BL2: Skip loading image id %u\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 != 0) {
    			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();
    	assert(bl2_to_next_bl_params != NULL);
    	assert(bl2_to_next_bl_params->head != NULL);
    	assert(bl2_to_next_bl_params->h.type == PARAM_BL_PARAMS);
    	assert(bl2_to_next_bl_params->h.version >= VERSION_2);
    	assert(bl2_to_next_bl_params->head->ep_info != NULL);
    
    	/* Populate arg0 for the next BL image if not already provided */
    	if (bl2_to_next_bl_params->head->ep_info->args.arg0 == (u_register_t)0)
    		bl2_to_next_bl_params->head->ep_info->args.arg0 =
    					(u_register_t)bl2_to_next_bl_params;
    
    	/* Flush the parameters to be passed to next image */
    	plat_flush_next_bl_params();
    
    	return bl2_to_next_bl_params->head->ep_info;
    }
    
    • plat_get_bl_image_load_info:获取加载所有镜像的信息,这里会调用到get_bl_load_info_from_mem_params_desc,该函数会创建待加载镜像的链表。

      bl_load_info_t *get_bl_load_info_from_mem_params_desc(void)
      {
      	unsigned int index = 0;
      
      	/* If there is no image to start with, return NULL */
      	if (bl_mem_params_desc_num == 0U)
      		return NULL;
      
      	/* Assign initial data structures */
      	bl_load_info_node_t *bl_node_info =
      		&bl_mem_params_desc_ptr[index].load_node_mem;
      	bl_load_info.head = bl_node_info;
      	SET_PARAM_HEAD(&bl_load_info, PARAM_BL_LOAD_INFO, VERSION_2, 0U);
      
      	/* Go through the image descriptor array and create the list */
      	for (; index < bl_mem_params_desc_num; index++) {
      
      		/* Populate the image information */
      		bl_node_info->image_id = bl_mem_params_desc_ptr[index].image_id;
      		bl_node_info->image_info = &bl_mem_params_desc_ptr[index].image_info;
      
      		/* Link next image if present */
      		if ((index + 1U) < bl_mem_params_desc_num) {
      			/* Get the memory and link the next node */
      			bl_node_info->next_load_info =
      				&bl_mem_params_desc_ptr[index + 1U].load_node_mem;
      			bl_node_info = bl_node_info->next_load_info;
      		}
      	}
      
      	return &bl_load_info;
      }
      

      待加载镜像的描述信息是由bl_mem_params_desc_ptr提供,其是REGISTER_BL_IMAGE_DESCS(bl2_mem_params_descs)注册到系统中的。镜像文件描述信息是由bl_mem_params_node_t结构体定义的。

      /* Following structure is used to store BL ep/image info. */
      typedef struct bl_mem_params_node {
      	unsigned int image_id;
      	image_info_t image_info;
      	entry_point_info_t ep_info;
      	unsigned int next_handoff_image_id;
      	bl_load_info_node_t load_node_mem;
      	bl_params_node_t params_node_mem;
      } bl_mem_params_node_t;
      
      • image_id:镜像ID
      • image_info:镜像信息
      • ep_info:跳转信息
      • next_handoff_image_id:交接的下一个镜像ID
      • load_node_mem:镜像加载流程节点
      • params_node_mem:镜像执行流程节点

      以qemu平台,只加载bl31和bl33的镜像描述信息如下。其中删除PRELOADED_BL33_BASE宏,其表示如果平台已经预加载BL33,就直接跳转过去执行,而不是依赖TF-A去加载。可以看出BL2先加载BL31镜像,然后再加载BL33镜像。

      static bl_mem_params_node_t bl2_mem_params_descs[] = {
      	/* 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),
      	  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,
      
      	  .next_handoff_image_id = BL33_IMAGE_ID,
      	},
      
      	/* 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),
      	  .ep_info.pc = NS_IMAGE_OFFSET,
      	  SET_STATIC_PARAM_HEAD(image_info, PARAM_EP, VERSION_2, image_info_t,
      				0),
      	  .image_info.image_base = NS_IMAGE_OFFSET,
      	  .image_info.image_max_size = NS_IMAGE_MAX_SIZE,
      
      	  .next_handoff_image_id = INVALID_IMAGE_ID,
      	}
      };
      
      REGISTER_BL_IMAGE_DESCS(bl2_mem_params_descs)
      
    • 进入循环遍历节点加载镜像,首先判断加载镜像之前是否需要执行平台初始化,即bl2_platform_setup,包括security_setup和update_dt两个函数,security_setup可以设置TrustZone地址空间控制或者其他外设,qemu平台为空;update_dt是更新设备树。

      static void security_setup(void)
      {
      	/*
      	 * This is where a TrustZone address space controller and other
      	 * security related peripherals, would be configured.
      	 */
      }
      
      static void update_dt(void)
      {
      	int ret;
      	void *fdt = (void *)(uintptr_t)ARM_PRELOADED_DTB_BASE;
      
      	ret = fdt_open_into(fdt, fdt, PLAT_QEMU_DT_MAX_SIZE);
      	if (ret < 0) {
      		ERROR("Invalid Device Tree at %p: error %d\n", fdt, ret);
      		return;
      	}
      
      	if (dt_add_psci_node(fdt)) {
      		ERROR("Failed to add PSCI Device Tree node\n");
      		return;
      	}
      
      	if (dt_add_psci_cpu_enable_methods(fdt)) {
      		ERROR("Failed to add PSCI cpu enable methods in Device Tree\n");
      		return;
      	}
      
      	ret = fdt_pack(fdt);
      	if (ret < 0)
      		ERROR("Failed to pack Device Tree at %p: error %d\n", fdt, ret);
      }
      
      void bl2_platform_setup(void)
      {
      	security_setup();
      	update_dt();
      
      	/* TODO Initialize timer */
      }
      
    • bl2_plat_handle_pre_image_load:镜像加载前可以进行一些预处理操作。

    • load_auth_image:加载并认证镜像,与BL1的加载流程完全一致。

    • bl2_plat_handle_post_image_load:平台镜像加载后处理,对于qemu平台会调用qemu_bl2_handle_post_image_load。

      static int qemu_bl2_handle_post_image_load(unsigned int image_id)
      {
      	int err = 0;
      	bl_mem_params_node_t *bl_mem_params = get_bl_mem_params_node(image_id);
      
      	assert(bl_mem_params);
      
      	switch (image_id) {
      	case BL32_IMAGE_ID:
              /* ... */
      		break;
      
      	case BL33_IMAGE_ID:
      
      #if ARM_LINUX_KERNEL_AS_BL33
      		/*
      		 * According to the file ``Documentation/arm64/booting.txt`` of
      		 * the Linux kernel tree, Linux expects the physical address of
      		 * the device tree blob (DTB) in x0, while x1-x3 are reserved
      		 * for future use and must be 0.
      		 */
      		bl_mem_params->ep_info.args.arg0 =
      			(u_register_t)ARM_PRELOADED_DTB_BASE;
      		bl_mem_params->ep_info.args.arg1 = 0U;
      		bl_mem_params->ep_info.args.arg2 = 0U;
      		bl_mem_params->ep_info.args.arg3 = 0U;
      #else
      		/* BL33 expects to receive the primary CPU MPID (through r0) */
      		bl_mem_params->ep_info.args.arg0 = 0xffff & read_mpidr();
      #endif
      
      		bl_mem_params->ep_info.spsr = qemu_get_spsr_for_bl33_entry();
      		break;
      	default:
      		/* Do nothing in default case */
      		break;
      	}
      
      	return err;
      }
      
      • 对于BL32,是加载Trust OS,我们这里不进行加载,也就不会处理

      • 对于BL33,如果是直接将Linux作为BL33启动,那么通过dtb地址传递Linux需要的启动参数,否则就讲当前处理器affinity信息(如处理器ID)作为启动参数传递给arg0。最后设置跳转bl33需要的spsr寄存器,指明进入非安全世界的异常等级、栈指针SP_ELx和关闭所有异常。

        static uint32_t qemu_get_spsr_for_bl33_entry(void)
        {
        	uint32_t spsr;
        	unsigned int mode;
        
        	/* Figure out what mode we enter the non-secure world in */
        	mode = (el_implemented(2) != EL_IMPL_NONE) ? MODE_EL2 : MODE_EL1;
        
        	/*
        	 * TODO: Consider the possibility of specifying the SPSR in
        	 * the FIP ToC and allowing the platform to have a say as
        	 * well.
        	 */
        	spsr = SPSR_64(mode, MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS);
        
        	return spsr;
        }
        
    • 当加载完所有镜像后,调用plat_get_next_bl_params获取传递给下一个镜像的信息。对于qemu平台,会调用get_next_bl_params_from_mem_params_desc,该函数会创建待执行镜像的链表。首先寻找第一个需要执行的镜像,在bl2_mem_params_descs中定义的是bl31,放入next_bl_params链表头,然后可执行镜像的参数节点添加到参数列表中。

      bl_params_t *get_next_bl_params_from_mem_params_desc(void)
      {
      	unsigned int count;
      	unsigned int img_id = 0U;
      	unsigned int link_index = 0U;
      	bl_params_node_t *bl_current_exec_node = NULL;
      	bl_params_node_t *bl_last_exec_node = NULL;
      	bl_mem_params_node_t *desc_ptr;
      
      	/* If there is no image to start with, return NULL */
      	if (bl_mem_params_desc_num == 0U)
      		return NULL;
      
      	/* Get the list HEAD */
      	for (count = 0U; count < bl_mem_params_desc_num; count++) {
      
      		desc_ptr = &bl_mem_params_desc_ptr[count];
      
      		if ((EP_GET_EXE(desc_ptr->ep_info.h.attr) == EXECUTABLE) &&
      			(EP_GET_FIRST_EXE(desc_ptr->ep_info.h.attr) == EP_FIRST_EXE)) {
      			next_bl_params.head = &desc_ptr->params_node_mem;
      			link_index = count;
      			break;
      		}
      	}
      
      	/* Make sure we have a HEAD node */
      	assert(next_bl_params.head != NULL);
      
      	/* Populate the HEAD information */
      	SET_PARAM_HEAD(&next_bl_params, PARAM_BL_PARAMS, VERSION_2, 0U);
      
      	/*
      	 * Go through the image descriptor array and create the list.
      	 * This bounded loop is to make sure that we are not looping forever.
      	 */
      	for (count = 0U; count < bl_mem_params_desc_num; count++) {
      
      		desc_ptr = &bl_mem_params_desc_ptr[link_index];
      
      		/* Make sure the image is executable */
      		assert(EP_GET_EXE(desc_ptr->ep_info.h.attr) == EXECUTABLE);
      
      		/* Get the memory for current node */
      		bl_current_exec_node = &desc_ptr->params_node_mem;
      
      		/* Populate the image information */
      		bl_current_exec_node->image_id = desc_ptr->image_id;
      		bl_current_exec_node->image_info = &desc_ptr->image_info;
      		bl_current_exec_node->ep_info = &desc_ptr->ep_info;
      
      		if (bl_last_exec_node != NULL) {
      			/* Assert if loop detected */
      			assert(bl_last_exec_node->next_params_info == NULL);
      
      			/* Link the previous node to the current one */
      			bl_last_exec_node->next_params_info = bl_current_exec_node;
      		}
      
      		/* Update the last node */
      		bl_last_exec_node = bl_current_exec_node;
      
      		/* If no next hand-off image then break out */
      		img_id = desc_ptr->next_handoff_image_id;
      		if (img_id == INVALID_IMAGE_ID)
      			break;
      
      		/* Get the index for the next hand-off image */
      		link_index = get_bl_params_node_index(img_id);
      		assert((link_index > 0U) &&
      			(link_index < bl_mem_params_desc_num));
      	}
      
      	/* Invalid image is expected to terminate the loop */
      	assert(img_id == INVALID_IMAGE_ID);
      
      	return &next_bl_params;
      }
      
    • plat_flush_next_bl_params:刷新参数给下一个镜像,由于BL2开启了dcache,在跳转之前,需要将参数数据从cache刷新到SRAM中。qemu平台调用flush_bl_params_desc,最终会调用到flush_bl_params_desc_args。

      void flush_bl_params_desc_args(bl_mem_params_node_t *mem_params_desc_ptr,
      	unsigned int mem_params_desc_num,
      	bl_params_t *next_bl_params_ptr)
      {
      	assert(mem_params_desc_ptr != NULL);
      	assert(mem_params_desc_num != 0U);
      	assert(next_bl_params_ptr != NULL);
      
      	flush_dcache_range((uintptr_t)mem_params_desc_ptr,
      		sizeof(*mem_params_desc_ptr) * mem_params_desc_num);
      
      	flush_dcache_range((uintptr_t)next_bl_params_ptr,
      			sizeof(*next_bl_params_ptr));
      }
      
  7. bl2_plat_mboot_finish:卸载mesured boot驱动

  8. console_flush:出BL2前,刷新串口中的所有数据

  9. 通过SMC跳转到下一镜像,由于BL2运行在S-EL1下,需要通过SMC异常再次进入BL1,由BL1的SMC处理函数来执行实际的镜像切换流程。这里触发了一个类型为BL1_SMC_RUN_IMAGE的安全监控模式调用,第二个参数为下一阶段的入口跳转信息。

    smc(BL1_SMC_RUN_IMAGE, (unsigned long)next_bl_ep_info, 0, 0, 0, 0, 0, 0);
    

    通过SMC调用会进入到bl1_exceptions.S中smc_handler64,如下。

    func smc_handler64
    
    	/* ----------------------------------------------
    	 * Detect if this is a RUN_IMAGE or other SMC.
    	 * ----------------------------------------------
    	 */
    	mov	x30, #BL1_SMC_RUN_IMAGE
    	cmp	x30, x0
    	b.ne	smc_handler
    
    	/* ------------------------------------------------
    	 * Make sure only Secure world reaches here.
    	 * ------------------------------------------------
    	 */
    	mrs	x30, scr_el3
    	tst	x30, #SCR_NS_BIT
    	b.ne	unexpected_sync_exception
    
    	/* ----------------------------------------------
    	 * Handling RUN_IMAGE SMC. First switch back to
    	 * SP_EL0 for the C runtime stack.
    	 * ----------------------------------------------
    	 */
    	ldr	x30, [sp, #CTX_EL3STATE_OFFSET + CTX_RUNTIME_SP]
    	msr	spsel, #MODE_SP_EL0
    	mov	sp, x30
    
    	/* ---------------------------------------------------------------------
    	 * Pass EL3 control to next BL image.
    	 * Here it expects X1 with the address of a entry_point_info_t
    	 * structure describing the next BL image entrypoint.
    	 * ---------------------------------------------------------------------
    	 */
    	mov	x20, x1
    
    	mov	x0, x20
    	bl	bl1_print_next_bl_ep_info
    
    	ldp	x0, x1, [x20, #ENTRY_POINT_INFO_PC_OFFSET]
    	msr	elr_el3, x0
    	msr	spsr_el3, x1
    	ubfx	x0, x1, #MODE_EL_SHIFT, #2
    	cmp	x0, #MODE_EL3
    	b.ne	unexpected_sync_exception
    
    	bl	disable_mmu_icache_el3
    	tlbi	alle3
    	dsb	ish /* ERET implies ISB, so it is not needed here */
    
    	mov	x0, x20
    	bl	bl1_plat_prepare_exit
    
    	ldp	x6, x7, [x20, #(ENTRY_POINT_INFO_ARGS_OFFSET + 0x30)]
    	ldp	x4, x5, [x20, #(ENTRY_POINT_INFO_ARGS_OFFSET + 0x20)]
    	ldp	x2, x3, [x20, #(ENTRY_POINT_INFO_ARGS_OFFSET + 0x10)]
    	ldp	x0, x1, [x20, #(ENTRY_POINT_INFO_ARGS_OFFSET + 0x0)]
    	exception_return
    endfunc smc_handler64
    
    • 首先判断是不是BL1_SMC_RUN_IMAGE,若是继续镜像切换,否则认为是普通类型的异常,进入smc_handler
    • 接着判断是否是安全世界执行到此,如果不是则认为不合法,产生异常
    • 然后恢复el3_exit流程中保存的运行时栈的值,恢复到SP_EL0中
    • bl1_print_next_bl_ep_info:打印bl3x参数信息
    • 从next_bl_ep_info参数中获取设置ELR_EL3和SPSR_EL3寄存器
    • 判断下一镜像是否是EL3,如果不是则报异常
    • bl1_plat_prepare_exit:退出BL1前的操作,默认为空操作
    • 设置x0-x7跳转参数,这里是next_bl_ep_info的arg0出栈传递给x0,arg1即bl_params指针出栈传递给x1
    • 最后通过ERET跳转到下一阶段镜像

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

安全有理

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值