]> git.saurik.com Git - apple/xnu.git/blobdiff - osfmk/i386/cpuid.c
xnu-792.22.5.tar.gz
[apple/xnu.git] / osfmk / i386 / cpuid.c
index d4879d3a36c2c4c197cd1b9f210a14b7eacec268..3fe712adb573e5c4c09b5e61b6dbd7daa8db0772 100644 (file)
 /*
  * @OSF_COPYRIGHT@
  */
-
+#include <platforms.h>
+#include <mach_kdb.h>
 #include <pexpert/pexpert.h>
 
 #include "cpuid.h"
+#if MACH_KDB
+#include <i386/db_machdep.h>
+#include <ddb/db_aout.h>
+#include <ddb/db_access.h>
+#include <ddb/db_sym.h>
+#include <ddb/db_variables.h>
+#include <ddb/db_command.h>
+#include <ddb/db_output.h>
+#include <ddb/db_expr.h>
+#endif
 
 #define min(a,b) ((a) < (b) ? (a) : (b))
+#define quad(hi,lo)    (((uint64_t)(hi)) << 32 | (lo))
+
+#define bit(n)         (1UL << (n))
+#define bitmask(h,l)   ((bit(h)|(bit(h)-1)) & ~(bit(l)-1))
+#define bitfield(x,h,l)        (((x) & bitmask(h,l)) >> l)
 
 /*
  * CPU identification routines.
@@ -44,6 +60,7 @@
 
 static unsigned int    cpuid_maxcpuid;
 
+static i386_cpu_info_t *cpuid_cpu_infop = NULL;
 static i386_cpu_info_t cpuid_cpu_info;
 
 uint32_t               cpuid_feature;          /* XXX obsolescent for compat */
@@ -78,10 +95,10 @@ cpuid_get_info(i386_cpu_info_t *info_p)
 
        /* do cpuid 0 to get vendor */
        do_cpuid(0, cpuid_result);
-       cpuid_maxcpuid = cpuid_result[0];
-       bcopy((char *)&cpuid_result[1], &info_p->cpuid_vendor[0], 4); /* ugh */
-       bcopy((char *)&cpuid_result[2], &info_p->cpuid_vendor[8], 4);
-       bcopy((char *)&cpuid_result[3], &info_p->cpuid_vendor[4], 4);
+       cpuid_maxcpuid = cpuid_result[eax];
+       bcopy((char *)&cpuid_result[ebx], &info_p->cpuid_vendor[0], 4); /* ug */
+       bcopy((char *)&cpuid_result[ecx], &info_p->cpuid_vendor[8], 4);
+       bcopy((char *)&cpuid_result[edx], &info_p->cpuid_vendor[4], 4);
        info_p->cpuid_vendor[12] = 0;
 
        /* look up vendor */
@@ -161,6 +178,8 @@ CACHE_DESC(CPUID_CACHE_UCACHE_1M,   L2U,    1*1024*1024,    32, \
        "Unified L2 cache, 1M, 4-way set associative, 32byte line size"),
 CACHE_DESC(CPUID_CACHE_UCACHE_2M,      L2U,    2*1024*1024,    32, \
        "Unified L2 cache, 2M, 4-way set associative, 32byte line size"),
+CACHE_DESC(CPUID_CACHE_UCACHE_4M,      L2U,    4*1024*1024,    64, \
+       "Unified L2 cache, 4M, 16-way set associative, 64byte line size"),
 CACHE_DESC(CPUID_CACHE_UCACHE_128K_64, L2U,    128*1024,       64, \
        "Unified L2 cache, 128K, 8-way set associative, 64byte line size"),
 CACHE_DESC(CPUID_CACHE_UCACHE_256K_64, L2U,    256*1024,       64, \
@@ -211,9 +230,12 @@ CACHE_DESC(CPUID_CACHE_NULL, Lnone, 0, 0, \
        (char *)0),
 };
 
