码迷,mamicode.com
首页 > 系统相关 > 详细

linux驱动之中断处理过程汇编部分

时间:2017-05-31 00:20:04      阅读:357      评论:0      收藏:0      [点我收藏+]

标签:流程   启动   arc   cpu   fse   oop   bit   not   handle   

      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语言总入口函数,到此为止中断处理汇编部分已经全部涉及到。具体的代码细节没有分析,主要是为了理清中断处理的整体脉络。

 

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

linux驱动之中断处理过程汇编部分

标签:流程   启动   arc   cpu   fse   oop   bit   not   handle   

原文地址:http://www.cnblogs.com/amanlikethis/p/6921826.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!