📄 io_apic.c
字号:
unsigned int v, ver, maxlvt; printk("\n" KERN_DEBUG "printing local APIC contents on CPU#%d/%d:\n", smp_processor_id(), hard_smp_processor_id()); v = apic_read(APIC_ID); printk(KERN_INFO "... APIC ID: %08x (%01x)\n", v, GET_APIC_ID(v)); v = apic_read(APIC_LVR); printk(KERN_INFO "... APIC VERSION: %08x\n", v); ver = GET_APIC_VERSION(v); maxlvt = get_maxlvt(); v = apic_read(APIC_TASKPRI); printk(KERN_DEBUG "... APIC TASKPRI: %08x (%02x)\n", v, v & APIC_TPRI_MASK); if (APIC_INTEGRATED(ver)) { /* !82489DX */ v = apic_read(APIC_ARBPRI); printk(KERN_DEBUG "... APIC ARBPRI: %08x (%02x)\n", v, v & APIC_ARBPRI_MASK); v = apic_read(APIC_PROCPRI); printk(KERN_DEBUG "... APIC PROCPRI: %08x\n", v); } v = apic_read(APIC_EOI); printk(KERN_DEBUG "... APIC EOI: %08x\n", v); v = apic_read(APIC_RRR); printk(KERN_DEBUG "... APIC RRR: %08x\n", v); v = apic_read(APIC_LDR); printk(KERN_DEBUG "... APIC LDR: %08x\n", v); v = apic_read(APIC_DFR); printk(KERN_DEBUG "... APIC DFR: %08x\n", v); v = apic_read(APIC_SPIV); printk(KERN_DEBUG "... APIC SPIV: %08x\n", v); printk(KERN_DEBUG "... APIC ISR field:\n"); print_APIC_bitfield(APIC_ISR); printk(KERN_DEBUG "... APIC TMR field:\n"); print_APIC_bitfield(APIC_TMR); printk(KERN_DEBUG "... APIC IRR field:\n"); print_APIC_bitfield(APIC_IRR); if (APIC_INTEGRATED(ver)) { /* !82489DX */ if (maxlvt > 3) /* Due to the Pentium erratum 3AP. */ apic_write(APIC_ESR, 0); v = apic_read(APIC_ESR); printk(KERN_DEBUG "... APIC ESR: %08x\n", v); } v = apic_read(APIC_ICR); printk(KERN_DEBUG "... APIC ICR: %08x\n", v); v = apic_read(APIC_ICR2); printk(KERN_DEBUG "... APIC ICR2: %08x\n", v); v = apic_read(APIC_LVTT); printk(KERN_DEBUG "... APIC LVTT: %08x\n", v); if (maxlvt > 3) { /* PC is LVT#4. */ v = apic_read(APIC_LVTPC); printk(KERN_DEBUG "... APIC LVTPC: %08x\n", v); } v = apic_read(APIC_LVT0); printk(KERN_DEBUG "... APIC LVT0: %08x\n", v); v = apic_read(APIC_LVT1); printk(KERN_DEBUG "... APIC LVT1: %08x\n", v); if (maxlvt > 2) { /* ERR is LVT#3. */ v = apic_read(APIC_LVTERR); printk(KERN_DEBUG "... APIC LVTERR: %08x\n", v); } v = apic_read(APIC_TMICT); printk(KERN_DEBUG "... APIC TMICT: %08x\n", v); v = apic_read(APIC_TMCCT); printk(KERN_DEBUG "... APIC TMCCT: %08x\n", v); v = apic_read(APIC_TDCR); printk(KERN_DEBUG "... APIC TDCR: %08x\n", v); printk("\n");}void print_all_local_APICs (void){ on_each_cpu(print_local_APIC, NULL, 1, 1);}void /*__init*/ print_PIC(void){ extern spinlock_t i8259A_lock; unsigned int v; unsigned long flags; printk(KERN_DEBUG "\nprinting PIC contents\n"); spin_lock_irqsave(&i8259A_lock, flags); v = inb(0xa1) << 8 | inb(0x21); printk(KERN_DEBUG "... PIC IMR: %04x\n", v); v = inb(0xa0) << 8 | inb(0x20); printk(KERN_DEBUG "... PIC IRR: %04x\n", v); outb(0x0b,0xa0); outb(0x0b,0x20); v = inb(0xa0) << 8 | inb(0x20); outb(0x0a,0xa0); outb(0x0a,0x20); spin_unlock_irqrestore(&i8259A_lock, flags); printk(KERN_DEBUG "... PIC ISR: %04x\n", v); v = inb(0x4d1) << 8 | inb(0x4d0); printk(KERN_DEBUG "... PIC ELCR: %04x\n", v);}static void __init enable_IO_APIC(void){ union IO_APIC_reg_01 reg_01; int i; unsigned long flags; for (i = 0; i < PIN_MAP_SIZE; i++) { irq_2_pin[i].pin = -1; irq_2_pin[i].next = 0; } if (!pirqs_enabled) for (i = 0; i < MAX_PIRQS; i++) pirq_entries[i] = -1; /* * The number of IO-APIC IRQ registers (== #pins): */ for (i = 0; i < nr_ioapics; i++) { spin_lock_irqsave(&ioapic_lock, flags); reg_01.raw = io_apic_read(i, 1); spin_unlock_irqrestore(&ioapic_lock, flags); nr_ioapic_registers[i] = reg_01.bits.entries+1; } /* * Do not trust the IO-APIC being empty at bootup */ clear_IO_APIC();}/* * Not an __init, needed by the reboot code */void disable_IO_APIC(void){ /* * Clear the IO-APIC before rebooting: */ clear_IO_APIC(); disconnect_bsp_APIC();}/* * function to set the IO-APIC physical IDs based on the * values stored in the MPC table. * * by Matt Domsch <Matt_Domsch@dell.com> Tue Dec 21 12:25:05 CST 1999 */#ifndef CONFIG_X86_NUMAQstatic void __init setup_ioapic_ids_from_mpc(void){ union IO_APIC_reg_00 reg_00; physid_mask_t phys_id_present_map; int apic; int i; unsigned char old_id; unsigned long flags; /* * This is broken; anything with a real cpu count has to * circumvent this idiocy regardless. */ phys_id_present_map = ioapic_phys_id_map(phys_cpu_present_map); /* * Set the IOAPIC ID to the value stored in the MPC table. */ for (apic = 0; apic < nr_ioapics; apic++) { /* Read the register 0 value */ spin_lock_irqsave(&ioapic_lock, flags); reg_00.raw = io_apic_read(apic, 0); spin_unlock_irqrestore(&ioapic_lock, flags); old_id = mp_ioapics[apic].mpc_apicid; if (mp_ioapics[apic].mpc_apicid >= get_physical_broadcast()) { printk(KERN_ERR "BIOS bug, IO-APIC#%d ID is %d in the MPC table!...\n", apic, mp_ioapics[apic].mpc_apicid); printk(KERN_ERR "... fixing up to %d. (tell your hw vendor)\n", reg_00.bits.ID); mp_ioapics[apic].mpc_apicid = reg_00.bits.ID; } /* Don't check I/O APIC IDs for some xAPIC systems. They have * no meaning without the serial APIC bus. */ if (NO_IOAPIC_CHECK) continue; /* * Sanity check, is the ID really free? Every APIC in a * system must have a unique ID or we get lots of nice * 'stuck on smp_invalidate_needed IPI wait' messages. */ if (check_apicid_used(phys_id_present_map, mp_ioapics[apic].mpc_apicid)) { printk(KERN_ERR "BIOS bug, IO-APIC#%d ID %d is already used!...\n", apic, mp_ioapics[apic].mpc_apicid); for (i = 0; i < get_physical_broadcast(); i++) if (!physid_isset(i, phys_id_present_map)) break; if (i >= get_physical_broadcast()) panic("Max APIC ID exceeded!\n"); printk(KERN_ERR "... fixing up to %d. (tell your hw vendor)\n", i); physid_set(i, phys_id_present_map); mp_ioapics[apic].mpc_apicid = i; } else { physid_mask_t tmp; tmp = apicid_to_cpu_present(mp_ioapics[apic].mpc_apicid); printk("Setting %d in the phys_id_present_map\n", mp_ioapics[apic].mpc_apicid); physids_or(phys_id_present_map, phys_id_present_map, tmp); } /* * We need to adjust the IRQ routing table * if the ID changed. */ if (old_id != mp_ioapics[apic].mpc_apicid) for (i = 0; i < mp_irq_entries; i++) if (mp_irqs[i].mpc_dstapic == old_id) mp_irqs[i].mpc_dstapic = mp_ioapics[apic].mpc_apicid; /* * Read the right value from the MPC table and * write it into the ID register. */ printk(KERN_INFO "...changing IO-APIC physical APIC ID to %d ...", mp_ioapics[apic].mpc_apicid); reg_00.bits.ID = mp_ioapics[apic].mpc_apicid; spin_lock_irqsave(&ioapic_lock, flags); io_apic_write(apic, 0, reg_00.raw); spin_unlock_irqrestore(&ioapic_lock, flags); /* * Sanity check */ spin_lock_irqsave(&ioapic_lock, flags); reg_00.raw = io_apic_read(apic, 0); spin_unlock_irqrestore(&ioapic_lock, flags); if (reg_00.bits.ID != mp_ioapics[apic].mpc_apicid) panic("could not set ID!\n"); else printk(" ok.\n"); }}#elsestatic void __init setup_ioapic_ids_from_mpc(void) { }#endif/* * There is a nasty bug in some older SMP boards, their mptable lies * about the timer IRQ. We do the following to work around the situation: * * - timer IRQ defaults to IO-APIC IRQ * - if this function detects that timer IRQs are defunct, then we fall * back to ISA timer IRQs */static int __init timer_irq_works(void){ unsigned long t1 = jiffies; local_irq_enable(); /* Let ten ticks pass... */ mdelay((10 * 1000) / HZ); /* * Expect a few ticks at least, to be sure some possible * glue logic does not lock up after one or two first * ticks in a non-ExtINT mode. Also the local APIC * might have cached one ExtINT interrupt. Finally, at * least one tick may be lost due to delays. */ if (jiffies - t1 > 4) return 1; return 0;}/* * In the SMP+IOAPIC case it might happen that there are an unspecified * number of pending IRQ events unhandled. These cases are very rare, * so we 'resend' these IRQs via IPIs, to the same CPU. It's much * better to do it this way as thus we do not have to be aware of * 'pending' interrupts in the IRQ path, except at this point. *//* * Edge triggered needs to resend any interrupt * that was delayed but this is now handled in the device * independent code. *//* * Starting up a edge-triggered IO-APIC interrupt is * nasty - we need to make sure that we get the edge. * If it is already asserted for some reason, we need * return 1 to indicate that is was pending. * * This is not complete - we should be able to fake * an edge even if it isn't on the 8259A... */static unsigned int startup_edge_ioapic_irq(unsigned int irq){ int was_pending = 0; unsigned long flags; spin_lock_irqsave(&ioapic_lock, flags); if (irq < 16) { disable_8259A_irq(irq); if (i8259A_irq_pending(irq)) was_pending = 1; } __unmask_IO_APIC_irq(irq); spin_unlock_irqrestore(&ioapic_lock, flags); return was_pending;}/* * Once we have recorded IRQ_PENDING already, we can mask the * interrupt for real. This prevents IRQ storms from unhandled * devices. */static void ack_edge_ioapic_irq(unsigned int irq){ move_irq(irq); if ((irq_desc[irq].status & (IRQ_PENDING | IRQ_DISABLED)) == (IRQ_PENDING | IRQ_DISABLED)) mask_IO_APIC_irq(irq); ack_APIC_irq();}/* * Level triggered interrupts can just be masked, * and shutting down and starting up the interrupt * is the same as enabling and disabling them -- except * with a startup need to return a "was pending" value. * * Level triggered interrupts are special because we * do not touch any IO-APIC register while handling * them. We ack the APIC in the end-IRQ handler, not * 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 */}static void end_level_ioapic_irq (unsigned int irq){ unsigned long v; int i; move_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)))) {#ifdef APIC_MISMATCH_DEBUG atomic_inc(&irq_mis_count);#endif spin_lock(&ioapic_lock); __mask_and_edge_IO_APIC_irq(irq); __unmask_and_level_IO_APIC_irq(irq); spin_unlock(&ioapic_lock); }}#ifdef CONFIG_PCI_MSIstatic 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 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_ioapic_affinity_irq(irq, cpu_mask);}#endif/* * 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, .shutdown = shutdown_edge_ioapic, .enable = enable_edge_ioapic, .disable = disable_edge_ioapic, .ack = ack_edge_ioapic, .end = end_edge_ioapic, .set_affinity = set_ioapic_affinity,};static struct hw_interrupt_type ioapic_level_type = { .typename = "IO-APIC-level", .startup = startup_level_ioapic,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -