xv6/ioapic.c

69 lines
2.1 KiB
C

// The I/O APIC manages hardware interrupts for an SMP system.
// http://www.intel.com/design/chipsets/datashts/29056601.pdf
// See also picirq.c.
#include "types.h"
#include "defs.h"
#include "traps.h"
#define IOAPIC 0xFEC00000 // Default physical address of IO APIC
#define REG_ID 0x00 // Register index: ID
#define REG_VER 0x01 // Register index: version
#define REG_TABLE 0x10 // Redirection table base
// The redirection table starts at REG_TABLE and uses
// two registers to configure each interrupt.
// The first (low) register in a pair contains configuration bits.
// The second (high) register contains a bitmask telling which
// CPUs can serve that interrupt.
#define INT_DISABLED 0x00010000 // Interrupt disabled
#define INT_LEVEL 0x00008000 // Level-triggered (vs edge-)
#define INT_ACTIVELOW 0x00002000 // Active low (vs high)
#define INT_LOGICAL 0x00000800 // Destination is CPU id (vs APIC ID)
volatile struct ioapic *ioapic;
// IO APIC MMIO structure: write reg, then read or write data.
struct ioapic {
uint reg;
uint pad[3];
uint data;
};
static uint ioapicread(int reg) {
ioapic->reg = reg;
return ioapic->data;
}
static void ioapicwrite(int reg, uint data) {
ioapic->reg = reg;
ioapic->data = data;
}
void ioapicinit(void) {
int i, id, maxintr;
ioapic = (volatile struct ioapic*)IOAPIC;
maxintr = (ioapicread(REG_VER) >> 16) & 0xFF;
id = ioapicread(REG_ID) >> 24;
if (id != ioapicid) {
cprintf("ioapicinit: id isn't equal to ioapicid; not a MP\n");
}
// Mark all interrupts edge-triggered, active high, disabled,
// and not routed to any CPUs.
for (i = 0; i <= maxintr; i++) {
ioapicwrite(REG_TABLE + 2 * i, INT_DISABLED | (T_IRQ0 + i));
ioapicwrite(REG_TABLE + 2 * i + 1, 0);
}
}
void ioapicenable(int irq, int cpunum) {
// Mark interrupt edge-triggered, active high,
// enabled, and routed to the given cpunum,
// which happens to be that cpu's APIC ID.
ioapicwrite(REG_TABLE + 2 * irq, T_IRQ0 + irq);
ioapicwrite(REG_TABLE + 2 * irq + 1, cpunum << 24);
}