After enabling the local APIC and mapping the legacy IRQs in the I/O APIC's redirection entries, the PIT interrupt appears to be coming in as IRQ10.
I checked the MADT for ISOs, and found one with bus-relative IRQ 0 and GSI of 2 (based on the HPET page of the wiki I assume this to be the timer ISO).
I've mapped the PIC IRQs to 0x20 through 0x2F and masked them.
I'm trying to map the I/O APIC IRQs starting at 0x30.
Here are some weird things I've noticed:
- The IRQ10 handler gets called regardless of whether or not I apply the ISO in my I/O APIC redirects.
- The PS2 emulation keyboard IRQ seems to be one less than the timer IRQ
Where did I go wrong?
Here's how I set a redirection entry in the I/O APIC (based on code found at https://ethv.net/workshops/osdev/notes/notes-3.html):
Code: Select all
void ioapic_set_irq(uint8_t irq, uint8_t isr)
{
uint32_t lo_index = irq * 2;
uint32_t hi_index = irq * 2 + 1;
// Get the current APIC ID
uint32_t lapic_id = k_lapic_get_id();
// Currently, the I/O APIC registers only have 4 bits for the
// local APIC ID when in "physical" destination mode.
// In theory, some chips can have up to 255 processors using "logical" destination mode.
// TODO: figure out how to handle this scenario.
if (lapic_id > 0xF)
{
fprintf(stddbg, "[ERROR] local APIC ID is more than 4 bits\n");
for (;;);
}
// Put the local APIC ID in bits [59:56].
// This is bits [31:24] of the high register.
uint32_t hi_contents = ioapic_read(hi_index);
// Clear bits [31:24] and set the local APIC address.
hi_contents &= ~0xFF000000;
hi_contents |= (lapic_id << 24);
ioapic_write(hi_index, hi_contents);
// Most of the IRQ description goes in the low register.
uint32_t lo_contents = ioapic_read(lo_index);
// Clear bits [10:8] to indicate fixed delivery mode.
// TODO: add doc comments for delivery mode.
lo_contents &= ~(0x700);
// Clear bit 11 to indicate physical destination mode.
lo_contents &= ~(0x800);
// Bits [15:12] are ignored for now.
// Bit 13 is the polarity. (1 for low, 0 for high)
// Bit 15 is the trigger mode. (1 for level, 0 for edge)
// Clear bit 16 to unmask the IRQ.
lo_contents &= ~(0x10000);
// Clear bits [7:0] in preparation for the new ISR index.
lo_contents &= ~0xFF;
// Set the index of the ISR in bits [7:0].
lo_contents |= isr;
ioapic_write(lo_index, lo_contents);
}
This is how I handle ISO entries in the MADT:
Code: Select all
uint8_t irq_src = *((uint8_t*)(entry + 3));
uint32_t gsi = *((uint32_t*)(entry + 4));
uint16_t flags = *((uint16_t*)(entry + 8));
// get the polarity and trigger mode from the flags
uint16_t pol = (flags & 0x3);
uint16_t trig = (flags & 0xC) >> 2;
// Add the redirection entry to the IRQ.
ioapic_set_iso(irq_src, 0x30 + gsi, pol, trig);