不考慮EL2和EL3,IRQ處理分兩種情況:用戶態發生的中斷和內核態發生的中斷,相應的中斷處理接口分別爲:
el1_irq
el0_irq
以內核態el1發生的irq爲例:
358 .align 6
359 el1_irq:
360 kernel_entry 1 //壓棧,保存上下文;
361 enable_dbg
362 #ifdef CONFIG_TRACE_IRQFLAGS
363 bl trace_hardirqs_off
364 #endif
365
366 irq_handler /*調用中斷處理默認函數*/
367
368 #ifdef CONFIG_PREEMPT
369 get_thread_info tsk
370 ldr w24, [tsk, #TI_PREEMPT] // get preempt count
371 cbnz w24, 1f // preempt count != 0
372 ldr x0, [tsk, #TI_FLAGS] // get flags
373 tbz x0, #TIF_NEED_RESCHED, 1f // needs rescheduling?
374 bl el1_preempt
375 1:
376 #endif
377 #ifdef CONFIG_TRACE_IRQFLAGS
378 bl trace_hardirqs_on
379 #endif
380 kernel_exit 1
381 ENDPROC(el1_irq)
irq_handler也是一個僞指令宏操作:
187 /*
188 * Interrupt handling.
189 */
190 .macro irq_handler
191 adrp x1, handle_arch_irq //把handle_arch_irq地址放到x1, handle_arch_irq是一個指針;
192 ldr x1, [x1, #:lo12:handle_arch_irq]//取出指針所指向的值;
193 mov x0, sp
194 blr x1 //跳轉到handle_arch_irq
195 .endm
handle_arch_irq 在arch/arm64/kernel/irq.c設置:
45 void __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
46 {
47 if (handle_arch_irq)
48 return;
49
50 handle_arch_irq = handle_irq;
51 }
根據不同的中斷控制器會設置不同的處理接口,通過在驅動控制器設置,代碼在drivers/irqchip/,以irq-gic.c(通用中斷控制器)爲例,設置set_handle_irq(gic_handle_irq):
437 static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
438 {
439 u32 irqstat, irqnr;
440 struct gic_chip_data *gic = &gic_data[0];
441 void __iomem *cpu_base = gic_data_cpu_base(gic);
442
443 do {
444 irqstat = readl_relaxed_no_log(cpu_base + GIC_CPU_INTACK);
445 irqnr = irqstat & GICC_IAR_INT_ID_MASK;
446
447 if (likely(irqnr > 15 && irqnr < 1021)) {
448 uncached_logk(LOGK_IRQ, (void *)(uintptr_t)irqnr);
449 handle_domain_irq(gic->domain, irqnr, regs);//根據中斷號調用相應的中斷處理函數;
450 continue;
451 }
452 if (irqnr < 16) {
453 writel_relaxed_no_log(irqstat, cpu_base + GIC_CPU_EOI);
454 uncached_logk(LOGK_IRQ, (void *)(uintptr_t)irqnr);
455 #ifdef CONFIG_SMP
456 handle_IPI(irqnr, regs);
457 #endif
458 continue;
459 }
460 break;
461 } while (1);
462 }