> 文章列表 > i.MX8MP平台开发分享(gicv3篇)-- set_handle_irq及中断路由过程分析

i.MX8MP平台开发分享(gicv3篇)-- set_handle_irq及中断路由过程分析

i.MX8MP平台开发分享(gicv3篇)-- set_handle_irq及中断路由过程分析

专栏目录:专栏目录传送门

平台 内核
i.MX8MP 5.15.71

文章目录

    • set_handle_irq
    • hard中断入口
set_handle_irq(gic_handle_irq);

set_handle_irq

这个函数的功能很简单,将gic_handle_irq设置为中断处理函数。在发生中断异常后,内核就会切入到这个中断处理函数中.

void (*handle_arch_irq)(struct pt_regs *) __ro_after_init = default_handle_irq;int __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
{if (handle_arch_irq != default_handle_irq)return -EBUSY;handle_arch_irq = handle_irq;pr_info("Root IRQ handler: %ps\\n", handle_irq);return 0;
}

hard中断入口

EL0和EL1会分别用到这里的handle_arch_irq函数。下面会各针对EL1和EL0进行分析。

static void noinstr __el0_irq_handler_common(struct pt_regs *regs)
{el0_interrupt(regs, handle_arch_irq);
}asmlinkage void noinstr el1h_64_irq_handler(struct pt_regs *regs)
{el1_interrupt(regs, handle_arch_irq);
}

中断代码路由过程如下图所示。

i.MX8MP平台开发分享(gicv3篇)-- set_handle_irq及中断路由过程分析

arm64所有的异常向量存储在一下所示的vectors中。

SYM_CODE_START(vectors)kernel_ventry	1, t, 64, sync		// Synchronous EL1tkernel_ventry	1, t, 64, irq		// IRQ EL1tkernel_ventry	1, t, 64, fiq		// FIQ EL1hkernel_ventry	1, t, 64, error		// Error EL1tkernel_ventry	1, h, 64, sync		// Synchronous EL1hkernel_ventry	1, h, 64, irq		// IRQ EL1hkernel_ventry	1, h, 64, fiq		// FIQ EL1hkernel_ventry	1, h, 64, error		// Error EL1hkernel_ventry	0, t, 64, sync		// Synchronous 64-bit EL0kernel_ventry	0, t, 64, irq		// IRQ 64-bit EL0kernel_ventry	0, t, 64, fiq		// FIQ 64-bit EL0kernel_ventry	0, t, 64, error		// Error 64-bit EL0kernel_ventry	0, t, 32, sync		// Synchronous 32-bit EL0kernel_ventry	0, t, 32, irq		// IRQ 32-bit EL0kernel_ventry	0, t, 32, fiq		// FIQ 32-bit EL0kernel_ventry	0, t, 32, error		// Error 32-bit EL0
SYM_CODE_END(vectors)

如何设置这个异常向量vectors?在__primary_switched函数中使用msr指令将vectors异常向量表的地址设置到vbar_el1寄存器中。

adr_l x8, vectors
msr vbar_el1, x8

如何正确看待每一个异常向量?这里涉及的代码比较复杂,以kernel_ventry 1, h, 64, irq这个异常向量为例,kernel_ventry通过 el\\el\\ht\\()_\\regsize\\()_\\label展开为了el1h_64_irq这个函数,然后跳转到这个函数。

这个el1h_64_irq函数是如何定义的?使用SYM_CODE_START_LOCAL(el\\el\\ht\\()_\\regsize\\()_\\label)定义每种异常函数。在这个函数中,会跳转到具体的el\\el\\ht\\()_\\regsize\\()_\\label\\()_handler

针对每一个异常向量,使用entry_handler将其信息展开,一开始就定义了上面的el\\el\\ht\\()_\\regsize\\()_\\label函数。通过el\\el\\ht\\()_\\regsize\\()_\\label\\()_handler展开为内核C代码中各自的handler,例如这里就是el1h_64_irq_handler,也就是arch/arm64/kernel/entry-common.c里的el1h_64_irq_handler

在el1h_64_irq函数一开始使用kernel_entry存储和清除EL0/1中的通用寄存器。通过 kernel_exit来退出异常,恢复刚才保存的通用寄存器,执行 ERET 来恢复 PC 和 PSTATE。如果是el0,则使用ret_to_user返回用户态,el1是ret_to_kernel返回内核态。

.macro entry_handler el:req, ht:req, regsize:req, label:req
SYM_CODE_START_LOCAL(el\\el\\ht\\()_\\regsize\\()_\\label)kernel_entry \\el, \\regsizemov	x0, spbl	el\\el\\ht\\()_\\regsize\\()_\\label\\()_handler.if \\el == 0b	ret_to_user.elseb	ret_to_kernel.endif
SYM_CODE_END(el\\el\\ht\\()_\\regsize\\()_\\label).endmentry_handler	1, t, 64, syncentry_handler	1, t, 64, irqentry_handler	1, t, 64, fiqentry_handler	1, t, 64, errorentry_handler	1, h, 64, syncentry_handler	1, h, 64, irqentry_handler	1, h, 64, fiqentry_handler	1, h, 64, errorentry_handler	0, t, 64, syncentry_handler	0, t, 64, irqentry_handler	0, t, 64, fiqentry_handler	0, t, 64, errorentry_handler	0, t, 32, syncentry_handler	0, t, 32, irqentry_handler	0, t, 32, fiqentry_handler	0, t, 32, error