专栏目录:专栏目录传送门
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 EL1t
kernel_ventry 1, t, 64, irq // IRQ EL1t
kernel_ventry 1, t, 64, fiq // FIQ EL1h
kernel_ventry 1, t, 64, error // Error EL1t
kernel_ventry 1, h, 64, sync // Synchronous EL1h
kernel_ventry 1, h, 64, irq // IRQ EL1h
kernel_ventry 1, h, 64, fiq // FIQ EL1h
kernel_ventry 1, h, 64, error // Error EL1h
kernel_ventry 0, t, 64, sync // Synchronous 64-bit EL0
kernel_ventry 0, t, 64, irq // IRQ 64-bit EL0
kernel_ventry 0, t, 64, fiq // FIQ 64-bit EL0
kernel_ventry 0, t, 64, error // Error 64-bit EL0
kernel_ventry 0, t, 32, sync // Synchronous 32-bit EL0
kernel_ventry 0, t, 32, irq // IRQ 32-bit EL0
kernel_ventry 0, t, 32, fiq // FIQ 32-bit EL0
kernel_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, \regsize
mov x0, sp
bl el\el\ht\()_\regsize\()_\label\()_handler
.if \el == 0
b ret_to_user
.else
b ret_to_kernel
.endif
SYM_CODE_END(el\el\ht\()_\regsize\()_\label)
.endm
entry_handler 1, t, 64, sync
entry_handler 1, t, 64, irq
entry_handler 1, t, 64, fiq
entry_handler 1, t, 64, error
entry_handler 1, h, 64, sync
entry_handler 1, h, 64, irq
entry_handler 1, h, 64, fiq
entry_handler 1, h, 64, error
entry_handler 0, t, 64, sync
entry_handler 0, t, 64, irq
entry_handler 0, t, 64, fiq
entry_handler 0, t, 64, error
entry_handler 0, t, 32, sync
entry_handler 0, t, 32, irq
entry_handler 0, t, 32, fiq
entry_handler 0, t, 32, error