Linux驱动之异常处理体系结构简析

发布时间:2024-08-20  

异常的概念在单片机中也接触过,它的意思是让CPU可以暂停当前的事情,跳到异常处理程序去执行。以前写单片机裸机程序属于前后台程序,前台指的就是mian函数里的while(1)大循环,后台指的就是产生异常后的处理程序。ARM9有以下几种异常模式:


ARM架构的异常向量的地址可以是0x00000000,也可以是0xffff0000,Linux使用地址0xffff0000。在初始化时先将中断向量表放到0xffff0000处,在init/main.c的start_kernel函数里的trap_init();函数中处理具体代码为:


718    void __init early_trap_init(void)

719    {

    ...

    ...

            /*将中断向量表的拷贝到vectors处*/

732        memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);//vectors=CONFIG_VECTORS_BASE=0xffff0000在配置内核时生成

                                                                                     //位于includelinuxAutoconf.h中            

            /*将中断向量表的跳转地址的处理代码拷贝到vectors+0x200处*/

733        memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);

    ...

    ...

745    }


下面将以IRQ异常处理为例子描述完整的异常处理流程


1、IRQ异常处理过程,这个异常的产生通常是可以由硬件配置的,S3C2440的中断结构最终都会反应在IRQ异常上


继续看到异常向量表,我们以产生IRQ异常为例:它位于archarmkernelentry-armv.S中,可以看到它跳转到了vector_irq + stubs_offset 处


    .equ    stubs_offset, __vectors_start + 0x200 - __stubs_start//计算跳转地址的偏移量


    .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       //跳转到IRQ的异常处理程序,b是位置无关码,其中vector_irq调用了vector_stub宏

    b    vector_fiq + stubs_offset


搜索vector_irq 发现没有搜到,它其实是调用vector_stub宏生成的。这个宏后面介绍,先看到vector_stub irq,它最终生成vector_irq


        .globl    __stubs_start  //调用vector_stub宏定义的变量的开始地址

__stubs_start:

/*

 * Interrupt dispatcher

 */

    vector_stub    irq, IRQ_MODE, 4//调用vector_stub宏定义了vector_irq变量,IRQ异常跳转到这里开始执行。


    .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


接着介绍vector_stub的调用过程


vector_irq:

    .if 4

    sub    lr, lr, 4//lr = lr-4

    .endif


    @

    @ Save r0, lr_ (parent PC) and spsr_

    @ (parent CPSR)

    @

    stmia    sp, {r0, lr}        @ save r0, lr//保存r0与lr寄存器到IRQ模式的堆栈

    mrs    lr, spsr                       //将spsr赋给lr

    str    lr, [sp, #8]        @ save spsr    //将lr入栈,即spsr入栈


    @

    @ Prepare for SVC32 mode.  IRQs remain disabled.

    @

    mrs    r0, cpsr

    eor    r0, r0, #(mode ^ SVC_MODE)

    msr    spsr_cxsf, r0                  //将r0的值赋给spsr_cxsf,此时的状态还是处于IRQ模式


    @

    @ the branch table must immediately follow this code

    @

    and    lr, lr, #0x0f                   //lr=lr&0x0f,lr起始就是spsr的值,它保存了进入IRQ模式前的CPU模式,其实是5位控制的,这里只用到4位,用来跳转到不同的处理函数

    mov    r0, sp                          //将管理模式的sp的值给r0

    ldr    lr, [pc, lr, lsl #2]            //lr = *(pc+lr<<2)。如果在进入IRQ之前是用户模式即是从应用层进入的,那么lr = pc = __irq_usr.否则是管理模式也就是处于内核层时发生了IRQ异常 lr = pc+12=__irq_svc

    movs    pc, lr            @ branch to handler in SVC mode//将lr的值给pc,同时将spsr的值赋给cpsr,此时才是进入了管理模式

    .endm


这个宏执行完成之后将进入SVC模式,然后调用__irq_usr或者__irq_svc。以__irq_usr为例继续说明异常函数调用过程


__irq_usr:

    usr_entry             //入口的一些处理,保存寄存器到堆栈

    get_thread_info tsk   //得到线程信息

    irq_handler           //真正的异常处理

    b    ret_to_user      //切换回异常前的状态,将堆栈的寄存器出栈

可以看到这个函数显示保存一些寄存器数据然后调用irq_handler这个真正的异常处理函数,先是判断INTPND寄存器是否有某一位被置1,如果置1,说明有中断发生,然后从INTOFFSET寄存器取得记录的中断号,经过处理后放入r0,然后irq_handler最终调用了这个C函数。最后再将寄存器恢复到异常前的状态。IRQ异常处理结束


.macro    irq_handler

    get_irqnr_preamble r5, lr

1:    get_irqnr_and_base r0, r6, r5, lr

    movne    r1, sp

    @

    @ routine called with r0 = irq number, r1 = struct pt_regs *

    @

    adrne    lr, 1b

    bne    asm_do_IRQ//最终调用了asm_do_IRQ。这个是C函数


#ifdef CONFIG_SMP

    /*

     * XXX

     *

     * this macro assumes that irqstat (r6) and base (r5) are

     * preserved from get_irqnr_and_base above

     */

    test_for_ipi r0, r6, r5, lr

    movne    r0, sp

    adrne    lr, 1b

    bne    do_IPI


#ifdef CONFIG_LOCAL_TIMERS

    test_for_ltirq r0, r6, r5, lr

    movne    r0, sp

    adrne    lr, 1b

    bne    do_local_timer

#endif

#endif


    .endm


文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>