分类: LINUX
2010-03-31 08:44:49
一、arm中断的相关硬件知识:
正常的程序执行流程发生暂时的停止时,称之为异常。例如处理一个外部的中断请求。在处理异常之前,当前处理器的状态必须保留,这样当异常处理完成之后,当前程序可以继续执行。处理器允许多个异常同时发生,它们将会按固定的优先级进行处理。ARM体系结构中的异常,与8位/16位体系结构的中断有很大的相似之处,但异常与中断的概念并不完全等同。
ARM的异常有七种
复位异常、SWI异常、未定义指令异常、数据中止和指令中止异常。外部中断分为FIQ和IRQ两种,分别为快速中断和通用中断。都可以通过CPSR中的相应位来屏蔽。
CPU知道一个source触发了中断,怎么调用执行一些函数(汇编,或者c语言),就是靠异常向量表(事实上,exception vector table 也是由汇编组成的)
Arm的7种异常对应的模式
地 址 |
异 常 |
进入模式 |
优先级 6最低 |
0x0000,0000 |
复位 |
管理模式 |
1 |
0x0000,0004 |
未定义指令 |
未定义模式 |
6 |
0x0000,0008 |
软件中断 |
管理模式 |
6 |
0x |
中止(预取指令) |
中止模式 |
5 |
0x0000,0010 |
中止(数据) |
中止模式 |
2 |
0x0000,0014 |
保留 |
保留 |
未使用 |
0x0000,0018 |
IRQ |
IRQ |
4 |
0x |
FIQ |
FIQ |
3 |
arm异常表,对应模式及向量表偏移 (摘自arm体系结构与编程一书)
当一个异常/中断出现后, 4020系统中ARM处理器对其的响应过程如下:
(1) 保存处理器当前状态、中断屏蔽位以及各条件标志位。将当前程序状态寄存器CPSR的内容保存到将要执行的异常中断对应的SPSR寄存器中。
(2) 设置当前程序状态寄存器CPSR中相应的位。包括设置CPSR中的位,使处理器进入相应的执行模式;设置CPSR中的位,禁止IRQ中断,当进入FIQ模式时,禁止FIQ中断。
(3) 将寄存器lr_mode设置成返回地址。
(4) 将程序计数器值(PC),设置成该异常中断的中断向量地址,从而跳转到相应的异常中断处理程序执行。即处理器跳转到异常向量表中相应的入口(对于IRQ , 显然pc=0x18) 。
所以当触发IRQ后,CPU会最后跳入0x18 这个入口,定制kernel时只需在这个入口填入自己的指令(当然是汇编语句) ,即可调用中断处理函数,可能这样:
而对于4020 linux系统中断向量的含义是:
触发IRQ—CPU jump 到0x18,同时要把irqno传入相应的寄存器调用一个中断通用处理函数如:asm_do_IRQ(unsigned int irqno) asm_do_IRQ() 这个函数根据irqno 就可以找到对应的中断描述符,然后调用中断描述符里面的handler()了。
二、linux中中断向量表的初始化
ARM linux内核启动时,通过start_kernel()->trap_init()的调用关系,初始化内核的中断异常向量表。
linux/init/main.c Start_kernel中的中断向量表初始化
asmlinkage void __init start_kernel(void)
{
.....
trap_init();
init_IRQ();
....
}
中断的初始化主要和这两个函数相关
(1)trap_init函数
这个trap_init函数主要起到搬运中断向量到高地址的作用
void __init trap_init(void)
{
extern char __stubs_start[], __stubs_end[];
extern char __vectors_start[], __vectors_end[];
extern char __kuser_helper_start[], __kuser_helper_end[];
int kuser_sz = __kuser_helper_end - __kuser_helper_start;
/*
* Copy the vectors, stubs and kuser helpers (in entry-armv.S)
* into the vector page, mapped at 0xffff0000, and ensure these
* are visible to the instruction stream.
在这里把中断向量表,中断低级处理句柄搬运到相应的高地址处
*/
memcpy((void *)0xffff0000, __vectors_start, __vectors_end - __vectors_start);
memcpy((void *)0xffff0200, __stubs_start, __stubs_end - __stubs_start);
memcpy((void *)0xffff1000 - kuser_sz, __kuser_helper_start, kuser_sz);
/*
* Copy signal return handlers into the vector page, and
* set sigreturn to be a pointer to these.
*/
memcpy((void *)KERN_SIGRETURN_CODE, sigreturn_codes,
sizeof(sigreturn_codes));
flush_icache_range(0xffff0000, 0xffff0000 + PAGE_SIZE);
modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
}
(2)init_IRQ函数
void __init init_IRQ(void)
{
struct irqdesc *desc;
int irq;
#ifdef CONFIG_SMP
bad_irq_desc.affinity = CPU_MASK_ALL;
bad_irq_desc.cpu = smp_processor_id();
#endif
// irq_desc数组是用来描述IRQ的请求队列,每一个中断号分配一个
//irq_desc结构,组成了一个数组。
for (irq = 0, desc = irq_desc; irq < NR_IRQS; irq++, desc++) {
*desc = bad_irq_desc; /* 把每个中断先初始化为bad_irq*/
INIT_LIST_HEAD(&desc->pend);
}
init_arch_irq(); /* 调用4020自己sep4020_init_irq函数来初始化各个中断句柄为do_level_IRQ*/
}
其中关键函数为:init_arch_irq();