1、Linux内核从start_kernel函数开始进行初始化
(1)trap_init主要就是异常的初始化。
(2)init_IRQ才是中断的初始化。
注意:ffff0000并不对应实际的物理内存,是虚拟地址。当建立地址映射之后,就需要把物理地址对应的向量表拷贝到虚拟地址ffff0000
2、trap_init都干了啥呢?
void __init trap_init(void)
{
.... 把向量表拷贝到ffff0000地址
memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
.... 把复杂向量表拷贝到ffff0200
memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);
}
3、__vectors_start和__stubs_start分别对应什么东西
①发生异常先到vectors地址找跳转代码
__vectors_start: 第一阶段的跳转
swi SYS_ERROR0 /* 复位时,CPU将执行这条指令 */
b vector_und + stubs_offset /* 未定义指令 */
ldr pc, .LCvswi + stubs_offset /* swi异常 */
b vector_pabt + stubs_offset /* 指令预取中止 */
b vector_dabt + stubs_offset /* 数据访问中止 */
b vector_addrexcptn + stubs_offset /* 没有用到 */
b vector_irq + stubs_offset /* irq异常 */
b vector_fiq + stubs_offset /* fiq异常 */
②上面这些表示发生异常时,要跳转去执行的代码。(举例 vector_und说明)stubs实际上一个宏,宏值里面处理了很多事情。
vector_und, UND_MODE /* "vector_stub" 是一个宏,根据 "und, UND_MODE" 定义一段代码*/
上面这段代码会处理很多东西,最后决定跳转到底下的哪个分支。
/* 下面这些代码是跳转去执行更复杂的代码 */
.long __und_usr @ 0 (USR_26 / USR_32) /* 用户模式下执行了未定义指令 */
.long __und_invalid @ 1 (FIQ_26 / FIQ_32)
.long __und_invalid @ 2 (IRQ_26 / IRQ_32)
.long __und_svc @ 3 (SVC_26 / SVC_32) /* 在管理模式下执行了未定义指令 */
...
③__und_usr以后又会跳转到可执行的C处理函数(其他异常处理同理)
__und_usr: /* __und_usr 用户模式下发生未定义指令异常的处理代码 */
usr_entry
tst r3, #PSR_T_BIT @ Thumb mode?
bne __und_usr_unknown @ ignore FP //跳转到__und_usr_unknown
sub r4, r2, #4
........ //下面还有一些汇编指令
__und_usr_unknown:
mov r0, sp /* 将栈顶地址,作为参数传入 */
adr lr, ret_from_exception /* 将返回地址写入到 r0, 处理完C函数后将返回到这里 */
b do_undefinstr /*C函数入口,处理未定义指令异常*/
同理:vector_irq最终跳转到asm_do_IRQ函数
以上2、3说明了两件事情:(1)trap_init的异常初始化只是把向量表(就是好几个跳转指令)复制到高地址ffff0000; (2)异常发生后,会直接跳到异常向量表里去寻找对应的分支,例如中断的话最终就跳到了asm_do_IRQ函数。也就是说上述两件事情完全是两码事情,一个是初始化,一个动作发生后的处理。
3、中断最终跳转到asm_do_IRQ函数
使用中断结构体数组irq_desc[]来描述一个中断,这样在初始化start_kernel的时候就可以
中断处理的流程:
①发生中断时,cpu响应执行异常向量表__vectors_start
②接着到--》vector_irq中断代码(在这里会计算返回值,保存一些寄存器,进入管理模式)
③vector_irq最后会调用中断处理总入口asm_do_IRQ
④asm_do_IRQ根据中断号调用irq_desc[??]数组项的处理函数handle_irq
⑤handle_irq接着会调用irq_desc[??]中chip成员设置硬件,比如清除中断,禁止中断,重新使能中断
⑥handle_irq会逐个调用action成员链表中注册的处理函数
4、再回到初始化init_IRQ
void __init init_IRQ(void)
{
for (irq = 0; irq < NR_IRQS(宏值16); irq++) 将每个中断结构体数组元素都初始化状态
{
irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_NOPROBE;
}
init_arch_irq();这个地方一个全局函数的地址。初始化为空,因为这里应该是初始化为架构相关的初始化函数,这里是中断处理的初始化函数s3c24xx_init_irq, 这里确实没看懂??
}
void (*init_arch_irq)(void) __initdata = NULL; 谁把这函数地址赋值s3c24xx_init_irq函数?????
5、先跳过去,看看s3c24xx_init_irq都干了啥?这里也就是芯片侧所需要的处理
void __init s3c24xx_init_irq(void)
{
....
for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23(宏值); irqno++) {
irqdbf("registering irq %d (extended s3c irq)\n", irqno);
set_irq_chip(irqno, &s3c_irqext_chip);初始化设置chip,以后就可以设置irq_desc[??].chip成员了
set_irq_handler(irqno, handle_edge_irq);初始化设置处理函数,**发生中后handle_edge_irq会调用用户注册的函数了
set_irq_flags(irqno, IRQF_VALID);设置它生效
}
}
static inline void set_irq_handler(unsigned int irq, irq_flow_handler_t handle)
{
__set_irq_handler(irq, handle, 0, NULL);
}
void __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained, const char *name)
{
desc->handle_irq = handle;也就说最终irq_desc[???].hander_irq 指向了-->handle_edge_irq才是最终的处理函数
desc->name = name;
}
6、内核侧已经将handle_edge_irq的处理过程定义到了action中的处理函数了
static struct irq_chip s3c_irqext_chip = { 全局结构体
.name = "s3c-ext",
.mask = s3c_irqext_mask,
.unmask = s3c_irqext_unmask,
.ack = s3c_irqext_ack,
.set_type = s3c_irqext_type,
.set_wake = s3c_irqext_wake
};
void fastcall handle_edge_irq(unsigned int irq, struct irq_desc *desc)
{
/* Start handling the irq */
desc->chip->ack(irq);
/* Mark the IRQ currently in progress.*/
desc->status |= IRQ_INPROGRESS;
do {
struct irqaction *action = desc->action;循环处理每个action的处理函数
action_ret = handle_IRQ_event(irq, action);
} while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING);
}
irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
{
do{
ret = action->handler(irq, action->dev_id);
action = action->next;
} while (action);
return retval;
}
7、用户注册(驱动程序)中断处理函数的过程
用户通过request_irq函数向内核注册中断函数,request_irq函数根据中断号找到irq_desc数组项,然后在它的action链表中添加一个选项。linux-2.6.22\kernel\irq\manage.c
int request_irq(unsigned int irq, irq_handler_t handler,unsigned long irqflags, const char *devname, void *dev_id)
{
去构建struct irqaction结构体成员
action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);
action->handler = handler;
action->flags = irqflags;
cpus_clear(action->mask);
action->name = devname;
action->next = NULL;
action->dev_id = dev_id;
retval = setup_irq(irq, action);
}
int setup_irq(unsigned int irq, struct irqaction *new)
{
①将新建的链表结构action成员加入链表,如果链表空就直接加入,否则先判断新建的action和链表中的action结构体中段类型是否一致;
是否都生命了共享中断,触发方式(flags),如果一致就加入链表
②设置irq_desc[irq]结构中的chip成员还没设置的指针,让他们指向默认函数。
③设置中断触发方式desc->chip->set_type(irq,new->flags & IRQF_TRIGGER_MASK);
④启用中断desc->status & IRQ_NOAUTOEN:即注册就已经是能了
irq_chip_set_defaults(desc->chip);
new->irq = irq;
register_irq_proc(irq);
new->dir = NULL;
register_handler_proc(irq, new);
}
8、再回头看(3)中断最终跳转到asm_do_IRQ函数
asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs(regs);
struct irq_desc *desc = irq_desc + irq;
irq_enter();
desc_handle_irq(irq, desc);直接调用了irq_desc[???]对应的handle_irq成员
}
static inline void desc_handle_irq(unsigned int irq, struct irq_desc *desc)
{
desc->handle_irq(irq, desc);
}
9、中断的初始化和中断的发生对比
中断初始化:确定中断发生的时候应该跳哪个地址
中断发生:cpu放弃当前任务,跳到中断向量表;
b verctor_irq + stubs_offset;
然后跳到verctor_irq(执行一系列操作)
最后跳到do_asm_IRQ;
void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
desc_handle_irq(irq, desc);
}
static inline void desc_handle_irq(unsigned int irq, struct irq_desc *desc)
{
desc->handle_irq(irq, desc);
}
也就说中断发生后,直接指向了desc[??]数组的第一个元素:即处理函数desc->handle_irq
那么handle_irq又是谁来设置的呢?????这就决定了最终处理函数是处理谁了!!!!!
void __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
const char *name)
{
。。。。
desc->handle_irq = handle;
}
static inline void set_irq_handler(unsigned int irq, irq_flow_handler_t handle)
{
__set_irq_handler(irq, handle, 0, NULL);
}
void set_irq_chip_and_handler(unsigned int irq, struct irq_chip *chip,
irq_flow_handler_t handle)
{
set_irq_chip(irq, chip);
__set_irq_handler(irq, handle, 0, NULL);
}
void set_irq_chip_and_handler_name(unsigned int irq, struct irq_chip *chip,
irq_flow_handler_t handle, const char *name)
{
set_irq_chip(irq, chip);
__set_irq_handler(irq, handle, 0, name);
}
set_irq_chip_and_handler_name和set_irq_chip_and_handler函数最终就是被芯片相关的初始化调用的。我们可以发现很多地方都有进行设置handler函数
./arch/arm/plat-s3c24xx/irq.c:725: set_irq_handler(irqno, handle_level_irq);
./arch/arm/plat-s3c24xx/irq.c:736: set_irq_handler(irqno, handle_edge_irq);
./arch/arm/plat-s3c24xx/irq.c:756: set_irq_handler(irqno, handle_edge_irq);
./arch/arm/plat-s3c24xx/irq.c:763: set_irq_handler(irqno, handle_edge_irq);
./arch/arm/plat-s3c24xx/irq.c:774: set_irq_handler(irqno, handle_level_irq);
./arch/arm/plat-s3c24xx/irq.c:781: set_irq_handler(irqno, handle_level_irq);
./arch/arm/plat-s3c24xx/irq.c:788: set_irq_handler(irqno, handle_level_irq);
./arch/arm/plat-s3c24xx/irq.c:795: set_irq_handler(irqno, handle_edge_irq);
void __init s3c24xx_init_irq(void)
{
。。。
for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) {
irqdbf("registering irq %d (ext int)\n", irqno);
set_irq_chip(irqno, &s3c_irq_eint0t4);
set_irq_handler(irqno, handle_edge_irq);
set_irq_flags(irqno, IRQF_VALID);
}
。。。
}
handle_edge_irq才是最终的处理函数。