-static const char * get_intel_model_string( i386_cpu_info_t * info_p )
+static const char * get_intel_model_string( i386_cpu_info_t * info_p, cpu_type_t* type, cpu_subtype_t* subtype)
 {
-    /* check for brand id */
+       *type = CPU_TYPE_X86;
+       *subtype = CPU_SUBTYPE_X86_ARCH1;
+
+    /* check for brand id string */
     switch(info_p->cpuid_brand) {
         case CPUID_BRAND_UNSUPPORTED:
             /* brand ID not supported; use alternate method. */
@@ -242,14 +264,13 @@ static const char * get_intel_model_string( i386_cpu_info_t * info_p )
                         default:
                             return "Unknown Intel P6 Family";
                     }
-                case CPUID_FAMILY_ITANIUM:
-                    return "Intel Itanium";
                 case CPUID_FAMILY_EXTENDED:
                     switch (info_p->cpuid_extfamily) {
                         case CPUID_EXTFAMILY_PENTIUM4:
+                           *subtype = CPU_SUBTYPE_PENTIUM_4;
                             return "Intel Pentium 4";
-                        case CPUID_EXTFAMILY_ITANIUM2:
-                            return "Intel Itanium 2";
+                                               default:
+                                   return "Unknown Intel Extended Family";
                     }
                 default:
                     return "Unknown Intel Family";
@@ -263,10 +284,11 @@ static const char * get_intel_model_string( i386_cpu_info_t * info_p )
         case CPUID_BRAND_PENTIUM_III_4:
             return "Pentium III";
         case CPUID_BRAND_PIII_XEON:
-            if (info_p->cpuid_signature == 0x6B1)
-                return "Intel Celeron";
-            else
-                return "Intel Pentium III Xeon";
+                       if (info_p->cpuid_signature == 0x6B1) {
+                               return "Intel Celeron";
+                       } else {
+                               return "Intel Pentium III Xeon";
+                       }
         case CPUID_BRAND_PENTIUM_III_M:
             return "Mobile Intel Pentium III-M";
         case CPUID_BRAND_M_CELERON_7:
@@ -276,16 +298,19 @@ static const char * get_intel_model_string( i386_cpu_info_t * info_p )
             return "Mobile Intel Celeron";
         case CPUID_BRAND_PENTIUM4_8:
         case CPUID_BRAND_PENTIUM4_9:
+           *subtype = CPU_SUBTYPE_PENTIUM_4;
             return "Intel Pentium 4";
         case CPUID_BRAND_XEON:
             return "Intel Xeon";
         case CPUID_BRAND_XEON_MP:
             return "Intel Xeon MP";
         case CPUID_BRAND_PENTIUM4_M:
-            if (info_p->cpuid_signature == 0xF13)
-                return "Intel Xeon";
-            else
-                return "Mobile Intel Pentium 4";
+                       if (info_p->cpuid_signature == 0xF13) {
+                               return "Intel Xeon";
+                       } else {
+                               *subtype = CPU_SUBTYPE_PENTIUM_4;
+                               return "Mobile Intel Pentium 4";
+                       }
         case CPUID_BRAND_CELERON_M:
             return "Intel Celeron M";
         case CPUID_BRAND_PENTIUM_M:
@@ -294,7 +319,6 @@ static const char * get_intel_model_string( i386_cpu_info_t * info_p )
         case CPUID_BRAND_MOBILE_17:
             return "Mobile Intel";
     }        
-
     return "Unknown Intel";
 }
 
@@ -353,17 +377,69 @@ static void set_intel_cache_info( i386_cpu_info_t * info_p )
         /* If we have no L2 cache, use the L1 data cache line size */
         if (info_p->cache_size[L2U] == 0)
             info_p->cache_linesize = l1d_cache_linesize;
