Chinaunix首页 | 论坛 | 博客
  • 博客访问: 934916
  • 博文数量: 63
  • 博客积分: 568
  • 博客等级: 中士
  • 技术积分: 3435
  • 用 户 组: 普通用户
  • 注册时间: 2012-10-05 11:44
文章分类
文章存档

2016年(4)

2015年(6)

2014年(3)

2013年(27)

2012年(23)

分类: LINUX

2012-11-04 23:08:00


Arm linux 中断的处理过程


Arm处理器触发外部中断后,处理器进入到状态

Arm的中断异常是由外部的中断输入产生的。它的优先级比快速中断要低。在快速中断产生后,中断被屏蔽掉。

CPSR中断I bit被设置后,中断被禁止掉。当I bit被清除掉后,中断被打开。

----------------------------------------------Note----------------------------------------------------

I bit位只有在处理器的特权状态中才能被更改

--------------------------------------------------------------------------------------------------------

当中断被触发之后,处理器进入下面的状态

Cpu进入irq状态

R14_irq = 下一条指令 + 4 的地址

SPSR_irq = CPSR

CPSR(4:0) = 0b10010

CPSR(5) = 0

CPSR(7) = 1  //disable normal interrupt

CPSR(8) = 1

CPSR(9) = CP15_reg1_EEbit

If VE=0

 If high vectors configured then

PC = 0xffff0018

 Else

PC = 0x00000018

Else

 PC = 不可预测



从上面的分析可知,arm 处理器触发中断置后进入到状态可以总结为

1) 进入了处理器的IRQ模式

2) R14_irq 保存了中断之前的指令地址,SPSR_irq保存了中断之前的寄存器状态

3) R13_irq中断状态下的堆栈


中断处理第一步,跳转执行异常向量表中的中断向量

arm体系中,中断属于处理器的一种异常状态,当处理器触发外部中断的时候,处理器会直接跳转到一个异常向量表中取中断异常入口点的指令执行。在ARM V4V4T以后的大部分处理器中,异常向量表的位置可以有两个位置:一个是0,另一个是0xffff0000。可以通过CP15协处理器c1寄存器中V(bit[13])控制。V和中断向量表的对应关系如下:

V=0        ~        0x00000000~0x0000001C

V=1        ~        0xffff0000~0xffff001C

Linux中选择的是设置0xffff0000为异常向量表的位置

early_trap_init函数对异常向量表进行了初始化操作

void __init early_trap_init(void

    unsigned long vectors = CONFIG_VECTORS_BASE; 
    extern char __stubs_start[], __stubs_end[]; 
    extern char __vectors_start[], __vectors_end[]; 
    extern char __kuser_helper_start[], __kuser_helper_end[]; 
    int kuser_sz = __kuser_helper_end - __kuser_helper_start; 
    /* 
     * Copy the vectors, stubs and kuser helpers (in entry-armv.S) 
     * into the vector page, mapped at 0xffff0000, and ensure these 
     * are visible to the instruction stream. 
     */ 
    memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
    memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);
    memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);
    /* 
     * Copy signal return handlers into the vector page, and 
     * set sigreturn to be a pointer to these. 
     */ 
    memcpy((void *)KERN_SIGRETURN_CODE, sigreturn_codes, 
           sizeof(sigreturn_codes)); 
    flush_icache_range(vectors, vectors + PAGE_SIZE); 
    modify_domain(DOMAIN_USER, DOMAIN_CLIENT); 

异常向量表的定义如下,当处理器触发了外部中断之后,跳转到红色的中断向量执行中断处理过程。

.globl __vectors_start 
    __vectors_start: 
    swi SYS_ERROR0: 
    b vector_und + stubs_offset //复位异常
    ldr pc, .LCvswi + stubs_offset        //未定义指令异常
    b vector_pabt + stubs_offset        //软件中断异常
    b vector_dabt + stubs_offset        //数据异常
    b vector_addrexcptn + stubs_offset        //保留
    b vector_irq + stubs_offset        //普通中断异常: 
    b vector_fiq + stubs_offset        //快速中断异常
    .globl __vectors_end: 
__vectors_end:



保存信息跳转到svc状态

1) lr的地址减4存入到lr中,这时lr中的地址就是发生中断时执行指令的下一条指令

2) r0 lr的地址保存到irq的栈中,将spsr的数值存到lr中,在将这个数值保存到中断的栈中。

