Set num_cores early in boot
authorBarret Rhoden <brho@cs.berkeley.edu>
Sun, 6 Nov 2016 16:21:02 +0000 (11:21 -0500)
committerBarret Rhoden <brho@cs.berkeley.edu>
Tue, 29 Nov 2016 16:27:40 +0000 (11:27 -0500)
The memory allocator will need to know the number of cores in the
system when it is initialized.  In the future, it may also need to know
the number of NUMA domains.  Determining the number of cores is somewhat
arch-specific.  We can do it with ACPI on x86, and on any other platform
that supports it.

Our ACPI code relies on the memory allocator and does a lot more than
determine the number of cores, so we have a simple helper that just
looks at the ACPI tables, finds the XSDT, then finds the MADT, then
counts the local apics.  We'll use this as num_cores (possibly an
overestimate).  The topology code will make sure we didn't
underestimate later in boot.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/arch/x86/topology.c
kern/drivers/dev/acpi.c
kern/include/acpi.h
kern/src/init.c

index 4f220a5..ef228dc 100644 (file)
@@ -97,15 +97,23 @@ static int find_numa_domain(int apic_id)
  * cpu_topology_info struct. */
 static void set_num_cores(void)
 {
+       int old_num_cores = num_cores;
+
        if (apics == NULL)
                return;
 
+       num_cores = 0;
        for (int i = 0; i < apics->nchildren; i++) {
                struct Apicst *temp = apics->children[i]->tbl;
 
                if (temp != NULL && temp->type == ASlapic)
                        num_cores++;
        }
+       if (num_cores < old_num_cores)
+               warn("Topology found less cores than early MADT parsing!");
+       /* Too many cores will be a problem for some data structures. */
+       if (num_cores > old_num_cores)
+               panic("Topology found more cores than early MADT parsing!");
 }
 
 /* Determine if srat has a unique numa domain compared to to all of the srat
index b891a2c..a09a355 100644 (file)
@@ -1385,7 +1385,7 @@ static void *rsdsearch(char *signature)
         * Search for the data structure signature:
         * 1) in the BIOS ROM between 0xE0000 and 0xFFFFF.
         */
-       return sigscan(KADDR(0xE0000), 0x20000, signature);
+       return sigscan(KADDR_NOCHECK(0xE0000), 0x20000, signature);
 }
 
 /*
@@ -1551,6 +1551,84 @@ static void parsersdptr(void)
        makeindex(root);
 }
 
+/* Given an xsdt, find the table matching 'sig', if any. */
+static struct Sdthdr *xsdt_find_tbl(struct Sdthdr *xsdt, const char *sig,
+                                    int addr_size)
+{
+       uint8_t *ptr_tbl;
+       size_t ptr_tbl_len;
+       physaddr_t sdt_pa;
+       struct Sdthdr *sdt;
+
+       ptr_tbl = (uint8_t*)xsdt + sizeof(struct Sdthdr);
+       ptr_tbl_len = l32get(xsdt->length) - sizeof(struct Sdthdr);
+       for (int i = 0; i < ptr_tbl_len; i += addr_size) {
+               sdt_pa = (addr_size == 8) ? l64get(ptr_tbl + i) : l32get(ptr_tbl + i);
+               sdt = KADDR_NOCHECK(sdt_pa);
+               if (memcmp(sdt->sig, sig, sizeof(sdt->sig)) == 0)
+                       return sdt;
+       }
+       return NULL;
+}
+
+/* This may be an overestimate, if some LAPICS are present but disabled */
+static int madt_get_nr_cores(struct Sdthdr *madt)
+{
+       uint8_t *p, *madt_end;
+       size_t entry_len;
+       int nr_cores = 0;
+
+       p = (uint8_t*)madt;
+       madt_end = p + l32get(madt->length);
+       for (p += 44; p < madt_end; p += entry_len) {
+               entry_len = p[1];
+               switch (p[0]) {
+               case ASlapic:
+                       nr_cores++;
+                       break;
+               default:
+                       break;
+               }
+       }
+       return nr_cores;
+}
+
+int get_early_num_cores(void)
+{
+       struct Rsdp *rsd;
+       int asize;
+       physaddr_t sdtpa;
+       struct Sdthdr *xsdt, *madt;
+       uint8_t *xsdt_buf;
+       int nr_cores;
+
+       rsd = rsdsearch("RSD PTR ");
+       assert(rsd);
+       if (rsd->revision >= 2) {
+               sdtpa = l64get(rsd->xaddr);
+               asize = 8;
+       } else {
+               sdtpa = l32get(rsd->raddr);
+               asize = 4;
+       }
+
+       xsdt = KADDR_NOCHECK(sdtpa);
+       xsdt_buf = (uint8_t*)xsdt;
+       if ((xsdt_buf[0] != 'R' && xsdt_buf[0] != 'X')
+               || memcmp(xsdt_buf + 1, "SDT", 3) != 0) {
+               panic("acpi: xsdt sig: %c%c%c%c\n",
+                      xsdt_buf[0], xsdt_buf[1], xsdt_buf[2], xsdt_buf[3]);
+       }
+       madt = xsdt_find_tbl(xsdt, "APIC", asize);
+       assert(madt);
+       nr_cores = madt_get_nr_cores(madt);
+       if (nr_cores == 0) {
+               warn("MADT parsing found 0 cores!");
+               nr_cores = 1;
+       }
+       return nr_cores;
+}
+
 /*
  * The invariant that each level in the tree has an associated
  * Atable implies that each chan can be mapped to an Atable.
index c9a340c..02b4df2 100644 (file)
@@ -509,6 +509,7 @@ struct Atable *mkatable(struct Atable *parent,
                         size_t rawsize, size_t addsize);
 struct Atable *finatable(struct Atable *t, struct slice *slice);
 struct Atable *finatable_nochildren(struct Atable *t);
+int get_early_num_cores(void);
 
 extern struct Atable *apics;
 extern struct Atable *dmar;
index 9dc2f90..b1fe12f 100644 (file)
@@ -139,6 +139,7 @@ void kernel_init(multiboot_info_t *mboot_info)
        printk("Boot Command Line: '%s'\n", boot_cmdline);
 
        exception_table_init();
+       num_cores = get_early_num_cores();
        pmem_init(multiboot_kaddr);
        kmem_cache_init();              // Sets up slab allocator
        kmalloc_init();