summaryrefslogtreecommitdiffstats
path: root/winpr/libwinpr/sysinfo
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
commita9bcc81f821d7c66f623779fa5147e728eb3c388 (patch)
tree98676963bcdd537ae5908a067a8eb110b93486a6 /winpr/libwinpr/sysinfo
parentInitial commit. (diff)
downloadfreerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.tar.xz
freerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.zip
Adding upstream version 3.3.0+dfsg1.upstream/3.3.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--winpr/libwinpr/sysinfo/CMakeLists.txt31
-rw-r--r--winpr/libwinpr/sysinfo/ModuleOptions.cmake9
-rw-r--r--winpr/libwinpr/sysinfo/cpufeatures/CMakeLists.txt20
-rw-r--r--winpr/libwinpr/sysinfo/cpufeatures/NOTICE13
-rw-r--r--winpr/libwinpr/sysinfo/cpufeatures/README4
-rw-r--r--winpr/libwinpr/sysinfo/cpufeatures/cpu-features.c1426
-rw-r--r--winpr/libwinpr/sysinfo/cpufeatures/cpu-features.h324
-rw-r--r--winpr/libwinpr/sysinfo/sysinfo.c1122
-rw-r--r--winpr/libwinpr/sysinfo/test/CMakeLists.txt30
-rw-r--r--winpr/libwinpr/sysinfo/test/TestCPUFeatures.c65
-rw-r--r--winpr/libwinpr/sysinfo/test/TestGetComputerName.c366
-rw-r--r--winpr/libwinpr/sysinfo/test/TestGetNativeSystemInfo.c29
-rw-r--r--winpr/libwinpr/sysinfo/test/TestLocalTime.c21
-rw-r--r--winpr/libwinpr/sysinfo/test/TestSystemTime.c21
14 files changed, 3481 insertions, 0 deletions
diff --git a/winpr/libwinpr/sysinfo/CMakeLists.txt b/winpr/libwinpr/sysinfo/CMakeLists.txt
new file mode 100644
index 0000000..799df05
--- /dev/null
+++ b/winpr/libwinpr/sysinfo/CMakeLists.txt
@@ -0,0 +1,31 @@
+# WinPR: Windows Portable Runtime
+# libwinpr-sysinfo cmake build script
+#
+# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.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.
+
+if(ANDROID)
+ add_subdirectory(cpufeatures)
+endif()
+
+winpr_module_add(sysinfo.c)
+
+if((NOT WIN32) AND (NOT APPLE) AND (NOT ANDROID) AND (NOT OPENBSD))
+ winpr_library_add_private(rt)
+endif()
+
+if(BUILD_TESTING)
+ add_subdirectory(test)
+endif()
+
diff --git a/winpr/libwinpr/sysinfo/ModuleOptions.cmake b/winpr/libwinpr/sysinfo/ModuleOptions.cmake
new file mode 100644
index 0000000..6a7ff02
--- /dev/null
+++ b/winpr/libwinpr/sysinfo/ModuleOptions.cmake
@@ -0,0 +1,9 @@
+
+set(MINWIN_LAYER "1")
+set(MINWIN_GROUP "core")
+set(MINWIN_MAJOR_VERSION "2")
+set(MINWIN_MINOR_VERSION "0")
+set(MINWIN_SHORT_NAME "sysinfo")
+set(MINWIN_LONG_NAME "System Information Functions")
+set(MODULE_LIBRARY_NAME "api-ms-win-${MINWIN_GROUP}-${MINWIN_SHORT_NAME}-l${MINWIN_LAYER}-${MINWIN_MAJOR_VERSION}-${MINWIN_MINOR_VERSION}")
+
diff --git a/winpr/libwinpr/sysinfo/cpufeatures/CMakeLists.txt b/winpr/libwinpr/sysinfo/cpufeatures/CMakeLists.txt
new file mode 100644
index 0000000..f1b93df
--- /dev/null
+++ b/winpr/libwinpr/sysinfo/cpufeatures/CMakeLists.txt
@@ -0,0 +1,20 @@
+# WinPR: Windows Portable Runtime
+# libwinpr-sysinfo cmake build script
+#
+# Copyright 2017 Armin Novak <armin.novak@thincast.com>
+# Copyright 2017 Thincast Technologies GmbH
+#
+# 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.
+
+winpr_module_add(cpu-features.c cpu-features.h)
+
diff --git a/winpr/libwinpr/sysinfo/cpufeatures/NOTICE b/winpr/libwinpr/sysinfo/cpufeatures/NOTICE
new file mode 100644
index 0000000..d6c0922
--- /dev/null
+++ b/winpr/libwinpr/sysinfo/cpufeatures/NOTICE
@@ -0,0 +1,13 @@
+Copyright (C) 2016 The Android Open Source Project
+
+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.
diff --git a/winpr/libwinpr/sysinfo/cpufeatures/README b/winpr/libwinpr/sysinfo/cpufeatures/README
new file mode 100644
index 0000000..ba85c20
--- /dev/null
+++ b/winpr/libwinpr/sysinfo/cpufeatures/README
@@ -0,0 +1,4 @@
+Android CPUFeatures Library
+
+https://developer.android.com/ndk/guides/cpu-features.html
+https://android.googlesource.com/platform/ndk/+/master/sources/android/cpufeatures
diff --git a/winpr/libwinpr/sysinfo/cpufeatures/cpu-features.c b/winpr/libwinpr/sysinfo/cpufeatures/cpu-features.c
new file mode 100644
index 0000000..d43b588
--- /dev/null
+++ b/winpr/libwinpr/sysinfo/cpufeatures/cpu-features.c
@@ -0,0 +1,1426 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* ChangeLog for this library:
+ *
+ * NDK r10e?: Add MIPS MSA feature.
+ *
+ * NDK r10: Support for 64-bit CPUs (Intel, ARM & MIPS).
+ *
+ * NDK r8d: Add android_setCpu().
+ *
+ * NDK r8c: Add new ARM CPU features: VFPv2, VFP_D32, VFP_FP16,
+ * VFP_FMA, NEON_FMA, IDIV_ARM, IDIV_THUMB2 and iWMMXt.
+ *
+ * Rewrite the code to parse /proc/self/auxv instead of
+ * the "Features" field in /proc/cpuinfo.
+ *
+ * Dynamically allocate the buffer that hold the content
+ * of /proc/cpuinfo to deal with newer hardware.
+ *
+ * NDK r7c: Fix CPU count computation. The old method only reported the
+ * number of _active_ CPUs when the library was initialized,
+ * which could be less than the real total.
+ *
+ * NDK r5: Handle buggy kernels which report a CPU Architecture number of 7
+ * for an ARMv6 CPU (see below).
+ *
+ * Handle kernels that only report 'neon', and not 'vfpv3'
+ * (VFPv3 is mandated by the ARM architecture is Neon is implemented)
+ *
+ * Handle kernels that only report 'vfpv3d16', and not 'vfpv3'
+ *
+ * Fix x86 compilation. Report ANDROID_CPU_FAMILY_X86 in
+ * android_getCpuFamily().
+ *
+ * NDK r4: Initial release
+ */
+
+#include "cpu-features.h"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/system_properties.h>
+#include <unistd.h>
+#include <winpr/wtypes.h>
+
+static pthread_once_t g_once;
+static int g_inited;
+static AndroidCpuFamily g_cpuFamily;
+static uint64_t g_cpuFeatures;
+static int g_cpuCount;
+
+#ifdef __arm__
+static uint32_t g_cpuIdArm;
+#endif
+
+static const int android_cpufeatures_debug = 0;
+
+#define D(...) \
+ do \
+ { \
+ if (android_cpufeatures_debug) \
+ { \
+ printf(__VA_ARGS__); \
+ fflush(stdout); \
+ } \
+ } while (0)
+
+#ifdef __i386__
+static __inline__ void x86_cpuid(int func, int values[4])
+{
+ int a, b, c, d;
+ /* We need to preserve ebx since we're compiling PIC code */
+ /* this means we can't use "=b" for the second output register */
+ __asm__ __volatile__("push %%ebx\n"
+ "cpuid\n"
+ "mov %%ebx, %1\n"
+ "pop %%ebx\n"
+ : "=a"(a), "=r"(b), "=c"(c), "=d"(d)
+ : "a"(func));
+ values[0] = a;
+ values[1] = b;
+ values[2] = c;
+ values[3] = d;
+}
+#elif defined(__x86_64__)
+static __inline__ void x86_cpuid(int func, int values[4])
+{
+ int64_t a, b, c, d;
+ /* We need to preserve ebx since we're compiling PIC code */
+ /* this means we can't use "=b" for the second output register */
+ __asm__ __volatile__("push %%rbx\n"
+ "cpuid\n"
+ "mov %%rbx, %1\n"
+ "pop %%rbx\n"
+ : "=a"(a), "=r"(b), "=c"(c), "=d"(d)
+ : "a"(func));
+ values[0] = a;
+ values[1] = b;
+ values[2] = c;
+ values[3] = d;
+}
+#endif
+
+/* Get the size of a file by reading it until the end. This is needed
+ * because files under /proc do not always return a valid size when
+ * using fseek(0, SEEK_END) + ftell(). Nor can they be mmap()-ed.
+ */
+static int get_file_size(const char* pathname)
+{
+ int fd, result = 0;
+ char buffer[256];
+ fd = open(pathname, O_RDONLY);
+
+ if (fd < 0)
+ {
+ char ebuffer[256] = { 0 };
+ D("Can't open %s: %s\n", pathname, winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
+ return -1;
+ }
+
+ for (;;)
+ {
+ int ret = read(fd, buffer, sizeof buffer);
+
+ if (ret < 0)
+ {
+ char ebuffer[256] = { 0 };
+ if (errno == EINTR)
+ continue;
+
+ D("Error while reading %s: %s\n", pathname,
+ winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
+ break;
+ }
+
+ if (ret == 0)
+ break;
+
+ result += ret;
+ }
+
+ close(fd);
+ return result;
+}
+
+/* Read the content of /proc/cpuinfo into a user-provided buffer.
+ * Return the length of the data, or -1 on error. Does *not*
+ * zero-terminate the content. Will not read more
+ * than 'buffsize' bytes.
+ */
+static int read_file(const char* pathname, char* buffer, size_t buffsize)
+{
+ int fd, count;
+ fd = open(pathname, O_RDONLY);
+
+ if (fd < 0)
+ {
+ char ebuffer[256] = { 0 };
+ D("Could not open %s: %s\n", pathname, winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
+ return -1;
+ }
+
+ count = 0;
+
+ while (count < (int)buffsize)
+ {
+ int ret = read(fd, buffer + count, buffsize - count);
+
+ if (ret < 0)
+ {
+ char ebuffer[256] = { 0 };
+ if (errno == EINTR)
+ continue;
+
+ D("Error while reading from %s: %s\n", pathname,
+ winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
+
+ if (count == 0)
+ count = -1;
+
+ break;
+ }
+
+ if (ret == 0)
+ break;
+
+ count += ret;
+ }
+
+ close(fd);
+ return count;
+}
+
+#ifdef __arm__
+/* Extract the content of a the first occurence of a given field in
+ * the content of /proc/cpuinfo and return it as a heap-allocated
+ * string that must be freed by the caller.
+ *
+ * Return NULL if not found
+ */
+static char* extract_cpuinfo_field(const char* buffer, int buflen, const char* field)
+{
+ int fieldlen = strlen(field);
+ const char* bufend = buffer + buflen;
+ char* result = NULL;
+ int len;
+ const char *p, *q;
+ /* Look for first field occurence, and ensures it starts the line. */
+ p = buffer;
+
+ for (;;)
+ {
+ p = memmem(p, bufend - p, field, fieldlen);
+
+ if (p == NULL)
+ goto EXIT;
+
+ if (p == buffer || p[-1] == '\n')
+ break;
+
+ p += fieldlen;
+ }
+
+ /* Skip to the first column followed by a space */
+ p += fieldlen;
+ p = memchr(p, ':', bufend - p);
+
+ if (p == NULL || p[1] != ' ')
+ goto EXIT;
+
+ /* Find the end of the line */
+ p += 2;
+ q = memchr(p, '\n', bufend - p);
+
+ if (q == NULL)
+ q = bufend;
+
+ /* Copy the line into a heap-allocated buffer */
+ len = q - p;
+ result = malloc(len + 1);
+
+ if (result == NULL)
+ goto EXIT;
+
+ memcpy(result, p, len);
+ result[len] = '\0';
+EXIT:
+ return result;
+}
+
+/* Checks that a space-separated list of items contains one given 'item'.
+ * Returns 1 if found, 0 otherwise.
+ */
+static int has_list_item(const char* list, const char* item)
+{
+ const char* p = list;
+ int itemlen = strlen(item);
+
+ if (list == NULL)
+ return 0;
+
+ while (*p)
+ {
+ const char* q;
+
+ /* skip spaces */
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ /* find end of current list item */
+ q = p;
+
+ while (*q && *q != ' ' && *q != '\t')
+ q++;
+
+ if (itemlen == q - p && !memcmp(p, item, itemlen))
+ return 1;
+
+ /* skip to next item */
+ p = q;
+ }
+
+ return 0;
+}
+#endif /* __arm__ */
+
+/* Parse a number starting from 'input', but not going further
+ * than 'limit'. Return the value into '*result'.
+ *
+ * NOTE: Does not skip over leading spaces, or deal with sign characters.
+ * NOTE: Ignores overflows.
+ *
+ * The function returns NULL in case of error (bad format), or the new
+ * position after the decimal number in case of success (which will always
+ * be <= 'limit').
+ */
+static const char* parse_number(const char* input, const char* limit, int base, int* result)
+{
+ const char* p = input;
+ int val = 0;
+
+ while (p < limit)
+ {
+ int d = (*p - '0');
+
+ if ((unsigned)d >= 10U)
+ {
+ d = (*p - 'a');
+
+ if ((unsigned)d >= 6U)
+ d = (*p - 'A');
+
+ if ((unsigned)d >= 6U)
+ break;
+
+ d += 10;
+ }
+
+ if (d >= base)
+ break;
+
+ val = val * base + d;
+ p++;
+ }
+
+ if (p == input)
+ return NULL;
+
+ *result = val;
+ return p;
+}
+
+static const char* parse_decimal(const char* input, const char* limit, int* result)
+{
+ return parse_number(input, limit, 10, result);
+}
+
+#ifdef __arm__
+static const char* parse_hexadecimal(const char* input, const char* limit, int* result)
+{
+ return parse_number(input, limit, 16, result);
+}
+#endif /* __arm__ */
+
+/* This small data type is used to represent a CPU list / mask, as read
+ * from sysfs on Linux. See http://www.kernel.org/doc/Documentation/cputopology.txt
+ *
+ * For now, we don't expect more than 32 cores on mobile devices, so keep
+ * everything simple.
+ */
+typedef struct
+{
+ uint32_t mask;
+} CpuList;
+
+static __inline__ void cpulist_init(CpuList* list)
+{
+ list->mask = 0;
+}
+
+static __inline__ void cpulist_and(CpuList* list1, CpuList* list2)
+{
+ list1->mask &= list2->mask;
+}
+
+static __inline__ void cpulist_set(CpuList* list, int index)
+{
+ if ((unsigned)index < 32)
+ {
+ list->mask |= (uint32_t)(1U << index);
+ }
+}
+
+static __inline__ int cpulist_count(CpuList* list)
+{
+ return __builtin_popcount(list->mask);
+}
+
+/* Parse a textual list of cpus and store the result inside a CpuList object.
+ * Input format is the following:
+ * - comma-separated list of items (no spaces)
+ * - each item is either a single decimal number (cpu index), or a range made
+ * of two numbers separated by a single dash (-). Ranges are inclusive.
+ *
+ * Examples: 0
+ * 2,4-127,128-143
+ * 0-1
+ */
+static void cpulist_parse(CpuList* list, const char* line, int line_len)
+{
+ const char* p = line;
+ const char* end = p + line_len;
+ const char* q;
+
+ /* NOTE: the input line coming from sysfs typically contains a
+ * trailing newline, so take care of it in the code below
+ */
+ while (p < end && *p != '\n')
+ {
+ int start_value = 0;
+ int end_value = 0;
+ /* Find the end of current item, and put it into 'q' */
+ q = memchr(p, ',', end - p);
+
+ if (q == NULL)
+ {
+ q = end;
+ }
+
+ /* Get first value */
+ p = parse_decimal(p, q, &start_value);
+
+ if (p == NULL)
+ goto BAD_FORMAT;
+
+ end_value = start_value;
+
+ /* If we're not at the end of the item, expect a dash and
+ * and integer; extract end value.
+ */
+ if (p < q && *p == '-')
+ {
+ p = parse_decimal(p + 1, q, &end_value);
+
+ if (p == NULL)
+ goto BAD_FORMAT;
+ }
+
+ /* Set bits CPU list bits */
+ for (int val = start_value; val <= end_value; val++)
+ {
+ cpulist_set(list, val);
+ }
+
+ /* Jump to next item */
+ p = q;
+
+ if (p < end)
+ p++;
+ }
+
+BAD_FORMAT:;
+}
+
+/* Read a CPU list from one sysfs file */
+static void cpulist_read_from(CpuList* list, const char* filename)
+{
+ char file[64];
+ int filelen;
+ cpulist_init(list);
+ filelen = read_file(filename, file, sizeof file);
+
+ if (filelen < 0)
+ {
+ char ebuffer[256] = { 0 };
+ D("Could not read %s: %s\n", filename, winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
+ return;
+ }
+
+ cpulist_parse(list, file, filelen);
+}
+#if defined(__aarch64__)
+// see <uapi/asm/hwcap.h> kernel header
+#define HWCAP_FP (1 << 0)
+#define HWCAP_ASIMD (1 << 1)
+#define HWCAP_AES (1 << 3)
+#define HWCAP_PMULL (1 << 4)
+#define HWCAP_SHA1 (1 << 5)
+#define HWCAP_SHA2 (1 << 6)
+#define HWCAP_CRC32 (1 << 7)
+#endif
+
+#if defined(__arm__)
+
+// See <asm/hwcap.h> kernel header.
+#define HWCAP_VFP (1 << 6)
+#define HWCAP_IWMMXT (1 << 9)
+#define HWCAP_NEON (1 << 12)
+#define HWCAP_VFPv3 (1 << 13)
+#define HWCAP_VFPv3D16 (1 << 14)
+#define HWCAP_VFPv4 (1 << 16)
+#define HWCAP_IDIVA (1 << 17)
+#define HWCAP_IDIVT (1 << 18)
+
+// see <uapi/asm/hwcap.h> kernel header
+#define HWCAP2_AES (1 << 0)
+#define HWCAP2_PMULL (1 << 1)
+#define HWCAP2_SHA1 (1 << 2)
+#define HWCAP2_SHA2 (1 << 3)
+#define HWCAP2_CRC32 (1 << 4)
+
+// This is the list of 32-bit ARMv7 optional features that are _always_
+// supported by ARMv8 CPUs, as mandated by the ARM Architecture Reference
+// Manual.
+#define HWCAP_SET_FOR_ARMV8 \
+ (HWCAP_VFP | HWCAP_NEON | HWCAP_VFPv3 | HWCAP_VFPv4 | HWCAP_IDIVA | HWCAP_IDIVT)
+#endif
+
+#if defined(__mips__)
+// see <uapi/asm/hwcap.h> kernel header
+#define HWCAP_MIPS_R6 (1 << 0)
+#define HWCAP_MIPS_MSA (1 << 1)
+#endif
+
+#if defined(__arm__) || defined(__aarch64__) || defined(__mips__)
+
+#define AT_HWCAP 16
+#define AT_HWCAP2 26
+
+// Probe the system's C library for a 'getauxval' function and call it if
+// it exits, or return 0 for failure. This function is available since API
+// level 20.
+//
+// This code does *NOT* check for '__ANDROID_API__ >= 20' to support the
+// edge case where some NDK developers use headers for a platform that is
+// newer than the one really targetted by their application.
+// This is typically done to use newer native APIs only when running on more
+// recent Android versions, and requires careful symbol management.
+//
+// Note that getauxval() can't really be re-implemented here, because
+// its implementation does not parse /proc/self/auxv. Instead it depends
+// on values that are passed by the kernel at process-init time to the
+// C runtime initialization layer.
+static uint32_t get_elf_hwcap_from_getauxval(int hwcap_type)
+{
+ typedef unsigned long getauxval_func_t(unsigned long);
+ dlerror();
+ void* libc_handle = dlopen("libc.so", RTLD_NOW);
+
+ if (!libc_handle)
+ {
+ D("Could not dlopen() C library: %s\n", dlerror());
+ return 0;
+ }
+
+ uint32_t ret = 0;
+ getauxval_func_t* func = (getauxval_func_t*)dlsym(libc_handle, "getauxval");
+
+ if (!func)
+ {
+ D("Could not find getauxval() in C library\n");
+ }
+ else
+ {
+ // Note: getauxval() returns 0 on failure. Doesn't touch errno.
+ ret = (uint32_t)(*func)(hwcap_type);
+ }
+
+ dlclose(libc_handle);
+ return ret;
+}
+#endif
+
+#if defined(__arm__)
+// Parse /proc/self/auxv to extract the ELF HW capabilities bitmap for the
+// current CPU. Note that this file is not accessible from regular
+// application processes on some Android platform releases.
+// On success, return new ELF hwcaps, or 0 on failure.
+static uint32_t get_elf_hwcap_from_proc_self_auxv(void)
+{
+ const char filepath[] = "/proc/self/auxv";
+ int fd = TEMP_FAILURE_RETRY(open(filepath, O_RDONLY));
+
+ if (fd < 0)
+ {
+ char ebuffer[256] = { 0 };
+ D("Could not open %s: %s\n", filepath, winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
+ return 0;
+ }
+
+ struct
+ {
+ uint32_t tag;
+ uint32_t value;
+ } entry;
+
+ uint32_t result = 0;
+
+ for (;;)
+ {
+ int ret = TEMP_FAILURE_RETRY(read(fd, (char*)&entry, sizeof entry));
+
+ if (ret < 0)
+ {
+ char ebuffer[256] = { 0 };
+ D("Error while reading %s: %s\n", filepath,
+ winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
+ break;
+ }
+
+ // Detect end of list.
+ if (ret == 0 || (entry.tag == 0 && entry.value == 0))
+ break;
+
+ if (entry.tag == AT_HWCAP)
+ {
+ result = entry.value;
+ break;
+ }
+ }
+
+ close(fd);
+ return result;
+}
+
+/* Compute the ELF HWCAP flags from the content of /proc/cpuinfo.
+ * This works by parsing the 'Features' line, which lists which optional
+ * features the device's CPU supports, on top of its reference
+ * architecture.
+ */
+static uint32_t get_elf_hwcap_from_proc_cpuinfo(const char* cpuinfo, int cpuinfo_len)
+{
+ uint32_t hwcaps = 0;
+ long architecture = 0;
+ char* cpuArch = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "CPU architecture");
+
+ if (cpuArch)
+ {
+ architecture = strtol(cpuArch, NULL, 10);
+ free(cpuArch);
+
+ if (architecture >= 8L)
+ {
+ // This is a 32-bit ARM binary running on a 64-bit ARM64 kernel.
+ // The 'Features' line only lists the optional features that the
+ // device's CPU supports, compared to its reference architecture
+ // which are of no use for this process.
+ D("Faking 32-bit ARM HWCaps on ARMv%ld CPU\n", architecture);
+ return HWCAP_SET_FOR_ARMV8;
+ }
+ }
+
+ char* cpuFeatures = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "Features");
+
+ if (cpuFeatures != NULL)
+ {
+ D("Found cpuFeatures = '%s'\n", cpuFeatures);
+
+ if (has_list_item(cpuFeatures, "vfp"))
+ hwcaps |= HWCAP_VFP;
+
+ if (has_list_item(cpuFeatures, "vfpv3"))
+ hwcaps |= HWCAP_VFPv3;
+
+ if (has_list_item(cpuFeatures, "vfpv3d16"))
+ hwcaps |= HWCAP_VFPv3D16;
+
+ if (has_list_item(cpuFeatures, "vfpv4"))
+ hwcaps |= HWCAP_VFPv4;
+
+ if (has_list_item(cpuFeatures, "neon"))
+ hwcaps |= HWCAP_NEON;
+
+ if (has_list_item(cpuFeatures, "idiva"))
+ hwcaps |= HWCAP_IDIVA;
+
+ if (has_list_item(cpuFeatures, "idivt"))
+ hwcaps |= HWCAP_IDIVT;
+
+ if (has_list_item(cpuFeatures, "idiv"))
+ hwcaps |= HWCAP_IDIVA | HWCAP_IDIVT;
+
+ if (has_list_item(cpuFeatures, "iwmmxt"))
+ hwcaps |= HWCAP_IWMMXT;
+
+ free(cpuFeatures);
+ }
+
+ return hwcaps;
+}
+#endif /* __arm__ */
+
+/* Return the number of cpus present on a given device.
+ *
+ * To handle all weird kernel configurations, we need to compute the
+ * intersection of the 'present' and 'possible' CPU lists and count
+ * the result.
+ */
+static int get_cpu_count(void)
+{
+ CpuList cpus_present[1];
+ CpuList cpus_possible[1];
+ cpulist_read_from(cpus_present, "/sys/devices/system/cpu/present");
+ cpulist_read_from(cpus_possible, "/sys/devices/system/cpu/possible");
+ /* Compute the intersection of both sets to get the actual number of
+ * CPU cores that can be used on this device by the kernel.
+ */
+ cpulist_and(cpus_present, cpus_possible);
+ return cpulist_count(cpus_present);
+}
+
+static void android_cpuInitFamily(void)
+{
+#if defined(__arm__)
+ g_cpuFamily = ANDROID_CPU_FAMILY_ARM;
+#elif defined(__i386__)
+ g_cpuFamily = ANDROID_CPU_FAMILY_X86;
+#elif defined(__mips64)
+ /* Needs to be before __mips__ since the compiler defines both */
+ g_cpuFamily = ANDROID_CPU_FAMILY_MIPS64;
+#elif defined(__mips__)
+ g_cpuFamily = ANDROID_CPU_FAMILY_MIPS;
+#elif defined(__aarch64__)
+ g_cpuFamily = ANDROID_CPU_FAMILY_ARM64;
+#elif defined(__x86_64__)
+ g_cpuFamily = ANDROID_CPU_FAMILY_X86_64;
+#else
+ g_cpuFamily = ANDROID_CPU_FAMILY_UNKNOWN;
+#endif
+}
+
+static void android_cpuInit(void)
+{
+ char* cpuinfo = NULL;
+ int cpuinfo_len;
+ android_cpuInitFamily();
+ g_cpuFeatures = 0;
+ g_cpuCount = 1;
+ g_inited = 1;
+ cpuinfo_len = get_file_size("/proc/cpuinfo");
+
+ if (cpuinfo_len < 0)
+ {
+ D("cpuinfo_len cannot be computed!");
+ return;
+ }
+
+ cpuinfo = malloc(cpuinfo_len);
+
+ if (cpuinfo == NULL)
+ {
+ D("cpuinfo buffer could not be allocated");
+ return;
+ }
+
+ cpuinfo_len = read_file("/proc/cpuinfo", cpuinfo, cpuinfo_len);
+ D("cpuinfo_len is (%d):\n%.*s\n", cpuinfo_len, cpuinfo_len >= 0 ? cpuinfo_len : 0, cpuinfo);
+
+ if (cpuinfo_len < 0) /* should not happen */
+ {
+ free(cpuinfo);
+ return;
+ }
+
+ /* Count the CPU cores, the value may be 0 for single-core CPUs */
+ g_cpuCount = get_cpu_count();
+
+ if (g_cpuCount == 0)
+ {
+ g_cpuCount = 1;
+ }
+
+ D("found cpuCount = %d\n", g_cpuCount);
+#ifdef __arm__
+ {
+ /* Extract architecture from the "CPU Architecture" field.
+ * The list is well-known, unlike the the output of
+ * the 'Processor' field which can vary greatly.
+ *
+ * See the definition of the 'proc_arch' array in
+ * $KERNEL/arch/arm/kernel/setup.c and the 'c_show' function in
+ * same file.
+ */
+ char* cpuArch = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "CPU architecture");
+
+ if (cpuArch != NULL)
+ {
+ char* end;
+ long archNumber;
+ int hasARMv7 = 0;
+ D("found cpuArch = '%s'\n", cpuArch);
+ /* read the initial decimal number, ignore the rest */
+ archNumber = strtol(cpuArch, &end, 10);
+
+ /* Note that ARMv8 is upwards compatible with ARMv7. */
+ if (end > cpuArch && archNumber >= 7)
+ {
+ hasARMv7 = 1;
+ }
+
+ /* Unfortunately, it seems that certain ARMv6-based CPUs
+ * report an incorrect architecture number of 7!
+ *
+ * See http://code.google.com/p/android/issues/detail?id=10812
+ *
+ * We try to correct this by looking at the 'elf_format'
+ * field reported by the 'Processor' field, which is of the
+ * form of "(v7l)" for an ARMv7-based CPU, and "(v6l)" for
+ * an ARMv6-one.
+ */
+ if (hasARMv7)
+ {
+ char* cpuProc = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "Processor");
+
+ if (cpuProc != NULL)
+ {
+ D("found cpuProc = '%s'\n", cpuProc);
+
+ if (has_list_item(cpuProc, "(v6l)"))
+ {
+ D("CPU processor and architecture mismatch!!\n");
+ hasARMv7 = 0;
+ }
+
+ free(cpuProc);
+ }
+ }
+
+ if (hasARMv7)
+ {
+ g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_ARMv7;
+ }
+
+ /* The LDREX / STREX instructions are available from ARMv6 */
+ if (archNumber >= 6)
+ {
+ g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_LDREX_STREX;
+ }
+
+ free(cpuArch);
+ }
+
+ /* Extract the list of CPU features from ELF hwcaps */
+ uint32_t hwcaps = 0;
+ hwcaps = get_elf_hwcap_from_getauxval(AT_HWCAP);
+
+ if (!hwcaps)
+ {
+ D("Parsing /proc/self/auxv to extract ELF hwcaps!\n");
+ hwcaps = get_elf_hwcap_from_proc_self_auxv();
+ }
+
+ if (!hwcaps)
+ {
+ // Parsing /proc/self/auxv will fail from regular application
+ // processes on some Android platform versions, when this happens
+ // parse proc/cpuinfo instead.
+ D("Parsing /proc/cpuinfo to extract ELF hwcaps!\n");
+ hwcaps = get_elf_hwcap_from_proc_cpuinfo(cpuinfo, cpuinfo_len);
+ }
+
+ if (hwcaps != 0)
+ {
+ int has_vfp = (hwcaps & HWCAP_VFP);
+ int has_vfpv3 = (hwcaps & HWCAP_VFPv3);
+ int has_vfpv3d16 = (hwcaps & HWCAP_VFPv3D16);
+ int has_vfpv4 = (hwcaps & HWCAP_VFPv4);
+ int has_neon = (hwcaps & HWCAP_NEON);
+ int has_idiva = (hwcaps & HWCAP_IDIVA);
+ int has_idivt = (hwcaps & HWCAP_IDIVT);
+ int has_iwmmxt = (hwcaps & HWCAP_IWMMXT);
+
+ // The kernel does a poor job at ensuring consistency when
+ // describing CPU features. So lots of guessing is needed.
+
+ // 'vfpv4' implies VFPv3|VFP_FMA|FP16
+ if (has_vfpv4)
+ g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3 | ANDROID_CPU_ARM_FEATURE_VFP_FP16 |
+ ANDROID_CPU_ARM_FEATURE_VFP_FMA;
+
+ // 'vfpv3' or 'vfpv3d16' imply VFPv3. Note that unlike GCC,
+ // a value of 'vfpv3' doesn't necessarily mean that the D32
+ // feature is present, so be conservative. All CPUs in the
+ // field that support D32 also support NEON, so this should
+ // not be a problem in practice.
+ if (has_vfpv3 || has_vfpv3d16)
+ g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3;
+
+ // 'vfp' is super ambiguous. Depending on the kernel, it can
+ // either mean VFPv2 or VFPv3. Make it depend on ARMv7.
+ if (has_vfp)
+ {
+ if (g_cpuFeatures & ANDROID_CPU_ARM_FEATURE_ARMv7)
+ g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3;
+ else
+ g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv2;
+ }
+
+ // Neon implies VFPv3|D32, and if vfpv4 is detected, NEON_FMA
+ if (has_neon)
+ {
+ g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3 | ANDROID_CPU_ARM_FEATURE_NEON |
+ ANDROID_CPU_ARM_FEATURE_VFP_D32;
+
+ if (has_vfpv4)
+ g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_NEON_FMA;
+ }
+
+ // VFPv3 implies VFPv2 and ARMv7
+ if (g_cpuFeatures & ANDROID_CPU_ARM_FEATURE_VFPv3)
+ g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv2 | ANDROID_CPU_ARM_FEATURE_ARMv7;
+
+ if (has_idiva)
+ g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_IDIV_ARM;
+
+ if (has_idivt)
+ g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2;
+
+ if (has_iwmmxt)
+ g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_iWMMXt;
+ }
+
+ /* Extract the list of CPU features from ELF hwcaps2 */
+ uint32_t hwcaps2 = 0;
+ hwcaps2 = get_elf_hwcap_from_getauxval(AT_HWCAP2);
+
+ if (hwcaps2 != 0)
+ {
+ int has_aes = (hwcaps2 & HWCAP2_AES);
+ int has_pmull = (hwcaps2 & HWCAP2_PMULL);
+ int has_sha1 = (hwcaps2 & HWCAP2_SHA1);
+ int has_sha2 = (hwcaps2 & HWCAP2_SHA2);
+ int has_crc32 = (hwcaps2 & HWCAP2_CRC32);
+
+ if (has_aes)
+ g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_AES;
+
+ if (has_pmull)
+ g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_PMULL;
+
+ if (has_sha1)
+ g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_SHA1;
+
+ if (has_sha2)
+ g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_SHA2;
+
+ if (has_crc32)
+ g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_CRC32;
+ }
+
+ /* Extract the cpuid value from various fields */
+ // The CPUID value is broken up in several entries in /proc/cpuinfo.
+ // This table is used to rebuild it from the entries.
+ static const struct CpuIdEntry
+ {
+ const char* field;
+ char format;
+ char bit_lshift;
+ char bit_length;
+ } cpu_id_entries[] = {
+ { "CPU implementer", 'x', 24, 8 },
+ { "CPU variant", 'x', 20, 4 },
+ { "CPU part", 'x', 4, 12 },
+ { "CPU revision", 'd', 0, 4 },
+ };
+ D("Parsing /proc/cpuinfo to recover CPUID\n");
+
+ for (size_t i = 0; i < sizeof(cpu_id_entries) / sizeof(cpu_id_entries[0]); ++i)
+ {
+ const struct CpuIdEntry* entry = &cpu_id_entries[i];
+ char* value = extract_cpuinfo_field(cpuinfo, cpuinfo_len, entry->field);
+
+ if (value == NULL)
+ continue;
+
+ D("field=%s value='%s'\n", entry->field, value);
+ char* value_end = value + strlen(value);
+ int val = 0;
+ const char* start = value;
+ const char* p;
+
+ if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X'))
+ {
+ start += 2;
+ p = parse_hexadecimal(start, value_end, &val);
+ }
+ else if (entry->format == 'x')
+ p = parse_hexadecimal(value, value_end, &val);
+ else
+ p = parse_decimal(value, value_end, &val);
+
+ if (p > (const char*)start)
+ {
+ val &= ((1 << entry->bit_length) - 1);
+ val <<= entry->bit_lshift;
+ g_cpuIdArm |= (uint32_t)val;
+ }
+
+ free(value);
+ }
+
+ // Handle kernel configuration bugs that prevent the correct
+ // reporting of CPU features.
+ static const struct CpuFix
+ {
+ uint32_t cpuid;
+ uint64_t or_flags;
+ } cpu_fixes[] = {
+ /* The Nexus 4 (Qualcomm Krait) kernel configuration
+ * forgets to report IDIV support. */
+ { 0x510006f2, ANDROID_CPU_ARM_FEATURE_IDIV_ARM | ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2 },
+ { 0x510006f3, ANDROID_CPU_ARM_FEATURE_IDIV_ARM | ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2 },
+ };
+
+ for (size_t n = 0; n < sizeof(cpu_fixes) / sizeof(cpu_fixes[0]); ++n)
+ {
+ const struct CpuFix* entry = &cpu_fixes[n];
+
+ if (g_cpuIdArm == entry->cpuid)
+ g_cpuFeatures |= entry->or_flags;
+ }
+
+ // Special case: The emulator-specific Android 4.2 kernel fails
+ // to report support for the 32-bit ARM IDIV instruction.
+ // Technically, this is a feature of the virtual CPU implemented
+ // by the emulator. Note that it could also support Thumb IDIV
+ // in the future, and this will have to be slightly updated.
+ char* hardware = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "Hardware");
+
+ if (hardware)
+ {
+ if (!strcmp(hardware, "Goldfish") && g_cpuIdArm == 0x4100c080 &&
+ (g_cpuFamily & ANDROID_CPU_ARM_FEATURE_ARMv7) != 0)
+ {
+ g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_IDIV_ARM;
+ }
+
+ free(hardware);
+ }
+ }
+#endif /* __arm__ */
+#ifdef __aarch64__
+ {
+ /* Extract the list of CPU features from ELF hwcaps */
+ uint32_t hwcaps = 0;
+ hwcaps = get_elf_hwcap_from_getauxval(AT_HWCAP);
+
+ if (hwcaps != 0)
+ {
+ int has_fp = (hwcaps & HWCAP_FP);
+ int has_asimd = (hwcaps & HWCAP_ASIMD);
+ int has_aes = (hwcaps & HWCAP_AES);
+ int has_pmull = (hwcaps & HWCAP_PMULL);
+ int has_sha1 = (hwcaps & HWCAP_SHA1);
+ int has_sha2 = (hwcaps & HWCAP_SHA2);
+ int has_crc32 = (hwcaps & HWCAP_CRC32);
+
+ if (has_fp == 0)
+ {
+ D("ERROR: Floating-point unit missing, but is required by Android on AArch64 "
+ "CPUs\n");
+ }
+
+ if (has_asimd == 0)
+ {
+ D("ERROR: ASIMD unit missing, but is required by Android on AArch64 CPUs\n");
+ }
+
+ if (has_fp)
+ g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_FP;
+
+ if (has_asimd)
+ g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_ASIMD;
+
+ if (has_aes)
+ g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_AES;
+
+ if (has_pmull)
+ g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_PMULL;
+
+ if (has_sha1)
+ g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_SHA1;
+
+ if (has_sha2)
+ g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_SHA2;
+
+ if (has_crc32)
+ g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_CRC32;
+ }
+ }
+#endif /* __aarch64__ */
+#if defined(__i386__) || defined(__x86_64__)
+ int regs[4];
+ /* According to http://en.wikipedia.org/wiki/CPUID */
+#define VENDOR_INTEL_b 0x756e6547
+#define VENDOR_INTEL_c 0x6c65746e
+#define VENDOR_INTEL_d 0x49656e69
+ x86_cpuid(0, regs);
+ int vendorIsIntel =
+ (regs[1] == VENDOR_INTEL_b && regs[2] == VENDOR_INTEL_c && regs[3] == VENDOR_INTEL_d);
+ x86_cpuid(1, regs);
+
+ if ((regs[2] & (1 << 9)) != 0)
+ {
+ g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_SSSE3;
+ }
+
+ if ((regs[2] & (1 << 23)) != 0)
+ {
+ g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_POPCNT;
+ }
+
+ if ((regs[2] & (1 << 19)) != 0)
+ {
+ g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_SSE4_1;
+ }
+
+ if ((regs[2] & (1 << 20)) != 0)
+ {
+ g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_SSE4_2;
+ }
+
+ if (vendorIsIntel && (regs[2] & (1 << 22)) != 0)
+ {
+ g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_MOVBE;
+ }
+
+ if ((regs[2] & (1 << 25)) != 0)
+ {
+ g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_AES_NI;
+ }
+
+ if ((regs[2] & (1 << 28)) != 0)
+ {
+ g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_AVX;
+ }
+
+ if ((regs[2] & (1 << 30)) != 0)
+ {
+ g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_RDRAND;
+ }
+
+ x86_cpuid(7, regs);
+
+ if ((regs[1] & (1 << 5)) != 0)
+ {
+ g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_AVX2;
+ }
+
+ if ((regs[1] & (1 << 29)) != 0)
+ {
+ g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_SHA_NI;
+ }
+
+#endif
+#if defined(__mips__)
+ {
+ /* MIPS and MIPS64 */
+ /* Extract the list of CPU features from ELF hwcaps */
+ uint32_t hwcaps = 0;
+ hwcaps = get_elf_hwcap_from_getauxval(AT_HWCAP);
+
+ if (hwcaps != 0)
+ {
+ int has_r6 = (hwcaps & HWCAP_MIPS_R6);
+ int has_msa = (hwcaps & HWCAP_MIPS_MSA);
+
+ if (has_r6)
+ g_cpuFeatures |= ANDROID_CPU_MIPS_FEATURE_R6;
+
+ if (has_msa)
+ g_cpuFeatures |= ANDROID_CPU_MIPS_FEATURE_MSA;
+ }
+ }
+#endif /* __mips__ */
+ free(cpuinfo);
+}
+
+AndroidCpuFamily android_getCpuFamily(void)
+{
+ pthread_once(&g_once, android_cpuInit);
+ return g_cpuFamily;
+}
+
+uint64_t android_getCpuFeatures(void)
+{
+ pthread_once(&g_once, android_cpuInit);
+ return g_cpuFeatures;
+}
+
+int android_getCpuCount(void)
+{
+ pthread_once(&g_once, android_cpuInit);
+ return g_cpuCount;
+}
+
+static void android_cpuInitDummy(void)
+{
+ g_inited = 1;
+}
+
+int android_setCpu(int cpu_count, uint64_t cpu_features)
+{
+ /* Fail if the library was already initialized. */
+ if (g_inited)
+ return 0;
+
+ android_cpuInitFamily();
+ g_cpuCount = (cpu_count <= 0 ? 1 : cpu_count);
+ g_cpuFeatures = cpu_features;
+ pthread_once(&g_once, android_cpuInitDummy);
+ return 1;
+}
+
+#ifdef __arm__
+uint32_t android_getCpuIdArm(void)
+{
+ pthread_once(&g_once, android_cpuInit);
+ return g_cpuIdArm;
+}
+
+int android_setCpuArm(int cpu_count, uint64_t cpu_features, uint32_t cpu_id)
+{
+ if (!android_setCpu(cpu_count, cpu_features))
+ return 0;
+
+ g_cpuIdArm = cpu_id;
+ return 1;
+}
+#endif /* __arm__ */
+
+/*
+ * Technical note: Making sense of ARM's FPU architecture versions.
+ *
+ * FPA was ARM's first attempt at an FPU architecture. There is no Android
+ * device that actually uses it since this technology was already obsolete
+ * when the project started. If you see references to FPA instructions
+ * somewhere, you can be sure that this doesn't apply to Android at all.
+ *
+ * FPA was followed by "VFP", soon renamed "VFPv1" due to the emergence of
+ * new versions / additions to it. ARM considers this obsolete right now,
+ * and no known Android device implements it either.
+ *
+ * VFPv2 added a few instructions to VFPv1, and is an *optional* extension
+ * supported by some ARMv5TE, ARMv6 and ARMv6T2 CPUs. Note that a device
+ * supporting the 'armeabi' ABI doesn't necessarily support these.
+ *
+ * VFPv3-D16 adds a few instructions on top of VFPv2 and is typically used
+ * on ARMv7-A CPUs which implement a FPU. Note that it is also mandated
+ * by the Android 'armeabi-v7a' ABI. The -D16 suffix in its name means
+ * that it provides 16 double-precision FPU registers (d0-d15) and 32
+ * single-precision ones (s0-s31) which happen to be mapped to the same
+ * register banks.
+ *
+ * VFPv3-D32 is the name of an extension to VFPv3-D16 that provides 16
+ * additional double precision registers (d16-d31). Note that there are
+ * still only 32 single precision registers.
+ *
+ * VFPv3xD is a *subset* of VFPv3-D16 that only provides single-precision
+ * registers. It is only used on ARMv7-M (i.e. on micro-controllers) which
+ * are not supported by Android. Note that it is not compatible with VFPv2.
+ *
+ * NOTE: The term 'VFPv3' usually designate either VFPv3-D16 or VFPv3-D32
+ * depending on context. For example GCC uses it for VFPv3-D32, but
+ * the Linux kernel code uses it for VFPv3-D16 (especially in
+ * /proc/cpuinfo). Always try to use the full designation when
+ * possible.
+ *
+ * NEON, a.k.a. "ARM Advanced SIMD" is an extension that provides
+ * instructions to perform parallel computations on vectors of 8, 16,
+ * 32, 64 and 128 bit quantities. NEON requires VFPv32-D32 since all
+ * NEON registers are also mapped to the same register banks.
+ *
+ * VFPv4-D16, adds a few instructions on top of VFPv3-D16 in order to
+ * perform fused multiply-accumulate on VFP registers, as well as
+ * half-precision (16-bit) conversion operations.
+ *
+ * VFPv4-D32 is VFPv4-D16 with 32, instead of 16, FPU double precision
+ * registers.
+ *
+ * VPFv4-NEON is VFPv4-D32 with NEON instructions. It also adds fused
+ * multiply-accumulate instructions that work on the NEON registers.
+ *
+ * NOTE: Similarly, "VFPv4" might either reference VFPv4-D16 or VFPv4-D32
+ * depending on context.
+ *
+ * The following information was determined by scanning the binutils-2.22
+ * sources:
+ *
+ * Basic VFP instruction subsets:
+ *
+ * #define FPU_VFP_EXT_V1xD 0x08000000 // Base VFP instruction set.
+ * #define FPU_VFP_EXT_V1 0x04000000 // Double-precision insns.
+ * #define FPU_VFP_EXT_V2 0x02000000 // ARM10E VFPr1.
+ * #define FPU_VFP_EXT_V3xD 0x01000000 // VFPv3 single-precision.
+ * #define FPU_VFP_EXT_V3 0x00800000 // VFPv3 double-precision.
+ * #define FPU_NEON_EXT_V1 0x00400000 // Neon (SIMD) insns.
+ * #define FPU_VFP_EXT_D32 0x00200000 // Registers D16-D31.
+ * #define FPU_VFP_EXT_FP16 0x00100000 // Half-precision extensions.
+ * #define FPU_NEON_EXT_FMA 0x00080000 // Neon fused multiply-add
+ * #define FPU_VFP_EXT_FMA 0x00040000 // VFP fused multiply-add
+ *
+ * FPU types (excluding NEON)
+ *
+ * FPU_VFP_V1xD (EXT_V1xD)
+ * |
+ * +--------------------------+
+ * | |
+ * FPU_VFP_V1 (+EXT_V1) FPU_VFP_V3xD (+EXT_V2+EXT_V3xD)
+ * | |
+ * | |
+ * FPU_VFP_V2 (+EXT_V2) FPU_VFP_V4_SP_D16 (+EXT_FP16+EXT_FMA)
+ * |
+ * FPU_VFP_V3D16 (+EXT_Vx3D+EXT_V3)
+ * |
+ * +--------------------------+
+ * | |
+ * FPU_VFP_V3 (+EXT_D32) FPU_VFP_V4D16 (+EXT_FP16+EXT_FMA)
+ * | |
+ * | FPU_VFP_V4 (+EXT_D32)
+ * |
+ * FPU_VFP_HARD (+EXT_FMA+NEON_EXT_FMA)
+ *
+ * VFP architectures:
+ *
+ * ARCH_VFP_V1xD (EXT_V1xD)
+ * |
+ * +------------------+
+ * | |
+ * | ARCH_VFP_V3xD (+EXT_V2+EXT_V3xD)
+ * | |
+ * | ARCH_VFP_V3xD_FP16 (+EXT_FP16)
+ * | |
+ * | ARCH_VFP_V4_SP_D16 (+EXT_FMA)
+ * |
+ * ARCH_VFP_V1 (+EXT_V1)
+ * |
+ * ARCH_VFP_V2 (+EXT_V2)
+ * |
+ * ARCH_VFP_V3D16 (+EXT_V3xD+EXT_V3)
+ * |
+ * +-------------------+
+ * | |
+ * | ARCH_VFP_V3D16_FP16 (+EXT_FP16)
+ * |
+ * +-------------------+
+ * | |
+ * | ARCH_VFP_V4_D16 (+EXT_FP16+EXT_FMA)
+ * | |
+ * | ARCH_VFP_V4 (+EXT_D32)
+ * | |
+ * | ARCH_NEON_VFP_V4 (+EXT_NEON+EXT_NEON_FMA)
+ * |
+ * ARCH_VFP_V3 (+EXT_D32)
+ * |
+ * +-------------------+
+ * | |
+ * | ARCH_VFP_V3_FP16 (+EXT_FP16)
+ * |
+ * ARCH_VFP_V3_PLUS_NEON_V1 (+EXT_NEON)
+ * |
+ * ARCH_NEON_FP16 (+EXT_FP16)
+ *
+ * -fpu=<name> values and their correspondance with FPU architectures above:
+ *
+ * {"vfp", FPU_ARCH_VFP_V2},
+ * {"vfp9", FPU_ARCH_VFP_V2},
+ * {"vfp3", FPU_ARCH_VFP_V3}, // For backwards compatbility.
+ * {"vfp10", FPU_ARCH_VFP_V2},
+ * {"vfp10-r0", FPU_ARCH_VFP_V1},
+ * {"vfpxd", FPU_ARCH_VFP_V1xD},
+ * {"vfpv2", FPU_ARCH_VFP_V2},
+ * {"vfpv3", FPU_ARCH_VFP_V3},
+ * {"vfpv3-fp16", FPU_ARCH_VFP_V3_FP16},
+ * {"vfpv3-d16", FPU_ARCH_VFP_V3D16},
+ * {"vfpv3-d16-fp16", FPU_ARCH_VFP_V3D16_FP16},
+ * {"vfpv3xd", FPU_ARCH_VFP_V3xD},
+ * {"vfpv3xd-fp16", FPU_ARCH_VFP_V3xD_FP16},
+ * {"neon", FPU_ARCH_VFP_V3_PLUS_NEON_V1},
+ * {"neon-fp16", FPU_ARCH_NEON_FP16},
+ * {"vfpv4", FPU_ARCH_VFP_V4},
+ * {"vfpv4-d16", FPU_ARCH_VFP_V4D16},
+ * {"fpv4-sp-d16", FPU_ARCH_VFP_V4_SP_D16},
+ * {"neon-vfpv4", FPU_ARCH_NEON_VFP_V4},
+ *
+ *
+ * Simplified diagram that only includes FPUs supported by Android:
+ * Only ARCH_VFP_V3D16 is actually mandated by the armeabi-v7a ABI,
+ * all others are optional and must be probed at runtime.
+ *
+ * ARCH_VFP_V3D16 (EXT_V1xD+EXT_V1+EXT_V2+EXT_V3xD+EXT_V3)
+ * |
+ * +-------------------+
+ * | |
+ * | ARCH_VFP_V3D16_FP16 (+EXT_FP16)
+ * |
+ * +-------------------+
+ * | |
+ * | ARCH_VFP_V4_D16 (+EXT_FP16+EXT_FMA)
+ * | |
+ * | ARCH_VFP_V4 (+EXT_D32)
+ * | |
+ * | ARCH_NEON_VFP_V4 (+EXT_NEON+EXT_NEON_FMA)
+ * |
+ * ARCH_VFP_V3 (+EXT_D32)
+ * |
+ * +-------------------+
+ * | |
+ * | ARCH_VFP_V3_FP16 (+EXT_FP16)
+ * |
+ * ARCH_VFP_V3_PLUS_NEON_V1 (+EXT_NEON)
+ * |
+ * ARCH_NEON_FP16 (+EXT_FP16)
+ *
+ */
diff --git a/winpr/libwinpr/sysinfo/cpufeatures/cpu-features.h b/winpr/libwinpr/sysinfo/cpufeatures/cpu-features.h
new file mode 100644
index 0000000..9520c8a
--- /dev/null
+++ b/winpr/libwinpr/sysinfo/cpufeatures/cpu-features.h
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#ifndef CPU_FEATURES_H
+#define CPU_FEATURES_H
+
+#include <sys/cdefs.h>
+#include <stdint.h>
+
+__BEGIN_DECLS
+
+/* A list of valid values returned by android_getCpuFamily().
+ * They describe the CPU Architecture of the current process.
+ */
+typedef enum
+{
+ ANDROID_CPU_FAMILY_UNKNOWN = 0,
+ ANDROID_CPU_FAMILY_ARM,
+ ANDROID_CPU_FAMILY_X86,
+ ANDROID_CPU_FAMILY_MIPS,
+ ANDROID_CPU_FAMILY_ARM64,
+ ANDROID_CPU_FAMILY_X86_64,
+ ANDROID_CPU_FAMILY_MIPS64,
+
+ ANDROID_CPU_FAMILY_MAX /* do not remove */
+
+} AndroidCpuFamily;
+
+/* Return the CPU family of the current process.
+ *
+ * Note that this matches the bitness of the current process. I.e. when
+ * running a 32-bit binary on a 64-bit capable CPU, this will return the
+ * 32-bit CPU family value.
+ */
+extern AndroidCpuFamily android_getCpuFamily(void);
+
+/* Return a bitmap describing a set of optional CPU features that are
+ * supported by the current device's CPU. The exact bit-flags returned
+ * depend on the value returned by android_getCpuFamily(). See the
+ * documentation for the ANDROID_CPU_*_FEATURE_* flags below for details.
+ */
+extern uint64_t android_getCpuFeatures(void);
+
+/* The list of feature flags for ANDROID_CPU_FAMILY_ARM that can be
+ * recognized by the library (see note below for 64-bit ARM). Value details
+ * are:
+ *
+ * VFPv2:
+ * CPU supports the VFPv2 instruction set. Many, but not all, ARMv6 CPUs
+ * support these instructions. VFPv2 is a subset of VFPv3 so this will
+ * be set whenever VFPv3 is set too.
+ *
+ * ARMv7:
+ * CPU supports the ARMv7-A basic instruction set.
+ * This feature is mandated by the 'armeabi-v7a' ABI.
+ *
+ * VFPv3:
+ * CPU supports the VFPv3-D16 instruction set, providing hardware FPU
+ * support for single and double precision floating point registers.
+ * Note that only 16 FPU registers are available by default, unless
+ * the D32 bit is set too. This feature is also mandated by the
+ * 'armeabi-v7a' ABI.
+ *
+ * VFP_D32:
+ * CPU VFP optional extension that provides 32 FPU registers,
+ * instead of 16. Note that ARM mandates this feature is the 'NEON'
+ * feature is implemented by the CPU.
+ *
+ * NEON:
+ * CPU FPU supports "ARM Advanced SIMD" instructions, also known as
+ * NEON. Note that this mandates the VFP_D32 feature as well, per the
+ * ARM Architecture specification.
+ *
+ * VFP_FP16:
+ * Half-width floating precision VFP extension. If set, the CPU
+ * supports instructions to perform floating-point operations on
+ * 16-bit registers. This is part of the VFPv4 specification, but
+ * not mandated by any Android ABI.
+ *
+ * VFP_FMA:
+ * Fused multiply-accumulate VFP instructions extension. Also part of
+ * the VFPv4 specification, but not mandated by any Android ABI.
+ *
+ * NEON_FMA:
+ * Fused multiply-accumulate NEON instructions extension. Optional
+ * extension from the VFPv4 specification, but not mandated by any
+ * Android ABI.
+ *
+ * IDIV_ARM:
+ * Integer division available in ARM mode. Only available
+ * on recent CPUs (e.g. Cortex-A15).
+ *
+ * IDIV_THUMB2:
+ * Integer division available in Thumb-2 mode. Only available
+ * on recent CPUs (e.g. Cortex-A15).
+ *
+ * iWMMXt:
+ * Optional extension that adds MMX registers and operations to an
+ * ARM CPU. This is only available on a few XScale-based CPU designs
+ * sold by Marvell. Pretty rare in practice.
+ *
+ * AES:
+ * CPU supports AES instructions. These instructions are only
+ * available for 32-bit applications running on ARMv8 CPU.
+ *
+ * CRC32:
+ * CPU supports CRC32 instructions. These instructions are only
+ * available for 32-bit applications running on ARMv8 CPU.
+ *
+ * SHA2:
+ * CPU supports SHA2 instructions. These instructions are only
+ * available for 32-bit applications running on ARMv8 CPU.
+ *
+ * SHA1:
+ * CPU supports SHA1 instructions. These instructions are only
+ * available for 32-bit applications running on ARMv8 CPU.
+ *
+ * PMULL:
+ * CPU supports 64-bit PMULL and PMULL2 instructions. These
+ * instructions are only available for 32-bit applications
+ * running on ARMv8 CPU.
+ *
+ * If you want to tell the compiler to generate code that targets one of
+ * the feature set above, you should probably use one of the following
+ * flags (for more details, see technical note at the end of this file):
+ *
+ * -mfpu=vfp
+ * -mfpu=vfpv2
+ * These are equivalent and tell GCC to use VFPv2 instructions for
+ * floating-point operations. Use this if you want your code to
+ * run on *some* ARMv6 devices, and any ARMv7-A device supported
+ * by Android.
+ *
+ * Generated code requires VFPv2 feature.
+ *
+ * -mfpu=vfpv3-d16
+ * Tell GCC to use VFPv3 instructions (using only 16 FPU registers).
+ * This should be generic code that runs on any CPU that supports the
+ * 'armeabi-v7a' Android ABI. Note that no ARMv6 CPU supports this.
+ *
+ * Generated code requires VFPv3 feature.
+ *
+ * -mfpu=vfpv3
+ * Tell GCC to use VFPv3 instructions with 32 FPU registers.
+ * Generated code requires VFPv3|VFP_D32 features.
+ *
+ * -mfpu=neon
+ * Tell GCC to use VFPv3 instructions with 32 FPU registers, and
+ * also support NEON intrinsics (see <arm_neon.h>).
+ * Generated code requires VFPv3|VFP_D32|NEON features.
+ *
+ * -mfpu=vfpv4-d16
+ * Generated code requires VFPv3|VFP_FP16|VFP_FMA features.
+ *
+ * -mfpu=vfpv4
+ * Generated code requires VFPv3|VFP_FP16|VFP_FMA|VFP_D32 features.
+ *
+ * -mfpu=neon-vfpv4
+ * Generated code requires VFPv3|VFP_FP16|VFP_FMA|VFP_D32|NEON|NEON_FMA
+ * features.
+ *
+ * -mcpu=cortex-a7
+ * -mcpu=cortex-a15
+ * Generated code requires VFPv3|VFP_FP16|VFP_FMA|VFP_D32|
+ * NEON|NEON_FMA|IDIV_ARM|IDIV_THUMB2
+ * This flag implies -mfpu=neon-vfpv4.
+ *
+ * -mcpu=iwmmxt
+ * Allows the use of iWMMXt instrinsics with GCC.
+ *
+ * IMPORTANT NOTE: These flags should only be tested when
+ * android_getCpuFamily() returns ANDROID_CPU_FAMILY_ARM, i.e. this is a
+ * 32-bit process.
+ *
+ * When running a 64-bit ARM process on an ARMv8 CPU,
+ * android_getCpuFeatures() will return a different set of bitflags
+ */
+enum
+{
+ ANDROID_CPU_ARM_FEATURE_ARMv7 = (1 << 0),
+ ANDROID_CPU_ARM_FEATURE_VFPv3 = (1 << 1),
+ ANDROID_CPU_ARM_FEATURE_NEON = (1 << 2),
+ ANDROID_CPU_ARM_FEATURE_LDREX_STREX = (1 << 3),
+ ANDROID_CPU_ARM_FEATURE_VFPv2 = (1 << 4),
+ ANDROID_CPU_ARM_FEATURE_VFP_D32 = (1 << 5),
+ ANDROID_CPU_ARM_FEATURE_VFP_FP16 = (1 << 6),
+ ANDROID_CPU_ARM_FEATURE_VFP_FMA = (1 << 7),
+ ANDROID_CPU_ARM_FEATURE_NEON_FMA = (1 << 8),
+ ANDROID_CPU_ARM_FEATURE_IDIV_ARM = (1 << 9),
+ ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2 = (1 << 10),
+ ANDROID_CPU_ARM_FEATURE_iWMMXt = (1 << 11),
+ ANDROID_CPU_ARM_FEATURE_AES = (1 << 12),
+ ANDROID_CPU_ARM_FEATURE_PMULL = (1 << 13),
+ ANDROID_CPU_ARM_FEATURE_SHA1 = (1 << 14),
+ ANDROID_CPU_ARM_FEATURE_SHA2 = (1 << 15),
+ ANDROID_CPU_ARM_FEATURE_CRC32 = (1 << 16),
+};
+
+/* The bit flags corresponding to the output of android_getCpuFeatures()
+ * when android_getCpuFamily() returns ANDROID_CPU_FAMILY_ARM64. Value details
+ * are:
+ *
+ * FP:
+ * CPU has Floating-point unit.
+ *
+ * ASIMD:
+ * CPU has Advanced SIMD unit.
+ *
+ * AES:
+ * CPU supports AES instructions.
+ *
+ * CRC32:
+ * CPU supports CRC32 instructions.
+ *
+ * SHA2:
+ * CPU supports SHA2 instructions.
+ *
+ * SHA1:
+ * CPU supports SHA1 instructions.
+ *
+ * PMULL:
+ * CPU supports 64-bit PMULL and PMULL2 instructions.
+ */
+enum
+{
+ ANDROID_CPU_ARM64_FEATURE_FP = (1 << 0),
+ ANDROID_CPU_ARM64_FEATURE_ASIMD = (1 << 1),
+ ANDROID_CPU_ARM64_FEATURE_AES = (1 << 2),
+ ANDROID_CPU_ARM64_FEATURE_PMULL = (1 << 3),
+ ANDROID_CPU_ARM64_FEATURE_SHA1 = (1 << 4),
+ ANDROID_CPU_ARM64_FEATURE_SHA2 = (1 << 5),
+ ANDROID_CPU_ARM64_FEATURE_CRC32 = (1 << 6),
+};
+
+/* The bit flags corresponding to the output of android_getCpuFeatures()
+ * when android_getCpuFamily() returns ANDROID_CPU_FAMILY_X86 or
+ * ANDROID_CPU_FAMILY_X86_64.
+ */
+enum
+{
+ ANDROID_CPU_X86_FEATURE_SSSE3 = (1 << 0),
+ ANDROID_CPU_X86_FEATURE_POPCNT = (1 << 1),
+ ANDROID_CPU_X86_FEATURE_MOVBE = (1 << 2),
+ ANDROID_CPU_X86_FEATURE_SSE4_1 = (1 << 3),
+ ANDROID_CPU_X86_FEATURE_SSE4_2 = (1 << 4),
+ ANDROID_CPU_X86_FEATURE_AES_NI = (1 << 5),
+ ANDROID_CPU_X86_FEATURE_AVX = (1 << 6),
+ ANDROID_CPU_X86_FEATURE_RDRAND = (1 << 7),
+ ANDROID_CPU_X86_FEATURE_AVX2 = (1 << 8),
+ ANDROID_CPU_X86_FEATURE_SHA_NI = (1 << 9),
+};
+
+/* The bit flags corresponding to the output of android_getCpuFeatures()
+ * when android_getCpuFamily() returns ANDROID_CPU_FAMILY_MIPS
+ * or ANDROID_CPU_FAMILY_MIPS64. Values are:
+ *
+ * R6:
+ * CPU executes MIPS Release 6 instructions natively, and
+ * supports obsoleted R1..R5 instructions only via kernel traps.
+ *
+ * MSA:
+ * CPU supports Mips SIMD Architecture instructions.
+ */
+enum
+{
+ ANDROID_CPU_MIPS_FEATURE_R6 = (1 << 0),
+ ANDROID_CPU_MIPS_FEATURE_MSA = (1 << 1),
+};
+
+/* Return the number of CPU cores detected on this device. */
+extern int android_getCpuCount(void);
+
+/* The following is used to force the CPU count and features
+ * mask in sandboxed processes. Under 4.1 and higher, these processes
+ * cannot access /proc, which is the only way to get information from
+ * the kernel about the current hardware (at least on ARM).
+ *
+ * It _must_ be called only once, and before any android_getCpuXXX
+ * function, any other case will fail.
+ *
+ * This function return 1 on success, and 0 on failure.
+ */
+extern int android_setCpu(int cpu_count, uint64_t cpu_features);
+
+#ifdef __arm__
+/* Retrieve the ARM 32-bit CPUID value from the kernel.
+ * Note that this cannot work on sandboxed processes under 4.1 and
+ * higher, unless you called android_setCpuArm() before.
+ */
+extern uint32_t android_getCpuIdArm(void);
+
+/* An ARM-specific variant of android_setCpu() that also allows you
+ * to set the ARM CPUID field.
+ */
+extern int android_setCpuArm(int cpu_count, uint64_t cpu_features, uint32_t cpu_id);
+#endif
+
+__END_DECLS
+
+#endif /* CPU_FEATURES_H */
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;
+}
diff --git a/winpr/libwinpr/sysinfo/test/CMakeLists.txt b/winpr/libwinpr/sysinfo/test/CMakeLists.txt
new file mode 100644
index 0000000..2632e89
--- /dev/null
+++ b/winpr/libwinpr/sysinfo/test/CMakeLists.txt
@@ -0,0 +1,30 @@
+
+set(MODULE_NAME "TestSysInfo")
+set(MODULE_PREFIX "TEST_SYSINFO")
+
+set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c)
+
+set(${MODULE_PREFIX}_TESTS
+ TestGetNativeSystemInfo.c
+ TestCPUFeatures.c
+ TestGetComputerName.c
+ TestSystemTime.c
+ TestLocalTime.c)
+
+create_test_sourcelist(${MODULE_PREFIX}_SRCS
+ ${${MODULE_PREFIX}_DRIVER}
+ ${${MODULE_PREFIX}_TESTS})
+
+add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
+
+target_link_libraries(${MODULE_NAME} winpr)
+
+set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
+
+foreach(test ${${MODULE_PREFIX}_TESTS})
+ get_filename_component(TestName ${test} NAME_WE)
+ add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName})
+endforeach()
+
+set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test")
+
diff --git a/winpr/libwinpr/sysinfo/test/TestCPUFeatures.c b/winpr/libwinpr/sysinfo/test/TestCPUFeatures.c
new file mode 100644
index 0000000..8a596dd
--- /dev/null
+++ b/winpr/libwinpr/sysinfo/test/TestCPUFeatures.c
@@ -0,0 +1,65 @@
+
+#include <winpr/crt.h>
+#include <winpr/sysinfo.h>
+#include <winpr/platform.h>
+
+#define TEST_FEATURE(feature) \
+ printf("\t" #feature ": %s\n", IsProcessorFeaturePresent(feature) ? "yes" : "no")
+#define TEST_FEATURE_EX(feature) \
+ printf("\t" #feature ": %s\n", IsProcessorFeaturePresentEx(feature) ? "yes" : "no")
+int TestCPUFeatures(int argc, char* argv[])
+{
+
+ WINPR_UNUSED(argc);
+ WINPR_UNUSED(argv);
+
+ printf("Base CPU Flags:\n");
+#ifdef _M_IX86_AMD64
+ TEST_FEATURE(PF_MMX_INSTRUCTIONS_AVAILABLE);
+ TEST_FEATURE(PF_XMMI_INSTRUCTIONS_AVAILABLE);
+ TEST_FEATURE(PF_XMMI64_INSTRUCTIONS_AVAILABLE);
+ TEST_FEATURE(PF_3DNOW_INSTRUCTIONS_AVAILABLE);
+ TEST_FEATURE(PF_SSE3_INSTRUCTIONS_AVAILABLE);
+ printf("\n");
+ printf("Extended CPU Flags (not found in windows API):\n");
+ TEST_FEATURE_EX(PF_EX_3DNOW_PREFETCH);
+ TEST_FEATURE_EX(PF_EX_SSSE3);
+ TEST_FEATURE_EX(PF_EX_SSE41);
+ TEST_FEATURE_EX(PF_EX_SSE42);
+ TEST_FEATURE_EX(PF_EX_AVX);
+ TEST_FEATURE_EX(PF_EX_FMA);
+ TEST_FEATURE_EX(PF_EX_AVX_AES);
+ TEST_FEATURE_EX(PF_EX_AVX_PCLMULQDQ);
+#elif defined(_M_ARM)
+ TEST_FEATURE(PF_ARM_NEON_INSTRUCTIONS_AVAILABLE);
+ TEST_FEATURE(PF_ARM_THUMB);
+ TEST_FEATURE(PF_ARM_VFP_32_REGISTERS_AVAILABLE);
+ TEST_FEATURE(PF_ARM_DIVIDE_INSTRUCTION_AVAILABLE);
+ TEST_FEATURE(PF_ARM_VFP3);
+ TEST_FEATURE(PF_ARM_THUMB);
+ TEST_FEATURE(PF_ARM_JAZELLE);
+ TEST_FEATURE(PF_ARM_DSP);
+ TEST_FEATURE(PF_ARM_THUMB2);
+ TEST_FEATURE(PF_ARM_T2EE);
+ TEST_FEATURE(PF_ARM_INTEL_WMMX);
+ printf("Extended CPU Flags (not found in windows API):\n");
+ TEST_FEATURE_EX(PF_EX_ARM_VFP1);
+ TEST_FEATURE_EX(PF_EX_ARM_VFP3D16);
+ TEST_FEATURE_EX(PF_EX_ARM_VFP4);
+ TEST_FEATURE_EX(PF_EX_ARM_IDIVA);
+ TEST_FEATURE_EX(PF_EX_ARM_IDIVT);
+#elif defined(_M_E2K)
+ TEST_FEATURE(PF_MMX_INSTRUCTIONS_AVAILABLE);
+ TEST_FEATURE(PF_3DNOW_INSTRUCTIONS_AVAILABLE);
+ TEST_FEATURE(PF_SSE3_INSTRUCTIONS_AVAILABLE);
+ printf("\n");
+ printf("Extended CPU Flags (not found in windows API):\n");
+ TEST_FEATURE_EX(PF_EX_SSSE3);
+ TEST_FEATURE_EX(PF_EX_SSE41);
+ TEST_FEATURE_EX(PF_EX_SSE42);
+ TEST_FEATURE_EX(PF_EX_AVX);
+ TEST_FEATURE_EX(PF_EX_FMA);
+#endif
+ printf("\n");
+ return 0;
+}
diff --git a/winpr/libwinpr/sysinfo/test/TestGetComputerName.c b/winpr/libwinpr/sysinfo/test/TestGetComputerName.c
new file mode 100644
index 0000000..4444056
--- /dev/null
+++ b/winpr/libwinpr/sysinfo/test/TestGetComputerName.c
@@ -0,0 +1,366 @@
+#include <stdio.h>
+#include <string.h>
+#include <winpr/wtypes.h>
+#include <winpr/sysinfo.h>
+#include <winpr/error.h>
+
+static BOOL Test_GetComputerName(void)
+{
+ /**
+ * BOOL WINAPI GetComputerName(LPTSTR lpBuffer, LPDWORD lpnSize);
+ *
+ * GetComputerName retrieves the NetBIOS name of the local computer.
+ *
+ * lpBuffer [out]
+ * A pointer to a buffer that receives the computer name or the cluster virtual server name.
+ * The buffer size should be large enough to contain MAX_COMPUTERNAME_LENGTH + 1 characters.
+ *
+ * lpnSize [in, out]
+ * On input, specifies the size of the buffer, in TCHARs.
+ * On output, the number of TCHARs copied to the destination buffer, not including the
+ * terminating null character. If the buffer is too small, the function fails and GetLastError
+ * returns ERROR_BUFFER_OVERFLOW. The lpnSize parameter specifies the size of the buffer
+ * required, including the terminating null character
+ *
+ */
+
+ CHAR netbiosName1[MAX_COMPUTERNAME_LENGTH + 1];
+ CHAR netbiosName2[MAX_COMPUTERNAME_LENGTH + 1];
+ const DWORD netbiosBufferSize = sizeof(netbiosName1) / sizeof(CHAR);
+ DWORD dwSize = 0;
+ DWORD dwNameLength = 0;
+ DWORD dwError = 0;
+
+ memset(netbiosName1, 0xAA, netbiosBufferSize);
+ memset(netbiosName2, 0xBB, netbiosBufferSize);
+
+ /* test with null buffer and zero size (required if buffer is null) */
+ dwSize = 0;
+ if (GetComputerNameA(NULL, &dwSize) == TRUE)
+ {
+ fprintf(stderr, "%s: (1) GetComputerNameA unexpectedly succeeded with null buffer\n",
+ __func__);
+ return FALSE;
+ }
+ if ((dwError = GetLastError()) != ERROR_BUFFER_OVERFLOW)
+ {
+ fprintf(stderr,
+ "%s: (2) GetLastError returned 0x%08" PRIX32 " (expected ERROR_BUFFER_OVERFLOW)\n",
+ __func__, dwError);
+ return FALSE;
+ }
+
+ /* test with valid buffer and zero size */
+ dwSize = 0;
+ if (GetComputerNameA(netbiosName1, &dwSize) == TRUE)
+ {
+ fprintf(stderr,
+ "%s: (3) GetComputerNameA unexpectedly succeeded with zero size parameter\n",
+ __func__);
+ return FALSE;
+ }
+ if ((dwError = GetLastError()) != ERROR_BUFFER_OVERFLOW)
+ {
+ fprintf(stderr,
+ "%s: (4) GetLastError returned 0x%08" PRIX32 " (expected ERROR_BUFFER_OVERFLOW)\n",
+ __func__, dwError);
+ return FALSE;
+ }
+ /* check if returned size is valid: must be the size of the buffer required, including the
+ * terminating null character in this case */
+ if (dwSize < 2 || dwSize > netbiosBufferSize)
+ {
+ fprintf(stderr,
+ "%s: (5) GetComputerNameA returned wrong size %" PRIu32
+ " (expected something in the range from 2 to %" PRIu32 ")\n",
+ __func__, dwSize, netbiosBufferSize);
+ return FALSE;
+ }
+ dwNameLength = dwSize - 1;
+
+ /* test with returned size */
+ if (GetComputerNameA(netbiosName1, &dwSize) == FALSE)
+ {
+ fprintf(stderr, "%s: (6) GetComputerNameA failed with error: 0x%08" PRIX32 "\n", __func__,
+ GetLastError());
+ return FALSE;
+ }
+ /* check if returned size is valid */
+ if (dwSize != dwNameLength)
+ {
+ fprintf(stderr,
+ "%s: (7) GetComputerNameA returned wrong size %" PRIu32 " (expected %" PRIu32 ")\n",
+ __func__, dwSize, dwNameLength);
+ return FALSE;
+ }
+ /* check if string is correctly terminated */
+ if (netbiosName1[dwSize] != 0)
+ {
+ fprintf(stderr, "%s: (8) string termination error\n", __func__);
+ return FALSE;
+ }
+
+ /* test with real buffer size */
+ dwSize = netbiosBufferSize;
+ if (GetComputerNameA(netbiosName2, &dwSize) == FALSE)
+ {
+ fprintf(stderr, "%s: (9) GetComputerNameA failed with error: 0x%08" PRIX32 "\n", __func__,
+ GetLastError());
+ return FALSE;
+ }
+ /* check if returned size is valid */
+ if (dwSize != dwNameLength)
+ {
+ fprintf(stderr,
+ "%s: (10) GetComputerNameA returned wrong size %" PRIu32 " (expected %" PRIu32
+ ")\n",
+ __func__, dwSize, dwNameLength);
+ return FALSE;
+ }
+ /* check if string is correctly terminated */
+ if (netbiosName2[dwSize] != 0)
+ {
+ fprintf(stderr, "%s: (11) string termination error\n", __func__);
+ return FALSE;
+ }
+
+ /* compare the results */
+ if (strcmp(netbiosName1, netbiosName2))
+ {
+ fprintf(stderr, "%s: (12) string compare mismatch\n", __func__);
+ return FALSE;
+ }
+
+ /* test with off by one buffer size */
+ dwSize = dwNameLength;
+ if (GetComputerNameA(netbiosName1, &dwSize) == TRUE)
+ {
+ fprintf(stderr,
+ "%s: (13) GetComputerNameA unexpectedly succeeded with limited buffer size\n",
+ __func__);
+ return FALSE;
+ }
+ /* check if returned size is valid */
+ if (dwSize != dwNameLength + 1)
+ {
+ fprintf(stderr,
+ "%s: (14) GetComputerNameA returned wrong size %" PRIu32 " (expected %" PRIu32
+ ")\n",
+ __func__, dwSize, dwNameLength + 1);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static BOOL Test_GetComputerNameEx_Format(COMPUTER_NAME_FORMAT format)
+{
+ /**
+ * BOOL WINAPI GetComputerNameEx(COMPUTER_NAME_FORMAT NameType, LPTSTR lpBuffer, LPDWORD
+ * lpnSize);
+ *
+ * Retrieves a NetBIOS or DNS name associated with the local computer.
+ *
+ * NameType [in]
+ * ComputerNameNetBIOS
+ * ComputerNameDnsHostname
+ * ComputerNameDnsDomain
+ * ComputerNameDnsFullyQualified
+ * ComputerNamePhysicalNetBIOS
+ * ComputerNamePhysicalDnsHostname
+ * ComputerNamePhysicalDnsDomain
+ * ComputerNamePhysicalDnsFullyQualified
+ *
+ * lpBuffer [out]
+ * A pointer to a buffer that receives the computer name or the cluster virtual server name.
+ * The length of the name may be greater than MAX_COMPUTERNAME_LENGTH characters because DNS
+ * allows longer names. To ensure that this buffer is large enough, set this parameter to NULL
+ * and use the required buffer size returned in the lpnSize parameter.
+ *
+ * lpnSize [in, out]
+ * On input, specifies the size of the buffer, in TCHARs.
+ * On output, receives the number of TCHARs copied to the destination buffer, not including the
+ * terminating null character. If the buffer is too small, the function fails and GetLastError
+ * returns ERROR_MORE_DATA. This parameter receives the size of the buffer required, including
+ * the terminating null character. If lpBuffer is NULL, this parameter must be zero.
+ *
+ */
+
+ CHAR computerName1[255 + 1];
+ CHAR computerName2[255 + 1];
+
+ const DWORD nameBufferSize = sizeof(computerName1) / sizeof(CHAR);
+ DWORD dwSize = 0;
+ DWORD dwMinSize = 0;
+ DWORD dwNameLength = 0;
+ DWORD dwError = 0;
+
+ memset(computerName1, 0xAA, nameBufferSize);
+ memset(computerName2, 0xBB, nameBufferSize);
+
+ if (format == ComputerNameDnsDomain || format == ComputerNamePhysicalDnsDomain)
+ {
+ /* domain names may be empty, terminating null only */
+ dwMinSize = 1;
+ }
+ else
+ {
+ /* computer names must be at least 1 character */
+ dwMinSize = 2;
+ }
+
+ /* test with null buffer and zero size (required if buffer is null) */
+ dwSize = 0;
+ if (GetComputerNameExA(format, NULL, &dwSize) == TRUE)
+ {
+ fprintf(stderr, "%s: (1/%d) GetComputerNameExA unexpectedly succeeded with null buffer\n",
+ __func__, format);
+ return FALSE;
+ }
+ if ((dwError = GetLastError()) != ERROR_MORE_DATA)
+ {
+ fprintf(stderr,
+ "%s: (2/%d) GetLastError returned 0x%08" PRIX32 " (expected ERROR_MORE_DATA)\n",
+ __func__, format, dwError);
+ return FALSE;
+ }
+
+ /* test with valid buffer and zero size */
+ dwSize = 0;
+ if (GetComputerNameExA(format, computerName1, &dwSize) == TRUE)
+ {
+ fprintf(stderr,
+ "%s: (3/%d) GetComputerNameExA unexpectedly succeeded with zero size parameter\n",
+ __func__, format);
+ return FALSE;
+ }
+ if ((dwError = GetLastError()) != ERROR_MORE_DATA)
+ {
+ fprintf(stderr,
+ "%s: (4/%d) GetLastError returned 0x%08" PRIX32 " (expected ERROR_MORE_DATA)\n",
+ __func__, format, dwError);
+ return FALSE;
+ }
+ /* check if returned size is valid: must be the size of the buffer required, including the
+ * terminating null character in this case */
+ if (dwSize < dwMinSize || dwSize > nameBufferSize)
+ {
+ fprintf(stderr,
+ "%s: (5/%d) GetComputerNameExA returned wrong size %" PRIu32
+ " (expected something in the range from %" PRIu32 " to %" PRIu32 ")\n",
+ __func__, format, dwSize, dwMinSize, nameBufferSize);
+ return FALSE;
+ }
+ dwNameLength = dwSize - 1;
+
+ /* test with returned size */
+ if (GetComputerNameExA(format, computerName1, &dwSize) == FALSE)
+ {
+ fprintf(stderr, "%s: (6/%d) GetComputerNameExA failed with error: 0x%08" PRIX32 "\n",
+ __func__, format, GetLastError());
+ return FALSE;
+ }
+ /* check if returned size is valid */
+ if (dwSize != dwNameLength)
+ {
+ fprintf(stderr,
+ "%s: (7/%d) GetComputerNameExA returned wrong size %" PRIu32 " (expected %" PRIu32
+ ")\n",
+ __func__, format, dwSize, dwNameLength);
+ return FALSE;
+ }
+ /* check if string is correctly terminated */
+ if (computerName1[dwSize] != 0)
+ {
+ fprintf(stderr, "%s: (8/%d) string termination error\n", __func__, format);
+ return FALSE;
+ }
+
+ /* test with real buffer size */
+ dwSize = nameBufferSize;
+ if (GetComputerNameExA(format, computerName2, &dwSize) == FALSE)
+ {
+ fprintf(stderr, "%s: (9/%d) GetComputerNameExA failed with error: 0x%08" PRIX32 "\n",
+ __func__, format, GetLastError());
+ return FALSE;
+ }
+ /* check if returned size is valid */
+ if (dwSize != dwNameLength)
+ {
+ fprintf(stderr,
+ "%s: (10/%d) GetComputerNameExA returned wrong size %" PRIu32 " (expected %" PRIu32
+ ")\n",
+ __func__, format, dwSize, dwNameLength);
+ return FALSE;
+ }
+ /* check if string is correctly terminated */
+ if (computerName2[dwSize] != 0)
+ {
+ fprintf(stderr, "%s: (11/%d) string termination error\n", __func__, format);
+ return FALSE;
+ }
+
+ /* compare the results */
+ if (strcmp(computerName1, computerName2))
+ {
+ fprintf(stderr, "%s: (12/%d) string compare mismatch\n", __func__, format);
+ return FALSE;
+ }
+
+ /* test with off by one buffer size */
+ dwSize = dwNameLength;
+ if (GetComputerNameExA(format, computerName1, &dwSize) == TRUE)
+ {
+ fprintf(stderr,
+ "%s: (13/%d) GetComputerNameExA unexpectedly succeeded with limited buffer size\n",
+ __func__, format);
+ return FALSE;
+ }
+ /* check if returned size is valid */
+ if (dwSize != dwNameLength + 1)
+ {
+ fprintf(stderr,
+ "%s: (14/%d) GetComputerNameExA returned wrong size %" PRIu32 " (expected %" PRIu32
+ ")\n",
+ __func__, format, dwSize, dwNameLength + 1);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+int TestGetComputerName(int argc, char* argv[])
+{
+
+ WINPR_UNUSED(argc);
+ WINPR_UNUSED(argv);
+
+ if (!Test_GetComputerName())
+ return -1;
+
+ if (!Test_GetComputerNameEx_Format(ComputerNameNetBIOS))
+ return -1;
+
+ if (!Test_GetComputerNameEx_Format(ComputerNameDnsHostname))
+ return -1;
+
+ if (!Test_GetComputerNameEx_Format(ComputerNameDnsDomain))
+ return -1;
+
+ if (!Test_GetComputerNameEx_Format(ComputerNameDnsFullyQualified))
+ return -1;
+
+ if (!Test_GetComputerNameEx_Format(ComputerNamePhysicalNetBIOS))
+ return -1;
+
+ if (!Test_GetComputerNameEx_Format(ComputerNamePhysicalDnsHostname))
+ return -1;
+
+ if (!Test_GetComputerNameEx_Format(ComputerNamePhysicalDnsDomain))
+ return -1;
+
+ if (!Test_GetComputerNameEx_Format(ComputerNamePhysicalDnsFullyQualified))
+ return -1;
+
+ return 0;
+}
diff --git a/winpr/libwinpr/sysinfo/test/TestGetNativeSystemInfo.c b/winpr/libwinpr/sysinfo/test/TestGetNativeSystemInfo.c
new file mode 100644
index 0000000..f227164
--- /dev/null
+++ b/winpr/libwinpr/sysinfo/test/TestGetNativeSystemInfo.c
@@ -0,0 +1,29 @@
+
+#include <winpr/crt.h>
+#include <winpr/sysinfo.h>
+
+int TestGetNativeSystemInfo(int argc, char* argv[])
+{
+ SYSTEM_INFO sysinfo;
+
+ WINPR_UNUSED(argc);
+ WINPR_UNUSED(argv);
+
+ GetNativeSystemInfo(&sysinfo);
+
+ printf("SystemInfo:\n");
+ printf("\twProcessorArchitecture: %" PRIu16 "\n", sysinfo.wProcessorArchitecture);
+ printf("\twReserved: %" PRIu16 "\n", sysinfo.wReserved);
+ printf("\tdwPageSize: 0x%08" PRIX32 "\n", sysinfo.dwPageSize);
+ printf("\tlpMinimumApplicationAddress: %p\n", sysinfo.lpMinimumApplicationAddress);
+ printf("\tlpMaximumApplicationAddress: %p\n", sysinfo.lpMaximumApplicationAddress);
+ printf("\tdwActiveProcessorMask: %p\n", (void*)sysinfo.dwActiveProcessorMask);
+ printf("\tdwNumberOfProcessors: %" PRIu32 "\n", sysinfo.dwNumberOfProcessors);
+ printf("\tdwProcessorType: %" PRIu32 "\n", sysinfo.dwProcessorType);
+ printf("\tdwAllocationGranularity: %" PRIu32 "\n", sysinfo.dwAllocationGranularity);
+ printf("\twProcessorLevel: %" PRIu16 "\n", sysinfo.wProcessorLevel);
+ printf("\twProcessorRevision: %" PRIu16 "\n", sysinfo.wProcessorRevision);
+ printf("\n");
+
+ return 0;
+}
diff --git a/winpr/libwinpr/sysinfo/test/TestLocalTime.c b/winpr/libwinpr/sysinfo/test/TestLocalTime.c
new file mode 100644
index 0000000..6ff5bf0
--- /dev/null
+++ b/winpr/libwinpr/sysinfo/test/TestLocalTime.c
@@ -0,0 +1,21 @@
+
+#include <winpr/crt.h>
+#include <winpr/sysinfo.h>
+
+int TestLocalTime(int argc, char* argv[])
+{
+ SYSTEMTIME lTime;
+
+ WINPR_UNUSED(argc);
+ WINPR_UNUSED(argv);
+
+ GetLocalTime(&lTime);
+
+ printf("GetLocalTime: wYear: %" PRIu16 " wMonth: %" PRIu16 " wDayOfWeek: %" PRIu16
+ " wDay: %" PRIu16 " wHour: %" PRIu16 " wMinute: %" PRIu16 " wSecond: %" PRIu16
+ " wMilliseconds: %" PRIu16 "\n",
+ lTime.wYear, lTime.wMonth, lTime.wDayOfWeek, lTime.wDay, lTime.wHour, lTime.wMinute,
+ lTime.wSecond, lTime.wMilliseconds);
+
+ return 0;
+}
diff --git a/winpr/libwinpr/sysinfo/test/TestSystemTime.c b/winpr/libwinpr/sysinfo/test/TestSystemTime.c
new file mode 100644
index 0000000..2a2b69e
--- /dev/null
+++ b/winpr/libwinpr/sysinfo/test/TestSystemTime.c
@@ -0,0 +1,21 @@
+
+#include <winpr/crt.h>
+#include <winpr/sysinfo.h>
+
+int TestSystemTime(int argc, char* argv[])
+{
+ SYSTEMTIME sTime;
+
+ WINPR_UNUSED(argc);
+ WINPR_UNUSED(argv);
+
+ GetSystemTime(&sTime);
+
+ printf("GetSystemTime: wYear: %" PRIu16 " wMonth: %" PRIu16 " wDayOfWeek: %" PRIu16
+ " wDay: %" PRIu16 " wHour: %" PRIu16 " wMinute: %" PRIu16 " wSecond: %" PRIu16
+ " wMilliseconds: %" PRIu16 "\n",
+ sTime.wYear, sTime.wMonth, sTime.wDayOfWeek, sTime.wDay, sTime.wHour, sTime.wMinute,
+ sTime.wSecond, sTime.wMilliseconds);
+
+ return 0;
+}