davispuh wrote:I don't know how to set up IO APIC, where should I look?
The IOAPIC registers are accessed through the use of two memory mapped locations - an index register and a data register. The index register is at base + 0x0, the data register at base + 0x10, where base is given in the IOAPIC structure at the end of the MADT table in the ACPI tables. To read a register you do a 32 bit write to the index register with the number of the actual register you want, followed by a 32 bit read from the data register. To do a write, do a 32 bit write to the index register, followed by a 32 bit write to the data register.
Essentially an IOAPIC has inputs from various devices, and outputs to any interrupt pin on any cpu. The ACPI specification defines a 'global system interrupt' - all interrupts given in the ACPI namespace are GSIs. Each IOAPIC in the system is responsible for a certain range of GSIs, defined by the global system interrupt base field in the IOAPIC structure in the MADT table. For a system with a single IOAPIC this is usually 0. To get the IOAPIC input pin from the GSI you do IOAPIC pin = GSI - gsi_base. Non-standard mappings from the normal ISA IRQs (e.g. PIT = 0, keyboard = 1 and so on) to GSIs are given by the Interrupt Source Overrides (as you've already spotted). PCI device -> GSI mappings are defined in the PCI configuration space.
The next mapping is from IOAPIC input pin to an interrupt pin on a particular CPU - this CPU interrupt pin is the interrupt which is raised within the CPU (i.e. the index in the IDT). The mappings are defined in the IOAPIC redirection table registers. IOREDTBL0 is for IOAPIC input pin 0 (normally, but not always GSI 0 - depending on the value of gsi_base as mentioned above) and so on. The redirection table entries are 64 bit values, therefore to set them you need to do two 32 bit writes - one to the low address and one to the high address. For IOREDTBL0, you would write 0x10 to the index register then the low 32 bits of the register value to the data register, then 0x11 to the index register then the high 32 bits of the register value to the data register.
The actual contents of the IOAPIC redirection table register fields are given in the IOAPIC specification. To send an interrupt to a particular processor, you can use physical mode. For this mode, set bits 0-7 to be the interrupt vector (i.e. which entry in the IDT to execute), bits 8-10 to 0 (for fixed delivery mode - only if this is a device, things like the NMI should be set up differently), bit 11 to 0 (for physical mode), bit 16 to 0 to unmask the interrupt, and bits 56-59 to the LAPIC ID of the processor you want to send the interrupt to. The triggering mode should hopefully be set up by the BIOS, so read the registers first, adjust what you need, then write them back. If not, you may need to parse the ACPI tables to find this information for ISA IRQs (Interrupt Source Override structure) or the PCI specification for PCI devices.
Please note this is the simplest way to set up the APIC and that you can acheive more than this (i.e. delivering interrupts to all a group of CPUs, delivering to only one of a predefined group, which changes dynamically - read up on interrupt priorities), but for starters should be enough.
As a side note, the LAPIC attached to each CPU also generates interrupts which are routed directly to the CPU they are attached to - again you can change the interrupt pin, but the registers are accessed in a different way - these are described in the CPU manuals. In particular the LAPICs provide a timer per-processor which is a good source to use for scheduler interrupts.
Regards,
John.