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