kernel通过psci的smc让从cpu boot的过程

kernel会调用smp_init 来让从核cpu 起来.
void __init smp_init(void)
{
    unsigned int cpu;

    idle_threads_init();
    cpuhp_threads_init();

    /* FIXME: This should be done in userspace --RR */
    for_each_present_cpu(cpu) {
        if (num_online_cpus() >= setup_max_cpus)
            break;
        if (!cpu_online(cpu))
            cpu_up(cpu);
    }

    /* Any cleanup work */
    smp_announce();
    smp_cpus_done(setup_max_cpus);
}
在smp_init 中会通过for_each_present_cpu,让每一个present的cpu wakeup起来。其会调用cpu_up
int cpu_up(unsigned int cpu)
{
    return do_cpu_up(cpu, CPUHP_ONLINE);
}

继续调用do_cpu_up,这里传递的参数是CPUHP_ONLINE
static int do_cpu_up(unsigned int cpu, enum cpuhp_state target)
{
    int err = 0;

    if (!cpu_possible(cpu)) {
        pr_err("can't online cpu %d because it is not configured as may-hotadd at boot time\n",
               cpu);
#if defined(CONFIG_IA64)
        pr_err("please check additional_cpus= boot parameter\n");
#endif
        return -EINVAL;
    }

    err = try_online_node(cpu_to_node(cpu));
    if (err)
        return err;

    cpu_maps_update_begin();

    if (cpu_hotplug_disabled) {
        err = -EBUSY;
        goto out;
    }

    err = _cpu_up(cpu, 0, target);
out:
    cpu_maps_update_done();
    return err;
}
在do_cpu_up 中的target就是CPUHP_ONLINE,继续调用_cpu_up
static int _cpu_up(unsigned int cpu, int tasks_frozen, enum cpuhp_state target)
{
    struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu);
    struct task_struct *idle;
    int ret = 0;

    cpu_hotplug_begin();

    if (!cpu_present(cpu)) {
        ret = -EINVAL;
        goto out;
    }

    /*
     * The caller of do_cpu_up might have raced with another
     * caller. Ignore it for now.
     */
    if (st->state >= target)
        goto out;

    if (st->state == CPUHP_OFFLINE) {
        /* Let it fail before we try to bring the cpu up */
        idle = idle_thread_get(cpu);
        if (IS_ERR(idle)) {
            ret = PTR_ERR(idle);
            goto out;
        }
    }

    cpuhp_tasks_frozen = tasks_frozen;

    st->target = target;
    /*
     * If the current CPU state is in the range of the AP hotplug thread,
     * then we need to kick the thread once more.
     */
    if (st->state > CPUHP_BRINGUP_CPU) {
        ret = cpuhp_kick_ap_work(cpu);
        /*
         * The AP side has done the error rollback already. Just
         * return the error code..
         */
        if (ret)
            goto out;
    }

    /*
     * Try to reach the target state. We max out on the BP at
     * CPUHP_BRINGUP_CPU. After that the AP hotplug thread is
     * responsible for bringing it up to the target state.
     */
这里的target是CPUHP_ONLINE。经过min((int)target, CPUHP_BRINGUP_CPU)之后target就变成CPUHP_BRINGUP_CPU。取两者的最小值
    target = min((int)target, CPUHP_BRINGUP_CPU);
    ret = cpuhp_up_callbacks(cpu, st, target);
out:
    cpu_hotplug_done();
    return ret;
}

这里CPUHP_BRINGUP_CPU 对应的处理函数为bringup_cpu
    [CPUHP_BRINGUP_CPU] = {
        .name            = "cpu:bringup",
        .startup.single        = bringup_cpu,
        .teardown.single    = NULL,
        .cant_stop        = true,
    },
