Skip to content

Commit b680cf1

Browse files
committed
Add HWCAP2 support for AArch64.
This doesn't extend the set of recognised features, but corrects some existing ones to refer to HWCAP2.
1 parent 00ea554 commit b680cf1

File tree

7 files changed

+174
-78
lines changed

7 files changed

+174
-78
lines changed

crates/std_detect/src/detect/os/linux/aarch64.rs

Lines changed: 153 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -23,58 +23,62 @@ pub(crate) fn detect_features() -> cache::Initializer {
2323
/// The names match those used for cpuinfo.
2424
///
2525
/// [hwcap]: https://github.com/torvalds/linux/blob/master/arch/arm64/include/uapi/asm/hwcap.h
26+
#[derive(Debug, Default, PartialEq)]
2627
struct AtHwcap {
27-
fp: bool, // 0
28-
asimd: bool, // 1
29-
// evtstrm: bool, // 2 No LLVM support
30-
aes: bool, // 3
31-
pmull: bool, // 4
32-
sha1: bool, // 5
33-
sha2: bool, // 6
34-
crc32: bool, // 7
35-
atomics: bool, // 8
36-
fphp: bool, // 9
37-
asimdhp: bool, // 10
38-
// cpuid: bool, // 11 No LLVM support
39-
asimdrdm: bool, // 12
40-
jscvt: bool, // 13
41-
fcma: bool, // 14
42-
lrcpc: bool, // 15
43-
dcpop: bool, // 16
44-
sha3: bool, // 17
45-
sm3: bool, // 18
46-
sm4: bool, // 19
47-
asimddp: bool, // 20
48-
sha512: bool, // 21
49-
sve: bool, // 22
50-
fhm: bool, // 23
51-
dit: bool, // 24
52-
uscat: bool, // 25
53-
ilrcpc: bool, // 26
54-
flagm: bool, // 27
55-
ssbs: bool, // 28
56-
sb: bool, // 29
57-
paca: bool, // 30
58-
pacg: bool, // 31
59-
dcpodp: bool, // 32
60-
sve2: bool, // 33
61-
sveaes: bool, // 34
62-
// svepmull: bool, // 35 No LLVM support
63-
svebitperm: bool, // 36
64-
svesha3: bool, // 37
65-
svesm4: bool, // 38
66-
// flagm2: bool, // 39 No LLVM support
67-
frint: bool, // 40
68-
// svei8mm: bool, // 41 See i8mm feature
69-
svef32mm: bool, // 42
70-
svef64mm: bool, // 43
71-
// svebf16: bool, // 44 See bf16 feature
72-
i8mm: bool, // 45
73-
bf16: bool, // 46
74-
// dgh: bool, // 47 No LLVM support
75-
rng: bool, // 48
76-
bti: bool, // 49
77-
mte: bool, // 50
28+
// AT_HWCAP
29+
fp: bool,
30+
asimd: bool,
31+
// evtstrm: No LLVM support.
32+
aes: bool,
33+
pmull: bool,
34+
sha1: bool,
35+
sha2: bool,
36+
crc32: bool,
37+
atomics: bool,
38+
fphp: bool,
39+
asimdhp: bool,
40+
// cpuid: No LLVM support.
41+
asimdrdm: bool,
42+
jscvt: bool,
43+
fcma: bool,
44+
lrcpc: bool,
45+
dcpop: bool,
46+
sha3: bool,
47+
sm3: bool,
48+
sm4: bool,
49+
asimddp: bool,
50+
sha512: bool,
51+
sve: bool,
52+
fhm: bool,
53+
dit: bool,
54+
uscat: bool,
55+
ilrcpc: bool,
56+
flagm: bool,
57+
ssbs: bool,
58+
sb: bool,
59+
paca: bool,
60+
pacg: bool,
61+
62+
// AT_HWCAP2
63+
dcpodp: bool,
64+
sve2: bool,
65+
sveaes: bool,
66+
// svepmull: No LLVM support.
67+
svebitperm: bool,
68+
svesha3: bool,
69+
svesm4: bool,
70+
// flagm2: No LLVM support.
71+
frint: bool,
72+
// svei8mm: See i8mm feature.
73+
svef32mm: bool,
74+
svef64mm: bool,
75+
// svebf16: See bf16 feature.
76+
i8mm: bool,
77+
bf16: bool,
78+
// dgh: No LLVM support.
79+
rng: bool,
80+
bti: bool,
81+
mte: bool,
7882
}
7983

8084
impl From<auxvec::AuxVec> for AtHwcap {
@@ -113,25 +117,25 @@ impl From<auxvec::AuxVec> for AtHwcap {
113117
sb: bit::test(auxv.hwcap, 29),
114118
paca: bit::test(auxv.hwcap, 30),
115119
pacg: bit::test(auxv.hwcap, 31),
116-
dcpodp: bit::test(auxv.hwcap, 32),
117-
sve2: bit::test(auxv.hwcap, 33),
118-
sveaes: bit::test(auxv.hwcap, 34),
119-
// svepmull: bit::test(auxv.hwcap, 35),
120-
svebitperm: bit::test(auxv.hwcap, 36),
121-
svesha3: bit::test(auxv.hwcap, 37),
122-
svesm4: bit::test(auxv.hwcap, 38),
123-
// flagm2: bit::test(auxv.hwcap, 39),
124-
frint: bit::test(auxv.hwcap, 40),
125-
// svei8mm: bit::test(auxv.hwcap, 41),
126-
svef32mm: bit::test(auxv.hwcap, 42),
127-
svef64mm: bit::test(auxv.hwcap, 43),
128-
// svebf16: bit::test(auxv.hwcap, 44),
129-
i8mm: bit::test(auxv.hwcap, 45),
130-
bf16: bit::test(auxv.hwcap, 46),
131-
// dgh: bit::test(auxv.hwcap, 47),
132-
rng: bit::test(auxv.hwcap, 48),
133-
bti: bit::test(auxv.hwcap, 49),
134-
mte: bit::test(auxv.hwcap, 50),
120+
dcpodp: bit::test(auxv.hwcap2, 0),
121+
sve2: bit::test(auxv.hwcap2, 1),
122+
sveaes: bit::test(auxv.hwcap2, 2),
123+
// svepmull: bit::test(auxv.hwcap2, 3),
124+
svebitperm: bit::test(auxv.hwcap2, 4),
125+
svesha3: bit::test(auxv.hwcap2, 5),
126+
svesm4: bit::test(auxv.hwcap2, 6),
127+
// flagm2: bit::test(auxv.hwcap2, 7),
128+
frint: bit::test(auxv.hwcap2, 8),
129+
// svei8mm: bit::test(auxv.hwcap2, 9),
130+
svef32mm: bit::test(auxv.hwcap2, 10),
131+
svef64mm: bit::test(auxv.hwcap2, 11),
132+
// svebf16: bit::test(auxv.hwcap2, 12),
133+
i8mm: bit::test(auxv.hwcap2, 13),
134+
bf16: bit::test(auxv.hwcap2, 14),
135+
// dgh: bit::test(auxv.hwcap2, 15),
136+
rng: bit::test(auxv.hwcap2, 16),
137+
bti: bit::test(auxv.hwcap2, 17),
138+
mte: bit::test(auxv.hwcap2, 18),
135139
}
136140
}
137141
}
@@ -288,3 +292,82 @@ impl AtHwcap {
288292
value
289293
}
290294
}
295+
296+
#[cfg(test)]
297+
mod tests {
298+
use super::auxvec::auxv_from_file;
299+
use super::*;
300+
301+
// The baseline hwcaps used in the (artificial) auxv test files.
302+
fn baseline_hwcaps() -> AtHwcap {
303+
AtHwcap {
304+
fp: true,
305+
asimd: true,
306+
aes: true,
307+
pmull: true,
308+
sha1: true,
309+
sha2: true,
310+
crc32: true,
311+
atomics: true,
312+
fphp: true,
313+
asimdhp: true,
314+
asimdrdm: true,
315+
lrcpc: true,
316+
dcpop: true,
317+
asimddp: true,
318+
ssbs: true,
319+
..AtHwcap::default()
320+
}
321+
}
322+
323+
#[test]
324+
fn linux_empty_hwcap2_aarch64() {
325+
let file = concat!(
326+
env!("CARGO_MANIFEST_DIR"),
327+
"/src/detect/test_data/linux-empty-hwcap2-aarch64.auxv"
328+
);
329+
println!("file: {}", file);
330+
let v = auxv_from_file(file).unwrap();
331+
println!("HWCAP : 0x{:0x}", v.hwcap);
332+
println!("HWCAP2: 0x{:0x}", v.hwcap2);
333+
assert_eq!(AtHwcap::from(v), baseline_hwcaps());
334+
}
335+
#[test]
336+
fn linux_no_hwcap2_aarch64() {
337+
let file = concat!(
338+
env!("CARGO_MANIFEST_DIR"),
339+
"/src/detect/test_data/linux-no-hwcap2-aarch64.auxv"
340+
);
341+
println!("file: {}", file);
342+
let v = auxv_from_file(file).unwrap();
343+
println!("HWCAP : 0x{:0x}", v.hwcap);
344+
println!("HWCAP2: 0x{:0x}", v.hwcap2);
345+
assert_eq!(AtHwcap::from(v), baseline_hwcaps());
346+
}
347+
#[test]
348+
fn linux_hwcap2_aarch64() {
349+
let file = concat!(
350+
env!("CARGO_MANIFEST_DIR"),
351+
"/src/detect/test_data/linux-hwcap2-aarch64.auxv"
352+
);
353+
println!("file: {}", file);
354+
let v = auxv_from_file(file).unwrap();
355+
println!("HWCAP : 0x{:0x}", v.hwcap);
356+
println!("HWCAP2: 0x{:0x}", v.hwcap2);
357+
assert_eq!(
358+
AtHwcap::from(v),
359+
AtHwcap {
360+
// Some other HWCAP bits.
361+
paca: true,
362+
pacg: true,
363+
// HWCAP2-only bits.
364+
dcpodp: true,
365+
frint: true,
366+
rng: true,
367+
bti: true,
368+
mte: true,
369+
..baseline_hwcaps()
370+
}
371+
);
372+
}
373+
}

