Chinaunix首页 | 论坛 | 博客
  • 博客访问: 974388
  • 博文数量: 214
  • 博客积分: 10173
  • 博客等级: 上将
  • 技术积分: 1867
  • 用 户 组: 普通用户
  • 注册时间: 2007-06-18 13:48
文章分类

全部博文(214)

文章存档

2012年(1)

2010年(13)

2009年(5)

2008年(98)

2007年(97)

分类: LINUX

2008-02-29 16:33:02

浅析armlinux 2.4.19中断irq分发例程的派发流程之中间层

文章来源:http://gliethttp.cublog.cn

接续前一篇《浅析armlinux 2.4.19中断irq分发例程的派发流程之根基》
看看irq寄存器环境保存和高层asm_do_IRQ()中断处理函数的调用跳转[gliethttp_20071225].

//----------------------------------------------------------------------
//1.先看看__irq_svc中断irq处理例程的实现代码'
//在arch\arm\kernel\entry-header.S中有如下3个常量定义
/*
#ifdef CONFIG_CPU_32
#define S_FRAME_SIZE    72
#define S_OLD_R0        68
#define S_PSR            64
#endif
其实这就是pt_regs结构体的偏移值,该结构体定义在include\asm-arm\proc-armv\ptrace.h中
struct pt_regs {
    long uregs[18];
};

#define ARM_cpsr      uregs[16]
#define ARM_pc        uregs[15]
#define ARM_lr        uregs[14]
#define ARM_sp        uregs[13]
#define ARM_ip        uregs[12]
#define ARM_fp        uregs[11]
#define ARM_r10       uregs[10]
#define ARM_r9        uregs[9]
#define ARM_r8        uregs[8]
#define ARM_r7        uregs[7]
#define ARM_r6        uregs[6]
#define ARM_r5        uregs[5]
#define ARM_r4        uregs[4]
#define ARM_r3        uregs[3]
#define ARM_r2        uregs[2]
#define ARM_r1        uregs[1]
#define ARM_r0        uregs[0]
#define ARM_ORIG_r0   uregs[17]
*/

        .align    5
//因为在vector_IRQ中irq模式下的lr_irq和lr_spsr执行msr spsr_c, r13之后,使用movs pc, lr进行跳
//转到__irq_svc,所以带s的操作指令movs,将使spsr_c的状态自动恢复到cpsr中,也就是使cpu工作在
//禁止irq中断的svc模式下[当然irq的I位在发生irq的同时就有硬件自动禁用了gliethttp_20071225]
//所以linux2.4内核下irq处理程序使用的sp就是暂时借用了svc内核空间程序的堆栈,如下代码也工作在svc模式
__irq_svc:sub sp, sp, #S_FRAME_SIZE
//因为我的at91rm9200在linux2.4.19中工作在"小端"模式,低址存低位,
//所以下面的r1=sp-S_FRAME_SIZE的地址就是&uregs[0]的地址[gliethttp_20071225]
//从低址向高址依次存放r0,r1,...,r12,也就是将数据存放到结构体的uregs[0]~uregs[12]中
        stmia sp, {r0 - r12}
//参看《浅析armlinux 2.4.19中断irq分发例程的派发流程之根基》
//.LCirq+0 存放了 lr-4的地址值,也就是irq中断处理函数处理完成之后,返回到的pc地址值
//.LCirq+4 存放了 spsr,也就是irq中断发生跳转到irq处理函数vector_IRQ之前cpu所处的模式值
// 主要是用来对I和F位以及几个N、Z、C和V标志位的恢复
        ldr    r7, .LCirq
        add    r5, sp, #S_FRAME_SIZE//r5存放了由该irq中断处理程序借用的svc的实际堆栈指针
        ldmia  r7, {r7 - r9}//r7=lr-4;r8=spsr;r9=随意值,实际为__temp_abt数值
        add    r4, sp, #S_SP//r4=&ARM_sp=&uregs[13]地址
//r6=当前svc模式下的lr,不允许嵌套中断发生;
//因为这是svc内核代码正在执行的时候发生的irq中断处理,所以当然要保存svc模式下正在运算的lr地址值,
//这样从irq返回时也才能够继续执行svc模式下被irq中断了的运行的内核代码[gliethttp_20071225]
        mov    r6, lr
//gliethttp_20071225
//按照存pt_regs结构体的储空间规则存放r0-r12,sp_svc,lr_svc,pc,cpsr,ARM_ORIG_r0;总之pt_regs存放了
//发生irq中断之前cpu所运行的整个环境,比如:正在内核空间进行pte_alloc()的操作的时候,irq发生到此,
//那么pt_regs存放着pte_alloc()运行到被irq中断的那个时刻,所有的寄存器值[gliethttp_20071225].
        stmia   r4, {r5, r6, r7, r8, r9}
/*
//at91rm9200中断号读取代码
        .macro get_irqnr_and_base, irqnr, irqstat, base, tmp
        ldr    \base, =(AT91C_VA_BASE_SYS)
        ldr    \irqnr, [\base, #AIC_IVR]
        ldr    \irqstat, [\base, #AIC_ISR]
        teq    \irqstat, #0
        streq \tmp, [\base, #AIC_EOICR]
        .endm
*/

1:        get_irqnr_and_base r0, r6, r5, lr
        movne    r1, sp
/*
//gliethttp_20071225[entry-header.S]
//Like adr, but force SVC mode (if required)
  .macro adrsvc, cond, reg, label
     adr\cond \reg, \label
  .endm
adrsvc ne, lr, 1b等价于subne lr, pc, #32
*/

        adrsvc    ne, lr, 1b
