APIC

Devices can raise an interrupt to attract CPU's attention. Raising the interrupt is just a process of maintaining some level of voltage across the interrupt line. As long as the voltage is maintained, line is held high and the PIC (Programmable Interrupt Controller) knows that a particular line dedicated to a device needs attention. The PIC will then inform the CPU, that an interrupt occured. CPU then invokes kernel's interrupt handlers to handle interrupt. 

 

Intel CPU's uses a table called IDT(Interrupt descriptor table) which has pointers to routines which have to be executed for it’s corresponding vector number. The table's address is stored in control registers, so when CPU wants to invoke an interrupt handler, it jumps to appropriate routine. IDT is an array of addresses. First 16 entries are exceptions. These are system defined exceptions and cannot be modified by users. Ex: Divide by zero, Null pointer, segmentation fault, etc…

Vector numbers 16-31 are reserved for future use and 32-255 are for external device interrupts.

 

Since devices can not connect directly to CPU, there should be a intermediate controller to pass the interrupt from devices to CPU. This is done by interrupt controller. In x86 the interrupt controller is 8259 chip and is also called PIC(Programmable interrupt controller). A single 8259 PIC contains 8 input interrupt lines and 1 output line. Since only 8 devices can be connected on such a system, we generally use two 8259 PIC’s cascaded onto each other. That makes 16 devices to be configured. But one of the 8259 PIC is also a device and hence the output line of one of the PIC’s is connected to one of it’s 8 input pins. Thus a total of 15 input devices can be connected on such a system.

 

The disadvantage of such a system is:

1: There exists a controller for each unique device and they have to be connected to the same input line always. If you change the pin, to which hardware device is connected, that information has to be conveyed to the OS so that vectors in IDT are modified suitably. But this is not possible on a PIC system.

2: Limited number of devices can be connected.

3: It’s not possible for multiple processors to talk to each other and share information.

 

These hindrances are overcome in APIC.(Advanced PIC). APIC(Advanced Programmable Interrupt Controller) is the updated Intel standard for the older PIC(Programmable interrupt controller).

APIC consists of 2 parts: LAPIC and IOAPIC. IOAPIC is the functional equivalent of 8259 PIC. LAPIC is part of CPU core.

 

Devices are connected to IOAPIC and receives interrupts from the devices and distributes them to the LAPIC. LAPIC interrupts the processor.

 

Apart from device interrupts, APIC is used for Interprocessor Interrupt.

 

Multiprocessor Booting

 

During boot-up, one processor is chosen as a primary processor and it boots up. All other secondary processors(application processors) are put into halt state.

Secondary processors are booted by the primary processor by sending the SIPI(Start Inter processor interrupt).

 

SIPI carries a vector field which holds physical address from where the secondary processor has to start up. Secondary processors receive the SIPI interrupt and then start in real mode. The startup routine on these secondary processors comprise of GDT, IDT, paging, etc….

Sequence of steps to set up APIC:

1: Initially only 1 processor boots up and runs in protected mode. This processor is called primary processor.

2: The primary processor reads the ACPI table to get information on APIC. The following information are available from ACPI

a: how many processors are available on the system

b: Count of IOAPIC’s.

c: Location of IO-APIC’s and LAPIC’s.

This is done by the function: kernel./i386/arch.c: SetupAPIC().

3: After loading the data structures with the required details to start APIC, LAPIC and IOAPIC registers are initialized.

This is done by InitAPIC() which is called from SetupAPIC().

4: The main work in initializing LAPIC and IOAPIC is to initialize the base vectors.

The specs of APIC gives the initial starting base of LAPIC and IOAPIC, which is a physical address.

So the OS can assign any virtual address to it and then can use the given physical address or relocate it to any other location. In ACE, we don’t relocate it.

We utilize MapPhysicalMemory() to allocate a VA to our given PA.

Note that LAPIC will have common base address on all processors because that address is local to each processor and is not put on system bus.

So we only have to take additional care in assigning unique addresses for each of the IOAPIC.

5: After initializing we have to now instruct the ACPI to use our APIC.

By using we mean, ACPI should have to now modify some things which will route interrupts to IO-APIC instead of 8259 PIC. It also has to enable APIC by modifying the corresponding register in LAPIC.

6: Once this is done, APIC is now live and kicking and ready to take in any interrupts

Note that vector table which is programmable by OS should already be setup, so that there are routines to handle any interrupt that arrives on the processor.

7: The primary processor can now start other application processors.

The specs give an universal algorithm for this:

IPI

Delay 10 ms

SIPI <startup routine>

Delay 200 Micro seconds

SIPI <startup routine>

Delay 200 Micro seconds

Now query the target processor if it’s up.

This procedure has to be followed in loop to bootup all processors.

References:


APIC:
http://jlbtc.eduunix.cn/index/html/linux/OReilly.Understanding.the.Linux.Kernel.3rd.Edition.Nov.2005.HAPPY.NEW.YEAR/0596005652/understandlk-CHP-4-SECT-2.html

Intel volume 3: System programming

Intel spec on Multiprocessor and IOAPIC.

APIC Intel Manual

IPI: http://jlbtc.eduunix.cn/index/html/linux/OReilly.Understanding.the.Linux.Kernel.3rd.Edition.Nov.2005.HAPPY.NEW.YEAR/0596005652/understandlk-CHP-4-SECT-6.html