欢迎来到得力文库 - 分享文档赚钱的网站! | 帮助中心 好文档才是您的得力助手!
得力文库 - 分享文档赚钱的网站
全部分类
  • 研究报告>
  • 管理文献>
  • 标准材料>
  • 技术资料>
  • 教育专区>
  • 应用文书>
  • 生活休闲>
  • 考试试题>
  • pptx模板>
  • 工商注册>
  • 期刊短文>
  • 图片设计>
  • ImageVerifierCode 换一换

    ARM Linux中断源码分析(2)——中断处理流程.docx

    • 资源ID:23870731       资源大小:1.17MB        全文页数:98页
    • 资源格式: DOCX        下载积分:15金币
    快捷下载 游客一键下载
    会员登录下载
    微信登录下载
    三方登录下载: 微信开放平台登录   QQ登录  
    二维码
    微信扫一扫登录
    下载资源需要15金币
    邮箱/手机:
    温馨提示:
    快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。
    如填写123,账号就是123,密码也是123。
    支付方式: 支付宝    微信支付   
    验证码:   换一换

     
    账号:
    密码:
    验证码:   换一换
      忘记密码?
        
    友情提示
    2、PDF文件下载后,可能会被浏览器默认打开,此种情况可以点击浏览器菜单,保存网页到桌面,就可以正常下载了。
    3、本站不支持迅雷下载,请使用电脑自带的IE浏览器,或者360浏览器、谷歌浏览器下载即可。
    4、本站资源下载后的文档和图纸-无水印,预览文档经过压缩,下载后原文更清晰。
    5、试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。

    ARM Linux中断源码分析(2)——中断处理流程.docx

    Four short words sum up what has lifted most successful individuals above the crowd: a little bit more.-author-dateARM Linux中断源码分析(2)中断处理流程ARM Linux中断源码分析(2)中断处理流程ARM Linux中断源码分析(2)中断处理流程ARM支持7类异常中断,所以中断向量表设8个条目,每个条目4字节,共32字节。 异常名称中断向量异常中断模式优先级复位0x0特权模式1未定义的指令0x4未定义指令中止模式6软件中断0x8特权模式6指令预取中止0x0c中止模式5数据访问中止0x10中止模式2保留0x14  外部中断请求IRQ0x18IRQ模式4快速中断请求FIQ0x1cFIQ模式3 回顾第一节所讲的内容,当一个异常或中断发生时,处理器会将PC设置为特定地址,从而跳转到已经初始化好的异常向量表。因此,要理清中断处理流程,先从异常向量表开始。对于ARM Linux而言,异常向量表和异常处理程序都存在arch/arm/kernel/entry_armv.S汇编文件中。vector异常向量表点击(此处)折叠或打开1. .globl    _vectors_start2. _vectors_start:3.     swi    SYS_ERROR04.     b    vector_und + stubs_offset5.     ldr    pc, .LCvswi + stubs_offset6.     b    vector_pabt + stubs_offset7.     b    vector_dabt + stubs_offset8.     b    vector_addrexcptn + stubs_offset9.     b    vector_irq + stubs_offset 中断入口,vector_irq10.     b    vector_fiq + stubs_offset11.12.     .globl    _vectors_end13. _vectors_end:vector_irq+stubs_offset为中断的入口点,此处之所以要加上stubs_offset,是为了实现位置无关编程。首先分析一下stubs_offset(宏)是如何计算的:.equ stubs_offset, _vectors_start + 0x200 - _stubs_start在第3节中已经提到,内核启动时会将异常向量表拷贝到 0xFFFF_0000,将异常向量处理程序的 stub 拷贝到 0xFFFF_0200。图5-1描述了异常向量表和异常处理程序搬移前后的内存布局。图5-1 异常向量表和异常处理程序搬移前后对比当汇编器看到B指令后会把要跳转的标签转化为相对于当前PC的偏移量(±32M)写入指令码。由于内核启动时中断向量表和stubs都发生了代码搬移,所以如果中断向量表中仍然写成b vector_irq,那么实际执行的时候就无法跳转到搬移后的vector_irq处,因为指令码里写的是原来的偏移量,所以需要把指令码中的偏移量写成搬移后的。设搬移后的偏移量为offset,如图5-1所示,offset = L1+L2     = 0x200 - (irq_PC_X - _vectors_start_X) + (vector_irq_X - _stubs_start_X)     = 0x200 - (irq_PC - _vectors_start) + (vector_irq - _stubs_start)     = 0x200 - irq_PC + _vectors_start + vector_irq - _stubs_start     = vector_irq + (_vectors_start + 0x200 - _stubs_start) - irq_PC令stubs_offset = _vectors_start + 0x200 - _stubs_start则offset = vector_irq + stubs_offset - irq_PC,所以中断入口点为“b       vector_irq + stubs_offset”,其中减去irq_PC是由汇编器在编译时完成的。vector_irq处理函数在分析vector_irq处理函数之前,先了解一下当一个异常或中断导致处理器模式改变时,ARM处理器内核的处理流程如下图所示:        中断刚发生时,处理器处于irq模式。在_stubs_start和_stubs_end之间找到vector_irq处理函数的定义vector_stub irq, IRQ_MODE, 4,其中vector_stub是一个宏(在arch/arm/kernel/entry_armv.S中定义),为了分析更直观,我们将vector_stub宏展开如下:1. /*2.  * Interrupt dispatcher3.  */4.     vector_irq:5.     .if 46.     sub    lr, lr, #4 在中断发生时,lr指向最后执行的指令地址加上8。只有在当前指令执行完毕后,才进入中断处理,所以返回地址应指向下一条指令,即(lr-4)处。7.     .endif8.9. 10.      Save r0, lr_<exception> (parent PC) and spsr_<exception>11.      (parent CPSR)12.     13.     stmia    sp, r0, lr         保存r0, lr到irq模式下的栈中14.     mrs    lr, spsr15.     str    lr, sp, #8        保存spsr到irq模式下的栈中16.17.     18.      Prepare for SVC32 mode. IRQs remain disabled.19.     20.     mrs    r0, cpsr 21.     eor    r0, r0, #( IRQ_MODE SVC_MODE) 设置成SVC模式,但未切换22.     msr    spsr_cxsf, r0 保存到spsr_irq中23.24.     25.      the branch table must immediately follow this code26.     27.     and    lr, lr, #0x0f lr存储着上一个处理器模式的cpsr值,lr = lr & 0x0f取出用于判断发生中断前是用户态还是核心态的信息,该值用于下面跳转表的索引。28.     mov    r0, sp 将irq模式下的sp保存到r0,作为参数传递给即将调用的_irq_usr或_irq_svc29.     ldr    lr, pc, lr, lsl #2 pc指向当前执行指令地址加8,即跳转表的基址。lr作为索引,由于是4字节对齐,所以lr = lr << 2.30.     movs    pc, lr branch to handler in SVC mode31.                   当mov指令后加“s”且目标寄存器为pc时,当前模式下的spsr会被复制到cpsr,从而完成模式切换(从irq模式切换到svc模式)并且跳转到pc指向的指令继续执行32. ENDPROC(vector_irq)33.34.     .long    _irq_usr             0 (USR_26 / USR_32)35.     .long    _irq_invalid             1 (FIQ_26 / FIQ_32)36.     .long    _irq_invalid             2 (IRQ_26 / IRQ_32)37.     .long    _irq_svc             3 (SVC_26 / SVC_32)38.     .long    _irq_invalid             439.     .long    _irq_invalid             540.     .long    _irq_invalid             641.     .long    _irq_invalid             742.     .long    _irq_invalid             843.     .long    _irq_invalid             944.     .long    _irq_invalid             a45.     .long    _irq_invalid             b46.     .long    _irq_invalid             c47.     .long    _irq_invalid             d48.     .long    _irq_invalid             e49.     .long    _irq_invalid             f_irq_usr如果发生中断前处于用户态则进入_irq_usr,其定义如下(arch/arm/kernel/entry_armv.S): 1. .align    52. _irq_usr:3.     usr_entry 保存中断上下文,稍后分析4.     kuser_cmpxchg_check5. #ifdef CONFIG_TRACE_IRQFLAGS6.     bl    trace_hardirqs_off7. #endif8.     get_thread_info tsk 获取当前进程的进程描述符中的成员变量thread_info的地址,并将该地址保存到寄存器tsk(r9)(在entry-header.S中定义)9. #ifdef CONFIG_PREEMPT 如果定义了抢占,增加抢占数值10.     ldr    r8, tsk, #TI_PREEMPT         获取preempt计数器值11.     add    r7, r8, #1             preempt加1,标识禁止抢占12.     str    r7, tsk, #TI_PREEMPT 将加1后的结果写入进程内核栈的变量中13. #endif14.     irq_handler 调用中断处理程序,稍后分析15. #ifdef CONFIG_PREEMPT16.     ldr    r0, tsk, #TI_PREEMPT 获取preempt计数器值17.     str    r8, tsk, #TI_PREEMPT 将preempt恢复到中断前的值18.     teq    r0, r7 比较中断前后preempt是否相等19.     strne    r0, r0, -r0 如果不等,则产生异常(向地址0写入数据)?20. #endif21. #ifdef CONFIG_TRACE_IRQFLAGS22.     bl    trace_hardirqs_on23. #endif24.     mov    why, #0 r8=025.     b    ret_to_user 中断处理完成,恢复中断上下文并返回中断产生的位置,稍后分析26.  UNWIND(.fnend        )27. ENDPROC(_irq_usr)宏定义usr_entry(保护上下文到栈)上面代码中的usr_entry是一个宏定义,主要用于保护上下文到栈中:1. .macro    usr_entry2.  UNWIND(.fnstart    )3.  UNWIND(.cantunwind    )     dont unwind the user space4.     sub    sp, sp, #S_FRAME_SIZE ATPCS中,堆栈被定义为递减式满堆栈,所以首先让sp向下移动#S_FRAME_SIZE(pt_regs结构体size),准备向栈中存放数据。此处的sp是svc模式下的栈指针。5.     stmib    sp, r1 - r126.7.     ldmia    r0, r1 - r38.     add    r0, sp, #S_PC         here for interlock avoidance9.     mov    r4, #-1             "" "" "" ""10.11.     str    r1, sp         save the "real" r0 copied12.                      from the exception stack13.14.     15.      We are now ready to fill in the remaining blanks on the stack:16.     17.      r2 - lr_<exception>, already fixed up for correct return/restart18.      r3 - spsr_<exception>19.      r4 - orig_r0 (see pt_regs definition in ptrace.h)20.     21.      Also, separately save sp_usr and lr_usr22.     23.     stmia    r0, r2 - r424.     stmdb    r0, sp, lr 将user模式下的sp和lr保存到svc模式的栈中25.26.     27.      Enable the alignment trap while in kernel mode28.     29.     alignment_trap r030.31.     32.      Clear FP to mark the first stack frame33.     34.     zero_fp35.     .endm上面的这段代码主要是在填充结构体pt_regs ,在include/asm/ptrace.h中定义:1. struct pt_regs 2.     long uregs18;3. ;4.5. #define ARM_cpsr    uregs166. #define ARM_pc        uregs157. #define ARM_lr        uregs148. #define ARM_sp        uregs139. #define ARM_ip        uregs1210. #define ARM_fp        uregs1111. #define ARM_r10        uregs1012. #define ARM_r9        uregs913. #define ARM_r8        uregs814. #define ARM_r7        uregs715. #define ARM_r6        uregs616. #define ARM_r5        uregs517. #define ARM_r4        uregs418. #define ARM_r3        uregs319. #define ARM_r2        uregs220. #define ARM_r1        uregs121. #define ARM_r0        uregs022. #define ARM_ORIG_r0    uregs17usr_entry宏填充pt_regs结构体的过程如图5-2所示,先将r1r12保存到ARM_r1ARM_ip(绿色部分),然后将产生中断时的r0寄存器内容保存到ARM_r0(蓝色部分),接下来将产生中断时的下一条指令地址lr_irq、spsr_irq和r4保存到ARM_pc、ARM_cpsr和ARM_ORIG_r0(红色部分),最后将用户模式下的sp和lr保存到ARM_sp 和ARM_lr 中。图5-2 usr_entry宏填充pt_regs结构体_irq_svc如果发生中断前处于核心态则进入_irq_svc,其定义如下(arch/arm/kernel/entry_armv.S):1. .align    52. _irq_svc:3.     svc_entry 保存中断上下文4.5. #ifdef CONFIG_TRACE_IRQFLAGS6.     bl    trace_hardirqs_off7. #endif8. #ifdef CONFIG_PREEMPT9.     get_thread_info tsk10.     ldr    r8, tsk, #TI_PREEMPT         获取preempt计数器值11.     add    r7, r8, #1             preempt加1,标识禁止抢占12.     str    r7, tsk, #TI_PREEMPT 将加1后的结果写入进程内核栈的变量中13. #endif14.15.     irq_handler 调用中断处理程序,稍后分析16. #ifdef CONFIG_PREEMPT17.     str    r8, tsk, #TI_PREEMPT         恢复中断前的preempt计数器18.     ldr    r0, tsk, #TI_FLAGS         获取flags19.     teq    r8, #0                 判断preempt是否等于020.     movne    r0, #0                 如果preempt不等于0,r0=021.     tst    r0, #_TIF_NEED_RESCHED 将r0与#_TIF_NEED_RESCHED做“与操作”22.     blne    svc_preempt 如果不等于0,说明发生内核抢占,需要重新调度。23. #endif24.25.     ldr    r0, sp, #S_PSR         irqs are already disabled26.     msr    spsr_cxsf, r027. #ifdef CONFIG_TRACE_IRQFLAGS28.     tst    r0, #PSR_I_BIT29.     bleq    trace_hardirqs_on30. #endif31.     svc_exit r4     恢复中断上下文,稍后分析。32.  UNWIND(.fnend        )33. ENDPROC(_irq_svc)宏定义svc_entry(保护中断上下文到栈)其中svc_entry是一个宏定义,主要用于保护中断上下文到栈中。svc_entry主要是在当前堆栈上分配一个pt_regs结构,把r0-r15以及cpsr等保存到这个结构中,在进入irq_handler时,sp指向pt_regs底端:1. .macro    svc_entry, stack_hole=02.  UNWIND(.fnstart        )3.  UNWIND(.save r0 - pc        )4.     sub    sp, sp, #(S_FRAME_SIZE + stack_hole)5.  SPFIX(    tst    sp, #4        )6.  SPFIX(    bicne    sp, sp, #4    )7.     stmib    sp, r1 - r128.9.     ldmia    r0, r1 - r310.     add    r5, sp, #S_SP         here for interlock avoidance11.     mov    r4, #-1             "" "" "" ""12.     add    r0, sp, #(S_FRAME_SIZE + stack_hole)13.  SPFIX(    addne    r0, r0, #4    )14.     str    r1, sp         save the "real" r0 copied15.                      from the exception stack16.17.     mov    r1, lr18.19.     20.      We are now ready to fill in the remaining blanks on the stack:21.     22.      r0 - sp_svc23.      r1 - lr_svc24.      r2 - lr_<exception>, already fixed up for correct return/restart25.      r3 - spsr_<exception>26.      r4 - orig_r0 (see pt_regs definition in ptrace.h)27.     28.     stmia    r5, r0 - r429.     .endmsvc_entry宏填充pt_regs结构体的过程如图5-2所示,先将r1r12保存到ARM_r1ARM_ip(绿色部分),然后将产生中断时的r0寄存器内容保存到ARM_r0(蓝色部分),由于是在svc模式下产生的中断,所以最后将sp_svc、lr_svc、lr_irq、spsr_irq和r4保存到ARM_sp、ARM_lr、ARM_pc、ARM_cpsr和ARM_ORIG_r0(红色部分)。图5-3 svc_entry宏填充pt_regs结构体上述的中断上下文保存过程共涉及了3种栈指针,分别是:用户空间栈指针sp_usr,内核空间栈指针sp_svc和irq模式下的栈栈指针sp_irq。sp_usr指向在setup_arg_pages函数中创建的用户空间栈。sp_svc指向在alloc_thread_info函数中创建的内核空间栈。sp_irq在cpu_init函数中被赋值,指向全局变量stacks.irq0。附录1,arm体系下pt_regs结构struct pt_regs long uregs18;uregs0 - uregs17分别对应,r0 - r15,cpsr,ORIG_r0附录1,irq中断时堆栈的变化-spsr-lr ,中断返回地址,修正后的-r0- <-进入irq_svc之前,sp的值,也是r0的值pt_regs- <-进入svc_entry后,sp的值irq_handler(中断处理程序)保存中断上下文后则进入中断处理程序irq_handler,定义在arch/arm/kernel/entry_armv.S文件中:1. .macro    irq_handler2.     get_irqnr_preamble r5, lr 3. 1:    get_irqnr_and_base r0, r6, r5, lr 获取中断号,存到r0中,稍后分析4.     movne    r1, sp 如果中断号不等于0,将r1=sp,即pt_regs结构体首地址5.     6.      routine called with r0 = irq number, r1 = struct pt_regs *7.     8.     adrne    lr, 1b 如果r0(中断号)不等于0, lr(返回地址)等于标号1处,即get_irqnr_and_base r0, r6, r5, lr的那行,即循环处理所有的中断。9.     bne    asm_do_IRQ 进入中断处理,稍后分析。10. 11.     .endmget_irqnr_and_base用于判断当前发生的中断号(与CPU紧密相关),此处不再分析。如果获取的中断号不等于0,则将中断号存入r0寄存器作为第一个参数,pt_regs结构体地址存入r1寄存器作为第二个参数,跳转到c语言函数asm_do_IRQ做进一步处理。为了不让大家在汇编语言和C语言之间来回切换,还是先把最后一点汇编语言代码(中断返回汇编代码)分析了再去分析asm_do_IRQ吧。回看_irq_usr和_irq_svc标号处的代码,在完成了irq_handler中断处理函数后,要完成从中断异常处理程序返回到中断点的工作。ret_to_user如果中断产生于用户空间,则调用ret_to_user来恢复中断现场并返回用户空间继续运行:1. arch/arm/kernel/entry_armv.S2. ENTRY(ret_to_user)3. ret_slow_syscall:4.     disable_irq     disable interrupts,此处不明白,disable_irq应该接受irq中断号作为参数,来禁止指定的irq号中断线。但是此处调用disable_irq之前并没有将irq中断号存入r0寄存器,这是为什么?5.     ldr    r1, tsk, #TI_FLAGS 获取thread_info->flags6.     tst    r1, #_TIF_WORK_MASK 判断是否有待处理的work7.     bne    work_pending 如果有,则进入work_pending进一步处理,主要是完成用户进程抢占相关处理。8. no_work_pending: 如果没有work待处理,则准备恢复中断现场,返回用户空间。9.     /* perform architecture specific actions before user return */10.     arch_ret_to_user r1, lr 调用体系结构相关的代码11.12.     restore_user_regs fast = 0, offset = 0 调用restore_user_regs13. ENDPROC(ret_to_user)14.15. 以下是恢复中断现场寄存器的宏,就是将发生中断时保存在内核空间堆栈上的寄存器还原,可以对照图5-2所示的内核空间堆栈保存的内容来理解下面代码:16. .macro    restore_user_regs, fast = 0, offset = 017.     ldr    r1, sp, #offset + S_PSR     从内核栈中获取发生中断时的cpsr值18.     ldr    lr, sp, #offset + S_PC!     从内核栈中获取发生中断时的下一条指令地址19.     msr    spsr_cxsf, r1             将r1保存到spsr_svc20. #if defined(CONFIG_CPU_32v6K)21.     clrex                     clear the exclusive monitor22. #elif defined (CONFIG_CPU_V6)23.     strex    r1, r2, sp             clear the exclusive monitor24. #endif25.     .if    fast26.     ldmdb    sp, r1 - lr     get calling r1 - lr27.     .else28.     ldmdb    sp, r0 - lr 存在,所以将内核栈保存的内容恢复到用户空间的r0lr寄存器29.     .endif30.     add    sp, sp, #S_FRAME_SIZE - S_PC 31.     movs    pc, lr    将发生中断时的下一条指令地址存入pc,从而返回中断点继续执行,并且将发生中断时的cpsr内容恢复到cpsr寄存器中(开启中断)。32.     .endmsvc_exit如果中断产生于内核空间,则调用svc_exit来恢复中断现场:1. arch/arm/kernel/ entry-header.S2. .macro    svc_exit, rpsr3.     msr    spsr_cxsf, rpsr4. #if defined(CONFIG_CPU_32v6K)5.     clrex                     clear the exclusive monitor6.     ldmia    sp, r0 - pc             load r0 - pc, cpsr7. #elif defined (CONFIG_CPU_V6)8.     ldr    r0, sp9.     strex    r1, r2, sp             clear the exclusive monitor10.     ldmib    sp, r1 - pc             load r1 - pc, cpsr11. #else12.     ldmia    sp, r0 - pc             返回内核空间时,恢复中断现场比较简单,就是将r0-pc以及cpsr恢复即可,同时中断也被开启。13. #endif14.     .endmasm_do_IRQ函数ok,分析完所有与中断相关的汇编语言代码后,下面开始分析C语言代码:在arch/arm/kernel/irq.c文件中找到asm_do_IRQ函数定义:1. asmlinkage void _exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)2. 3.     /*保存新的寄存器集合指针到全局cpu变量,方便后续处理程序访问寄存器集合。*/4.     struct pt_regs *old_regs = set_irq_regs(regs); 5.6.     irq_enter();7.8.     /*9.      * Some hardware gives randomly wrong interrupts. Rather10.      * than crashing, do something sensible.11.      */12.     if (unlikely(irq >= NR_IRQS)  /判断中断号13.         if (printk_ratelimit()14.             printk(KERN_WARNING "Bad IRQ%un", irq);15.         ack_bad_irq(irq);16.      else 17.         generic_handle_irq(irq); /调用中断处理函数18.     19.20.     /* AT91 specific workaround */21.     irq_finish(irq);22.23.     irq_exit();24.     set_irq_regs(old_regs);25. asm_do_IRQ是中断处理的C入口函数,主要负责调用request_irq注册的中断处理函数,其流程如图5-4所示:图5-4 asm_do_IRQ流程1、old_regs = set_irq_regs(regs)其中,set_irq_regs将指向寄存器结构体的指针保存在一个全局的CPU变量中,后续的程序可以通过该变量访问寄存器结构体。所以在进入中断处理前,先将全局CPU变量中保存的旧指针保留下来,等到中断处理结束后再将其恢复。2、irq_enterirq_enter负责更新一些统计量:1. <kernel/softirq.c>2. void irq_enter(void)3

    注意事项

    本文(ARM Linux中断源码分析(2)——中断处理流程.docx)为本站会员(豆****)主动上传,得力文库 - 分享文档赚钱的网站仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知得力文库 - 分享文档赚钱的网站(点击联系客服),我们立即给予删除!

    温馨提示:如果因为网速或其他原因下载失败请重新下载,重复下载不扣分。




    关于得利文库 - 版权申诉 - 用户使用规则 - 积分规则 - 联系我们

    本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知得利文库网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

    工信部备案号:黑ICP备15003705号-8 |  经营许可证:黑B2-20190332号 |   黑公网安备:91230400333293403D

    © 2020-2023 www.deliwenku.com 得利文库. All Rights Reserved 黑龙江转换宝科技有限公司 

    黑龙江省互联网违法和不良信息举报
    举报电话:0468-3380021 邮箱:hgswwxb@163.com  

    收起
    展开