GICV3中断控制器调用流程

  • GICV3中断控制器调用流程已关闭评论
  • 45 次浏览
  • A+
所属分类:linux技术
摘要

异常向量表vectorsel1_irqel0_irqirq_handlergic_handle_irq需要注意的是,INACTIVE PENDING ACTIVE ACTIVE_AND_PENDING,在此例中均属于GIC硬件所标记的中断状态,另外当GIC中断信号处于PENDING状态时,硬件外设无法发送新的中断信号给GIC中断控制器。事实上中断属异步通知并且没有排队的概念。


GICV3中断控制器初始化调用链

GICV3中断控制器调用流程

/kernel/irq/handle.c: #ifdef CONFIG_GENERIC_IRQ_MULTI_HANDLER int __init set_handle_irq(void (*handle_irq)(struct pt_regs *)) {     if (handle_arch_irq)         return -EBUSY;      handle_arch_irq = handle_irq;     return 0; } #endif 
/drivers/irqchip/irq-gic-v3.c: static int __init gic_init_bases(void __iomem *dist_base,                  struct redist_region *rdist_regs,                  u32 nr_redist_regions,                  u64 redist_stride,                  struct fwnode_handle *handle) { ... ...     set_handle_irq(gic_handle_irq); ... ... } 
/drivers/irqchip/irq-gic-v3.c:  IRQCHIP_DECLARE(gic_v3, "arm,gic-v3", gic_of_init);  static int __init gic_of_init(struct device_node *node, struct device_node *parent) { ... ...     err = gic_init_bases(dist_base, rdist_regs, nr_redist_regions,                  redist_stride, &node->fwnode); ... .... } 
/include/linux/irqchip.h: #define IRQCHIP_DECLARE(name, compat, fn) OF_DECLARE_2(irqchip, name, compat, fn)  /include/linux/of.h: #define OF_DECLARE_2(table, name, compat, fn)          _OF_DECLARE(table, name, compat, fn, of_init_fn_2)          #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  } 
/arch/arm64/kernel/vmlinux.lds:  .init.data : {  ... ...  . = ALIGN(8); __irqchip_of_table = .; KEEP(*(__irqchip_of_table)) KEEP(*(__irqchip_of_table_end)) . = ALIGN(8);  ... ...  } 
/drivers/irqchip/irqchip.c:  void __init irqchip_init(void) {     of_irq_init(__irqchip_of_table);     acpi_probe_device_table(irqchip); } 
/arch/arm64/kernel/irq.c: void __init init_IRQ(void) {     init_irq_stacks();     irqchip_init(); ... ... } 
asmlinkage __visible void __init start_kernel(void) { ... ... init_IRQ(); ... ... } 

中断的处理过程

GICV3中断控制器调用流程

异常向量表vectors

/arch/arm64/kernel/entry.S: ENTRY(vectors)     kernel_ventry    1, sync_invalid            // Synchronous EL1t     kernel_ventry    1, irq_invalid            // IRQ EL1t     kernel_ventry    1, fiq_invalid            // FIQ EL1t     kernel_ventry    1, error_invalid        // Error EL1t      kernel_ventry    1, sync                // Synchronous EL1h     kernel_ventry    1, irq                // IRQ EL1h     kernel_ventry    1, fiq_invalid            // FIQ EL1h     kernel_ventry    1, error            // Error EL1h      kernel_ventry    0, sync                // Synchronous 64-bit EL0     kernel_ventry    0, irq                // IRQ 64-bit EL0     kernel_ventry    0, fiq_invalid            // FIQ 64-bit EL0     kernel_ventry    0, error            // Error 64-bit EL0  #ifdef CONFIG_COMPAT     kernel_ventry    0, sync_compat, 32        // Synchronous 32-bit EL0     kernel_ventry    0, irq_compat, 32        // IRQ 32-bit EL0     kernel_ventry    0, fiq_invalid_compat, 32    // FIQ 32-bit EL0     kernel_ventry    0, error_compat, 32        // Error 32-bit EL0 #else     kernel_ventry    0, sync_invalid, 32        // Synchronous 32-bit EL0     kernel_ventry    0, irq_invalid, 32        // IRQ 32-bit EL0     kernel_ventry    0, fiq_invalid, 32        // FIQ 32-bit EL0     kernel_ventry    0, error_invalid, 32        // Error 32-bit EL0 #endif END(vectors) 

el1_irq

