⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 io_apic.c

📁 xen 3.2.2 源码
💻 C
📖 第 1 页 / 共 5 页
字号:
 * in the start-IRQ-handler. Protection against reentrance * from the same interrupt is still provided, both by the * generic IRQ layer and by the fact that an unacked local * APIC does not accept IRQs. */static unsigned int startup_level_ioapic_irq (unsigned int irq){    unmask_IO_APIC_irq(irq);    return 0; /* don't check for pending */}int ioapic_ack_new = 1;static void setup_ioapic_ack(char *s){    if ( !strcmp(s, "old") )        ioapic_ack_new = 0;    else if ( !strcmp(s, "new") )        ioapic_ack_new = 1;    else        printk("Unknown ioapic_ack value specified: '%s'\n", s);}custom_param("ioapic_ack", setup_ioapic_ack);static void mask_and_ack_level_ioapic_irq (unsigned int irq){    unsigned long v;    int i;    if ( ioapic_ack_new )        return;    mask_IO_APIC_irq(irq);/* * It appears there is an erratum which affects at least version 0x11 * of I/O APIC (that's the 82093AA and cores integrated into various * chipsets).  Under certain conditions a level-triggered interrupt is * erroneously delivered as edge-triggered one but the respective IRR * bit gets set nevertheless.  As a result the I/O unit expects an EOI * message but it will never arrive and further interrupts are blocked * from the source.  The exact reason is so far unknown, but the * phenomenon was observed when two consecutive interrupt requests * from a given source get delivered to the same CPU and the source is * temporarily disabled in between. * * A workaround is to simulate an EOI message manually.  We achieve it * by setting the trigger mode to edge and then to level when the edge * trigger mode gets detected in the TMR of a local APIC for a * level-triggered interrupt.  We mask the source for the time of the * operation to prevent an edge-triggered interrupt escaping meanwhile. * The idea is from Manfred Spraul.  --macro */    i = IO_APIC_VECTOR(irq);    v = apic_read(APIC_TMR + ((i & ~0x1f) >> 1));    ack_APIC_irq();    if (!(v & (1 << (i & 0x1f)))) {        atomic_inc(&irq_mis_count);        spin_lock(&ioapic_lock);        __edge_IO_APIC_irq(irq);        __level_IO_APIC_irq(irq);        spin_unlock(&ioapic_lock);    }}static void end_level_ioapic_irq (unsigned int irq){    unsigned long v;    int i;    if ( !ioapic_ack_new )    {        if ( !(irq_desc[IO_APIC_VECTOR(irq)].status & IRQ_DISABLED) )            unmask_IO_APIC_irq(irq);        return;    }/* * It appears there is an erratum which affects at least version 0x11 * of I/O APIC (that's the 82093AA and cores integrated into various * chipsets).  Under certain conditions a level-triggered interrupt is * erroneously delivered as edge-triggered one but the respective IRR * bit gets set nevertheless.  As a result the I/O unit expects an EOI * message but it will never arrive and further interrupts are blocked * from the source.  The exact reason is so far unknown, but the * phenomenon was observed when two consecutive interrupt requests * from a given source get delivered to the same CPU and the source is * temporarily disabled in between. * * A workaround is to simulate an EOI message manually.  We achieve it * by setting the trigger mode to edge and then to level when the edge * trigger mode gets detected in the TMR of a local APIC for a * level-triggered interrupt.  We mask the source for the time of the * operation to prevent an edge-triggered interrupt escaping meanwhile. * The idea is from Manfred Spraul.  --macro */    i = IO_APIC_VECTOR(irq);    v = apic_read(APIC_TMR + ((i & ~0x1f) >> 1));    ack_APIC_irq();    if (!(v & (1 << (i & 0x1f)))) {        atomic_inc(&irq_mis_count);        spin_lock(&ioapic_lock);        __mask_IO_APIC_irq(irq);        __edge_IO_APIC_irq(irq);        __level_IO_APIC_irq(irq);        if ( !(irq_desc[IO_APIC_VECTOR(irq)].status & IRQ_DISABLED) )            __unmask_IO_APIC_irq(irq);        spin_unlock(&ioapic_lock);    }}static unsigned int startup_edge_ioapic_vector(unsigned int vector){    int irq = vector_to_irq(vector);    return startup_edge_ioapic_irq(irq);}static void ack_edge_ioapic_vector(unsigned int vector){    int irq = vector_to_irq(vector);    ack_edge_ioapic_irq(irq);}static unsigned int startup_level_ioapic_vector(unsigned int vector){    int irq = vector_to_irq(vector);    return startup_level_ioapic_irq (irq);}static void mask_and_ack_level_ioapic_vector(unsigned int vector){    int irq = vector_to_irq(vector);    mask_and_ack_level_ioapic_irq(irq);}static void end_level_ioapic_vector(unsigned int vector){    int irq = vector_to_irq(vector);    end_level_ioapic_irq(irq);}static void mask_IO_APIC_vector(unsigned int vector){    int irq = vector_to_irq(vector);    mask_IO_APIC_irq(irq);}static void unmask_IO_APIC_vector(unsigned int vector){    int irq = vector_to_irq(vector);    unmask_IO_APIC_irq(irq);}static void set_ioapic_affinity_vector(    unsigned int vector, cpumask_t cpu_mask){    int irq = vector_to_irq(vector);    set_native_irq_info(vector, cpu_mask);    set_ioapic_affinity_irq(irq, cpu_mask);}static void disable_edge_ioapic_vector(unsigned int vector){}static void end_edge_ioapic_vector(unsigned int vector){}/* * Level and edge triggered IO-APIC interrupts need different handling, * so we use two separate IRQ descriptors. Edge triggered IRQs can be * handled with the level-triggered descriptor, but that one has slightly * more overhead. Level-triggered interrupts cannot be handled with the * edge-triggered handler, without risking IRQ storms and other ugly * races. */static struct hw_interrupt_type ioapic_edge_type = {    .typename 	= "IO-APIC-edge",    .startup 	= startup_edge_ioapic_vector,    .shutdown 	= disable_edge_ioapic_vector,    .enable 	= unmask_IO_APIC_vector,    .disable 	= disable_edge_ioapic_vector,    .ack 		= ack_edge_ioapic_vector,    .end 		= end_edge_ioapic_vector,    .set_affinity 	= set_ioapic_affinity_vector,};static struct hw_interrupt_type ioapic_level_type = {    .typename 	= "IO-APIC-level",    .startup 	= startup_level_ioapic_vector,    .shutdown 	= mask_IO_APIC_vector,    .enable 	= unmask_IO_APIC_vector,    .disable 	= mask_IO_APIC_vector,    .ack 		= mask_and_ack_level_ioapic_vector,    .end 		= end_level_ioapic_vector,    .set_affinity 	= set_ioapic_affinity_vector,};static inline void init_IO_APIC_traps(void){    int irq;    /* Xen: This is way simpler than the Linux implementation. */    for (irq = 0; irq < 16 ; irq++)        if (IO_APIC_IRQ(irq) && !IO_APIC_VECTOR(irq))            make_8259A_irq(irq);}static void enable_lapic_vector(unsigned int vector){    unsigned long v;    v = apic_read(APIC_LVT0);    apic_write_around(APIC_LVT0, v & ~APIC_LVT_MASKED);}static void disable_lapic_vector(unsigned int vector){    unsigned long v;    v = apic_read(APIC_LVT0);    apic_write_around(APIC_LVT0, v | APIC_LVT_MASKED);}static void ack_lapic_vector(unsigned int vector){    ack_APIC_irq();}static void end_lapic_vector(unsigned int vector) { /* nothing */ }static struct hw_interrupt_type lapic_irq_type = {    .typename 	= "local-APIC-edge",    .startup 	= NULL, /* startup_irq() not used for IRQ0 */    .shutdown 	= NULL, /* shutdown_irq() not used for IRQ0 */    .enable 	= enable_lapic_vector,    .disable 	= disable_lapic_vector,    .ack 		= ack_lapic_vector,    .end 		= end_lapic_vector};/* * This looks a bit hackish but it's about the only one way of sending * a few INTA cycles to 8259As and any associated glue logic.  ICR does * not support the ExtINT mode, unfortunately.  We need to send these * cycles as some i82489DX-based boards have glue logic that keeps the * 8259A interrupt line asserted until INTA.  --macro */static inline void unlock_ExtINT_logic(void){    int apic, pin, i;    struct IO_APIC_route_entry entry0, entry1;    unsigned char save_control, save_freq_select;    unsigned long flags;    pin = find_isa_irq_pin(8, mp_INT);    apic = find_isa_irq_apic(8, mp_INT);    if (pin == -1)        return;    spin_lock_irqsave(&ioapic_lock, flags);    *(((int *)&entry0) + 1) = io_apic_read(apic, 0x11 + 2 * pin);    *(((int *)&entry0) + 0) = io_apic_read(apic, 0x10 + 2 * pin);    spin_unlock_irqrestore(&ioapic_lock, flags);    clear_IO_APIC_pin(apic, pin);    memset(&entry1, 0, sizeof(entry1));    entry1.dest_mode = 0;			/* physical delivery */    entry1.mask = 0;			/* unmask IRQ now */    entry1.dest.physical.physical_dest = hard_smp_processor_id();    entry1.delivery_mode = dest_ExtINT;    entry1.polarity = entry0.polarity;    entry1.trigger = 0;    entry1.vector = 0;    spin_lock_irqsave(&ioapic_lock, flags);    io_apic_write(apic, 0x11 + 2 * pin, *(((int *)&entry1) + 1));    io_apic_write(apic, 0x10 + 2 * pin, *(((int *)&entry1) + 0));    spin_unlock_irqrestore(&ioapic_lock, flags);    save_control = CMOS_READ(RTC_CONTROL);    save_freq_select = CMOS_READ(RTC_FREQ_SELECT);    CMOS_WRITE((save_freq_select & ~RTC_RATE_SELECT) | 0x6,               RTC_FREQ_SELECT);    CMOS_WRITE(save_control | RTC_PIE, RTC_CONTROL);    i = 100;    while (i-- > 0) {        mdelay(10);        if ((CMOS_READ(RTC_INTR_FLAGS) & RTC_PF) == RTC_PF)            i -= 10;    }    CMOS_WRITE(save_control, RTC_CONTROL);    CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);    clear_IO_APIC_pin(apic, pin);    spin_lock_irqsave(&ioapic_lock, flags);    io_apic_write(apic, 0x11 + 2 * pin, *(((int *)&entry0) + 1));    io_apic_write(apic, 0x10 + 2 * pin, *(((int *)&entry0) + 0));    spin_unlock_irqrestore(&ioapic_lock, flags);}int timer_uses_ioapic_pin_0;/* * This code may look a bit paranoid, but it's supposed to cooperate with * a wide range of boards and BIOS bugs.  Fortunately only the timer IRQ * is so screwy.  Thanks to Brian Perkins for testing/hacking this beast * fanatically on his truly buggy board. */static inline void check_timer(void){    int apic1, pin1, apic2, pin2;    int vector;    /*     * get/set the timer IRQ vector:     */    disable_8259A_irq(0);    vector = assign_irq_vector(0);    irq_desc[IO_APIC_VECTOR(0)].action = irq_desc[LEGACY_VECTOR(0)].action;    irq_desc[IO_APIC_VECTOR(0)].depth  = 0;    irq_desc[IO_APIC_VECTOR(0)].status &= ~IRQ_DISABLED;    /*     * Subtle, code in do_timer_interrupt() expects an AEOI     * mode for the 8259A whenever interrupts are routed     * through I/O APICs.  Also IRQ0 has to be enabled in     * the 8259A which implies the virtual wire has to be     * disabled in the local APIC.     */    apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT);    init_8259A(1);    /* XEN: Ripped out the legacy missed-tick logic, so below is not needed. */    /*timer_ack = 1;*/    /*enable_8259A_irq(0);*/    pin1  = find_isa_irq_pin(0, mp_INT);    apic1 = find_isa_irq_apic(0, mp_INT);    pin2  = ioapic_i8259.pin;    apic2 = ioapic_i8259.apic;    if (pin1 == 0)        timer_uses_ioapic_pin_0 = 1;    printk(KERN_INFO "..TIMER: vector=0x%02X apic1=%d pin1=%d apic2=%d pin2=%d\n",           vector, apic1, pin1, apic2, pin2);    if (pin1 != -1) {        /*         * Ok, does IRQ0 through the IOAPIC work?         */        unmask_IO_APIC_irq(0);        if (timer_irq_works()) {            if (disable_timer_pin_1 > 0)                clear_IO_APIC_pin(apic1, pin1);            return;        }        clear_IO_APIC_pin(apic1, pin1);        printk(KERN_ERR "..MP-BIOS bug: 8254 timer not connected to "               "IO-APIC\n");    }    printk(KERN_INFO "...trying to set up timer (IRQ0) through the 8259A ... ");    if (pin2 != -1) {        printk("\n..... (found pin %d) ...", pin2);        /*         * legacy devices should be connected to IO APIC #0         */        setup_ExtINT_IRQ0_pin(apic2, pin2, vector);        if (timer_irq_works()) {            printk("works.\n");            if (pin1 != -1)                replace_pin_at_irq(0, apic1, pin1, apic2, pin2);            else                add_pin_to_irq(0, apic2, pin2);            return;        }        /*         * Cleanup, just in case ...         */        clear_IO_APIC_pin(apic2, pin2);    }    printk(" failed.\n");    if (nmi_watchdog == NMI_IO_APIC) {        printk(KERN_WARNING "timer doesn't work through the IO-APIC - disabling NMI Watchdog!\n");        nmi_watchdog = 0;    }    printk(KERN_INFO "...trying to set up timer as Virtual Wire IRQ...");    disable_8259A_irq(0);    irq_desc[vector].handler = &lapic_irq_type;    apic_write_around(APIC_LVT0, APIC_DM_FIXED | vector);	/* Fixed mode */    enable_8259A_irq(0);    if (timer_irq_works()) {        printk(" works.\n");        return;    }    apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_FIXED | vector);    printk(" failed.\n");    printk(KERN_INFO "...trying to set up timer as ExtINT IRQ...");    /*timer_ack = 0;*/    init_8259A(0);    make_8259A_irq(0);    apic_write_around(APIC_LVT0, APIC_DM_EXTINT);    unlock_ExtINT_logic();    if (timer_irq_works()) {        printk(" works.\n");        return;    }    printk(" failed :(.\n");    panic("IO-APIC + timer doesn't work!  Boot with apic=debug and send a "          "report.  Then try booting with the 'noapic' option");}/* * * IRQ's that are handled by the PIC in the MPS IOAPIC case. * - IRQ2 is the cascade IRQ, and cannot be a io-apic IRQ. *   Linux doesn't really care, as it's not actually used *   for any interrupt handling anyway. */#define PIC_IRQS	(1 << PIC_CASCADE_IR)void __init setup_IO_APIC(void)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -