init/main.c
start_kernel
- >boot_cpu_init //引导cpu初始化 设置引导cpu的位掩码 online active present possible都为true
- >setup_arch // arch/arm64/kernel/setup.c
- > if (acpi_disabled) //不支持acpi
psci_dt_init(); //drivers/firmware/psci.c(psci主要文件) psci初始化 解析设备树 寻找psci匹配的节点
else
psci_acpi_init(); //acpi中允许使用psci情况
- >rest_init
- >kernel_init
- >kernel_init_freeable
- >smp_prepare_cpus //准备cpu 对于每个可能的cpu 1. cpu_ops[cpu]- >cpu_prepare(cpu) 2.set_cpu_present(cpu, true) cpu处于present状态
- >do_pre_smp_initcalls //多核启动之前的调用initcall回调
- >smp_init //smp初始化 kernel/smp.c 会启动其他从处理器
我们主要关注两个函数:psci_dt_init和smp_init psci_dt_init是解析设备树,设置操作函数,smp_init用于启动从处理器。
- >psci_dt_init() //drivers/firmware/psci.c:
- >init_fn()
- >psci_0_1_init() //设备树中compatible = "arm,psci"为例
- >get_set_conduit_method() //根据设备树method属性设置 invoke_psci_fn = __invoke_psci_fn_smc; (method="smc")
- > invoke_psci_fn = __invoke_psci_fn_smc
- > if (!of_property_read_u32(np, "cpu_on", &id)) {
651 psci_function_id[PSCI_FN_CPU_ON] = id;
652 psci_ops.cpu_on = psci_cpu_on; //设置psci操作的开核接口
653 }
- >psci_cpu_on()
- >invoke_psci_fn()
- >__invoke_psci_fn_smc()
- > arm_smccc_smc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res) //这个时候x0=function_id x1=arg0, x2=arg1, x3arg2,...
- >__arm_smccc_smc()
- >SMCCC smc //arch/arm64/kernel/smccc-call.S
- > 20 .macro SMCCC instr
21 .cfi_startproc
22 instr #0 //即是smc #0 陷入到el3
23 ldr x4, [sp]
24 stp x0, x1, [x4, #ARM_SMCCC_RES_X0_OFFS]
25 stp x2, x3, [x4, #ARM_SMCCC_RES_X2_OFFS]
26 ldr x4, [sp, #8]
27 cbz x4, 1f /* no quirk structure */
28 ldr x9, [x4, #ARM_SMCCC_QUIRK_ID_OFFS]
29 cmp x9, #ARM_SMCCC_QUIRK_QCOM_A6
30 b.ne 1f
31 str x6, [x4, ARM_SMCCC_QUIRK_STATE_OFFS]
32 1: ret
33 .cfi_endproc
34 .endm
最终通过22行 陷入了el3中。(这是因为安全所以还需要到ATF中启动)smp_init函数做从处理器启动:
start_kernel
- >arch_call_rest_init
- >rest_init
- >kernel_init,
- >kernel_init_freeable
- >smp_prepare_cpus //arch/arm64/kernel/smp.c
- >smp_init //kernel/smp.c (这是从处理器启动的函数)
- >cpu_up
- >do_cpu_up
- >_cpu_up
- >cpuhp_up_callbacks
- >cpuhp_invoke_callback
- >cpuhp_hp_states[CPUHP_BRINGUP_CPU]
- >bringup_cpu
- >__cpu_up //arch/arm64/kernel/smp.c
- >boot_secondary
- >cpu_ops[cpu]- >cpu_boot(cpu)
- >cpu_psci_ops.cpu_boot
- >cpu_psci_cpu_boot //arch/arm64/kernel/psci.c
46 static int cpu_psci_cpu_boot(unsigned int cpu)
47 {
48 int err = psci_ops.cpu_on(cpu_logical_map(cpu), __pa_symbol(secondary_entry));
49 if (err)
50 pr_err("failed to boot CPU%d (%d)n", cpu, err);
51
52 return err;
53 }
启动从处理的时候最终调用到psci的cpu操作集的cpu_psci_cpu_boot函数 ,会调用上面的psci_cpu_on,最终调用smc,传递第一个参数为cpu的id标识启动哪个cpu,第二个参数为从处理器启动后进入内核执行的地址secondary_entry(这是个物理地址)。
所以综上,最后smc调用时传递的参数为arm_smccc_smc(0xC4000003, cpuid, secondary_entry, arg2, 0, 0, 0, 0, &res)。这样陷入el3之后,就可以启动对应的从处理器, 最终从处理器回到内核(el3->el1),执行secondary_entry处指令 ,从处理器启动完成。
可以发现psci的方式启动从处理器的方式相当复杂,这里面涉及到了el1到安全的el3的跳转,而且涉及到大量的函数回调,很容易绕晕。
(其实为了安全,所以启动从核开核这个操作必须在EL3,开了以后,就可以会EL1,因为已经在EL3给你了准确安全的启动位置了。)
全部0条评论
快来发表一下你的评论吧 !