Sixty-five headers sat side by side in include/, so `#include "irq.h"` said nothing about where irq.h was or what it belonged to, and errno.h, stat.h and fcntl.h at the top shadowed the system's for any host tool built with -I./include. They are in folders now -- arch/ core/ fs/ dev/ gfx/ net/ uapi/ (and ldls/ for the Linux driver layer) -- and every include names the folder, as Linux's do: "core/proc.h". Done mechanically and checked the hard way: the kernel built from this tree disassembles identically to the one built from the tree before it. The full image boots, and userland is the same set of programs. Paths in docs and comments follow; docs/guides/layout.md has a table of what is where, and tools/fix-includes.py rewrites old-style includes from the folders in the tree, for code written before this. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
119 lines
3.7 KiB
C
119 lines
3.7 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 "core/types.h"
|
|
#include "core/defs.h"
|
|
#include "arch/traps.h"
|
|
#include "arch/memlayout.h"
|
|
#include "arch/mmu.h"
|
|
#include "arch/mp.h"
|
|
#include "core/param.h"
|
|
#include "core/proc.h"
|
|
|
|
|
|
#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;
|
|
|
|
// The firmware's address for it, not a constant: see ioapic_phys in mp.c.
|
|
// ioremap() rather than P2V because nothing obliges it to be in the low
|
|
// four gigabytes the direct map is sure to cover.
|
|
ioapic = ioremap(ioapic_phys, PGSIZE);
|
|
if(!ioapic)
|
|
panic("ioapicinit: cannot map the I/O APIC");
|
|
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);
|
|
}
|
|
}
|
|
|
|
// Route an interrupt to a CPU and unmask it.
|
|
//
|
|
// `level_low` picks PCI's signalling instead of ISA's. This distinction did
|
|
// not matter while every interrupt in the kernel came from an ISA-era device
|
|
// at a fixed pin, all of which are edge-triggered and active high -- which is
|
|
// why the original ioapicenable() hardcoded exactly that. A PCI device wired
|
|
// through an INTx pin is the opposite on both counts, and configuring it as
|
|
// edge/high does not fail cleanly: the device raises the line, we take one
|
|
// interrupt, and because the level never goes away there is no second edge
|
|
// and the device is never heard from again.
|
|
void
|
|
ioapicroute(int irq, int cpunum, int level_low)
|
|
{
|
|
uint lo = T_IRQ0 + irq;
|
|
|
|
// A mode from include/core/irq.h; 1, the PCI case, is what `level_low` meant
|
|
// when it was a flag.
|
|
if(level_low == 1 || level_low == 2)
|
|
lo |= INT_LEVEL;
|
|
if(level_low == 1 || level_low == 3)
|
|
lo |= INT_ACTIVELOW;
|
|
|
|
// Program the destination before unmasking, so the entry is never briefly
|
|
// live pointing at CPU 0 when it was meant for another.
|
|
//
|
|
// The destination is an APIC *ID*, not a CPU index. This wrote the index,
|
|
// which is the same number only when the boot processor's APIC ID is 0 --
|
|
// true under every emulator and on most boards, and not promised: an
|
|
// interrupt aimed at an ID nobody has is an interrupt nobody receives.
|
|
ioapicwrite(REG_TABLE+2*irq+1, (uint)cpus[cpunum].apicid << 24);
|
|
ioapicwrite(REG_TABLE+2*irq, lo);
|
|
}
|
|
|
|
void
|
|
ioapicmask(int irq)
|
|
{
|
|
ioapicwrite(REG_TABLE+2*irq, INT_DISABLED | (T_IRQ0 + irq));
|
|
}
|
|
|
|
void
|
|
ioapicenable(int irq, int cpunum)
|
|
{
|
|
ioapicroute(irq, cpunum, 0);
|
|
}
|