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