Skip to content

Commit

Permalink
[WIP] cpufeatures: aarch64 support (Linux and macOS/M4)
Browse files Browse the repository at this point in the history
Adds preliminary support for runtime feature detection on `aarch64`
targets, presently restricted to the following set of target features
which are present on both `aarch64-unknown-linux-gnu` and
`aarch64-apple-darwin` targets:

- `aes`: AES support
- `sha2`: SHA1 and SHA256 support
- `sha3`: SHA512 and SHA3 support
  • Loading branch information
tarcieri committed May 5, 2021
1 parent 23a677e commit 16e4ecf
Show file tree
Hide file tree
Showing 8 changed files with 420 additions and 127 deletions.
95 changes: 86 additions & 9 deletions .github/workflows/cpufeatures.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,95 @@ env:
RUSTFLAGS: "-Dwarnings"

jobs:
test:
# Linux tests
linux:
strategy:
matrix:
include:
# 32-bit Linux/x86
- target: i686-unknown-linux-gnu
rust: 1.40.0 # MSRV
deps: sudo apt update && sudo apt install gcc-multilib
- target: i686-unknown-linux-gnu
rust: stable
deps: sudo apt update && sudo apt install gcc-multilib

# 64-bit Linux/x86_64
- target: x86_64-unknown-linux-gnu
rust: 1.40.0 # MSRV
- target: x86_64-unknown-linux-gnu
rust: stable
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
profile: minimal
- run: ${{ matrix.deps }}
- run: cargo test --target ${{ matrix.target }} --release

# macOS tests
macos:
strategy:
matrix:
rust:
toolchain:
- 1.40.0 # MSRV
- stable
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.toolchain }}
target: x86_64-apple-darwin
override: true
- run: cargo test --release

# Windows tests
windows:
strategy:
matrix:
include:
# 64-bit Windows (GNU)
# TODO(tarcieri): try re-enabling this when we bump MSRV
#- target: x86_64-pc-windows-gnu
# toolchain: 1.40.0 # MSRV
- target: x86_64-pc-windows-gnu
toolchain: stable
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.toolchain }}
target: ${{ matrix.target }}
override: true
- run: cargo test --target ${{ matrix.target }} --release

# Cross-compiled tests
cross:
strategy:
matrix:
include:
# ARM64
# TODO(tarcieri): try re-enabling this when we bump MSRV
#- target: aarch64-unknown-linux-gnu
# rust: 1.40.0 # MSRV
- target: aarch64-unknown-linux-gnu
rust: stable
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
override: true
- run: cargo test --release
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
profile: minimal
- run: cargo install cross
- run: cross test --target ${{ matrix.target }} --release
9 changes: 9 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 10 additions & 1 deletion cpufeatures/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
[package]
name = "cpufeatures"
version = "0.1.0" # Also update html_root_url in lib.rs when bumping this
description = """
Lightweight and efficient no-std compatible alternative to the
is_x86_feature_detected! macro
"""
authors = ["RustCrypto Developers"]
license = "MIT OR Apache-2.0"
description = "Lightweight and efficient no-std compatible alternative to the is_x86_feature_detected macro"
documentation = "https://docs.rs/cpufeatures"
repository = "https://github.com/RustCrypto/utils"
keywords = ["cpuid", "target-feature"]
categories = ["no-std"]
edition = "2018"
readme = "README.md"

[target.aarch64-apple-darwin.dependencies]
libc = "0.2"

[target.'cfg(all(target_arch = "aarch64", target_os = "linux"))'.dependencies]
libc = "0.2"
138 changes: 138 additions & 0 deletions cpufeatures/src/aarch64.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/// ARM64 CPU feature detection support.
///
/// Unfortunately ARM instructions to detect CPU features cannot be called from
/// unprivileged userspace code, so this implementation relies on OS-specific
/// APIs for feature detection.

/// Create module with CPU feature detection code.
#[macro_export]
macro_rules! new {
($mod_name:ident, $($tf:tt),+ $(,)? ) => {
mod $mod_name {
use core::sync::atomic::{AtomicU8, Ordering::Relaxed};

const UNINIT: u8 = u8::max_value();
static STORAGE: AtomicU8 = AtomicU8::new(UNINIT);

/// Initialization token
#[derive(Copy, Clone, Debug)]
pub struct InitToken(());

impl InitToken {
/// Get initialized value
#[inline(always)]
pub fn get(&self) -> bool {
#[cfg(not(all($(target_feature=$tf, )*)))]
let res = STORAGE.load(Relaxed) == 1;
#[cfg(all($(target_feature=$tf, )*))]
let res = true;
res
}
}

/// Initialize underlying storage if needed and get
/// stored value and initialization token.
#[inline]
pub fn init_get() -> (InitToken, bool) {
#[cfg(not(all($(target_feature=$tf, )*)))]
let res = {
// Relaxed ordering is fine, as we only have a single atomic variable.
let val = STORAGE.load(Relaxed);
if val == UNINIT {
#[cfg(target_os = "linux")]
let res = {
let hwcaps = unsafe { libc::getauxval(libc::AT_HWCAP) };
$(cpufeatures::check!(hwcaps, $tf) & )+ true
}

#[cfg(target_os = "macos")]
let res = $(cpufeatures::check!($tf) & )+ true;

STORAGE.store(res as u8, Relaxed);
res
} else {
val == 1
}
};
#[cfg(all($(target_feature=$tf, )*))]
let res = true;

(InitToken(()), res)
}

/// Initialize underlying storage if needed and get
/// initialization token.
#[inline]
pub fn init() -> InitToken {
init_get().0
}

/// Initialize underlying storage if needed and get
/// stored value.
#[inline]
pub fn get() -> bool {
init_get().1
}
}
};
}

#[cfg(target_os = "linux")]
macro_rules! expand_check_macro {
($(($name:tt, $hwcap:expr)),* $(,)?) => {
#[macro_export]
#[doc(hidden)]
macro_rules! check {
$(
($hwcaps:expr, $name) => { (($hwcaps & libc::$hwcap) != 0) };
)*
}
};
}

#[cfg(target_os = "linux")]
expand_check_macro! {
("aes", HWCAP_AES), // Enable AES support.
("sha2", HWCAP_SHA2), // Enable SHA1 and SHA256 support.
("sha3", HWCAP_SHA3), // Enable SHA512 and SHA3 support.
}

// TODO(tarcieri): extract this into a function?
#[cfg(target_os = "macos")]
#[macro_export]
#[doc(hidden)]
macro_rules! sysctlbyname {
// WARNING: `$name` MUST be a byte slice terminated by `\0`
($name:expr) => {{
let mut value: u32 = 0;
let mut size = core::mem::size_of::<u32>();

let rc = unsafe {
libc::sysctlbyname(
$name.as_ptr() as *const i8,
&mut value as *mut _ as *mut libc::c_void,
&mut size,
core::ptr::null_mut(),
0,
)
};

assert_eq!(rc, 0, "sysctlbyname returned error code: {}", rc);
value != 0
}};
}

#[cfg(target_os = "macos")]
#[macro_export]
#[doc(hidden)]
macro_rules! check {
("aes") => {
true
};
("sha2") => {
true
};
("sha3") => {
cpufeatures::sysctlbyname!(b"hw.optional.armv8_2_sha3\0")
};
}
Loading

0 comments on commit 16e4ecf

Please sign in to comment.