APIC: Difference between revisions

3,637 bytes added ,  26 days ago
m
Bot: Replace deprecated source tag with syntaxhighlight
[unchecked revision][unchecked revision]
m (Fix typos in C example)
m (Bot: Replace deprecated source tag with syntaxhighlight)
 
(34 intermediate revisions by 14 users not shown)
Line 3:
== Detection ==
 
The [[CPUID]].01h:EDX[bit 9] flag specifies whether a CPU has a built-in local APIC. You can find all of the APICs on a system (both local and IO APICs) by parsing the [[MADT]].
 
== Local APIC and IO-APIC ==
Line 9:
In an APIC-based system, each CPU is made of a "core" and a "local APIC". The local APIC is responsible for handling cpu-specific interrupt configuration. Among other things, it contains the ''Local Vector Table (LVT)'' that translates events such as "internal clock" and other "local" interrupt sources into a interrupt vector (e.g. LocalINT1 pin could be raising an NMI exception by storing "2" in the corresponding entry of the LVT).
 
More information about the local APIC can be found in Chapter 10 of the "[https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-vol-3a-part-1-manual.pdf Intel System Programming Guide", ofVol current3A IntelPart processors1].
 
In addition, there is an I/O APIC (e.g. intel 82093AA) that is part of the chipset and provides multi-processor interrupt management, incorporating both static and dynamic symmetric interrupt distribution across all processors. In systems with multiple I/O subsystems, each subsystem can have its own set of interrupts.
Line 20:
 
Inter-Processor Interrupts (IPIs) are generated by a local APIC and can be used as basic signaling for scheduling coordination, multi-processor bootstrapping, etc.
Detailed information on issuing them are available in the Chapter 11.6 of Volume 3 of the Intel Software Developer's Manual, available at the bottom of the page.
 
== Local APIC configuration ==
Line 25 ⟶ 26:
The local APIC is enabled at boot-time and can be disabled by clearing bit 11 of the IA32_APIC_BASE [[Model Specific Registers| Model Specific Register]] (MSR) (see example below, this only works on CPUs with family >5, as the Pentium does not have such MSR). The CPU then receives its interrupts directly from a 8259-compatible PIC. The Intel Software Developer's Manual, however states that, once you have disabled the local APIC through IA32_APIC_BASE you can't enable it anymore until a complete reset. The I/O APIC can also be configured to run in legacy mode so that it emulates an 8259 device.
 
