Correct the logical_cpus calculation in mptable_hyperthread_fixup()
[dragonfly.git] / sys / platform / pc64 / x86_64 / mp_machdep.c
index 97d4ff8..4d7883a 100644 (file)
@@ -222,6 +222,9 @@ static int need_hyperthreading_fixup;
 static u_int logical_cpus;
 u_int  logical_cpus_mask;
 
+static int madt_probe_test;
+TUNABLE_INT("hw.madt_probe_test", &madt_probe_test);
+
 /** XXX FIXME: where does this really belong, isa.h/isa.c perhaps? */
 int    current_postcode;
 
@@ -288,17 +291,18 @@ static u_int      boot_address;
 static u_int   base_memory;
 static int     mp_finish;
 
-static long    search_for_sig(u_int32_t target, int count);
 static void    mp_enable(u_int boot_addr);
 
 static int     mptable_probe(void);
+static long    mptable_search_sig(u_int32_t target, int count);
 static void    mptable_hyperthread_fixup(u_int id_mask);
 static void    mptable_pass1(struct mptable_pos *);
 static int     mptable_pass2(struct mptable_pos *);
-static void    default_mp_table(int type);
-static void    fix_mp_table(void);
+static void    mptable_default(int type);
+static void    mptable_fix(void);
 static void    mptable_map(struct mptable_pos *, vm_paddr_t);
 static void    mptable_unmap(struct mptable_pos *);
+
 #ifdef APIC_IO
 static void    setup_apic_irq_mapping(void);
 static int     apic_int_is_bus_type(int intr, int bus_type);
@@ -357,18 +361,18 @@ mptable_probe(void)
        if (ebda_addr != 0) {
                /* search first 1K of EBDA */
                target = (u_int32_t)ebda_addr;
-               if ((x = search_for_sig(target, 1024 / 4)) > 0)
+               if ((x = mptable_search_sig(target, 1024 / 4)) > 0)
                        return x;
        } else {
                /* last 1K of base memory, effective 'top of base' passed in */
                target = (u_int32_t)(base_memory - 0x400);
-               if ((x = search_for_sig(target, 1024 / 4)) > 0)
+               if ((x = mptable_search_sig(target, 1024 / 4)) > 0)
                        return x;
        }
 
        /* search the BIOS */
        target = (u_int32_t)BIOS_BASE;
-       if ((x = search_for_sig(target, BIOS_COUNT)) > 0)
+       if ((x = mptable_search_sig(target, BIOS_COUNT)) > 0)
                return x;
 
        /* nothing found */
@@ -536,36 +540,72 @@ mp_enable(u_int boot_addr)
        u_int   ux;
 #endif /* APIC_IO */
        vm_paddr_t mpfps_paddr;
-       struct mptable_pos mpt;
 
        POSTCODE(MP_ENABLE_POST);
 
-       mpfps_paddr = mptable_probe();
-       if (mpfps_paddr == 0)
-               panic("mp_enable: mptable_probe failed\n");
+       if (madt_probe_test)
+               mpfps_paddr = 0;
+       else
+               mpfps_paddr = mptable_probe();
 
-       mptable_map(&mpt, mpfps_paddr);
+       if (mpfps_paddr) {
+               struct mptable_pos mpt;
 
-       /*
-        * We can safely map physical memory into SMPpt after
-        * mptable_pass1() completes.
-        */
-       mptable_pass1(&mpt);
+               mptable_map(&mpt, mpfps_paddr);
+
+               /*
+                * We can safely map physical memory into SMPpt after
+                * mptable_pass1() completes.
+                */
+               mptable_pass1(&mpt);
+
+               if (cpu_apic_address == 0)
+                       panic("mp_enable: no local apic!\n");
 
-       if (cpu_apic_address == 0)
-               panic("mp_enable: no local apic!\n");
+               /* examine the MP table for needed info */
+               x = mptable_pass2(&mpt);
 
-       /* examine the MP table for needed info, uses physical addresses */
-       x = mptable_pass2(&mpt);
+               mptable_unmap(&mpt);
 
-       mptable_unmap(&mpt);
+               /*
+                * can't process default configs till the
+                * CPU APIC is pmapped
+                */
+               if (x)
+                       mptable_default(x);
 
-       /* can't process default configs till the CPU APIC is pmapped */
-       if (x)
-               default_mp_table(x);
+               /* post scan cleanup */
+               mptable_fix();
 
-       /* post scan cleanup */
-       fix_mp_table();
+               /*
+                * lapic not mapped yet (pmap_init is called too late)
+                */
+               lapic = pmap_mapdev_uncacheable(cpu_apic_address,
+                                               sizeof(struct LAPIC));
+       } else {
+               vm_paddr_t madt_paddr;
+               int bsp_apic_id;
+
+               madt_paddr = madt_probe();
+               if (madt_paddr == 0)
+                       panic("mp_enable: madt_probe failed\n");
+
+               cpu_apic_address = madt_pass1(madt_paddr);
+               if (cpu_apic_address == 0)
+                       panic("mp_enable: no local apic (madt)!\n");
+
+               /*
+                * lapic not mapped yet (pmap_init is called too late)
+                *
+                * XXX: where is the best place to set lapic?
+                */
+               lapic = pmap_mapdev_uncacheable(cpu_apic_address,
+                                               sizeof(struct LAPIC));
+
+               bsp_apic_id = (lapic->id & 0xff000000) >> 24;
+               if (madt_pass2(madt_paddr, bsp_apic_id))
+                       panic("mp_enable: madt_pass2 failed\n");
+       }
 
 #if defined(APIC_IO)
 
