Linux异常处理结构、中断处理结构

LINUX 的异常向量在哪里:0xffff0000(可通过配置某个寄存器来配置)

1、ARM异常向量表

void __init trap_init(void)   //将0x00000000异常向量 复制到 0xffff0000 处

      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

.globl    __stubs_start
__stubs_start:
    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

  从第4行到第19行,记录了(代码链接阶段填入的地址数据)在各个模式下遇到irq中断时,发生异常的处理分支。比如第4行__irq_usr表示用户模式下发生irq中断时,由__irq_usr对应的代码段来处理这种情况。

vector_stub是一个宏,将宏展开内容如下:

vector_irq:

    //保存cpsr/lr

    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语言总入口函数。

5. asm_do_IRQ函数原型为

smlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)

进入asm_do_IRQ这个函数

(1)struct irq_desc *desc = irq_desc + irq;

    从名字上看可知    irq_desc[NR_IRQS]这是一个“中断描述”数组。以中断号“NR_IRQS”为下标。
   struct irq_desc *desc = irq_desc + irq;
   在这个数组下标处取到一个“数组项”。

(2) 进入处理函数 desc_handle_irq(irq, desc);
            
       static inline void desc_handle_irq(unsigned int irq, struct irq_desc *desc)
       {
                        desc->handle_irq(irq, desc);
       }
            

 搜索handle_irq,发现handle_irq被__set_irq_handler调用过
 搜索__set_irq_handler,发现__set_irq_handler被set_irq_handler调用过
 搜索set_irq_handler,发现__set_irq_handler被s3c24xx_init_irq(void)调用过

1~2作用: 
                在“void __init s3c24xx_init_irq(void)”中实现初始化

中断处理 C 函数入口 “asm_do_IRQ”会调用到事先初始化好的“handle_irq”函数。
handle_irq == handle_edge_irq
 handle_edge_irq:
  -->desc->chip->ack(irq). 清中断
 -->handle_IRQ_event(irq, action). 
                      --> action->handler()处理中断

猜你喜欢

转载自blog.csdn.net/qq_39541098/article/details/84565796