3) 将原来的IRQ_MODE清除掉,将SVC_MODE设置到r0寄存器,然后将r0值写入到spsr_cxsf寄存器。

4) and lr, lr, #0x0f  确定中断之前是处理器处于什么样的状态,一般只能处于USER状态或者是SVC状态

5) mov r0, sp irq状态下的栈指针通过r0传递到SVC状态下的中断处理程序

6) ldr lr, [pc, lr, lsl #2] 计算跳转地址,PC指针加0或者加12,如果中断前是user模式就加0,如果中断前是svc模式就增加12

7) movs pc, lr __irq_svc地址载入到PC,模式切换到SVC模式

中断处理的跳转表就挨在vector_stub之后,定义如下。

vector_stub    irq, IRQ_MODE, 4  
 
    .macro    vector_stub, name, mode, correction=0 
    .align    5 
 
vector_\name: 
    .if \correction 
    sub    lr, lr, #\correction 
    .endif 
lr的地址减4存入到lr中,这时lr中的地址就是发生中断时执行指令的下一条指令 
    @ 
    @ Save r0, lr_ (parent PC) and spsr_ 
    @ (parent CPSR) 
    @ 
    stmia    sp, {r0, lr}        @ save r0, lr 
    mrs    lr, spsr 
    str    lr, [sp, #8]        @ save spsr 
r0 lr的地址保存到irq的栈中,将spsr的数值存到lr中,在将这个数值保存到中断的栈中。 
    @ 
    @ Prepare for SVC32 mode.  IRQs remain disabled. 
    @ 
    mrs    r0, cpsr 
    eor    r0, r0, #(\mode ^ SVC_MODE) 
    msr    spsr_cxsf, r0 
将原来的IRQ_MODE清除掉,将SVC_MODE设置到r0寄存器,然后将r0值写入到spsr_cxsf寄存器。 
    @ 
    @ the branch table must immediately follow this code 
    @ 
    and    lr, lr, #0x0f 
    mov    r0, sp 
    ldr    lr, [pc, lr, lsl #2] 
    movs    pc, lr            @ branch to handler in SVC mode 
ENDPROC(vector_\name) 
    .endm 


__irq_svc的处理流程

Svc_enctry紧接着后面会做分析,它的作用就是将cpu相应的寄存器保存到堆栈,并将irq状态中保存的spsr_riq lr_irq等也保存到svc状态下的栈中。

get_thread_info tsk取得thread_info结构体,增加preempt计数。

执行irq_handler,它的主要处理就是读取中断相关的寄存器,读出相应的中断号,然后传递给函数asm_do_IRQ函数进行中断处理。asm_do_IRQ是一个c语言实现的函数,到了linux内核层面,基本上和体系不相关。之后的分析中再介绍

asm_do_IRQ函数中返回后,检查是否发生抢占,如果满足抢占的条件,就调用svc_preempt

最后完成了中断的处理之后,恢复处理器中断之前的状态 

ldmia    sp, {r0 - pc}^              @ load r0 - pc, cpsr

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 


Svc_entry保存中断现场的处理过程

Svc_entry主要将cpu相应的寄存器保存到堆栈,并将irq状态中保存的spsr_riq lr_irq等也保存到svc状态下的栈中。

保存中断现场寄存器处理过程中,将这些寄存器保存在栈中,相应的寄存器组织结构如下图所示。

__irq_svc: 
    svc_entry 
 
#ifdef CONFIG_TRACE_IRQFLAGS 
    bl    trace_hardirqs_off 
#endif 
#ifdef CONFIG_PREEMPT 
    get_thread_info tsk 
    ldr    r8, [tsk, #TI_PREEMPT]        @ get preempt count 
    add    r7, r8, #1                    @ increment it 
    str    r7, [tsk, #TI_PREEMPT] 
#endif 
 
    irq_handler 
#ifdef CONFIG_PREEMPT 
    str    r8, [tsk, #TI_PREEMPT]        @ restore 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 
    ldr    r0, [sp, #S_PSR]              @ irqs are already disabled 
    msr    spsr_cxsf, r0 
#ifdef CONFIG_TRACE_IRQFLAGS 
    tst    r0, #PSR_I_BIT 
    bleq    trace_hardirqs_on 
#endif 
    ldmia    sp, {r0 - pc}^              @ load r0 - pc, cpsr 
ENDPROC(__irq_svc) 


阅读(4110) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~