Some general notes. If your OS is "long mode only" or something, then some of these things may not apply...
Don't use cluster mode addressing, especially "hierarchical cluster mode". AFAIK it was intended for large NUMA systems, where there's a "node controller" for each NUMA domain that forwarded interrupts to CPUs within that NUMA domain (with a seperate APIC bus for each NUMA domain). Unless your chipset has these "node controllers" (or "cluster managers" as Intel calls them) it won't work, and no modern computers have them (AFAIK there are only a few obscure Pentium III/P6 NUMA systems that ever did). You want to use "flat model" for normal SMP and for most NUMA systems (including AMD's).
For compatability with P6 family CPUs, it's best to use a spurious interrupt vector that has the lowest 3 bits set (e.g. 0x1F, 0x2F, 0x3F, etc) because these bits are hardwired to '1' in P6 CPUs. At the moment you're doing "spurious = spurious | 0101001101b", so on a P4 you'd be using vector 0x4D and on a Pentium III you'd be using vector 0x4F.
For most local APIC registers, there isn't much point doing "register = register | stuff" and it's much simpler to just do "register = stuff". For example, if (for some strange reason) the BIOS left the spurious interrupt register left set to 0x000000FF, then you'd end up doing "spurious = 0xFF | 0x14D" and using vector 0xFF for the spurious interrupt.
The "logical ID" should be treated as a bit mask, as the CPU does the equivelent of "if( (APIC_message_destination & local_APIC_logical_ID) != 0" when it decides if it should accept the message or not. Some OSs set one bit for each CPU, so that (for e.g.) sending a message to the logical destination 0x10101010 would send the message to every second CPU. Of course this doesn't work if there's more than 8 CPUs. I give special meanings to each bit, for e.g.:
- bit 0 - set in all running CPUs
bit 1 - set if the CPU is the first CPU in the core
bit 2 - set if the CPU is the first CPU in the chip
bit 3 - set if the CPU is the first CPU in the NUMA domain
LINT0 and LINT1 should be setup dynamically (according to whatever the MPS table or ACPI says), rather than hardcoded. Harcoding them (like in the example in Intel manuals) is only really appropriate for BIOSs (where they know how the inputs are connected in advance). The same applies to I/O APIC inputs (for e.g. the first entry in the I/O APIC redirection table might not be the PIT chip).
Also, for older systems, you may need to disable the PIC chips using the IMCR. For e.g.:
Code: Select all
test dword [SIBBOOTdetectionFlags],DETECTFLAGhasIMCRP ;Does IMCR need to be disabled?
je .noIMCR ; no
mov al,0x70
out 0x22,al ;Select IMCR register
mov al,1
out 0x23,al ;Disable PICs
.noIMCR:
For testing purposes, start by sending an IPI using "broadcast to all including self" destination shorthand - it doesn't rely on things like the I/O APIC or half the local APIC configuration, which makes it a good place to start. If that works, then try sending IPIs to specific CPUs (you can do these steps while you're still using the PIC). Once you're sure the local APIC is setup right and working correctly, then try setting up I/O APIC IRQs. The idea is to minimise the things that could be wrong at each step, so that if something is wrong there's less you need to check...

Cheers,
Brendan