That was an excellent explanation of things, and I followed most (if not all) of the advice given and have implemented them, but now I still don't get ISR interrupts, I only get an INT 8 (double fault). IIRC exceptions are a local interrupt set, and therefor do not even pass through the IO-APIC anyways, so no progress for the interrupts.
As I believe I am so close to actually figuring this out, I'm going to post some of the experiment code and hopefully we can figure this out. I understand your point on the wiki article, and believe that once this is worked out, I'd like to start a programming tutorial on it (once I've figure more about it out of course).
--update--
* I have switched the lowest interrupt vector to 0x20 (32) and scale up by 1 through the entire array (24 entries).
* All reads and writes are 32-bits, but my function uses a 64-bit integer and splits it up later (see io_apic_write_64(x,x);)
* I now set the TPR to 0
* I am almost 100% sure the Local APIC's are enabled (see the code + error tests)
* I send an EOI before enabling interrupts to make sure no hangs occur before interrupts can even happen.
* I now test for the IMCRP bit in the MP tables, and set those port values when appropriate.
[edit] BTW, this is post MP table parsing, so I've already checked for IO apics and IO interrupts.[/edit]
Code: Select all
// v = void
v Write_Local_APIC(uint32 reg, uint32 val)
{*(uint32*)((uint64)0xFEE00000 + reg) = val;}
uint32 Read_Local_APIC(uint32 reg)
{return (uint32)(*(uint32*)((uint64)0xFEE00000 + reg));}
static void io_apic_set_reg(uint8 io_apic, uint8 reg) // set active reg
{
if(io_apic >= io_apic_count){error("ioapic: (set) io_apic >= io_apic_count",0); return;}
*(uint32*)((uint64)smp_ioae[io_apic].address) = (uint32)reg;
}
static uint64 io_apic_read_64(uint8 io_apic, uint8 reg)
{
uint32 lo, hi;
uint64 val;
if(io_apic >= io_apic_count){error("ioapic: (read) io_apic >= io_apic_count",0); return 0;}
io_apic_set_reg(io_apic, reg);
hi = *(uint32*)((uint64)smp_ioae[io_apic].address + 0x10);
io_apic_set_reg(io_apic, reg+1);
lo = *(uint32*)((uint64)smp_ioae[io_apic].address + 0x10);
val = hi;
val <<= 32;
val |= lo;
return val;
}
static void io_apic_write_64(uint8 io_apic, uint8 reg, uint64 val)
{
io_apic_set_reg(io_apic, reg);
*(uint32*)((uint64)smp_ioae[io_apic].address + 0x10) = (uint32)(val >> 32);
io_apic_set_reg(io_apic, reg+1);
*(uint32*)((uint64)smp_ioae[io_apic].address + 0x10) = (uint32)(val & 0xFFFFFFFF);
}
v Send_INIT_IPI()
{
printf("apic: sending INIT IPI (%x | %u)\n", Read_Local_APIC(0xF0), cpu_count);
Write_Local_APIC(0x310, 0xFF000000); // set the destination as 0xFF (broadcast)
Write_Local_APIC(0x300, 0x000C4500); // send a broadcast INIT IPI
while(Read_Local_APIC(0x300) & 01000){wait(10);} // wait until all the CPU's ACK
Write_Local_APIC(0x310, 0xFF000000); // set the destination as 0xFF (broadcast)
Write_Local_APIC(0x300, 0x000C4610); // send a broadcast SIPI IP
}
v Enable_Local_APIC()
{
Write_Local_APIC(0xF0, (Read_Local_APIC(0xF0) | 0x1FF)); // set the Enable bit (SVR)
__asm__ __volatile__("movq $0x1b, %rcx; rdmsr; or $0x800, %rax; wrmsr;"); // set the Enable bit (MSR_1b)
if(!(Read_Local_APIC(0xF0) & 0x100)) // if (SVR) does not have the Enable bit set
{error("Local APIC will not enable (%x)", Read_Local_APIC(0xF0));}
if((Read_Local_APIC(0x30) & 0xFF) != 0x14) // if the APIC version is not 0x14
{error("Local APIC has incorrect version (%x)", Read_Local_APIC(0x30));}
Write_Local_APIC(0x80, 0); // set TPR to 0 (enable all interrupts)
if(Read_Local_APIC(0x80) != 0)
{error("Local APIC has incorrect TPR (%x)", Read_Local_APIC(0x80));}
}
v Detect_xAPIC()
{
if(!(cpuid_features_rcx & (1 << 21)))
{error("xAPIC not found",0);}
printf("apic: cpuid_features_rcx: %x \n", cpuid_features_rcx);
}
v Setup_IOAPIC()
{
uint16 i;
// set vectors 0x20 -> (0x20 + 24) to enabled and unmasked
for(i = 0; i < 24; i++)
{io_apic_write_64(0, 0x10 + (i * 2), (0xFF00000000000800 | (0x20 + i)));} // fixed int, dst: logical bcast
if(IMCRP_bit)
{
printf("ioapic: IMCRP bit set, switching from PIC to IOAPIC mode\n");
outportb(0x22, 0x70); // make sure IOAPIC is in use and not the PIC
outportb(0x23, 0x01);
}
Write_Local_APIC(0xB0, 0); // send EOI to Local APIC to clear things out
__asm__ __volatile__("sti;"); // enable interrupts
}
v init_APIC()
{
Detect_xAPIC(); // detect an extended APIC
Enable_Local_APIC();
init_idt(); // setup the IDT, setup ISRs, setup timer and keyboard handler (int 0 and 1) (no PIC remapping)
Send_INIT_IPI(); // wake up APs
Setup_IOAPIC();
}
Hopefully the code was not too rough to read, I didn't take the time to make it look good haha.
There is no PIC remapping, and even when there was, the result was the same. I only get a double fault and no other interrupts (it halts after exception). The result is the same with or without the AP wakeup code as the AP's never enable interrupts and halt before the double fault even occurs.
Am I correct in my assumption that exceptions do not travel through the IO-APIC and that is why it can be recieved? I also find it relevant to remind that a double fault is caused by (says intel) NMI's and INTR's, so of all the exceptions to occur, I believe this one is actually relevant to this task at hand.