init/main.c
arch/x86/kernel/traps.c
trap_init(void)
arch/x86/kernel/traps.c
set_intr_gate(0, ÷_error);
…
set_intr_gate(19, &simd_coprocessor_error);
arch/x86/include/asm/irq_vectors.h
# define SYSCALL_VECTOR 0x80
set_system_trap_gate(SYSCALL_VECTOR, &system_call);
/*
* This needs to use 'idt_table' rather than 'idt', and
* thus use the _nonmapped_ version of the IDT, as the
* Pentium F0 0F bugfix can have resulted in the mapped
* IDT being write-protected.
*/
arch/x86/include/asm/desc.h
static inline void set_intr_gate(unsigned int n, void *addr)
{
BUG_ON((unsigned)n > 0xFF);
_set_gate(n, GATE_INTERRUPT, addr, 0, 0, __KERNEL_CS);
}
arch/x86/include/asm/desc_defs.h
#ifdef CONFIG_X86_64
typedef struct gate_struct64 gate_desc;
#else
typedef struct desc_struct gate_desc;
#endif
arch/x86/include/asm/irq_vectors.h
#define NR_VECTORS 256
arch/x86/kernel/traps.c
gate_desc idt_table[NR_VECTORS] __page_aligned_data = { { { { 0, 0 } } }, };
static inline void _set_gate(int gate, unsigned type, void *addr,
unsigned dpl, unsigned ist, unsigned seg)
{
gate_desc s;
pack_gate(&s, type, (unsigned long)addr, dpl, ist, seg);
/*
* does not need to be atomic because it is only done once at
* setup time
*/
write_idt_entry(idt_table, gate, &s);
}
arch/x86/include/asm/desc.h
#define write_idt_entry(dt, entry, g) \
native_write_idt_entry(dt, entry, g)
static inline void native_write_idt_entry(gate_desc *idt, int entry,
const gate_desc *gate)
{
memcpy(&idt[entry], gate, sizeof(*gate));
}
arch/x86/kernel/irqinit.c
init_IRQ(void)
x86_init.irqs.intr_init();
arch/x86/kernel/x86_init.c
.pre_vector_init = init_ISA_irqs,
.intr_init = native_init_IRQ,
void __init native_init_IRQ(void)
x86_init.irqs.pre_vector_init();
arch/x86/kernel/irqinit.c
void __init init_ISA_irqs(void)
for (i = 0; i < legacy_pic->nr_legacy_irqs; i++) {
struct irq_desc *desc = irq_to_desc(i);
desc->status = IRQ_DISABLED;
desc->action = NULL;
desc->depth = 1;
kernel/irq/chip.c
set_irq_chip_and_handler_name(i, &i8259A_chip,
handle_level_irq, "XT");
set_irq_chip(irq, chip);
__set_irq_handler(irq, handle, 0, name);
}
arch/x86/include/asm/irq_vectors.h
#define FIRST_EXTERNAL_VECTOR 0x20
for (i = FIRST_EXTERNAL_VECTOR; i < NR_VECTORS; i++) {
if (!test_bit(i, used_vectors))
set_intr_gate(i, interrupt[i-FIRST_EXTERNAL_VECTOR]);//中断处理函数
}
arch/x86/kernel/entry_32.S
/*
* Build the entry stubs and pointer table with some assembler magic.
* We pack 7 stubs into a single 32-byte chunk, which will fit in a
* single cache line on all modern x86 implementations.
*/
.section .init.rodata,"a"
ENTRY(interrupt)
.text
.p2align 5
.p2align CONFIG_X86_L1_CACHE_SHIFT
ENTRY(irq_entries_start)
RING0_INT_FRAME
vector=FIRST_EXTERNAL_VECTOR
.rept (NR_VECTORS-FIRST_EXTERNAL_VECTOR+6)/7
.balign 32
.rept 7
.if vector < NR_VECTORS
.if vector <> FIRST_EXTERNAL_VECTOR
CFI_ADJUST_CFA_OFFSET -4
.endif
1: pushl $(~vector+0x80) /* Note: always in signed byte range */
CFI_ADJUST_CFA_OFFSET 4
.if ((vector-FIRST_EXTERNAL_VECTOR)%7) <> 6
jmp 2f
.endif
.previous
.long 1b
.text
vector=vector+1
.endif
.endr
2: jmp common_interrupt
.endr
END(irq_entries_start)
.previous
END(interrupt)
.previous
/*
* the CPU automatically disables interrupts when executing an IRQ vector,
* so IRQ-flags tracing has to follow that:
*/
.p2align CONFIG_X86_L1_CACHE_SHIFT
common_interrupt:
addl $-0x80,(%esp) /* Adjust vector into the [-256,-1] range */
SAVE_ALL
TRACE_IRQS_OFF
movl %esp,%eax
call do_IRQ
jmp ret_from_intr
ENDPROC(common_interrupt)
中断处理入口(http://www.cnblogs.com/hustcat/archive/2009/08/14/1546011.html)
由上节可知,中断向量的对应的处理程序位于interrupt数组中,下面来看看interrupt:
341 .data #数据段
342 ENTRY(interrupt)
343 .text
344
345 vector=0
346 ENTRY(irq_entries_start)
347 .rept NR_IRQS #348-354行重复NR_IRQS次
348 ALIGN
349 1: pushl $vector-256 #vector在354行递增
350 jmp common_interrupt #所有的外部中断处理函数的统一部分,以后再讲述
351 .data
352 .long 1b #存储着指向349行的地址,但是随着348行-354被gcc展开,每次的值都不同
353 .text
354 vector=vector+1
355 .endr #与347行呼应
356
357 ALIGN
#公共处理函数
common_interrupt:
SAVE_ALL /*寄存器值入栈*/
movl %esp,%eax /*栈顶指针保存到eax*/
call do_IRQ /*处理中断*/
jmp ret_from_intr /*从中断返回*/
分析如下:
首先342行和352行都处于.data段,虽然看起来它们是隔开的,但实际上被gcc安排在了连续的数据段内存中,同理在代码段内存中,354行与350行的指令序列也是连续存储的。另外,348-354行会被gcc展开NR_IRQS次,因此每次352行都会存储一个新的指针,该指针指向每个349行展开的新对象。最后在代码段内存中连续存储了NR_IRQS个代码片断,首地址由 irq_entries_start指向。而在数据段内存中连续存储了NR_IRQS个指针,首址存储在interrupt这个全局变量中。这样,例如 IRQ号是0 (从init_IRQ()调用,它对应的中断向量是FIRST_EXTERNAL_VECTOR)的中断通过中断门后会触发 interrput[0],从而执行:
pushl 0-256
jmp common_interrupt
的代码片断,进入到Linux内核安排好的中断入口路径。
中断回调函数基本格式如下
1: pushl $(~vector+0x80)
jmp common_interrupt
SAVE_ALL
call do_IRQ
jmp ret_from_intr
arch/x86/kernel/irq.c
/*
* do_IRQ handles all normal device IRQ's (the special
* SMP cross-CPU interrupts have their own specific
* handlers).
*/
unsigned int __irq_entry do_IRQ(struct pt_regs *regs)
irq_enter();
arch/x86/kernel/irq_32.c
handle_irq(irq, regs)
desc = irq_to_desc(irq);
if (!execute_on_irq_stack(overflow, desc, irq)) {
if (unlikely(overflow))
print_stack_overflow();
desc->handle_irq(irq, desc);
}
irq_exit();
arch/x86/kernel/irq_32.c
execute_on_irq_stack(int overflow, struct irq_desc *desc, int irq)
{
union irq_ctx *curctx, *irqctx;
u32 *isp, arg1, arg2;
curctx = (union irq_ctx *) current_thread_info();
irqctx = __get_cpu_var(hardirq_ctx);
/*
* this is where we switch to the IRQ stack. However, if we are
* already using the IRQ stack (because we interrupted a hardirq
* handler) we can't do that and just have to keep using the
* current stack (which is the irq stack already after all)
*/
if (unlikely(curctx == irqctx))
return 0;
/* build the stack frame on the IRQ stack */
isp = (u32 *) ((char *)irqctx + sizeof(*irqctx));
irqctx->tinfo.task = curctx->tinfo.task;
irqctx->tinfo.previous_esp = current_stack_pointer;
/*
* Copy the softirq bits in preempt_count so that the
* softirq checks work in the hardirq context.
*/
irqctx->tinfo.preempt_count =
(irqctx->tinfo.preempt_count & ~SOFTIRQ_MASK) |
(curctx->tinfo.preempt_count & SOFTIRQ_MASK);
if (unlikely(overflow))
call_on_stack(print_stack_overflow, isp);
/*
* isp为上边计算出的栈地址,将其存入ebx,并通过xchgl存入esp,从而将原来的栈地址保存到
* ebx,而新栈变为isp, 在新栈上执行desc->handle_irq,执行结束后,从ebx中恢复原栈。
*/
asm volatile("xchgl %%ebx,%%esp \n"
"call *%%edi \n"
"movl %%ebx,%%esp \n"
: "=a" (arg1), "=d" (arg2), "=b" (isp)
: "0" (irq), "1" (desc), "2" (isp),
"D" (desc->handle_irq)
: "memory", "cc", "ecx");
return 1;
}
typedef struct irq_desc {
unsigned int status; /* IRQ status */
hw_irq_controller *handler;
struct irqaction *action; /* IRQ action list */
unsigned int depth; /* nested irq disables */
unsigned int irq_count; /* For detecting broken interrupts */
unsigned int irqs_unhandled;
spinlock_t lock;
} ____cacheline_aligned irq_desc_t;
desc->handle_irq的初始化过程如下:
arch/x86/kernel/irqinit.c
init_IRQ(void)
x86_init.irqs.intr_init();
void __init native_init_IRQ(void)
x86_init.irqs.pre_vector_init();
arch/x86/kernel/irqinit.c
void __init init_ISA_irqs(void)
set_irq_chip_and_handler_name
desc->handle_irq=handle_level_irq
handle_level_irq(unsigned int irq, struct irq_desc *desc)
mask_ack_irq(desc, irq);
desc->chip->mask_ack(irq);
if (unlikely(desc->status & IRQ_INPROGRESS))
goto out_unlock;
action = desc->action;
desc->status |= IRQ_INPROGRESS;
kernel/irq/handle.c
handle_IRQ_event(irq, action);
if (!(action->flags & IRQF_DISABLED))
local_irq_enable_in_hardirq();
do {
action->handler(irq, action->dev_id);
action = action->next;
} while (action);
desc->status &= ~IRQ_INPROGRESS;
if (!(desc->status & (IRQ_DISABLED | IRQ_ONESHOT)))
unmask_irq(desc, irq);
联系客服