Index: sys/amd64/amd64/mp_machdep.c =================================================================== RCS file: /home/ncvs/src/sys/amd64/amd64/mp_machdep.c,v retrieving revision 1.242.2.10 diff -u -p -r1.242.2.10 mp_machdep.c --- sys/amd64/amd64/mp_machdep.c 1 May 2005 05:34:45 -0000 1.242.2.10 +++ sys/amd64/amd64/mp_machdep.c 12 May 2005 15:22:54 -0000 @@ -142,6 +142,9 @@ static int start_ap(int apic_id); static void release_aps(void *dummy); static int hlt_logical_cpus; +static u_int hyperthreading_cpus; +static cpumask_t hyperthreading_cpus_mask; +static int hyperthreading_allowed; static struct sysctl_ctx_list logical_cpu_clist; static u_int bootMP_size; @@ -301,6 +304,7 @@ void cpu_mp_start(void) { int i; + u_int threads_per_cache, p[4]; /* Initialize the logical ID to APIC ID table. */ for (i = 0; i < MAXCPU; i++) { @@ -340,6 +344,48 @@ cpu_mp_start(void) if (cpu_feature & CPUID_HTT) logical_cpus = (cpu_procinfo & CPUID_HTT_CORES) >> 16; + /* + * Work out if hyperthreading is *really* enabled. This + * is made really ugly by the fact that processors lie: Dual + * core processors claim to be hyperthreaded even when they're + * not, presumably because they want to be treated the same + * way as HTT with respect to per-cpu software licensing. + * At the time of writing (May 12, 2005) the only hyperthreaded + * cpus are from Intel, and Intel's dual-core processors can be + * identified via the "deterministic cache parameters" cpuid + * calls. + */ + /* + * First determine if this is an Intel processor which claims + * to have hyperthreading support. + */ + if ((cpu_feature & CPUID_HTT) && + (strcmp(cpu_vendor, "GenuineIntel") == 0)) { + /* + * If the "deterministic cache parameters" cpuid calls + * are available, use them. + */ + if (cpu_high >= 4) { + /* Ask the processor about up to 32 caches. */ + for (i = 0; i < 32; i++) { + cpuid_count(4, i, p); + threads_per_cache = ((p[0] & 0x3ffc000) >> 14) + 1; + if (hyperthreading_cpus < threads_per_cache) + hyperthreading_cpus = threads_per_cache; + if ((p[0] & 0x1f) == 0) + break; + } + } + + /* + * If the deterministic cache parameters are not + * available, or if no caches were reported to exist, + * just accept what the HTT flag indicated. + */ + if (hyperthreading_cpus == 0) + hyperthreading_cpus = logical_cpus; + } + set_logical_apic_ids(); } @@ -474,6 +520,11 @@ init_secondary(void) if (logical_cpus > 1 && PCPU_GET(apic_id) % logical_cpus != 0) logical_cpus_mask |= PCPU_GET(cpumask); + /* Determine if we are a hyperthread. */ + if (hyperthreading_cpus > 1 && + PCPU_GET(apic_id) % hyperthreading_cpus != 0) + hyperthreading_cpus_mask |= PCPU_GET(cpumask); + /* Build our map of 'other' CPUs. */ PCPU_SET(other_cpus, all_cpus & ~PCPU_GET(cpumask)); @@ -1148,6 +1199,9 @@ sysctl_hlt_cpus(SYSCTL_HANDLER_ARGS) else hlt_logical_cpus = 0; + if (! hyperthreading_allowed) + mask |= hyperthreading_cpus_mask; + if ((mask & all_cpus) == all_cpus) mask &= ~(1<<0); hlt_cpus_mask = mask; @@ -1172,6 +1226,9 @@ sysctl_hlt_logical_cpus(SYSCTL_HANDLER_A else hlt_cpus_mask &= ~logical_cpus_mask; + if (! hyperthreading_allowed) + hlt_cpus_mask |= hyperthreading_cpus_mask; + if ((hlt_cpus_mask & all_cpus) == all_cpus) hlt_cpus_mask &= ~(1<<0); @@ -1179,6 +1236,34 @@ sysctl_hlt_logical_cpus(SYSCTL_HANDLER_A return (error); } +static int +sysctl_hyperthreading_allowed(SYSCTL_HANDLER_ARGS) +{ + int allowed, error; + + allowed = hyperthreading_allowed; + error = sysctl_handle_int(oidp, &allowed, 0, req); + if (error || !req->newptr) + return (error); + + if (allowed) + hlt_cpus_mask &= ~hyperthreading_cpus_mask; + else + hlt_cpus_mask |= hyperthreading_cpus_mask; + + if (logical_cpus_mask != 0 && + (hlt_cpus_mask & logical_cpus_mask) == logical_cpus_mask) + hlt_logical_cpus = 1; + else + hlt_logical_cpus = 0; + + if ((hlt_cpus_mask & all_cpus) == all_cpus) + hlt_cpus_mask &= ~(1<<0); + + hyperthreading_allowed = allowed; + return (error); +} + static void cpu_hlt_setup(void *dummy __unused) { @@ -1198,6 +1283,22 @@ cpu_hlt_setup(void *dummy __unused) if (hlt_logical_cpus) hlt_cpus_mask |= logical_cpus_mask; + + /* + * If necessary for security purposes, force + * hyperthreading off, regardless of the value + * of hlt_logical_cpus. + */ + if (hyperthreading_cpus_mask) { + TUNABLE_INT_FETCH("machdep.hyperthreading_allowed", + &hyperthreading_allowed); + SYSCTL_ADD_PROC(&logical_cpu_clist, + SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, + "hyperthreading_allowed", CTLTYPE_INT|CTLFLAG_RW, + 0, 0, sysctl_hyperthreading_allowed, "IU", ""); + if (! hyperthreading_allowed) + hlt_cpus_mask |= hyperthreading_cpus_mask; + } } } SYSINIT(cpu_hlt, SI_SUB_SMP, SI_ORDER_ANY, cpu_hlt_setup, NULL); Index: sys/amd64/include/cpufunc.h =================================================================== RCS file: /home/ncvs/src/sys/amd64/include/cpufunc.h,v retrieving revision 1.145 diff -u -p -r1.145 cpufunc.h --- sys/amd64/include/cpufunc.h 30 Jul 2004 16:44:29 -0000 1.145 +++ sys/amd64/include/cpufunc.h 12 May 2005 15:22:55 -0000 @@ -110,6 +110,14 @@ do_cpuid(u_int ax, u_int *p) } static __inline void +cpuid_count(u_int ax, u_int cx, u_int *p) +{ + __asm __volatile("cpuid" + : "=a" (p[0]), "=b" (p[1]), "=c" (p[2]), "=d" (p[3]) + : "0" (ax), "c" (cx)); +} + +static __inline void enable_intr(void) { __asm __volatile("sti"); Index: sys/i386/i386/mp_machdep.c =================================================================== RCS file: /home/ncvs/src/sys/i386/i386/mp_machdep.c,v retrieving revision 1.235.2.9 diff -u -p -r1.235.2.9 mp_machdep.c --- sys/i386/i386/mp_machdep.c 1 May 2005 05:34:46 -0000 1.235.2.9 +++ sys/i386/i386/mp_machdep.c 12 May 2005 15:22:55 -0000 @@ -217,6 +217,9 @@ static int start_ap(int apic_id); static void release_aps(void *dummy); static int hlt_logical_cpus; +static u_int hyperthreading_cpus; +static cpumask_t hyperthreading_cpus_mask; +static int hyperthreading_allowed; static struct sysctl_ctx_list logical_cpu_clist; static void @@ -353,6 +356,7 @@ void cpu_mp_start(void) { int i; + u_int threads_per_cache, p[4]; POSTCODE(MP_START_POST); @@ -404,6 +408,48 @@ cpu_mp_start(void) if (cpu_feature & CPUID_HTT) logical_cpus = (cpu_procinfo & CPUID_HTT_CORES) >> 16; + /* + * Work out if hyperthreading is *really* enabled. This + * is made really ugly by the fact that processors lie: Dual + * core processors claim to be hyperthreaded even when they're + * not, presumably because they want to be treated the same + * way as HTT with respect to per-cpu software licensing. + * At the time of writing (May 12, 2005) the only hyperthreaded + * cpus are from Intel, and Intel's dual-core processors can be + * identified via the "deterministic cache parameters" cpuid + * calls. + */ + /* + * First determine if this is an Intel processor which claims + * to have hyperthreading support. + */ + if ((cpu_feature & CPUID_HTT) && + (strcmp(cpu_vendor, "GenuineIntel") == 0)) { + /* + * If the "deterministic cache parameters" cpuid calls + * are available, use them. + */ + if (cpu_high >= 4) { + /* Ask the processor about up to 32 caches. */ + for (i = 0; i < 32; i++) { + cpuid_count(4, i, p); + threads_per_cache = ((p[0] & 0x3ffc000) >> 14) + 1; + if (hyperthreading_cpus < threads_per_cache) + hyperthreading_cpus = threads_per_cache; + if ((p[0] & 0x1f) == 0) + break; + } + } + + /* + * If the deterministic cache parameters are not + * available, or if no caches were reported to exist, + * just accept what the HTT flag indicated. + */ + if (hyperthreading_cpus == 0) + hyperthreading_cpus = logical_cpus; + } + set_logical_apic_ids(); } @@ -539,6 +585,11 @@ init_secondary(void) if (logical_cpus > 1 && PCPU_GET(apic_id) % logical_cpus != 0) logical_cpus_mask |= PCPU_GET(cpumask); + /* Determine if we are a hyperthread. */ + if (hyperthreading_cpus > 1 && + PCPU_GET(apic_id) % hyperthreading_cpus != 0) + hyperthreading_cpus_mask |= PCPU_GET(cpumask); + /* Build our map of 'other' CPUs. */ PCPU_SET(other_cpus, all_cpus & ~PCPU_GET(cpumask)); @@ -1368,6 +1419,9 @@ sysctl_hlt_cpus(SYSCTL_HANDLER_ARGS) else hlt_logical_cpus = 0; + if (! hyperthreading_allowed) + mask |= hyperthreading_cpus_mask; + if ((mask & all_cpus) == all_cpus) mask &= ~(1<<0); hlt_cpus_mask = mask; @@ -1392,6 +1446,9 @@ sysctl_hlt_logical_cpus(SYSCTL_HANDLER_A else hlt_cpus_mask &= ~logical_cpus_mask; + if (! hyperthreading_allowed) + hlt_cpus_mask |= hyperthreading_cpus_mask; + if ((hlt_cpus_mask & all_cpus) == all_cpus) hlt_cpus_mask &= ~(1<<0); @@ -1399,6 +1456,34 @@ sysctl_hlt_logical_cpus(SYSCTL_HANDLER_A return (error); } +static int +sysctl_hyperthreading_allowed(SYSCTL_HANDLER_ARGS) +{ + int allowed, error; + + allowed = hyperthreading_allowed; + error = sysctl_handle_int(oidp, &allowed, 0, req); + if (error || !req->newptr) + return (error); + + if (allowed) + hlt_cpus_mask &= ~hyperthreading_cpus_mask; + else + hlt_cpus_mask |= hyperthreading_cpus_mask; + + if (logical_cpus_mask != 0 && + (hlt_cpus_mask & logical_cpus_mask) == logical_cpus_mask) + hlt_logical_cpus = 1; + else + hlt_logical_cpus = 0; + + if ((hlt_cpus_mask & all_cpus) == all_cpus) + hlt_cpus_mask &= ~(1<<0); + + hyperthreading_allowed = allowed; + return (error); +} + static void cpu_hlt_setup(void *dummy __unused) { @@ -1418,6 +1503,22 @@ cpu_hlt_setup(void *dummy __unused) if (hlt_logical_cpus) hlt_cpus_mask |= logical_cpus_mask; + + /* + * If necessary for security purposes, force + * hyperthreading off, regardless of the value + * of hlt_logical_cpus. + */ + if (hyperthreading_cpus_mask) { + TUNABLE_INT_FETCH("machdep.hyperthreading_allowed", + &hyperthreading_allowed); + SYSCTL_ADD_PROC(&logical_cpu_clist, + SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, + "hyperthreading_allowed", CTLTYPE_INT|CTLFLAG_RW, + 0, 0, sysctl_hyperthreading_allowed, "IU", ""); + if (! hyperthreading_allowed) + hlt_cpus_mask |= hyperthreading_cpus_mask; + } } } SYSINIT(cpu_hlt, SI_SUB_SMP, SI_ORDER_ANY, cpu_hlt_setup, NULL); Index: sys/i386/include/cpufunc.h =================================================================== RCS file: /home/ncvs/src/sys/i386/include/cpufunc.h,v retrieving revision 1.142 diff -u -p -r1.142 cpufunc.h --- sys/i386/include/cpufunc.h 7 Apr 2004 20:46:05 -0000 1.142 +++ sys/i386/include/cpufunc.h 12 May 2005 15:22:55 -0000 @@ -89,6 +89,14 @@ do_cpuid(u_int ax, u_int *p) } static __inline void +cpuid_count(u_int ax, u_int cx, u_int *p) +{ + __asm __volatile("cpuid" + : "=a" (p[0]), "=b" (p[1]), "=c" (p[2]), "=d" (p[3]) + : "0" (ax), "c" (cx)); +} + +static __inline void enable_intr(void) { __asm __volatile("sti");