分类: BSD
2012-03-31 15:25:51
Call tree for interrupt:
(exception.S)VECTOR(Mip***ception, unknown)->machExceptionTable->MipsUserIntr/MipsKernIntr->cpu_intr
Linux:
trap_init():loop call->set_vi_handler()->set_vi_srs_handler->msc_bind_eic_interrupt
set_vi_srs_handler:
b = (unsigned char *)(ebase + 0x200 + n*VECTORSPACING);
vec_start = except_vec_vi
memcpy(b, vec_start, handler_len);
BUILD_ROLLBACK_PROLOGUE except_vec_vi
NESTED(except_vec_vi, 0, sp)
SAVE_SOME
SAVE_AT
.set push
.set noreorder
FEXPORT(except_vec_vi_lui)
lui v0, 0 /* Patched */
j except_vec_vi_handler
FEXPORT(except_vec_vi_ori)
ori v0, 0 /* Patched */
.set pop
END(except_vec_vi)
EXPORT(except_vec_vi_end)
具体的异常向量初始化代码参见arch/mips/kernel/traps.c。大概执行流程如下:
1、计算所有外部中断向量的size(个数xIntCtl[VS])
2、通过size计算ebase,并将结果写入EBASE寄存器
3、循环调用set_vi_handler初始化外部中断向量(根据VEIC或者VINT支持的外部中断个数决定循环次数)。handler地址如果是
NULL,则会指向do_default_vi。细节还由是否支持shadow寄存器决定。do_default_vi就是产生一个kernel
panic。
set_vi_handler就是调用set_vi_srs_handler,其中srs参数为0,表示目前没有支持shadow寄存器。set_vi_srs_handler流程如下:
1、获得中断handler的地址,如果是NULL则使用do_default_vi
2、计算对应中断向量的基址(ebase+x200+nxIntCtl[VS])
3、计算中断vector的长度(except_vec_vi_end - vec_start)。vec_start等于except_vec_vi
4、copy从vec_start开始,handler长度的代码到该中断向量的基址。目前copy的是默认代码
5、计算handler中lui和ori指令的地址,修改这两条指令的内容,使得它们load真正的中断handler的地址到v0寄存器中
6、flush icache
注意,这里面有一个技巧,就是修改默认的lui和ori指令。默认的行为是load地址0到v0中,修改后就是load真正的中断handler到v0中。
w = (u32 *)(b + lui_offset);
*w = (*w & 0xffff0000) | (((u32)handler >> 16) & 0xffff);
w = (u32 *)(b + ori_offset);
*w = (*w & 0xffff0000) | ((u32)handler & 0xffff);
local_flush_icache_range((unsigned long)b,(unsigned long)(b+handler_len));
最终每一个中断vector都是这个样子:
保存部分上下文(包括切换堆栈)
load真正的中断handler到v0中
跳转到except_vec_vi_handler
在except_vec_vi_handler中,保存剩下的上下文,load ret_from_irq到ra中
jr v0跳转到真正的中断handler处理中断