FreeRDP
sysinfo.c
1 
21 #include <winpr/config.h>
22 
23 #include <winpr/assert.h>
24 #include <winpr/sysinfo.h>
25 #include <winpr/platform.h>
26 
27 #if defined(ANDROID)
28 #include "cpufeatures/cpu-features.h"
29 #endif
30 
31 #if defined(__linux__)
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35 #endif
36 
37 #if !defined(_WIN32)
38 #if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 199309L)
39 #include <time.h>
40 #elif !defined(__APPLE__)
41 #include <sys/time.h>
42 #include <sys/sysinfo.h>
43 #endif
44 #endif
45 
46 #include "../log.h"
47 #define TAG WINPR_TAG("sysinfo")
48 
49 #define FILETIME_TO_UNIX_OFFSET_S 11644473600UL
50 
51 #if defined(__MACH__) && defined(__APPLE__)
52 
53 #include <mach/mach_time.h>
54 
55 static UINT64 scaleHighPrecision(UINT64 i, UINT32 numer, UINT32 denom)
56 {
57  UINT64 high = (i >> 32) * numer;
58  UINT64 low = (i & 0xffffffffull) * numer / denom;
59  UINT64 highRem = ((high % denom) << 32) / denom;
60  high /= denom;
61  return (high << 32) + highRem + low;
62 }
63 
64 static UINT64 mac_get_time_ns(void)
65 {
66  mach_timebase_info_data_t timebase = { 0 };
67  mach_timebase_info(&timebase);
68  UINT64 t = mach_absolute_time();
69  return scaleHighPrecision(t, timebase.numer, timebase.denom);
70 }
71 
72 #endif
73 
94 #ifndef _WIN32
95 
96 #include <time.h>
97 #include <sys/time.h>
98 
99 #ifdef WINPR_HAVE_UNISTD_H
100 #include <unistd.h>
101 #endif
102 
103 #include <winpr/crt.h>
104 #include <winpr/platform.h>
105 
106 #if defined(__MACOSX__) || defined(__IOS__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
107  defined(__OpenBSD__) || defined(__DragonFly__)
108 #include <sys/sysctl.h>
109 #endif
110 
111 static WORD GetProcessorArchitecture(void)
112 {
113  WORD cpuArch = PROCESSOR_ARCHITECTURE_UNKNOWN;
114 #if defined(ANDROID)
115  AndroidCpuFamily family = android_getCpuFamily();
116 
117  switch (family)
118  {
119  case ANDROID_CPU_FAMILY_ARM:
120  return PROCESSOR_ARCHITECTURE_ARM;
121 
122  case ANDROID_CPU_FAMILY_X86:
123  return PROCESSOR_ARCHITECTURE_INTEL;
124 
125  case ANDROID_CPU_FAMILY_MIPS:
126  return PROCESSOR_ARCHITECTURE_MIPS;
127 
128  case ANDROID_CPU_FAMILY_ARM64:
129  return PROCESSOR_ARCHITECTURE_ARM64;
130 
131  case ANDROID_CPU_FAMILY_X86_64:
132  return PROCESSOR_ARCHITECTURE_AMD64;
133 
134  case ANDROID_CPU_FAMILY_MIPS64:
135  return PROCESSOR_ARCHITECTURE_MIPS64;
136 
137  default:
138  return PROCESSOR_ARCHITECTURE_UNKNOWN;
139  }
140 
141 #elif defined(_M_ARM)
142  cpuArch = PROCESSOR_ARCHITECTURE_ARM;
143 #elif defined(_M_IX86)
144  cpuArch = PROCESSOR_ARCHITECTURE_INTEL;
145 #elif defined(_M_MIPS64)
146  /* Needs to be before __mips__ since the compiler defines both */
147  cpuArch = PROCESSOR_ARCHITECTURE_MIPS64;
148 #elif defined(_M_MIPS)
149  cpuArch = PROCESSOR_ARCHITECTURE_MIPS;
150 #elif defined(_M_ARM64)
151  cpuArch = PROCESSOR_ARCHITECTURE_ARM64;
152 #elif defined(_M_AMD64)
153  cpuArch = PROCESSOR_ARCHITECTURE_AMD64;
154 #elif defined(_M_PPC)
155  cpuArch = PROCESSOR_ARCHITECTURE_PPC;
156 #elif defined(_M_ALPHA)
157  cpuArch = PROCESSOR_ARCHITECTURE_ALPHA;
158 #elif defined(_M_E2K)
159  cpuArch = PROCESSOR_ARCHITECTURE_E2K;
160 #endif
161  return cpuArch;
162 }
163 
164 static DWORD GetNumberOfProcessors(void)
165 {
166  DWORD numCPUs = 1;
167 #if defined(ANDROID)
168  return android_getCpuCount();
169  /* TODO: iOS */
170 #elif defined(__linux__) || defined(__sun) || defined(_AIX)
171  numCPUs = (DWORD)sysconf(_SC_NPROCESSORS_ONLN);
172 #elif defined(__MACOSX__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
173  defined(__OpenBSD__) || defined(__DragonFly__)
174  {
175  int mib[4];
176  size_t length = sizeof(numCPUs);
177  mib[0] = CTL_HW;
178 #if defined(__FreeBSD__) || defined(__OpenBSD__)
179  mib[1] = HW_NCPU;
180 #else
181  mib[1] = HW_AVAILCPU;
182 #endif
183  sysctl(mib, 2, &numCPUs, &length, NULL, 0);
184 
185  if (numCPUs < 1)
186  {
187  mib[1] = HW_NCPU;
188  sysctl(mib, 2, &numCPUs, &length, NULL, 0);
189 
190  if (numCPUs < 1)
191  numCPUs = 1;
192  }
193  }
194 #elif defined(__hpux)
195  numCPUs = (DWORD)mpctl(MPC_GETNUMSPUS, NULL, NULL);
196 #elif defined(__sgi)
197  numCPUs = (DWORD)sysconf(_SC_NPROC_ONLN);
198 #endif
199  return numCPUs;
200 }
201 
202 static DWORD GetSystemPageSize(void)
203 {
204  DWORD dwPageSize = 0;
205  long sc_page_size = -1;
206 #if defined(_SC_PAGESIZE)
207 
208  if (sc_page_size < 0)
209  sc_page_size = sysconf(_SC_PAGESIZE);
210 
211 #endif
212 #if defined(_SC_PAGE_SIZE)
213 
214  if (sc_page_size < 0)
215  sc_page_size = sysconf(_SC_PAGE_SIZE);
216 
217 #endif
218 
219  if (sc_page_size > 0)
220  dwPageSize = (DWORD)sc_page_size;
221 
222  if (dwPageSize < 4096)
223  dwPageSize = 4096;
224 
225  return dwPageSize;
226 }
227 
228 void GetSystemInfo(LPSYSTEM_INFO lpSystemInfo)
229 {
230  const SYSTEM_INFO empty = { 0 };
231  WINPR_ASSERT(lpSystemInfo);
232 
233  *lpSystemInfo = empty;
234  lpSystemInfo->DUMMYUNIONNAME.DUMMYSTRUCTNAME.wProcessorArchitecture =
235  GetProcessorArchitecture();
236  lpSystemInfo->dwPageSize = GetSystemPageSize();
237  lpSystemInfo->dwNumberOfProcessors = GetNumberOfProcessors();
238 }
239 
240 void GetNativeSystemInfo(LPSYSTEM_INFO lpSystemInfo)
241 {
242  GetSystemInfo(lpSystemInfo);
243 }
244 
245 void GetSystemTime(LPSYSTEMTIME lpSystemTime)
246 {
247  time_t ct = 0;
248  struct tm tres;
249  struct tm* stm = NULL;
250  WORD wMilliseconds = 0;
251  ct = time(NULL);
252  wMilliseconds = (WORD)(GetTickCount() % 1000);
253  stm = gmtime_r(&ct, &tres);
254  ZeroMemory(lpSystemTime, sizeof(SYSTEMTIME));
255 
256  if (stm)
257  {
258  lpSystemTime->wYear = (WORD)(stm->tm_year + 1900);
259  lpSystemTime->wMonth = (WORD)(stm->tm_mon + 1);
260  lpSystemTime->wDayOfWeek = (WORD)stm->tm_wday;
261  lpSystemTime->wDay = (WORD)stm->tm_mday;
262  lpSystemTime->wHour = (WORD)stm->tm_hour;
263  lpSystemTime->wMinute = (WORD)stm->tm_min;
264  lpSystemTime->wSecond = (WORD)stm->tm_sec;
265  lpSystemTime->wMilliseconds = wMilliseconds;
266  }
267 }
268 
269 BOOL SetSystemTime(CONST SYSTEMTIME* lpSystemTime)
270 {
271  /* TODO: Implement */
272  return FALSE;
273 }
274 
275 VOID GetLocalTime(LPSYSTEMTIME lpSystemTime)
276 {
277  time_t ct = 0;
278  struct tm tres;
279  struct tm* ltm = NULL;
280  WORD wMilliseconds = 0;
281  ct = time(NULL);
282  wMilliseconds = (WORD)(GetTickCount() % 1000);
283  ltm = localtime_r(&ct, &tres);
284  ZeroMemory(lpSystemTime, sizeof(SYSTEMTIME));
285 
286  if (ltm)
287  {
288  lpSystemTime->wYear = (WORD)(ltm->tm_year + 1900);
289  lpSystemTime->wMonth = (WORD)(ltm->tm_mon + 1);
290  lpSystemTime->wDayOfWeek = (WORD)ltm->tm_wday;
291  lpSystemTime->wDay = (WORD)ltm->tm_mday;
292  lpSystemTime->wHour = (WORD)ltm->tm_hour;
293  lpSystemTime->wMinute = (WORD)ltm->tm_min;
294  lpSystemTime->wSecond = (WORD)ltm->tm_sec;
295  lpSystemTime->wMilliseconds = wMilliseconds;
296  }
297 }
298 
299 BOOL SetLocalTime(CONST SYSTEMTIME* lpSystemTime)
300 {
301  /* TODO: Implement */
302  return FALSE;
303 }
304 
305 VOID GetSystemTimeAsFileTime(LPFILETIME lpSystemTimeAsFileTime)
306 {
307  union
308  {
309  UINT64 u64;
310  FILETIME ft;
311  } t;
312 
313  t.u64 = (winpr_GetUnixTimeNS() / 100ull) + FILETIME_TO_UNIX_OFFSET_S * 10000000ull;
314  *lpSystemTimeAsFileTime = t.ft;
315 }
316 
317 BOOL GetSystemTimeAdjustment(PDWORD lpTimeAdjustment, PDWORD lpTimeIncrement,
318  PBOOL lpTimeAdjustmentDisabled)
319 {
320  /* TODO: Implement */
321  return FALSE;
322 }
323 
324 #ifndef CLOCK_MONOTONIC_RAW
325 #define CLOCK_MONOTONIC_RAW 4
326 #endif
327 
328 DWORD GetTickCount(void)
329 {
330  return (DWORD)GetTickCount64();
331 }
332 #endif // _WIN32
333 
334 #if !defined(_WIN32) || defined(_UWP)
335 
336 #if defined(WITH_WINPR_DEPRECATED)
337 /* OSVERSIONINFOEX Structure:
338  * http://msdn.microsoft.com/en-us/library/windows/desktop/ms724833
339  */
340 
341 BOOL GetVersionExA(LPOSVERSIONINFOA lpVersionInformation)
342 {
343 #ifdef _UWP
344 
345  /* Windows 10 Version Info */
346  if ((lpVersionInformation->dwOSVersionInfoSize == sizeof(OSVERSIONINFOA)) ||
347  (lpVersionInformation->dwOSVersionInfoSize == sizeof(OSVERSIONINFOEXA)))
348  {
349  lpVersionInformation->dwMajorVersion = 10;
350  lpVersionInformation->dwMinorVersion = 0;
351  lpVersionInformation->dwBuildNumber = 0;
352  lpVersionInformation->dwPlatformId = VER_PLATFORM_WIN32_NT;
353  ZeroMemory(lpVersionInformation->szCSDVersion, sizeof(lpVersionInformation->szCSDVersion));
354 
355  if (lpVersionInformation->dwOSVersionInfoSize == sizeof(OSVERSIONINFOEXA))
356  {
357  LPOSVERSIONINFOEXA lpVersionInformationEx = (LPOSVERSIONINFOEXA)lpVersionInformation;
358  lpVersionInformationEx->wServicePackMajor = 0;
359  lpVersionInformationEx->wServicePackMinor = 0;
360  lpVersionInformationEx->wSuiteMask = 0;
361  lpVersionInformationEx->wProductType = VER_NT_WORKSTATION;
362  lpVersionInformationEx->wReserved = 0;
363  }
364 
365  return TRUE;
366  }
367 
368 #else
369 
370  /* Windows 7 SP1 Version Info */
371  if ((lpVersionInformation->dwOSVersionInfoSize == sizeof(OSVERSIONINFOA)) ||
372  (lpVersionInformation->dwOSVersionInfoSize == sizeof(OSVERSIONINFOEXA)))
373  {
374  lpVersionInformation->dwMajorVersion = 6;
375  lpVersionInformation->dwMinorVersion = 1;
376  lpVersionInformation->dwBuildNumber = 7601;
377  lpVersionInformation->dwPlatformId = VER_PLATFORM_WIN32_NT;
378  ZeroMemory(lpVersionInformation->szCSDVersion, sizeof(lpVersionInformation->szCSDVersion));
379 
380  if (lpVersionInformation->dwOSVersionInfoSize == sizeof(OSVERSIONINFOEXA))
381  {
382  LPOSVERSIONINFOEXA lpVersionInformationEx = (LPOSVERSIONINFOEXA)lpVersionInformation;
383  lpVersionInformationEx->wServicePackMajor = 1;
384  lpVersionInformationEx->wServicePackMinor = 0;
385  lpVersionInformationEx->wSuiteMask = 0;
386  lpVersionInformationEx->wProductType = VER_NT_WORKSTATION;
387  lpVersionInformationEx->wReserved = 0;
388  }
389 
390  return TRUE;
391  }
392 
393 #endif
394  return FALSE;
395 }
396 
397 BOOL GetVersionExW(LPOSVERSIONINFOW lpVersionInformation)
398 {
399  ZeroMemory(lpVersionInformation->szCSDVersion, sizeof(lpVersionInformation->szCSDVersion));
400  return GetVersionExA((LPOSVERSIONINFOA)lpVersionInformation);
401 }
402 
403 #endif
404 
405 #endif
406 
407 #if !defined(_WIN32) || defined(_UWP)
408 
409 BOOL GetComputerNameW(LPWSTR lpBuffer, LPDWORD lpnSize)
410 {
411  BOOL rc = 0;
412  LPSTR buffer = NULL;
413  if (!lpnSize || (*lpnSize > INT_MAX))
414  return FALSE;
415 
416  if (*lpnSize > 0)
417  {
418  buffer = malloc(*lpnSize);
419  if (!buffer)
420  return FALSE;
421  }
422  rc = GetComputerNameA(buffer, lpnSize);
423 
424  if (rc && (*lpnSize > 0))
425  {
426  const SSIZE_T res = ConvertUtf8NToWChar(buffer, *lpnSize, lpBuffer, *lpnSize);
427  rc = res > 0;
428  }
429 
430  free(buffer);
431 
432  return rc;
433 }
434 
435 BOOL GetComputerNameA(LPSTR lpBuffer, LPDWORD lpnSize)
436 {
437  if (!lpnSize)
438  {
439  SetLastError(ERROR_BAD_ARGUMENTS);
440  return FALSE;
441  }
442 
443  char hostname[256 + 1] = { 0 };
444  if (gethostname(hostname, ARRAYSIZE(hostname) - 1) == -1)
445  return FALSE;
446 
447  size_t length = strnlen(hostname, MAX_COMPUTERNAME_LENGTH);
448  const char* dot = strchr(hostname, '.');
449  if (dot)
450  length = WINPR_ASSERTING_INT_CAST(size_t, (dot - hostname));
451 
452  if ((*lpnSize <= (DWORD)length) || !lpBuffer)
453  {
454  SetLastError(ERROR_BUFFER_OVERFLOW);
455  *lpnSize = (DWORD)(length + 1);
456  return FALSE;
457  }
458 
459  strncpy(lpBuffer, hostname, length);
460  lpBuffer[length] = '\0';
461  *lpnSize = (DWORD)length;
462  return TRUE;
463 }
464 
465 BOOL GetComputerNameExA(COMPUTER_NAME_FORMAT NameType, LPSTR lpBuffer, LPDWORD lpnSize)
466 {
467  size_t length = 0;
468  char hostname[256] = { 0 };
469 
470  if (!lpnSize)
471  {
472  SetLastError(ERROR_BAD_ARGUMENTS);
473  return FALSE;
474  }
475 
476  if ((NameType == ComputerNameNetBIOS) || (NameType == ComputerNamePhysicalNetBIOS))
477  {
478  BOOL rc = GetComputerNameA(lpBuffer, lpnSize);
479 
480  if (!rc)
481  {
482  if (GetLastError() == ERROR_BUFFER_OVERFLOW)
483  SetLastError(ERROR_MORE_DATA);
484  }
485 
486  return rc;
487  }
488 
489  if (gethostname(hostname, sizeof(hostname)) == -1)
490  return FALSE;
491 
492  length = strnlen(hostname, sizeof(hostname));
493 
494  switch (NameType)
495  {
496  case ComputerNameDnsHostname:
497  case ComputerNameDnsDomain:
498  case ComputerNameDnsFullyQualified:
499  case ComputerNamePhysicalDnsHostname:
500  case ComputerNamePhysicalDnsDomain:
501  case ComputerNamePhysicalDnsFullyQualified:
502  if ((*lpnSize <= (DWORD)length) || !lpBuffer)
503  {
504  *lpnSize = (DWORD)(length + 1);
505  SetLastError(ERROR_MORE_DATA);
506  return FALSE;
507  }
508 
509  CopyMemory(lpBuffer, hostname, length);
510  lpBuffer[length] = '\0';
511  *lpnSize = (DWORD)length;
512  break;
513 
514  default:
515  return FALSE;
516  }
517 
518  return TRUE;
519 }
520 
521 BOOL GetComputerNameExW(COMPUTER_NAME_FORMAT NameType, LPWSTR lpBuffer, LPDWORD lpnSize)
522 {
523  BOOL rc = 0;
524  LPSTR lpABuffer = NULL;
525 
526  if (!lpnSize)
527  {
528  SetLastError(ERROR_BAD_ARGUMENTS);
529  return FALSE;
530  }
531 
532  if (*lpnSize > 0)
533  {
534  lpABuffer = calloc(*lpnSize, sizeof(CHAR));
535 
536  if (!lpABuffer)
537  return FALSE;
538  }
539 
540  rc = GetComputerNameExA(NameType, lpABuffer, lpnSize);
541 
542  if (rc && (*lpnSize > 0))
543  {
544  const SSIZE_T res = ConvertUtf8NToWChar(lpABuffer, *lpnSize, lpBuffer, *lpnSize);
545  rc = res > 0;
546  }
547 
548  free(lpABuffer);
549  return rc;
550 }
551 
552 #endif
553 
554 #if defined(_UWP)
555 
556 DWORD GetTickCount(void)
557 {
558  return (DWORD)GetTickCount64();
559 }
560 
561 #endif
562 
563 #if (!defined(_WIN32)) || (defined(_WIN32) && (_WIN32_WINNT < 0x0600))
564 
565 ULONGLONG winpr_GetTickCount64(void)
566 {
567  const UINT64 ns = winpr_GetTickCount64NS();
568  return WINPR_TIME_NS_TO_MS(ns);
569 }
570 
571 #endif
572 
573 UINT64 winpr_GetTickCount64NS(void)
574 {
575  UINT64 ticks = 0;
576 #if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 199309L)
577  struct timespec ts = { 0 };
578 
579  if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) == 0)
580  ticks = (WINPR_ASSERTING_INT_CAST(uint64_t, ts.tv_sec) * 1000000000ull) +
581  WINPR_ASSERTING_INT_CAST(uint64_t, ts.tv_nsec);
582 #elif defined(__MACH__) && defined(__APPLE__)
583  ticks = mac_get_time_ns();
584 #elif defined(_WIN32)
585  LARGE_INTEGER li = { 0 };
586  LARGE_INTEGER freq = { 0 };
587  if (QueryPerformanceFrequency(&freq) && QueryPerformanceCounter(&li))
588  ticks = li.QuadPart * 1000000000ull / freq.QuadPart;
589 #else
590  struct timeval tv = { 0 };
591 
592  if (gettimeofday(&tv, NULL) == 0)
593  ticks = (tv.tv_sec * 1000000000ull) + (tv.tv_usec * 1000ull);
594 
595  /* We need to trick here:
596  * this function should return the system uptime, but we need higher resolution.
597  * so on first call get the actual timestamp along with the system uptime.
598  *
599  * return the uptime measured from now on (e.g. current measure - first measure + uptime at
600  * first measure)
601  */
602  static UINT64 first = 0;
603  static UINT64 uptime = 0;
604  if (first == 0)
605  {
606  struct sysinfo info = { 0 };
607  if (sysinfo(&info) == 0)
608  {
609  first = ticks;
610  uptime = 1000000000ull * info.uptime;
611  }
612  }
613 
614  ticks = ticks - first + uptime;
615 #endif
616  return ticks;
617 }
618 
619 UINT64 winpr_GetUnixTimeNS(void)
620 {
621 #if defined(_WIN32)
622 
623  union
624  {
625  UINT64 u64;
626  FILETIME ft;
627  } t = { 0 };
628  GetSystemTimeAsFileTime(&t.ft);
629  return (t.u64 - FILETIME_TO_UNIX_OFFSET_S * 10000000ull) * 100ull;
630 #elif defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 199309L)
631  struct timespec ts = { 0 };
632  if (clock_gettime(CLOCK_REALTIME, &ts) != 0)
633  return 0;
634  return WINPR_ASSERTING_INT_CAST(uint64_t, ts.tv_sec) * 1000000000ull +
635  WINPR_ASSERTING_INT_CAST(uint64_t, ts.tv_nsec);
636 #else
637  struct timeval tv = { 0 };
638  if (gettimeofday(&tv, NULL) != 0)
639  return 0;
640  return tv.tv_sec * 1000000000ULL + tv.tv_usec * 1000ull;
641 #endif
642 }
643 
644 /* If x86 */
645 #ifdef _M_IX86_AMD64
646 
647 #if defined(__GNUC__)
648 #define xgetbv(_func_, _lo_, _hi_) \
649  __asm__ __volatile__("xgetbv" : "=a"(_lo_), "=d"(_hi_) : "c"(_func_))
650 #elif defined(_MSC_VER)
651 #define xgetbv(_func_, _lo_, _hi_) \
652  { \
653  unsigned __int64 val = _xgetbv(_func_); \
654  _lo_ = val & 0xFFFFFFFF; \
655  _hi_ = (val >> 32); \
656  }
657 #endif
658 
659 #define B_BIT_AVX2 (1 << 5)
660 #define B_BIT_AVX512F (1 << 16)
661 #define D_BIT_MMX (1 << 23)
662 #define D_BIT_SSE (1 << 25)
663 #define D_BIT_SSE2 (1 << 26)
664 #define D_BIT_3DN (1 << 30)
665 #define C_BIT_SSE3 (1 << 0)
666 #define C_BIT_PCLMULQDQ (1 << 1)
667 #define C81_BIT_LZCNT (1 << 5)
668 #define C_BIT_3DNP (1 << 8)
669 #define C_BIT_3DNP (1 << 8)
670 #define C_BIT_SSSE3 (1 << 9)
671 #define C_BIT_SSE41 (1 << 19)
672 #define C_BIT_SSE42 (1 << 20)
673 #define C_BIT_FMA (1 << 12)
674 #define C_BIT_AES (1 << 25)
675 #define C_BIT_XGETBV (1 << 27)
676 #define C_BIT_AVX (1 << 28)
677 #define E_BIT_XMM (1 << 1)
678 #define E_BIT_YMM (1 << 2)
679 #define E_BITS_AVX (E_BIT_XMM | E_BIT_YMM)
680 
681 static void cpuid(unsigned info, unsigned* eax, unsigned* ebx, unsigned* ecx, unsigned* edx)
682 {
683 #ifdef __GNUC__
684  *eax = *ebx = *ecx = *edx = 0;
685  __asm volatile(
686  /* The EBX (or RBX register on x86_64) is used for the PIC base address
687  * and must not be corrupted by our inline assembly.
688  */
689 #ifdef _M_IX86
690  "mov %%ebx, %%esi;"
691  "cpuid;"
692  "xchg %%ebx, %%esi;"
693 #else
694  "mov %%rbx, %%rsi;"
695  "cpuid;"
696  "xchg %%rbx, %%rsi;"
697 #endif
698  : "=a"(*eax), "=S"(*ebx), "=c"(*ecx), "=d"(*edx)
699  : "a"(info), "c"(0));
700 #elif defined(_MSC_VER)
701  int a[4];
702  __cpuid(a, info);
703  *eax = a[0];
704  *ebx = a[1];
705  *ecx = a[2];
706  *edx = a[3];
707 #endif
708 }
709 #elif defined(_M_ARM) || defined(_M_ARM64)
710 #if defined(__linux__)
711 // HWCAP flags from linux kernel - uapi/asm/hwcap.h
712 #define HWCAP_SWP (1 << 0)
713 #define HWCAP_HALF (1 << 1)
714 #define HWCAP_THUMB (1 << 2)
715 #define HWCAP_26BIT (1 << 3) /* Play it safe */
716 #define HWCAP_FAST_MULT (1 << 4)
717 #define HWCAP_FPA (1 << 5)
718 #define HWCAP_VFP (1 << 6)
719 #define HWCAP_EDSP (1 << 7)
720 #define HWCAP_JAVA (1 << 8)
721 #define HWCAP_IWMMXT (1 << 9)
722 #define HWCAP_CRUNCH (1 << 10)
723 #define HWCAP_THUMBEE (1 << 11)
724 #define HWCAP_NEON (1 << 12)
725 #define HWCAP_VFPv3 (1 << 13)
726 #define HWCAP_VFPv3D16 (1 << 14) /* also set for VFPv4-D16 */
727 #define HWCAP_TLS (1 << 15)
728 #define HWCAP_VFPv4 (1 << 16)
729 #define HWCAP_IDIVA (1 << 17)
730 #define HWCAP_IDIVT (1 << 18)
731 #define HWCAP_VFPD32 (1 << 19) /* set if VFP has 32 regs (not 16) */
732 #define HWCAP_IDIV (HWCAP_IDIVA | HWCAP_IDIVT)
733 
734 // From linux kernel uapi/linux/auxvec.h
735 #define AT_HWCAP 16
736 
737 static unsigned GetARMCPUCaps(void)
738 {
739  unsigned caps = 0;
740  int fd = open("/proc/self/auxv", O_RDONLY);
741 
742  if (fd == -1)
743  return 0;
744 
745  static struct
746  {
747  unsigned a_type; /* Entry type */
748  unsigned a_val; /* Integer value */
749  } auxvec;
750 
751  while (1)
752  {
753  int num;
754  num = read(fd, (char*)&auxvec, sizeof(auxvec));
755 
756  if (num < 1 || (auxvec.a_type == 0 && auxvec.a_val == 0))
757  break;
758 
759  if (auxvec.a_type == AT_HWCAP)
760  {
761  caps = auxvec.a_val;
762  }
763  }
764 
765  close(fd);
766  return caps;
767 }
768 
769 #endif // defined(__linux__)
770 #endif // _M_IX86_AMD64
771 
772 #ifndef _WIN32
773 
774 #if defined(_M_ARM) || defined(_M_ARM64)
775 #ifdef __linux__
776 #include <sys/auxv.h>
777 #endif
778 #endif
779 
780 BOOL IsProcessorFeaturePresent(DWORD ProcessorFeature)
781 {
782  BOOL ret = FALSE;
783 #if defined(ANDROID)
784  const uint64_t features = android_getCpuFeatures();
785 
786  switch (ProcessorFeature)
787  {
788  case PF_ARM_NEON_INSTRUCTIONS_AVAILABLE:
789  case PF_ARM_NEON:
790  return features & ANDROID_CPU_ARM_FEATURE_NEON;
791 
792  default:
793  WLog_WARN(TAG, "feature 0x%08" PRIx32 " check not implemented", ProcessorFeature);
794  return FALSE;
795  }
796 
797 #elif defined(_M_ARM) || defined(_M_ARM64)
798 #ifdef __linux__
799  const unsigned long caps = getauxval(AT_HWCAP);
800 
801  switch (ProcessorFeature)
802  {
803  case PF_ARM_NEON_INSTRUCTIONS_AVAILABLE:
804  case PF_ARM_NEON:
805 
806  if (caps & HWCAP_NEON)
807  ret = TRUE;
808 
809  break;
810 
811  case PF_ARM_THUMB:
812  if (caps & HWCAP_THUMB)
813  ret = TRUE;
814 
815  case PF_ARM_VFP_32_REGISTERS_AVAILABLE:
816  if (caps & HWCAP_VFPD32)
817  ret = TRUE;
818 
819  case PF_ARM_DIVIDE_INSTRUCTION_AVAILABLE:
820  if ((caps & HWCAP_IDIVA) || (caps & HWCAP_IDIVT))
821  ret = TRUE;
822 
823  case PF_ARM_VFP3:
824  if (caps & HWCAP_VFPv3)
825  ret = TRUE;
826 
827  break;
828 
829  case PF_ARM_JAZELLE:
830  if (caps & HWCAP_JAVA)
831  ret = TRUE;
832 
833  break;
834 
835  case PF_ARM_DSP:
836  if (caps & HWCAP_EDSP)
837  ret = TRUE;
838 
839  break;
840 
841  case PF_ARM_MPU:
842  if (caps & HWCAP_EDSP)
843  ret = TRUE;
844 
845  break;
846 
847  case PF_ARM_THUMB2:
848  if ((caps & HWCAP_IDIVT) || (caps & HWCAP_VFPv4))
849  ret = TRUE;
850 
851  break;
852 
853  case PF_ARM_T2EE:
854  if (caps & HWCAP_THUMBEE)
855  ret = TRUE;
856 
857  break;
858 
859  case PF_ARM_INTEL_WMMX:
860  if (caps & HWCAP_IWMMXT)
861  ret = TRUE;
862 
863  break;
864  case PF_ARM_V8_INSTRUCTIONS_AVAILABLE:
865  case PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE:
866  case PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE:
867  case PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE:
868  case PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE:
869  case PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE:
870  case PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE:
871  default:
872  WLog_WARN(TAG, "feature 0x%08" PRIx32 " check not implemented", ProcessorFeature);
873  break;
874  }
875 
876 #else // __linux__
877 
878  switch (ProcessorFeature)
879  {
880  case PF_ARM_NEON_INSTRUCTIONS_AVAILABLE:
881  case PF_ARM_NEON:
882 #ifdef __ARM_NEON
883  ret = TRUE;
884 #endif
885  break;
886  case PF_ARM_V8_INSTRUCTIONS_AVAILABLE:
887  case PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE:
888  case PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE:
889  case PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE:
890  case PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE:
891  case PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE:
892  case PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE:
893  default:
894  WLog_WARN(TAG, "feature 0x%08" PRIx32 " check not implemented", ProcessorFeature);
895  break;
896  }
897 
898 #endif // __linux__
899 #endif
900 
901 #if defined(_M_IX86_AMD64)
902 #ifdef __GNUC__
903  unsigned a = 0;
904  unsigned b = 0;
905  unsigned c = 0;
906  unsigned d = 0;
907  cpuid(1, &a, &b, &c, &d);
908 
909  switch (ProcessorFeature)
910  {
911  case PF_MMX_INSTRUCTIONS_AVAILABLE:
912  if (d & D_BIT_MMX)
913  ret = TRUE;
914 
915  break;
916 
917  case PF_XMMI_INSTRUCTIONS_AVAILABLE:
918  if (d & D_BIT_SSE)
919  ret = TRUE;
920 
921  break;
922 
923  case PF_XMMI64_INSTRUCTIONS_AVAILABLE:
924  if (d & D_BIT_SSE2)
925  ret = TRUE;
926 
927  break;
928 
929  case PF_3DNOW_INSTRUCTIONS_AVAILABLE:
930  if (d & D_BIT_3DN)
931  ret = TRUE;
932 
933  break;
934 
935  case PF_SSE3_INSTRUCTIONS_AVAILABLE:
936  ret = __builtin_cpu_supports("sse3");
937  break;
938 
939  case PF_SSSE3_INSTRUCTIONS_AVAILABLE:
940  ret = __builtin_cpu_supports("ssse3");
941  break;
942  case PF_SSE4_1_INSTRUCTIONS_AVAILABLE:
943  ret = __builtin_cpu_supports("sse4.1");
944  break;
945  case PF_SSE4_2_INSTRUCTIONS_AVAILABLE:
946  ret = __builtin_cpu_supports("sse4.2");
947  break;
948  case PF_AVX_INSTRUCTIONS_AVAILABLE:
949  ret = __builtin_cpu_supports("avx");
950  break;
951  case PF_AVX2_INSTRUCTIONS_AVAILABLE:
952  ret = __builtin_cpu_supports("avx2");
953  break;
954  case PF_AVX512F_INSTRUCTIONS_AVAILABLE:
955  ret = __builtin_cpu_supports("avx512f");
956  break;
957  default:
958  WLog_WARN(TAG, "feature 0x%08" PRIx32 " check not implemented", ProcessorFeature);
959  break;
960  }
961 
962 #endif // __GNUC__
963 #endif
964 
965 #if defined(_M_E2K)
966  /* compiler flags on e2k arch determine CPU features */
967  switch (ProcessorFeature)
968  {
969  case PF_MMX_INSTRUCTIONS_AVAILABLE:
970 #ifdef __MMX__
971  ret = TRUE;
972 #endif
973  break;
974 
975  case PF_3DNOW_INSTRUCTIONS_AVAILABLE:
976 #ifdef __3dNOW__
977  ret = TRUE;
978 #endif
979  break;
980 
981  case PF_SSE3_INSTRUCTIONS_AVAILABLE:
982 #ifdef __SSE3__
983  ret = TRUE;
984 #endif
985  break;
986 
987  default:
988  break;
989  }
990 
991 #endif
992  return ret;
993 }
994 
995 #endif //_WIN32
996 
997 DWORD GetTickCountPrecise(void)
998 {
999 #ifdef _WIN32
1000  LARGE_INTEGER freq = { 0 };
1001  LARGE_INTEGER current = { 0 };
1002  QueryPerformanceFrequency(&freq);
1003  QueryPerformanceCounter(&current);
1004  return (DWORD)(current.QuadPart * 1000LL / freq.QuadPart);
1005 #else
1006  return GetTickCount();
1007 #endif
1008 }
1009 
1010 BOOL IsProcessorFeaturePresentEx(DWORD ProcessorFeature)
1011 {
1012  BOOL ret = FALSE;
1013 #if defined(_M_ARM) || defined(_M_ARM64)
1014 #ifdef __linux__
1015  unsigned caps;
1016  caps = GetARMCPUCaps();
1017 
1018  switch (ProcessorFeature)
1019  {
1020  case PF_EX_ARM_VFP1:
1021  if (caps & HWCAP_VFP)
1022  ret = TRUE;
1023 
1024  break;
1025 
1026  case PF_EX_ARM_VFP3D16:
1027  if (caps & HWCAP_VFPv3D16)
1028  ret = TRUE;
1029 
1030  break;
1031 
1032  case PF_EX_ARM_VFP4:
1033  if (caps & HWCAP_VFPv4)
1034  ret = TRUE;
1035 
1036  break;
1037 
1038  case PF_EX_ARM_IDIVA:
1039  if (caps & HWCAP_IDIVA)
1040  ret = TRUE;
1041 
1042  break;
1043 
1044  case PF_EX_ARM_IDIVT:
1045  if (caps & HWCAP_IDIVT)
1046  ret = TRUE;
1047 
1048  break;
1049  }
1050 
1051 #endif // __linux__
1052 #elif defined(_M_IX86_AMD64)
1053  unsigned a = 0;
1054  unsigned b = 0;
1055  unsigned c = 0;
1056  unsigned d = 0;
1057  cpuid(1, &a, &b, &c, &d);
1058 
1059  switch (ProcessorFeature)
1060  {
1061  case PF_EX_LZCNT:
1062  {
1063  unsigned a81 = 0;
1064  unsigned b81 = 0;
1065  unsigned c81 = 0;
1066  unsigned d81 = 0;
1067  cpuid(0x80000001, &a81, &b81, &c81, &d81);
1068 
1069  if (c81 & C81_BIT_LZCNT)
1070  ret = TRUE;
1071  }
1072  break;
1073 
1074  case PF_EX_3DNOW_PREFETCH:
1075  if (c & C_BIT_3DNP)
1076  ret = TRUE;
1077 
1078  break;
1079 
1080  case PF_EX_SSSE3:
1081  if (c & C_BIT_SSSE3)
1082  ret = TRUE;
1083 
1084  break;
1085 
1086  case PF_EX_SSE41:
1087  if (c & C_BIT_SSE41)
1088  ret = TRUE;
1089 
1090  break;
1091 
1092  case PF_EX_SSE42:
1093  if (c & C_BIT_SSE42)
1094  ret = TRUE;
1095 
1096  break;
1097 #if defined(__GNUC__) || defined(_MSC_VER)
1098 
1099  case PF_EX_AVX:
1100  case PF_EX_AVX2:
1101  case PF_EX_AVX512F:
1102  case PF_EX_FMA:
1103  case PF_EX_AVX_AES:
1104  case PF_EX_AVX_PCLMULQDQ:
1105  {
1106  /* Check for general AVX support */
1107  if (!(c & C_BIT_AVX))
1108  break;
1109 
1110  /* Check for xgetbv support */
1111  if (!(c & C_BIT_XGETBV))
1112  break;
1113 
1114  int e = 0;
1115  int f = 0;
1116  xgetbv(0, e, f);
1117 
1118  /* XGETBV enabled for applications and XMM/YMM states enabled */
1119  if ((e & E_BITS_AVX) == E_BITS_AVX)
1120  {
1121  switch (ProcessorFeature)
1122  {
1123  case PF_EX_AVX:
1124  ret = TRUE;
1125  break;
1126 
1127  case PF_EX_AVX2:
1128  case PF_EX_AVX512F:
1129  cpuid(7, &a, &b, &c, &d);
1130  switch (ProcessorFeature)
1131  {
1132  case PF_EX_AVX2:
1133  if (b & B_BIT_AVX2)
1134  ret = TRUE;
1135  break;
1136 
1137  case PF_EX_AVX512F:
1138  if (b & B_BIT_AVX512F)
1139  ret = TRUE;
1140  break;
1141 
1142  default:
1143  break;
1144  }
1145  break;
1146 
1147  case PF_EX_FMA:
1148  if (c & C_BIT_FMA)
1149  ret = TRUE;
1150 
1151  break;
1152 
1153  case PF_EX_AVX_AES:
1154  if (c & C_BIT_AES)
1155  ret = TRUE;
1156 
1157  break;
1158 
1159  case PF_EX_AVX_PCLMULQDQ:
1160  if (c & C_BIT_PCLMULQDQ)
1161  ret = TRUE;
1162 
1163  break;
1164  default:
1165  break;
1166  }
1167  }
1168  }
1169  break;
1170 #endif // __GNUC__ || _MSC_VER
1171 
1172  default:
1173  break;
1174  }
1175 #elif defined(_M_E2K)
1176  /* compiler flags on e2k arch determine CPU features */
1177  switch (ProcessorFeature)
1178  {
1179  case PF_EX_LZCNT:
1180 #ifdef __LZCNT__
1181  ret = TRUE;
1182 #endif
1183  break;
1184 
1185  case PF_EX_SSSE3:
1186 #ifdef __SSSE3__
1187  ret = TRUE;
1188 #endif
1189  break;
1190 
1191  case PF_EX_SSE41:
1192 #ifdef __SSE4_1__
1193  ret = TRUE;
1194 #endif
1195  break;
1196 
1197  case PF_EX_SSE42:
1198 #ifdef __SSE4_2__
1199  ret = TRUE;
1200 #endif
1201  break;
1202 
1203  case PF_EX_AVX:
1204 #ifdef __AVX__
1205  ret = TRUE;
1206 #endif
1207  break;
1208 
1209  case PF_EX_AVX2:
1210 #ifdef __AVX2__
1211  ret = TRUE;
1212 #endif
1213  break;
1214 
1215  case PF_EX_FMA:
1216 #ifdef __FMA__
1217  ret = TRUE;
1218 #endif
1219  break;
1220 
1221  default:
1222  break;
1223  }
1224 #endif
1225  return ret;
1226 }