4.4.3. ARM64中断初始化

4.4.3.1. init_IRQ

ARM64的中断初始化主要在init_IRQ中完成,包括中断栈的初始化以及中断控制器的初始化

void __init init_IRQ(void)
{
    init_irq_stacks();  //中断栈的初始化,中断栈将从vmalloc区域分配,每个CPU的中断栈指针将保存在全局per-cpu变量irq_stack_ptr中
    irqchip_init();     //初始化irq控制器,并注册irq_domain
    if (!handle_arch_irq)
        panic("No interrupt controller found.");

    if (system_uses_irq_prio_masking()) {
        /*
         * Now that we have a stack for our IRQ handler, set
         * the PMR/PSR pair to a consistent state.
         */
        WARN_ON(read_sysreg(daif) & PSR_A_BIT);
        local_daif_restore(DAIF_PROCCTX_NOIRQ);
    }
}

void __init irqchip_init(void)
{
    //扫描__irqchip_of_table,匹配DTB中定义的中断控制器,匹配成功则调用中断控制器设置的初始化函数
    of_irq_init(__irqchip_of_table);
    acpi_probe_device_table(irqchip);
}

4.4.3.2. of_irq_init

首先看一下__irqchip_of_table的来历

//include/linux/of.h
#if defined(CONFIG_OF) && !defined(MODULE)
#define _OF_DECLARE(table, name, compat, fn, fn_type)                       \
    static const struct of_device_id __of_table_##name              \
        __used __section(__##table##_of_table)                      \
         = { .compatible = compat,                          \
             .data = (fn == (fn_type)NULL) ? fn : fn  }
#else
#define _OF_DECLARE(table, name, compat, fn, fn_type)                       \
    static const struct of_device_id __of_table_##name              \
        __attribute__((unused))                                     \
         = { .compatible = compat,                          \
             .data = (fn == (fn_type)NULL) ? fn : fn }
#endif

#define OF_DECLARE_1(table, name, compat, fn) \
        _OF_DECLARE(table, name, compat, fn, of_init_fn_1)
#define OF_DECLARE_1_RET(table, name, compat, fn) \
        _OF_DECLARE(table, name, compat, fn, of_init_fn_1_ret)
#define OF_DECLARE_2(table, name, compat, fn) \
        _OF_DECLARE(table, name, compat, fn, of_init_fn_2)

//include/linux/irqchip.h
#define IRQCHIP_DECLARE(name, compat, fn) OF_DECLARE_2(irqchip, name, compat, fn)

对于每种中断控制器,都会通过IRQCHIP_DECLARE静态声明自己的struct of_device_id 结构体。下面针对GIC-V4进行分析

IRQCHIP_DECLARE(gic_400, "arm,gic-400", gic_of_init);

等价于

static const struct of_device_id __of_table_gic-400  \
    __used __section("__irqchip_of_table")  \
    = { .compatible = "arm,gic-400" ,   \
        .data = gic_of_init }

不同的中断控制器有对应的IRQCHIP_DECLARE声明,他们都位于__irqchip_of_table段,of_irq_init就是遍历__irqchip_of_table段中所有的中断控制器, 对其执行相应的中断控制器初始化

void __init of_irq_init(const struct of_device_id *matches)
{
    const struct of_device_id *match;
    struct device_node *np, *parent = NULL;
    struct of_intc_desc *desc, *temp_desc;
    struct list_head intc_desc_list, intc_parent_list;

    INIT_LIST_HEAD(&intc_desc_list);    //初始化两个list,分别为dts节点中init-controller属性的list和init-controller-parent属性的list
    INIT_LIST_HEAD(&intc_parent_list);

    for_each_matching_node_and_match(np, matches, &match) {     //遍历每个中断控制器为其建立intc_dest,并设置每个中断控制器的parent
        if (!of_property_read_bool(np, "interrupt-controller") ||   //如果当前节点不是interrupt-controller或者处于disabled状态则继续遍历
                !of_device_is_available(np))
            continue;

        if (WARN(!match->data, "of_irq_init: no init function for %s\n",
             match->compatible))
            continue;

        /*
         * Here, we allocate and populate an of_intc_desc with the node
         * pointer, interrupt-parent device_node etc.
         */
        desc = kzalloc(sizeof(*desc), GFP_KERNEL);      //为desc分配空间
        if (!desc) {
            of_node_put(np);
            goto err;
        }

        desc->irq_init_cb = match->data;    //中断初始化回调,match->data就是上面的__of_table_gic-400.data
        desc->dev = of_node_get(np);
        desc->interrupt_parent = of_irq_find_parent(np);    //解析当前节点的parent
        if (desc->interrupt_parent == np)
            desc->interrupt_parent = NULL;
        list_add_tail(&desc->list, &intc_desc_list);
    }

    /*
     * The root irq controller is the one without an interrupt-parent.
     * That one goes first, followed by the controllers that reference it,
     * followed by the ones that reference the 2nd level controllers, etc.
     */
    while (!list_empty(&intc_desc_list)) {
        /*
         * Process all controllers with the current 'parent'.
         * First pass will be looking for NULL as the parent.
         * The assumption is that NULL parent means a root controller.
         */
        list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {
            int ret;

            if (desc->interrupt_parent != parent)
                continue;

            list_del(&desc->list);

            of_node_set_flag(desc->dev, OF_POPULATED);

            pr_debug("of_irq_init: init %pOF (%p), parent %p\n",
                 desc->dev,
                 desc->dev, desc->interrupt_parent);
            //执行中断初始化函数
            ret = desc->irq_init_cb(desc->dev,
                        desc->interrupt_parent);
            if (ret) {
                of_node_clear_flag(desc->dev, OF_POPULATED);
                kfree(desc);
                continue;
            }

            /*
             * This one is now set up; add it to the parent list so
             * its children can get processed in a subsequent pass.
             */
            list_add_tail(&desc->list, &intc_parent_list);
        }

        /* Get the next pending parent that might have children */
        desc = list_first_entry_or_null(&intc_parent_list,
                        typeof(*desc), list);
        if (!desc) {
            pr_err("of_irq_init: children remain, but no parents\n");
            break;
        }
        list_del(&desc->list);
        parent = desc->dev;
        kfree(desc);
    }

    list_for_each_entry_safe(desc, temp_desc, &intc_parent_list, list) {
        list_del(&desc->list);
        kfree(desc);
    }
err:
    list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {
        list_del(&desc->list);
        of_node_put(desc->dev);
        kfree(desc);
    }
}

4.4.3.3. gic_of_init

//drivers/irqchip/irq-gic.c
int __init
gic_of_init(struct device_node *node, struct device_node *parent)
{
    struct gic_chip_data *gic;
    int irq, ret;

    if (WARN_ON(!node))
        return -ENODEV;

    if (WARN_ON(gic_cnt >= CONFIG_ARM_GIC_MAX_NR))
        return -EINVAL;

    gic = &gic_data[gic_cnt];

    ret = gic_of_setup(gic, node);  //根据device node初始化gic
    if (ret)
        return ret;

    /*
     * Disable split EOI/Deactivate if either HYP is not available
     * or the CPU interface is too small.
     */
    if (gic_cnt == 0 && !gic_check_eoimode(node, &gic->raw_cpu_base))
        static_branch_disable(&supports_deactivate_key);

    ret = __gic_init_bases(gic, &node->fwnode); //此函数中会设置中断处理的主函数
    if (ret) {
        gic_teardown(gic);
        return ret;
    }

    if (!gic_cnt) {
        gic_init_physaddr(node);
        gic_of_setup_kvm_info(node);
    }

    if (parent) {
        irq = irq_of_parse_and_map(node, 0);
        gic_cascade_irq(gic_cnt, irq);
    }

    if (IS_ENABLED(CONFIG_ARM_GIC_V2M))
        gicv2m_init(&node->fwnode, gic_data[gic_cnt].domain);

    gic_cnt++;
    return 0;
}
static int __init __gic_init_bases(struct gic_chip_data *gic,
                   struct fwnode_handle *handle)
{
    char *name;
    int i, ret;

    if (WARN_ON(!gic || gic->domain))
        return -EINVAL;

    if (gic == &gic_data[0]) {
        /*
         * Initialize the CPU interface map to all CPUs.
         * It will be refined as each CPU probes its ID.
         * This is only necessary for the primary GIC.
         */
        for (i = 0; i < NR_GIC_CPU_IF; i++)
            gic_cpu_map[i] = 0xff;
#ifdef CONFIG_SMP
        set_smp_cross_call(gic_raise_softirq);
#endif
        cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_GIC_STARTING,
                      "irqchip/arm/gic:starting",
                      gic_starting_cpu, NULL);
        //设置中断顶层处理函数handle_arch_irq为gic_handle_irq,此为中断处理的主函数
        set_handle_irq(gic_handle_irq);
        if (static_branch_likely(&supports_deactivate_key))
            pr_info("GIC: Using split EOI/Deactivate mode\n");
    }

    if (static_branch_likely(&supports_deactivate_key) && gic == &gic_data[0]) {
        name = kasprintf(GFP_KERNEL, "GICv2");
        gic_init_chip(gic, NULL, name, true);
    } else {
        name = kasprintf(GFP_KERNEL, "GIC-%d", (int)(gic-&gic_data[0]));
        gic_init_chip(gic, NULL, name, false);  //进一步初始化gic_chip
    }

    ret = gic_init_bases(gic, handle);  //中断控制器初始化
    if (ret)
        kfree(name);

    return ret;
}