☰
冷启动 vs 热启动2——CPU1 如何上线
2026/10/30 6:32:43 网站建设 项目流程

系列文章目录


文章目录

  • 系列文章目录
  • 1. 引言
  • 2. Linux 什么时候触发 CPU1
  • 3. TF-A 在 EL3 处理 PSCI `CPU_ON`
    • 3.1 CPU0 保存入口并唤醒 CPU1
    • 3.2 CPU1 进入 BL31 warm path
    • 3.3 opteed 把 CPU1 送入 OP-TEE
  • 4. OP-TEE 初始化 CPU1 并返回 Linux
    • 4.1 汇编入口:打开本核 MMU 并切换 per-core 栈
    • 4.2 `init_secondary_helper()` 初始化 per-CPU 状态
    • 4.3 通过 EL3 返回 Linux `secondary_entry`
  • 5. 完整流程总结
    • 调用链

1. 引言

上一篇《冷启动 vs 热启动——mailbox 机制与 CPU1 唤醒》分析了 cold boot 与 warm boot 的入口选择。冷启动时,primary CPU 继续执行完整启动流程,secondary CPU 则进入plat_secondary_cold_boot_setup():真实硬件可以在这里将 secondary CPU 下电,QEMU 则让 CPU1 停住等待。

本篇从这个状态继续分析 CPU1 的上线流程:Linux 发起 PSCICPU_ON,TF-A 唤醒 CPU1 并执行 BL31 warm path,OP-TEE 完成CPU1初始化,最后返回 Linux 的secondary_entry。

2. Linux 什么时候触发 CPU1

入口位于linux/arch/arm64/kernel/psci.c:

staticintcpu_psci_cpu_boot(unsignedintcpu){phys_addr_tpa_secondary_entry=__pa_symbol(secondary_entry);interr=psci_ops.cpu_on(cpu_logical_map(cpu),pa_secondary_entry);if(err&&err!=-EPERM)pr_err("failed to boot CPU%d (%d)\n",cpu,err);returnerr;}

从psci_ops.cpu_on()到执行 SMC 的调用关系如下:

cpu_psci_cpu_boot(1) → psci_ops.cpu_on(MPIDR, PA(secondary_entry)) → psci_0_2_cpu_on() → __psci_cpu_on(PSCI_FN_NATIVE(0_2, CPU_ON), ...) → AArch64 下展开为 PSCI_0_2_FN64_CPU_ON → Function ID = 0xC4000003 → invoke_psci_fn(fn, cpuid, entry_point, 0) → __invoke_psci_fn_smc() → arm_smccc_smc()

PSCI_FN_NATIVE(0_2, CPU_ON)展开为PSCI_0_2_FN64_CPU_ON,对应0xC4000003。

Linux PSCI 驱动linux/drivers/firmware/psci/psci.c再执行:

invoke_psci_fn(fn,cpuid,entry_point,0);
static__always_inlineunsignedlong__invoke_psci_fn_smc(unsignedlongfunction_id,unsignedlongarg0,unsignedlongarg1,unsignedlongarg2){structarm_smccc_resres;arm_smccc_smc(function_id,arg0,arg1,arg2,0,0,0,0,&res);returnres.a0;}

上面smc call进入 EL3 时寄存器值及其含义为:

x0 = 0xC4000003 // PSCI_CPU_ON_AARCH64 x1 = CPU1 MPIDR // target_cpu x2 = PA(secondary_entry) // secondary cpu返回entry_point_address x3 = 0 // context_id,本仓库 Linux 传 0

3. TF-A 在 EL3 处理 PSCICPU_ON

3.1 CPU0 保存入口并唤醒 CPU1

PSCI runtime service 收到CPU_ON后进入psci_cpu_on_start(),代码位于trusted-firmware-a/lib/psci/psci_on.c。这一阶段运行在发起请求的 CPU0 上。

核心动作有三步:

  1. 检查 CPU1 当前必须为AFF_STATE_OFF。
  2. 保存 Linux 传入的entry_point_address和 context_id,供 CPU1 warm path 最后恢复 Non-secure 上下文。
  3. 把 CPU1 状态设为AFF_STATE_ON_PENDING,然后调用pwr_domain_on()。

状态先设为ON_PENDING很重要。CPU1 后面进入psci_warmboot_entrypoint()时,正是靠这个状态区分“刚被CPU_ON打开”和“从 CPU suspend 恢复”。

调用pwr_domain_on后,CPU0 从 PSCI SMC 返回 Linux,CPU1 则开始执行 warm path,两条路径随后并行继续。
讲上一篇中介绍的qemu_pwr_domain_on()会唤醒或者上电BL1中pending和下电的CPU,然后跳转到warm path!

3.2 CPU1 进入 BL31 warm path

