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