Skip to content

Commit 7d8483b

Browse files
lu-zerognzlbg
authored andcommitted
Extract the cpu capabilities from the auxiliary vector
Check for neon/asimd and pmull for arm and aarch64.
1 parent 7d8709d commit 7d8483b

File tree

4 files changed

+135
-2
lines changed

4 files changed

+135
-2
lines changed

src/runtime/aarch64.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,25 @@ pub fn detect_features<T: linux::FeatureQuery>(mut x: T) -> usize {
4545
value
4646
}
4747

48+
/// Probe the ELF Auxiliary vector for hardware capabilities
49+
///
50+
/// The values are part of the platform-specific [asm/hwcap.h][hwcap]
51+
///
52+
/// [hwcap]: https://github.com/torvalds/linux/blob/master/arch/arm64/include/uapi/asm/hwcap.h
53+
impl linux::FeatureQuery for linux::AuxVec {
54+
fn has_feature(&mut self, x: &__Feature) -> bool {
55+
use self::__Feature::*;
56+
if let Some(caps) = self.lookup(linux::AT::HWCAP) {
57+
match *x {
58+
asimd => caps & (1 << 1) != 0,
59+
pmull => caps & (1 << 4) != 0,
60+
}
61+
} else {
62+
false
63+
}
64+
}
65+
}
66+
4867
impl linux::FeatureQuery for linux::CpuInfo {
4968
fn has_feature(&mut self, x: &__Feature) -> bool {
5069
use self::__Feature::*;

src/runtime/arm.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,25 @@ pub fn detect_features<T: linux::FeatureQuery>(mut x: T) -> usize {
4242
value
4343
}
4444

45+
/// Probe the ELF Auxiliary vector for hardware capabilities
46+
///
47+
/// The values are part of the platform-specific [asm/hwcap.h][hwcap]
48+
///
49+
/// [hwcap]: https://github.com/torvalds/linux/blob/master/arch/arm64/include/uapi/asm/hwcap.h
50+
impl linux::FeatureQuery for linux::AuxVec {
51+
fn has_feature(&mut self, x: &__Feature) -> bool {
52+
use self::__Feature::*;
53+
match *x {
54+
neon => self.lookup(linux::AT::HWCAP)
55+
.map(|caps| caps & (1 << 12) != 0)
56+
.unwrap_or(false),
57+
pmull => self.lookup(linux::AT::HWCAP2)
58+
.map(|caps| caps & (1 << 1) != 0)
59+
.unwrap_or(false),
60+
}
61+
}
62+
}
63+
4564
/// Is the CPU known to have a broken NEON unit?
4665
///
4766
/// See https://crbug.com/341598.

src/runtime/linux/auxvec.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
//! Reads /proc/self/auxv on Linux systems
2+
3+
use std::prelude::v1::*;
4+
use std::slice;
5+
use std::mem;
6+
7+
/// Simple abstraction for the ELF Auxiliary Vector
8+
///
9+
/// the elf.h provide the layout of the single entry as auxv_t.
10+
/// The desugared version is a usize tag followed by a union with
11+
/// the same storage size.
12+
///
13+
/// Cache only the HWCAP and HWCAP2 entries.
14+
#[derive(Debug)]
15+
pub struct AuxVec {
16+
hwcap: Option<usize>,
17+
hwcap2: Option<usize>,
18+
}
19+
20+
#[derive(Clone, Debug, PartialEq)]
21+
#[allow(dead_code)]
22+
/// ELF Auxiliary vector entry types
23+
///
24+
/// The entry types are specified in [linux/auxvec.h][auxvec_h].
25+
///
26+
/// [auxvec_h]: https://github.com/torvalds/linux/blob/master/include/uapi/linux/auxvec.h
27+
pub enum AT {
28+
/// CPU Hardware capabilities, it is a bitfield.
29+
HWCAP = 16,
30+
/// CPU Hardware capabilities, additional bitfield.
31+
HWCAP2 = 26,
32+
}
33+
34+
impl AuxVec {
35+
/// Reads the ELF Auxiliary Vector
36+
///
37+
/// Try to read `/proc/self/auxv`.
38+
// TODO: Make use of getauxval once it is available in a
39+
// reliable way.
40+
pub fn new() -> Result<Self, ::std::io::Error> {
41+
use std::io::Read;
42+
let mut file = ::std::fs::File::open("/proc/self/auxv")?;
43+
let mut buf = [0usize; 64];
44+
let mut raw = unsafe {
45+
slice::from_raw_parts_mut(
46+
buf.as_mut_ptr() as *mut u8,
47+
buf.len() * mem::size_of::<usize>(),
48+
)
49+
};
50+
51+
let _ = file.read(&mut raw)?;
52+
53+
mem::forget(raw);
54+
55+
let mut auxv = AuxVec { hwcap: None, hwcap2: None };
56+
57+
for el in buf.chunks(2) {
58+
if el[0] == AT::HWCAP as usize {
59+
auxv.hwcap = Some(el[1]);
60+
}
61+
if el[0] == AT::HWCAP2 as usize {
62+
auxv.hwcap2 = Some(el[1]);
63+
}
64+
}
65+
66+
Ok(auxv)
67+
}
68+
69+
/// Returns the value for the AT key
70+
pub fn lookup(&self, key: AT) -> Option<usize> {
71+
match key {
72+
AT::HWCAP => self.hwcap,
73+
AT::HWCAP2 => self.hwcap2,
74+
}
75+
}
76+
}
77+
78+
#[cfg(test)]
79+
mod tests {
80+
use super::*;
81+
82+
#[cfg(target_os = "linux")]
83+
#[test]
84+
fn test_auxvec_linux() {
85+
let auxvec = AuxVec::new().unwrap();
86+
println!("{:?}", auxvec.lookup(AT::HWCAP));
87+
println!("{:?}", auxvec);
88+
}
89+
}

src/runtime/linux/mod.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
mod cpuinfo;
33
pub use self::cpuinfo::CpuInfo;
44

5+
mod auxvec;
6+
pub use self::auxvec::*;
7+
58
use super::__Feature;
69

710
pub trait FeatureQuery {
@@ -19,9 +22,12 @@ fn detect_features_impl<T: FeatureQuery>(x: T) -> usize {
1922
}
2023
}
2124

22-
/// Detects ARM features:
25+
/// Detects CPU features:
2326
pub fn detect_features() -> usize {
24-
// FIXME: use libc::getauxval, and if that fails /proc/auxv
27+
// Try to read the ELF Auxiliary Vector
28+
if let Ok(v) = auxvec::AuxVec::new() {
29+
return detect_features_impl(v);
30+
}
2531
// Try to read /proc/cpuinfo
2632
if let Ok(v) = cpuinfo::CpuInfo::new() {
2733
return detect_features_impl(v);

0 commit comments

Comments
 (0)