安全引导功能及ATF的启动过程(四)

发布于:2025-08-09 ⋅ 阅读:(15) ⋅ 点赞:(0)

安全引导功能及ATF的启动过程(四)

ATF中bl31的启动

在bl2中触发安全监控模式调用后会跳转到bl31中执行,bl31最主要的作用是建立EL3运行态的软件配置,在该阶段会完成各种类型的安全监控模式调用ID的注册和对应的ARM核状态的切换,bl31运行在EL3。bl31的执行流程如图所示。

在这里插入图片描述

bl31_entrypoint函数

通过bl31.ld.S文件可知,bl31的入口函数是bl31_entrypoint。该函数的内容如下:

/root/optee/trusted-firmware-a/bl31/bl31.ld.S

OUTPUT_FORMAT(PLATFORM_LINKER_FORMAT)
OUTPUT_ARCH(PLATFORM_LINKER_ARCH)
ENTRY(bl31_entrypoint)

/root/optee/trusted-firmware-a/bl31/aarch64/bl31_entrypoint.S

func bl31_entrypoint
        /* ---------------------------------------------------------------
         * Stash the previous bootloader arguments x0 - x3 for later use.
         * ---------------------------------------------------------------
         */
        //保存x0/x1/x2/x3寄存器中的值 
        mov     x20, x0
        mov     x21, x1
        mov     x22, x2
        mov     x23, x3

#if !RESET_TO_BL31
				//根据是否启用了RESET_TO_BL31参数为el3_entrypoint_common传入不同参数
				// RESET_TO_BL31=y 系统复位后直接进入 BL31
				// RESET_TO_BL31=n BL31 是由前一级(如 BL2)加载并跳转进来的
        /* ---------------------------------------------------------------------
         * 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                               \
                //设置 EL3 异常向量               
                _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
#endif /* RESET_TO_BL31 */
				/* --------------------------------------------------------------------
         * Perform BL31 setup
         * --------------------------------------------------------------------
         */
        //恢复x0/x1/x2/x3数据,配置bl31
        mov     x0, x20
        mov     x1, x21
        mov     x2, x22
        mov     x3, x23
        bl      bl31_setup

#if ENABLE_PAUTH
        /* --------------------------------------------------------------------
         * Program APIAKey_EL1 and enable pointer authentication
         * --------------------------------------------------------------------
         */
        //执行指针认证初始化 
        bl      pauth_init_enable_el3
#endif /* ENABLE_PAUTH */

        /* --------------------------------------------------------------------
         * Jump to main function
         * --------------------------------------------------------------------
         */
        // 执行bl31_main
        bl      bl31_main   

        /* --------------------------------------------------------------------
         * 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
endfunc bl31_entrypoint         
         

bl31_main函数

该函数主要完成必要的初始化操作,注册EL3中各种安全监控模式调用的处理函数,以便在启动完成后响应在REE侧和TEE侧产生的安全监控模式调用。该函数的内容如下:

/root/optee/trusted-firmware-a/bl31/bl31_main.c

//BL31 负责为启动 CPU 设置运行时服务,然后将控制权移交给引导加载程序或操作系统。
//该函数调用 runtime_svc_init(),该函数会初始化所有已注册的运行时服务。
//这些运行时服务会为处理器核心切换到下一个异常级别建立足够的上下文环境。
//当该函数返回后,核心将通过 ERET 指令切换到已设定的异常级别。
void bl31_main(void)
{
	/* Init registers that never change for the lifetime of TF-A */
	//初始化在 TF-A 生命周期内永不更改的寄存器。
	cm_manage_extensions_el3(plat_my_core_pos());

	/* Init per-world context registers for non-secure world */
	// 设置那些会在 安全世界(Secure)与非安全世界之间切换时需要恢复的寄存器
	manage_extensions_nonsecure_per_world();

	NOTICE("BL31: %s\n", build_version_string);
	NOTICE("BL31: %s\n", build_message);

#if FEATURE_DETECTION
	/* Detect if features enabled during compilation are supported by PE. */
	//检测在编译期间启用的功能是否被处理器单元(PE)所支持
	detect_arch_features();
#endif /* FEATURE_DETECTION */

#if ENABLE_RUNTIME_INSTRUMENTATION
	//运行时性能检测
	PMF_CAPTURE_TIMESTAMP(bl_svc, BL31_ENTRY, PMF_CACHE_MAINT);
#endif

#ifdef SUPPORT_UNKNOWN_MPID
	//处理未知的 MPID(多核标识)
	if (unsupported_mpid_flag == 0) {
		NOTICE("Unsupported MPID detected!\n");
	}
#endif

	/* Perform platform setup in BL31 */
	//平台相关设置
	bl31_platform_setup();

#if USE_GIC_DRIVER
	/*
	 * Initialize the GIC driver as well as per-cpu and global interfaces.
	 * Platform has had an opportunity to initialise specifics.
	 */
	//初始化 GIC 
	unsigned int core_pos = plat_my_core_pos();

	gic_init(core_pos);   //初始化 GIC 分发器
	gic_pcpu_init(core_pos);   //初始化当前 CPU 的中断接口
	gic_cpuif_enable(core_pos);     //使能当前 CPU 的中断接收
#endif /* USE_GIC_DRIVER */

	/* Initialise helper libraries */
	//初始化辅助库
	bl31_lib_init();

#if EL3_EXCEPTION_HANDLING
	//初始化异常处理框架(EXCEPTION_HANDLING_FRAMEWORK) 
	INFO("BL31: Initialising Exception Handling Framework\n");
	ehf_init();
