Linux中断子系统(二)——ARM架构下Linux系统的中断处理函数设置

这系列文章将会介绍Linux内核的中断子系统,关于中断的基本概念网上有很多文章,这里就不再介绍了。

内核中第一个被汇编语言调用的C函数设置

在梳理Linux内核的中断服务程序执行路线过程中,在arch/arm/kernel/entry-armv.S文件中,程序的执行将会出现分支。具体取决于内核配置。接下来介绍详细内容。

ARM架构下Linux内核的中断向量表如下

/*arch/arm/kernel/entry-armv.S*/
/*
 * Interrupt dispatcher
 */
    vector_stub irq, IRQ_MODE, 4

    .long   __irq_usr           @  0  (USR_26 / USR_32)
    .long   __irq_invalid           @  1  (FIQ_26 / FIQ_32)
    .long   __irq_invalid           @  2  (IRQ_26 / IRQ_32)
    .long   __irq_svc           @  3  (SVC_26 / SVC_32)
    .long   __irq_invalid           @  4
    .long   __irq_invalid           @  5
    .long   __irq_invalid           @  6
    .long   __irq_invalid           @  7
    .long   __irq_invalid           @  8
    .long   __irq_invalid           @  9
    .long   __irq_invalid           @  a
    .long   __irq_invalid           @  b
    .long   __irq_invalid           @  c
    .long   __irq_invalid           @  d
    .long   __irq_invalid           @  e
    .long   __irq_invalid           @  f

其中__irq_svc即为发生异常或中断时的向量,循迹追索

/*arch/arm/kernel/entry-armv.S*/
    .align  5
__irq_svc:
    svc_entry
    irq_handler

#ifdef CONFIG_PREEMPTION
    ldr r8, [tsk, #TI_PREEMPT]      @ get preempt count
    ldr r0, [tsk, #TI_FLAGS]        @ get flags
    teq r8, #0              @ if preempt count != 0
    movne   r0, #0              @ force flags to 0
    tst r0, #_TIF_NEED_RESCHED
    blne    svc_preempt
#endif

    svc_exit r5, irq = 1            @ return from exception
 UNWIND(.fnend      )
ENDPROC(__irq_svc)

这里的.align用来指定下一条指令或数据应该对齐到内存中的某个边界,对于大多数编译器这意味着将下一条指令对其到2的指数幂大小的地址,即为2^5=32,有效的指令对齐可以更高的性能效率。svc向量里,调用了irq_handler函数,这是一个汇编宏定义,继续追踪。

/*arch/arm/kernel/entry-armv.S*/
/*
 * Interrupt handling.
 */
    .macro  irq_handler
#ifdef CONFIG_GENERIC_IRQ_MULTI_HANDLER
    ldr r1, =handle_arch_irq
    mov r0, sp
    badr    lr, 9997f
    ldr pc, [r1]
#else
    arch_irq_handler_default
#endif
9997:
    .endm

我的源码树中,定义了CONFIG_GENERIC_IRQ_MULTI_HANDLER这个宏定义,因此走入handle_arch_irq这个分支。
在内核代码中,查找handle_arch_irq的定义,这是一个函数指针。

/*include/linux/irq.h*/
#ifdef CONFIG_GENERIC_IRQ_MULTI_HANDLER
/*
 * Registers a generic IRQ handling function as the top-level IRQ handler in
 * the system, which is generally the first C code called from an assembly
 * architecture-specific interrupt handler.
 *
 * Returns 0 on success, or -EBUSY if an IRQ handler has already been
 * registered.
 */
#ifndef CONFIG_IRQCHIP_XILINX_INTC_MODULE_SUPPORT_EXPERIMENTAL
int __init set_handle_irq(void (*handle_irq)(struct pt_regs *));
#else
int set_handle_irq(void (*handle_irq)(struct pt_regs *));
#endif

/*
 * Allows interrupt handlers to find the irqchip that's been registered as the
 * top-level IRQ handler.
 */
extern void (*handle_arch_irq)(struct pt_regs *) __ro_after_init;
#else
#define set_handle_irq(handle_irq)      \
    do {                    \
        (void)handle_irq;       \
        WARN_ON(1);         \
    } while (0)
#endif

/*kernel/irq/handle.c*/
#ifdef CONFIG_GENERIC_IRQ_MULTI_HANDLER
void (*handle_arch_irq)(struct pt_regs *) __ro_after_init;
#endif

#ifdef CONFIG_GENERIC_IRQ_MULTI_HANDLER
#ifndef CONFIG_IRQCHIP_XILINX_INTC_MODULE_SUPPORT_EXPERIMENTAL
int __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
#else
int set_handle_irq(void (*handle_irq)(struct pt_regs *))
#endif
{
    if (handle_arch_irq)
        return -EBUSY;

    handle_arch_irq = handle_irq;
    return 0;
}
#endif

以上代码可以看出,handle_arch_irq函数的值在set_handle_rq函数里被赋值,__init是否启用则取决于另一个宏定义,感兴趣的话可以自行继续查找。
set_handle_irq函数则在另外的地方被调用,这部分就和中断控制器的初始化有关了,放到下一篇再讲。

/*drivers/irqchip/irq-gic.c*/
__gic_init_bases()->set_handle_irq(gic_handle_irq)
static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{
    ...
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值