1 // Copyright 2012 The Chromium Authors
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 "partition_alloc/partition_alloc_base/cpu.h"
6
7 #include <inttypes.h>
8 #include <limits.h>
9 #include <stddef.h>
10 #include <stdint.h>
11 #include <string.h>
12
13 #include <algorithm>
14 #include <sstream>
15 #include <utility>
16
17 #include "build/build_config.h"
18
19 #if defined(ARCH_CPU_ARM_FAMILY) && \
20 (BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS))
21 #include <asm/hwcap.h>
22 #include <sys/auxv.h>
23
24 // Temporary definitions until a new hwcap.h is pulled in everywhere.
25 // https://crbug.com/1265965
26 #if defined(ARCH_CPU_ARM64)
27 #ifndef HWCAP2_MTE
28 #define HWCAP2_MTE (1 << 18)
29 #endif
30 #ifndef HWCAP2_BTI
31 #define HWCAP2_BTI (1 << 17)
32 #endif
33 #endif // # defined(ARCH_CPU_ARM64)
34
35 #endif // defined(ARCH_CPU_ARM_FAMILY) && (BUILDFLAG(IS_ANDROID) ||
36 // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS))
37
38 #if defined(ARCH_CPU_X86_FAMILY)
39 #if defined(COMPILER_MSVC)
40 #include <immintrin.h> // For _xgetbv()
41 #include <intrin.h>
42 #endif
43 #endif
44
45 namespace partition_alloc::internal::base {
46
CPU()47 CPU::CPU() {
48 Initialize();
49 }
50 CPU::CPU(CPU&&) = default;
51
52 namespace {
53
54 #if defined(ARCH_CPU_X86_FAMILY)
55 #if !defined(COMPILER_MSVC)
56
57 #if defined(__pic__) && defined(__i386__)
58
__cpuid(int cpu_info[4],int info_type)59 void __cpuid(int cpu_info[4], int info_type) {
60 __asm__ volatile(
61 "mov %%ebx, %%edi\n"
62 "cpuid\n"
63 "xchg %%edi, %%ebx\n"
64 : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]),
65 "=d"(cpu_info[3])
66 : "a"(info_type), "c"(0));
67 }
68
69 #else
70
71 void __cpuid(int cpu_info[4], int info_type) {
72 __asm__ volatile("cpuid\n"
73 : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]),
74 "=d"(cpu_info[3])
75 : "a"(info_type), "c"(0));
76 }
77
78 #endif
79 #endif // !defined(COMPILER_MSVC)
80
81 // xgetbv returns the value of an Intel Extended Control Register (XCR).
82 // Currently only XCR0 is defined by Intel so |xcr| should always be zero.
xgetbv(uint32_t xcr)83 uint64_t xgetbv(uint32_t xcr) {
84 #if defined(COMPILER_MSVC)
85 return _xgetbv(xcr);
86 #else
87 uint32_t eax, edx;
88
89 __asm__ volatile("xgetbv" : "=a"(eax), "=d"(edx) : "c"(xcr));
90 return (static_cast<uint64_t>(edx) << 32) | eax;
91 #endif // defined(COMPILER_MSVC)
92 }
93
94 #endif // ARCH_CPU_X86_FAMILY
95
96 } // namespace
97
Initialize()98 void CPU::Initialize() {
99 #if defined(ARCH_CPU_X86_FAMILY)
100 int cpu_info[4] = {-1};
101
102 // __cpuid with an InfoType argument of 0 returns the number of
103 // valid Ids in CPUInfo[0] and the CPU identification string in
104 // the other three array elements. The CPU identification string is
105 // not in linear order. The code below arranges the information
106 // in a human readable form. The human readable order is CPUInfo[1] |
107 // CPUInfo[3] | CPUInfo[2]. CPUInfo[2] and CPUInfo[3] are swapped
108 // before using memcpy() to copy these three array elements to |cpu_string|.
109 __cpuid(cpu_info, 0);
110 int num_ids = cpu_info[0];
111 std::swap(cpu_info[2], cpu_info[3]);
112
113 // Interpret CPU feature information.
114 if (num_ids > 0) {
115 int cpu_info7[4] = {0};
116 __cpuid(cpu_info, 1);
117 if (num_ids >= 7) {
118 __cpuid(cpu_info7, 7);
119 }
120 signature_ = cpu_info[0];
121 stepping_ = cpu_info[0] & 0xf;
122 type_ = (cpu_info[0] >> 12) & 0x3;
123 has_mmx_ = (cpu_info[3] & 0x00800000) != 0;
124 has_sse_ = (cpu_info[3] & 0x02000000) != 0;
125 has_sse2_ = (cpu_info[3] & 0x04000000) != 0;
126 has_sse3_ = (cpu_info[2] & 0x00000001) != 0;
127 has_ssse3_ = (cpu_info[2] & 0x00000200) != 0;
128 has_sse41_ = (cpu_info[2] & 0x00080000) != 0;
129 has_sse42_ = (cpu_info[2] & 0x00100000) != 0;
130 has_popcnt_ = (cpu_info[2] & 0x00800000) != 0;
131
132 // "Hypervisor Present Bit: Bit 31 of ECX of CPUID leaf 0x1."
133 // See https://lwn.net/Articles/301888/
134 // This is checking for any hypervisor. Hypervisors may choose not to
135 // announce themselves. Hypervisors trap CPUID and sometimes return
136 // different results to underlying hardware.
137 is_running_in_vm_ = (cpu_info[2] & 0x80000000) != 0;
138
139 // AVX instructions will generate an illegal instruction exception unless
140 // a) they are supported by the CPU,
141 // b) XSAVE is supported by the CPU and
142 // c) XSAVE is enabled by the kernel.
143 // See http://software.intel.com/en-us/blogs/2011/04/14/is-avx-enabled
144 //
145 // In addition, we have observed some crashes with the xgetbv instruction
146 // even after following Intel's example code. (See crbug.com/375968.)
147 // Because of that, we also test the XSAVE bit because its description in
148 // the CPUID documentation suggests that it signals xgetbv support.
149 has_avx_ = (cpu_info[2] & 0x10000000) != 0 &&
150 (cpu_info[2] & 0x04000000) != 0 /* XSAVE */ &&
151 (cpu_info[2] & 0x08000000) != 0 /* OSXSAVE */ &&
152 (xgetbv(0) & 6) == 6 /* XSAVE enabled by kernel */;
153 has_aesni_ = (cpu_info[2] & 0x02000000) != 0;
154 has_fma3_ = (cpu_info[2] & 0x00001000) != 0;
155 has_avx2_ = has_avx_ && (cpu_info7[1] & 0x00000020) != 0;
156
157 has_pku_ = (cpu_info7[2] & 0x00000010) != 0;
158 }
159
160 // Get the brand string of the cpu.
161 __cpuid(cpu_info, 0x80000000);
162 const int max_parameter = cpu_info[0];
163
164 static constexpr int kParameterContainingNonStopTimeStampCounter = 0x80000007;
165 if (max_parameter >= kParameterContainingNonStopTimeStampCounter) {
166 __cpuid(cpu_info, kParameterContainingNonStopTimeStampCounter);
167 has_non_stop_time_stamp_counter_ = (cpu_info[3] & (1 << 8)) != 0;
168 }
169
170 if (!has_non_stop_time_stamp_counter_ && is_running_in_vm_) {
171 int cpu_info_hv[4] = {};
172 __cpuid(cpu_info_hv, 0x40000000);
173 if (cpu_info_hv[1] == 0x7263694D && // Micr
174 cpu_info_hv[2] == 0x666F736F && // osof
175 cpu_info_hv[3] == 0x76482074) { // t Hv
176 // If CPUID says we have a variant TSC and a hypervisor has identified
177 // itself and the hypervisor says it is Microsoft Hyper-V, then treat
178 // TSC as invariant.
179 //
180 // Microsoft Hyper-V hypervisor reports variant TSC as there are some
181 // scenarios (eg. VM live migration) where the TSC is variant, but for
182 // our purposes we can treat it as invariant.
183 has_non_stop_time_stamp_counter_ = true;
184 }
185 }
186 #elif defined(ARCH_CPU_ARM_FAMILY)
187 #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
188
189 #if defined(ARCH_CPU_ARM64)
190 // Check for Armv8.5-A BTI/MTE support, exposed via HWCAP2
191 unsigned long hwcap2 = getauxval(AT_HWCAP2);
192 has_mte_ = hwcap2 & HWCAP2_MTE;
193 has_bti_ = hwcap2 & HWCAP2_BTI;
194 #endif
195
196 #elif BUILDFLAG(IS_WIN)
197 // Windows makes high-resolution thread timing information available in
198 // user-space.
199 has_non_stop_time_stamp_counter_ = true;
200 #endif
201 #endif
202 }
203
GetInstanceNoAllocation()204 const CPU& CPU::GetInstanceNoAllocation() {
205 static const CPU cpu;
206 return cpu;
207 }
208
209 } // namespace partition_alloc::internal::base
210