@@ -622,7 +662,7 @@ mp_enable(u_int boot_addr)
 #define MP_SIG         0x5f504d5f      /* _MP_ */
 #define NEXT(X)                ((X) += 4)
 static long
-search_for_sig(u_int32_t target, int count)
+mptable_search_sig(u_int32_t target, int count)
 {
        vm_size_t map_size;
        u_int32_t *addr;
@@ -1023,22 +1063,90 @@ mptable_pass2(struct mptable_pos *mpt)
 static void
 mptable_hyperthread_fixup(u_int id_mask)
 {
-       u_int i, id;
+       int i, id, lcpus_max;
 
-       /* Nothing to do if there is no HTT support. */
        if ((cpu_feature & CPUID_HTT) == 0)
                return;
-       logical_cpus = (cpu_procinfo & CPUID_HTT_CORES) >> 16;
-       if (logical_cpus <= 1)
+
+       lcpus_max = (cpu_procinfo & CPUID_HTT_CORES) >> 16;
+       if (lcpus_max <= 1)
                return;
 
+       if (strcmp(cpu_vendor, "GenuineIntel") == 0) {
+               /*
+                * INSTRUCTION SET REFERENCE, A-M (#253666)
+                * Page 3-181, Table 3-20
+                * "The nearest power-of-2 integer that is not smaller
+                *  than EBX[23:16] is the number of unique initial APIC
+                *  IDs reserved for addressing different logical
+                *  processors in a physical package."
+                */
+               for (i = 0; ; ++i) {
+                       if ((1 << i) >= lcpus_max) {
+                               lcpus_max = 1 << i;
+                               break;
+                       }
+               }
+       }
+
+       if (mp_naps == lcpus_max) {
+               /* We have nothing to fix */
+               return;
+       } else if (mp_naps == 1) {
+               /* XXX this may be incorrect */
+               logical_cpus = lcpus_max;
+       } else {
+               int cur, prev, dist;
+
+               /*
+                * Calculate the distances between two nearest
+                * APIC IDs.  If all such distances are same,
+                * then it is the number of missing cpus that
+                * we are going to fill later.
+                */
+               dist = cur = prev = -1;
+               for (id = 0; id < MAXCPU; ++id) {
+                       if ((id_mask & 1 << id) == 0)
+                               continue;
+
+                       cur = id;
+                       if (prev >= 0) {
+                               int new_dist = cur - prev;
+
+                               if (dist < 0)
+                                       dist = new_dist;
+
+                               /*
+                                * Make sure that all distances
+                                * between two nearest APIC IDs
+                                * are same.
+                                */
+                               if (dist != new_dist)
+                                       return;
+                       }
+                       prev = cur;
+               }
+               if (dist == 1)
+                       return;
+
+               /* Must be power of 2 */
+               if (dist & (dist - 1))
+                       return;
+
+               /* Can't exceed CPU package capacity */
+               if (dist > lcpus_max)
+                       logical_cpus = lcpus_max;
+               else
+                       logical_cpus = dist;
+       }
+
        /*
         * For each APIC ID of a CPU that is set in the mask,
         * scan the other candidate APIC ID's for this
         * physical processor.  If any of those ID's are
         * already in the table, then kill the fixup.
         */
-       for (id = 0; id <= MAXCPU; id++) {
+       for (id = 0; id < MAXCPU; id++) {
                if ((id_mask & 1 << id) == 0)
                        continue;
                /* First, make sure we are on a logical_cpus boundary. */
@@ -1303,7 +1411,7 @@ io_apic_find_int_entry(int apic, int pin)
  * parse an Intel MP specification table
  */
 static void
-fix_mp_table(void)
+mptable_fix(void)
 {
        int     x;
 #ifdef APIC_IO
@@ -1503,6 +1611,13 @@ setup_apic_irq_mapping(void)
 
 #endif
 
+void
+mp_set_cpuids(int cpu_id, int apic_id)
+{
+       CPU_TO_ID(cpu_id) = apic_id;
+       ID_TO_CPU(apic_id) = cpu_id;
+}
+
 static int
 processor_entry(proc_entry_ptr entry, int cpu)
 {
@@ -1516,15 +1631,13 @@ processor_entry(proc_entry_ptr entry, int cpu)
                panic("CPU APIC ID out of range (0..%d)", NAPICID - 1);
        /* check for BSP flag */
        if (entry->cpu_flags & PROCENTRY_FLAG_BP) {
-               CPU_TO_ID(0) = entry->apic_id;
-               ID_TO_CPU(entry->apic_id) = 0;
+               mp_set_cpuids(0, entry->apic_id);
                return 0;       /* its already been counted */
        }
 
        /* add another AP to list, if less than max number of CPUs */
        else if (cpu < MAXCPU) {
-               CPU_TO_ID(cpu) = entry->apic_id;
-               ID_TO_CPU(entry->apic_id) = cpu;
+               mp_set_cpuids(cpu, entry->apic_id);
                return 1;
        }
 
@@ -1970,7 +2083,7 @@ apic_polarity(int apic, int pin)
  * FIXME: probably not complete yet...
  */
 static void
-default_mp_table(int type)
+mptable_default(int type)
 {
        int     ap_cpu_id, boot_cpu_id;
 #if defined(APIC_IO)