Home | History | Annotate | Download | only in src
      1 // Copyright 2013 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "src/cpu.h"
      6 
      7 #if V8_LIBC_MSVCRT
      8 #include <intrin.h>  // __cpuid()
      9 #endif
     10 #if V8_OS_POSIX
     11 #include <unistd.h>  // sysconf()
     12 #endif
     13 #if V8_OS_QNX
     14 #include <sys/syspage.h>  // cpuinfo
     15 #endif
     16 
     17 #include <ctype.h>
     18 #include <limits.h>
     19 #include <stdio.h>
     20 #include <stdlib.h>
     21 #include <string.h>
     22 #include <algorithm>
     23 
     24 #include "src/checks.h"
     25 #if V8_OS_WIN
     26 #include "src/base/win32-headers.h"
     27 #endif
     28 
     29 namespace v8 {
     30 namespace internal {
     31 
     32 #if V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64
     33 
     34 // Define __cpuid() for non-MSVC libraries.
     35 #if !V8_LIBC_MSVCRT
     36 
     37 static V8_INLINE void __cpuid(int cpu_info[4], int info_type) {
     38 #if defined(__i386__) && defined(__pic__)
     39   // Make sure to preserve ebx, which contains the pointer
     40   // to the GOT in case we're generating PIC.
     41   __asm__ volatile (
     42     "mov %%ebx, %%edi\n\t"
     43     "cpuid\n\t"
     44     "xchg %%edi, %%ebx\n\t"
     45     : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
     46     : "a"(info_type)
     47   );
     48 #else
     49   __asm__ volatile (
     50     "cpuid \n\t"
     51     : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
     52     : "a"(info_type)
     53   );
     54 #endif  // defined(__i386__) && defined(__pic__)
     55 }
     56 
     57 #endif  // !V8_LIBC_MSVCRT
     58 
     59 #elif V8_HOST_ARCH_ARM || V8_HOST_ARCH_ARM64 || V8_HOST_ARCH_MIPS
     60 
     61 #if V8_OS_LINUX
     62 
     63 #if V8_HOST_ARCH_ARM
     64 
     65 // See <uapi/asm/hwcap.h> kernel header.
     66 /*
     67  * HWCAP flags - for elf_hwcap (in kernel) and AT_HWCAP
     68  */
     69 #define HWCAP_SWP (1 << 0)
     70 #define HWCAP_HALF  (1 << 1)
     71 #define HWCAP_THUMB (1 << 2)
     72 #define HWCAP_26BIT (1 << 3)  /* Play it safe */
     73 #define HWCAP_FAST_MULT (1 << 4)
     74 #define HWCAP_FPA (1 << 5)
     75 #define HWCAP_VFP (1 << 6)
     76 #define HWCAP_EDSP  (1 << 7)
     77 #define HWCAP_JAVA  (1 << 8)
     78 #define HWCAP_IWMMXT  (1 << 9)
     79 #define HWCAP_CRUNCH  (1 << 10)
     80 #define HWCAP_THUMBEE (1 << 11)
     81 #define HWCAP_NEON  (1 << 12)
     82 #define HWCAP_VFPv3 (1 << 13)
     83 #define HWCAP_VFPv3D16  (1 << 14) /* also set for VFPv4-D16 */
     84 #define HWCAP_TLS (1 << 15)
     85 #define HWCAP_VFPv4 (1 << 16)
     86 #define HWCAP_IDIVA (1 << 17)
     87 #define HWCAP_IDIVT (1 << 18)
     88 #define HWCAP_VFPD32  (1 << 19) /* set if VFP has 32 regs (not 16) */
     89 #define HWCAP_IDIV  (HWCAP_IDIVA | HWCAP_IDIVT)
     90 #define HWCAP_LPAE  (1 << 20)
     91 
     92 #define AT_HWCAP 16
     93 
     94 // Read the ELF HWCAP flags by parsing /proc/self/auxv.
     95 static uint32_t ReadELFHWCaps() {
     96   uint32_t result = 0;
     97   FILE* fp = fopen("/proc/self/auxv", "r");
     98   if (fp != NULL) {
     99     struct { uint32_t tag; uint32_t value; } entry;
    100     for (;;) {
    101       size_t n = fread(&entry, sizeof(entry), 1, fp);
    102       if (n == 0 || (entry.tag == 0 && entry.value == 0)) {
    103         break;
    104       }
    105       if (entry.tag == AT_HWCAP) {
    106         result = entry.value;
    107         break;
    108       }
    109     }
    110     fclose(fp);
    111   }
    112   return result;
    113 }
    114 
    115 #endif  // V8_HOST_ARCH_ARM
    116 
    117 // Extract the information exposed by the kernel via /proc/cpuinfo.
    118 class CPUInfo V8_FINAL BASE_EMBEDDED {
    119  public:
    120   CPUInfo() : datalen_(0) {
    121     // Get the size of the cpuinfo file by reading it until the end. This is
    122     // required because files under /proc do not always return a valid size
    123     // when using fseek(0, SEEK_END) + ftell(). Nor can the be mmap()-ed.
    124     static const char PATHNAME[] = "/proc/cpuinfo";
    125     FILE* fp = fopen(PATHNAME, "r");
    126     if (fp != NULL) {
    127       for (;;) {
    128         char buffer[256];
    129         size_t n = fread(buffer, 1, sizeof(buffer), fp);
    130         if (n == 0) {
    131           break;
    132         }
    133         datalen_ += n;
    134       }
    135       fclose(fp);
    136     }
    137 
    138     // Read the contents of the cpuinfo file.
    139     data_ = new char[datalen_ + 1];
    140     fp = fopen(PATHNAME, "r");
    141     if (fp != NULL) {
    142       for (size_t offset = 0; offset < datalen_; ) {
    143         size_t n = fread(data_ + offset, 1, datalen_ - offset, fp);
    144         if (n == 0) {
    145           break;
    146         }
    147         offset += n;
    148       }
    149       fclose(fp);
    150     }
    151 
    152     // Zero-terminate the data.
    153     data_[datalen_] = '\0';
    154   }
    155 
    156   ~CPUInfo() {
    157     delete[] data_;
    158   }
    159 
    160   // Extract the content of a the first occurence of a given field in
    161   // the content of the cpuinfo file and return it as a heap-allocated
    162   // string that must be freed by the caller using delete[].
    163   // Return NULL if not found.
    164   char* ExtractField(const char* field) const {
    165     ASSERT(field != NULL);
    166 
    167     // Look for first field occurence, and ensure it starts the line.
    168     size_t fieldlen = strlen(field);
    169     char* p = data_;
    170     for (;;) {
    171       p = strstr(p, field);
    172       if (p == NULL) {
    173         return NULL;
    174       }
    175       if (p == data_ || p[-1] == '\n') {
    176         break;
    177       }
    178       p += fieldlen;
    179     }
    180 
    181     // Skip to the first colon followed by a space.
    182     p = strchr(p + fieldlen, ':');
    183     if (p == NULL || !isspace(p[1])) {
    184       return NULL;
    185     }
    186     p += 2;
    187 
    188     // Find the end of the line.
    189     char* q = strchr(p, '\n');
    190     if (q == NULL) {
    191       q = data_ + datalen_;
    192     }
    193 
    194     // Copy the line into a heap-allocated buffer.
    195     size_t len = q - p;
    196     char* result = new char[len + 1];
    197     if (result != NULL) {
    198       memcpy(result, p, len);
    199       result[len] = '\0';
    200     }
    201     return result;
    202   }
    203 
    204  private:
    205   char* data_;
    206   size_t datalen_;
    207 };
    208 
    209 #if V8_HOST_ARCH_ARM || V8_HOST_ARCH_MIPS
    210 
    211 // Checks that a space-separated list of items contains one given 'item'.
    212 static bool HasListItem(const char* list, const char* item) {
    213   ssize_t item_len = strlen(item);
    214   const char* p = list;
    215   if (p != NULL) {
    216     while (*p != '\0') {
    217       // Skip whitespace.
    218       while (isspace(*p)) ++p;
    219 
    220       // Find end of current list item.
    221       const char* q = p;
    222       while (*q != '\0' && !isspace(*q)) ++q;
    223 
    224       if (item_len == q - p && memcmp(p, item, item_len) == 0) {
    225         return true;
    226       }
    227 
    228       // Skip to next item.
    229       p = q;
    230     }
    231   }
    232   return false;
    233 }
    234 
    235 #endif  // V8_HOST_ARCH_ARM || V8_HOST_ARCH_MIPS
    236 
    237 #endif  // V8_OS_LINUX
    238 
    239 #endif  // V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64
    240 
    241 CPU::CPU() : stepping_(0),
    242              model_(0),
    243              ext_model_(0),
    244              family_(0),
    245              ext_family_(0),
    246              type_(0),
    247              implementer_(0),
    248              architecture_(0),
    249              part_(0),
    250              has_fpu_(false),
    251              has_cmov_(false),
    252              has_sahf_(false),
    253              has_mmx_(false),
    254              has_sse_(false),
    255              has_sse2_(false),
    256              has_sse3_(false),
    257              has_ssse3_(false),
    258              has_sse41_(false),
    259              has_sse42_(false),
    260              has_idiva_(false),
    261              has_neon_(false),
    262              has_thumb2_(false),
    263              has_vfp_(false),
    264              has_vfp3_(false),
    265              has_vfp3_d32_(false) {
    266   memcpy(vendor_, "Unknown", 8);
    267 #if V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64
    268   int cpu_info[4];
    269 
    270   // __cpuid with an InfoType argument of 0 returns the number of
    271   // valid Ids in CPUInfo[0] and the CPU identification string in
    272   // the other three array elements. The CPU identification string is
    273   // not in linear order. The code below arranges the information
    274   // in a human readable form. The human readable order is CPUInfo[1] |
    275   // CPUInfo[3] | CPUInfo[2]. CPUInfo[2] and CPUInfo[3] are swapped
    276   // before using memcpy to copy these three array elements to cpu_string.
    277   __cpuid(cpu_info, 0);
    278   unsigned num_ids = cpu_info[0];
    279   std::swap(cpu_info[2], cpu_info[3]);
    280   memcpy(vendor_, cpu_info + 1, 12);
    281   vendor_[12] = '\0';
    282 
    283   // Interpret CPU feature information.
    284   if (num_ids > 0) {
    285     __cpuid(cpu_info, 1);
    286     stepping_ = cpu_info[0] & 0xf;
    287     model_ = ((cpu_info[0] >> 4) & 0xf) + ((cpu_info[0] >> 12) & 0xf0);
    288     family_ = (cpu_info[0] >> 8) & 0xf;
    289     type_ = (cpu_info[0] >> 12) & 0x3;
    290     ext_model_ = (cpu_info[0] >> 16) & 0xf;
    291     ext_family_ = (cpu_info[0] >> 20) & 0xff;
    292     has_fpu_ = (cpu_info[3] & 0x00000001) != 0;
    293     has_cmov_ = (cpu_info[3] & 0x00008000) != 0;
    294     has_mmx_ = (cpu_info[3] & 0x00800000) != 0;
    295     has_sse_ = (cpu_info[3] & 0x02000000) != 0;
    296     has_sse2_ = (cpu_info[3] & 0x04000000) != 0;
    297     has_sse3_ = (cpu_info[2] & 0x00000001) != 0;
    298     has_ssse3_ = (cpu_info[2] & 0x00000200) != 0;
    299     has_sse41_ = (cpu_info[2] & 0x00080000) != 0;
    300     has_sse42_ = (cpu_info[2] & 0x00100000) != 0;
    301   }
    302 
    303 #if V8_HOST_ARCH_IA32
    304   // SAHF is always available in compat/legacy mode,
    305   has_sahf_ = true;
    306 #else
    307   // Query extended IDs.
    308   __cpuid(cpu_info, 0x80000000);
    309   unsigned num_ext_ids = cpu_info[0];
    310 
    311   // Interpret extended CPU feature information.
    312   if (num_ext_ids > 0x80000000) {
    313     __cpuid(cpu_info, 0x80000001);
    314     // SAHF must be probed in long mode.
    315     has_sahf_ = (cpu_info[2] & 0x00000001) != 0;
    316   }
    317 #endif
    318 
    319 #elif V8_HOST_ARCH_ARM
    320 
    321 #if V8_OS_LINUX
    322 
    323   CPUInfo cpu_info;
    324 
    325   // Extract implementor from the "CPU implementer" field.
    326   char* implementer = cpu_info.ExtractField("CPU implementer");
    327   if (implementer != NULL) {
    328     char* end ;
    329     implementer_ = strtol(implementer, &end, 0);
    330     if (end == implementer) {
    331       implementer_ = 0;
    332     }
    333     delete[] implementer;
    334   }
    335 
    336   // Extract part number from the "CPU part" field.
    337   char* part = cpu_info.ExtractField("CPU part");
    338   if (part != NULL) {
    339     char* end ;
    340     part_ = strtol(part, &end, 0);
    341     if (end == part) {
    342       part_ = 0;
    343     }
    344     delete[] part;
    345   }
    346 
    347   // Extract architecture from the "CPU Architecture" field.
    348   // The list is well-known, unlike the the output of
    349   // the 'Processor' field which can vary greatly.
    350   // See the definition of the 'proc_arch' array in
    351   // $KERNEL/arch/arm/kernel/setup.c and the 'c_show' function in
    352   // same file.
    353   char* architecture = cpu_info.ExtractField("CPU architecture");
    354   if (architecture != NULL) {
    355     char* end;
    356     architecture_ = strtol(architecture, &end, 10);
    357     if (end == architecture) {
    358       architecture_ = 0;
    359     }
    360     delete[] architecture;
    361 
    362     // Unfortunately, it seems that certain ARMv6-based CPUs
    363     // report an incorrect architecture number of 7!
    364     //
    365     // See http://code.google.com/p/android/issues/detail?id=10812
    366     //
    367     // We try to correct this by looking at the 'elf_format'
    368     // field reported by the 'Processor' field, which is of the
    369     // form of "(v7l)" for an ARMv7-based CPU, and "(v6l)" for
    370     // an ARMv6-one. For example, the Raspberry Pi is one popular
    371     // ARMv6 device that reports architecture 7.
    372     if (architecture_ == 7) {
    373       char* processor = cpu_info.ExtractField("Processor");
    374       if (HasListItem(processor, "(v6l)")) {
    375         architecture_ = 6;
    376       }
    377       delete[] processor;
    378     }
    379   }
    380 
    381   // Try to extract the list of CPU features from ELF hwcaps.
    382   uint32_t hwcaps = ReadELFHWCaps();
    383   if (hwcaps != 0) {
    384     has_idiva_ = (hwcaps & HWCAP_IDIVA) != 0;
    385     has_neon_ = (hwcaps & HWCAP_NEON) != 0;
    386     has_vfp_ = (hwcaps & HWCAP_VFP) != 0;
    387     has_vfp3_ = (hwcaps & (HWCAP_VFPv3 | HWCAP_VFPv3D16 | HWCAP_VFPv4)) != 0;
    388     has_vfp3_d32_ = (has_vfp3_ && ((hwcaps & HWCAP_VFPv3D16) == 0 ||
    389                                    (hwcaps & HWCAP_VFPD32) != 0));
    390   } else {
    391     // Try to fallback to "Features" CPUInfo field.
    392     char* features = cpu_info.ExtractField("Features");
    393     has_idiva_ = HasListItem(features, "idiva");
    394     has_neon_ = HasListItem(features, "neon");
    395     has_thumb2_ = HasListItem(features, "thumb2");
    396     has_vfp_ = HasListItem(features, "vfp");
    397     if (HasListItem(features, "vfpv3d16")) {
    398       has_vfp3_ = true;
    399     } else if (HasListItem(features, "vfpv3")) {
    400       has_vfp3_ = true;
    401       has_vfp3_d32_ = true;
    402     }
    403     delete[] features;
    404   }
    405 
    406   // Some old kernels will report vfp not vfpv3. Here we make an attempt
    407   // to detect vfpv3 by checking for vfp *and* neon, since neon is only
    408   // available on architectures with vfpv3. Checking neon on its own is
    409   // not enough as it is possible to have neon without vfp.
    410   if (has_vfp_ && has_neon_) {
    411     has_vfp3_ = true;
    412   }
    413 
    414   // VFPv3 implies ARMv7, see ARM DDI 0406B, page A1-6.
    415   if (architecture_ < 7 && has_vfp3_) {
    416     architecture_ = 7;
    417   }
    418 
    419   // ARMv7 implies Thumb2.
    420   if (architecture_ >= 7) {
    421     has_thumb2_ = true;
    422   }
    423 
    424   // The earliest architecture with Thumb2 is ARMv6T2.
    425   if (has_thumb2_ && architecture_ < 6) {
    426     architecture_ = 6;
    427   }
    428 
    429   // We don't support any FPUs other than VFP.
    430   has_fpu_ = has_vfp_;
    431 
    432 #elif V8_OS_QNX
    433 
    434   uint32_t cpu_flags = SYSPAGE_ENTRY(cpuinfo)->flags;
    435   if (cpu_flags & ARM_CPU_FLAG_V7) {
    436     architecture_ = 7;
    437     has_thumb2_ = true;
    438   } else if (cpu_flags & ARM_CPU_FLAG_V6) {
    439     architecture_ = 6;
    440     // QNX doesn't say if Thumb2 is available.
    441     // Assume false for the architectures older than ARMv7.
    442   }
    443   ASSERT(architecture_ >= 6);
    444   has_fpu_ = (cpu_flags & CPU_FLAG_FPU) != 0;
    445   has_vfp_ = has_fpu_;
    446   if (cpu_flags & ARM_CPU_FLAG_NEON) {
    447     has_neon_ = true;
    448     has_vfp3_ = has_vfp_;
    449 #ifdef ARM_CPU_FLAG_VFP_D32
    450     has_vfp3_d32_ = (cpu_flags & ARM_CPU_FLAG_VFP_D32) != 0;
    451 #endif
    452   }
    453   has_idiva_ = (cpu_flags & ARM_CPU_FLAG_IDIV) != 0;
    454 
    455 #endif  // V8_OS_LINUX
    456 
    457 #elif V8_HOST_ARCH_MIPS
    458 
    459   // Simple detection of FPU at runtime for Linux.
    460   // It is based on /proc/cpuinfo, which reveals hardware configuration
    461   // to user-space applications.  According to MIPS (early 2010), no similar
    462   // facility is universally available on the MIPS architectures,
    463   // so it's up to individual OSes to provide such.
    464   CPUInfo cpu_info;
    465   char* cpu_model = cpu_info.ExtractField("cpu model");
    466   has_fpu_ = HasListItem(cpu_model, "FPU");
    467   delete[] cpu_model;
    468 
    469 #elif V8_HOST_ARCH_ARM64
    470 
    471   CPUInfo cpu_info;
    472 
    473   // Extract implementor from the "CPU implementer" field.
    474   char* implementer = cpu_info.ExtractField("CPU implementer");
    475   if (implementer != NULL) {
    476     char* end ;
    477     implementer_ = strtol(implementer, &end, 0);
    478     if (end == implementer) {
    479       implementer_ = 0;
    480     }
    481     delete[] implementer;
    482   }
    483 
    484   // Extract part number from the "CPU part" field.
    485   char* part = cpu_info.ExtractField("CPU part");
    486   if (part != NULL) {
    487     char* end ;
    488     part_ = strtol(part, &end, 0);
    489     if (end == part) {
    490       part_ = 0;
    491     }
    492     delete[] part;
    493   }
    494 
    495 #endif
    496 }
    497 
    498 } }  // namespace v8::internal
    499