#endif

	/* Initialize the runtime services e.g. psci. */
	//初始化运行时服务,如PSCI
	// PSCI(Power State Coordination Interface):负责 CPU 启动、关闭、挂起等
	// SPD(Secure Payload Dispatcher):调度 OP-TEE(BL32)
	// SPMC(Secure Partition Manager Component):用于 FF-A 架构
	// RMM(Realm Management Monitor):用于 CCA(Confidential Compute Architecture)
	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.
	 */
  //所有在主 CPU 上的冷启动操作已经完成。
  //我们现在需要决定下一个要运行的镜像以及如何执行它。

  //如果存在 SPD(Secure Payload Dispatcher)运行时服务,
  //它会希望先将控制权交给运行在 S-EL1 的 BL32。
  //在这种情况下,SPD 会注册一个用于初始化 BL32 的函数,
  //该函数负责进入 S-EL1 并在完成后将控制权交还给 bl31_main。
  //同样地,如果启用了 RME(Realm Management Extension)
  //并注册了用于初始化 RMM(Realm Management Monitor)的函数,
  //则会将控制权转移到运行在 R-EL2 的 RMM。RMM 初始化完成后,
  //控制权也会返回到 bl31_main。完成这些步骤后,我们就可以像往常一样准备进入 BL33。
  
  // S-EL安全世界 中运行的异常等级。
  // R-EL普通世界 中运行的异常等级。
	/*
	 * If SPD had registered an init hook, invoke it.
	 */
	if (bl32_init != NULL) {
		INFO("BL31: Initializing BL32\n");

		console_flush();
		int32_t rc = (*bl32_init)();

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

	/*
	 * If RME is enabled and init hook is registered, initialize RMM
	 * in R-EL2.
	 */
#if ENABLE_RME
	if (rmm_init != NULL) {
		INFO("BL31: Initializing RMM\n");

		console_flush();
		int32_t rc = (*rmm_init)();

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

	/*
	 * We are ready to enter the next EL. Prepare entry into the image
	 * corresponding to the desired security state after the next ERET.
	 */
	//我们已经准备好进入下一个异常级别。
	//请准备在下一次 ERET 指令后,进入对应目标安全状态的镜像 
	bl31_prepare_next_image_entry();

	/*
	 * Perform any platform specific runtime setup prior to cold boot exit
	 * from BL31
	 */
	// 在从 BL31 冷启动退出之前,执行任何必要的平台特定运行时设置
	bl31_plat_runtime_setup();

#if ENABLE_RUNTIME_INSTRUMENTATION
	console_flush();  //运行时性能检测
	PMF_CAPTURE_TIMESTAMP(bl_svc, BL31_EXIT, PMF_CACHE_MAINT);
#endif
  //切换控制台状态并刷新输出
	console_flush();
	console_switch_state(CONSOLE_FLAG_RUNTIME);  //将控制台切换到“运行时”模式
}

runtime_svc_init函数会将各种安全监控模式调用的处理函数的指针注册到EL3中,并通过service init函数来进行初始化,将TEE OS镜像的入口函数赋值给bl32_init,通过执行bl32_init指向的函数进入到TEE OS的启动过程。待TEE OS启动完成之后就会去查找bl33的镜像文件,即REE侧的镜像文件,开始进入REE侧镜像的启动。

runtime_svc_init函数

该函数主要用来建立安全监控模式调用处理函数的索引表,并执行EL3中提供的服务项的初始化操作,获取TEE OS的入口地址并赋值给bl32_init变量,以备启动TEE OS。而这些处理函数是通过DECLARE_RT_SVC宏定义被编译到镜像文件的rt_svc_descs段中的。

/root/optee/trusted-firmware-a/common/runtime_svc.c

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 */
        //检查是否描述符的结束地址 ≥ 开始地址
        //检查是否注册的服务数量 < MAX_RT_SVCS(防止数组越界)
        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.
                 */
                // 为每个服务分配一个OEN范围,用来处理所有的SMC调用
                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;
                }
        }
}                

DECLARE_RT_SVC

该宏用来在编译时将EL3中的service编译进rt_svc_descs段中。该宏定义如下:

/root/optee/trusted-firmware-a/include/common/runtime_svc.h

/*
 * Convenience macros to declare a service descriptor
 */
// ##:将 __svc_desc_ 和 _name 拼接成一个唯一变量名。
// #:将 __svc_desc_ 和 _name 拼接成一个唯一变量名。 
// .rt_svc_descs: 将该变量放入名为 .rt_svc_descs 的自定义链接段(section)中。
// __used:告诉编译器“这个变量虽然可能没被显式引用,但也别优化掉”
#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)                               \
                }

该宏中的各种参数说明如下:

  • start_oen:该service的起始内部编号
  • end.oen:该service的末尾编号
  • call_type:调用的smc的类型
  • name:该service的名字
  • init:该service在执行之前需要被执行的初始化操作
  • handle:当触发了call type的调用时调用的处理该请求的函数

REE侧镜像文件的启动

在bl31_main中启动完TEE OS之后通过调用bl31_prepare_next_image_entry函数来获取下一个阶段需要被加载的镜像文件,即REE侧的镜像文件,并配置好REE侧镜像的运行环境。bl31_main执行完成之后会跳转到bl31_entrypoint中继续执行,计算出需要被加载的镜像文件的数据段大小和起始地址并清空BSS端中的数据,从EL3进入到EL1-NS开始执行REE侧的代码。

TEE侧的加载在bl31_prepare_next_image_entry前,通过runtime_svc_init初始化的函数进行加载,将在下一节进行介绍。

参考资料:

  • 《手机安全和可信应用开发指南:TrustZone与OP-TEE技术详解》

网站公告

今日签到

点亮在社区的每一天
去签到