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 */
46
bool
X86Features
[
X86FeaturesSize
] = {0};
47
48
static
bool
49
mask_available
(
uint32
value
,
uint32
mask)
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
*/
63
static
inline
void
64
pg_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
, ®[
EAX
], ®[
EBX
], ®[
ECX
], ®[
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
*/
79
static
inline
bool
80
pg_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
, ®[
EAX
], ®[
EBX
], ®[
ECX
], ®[
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
97
pg_attribute_target
(
"xsave"
)
98
#endif
99
void
100
set_x86_features
(
void
)
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? */
127
if
(
mask_available
(
xcr0_val
,
XMM
|
YMM
))
128
X86Features
[
PG_AVX2
] = reg[
EBX
] >> 5 & 1;
129
130
/* Are ZMM registers enabled? */
131
if
(
mask_available
(
xcr0_val
,
XMM
|
YMM
|
132
OPMASK
|
ZMM0_15
|
ZMM16_31
))
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
154
static
uint32
x86_hypervisor_tsc_frequency_khz
(
void
);
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
*/
162
uint32
163
x86_tsc_frequency_khz
(
void
)
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
*/
175
if
(
x86_feature_available
(
PG_HYPERVISOR
))
176
return
x86_hypervisor_tsc_frequency_khz
();
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 */
237
static
uint32
238
x86_hypervisor_tsc_frequency_khz
(
void
)
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 */
268
extern
int
pg_cpu_x86_dummy_variable
;
269
int
pg_cpu_x86_dummy_variable
= 0;
270
271
#endif
/* ! (USE_SSE2 || __i386__) */
c.h
uint32
uint32_t uint32
Definition
c.h:624
pg_attribute_target
#define pg_attribute_target(...)
Definition
c.h:238
value
static struct @177 value
pg_cpu.h
pg_cpu_x86_dummy_variable
int pg_cpu_x86_dummy_variable
Definition
pg_cpu_x86.c:269
fb
static int fb(int x)
Definition
preproc-init.c:92
src
port
pg_cpu_x86.c
Generated on Wed May 13 2026 15:13:18 for PostgreSQL Source Code by
1.9.8