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);
}
中断代码路由过程如下图所示。
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