wireshark/wsutil/ws_cpuid.h

139 lines
4.0 KiB
C
Raw Permalink Normal View History

/** @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"
#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 gboolean
ws_cpuid(guint32 *CPUInfo, guint32 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 gboolean
ws_cpuid(guint32 *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 gboolean
ws_cpuid(guint32 *CPUInfo, int selector)
{
__asm__ __volatile__("cpuid"
: "=a" (CPUInfo[0]),
"=b" (CPUInfo[1]),
"=c" (CPUInfo[2]),
"=d" (CPUInfo[3])
Tweak ws_cpuid so that Valgrind is happy. Valgrind considers the "cpuid" instruction to always depend on inputs from eax and ecx, even though it's only a subset of values of eax for which ecx is relevant. If ecx is undefined when cpuid is executed, the outputs of cpuid will be considered undefined. Instead of suppressing the resulting uninitialised-value warning (the suppression for which is now out-of-date anyway, now that register_all_protocols is moved to a worker thread), let's simply set ecx to zero in ws_cpuid. Testing done: Built Wireshark on Linux amd64. Before this change, running "tools/valgrind-wireshark.sh ./test/captures/dhcp.pcap" with valgrind-3.12.0.SVN on Debian 9.4 amd64 would yield the following Valgrind error: ==2416== Thread 2: ==2416== Conditional jump or move depends on uninitialised value(s) ==2416== at 0xACB8B22: ws_mempbrk_sse42_compile (ws_mempbrk_sse42.c:58) ==2416== by 0x74F4960: register_all_protocols_worker (register.c:37) ==2416== by 0xB1403D4: g_thread_proxy (gthread.c:784) ==2416== by 0xD438493: start_thread (pthread_create.c:333) ==2416== by 0xB4CAACE: clone (clone.S:97) With the change, the above message is gone. Inspected the disassembly of function ws_cpuid, and it looks sane -- just an added "xor ecx, ecx" at the top. Change-Id: I2fb382309cac234c400286a6e9fac7d922912c63 Reviewed-on: https://code.wireshark.org/review/26733 Petri-Dish: Anders Broman <a.broman58@gmail.com> Tested-by: Petri Dish Buildbot Reviewed-by: Anders Broman <a.broman58@gmail.com>
2018-04-03 23:06:20 +00:00
: "a" (selector),
"c" (0));
return TRUE;
}
#elif defined(__i386__)
static gboolean
ws_cpuid(guint32 *CPUInfo _U_, int selector _U_)
{
/*
2020-10-10 23:42:05 +00:00
* 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 gboolean
ws_cpuid(guint32 *CPUInfo _U_, int selector _U_)
{
/* Not x86, so no cpuid instruction */
return FALSE;
}
#endif
#else /* Other compilers */
static gboolean
ws_cpuid(guint32 *CPUInfo _U_, int selector _U_)
{
return FALSE;
}
#endif
static int
ws_cpuid_sse42(void)
{
guint32 CPUInfo[4];
if (!ws_cpuid(CPUInfo, 1))
return 0;
/* in ECX bit 20 toggled on */
return (CPUInfo[2] & (1 << 20));
}