+
+       /*
+        * Get cache sharing info if available.
+        */
+       do_cpuid(0, cpuid_result);
+       if (cpuid_result[eax] >= 4) {
+               uint32_t        reg[4];
+               uint32_t        index;
+               for (index = 0;; index++) {
+                       /*
+                        * Scan making calls for cpuid with %eax = 4
+                        * to get info about successive cache levels
+                        * until a null type is returned.
+                        */
+                       cache_type_t    type = Lnone;
+                       uint32_t        cache_type;
+                       uint32_t        cache_level;
+                       uint32_t        cache_sharing;
+
+                       reg[eax] = 4;           /* cpuid request 4 */
+                       reg[ecx] = index;       /* index starting at 0 */
+                       cpuid(reg);
+//kprintf("cpuid(4) index=%d eax=%p\n", index, reg[eax]);
+                       cache_type = bitfield(reg[eax], 4, 0);
+                       if (cache_type == 0)
+                               break;          /* done with cache info */
+                       cache_level   = bitfield(reg[eax],  7,  5);
+                       cache_sharing = bitfield(reg[eax], 25, 14);
+                       info_p->cpuid_cores_per_package = 
+                                       bitfield(reg[eax], 31, 26) + 1;
+                       switch (cache_level) {
+                       case 1:
+                               type = cache_type == 1 ? L1D :
+                                      cache_type == 2 ? L1I :
+                                                        Lnone;
+                               break;
+                       case 2:
+                               type = cache_type == 3 ? L2U :
+                                                        Lnone;
+                               break;
+                       case 3:
+                               type = cache_type == 3 ? L3U :
+                                                        Lnone;
+                       }
+                       if (type != Lnone)
+                               info_p->cache_sharing[type] = cache_sharing + 1;
+               } 
+       }
 }
 
 static void set_cpu_intel( i386_cpu_info_t * info_p )
 {
     set_cpu_generic(info_p);
     set_intel_cache_info(info_p);
-    info_p->cpuid_model_string = get_intel_model_string(info_p);
+    info_p->cpuid_model_string = get_intel_model_string(info_p, &info_p->cpuid_cpu_type, &info_p->cpuid_cpu_subtype);
 }
 
-static const char * get_amd_model_string( i386_cpu_info_t * info_p )
+static const char * get_amd_model_string( i386_cpu_info_t * info_p, cpu_type_t* type, cpu_subtype_t* subtype )
 {
+       *type = CPU_TYPE_X86;
+       *subtype = CPU_SUBTYPE_X86_ARCH1;
+
+    /* check for brand id string */
     switch (info_p->cpuid_family)
     {
         case CPUID_FAMILY_486:
@@ -444,11 +520,11 @@ static void set_amd_cache_info( i386_cpu_info_t * info_p )
     /* (ignore) */
     
     /* ECX: L1 Data Cache Information */
-    info_p->cache_size[L1D] = ((cpuid_result[2] >> 24) & 0xFF) * 1024;
-    info_p->cache_linesize = (cpuid_result[2] & 0xFF);
+    info_p->cache_size[L1D] = ((cpuid_result[ecx] >> 24) & 0xFF) * 1024;
+    info_p->cache_linesize = (cpuid_result[ecx] & 0xFF);
     
     /* EDX: L1 Instruction Cache Information */
-    info_p->cache_size[L1I] = ((cpuid_result[3] >> 24) & 0xFF) * 1024;
+    info_p->cache_size[L1I] = ((cpuid_result[edx] >> 24) & 0xFF) * 1024;
 
     /* L2 Cache Information */
     do_cpuid(0x80000006, cpuid_result);
@@ -460,16 +536,16 @@ static void set_amd_cache_info( i386_cpu_info_t * info_p )
     /* (ignore) */
     
     /* ECX: L2 Cache Information */
-    info_p->cache_size[L2U] = ((cpuid_result[2] >> 16) & 0xFFFF) * 1024;
+    info_p->cache_size[L2U] = ((cpuid_result[ecx] >> 16) & 0xFFFF) * 1024;
     if (info_p->cache_size[L2U] > 0)
-        info_p->cache_linesize = cpuid_result[2] & 0xFF;
+        info_p->cache_linesize = cpuid_result[ecx] & 0xFF;
 }
 
 static void set_cpu_amd( i386_cpu_info_t * info_p )
 {
     set_cpu_generic(info_p);
     set_amd_cache_info(info_p);
-    info_p->cpuid_model_string = get_amd_model_string(info_p);
+    info_p->cpuid_model_string = get_amd_model_string(info_p, &info_p->cpuid_cpu_type, &info_p->cpuid_cpu_subtype);
 }
 
 static void set_cpu_nsc( i386_cpu_info_t * info_p )
@@ -477,12 +553,16 @@ static void set_cpu_nsc( i386_cpu_info_t * info_p )
     set_cpu_generic(info_p);
     set_amd_cache_info(info_p);
 
-    if (info_p->cpuid_family == CPUID_FAMILY_586 && info_p->cpuid_model == CPUID_MODEL_GX1)
+    /* check for brand id string */
+    if (info_p->cpuid_family == CPUID_FAMILY_586 && info_p->cpuid_model == CPUID_MODEL_GX1) {
         info_p->cpuid_model_string = "AMD Geode GX1";
-    else if (info_p->cpuid_family == CPUID_FAMILY_586 && info_p->cpuid_model == CPUID_MODEL_GX2)
+    } else if (info_p->cpuid_family == CPUID_FAMILY_586 && info_p->cpuid_model == CPUID_MODEL_GX2) {
         info_p->cpuid_model_string = "AMD Geode GX";
-    else
+    } else {
         info_p->cpuid_model_string = "Unknown National Semiconductor";
+    }
+    info_p->cpuid_cpu_type = CPU_TYPE_X86;
+    info_p->cpuid_cpu_subtype = CPU_SUBTYPE_X86_ARCH1;
 }
 
 static void
