博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
硬件中断信号处理
阅读量:4095 次
发布时间:2019-05-25

本文共 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/

你可能感兴趣的文章
zookeeper(2)---shell操作
查看>>
mapReduce(3)---入门示例WordCount
查看>>
hbase(3)---shell操作
查看>>
hbase(1)---概述
查看>>
hbase(5)---API示例
查看>>
SSM-CRUD(1)---环境搭建
查看>>
SSM-CRUD(2)---查询
查看>>
SSM-CRUD (3)---查询功能改造
查看>>
Nginx(2)---安装与启动
查看>>
springBoot(5)---整合servlet、Filter、Listener
查看>>
C++ 模板类型参数
查看>>
C++ 非类型模版参数
查看>>
设计模式 依赖倒转原则 & 里氏代换原则
查看>>
DirectX11 光照
查看>>
图形学 图形渲染管线
查看>>
DirectX11 计时和动画
查看>>
DirectX11 光照与材质的相互作用
查看>>
DirectX11 法线向量
查看>>
DirectX11 兰伯特余弦定理(Lambert)
查看>>
DirectX11 漫反射光
查看>>