2007-08-28 00:53:31 +02:00
|
|
|
// The I/O APIC manages hardware interrupts for an SMP system.
|
|
|
|
// http://www.intel.com/design/chipsets/datashts/29056601.pdf
|
2007-08-28 21:04:36 +02:00
|
|
|
// See also picirq.c.
|
2007-08-28 00:53:31 +02:00
|
|
|
|
2006-08-04 20:12:31 +02:00
|
|
|
#include "types.h"
|
|
|
|
#include "defs.h"
|
|
|
|
#include "traps.h"
|
|
|
|
|
2007-08-28 00:53:31 +02:00
|
|
|
#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
|
2006-08-04 20:12:31 +02:00
|
|
|
|
2007-08-28 00:53:31 +02:00
|
|
|
// 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.
|
2007-12-20 19:27:07 +01:00
|
|
|
#define INT_DISABLED 0x00010000 // Interrupt disabled
|
2007-08-28 00:53:31 +02:00
|
|
|
#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)
|
2006-08-04 20:12:31 +02:00
|
|
|
|
2007-08-28 00:53:31 +02:00
|
|
|
volatile struct ioapic *ioapic;
|
|
|
|
|
|
|
|
// IO APIC MMIO structure: write reg, then read or write data.
|
|
|
|
struct ioapic {
|
|
|
|
uint reg;
|
|
|
|
uint pad[3];
|
|
|
|
uint data;
|
|
|
|
};
|
2006-08-04 20:12:31 +02:00
|
|
|
|
|
|
|
static uint
|
2007-08-28 00:53:31 +02:00
|
|
|
ioapic_read(int reg)
|
2006-08-04 20:12:31 +02:00
|
|
|
{
|
2007-08-28 00:53:31 +02:00
|
|
|
ioapic->reg = reg;
|
|
|
|
return ioapic->data;
|
2006-08-04 20:12:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2007-08-28 00:53:31 +02:00
|
|
|
ioapic_write(int reg, uint data)
|
2006-08-04 20:12:31 +02:00
|
|
|
{
|
2007-08-28 00:53:31 +02:00
|
|
|
ioapic->reg = reg;
|
|
|
|
ioapic->data = data;
|
2006-08-04 20:12:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ioapic_init(void)
|
|
|
|
{
|
2007-08-28 00:53:31 +02:00
|
|
|
int i, id, maxintr;
|
2006-08-04 20:12:31 +02:00
|
|
|
|
2006-09-08 17:14:43 +02:00
|
|
|
if(!ismp)
|
2006-09-08 17:07:45 +02:00
|
|
|
return;
|
|
|
|
|
2007-08-28 00:53:31 +02:00
|
|
|
ioapic = (volatile struct ioapic*)IOAPIC;
|
|
|
|
maxintr = (ioapic_read(REG_VER) >> 16) & 0xFF;
|
|
|
|
id = ioapic_read(REG_ID) >> 24;
|
2006-09-08 17:07:45 +02:00
|
|
|
if(id != ioapic_id)
|
|
|
|
cprintf("ioapic_init: id isn't equal to ioapic_id; not a MP\n");
|
2007-08-28 00:53:31 +02:00
|
|
|
|
|
|
|
// Mark all interrupts edge-triggered, active high, disabled,
|
|
|
|
// and not routed to any CPUs.
|
|
|
|
for(i = 0; i <= maxintr; i++){
|
|
|
|
ioapic_write(REG_TABLE+2*i, INT_DISABLED | (IRQ_OFFSET + i));
|
|
|
|
ioapic_write(REG_TABLE+2*i+1, 0);
|
2006-08-04 20:12:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2007-08-28 00:53:31 +02:00
|
|
|
ioapic_enable(int irq, int cpunum)
|
2006-08-04 20:12:31 +02:00
|
|
|
{
|
2006-09-08 17:14:43 +02:00
|
|
|
if(!ismp)
|
2006-09-08 17:07:45 +02:00
|
|
|
return;
|
|
|
|
|
2007-08-28 00:53:31 +02:00
|
|
|
// Mark interrupt edge-triggered, active high,
|
|
|
|
// enabled, and routed to the given cpunum,
|
|
|
|
// which happens to be that cpu's APIC ID.
|
|
|
|
ioapic_write(REG_TABLE+2*irq, IRQ_OFFSET + irq);
|
|
|
|
ioapic_write(REG_TABLE+2*irq+1, cpunum << 24);
|
2006-08-04 20:12:31 +02:00
|
|
|
}
|