@@ -494,7 +574,7 @@ set_cpu_generic(i386_cpu_info_t *info_p)
 
        /* get extended cpuid results */
        do_cpuid(0x80000000, cpuid_result);
-       max_extid = cpuid_result[0];
+       max_extid = cpuid_result[eax];
 
        /* check to see if we can get brand string */
        if (max_extid >= 0x80000004) {
@@ -526,15 +606,23 @@ set_cpu_generic(i386_cpu_info_t *info_p)
     
        /* get processor signature and decode */
        do_cpuid(1, cpuid_result);
-       info_p->cpuid_signature =  cpuid_result[0];
-       info_p->cpuid_stepping  =  cpuid_result[0]        & 0x0f;
-       info_p->cpuid_model     = (cpuid_result[0] >> 4)  & 0x0f;
-       info_p->cpuid_family    = (cpuid_result[0] >> 8)  & 0x0f;
-       info_p->cpuid_type      = (cpuid_result[0] >> 12) & 0x03;
-       info_p->cpuid_extmodel  = (cpuid_result[0] >> 16) & 0x0f;
-       info_p->cpuid_extfamily = (cpuid_result[0] >> 20) & 0xff;
-       info_p->cpuid_brand     =  cpuid_result[1]        & 0xff;
-       info_p->cpuid_features  =  cpuid_result[3];
+       info_p->cpuid_signature = cpuid_result[eax];
+       info_p->cpuid_stepping  = bitfield(cpuid_result[eax],  3,  0);
+       info_p->cpuid_model     = bitfield(cpuid_result[eax],  7,  4);
+       info_p->cpuid_family    = bitfield(cpuid_result[eax], 11,  8);
+       info_p->cpuid_type      = bitfield(cpuid_result[eax], 13, 12);
+       info_p->cpuid_extmodel  = bitfield(cpuid_result[eax], 19, 16);
+       info_p->cpuid_extfamily = bitfield(cpuid_result[eax], 27, 20);
+       info_p->cpuid_brand     = bitfield(cpuid_result[ebx],  7,  0);
+       info_p->cpuid_logical_per_package =
+                                 bitfield(cpuid_result[ebx], 23, 16);
+       info_p->cpuid_features  = quad(cpuid_result[ecx], cpuid_result[edx]);
+
+       if (max_extid >= 0x80000001) {
+               do_cpuid(0x80000001, cpuid_result);
+               info_p->cpuid_extfeatures =
+                               quad(cpuid_result[ecx], cpuid_result[edx]);
+       }
 
        return;
 }