//gliethttp_20071225
//arch\arm\kernel\irq.c->asmlinkage void asm_do_IRQ(int irq, struct pt_regs *regs)
//对asm_do_IRQ的理解将在另一篇中《浅析armlinux 2.4.19中断irq分发例程的派发流程之高端层》浅析
        bne    asm_do_IRQ//在I位禁用irq中断期间执行asm_do_IRQ高端处理函数
        ldr    r0, [sp, #S_PSR]//将发生这个irq中断之前的cpu状态寄存器spsr拿出来
        msr    spsr, r0//拷贝到svc模式的spsr中
//弹出pt_regs结构体中的所有寄存器数值,恢复到被irq中断的内核代码执行处继续执行[gliethttp_20071225]
//^号操作中,如果有pc寄存器,那么和s操作一样,将恢复spsr中的内容到cpsr,同时自动切换cpu模式到spsr
        ldmia  sp, {r0 - pc}^
//----------------------------------------------------------------------
//2.再看看__irq_usr中断irq处理例程的实现代码
//从用户空间发生的irq中断,
//因为在vector_IRQ中irq模式下的lr_irq和lr_spsr执行msr spsr_c, r13之后,使用movs pc, lr进行跳
//转到__irq_svc,所以带s的操作指令movs,将使spsr_c的状态自动恢复到cpsr中,也就是使cpu工作在
//禁止irq中断的svc模式下[当然irq的I位在发生irq的同时就有硬件自动禁用了gliethttp_20071225]
//所以linux2.4内核下irq处理程序使用的sp就是暂时借用了svc内核空间程序的堆栈,如下代码也工作在svc模式
        .align    5
__irq_usr:sub sp, sp, #S_FRAME_SIZE//理解同__irq_svc
        stmia sp, {r0 - r12}//注意usr用户模式下的r0-r12和svc模式下的r0-r12是共用的
//参看《浅析armlinux 2.4.19中断irq分发例程的派发流程之根基》
//.LCirq+0 存放了 lr-4的地址值,也就是irq中断处理函数处理完成之后,返回到的pc地址值
//.LCirq+4 存放了 spsr,也就是irq中断发生跳转到irq处理函数vector_IRQ之前cpu所处的模式值
// 主要是用来对I和F位以及几个N、Z、C和V标志位的恢复
        ldr    r4, .LCirq
        add    r8, sp, #S_PC
//r5=用户模式下lr-4的地址值,也就是irq中断处理函数处理完成之后,返回到的pc地址值
//r6=用户模式下的cpsr值,返回时会被使用到
//r7=暂时无用
        ldmia    r4, {r5 - r7}
        stmia    r8, {r5 - r7}//uregs[15]=r5;uregs[16]=r6;uregs[17]=r7
        stmdb    r8, {sp, lr}^//^表示存储user用户模式下的sp,lr寄存器到r8,r8-4处
//通过上面的几个简单操作,被irq中断了的用户空间程序所使用的所有cpu寄存器都被暂时
//保存到了svc内核程序空间的sp堆栈上[gliethttp_20071225]
/*arch\arm\kernel\entry-header.S
        .macro    alignment_trap, rbase, rtemp, sym
#ifdef CONFIG_ALIGNMENT_TRAP
#define OFF_CR_ALIGNMENT(x)    cr_alignment - x
        ldr    \rtemp, [\rbase, #OFF_CR_ALIGNMENT(\sym)]
        mcr    p15, 0, \rtemp, c1, c0
#endif
        .endm

alignment_trap r4, r7, __temp_irq
zero_fp
对应的反汇编为:
c001737c:    e5947024     ldr    r7, [r4, #36]
c0017380:    ee017f10     mcr    15, 0, r7, cr1, cr0, {0}
c0017384:    e3a0b000     mov    fp, #0    ; 0x0
*/

//__temp_irq为.LCirq+0的值,也就是lr-4,也就是irq中断处理完成之后,用户空间的pc值
//所以它必须是合法的,必须经过检查
        alignment_trap r4, r7, __temp_irq
        zero_fp
1:      get_irqnr_and_base r0, r6, r5, lr
        movne  r1, sp
        adrsvc ne, lr, 1b
        bne    asm_do_IRQ//参数r0=irq,r1=regs
        mov    why, #0
/*
arch\arm\kernel\entry-header.S
        .macro    get_current_task, rd
        mov    \rd, sp, lsr #13
        mov    \rd, \rd, lsl #13
        .endm
*/

//因为当前在svc模式下,和当前的usr空间是一一对应的
//在schedule的时候,没有用户模式下的task都会对应一个内核模式堆栈
//所以这个sp就是svc内核模式下的用户task堆栈
//该内核模式堆栈的低8k空间存放了current的进程task_struct内核数据结构体
        get_current_task tsk
        b    ret_to_user
/*
arch\arm\kernel\entry-common.S
ENTRY(ret_to_user)
ret_slow_syscall:
    ldr    r1, [tsk, #TSK_NEED_RESCHED]
    ldr    r2, [tsk, #TSK_SIGPENDING]
    teq    r1, #0                    @ need_resched => schedule()
    bne    reschedule
1:    teq    r2, #0                  @ sigpending => do_signal()
    bne    __do_signal
restore:
//从当前sp恢复到usr用户空间,继续执行刚才被irq中断了的用户空间程序[gliethttp_20071225]
    restore_user_regs
__do_signal:
    enable_irq r1
    mov    r0, #0                    @ NULL 'oldset'
    mov    r1, sp                    @ 'regs'
    mov    r2, why                   @ 'syscall'
    bl    SYMBOL_NAME(do_signal)     @ note the bl above sets lr
    disable_irq r1                   @ ensure IRQs are disabled
    b    restore
*/
阅读(843) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~