From OSDev Wiki
Jump to: navigation, search

This page is about the ACPI MADT (Multiple APIC Description Table) It describes how the APIC works.



The MADT describes all of the interrupt controllers in the system. It can be used to enumerate the processors currently available.

You can look at the length field in the MADT's header to determine when you have read all the entries.

Table Structure

The MADT starts with the standard ACPI table header. The signature is 'APIC'.

Offset (hex) Length Description
00 4 Signature 'APIC'
04 4 Length
08 1 Revision
09 1 Checksum
0a 6 OEMID
10 8 OEM Table ID
18 4 OEM Revision
1c 4 Creator ID
20 4 Creator Revision

After the standard header, the following fields are located.

Offset (hex) Length Description
24 4 Local APIC Address
28 4 Flags (1 = Dual 8259 Legacy PICs Installed)

If bit 0 in the flags field is set then you need to mask all the 8259 PIC's interrupts, but you should probably do this anyway.

After the Flags field, starting at offset 0x2C, the rest of the MADT table contains a sequence of variable length records which enumerate the interrupt devices on this machine. Each record begins with the following header fields.

Offset (hex) Length Description
0 1 Entry Type
1 1 Record Length

Based on the Entry Type field value, the rest of the record layout can be determined.

Entry Type 0 : Processor Local APIC

This type represents a single physical processor and its local interrupt controller.

Offset (hex) Length Description
2 1 ACPI Processor ID
4 4 Flags (bit 0 = Processor Enabled) (bit 1 = Online Capable)

If flags bit 0 is set the CPU is able to be enabled, if it is not set you need to check bit 1. If that one is set you can still enable it, if it is not the CPU can not be enabled and the OS should not try.

Entry Type 1 : I/O APIC

This type represents a I/O APIC. The global system interrupt base is the first interrupt number that this I/O APIC handles. You can see how many interrupts it handles using the register by getting the number of redirection entries from register 0x01, as described in IO APIC Registers.

Offset (hex) Length Description
2 1 I/O APIC's ID
3 1 Reserved (0)
4 4 I/O APIC Address
8 4 Global System Interrupt Base

Entry Type 2 : IO/APIC Interrupt Source Override

This entry type contains the data for an Interrupt Source Override. This explains how IRQ sources are mapped to global system interrupts. For example, IRQ source for the timer is 0, and the global system interrupt will usually be 2. So you could look for the I/O APIC with the base below 2 and within its redirection entries, then make the redirection entry for (2 - base) to be the timer interrupt.

Offset (hex) Length Description
2 1 Bus Source
3 1 IRQ Source
4 4 Global System Interrupt
8 2 Flags (see below)

Entry type 3 : IO/APIC Non-maskable interrupt source

Specifies which IO/APIC interrupt inputs should be enabled as non-maskable.

Offset (hex) Length Description
2 1 NMI Source
3 1 Reserved
4 2 Flags (see below)
6 4 Global System Interrupt

Entry Type 4 : Local APIC Non-maskable interrupts

Configure these with the LINT0 and LINT1 entries in the Local vector table of the relevant processor(')s(') local APIC.

Offset (hex) Length Description
2 1 ACPI Processor ID (0xFF means all processors)
3 2 Flags (see below)
5 1 LINT# (0 or 1)

Entry Type 5 : Local APIC Address Override

Provides 64 bit systems with an override of the physical address of the Local APIC. There can only be one of these defined in the MADT. If this structure is defined, the 64-bit Local APIC address stored within it should be used instead of the 32-bit Local APIC address stored in the MADT header.

Offset (hex) Length Description
2 2 Reserved
4 8 64-bit physical address of Local APIC

Entry Type 9 : Processor Local x2APIC

Represents a physical processor and its Local x2APIC. Identical to Local APIC; used only when that struct would not be able to hold the required values.

Offset (hex) Length Description
2 2 Reserved
4 4 Processor's local x2APIC ID
8 4 Flags (same as the Local APIC flags)


Entry type 2, 3 and 4 have a flags field, which is useful for settings up the I/O APIC redirection entry or local vector table entry respectively. If (flags & 2) then the interrupt is active when low, and if (flags & 8) then interrupt is level-triggered.

Here is a diagram of the interrupt types: HRDwoa6.png

Example Code

The following code snippet detects and parses MADT table to collect Local APIC data on SMP systems. Works with both RSDT and XSDT, and compiles for both protected mode and long mode.

uint8_t lapic_ids[256]={0}; // CPU core Local APIC IDs
uint8_t numcore=0;          // number of cores detected
uint64_t lapic_ptr=0;       // pointer to the Local APIC MMIO registers
uint64_t ioapic_ptr=0;      // pointer to the IO APIC MMIO registers
void detect_cores(uint8_t *rsdt)
  uint8_t *ptr, *ptr2;
  uint32_t len;
  // iterate on ACPI table pointers
  for(len = *((uint32_t*)(rsdt + 4)), ptr2 = rsdt + 36; ptr2 < rsdt + len; ptr2 += rsdt[0]=='X' ? 8 : 4) {
    ptr = (uint8_t*)(uintptr_t)(rsdt[0]=='X' ? *((uint64_t*)ptr2) : *((uint32_t*)ptr2));
    if(!memcmp(ptr, "APIC", 4)) {
      // found MADT
      lapic_ptr = (uint64_t)(*((uint32_t)(ptr+0x24)));
      ptr2 = ptr + *((uint32_t*)(ptr + 4));
      // iterate on variable length records
      for(ptr += 44; ptr < ptr2; ptr += ptr[1]) {
        switch(ptr[0]) {
          case 0: if(ptr[4] & 1) lapic_ids[numcore++] = ptr[3]; break; // found Processor Local APIC
          case 1: ioapic_ptr = (uint64_t)*((uint32_t*)(ptr+4); break;  // found IOAPIC
          case 5: lapic_ptr = *((uint64_t*)(ptr+4); break;             // found 64 bit LAPIC
// detect SMP cores and print out results
printf("Found %d cores, IOAPIC %lx, LAPIC %lx, Processor IDs:", numcore, ioapic_ptr, lapic_ptr);
for(i = 0; i < numcore; i++)
  printf(" %d", lapic_ids[i]);

See also

Personal tools