Correct the logical_cpus calculation in mptable_hyperthread_fixup()
[dragonfly.git] / sys / platform / pc64 / x86_64 / mp_machdep.c
index 82524e3..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;
 
@@ -234,7 +237,6 @@ int mp_nbusses;             /* # of busses */
 #ifdef APIC_IO
 int    mp_napics;              /* # of IO APICs */
 #endif
-int    boot_cpu_id;            /* designated BSP */
 vm_offset_t cpu_apic_address;
 #ifdef APIC_IO
 vm_offset_t io_apic_address[NAPICID];  /* NAPICID is more than enough */
@@ -289,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);
@@ -358,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 */
@@ -537,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");
+
+               /* examine the MP table for needed info */
+               x = mptable_pass2(&mpt);
+
+               mptable_unmap(&mpt);
+
+               /*
+                * can't process default configs till the
+                * CPU APIC is pmapped
+                */
+               if (x)
+                       mptable_default(x);
 
-       if (cpu_apic_address == 0)
-               panic("mp_enable: no local apic!\n");
+               /* post scan cleanup */
+               mptable_fix();
 
-       /* examine the MP table for needed info, uses physical addresses */
-       x = mptable_pass2(&mpt);
+               /*
+                * 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;
 
-       mptable_unmap(&mpt);
+               madt_paddr = madt_probe();
+               if (madt_paddr == 0)
+                       panic("mp_enable: madt_probe failed\n");
 
-       /* can't process default configs till the CPU APIC is pmapped */
-       if (x)
-               default_mp_table(x);
+               cpu_apic_address = madt_pass1(madt_paddr);
+               if (cpu_apic_address == 0)
+                       panic("mp_enable: no local apic (madt)!\n");
 
-       /* post scan cleanup */
-       fix_mp_table();
+               /*
+                * 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)
 
@@ -623,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;
@@ -734,9 +773,6 @@ static int lookup_bus_type  (char *name);
 /*
  * 1st pass on motherboard's Intel MP specification table.
  *
- * initializes:
- *     ncpus = 1
- *
  * determines:
  *     cpu_apic_address (common to all CPUs)
  *     io_apic_address[N]
@@ -744,6 +780,8 @@ static int lookup_bus_type  (char *name);
  *     mp_nbusses
  *     mp_napics
  *     nintrs
+ *     need_hyperthreading_fixup
+ *     logical_cpus
  */
 static void
 mptable_pass1(struct mptable_pos *mpt)
@@ -866,7 +904,7 @@ mptable_pass1(struct mptable_pos *mpt)
  * 2nd pass on motherboard's Intel MP specification table.
  *
  * sets:
- *     boot_cpu_id
+ *     logical_cpus_mask
  *     ID_TO_IO(N), phy APIC ID to log CPU/IO table
  *     CPU_TO_ID(N), logical CPU to APIC ID table
  *     IO_TO_ID(N), logical IO to APIC ID table
@@ -935,9 +973,6 @@ mptable_pass2(struct mptable_pos *mpt)
        }
 #endif
 
-       /* setup the cpu/apic mapping arrays */
-       boot_cpu_id = -1;
-
        /* record whether PIC or virtual-wire mode */
        machintr_setvar_simple(MACHINTR_VAR_IMCR_PRESENT, fps->mpfb2 & 0x80);
 
@@ -1005,7 +1040,7 @@ mptable_pass2(struct mptable_pos *mpt)
                position = (uint8_t *)position + basetable_entry_types[type].length;
        }
 
-       if (boot_cpu_id == -1)
+       if (CPU_TO_ID(0) < 0)
                panic("NO BSP found!");
 
        /* report fact that its NOT a default configuration */
@@ -1028,14 +1063,82 @@ 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,
@@ -1043,7 +1146,7 @@ mptable_hyperthread_fixup(u_int id_mask)
         * 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. */
@@ -1308,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
@@ -1508,9 +1611,18 @@ 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)
 {
+       KKASSERT(cpu > 0);
+
        /* check for usability */
        if (!(entry->cpu_flags & PROCENTRY_FLAG_EN))
                return 0;
@@ -1519,16 +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) {
-               boot_cpu_id = entry->apic_id;
-               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;
        }
 
@@ -1974,9 +2083,9 @@ 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;
+       int     ap_cpu_id, boot_cpu_id;
 #if defined(APIC_IO)
        int     io_apic_id;
        int     pin;