From a02a22d4b55d233ada2e030a44065f464b79f137 Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Wed, 18 May 2016 10:29:03 +0100 Subject: [PATCH] base/dispatch.c: Add (unused) machinery for probing ELF auxilary vector. Some platforms don't allow CPU feature-probing from userland, and rely on the kernel informing processes in some other way. ELF-ish systems hide this information in the auxiliary vector, which is very hard to find. Add some machinery for doing this anyway. --- base/dispatch.c | 199 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ configure.ac | 6 ++ 2 files changed, 205 insertions(+) diff --git a/base/dispatch.c b/base/dispatch.c index 8936ea4a..10ae4044 100644 --- a/base/dispatch.c +++ b/base/dispatch.c @@ -199,6 +199,199 @@ static int xmm_registers_available_p(void) #endif +/*----- General feature probing using auxiliary vectors -------------------*/ + +/* Try to find the system's definitions for auxiliary vector entries. */ +#ifdef HAVE_SYS_AUXV_H +# include +#else +# ifdef HAVE_LINUX_AUXVEC_H +# include +# endif +# ifdef HAVE_ASM_HWCAP_H +# include +# endif +#endif + +/* The type of entries in the auxiliary vector. I'm assuming that `unsigned + * long' matches each platform's word length; if this is false then we'll + * need some host-specific tweaking here. + */ +union auxval { long i; unsigned long u; const void *p; }; +struct auxentry { unsigned long type; union auxval value; }; + +/* Register each CPU family's interest in the auxiliary vector. Make sure + * that the necessary entry types are defined. This is primarily ordered by + * entry type to minimize duplication. + */ + +/* If we couldn't find any interesting entries then we can switch all of this + * machinery off. Also do that if we have no means for atomic updates. + */ +#if WANT_ANY && CPU_DISPATCH_P + +/* The main output of this section is a bitmask of detected features. The + * least significant bit will be set if we've tried to probe. Always access + * this using `DISPATCH_LOAD' and `DISPATCH_STORE'. + */ +static unsigned hwcaps = 0; + +/* For each potentially interesting type which turned out not to exist or be + * wanted, define a dummy macro for the sake of the next step. + */ +#ifndef WANT_AT_HWCAP +# define WANT_AT_HWCAP(_) +#endif + +/* For each CPU family, define two lists. + * + * * `WANTAUX' is a list of the `WANT_AT_MUMBLE' macros which the CPU + * family tried to register interest in above. Each entry contains the + * interesting auxiliary vector entry type, the name of the union branch + * for its value, and the name of the slot in `struct auxprobe' in which + * to store the value. + * + * * `CAPMAP' is a list describing the output features which the CPU family + * intends to satisfy from the auxiliary vector. Each entry contains a + * feature name suffix, and the token name (for `check_env'). + */ + +/* Build the bitmask for `hwcaps' from the `CAPMAP' list. */ +enum { + HFI_PROBED = 0, +#define HFI__ENUM(feat, tok) HFI_##feat, + CAPMAP(HFI__ENUM) +#undef HFI__ENUM + HFI__END +}; +enum { + HF_PROBED = 1, +#define HF__FLAG(feat, tok) HF_##feat = 1 << HFI_##feat, + CAPMAP(HF__FLAG) +#undef HF__FLAG + HF__END +}; + +/* Build a structure in which we can capture the interesting data from the + * auxiliary vector. + */ +#define AUXUTYPE_i long +#define AUXUTYPE_u unsigned long +#define AUXUTYPE_p const void * +struct auxprobe { +#define AUXPROBE__SLOT(type, ubranch, slot) AUXUTYPE_##ubranch slot; + WANTAUX(AUXPROBE__SLOT) +#undef AUXPROBE_SLOT +}; + +/* --- @probe_hwcaps@ --- * + * + * Arguments: --- + * + * Returns: --- + * + * Use: Attempt to find the auxiliary vector (which is well hidden) + * and discover interesting features from it. + */ + +static void probe_hwcaps(void) +{ + unsigned hw = HF_PROBED; + struct auxprobe probed = { 0 }; + + /* Populate `probed' with the information we manage to retrieve from the + * auxiliary vector. Slots we couldn't find are left zero-valued. + */ +#if defined(HAVE_GETAUXVAL) + /* Shiny new libc lets us request individual entry types. This is almost + * too easy. + */ +# define CAP__GET(type, slot, ubranch) \ + probed.slot.ubranch = (AUXUTYPE_##ubranch)getauxval(type); + WANTAUX(CAP__GET) +#else + /* Otherwise we're a bit stuck, really. Modern Linux kernels make a copy + * of the vector available in `/procc' so we could try that. + * + * The usual place is stuck on the end of the environment vector, but that + * may well have moved, and we have no way of telling whether it has or + * whether there was ever an auxiliary vector there at all; so don't do + * that. + */ + { + FILE *fp = 0; + unsigned char *p = 0, *q = 0; + const struct auxentry *a; + size_t sz, off, n; + + /* Open the file and read it into a memory chunk. */ + if ((fp = fopen("/proc/self/auxv", "rb")) == 0) goto clean; + sz = 4096; off = 0; + if ((p = malloc(sz)) == 0) goto clean; + for (;;) { + n = fread(p + off, 1, sz - off, fp); + off += n; + if (off < sz) break; + sz *= 2; if ((q = realloc(p, sz)) == 0) break; + p = q; + } + + /* Work through the vector (or as much of it as we found) and extract the + * types we're interested in. + */ + for (a = (const struct auxentry *)p, + n = sz/sizeof(struct auxentry); + n--; a++) { + switch (a->type) { +#define CAP__SWITCH(type, ubranch, slot) \ + case type: probed.slot = a->value.ubranch; break; + WANTAUX(CAP__SWITCH) + } + } + + clean: + if (p) free(p); + if (fp) fclose(fp); + } +#endif + + /* Each CPU family now has to pick through what was found and stashed in + * `probed', and set the appropriate flag bits in `hw'. + */ + + /* Store the bitmask of features we probed for everyone to see. */ + DISPATCH_STORE(hwcaps, hw); + + /* Finally, make a report about the things we found. (Doing this earlier + * will pointlessly widen the window in which multiple threads will do the + * above auxiliary-vector probing.) + */ +#define CAP__DEBUG(feat, tok) \ + dispatch_debug("check auxv for feature `%s': %s", tok, \ + hw & HF_##feat ? "available" : "absent"); + CAPMAP(CAP__DEBUG) +#undef CAP__DEBUG +} + +/* --- @get_hwcaps@ --- * + * + * Arguments: --- + * + * Returns: A mask of hardware capabilities and other features, as probed + * from the auxiliary vector. + */ + +static unsigned get_hwcaps(void) +{ + unsigned hw; + + DISPATCH_LOAD(hwcaps, hw); + if (!(hwcaps & HF_PROBED)) { probe_hwcaps(); DISPATCH_LOAD(hwcaps, hw); } + return (hw); +} + +#endif + /*----- External interface ------------------------------------------------*/ /* --- @dispatch_debug@ --- * @@ -304,6 +497,12 @@ int cpu_feature_p(int feat) xmm_registers_available_p() && cpuid_features_p(CPUID1D_SSE2, CPUID1C_AESNI)); #endif +#ifdef CAPMAP +# define FEATP__CASE(feat, tok) \ + CASE_CPUFEAT(feat, tok, get_hwcaps & HF_##feat) + CAPMAP(FEATP__CASE) +#undef FEATP__CASE +#endif default: dispatch_debug("denying unknown feature %d", feat); return (0); diff --git a/configure.ac b/configure.ac index 8a58d782..dcf70281 100644 --- a/configure.ac +++ b/configure.ac @@ -128,6 +128,12 @@ case $CPUFAM in *) AC_MSG_RESULT([$CPUFAM/$ABI]) ;; esac +dnl Some equipment wanted for checking CPU features at runtime. +AC_CHECK_HEADERS([asm/hwcap.h]) +AC_CHECK_HEADERS([sys/auxv.h]) +AC_CHECK_HEADERS([linux/auxvec.h]) +AC_CHECK_FUNCS([getauxval]) + dnl-------------------------------------------------------------------------- dnl C programming environment. -- 2.11.0