init_ir_技术实现篇
1. 中断的初始化
中断向量号是8位的,那么它一共有256项(0-255),所以中断描述符表最多只有256项,中断向量表也是256项。
对于不同的中断,在中断初始化和中断处理过程中,其处理方式是不一样的,尤其是不同类型的中断,其处理响应机制差别较大,后面会详述。这里先描述中断的初始化过程。
从中断触发的源角度,可以将中断分为内部中断(0~31号中断向量号)和外部中断(32~255号中断向量号),linux初始化过程中,会先初始化内部中断,再初始化外部中断。这两种中断的初始化区别如下:
1. 内部中断(0~31号、0x80作为中断号) 只要初始化: 1) 相关的中断向量表 2. 外部中断(0~255中的除了0~31号、0x80的其他中断号) 需要初始化: 1) 相关的中断向量表 2) 以及中断控制器(控制器负责优先级排队、屏蔽等工作)
0x1: 内部中断初始化
内部中断的初始化需要对0~31号和0x80号系统保留中断向量的初始化,这部分草走在trap_init()中完成
\linux-3.15.5\arch\x86\kernel\traps
void __init trap_init(void) { int i; #ifdef CONFIG_EISA void __iomem *p = early_ioremap(0x0FFFD9, 4); if (readl(p) == 'E' + ('I'<<8) + ('S'<<16) + ('A'<<24)) EISA_bus = 1; early_iounmap(p, 4); #endif /* trap_init()主要是调用set_xxx_gate(中断向量, 中断处理函数) set_xxx_gate()就是按照中断门的格式填写中断向量表的 Intel x86支持4种"门描述符": 1) 调用门(call gate) 2) 陷阱门(trap gate) 3) 中断门(iinterrupt gate) 4) 任务门(task gate) */ set_intr_gate(X86_TRAP_DE, divide_error); set_intr_gate_ist(X86_TRAP_NMI, &nmi, NMI_STACK); /* int4 can be called from all */ set_system_intr_gate(X86_TRAP_OF, &overflow); set_intr_gate(X86_TRAP_BR, bounds); set_intr_gate(X86_TRAP_UD, invalid_op); set_intr_gate(X86_TRAP_NM, device_not_available); #ifdef CONFIG_X86_32 set_task_gate(X86_TRAP_DF, GDT_ENTRY_DOUBLEFAULT_TSS); #else set_intr_gate_ist(X86_TRAP_DF, &double_fault, DOUBLEFAULT_STACK); #endif set_intr_gate(X86_TRAP_OLD_MF, coprocessor_segment_overrun); set_intr_gate(X86_TRAP_TS, invalid_TSS); set_intr_gate(X86_TRAP_NP, segment_not_present); set_intr_gate_ist(X86_TRAP_SS, &stack_segment, STACKFAULT_STACK); set_intr_gate(X86_TRAP_GP, general_protection); set_intr_gate(X86_TRAP_SPURIOUS, spurious_interrupt_bug); set_intr_gate(X86_TRAP_MF, coprocessor_error); set_intr_gate(X86_TRAP_AC, alignment_check); #ifdef CONFIG_X86_MCE set_intr_gate_ist(X86_TRAP_MC, &machine_check, MCE_STACK); #endif set_intr_gate(X86_TRAP_XF, simd_coprocessor_error); /* Reserve all the builtin and the syscall vector: */ for (i = 0; i < FIRST_EXTERNAL_VECTOR; i++) set_bit(i, used_vectors); #ifdef CONFIG_IA32_EMULATION set_system_intr_gate(IA32_SYSCALL_VECTOR, ia32_syscall); set_bit(IA32_SYSCALL_VECTOR, used_vectors); #endif //设置系统调用中断 #ifdef CONFIG_X86_32 set_system_trap_gate(SYSCALL_VECTOR, &system_call); set_bit(SYSCALL_VECTOR, used_vectors); #endif /* * Set the IDT descriptor to a fixed read-only location, so that the * "sidt" instruction will not leak the location of the kernel, and * to defend the IDT against arbitrary memory write vulnerabilities. * It will be reloaded in cpu_init() */ __set_fixmap(FIX_RO_IDT, __pa_symbol(idt_table), PAGE_KERNEL_RO); idt_descr.address = fix_to_virt(FIX_RO_IDT); /* * Should be a barrier for any external CPU state: */ cpu_init(); x86_init.irqs.trap_init(); #ifdef CONFIG_X86_64 memcpy(&debug_idt_table, &idt_table, IDT_ENTRIES * 16); set_nmi_gate(X86_TRAP_DB, &debug); set_nmi_gate(X86_TRAP_BP, &int3); #endif }
0x2: 外部中断初始化
外部中断的初始化需要:
1. 对除了0~31、0x80中断号之外的其它中断向量 2. 中断控制器的初始化(相比内部中断初始化多了这一步)
这两步操作都在在init_IRQ()中完成
\linux-3.15.5\arch\x86\kernel\i8259.c
void __init init_IRQ(void) { int i; /* * We probably need a better place for this, but it works for * now ... */ x86_add_irq_domains(); /* * On cpu 0, Assign IRQ0_VECTOR..IRQ15_VECTOR's to IRQ 0..15. * If these IRQ's are handled by legacy interrupt-controllers like PIC, * then this configuration will likely be static after the boot. If * these IRQ's are handled by more mordern controllers like IO-APIC, * then this vector space can be freed and re-used dynamically as the * irq's migrate etc. */ for (i = 0; i < legacy_pic->nr_legacy_irqs; i++) //对于单CPU结构, per_cpu(vector_irq, 0)[IRQ0_VECTOR + i] = i; //x86_init.irqs.intr_init()等价于调用:native_init_IRQ() x86_init.irqs.intr_init(); } void __init native_init_IRQ(void) { int i; /* Execute any quirks before the call gates are initialised: */ x86_init.irqs.pre_vector_init(); //调用 init_ISA_irqs apic_intr_init(); /* * Cover the whole vector space, no vector can escape * us. (some of these will be overridden and become * 'special' SMP interrupts) */ /* interrupt数组,它保存的是每个中断服务程序的入口地址,它的定义是在\linux-3.15.5\arch\x86\kernel\entry_32.S中 */ for (i = FIRST_EXTERNAL_VECTOR; i < NR_VECTORS; i++) { //设置32~255号中断 /* IA32_SYSCALL_VECTOR could be used in trap_init already. */ if (!test_bit(i, used_vectors)) { //要除去0x80中断 set_intr_gate(i, interrupt[i-FIRST_EXTERNAL_VECTOR]); } } if (!acpi_ioapic && !of_ioapic) setup_irq(2, &irq2); #ifdef CONFIG_X86_32 /* * External FPU? Set up irq13 if so, for * original braindamaged IBM FERR coupling. */ if (boot_cpu_data.hard_math && !cpu_has_fpu) setup_irq(FPU_IRQ, &fpu_irq); irq_ctx_init(smp_processor_id()); #endif }