linux irq中断过程解析(基于ARM处理器)

linux irq中断过程解析(基于ARM处理器)

1、中断向量

1.1、__vectors_start

        .section .vectors, "ax", %progbits
__vectors_start:
        W(b)    vector_rst
        W(b)    vector_und
        W(ldr)  pc, __vectors_start + 0x1000
        W(b)    vector_pabt
        W(b)    vector_dabt
        W(b)    vector_addrexcptn
         W(b)    vector_irq // irq中断向量入口
        W(b)    vector_fiq

1.2、vector_stub

        .macro  vector_stub, name, mode, correction=0
        .align  5


vector_\name:   
        .if \correction
sub     lr, lr, #\correction //中断返回地址修正,ARM处理器有取指、译码、执行等多级流水线,pc指向取值的指令地址,硬件中断会将pc-4存在lr,此时lr指向译码指令地址而不是正在执行的指令地址,因此需要再修正中断返回地址
        .endif


        @
        @ Save r0, lr_<exception> (parent PC) and spsr_<exception>
        @ (parent CPSR)
        @
 stmia   sp, {r0, lr}            @ save r0, lr // 保存r0, lr到irq栈
mrs     lr, spsr // 保存中断前的cpsr(中断时硬件自动将中断前的cpsr保存到spsr)
str     lr, [sp, #8]            @ save spsr // 保存cpsr到irq栈,此时irq栈的内容为r0,lr,cpsr


        @
        @ Prepare for SVC32 mode.  IRQs remain disabled.
        @
        mrs     r0, cpsr // 此时IRQs仍然处于禁止状态(中断的时候由硬件禁止)
        eor     r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE) // 设置svc状态位
        msr     spsr_cxsf, r0 // 保存cpsr到spsr


        @
        @ the branch table must immediately follow this code
        @
