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