.align 6
el1_irq:
kernel_entry 1
msr daifclr, #1 //enable fiq
enable_dbg_if_not_stepping x0
#ifdef CONFIG_TRACE_IRQFLAGS
bl trace_hardirqs_off
#endif
#ifdef CONFIG_PREEMPT
get_thread_info tsk
ldr w24, [tsk, #TI_PREEMPT] // get preempt count
add w0, w24, #1 // increment it
str w0, [tsk, #TI_PREEMPT]
#endif
irq_handler
#ifdef CONFIG_PREEMPT str w24, [tsk, #TI_PREEMPT] // restore 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
#ifdef CONFIG_TRACE_IRQFLAGS bl trace_hardirqs_on #endif
kernel_exit 1 ENDPROC(el1_irq)
/*
* Interrupt handling.
*/
.macro irq_handler
ldr_l x1, handle_arch_irq
mov x0, sp
irq_stack_entry
blr x1
irq_stack_exit
.endm
相关栈处理:
.macro irq_stack_entry mov x19, sp // preserve the original sp
/* * Compare sp with the current thread_info, if the top * ~(THREAD_SIZE - 1) bits match, we are on a task stack, and * should switch to the irq stack. */ and x25, x19, #~(THREAD_SIZE - 1) cmp x25, tsk b.ne 9998f
this_cpu_ptr irq_stack, x25, x26 mov x26, #IRQ_STACK_START_SP add x26, x25, x26
/* switch to the irq stack */ mov sp, x26
/* * Add a dummy stack frame, this non-standard format is fixed up * by unwind_frame() */ stp x29, x19, [sp, #-16]! mov x29, sp
9998: .endm
/* * x19 should be preserved between irq_stack_entry and * irq_stack_exit. */ .macro irq_stack_exit mov sp, x19 .endm
初始化处理:
kernel/irq.c
void __init set_handle_irq(void (*handle_irq)(struct pt_regs *)) { if (handle_arch_irq) return;
handle_arch_irq = handle_irq; }
需要注意的是,这里设置的handle_irq是主irq_chip下的处理函数,比如对于GIC来说,其设置是:gic_handle_irq。
static 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(cpu_base + GIC_CPU_INTACK); irqnr = irqstat & GICC_IAR_INT_ID_MASK;//#define GICC_IAR_INT_ID_MASK 0x3ff
上面两个操作说明,中断号是中断处理程序去读状态寄存器后得到的。
if (likely(irqnr > 15 && irqnr < 1020)) { if (static_key_true(&supports_deactivate)) writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI); handle_domain_irq(gic->domain, irqnr, regs); continue; }
if (irqnr < 16) { writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI); if (static_key_true(&supports_deactivate)) writel_relaxed(irqstat, cpu_base + GIC_CPU_DEACTIVATE); #ifdef CONFIG_SMP /* * Ensure any shared data written by the CPU sending * the IPI is read after we've read the ACK register * on the GIC. * * Pairs with the write barrier in gic_raise_softirq */ smp_rmb(); handle_IPI(irqnr, regs); #endif continue; } break; } while (1);
}