and     lr, lr, #0x0f // 前面已经将中断前的cpsr保存到lr,此处获取中断前的模式,用于判断是用户态被中断还是内核态被中断
 THUMB( adr     r0, 1f                  )
 THUMB( ldr     lr, [r0, lr, lsl #2]    )
        mov     r0, sp // sp保存到r0,切换模式后sp会改变成对应模式的sp,栈里面已经保存了部分中断上下文r0,lr,cpsr
 ARM(   ldr     lr, [pc, lr, lsl #2]    ) 
 movs    pc, lr                  @ branch to handler in SVC mode // irq/fiq中断向量表正好紧接当前指令之后,即pc等价于irq/fiq中断向量表基地址,lr为中断前模式,pc + lr * 4即得到对应模式的中断入口函数地址,例如__irq_usr、__irq_svc,从不同模式进入中断,处理流程有所不同,此处跳转到对应模式的中断处理程序
ENDPROC(vector_\name)

1.3、vector_irq

/*
 * 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) // svc模式进入中断
        .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

2、中断处理

2.1、__irq_usr(用户模式进入中断)

2.1.1、usr_entry

 
        .macro  usr_entry, trace=1, uaccess=1
 UNWIND(.fnstart        )
 UNWIND(.cantunwind     )       @ don't unwind the user space                                                                                                                                     
        sub     sp, sp, #S_FRAME_SIZE // 预留72字节保留中断上下文
 ARM(   stmib   sp, {r1 - r12}  ) // 1.2中已经保存了r0,lr,cpsr
 THUMB( stmia   sp, {r0 - r12}  )


 ATRAP( mrc     p15, 0, r7, c1, c0, 0)
 ATRAP( ldr     r8, .LCcralign)


        ldmia   r0, {r3 - r5} // r0为切换模式前的sp,sp栈里面保存了中断前的r0,lr,cpsr,即中断上下文的一部分寄存器,将r0,lr,cpsr装载到r3-r5
        add     r0, sp, #S_PC           @ here for interlock avoidance // sp + pc偏移 -> r0,r0指向栈地址用于保存中断返回地址pc
        mov     r6, #-1                 @  ""  ""     ""        "" // r6 = 0xffffffff


        str     r3, [sp]                @ save the "real" r0 copied // 将中断前的r0寄存器值保存到但前栈,当前栈内容变为r0-r12
                                        @ from the exception stack


 ATRAP( ldr     r8, [r8, #0])


        @
        @ We are now ready to fill in the remaining blanks on the stack:
        @
        @  r4 - lr_<exception>, already fixed up for correct return/restart
        @  r5 - spsr_<exception>
        @  r6 - orig_r0 (see pt_regs definition in ptrace.h)
        @
        @ Also, separately save sp_usr and lr_usr
        @
        stmia   r0, {r4 - r6} // r4-r6从pc地址开始入栈,r4-r6的内容依次为lr(pc),cpsr,0xffffffff
 ARM(   stmdb   r0, {sp, lr}^                   ) // r0指向了pc保存地址,sp、lr保存地址介于r0-r12及pc之间,stmdb将用户态的sp、lr保存在栈中间,保存之后栈内容依次是r0-r12,sp,lr,pc,cpsr,0xffffffff,此时中断前的寄存器等都已经保存到栈里面了
 THUMB( store_user_sp_lr r0, r1, S_SP - S_PC    )


        .if \uaccess
        uaccess_disable ip
        .endif


        @ Enable the alignment trap while in kernel mode
 ATRAP( teq     r8, r7)
 ATRAP( mcrne   p15, 0, r8, c1, c0, 0)


        @
        @ Clear FP to mark the first stack frame
        @
        zero_fp


        .if     \trace
#ifdef CONFIG_TRACE_IRQFLAGS
        bl      trace_hardirqs_off
#endif
        ct_user_exit save = 0
        .endif
        .endm

2.1.2、irq_handler

        .macro  irq_handler
#ifdef CONFIG_MULTI_IRQ_HANDLER
        ldr     r1, =handle_arch_irq // 处理器相关中断处理函数地址
        mov     r0, sp // sp栈保存了中断上下文
        badr    lr, 9997f // handler_arch_irq返回地址,即irq_handler的下一条指令地址,__irq_usr中的get_thread_info tsk指令
        ldr     pc, [r1] // 跳转到irq中断处理函数
#else
        arch_irq_handler_default
#endif
9997:
        .endm

2.1.3、ret_to_user_from_irq

ENTRY(ret_to_user_from_irq)
        ldr     r1, [tsk, #TI_FLAGS]
        tst     r1, #_TIF_WORK_MASK // 检查是否有挂起的待处理任务,用户进程被中断后有可能需要重新调度等
        bne     slow_work_pending // 处理挂起的待处理任务
no_work_pending:
        asm_trace_hardirqs_on save = 0


        /* perform architecture specific actions before user return */
        arch_ret_to_user r1, lr
        ct_user_enter save = 0

        restore_user_regs fast = 0, offset = 0
ENDPROC(ret_to_user_from_irq)

2.1.4 restore_user_regs

        .macro  restore_user_regs, fast = 0, offset = 0
        uaccess_enable r1, isb=0
#ifndef CONFIG_THUMB2_KERNEL
        @ ARM mode restore
        mov     r2, sp // sp保存了中断上下文,即用户态寄存器、返回地址等信息
        ldr     r1, [r2, #\offset + S_PSR]      @ get calling cpsr // 获取保存的用户态cpsr(里面已经设置了用户态模式标志位)
        ldr     lr, [r2, #\offset + S_PC]!      @ get pc // 获取中断返回地址,用户模式被中断的指令执行地址
        msr     spsr_cxsf, r1                   @ save in spsr_svc // 用户态cpsr保存到spsr
#if defined(CONFIG_CPU_V6) || defined(CONFIG_CPU_32v6K)
        @ We must avoid clrex due to Cortex-A15 erratum #830321
        strex   r1, r2, [r2]                    @ clear the exclusive monitor
#endif
        .if     \fast
        ldmdb   r2, {r1 - lr}^                  @ get calling r1 - lr
        .else
        ldmdb   r2, {r0 - lr}^                  @ get calling r0 - lr // sp栈里面保存的数据恢复到用户态r0-lr (有部分寄存器svc模式与usr模式不共用)
        .endif
        mov     r0, r0                          @ ARMv5T and earlier require a nop
                                                @ after ldm {}^
        add     sp, sp, #\offset + S_FRAME_SIZE // 释放sp中断上下文所占用的内存
        movs    pc, lr                          @ return & move spsr_svc into cpsr // 跳转到被中断的用户态指令地址,并且恢复spsr到cpsr(cpu模式切换)
......
#endif  /* !CONFIG_THUMB2_KERNEL */
        .endm

2.1.5、__irq_usr

        .align  5
__irq_usr:                                                                                                                                                                                        
        usr_entry // 保存用户态寄存器到栈里面(保存中断上下文)
        kuser_cmpxchg_check
        irq_handler // 调用irq中断处理函数
        get_thread_info tsk //- 获取当前进程/线程的thread_info地址
        mov     why, #0
        b       ret_to_user_from_irq // 从irq模式返回到用户模式(中断并没有改变mmu,因此不需要切换mmu)
 UNWIND(.fnend          )
ENDPROC(__irq_usr)

2.2、__irq_svc(svc模式进入中断)

2.2.1、__irq_svc

        .align  5
__irq_svc:                                                                                                                                                                                        
        svc_entry // 保存中断上下文,与用户模式不同的是,中断前后处于同一模式,使用同一套寄存器,保存中断上下文方法稍微有些不同,此处不过多解释
        irq_handler // 中断处理,与从用户模式进入中断是一样的


#ifdef CONFIG_PREEMPT
        get_thread_info tsk
        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 // 内核态抢占(需要打开CONFIG_PREEMPT开关)
#endif


        svc_exit r5, irq = 1                    @ return from exception // svc_entry已经将中断前的cpsr保存在r5寄存器中
 UNWIND(.fnend          )
ENDPROC(__irq_svc)

2.2.2、svc_exit

  
        .macro  svc_exit, rpsr, irq = 0
        .if     \irq != 0
......
        .else
        @ IRQs off again before pulling preserved data off the stack
        disable_irq_notrace
#ifdef CONFIG_TRACE_IRQFLAGS
        tst     \rpsr, #PSR_I_BIT
        bleq    trace_hardirqs_on
        tst     \rpsr, #PSR_I_BIT
        blne    trace_hardirqs_off
#endif
        .endif
        uaccess_restore


#ifndef CONFIG_THUMB2_KERNEL
        @ ARM mode SVC restore
        msr     spsr_cxsf, \rpsr // 中断前cpsr保存到spsr
#if defined(CONFIG_CPU_V6) || defined(CONFIG_CPU_32v6K)
        @ We must avoid clrex due to Cortex-A15 erratum #830321
        sub     r0, sp, #4                      @ uninhabited address
        strex   r1, r2, [r0]                    @ clear the exclusive monitor
#endif
        ldmia   sp, {r0 - pc}^                  @ load r0 - pc, cpsr // 从栈里面恢复中断上下文,并且恢复cpsr
#else
......
#endif
        .endm

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
ARM处理器中的IRQ(Interrupt Request)中断是一种硬件中断,用于处理外部设备请求处理器的事件。当外部设备需要处理器的注意时,它会发送一个中断请求信号给处理器处理器在合适的时候会响应并处理该中断ARM处理器中的IRQ中断有以下几个关键概念: 1. 中断源(Interrupt Source):触发中断的外部设备或事件,例如定时器、串口、外部中断引脚等。 2. 中断控制器(Interrupt Controller):负责管理和分发中断信号的硬件模块。在ARM处理器中,常见的中断控制器包括ARM GIC(Generic Interrupt Controller)等。 3. 中断服务例程(Interrupt Service Routine,ISR):是用来处理中断的代码段。当中断发生时,处理器会暂停当前正在执行的任务,跳转到对应的ISR执行相应的操作。 ARM处理器IRQ中断的工作流程如下: 1. 中断源产生中断请求信号。 2. 中断控制器接收到中断请求信号后,根据优先级和屏蔽设置确定是否将中断传递给处理器。 3. 如果中断被传递给处理器处理器会保存当前的上下文信息,并跳转到对应的ISR执行相应的操作。 4. 执行完ISR后,处理器会恢复之前保存的上下文信息,并继续执行被中断打断的任务。 需要注意的是,中断的响应时间通常要比程序的执行时间短得多,这使得处理器可以快速响应外部设备的请求并及时处理。同时,在编写中断服务例程时,需要考虑中断处理的实时性和可靠性,以确保正确处理中断事件。 以上是关于ARM处理器IRQ中断的简要介绍,如有更深入的问题欢迎继续提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值