diff options
Diffstat (limited to 'winpr/libwinpr/sysinfo')
-rw-r--r-- | winpr/libwinpr/sysinfo/CMakeLists.txt | 31 | ||||
-rw-r--r-- | winpr/libwinpr/sysinfo/ModuleOptions.cmake | 9 | ||||
-rw-r--r-- | winpr/libwinpr/sysinfo/cpufeatures/CMakeLists.txt | 20 | ||||
-rw-r--r-- | winpr/libwinpr/sysinfo/cpufeatures/NOTICE | 13 | ||||
-rw-r--r-- | winpr/libwinpr/sysinfo/cpufeatures/README | 4 | ||||
-rw-r--r-- | winpr/libwinpr/sysinfo/cpufeatures/cpu-features.c | 1426 | ||||
-rw-r--r-- | winpr/libwinpr/sysinfo/cpufeatures/cpu-features.h | 324 | ||||
-rw-r--r-- | winpr/libwinpr/sysinfo/sysinfo.c | 1122 | ||||
-rw-r--r-- | winpr/libwinpr/sysinfo/test/CMakeLists.txt | 30 | ||||
-rw-r--r-- | winpr/libwinpr/sysinfo/test/TestCPUFeatures.c | 65 | ||||
-rw-r--r-- | winpr/libwinpr/sysinfo/test/TestGetComputerName.c | 366 | ||||
-rw-r--r-- | winpr/libwinpr/sysinfo/test/TestGetNativeSystemInfo.c | 29 | ||||
-rw-r--r-- | winpr/libwinpr/sysinfo/test/TestLocalTime.c | 21 | ||||
-rw-r--r-- | winpr/libwinpr/sysinfo/test/TestSystemTime.c | 21 |
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(¤t); + 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; +} |