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