xref: /xv6-public/ioapic.c (revision 99b11b6c)
1*99b11b6cSrsc // The I/O APIC manages hardware interrupts for an SMP system.
2*99b11b6cSrsc // http://www.intel.com/design/chipsets/datashts/29056601.pdf
3*99b11b6cSrsc 
4c8b29f6dSkaashoek #include "types.h"
5c8b29f6dSkaashoek #include "mp.h"
6c8b29f6dSkaashoek #include "defs.h"
7c8b29f6dSkaashoek #include "x86.h"
8c8b29f6dSkaashoek #include "traps.h"
9c8b29f6dSkaashoek 
10*99b11b6cSrsc #define IOAPIC  0xFEC00000   // Default physical address of IO APIC
11*99b11b6cSrsc 
12*99b11b6cSrsc #define REG_ID     0x00  // Register index: ID
13*99b11b6cSrsc #define REG_VER    0x01  // Register index: version
14*99b11b6cSrsc #define REG_TABLE  0x10  // Redirection table base
15*99b11b6cSrsc 
16*99b11b6cSrsc // The redirection table starts at REG_TABLE and uses
17*99b11b6cSrsc // two registers to configure each interrupt.
18*99b11b6cSrsc // The first (low) register in a pair contains configuration bits.
19*99b11b6cSrsc // The second (high) register contains a bitmask telling which
20*99b11b6cSrsc // CPUs can serve that interrupt.
21*99b11b6cSrsc #define INT_DISABLED   0x00100000  // Interrupt disabled
22*99b11b6cSrsc #define INT_LEVEL      0x00008000  // Level-triggered (vs edge-)
23*99b11b6cSrsc #define INT_ACTIVELOW  0x00002000  // Active low (vs high)
24*99b11b6cSrsc #define INT_LOGICAL    0x00000800  // Destination is CPU id (vs APIC ID)
25*99b11b6cSrsc 
26*99b11b6cSrsc volatile struct ioapic *ioapic;
27*99b11b6cSrsc 
28*99b11b6cSrsc // IO APIC MMIO structure: write reg, then read or write data.
29c8b29f6dSkaashoek struct ioapic {
30*99b11b6cSrsc   uint reg;
31*99b11b6cSrsc   uint pad[3];
32*99b11b6cSrsc   uint data;
33c8b29f6dSkaashoek };
34c8b29f6dSkaashoek 
35c8b29f6dSkaashoek static uint
36*99b11b6cSrsc ioapic_read(int reg)
37c8b29f6dSkaashoek {
38*99b11b6cSrsc   ioapic->reg = reg;
39*99b11b6cSrsc   return ioapic->data;
40c8b29f6dSkaashoek }
41c8b29f6dSkaashoek 
42c8b29f6dSkaashoek static void
43*99b11b6cSrsc ioapic_write(int reg, uint data)
44c8b29f6dSkaashoek {
45*99b11b6cSrsc   ioapic->reg = reg;
46*99b11b6cSrsc   ioapic->data = data;
47c8b29f6dSkaashoek }
48c8b29f6dSkaashoek 
49c8b29f6dSkaashoek void
50c8b29f6dSkaashoek ioapic_init(void)
51c8b29f6dSkaashoek {
52*99b11b6cSrsc   int i, id, maxintr;
53c8b29f6dSkaashoek 
545c596bb3Skaashoek   if(!ismp)
555c596bb3Skaashoek     return;
565c596bb3Skaashoek 
57*99b11b6cSrsc   ioapic = (volatile struct ioapic*)IOAPIC;
58*99b11b6cSrsc   maxintr = (ioapic_read(REG_VER) >> 16) & 0xFF;
59*99b11b6cSrsc   id = ioapic_read(REG_ID) >> 24;
60c8b29f6dSkaashoek   if(id != ioapic_id)
61f7017212Skaashoek     cprintf("ioapic_init: id isn't equal to ioapic_id; not a MP\n");
62*99b11b6cSrsc 
63*99b11b6cSrsc   // Mark all interrupts edge-triggered, active high, disabled,
64*99b11b6cSrsc   // and not routed to any CPUs.
65*99b11b6cSrsc   for(i = 0; i <= maxintr; i++){
66*99b11b6cSrsc     ioapic_write(REG_TABLE+2*i, INT_DISABLED | (IRQ_OFFSET + i));
67*99b11b6cSrsc     ioapic_write(REG_TABLE+2*i+1, 0);
68c8b29f6dSkaashoek   }
69c8b29f6dSkaashoek }
70c8b29f6dSkaashoek 
71c8b29f6dSkaashoek void
720e84a0ecSrtm ioapic_enable(int irq, int cpunum)
73c8b29f6dSkaashoek {
745c596bb3Skaashoek   if(!ismp)
755c596bb3Skaashoek     return;
765c596bb3Skaashoek 
77*99b11b6cSrsc   // Mark interrupt edge-triggered, active high,
78*99b11b6cSrsc   // enabled, and routed to the given cpunum,
79*99b11b6cSrsc   // which happens to be that cpu's APIC ID.
80*99b11b6cSrsc   ioapic_write(REG_TABLE+2*irq, IRQ_OFFSET + irq);
81*99b11b6cSrsc   ioapic_write(REG_TABLE+2*irq+1, cpunum << 24);
82c8b29f6dSkaashoek }
83