Arm v8 中断处理

来源:互联网 发布:亚马逊美工岗位职责 编辑:程序博客网 时间:2024/05/17 01:37

ARM v8的工作模式

ARM v8支持的工作状态, EL0(用户态),EL1(内核态),EL2(虚拟机/可选),EL3(监控态 Monitor/可选)。每一个工作状态,有不同的访问权限。CPU运行在ELx,中断类型有同步的和异步的。同步中段有1种,syn;异步中断有3种,irq、fiq、err. 外部中断通过irq告知CPU。


ARM v8的中断处理

基于ARM的SoC大都采用GIC作为中断控制器, v8也不例外。

在arch/arm64/kernel/entry.S中有一个中断向量表,


ENTRY(vectors)
        ventry  el1_sync_invalid                // Synchronous EL1t
        ventry  el1_irq_invalid                 // IRQ EL1t
        ventry  el1_fiq_invalid                 // FIQ EL1t
        ventry  el1_error_invalid               // Error EL1t

        ventry  el1_sync                        // Synchronous EL1h
        ventry  el1_irq                         // IRQ EL1h
        ventry  el1_fiq_invalid                 // FIQ EL1h
        ventry  el1_error_invalid               // Error EL1h

        ventry  el0_sync                        // Synchronous 64-bit EL0
        ventry  el0_irq                         // IRQ 64-bit EL0
        ventry  el0_fiq_invalid                 // FIQ 64-bit EL0
        ventry  el0_error_invalid               // Error 64-bit EL0

#ifdef CONFIG_COMPAT
        ventry  el0_sync_compat                 // Synchronous 32-bit EL0
        ventry  el0_irq_compat                  // IRQ 32-bit EL0
        ventry  el0_fiq_invalid_compat          // FIQ 32-bit EL0
        ventry  el0_error_invalid_compat        // Error 32-bit EL0
#else
        ventry  el0_sync_invalid                // Synchronous 32-bit EL0
        ventry  el0_irq_invalid                 // IRQ 32-bit EL0
        ventry  el0_fiq_invalid                 // FIQ 32-bit EL0
        ventry  el0_error_invalid               // Error 32-bit EL0
#endif
END(vectors)


内核初始化时, 在文件arch/arm64/kernel/head.S

__h_enable_mmu:
        ldr     x5, =vectors
        msr     vbar_el1, x5
                //初始化中断向量地址。
        msr     ttbr0_el1, x25                  // load TTBR0
        msr     ttbr1_el1, x26                  // load TTBR1
        isb
        b       __h_turn_mmu_on
ENDPROC(__h_enable_mmu)


外部中断主要通过el1_irq处理。

el1_irq

文件arch/arm64/kernel/entry.S

el1_irq:
        kernel_entry 1
        enable_dbg
#ifdef CONFIG_TRACE_IRQFLAGS
        bl      trace_hardirqs_off
#endif

        irq_handler

#ifdef CONFIG_PREEMPT
        get_thread_info tsk
        ldr     w24, [tsk, #TI_PREEMPT]         // get preempt count
        cbnz    w24, 1f                         // preempt count != 0
        ldr     x0, [tsk, #TI_FLAGS]            // get flags
        tbz     x0, #TIF_NEED_RESCHED, 1f       // needs rescheduling?
        bl      el1_preempt
1:
#endif


        .macro  irq_handler
#ifdef CONFIG_STRICT_MEMORY_RWX
        ldr     x1, =handle_arch_irq
        ldr     x1, [x1]
#else
        ldr     x1, handle_arch_irq
#endif
        mov     x0, sp
        blr     x1
        .endm



irq_handler主要作中断处理工作,调用handle_arch_irq。handle_arch_irq在文件arch/arm64/kernel/irq.c定义,通过下面函数设置。

void __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
{
        if (handle_arch_irq)
                return;

        handle_arch_irq = handle_irq;
}

在文件drivers/irqchip/irq-gic.c, handle_arch_irq设置为gic_handle_irq。

void __init gic_init_bases(unsigned int gic_nr, int irq_start,
                           void __iomem *dist_base, void __iomem *cpu_base,
                           u32 percpu_offset, struct device_node *node)
{
        irq_hw_number_t hwirq_base;
        struct gic_chip_data *gic;
        int gic_irqs, irq_base, i;

        BUG_ON(gic_nr >= MAX_GIC_NR);

。。。。。

#ifdef CONFIG_SMP
        set_smp_cross_call(gic_raise_softirq);
        register_cpu_notifier(&gic_cpu_notifier);
#endif

        set_handle_irq(gic_handle_irq);

        gic_chip.flags |= gic_arch_extn.flags;
        gic_dist_init(gic);
        gic_cpu_init(gic);
        gic_pm_init(gic);
}

最终中断处理函数是gic_handle_irq。


吹尽黄沙始得金 gic_handle_irq


static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{
        u32 irqstat, irqnr;
        struct gic_chip_data *gic = &gic_data[0];
        void __iomem *cpu_base = gic_data_cpu_base(gic);

        do {
                irqstat = readl_relaxed_no_log(cpu_base + GIC_CPU_INTACK);
                irqnr = irqstat & ~0x1c00;

                if (likely(irqnr > 15 && irqnr < 1021)) {
                        irqnr = irq_find_mapping(gic->domain, irqnr);
                        handle_IRQ(irqnr, regs);
                        uncached_logk(LOGK_IRQ, (void *)(uintptr_t)irqnr);
                        continue;
                }
                if (irqnr < 16) {
                        writel_relaxed_no_log(irqstat, cpu_base + GIC_CPU_EOI);
#ifdef CONFIG_SMP
                        handle_IPI(irqnr, regs);
#endif
                        uncached_logk(LOGK_IRQ, (void *)(uintptr_t)irqnr);
                        continue;
                }
                break;
        } while (1);
}

到了这里,已经知道了有关中断入门的知识了。


参考文档:

ARM Generic Interrupt Controller®Architecture version 2.0

ARM Architecture Reference Manual®ARMv8, for ARMv8-A architecture profile

Linux kernel version 3.10







0 0