Skip to content

Commit

Permalink
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 6, 2021
1 parent 23a677e commit cfbcd7e
Show file tree
Hide file tree
Showing 8 changed files with 371 additions and 82 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"
112 changes: 112 additions & 0 deletions cpufeatures/src/aarch64.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
//! 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.

#[macro_export]
#[doc(hidden)]
macro_rules! __unless_target_features {
($($tf:tt),+ => $body:expr ) => {
{
#[cfg(not(all($(target_feature=$tf,)*)))]
$body

#[cfg(all($(target_feature=$tf,)*))]
true
}
};
}

#[cfg(target_os = "linux")]
#[macro_export]
#[doc(hidden)]
macro_rules! __detect_target_features {
($($tf:tt),+) => {{
let hwcaps = unsafe { libc::getauxval(libc::AT_HWCAP) };
$($crate::check!(hwcaps, $tf) & )+ true
}};
}

#[cfg(target_os = "macos")]
#[macro_export]
#[doc(hidden)]
macro_rules! __detect_target_features {
($($tf:tt),+) => {{
$($crate::check!($tf) & )+ true
}};
}

/// Linux `expand_check_macro`
#[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) };
)*
}
};
}

/// Linux `expand_check_macro`
#[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.
}

/// macOS `check!` macro.
///
/// NOTE: several of these instructions (e.g. `aes`, `sha2`) can be assumed to
/// be present on all Apple ARM64 hardware.
///
/// Newer CPU instructions now have nodes within sysctl's `hw.optional`
/// namespace, however the ones that do not can safely be assumed to be
/// present on all Apple ARM64 devices, now and for the foreseeable future.
///
/// See discussion on this issue for more information:
/// <https://github.com/RustCrypto/utils/issues/378>
#[cfg(target_os = "macos")]
#[macro_export]
#[doc(hidden)]
macro_rules! check {
("aes") => {
true
};
("sha2") => {
true
};
("sha3") => {
unsafe { $crate::aarch64::sysctlbyname(b"hw.optional.armv8_2_sha3\0") }
};
}

/// macOS helper function for calling `sysctlbyname`.
#[cfg(target_os = "macos")]
pub unsafe fn sysctlbyname(name: &[u8]) -> bool {
assert_eq!(
name.last().cloned(),
Some(0),
"name is not NUL terminated: {:?}",
name
);

let mut value: u32 = 0;
let mut size = core::mem::size_of::<u32>();

let rc = 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!(size, 4, "unexpected sysctlbyname(3) result size");
assert_eq!(rc, 0, "sysctlbyname returned error code: {}", rc);
value != 0
}
Loading

0 comments on commit cfbcd7e

Please sign in to comment.