zoukankan      html  css  js  c++  java
  • linux驱动之中断处理过程汇编部分

          linux系统下驱动中,中断异常的处理过程,与裸机开发中断处理过程非常类似。通过简单的回顾裸机开发中断处理部分,来参考学习linux系统下中断处理流程。

    一、ARM裸机开发中断处理过程

          以S3C2440的裸机开发启动文件中,有关irq中断部分代码为例进行说明:

    .extern     main
    .text 
    .global _start 
    _start:      
        b   Reset
    HandleUndef:
        b   HandleUndef 
    HandleSWI:
        b   HandleSWI
    HandlePrefetchAbort:
        b   HandlePrefetchAbort
    HandleDataAbort:
        b   HandleDataAbort
    HandleNotUsed:
        b   HandleNotUsed
        b   HandleIRQ
    HandleFIQ:
        b   HandleFIQ
    
    Reset:                  
        ldr sp, =4096           @ 设置栈指针,以下都是C函数,调用前需要设好栈
        bl  disable_watch_dog   @ 关闭WATCHDOG,否则CPU会不断重启
        
        msr cpsr_c, #0xd2       @ 进入中断模式
        ldr sp, =3072           @ 设置中断模式栈指针
    
        msr cpsr_c, #0xdf       @ 进入系统模式
        ldr sp, =4096           @ 设置系统模式栈指针
    
        bl  init_led            @ 初始化LED的GPIO管脚
        bl  init_irq            @ 调用中断初始化函数,在init.c中
        msr cpsr_c, #0x5f       @ 设置I-bit=0,开IRQ中断
        
        ldr lr, =halt_loop      @ 设置返回地址
        ldr pc, =main           @ 调用main函数
    halt_loop:
        b   halt_loop
    
    HandleIRQ:
        sub lr, lr, #4                  @ 计算返回地址
        stmdb   sp!,    { r0-r12,lr }   @ 保存使用到的寄存器
                                        @ 注意,此时的sp是中断模式的sp,初始值是上面设置的3072   
        ldr lr, =int_return             @ 设置调用ISR即EINT_Handle函数后的返回地址  
        ldr pc, =EINT_Handle            @ 调用中断服务函数,在interrupt.c中
    int_return:
        ldmia   sp!,    { r0-r12,pc }^  @ 中断返回, ^表示将spsr的值复制到cpsr

          当irq中断发生时,一些列的处理流程如下:

    1、硬件自动令PC置为irq的中断向量,从而执行跳转指令“b HandleIRQ”。

         其实,之前还伴随着保存中断断点地址到lr(还要换算);CPSR的值到SPSR;将CPSR切换到异常模式。

    2、保存中断现场

    sub lr, lr, #4                  @ 计算返回地址
    stmdb   sp!,    { r0-r12,lr }   @ 保存使用到的寄存器

    3、执行中断服务程序

    ldr lr, =int_return             @ 设置调用ISR即EINT_Handle函数后的返回地址  
    ldr pc, =EINT_Handle            @ 调用中断服务函数,在interrupt.c中

    4、从中断异常工作模式返回

    int_return:
    ldmia   sp!,    { r0-r12,pc }^  @ 中断返回, ^表示将spsr的值复制到cpsr

    二、linux系统中断处理流程

          具体的代码细节没有分析,主要是为了理清中断处理的整体脉络。

    1、ARM异常向量表

          arch/arm/kernel/entry-armv.S

        .globl    __vectors_start
    __vectors_start:
        swi    SYS_ERROR0                            /* 复位时,执行这条指令 */
        b    vector_und + stubs_offset               /* 未定义异常 */
        ldr    pc, .LCvswi + stubs_offset            /* swi异常 */
        b    vector_pabt + stubs_offset              /* 指令预取异常 */
        b    vector_dabt + stubs_offset              /* 数据访问终止 */
        b    vector_addrexcptn + stubs_offset        /* 没有用 */
        b    vector_irq + stubs_offset               /* irq异常 */
        b    vector_fiq + stubs_offset               /* fiq异常 */
    
        .globl    __vectors_end
    __vectors_end:

          异常向量表,无非还是一些跳转指令。当发生irq中断,执行指令“b vector_irq + stubs_offset”,也就是跳转到vector_irq代码段继续执行。

          在linux内核初始化阶段,start_kernel函数(init/main.c)会调用trap_init、init_IRQ两个函数来初始化异常向量相关处理函数。简要说明就是,将异常向量表拷贝到地址0xffff0000处(ARM体系协处理器寄存器c1能设置异常向量的基地址为0xffff0000),再把异常向量表中异常处理的进一步函数代码段拷贝到0xffff0200位置(vector_und、vector_irq等)。

    2、异常处理进一步函数----vector_irq

          arch/arm/kernel/entry-armv.S

     1     .globl    __stubs_start
     2 __stubs_start:
     3     vector_stub    irq, IRQ_MODE, 4
     4     .long    __irq_usr            @  0  (USR_26 / USR_32)
     5     .long    __irq_invalid            @  1  (FIQ_26 / FIQ_32)
     6     .long    __irq_invalid            @  2  (IRQ_26 / IRQ_32)
     7     .long    __irq_svc            @  3  (SVC_26 / SVC_32)
     8     .long    __irq_invalid            @  4
     9     .long    __irq_invalid            @  5
    10     .long    __irq_invalid            @  6
    11     .long    __irq_invalid            @  7
    12     .long    __irq_invalid            @  8
    13     .long    __irq_invalid            @  9
    14     .long    __irq_invalid            @  a
    15     .long    __irq_invalid            @  b
    16     .long    __irq_invalid            @  c
    17     .long    __irq_invalid            @  d
    18     .long    __irq_invalid            @  e
    19     .long    __irq_invalid            @  f

          从第4行到第19行,记录了(代码链接阶段填入的地址数据)在各个模式下遇到irq中断时,发生异常的处理分支。比如第4行__irq_usr表示用户模式下发生irq中断时,由__irq_usr对应的代码段来处理这种情况。

          vector_stub是一个宏,将宏展开内容如下:

    vector_irq:
        sub    lr, lr, #4
        stmia    sp, {r0, lr}    @ save r0, lr
        
        mrs    lr, spsr
        str    lr, [sp, #8]        @ save spsr
        
        mrs    r0, cpsr
        eor    r0, r0, #(IRQ_MODE ^ SVC_MODE)
        msr    spsr_cxsf, r0
    
        and    lr, lr, #0x0f
        mov    r0, sp
        ldr    lr, [pc, lr, lsl #2]
        movs    pc, lr            @ branch to handler in SVC mode
        .endm

          这个宏的目的就是,根据进入irq中断前处理器所处的模式,将紧接着其下边的16个地址池中对应位置的处理向量,取出来赋给PC,完成进一步跳转。这里我们选择让程序跳转到__irq_usr代码段继续执行。

    3、异常处理进一步函数----__irq_usr

          arch/arm/kernel/entry-armv.S

    __irq_usr:
        usr_entry             @将usr模式下的寄存器、中断返回地址保存到堆栈中
    
        get_thread_info tsk  @获取当前进程的进程描述符中的成员变量thread_info的地址,并将该地址保存到寄存器tsk等于r9
    
        irq_handler          @中断处理
    
        mov    why, #0       
        b    ret_to_user     @中断处理完成,返回中断产生的位置
    
    

    4、irq_handler

          irq_handler是一个宏,将其内容展开如下:

          arch/arm/kernel/entry-armv.S

        .macro    irq_handler
        get_irqnr_preamble r5, lr
    1:    get_irqnr_and_base r0, r6, r5, lr
        movne    r1, sp
        adrne    lr, 1b
        bne    asm_do_IRQ
    .endm

          由此可见,进入asm_do_IRQ函数开始具体的中断处理。需要指出的是,asm_do_IRQ是中断的C语言总入口函数。asm_do_IRQ函数原型为:

    asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)

          在汇编处理阶段,会为asm_do_IRQ传入两个参数irq(中断号)和regs,中断号对应着发生了什么样的中断事件,于是可以采取什么样的中断服务程序进行处理。

    5、get_irqnr_and_base

          include/asm-arm/arch-s3c2410/entry-macro.s

        .macro    get_irqnr_and_base, irqnr, irqstat, base, tmp
    
            mov    ase, #S3C24XX_VA_IRQ
    
            @@ try the interrupt offset register, since it is there
    
            ldr    irqstat, [ ase, #INTPND ]
            teq    irqstat, #0
            beq    1002f
            ldr    irqnr, [ ase, #INTOFFSET ]
            mov    	mp, #1
            tst    irqstat, 	mp, lsl irqnr
            bne    1001f
    
            @@ the number specified is not a valid irq, so try
            @@ and work it out for ourselves
    
            mov    irqnr, #0        @@ start here
    
            @@ work out which irq (if any) we got
    
            movs    	mp, irqstat, lsl#16
            addeq    irqnr, irqnr, #16
            moveq    irqstat, irqstat, lsr#16
            tst    irqstat, #0xff
            addeq    irqnr, irqnr, #8
            moveq    irqstat, irqstat, lsr#8
            tst    irqstat, #0xf
            addeq    irqnr, irqnr, #4
            moveq    irqstat, irqstat, lsr#4
            tst    irqstat, #0x3
            addeq    irqnr, irqnr, #2
            moveq    irqstat, irqstat, lsr#2
            tst    irqstat, #0x1
            addeq    irqnr, irqnr, #1
    
            @@ we have the value
    1001:
            adds    irqnr, irqnr, #IRQ_EINT0    @加上中断号的基准数值,得到最终的中断号
    1002:
            @@ exit here, Z flag unset if IRQ
    
        .endm

          linux系统中断号判断过程,是与硬件平台相关的。例如S3C2410的中断号判断过程,是根据INTOFFSET来判断的。但是,需要注意的是,中断号的具体值是有平台相关的代码决定的,和硬件中断挂起寄存器中的中断号是不等的。

    #define S3C2410_CPUIRQ_OFFSET     (16)
    #define S3C2410_IRQ(x) ((x) + S3C2410_CPUIRQ_OFFSET)
    
    /* main cpu interrupts */
    #define IRQ_EINT0      S3C2410_IRQ(0)        /* 16 */
    #define IRQ_EINT1      S3C2410_IRQ(1)        /* 17 */
    #define IRQ_EINT2      S3C2410_IRQ(2)        /* 18 */
    #define IRQ_EINT3      S3C2410_IRQ(3)        /* 19 */
          ...............

    参考资料:linux-2.6.26内核中ARM中断实现详解(转)

  • 相关阅读:
    attention 介绍
    卷积神经网络中的channel 和filter
    滤波和卷积
    最容易理解的对卷积(convolution)的解释
    随机梯度下降
    optimizer
    一个深度学习的例子
    Batch_Size 详解
    softmax函数详解
    Must Know Tips/Tricks in Deep Neural Networks
  • 原文地址:https://www.cnblogs.com/amanlikethis/p/6921826.html
Copyright © 2011-2022 走看看