GICV3中断控制器调用流程

cnblogs 2024-07-18 17:45:03 阅读 84

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

image

<code>/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();

... ...

}

中断的处理过程

中断调用流程

异常向量表vectors

<code>/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

}

}

中断状态

中断状态切换

  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寄存器)

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



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。