diff --git a/Cargo.toml b/Cargo.toml index 089bb1e597..881aff0ffd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -155,8 +155,8 @@ include = [ "crypto/cipher/e_aes.c", "crypto/cipher/internal.h", "crypto/constant_time_test.c", + "crypto/cpu.c", "crypto/cpu-aarch64-linux.c", - "crypto/cpu-arm-linux.c", "crypto/cpu-arm.c", "crypto/cpu-intel.c", "crypto/crypto.c", @@ -261,6 +261,7 @@ name = "ring" [dependencies] untrusted = "0.3.2" +byteorder = "0.5.3" [target.'cfg(unix)'.dependencies] lazy_static = "0.2.1" diff --git a/crypto/cpu-arm-linux.c b/crypto/cpu-arm-linux.c deleted file mode 100644 index 8851caf300..0000000000 --- a/crypto/cpu-arm-linux.c +++ /dev/null @@ -1,358 +0,0 @@ -/* Copyright (c) 2016, Google Inc. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ - -#include - -#if defined(OPENSSL_ARM) && !defined(OPENSSL_STATIC_ARMCAP) - -#include -#include -#include -#include -#include - -#include -#include - -#include "internal.h" - - -#define AT_HWCAP 16 -#define AT_HWCAP2 26 - -#define HWCAP_NEON (1 << 12) - -/* See /usr/include/asm/hwcap.h on an ARM installation for the source of - * these values. */ -#define HWCAP2_AES (1 << 0) -#define HWCAP2_PMULL (1 << 1) -#define HWCAP2_SHA1 (1 << 2) -#define HWCAP2_SHA2 (1 << 3) - -/* |getauxval| is not available on Android until API level 20. Link it as a weak - * symbol and use other methods as fallback. */ -unsigned long getauxval(unsigned long type) __attribute__((weak)); - -static int open_eintr(const char *path, int flags) { - int ret; - do { - ret = open(path, flags); - } while (ret < 0 && errno == EINTR); - return ret; -} - -static ssize_t read_eintr(int fd, void *out, size_t len) { - ssize_t ret; - do { - ret = read(fd, out, len); - } while (ret < 0 && errno == EINTR); - return ret; -} - -/* read_full reads exactly |len| bytes from |fd| to |out|. On error or end of - * file, it returns zero. */ -static int read_full(int fd, void *out, size_t len) { - while (len > 0) { - ssize_t ret = read_eintr(fd, out, len); - if (ret <= 0) { - return 0; - } - out = (uint8_t *)out + ret; - len -= ret; - } - return 1; -} - -/* read_file opens |path| and reads until end-of-file. On success, it returns - * one and sets |*out_ptr| and |*out_len| to a newly-allocated buffer with the - * contents. Otherwise, it returns zero. */ -static int read_file(char **out_ptr, size_t *out_len, const char *path) { - int fd = open_eintr(path, O_RDONLY); - if (fd < 0) { - return 0; - } - - static const size_t kReadSize = 1024; - int ret = 0; - size_t cap = kReadSize, len = 0; - char *buf = OPENSSL_malloc(cap); - if (buf == NULL) { - goto err; - } - - for (;;) { - if (cap - len < kReadSize) { - size_t new_cap = cap * 2; - if (new_cap < cap) { - goto err; - } - char *new_buf = OPENSSL_realloc(buf, new_cap); - if (new_buf == NULL) { - goto err; - } - buf = new_buf; - cap = new_cap; - } - - ssize_t bytes_read = read_eintr(fd, buf + len, kReadSize); - if (bytes_read < 0) { - goto err; - } - if (bytes_read == 0) { - break; - } - len += bytes_read; - } - - *out_ptr = buf; - *out_len = len; - ret = 1; - buf = NULL; - -err: - OPENSSL_free(buf); - close(fd); - return ret; -} - -/* getauxval_proc behaves like |getauxval| but reads from /proc/self/auxv. */ -static unsigned long getauxval_proc(unsigned long type) { - int fd = open_eintr("/proc/self/auxv", O_RDONLY); - if (fd < 0) { - return 0; - } - - struct { - unsigned long tag; - unsigned long value; - } entry; - - for (;;) { - if (!read_full(fd, &entry, sizeof(entry)) || - (entry.tag == 0 && entry.value == 0)) { - break; - } - if (entry.tag == type) { - close(fd); - return entry.value; - } - } - close(fd); - return 0; -} - -typedef struct { - const char *data; - size_t len; -} STRING_PIECE; - -static int STRING_PIECE_equals(const STRING_PIECE *a, const char *b) { - size_t b_len = strlen(b); - return a->len == b_len && memcmp(a->data, b, b_len) == 0; -} - -/* STRING_PIECE_split finds the first occurence of |sep| in |in| and, if found, - * sets |*out_left| and |*out_right| to |in| split before and after it. It - * returns one if |sep| was found and zero otherwise. */ -static int STRING_PIECE_split(STRING_PIECE *out_left, STRING_PIECE *out_right, - const STRING_PIECE *in, char sep) { - const char *p = memchr(in->data, sep, in->len); - if (p == NULL) { - return 0; - } - /* |out_left| or |out_right| may alias |in|, so make a copy. */ - STRING_PIECE in_copy = *in; - out_left->data = in_copy.data; - out_left->len = p - in_copy.data; - out_right->data = in_copy.data + out_left->len + 1; - out_right->len = in_copy.len - out_left->len - 1; - return 1; -} - -/* STRING_PIECE_trim removes leading and trailing whitespace from |s|. */ -static void STRING_PIECE_trim(STRING_PIECE *s) { - while (s->len != 0 && (s->data[0] == ' ' || s->data[0] == '\t')) { - s->data++; - s->len--; - } - while (s->len != 0 && - (s->data[s->len - 1] == ' ' || s->data[s->len - 1] == '\t')) { - s->len--; - } -} - -/* extract_cpuinfo_field extracts a /proc/cpuinfo field named |field| from - * |in|. If found, it sets |*out| to the value and returns one. Otherwise, it - * returns zero. */ -static int extract_cpuinfo_field(STRING_PIECE *out, const STRING_PIECE *in, - const char *field) { - /* Process |in| one line at a time. */ - STRING_PIECE remaining = *in, line; - while (STRING_PIECE_split(&line, &remaining, &remaining, '\n')) { - STRING_PIECE key, value; - if (!STRING_PIECE_split(&key, &value, &line, ':')) { - continue; - } - STRING_PIECE_trim(&key); - if (STRING_PIECE_equals(&key, field)) { - STRING_PIECE_trim(&value); - *out = value; - return 1; - } - } - - return 0; -} - -static int cpuinfo_field_equals(const STRING_PIECE *cpuinfo, const char *field, - const char *value) { - STRING_PIECE extracted; - return extract_cpuinfo_field(&extracted, cpuinfo, field) && - STRING_PIECE_equals(&extracted, value); -} - -/* has_list_item treats |list| as a space-separated list of items and returns - * one if |item| is contained in |list| and zero otherwise. */ -static int has_list_item(const STRING_PIECE *list, const char *item) { - STRING_PIECE remaining = *list, feature; - while (STRING_PIECE_split(&feature, &remaining, &remaining, ' ')) { - if (STRING_PIECE_equals(&feature, item)) { - return 1; - } - } - return 0; -} - -static unsigned long get_hwcap_cpuinfo(const STRING_PIECE *cpuinfo) { - if (cpuinfo_field_equals(cpuinfo, "CPU architecture", "8")) { - /* This is a 32-bit ARM binary running on a 64-bit kernel. NEON is always - * available on ARMv8. Linux omits required features, so reading the - * "Features" line does not work. (For simplicity, use strict equality. We - * assume everything running on future ARM architectures will have a - * working |getauxval|.) */ - return HWCAP_NEON; - } - - STRING_PIECE features; - if (extract_cpuinfo_field(&features, cpuinfo, "Features") && - has_list_item(&features, "neon")) { - return HWCAP_NEON; - } - return 0; -} - -static unsigned long get_hwcap2_cpuinfo(const STRING_PIECE *cpuinfo) { - STRING_PIECE features; - if (!extract_cpuinfo_field(&features, cpuinfo, "Features")) { - return 0; - } - - unsigned long ret = 0; - if (has_list_item(&features, "aes")) { - ret |= HWCAP2_AES; - } - if (has_list_item(&features, "pmull")) { - ret |= HWCAP2_PMULL; - } - if (has_list_item(&features, "sha1")) { - ret |= HWCAP2_SHA1; - } - if (has_list_item(&features, "sha2")) { - ret |= HWCAP2_SHA2; - } - return ret; -} - -/* has_broken_neon returns one if |in| matches a CPU known to have a broken - * NEON unit. See https://crbug.com/341598. */ -static int has_broken_neon(const STRING_PIECE *cpuinfo) { - return cpuinfo_field_equals(cpuinfo, "CPU implementer", "0x51") && - cpuinfo_field_equals(cpuinfo, "CPU architecture", "7") && - cpuinfo_field_equals(cpuinfo, "CPU variant", "0x1") && - cpuinfo_field_equals(cpuinfo, "CPU part", "0x04d") && - cpuinfo_field_equals(cpuinfo, "CPU revision", "0"); -} - -extern uint32_t GFp_armcap_P; - -static int g_has_broken_neon; - -void GFp_cpuid_setup(void) { - char *cpuinfo_data; - size_t cpuinfo_len; - if (!read_file(&cpuinfo_data, &cpuinfo_len, "/proc/cpuinfo")) { - return; - } - STRING_PIECE cpuinfo; - cpuinfo.data = cpuinfo_data; - cpuinfo.len = cpuinfo_len; - - /* |getauxval| is not available on Android until API level 20. If it is - * unavailable, read from /proc/self/auxv as a fallback. This is unreadable - * on some versions of Android, so further fall back to /proc/cpuinfo. - * - * See - * https://android.googlesource.com/platform/ndk/+/882ac8f3392858991a0e1af33b4b7387ec856bd2 - * and b/13679666 (Google-internal) for details. */ - unsigned long hwcap = 0; - if (getauxval != NULL) { - hwcap = getauxval(AT_HWCAP); - } - if (hwcap == 0) { - hwcap = getauxval_proc(AT_HWCAP); - } - if (hwcap == 0) { - hwcap = get_hwcap_cpuinfo(&cpuinfo); - } - - /* Clear NEON support if known broken. */ - g_has_broken_neon = has_broken_neon(&cpuinfo); - if (g_has_broken_neon) { - hwcap &= ~HWCAP_NEON; - } - - /* Matching OpenSSL, only report other features if NEON is present. */ - if (hwcap & HWCAP_NEON) { - GFp_armcap_P |= ARMV7_NEON; - - /* Some ARMv8 Android devices don't expose AT_HWCAP2. Fall back to - * /proc/cpuinfo. See https://crbug.com/596156. */ - unsigned long hwcap2 = 0; - if (getauxval != NULL) { - hwcap2 = getauxval(AT_HWCAP2); - } - if (hwcap2 == 0) { - hwcap2 = get_hwcap2_cpuinfo(&cpuinfo); - } - - if (hwcap2 & HWCAP2_AES) { - GFp_armcap_P |= ARMV8_AES; - } - if (hwcap2 & HWCAP2_PMULL) { - GFp_armcap_P |= ARMV8_PMULL; - } - if (hwcap2 & HWCAP2_SHA1) { - GFp_armcap_P |= ARMV8_SHA1; - } - if (hwcap2 & HWCAP2_SHA2) { - GFp_armcap_P |= ARMV8_SHA256; - } - } - - OPENSSL_free(cpuinfo_data); -} - -int GFp_has_broken_NEON(void) { return g_has_broken_neon; } - -#endif /* OPENSSL_ARM && !OPENSSL_STATIC_ARMCAP */ diff --git a/crypto/cpu.c b/crypto/cpu.c new file mode 100644 index 0000000000..697f581043 --- /dev/null +++ b/crypto/cpu.c @@ -0,0 +1,19 @@ +#include + +#ifdef __linux__ +unsigned long getauxval_wrapper(unsigned long type, char *success) { + if (getauxval == NULL) { + *success = 0; + return 0; + } + + unsigned long result = getauxval(type); + if (errno != 0) { + *success = 0; + return 0; + } + + *success = 1; + return result; +} +#endif diff --git a/include/openssl/cpu.h b/include/openssl/cpu.h index 04926e31c2..3697f6b14d 100644 --- a/include/openssl/cpu.h +++ b/include/openssl/cpu.h @@ -70,6 +70,16 @@ extern "C" { /* Runtime CPU feature support */ +#ifdef __linux__ + +/* |getauxval| is not available on Android until API level 20. Link it as a weak + * symbol and use other methods as fallback. */ +unsigned long getauxval(unsigned long type) __attribute__((weak)); + +#include +unsigned long getauxval_wrapper(unsigned long type, char *success); + +#endif #if defined(OPENSSL_X86) || defined(OPENSSL_X86_64) /* GFp_ia32cap_P contains the Intel CPUID bits when running on an x86 or diff --git a/mk/ring.mk b/mk/ring.mk index c00e65cc5e..a2cbb95dd0 100644 --- a/mk/ring.mk +++ b/mk/ring.mk @@ -32,6 +32,7 @@ RING_SRCS = $(addprefix $(RING_PREFIX), \ crypto/bn/random.c \ crypto/bn/shift.c \ crypto/cipher/e_aes.c \ + crypto/cpu.c \ crypto/crypto.c \ crypto/curve25519/curve25519.c \ crypto/ec/ecp_nistz.c \ @@ -91,7 +92,6 @@ RING_x86_64_SRCS = $(addprefix $(RING_PREFIX), \ RING_ARM_SHARED_SRCS = $(addprefix $(RING_PREFIX), \ crypto/cpu-arm.c \ - crypto/cpu-arm-linux.c \ \ crypto/aes/asm/aesv8-armx.pl \ crypto/modes/asm/ghashv8-armx.pl \ diff --git a/src/c.rs b/src/c.rs index e4a8ee64cb..5199c169e5 100644 --- a/src/c.rs +++ b/src/c.rs @@ -45,7 +45,7 @@ macro_rules! define_metrics_tests { // We can't use `size_t` because we need to test that our // definition of `size_t` is correct using this code! We use `u16` // because even 8-bit and 16-bit microcontrollers have no trouble - // with it, and because `u16` is always as smaller or smaller than + // with it, and because `u16` is always as small as or smaller than // `usize`. static $c_align: u16; static $c_size: u16; @@ -91,7 +91,6 @@ define_type!(long, i32, test_long_metrics, GFp_long_align, GFp_long_size, define_type!(long, i64, test_long_metrics, GFp_long_align, GFp_long_size, "The C `long` type. Equivalent to `libc::c_long`."); - define_type!( size_t, usize, test_size_t_metrics, GFp_size_t_align, GFp_size_t_size, "The C `size_t` type from ``. diff --git a/src/cpu_feature/arm_linux/arm_linux.rs b/src/cpu_feature/arm_linux/arm_linux.rs new file mode 100644 index 0000000000..553cf63660 --- /dev/null +++ b/src/cpu_feature/arm_linux/arm_linux.rs @@ -0,0 +1,514 @@ +use std::collections::HashSet; +use std::string::{String, ToString}; +use self::auxv::AuxvUnsignedLong; +#[cfg(all(any(target_arch = "arm", target_arch = "aarch64"), target_os="linux"))] +use self::auxv::NativeGetauxvalProvider; +#[cfg(any(test, all(any(target_arch = "arm", target_arch = "aarch64"), target_os="linux")))] +use self::auxv::{AuxVals, AuxvTypes, AuxvUnsignedLongNative, GetauxvalProvider}; +use self::cpuinfo::CpuInfo; +#[cfg(all(any(target_arch = "arm", target_arch = "aarch64"), target_os="linux"))] +use self::cpuinfo::parse_cpuinfo; + +// Bits exposed in HWCAP and HWCAP2 auxv values +#[derive(Clone, Copy)] +#[allow(non_snake_case)] +struct ArmHwcapFeatures { + ARM_HWCAP2_AES: T, + ARM_HWCAP2_PMULL: T, + ARM_HWCAP2_SHA1: T, + ARM_HWCAP2_SHA2: T, + ARM_HWCAP_NEON: T, +} + +impl ArmHwcapFeatures { + fn new() -> ArmHwcapFeatures { + ArmHwcapFeatures { + // See /usr/include/asm/hwcap.h on an ARM installation for the + // source of these values. + ARM_HWCAP2_AES: T::from(1 << 0), + ARM_HWCAP2_PMULL: T::from(1 << 1), + ARM_HWCAP2_SHA1: T::from(1 << 2), + ARM_HWCAP2_SHA2: T::from(1 << 3), + + ARM_HWCAP_NEON: T::from(1 << 12), + } + } +} + +// Constants used in GFp_armcap_P +// from include/openssl/arm_arch.h +#[cfg(any(test, all(any(target_arch = "arm", target_arch = "aarch64"), + target_os="linux")))] +const ARMV7_NEON: u32 = 1 << 0; +// not a typo; there is no constant for 1 << 1 +const ARMV8_AES: u32 = 1 << 2; +const ARMV8_SHA1: u32 = 1 << 3; +const ARMV8_SHA256: u32 = 1 << 4; +const ARMV8_PMULL: u32 = 1 << 5; + +extern "C" { + #[cfg(all(any(target_arch = "arm", target_arch = "aarch64"), + target_os="linux"))] + #[allow(non_upper_case_globals)] + pub static mut GFp_armcap_P: u32; +} + +#[cfg(all(any(target_arch = "arm", target_arch = "aarch64"), + target_os="linux"))] +#[allow(non_snake_case)] +extern "C" fn GFp_cpuid_setup() { + let auxv_types: AuxvTypes = AuxvTypes::new(); + let hwcap_features: ArmHwcapFeatures + = ArmHwcapFeatures::new(); + if let Ok(c) = parse_cpuinfo() { + // if we can't load procfs auxv, just let it be empty + let procfs_auxv = match + auxv::search_procfs_auxv:: + (&Path::from("/proc/self/auxv"), + &[auxv_types.AT_HWCAP, auxv_types.AT_HWCAP2]) { + Ok(auxv) => auxv, + Err(_) => AuxVals::::new() + }; + + let getauxval = NativeGetauxvalProvider{}; + let armcap = + armcap_bits::(&c, &procfs_auxv, auxv_types, + hwcap_features, getauxval); + unsafe { + GFp_armcap_P |= armcap; + } + } +} + +/// returns a u32 with bits set for use in GFp_armcap_P +#[cfg(any(test, all(any(target_arch = "arm", target_arch = "aarch64"), + target_os="linux")))] +fn armcap_bits (cpuinfo: &CpuInfo, + procfs_auxv: &AuxVals, + auxval_types: AuxvTypes, + hwcap_features: ArmHwcapFeatures, + getauxval_provider: G) + -> u32 { + let mut hwcap = AuxvUnsignedLongNative::from(0_u32); + + // |getauxval| is not available on Android until API level 20. If it is + // unavailable, read from /proc/self/auxv as a fallback. This is unreadable + // on some versions of Android, so further fall back to /proc/cpuinfo. + // See + // https://android.googlesource.com/platform/ndk/+/882ac8f3392858991a0e1af33b4b7387ec856bd2 + // and b/13679666 (Google-internal) for details. */ + + if let Some(v) = getauxval_provider.getauxval(auxval_types.AT_HWCAP) { + hwcap = v; + } else if let Some(v) = procfs_auxv.get(&auxval_types.AT_HWCAP) { + hwcap = *v; + } else if let Some(v) = hwcap_from_cpuinfo(&cpuinfo, hwcap_features) { + hwcap = v; + } + + // Clear NEON support if known broken + if cpu_has_broken_neon(&cpuinfo) { + hwcap &= !hwcap_features.ARM_HWCAP_NEON; + } + + // Matching OpenSSL, only report other features if NEON is present + let mut armcap: u32 = 0; + if hwcap & hwcap_features.ARM_HWCAP_NEON > AuxvUnsignedLongNative::from(0_u32) { + armcap |= ARMV7_NEON; + + // Some ARMv8 Android devices don't expose AT_HWCAP2. Fall back to + // /proc/cpuinfo. See https://crbug.com/596156 + + let mut hwcap2 = AuxvUnsignedLongNative::from(0_u32); + if let Some(v) = getauxval_provider.getauxval(auxval_types.AT_HWCAP2) { + hwcap2 = v; + } else if let Some(v) = procfs_auxv.get(&auxval_types.AT_HWCAP2) { + hwcap2 = *v; + } else if let Some(v) = hwcap2_from_cpuinfo(&cpuinfo, hwcap_features) { + hwcap2 = v; + } + + armcap |= armcap_for_hwcap2(hwcap2, hwcap_features); + } + + return armcap; +} + +fn armcap_for_hwcap2(hwcap2: T, + hwcap_features: ArmHwcapFeatures) + -> u32 { + let mut ret: u32 = 0; + if hwcap2 & hwcap_features.ARM_HWCAP2_AES > T::from(0) { + ret |= ARMV8_AES; + } + if hwcap2 & hwcap_features.ARM_HWCAP2_PMULL > T::from(0) { + ret |= ARMV8_PMULL; + } + if hwcap2 & hwcap_features.ARM_HWCAP2_SHA1 > T::from(0) { + ret |= ARMV8_SHA1; + } + if hwcap2 & hwcap_features.ARM_HWCAP2_SHA2 > T::from(0) { + ret |= ARMV8_SHA256; + } + + return ret; +} + +fn hwcap_from_cpuinfo(cpuinfo: &CpuInfo, + hwcap_features: ArmHwcapFeatures) + -> Option { + if let Some(v) = cpuinfo.get("CPU architecture") { + if v == "8" { + // This is a 32-bit ARM binary running on a 64-bit kernel. NEON is + // always available on ARMv8. Linux omits required features, so + // reading the "Features" line does not work. (For simplicity, + // use strict equality. We assume everything running on future + // ARM architectures will have a working |getauxval|.) + return Some(hwcap_features.ARM_HWCAP_NEON); + } + } + + if let Some(v) = cpuinfo.get("Features") { + if parse_arm_cpuinfo_features(v).contains("neon") { + return Some(hwcap_features.ARM_HWCAP_NEON); + } + } + + return None; +} + +fn hwcap2_from_cpuinfo(cpuinfo: &CpuInfo, + hwcap_features: ArmHwcapFeatures) + -> Option { + if let Some(v) = cpuinfo.get("Features") { + let mut ret: T = T::from(0); + let features = parse_arm_cpuinfo_features(v); + + if features.contains("aes") { + ret |= hwcap_features.ARM_HWCAP2_AES; + } + if features.contains("pmull") { + ret |= hwcap_features.ARM_HWCAP2_PMULL; + } + if features.contains("sha1") { + ret |= hwcap_features.ARM_HWCAP2_SHA1; + } + if features.contains("sha2") { + ret |= hwcap_features.ARM_HWCAP2_SHA2; + } + + return Some(ret); + } else { + return None; + } +} + +fn cpu_has_broken_neon(cpuinfo: &CpuInfo) -> bool { + return cpuinfo.get("CPU implementer").map_or(false, |s| s == "0x51") && + cpuinfo.get("CPU architecture").map_or( false, |s| s == "7") && + cpuinfo.get("CPU variant").map_or(false, |s| s == "0x1") && + cpuinfo.get("CPU part").map_or(false, |s| s == "0x04d") && + cpuinfo.get("CPU revision").map_or(false, |s| s == "0") +} + +fn parse_arm_cpuinfo_features(features_val: &str) -> HashSet { + return features_val.trim_right_matches(' ') + .split(' ') + .map(|s| s.to_string()) + .collect(); +} + +mod auxv; +mod cpuinfo; + +#[cfg(test)] +mod tests { + extern crate byteorder; + + use std::collections::HashMap; + use std::fs::File; + use std::io::Read; + use std::path::Path; + use std::string::{String, ToString}; + use std::vec::Vec; + + use super::{armcap_bits, ArmHwcapFeatures, ARMV7_NEON, ARMV8_AES, + ARMV8_PMULL, ARMV8_SHA1, ARMV8_SHA256}; + use super::cpuinfo::{parse_cpuinfo_reader, CpuInfo, CpuInfoError}; + use super::auxv::{AuxvTypes, AuxVals, AuxvUnsignedLongNative, + GetauxvalProvider}; + + struct StubGetauxvalProvider { + auxv: AuxVals + } + + impl GetauxvalProvider for StubGetauxvalProvider { + fn getauxval(&self, auxv_type: AuxvUnsignedLongNative) + -> Option { + self.auxv.get(&auxv_type).map(|v| *v) + } + } + + #[test] + fn armcap_bits_broken_neon_without_auxv_yields_zero_armcap() { + do_armcap_bits_test("src/cpu_feature/arm_linux/test-data/linux-arm-broken.cpuinfo", + not_found_getauxv(), + &empty_procfs_auxv(), + 0); + } + + #[test] + fn armcap_bits_broken_neon_with_neon_getauxv_yields_zero_armcap() { + do_armcap_bits_test("src/cpu_feature/arm_linux/test-data/linux-arm-broken.cpuinfo", + hwcap_neon_getauxv(), + &empty_procfs_auxv(), + 0); + } + + #[test] + fn armcap_bits_broken_neon_with_neon_procfs_yields_zero_armcap() { + do_armcap_bits_test("src/cpu_feature/arm_linux/test-data/linux-arm-broken.cpuinfo", + not_found_getauxv(), + &hwcap_neon_procfs_auxv(), + 0); + } + + #[test] + fn armcap_bits_ok_neon_with_neon_getauxv_yields_neon_armcap() { + do_armcap_bits_test("src/cpu_feature/arm_linux/test-data/linux-arm-C1904.cpuinfo", + hwcap_neon_getauxv(), + &empty_procfs_auxv(), + ARMV7_NEON); + } + + #[test] + fn armcap_bits_ok_neon_with_neon_procfs_auxv_yields_neon_armcap() { + do_armcap_bits_test("src/cpu_feature/arm_linux/test-data/linux-arm-C1904.cpuinfo", + not_found_getauxv(), + &hwcap_neon_procfs_auxv(), + ARMV7_NEON); + } + + #[test] + fn armcap_bits_ok_neon_without_auxv_yields_neon_only_armcap() { + do_armcap_bits_test("src/cpu_feature/arm_linux/test-data/linux-arm-C1904.cpuinfo", + not_found_getauxv(), + &empty_procfs_auxv(), + ARMV7_NEON); + } + + #[test] + fn armcap_bits_arm_8_with_cpuinfo_features_without_auxv_yields_fully_populated_armcap() { + do_armcap_bits_test("src/cpu_feature/arm_linux/test-data/linux-arm-cavium-thunderx.cpuinfo", + not_found_getauxv(), + &empty_procfs_auxv(), + ARMV7_NEON | ARMV8_PMULL | ARMV8_AES | ARMV8_SHA1 + | ARMV8_SHA256); + } + + #[test] + fn armcap_bits_arm_8_with_cpuinfo_features_with_neon_only_getauxv_hwcap_yields_fully_populated_armcap() { + do_armcap_bits_test("src/cpu_feature/arm_linux/test-data/linux-arm-cavium-thunderx.cpuinfo", + hwcap_neon_getauxv(), + &empty_procfs_auxv(), + ARMV7_NEON | ARMV8_PMULL | ARMV8_AES | ARMV8_SHA1 + | ARMV8_SHA256); + } + + #[test] + fn armcap_bits_arm_8_with_cpuinfo_features_with_neon_only_procfs_auxv_hwcap_yields_fully_populated_armcap() { + do_armcap_bits_test("src/cpu_feature/arm_linux/test-data/linux-arm-cavium-thunderx.cpuinfo", + not_found_getauxv(), + &hwcap_neon_procfs_auxv(), + ARMV7_NEON | ARMV8_PMULL | ARMV8_AES | ARMV8_SHA1 + | ARMV8_SHA256); + } + + #[test] + fn armcap_bits_arm_8_with_cpuinfo_features_with_neon_only_getauxv_hwcap_and_aes_getauxv_hwcap2_yields_only_neon_aes_armcap() { + let mut auxv = HashMap::new(); + let _ = auxv.insert(native_auxv_types().AT_HWCAP, + native_hwcap_features().ARM_HWCAP_NEON); + let _ = auxv.insert(native_auxv_types().AT_HWCAP2, + native_hwcap_features().ARM_HWCAP2_AES); + let getauxv = StubGetauxvalProvider { + auxv: auxv + }; + + do_armcap_bits_test("src/cpu_feature/arm_linux/test-data/linux-arm-cavium-thunderx.cpuinfo", + getauxv, + &empty_procfs_auxv(), + ARMV7_NEON | ARMV8_AES); + } + + #[test] + fn armcap_bits_arm_8_with_cpuinfo_features_with_neon_only_procfs_hwcap_and_pmull_procfs_hwcap2_yields_only_neon_aes_armcap() { + let mut proc_auxv = AuxVals::new(); + let _ = proc_auxv.insert(native_auxv_types().AT_HWCAP, + native_hwcap_features().ARM_HWCAP_NEON); + let _ = proc_auxv.insert(native_auxv_types().AT_HWCAP2, + native_hwcap_features().ARM_HWCAP2_PMULL); + do_armcap_bits_test("src/cpu_feature/arm_linux/test-data/linux-arm-cavium-thunderx.cpuinfo", + not_found_getauxv(), + &proc_auxv, + ARMV7_NEON | ARMV8_PMULL); + } + + #[test] + fn armcap_for_hwcap2_zero_returns_zero() { + assert_eq!(0, super::armcap_for_hwcap2(0, test_hwcap_features())); + } + + #[test] + fn armcap_for_hwcap2_all_hwcap2_returns_all_armcap() { + assert_eq!(ARMV8_AES | ARMV8_PMULL | ARMV8_SHA1 | ARMV8_SHA256, + super::armcap_for_hwcap2(test_hwcap_features().ARM_HWCAP2_AES + | test_hwcap_features().ARM_HWCAP2_PMULL + | test_hwcap_features().ARM_HWCAP2_SHA1 + | test_hwcap_features().ARM_HWCAP2_SHA2, + test_hwcap_features())); + } + + #[test] + fn arm_hwcap_cpuinfo_arch_8_returns_neon() { + let mut cpuinfo = HashMap::::new(); + let _ = cpuinfo.insert("CPU architecture".to_string(), "8".to_string()); + + assert_eq!(Some(test_hwcap_features().ARM_HWCAP_NEON), + super::hwcap_from_cpuinfo(&cpuinfo, test_hwcap_features())); + } + + #[test] + fn arm_hwcap_cpuinfo_arch_7_with_feature_returns_neon() { + let mut cpuinfo = HashMap::::new(); + let _ = cpuinfo.insert("CPU architecture".to_string(), "7".to_string()); + let _ = cpuinfo.insert("Features".to_string(), + "foo neon bar ".to_string()); + + assert_eq!(Some(test_hwcap_features().ARM_HWCAP_NEON), + super::hwcap_from_cpuinfo(&cpuinfo, test_hwcap_features())); + } + + #[test] + fn arm_hwcap_cpuinfo_arch_7_without_feature_returns_none() { + let mut cpuinfo = HashMap::::new(); + let _ = cpuinfo.insert("CPU architecture".to_string(), "7".to_string()); + + assert_eq!(None, super::hwcap_from_cpuinfo(&cpuinfo, + test_hwcap_features())); + } + + #[test] + fn arm_hwcap2_cpuinfo_missing_features_returns_none() { + // x86 doesn't have "Features", it has "flags" + let cpuinfo = parse_cpuinfo_file( + Path::new("src/cpu_feature/arm_linux/test-data/linux-x64-i7-6850k.cpuinfo")).unwrap(); + + assert_eq!(None, super::hwcap2_from_cpuinfo(&cpuinfo, + test_hwcap_features())); + } + + #[test] + fn arm_hwcap2_cpuinfo_sad_features_returns_zero() { + // the broken cpu has weaksauce features + let cpuinfo = parse_cpuinfo_file( + Path::new("src/cpu_feature/arm_linux/test-data/linux-arm-broken.cpuinfo")).unwrap(); + + assert_eq!(Some(0), super::hwcap2_from_cpuinfo(&cpuinfo, + test_hwcap_features())); + } + + #[test] + fn arm_hwcap2_cpuinfo_fancy_features_returns_all() { + let mut cpuinfo = HashMap::::new(); + let _ = cpuinfo.insert("Features".to_string(), + "quux aes pmull sha1 sha2 foo ".to_string()); + + assert_eq!(Some(test_hwcap_features().ARM_HWCAP2_AES + | test_hwcap_features().ARM_HWCAP2_PMULL + | test_hwcap_features().ARM_HWCAP2_SHA1 + | test_hwcap_features().ARM_HWCAP2_SHA2), + super::hwcap2_from_cpuinfo(&cpuinfo, test_hwcap_features())); + } + + #[test] + fn arm_broken_neon_cpuinfo_detects_broken_arm() { + let cpuinfo = parse_cpuinfo_file( + Path::new("src/cpu_feature/arm_linux/test-data/linux-arm-broken.cpuinfo")).unwrap(); + + assert!(super::cpu_has_broken_neon(&cpuinfo)); + } + + #[test] + fn arm_broken_neon_cpuinfo_ignores_x86() { + let cpuinfo = parse_cpuinfo_file( + Path::new("src/cpu_feature/arm_linux/test-data/linux-x64-i7-6850k.cpuinfo")).unwrap(); + + assert!(!super::cpu_has_broken_neon(&cpuinfo)); + } + + #[test] + fn parse_arm_features_handles_trailing_space() { + let set = super::parse_arm_cpuinfo_features("foo bar baz "); + assert_eq!(3, set.len()); + assert!(set.contains("baz")); + } + + fn do_armcap_bits_test(path: &str, getauxval: StubGetauxvalProvider, + proc_auxv: &AuxVals, expected_armcap: u32) { + let cpuinfo = parse_cpuinfo_file(Path::new(path)).unwrap(); + + assert_eq!(expected_armcap, + armcap_bits::(&cpuinfo, proc_auxv, + native_auxv_types(), + native_hwcap_features(), + getauxval)); + } + + fn parse_cpuinfo_file(path: &Path) -> Result { + let mut buf = Vec::new(); + let mut f = File::open(path).unwrap(); + let _ = f.read_to_end(&mut buf).unwrap(); + + let mut buffer = &buf[..]; + parse_cpuinfo_reader(&mut buffer) + } + + fn test_hwcap_features() -> ArmHwcapFeatures { + ArmHwcapFeatures::new() + } + + fn native_auxv_types() -> AuxvTypes { + AuxvTypes::new() + } + + fn native_hwcap_features() -> ArmHwcapFeatures { + ArmHwcapFeatures::new() + } + + fn empty_procfs_auxv() -> AuxVals { + AuxVals::new() + } + + fn hwcap_neon_procfs_auxv() -> AuxVals { + let mut proc_auxv = AuxVals::::new(); + let _ = proc_auxv.insert(native_auxv_types().AT_HWCAP, + native_hwcap_features().ARM_HWCAP_NEON); + + proc_auxv + } + + fn not_found_getauxv() -> StubGetauxvalProvider { + StubGetauxvalProvider { auxv: AuxVals::::new() } + } + + fn hwcap_neon_getauxv() -> StubGetauxvalProvider { + let mut auxv = AuxVals::new(); + let _ = auxv.insert(native_auxv_types().AT_HWCAP, + native_hwcap_features().ARM_HWCAP_NEON); + + StubGetauxvalProvider { + auxv: auxv + } + } +} diff --git a/src/cpu_feature/arm_linux/auxv.rs b/src/cpu_feature/arm_linux/auxv.rs new file mode 100644 index 0000000000..c3a3c42c47 --- /dev/null +++ b/src/cpu_feature/arm_linux/auxv.rs @@ -0,0 +1,313 @@ +extern crate byteorder; + +use std; +use std::collections::HashMap; +use std::io::{BufReader, Read}; +use std::fs::File; +use std::path::Path; +use std::vec::Vec; +use std::ops::{BitAnd, BitAndAssign, BitOrAssign, Not}; +use std::hash::Hash; + +use self::byteorder::{ByteOrder, ReadBytesExt}; + +// /proc/self/auxv is defined to be pairs of `unsigned long`-width bits, and +// getauxval() also is defined to take an `unsigned long` param and return the same. +// Adding further complexity, we want to always run tests against auxv files that are +// from systems with 64-bit unsigned longs, so we can't just always use the native +// size. +pub trait AuxvUnsignedLong : BitAndAssign + BitAnd + + BitOrAssign + Not + Eq + Ord + Hash + Sized + Copy + + From { + fn read(&mut Read) -> std::io::Result; +} + +impl AuxvUnsignedLong for u32 { + fn read (reader: &mut Read) -> std::io::Result{ + reader.read_u32::() + } +} + +impl AuxvUnsignedLong for u64 { + fn read (reader: &mut Read) -> std::io::Result{ + reader.read_u64::() + } +} + +#[cfg(any(all(target_pointer_width = "32", test), + all(target_pointer_width = "32", target_os = "linux")))] +pub type AuxvUnsignedLongNative = u32; +#[cfg(any(all(target_pointer_width = "64", test), + all(target_pointer_width = "64", target_os = "linux")))] +pub type AuxvUnsignedLongNative = u64; + +extern "C" { + /// Invoke getauxval(3) if available. If it's not linked, or if invocation + /// fails or the type is not found, sets success to false and returns 0. + #[cfg(target_os="linux")] + pub fn getauxval_wrapper(auxv_type: AuxvUnsignedLongNative, + success: *mut u8) + -> AuxvUnsignedLongNative; +} + +pub trait GetauxvalProvider { + fn getauxval(&self, auxv_type: AuxvUnsignedLongNative) + -> Option; +} + +#[cfg(target_os="linux")] +pub struct NativeGetauxvalProvider {} + +#[cfg(target_os="linux")] +impl GetauxvalProvider for NativeGetauxvalProvider { + /// Returns Some if the native invocation succeeds and the requested type was + /// found, otherwise None. + fn getauxval(&self, auxv_type: AuxvUnsignedLongNative) + -> Option { + let mut success = 0; + unsafe { + let result = getauxval_wrapper(auxv_type, &mut success); + if success == 1 { + return Some(result); + } + } + + None + } +} + +/// auxv "types": the argument to getauxv, or the first of each pair in +/// /proc/self/auxv. +/// This structure allows us to bind these constants in a way that allows +/// 64-bit testing on all platforms but also can express the native +/// underlying type. +/// Don't modify the fields; they're meant to be read only. +#[allow(non_snake_case, non_camel_case_types)] +pub struct AuxvTypes { + pub AT_HWCAP: T, + pub AT_HWCAP2: T +} + +impl AuxvTypes { + pub fn new() -> AuxvTypes { + AuxvTypes { + // from [linux]/include/uapi/linux/auxvec.h. First 32 bits of HWCAP + // even on platforms where unsigned long is 64 bits. + AT_HWCAP: T::from(16), + // currently only used by powerpc and arm64 AFAICT + AT_HWCAP2: T::from(26) + } + } +} + +pub type AuxVals = HashMap; + +#[derive(Debug, PartialEq)] +pub enum AuxValError { + IoError, + InvalidFormat +} + +/// Read an entry from the procfs auxv file. +/// +/// input: pairs of unsigned longs, as in /proc/self/auxv. The first of each +/// pair is the 'type' and the second is the 'value'. +/// +/// aux_types: the types to look for +/// returns a map of types to values, only including entries for types that were +/// requested that also had values in the aux vector +pub fn search_procfs_auxv(path: &Path, + aux_types: &[T]) + -> Result, AuxValError> { + let mut input = File::open(path) + .map_err(|_| AuxValError::IoError) + .map(|f| BufReader::new(f))?; + + let ulong_size = std::mem::size_of::(); + let mut buf: Vec = Vec::with_capacity(2 * ulong_size); + let mut result = HashMap::::new(); + + loop { + buf.clear(); + // fill vec so we can slice into it + for _ in 0 .. 2 * ulong_size { + buf.push(0); + } + + let mut read_bytes: usize = 0; + while read_bytes < 2 * ulong_size { + // read exactly buf's len of bytes. + match input.read(&mut buf[read_bytes..]) { + Ok(n) => { + if n == 0 { + // should not hit EOF before AT_NULL + return Err(AuxValError::InvalidFormat) + } + + read_bytes += n; + } + Err(_) => return Err(AuxValError::IoError) + } + } + + let mut reader = &buf[..]; + let found_aux_type = T::read::(&mut reader) + .map_err(|_| AuxValError::InvalidFormat)?; + let aux_val = T::read::(&mut reader) + .map_err(|_| AuxValError::InvalidFormat)?; + + if aux_types.contains(&found_aux_type) { + let _ = result.insert(found_aux_type, aux_val); + } + + // AT_NULL (0) signals the end of auxv + if found_aux_type == T::from(0) { + return Ok(result); + } + } +} + + +#[cfg(test)] +mod tests { + extern crate byteorder; + + use std::path::Path; + use super::{AuxValError, AuxvTypes, search_procfs_auxv}; + #[cfg(target_os="linux")] + use super::{AuxvUnsignedLongNative, GetauxvalProvider, + NativeGetauxvalProvider}; + + use self::byteorder::LittleEndian; + + // uid of program that read /proc/self/auxv + const AT_UID_64: u64 = 11; + const AT_UID_32: u32 = 11; + + // x86 hwcap bits from [linux]/arch/x86/include/asm/cpufeature.h + const X86_FPU: u32 = 0 * 32 + 0; + const X86_ACPI: u32 = 0 * 32 + 22; + + #[test] + #[cfg(target_os="linux")] + fn test_getauxv_hwcap_linux_finds_hwcap() { + let native_getauxval = NativeGetauxvalProvider{}; + let result = native_getauxval.getauxval(AuxvTypes::new().AT_HWCAP); + assert!(result.is_some()); + // there should be SOMETHING in the value + assert!(result.unwrap() > 0); + } + + #[test] + #[cfg(target_os="linux")] + fn test_getauxv_hwcap_linux_doesnt_find_bogus_type() { + let native_getauxval = NativeGetauxvalProvider{}; + + assert!(native_getauxval.getauxval( + AuxvUnsignedLongNative::from(555555555_u32)).is_none()); + } + + #[test] + fn test_parse_auxv_virtualbox_linux() { + let path = Path::new("src/cpu_feature/arm_linux/test-data/macos-virtualbox-linux-x64-4850HQ.auxv"); + let vals = search_procfs_auxv::(path, + &[u64_auxv_types().AT_HWCAP, + u64_auxv_types().AT_HWCAP2, + AT_UID_64]) + .unwrap(); + let hwcap = vals.get(&u64_auxv_types().AT_HWCAP).unwrap(); + assert_eq!(&395049983_u64, hwcap); + + assert_eq!(1, 1 << X86_FPU & hwcap); + // virtualized, no acpi via msr I guess + assert_eq!(0, 1 << X86_ACPI & hwcap); + + assert!(!vals.contains_key(&u64_auxv_types().AT_HWCAP2)); + + assert_eq!(&1000_u64, vals.get(&AT_UID_64).unwrap()); + } + + #[test] + fn test_parse_auxv_virtualbox_linux_32bit() { + let path = Path::new("src/cpu_feature/arm_linux/test-data/macos-virtualbox-linux-x86-4850HQ.auxv"); + let vals = search_procfs_auxv::(path, + &[u32_auxv_types().AT_HWCAP, + u32_auxv_types().AT_HWCAP2, + AT_UID_32]) + .unwrap(); + let hwcap = vals.get(&u32_auxv_types().AT_HWCAP).unwrap(); + assert_eq!(&126614527_u32, hwcap); + + assert_eq!(1, 1 << X86_FPU & hwcap); + // virtualized, no acpi via msr I guess + assert_eq!(0, 1 << X86_ACPI & hwcap); + + assert!(!vals.contains_key(&u32_auxv_types().AT_HWCAP2)); + + // this auxv was while running as root (unlike other auxv files) + assert_eq!(&0_u32, vals.get(&AT_UID_32).unwrap()); + } + + #[test] + fn test_parse_auxv_virtualbox_linux_32bit_in_64bit_mode_invalidformat() { + let path = Path::new("src/cpu_feature/arm_linux/test-data/macos-virtualbox-linux-x86-4850HQ.auxv"); + let vals = search_procfs_auxv::(path, + &[u64_auxv_types().AT_HWCAP, + u64_auxv_types().AT_HWCAP2, + AT_UID_64]); + + assert_eq!(Err(AuxValError::InvalidFormat), vals); + } + + #[test] + fn test_parse_auxv_real_linux() { + let path = Path::new("src/cpu_feature/arm_linux/test-data/linux-x64-i7-6850k.auxv"); + let vals = search_procfs_auxv::(path, + &[u64_auxv_types().AT_HWCAP, + u64_auxv_types().AT_HWCAP2, + AT_UID_64]) + .unwrap(); + let hwcap = vals.get(&u64_auxv_types().AT_HWCAP).unwrap(); + + assert_eq!(&3219913727_u64, hwcap); + + assert_eq!(1, 1 << X86_FPU & hwcap); + assert_eq!(1 << X86_ACPI, 1 << X86_ACPI & hwcap); + + assert!(!vals.contains_key(&u64_auxv_types().AT_HWCAP2)); + + assert_eq!(&1000_u64, vals.get(&AT_UID_64).unwrap()); + } + + #[test] + fn test_parse_auxv_real_linux_half_of_trailing_null_missing_error() { + let path = Path::new("src/cpu_feature/arm_linux/test-data/linux-x64-i7-6850k-mangled-no-value-in-trailing-null.auxv"); + assert_eq!(AuxValError::InvalidFormat, + search_procfs_auxv::(path, + &[555555555]).unwrap_err()); + } + + #[test] + fn test_parse_auxv_real_linux_trailing_null_missing_error() { + let path = Path::new("src/cpu_feature/arm_linux/test-data/linux-x64-i7-6850k-mangled-no-trailing-null.auxv"); + assert_eq!(AuxValError::InvalidFormat, + search_procfs_auxv::(path, + &[555555555]).unwrap_err()); + } + + #[test] + fn test_parse_auxv_real_linux_truncated_entry_error() { + let path = Path::new("src/cpu_feature/arm_linux/test-data/linux-x64-i7-6850k-mangled-truncated-entry.auxv"); + assert_eq!(AuxValError::InvalidFormat, + search_procfs_auxv::(path, + &[555555555]).unwrap_err()); + } + + fn u64_auxv_types() -> AuxvTypes { + AuxvTypes::new() + } + + fn u32_auxv_types() -> AuxvTypes { + AuxvTypes::new() + } +} diff --git a/src/cpu_feature/arm_linux/cpuinfo.rs b/src/cpu_feature/arm_linux/cpuinfo.rs new file mode 100644 index 0000000000..93614e5f20 --- /dev/null +++ b/src/cpu_feature/arm_linux/cpuinfo.rs @@ -0,0 +1,153 @@ +use std::collections::HashMap; +#[cfg(all(any(target_arch = "arm", target_arch = "aarch64"), target_os="linux"))] +use std::fs::File; +#[cfg(all(any(target_arch = "arm", target_arch = "aarch64"), target_os="linux"))] +use std::io::BufReader; +use std::io::BufRead; +use std::string::{String, ToString}; + +#[derive(Debug, PartialEq)] +pub enum CpuInfoError { + IoError, + InvalidFormat +} + +pub type CpuInfo = HashMap; + +#[cfg(all(any(target_arch = "arm", target_arch = "aarch64"), target_os="linux"))] +pub fn parse_cpuinfo() -> Result { + match File::open("/proc/cpuinfo").map(|f| BufReader::new(f)) { + Ok(mut r) => parse_cpuinfo_reader(&mut r), + Err(_) => Err(CpuInfoError::IoError) + } +} + +/// parse the contents of /proc/cpuinfo into a map of field names to values. +/// Keeps the first encountered value for each name. +#[cfg(any(test, target_os="linux"))] +pub fn parse_cpuinfo_reader(input: &mut BufRead) + -> Result { + // cpu flags can be quite long + let mut line_buf = String::with_capacity(300); + let mut fields = HashMap::new(); + + loop { + line_buf.clear(); + + let bytes_read = input.read_line(&mut line_buf) + .map_err(|_| CpuInfoError::IoError)?; + if bytes_read == 0 { + // eof + return Ok(fields); + } + + let trimmed_line = line_buf.trim_right_matches('\n'); + if trimmed_line.is_empty() { + // skip blank lines + continue; + } + + let mut chunks = trimmed_line.splitn(2, ":"); + let name = chunks.next().ok_or(CpuInfoError::InvalidFormat)? + .trim_right_matches('\t') + .to_string(); + let value = chunks.next().ok_or(CpuInfoError::InvalidFormat)? + .trim_left_matches(' ') + .to_string(); + + let _ = fields.entry(name).or_insert(value); + } +} + +#[cfg(test)] +mod tests { + use std::io::Read; + use std::fs::File; + use std::path::Path; + use std::vec::Vec; + use super::{CpuInfo, CpuInfoError}; + + #[test] + fn test_parse_cpuinfo_virtualbox_linux() { + let path = Path::new("src/cpu_feature/arm_linux/test-data/macos-virtualbox-linux-x64-4850HQ.cpuinfo"); + let cpuinfo = parse_cpuinfo_file(path).unwrap(); + + // 1 tab + assert_eq!("0", cpuinfo.get("processor").unwrap()); + // 2 tabs of spacing + assert_eq!("70", cpuinfo.get("model").unwrap()); + // 0 tabs of spacing + assert_eq!("", cpuinfo.get("power management").unwrap()); + } + + #[test] + fn test_parse_cpuinfo_real_linux() { + let path = Path::new("src/cpu_feature/arm_linux/test-data/linux-x64-i7-6850k.cpuinfo"); + let cpuinfo = parse_cpuinfo_file(path).unwrap(); + + // 1 tab + assert_eq!("0", cpuinfo.get("processor").unwrap()); + // 2 tabs of spacing + assert_eq!("79", cpuinfo.get("model").unwrap()); + // 0 tabs of spacing + assert_eq!("", cpuinfo.get("power management").unwrap()); + } + + #[test] + fn test_parse_cpuinfo_no_colon_delimiter_error() { + let mut input = "foobar\n".as_bytes(); + let cpuinfo = super::parse_cpuinfo_reader(&mut input); + assert_eq!(CpuInfoError::InvalidFormat, cpuinfo.unwrap_err()); + } + + #[test] + fn test_parse_cpuinfo_no_trailing_blank_line_ok() { + // this is how it would be if there was only 1 core + let mut input = "wp : yes\n".as_bytes(); + let cpuinfo = super::parse_cpuinfo_reader(&mut input).unwrap(); + assert_eq!(1, cpuinfo.len()); + } + + #[test] + fn test_parse_cpuinfo_empty_line_ok() { + let mut input = "\n".as_bytes(); + let cpuinfo = super::parse_cpuinfo_reader(&mut input).unwrap(); + assert_eq!(0, cpuinfo.len()); + } + + #[test] + fn test_parse_cpuinfo_empty_input_ok() { + let mut input = "".as_bytes(); + let cpuinfo = super::parse_cpuinfo_reader(&mut input).unwrap(); + assert_eq!(0, cpuinfo.len()); + } + + #[test] + fn test_parse_cpuinfo_skips_blank_line() { + let mut input = "foo\t: bar\n\nbaz\t: quux\n".as_bytes(); + let cpuinfo = super::parse_cpuinfo_reader(&mut input).unwrap(); + assert_eq!(2, cpuinfo.len()); + assert_eq!("bar", cpuinfo.get("foo").unwrap()); + assert_eq!("quux", cpuinfo.get("baz").unwrap()); + } + + #[test] + fn test_parse_cpuinfo_broken_arm() { + let path = Path::new("src/cpu_feature/arm_linux/test-data/linux-arm-broken.cpuinfo"); + let cpuinfo = parse_cpuinfo_file(path).unwrap(); + + // kept the first 'processor' + assert_eq!("0", cpuinfo.get("processor").unwrap()); + // found things after several blank lines + assert_eq!("0x51", cpuinfo.get("CPU implementer").unwrap()); + } + + fn parse_cpuinfo_file(path: &Path) -> Result { + let mut buf = Vec::new(); + let mut f = File::open(path).unwrap(); + let _ = f.read_to_end(&mut buf).unwrap(); + + let mut buffer = &buf[..]; + super::parse_cpuinfo_reader(&mut buffer) + } +} diff --git a/src/cpu_feature/arm_linux/test-data/linux-arm-C1904.cpuinfo b/src/cpu_feature/arm_linux/test-data/linux-arm-C1904.cpuinfo new file mode 100644 index 0000000000..13c1230186 --- /dev/null +++ b/src/cpu_feature/arm_linux/test-data/linux-arm-C1904.cpuinfo @@ -0,0 +1,17 @@ +Processor : ARMv7 Processor rev 4 (v7l) +processor : 0 +BogoMIPS : 13.53 + +processor : 1 +BogoMIPS : 13.53 + +Features : swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 +CPU implementer : 0x51 +CPU architecture: 7 +CPU variant : 0x1 +CPU part : 0x04d +CPU revision : 4 + +Hardware : QCT MSM8627 MTP +Revision : 0000 +Serial : 0000000000000000 diff --git a/src/cpu_feature/arm_linux/test-data/linux-arm-broken.cpuinfo b/src/cpu_feature/arm_linux/test-data/linux-arm-broken.cpuinfo new file mode 100644 index 0000000000..11979dbd92 --- /dev/null +++ b/src/cpu_feature/arm_linux/test-data/linux-arm-broken.cpuinfo @@ -0,0 +1,16 @@ +Processor : ARMv7 Processor rev 0 (v7l) +processor : 0 +BogoMIPS : 13.50 + +processor : 1 +BogoMIPS : 13.50 + +Features : swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 +CPU implementer : 0x51 +CPU architecture: 7 +CPU variant : 0x1 +CPU part : 0x04d +CPU revision : 0 + +Hardware : SAMSUNG M2 +Revision : 000e diff --git a/src/cpu_feature/arm_linux/test-data/linux-arm-cavium-thunderx.cpuinfo b/src/cpu_feature/arm_linux/test-data/linux-arm-cavium-thunderx.cpuinfo new file mode 100644 index 0000000000..0d798455c6 --- /dev/null +++ b/src/cpu_feature/arm_linux/test-data/linux-arm-cavium-thunderx.cpuinfo @@ -0,0 +1,15 @@ +processor : 0 +Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 +CPU implementer : 0x43 +CPU architecture: 8 +CPU variant : 0x0 +CPU part : 0x0a1 +CPU revision : 1 + +processor : 1 +Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 +CPU implementer : 0x43 +CPU architecture: 8 +CPU variant : 0x0 +CPU part : 0x0a1 +CPU revision : 1 diff --git a/src/cpu_feature/arm_linux/test-data/linux-x64-i7-6850k-mangled-no-trailing-null.auxv b/src/cpu_feature/arm_linux/test-data/linux-x64-i7-6850k-mangled-no-trailing-null.auxv new file mode 100644 index 0000000000..7ad3821ce0 Binary files /dev/null and b/src/cpu_feature/arm_linux/test-data/linux-x64-i7-6850k-mangled-no-trailing-null.auxv differ diff --git a/src/cpu_feature/arm_linux/test-data/linux-x64-i7-6850k-mangled-no-value-in-trailing-null.auxv b/src/cpu_feature/arm_linux/test-data/linux-x64-i7-6850k-mangled-no-value-in-trailing-null.auxv new file mode 100644 index 0000000000..08db05b87b Binary files /dev/null and b/src/cpu_feature/arm_linux/test-data/linux-x64-i7-6850k-mangled-no-value-in-trailing-null.auxv differ diff --git a/src/cpu_feature/arm_linux/test-data/linux-x64-i7-6850k-mangled-truncated-entry.auxv b/src/cpu_feature/arm_linux/test-data/linux-x64-i7-6850k-mangled-truncated-entry.auxv new file mode 100644 index 0000000000..c636e3c08c Binary files /dev/null and b/src/cpu_feature/arm_linux/test-data/linux-x64-i7-6850k-mangled-truncated-entry.auxv differ diff --git a/src/cpu_feature/arm_linux/test-data/linux-x64-i7-6850k.auxv b/src/cpu_feature/arm_linux/test-data/linux-x64-i7-6850k.auxv new file mode 100644 index 0000000000..6afe1b3b46 Binary files /dev/null and b/src/cpu_feature/arm_linux/test-data/linux-x64-i7-6850k.auxv differ diff --git a/src/cpu_feature/arm_linux/test-data/linux-x64-i7-6850k.cpuinfo b/src/cpu_feature/arm_linux/test-data/linux-x64-i7-6850k.cpuinfo new file mode 100644 index 0000000000..5b6b867fee --- /dev/null +++ b/src/cpu_feature/arm_linux/test-data/linux-x64-i7-6850k.cpuinfo @@ -0,0 +1,324 @@ +processor : 0 +vendor_id : GenuineIntel +cpu family : 6 +model : 79 +model name : Intel(R) Core(TM) i7-6850K CPU @ 3.60GHz +stepping : 1 +microcode : 0xb00001c +cpu MHz : 1286.156 +cache size : 15360 KB +physical id : 0 +siblings : 12 +core id : 0 +cpu cores : 6 +apicid : 0 +initial apicid : 0 +fpu : yes +fpu_exception : yes +cpuid level : 20 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch ida arat epb pln pts dtherm intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm rdseed adx smap xsaveopt cqm_llc cqm_occup_llc +bugs : +bogomips : 7196.31 +clflush size : 64 +cache_alignment : 64 +address sizes : 46 bits physical, 48 bits virtual +power management: + +processor : 1 +vendor_id : GenuineIntel +cpu family : 6 +model : 79 +model name : Intel(R) Core(TM) i7-6850K CPU @ 3.60GHz +stepping : 1 +microcode : 0xb00001c +cpu MHz : 1699.171 +cache size : 15360 KB +physical id : 0 +siblings : 12 +core id : 1 +cpu cores : 6 +apicid : 2 +initial apicid : 2 +fpu : yes +fpu_exception : yes +cpuid level : 20 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch ida arat epb pln pts dtherm intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm rdseed adx smap xsaveopt cqm_llc cqm_occup_llc +bugs : +bogomips : 7196.31 +clflush size : 64 +cache_alignment : 64 +address sizes : 46 bits physical, 48 bits virtual +power management: + +processor : 2 +vendor_id : GenuineIntel +cpu family : 6 +model : 79 +model name : Intel(R) Core(TM) i7-6850K CPU @ 3.60GHz +stepping : 1 +microcode : 0xb00001c +cpu MHz : 3599.859 +cache size : 15360 KB +physical id : 0 +siblings : 12 +core id : 2 +cpu cores : 6 +apicid : 4 +initial apicid : 4 +fpu : yes +fpu_exception : yes +cpuid level : 20 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch ida arat epb pln pts dtherm intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm rdseed adx smap xsaveopt cqm_llc cqm_occup_llc +bugs : +bogomips : 7196.31 +clflush size : 64 +cache_alignment : 64 +address sizes : 46 bits physical, 48 bits virtual +power management: + +processor : 3 +vendor_id : GenuineIntel +cpu family : 6 +model : 79 +model name : Intel(R) Core(TM) i7-6850K CPU @ 3.60GHz +stepping : 1 +microcode : 0xb00001c +cpu MHz : 1199.953 +cache size : 15360 KB +physical id : 0 +siblings : 12 +core id : 3 +cpu cores : 6 +apicid : 6 +initial apicid : 6 +fpu : yes +fpu_exception : yes +cpuid level : 20 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch ida arat epb pln pts dtherm intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm rdseed adx smap xsaveopt cqm_llc cqm_occup_llc +bugs : +bogomips : 7196.31 +clflush size : 64 +cache_alignment : 64 +address sizes : 46 bits physical, 48 bits virtual +power management: + +processor : 4 +vendor_id : GenuineIntel +cpu family : 6 +model : 79 +model name : Intel(R) Core(TM) i7-6850K CPU @ 3.60GHz +stepping : 1 +microcode : 0xb00001c +cpu MHz : 1200.093 +cache size : 15360 KB +physical id : 0 +siblings : 12 +core id : 4 +cpu cores : 6 +apicid : 8 +initial apicid : 8 +fpu : yes +fpu_exception : yes +cpuid level : 20 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch ida arat epb pln pts dtherm intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm rdseed adx smap xsaveopt cqm_llc cqm_occup_llc +bugs : +bogomips : 7196.31 +clflush size : 64 +cache_alignment : 64 +address sizes : 46 bits physical, 48 bits virtual +power management: + +processor : 5 +vendor_id : GenuineIntel +cpu family : 6 +model : 79 +model name : Intel(R) Core(TM) i7-6850K CPU @ 3.60GHz +stepping : 1 +microcode : 0xb00001c +cpu MHz : 1200.093 +cache size : 15360 KB +physical id : 0 +siblings : 12 +core id : 5 +cpu cores : 6 +apicid : 10 +initial apicid : 10 +fpu : yes +fpu_exception : yes +cpuid level : 20 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch ida arat epb pln pts dtherm intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm rdseed adx smap xsaveopt cqm_llc cqm_occup_llc +bugs : +bogomips : 7196.31 +clflush size : 64 +cache_alignment : 64 +address sizes : 46 bits physical, 48 bits virtual +power management: + +processor : 6 +vendor_id : GenuineIntel +cpu family : 6 +model : 79 +model name : Intel(R) Core(TM) i7-6850K CPU @ 3.60GHz +stepping : 1 +microcode : 0xb00001c +cpu MHz : 1220.203 +cache size : 15360 KB +physical id : 0 +siblings : 12 +core id : 0 +cpu cores : 6 +apicid : 1 +initial apicid : 1 +fpu : yes +fpu_exception : yes +cpuid level : 20 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch ida arat epb pln pts dtherm intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm rdseed adx smap xsaveopt cqm_llc cqm_occup_llc +bugs : +bogomips : 7196.31 +clflush size : 64 +cache_alignment : 64 +address sizes : 46 bits physical, 48 bits virtual +power management: + +processor : 7 +vendor_id : GenuineIntel +cpu family : 6 +model : 79 +model name : Intel(R) Core(TM) i7-6850K CPU @ 3.60GHz +stepping : 1 +microcode : 0xb00001c +cpu MHz : 1225.546 +cache size : 15360 KB +physical id : 0 +siblings : 12 +core id : 1 +cpu cores : 6 +apicid : 3 +initial apicid : 3 +fpu : yes +fpu_exception : yes +cpuid level : 20 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch ida arat epb pln pts dtherm intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm rdseed adx smap xsaveopt cqm_llc cqm_occup_llc +bugs : +bogomips : 7196.31 +clflush size : 64 +cache_alignment : 64 +address sizes : 46 bits physical, 48 bits virtual +power management: + +processor : 8 +vendor_id : GenuineIntel +cpu family : 6 +model : 79 +model name : Intel(R) Core(TM) i7-6850K CPU @ 3.60GHz +stepping : 1 +microcode : 0xb00001c +cpu MHz : 2304.140 +cache size : 15360 KB +physical id : 0 +siblings : 12 +core id : 2 +cpu cores : 6 +apicid : 5 +initial apicid : 5 +fpu : yes +fpu_exception : yes +cpuid level : 20 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch ida arat epb pln pts dtherm intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm rdseed adx smap xsaveopt cqm_llc cqm_occup_llc +bugs : +bogomips : 7196.31 +clflush size : 64 +cache_alignment : 64 +address sizes : 46 bits physical, 48 bits virtual +power management: + +processor : 9 +vendor_id : GenuineIntel +cpu family : 6 +model : 79 +model name : Intel(R) Core(TM) i7-6850K CPU @ 3.60GHz +stepping : 1 +microcode : 0xb00001c +cpu MHz : 1215.281 +cache size : 15360 KB +physical id : 0 +siblings : 12 +core id : 3 +cpu cores : 6 +apicid : 7 +initial apicid : 7 +fpu : yes +fpu_exception : yes +cpuid level : 20 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch ida arat epb pln pts dtherm intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm rdseed adx smap xsaveopt cqm_llc cqm_occup_llc +bugs : +bogomips : 7196.31 +clflush size : 64 +cache_alignment : 64 +address sizes : 46 bits physical, 48 bits virtual +power management: + +processor : 10 +vendor_id : GenuineIntel +cpu family : 6 +model : 79 +model name : Intel(R) Core(TM) i7-6850K CPU @ 3.60GHz +stepping : 1 +microcode : 0xb00001c +cpu MHz : 1201.500 +cache size : 15360 KB +physical id : 0 +siblings : 12 +core id : 4 +cpu cores : 6 +apicid : 9 +initial apicid : 9 +fpu : yes +fpu_exception : yes +cpuid level : 20 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch ida arat epb pln pts dtherm intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm rdseed adx smap xsaveopt cqm_llc cqm_occup_llc +bugs : +bogomips : 7196.31 +clflush size : 64 +cache_alignment : 64 +address sizes : 46 bits physical, 48 bits virtual +power management: + +processor : 11 +vendor_id : GenuineIntel +cpu family : 6 +model : 79 +model name : Intel(R) Core(TM) i7-6850K CPU @ 3.60GHz +stepping : 1 +microcode : 0xb00001c +cpu MHz : 1200.375 +cache size : 15360 KB +physical id : 0 +siblings : 12 +core id : 5 +cpu cores : 6 +apicid : 11 +initial apicid : 11 +fpu : yes +fpu_exception : yes +cpuid level : 20 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch ida arat epb pln pts dtherm intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm rdseed adx smap xsaveopt cqm_llc cqm_occup_llc +bugs : +bogomips : 7196.31 +clflush size : 64 +cache_alignment : 64 +address sizes : 46 bits physical, 48 bits virtual +power management: + diff --git a/src/cpu_feature/arm_linux/test-data/macos-virtualbox-linux-x64-4850HQ.auxv b/src/cpu_feature/arm_linux/test-data/macos-virtualbox-linux-x64-4850HQ.auxv new file mode 100644 index 0000000000..f421e5d509 Binary files /dev/null and b/src/cpu_feature/arm_linux/test-data/macos-virtualbox-linux-x64-4850HQ.auxv differ diff --git a/src/cpu_feature/arm_linux/test-data/macos-virtualbox-linux-x64-4850HQ.cpuinfo b/src/cpu_feature/arm_linux/test-data/macos-virtualbox-linux-x64-4850HQ.cpuinfo new file mode 100644 index 0000000000..233e7db663 --- /dev/null +++ b/src/cpu_feature/arm_linux/test-data/macos-virtualbox-linux-x64-4850HQ.cpuinfo @@ -0,0 +1,104 @@ +processor : 0 +vendor_id : GenuineIntel +cpu family : 6 +model : 70 +model name : Intel(R) Core(TM) i7-4850HQ CPU @ 2.30GHz +stepping : 1 +cpu MHz : 2294.688 +cache size : 6144 KB +physical id : 0 +siblings : 4 +core id : 0 +cpu cores : 4 +apicid : 0 +initial apicid : 0 +fpu : yes +fpu_exception : yes +cpuid level : 13 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc eagerfpu pni pclmulqdq ssse3 cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx rdrand hypervisor lahf_lm abm +bugs : +bogomips : 4589.37 +clflush size : 64 +cache_alignment : 64 +address sizes : 39 bits physical, 48 bits virtual +power management: + +processor : 1 +vendor_id : GenuineIntel +cpu family : 6 +model : 70 +model name : Intel(R) Core(TM) i7-4850HQ CPU @ 2.30GHz +stepping : 1 +cpu MHz : 2294.688 +cache size : 6144 KB +physical id : 0 +siblings : 4 +core id : 1 +cpu cores : 4 +apicid : 1 +initial apicid : 1 +fpu : yes +fpu_exception : yes +cpuid level : 13 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc eagerfpu pni pclmulqdq ssse3 cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx rdrand hypervisor lahf_lm abm +bugs : +bogomips : 4589.37 +clflush size : 64 +cache_alignment : 64 +address sizes : 39 bits physical, 48 bits virtual +power management: + +processor : 2 +vendor_id : GenuineIntel +cpu family : 6 +model : 70 +model name : Intel(R) Core(TM) i7-4850HQ CPU @ 2.30GHz +stepping : 1 +cpu MHz : 2294.688 +cache size : 6144 KB +physical id : 0 +siblings : 4 +core id : 2 +cpu cores : 4 +apicid : 2 +initial apicid : 2 +fpu : yes +fpu_exception : yes +cpuid level : 13 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc eagerfpu pni pclmulqdq ssse3 cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx rdrand hypervisor lahf_lm abm +bugs : +bogomips : 4589.37 +clflush size : 64 +cache_alignment : 64 +address sizes : 39 bits physical, 48 bits virtual +power management: + +processor : 3 +vendor_id : GenuineIntel +cpu family : 6 +model : 70 +model name : Intel(R) Core(TM) i7-4850HQ CPU @ 2.30GHz +stepping : 1 +cpu MHz : 2294.688 +cache size : 6144 KB +physical id : 0 +siblings : 4 +core id : 3 +cpu cores : 4 +apicid : 3 +initial apicid : 3 +fpu : yes +fpu_exception : yes +cpuid level : 13 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc eagerfpu pni pclmulqdq ssse3 cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx rdrand hypervisor lahf_lm abm +bugs : +bogomips : 4589.37 +clflush size : 64 +cache_alignment : 64 +address sizes : 39 bits physical, 48 bits virtual +power management: + diff --git a/src/cpu_feature/arm_linux/test-data/macos-virtualbox-linux-x86-4850HQ.auxv b/src/cpu_feature/arm_linux/test-data/macos-virtualbox-linux-x86-4850HQ.auxv new file mode 100644 index 0000000000..75abc02d17 Binary files /dev/null and b/src/cpu_feature/arm_linux/test-data/macos-virtualbox-linux-x86-4850HQ.auxv differ diff --git a/src/cpu_feature/arm_linux/test-data/macos-virtualbox-linux-x86-4850HQ.cpuinfo b/src/cpu_feature/arm_linux/test-data/macos-virtualbox-linux-x86-4850HQ.cpuinfo new file mode 100644 index 0000000000..6b19c61a27 --- /dev/null +++ b/src/cpu_feature/arm_linux/test-data/macos-virtualbox-linux-x86-4850HQ.cpuinfo @@ -0,0 +1,29 @@ +processor : 0 +vendor_id : GenuineIntel +cpu family : 6 +model : 70 +model name : Intel(R) Core(TM) i7-4850HQ CPU @ 2.30GHz +stepping : 1 +cpu MHz : 2294.688 +cache size : 6144 KB +physical id : 0 +siblings : 1 +core id : 0 +cpu cores : 1 +apicid : 0 +initial apicid : 0 +fdiv_bug : no +f00f_bug : no +coma_bug : no +fpu : yes +fpu_exception : yes +cpuid level : 13 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 syscall nx rdtscp lm constant_tsc xtopology nonstop_tsc eagerfpu pni pclmulqdq monitor ssse3 cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx rdrand hypervisor lahf_lm abm +bugs : +bogomips : 4589.37 +clflush size : 64 +cache_alignment : 64 +address sizes : 39 bits physical, 48 bits virtual +power management: + diff --git a/src/cpu_feature/arm_linux/test-data/macos-virtualbox-linux-x86-4850HQ.ld_show_auxv b/src/cpu_feature/arm_linux/test-data/macos-virtualbox-linux-x86-4850HQ.ld_show_auxv new file mode 100644 index 0000000000..89da6cfb00 --- /dev/null +++ b/src/cpu_feature/arm_linux/test-data/macos-virtualbox-linux-x86-4850HQ.ld_show_auxv @@ -0,0 +1,19 @@ +AT_SYSINFO: 0xb772bcfc +AT_SYSINFO_EHDR: 0xb772b000 +AT_HWCAP: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 +AT_PAGESZ: 4096 +AT_CLKTCK: 100 +AT_PHDR: 0x8048034 +AT_PHENT: 32 +AT_PHNUM: 9 +AT_BASE: 0xb772d000 +AT_FLAGS: 0x0 +AT_ENTRY: 0x8048e75 +AT_UID: 0 +AT_EUID: 0 +AT_GID: 0 +AT_EGID: 0 +AT_SECURE: 0 +AT_RANDOM: 0xbfd2c5eb +AT_EXECFN: /bin/true +AT_PLATFORM: i686 diff --git a/src/cpu_feature/cpu_feature.rs b/src/cpu_feature/cpu_feature.rs new file mode 100644 index 0000000000..a3b283b3b4 --- /dev/null +++ b/src/cpu_feature/cpu_feature.rs @@ -0,0 +1,3 @@ +#[cfg(any(test, all(any(target_arch = "arm", target_arch = "aarch64"), target_os="linux")))] +#[path = "arm_linux/arm_linux.rs"] +mod arm_linux; diff --git a/src/lib.rs b/src/lib.rs index c583b163d7..9d1b41a8e7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -161,6 +161,9 @@ pub mod signature; #[cfg(any(feature = "use_heap", test))] pub mod test; +#[path = "cpu_feature/cpu_feature.rs"] +mod cpu_feature; + mod private { /// Traits that are designed to only be implemented internally in *ring*. //