您的位置:首页 > 编程语言

读代码细学内核中断机制-中断的初始化

2012-08-11 16:36 239 查看
刚开始正式工作不久,也没有分到特别重的任务,因此这星期利用时间好好学习了一下之前自己一直很敬而远之的中断,之前一直都是看书,书上学习的都是一些理论知识,有点太空泛了,所以我决定从内核代码来学习中断机制,这样感觉更踏实一些,下面就是自己的一个大总结,因为公司做龙芯处理器,下面分析以mips处理器 龙芯1B内核为例。

本人小菜鸟一个,理解浅短,往大牛多多指导。

首先对于中断机制的学习,我分了三部分,这也是中断机制最主要的三部分:

内核中断机制的初始化,设备中断请求的注册,内核对中断的响应。

这一篇先介绍中断机制的初始化

首先找到内核代码的入口,在arch/mips/kernel下有一个head.S,这个就是整个内核启动的入口文件,内核加载后就是从这里开始初始化,打开这个文件,阅读汇编可以知道,这个文件主要是清楚bss段,初始化寄存器,设置堆栈指针,最后跳转到了start_kernel函数,

start_kernel这个函数在init/main.c里,进入这个函数,这个函数也是进行初始化,主要是初始化中断,内存,外设之类,这里就有我们要找的中断的初始化,首先找到trap_init函数,这个函数是内核异常向量表的初始化。

首先说明一点,很多人分不清楚异常和中断的关系,我的理解是,异常是核级的,中断是板级的,怎么说呢,中断是有外设发出的,对于内核来说他不知道什么是中断,他只知道异常,中断也是异常的一种,mips处理器来说,有32种通用异常,这个可以从mips的cp0协处理器寄存器cause上的exccode例外码得知,其中第一种就是中断。异常向量表很重要,当有中断发生,硬件自动就会跳转到预先设定好的地址,其实就是中断向量表的入口地址,查找相应中断执行。

阅读trap_init函数,发现这样一段代码

/*

* Copy the generic exception handlers to their final destination.

* This will be overriden later as suitable for a particular

* configuration.

*/

set_handler(0x180, &except_vec3_generic, 0x80);



这个函数就是拷贝except_vec3_generic到0x80000180处(这个是ebase+0x180得出,ebase在trap_init开始出赋值,这个地址是发生异常后内核的跳转地址)。

在arch/mips/kernelgenex.S下可以找到except_vec3_generic的定义:

/*

* General exception vector for all other CPUs.

*

* Be careful when changing this, it has to be at most 128 bytes

* to fit into space reserved for the exception handler.

*/

NESTED(except_vec3_generic, 0, sp)

.set push

.set noat

#ifdef CONFIG_LOONGSON_FIX_RANDOM_INSTRUCTION_FETCH_SIDE_EFFECT_TO_DEVICE

li k0,2

mtc0 k0,$22

#endif

#if R5432_CP0_INTERRUPT_WAR

mfc0 k0, CP0_INDEX

#endif

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是一个汇编定义的符号(相当与入口地址),这个符号主要是读出CP0_CAUSE寄存器的值,然后与0x7c相与,其实就是取出寄存器上例外码的值,然后跳转到了exception_handlers,这是一个异常处理函数组成的数组,这个数组容量是32,正好容纳下32种通用异常,根据例外码就能找到相应的处理函数,exception_handlers数组就是龙芯1B内核的异常向量表。

需要说明的一点是,mips处理器支持向量中断模式,就是在将每个中断处理入口分配到不同的地址上,1B内核没有采用向量中断,而是都放在了异常向量表的0号上。

这样trap_init中的set_handler说完再看后面,可以看到大部分代码都是在给数组 except_vec3_generic赋值。

0号赋到的处理函数是handle_init,在genex.S中可以找到这个符号的定义。

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

S***E_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)

__INIT

这一段汇编是检查了一下status寄存器ie位是否为1,ie位是全部中断的开关,ie位为1则所有中断打开,是的话就跳转到plat_irq_dispatch函数。这个plat_irq_dispatch就是中断的下发函数,这个函数在后面中断的响应会详细分解,这里先不做解释。

这样异常向量表初始化完成。

在start_kernel中之后还有一个函数init_IRQ,这个是中断的初始化函数,来分析一下。

跳到init_IRQ中,主要就是一个arch_init_IRQ函数,这个就是各个不同平台的初始化函数,进入arch_init_irq函数,开始是初始化协处理器status,IM0~7置1,允许所有中断,BEV置1,然后初始化3条中断输入INT0~3的寄存器,然后初始化结构体数组irq_desc[ ],这个数组是内核中断的核心数组,后面中断的注册响应还会详细分解。

以上就是基于龙芯1B内核的中断初始化大体步骤,分为2步,其实是一个包含关系,先初始化异常向量表,然后初始化异常向量表中的中断。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: