init_ir_技術實現篇

1. 中斷的初始化

中斷向量號是8位的,那麼它一共有256項(0-255),因此中斷描述符表最多隻有256項,中斷向量表也是256項。linux

對於不一樣的中斷,在中斷初始化和中斷處理過程當中,其處理方式是不同的,尤爲是不一樣類型的中斷,其處理響應機制差異較大,後面會詳述。這裏先描述中斷的初始化過程。api

從中斷觸發的源角度,能夠將中斷分爲內部中斷(0~31號中斷向量號)和外部中斷(32~255號中斷向量號),linux初始化過程當中,會先初始化內部中斷,再初始化外部中斷。這兩種中斷的初始化區別以下:數組

1. 內部中斷(0~31號、0x80做爲中斷號)
只要初始化:
    1) 相關的中斷向量表
2. 外部中斷(0~255中的除了0~31號、0x80的其餘中斷號)
須要初始化:
    1) 相關的中斷向量表
    2) 以及中斷控制器(控制器負責優先級排隊、屏蔽等工做)

 

0x1: 內部中斷初始化dom

內部中斷的初始化須要對0~31號和0x80號系統保留中斷向量的初始化,這部分草走在trap_init()中完成ide

\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: 外部中斷初始化ui

外部中斷的初始化須要:this

1. 對除了0~3一、0x80中斷號以外的其它中斷向量
2. 中斷控制器的初始化(相比內部中斷初始化多了這一步)
這兩步操做都在在init_IRQ()中完成

\linux-3.15.5\arch\x86\kernel\i8259.cspa

複製代碼
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
}
相關文章
相關標籤/搜索