The local APIC's registers are memory-mapped in physical page FEE00xxx (as seen in table 8-1 of Intel P4 SPG). This address is the same for each local APIC that exists in a configuration, meaning you are only able to directly access the registers of the local APIC of the core that your code is currently executing on. Note that there is a MSR that specifies the actual APIC base (only available on CPUs with family >5). The [[MADT]] contains the local APIC base and on 64-bit systems it may also contain a field specifying a 64-bit base address override which you ought to use instead. You can choose to leave the Local APIC base just where you find it, or to move it at your pleasure. '''Note:''' I don't think you can move it any further than the 4th Gb.
 
To enable the Local APIC to receive interrupts it is necessary to configure the "Spurious Interrupt Vector Register". The correct value for this field is the IRQ number that you want to map the spurious interrupts to within the lowest 8 bits, and the 8th bit set to 1 to actually enable the APIC (see the specification for more details). You should choose an interrupt number that has its lowest 4 bits set and is above 32 (as you might guess); easiest is to use 0xFF. This is important on some older processors because the lowest 4 bits for this value must be set to 1 on these.
 
'''Disable the [[PIC|8259 PIC]]''' properly. This is nearly as important as setting up the APIC. You do this in two steps: masking all interrupts and remapping the IRQs. Masking all interrupts disables them in the PIC. Remapping is what you probably already did when you used the PIC: you want interrupt requests to start at 32 instead of 0 to avoid conflicts with the exceptions. You should then avoid using these interrupt vectors for other purposes. This is necessary because even though you masked all interrupts on the PIC, it could still give out spurious interrupts which will then be misinterpreted from your kernel as exceptions.
 
 
Here are some code examples on setting up the APIC:
<sourcesyntaxhighlight lang="c">
#define IA32_APIC_BASE_MSR 0x1B
#define IA32_APIC_BASE_MSR_BSP 0x100 // Processor is a BSP
Line 45 ⟶ 46:
uint32_t eax, edx;
cpuid(1, &eax, &edx);
return edx & CPUID_FLAG_APICCPUID_FEAT_EDX_APIC;
}
 
Line 51 ⟶ 52:
void cpu_set_apic_base(uintptr_t apic) {
uint32_t edx = 0;
uint32_t eax = (apic & 0xfffff0000xfffff0000) | IA32_APIC_BASE_MSR_ENABLE;
 
#ifdef __PHYSICAL_MEMORY_EXTENSION__
Line 57 ⟶ 58:
#endif
 
cpu_set_msrcpuSetMSR(IA32_APIC_BASE_MSR, eax, edx);
}
 
Line 66 ⟶ 67:
uintptr_t cpu_get_apic_base() {
uint32_t eax, edx;
cpu_get_msrcpuGetMSR(IA32_APIC_BASE_MSR, &eax, &edx);
 
#ifdef __PHYSICAL_MEMORY_EXTENSION__
Line 76 ⟶ 77:
 
void enable_apic() {
/* Section 11.4.1 of 3rd volume of Intel SDM recommends mapping the base address page as strong uncacheable for correct APIC operation. */
 
/* Hardware enable the Local APIC if it wasn't enabled */
cpu_set_apic_base(cpu_get_apic_base());
Line 82 ⟶ 85:
write_reg(0xF0, ReadRegister(0xF0) | 0x100);
}
</syntaxhighlight>
</source>
 
== Local APIC and x86 SMM Attacks ==
The APIC was introduced to the core Intel processor architecture skeleton in [https://4donline.ihs.com/images/VipMasterIC/IC/INTL/INTLD047/INTLD047-2-1259.pdf?hkey=EF798316E3902B6ED9A73243A3159BB0 Intel's 82489DX discrete chip] in a similar time period as [[System Management Mode]] was introduced to operating systems. In original architecture, the APIC could not be mapped to memory, and it wasn't until later changes that it became mappable.
 
As System Management Mode's memory (SMRAM) is given a protected range of memory (which can vary from system to system), it is possible to map the APIC memory location into the SMRAM. The result of this is that SMM memory is pushed outside its protected range and exposed to lesser-privileged permission rings. Using this method, attackers can leverage their permissions using System Management Mode, which is protected from all rings above -2.
 
In newer generation Intel processors (starting with the [https://en.wikipedia.org/wiki/Intel_Atom Intel Atom] in 2013), this has been taken into account. An undocumented check is performed against the [[System Management Range Registers]] when the APIC is relocated to memory. This check ensures that the APIC does not overlap with the SMRAM. '''However''', this relies on the SMRR to be configured correctly. Otherwise, this mitigation will not work properly and attackers will still be able to use the attack.
 
== Local APIC registers ==
The local APIC registers are memory mapped to an address that can be found in the MP/MADT tables. Make sure you map these to virtual memory if you are using paging. Each register is 32 bits long, and expects to be written and read as a 32 bit integer. Although each register is 4 bytes, they are all aligned on a 16 byte boundary. (TODO: Complete the list of registers)
 
List of local APIC registers (TODO: Add descriptions for all registers):
{| {{wikitable}}
|-
| Offset
| Register name
| Read/Write permissions
|-
|-
| 000h - 010h
| Reserved
|
|-
|-
| 020h
| LAPIC ID Register
| Read/Write
|-
|-
| 030h
| LAPIC Version Register
| Read only
|-
|-
| 040h - 070h
| Reserved
|
|-
|-
| 080h
| Task Priority Register (TPR)
| Read/Write
|-
|-
| 090h
| Arbitration Priority Register (APR)
| Read only
|-
|-
| 0A0h
| Processor Priority Register (PPR)
| Read only
|-
|-
| 0B0h
| EOI register
| Write only
|-
|-
| 0C0h
| Remote Read Register (RRD)
| Read only
|-
|-
| 0D0h
| Logical Destination Register
| Read/Write
|-
|-
| 0E0h
| Destination Format Register
| Read/Write
|-
|-
| 0F0h
| Spurious Interrupt Vector Register
| Read/Write
|-
|-
| 100h - 170h
| In-Service Register (ISR)
| Read only
|-
|-
| 180h - 1F0h
| Trigger Mode Register (TMR)
| Read only
|-
|-
| 200h - 270h
| Interrupt Request Register (IRR)
| Read only
|-
|-
| 280h
| Error Status Register
| Read only
|-
|-
| 290h - 2E0h
| Reserved
|
|-
|-
| 2F0h
| LVT Corrected Machine Check Interrupt (CMCI) Register
| Read/Write
|-
|-
| 300h - 310h
| Interrupt Command Register (ICR)
| Read/Write
|-
|-
| 320h
| LVT Timer Register
| Read/Write
|-
|-
| 330h
| LVT Thermal Sensor Register
| Read/Write
|-
|-
| 340h
| LVT Performance Monitoring Counters Register
| Read/Write
|-
|-
| 350h
| LVT LINT0 Register
| Read/Write
|-
|-
| 360h
| LVT LINT1 Register
| Read/Write
|-
|-
| 370h
| LVT Error Register
| Read/Write
|-
|-
| 380h
| Initial Count Register (for Timer)
| Read/Write
|-
|-
| 390h
| Current Count Register (for Timer)
| Read only
|-
|-
| 3A0h - 3D0h
| Reserved
|
|-
|-
| 3E0h
| Divide Configuration Register (for Timer)
| Read/Write
|-
|-
| 3F0h
| Reserved
|
|-
|}
 
=== EOI Register ===
Write to the register with offset 0xB0 using the value 0 to signal an end of interrupt. A non-zero valuesvalue causesmay cause a general protection fault.
 
=== Local Vector Table Registers ===
Line 99 ⟶ 268:
| The vector number
|-
| BitBits 8-1110 (reserved for timer)
| 100b if NMI
|-
| Bit 11
| Reserved
|-
| Bit 12
Line 133 ⟶ 305:
|-
| Bits 8-10
| The destinationdelivery mode. 0 is normal, 1 is lowest priority, 2 is SMI, 4 is NMI, 5 can be INIT or INIT level de-assert, 6 is a SIPI.
|-
| Bit 11
Line 151 ⟶ 323:
|-
| Bits 18-19
| Destination type. If this is > 0 then the destination field in 0x310 is ignored. 1 will always send the interrupt to the itself, 2 will send it to all processors, and 3 will send it to all processors aside from the current one. It is best to avoid using modes 1, 2 and 3, and stick with 0.
|-
| Bits 20-31
Line 160 ⟶ 332:
 
The IO APIC uses two registers for most of its operation - an address register at IOAPICBASE+0 and a data register at
IOAPICBASE+0x10. All accesses must be done on uint32_t4 byte boundaries. The address register uses the bottom 8 bits for register select. Here is some example code that illustrates this:
 
<sourcesyntaxhighlight lang="c">
uint32_t cpuReadIoApic(void *ioapicaddr, uint32_t reg)
{
Line 176 ⟶ 348:
ioapic[4] = value;
}
</syntaxhighlight>
</source>
 
Note the use of the [[volatile (keyword)|volatile]] keyword. This prevents a compiler like [[Visual C]] from reordering or optimizing away the memory accesses, which would be a Bad Thing&trade;. The volatile keyword is put before the '*' sign. It means that the ''value pointed to'' is volatile, not the pointer itself.
Line 215 ⟶ 387:
|-
| Bit 14
| Used for level triggedtriggered interrupts only to show if a local APIC has received the interrupt (= 1), or has sent an EOI (= 0). Read only.
|-
| Bit 15
Line 229 ⟶ 401:
| Destination field. If the destination mode bit was clear, then the lower 4 bits contain the bit APIC ID to sent the interrupt to. If the bit was set, the upper 4 bits also contain a set of processors. (See below)
|}
For more information, check out chapter 3 of [http://web.archive.org/web/20161130153145/http://download.intel.com/design/chipsets/datashts/29056601.pdf the I/O APIC datasheet].
 
The redirection table allows you to choose which externelexternal interrupts are sent to which processors and with which interrupt vectors. When choosing the processors you should consider: spreading out the workload between the processors, avoiding processors in a low-power state, and avoiding throttled processors. When choosing the interrupt vectors you should remember that interrupts 0x00 to 0x1F are reserved for internal processor exceptions, the interrupts you remapedremapped the PIC to may receive spurious interrupts, that 0xFF is probably where you put the APIC spurious interrupt, and that the upper 4 bits of aan interrupt vector indicate its priority.
 
== Logical Destination Mode ==
Line 244 ⟶ 416:
| rowspan="3" | Bits 24-31
| Flat model
| Bitmap of target processors (each bit identifies single processor; supports a maximum of 8 local APIC units)
|-
| rowspan="2" | Cluster model
Line 264 ⟶ 436:
|}
 
The cluster addressing scheme has some limitations on the Pentium era systems. It may require special hardware to route the APIC bus messages between different CPU clusters or that it is sometimes limited to 15 agents (CPUs) in total. More info can be found in [https://books.google.com/books?id=TVzjEZg1--YC&printsec=frontcover "Pentium Processor System Architecture. Chapter 15: The APIC"]
'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).' ([http://forum.osdev.org/viewtopic.php?f=1&t=14808&start=17 Brendan])
 
Operating systems running on 64-bit processors typically use "Flat model" when the system has up to 8 CPUs. If more than 8 CPUs is used some OSes use cluster model which allow to address in total 60 CPUs (cluster 0xf is a broadcast and there is 15 clusters with 4 CPUs in each).
More info can be found in [https://books.google.nl/books?id=TVzjEZg1--YC&printsec=frontcover "Pentium Processor System Architecture. Chapter 15: The APIC"]
 
== SIPI Sequence ==
 
The INIT-SIPI sequence is done by the operating system after the firmware already gives the first INIT signal. This example code from the [ Silcos Kernel http://wiki.osdev.org/Silcos_Kernel]
 
<source lang="C">
void APIC::wakeupSequence(U32 apicId, U8 pvect)
{
ICRHigh hreg = {
.destField = apicId
};
 
ICRLow lreg(DeliveryMode::INIT, Level::Deassert, TriggerMode::Edge);
 
xAPICDriver::write(APIC_REGISTER_ICR_HIGH, hreg.value);
xAPICDriver::write(APIC_REGISTER_ICR_LOW, lreg.value);
 
lreg.vectorNo = pvect;
lreg.delvMode = DeliveryMode::StartUp;
 
Dbg("APBoot: Wakeup sequence following...");
 
xAPICDriver::write(APIC_REGISTER_ICR_HIGH, hreg.value);
xAPICDriver::write(APIC_REGISTER_ICR_LOW, lreg.value);
}
 
The logical delivery mode is handy to address multiple CPUs when doing IPIs or it might be used in conjunction with lowest priority delivery mode to deliver IRQs from MSI/IO-APICs to a certain CPU in the group specified in the destination field.
// NOTE: ICRLow and ICRHigh are types in the Silcos kernel. If your code uses direct bit
// manipulations you must replace some code with bit operations.
</source>
 
== See Also ==
Line 304 ⟶ 449:
 
===Threads===
* [httphttps://wwwforum.osdev.org/phpBB2/viewtopic.php?t=10686 APIC timer]
* [httphttps://wwwforum.osdev.org/phpBB2/viewtopic.php?t=11529 Mapping the I/O APIC]
* [httphttps://wwwforum.osdev.org/phpBB2/viewtopic.php?p=107868#107868 Brendan gives some general info on the APIC and implementing it.]
 
===External Links===
* [https://web.archive.org/web/20161128201003/http://www.intel.com/design/chipsets/datashts/290566.htm originalOriginal I/O APIC specification/datasheet]
* [https://web.archive.org/web/20170430050751/http://developerwww.intel.com/design/chipsets/specupdt/290710.htm updatedUpdated I/O APIC specification/datasheet]
* [httphttps://www.intel.com/products/processor/manuals/ Volume 3A:System Programming Guide, Part 1,manuals has a chapter on the APIC]
* [https://www.intel.com/products/processor/manuals/ Volume 3A:System Programming Guide, Chapter 10.4 for further reading about the LAPIC]
* [http://web.archive.org/web/20140308064246/http://www.osdever.net/tutorials/pdf/apic.pdf Advanced Programmable Interrupt Controller by Mike Rieker]
* [httphttps://web.archive.org/web/2010091808475020140308064246/http://www.microsoftosdever.comnet/whdctutorials/archivepdf/apic.mspxpdf "TheAdvanced Importance of Implementing APIC-BasedProgrammable Interrupt Subsystems on Uniprocessor PCs". Microsoft.Controller 18by SeptemberMike 2010Rieker]
* [https://web.archive.org/web/20121026064337/http://msdn.microsoft.com/en-us/windows/hardware/gg462964.aspx "The Importance of Implementing APIC-Based Interrupt Subsystems on Uniprocessor PCs". Microsoft. 7 January 2003]
* [https://books.google.nl/books?id=TVzjEZg1--YC&printsec=frontcover Pentium Processor System Architecture]
* [https://books.google.com/books?id=TVzjEZg1--YC Pentium Processor System Architecture]
 
[[Category:Interrupts]]
[[Category:Time]]
[[Category:Multiprocessing]]
[[de:Advanced Programmable Interrupt Controller]]