1// Copyright (c) 2012 The Chromium 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 "base/cpu.h" 6 7#include <stdlib.h> 8#include <string.h> 9 10#include <algorithm> 11 12#include "base/basictypes.h" 13#include "base/strings/string_piece.h" 14#include "build/build_config.h" 15 16#if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX)) 17#include "base/files/file_util.h" 18#include "base/lazy_instance.h" 19#endif 20 21#if defined(ARCH_CPU_X86_FAMILY) 22#if defined(_MSC_VER) 23#include <intrin.h> 24#include <immintrin.h> // For _xgetbv() 25#endif 26#endif 27 28namespace base { 29 30CPU::CPU() 31 : signature_(0), 32 type_(0), 33 family_(0), 34 model_(0), 35 stepping_(0), 36 ext_model_(0), 37 ext_family_(0), 38 has_mmx_(false), 39 has_sse_(false), 40 has_sse2_(false), 41 has_sse3_(false), 42 has_ssse3_(false), 43 has_sse41_(false), 44 has_sse42_(false), 45 has_avx_(false), 46 has_avx_hardware_(false), 47 has_aesni_(false), 48 has_non_stop_time_stamp_counter_(false), 49 has_broken_neon_(false), 50 cpu_vendor_("unknown") { 51 Initialize(); 52} 53 54namespace { 55 56#if defined(ARCH_CPU_X86_FAMILY) 57#ifndef _MSC_VER 58 59#if defined(__pic__) && defined(__i386__) 60 61void __cpuid(int cpu_info[4], int info_type) { 62 __asm__ volatile ( 63 "mov %%ebx, %%edi\n" 64 "cpuid\n" 65 "xchg %%edi, %%ebx\n" 66 : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3]) 67 : "a"(info_type) 68 ); 69} 70 71#else 72 73void __cpuid(int cpu_info[4], int info_type) { 74 __asm__ volatile ( 75 "cpuid \n\t" 76 : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3]) 77 : "a"(info_type) 78 ); 79} 80 81#endif 82 83// _xgetbv returns the value of an Intel Extended Control Register (XCR). 84// Currently only XCR0 is defined by Intel so |xcr| should always be zero. 85uint64 _xgetbv(uint32 xcr) { 86 uint32 eax, edx; 87 88 __asm__ volatile ("xgetbv" : "=a" (eax), "=d" (edx) : "c" (xcr)); 89 return (static_cast<uint64>(edx) << 32) | eax; 90} 91 92#endif // !_MSC_VER 93#endif // ARCH_CPU_X86_FAMILY 94 95#if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX)) 96class LazyCpuInfoValue { 97 public: 98 LazyCpuInfoValue() : has_broken_neon_(false) { 99 // This function finds the value from /proc/cpuinfo under the key "model 100 // name" or "Processor". "model name" is used in Linux 3.8 and later (3.7 101 // and later for arm64) and is shown once per CPU. "Processor" is used in 102 // earler versions and is shown only once at the top of /proc/cpuinfo 103 // regardless of the number CPUs. 104 const char kModelNamePrefix[] = "model name\t: "; 105 const char kProcessorPrefix[] = "Processor\t: "; 106 107 // This function also calculates whether we believe that this CPU has a 108 // broken NEON unit based on these fields from cpuinfo: 109 unsigned implementer = 0, architecture = 0, variant = 0, part = 0, 110 revision = 0; 111 const struct { 112 const char key[17]; 113 unsigned *result; 114 } kUnsignedValues[] = { 115 {"CPU implementer", &implementer}, 116 {"CPU architecture", &architecture}, 117 {"CPU variant", &variant}, 118 {"CPU part", &part}, 119 {"CPU revision", &revision}, 120 }; 121 122 std::string contents; 123 ReadFileToString(FilePath("/proc/cpuinfo"), &contents); 124 DCHECK(!contents.empty()); 125 if (contents.empty()) { 126 return; 127 } 128 129 std::istringstream iss(contents); 130 std::string line; 131 while (std::getline(iss, line)) { 132 if (brand_.empty() && 133 (line.compare(0, strlen(kModelNamePrefix), kModelNamePrefix) == 0 || 134 line.compare(0, strlen(kProcessorPrefix), kProcessorPrefix) == 0)) { 135 brand_.assign(line.substr(strlen(kModelNamePrefix))); 136 } 137 138 for (size_t i = 0; i < arraysize(kUnsignedValues); i++) { 139 const char *key = kUnsignedValues[i].key; 140 const size_t len = strlen(key); 141 142 if (line.compare(0, len, key) == 0 && 143 line.size() >= len + 1 && 144 (line[len] == '\t' || line[len] == ' ' || line[len] == ':')) { 145 size_t colon_pos = line.find(':', len); 146 if (colon_pos == std::string::npos) { 147 continue; 148 } 149 150 const StringPiece line_sp(line); 151 StringPiece value_sp = line_sp.substr(colon_pos + 1); 152 while (!value_sp.empty() && 153 (value_sp[0] == ' ' || value_sp[0] == '\t')) { 154 value_sp = value_sp.substr(1); 155 } 156 157 // The string may have leading "0x" or not, so we use strtoul to 158 // handle that. 159 char *endptr; 160 std::string value(value_sp.as_string()); 161 unsigned long int result = strtoul(value.c_str(), &endptr, 0); 162 if (*endptr == 0 && result <= UINT_MAX) { 163 *kUnsignedValues[i].result = result; 164 } 165 } 166 } 167 } 168 169 has_broken_neon_ = 170 implementer == 0x51 && 171 architecture == 7 && 172 variant == 1 && 173 part == 0x4d && 174 revision == 0; 175 } 176 177 const std::string& brand() const { return brand_; } 178 bool has_broken_neon() const { return has_broken_neon_; } 179 180 private: 181 std::string brand_; 182 bool has_broken_neon_; 183 DISALLOW_COPY_AND_ASSIGN(LazyCpuInfoValue); 184}; 185 186base::LazyInstance<LazyCpuInfoValue>::Leaky g_lazy_cpuinfo = 187 LAZY_INSTANCE_INITIALIZER; 188 189#endif // defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || 190 // defined(OS_LINUX)) 191 192} // anonymous namespace 193 194void CPU::Initialize() { 195#if defined(ARCH_CPU_X86_FAMILY) 196 int cpu_info[4] = {-1}; 197 char cpu_string[48]; 198 199 // __cpuid with an InfoType argument of 0 returns the number of 200 // valid Ids in CPUInfo[0] and the CPU identification string in 201 // the other three array elements. The CPU identification string is 202 // not in linear order. The code below arranges the information 203 // in a human readable form. The human readable order is CPUInfo[1] | 204 // CPUInfo[3] | CPUInfo[2]. CPUInfo[2] and CPUInfo[3] are swapped 205 // before using memcpy to copy these three array elements to cpu_string. 206 __cpuid(cpu_info, 0); 207 int num_ids = cpu_info[0]; 208 std::swap(cpu_info[2], cpu_info[3]); 209 memcpy(cpu_string, &cpu_info[1], 3 * sizeof(cpu_info[1])); 210 cpu_vendor_.assign(cpu_string, 3 * sizeof(cpu_info[1])); 211 212 // Interpret CPU feature information. 213 if (num_ids > 0) { 214 __cpuid(cpu_info, 1); 215 signature_ = cpu_info[0]; 216 stepping_ = cpu_info[0] & 0xf; 217 model_ = ((cpu_info[0] >> 4) & 0xf) + ((cpu_info[0] >> 12) & 0xf0); 218 family_ = (cpu_info[0] >> 8) & 0xf; 219 type_ = (cpu_info[0] >> 12) & 0x3; 220 ext_model_ = (cpu_info[0] >> 16) & 0xf; 221 ext_family_ = (cpu_info[0] >> 20) & 0xff; 222 has_mmx_ = (cpu_info[3] & 0x00800000) != 0; 223 has_sse_ = (cpu_info[3] & 0x02000000) != 0; 224 has_sse2_ = (cpu_info[3] & 0x04000000) != 0; 225 has_sse3_ = (cpu_info[2] & 0x00000001) != 0; 226 has_ssse3_ = (cpu_info[2] & 0x00000200) != 0; 227 has_sse41_ = (cpu_info[2] & 0x00080000) != 0; 228 has_sse42_ = (cpu_info[2] & 0x00100000) != 0; 229 has_avx_hardware_ = 230 (cpu_info[2] & 0x10000000) != 0; 231 // AVX instructions will generate an illegal instruction exception unless 232 // a) they are supported by the CPU, 233 // b) XSAVE is supported by the CPU and 234 // c) XSAVE is enabled by the kernel. 235 // See http://software.intel.com/en-us/blogs/2011/04/14/is-avx-enabled 236 // 237 // In addition, we have observed some crashes with the xgetbv instruction 238 // even after following Intel's example code. (See crbug.com/375968.) 239 // Because of that, we also test the XSAVE bit because its description in 240 // the CPUID documentation suggests that it signals xgetbv support. 241 has_avx_ = 242 has_avx_hardware_ && 243 (cpu_info[2] & 0x04000000) != 0 /* XSAVE */ && 244 (cpu_info[2] & 0x08000000) != 0 /* OSXSAVE */ && 245 (_xgetbv(0) & 6) == 6 /* XSAVE enabled by kernel */; 246 has_aesni_ = (cpu_info[2] & 0x02000000) != 0; 247 } 248 249 // Get the brand string of the cpu. 250 __cpuid(cpu_info, 0x80000000); 251 const int parameter_end = 0x80000004; 252 int max_parameter = cpu_info[0]; 253 254 if (cpu_info[0] >= parameter_end) { 255 char* cpu_string_ptr = cpu_string; 256 257 for (int parameter = 0x80000002; parameter <= parameter_end && 258 cpu_string_ptr < &cpu_string[sizeof(cpu_string)]; parameter++) { 259 __cpuid(cpu_info, parameter); 260 memcpy(cpu_string_ptr, cpu_info, sizeof(cpu_info)); 261 cpu_string_ptr += sizeof(cpu_info); 262 } 263 cpu_brand_.assign(cpu_string, cpu_string_ptr - cpu_string); 264 } 265 266 const int parameter_containing_non_stop_time_stamp_counter = 0x80000007; 267 if (max_parameter >= parameter_containing_non_stop_time_stamp_counter) { 268 __cpuid(cpu_info, parameter_containing_non_stop_time_stamp_counter); 269 has_non_stop_time_stamp_counter_ = (cpu_info[3] & (1 << 8)) != 0; 270 } 271#elif defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX)) 272 cpu_brand_.assign(g_lazy_cpuinfo.Get().brand()); 273 has_broken_neon_ = g_lazy_cpuinfo.Get().has_broken_neon(); 274#endif 275} 276 277CPU::IntelMicroArchitecture CPU::GetIntelMicroArchitecture() const { 278 if (has_avx()) return AVX; 279 if (has_sse42()) return SSE42; 280 if (has_sse41()) return SSE41; 281 if (has_ssse3()) return SSSE3; 282 if (has_sse3()) return SSE3; 283 if (has_sse2()) return SSE2; 284 if (has_sse()) return SSE; 285 return PENTIUM; 286} 287 288} // namespace base 289