[M85 Dev][EFL] Fix crashes at webview launch
[platform/framework/web/chromium-efl.git] / base / cpu.cc
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 <limits.h>
8 #include <stddef.h>
9 #include <stdint.h>
10 #include <string.h>
11
12 #include <algorithm>
13 #include <sstream>
14 #include <utility>
15
16 #include "base/stl_util.h"
17
18 #if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX))
19 #include "base/files/file_util.h"
20 #endif
21
22 #if defined(ARCH_CPU_X86_FAMILY)
23 #if defined(COMPILER_MSVC)
24 #include <intrin.h>
25 #include <immintrin.h>  // For _xgetbv()
26 #endif
27 #endif
28
29 namespace base {
30
31 #if defined(ARCH_CPU_X86_FAMILY)
32 namespace internal {
33
34 std::tuple<int, int, int, int> ComputeX86FamilyAndModel(
35     const std::string& vendor,
36     int signature) {
37   int family = (signature >> 8) & 0xf;
38   int model = (signature >> 4) & 0xf;
39   int ext_family = 0;
40   int ext_model = 0;
41
42   // The "Intel 64 and IA-32 Architectures Developer's Manual: Vol. 2A"
43   // specifies the Extended Model is defined only when the Base Family is
44   // 06h or 0Fh.
45   // The "AMD CPUID Specification" specifies that the Extended Model is
46   // defined only when Base Family is 0Fh.
47   // Both manuals define the display model as
48   // {ExtendedModel[3:0],BaseModel[3:0]} in that case.
49   if (family == 0xf || (family == 0x6 && vendor == "GenuineIntel")) {
50     ext_model = (signature >> 16) & 0xf;
51     model += ext_model << 4;
52   }
53   // Both the "Intel 64 and IA-32 Architectures Developer's Manual: Vol. 2A"
54   // and the "AMD CPUID Specification" specify that the Extended Family is
55   // defined only when the Base Family is 0Fh.
56   // Both manuals define the display family as {0000b,BaseFamily[3:0]} +
57   // ExtendedFamily[7:0] in that case.
58   if (family == 0xf) {
59     ext_family = (signature >> 20) & 0xff;
60     family += ext_family;
61   }
62
63   return {family, model, ext_family, ext_model};
64 }
65
66 }  // namespace internal
67 #endif  // defined(ARCH_CPU_X86_FAMILY)
68
69 CPU::CPU()
70   : signature_(0),
71     type_(0),
72     family_(0),
73     model_(0),
74     stepping_(0),
75     ext_model_(0),
76     ext_family_(0),
77     has_mmx_(false),
78     has_sse_(false),
79     has_sse2_(false),
80     has_sse3_(false),
81     has_ssse3_(false),
82     has_sse41_(false),
83     has_sse42_(false),
84     has_popcnt_(false),
85     has_avx_(false),
86     has_avx2_(false),
87     has_aesni_(false),
88     has_non_stop_time_stamp_counter_(false),
89     is_running_in_vm_(false),
90     cpu_vendor_("unknown") {
91   Initialize();
92 }
93
94 namespace {
95
96 #if defined(ARCH_CPU_X86_FAMILY)
97 #if !defined(COMPILER_MSVC)
98
99 #if defined(__pic__) && defined(__i386__)
100
101 void __cpuid(int cpu_info[4], int info_type) {
102   __asm__ volatile(
103       "mov %%ebx, %%edi\n"
104       "cpuid\n"
105       "xchg %%edi, %%ebx\n"
106       : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]),
107         "=d"(cpu_info[3])
108       : "a"(info_type), "c"(0));
109 }
110
111 #else
112
113 void __cpuid(int cpu_info[4], int info_type) {
114   __asm__ volatile("cpuid\n"
115                    : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]),
116                      "=d"(cpu_info[3])
117                    : "a"(info_type), "c"(0));
118 }
119
120 #endif
121 #endif  // !defined(COMPILER_MSVC)
122
123 // xgetbv returns the value of an Intel Extended Control Register (XCR).
124 // Currently only XCR0 is defined by Intel so |xcr| should always be zero.
125 uint64_t xgetbv(uint32_t xcr) {
126 #if defined(COMPILER_MSVC)
127   return _xgetbv(xcr);
128 #else
129   uint32_t eax, edx;
130
131   __asm__ volatile (
132     "xgetbv" : "=a"(eax), "=d"(edx) : "c"(xcr));
133   return (static_cast<uint64_t>(edx) << 32) | eax;
134 #endif  // defined(COMPILER_MSVC)
135 }
136
137 #endif  // ARCH_CPU_X86_FAMILY
138
139 #if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX))
140 std::string* CpuInfoBrand() {
141   static std::string* brand = []() {
142     // This function finds the value from /proc/cpuinfo under the key "model
143     // name" or "Processor". "model name" is used in Linux 3.8 and later (3.7
144     // and later for arm64) and is shown once per CPU. "Processor" is used in
145     // earler versions and is shown only once at the top of /proc/cpuinfo
146     // regardless of the number CPUs.
147     const char kModelNamePrefix[] = "model name\t: ";
148     const char kProcessorPrefix[] = "Processor\t: ";
149
150     std::string contents;
151     ReadFileToString(FilePath("/proc/cpuinfo"), &contents);
152     DCHECK(!contents.empty());
153
154     std::istringstream iss(contents);
155     std::string line;
156     while (std::getline(iss, line)) {
157       if (line.compare(0, strlen(kModelNamePrefix), kModelNamePrefix) == 0)
158         return new std::string(line.substr(strlen(kModelNamePrefix)));
159       if (line.compare(0, strlen(kProcessorPrefix), kProcessorPrefix) == 0)
160         return new std::string(line.substr(strlen(kProcessorPrefix)));
161     }
162
163     return new std::string();
164   }();
165
166   return brand;
167 }
168 #endif  // defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) ||
169         // defined(OS_LINUX))
170
171 }  // namespace
172
173 void CPU::Initialize() {
174 #if defined(ARCH_CPU_X86_FAMILY)
175   int cpu_info[4] = {-1};
176   // This array is used to temporarily hold the vendor name and then the brand
177   // name. Thus it has to be big enough for both use cases. There are
178   // static_asserts below for each of the use cases to make sure this array is
179   // big enough.
180   char cpu_string[sizeof(cpu_info) * 3 + 1];
181
182   // __cpuid with an InfoType argument of 0 returns the number of
183   // valid Ids in CPUInfo[0] and the CPU identification string in
184   // the other three array elements. The CPU identification string is
185   // not in linear order. The code below arranges the information
186   // in a human readable form. The human readable order is CPUInfo[1] |
187   // CPUInfo[3] | CPUInfo[2]. CPUInfo[2] and CPUInfo[3] are swapped
188   // before using memcpy() to copy these three array elements to |cpu_string|.
189   __cpuid(cpu_info, 0);
190   int num_ids = cpu_info[0];
191   std::swap(cpu_info[2], cpu_info[3]);
192   static constexpr size_t kVendorNameSize = 3 * sizeof(cpu_info[1]);
193   static_assert(kVendorNameSize < base::size(cpu_string),
194                 "cpu_string too small");
195   memcpy(cpu_string, &cpu_info[1], kVendorNameSize);
196   cpu_string[kVendorNameSize] = '\0';
197   cpu_vendor_ = cpu_string;
198
199   // Interpret CPU feature information.
200   if (num_ids > 0) {
201     int cpu_info7[4] = {0};
202     __cpuid(cpu_info, 1);
203     if (num_ids >= 7) {
204       __cpuid(cpu_info7, 7);
205     }
206     signature_ = cpu_info[0];
207     stepping_ = cpu_info[0] & 0xf;
208     type_ = (cpu_info[0] >> 12) & 0x3;
209     std::tie(family_, model_, ext_family_, ext_model_) =
210         internal::ComputeX86FamilyAndModel(cpu_vendor_, signature_);
211     has_mmx_ =   (cpu_info[3] & 0x00800000) != 0;
212     has_sse_ =   (cpu_info[3] & 0x02000000) != 0;
213     has_sse2_ =  (cpu_info[3] & 0x04000000) != 0;
214     has_sse3_ =  (cpu_info[2] & 0x00000001) != 0;
215     has_ssse3_ = (cpu_info[2] & 0x00000200) != 0;
216     has_sse41_ = (cpu_info[2] & 0x00080000) != 0;
217     has_sse42_ = (cpu_info[2] & 0x00100000) != 0;
218     has_popcnt_ = (cpu_info[2] & 0x00800000) != 0;
219
220     // "Hypervisor Present Bit: Bit 31 of ECX of CPUID leaf 0x1."
221     // See https://lwn.net/Articles/301888/
222     // This is checking for any hypervisor. Hypervisors may choose not to
223     // announce themselves. Hypervisors trap CPUID and sometimes return
224     // different results to underlying hardware.
225     is_running_in_vm_ = (cpu_info[2] & 0x80000000) != 0;
226
227     // AVX instructions will generate an illegal instruction exception unless
228     //   a) they are supported by the CPU,
229     //   b) XSAVE is supported by the CPU and
230     //   c) XSAVE is enabled by the kernel.
231     // See http://software.intel.com/en-us/blogs/2011/04/14/is-avx-enabled
232     //
233     // In addition, we have observed some crashes with the xgetbv instruction
234     // even after following Intel's example code. (See crbug.com/375968.)
235     // Because of that, we also test the XSAVE bit because its description in
236     // the CPUID documentation suggests that it signals xgetbv support.
237     has_avx_ =
238         (cpu_info[2] & 0x10000000) != 0 &&
239         (cpu_info[2] & 0x04000000) != 0 /* XSAVE */ &&
240         (cpu_info[2] & 0x08000000) != 0 /* OSXSAVE */ &&
241         (xgetbv(0) & 6) == 6 /* XSAVE enabled by kernel */;
242     has_aesni_ = (cpu_info[2] & 0x02000000) != 0;
243     has_avx2_ = has_avx_ && (cpu_info7[1] & 0x00000020) != 0;
244   }
245
246   // Get the brand string of the cpu.
247   __cpuid(cpu_info, 0x80000000);
248   const int max_parameter = cpu_info[0];
249
250   static constexpr int kParameterStart = 0x80000002;
251   static constexpr int kParameterEnd = 0x80000004;
252   static constexpr int kParameterSize = kParameterEnd - kParameterStart + 1;
253   static_assert(kParameterSize * sizeof(cpu_info) + 1 == base::size(cpu_string),
254                 "cpu_string has wrong size");
255
256   if (max_parameter >= kParameterEnd) {
257     size_t i = 0;
258     for (int parameter = kParameterStart; parameter <= kParameterEnd;
259          ++parameter) {
260       __cpuid(cpu_info, parameter);
261       memcpy(&cpu_string[i], cpu_info, sizeof(cpu_info));
262       i += sizeof(cpu_info);
263     }
264     cpu_string[i] = '\0';
265     cpu_brand_ = cpu_string;
266   }
267
268   static constexpr int kParameterContainingNonStopTimeStampCounter = 0x80000007;
269   if (max_parameter >= kParameterContainingNonStopTimeStampCounter) {
270     __cpuid(cpu_info, kParameterContainingNonStopTimeStampCounter);
271     has_non_stop_time_stamp_counter_ = (cpu_info[3] & (1 << 8)) != 0;
272   }
273
274   if (!has_non_stop_time_stamp_counter_ && is_running_in_vm_) {
275     int cpu_info_hv[4] = {};
276     __cpuid(cpu_info_hv, 0x40000000);
277     if (cpu_info_hv[1] == 0x7263694D &&  // Micr
278         cpu_info_hv[2] == 0x666F736F &&  // osof
279         cpu_info_hv[3] == 0x76482074) {  // t Hv
280       // If CPUID says we have a variant TSC and a hypervisor has identified
281       // itself and the hypervisor says it is Microsoft Hyper-V, then treat
282       // TSC as invariant.
283       //
284       // Microsoft Hyper-V hypervisor reports variant TSC as there are some
285       // scenarios (eg. VM live migration) where the TSC is variant, but for
286       // our purposes we can treat it as invariant.
287       has_non_stop_time_stamp_counter_ = true;
288     }
289   }
290 #elif defined(ARCH_CPU_ARM_FAMILY)
291 #if (defined(OS_ANDROID) || defined(OS_LINUX))
292   cpu_brand_ = *CpuInfoBrand();
293 #elif defined(OS_WIN)
294   // Windows makes high-resolution thread timing information available in
295   // user-space.
296   has_non_stop_time_stamp_counter_ = true;
297 #endif
298 #endif
299 }
300
301 CPU::IntelMicroArchitecture CPU::GetIntelMicroArchitecture() const {
302   if (has_avx2()) return AVX2;
303   if (has_avx()) return AVX;
304   if (has_sse42()) return SSE42;
305   if (has_sse41()) return SSE41;
306   if (has_ssse3()) return SSSE3;
307   if (has_sse3()) return SSE3;
308   if (has_sse2()) return SSE2;
309   if (has_sse()) return SSE;
310   return PENTIUM;
311 }
312
313 }  // namespace base