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]);//中斷處理函數(shù) }
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) 由上節(jié)可知,,中斷向量的對(duì)應(yīng)的處理程序位于interrupt數(shù)組中,,下面來看看interrupt: 341 .data #數(shù)據(jù)段 342 ENTRY(interrupt) 343 .text 344 345 vector=0 346 ENTRY(irq_entries_start) 347 .rept NR_IRQS #348-354行重復(fù)NR_IRQS次 348 ALIGN 349 1: pushl $vector-256 #vector在354行遞增 350 jmp common_interrupt #所有的外部中斷處理函數(shù)的統(tǒng)一部分,,以后再講述 351 .data 352 .long 1b #存儲(chǔ)著指向349行的地址,,但是隨著348行-354被gcc展開,,每次的值都不同 353 .text 354 vector=vector+1 355 .endr #與347行呼應(yīng) 356 357 ALIGN
#公共處理函數(shù) common_interrupt: SAVE_ALL /*寄存器值入棧*/ movl %esp,%eax /*棧頂指針保存到eax*/ call do_IRQ /*處理中斷*/ jmp ret_from_intr /*從中斷返回*/ 分析如下: 首先342行和352行都處于.data段,,雖然看起來它們是隔開的,但實(shí)際上被gcc安排在了連續(xù)的數(shù)據(jù)段內(nèi)存中,,同理在代碼段內(nèi)存中,,354行與350行的指令序列也是連續(xù)存儲(chǔ)的。另外,,348-354行會(huì)被gcc展開NR_IRQS次,,因此每次352行都會(huì)存儲(chǔ)一個(gè)新的指針,,該指針指向每個(gè)349行展開的新對(duì)象。最后在代碼段內(nèi)存中連續(xù)存儲(chǔ)了NR_IRQS個(gè)代碼片斷,,首地址由 irq_entries_start指向,。而在數(shù)據(jù)段內(nèi)存中連續(xù)存儲(chǔ)了NR_IRQS個(gè)指針,首址存儲(chǔ)在interrupt這個(gè)全局變量中,。這樣,,例如 IRQ號(hào)是0 (從init_IRQ()調(diào)用,它對(duì)應(yīng)的中斷向量是FIRST_EXTERNAL_VECTOR)的中斷通過中斷門后會(huì)觸發(fā) interrput[0],,從而執(zhí)行: pushl 0-256 jmp common_interrupt 的代碼片斷,,進(jìn)入到Linux內(nèi)核安排好的中斷入口路徑。
中斷回調(diào)函數(shù)基本格式如下 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為上邊計(jì)算出的棧地址,,將其存入ebx,并通過xchgl存入esp,從而將原來的棧地址保存到 * ebx,而新棧變?yōu)?/SPAN>isp, 在新棧上執(zhí)行desc->handle_irq,,執(zhí)行結(jié)束后,,從ebx中恢復(fù)原棧。 */ 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); |
|