summaryrefslogtreecommitdiffstats
path: root/winpr/libwinpr/sysinfo/sysinfo.c
diff options
context:
space:
mode:
Diffstat (limited to 'winpr/libwinpr/sysinfo/sysinfo.c')
-rw-r--r--winpr/libwinpr/sysinfo/sysinfo.c1122
1 files changed, 1122 insertions, 0 deletions
diff --git a/winpr/libwinpr/sysinfo/sysinfo.c b/winpr/libwinpr/sysinfo/sysinfo.c
new file mode 100644
index 0000000..f12f4eb
--- /dev/null
+++ b/winpr/libwinpr/sysinfo/sysinfo.c
@@ -0,0 +1,1122 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * System Information
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ * Copyright 2013 Bernhard Miklautz <bernhard.miklautz@thincast.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+
+#include <winpr/sysinfo.h>
+#include <winpr/platform.h>
+
+#if defined(ANDROID)
+#include "cpufeatures/cpu-features.h"
+#endif
+
+#if defined(__linux__)
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#endif
+
+#include "../log.h"
+#define TAG WINPR_TAG("sysinfo")
+
+/**
+ * api-ms-win-core-sysinfo-l1-1-1.dll:
+ *
+ * EnumSystemFirmwareTables
+ * GetSystemFirmwareTable
+ * GetLogicalProcessorInformation
+ * GetLogicalProcessorInformationEx
+ * GetProductInfo
+ * GetSystemDirectoryA
+ * GetSystemDirectoryW
+ * GetSystemTimeAdjustment
+ * GetSystemWindowsDirectoryA
+ * GetSystemWindowsDirectoryW
+ * GetWindowsDirectoryA
+ * GetWindowsDirectoryW
+ * GlobalMemoryStatusEx
+ * SetComputerNameExW
+ * VerSetConditionMask
+ */
+
+#ifndef _WIN32
+
+#include <time.h>
+#include <sys/time.h>
+
+#ifdef WINPR_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <winpr/crt.h>
+#include <winpr/platform.h>
+
+#if defined(__MACOSX__) || defined(__IOS__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
+ defined(__OpenBSD__) || defined(__DragonFly__)
+#include <sys/sysctl.h>
+#endif
+
+static DWORD GetProcessorArchitecture(void)
+{
+ DWORD cpuArch = PROCESSOR_ARCHITECTURE_UNKNOWN;
+#if defined(ANDROID)
+ AndroidCpuFamily family = android_getCpuFamily();
+
+ switch (family)
+ {
+ case ANDROID_CPU_FAMILY_ARM:
+ return PROCESSOR_ARCHITECTURE_ARM;
+
+ case ANDROID_CPU_FAMILY_X86:
+ return PROCESSOR_ARCHITECTURE_INTEL;
+
+ case ANDROID_CPU_FAMILY_MIPS:
+ return PROCESSOR_ARCHITECTURE_MIPS;
+
+ case ANDROID_CPU_FAMILY_ARM64:
+ return PROCESSOR_ARCHITECTURE_ARM64;
+
+ case ANDROID_CPU_FAMILY_X86_64:
+ return PROCESSOR_ARCHITECTURE_AMD64;
+
+ case ANDROID_CPU_FAMILY_MIPS64:
+ return PROCESSOR_ARCHITECTURE_MIPS64;
+
+ default:
+ return PROCESSOR_ARCHITECTURE_UNKNOWN;
+ }
+
+#elif defined(_M_ARM)
+ cpuArch = PROCESSOR_ARCHITECTURE_ARM;
+#elif defined(_M_IX86)
+ cpuArch = PROCESSOR_ARCHITECTURE_INTEL;
+#elif defined(_M_MIPS64)
+ /* Needs to be before __mips__ since the compiler defines both */
+ cpuArch = PROCESSOR_ARCHITECTURE_MIPS64;
+#elif defined(_M_MIPS)
+ cpuArch = PROCESSOR_ARCHITECTURE_MIPS;
+#elif defined(_M_ARM64)
+ cpuArch = PROCESSOR_ARCHITECTURE_ARM64;
+#elif defined(_M_AMD64)
+ cpuArch = PROCESSOR_ARCHITECTURE_AMD64;
+#elif defined(_M_PPC)
+ cpuArch = PROCESSOR_ARCHITECTURE_PPC;
+#elif defined(_M_ALPHA)
+ cpuArch = PROCESSOR_ARCHITECTURE_ALPHA;
+#elif defined(_M_E2K)
+ cpuArch = PROCESSOR_ARCHITECTURE_E2K;
+#endif
+ return cpuArch;
+}
+
+static DWORD GetNumberOfProcessors(void)
+{
+ DWORD numCPUs = 1;
+#if defined(ANDROID)
+ return android_getCpuCount();
+ /* TODO: iOS */
+#elif defined(__linux__) || defined(__sun) || defined(_AIX)
+ numCPUs = (DWORD)sysconf(_SC_NPROCESSORS_ONLN);
+#elif defined(__MACOSX__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
+ defined(__OpenBSD__) || defined(__DragonFly__)
+ {
+ int mib[4];
+ size_t length = sizeof(numCPUs);
+ mib[0] = CTL_HW;
+#if defined(__FreeBSD__) || defined(__OpenBSD__)
+ mib[1] = HW_NCPU;
+#else
+ mib[1] = HW_AVAILCPU;
+#endif
+ sysctl(mib, 2, &numCPUs, &length, NULL, 0);
+
+ if (numCPUs < 1)
+ {
+ mib[1] = HW_NCPU;
+ sysctl(mib, 2, &numCPUs, &length, NULL, 0);
+
+ if (numCPUs < 1)
+ numCPUs = 1;
+ }
+ }
+#elif defined(__hpux)
+ numCPUs = (DWORD)mpctl(MPC_GETNUMSPUS, NULL, NULL);
+#elif defined(__sgi)
+ numCPUs = (DWORD)sysconf(_SC_NPROC_ONLN);
+#endif
+ return numCPUs;
+}
+
+static DWORD GetSystemPageSize(void)
+{
+ DWORD dwPageSize = 0;
+ long sc_page_size = -1;
+#if defined(_SC_PAGESIZE)
+
+ if (sc_page_size < 0)
+ sc_page_size = sysconf(_SC_PAGESIZE);
+
+#endif
+#if defined(_SC_PAGE_SIZE)
+
+ if (sc_page_size < 0)
+ sc_page_size = sysconf(_SC_PAGE_SIZE);
+
+#endif
+
+ if (sc_page_size > 0)
+ dwPageSize = (DWORD)sc_page_size;
+
+ if (dwPageSize < 4096)
+ dwPageSize = 4096;
+
+ return dwPageSize;
+}
+
+void GetSystemInfo(LPSYSTEM_INFO lpSystemInfo)
+{
+ lpSystemInfo->wProcessorArchitecture = GetProcessorArchitecture();
+ lpSystemInfo->wReserved = 0;
+ lpSystemInfo->dwPageSize = GetSystemPageSize();
+ lpSystemInfo->lpMinimumApplicationAddress = NULL;
+ lpSystemInfo->lpMaximumApplicationAddress = NULL;
+ lpSystemInfo->dwActiveProcessorMask = 0;
+ lpSystemInfo->dwNumberOfProcessors = GetNumberOfProcessors();
+ lpSystemInfo->dwProcessorType = 0;
+ lpSystemInfo->dwAllocationGranularity = 0;
+ lpSystemInfo->wProcessorLevel = 0;
+ lpSystemInfo->wProcessorRevision = 0;
+}
+
+void GetNativeSystemInfo(LPSYSTEM_INFO lpSystemInfo)
+{
+ GetSystemInfo(lpSystemInfo);
+}
+
+void GetSystemTime(LPSYSTEMTIME lpSystemTime)
+{
+ time_t ct = 0;
+ struct tm tres;
+ struct tm* stm = NULL;
+ WORD wMilliseconds = 0;
+ ct = time(NULL);
+ wMilliseconds = (WORD)(GetTickCount() % 1000);
+ stm = gmtime_r(&ct, &tres);
+ ZeroMemory(lpSystemTime, sizeof(SYSTEMTIME));
+
+ if (stm)
+ {
+ lpSystemTime->wYear = (WORD)(stm->tm_year + 1900);
+ lpSystemTime->wMonth = (WORD)(stm->tm_mon + 1);
+ lpSystemTime->wDayOfWeek = (WORD)stm->tm_wday;
+ lpSystemTime->wDay = (WORD)stm->tm_mday;
+ lpSystemTime->wHour = (WORD)stm->tm_hour;
+ lpSystemTime->wMinute = (WORD)stm->tm_min;
+ lpSystemTime->wSecond = (WORD)stm->tm_sec;
+ lpSystemTime->wMilliseconds = wMilliseconds;
+ }
+}
+
+BOOL SetSystemTime(CONST SYSTEMTIME* lpSystemTime)
+{
+ /* TODO: Implement */
+ return FALSE;
+}
+
+VOID GetLocalTime(LPSYSTEMTIME lpSystemTime)
+{
+ time_t ct = 0;
+ struct tm tres;
+ struct tm* ltm = NULL;
+ WORD wMilliseconds = 0;
+ ct = time(NULL);
+ wMilliseconds = (WORD)(GetTickCount() % 1000);
+ ltm = localtime_r(&ct, &tres);
+ ZeroMemory(lpSystemTime, sizeof(SYSTEMTIME));
+
+ if (ltm)
+ {
+ lpSystemTime->wYear = (WORD)(ltm->tm_year + 1900);
+ lpSystemTime->wMonth = (WORD)(ltm->tm_mon + 1);
+ lpSystemTime->wDayOfWeek = (WORD)ltm->tm_wday;
+ lpSystemTime->wDay = (WORD)ltm->tm_mday;
+ lpSystemTime->wHour = (WORD)ltm->tm_hour;
+ lpSystemTime->wMinute = (WORD)ltm->tm_min;
+ lpSystemTime->wSecond = (WORD)ltm->tm_sec;
+ lpSystemTime->wMilliseconds = wMilliseconds;
+ }
+}
+
+BOOL SetLocalTime(CONST SYSTEMTIME* lpSystemTime)
+{
+ /* TODO: Implement */
+ return FALSE;
+}
+
+VOID GetSystemTimeAsFileTime(LPFILETIME lpSystemTimeAsFileTime)
+{
+ ULARGE_INTEGER time64;
+ time64.u.HighPart = 0;
+ /* time represented in tenths of microseconds since midnight of January 1, 1601 */
+ time64.QuadPart = time(NULL) + 11644473600LL; /* Seconds since January 1, 1601 */
+ time64.QuadPart *= 10000000; /* Convert timestamp to tenths of a microsecond */
+ lpSystemTimeAsFileTime->dwLowDateTime = time64.u.LowPart;
+ lpSystemTimeAsFileTime->dwHighDateTime = time64.u.HighPart;
+}
+
+BOOL GetSystemTimeAdjustment(PDWORD lpTimeAdjustment, PDWORD lpTimeIncrement,
+ PBOOL lpTimeAdjustmentDisabled)
+{
+ /* TODO: Implement */
+ return FALSE;
+}
+
+#ifndef CLOCK_MONOTONIC_RAW
+#define CLOCK_MONOTONIC_RAW 4
+#endif
+
+DWORD GetTickCount(void)
+{
+ DWORD ticks = 0;
+#ifdef __linux__
+ struct timespec ts;
+
+ if (!clock_gettime(CLOCK_MONOTONIC_RAW, &ts))
+ ticks = (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000);
+
+#else
+ /**
+ * FIXME: this is relative to the Epoch time, and we
+ * need to return a value relative to the system uptime.
+ */
+ struct timeval tv;
+
+ if (!gettimeofday(&tv, NULL))
+ ticks = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+
+#endif
+ return ticks;
+}
+#endif // _WIN32
+
+#if !defined(_WIN32) || defined(_UWP)
+
+#if defined(WITH_WINPR_DEPRECATED)
+/* OSVERSIONINFOEX Structure:
+ * http://msdn.microsoft.com/en-us/library/windows/desktop/ms724833
+ */
+
+BOOL GetVersionExA(LPOSVERSIONINFOA lpVersionInformation)
+{
+#ifdef _UWP
+
+ /* Windows 10 Version Info */
+ if ((lpVersionInformation->dwOSVersionInfoSize == sizeof(OSVERSIONINFOA)) ||
+ (lpVersionInformation->dwOSVersionInfoSize == sizeof(OSVERSIONINFOEXA)))
+ {
+ lpVersionInformation->dwMajorVersion = 10;
+ lpVersionInformation->dwMinorVersion = 0;
+ lpVersionInformation->dwBuildNumber = 0;
+ lpVersionInformation->dwPlatformId = VER_PLATFORM_WIN32_NT;
+ ZeroMemory(lpVersionInformation->szCSDVersion, sizeof(lpVersionInformation->szCSDVersion));
+
+ if (lpVersionInformation->dwOSVersionInfoSize == sizeof(OSVERSIONINFOEXA))
+ {
+ LPOSVERSIONINFOEXA lpVersionInformationEx = (LPOSVERSIONINFOEXA)lpVersionInformation;
+ lpVersionInformationEx->wServicePackMajor = 0;
+ lpVersionInformationEx->wServicePackMinor = 0;
+ lpVersionInformationEx->wSuiteMask = 0;
+ lpVersionInformationEx->wProductType = VER_NT_WORKSTATION;
+ lpVersionInformationEx->wReserved = 0;
+ }
+
+ return TRUE;
+ }
+
+#else
+
+ /* Windows 7 SP1 Version Info */
+ if ((lpVersionInformation->dwOSVersionInfoSize == sizeof(OSVERSIONINFOA)) ||
+ (lpVersionInformation->dwOSVersionInfoSize == sizeof(OSVERSIONINFOEXA)))
+ {
+ lpVersionInformation->dwMajorVersion = 6;
+ lpVersionInformation->dwMinorVersion = 1;
+ lpVersionInformation->dwBuildNumber = 7601;
+ lpVersionInformation->dwPlatformId = VER_PLATFORM_WIN32_NT;
+ ZeroMemory(lpVersionInformation->szCSDVersion, sizeof(lpVersionInformation->szCSDVersion));
+
+ if (lpVersionInformation->dwOSVersionInfoSize == sizeof(OSVERSIONINFOEXA))
+ {
+ LPOSVERSIONINFOEXA lpVersionInformationEx = (LPOSVERSIONINFOEXA)lpVersionInformation;
+ lpVersionInformationEx->wServicePackMajor = 1;
+ lpVersionInformationEx->wServicePackMinor = 0;
+ lpVersionInformationEx->wSuiteMask = 0;
+ lpVersionInformationEx->wProductType = VER_NT_WORKSTATION;
+ lpVersionInformationEx->wReserved = 0;
+ }
+
+ return TRUE;
+ }
+
+#endif
+ return FALSE;
+}
+
+BOOL GetVersionExW(LPOSVERSIONINFOW lpVersionInformation)
+{
+ ZeroMemory(lpVersionInformation->szCSDVersion, sizeof(lpVersionInformation->szCSDVersion));
+ return GetVersionExA((LPOSVERSIONINFOA)lpVersionInformation);
+}
+
+#endif
+
+#endif
+
+#if !defined(_WIN32) || defined(_UWP)
+
+BOOL GetComputerNameW(LPWSTR lpBuffer, LPDWORD lpnSize)
+{
+ BOOL rc = 0;
+ LPSTR buffer = NULL;
+ if (!lpnSize || (*lpnSize > INT_MAX))
+ return FALSE;
+
+ if (*lpnSize > 0)
+ {
+ buffer = malloc(*lpnSize);
+ if (!buffer)
+ return FALSE;
+ }
+ rc = GetComputerNameA(buffer, lpnSize);
+
+ if (rc && (*lpnSize > 0))
+ {
+ const SSIZE_T res = ConvertUtf8NToWChar(buffer, *lpnSize, lpBuffer, *lpnSize);
+ rc = res > 0;
+ }
+
+ free(buffer);
+
+ return rc;
+}
+
+BOOL GetComputerNameA(LPSTR lpBuffer, LPDWORD lpnSize)
+{
+ char* dot = NULL;
+ size_t length = 0;
+ char hostname[256] = { 0 };
+
+ if (!lpnSize)
+ {
+ SetLastError(ERROR_BAD_ARGUMENTS);
+ return FALSE;
+ }
+
+ if (gethostname(hostname, sizeof(hostname)) == -1)
+ return FALSE;
+
+ length = strnlen(hostname, sizeof(hostname));
+ dot = strchr(hostname, '.');
+
+ if (dot)
+ length = (dot - hostname);
+
+ if ((*lpnSize <= (DWORD)length) || !lpBuffer)
+ {
+ SetLastError(ERROR_BUFFER_OVERFLOW);
+ *lpnSize = (DWORD)(length + 1);
+ return FALSE;
+ }
+
+ CopyMemory(lpBuffer, hostname, length);
+ lpBuffer[length] = '\0';
+ *lpnSize = (DWORD)length;
+ return TRUE;
+}
+
+BOOL GetComputerNameExA(COMPUTER_NAME_FORMAT NameType, LPSTR lpBuffer, LPDWORD lpnSize)
+{
+ size_t length = 0;
+ char hostname[256] = { 0 };
+
+ if (!lpnSize)
+ {
+ SetLastError(ERROR_BAD_ARGUMENTS);
+ return FALSE;
+ }
+
+ if ((NameType == ComputerNameNetBIOS) || (NameType == ComputerNamePhysicalNetBIOS))
+ {
+ BOOL rc = GetComputerNameA(lpBuffer, lpnSize);
+
+ if (!rc)
+ {
+ if (GetLastError() == ERROR_BUFFER_OVERFLOW)
+ SetLastError(ERROR_MORE_DATA);
+ }
+
+ return rc;
+ }
+
+ if (gethostname(hostname, sizeof(hostname)) == -1)
+ return FALSE;
+
+ length = strnlen(hostname, sizeof(hostname));
+
+ switch (NameType)
+ {
+ case ComputerNameDnsHostname:
+ case ComputerNameDnsDomain:
+ case ComputerNameDnsFullyQualified:
+ case ComputerNamePhysicalDnsHostname:
+ case ComputerNamePhysicalDnsDomain:
+ case ComputerNamePhysicalDnsFullyQualified:
+ if ((*lpnSize <= (DWORD)length) || !lpBuffer)
+ {
+ *lpnSize = (DWORD)(length + 1);
+ SetLastError(ERROR_MORE_DATA);
+ return FALSE;
+ }
+
+ CopyMemory(lpBuffer, hostname, length);
+ lpBuffer[length] = '\0';
+ *lpnSize = (DWORD)length;
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL GetComputerNameExW(COMPUTER_NAME_FORMAT NameType, LPWSTR lpBuffer, LPDWORD lpnSize)
+{
+ BOOL rc = 0;
+ LPSTR lpABuffer = NULL;
+
+ if (!lpnSize)
+ {
+ SetLastError(ERROR_BAD_ARGUMENTS);
+ return FALSE;
+ }
+
+ if (*lpnSize > 0)
+ {
+ lpABuffer = calloc(*lpnSize, sizeof(CHAR));
+
+ if (!lpABuffer)
+ return FALSE;
+ }
+
+ rc = GetComputerNameExA(NameType, lpABuffer, lpnSize);
+
+ if (rc && (*lpnSize > 0))
+ {
+ const SSIZE_T res = ConvertUtf8NToWChar(lpABuffer, *lpnSize, lpBuffer, *lpnSize);
+ rc = res > 0;
+ }
+
+ free(lpABuffer);
+ return rc;
+}
+
+#endif
+
+#if defined(_UWP)
+
+DWORD GetTickCount(void)
+{
+ return (DWORD)GetTickCount64();
+}
+
+#endif
+
+#if (!defined(_WIN32)) || (defined(_WIN32) && (_WIN32_WINNT < 0x0600))
+
+ULONGLONG winpr_GetTickCount64(void)
+{
+ ULONGLONG ticks = 0;
+#if defined(__linux__)
+ struct timespec ts;
+
+ if (!clock_gettime(CLOCK_MONOTONIC_RAW, &ts))
+ ticks = (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000);
+
+#elif defined(_WIN32)
+ FILETIME ft;
+ ULARGE_INTEGER ul;
+ GetSystemTimeAsFileTime(&ft);
+ ul.LowPart = ft.dwLowDateTime;
+ ul.HighPart = ft.dwHighDateTime;
+ ticks = ul.QuadPart;
+#else
+ /**
+ * FIXME: this is relative to the Epoch time, and we
+ * need to return a value relative to the system uptime.
+ */
+ struct timeval tv;
+
+ if (!gettimeofday(&tv, NULL))
+ ticks = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+
+#endif
+ return ticks;
+}
+
+#endif
+
+/* If x86 */
+#ifdef _M_IX86_AMD64
+
+#if defined(__GNUC__)
+#define xgetbv(_func_, _lo_, _hi_) \
+ __asm__ __volatile__("xgetbv" : "=a"(_lo_), "=d"(_hi_) : "c"(_func_))
+#elif defined(_MSC_VER)
+#define xgetbv(_func_, _lo_, _hi_) \
+ { \
+ unsigned __int64 val = _xgetbv(_func_); \
+ _lo_ = val & 0xFFFFFFFF; \
+ _hi_ = (val >> 32); \
+ }
+#endif
+
+#define B_BIT_AVX2 (1 << 5)
+#define B_BIT_AVX512F (1 << 16)
+#define D_BIT_MMX (1 << 23)
+#define D_BIT_SSE (1 << 25)
+#define D_BIT_SSE2 (1 << 26)
+#define D_BIT_3DN (1 << 30)
+#define C_BIT_SSE3 (1 << 0)
+#define C_BIT_PCLMULQDQ (1 << 1)
+#define C81_BIT_LZCNT (1 << 5)
+#define C_BIT_3DNP (1 << 8)
+#define C_BIT_3DNP (1 << 8)
+#define C_BIT_SSSE3 (1 << 9)
+#define C_BIT_SSE41 (1 << 19)
+#define C_BIT_SSE42 (1 << 20)
+#define C_BIT_FMA (1 << 12)
+#define C_BIT_AES (1 << 25)
+#define C_BIT_XGETBV (1 << 27)
+#define C_BIT_AVX (1 << 28)
+#define E_BIT_XMM (1 << 1)
+#define E_BIT_YMM (1 << 2)
+#define E_BITS_AVX (E_BIT_XMM | E_BIT_YMM)
+
+static void cpuid(unsigned info, unsigned* eax, unsigned* ebx, unsigned* ecx, unsigned* edx)
+{
+#ifdef __GNUC__
+ *eax = *ebx = *ecx = *edx = 0;
+ __asm volatile(
+ /* The EBX (or RBX register on x86_64) is used for the PIC base address
+ * and must not be corrupted by our inline assembly.
+ */
+#ifdef _M_IX86
+ "mov %%ebx, %%esi;"
+ "cpuid;"
+ "xchg %%ebx, %%esi;"
+#else
+ "mov %%rbx, %%rsi;"
+ "cpuid;"
+ "xchg %%rbx, %%rsi;"
+#endif
+ : "=a"(*eax), "=S"(*ebx), "=c"(*ecx), "=d"(*edx)
+ : "a"(info), "c"(0));
+#elif defined(_MSC_VER)
+ int a[4];
+ __cpuid(a, info);
+ *eax = a[0];
+ *ebx = a[1];
+ *ecx = a[2];
+ *edx = a[3];
+#endif
+}
+#elif defined(_M_ARM)
+#if defined(__linux__)
+// HWCAP flags from linux kernel - uapi/asm/hwcap.h
+#define HWCAP_SWP (1 << 0)
+#define HWCAP_HALF (1 << 1)
+#define HWCAP_THUMB (1 << 2)
+#define HWCAP_26BIT (1 << 3) /* Play it safe */
+#define HWCAP_FAST_MULT (1 << 4)
+#define HWCAP_FPA (1 << 5)
+#define HWCAP_VFP (1 << 6)
+#define HWCAP_EDSP (1 << 7)
+#define HWCAP_JAVA (1 << 8)
+#define HWCAP_IWMMXT (1 << 9)
+#define HWCAP_CRUNCH (1 << 10)
+#define HWCAP_THUMBEE (1 << 11)
+#define HWCAP_NEON (1 << 12)
+#define HWCAP_VFPv3 (1 << 13)
+#define HWCAP_VFPv3D16 (1 << 14) /* also set for VFPv4-D16 */
+#define HWCAP_TLS (1 << 15)
+#define HWCAP_VFPv4 (1 << 16)
+#define HWCAP_IDIVA (1 << 17)
+#define HWCAP_IDIVT (1 << 18)
+#define HWCAP_VFPD32 (1 << 19) /* set if VFP has 32 regs (not 16) */
+#define HWCAP_IDIV (HWCAP_IDIVA | HWCAP_IDIVT)
+
+// From linux kernel uapi/linux/auxvec.h
+#define AT_HWCAP 16
+
+static unsigned GetARMCPUCaps(void)
+{
+ unsigned caps = 0;
+ int fd = open("/proc/self/auxv", O_RDONLY);
+
+ if (fd == -1)
+ return 0;
+
+ static struct
+ {
+ unsigned a_type; /* Entry type */
+ unsigned a_val; /* Integer value */
+ } auxvec;
+
+ while (1)
+ {
+ int num;
+ num = read(fd, (char*)&auxvec, sizeof(auxvec));
+
+ if (num < 1 || (auxvec.a_type == 0 && auxvec.a_val == 0))
+ break;
+
+ if (auxvec.a_type == AT_HWCAP)
+ {
+ caps = auxvec.a_val;
+ }
+ }
+
+ close(fd);
+ return caps;
+}
+
+#endif // defined(__linux__)
+#endif // _M_IX86_AMD64
+
+#ifndef _WIN32
+
+BOOL IsProcessorFeaturePresent(DWORD ProcessorFeature)
+{
+ BOOL ret = FALSE;
+#if defined(ANDROID)
+ const uint64_t features = android_getCpuFeatures();
+
+ switch (ProcessorFeature)
+ {
+ case PF_ARM_NEON_INSTRUCTIONS_AVAILABLE:
+ case PF_ARM_NEON:
+ return features & ANDROID_CPU_ARM_FEATURE_NEON;
+
+ default:
+ return FALSE;
+ }
+
+#elif defined(_M_ARM)
+#ifdef __linux__
+ const unsigned caps = GetARMCPUCaps();
+
+ switch (ProcessorFeature)
+ {
+ case PF_ARM_NEON_INSTRUCTIONS_AVAILABLE:
+ case PF_ARM_NEON:
+ if (caps & HWCAP_NEON)
+ ret = TRUE;
+
+ break;
+
+ case PF_ARM_THUMB:
+ if (caps & HWCAP_THUMB)
+ ret = TRUE;
+
+ case PF_ARM_VFP_32_REGISTERS_AVAILABLE:
+ if (caps & HWCAP_VFPD32)
+ ret = TRUE;
+
+ case PF_ARM_DIVIDE_INSTRUCTION_AVAILABLE:
+ if ((caps & HWCAP_IDIVA) || (caps & HWCAP_IDIVT))
+ ret = TRUE;
+
+ case PF_ARM_VFP3:
+ if (caps & HWCAP_VFPv3)
+ ret = TRUE;
+
+ break;
+
+ case PF_ARM_JAZELLE:
+ if (caps & HWCAP_JAVA)
+ ret = TRUE;
+
+ break;
+
+ case PF_ARM_DSP:
+ if (caps & HWCAP_EDSP)
+ ret = TRUE;
+
+ break;
+
+ case PF_ARM_MPU:
+ if (caps & HWCAP_EDSP)
+ ret = TRUE;
+
+ break;
+
+ case PF_ARM_THUMB2:
+ if ((caps & HWCAP_IDIVT) || (caps & HWCAP_VFPv4))
+ ret = TRUE;
+
+ break;
+
+ case PF_ARM_T2EE:
+ if (caps & HWCAP_THUMBEE)
+ ret = TRUE;
+
+ break;
+
+ case PF_ARM_INTEL_WMMX:
+ if (caps & HWCAP_IWMMXT)
+ ret = TRUE;
+
+ break;
+
+ default:
+ break;
+ }
+
+#else // __linux__
+
+ switch (ProcessorFeature)
+ {
+ case PF_ARM_NEON_INSTRUCTIONS_AVAILABLE:
+ case PF_ARM_NEON:
+#ifdef __ARM_NEON
+ ret = TRUE;
+#endif
+ break;
+ default:
+ break;
+ }
+
+#endif // __linux__
+#elif defined(_M_IX86_AMD64)
+#ifdef __GNUC__
+ unsigned a = 0;
+ unsigned b = 0;
+ unsigned c = 0;
+ unsigned d = 0;
+ cpuid(1, &a, &b, &c, &d);
+
+ switch (ProcessorFeature)
+ {
+ case PF_MMX_INSTRUCTIONS_AVAILABLE:
+ if (d & D_BIT_MMX)
+ ret = TRUE;
+
+ break;
+
+ case PF_XMMI_INSTRUCTIONS_AVAILABLE:
+ if (d & D_BIT_SSE)
+ ret = TRUE;
+
+ break;
+
+ case PF_XMMI64_INSTRUCTIONS_AVAILABLE:
+ if (d & D_BIT_SSE2)
+ ret = TRUE;
+
+ break;
+
+ case PF_3DNOW_INSTRUCTIONS_AVAILABLE:
+ if (d & D_BIT_3DN)
+ ret = TRUE;
+
+ break;
+
+ case PF_SSE3_INSTRUCTIONS_AVAILABLE:
+ if (c & C_BIT_SSE3)
+ ret = TRUE;
+
+ break;
+
+ default:
+ break;
+ }
+
+#endif // __GNUC__
+#elif defined(_M_E2K)
+ /* compiler flags on e2k arch determine CPU features */
+ switch (ProcessorFeature)
+ {
+ case PF_MMX_INSTRUCTIONS_AVAILABLE:
+#ifdef __MMX__
+ ret = TRUE;
+#endif
+ break;
+
+ case PF_3DNOW_INSTRUCTIONS_AVAILABLE:
+#ifdef __3dNOW__
+ ret = TRUE;
+#endif
+ break;
+
+ case PF_SSE3_INSTRUCTIONS_AVAILABLE:
+#ifdef __SSE3__
+ ret = TRUE;
+#endif
+ break;
+
+ default:
+ break;
+ }
+
+#endif
+ return ret;
+}
+
+#endif //_WIN32
+
+DWORD GetTickCountPrecise(void)
+{
+#ifdef _WIN32
+ LARGE_INTEGER freq;
+ LARGE_INTEGER current;
+ QueryPerformanceFrequency(&freq);
+ QueryPerformanceCounter(&current);
+ return (DWORD)(current.QuadPart * 1000LL / freq.QuadPart);
+#else
+ return GetTickCount();
+#endif
+}
+
+BOOL IsProcessorFeaturePresentEx(DWORD ProcessorFeature)
+{
+ BOOL ret = FALSE;
+#ifdef _M_ARM
+#ifdef __linux__
+ unsigned caps;
+ caps = GetARMCPUCaps();
+
+ switch (ProcessorFeature)
+ {
+ case PF_EX_ARM_VFP1:
+ if (caps & HWCAP_VFP)
+ ret = TRUE;
+
+ break;
+
+ case PF_EX_ARM_VFP3D16:
+ if (caps & HWCAP_VFPv3D16)
+ ret = TRUE;
+
+ break;
+
+ case PF_EX_ARM_VFP4:
+ if (caps & HWCAP_VFPv4)
+ ret = TRUE;
+
+ break;
+
+ case PF_EX_ARM_IDIVA:
+ if (caps & HWCAP_IDIVA)
+ ret = TRUE;
+
+ break;
+
+ case PF_EX_ARM_IDIVT:
+ if (caps & HWCAP_IDIVT)
+ ret = TRUE;
+
+ break;
+ }
+
+#endif // __linux__
+#elif defined(_M_IX86_AMD64)
+ unsigned a = 0;
+ unsigned b = 0;
+ unsigned c = 0;
+ unsigned d = 0;
+ cpuid(1, &a, &b, &c, &d);
+
+ switch (ProcessorFeature)
+ {
+ case PF_EX_LZCNT:
+ {
+ unsigned a81 = 0;
+ unsigned b81 = 0;
+ unsigned c81 = 0;
+ unsigned d81 = 0;
+ cpuid(0x80000001, &a81, &b81, &c81, &d81);
+
+ if (c81 & C81_BIT_LZCNT)
+ ret = TRUE;
+ }
+ break;
+
+ case PF_EX_3DNOW_PREFETCH:
+ if (c & C_BIT_3DNP)
+ ret = TRUE;
+
+ break;
+
+ case PF_EX_SSSE3:
+ if (c & C_BIT_SSSE3)
+ ret = TRUE;
+
+ break;
+
+ case PF_EX_SSE41:
+ if (c & C_BIT_SSE41)
+ ret = TRUE;
+
+ break;
+
+ case PF_EX_SSE42:
+ if (c & C_BIT_SSE42)
+ ret = TRUE;
+
+ break;
+#if defined(__GNUC__) || defined(_MSC_VER)
+
+ case PF_EX_AVX:
+ case PF_EX_AVX2:
+ case PF_EX_AVX512F:
+ case PF_EX_FMA:
+ case PF_EX_AVX_AES:
+ case PF_EX_AVX_PCLMULQDQ:
+ {
+ /* Check for general AVX support */
+ if (!(c & C_BIT_AVX))
+ break;
+
+ /* Check for xgetbv support */
+ if (!(c & C_BIT_XGETBV))
+ break;
+
+ int e = 0;
+ int f = 0;
+ xgetbv(0, e, f);
+
+ /* XGETBV enabled for applications and XMM/YMM states enabled */
+ if ((e & E_BITS_AVX) == E_BITS_AVX)
+ {
+ switch (ProcessorFeature)
+ {
+ case PF_EX_AVX:
+ ret = TRUE;
+ break;
+
+ case PF_EX_AVX2:
+ case PF_EX_AVX512F:
+ cpuid(7, &a, &b, &c, &d);
+ switch (ProcessorFeature)
+ {
+ case PF_EX_AVX2:
+ if (b & B_BIT_AVX2)
+ ret = TRUE;
+ break;
+
+ case PF_EX_AVX512F:
+ if (b & B_BIT_AVX512F)
+ ret = TRUE;
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case PF_EX_FMA:
+ if (c & C_BIT_FMA)
+ ret = TRUE;
+
+ break;
+
+ case PF_EX_AVX_AES:
+ if (c & C_BIT_AES)
+ ret = TRUE;
+
+ break;
+
+ case PF_EX_AVX_PCLMULQDQ:
+ if (c & C_BIT_PCLMULQDQ)
+ ret = TRUE;
+
+ break;
+ }
+ }
+ }
+ break;
+#endif // __GNUC__ || _MSC_VER
+
+ default:
+ break;
+ }
+#elif defined(_M_E2K)
+ /* compiler flags on e2k arch determine CPU features */
+ switch (ProcessorFeature)
+ {
+ case PF_EX_LZCNT:
+#ifdef __LZCNT__
+ ret = TRUE;
+#endif
+ break;
+
+ case PF_EX_SSSE3:
+#ifdef __SSSE3__
+ ret = TRUE;
+#endif
+ break;
+
+ case PF_EX_SSE41:
+#ifdef __SSE4_1__
+ ret = TRUE;
+#endif
+ break;
+
+ case PF_EX_SSE42:
+#ifdef __SSE4_2__
+ ret = TRUE;
+#endif
+ break;
+
+ case PF_EX_AVX:
+#ifdef __AVX__
+ ret = TRUE;
+#endif
+ break;
+
+ case PF_EX_AVX2:
+#ifdef __AVX2__
+ ret = TRUE;
+#endif
+ break;
+
+ case PF_EX_FMA:
+#ifdef __FMA__
+ ret = TRUE;
+#endif
+ break;
+
+ default:
+ break;
+ }
+#endif
+ return ret;
+}