基于原生 entry_a64.S 的深入分析)
基于原生entry_a64.S的深入分析这是 AArch64 架构下 OP-TEE 核心态的冷启动汇编入口文件路径为 core/arch/arm/kernel/entry_a64.S符号 _start 与 kern.ld.S 中 ENTRY(_start) 严格对应——TF-A 的 BL31 跳转到 BL32 后执行的第一条指令就从这里开始。整个启动流程严格遵循「物理地址早期初始化 → 镜像重排与内存准备 → 开启MMU进入虚拟地址 → C语言分层初始化 → 返回向量表完成注册」的顺序所有符号、段与 kern.ld.S 链接脚本一一对应。一、启动总览与核心阶段划分_start 函数全程在 S-EL1 安全异常等级执行分为 10 个核心阶段从纯物理地址的裸机状态逐步初始化到可接收正常世界调用的稳态TF-A 跳转进入 _start ↓ 1. 寄存器与异常向量早期配置物理地址关MMU ↓ 2. 分页模式专属镜像分段重定位init段拷贝 ↓ 3. BSS段清零、重定位、安全特性早期初始化 ↓ 4. 栈初始化、异常使能 ↓ 5. Cache维护、串口初始化 ↓ 6. 内存管理器初始化、MMU页表构建 ↓ 7. 开启MMU切换到虚拟地址运行 ↓ 8. C语言三层初始化early/late/runtime ↓ 9. 安全特性收尾、栈保护更新 ↓ 10. SMC返回向量表给BL31进入空闲等待二、逐阶段源码深度解析阶段 1入口与早期寄存器配置物理地址运行对应代码_start 函数开头到 set_sctlr_el11.保存启动入参mov x19, x0 mov x20, x1 mov x21, x2 mov x22, x3TF-A 跳转时通过 x0-x3 传入启动参数可分页段地址、设备树指针、安全内存大小等先存入 x19-x22 被调用者保存寄存器避免后续函数调用被覆盖后续传给 boot_save_args 统一保存。2.挂载临时异常向量表adr x0, reset_vect_table msr vbar_el1, x0将 reset_vect_table 写入 VBAR_EL1先挂一套最简异常向量默认死循环防止启动过程中出现异常直接跑飞。初始化完成后会替换为完整异常处理向量。3.硬件安全特性早期配置init_pan可选开启特权访问永不Privileged Access Never防止内核态意外访问用户态内存阻断部分漏洞利用路径。set_sctlr_el1配置系统控制寄存器 SCTLR_EL1是安全加固的核心硬件配置开启 SCTLR_I指令缓存 ICache开启 SCTLR_SA栈对齐检查开启 SCTLR_SPANPAN 生效位CFG_CORE_RWDATA_NOEXEC 开启时置位 SCTLR_WXN所有可写内存不可执行硬件级 NX 防护防止代码注入可选开启 MTE 内存标签、BTI 分支目标标识、PAUTH 指针认证等 ARMv8.5 安全特性阶段 2分页模式专属镜像分段重定位对应代码#ifdef CFG_WITH_PAGER 分支也就是你使用 tee-pager_v2.bin 的核心逻辑分页镜像的文件布局tee-pager_v2.bin 在 Flash/内存中的原始排布是[Pager常驻代码/数据] → 已在正确链接地址 [Init初始化段] → 需要拷贝到 __init_start 位置 [boot_embdata 元数据] → 哈希、重定位表等附加数据刚加载到内存时init 段紧接在常驻段后面不在链接脚本指定的 __init_start 地址需要手动搬运。拷贝逻辑adr x0, __init_start // 目标地址 adr x1, __data_end // 源地址常驻段结束init段起始 adr x2, __init_end sub x2, x2, x0 // init段长度 ldr w4, [x1, x2] // boot_embdata 总长度 add x2, x2, x4 // 总拷贝长度 init段 元数据采用倒序拷贝从后往前类似 memmove避免源地址和目标地址重叠时数据被覆盖。最后将内存末尾保存到 boot_cached_mem_end供后续刷 Cache 使用。非分页模式下逻辑简化仅将 boot_embdata 元数据搬到空闲内存末尾不做整段重排。阶段 3内存初始化与安全前置1.BSS 段清零adr_l x0, __bss_start adr_l x1, __bss_end clear_bss: str xzr, [x0], #8 cmp x0, x1 b.lt clear_bss按 8 字节步长清零所有未初始化全局变量防止残留随机敏感数据同时保证变量初始值为 0 的语义正确。虚拟化场景下还会额外清零 .nex_bss 分区段。2.物理重定位可选开启 CFG_CORE_PHYS_RELOCATABLE 时计算实际加载地址与编译链接地址的偏移执行 relocate 函数做重定位支持镜像加载到任意物理地址。3.ASAN 影子内存初始化可选开启地址消毒时初始化影子内存区域标记栈空间为合法访问启动期就开启内存越界检测。阶段 4栈初始化与异常使能1.set_sp宏双栈设置OP-TEE 采用 SP_EL0 SP_EL1 双栈设计先获取 CPU 核号超过配置的最大核数则进入 unhandled_cpu 死循环挂起防止异常核破坏系统SP_EL0临时运行栈每个核按 stack_tmp_stride 偏移预留栈守护区SP_EL1指向 thread_core_local[cpu_id]每个核的私有核心数据结构存放线程上下文、栈信息、异常信息2.初始化核心私有结构填充 thread_core_local 的临时栈、中止栈、当前线程ID、标志位为异常处理准备环境。3.开异常msr daifclr, #DAIFBIT_ABT之前为了防止启动中异常崩溃一直关闭中止异常现在栈和异常向量都就绪了使能数据/指令中止异常支持缺页、访问权限错误等异常处理。/* Enable Console */ bl console_init阶段 5Cache 维护与串口初始化1.全量刷 D-Cacheadr_l x0, __text_start adr_l x1, boot_cached_mem_end ldr x1, [x1] sub x1, x1, x0 bl dcache_cleaninv_range对启动阶段 touched 的所有内存做 Cache 清洁无效化保证后续开启 Cache 时物理内存与 Cache 数据一致避免和 TF-A 的 Cache 操作产生一致性问题。2.串口初始化调用 console_init 初始化 PL011 串口至此可以输出启动日志进入可调试状态。/* Enable Console */ bl console_init阶段 6内存与 MMU 页表初始化1.保存启动参数调用 boot_save_args把最开始保存的 x19-x22 入参持久化到全局变量供后续初始化流程使用。mov x0, x19 mov x1, x20 mov x2, x21 mov x3, x22 mov x4, xzr bl boot_save_args2.内存管理器初始化#ifdef CFG_WITH_PAGER adr_l x0, __init_end /* pointer to boot_embdata */ ldr w1, [x0] /* struct boot_embdata::total_len */ add x0, x0, x1 add x0, x0, #0xfff /* round up */ bic x0, x0, #0xfff /* to next page */ mov_imm x1, (TEE_RAM_PH_SIZE TEE_RAM_START) mov x2, x1 #else adr_l x0, __vcore_free_start adr_l x1, boot_embdata_ptr ldr x1, [x1] #ifdef CFG_DYN_CONFIG sub x1, x1, #THREAD_BOOT_INIT_TMP_ALLOC #endif adr_l x2, __vcore_free_end; #endif bl boot_mem_init调用 boot_mem_init传入可用内存起止地址分页模式从 __init_end 之后到安全内存末尾非分页模式空闲内存区域初始化内核堆、TA 内存池、分页管理结构。3.构建 MMU 页表adr x1, boot_mmu_config bl core_init_mmu_map调用 C 语言函数构建完整页表基于链接脚本导出的 __vcore_* 段边界符号按段设置内存权限代码段 RX、数据段 RW、空闲段不可访问。开启 ASLR 时会在此处生成随机地址偏移。阶段 7开启 MMU切换虚拟地址对应函数enable_mmu位于 .identity_map 恒等映射段这是启动最核心的临界区该段代码的物理地址 虚拟地址保证开启 MMU 的瞬间不会因为地址跳变而跑飞和 kern.ld.S 中的 .identity_map 段严格对应。执行步骤加载 boot_mmu_config 配置写入 TCR_EL1内存转换控制、MAIR_EL1内存属性、TTBR0_EL1页表基地址全量无效化 TLB保证旧映射不干扰置位 SCTLR_EL1.M正式开启 MMU更新 VBAR_EL1 异常向量表到虚拟地址无效化 ICache、分支预测器开启 I-Cache 和 D-Cache调整 SP_EL0/SP_EL1 栈指针、返回地址 LR加上虚拟地址偏移保证返回后地址正确这一步完成后CPU 从物理地址模式切换到虚拟地址模式后续所有代码都运行在虚拟地址空间。阶段 8MMU 开启后收尾更新 thread_core_local 中的栈地址为虚拟地址执行 boot_mem_relocate更新内存分配器的虚拟地址偏移重新初始化串口之前用物理地址注册现在切换为虚拟地址保证日志正常输出MTE 内存标签清零、虚拟化分区表初始化阶段 9四层主初始化函数执行阶段核心职责boot_init_primary_early早期硬件初始化GIC 中断控制器、安全定时器、平台外设早期初始化boot_init_primary_late内核核心初始化线程调度系统、密码学框架、SMC 调用接口、TA 加载器boot_init_primary_runtime服务与应用初始化内置 TA 验签加载、系统服务注册、会话管理初始化boot_init_primary_final启动收尾释放.text_init/.rodata_init初始化段内存到堆唤醒所有从核切换到空闲线程1.boot_init_primary_early()硬件底层初始化对应「硬件」阶段完成最基础的硬件外设配置GIC 中断控制器初始化配置分组、优先级、注册中断处理安全物理定时器初始化平台级外设早期初始化串口、时钟、电源域核间通信机制初始化2.boot_init_primary_late()内核核心初始化对应「内核」阶段初始化 OP-TEE 内核核心机制线程调度系统完整初始化线程控制块、调度器、空闲线程内存管理单元收尾配置分页机制完整初始化分页模式下异常向量表替换为完整处理向量栈保护、内存标签等安全特性收尾3.boot_init_primary_runtime()系统服务初始化对应「服务」阶段初始化核心安全服务密码学服务框架初始化软件算法库、硬件加密引擎SMC 调用接口初始化SMCCC 标准协议、功能分发TA 加载器 ldelf 初始化ELF 解析、验签、沙箱环境TEE 内部核心 API 初始化会话管理、对象管理、时间服务4.boot_init_primary_final()应用加载与启动收尾对应「应用」阶段加载可信应用并进入稳态内置 TA 批量加载逐个验签、重定位、创建沙箱实例初始化段内存回收释放 .text_init / .rodata_init 占用的内存叠加到内核堆唤醒所有从核进入多核心调度状态切换到空闲线程等待正常世界 SMC 调用这就是「硬件→内核→服务→应用」自底向上初始化顺序的真实体现初始化段内存释放是分页模式的核心优化——仅运行一次的代码启动后直接回收最小化常驻内存体积。阶段 10安全收尾与 TF-A 交互闭环1.栈保护金丝雀更新调用 plat_get_random_stack_canaries 生成随机数写入 __stack_chk_guard开启栈溢出检测符合 Android 安全编译规范。#ifdef _CFG_CORE_STACK_PROTECTOR /* Update stack canary value */ sub sp, sp, #0x10 mov x0, sp mov x1, #1 mov x2, #0x8 bl plat_get_random_stack_canaries ldr x0, [sp] adr_l x5, __stack_chk_guard str x0, [x5] add sp, sp, #0x10 #endif2.再次刷 Cache对所有启动期内存再次做 Cache 清洁无效化保证从核启动时Cache 关闭状态读到正确的物理内存数据避免多核 Cache 不一致。/* * In case weve touched memory that secondary CPUs will use before * they have turned on their D-cache, clean and invalidate the * D-cache before exiting to normal world. */ adr_l x0, __text_start adr_l x1, boot_cached_mem_end ldr x1, [x1] sub x1, x1, x0 bl dcache_cleaninv_range3.清除启动线程标记调用 thread_clr_boot_thread释放启动专用线程供后续调度复用。/* * Clear current thread id now to allow the thread to be reused on * next entry. Matches the thread_init_boot_thread in * boot.c. */ #ifndef CFG_NS_VIRTUALIZATION bl thread_clr_boot_thread #endif4.向 BL31 返回向量表完成启动/* * Pass the vector address returned from main_init Compensate for * the virtual map offset since cpu_on_handler() is called with MMU * off. */ ldr x0, boot_mmu_config CORE_MMU_CONFIG_MAP_OFFSET adr x1, thread_vector_table sub x1, x1, x0 mov x0, #TEESMC_OPTEED_RETURN_ENTRY_DONE smc #0 /* SMC should not return */ panic_at_smc_return通过 SMC 调用向 BL31 返回 OP-TEE 的线程向量表地址BL31 收到后完成安全世界注册。后续正常世界触发 SMC 调用时BL31 会根据向量表路由到 OP-TEE 对应处理函数该 SMC 不会返回OP-TEE 直接进入空闲线程等待调用三、补充从核启动入口cpu_on_handler主核初始化完成、唤醒从核后从核从 cpu_on_handler 入口启动流程是主核的简化版只做本核的异常向量、SCTLR、栈、MMU 初始化不重复执行全局硬件、内存、服务初始化最终调用 boot_cpu_on_handler 完成从核注册进入空闲线程参与调度四、与链接脚本kern.ld.S的对应关系所有地址符号都直接来自链接脚本是配置驱动内存布局的完整闭环源码符号链接脚本定义作用__text_start/__text_end.text段边界代码段起止Cache 维护、权限配置用__data_end.data段结束常驻段末尾分页模式 init 段的源地址__init_start/__init_end.text_init段边界初始化段目标地址__bss_start/__bss_end.bss段边界清零范围__vcore_free_start/__vcore_free_end空闲内存边界内存管理器可用范围.identity_map段恒等映射段开启 MMU 的临界区代码五、Android 安全视角启动期的安全设计安全左移WXN 不可执行、栈对齐检查、PAN、MTE 等硬件安全特性在启动最早期就全部开启整个初始化过程都在安全防护下执行最小化攻击面初始化段启动后立即释放常驻内存仅保留核心调度、异常处理、分页逻辑大幅减少攻击面内存完整性BSS 强制清零、Cache 全量维护、重定位校验避免内存残留与一致性问题引入的漏洞信任链连续从 BL31 跳转过来后第一时间完成安全配置整个启动过程在可信环境内执行信任链无断点