crates/std_detect/src/detect/os/linux/auxvec.rs

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pub(crate) const AT_NULL: usize = 0;
77
pub(crate) const AT_HWCAP: usize = 16;
88
/// Key to access the CPU Hardware capabilities 2 bitfield.
99
#[cfg(any(
10+
target_arch = "aarch64",
1011
target_arch = "arm",
1112
target_arch = "powerpc",
1213
target_arch = "powerpc64"
@@ -21,6 +22,7 @@ pub(crate) const AT_HWCAP2: usize = 26;
2122
pub(crate) struct AuxVec {
2223
pub hwcap: usize,
2324
#[cfg(any(
25+
target_arch = "aarch64",
2426
target_arch = "arm",
2527
target_arch = "powerpc",
2628
target_arch = "powerpc64"
@@ -64,7 +66,6 @@ pub(crate) fn auxv() -> Result<AuxVec, ()> {
6466
if let Ok(hwcap) = getauxval(AT_HWCAP) {
6567
// Targets with only AT_HWCAP:
6668
#[cfg(any(
67-
target_arch = "aarch64",
6869
target_arch = "riscv32",
6970
target_arch = "riscv64",
7071
target_arch = "mips",
@@ -80,6 +81,7 @@ pub(crate) fn auxv() -> Result<AuxVec, ()> {
8081

8182
// Targets with AT_HWCAP and AT_HWCAP2:
8283
#[cfg(any(
84+
target_arch = "aarch64",
8385
target_arch = "arm",
8486
target_arch = "powerpc",
8587
target_arch = "powerpc64"
@@ -103,7 +105,6 @@ pub(crate) fn auxv() -> Result<AuxVec, ()> {
103105
{
104106
// Targets with only AT_HWCAP:
105107
#[cfg(any(
106-
target_arch = "aarch64",
107108
target_arch = "riscv32",
108109
target_arch = "riscv64",
109110
target_arch = "mips",
@@ -120,6 +121,7 @@ pub(crate) fn auxv() -> Result<AuxVec, ()> {
120121

121122
// Targets with AT_HWCAP and AT_HWCAP2:
122123
#[cfg(any(
124+
target_arch = "aarch64",
123125
target_arch = "arm",
124126
target_arch = "powerpc",
125127
target_arch = "powerpc64"
@@ -170,7 +172,7 @@ fn getauxval(key: usize) -> Result<usize, ()> {
170172
/// Tries to read the auxiliary vector from the `file`. If this fails, this
171173
/// function returns `Err`.
172174
#[cfg(feature = "std_detect_file_io")]
173-
fn auxv_from_file(file: &str) -> Result<AuxVec, ()> {
175+
pub(super) fn auxv_from_file(file: &str) -> Result<AuxVec, ()> {
174176
let file = super::read_file(file)?;
175177

176178
// See <https://github.com/torvalds/linux/blob/v3.19/include/uapi/linux/auxvec.h>.
@@ -193,7 +195,6 @@ fn auxv_from_file(file: &str) -> Result<AuxVec, ()> {
193195
fn auxv_from_buf(buf: &[usize; 64]) -> Result<AuxVec, ()> {
194196
// Targets with only AT_HWCAP:
195197
#[cfg(any(
196-
target_arch = "aarch64",
197198
target_arch = "riscv32",
198199
target_arch = "riscv64",
199200
target_arch = "mips",
@@ -210,6 +211,7 @@ fn auxv_from_buf(buf: &[usize; 64]) -> Result<AuxVec, ()> {
210211
}
211212
// Targets with AT_HWCAP and AT_HWCAP2:
212213
#[cfg(any(
214+
target_arch = "aarch64",
213215
target_arch = "arm",
214216
target_arch = "powerpc",
215217
target_arch = "powerpc64"
@@ -269,7 +271,6 @@ mod tests {
269271
// FIXME: on mips/mips64 getauxval returns 0, and /proc/self/auxv
270272
// does not always contain the AT_HWCAP key under qemu.
271273
#[cfg(any(
272-
target_arch = "aarch64",
273274
target_arch = "arm",
274275
target_arch = "powerpc",
275276
target_arch = "powerpc64"
@@ -284,6 +285,7 @@ mod tests {
284285

285286
// Targets with AT_HWCAP and AT_HWCAP2:
286287
#[cfg(any(
288+
target_arch = "aarch64",
287289
target_arch = "arm",
288290
target_arch = "powerpc",
289291
target_arch = "powerpc64"
@@ -328,11 +330,21 @@ mod tests {
328330
}
329331
} else if #[cfg(target_arch = "aarch64")] {
330332
#[test]
331-
fn linux_x64() {
332-
let file = concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/linux-x64-i7-6850k.auxv");
333+
fn linux_artificial_aarch64() {
334+
let file = concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/linux-artificial-aarch64.auxv");
333335
println!("file: {}", file);
334336
let v = auxv_from_file(file).unwrap();
335-
assert_eq!(v.hwcap, 3219913727);
337+
assert_eq!(v.hwcap, 0x0123456789abcdef);
338+
assert_eq!(v.hwcap2, 0x02468ace13579bdf);
339+
}
340+
#[test]
341+
fn linux_no_hwcap2_aarch64() {
342+
let file = concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/linux-no-hwcap2-aarch64.auxv");
343+
println!("file: {}", file);
344+
let v = auxv_from_file(file).unwrap();
345+
// An absent HWCAP2 is treated as zero, and does not prevent acceptance of HWCAP.
346+
assert_ne!(v.hwcap, 0);
347+
assert_eq!(v.hwcap2, 0);
336348
}
337349
}
338350
}
@@ -363,6 +375,7 @@ mod tests {
363375

364376
// Targets with AT_HWCAP and AT_HWCAP2:
365377
#[cfg(any(
378+
target_arch = "aarch64",
366379
target_arch = "arm",
367380
target_arch = "powerpc",
368381
target_arch = "powerpc64"
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)