diff --git a/src/acpi-dsdt.dsl b/src/acpi-dsdt.dsl
index 2060686..51ca037 100644
--- a/src/acpi-dsdt.dsl
+++ b/src/acpi-dsdt.dsl
@@ -674,20 +674,23 @@ DefinitionBlock (
         External(CPON, PkgObj)
 
         /* Methods called by run-time generated SSDT Processor objects */
-        Method (CPMA, 1, NotSerialized) {
+        Method (CPMA, 2, NotSerialized) {
             // _MAT method - create an madt apic buffer
+            // Arg0 = Processor ID
+            // Arg1 = Local APIC ID
             // Local0 = CPON flag for this cpu
-            Store(DerefOf(Index(CPON, Arg0)), Local0)
+            Store(DerefOf(Index(CPON, Arg1)), Local0)
             // Local1 = Buffer (in madt apic form) to return
             Store(Buffer(8) {0x00, 0x08, 0x00, 0x00, 0x00, 0, 0, 0}, Local1)
             // Update the processor id, lapic id, and enable/disable status
             Store(Arg0, Index(Local1, 2))
-            Store(Arg0, Index(Local1, 3))
+            Store(Arg1, Index(Local1, 3))
             Store(Local0, Index(Local1, 4))
             Return (Local1)
         }
         Method (CPST, 1, NotSerialized) {
             // _STA method - return ON status of cpu
+            // Arg0 = Local APIC ID
             // Local0 = CPON flag for this cpu
             Store(DerefOf(Index(CPON, Arg0)), Local0)
             If (Local0) { Return(0xF) } Else { Return(0x0) }
@@ -708,7 +711,7 @@ DefinitionBlock (
             Store (PRS, Local5)
             // Local2 = last read byte from bitmap
             Store (Zero, Local2)
-            // Local0 = cpuid iterator
+            // Local0 = APIC ID iterator
             Store (Zero, Local0)
             While (LLess(Local0, SizeOf(CPON))) {
                 // Local1 = CPON flag for this cpu
diff --git a/src/acpi.c b/src/acpi.c
index 55e4607..149ff42 100644
--- a/src/acpi.c
+++ b/src/acpi.c
@@ -302,6 +302,63 @@ build_fadt(struct pci_device *pci)
     return fadt;
 }
 
+
+/* APIC IDs of each Processor indexed by ACPI Processor ID.
+ * Used to make the MADT entries and SSDT Processor declarations
+ * match each other.
+ */
+static u8 *processor_apic_ids;
+
+/* Number of Processor IDs that have a valid 8-bit APIC ID */
+static int count_valid_apic_ids;
+
+/* Build a ACPI Processor ID -> Local APIC ID table, used for the MADT
+ * and SSDT Processor declarations.
+ * The CPUs present at boot will be assigned to the first entries.
+ * The remaining entries will get sequential APIC IDs.
+ */
+static void
+check_apic_ids(void)
+{
+    processor_apic_ids = malloc_high(MaxCountCPUs);
+    if (!processor_apic_ids) {
+        warn_noalloc();
+        count_valid_apic_ids = 0;
+        return;
+    }
+
+    int i;
+    int apic_id = 0;
+    int last_present_apic_id = 0;
+
+    // the first entries are for the already-present CPUs:
+    for (i = 0; i < MaxCountCPUs; i++) {
+        // look for next present APIC ID:
+        while (apic_id <= MAX_APIC_ID && !apic_id_is_present(apic_id))
+            apic_id++;
+
+        if (apic_id > MAX_APIC_ID)
+            break; // no more valid APIC IDs
+
+        processor_apic_ids[i] = apic_id;
+        last_present_apic_id = apic_id;
+        apic_id++;
+    }
+
+    // the remaining entries will get sequential APIC IDs assigned, up to
+    // MAX_APIC_ID
+    apic_id = last_present_apic_id;
+    for ( ; i < MaxCountCPUs; i++) {
+        apic_id++;
+        if (apic_id > MAX_APIC_ID)
+            break;
+
+        processor_apic_ids[i] = apic_id;
+    }
+
+    count_valid_apic_ids = i;
+}
+
 static void*
 build_madt(void)
 {
@@ -321,15 +378,16 @@ build_madt(void)
     madt->flags = cpu_to_le32(1);
     struct madt_processor_apic *apic = (void*)&madt[1];
     int i;
-    for (i=0; i<MaxCountCPUs; i++) {
+    for (i = 0; i < count_valid_apic_ids; i++) {
         apic->type = APIC_PROCESSOR;
         apic->length = sizeof(*apic);
         apic->processor_id = i;
-        apic->local_apic_id = i;
-        if (i < CountCPUs)
+        apic->local_apic_id = processor_apic_ids[i];
+        if (apic_id_is_present(apic->local_apic_id)) {
             apic->flags = cpu_to_le32(1);
-        else
+        } else {
             apic->flags = cpu_to_le32(0);
+        }
         apic++;
     }
     struct madt_io_apic *io_apic = (void*)apic;
@@ -402,6 +460,7 @@ encodeLen(u8 *ssdt_ptr, int length, int bytes)
 #define SD_OFFSET_CPUHEX (*ssdt_proc_name - *ssdt_proc_start + 2)
 #define SD_OFFSET_CPUID1 (*ssdt_proc_name - *ssdt_proc_start + 4)
 #define SD_OFFSET_CPUID2 (*ssdt_proc_id - *ssdt_proc_start)
+#define SD_OFFSET_APICID (*ssdt_proc_apic_id - *ssdt_proc_start)
 #define SD_SIZEOF (*ssdt_proc_end - *ssdt_proc_start)
 #define SD_PROC (ssdp_proc_aml + *ssdt_proc_start)
 
@@ -410,12 +469,13 @@ encodeLen(u8 *ssdt_ptr, int length, int bytes)
 static void*
 build_ssdt(void)
 {
-    int acpi_cpus = MaxCountCPUs > 0xff ? 0xff : MaxCountCPUs;
+    int acpi_cpus = count_valid_apic_ids;
+    int cpon_size = count_valid_apic_ids ? processor_apic_ids[count_valid_apic_ids - 1] + 1 : 0;
     // length = ScopeOp + procs + NTYF method + CPON package
     int length = ((1+3+4)
                   + (acpi_cpus * SD_SIZEOF)
                   + (1+2+5+(12*acpi_cpus))
-                  + (6+2+1+(1*acpi_cpus))
+                  + (6+2+1+(1*cpon_size))
                   + 17);
     u8 *ssdt = malloc_high(sizeof(struct acpi_table_header) + length);
     if (! ssdt) {
@@ -440,10 +500,12 @@ build_ssdt(void)
         ssdt_ptr[SD_OFFSET_CPUHEX+1] = getHex(i);
         ssdt_ptr[SD_OFFSET_CPUID1] = i;
         ssdt_ptr[SD_OFFSET_CPUID2] = i;
+        ssdt_ptr[SD_OFFSET_APICID] = processor_apic_ids[i];
         ssdt_ptr += SD_SIZEOF;
     }
 
     // build "Method(NTFY, 2) {If (LEqual(Arg0, 0x00)) {Notify(CP00, Arg1)} ...}"
+    // Arg0 = APIC ID
     *(ssdt_ptr++) = 0x14; // MethodOp
     ssdt_ptr = encodeLen(ssdt_ptr, 2+5+(12*acpi_cpus), 2);
     *(ssdt_ptr++) = 'N';
@@ -457,7 +519,7 @@ build_ssdt(void)
         *(ssdt_ptr++) = 0x93; // LEqualOp
         *(ssdt_ptr++) = 0x68; // Arg0Op
         *(ssdt_ptr++) = 0x0A; // BytePrefix
-        *(ssdt_ptr++) = i;
+        *(ssdt_ptr++) = processor_apic_ids[i];
         *(ssdt_ptr++) = 0x86; // NotifyOp
         *(ssdt_ptr++) = 'C';
         *(ssdt_ptr++) = 'P';
@@ -467,6 +529,7 @@ build_ssdt(void)
     }
 
     // build "Name(CPON, Package() { One, One, ..., Zero, Zero, ... })"
+    // CPON array is APIC-ID based, not Processor ID based
     *(ssdt_ptr++) = 0x08; // NameOp
     *(ssdt_ptr++) = 'C';
     *(ssdt_ptr++) = 'P';
@@ -474,9 +537,9 @@ build_ssdt(void)
     *(ssdt_ptr++) = 'N';
     *(ssdt_ptr++) = 0x12; // PackageOp
     ssdt_ptr = encodeLen(ssdt_ptr, 2+1+(1*acpi_cpus), 2);
-    *(ssdt_ptr++) = acpi_cpus;
-    for (i=0; i<acpi_cpus; i++)
-        *(ssdt_ptr++) = (i < CountCPUs) ? 0x01 : 0x00;
+    *(ssdt_ptr++) = cpon_size;
+    for (i=0; i<cpon_size; i++)
+        *(ssdt_ptr++) = (apic_id_is_present(i)) ? 0x01 : 0x00;
 
     // store pci io windows: start, end, length
     // this way we don't have to do the math in the dsdt
@@ -644,30 +707,40 @@ build_srat(void)
     memset(srat, 0, srat_size);
     srat->reserved1=1;
     struct srat_processor_affinity *core = (void*)(srat + 1);
-    int i;
+    int apic_id;
     u64 curnode;
+    u64 *cpudata = numadata;
 
-    for (i = 0; i < MaxCountCPUs; ++i) {
+    /* Note that the fw_cfg NUMA CPU affinity data is APIC-ID-based, not
+     * Processor ID based, but the size of the tablesble is based on MaxCountCPUs
+     */
+    for (apic_id = 0; apic_id < MaxCountCPUs; ++apic_id) {
+        if (apic_id > MAX_APIC_ID)
+            break;
+        curnode = *cpudata++;
         core->type = SRAT_PROCESSOR;
         core->length = sizeof(*core);
-        core->local_apic_id = i;
-        curnode = *numadata++;
         core->proximity_lo = curnode;
         memset(core->proximity_hi, 0, 3);
         core->local_sapic_eid = 0;
-        if (i < CountCPUs)
+        core->local_apic_id = apic_id;
+        if (apic_id_is_present(apic_id)) {
             core->flags = cpu_to_le32(1);
-        else
-            core->flags = 0;
+        } else {
+            core->flags = cpu_to_le32(0);
+        }
         core++;
     }
 
+    numadata += MaxCountCPUs;
+
 
     /* the memory map is a bit tricky, it contains at least one hole
      * from 640k-1M and possibly another one from 3.5G-4G.
      */
     struct srat_memory_affinity *numamem = (void*)core;
     int slots = 0;
+    int i;
     u64 mem_len, mem_base, next_base = 0;
 
     acpi_build_srat_memory(numamem, 0, 640*1024, 0, 1);
@@ -742,6 +815,8 @@ acpi_bios_init(void)
             tbl_idx++;                                       \
     } while(0)
 
+    check_apic_ids();
+
     struct fadt_descriptor_rev1 *fadt = build_fadt(pci);
     ACPI_INIT_TABLE(fadt);
     ACPI_INIT_TABLE(build_ssdt());
diff --git a/src/smp.c b/src/smp.c
index 8c077a1..af92e4a 100644
--- a/src/smp.c
+++ b/src/smp.c
@@ -36,6 +36,8 @@ wrmsr_smp(u32 index, u64 val)
 
 u32 CountCPUs VAR16VISIBLE;
 u32 MaxCountCPUs VAR16VISIBLE;
+// 256 bits for the found APIC IDs
+u32 FoundAPICIDs[256/32] VAR16VISIBLE;
 extern void smp_ap_boot_code(void);
 ASM16(
     "  .global smp_ap_boot_code\n"
@@ -59,6 +61,12 @@ ASM16(
     "  jmp 1b\n"
     "2:\n"
 
+    // get apic ID on EBX, set bit on FoundAPICIDs
+    "  mov $1, %eax\n"
+    "  cpuid\n"
+    "  shrl $24, %ebx\n"
+    "  lock bts %ebx, FoundAPICIDs\n"
+
     // Increment the cpu counter
     "  lock incl CountCPUs\n"
 
@@ -67,6 +75,11 @@ ASM16(
     "  jmp 1b\n"
     );
 
+int apic_id_is_present(u8 apic_id)
+{
+    return FoundAPICIDs[apic_id/32] & (1 << (apic_id % 32));
+}
+
 // find and initialize the CPUs by launching a SIPI to them
 void
 smp_probe(void)
@@ -82,6 +95,10 @@ smp_probe(void)
         return;
     }
 
+    // mark the BSP initial APIC ID as found, too:
+    u8 apic_id = ebx>>24;
+    FoundAPICIDs[apic_id/32] |= (1 << (apic_id % 32));
+
     // Init the counter.
     writel(&CountCPUs, 1);
 
diff --git a/src/ssdt-proc.dsl b/src/ssdt-proc.dsl
index 0339422..7e7e855 100644
--- a/src/ssdt-proc.dsl
+++ b/src/ssdt-proc.dsl
@@ -5,13 +5,15 @@
  * be placed in the \_SB_ namespace.
  *
  * In addition to the aml code generated from this file, the
- * src/acpi.c file creates a NTFY method with an entry for each cpu:
+ * src/acpi.c file creates a NTFY method with an entry for each cpu,
+ * that checks for the APIC ID:
  *     Method(NTFY, 2) {
  *         If (LEqual(Arg0, 0x00)) { Notify(CP00, Arg1) }
  *         If (LEqual(Arg0, 0x01)) { Notify(CP01, Arg1) }
  *         ...
  *     }
- * and a CPON array with the list of active and inactive cpus:
+ * and a CPON array with the list of active and inactive cpus,
+ * indexed by the APIC ID:
  *     Name(CPON, Package() { One, One, ..., Zero, Zero, ... })
  */
 
@@ -25,6 +27,8 @@ DefinitionBlock ("ssdt-proc.aml", "SSDT", 0x01, "BXPC", "BXSSDT", 0x1)
     Processor (CPAA, 0xAA, 0x0000b010, 0x06) {
         ACPI_EXTRACT_NAME_BYTE_CONST ssdt_proc_id
         Name (ID, 0xAA)
+        ACPI_EXTRACT_NAME_BYTE_CONST ssdt_proc_apic_id
+        Name (APIC, 0xAA)
 /*
  * The src/acpi.c code requires the above ACP_EXTRACT tags so that it can update
  * CPAA and 0xAA with the appropriate CPU id (see
@@ -36,10 +40,10 @@ DefinitionBlock ("ssdt-proc.aml", "SSDT", 0x01, "BXPC", "BXSSDT", 0x1)
         External(CPST, MethodObj)
         External(CPEJ, MethodObj)
         Method(_MAT, 0) {
-            Return(CPMA(ID))
+            Return(CPMA(ID, APIC))
         }
         Method (_STA, 0) {
-            Return(CPST(ID))
+            Return(CPST(APIC))
         }
         Method (_EJ0, 1, NotSerialized) {
             CPEJ(ID, Arg0)
diff --git a/src/util.h b/src/util.h
index ef8ec7c..a68f56b 100644
--- a/src/util.h
+++ b/src/util.h
@@ -351,10 +351,13 @@ void pci_setup(void);
 void smm_init(void);
 
 // smp.c
+#define MAX_APIC_ID 0xFE
+
 extern u32 CountCPUs;
 extern u32 MaxCountCPUs;
 void wrmsr_smp(u32 index, u64 val);
 void smp_probe(void);
+int apic_id_is_present(u8 apic_id);
 
 // coreboot.c
 extern const char *CBvendor, *CBpart;