static int bringup_cpu(unsigned int cpu)
{
    struct task_struct *idle = idle_thread_get(cpu);
    int ret;

    /*
     * Some architectures have to walk the irq descriptors to
     * setup the vector space for the cpu which comes online.
     * Prevent irq alloc/free across the bringup.
     */
    irq_lock_sparse();

    /* Arch-specific enabling code. */
    ret = __cpu_up(cpu, idle);
    irq_unlock_sparse();
    if (ret) {
        cpu_notify(CPU_UP_CANCELED, cpu);
        return ret;
    }
    ret = bringup_wait_for_ap(cpu);
    BUG_ON(!cpu_online(cpu));
    return ret;
}
继续调用__cpu_up->boot_secondary
static int boot_secondary(unsigned int cpu, struct task_struct *idle)
{
    if (cpu_ops[cpu]->cpu_boot)
        return cpu_ops[cpu]->cpu_boot(cpu);

    return -EOPNOTSUPP;
}
这里会直接调用cpu_ops的cpu_boot
而针对arm64 而言,
const struct cpu_operations cpu_psci_ops = {
    .name        = "psci",
#ifdef CONFIG_CPU_IDLE
    .cpu_init_idle    = psci_cpu_init_idle,
    .cpu_suspend    = psci_cpu_suspend_enter,
#endif
    .cpu_init    = cpu_psci_cpu_init,
    .cpu_prepare    = cpu_psci_cpu_prepare,
    .cpu_boot    = cpu_psci_cpu_boot,
#ifdef CONFIG_HOTPLUG_CPU
    .cpu_disable    = cpu_psci_cpu_disable,
    .cpu_die    = cpu_psci_cpu_die,
    .cpu_kill    = cpu_psci_cpu_kill,
#endif
};
cpu_boot 对应的处理函数是cpu_psci_cpu_boot
static int cpu_psci_cpu_boot(unsigned int cpu)
{
    int err = psci_ops.cpu_on(cpu_logical_map(cpu), __pa(secondary_entry));
    if (err)
        pr_err("failed to boot CPU%d (%d)\n", cpu, err);

    return err;
}
这里有调用psci_ops.cpu_on。
static void __init psci_0_2_set_functions(void)
{
    pr_info("Using standard PSCI v0.2 function IDs\n");
    psci_function_id[PSCI_FN_CPU_SUSPEND] =
                    PSCI_FN_NATIVE(0_2, CPU_SUSPEND);
    psci_ops.cpu_suspend = psci_cpu_suspend;

    psci_function_id[PSCI_FN_CPU_OFF] = PSCI_0_2_FN_CPU_OFF;
    psci_ops.cpu_off = psci_cpu_off;

    psci_function_id[PSCI_FN_CPU_ON] = PSCI_FN_NATIVE(0_2, CPU_ON);
    psci_ops.cpu_on = psci_cpu_on;

    psci_function_id[PSCI_FN_MIGRATE] = PSCI_FN_NATIVE(0_2, MIGRATE);
    psci_ops.migrate = psci_migrate;

    psci_ops.affinity_info = psci_affinity_info;

    psci_ops.migrate_info_type = psci_migrate_info_type;

    arm_pm_restart = psci_sys_reset;

    pm_power_off = psci_sys_poweroff;
}
psci_ops.cpu_on 对应的处理函数是psci_cpu_on
static int psci_cpu_on(unsigned long cpuid, unsigned long entry_point)
{
    int err;
    u32 fn;

    fn = psci_function_id[PSCI_FN_CPU_ON];
    err = invoke_psci_fn(fn, cpuid, entry_point, 0);
    return psci_to_linux_errno(err);
}
psci_cpu_on 中调用invoke_psci_fn 来发送smc 给atf.
而invoke_psci_fn是在get_set_conduit_method 中是遏制,可见和atf通信可以分为hvc和smc。我们这里选用smc

static int get_set_conduit_method(struct device_node *np)
{
    const char *method;

    pr_info("probing for conduit method from DT.\n");

    if (of_property_read_string(np, "method", &method)) {
        pr_warn("missing \"method\" property\n");
        return -ENXIO;
    }

    if (!strcmp("hvc", method)) {
        invoke_psci_fn = __invoke_psci_fn_hvc;
    } else if (!strcmp("smc", method)) {
        invoke_psci_fn = __invoke_psci_fn_smc;
    } else {
        pr_warn("invalid \"method\" property: %s\n", method);
        return -EINVAL;
    }
    return 0;
}
static unsigned long __invoke_psci_fn_smc(unsigned long function_id,
            unsigned long arg0, unsigned long arg1,
            unsigned long arg2)
{
    struct arm_smccc_res res;

    arm_smccc_smc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res);
    return res.a0;
}

而arm_smccc_smc是定义在arch/arm64/kernel/smccc-call.S中。
 .macro SMCCC instr
     .cfi_startproc
     \instr  #0
     ldr x4, [sp]
     stp x0, x1, [x4, #ARM_SMCCC_RES_X0_OFFS]
     stp x2, x3, [x4, #ARM_SMCCC_RES_X2_OFFS]
     ret
     .cfi_endproc
     .endm
 
 /*
  * void arm_smccc_smc(unsigned long a0, unsigned long a1, unsigned long a2,
  *        unsigned long a3, unsigned long a4, unsigned long a5,
  *        unsigned long a6, unsigned long a7, struct arm_smccc_res *res)
  */
 ENTRY(arm_smccc_smc)
     SMCCC   smc
 ENDPROC(arm_smccc_smc)
调用arm_smccc_smc后就会陷入到el3的atf
  • 1
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值