summaryrefslogtreecommitdiffstats
path: root/wsutil/ws_cpuid.h
blob: cc17e2a58c2bc23aee3a52433ee365f7ac98a03c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/** @file
 * Get the CPU info on x86 processors that support it
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

/*
 * Get CPU info on platforms where the x86 cpuid instruction can be used.
 *
 * Skip 32-bit versions for GCC and Clang, as older IA-32 processors don't
 * have cpuid.
 *
 * Intel has documented the CPUID instruction in the "Intel(r) 64 and IA-32
 * Architectures Developer's Manual" at
 *
 * https://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-vol-2a-manual.html
 *
 * The ws_cpuid() routine will return 0 if cpuinfo isn't available, including
 * on non-x86 platforms and on 32-bit x86 platforms with GCC and Clang, as
 * well as non-MSVC and non-GCC-or-Clang platforms.
 *
 * The "selector" argument to ws_cpuid() is the "initial EAX value" for the
 * instruction.  The initial ECX value is 0.
 *
 * The "CPUInfo" argument points to 4 32-bit values into which the
 * resulting values of EAX, EBX, ECX, and EDX are store, in order.
 */

#include "ws_attributes.h"

#include <inttypes.h>
#include <stdbool.h>

#if defined(_MSC_VER)     /* MSVC */

/*
 * XXX - do the same IA-32 (which doesn't have CPUID prior to some versions
 * of the 80486 and all versions of the 80586^Woriginal Pentium) vs.
 * x86-64 (which always has CPUID) stuff that we do with GCC/Clang?
 *
 * You will probably not be happy running current versions of Wireshark
 * on an 80386 or 80486 machine, and we're dropping support for IA-32
 * on Windows anyway, so the answer is probably "no".
 */
#if defined(_M_IX86) || defined(_M_X64)
static bool
ws_cpuid(uint32_t *CPUInfo, uint32_t selector)
{
	/* https://docs.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex */

	CPUInfo[0] = CPUInfo[1] = CPUInfo[2] = CPUInfo[3] = 0;
	__cpuid((int *) CPUInfo, selector);
	/* XXX, how to check if it's supported on MSVC? just in case clear all flags above */
	return true;
}
#else /* not x86 */
static bool
ws_cpuid(uint32_t *CPUInfo _U_, int selector _U_)
{
	/* Not x86, so no cpuid instruction */
	return false;
}
#endif

#elif defined(__GNUC__)  /* GCC/clang */

#if defined(__x86_64__)
static inline bool
ws_cpuid(uint32_t *CPUInfo, int selector)
{
	__asm__ __volatile__("cpuid"
						: "=a" (CPUInfo[0]),
							"=b" (CPUInfo[1]),
							"=c" (CPUInfo[2]),
							"=d" (CPUInfo[3])
						: "a" (selector),
							"c" (0));
	return true;
}
#elif defined(__i386__)
static bool
ws_cpuid(uint32_t *CPUInfo _U_, int selector _U_)
{
	/*
	 * TODO: need a test if older processors have the cpuid instruction.
	 *
	 * The correct way to test for this, according to the Intel64/IA-32
	 * documentation from Intel, in section 17.1 "USING THE CPUID
	 * INSTRUCTION", is to try to change the ID bit (bit 21) in
	 * EFLAGS.  If it can be changed, the machine supports CPUID,
	 * otherwise it doesn't.
	 *
	 * Some 486's, and all subsequent processors, support CPUID.
	 *
	 * For those who are curious, the way you distinguish between
	 * an 80386 and an 80486 is to try to set the flag in EFLAGS
	 * that causes unaligned accesses to fault - that's bit 18.
	 * However, if the SMAP bit is set in CR4, that bit controls
	 * whether explicit supervisor-mode access to user-mode pages
	 * are allowed, so that should presumably only be done in a
	 * very controlled environment, such as the system boot process.
	 *
	 * So, if you want to find out what type of CPU the system has,
	 * it's probably best to ask the OS, if it supplies the result
	 * of any CPU type testing it's done.
	 */
	return false;
}
#else /* not x86 */
static bool
ws_cpuid(uint32_t *CPUInfo _U_, int selector _U_)
{
	/* Not x86, so no cpuid instruction */
	return false;
}
#endif

#else /* Other compilers */

static bool
ws_cpuid(uint32_t *CPUInfo _U_, int selector _U_)
{
	return false;
}
#endif

static int
ws_cpuid_sse42(void)
{
	uint32_t CPUInfo[4];

	if (!ws_cpuid(CPUInfo, 1))
		return 0;

	/* in ECX bit 20 toggled on */
	return (CPUInfo[2] & (1 << 20));
}