本文共 4166 字,大约阅读时间需要 13 分钟。
关于中断,可以简单的划分为两类,一类是软中断(比如:系统调用,系统报错等);另一类则是硬中断(即硬件设备所产生的中断)。这里,来分析一下内核是如何识别并处理这些中断的。
首先,了解一下MIPS架构中是如何通过寄存器来识别并处理中断的。在MIPS架构的CPU中,其内部存在的协处理器中有许多的功能不同的寄存器,其中与中断相关的寄存器有以下几个:
寄存器名称 | 作用 |
---|---|
Status | 处理器状态与控制寄存器,在该寄存器中与中断向量入口地址相关的为BEV位域 |
IntCtl | 中断系统状态与控制寄存器,该寄存器中的VS位域代表了各个中断向量入口地址的间距 |
Cause | 存放上次异常原因(MIPS将中断也归类为异常的一种),其中IV位域表示中断异常向量入口控制位(1:使用特殊中断向量0x200;0:使用通用异常向量0x180),ExcCode位域表示异常类型(0x00:中断) |
EBase | 异常入口基址寄存器,其中Exception Base位域左移12位为异常入口向量基址 |
当发生中断时,内核会根据EBase寄存器以及偏移量来跳转到异常处理入口地址处(因为中断也属于异常的一种形式),而关于EBase和偏移量的大小则是根据Status和Cause寄存器来确定的,如下:
异常向量基址
例外 | Status.BEV=0 | Status.BEV=1 |
---|---|---|
基地址 | EBase 63…12 or 0x000 | 0xFFFF.FFFF.BFC0.0200 |
异常向量偏移
例外 | 向量偏移 |
---|---|
中断(Cause.IV=0) | 0x180 |
中断(IntCtl.VS=0 且 Cause.IV=1) | 0x200 |
中断(Status.BEV=1 且 Cause.IV=1) | 0x200 |
中断(Cause.IV=1 且 Status.BEV=0 且 IntCtl.VS!=0) | 0x200 + (中断向量号 × (IntCtl.VS or 0xb00000)) |
这里的“or”代表或操作。所以当处理器接收到中断信号后,会根据上述的寄存器跳转到所对应的异常处理入口。
而在内核中MIPS架构提供的异常处理入口为except_vec3_generic函数,随后该函数通过读取Cause寄存器中的ExCode来判断是哪种异常类型,并通过全局变量exception_handlers数组来找到对应的入口函数,如果是中断类型,则会去调用handle_int()函数。关于上述这些,设置异常处理入口地址,以及根据类型找到对应的处理函数,都是在内核的初始化阶段trap_init()函数中来完成的。
void __init trap_init(void){ ... set_handler(0x180, &except_vec3_generic, 0x80); //关于该函数,其内部实现如下: //void set_handler(unsigned long offset, void *addr, unsigned long size) //{ // ... // memcpy((void *)(ebase + offset), addr, size); // ... //} //结合上边对寄存器的描述,可以看出内核将except_vec3_generic的入口地址复制到了ebase+offset处,即异常处理入口地址。 ... set_except_vector(EXCCODE_INT, using_rollback_handler() ? rollback_handle_int : handle_int); //该函数的内部实现如下: //void __init *set_except_vector(int n, void *addr) //{ // ... // old_handler = xchg(&exception_handlers[n], handler); // ... //} //该函数主要将中断处理函数rollback_handle_int()的地址存放到全局变量exception_handlers数组中,即异常向量中。注意,这里不一定是前者,只是自己分析代码时进行的假设。如果是后者,则会直接去执行handle_int()。}
根据上述代码,可知内核在trap_init中对异常的处理入口,以及异常类型的处理入口进行了设置。关于except_vec3_generic以及rollback_handle_int的实现,均在arch/mips/kernel/genex.S文件中。
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)
可以看到,当接收到中断信号后,首先由except_vec3_generic()函数来进行处理,读取中断号,并调用exception_handlers数组中对应下标中保存的中断处理函数,即rollback_handle_init()。
这里,存放的为rollback_handle_init函数,该函数是通过宏来定义的,如下:
.macro BUILD_ROLLBACK_PROLOGUE handler FEXPORT(rollback_\handler) .set push .set noat MFC0 k0, CP0_EPC PTR_LA k1, __r4k_wait ori k0, 0x1f /* 32 byte rollback region */ xori k0, 0x1f bne k0, k1, \handler MTC0 k0, CP0_EPC .set pop .endm .align 5BUILD_ROLLBACK_PROLOGUE handle_int
在当前的宏定义中可以看出,这里调用了__r4k_wait函数做了处理,随后在调用handle_init()函数。
关于handle_init()函数,其主要完成中断处理前期的任务,比如:关闭中断,保护现场等。随后调用plat_irq_dispatch函数来路由中断。plat_irq_dispatch()通过读取Cause、Status、SR(IM)来得知当前中断所在的引脚(IP0~IP7)。
BUILD_ROLLBACK_PROLOGUE handle_intNESTED(handle_int, PT_SIZE, sp) ...2: jal plat_irq_dispatch ... j ret_from_irq ... END(handle_int)
从这之后,便由plat_irq_dispatch()进行中断路由分发,来选择对应的真正的中断处理函数来进行处理。所以,结合上述的分析,mips架构响应中断处理的顺序如下:
except_vec3_generic() | rollback_handle_int() | __r4k_wait() | handle_int() | plat_irq_dispatch()
注意,异常入口不一定是except_vec3_generic。因为mips架构中存在其他的异常入口函数,比如:exept_vec3_r4000。
当开始对中断进行路由处理时,内核在这之前需要对内核中的数据结构进行初始化。在内核启动阶段的代码中可以看到init_IRQ()函数,顾名思义,该函数主要的任务便是初始化中断数据结构。其实现如下:
void __init init_IRQ(void){ int i; unsigned int order = get_order(IRQ_STACK_SIZE); //IRQ_STACK_SIZE被定义为2^14,该函数的作用便是获取到指数14。 for (i = 0; i < NR_IRQS; i++) irq_set_noprobe(i); //NR_IRQS被定义为128,另外,在init_IRQ函数之前,执行early_irq_init函数对中断结构体struct irq_desc进行组织与初始化。所以,irq_set_noprobe函数通过中断号来查找所对应的struct irq_desc结构体对象,并修改其中相关的成员参数。 if (cpu_has_veic) clear_c0_status(ST0_IM); arch_init_irq(); //该函数主要对所使用的中断控制器进行初始化。而中断初始化的过程主要通过struct irq_desc、struct irq_action、struct irq_chip、struct irq_domain这几个结构体来完成。 for_each_possible_cpu(i) { void *s = (void *)__get_free_pages(GFP_KERNEL, order); irq_stack[i] = s; pr_debug("CPU%d IRQ stack at 0x%p - 0x%p\n", i, irq_stack[i], irq_stack[i] + IRQ_STACK_SIZE); } //该循环主要为每一个中断所对应的栈来分配空间,每个栈的空间大小为2^14. }
当中断初始化完成之后,便可以利用中断数据结构来处理中断。
转载地址:http://gsxii.baihongyu.com/