/arch/arm64/kernel/entry.S:     .align    6 el1_irq:     kernel_entry 1     gic_prio_irq_setup pmr=x20, tmp=x1     enable_da_f  #ifdef CONFIG_ARM64_PSEUDO_NMI     test_irqs_unmasked    res=x0, pmr=x20     cbz    x0, 1f     bl    asm_nmi_enter 1: #endif  #ifdef CONFIG_TRACE_IRQFLAGS     bl    trace_hardirqs_off #endif      irq_handler  #ifdef CONFIG_PREEMPT     ldr    x24, [tsk, #TSK_TI_PREEMPT]    // get preempt count alternative_if ARM64_HAS_IRQ_PRIO_MASKING     /*      * DA_F were cleared at start of handling. If anything is set in DAIF,      * we come back from an NMI, so skip preemption      */     mrs    x0, daif     orr    x24, x24, x0 alternative_else_nop_endif     cbnz    x24, 1f                // preempt count != 0 || NMI return path     bl    arm64_preempt_schedule_irq    // irq en/disable is done inside 1: #endif  #ifdef CONFIG_ARM64_PSEUDO_NMI     /*      * When using IRQ priority masking, we can get spurious interrupts while      * PMR is set to GIC_PRIO_IRQOFF. An NMI might also have occurred in a      * section with interrupts disabled. Skip tracing in those cases.      */     test_irqs_unmasked    res=x0, pmr=x20     cbz    x0, 1f     bl    asm_nmi_exit 1: #endif  #ifdef CONFIG_TRACE_IRQFLAGS #ifdef CONFIG_ARM64_PSEUDO_NMI     test_irqs_unmasked    res=x0, pmr=x20     cbnz    x0, 1f #endif     bl    trace_hardirqs_on 1: #endif      kernel_exit 1 ENDPROC(el1_irq) 

el0_irq

    .align    6 el0_irq:     kernel_entry 0 el0_irq_naked:     gic_prio_irq_setup pmr=x20, tmp=x0     ct_user_exit_irqoff     enable_da_f  #ifdef CONFIG_TRACE_IRQFLAGS     bl    trace_hardirqs_off #endif  #ifdef CONFIG_HARDEN_BRANCH_PREDICTOR     tbz    x22, #55, 1f     bl    do_el0_irq_bp_hardening 1: #endif     irq_handler  #ifdef CONFIG_TRACE_IRQFLAGS     bl    trace_hardirqs_on #endif     b    ret_to_user ENDPROC(el0_irq)  

irq_handler

/arch/arm64/kernel/entry.S: /*  * Interrupt handling.  */     .macro    irq_handler     ldr_l    x1, handle_arch_irq     mov    x0, sp     irq_stack_entry     blr    x1     irq_stack_exit     .endm 

gic_handle_irq

/drivers/irqchip/irq-gic-v3.c: static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs) {     u32 irqnr;      irqnr = gic_read_iar(); ... ...      /* Check for special IDs first */     if ((irqnr >= 1020 && irqnr <= 1023))         return;      /* Treat anything but SGIs in a uniform way */     if (likely(irqnr > 15)) { ... ...         err = handle_domain_irq(gic_data.domain, irqnr, regs); ... ...     }     if (irqnr < 16) {         gic_write_eoir(irqnr);         if (static_branch_likely(&supports_deactivate_key))             gic_write_dir(irqnr); #ifdef CONFIG_SMP         /*          * Unlike GICv2, we don't need an smp_rmb() here.          * The control dependency from gic_read_iar to          * the ISB in gic_write_eoir is enough to ensure          * that any shared data read by handle_IPI will          * be read after the ACK.          */         handle_IPI(irqnr, regs); #else         WARN_ONCE(true, "Unexpected SGI received!n"); #endif     } }  

中断状态

GICV3中断控制器调用流程

  1. 硬件触发中断信号,中断assert,GIC标记中断为PENDING状态。
  2. GIC中distributor选择优先级最高的PENDING中断,发送给CPU interface, CPU interface对优先级进行判定,然后GIC发送中断请求信号给CPU, CPU进入中断异常后,通过GICC_IAR读取硬中断号,中断进入ACTIVE状态。
  3. 硬件触发新的中断信号,中断assert, GIC标记中断为ACTIVE_AND_PENDING状态
  4. CPU完成中断处理,发送EIO信号到GIC(写EOIR寄存器)

需要注意的是,INACTIVE PENDING ACTIVE ACTIVE_AND_PENDING,在此例中均属于GIC硬件所标记的中断状态,另外当GIC中断信号处于PENDING状态时,硬件外设无法发送新的中断信号给GIC中断控制器。事实上中断属异步通知并且没有排队的概念。