CPU1 到达bl31_warm_entrypoint后,经过bl31_warmboot进入trusted-firmware-a/lib/psci/psci_common.c中的psci_warmboot_entrypoint(cpu_idx=1)。该函数读取 CPU1 的 affinity state:

if(psci_get_aff_info_state()==AFF_STATE_ON_PENDING)psci_cpu_on_finish(cpu_idx,&state_info);elsepsci_cpu_suspend_to_powerdown_finish(...);

当前状态是 CPU0 在第 3.1 节设置的AFF_STATE_ON_PENDING,所以进入psci_cpu_on_finish()。TF-A 在 CPU1 上完成:

  • QEMU 平台pwr_domain_on_finish();
  • GIC per-CPU 初始化和 CPU interface 使能;
  • psci_arch_setup();
  • 调用 SPD 注册的svc_on_finish。

3.3 opteed 把 CPU1 送入 OP-TEE

opteed_cpu_on_finish_handler()位于trusted-firmware-a/services/spd/opteed/opteed_pm.c,它在 CPU1/EL3 上运行:

voidopteed_cpu_on_finish_handler(u_register_tunused){uint32_tlinear_id=plat_my_core_pos();optee_context_t*optee_ctx=&opteed_sp_context[linear_id];entry_point_info_toptee_on_entrypoint;opteed_init_optee_ep_state(&optee_on_entrypoint,opteed_rw,(uint64_t)&optee_vector_table->cpu_on_entry,0,0,0,0,optee_ctx);cm_init_my_context(&optee_on_entrypoint);rc=opteed_synchronous_sp_entry(optee_ctx);set_optee_pstate(optee_ctx->state,OPTEE_PSTATE_ON);}

这里的optee_vector_table是 CPU0 首次完成 OP-TEE 初始化时,通过TEESMC_OPTEED_RETURN_ENTRY_DONE交给 BL31 的thread_vector_table。其中cpu_on_entry专门用于 secondary CPU 上线。

opteed 的工作可以概括为:

  1. 选择opteed_sp_context[1],即 CPU1 的 Secure payload 上下文。
  2. 把 S-EL1 入口设为optee_vector_table->cpu_on_entry。
  3. 初始化 CPU1 的 Secure context。
  4. opteed_synchronous_sp_entry()保存 EL3 C 调用现场并 ERET 到 OP-TEE。
  5. 等 OP-TEE 用特定 SMC 报告完成,再从同步调用中返回。

4. OP-TEE 初始化 CPU1 并返回 Linux

CPU1 进入 OP-TEE 后的整体处理流程如下:

4.1 汇编入口:打开本核 MMU 并切换 per-core 栈

cpu_on_entry对应optee_os/core/arch/arm/kernel/thread_optee_smc_a64.S中的vector_cpu_on_entry:

LOCAL_FUNC vector_cpu_on_entry , : , .identity_map bl cpu_on_handler mov x1, x0 ldr x0, =TEESMC_OPTEED_RETURN_ON_DONE smc #0 END_FUNC vector_cpu_on_entry

入口位于.identity_map,因为 CPU1 刚进入 OP-TEE 时 S-EL1 MMU 还没打开。它先调用entry_a64.S的cpu_on_handler。

cpu_on_handler位于optee_os/core/arch/arm/kernel/entry_a64.S。下面只保留当前CFG_DYN_CONFIG=y配置下的关键代码:

/* 设置 MMU 打开前使用的临时异常向量 */ adr x0, reset_vect_table msr vbar_el1, x0 set_sctlr_el1 init_pan msr daifclr, #DAIFBIT_ABT /* CPU1 上 __get_core_pos() 返回 1,据此打开本核 MMU */ bl __get_core_pos bl enable_mmu /* 定位 CPU1 的 thread_core_local */ bl __get_core_pos adr_l x1, thread_core_local ldr x1, [x1] mov x2, #THREAD_CORE_LOCAL_SIZE madd x3, x2, x0, x1 /* 设置 CPU1 的临时栈和 SP_EL1 */ ldr x0, [x3, #THREAD_CORE_LOCAL_TMP_STACK_VA_END] mov sp, x0 msr spsel, #1 mov sp, x3 msr spsel, #0 b boot_cpu_on_handler

代码先建立临时异常环境并打开本核 MMU,再选择 CPU1 的 core-local 状态和栈,最后进入boot_cpu_on_handler()。

enable_mmu(1)不是重新建立整个 OP-TEE 地址空间。页表和全局内存布局已经由 CPU0 冷启动建立;这里为 CPU1 装载该核的 MMU 寄存器并打开SCTLR_EL1.M。

随后必须切换到 CPU1 对应的thread_core_local和 per-CPU 临时栈。正常运行时的 OP-TEE thread stack 则按线程分配,并不固定属于某个 CPU。

