PostgreSQL Source Code git master
Loading...
Searching...
No Matches
pg_cpu_x86.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * pg_cpu_x86.c
4 * Runtime CPU feature detection for x86
5 *
6 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 *
10 * IDENTIFICATION
11 * src/port/pg_cpu_x86.c
12 *
13 *-------------------------------------------------------------------------
14 */
15
16#include "c.h"
17
18#if defined(USE_SSE2) || defined(__i386__)
19
20#ifdef _MSC_VER
21#include <intrin.h>
22#else
23#include <cpuid.h>
24#endif
25
26#ifdef HAVE_XSAVE_INTRINSICS
27#include <immintrin.h>
28#endif
29
30#include "port/pg_cpu.h"
31
32/*
33 * XSAVE state component bits that we need
34 *
35 * https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-vol-1-manual.pdf
36 * Chapter "MANAGING STATE USING THE XSAVE FEATURE SET"
37 */
38#define XMM (1<<1)
39#define YMM (1<<2)
40#define OPMASK (1<<5)
41#define ZMM0_15 (1<<6)
42#define ZMM16_31 (1<<7)
43
44
45/* array indexed by enum X86FeatureId */
47
48static bool
50{
51 return (value & mask) == mask;
52}
53
54/* Named indexes for CPUID register array */
55#define EAX 0
56#define EBX 1
57#define ECX 2
58#define EDX 3
59
60/*
61 * Request CPUID information for the specified leaf.
62 */
63static inline void
64pg_cpuid(int leaf, unsigned int *reg)
65{
66 memset(reg, 0, 4 * sizeof(unsigned int));
67#if defined(HAVE__GET_CPUID)
68 __get_cpuid(leaf, &reg[EAX], &reg[EBX], &reg[ECX], &reg[EDX]);
69#elif defined(HAVE__CPUID)
70 __cpuid((int *) reg, leaf);
71#endif
72}
73
74/*
75 * Request CPUID information for the specified leaf and subleaf.
76 *
77 * Returns true if the CPUID leaf/subleaf is supported, false otherwise.
78 */
79static inline bool
80pg_cpuid_subleaf(int leaf, int subleaf, unsigned int *reg)
81{
82 memset(reg, 0, 4 * sizeof(unsigned int));
83#if defined(HAVE__GET_CPUID_COUNT)
84 return __get_cpuid_count(leaf, subleaf, &reg[EAX], &reg[EBX], &reg[ECX], &reg[EDX]) == 1;
85#elif defined(HAVE__CPUIDEX)
86 __cpuidex((int *) reg, leaf, subleaf);
87 return true;
88#else
89 return false;
90#endif
91}
92
93/*
94 * Parse the CPU ID info for runtime checks.
95 */
96#ifdef HAVE_XSAVE_INTRINSICS
98#endif
99void
101{
102 unsigned int reg[4] = {0};
103 bool have_osxsave;
104
105 pg_cpuid(0x01, reg);
106
107 X86Features[PG_SSE4_2] = reg[ECX] >> 20 & 1;
108 X86Features[PG_POPCNT] = reg[ECX] >> 23 & 1;
109 X86Features[PG_HYPERVISOR] = reg[ECX] >> 31 & 1;
110 have_osxsave = reg[ECX] >> 27 & 1;
111
112 pg_cpuid_subleaf(0x07, 0, reg);
113
114 X86Features[PG_TSC_ADJUST] = reg[EBX] >> 1 & 1;
115
116 /* leaf 7 features that depend on OSXSAVE */
117 if (have_osxsave)
118 {
119 uint32 xcr0_val = 0;
120
121#ifdef HAVE_XSAVE_INTRINSICS
122 /* get value of Extended Control Register */
123 xcr0_val = _xgetbv(0);
124#endif
125
126 /* Are YMM registers enabled? */
128 X86Features[PG_AVX2] = reg[EBX] >> 5 & 1;
129
130 /* Are ZMM registers enabled? */
133 {
134 X86Features[PG_AVX512_BW] = reg[EBX] >> 30 & 1;
135 X86Features[PG_AVX512_VL] = reg[EBX] >> 31 & 1;
136
137 X86Features[PG_AVX512_VPCLMULQDQ] = reg[ECX] >> 10 & 1;
138 X86Features[PG_AVX512_VPOPCNTDQ] = reg[ECX] >> 14 & 1;
139 }
140 }
141
142 /* Check for other TSC related flags */
143 pg_cpuid(0x80000001, reg);
144 X86Features[PG_RDTSCP] = reg[EDX] >> 27 & 1;
145
146 pg_cpuid(0x80000007, reg);
147 X86Features[PG_TSC_INVARIANT] = reg[EDX] >> 8 & 1;
148
149 X86Features[INIT_PG_X86] = true;
150}
151
152/* TSC (Time-stamp Counter) handling code */
153
155
156/*
157 * Determine the TSC frequency of the CPU through CPUID, where supported.
158 *
159 * Needed to interpret the tick value returned by RDTSC/RDTSCP. Return value of
160 * 0 indicates the frequency information was not accessible via CPUID.
161 */
162uint32
164{
165 unsigned int reg[4] = {0};
166
167 /*
168 * If we're inside a virtual machine, try to fetch the TSC frequency from
169 * the hypervisor, using a hypervisor specific method.
170 *
171 * Note it is not safe to utilize the regular 0x15/0x16 CPUID registers
172 * (i.e. the logic below) in virtual machines, as they have been observed
173 * to be wildly incorrect when virtualized.
174 */
177
178 /*
179 * On modern Intel CPUs, the TSC is implemented by invariant timekeeping
180 * hardware, also called "Always Running Timer", or ART. The ART stays
181 * consistent even if the CPU changes frequency due to changing power
182 * levels.
183 *
184 * As documented in "Determining the Processor Base Frequency" in the
185 * "IntelĀ® 64 and IA-32 Architectures Software Developer's Manual",
186 * February 2026 Edition, we can get the TSC frequency as follows:
187 *
188 * Nominal TSC frequency = ( CPUID.15H:ECX[31:0] * CPUID.15H:EBX[31:0] ) /
189 * CPUID.15H:EAX[31:0]
190 *
191 * With CPUID.15H:ECX representing the nominal core crystal clock
192 * frequency, and EAX/EBX representing values used to translate the TSC
193 * value to that frequency, see "Chapter 20.17 "Time-Stamp Counter" of
194 * that manual.
195 *
196 * Older Intel CPUs, and other vendors do not set CPUID.15H:ECX, and as
197 * such we fall back to alternate approaches.
198 */
199 pg_cpuid(0x15, reg);
200 if (reg[ECX] > 0)
201 {
202 /*
203 * EBX not being set indicates invariant TSC is not available. Require
204 * EAX being non-zero too, to avoid a theoretical divide by zero.
205 */
206 if (reg[EAX] == 0 || reg[EBX] == 0)
207 return 0;
208
209 return reg[ECX] / 1000 * reg[EBX] / reg[EAX];
210 }
211
212 /*
213 * When CPUID.15H is not available/incomplete, we can instead try to get
214 * the processor base frequency in MHz from CPUID.16H:EAX, the "Processor
215 * Frequency Information Leaf".
216 */
217 pg_cpuid(0x16, reg);
218 if (reg[EAX] > 0)
219 return reg[EAX] * 1000;
220
221 return 0;
222}
223
224/*
225 * Support for reading TSC frequency for hypervisors passing it to a guest VM.
226 *
227 * Two Hypervisors (VMware and KVM) are known to make TSC frequency in KHz
228 * available at the vendor-specific 0x40000010 leaf in the EAX register.
229 *
230 * For some other Hypervisors that have an invariant TSC, e.g. HyperV, we would
231 * need to access a model-specific register (MSR) to get the frequency. MSRs are
232 * separate from CPUID and typically not available for unprivileged processes,
233 * so we can't get the frequency this way.
234 */
235#define CPUID_HYPERVISOR_VMWARE(r) (r[EBX] == 0x61774d56 && r[ECX] == 0x4d566572 && r[EDX] == 0x65726177) /* VMwareVMware */
236#define CPUID_HYPERVISOR_KVM(r) (r[EBX] == 0x4b4d564b && r[ECX] == 0x564b4d56 && r[EDX] == 0x0000004d) /* KVMKVMKVM */
237static uint32
239{
240#if defined(HAVE__CPUIDEX)
241 unsigned int reg[4] = {0};
242
243 /*
244 * The hypervisor is determined using the 0x40000000 Hypervisor
245 * information leaf, which requires use of __cpuidex to set ECX to 0 to
246 * access it.
247 *
248 * The similar __get_cpuid_count function does not work as expected since
249 * it contains a check for __get_cpuid_max, which has been observed to be
250 * lower than the special Hypervisor leaf, despite it being available.
251 */
252 __cpuidex((int *) reg, 0x40000000, 0);
253
254 if (reg[EAX] >= 0x40000010 && (CPUID_HYPERVISOR_VMWARE(reg) || CPUID_HYPERVISOR_KVM(reg)))
255 {
256 __cpuidex((int *) reg, 0x40000010, 0);
257 if (reg[EAX] > 0)
258 return reg[EAX];
259 }
260#endif /* HAVE__CPUIDEX */
261
262 return 0;
263}
264
265#else /* defined(USE_SSE2) || defined(__i386__) */
266
267/* prevent linker complaints about empty module */
270
271#endif /* ! (USE_SSE2 || __i386__) */
uint32_t uint32
Definition c.h:624
#define pg_attribute_target(...)
Definition c.h:238
static struct @177 value
int pg_cpu_x86_dummy_variable
Definition pg_cpu_x86.c:269
static int fb(int x)