Patchwork [seabios,2/2] allow CPUs to have non-contiguous Local APIC IDs

login
register
mail settings
Submitter Eduardo Habkost
Date July 19, 2012, 8:52 p.m.
Message ID <1342731162-14285-3-git-send-email-ehabkost@redhat.com>
Download mbox | patch
Permalink /patch/172059/
State New
Headers show

Comments

Eduardo Habkost - July 19, 2012, 8:52 p.m.
Extract Local APIC IDs directly from the CPUs, and instead of check for
"i < CountCPUs", check if the APIC ID was present on boot, when building
ACPI tables and the MP-Table.

This keeps ACPI Processor ID == APIC ID, but allows the
hardware<->Seabios interface be completely APIC-ID based, and Seabios
may change the way ACPI Processor IDs are chosen in the future.

As we currently Seabios support only xAPIC and not x2APIC, the list of
present-on-boot APIC IDs is a 256-bit bitmap. If one day Seabios starts
to support x2APIC, the data structure used to enumerate the APIC IDs
will have to be changed.

Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
---
 src/acpi-dsdt.dsl |    4 +++-
 src/acpi.c        |    9 +++++----
 src/mptable.c     |    2 +-
 src/smp.c         |   17 +++++++++++++++++
 src/util.h        |    1 +
 5 files changed, 27 insertions(+), 6 deletions(-)
Kevin O'Connor - July 20, 2012, 1:12 a.m.
On Thu, Jul 19, 2012 at 05:52:42PM -0300, Eduardo Habkost wrote:
> Extract Local APIC IDs directly from the CPUs, and instead of check for
> "i < CountCPUs", check if the APIC ID was present on boot, when building
> ACPI tables and the MP-Table.
> 
> This keeps ACPI Processor ID == APIC ID, but allows the
> hardware<->Seabios interface be completely APIC-ID based, and Seabios
> may change the way ACPI Processor IDs are chosen in the future.
> 
> As we currently Seabios support only xAPIC and not x2APIC, the list of
> present-on-boot APIC IDs is a 256-bit bitmap. If one day Seabios starts
> to support x2APIC, the data structure used to enumerate the APIC IDs
> will have to be changed.

Is this SeaBIOS change dependent on a particular version of QEMU?

[...]
> --- 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"

Because of the insanity that is 16bit mode, please use explicit size
suffixes on assembler instructions (ie, "movl" and "btsl").

-Kevin
Eduardo Habkost - July 20, 2012, 1:57 a.m.
On Thu, Jul 19, 2012 at 09:12:54PM -0400, Kevin O'Connor wrote:
> On Thu, Jul 19, 2012 at 05:52:42PM -0300, Eduardo Habkost wrote:
> > Extract Local APIC IDs directly from the CPUs, and instead of check for
> > "i < CountCPUs", check if the APIC ID was present on boot, when building
> > ACPI tables and the MP-Table.
> > 
> > This keeps ACPI Processor ID == APIC ID, but allows the
> > hardware<->Seabios interface be completely APIC-ID based, and Seabios
> > may change the way ACPI Processor IDs are chosen in the future.
> > 
> > As we currently Seabios support only xAPIC and not x2APIC, the list of
> > present-on-boot APIC IDs is a 256-bit bitmap. If one day Seabios starts
> > to support x2APIC, the data structure used to enumerate the APIC IDs
> > will have to be changed.
> 
> Is this SeaBIOS change dependent on a particular version of QEMU?

No, it's the opposite: QEMU needs this change first, to be able to fix
some CPU socket/core/thread topology bugs caused by incorrect APIC IDs.

For hardware that has contiguous APIC IDs (that's the case for existing
QEMU versions), the OS-visible behavior and resulting tables should be
exactly the same.

> 
> [...]
> > --- 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"
> 
> Because of the insanity that is 16bit mode, please use explicit size
> suffixes on assembler instructions (ie, "movl" and "btsl").

I'll do. Thanks for the feedback.

Patch

diff --git a/src/acpi-dsdt.dsl b/src/acpi-dsdt.dsl
index 2060686..72dc7d8 100644
--- a/src/acpi-dsdt.dsl
+++ b/src/acpi-dsdt.dsl
@@ -676,6 +676,7 @@  DefinitionBlock (
         /* Methods called by run-time generated SSDT Processor objects */
         Method (CPMA, 1, NotSerialized) {
             // _MAT method - create an madt apic buffer
+            // Arg0 = Processor ID = Local APIC ID
             // Local0 = CPON flag for this cpu
             Store(DerefOf(Index(CPON, Arg0)), Local0)
             // Local1 = Buffer (in madt apic form) to return
@@ -688,6 +689,7 @@  DefinitionBlock (
         }
         Method (CPST, 1, NotSerialized) {
             // _STA method - return ON status of cpu
+            // Arg0 = Processor ID = Local APIC ID
             // Local0 = CPON flag for this cpu
             Store(DerefOf(Index(CPON, Arg0)), Local0)
             If (Local0) { Return(0xF) } Else { Return(0x0) }
@@ -708,7 +710,7 @@  DefinitionBlock (
             Store (PRS, Local5)
             // Local2 = last read byte from bitmap
             Store (Zero, Local2)
-            // Local0 = cpuid iterator
+            // Local0 = Processor ID / 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 3f55de9..d79a1e9 100644
--- a/src/acpi.c
+++ b/src/acpi.c
@@ -326,7 +326,7 @@  build_madt(void)
         apic->length = sizeof(*apic);
         apic->processor_id = i;
         apic->local_apic_id = i;
-        if (i < CountCPUs)
+        if (apic_id_is_present(apic->local_apic_id))
             apic->flags = cpu_to_le32(1);
         else
             apic->flags = cpu_to_le32(0);
@@ -444,6 +444,7 @@  build_ssdt(void)
     }
 
     // build "Method(NTFY, 2) {If (LEqual(Arg0, 0x00)) {Notify(CP00, Arg1)} ...}"
+    // Arg0 = Processor ID = APIC ID
     *(ssdt_ptr++) = 0x14; // MethodOp
     ssdt_ptr = encodeLen(ssdt_ptr, 2+5+(12*acpi_cpus), 2);
     *(ssdt_ptr++) = 'N';
@@ -476,7 +477,7 @@  build_ssdt(void)
     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++) = (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
@@ -655,10 +656,10 @@  build_srat(void)
         core->proximity_lo = curnode;
         memset(core->proximity_hi, 0, 3);
         core->local_sapic_eid = 0;
-        if (i < CountCPUs)
+        if (apic_id_is_present(i))
             core->flags = cpu_to_le32(1);
         else
-            core->flags = 0;
+            core->flags = cpu_to_le32(0);
         core++;
     }
 
diff --git a/src/mptable.c b/src/mptable.c
index 103f462..9406f98 100644
--- a/src/mptable.c
+++ b/src/mptable.c
@@ -59,7 +59,7 @@  mptable_init(void)
         cpu->apicid = i;
         cpu->apicver = apic_version;
         /* cpu flags: enabled, bootstrap cpu */
-        cpu->cpuflag = ((i<CountCPUs) ? 0x01 : 0x00) | ((i==0) ? 0x02 : 0x00);
+        cpu->cpuflag = (apic_id_is_present(i) ? 0x01 : 0x00) | ((i==0) ? 0x02 : 0x00);
         cpu->cpusignature = cpuid_signature;
         cpu->featureflag = cpuid_features;
         cpu++;
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/util.h b/src/util.h
index ef8ec7c..89e928c 100644
--- a/src/util.h
+++ b/src/util.h
@@ -355,6 +355,7 @@  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;