4.2init_secondary_helper()初始化 per-CPU 状态

boot_cpu_on_handler()位于optee_os/core/arch/arm/kernel/boot.c,当前非 FF-A 路径直接调用:

unsignedlongboot_cpu_on_handler(unsignedlonga0,unsignedlonga1){init_secondary_helper();return0;}

init_secondary_helper()就是两条日志之间的主体:

staticvoidinit_secondary_helper(void){IMSG("Secondary CPU %zu initializing",get_core_pos());thread_set_exceptions(THREAD_EXCP_ALL);secondary_init_cntfrq();thread_init_per_cpu();boot_secondary_init_intc();init_vfp_sec();init_vfp_nsec();IMSG("Secondary CPU %zu switching to normal world boot",get_core_pos());}

各调用的作用如下:

调用初始化内容为什么是 per-CPU
thread_set_exceptions(THREAD_EXCP_ALL)初始化期间先屏蔽本核异常DAIF等异常掩码是本核状态
secondary_init_cntfrq()检查或设置本核CNTFRQ_EL0系统计数器频率寄存器按 PE 使用
thread_init_per_cpu()设置本核运行时异常向量和线程 core-local 状态VBAR_EL1、线程状态属于当前核
boot_secondary_init_intc()初始化 Secure interrupt controller 的 CPU interfaceGIC CPU interface 是 per-CPU
init_vfp_sec()初始化 Secure 状态下的 VFP/SIMD 控制状态浮点控制寄存器属于当前核
init_vfp_nsec()准备 Non-secure VFP/SIMD 状态返回 Linux 前要建立正确的 NS 状态

CPU1 不会重跑 CPU0 的全局初始化,它只补齐当前核进入 Secure/Normal World 所需的 per-CPU 状态。

4.3 通过 EL3 返回 Linuxsecondary_entry

init_secondary_helper()返回 0 后,vector_cpu_on_entry设置:

x1 = 0 x0 = TEESMC_OPTEED_RETURN_ON_DONE smc #0

这个 SMC 不是 Linux 调 OP-TEE 的普通请求,而是 OP-TEE 向 opteed 报告“CPU_ON 初始化完成”。trusted-firmware-a/services/spd/opteed/opteed_main.c处理该 Function ID:

caseTEESMC_OPTEED_RETURN_ON_DONE:opteed_synchronous_sp_exit(optee_ctx,x1);break;

opteed_synchronous_sp_exit()恢复第 3.3 节保存的 EL3 C 调用现场,使opteed_synchronous_sp_entry()返回。返回值x1=0表示成功,随后 opteed 把 CPU1 的 payload 状态设为OPTEE_PSTATE_ON。

接着回到psci_warmboot_entrypoint()。TF-A 读取 CPU0 处理CPU_ON时保存的warmboot_ep_info,恢复 CPU1 的 Non-secure EL1 上下文,并执行:

cm_prepare_el3_exit_ns() → ERET → Linux PA(secondary_entry) → secondary_start_kernel()

到这里,Linux 才会打印:

CPU1: Booted secondary processor ...

5. 完整流程总结

调用链

CPU0 / Linux EL1 __cpu_up() → cpu_psci_cpu_boot(1) → psci_ops.cpu_on(MPIDR, PA(secondary_entry)) → SMC 0xC4000003 CPU0 / TF-A EL3 psci_cpu_on_start() → 保存 warmboot_ep_info → AFF_STATE_ON_PENDING → qemu_pwr_domain_on() → hold_base[1] = GO; sev() CPU1 / TF-A EL3 poll_mailbox → bl31_warm_entrypoint → psci_warmboot_entrypoint(1) → psci_cpu_on_finish() → opteed_cpu_on_finish_handler() CPU1 / OP-TEE S-EL1 vector_cpu_on_entry → cpu_on_handler → enable_mmu(1) → boot_cpu_on_handler() → init_secondary_helper() → SMC RETURN_ON_DONE CPU1 / TF-A EL3 → Linux EL1 opteed_synchronous_sp_exit() → OPTEE_PSTATE_ON → cm_prepare_el3_exit_ns() → ERET PA(secondary_entry) → secondary_start_kernel()

本文主要介绍了CPU1的初始化流程:Linux 通过 PSCICPU_ON启动 CPU1。TF-A 保存 Linux 的secondary_entry,完成 CPU1 的 EL3 和 GIC 初始化,再由 opteed 进入 OP-TEE;OP-TEE 补齐本核的 MMU、栈、线程、中断和 VFP 等 per-CPU 状态,最后经 EL3 返回 Linux。
CPU1 上线不会重跑完整安全启动,而是在现有 BL31 和 OP-TEE 全局状态上完成本核初始化。其它如果支持更多核的系统,cpu2/cpu3等初始化也遵循相同流程。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询