Mips 中断处理分析
本文分析linux内核中MIPS架构对于中断的处理过程。首先分析mips架构中cpu响应中断的过程,然后在总结内核在MIPS架构下对中断的处理
1. mips 架构中断处理架构
软件的设计取决于硬件的行为。MIPS和ARM都是RISC架构,但是两者在很多方面有比较大的区别。CPU的行为比ARM更加精简,连TBL查找都需要靠软件来设置。ARM更像是介于MIPS和X86之间的一种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. CP0中STATUS寄存器 EXL置位为1,表示正在异常状态,这个时候CPU不能再响应中断(不管IE是否开启),自动进入核心状态。
3. CP0中CAUSE寄存器中ExcCode为0,表示是中断异常
4. CP0中CAUSE寄存器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 南京
阅读(10899) | 评论(1) | 转发(2) |