@@ -547,9 +635,9 @@ set_cpu_unknown(__unused i386_cpu_info_t *info_p)
 
 
 static struct {
-       uint32_t        mask;
+       uint64_t        mask;
        const char      *name;
-} feature_names[] = {
+} feature_map[] = {
        {CPUID_FEATURE_FPU,   "FPU",},
        {CPUID_FEATURE_VME,   "VME",},
        {CPUID_FEATURE_DE,    "DE",},
@@ -578,25 +666,76 @@ static struct {
        {CPUID_FEATURE_SS,    "SS",},
        {CPUID_FEATURE_HTT,   "HTT",},
        {CPUID_FEATURE_TM,    "TM",},
+       {CPUID_FEATURE_SSE3,    "SSE3"},
+       {CPUID_FEATURE_MONITOR, "MON"},
+       {CPUID_FEATURE_DSCPL,   "DSCPL"},
+       {CPUID_FEATURE_VMX,     "VMX"},
+       {CPUID_FEATURE_SMX,     "SMX"},
+       {CPUID_FEATURE_EST,     "EST"},
+       {CPUID_FEATURE_TM2,     "TM2"},
+       {CPUID_FEATURE_MNI,     "MNI"},
+       {CPUID_FEATURE_CID,     "CID"},
+       {CPUID_FEATURE_CX16,    "CX16"},
+       {CPUID_FEATURE_xTPR,    "TPR"},
+       {0, 0}
+},
+extfeature_map[] = {
+       {CPUID_EXTFEATURE_SYSCALL, "SYSCALL"},
+       {CPUID_EXTFEATURE_XD,      "XD"},
+       {CPUID_EXTFEATURE_EM64T,   "EM64T"},
+       {CPUID_EXTFEATURE_LAHF,    "LAHF"},
        {0, 0}
 };
 
+i386_cpu_info_t        *
+cpuid_info(void)
+{
+       /* Set-up the cpuid_indo stucture lazily */
+       if (cpuid_cpu_infop == NULL) {
+               cpuid_get_info(&cpuid_cpu_info);
+               cpuid_cpu_infop = &cpuid_cpu_info;
+       }
+       return cpuid_cpu_infop;
+}
+
 char *
-cpuid_get_feature_names(uint32_t feature, char *buf, unsigned buf_len)
+cpuid_get_feature_names(uint64_t features, char *buf, unsigned buf_len)
 {
+       int     len = -1;
+       char    *p = buf;
        int     i;
-       int     len;
+
+       for (i = 0; feature_map[i].mask != 0; i++) {
+               if ((features & feature_map[i].mask) == 0)
+                       continue;
+               if (len > 0)
+                       *p++ = ' ';
+               len = min(strlen(feature_map[i].name), (buf_len-1) - (p-buf));
+               if (len == 0)
+                       break;
+               bcopy(feature_map[i].name, p, len);
+               p += len;
+       }
+       *p = '\0';
+       return buf;
+}
+
+char *
+cpuid_get_extfeature_names(uint64_t extfeatures, char *buf, unsigned buf_len)
+{
+       int     len = -1;
        char    *p = buf;
+       int     i;
 
-       for (i = 0; feature_names[i].mask != 0; i++) {
-               if ((feature & feature_names[i].mask) == 0)
+       for (i = 0; extfeature_map[i].mask != 0; i++) {
+               if ((extfeatures & extfeature_map[i].mask) == 0)
                        continue;
-               if (i > 0)
+               if (len > 0)
                        *p++ = ' ';
-               len = min(strlen(feature_names[i].name), (buf_len-1) - (p-buf));
+               len = min(strlen(extfeature_map[i].name), (buf_len-1)-(p-buf));
                if (len == 0)
                        break;
-               bcopy(feature_names[i].name, p, len);
+               bcopy(extfeature_map[i].name, p, len);
                p += len;
        }
        *p = '\0';
@@ -605,37 +744,69 @@ cpuid_get_feature_names(uint32_t feature, char *buf, unsigned buf_len)
 
 void
 cpuid_feature_display(
-       const char      *header,
-       __unused int    my_cpu)
+       const char      *header)
+{
+       char    buf[256];
+
+       kprintf("%s: %s\n", header,
+                 cpuid_get_feature_names(cpuid_features(),
+                                               buf, sizeof(buf)));
+       if (cpuid_features() & CPUID_FEATURE_HTT) {
+#define s_if_plural(n) ((n > 1) ? "s" : "")
+               kprintf("  HTT: %d core%s per package;"
+                            " %d logical cpu%s per package\n",
+                       cpuid_cpu_info.cpuid_cores_per_package,
+                       s_if_plural(cpuid_cpu_info.cpuid_cores_per_package),
+                       cpuid_cpu_info.cpuid_logical_per_package,
+                       s_if_plural(cpuid_cpu_info.cpuid_logical_per_package));
+       }
+}
+
+void
+cpuid_extfeature_display(
+       const char      *header)
 {
        char    buf[256];
 
-       printf("%s: %s\n", header,
-                 cpuid_get_feature_names(cpuid_features(), buf, sizeof(buf)));
+       kprintf("%s: %s\n", header,
+                 cpuid_get_extfeature_names(cpuid_extfeatures(),
+                                               buf, sizeof(buf)));
 }
 
 void
 cpuid_cpu_display(
-       const char      *header,
-       __unused int    my_cpu)
+       const char      *header)
 {
-    if (cpuid_cpu_info.cpuid_brand_string[0] != '\0') {
-       printf("%s: %s\n", header,
-               cpuid_cpu_info.cpuid_brand_string);
+    if (cpuid_info()->cpuid_brand_string[0] != '\0') {
+       kprintf("%s: %s\n", header, cpuid_cpu_info.cpuid_brand_string);
     }
 }
 
 unsigned int
 cpuid_family(void)
 {
-       return cpuid_cpu_info.cpuid_family;
+       return cpuid_info()->cpuid_family;
 }
 
-unsigned int
+cpu_type_t
+cpuid_cputype(void)
+{
+       return cpuid_info()->cpuid_cpu_type;
+}
+
+cpu_subtype_t
+cpuid_cpusubtype(void)
+{
+       return cpuid_info()->cpuid_cpu_subtype;
+}
+
+uint64_t
 cpuid_features(void)
 {
        static int checked = 0;
        char    fpu_arg[16] = { 0 };
+
+       (void) cpuid_info();
        if (!checked) {
                    /* check for boot-time fpu limitations */
                        if (PE_parse_boot_arg("_fpu", &fpu_arg[0])) {
@@ -653,17 +824,59 @@ cpuid_features(void)
        return cpuid_cpu_info.cpuid_features;
 }
 
-i386_cpu_info_t        *
-cpuid_info(void)
+uint64_t
+cpuid_extfeatures(void)
 {
-       return &cpuid_cpu_info;
+       return cpuid_info()->cpuid_extfeatures;
 }
-
-/* XXX for temporary compatibility */
 void
-set_cpu_model(void)
+cpuid_set_info(void)
 {
        cpuid_get_info(&cpuid_cpu_info);
-       cpuid_feature = cpuid_cpu_info.cpuid_features;  /* XXX compat */
 }
 
+#if MACH_KDB
+
+/*
+ *     Display the cpuid
+ * *           
+ *     cp
+ */
+void 
+db_cpuid(__unused db_expr_t addr,
+        __unused int have_addr,
+        __unused db_expr_t count,
+        __unused char *modif)
+{
+
+       uint32_t        i, mid;
+       uint32_t        cpid[4];
+
+       do_cpuid(0, cpid);      /* Get the first cpuid which is the number of
+                                * basic ids */
+       db_printf("%08X - %08X %08X %08X %08X\n",
+               0, cpid[eax], cpid[ebx], cpid[ecx], cpid[edx]);
+
+       mid = cpid[eax];        /* Set the number */
+       for (i = 1; i <= mid; i++) {    /* Dump 'em out */
+               do_cpuid(i, cpid);      /* Get the next */
+               db_printf("%08X - %08X %08X %08X %08X\n",
+                       i, cpid[eax], cpid[ebx], cpid[ecx], cpid[edx]);
+       }
+       db_printf("\n");
+
+       do_cpuid(0x80000000, cpid);     /* Get the first extended cpuid which
+                                        * is the number of extended ids */
+       db_printf("%08X - %08X %08X %08X %08X\n",
+               0x80000000, cpid[eax], cpid[ebx], cpid[ecx], cpid[edx]);
+
+       mid = cpid[eax];        /* Set the number */
+       for (i = 0x80000001; i <= mid; i++) {   /* Dump 'em out */
+               do_cpuid(i, cpid);      /* Get the next */
+               db_printf("%08X - %08X %08X %08X %08X\n",
+                       i, cpid[eax], cpid[ebx], cpid[ecx], cpid[edx]);
+       }
+}
+
+#endif