From f775bf393197c52b616ebd0b6a7dcfdf84f5c268 Mon Sep 17 00:00:00 2001 From: Luca Barbato Date: Mon, 11 Dec 2017 18:14:06 +0100 Subject: [PATCH] Extract the cpu capabilities from the auxiliary vector Check for neon/asimd and pmull for arm and aarch64. --- library/stdarch/src/runtime/aarch64.rs | 19 +++++ library/stdarch/src/runtime/arm.rs | 19 +++++ library/stdarch/src/runtime/linux/auxvec.rs | 89 +++++++++++++++++++++ library/stdarch/src/runtime/linux/mod.rs | 10 ++- 4 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 library/stdarch/src/runtime/linux/auxvec.rs diff --git a/library/stdarch/src/runtime/aarch64.rs b/library/stdarch/src/runtime/aarch64.rs index 4c546d9380aa..273c314d21d7 100644 --- a/library/stdarch/src/runtime/aarch64.rs +++ b/library/stdarch/src/runtime/aarch64.rs @@ -45,6 +45,25 @@ pub fn detect_features(mut x: T) -> usize { value } +/// Probe the ELF Auxiliary vector for hardware capabilities +/// +/// The values are part of the platform-specific [asm/hwcap.h][hwcap] +/// +/// [hwcap]: https://github.com/torvalds/linux/blob/master/arch/arm64/include/uapi/asm/hwcap.h +impl linux::FeatureQuery for linux::AuxVec { + fn has_feature(&mut self, x: &__Feature) -> bool { + use self::__Feature::*; + if let Some(caps) = self.lookup(linux::AT::HWCAP) { + match *x { + asimd => caps & (1 << 1) != 0, + pmull => caps & (1 << 4) != 0, + } + } else { + false + } + } +} + impl linux::FeatureQuery for linux::CpuInfo { fn has_feature(&mut self, x: &__Feature) -> bool { use self::__Feature::*; diff --git a/library/stdarch/src/runtime/arm.rs b/library/stdarch/src/runtime/arm.rs index 997eac7527f4..f9a71a044f2b 100644 --- a/library/stdarch/src/runtime/arm.rs +++ b/library/stdarch/src/runtime/arm.rs @@ -42,6 +42,25 @@ pub fn detect_features(mut x: T) -> usize { value } +/// Probe the ELF Auxiliary vector for hardware capabilities +/// +/// The values are part of the platform-specific [asm/hwcap.h][hwcap] +/// +/// [hwcap]: https://github.com/torvalds/linux/blob/master/arch/arm64/include/uapi/asm/hwcap.h +impl linux::FeatureQuery for linux::AuxVec { + fn has_feature(&mut self, x: &__Feature) -> bool { + use self::__Feature::*; + match *x { + neon => self.lookup(linux::AT::HWCAP) + .map(|caps| caps & (1 << 12) != 0) + .unwrap_or(false), + pmull => self.lookup(linux::AT::HWCAP2) + .map(|caps| caps & (1 << 1) != 0) + .unwrap_or(false), + } + } +} + /// Is the CPU known to have a broken NEON unit? /// /// See https://crbug.com/341598. diff --git a/library/stdarch/src/runtime/linux/auxvec.rs b/library/stdarch/src/runtime/linux/auxvec.rs new file mode 100644 index 000000000000..657d74e25d7c --- /dev/null +++ b/library/stdarch/src/runtime/linux/auxvec.rs @@ -0,0 +1,89 @@ +//! Reads /proc/self/auxv on Linux systems + +use std::prelude::v1::*; +use std::slice; +use std::mem; + +/// Simple abstraction for the ELF Auxiliary Vector +/// +/// the elf.h provide the layout of the single entry as auxv_t. +/// The desugared version is a usize tag followed by a union with +/// the same storage size. +/// +/// Cache only the HWCAP and HWCAP2 entries. +#[derive(Debug)] +pub struct AuxVec { + hwcap: Option, + hwcap2: Option, +} + +#[derive(Clone, Debug, PartialEq)] +#[allow(dead_code)] +/// ELF Auxiliary vector entry types +/// +/// The entry types are specified in [linux/auxvec.h][auxvec_h]. +/// +/// [auxvec_h]: https://github.com/torvalds/linux/blob/master/include/uapi/linux/auxvec.h +pub enum AT { + /// CPU Hardware capabilities, it is a bitfield. + HWCAP = 16, + /// CPU Hardware capabilities, additional bitfield. + HWCAP2 = 26, +} + +impl AuxVec { + /// Reads the ELF Auxiliary Vector + /// + /// Try to read `/proc/self/auxv`. + // TODO: Make use of getauxval once it is available in a + // reliable way. + pub fn new() -> Result { + use std::io::Read; + let mut file = ::std::fs::File::open("/proc/self/auxv")?; + let mut buf = [0usize; 64]; + let mut raw = unsafe { + slice::from_raw_parts_mut( + buf.as_mut_ptr() as *mut u8, + buf.len() * mem::size_of::(), + ) + }; + + let _ = file.read(&mut raw)?; + + mem::forget(raw); + + let mut auxv = AuxVec { hwcap: None, hwcap2: None }; + + for el in buf.chunks(2) { + if el[0] == AT::HWCAP as usize { + auxv.hwcap = Some(el[1]); + } + if el[0] == AT::HWCAP2 as usize { + auxv.hwcap2 = Some(el[1]); + } + } + + Ok(auxv) + } + + /// Returns the value for the AT key + pub fn lookup(&self, key: AT) -> Option { + match key { + AT::HWCAP => self.hwcap, + AT::HWCAP2 => self.hwcap2, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[cfg(target_os = "linux")] + #[test] + fn test_auxvec_linux() { + let auxvec = AuxVec::new().unwrap(); + println!("{:?}", auxvec.lookup(AT::HWCAP)); + println!("{:?}", auxvec); + } +} diff --git a/library/stdarch/src/runtime/linux/mod.rs b/library/stdarch/src/runtime/linux/mod.rs index 6625152bafbe..0bd7a7940da0 100644 --- a/library/stdarch/src/runtime/linux/mod.rs +++ b/library/stdarch/src/runtime/linux/mod.rs @@ -2,6 +2,9 @@ mod cpuinfo; pub use self::cpuinfo::CpuInfo; +mod auxvec; +pub use self::auxvec::*; + use super::__Feature; pub trait FeatureQuery { @@ -19,9 +22,12 @@ fn detect_features_impl(x: T) -> usize { } } -/// Detects ARM features: +/// Detects CPU features: pub fn detect_features() -> usize { - // FIXME: use libc::getauxval, and if that fails /proc/auxv + // Try to read the ELF Auxiliary Vector + if let Ok(v) = auxvec::AuxVec::new() { + return detect_features_impl(v); + } // Try to read /proc/cpuinfo if let Ok(v) = cpuinfo::CpuInfo::new() { return detect_features_impl(v);