diff --git a/Makefile b/Makefile index 957a57f..35b2367 100644 --- a/Makefile +++ b/Makefile @@ -60,6 +60,12 @@ override CPPFLAGS += -DFREESTANDING LIB_CFLAGS += -ffreestanding -nostdlib endif +# Don't use this option except for testing; it isn't a stable interface. +TEST_SUPPORT__DO_NOT_USE := +ifdef TEST_SUPPORT__DO_NOT_USE +override CPPFLAGS += -DTEST_SUPPORT__DO_NOT_USE +endif + ############################################################################## PREFIX ?= /usr/local diff --git a/lib/arm/cpu_features.c b/lib/arm/cpu_features.c index 8d1facc..c4556a3 100644 --- a/lib/arm/cpu_features.c +++ b/lib/arm/cpu_features.c @@ -35,6 +35,7 @@ * added to API level 18 for ARM and level 21 for AArch64. */ +#include "../cpu_features_common.h" /* must be included first */ #include "cpu_features.h" #if ARM_CPU_FEATURES_ENABLED @@ -91,6 +92,11 @@ out: close(fd); } +static const struct cpu_feature arm_cpu_feature_table[] = { + {ARM_CPU_FEATURE_NEON, "neon"}, + {ARM_CPU_FEATURE_PMULL, "pmull"}, +}; + void setup_cpu_features(void) { u32 features = 0; @@ -113,6 +119,9 @@ void setup_cpu_features(void) features |= ARM_CPU_FEATURE_PMULL; #endif + disable_cpu_features_for_testing(&features, arm_cpu_feature_table, + ARRAY_LEN(arm_cpu_feature_table)); + _cpu_features = features | ARM_CPU_FEATURES_KNOWN; } diff --git a/lib/cpu_features_common.h b/lib/cpu_features_common.h new file mode 100644 index 0000000..0c2cc6e --- /dev/null +++ b/lib/cpu_features_common.h @@ -0,0 +1,83 @@ +/* + * cpu_features_common.h - code shared by all lib/$arch/cpu_features.c + * + * Copyright 2020 Eric Biggers + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#if defined(TEST_SUPPORT__DO_NOT_USE) && !defined(FREESTANDING) +# define _GNU_SOURCE 1 /* for strdup() and strtok_r() */ +# include +# include +# include +#endif + +#include "lib_common.h" + +struct cpu_feature { + u32 bit; + const char *name; +}; + +#if defined(TEST_SUPPORT__DO_NOT_USE) && !defined(FREESTANDING) +/* Disable any features that are listed in $LIBDEFLATE_DISABLE_CPU_FEATURES. */ +static inline void +disable_cpu_features_for_testing(u32 *features, + const struct cpu_feature *feature_table, + size_t feature_table_length) +{ + char *env_value, *strbuf, *p, *saveptr = NULL; + size_t i; + + env_value = getenv("LIBDEFLATE_DISABLE_CPU_FEATURES"); + if (!env_value) + return; + strbuf = strdup(env_value); + if (!strbuf) + abort(); + p = strtok_r(strbuf, ",", &saveptr); + while (p) { + for (i = 0; i < feature_table_length; i++) { + if (strcmp(p, feature_table[i].name) == 0) { + *features &= ~feature_table[i].bit; + break; + } + } + if (i == feature_table_length) { + fprintf(stderr, + "unrecognized feature in LIBDEFLATE_DISABLE_CPU_FEATURES: \"%s\"\n", + p); + abort(); + } + p = strtok_r(NULL, ",", &saveptr); + } + free(strbuf); +} +#else /* TEST_SUPPORT__DO_NOT_USE */ +static inline void +disable_cpu_features_for_testing(u32 *features, + const struct cpu_feature *feature_table, + size_t feature_table_length) +{ +} +#endif /* !TEST_SUPPORT__DO_NOT_USE */ diff --git a/lib/x86/cpu_features.c b/lib/x86/cpu_features.c index e931e62..e3471d4 100644 --- a/lib/x86/cpu_features.c +++ b/lib/x86/cpu_features.c @@ -25,6 +25,7 @@ * OTHER DEALINGS IN THE SOFTWARE. */ +#include "../cpu_features_common.h" /* must be included first */ #include "cpu_features.h" #if X86_CPU_FEATURES_ENABLED @@ -75,6 +76,15 @@ read_xcr(u32 index) #define IS_SET(reg, nr) ((reg) & BIT(nr)) #define IS_ALL_SET(reg, mask) (((reg) & (mask)) == (mask)) +static const struct cpu_feature x86_cpu_feature_table[] = { + {X86_CPU_FEATURE_SSE2, "sse2"}, + {X86_CPU_FEATURE_PCLMUL, "pclmul"}, + {X86_CPU_FEATURE_AVX, "avx"}, + {X86_CPU_FEATURE_AVX2, "avx2"}, + {X86_CPU_FEATURE_BMI2, "bmi2"}, + {X86_CPU_FEATURE_AVX512BW, "avx512bw"}, +}; + /* Initialize _cpu_features with bits for interesting processor features. */ void setup_cpu_features(void) { @@ -133,6 +143,9 @@ void setup_cpu_features(void) features |= X86_CPU_FEATURE_AVX512BW; out: + disable_cpu_features_for_testing(&features, x86_cpu_feature_table, + ARRAY_LEN(x86_cpu_feature_table)); + _cpu_features = features | X86_CPU_FEATURES_KNOWN; }