text: // 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);
  }
  