Skip to content

Commit 32733f4

Browse files
authored
cpufeatures: aarch64 support (Linux and macOS/M4) (#393)
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
1 parent e887192 commit 32733f4

File tree

8 files changed

+371
-82
lines changed

8 files changed

+371
-82
lines changed

.github/workflows/cpufeatures.yml

+86-9
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,95 @@ env:
1717
RUSTFLAGS: "-Dwarnings"
1818

1919
jobs:
20-
test:
20+
# Linux tests
21+
linux:
22+
strategy:
23+
matrix:
24+
include:
25+
# 32-bit Linux/x86
26+
- target: i686-unknown-linux-gnu
27+
rust: 1.40.0 # MSRV
28+
deps: sudo apt update && sudo apt install gcc-multilib
29+
- target: i686-unknown-linux-gnu
30+
rust: stable
31+
deps: sudo apt update && sudo apt install gcc-multilib
32+
33+
# 64-bit Linux/x86_64
34+
- target: x86_64-unknown-linux-gnu
35+
rust: 1.40.0 # MSRV
36+
- target: x86_64-unknown-linux-gnu
37+
rust: stable
2138
runs-on: ubuntu-latest
39+
steps:
40+
- uses: actions/checkout@v2
41+
- uses: actions-rs/toolchain@v1
42+
with:
43+
toolchain: ${{ matrix.rust }}
44+
target: ${{ matrix.target }}
45+
override: true
46+
profile: minimal
47+
- run: ${{ matrix.deps }}
48+
- run: cargo test --target ${{ matrix.target }} --release
49+
50+
# macOS tests
51+
macos:
2252
strategy:
2353
matrix:
24-
rust:
54+
toolchain:
2555
- 1.40.0 # MSRV
2656
- stable
57+
runs-on: macos-latest
58+
steps:
59+
- uses: actions/checkout@v2
60+
- uses: actions-rs/toolchain@v1
61+
with:
62+
profile: minimal
63+
toolchain: ${{ matrix.toolchain }}
64+
target: x86_64-apple-darwin
65+
override: true
66+
- run: cargo test --release
67+
68+
# Windows tests
69+
windows:
70+
strategy:
71+
matrix:
72+
include:
73+
# 64-bit Windows (GNU)
74+
# TODO(tarcieri): try re-enabling this when we bump MSRV
75+
#- target: x86_64-pc-windows-gnu
76+
# toolchain: 1.40.0 # MSRV
77+
- target: x86_64-pc-windows-gnu
78+
toolchain: stable
79+
runs-on: windows-latest
80+
steps:
81+
- uses: actions/checkout@v2
82+
- uses: actions-rs/toolchain@v1
83+
with:
84+
profile: minimal
85+
toolchain: ${{ matrix.toolchain }}
86+
target: ${{ matrix.target }}
87+
override: true
88+
- run: cargo test --target ${{ matrix.target }} --release
89+
90+
# Cross-compiled tests
91+
cross:
92+
strategy:
93+
matrix:
94+
include:
95+
# ARM64
96+
# TODO(tarcieri): try re-enabling this when we bump MSRV
97+
#- target: aarch64-unknown-linux-gnu
98+
# rust: 1.40.0 # MSRV
99+
- target: aarch64-unknown-linux-gnu
100+
rust: stable
101+
runs-on: ubuntu-latest
27102
steps:
28-
- uses: actions/checkout@v1
29-
- uses: actions-rs/toolchain@v1
30-
with:
31-
profile: minimal
32-
toolchain: ${{ matrix.rust }}
33-
override: true
34-
- run: cargo test --release
103+
- uses: actions/checkout@v2
104+
- uses: actions-rs/toolchain@v1
105+
with:
106+
toolchain: ${{ matrix.rust }}
107+
target: ${{ matrix.target }}
108+
override: true
109+
profile: minimal
110+
- run: cargo install cross
111+
- run: cross test --target ${{ matrix.target }} --release

Cargo.lock

+9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cpufeatures/Cargo.toml

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
11
[package]
22
name = "cpufeatures"
33
version = "0.1.0" # Also update html_root_url in lib.rs when bumping this
4+
description = """
5+
Lightweight and efficient no-std compatible alternative to the
6+
is_x86_feature_detected! macro
7+
"""
48
authors = ["RustCrypto Developers"]
59
license = "MIT OR Apache-2.0"
6-
description = "Lightweight and efficient no-std compatible alternative to the is_x86_feature_detected macro"
710
documentation = "https://docs.rs/cpufeatures"
811
repository = "https://github.com/RustCrypto/utils"
912
keywords = ["cpuid", "target-feature"]
1013
categories = ["no-std"]
1114
edition = "2018"
1215
readme = "README.md"
16+
17+
[target.aarch64-apple-darwin.dependencies]
18+
libc = "0.2"
19+
20+
[target.'cfg(all(target_arch = "aarch64", target_os = "linux"))'.dependencies]
21+
libc = "0.2"

cpufeatures/src/aarch64.rs

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
//! ARM64 CPU feature detection support.
2+
//!
3+
//! Unfortunately ARM instructions to detect CPU features cannot be called from
4+
//! unprivileged userspace code, so this implementation relies on OS-specific
5+
//! APIs for feature detection.
6+
7+
#[macro_export]
8+
#[doc(hidden)]
9+
macro_rules! __unless_target_features {
10+
($($tf:tt),+ => $body:expr ) => {
11+
{
12+
#[cfg(not(all($(target_feature=$tf,)*)))]
13+
$body
14+
15+
#[cfg(all($(target_feature=$tf,)*))]
16+
true
17+
}
18+
};
19+
}
20+
21+
#[cfg(target_os = "linux")]
22+
#[macro_export]
23+
#[doc(hidden)]
24+
macro_rules! __detect_target_features {
25+
($($tf:tt),+) => {{
26+
let hwcaps = unsafe { libc::getauxval(libc::AT_HWCAP) };
27+
$($crate::check!(hwcaps, $tf) & )+ true
28+
}};
29+
}
30+
31+
#[cfg(target_os = "macos")]
32+
#[macro_export]
33+
#[doc(hidden)]
34+
macro_rules! __detect_target_features {
35+
($($tf:tt),+) => {{
36+
$($crate::check!($tf) & )+ true
37+
}};
38+
}
39+
40+
/// Linux `expand_check_macro`
41+
#[cfg(target_os = "linux")]
42+
macro_rules! __expand_check_macro {
43+
($(($name:tt, $hwcap:expr)),* $(,)?) => {
44+
#[macro_export]
45+
#[doc(hidden)]
46+
macro_rules! check {
47+
$(
48+
($hwcaps:expr, $name) => { (($hwcaps & libc::$hwcap) != 0) };
49+
)*
50+
}
51+
};
52+
}
53+
54+
/// Linux `expand_check_macro`
55+
#[cfg(target_os = "linux")]
56+
__expand_check_macro! {
57+
("aes", HWCAP_AES), // Enable AES support.
58+
("sha2", HWCAP_SHA2), // Enable SHA1 and SHA256 support.
59+
("sha3", HWCAP_SHA3), // Enable SHA512 and SHA3 support.
60+
}
61+
62+
/// macOS `check!` macro.
63+
///
64+
/// NOTE: several of these instructions (e.g. `aes`, `sha2`) can be assumed to
65+
/// be present on all Apple ARM64 hardware.
66+
///
67+
/// Newer CPU instructions now have nodes within sysctl's `hw.optional`
68+
/// namespace, however the ones that do not can safely be assumed to be
69+
/// present on all Apple ARM64 devices, now and for the foreseeable future.
70+
///
71+
/// See discussion on this issue for more information:
72+
/// <https://github.com/RustCrypto/utils/issues/378>
73+
#[cfg(target_os = "macos")]
74+
#[macro_export]
75+
#[doc(hidden)]
76+
macro_rules! check {
77+
("aes") => {
78+
true
79+
};
80+
("sha2") => {
81+
true
82+
};
83+
("sha3") => {
84+
unsafe { $crate::aarch64::sysctlbyname(b"hw.optional.armv8_2_sha3\0") }
85+
};
86+
}
87+
88+
/// macOS helper function for calling `sysctlbyname`.
89+
#[cfg(target_os = "macos")]
90+
pub unsafe fn sysctlbyname(name: &[u8]) -> bool {
91+
assert_eq!(
92+
name.last().cloned(),
93+
Some(0),
94+
"name is not NUL terminated: {:?}",
95+
name
96+
);
97+
98+
let mut value: u32 = 0;
99+
let mut size = core::mem::size_of::<u32>();
100+
101+
let rc = libc::sysctlbyname(
102+
name.as_ptr() as *const i8,
103+
&mut value as *mut _ as *mut libc::c_void,
104+
&mut size,
105+
core::ptr::null_mut(),
106+
0,
107+
);
108+
109+
assert_eq!(size, 4, "unexpected sysctlbyname(3) result size");
110+
assert_eq!(rc, 0, "sysctlbyname returned error code: {}", rc);
111+
value != 0
112+
}

0 commit comments

Comments
 (0)