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

2016年(4)

2015年(6)

2014年(3)

2013年(27)

2012年(23)

分类: LINUX

2013-09-22 22:54:37

Mips 中断处理分析

本文分析linux内核中MIPS架构对于中断的处理过程。首先分析mips架构中cpu响应中断的过程,然后在总结内核在MIPS架构下对中断的处理

1. mips 架构中断处理架构

软件的设计取决于硬件的行为。MIPSARM都是RISC架构,但是两者在很多方面有比较大的区别。CPU的行为比ARM更加精简,连TBL查找都需要靠软件来设置。ARM更像是介于MIPSX86之间的一种CPU,结合了两者的一些特点。

了解内核在MIPS架构下对中断的处理,首先需要了解一下MIPS架构的CPU对中断的响应。和ARM不相同,MIPS将中断归为异常。MIPS CPU总共定义了5种异常。分别为:

1. 冷重启、热重启异常

2. TBL充填异常

3. XTBL充填异常

4. Cache错误异常

5. 其它异常

 

当发生上面异常后,CPU会跳转到上面所述地址开始执行异常处理程序。对于中断,CPU会跳转到其他类型的异常位置开始执行异常处理程序。

MIPS CPU发生中断后,CPU的状态变化到如下状态,总结如下列表:

1. EPC寄存器保存了发生中断是程序执行指令的地址

2. CP0STATUS寄存器 EXL置位为1,表示正在异常状态,这个时候CPU不能再响应中断(不管IE是否开启),自动进入核心状态。

3. CP0CAUSE寄存器中ExcCode0,表示是中断异常

4. CP0CAUSE寄存器IP被设置为不同的中断号

 

下图为MIPS CPU异常、中断的相应关系。

 

 

2. Linux内核MIPS架构下对中断的处理过程。

 

NESTED(except_vec3_generic, 0, sp) 
    .set    push 
    .set    noat 
    mfc0    k1, CP0_CAUSE 
    andi    k1, k1, 0x7c 
#ifdef CONFIG_64BIT 
    dsll    k1, k1, 1 
#endif 
    PTR_L    k0, exception_handlers(k1) 
    jr    k0 
    .set    pop 
    END(except_vec3_generic) 

Excepte_vec3_generic函数被拷贝到了其它异常的处理函数入口点。所以如果是中断触发其它类型的异常,首先调用这个函数。这个函数的处理很简单,只是检查了CP0_CAUSE域的数值n,然后根据这个数值可以判断是什么类型的异常,将这个数值n作为一个索引去调用exception_handler[n]中的异常处理函数。对于中断n等于0.

 

3. Handle_int函数的处理

Handle_int函数实际上就是注册到exception_handler[0]上专门处理中断引发的异常。该函数主要做的工作就是:

1. 调用SAVE_ALL保存中断现场

2. 调用CLI关闭中断

3. 调用plat_irq_dispatch判断中断类型进行分别处理

4. 处理中断完成后从ret_from_irq退出中断处理流程

NESTED(handle_int, PT_SIZE, sp) 
#ifdef CONFIG_TRACE_IRQFLAGS 
    /* 
     * Check to see if the interrupted code has just disabled 
     * interrupts and ignore this interrupt for now if so. 
     * 
     * local_irq_disable() disables interrupts and then calls 
     * trace_hardirqs_off() to track the state. If an interrupt is taken 
     * after interrupts are disabled but before the state is updated 
     * it will appear to restore_all that it is incorrectly returning with 
     * interrupts disabled 
     */ 
    .set    push 
    .set    noat 
    mfc0    k0, CP0_STATUS 
#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX) 
    and    k0, ST0_IEP 
    bnez    k0, 1f 
 
    mfc0    k0, EP0_EPC 
    .set    noreorder 
    j    k0 
    rfe 
#else 
    and    k0, ST0_IE 
    bnez    k0, 1f 
 
    eret 
#endif 
1
    .set pop 
#endif 
    SAVE_ALL 
    CLI 
    TRACE_IRQS_OFF 
 
    LONG_L    s0, TI_REGS($28
    LONG_S    sp, TI_REGS($28
    PTR_LA    ra, ret_from_irq 
    j    plat_irq_dispatch 
    END(handle_int) 

 

4. plat_irq_dispatch函数的处理

plat_irq_dispatch函数实际上就是读取触发中断的类型,然后根据不同类型调用do_IRQ函数,do_IRQ函数是内核中的接口函数,和体系结构的关联就不是那么紧密了,所以本文暂时不做分析。

asmlinkage void plat_irq_dispatch(void

    unsigned long pending; 
 
    pending = read_c0_status() & read_c0_cause() & ST0_IM; 
 
    if (pending & STATUSF_IP7) 
        do_IRQ(RALINK_CPU_IRQ_COUNTER); 
 
    else if (pending & STATUSF_IP5) 
        do_IRQ(RALINK_CPU_IRQ_FE); 
 
    else if (pending & STATUSF_IP6) 
        do_IRQ(RALINK_CPU_IRQ_WIFI); 
 
    else if (pending & STATUSF_IP4) 
        do_IRQ(RALINK_CPU_IRQ_PCI); 
 
    else if (pending & STATUSF_IP2) 
        do_IRQ(RALINK_CPU_IRQ_INTC); 
 
    else 
        spurious_interrupt(); 

5. ret_from_irq函数的处理流程

ret_from_irq函数主要负责从中断返回的处理过程,其中包括从kernel->user或者从kernel->kernel的恢复。函数的主要处理流程如下图。

 

从上面的函数调用关系可以看到,从中断返回分为两中场景,一种是从中断返回到用户态,一种是返回内核态。返回用户态首先检查是否有信号量需要处理,或者是否需要调度进程。如果不需要直接restore_all返回。返回到内核态是差不多的场景,首先检查是不是能抢占,如果能抢占就试图调度进程。如果不能抢占,调用restore_all返回用户态。

FEXPORT(ret_from_irq) 
    LONG_S    s0, TI_REGS($28
FEXPORT(__ret_from_irq) 
    LONG_L    t0, PT_STATUS(sp)        # returning to kernel mode? 
    andi    t0, t0, KU_USER 
    beqz    t0, resume_kernel 
 
resume_userspace: 
    local_irq_disable        # make sure we dont miss an 
                    # interrupt setting need_resched 
                    # between sampling and return 
    LONG_L    a2, TI_FLAGS($28)    # current->work 
    andi    t0, a2, _TIF_WORK_MASK    # (ignoring syscall_trace) 
    bnez    t0, work_pending 
    j    restore_all 
 
#ifdef CONFIG_PREEMPT 
resume_kernel: 
    local_irq_disable 
    lw    t0, TI_PRE_COUNT($28
    bnez    t0, restore_all 
need_resched: 
    LONG_L    t0, TI_FLAGS($28
    andi    t1, t0, _TIF_NEED_RESCHED 
    beqz    t1, restore_all 
    LONG_L    t0, PT_STATUS(sp)        # Interrupts off
    andi    t0, 1 
    beqz    t0, restore_all 
    jal    preempt_schedule_irq 
    b    need_resched 
#endif 

 

Andy Yixin Deng

2013-09-20 南京

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

wzw2002014-10-14 16:32:51

你好,真是一好文章,为什么看不到图片呢,,,,?