linux中断子系统(三) - 中断调用过程

cpu中断入口

我们知道,arm的异常和复位向量表有两种选择,一种是低端向量,向量地址位于0x00000000,另一种是高端向量,向量地址位于0xffff0000,Linux选择使用高端向量模式,也就是说,当异常发生时,CPU会把PC指针自动跳转到始于0xffff0000开始的某一个地址上。

ARM异常向量表

地址异常类别
0xffff0000复位
0xffff0004未定义指令
0xffff0008软中断swi
0xffff000cprefetch abort
0xffff0010data abort
0xffff0014保留
0xffff0018IRQ
0xffff001cFIQ

中断向量表在arch/arm/kernel/entry_armv.S中定义,系统启动阶段setup_arch函数会调用arch/arm/kernel/traps.h中的early_trap_init,
这个函数会把__vectors_start(这个地址是不是链接在内核镜像,编译完了赋值的)开始的代码拷贝到0xffff0000处,
把__stubs_start开始的代码拷贝到0xffff+0x200处,这样中断来时,就可以跳转到相应的中断向量入口。
memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);

真正的向量跳转表,位于__vectors_start和__vectors_end之间

.equ    stubs_offset, __vectors_start + 0x200 - __stubs_start
    .globl  __vectors_start
__vectors_start:
 ARM(   swi SYS_ERROR0  )
 THUMB( svc #0      )
 THUMB( nop         )
    W(b)    vector_und + stubs_offset
    W(ldr)  pc, .LCvswi + stubs_offset
    W(b)    vector_pabt + stubs_offset
    W(b)    vector_dabt + stubs_offset
    W(b)    vector_addrexcptn + stubs_offset
    W(b)    vector_irq + stubs_offset
    W(b)    vector_fiq + stubs_offset

    .globl  __vectors_end
__vectors_end:

跳转部分

    .globl  __stubs_start
__stubs_start:
    /*
     * Interrupt dispatcher
     */
    //这一句宏展开后实际上就是定义了vector_irq,根据进入cpu中断前的模式,分别跳转到__irq_usr或__irq_svc
    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
    ...

    /*
     * Data abort dispatcher
     * Enter in ABT mode, spsr = USR CPSR, lr = USR PC
     */
    //这一句宏展开后实际上就是定义了vector_dabt,根据进入cpu中断前的模式,分别跳转到__dabt_usr或__dabt_svc
    vector_stub dabt, ABT_MODE, 8
    ...

    /*
     * Prefetch abort dispatcher
     * Enter in ABT mode, spsr = USR CPSR, lr = USR PC
     */
    vector_stub pabt, ABT_MODE, 4
    ...

    /*
     * Undef instr entry dispatcher
     * Enter in UND mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
     */
    vector_stub und, UND_MODE
    ...

vector_fiq:
    disable_fiq
    subs    pc, lr, #4

vector_addrexcptn:
    b   vector_addrexcptn

.LCvswi:
    .word   vector_swi

    .globl  __stubs_end
__stubs_end:

__irq_usr和__irq_svc的区别是进入和退出中断时是否进行用户栈和内核栈之间的切换等,他们最终都会进入到irq_handler这个宏

    .macro  irq_handler
    get_irqnr_preamble r5, lr
1:  get_irqnr_and_base r0, r6, r5, lr
    movne   r1, sp
    @
    @ routine called with r0 = irq number, r1 = struct pt_regs *
    @
    adrne   lr, BSYM(1b)
    bne asm_do_IRQ

    .endm

get_irqnr_preamble和get_irqnr_and_base这两个宏的目的是从中断控制器获得IRQ的编号,然后调用asm_do_IRQ,到这里中断程序完成了从汇编代码到c代码的传递,并且获得了引起中断的IRQ编号。


asm_do_IRQ

asm_do_IRQ()  arch/arm/kernel/irq.c
    generic_handle_irq()
        generic_handle_irq_desc
            desc->handle_irq 系统中断处理函数
                desc->chip->ack(irq) 清除中断
                handle_IRQ_event(irq, action) 遍历并调用这个desc下所有的用户中断处理函数action
                    action->handler(irq, action->dev_id); 用户中断处理函数

当系统进入中断后,首先会进入系统中断处理函数,这个之前由set_irq_handler注册给desc->irq_flow_handler_t handler_irq了,在系统中断处理函数中又会调用用户中断处理函数,这个之前由request_irq注册给desc->struct irq_action *action了


参考文章

  1. Linux中断(interrupt)子系统之二:arch相关的硬件封装层
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

luckywang1103

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值