Skip to content

Commit 1474c1d

Browse files
committed
std_detect: Check hwcap to detect RISC-V V extension
It reflects Vector enablement status, unlike hwprobe.
1 parent eeb0002 commit 1474c1d

File tree

1 file changed

+137
-144
lines changed
  • crates/std_detect/src/detect/os/linux

1 file changed

+137
-144
lines changed

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

+137-144
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@ struct riscv_hwprobe {
2121
#[allow(non_upper_case_globals)]
2222
const __NR_riscv_hwprobe: libc::c_long = 258;
2323

24-
const RISCV_HWPROBE_KEY_BASE_BEHAVIOR: i64 = 3;
25-
const RISCV_HWPROBE_BASE_BEHAVIOR_IMA: u64 = 1 << 0;
24+
// const RISCV_HWPROBE_KEY_BASE_BEHAVIOR: i64 = 3;
25+
// const RISCV_HWPROBE_BASE_BEHAVIOR_IMA: u64 = 1 << 0;
2626

2727
const RISCV_HWPROBE_KEY_IMA_EXT_0: i64 = 4;
28-
const RISCV_HWPROBE_IMA_FD: u64 = 1 << 0;
29-
const RISCV_HWPROBE_IMA_C: u64 = 1 << 1;
30-
const RISCV_HWPROBE_IMA_V: u64 = 1 << 2;
28+
// const RISCV_HWPROBE_IMA_FD: u64 = 1 << 0;
29+
// const RISCV_HWPROBE_IMA_C: u64 = 1 << 1;
30+
// const RISCV_HWPROBE_IMA_V: u64 = 1 << 2;
3131
const RISCV_HWPROBE_EXT_ZBA: u64 = 1 << 3;
3232
const RISCV_HWPROBE_EXT_ZBB: u64 = 1 << 4;
3333
const RISCV_HWPROBE_EXT_ZBS: u64 = 1 << 5;
@@ -62,11 +62,11 @@ const RISCV_HWPROBE_EXT_ZTSO: u64 = 1 << 33;
6262
const RISCV_HWPROBE_EXT_ZACAS: u64 = 1 << 34;
6363
// const RISCV_HWPROBE_EXT_ZICOND: u64 = 1 << 35;
6464
const RISCV_HWPROBE_EXT_ZIHINTPAUSE: u64 = 1 << 36;
65-
const RISCV_HWPROBE_EXT_ZVE32X: u64 = 1 << 37;
66-
const RISCV_HWPROBE_EXT_ZVE32F: u64 = 1 << 38;
67-
const RISCV_HWPROBE_EXT_ZVE64X: u64 = 1 << 39;
68-
const RISCV_HWPROBE_EXT_ZVE64F: u64 = 1 << 40;
69-
const RISCV_HWPROBE_EXT_ZVE64D: u64 = 1 << 41;
65+
// const RISCV_HWPROBE_EXT_ZVE32X: u64 = 1 << 37;
66+
// const RISCV_HWPROBE_EXT_ZVE32F: u64 = 1 << 38;
67+
// const RISCV_HWPROBE_EXT_ZVE64X: u64 = 1 << 39;
68+
// const RISCV_HWPROBE_EXT_ZVE64F: u64 = 1 << 40;
69+
// const RISCV_HWPROBE_EXT_ZVE64D: u64 = 1 << 41;
7070
// const RISCV_HWPROBE_EXT_ZIMOP: u64 = 1 << 42;
7171
// const RISCV_HWPROBE_EXT_ZCA: u64 = 1 << 43;
7272
// const RISCV_HWPROBE_EXT_ZCB: u64 = 1 << 44;
@@ -113,11 +113,79 @@ fn _riscv_hwprobe(out: &mut [riscv_hwprobe]) -> bool {
113113
pub(crate) fn detect_features() -> cache::Initializer {
114114
let mut value = cache::Initializer::default();
115115

116+
let enable_feature = |value: &mut cache::Initializer, feature, enable| {
117+
if enable {
118+
value.set(feature as u32);
119+
}
120+
};
121+
let enable_features = |value: &mut cache::Initializer, feature_slice: &[Feature], enable| {
122+
if enable {
123+
for feature in feature_slice {
124+
value.set(*feature as u32);
125+
}
126+
}
127+
};
128+
129+
// The values are part of the platform-specific [asm/hwcap.h][hwcap]
130+
//
131+
// [hwcap]: https://github.com/torvalds/linux/blob/master/arch/riscv/include/asm/hwcap.h
132+
let auxv = auxvec::auxv().expect("read auxvec"); // should not fail on RISC-V platform
133+
#[allow(clippy::eq_op)]
134+
enable_feature(
135+
&mut value,
136+
Feature::a,
137+
bit::test(auxv.hwcap, (b'a' - b'a').into()),
138+
);
139+
enable_feature(
140+
&mut value,
141+
Feature::c,
142+
bit::test(auxv.hwcap, (b'c' - b'a').into()),
143+
);
144+
enable_features(
145+
&mut value,
146+
&[Feature::d, Feature::f, Feature::zicsr],
147+
bit::test(auxv.hwcap, (b'd' - b'a').into()),
148+
);
149+
enable_features(
150+
&mut value,
151+
&[Feature::f, Feature::zicsr],
152+
bit::test(auxv.hwcap, (b'f' - b'a').into()),
153+
);
154+
let has_i = bit::test(auxv.hwcap, (b'i' - b'a').into());
155+
// If future RV128I is supported, implement with `enable_feature` here
156+
// Checking target_pointer_width instead of target_arch is incorrect since
157+
// there are RV64ILP32* ABIs.
158+
#[cfg(target_arch = "riscv64")]
159+
enable_feature(&mut value, Feature::rv64i, has_i);
160+
#[cfg(target_arch = "riscv32")]
161+
enable_feature(&mut value, Feature::rv32i, has_i);
162+
// FIXME: e is not exposed in any of asm/hwcap.h, uapi/asm/hwcap.h, uapi/asm/hwprobe.h
163+
#[cfg(target_arch = "riscv32")]
164+
enable_feature(
165+
&mut value,
166+
Feature::rv32e,
167+
bit::test(auxv.hwcap, (b'e' - b'a').into()),
168+
);
169+
enable_feature(
170+
&mut value,
171+
Feature::m,
172+
bit::test(auxv.hwcap, (b'm' - b'a').into()),
173+
);
174+
let has_v = bit::test(auxv.hwcap, (b'v' - b'a').into());
175+
enable_features(
176+
&mut value,
177+
&[
178+
Feature::v,
179+
Feature::zve32f,
180+
Feature::zve32x,
181+
Feature::zve64d,
182+
Feature::zve64f,
183+
Feature::zve64x,
184+
],
185+
has_v,
186+
);
187+
116188
let mut out = [
117-
riscv_hwprobe {
118-
key: RISCV_HWPROBE_KEY_BASE_BEHAVIOR,
119-
value: 0,
120-
},
121189
riscv_hwprobe {
122190
key: RISCV_HWPROBE_KEY_IMA_EXT_0,
123191
value: 0,
@@ -138,23 +206,13 @@ pub(crate) fn detect_features() -> cache::Initializer {
138206
}
139207
};
140208
if out[0].key != -1 {
141-
let base_behavior = out[0].value;
142-
let ima = base_behavior & RISCV_HWPROBE_BASE_BEHAVIOR_IMA != 0;
143-
// If future RV128I is supported, implement with `enable_feature` here
144-
#[cfg(target_arch = "riscv32")]
145-
enable_feature(Feature::rv32i, ima);
146-
#[cfg(target_arch = "riscv64")]
147-
enable_feature(Feature::rv64i, ima);
148-
enable_feature(Feature::m, ima);
149-
enable_feature(Feature::a, ima);
150-
}
151-
if out[1].key != -1 {
152-
let ima_ext_0 = out[1].value;
153-
let fd = ima_ext_0 & RISCV_HWPROBE_IMA_FD != 0;
154-
enable_feature(Feature::f, fd);
155-
enable_feature(Feature::d, fd);
156-
enable_feature(Feature::zicsr, fd); // implied by f
157-
enable_feature(Feature::c, ima_ext_0 & RISCV_HWPROBE_IMA_C != 0);
209+
let ima_ext_0 = out[0].value;
210+
// i, m, a, f, d, zicsr, and c extensions are detected by hwcap.
211+
// let fd = ima_ext_0 & RISCV_HWPROBE_IMA_FD != 0;
212+
// enable_feature(Feature::f, fd);
213+
// enable_feature(Feature::d, fd);
214+
// enable_feature(Feature::zicsr, fd); // implied by f
215+
// enable_feature(Feature::c, ima_ext_0 & RISCV_HWPROBE_IMA_C != 0);
158216
// enable_feature(Feature::zicboz, ima_ext_0 & RISCV_HWPROBE_EXT_ZICBOZ != 0);
159217
enable_feature(Feature::zfh, ima_ext_0 & RISCV_HWPROBE_EXT_ZFH != 0);
160218
enable_feature(Feature::zfhmin, ima_ext_0 & RISCV_HWPROBE_EXT_ZFHMIN != 0);
@@ -203,129 +261,64 @@ pub(crate) fn detect_features() -> cache::Initializer {
203261
enable_feature(Feature::zkn, zkn);
204262
// enable_feature(Feature::zk, zkn & zkr & zkt);
205263
enable_feature(Feature::zks, zbkb & zbkc & zbkx & zksed & zksh);
206-
// Standard Vector Extensions
207-
enable_feature(Feature::v, ima_ext_0 & RISCV_HWPROBE_IMA_V != 0);
208-
enable_feature(Feature::zvfh, ima_ext_0 & RISCV_HWPROBE_EXT_ZVFH != 0);
209-
enable_feature(Feature::zvfhmin, ima_ext_0 & RISCV_HWPROBE_EXT_ZVFHMIN != 0);
210-
enable_feature(Feature::zve32x, ima_ext_0 & RISCV_HWPROBE_EXT_ZVE32X != 0);
211-
enable_feature(Feature::zve32f, ima_ext_0 & RISCV_HWPROBE_EXT_ZVE32F != 0);
212-
enable_feature(Feature::zve64x, ima_ext_0 & RISCV_HWPROBE_EXT_ZVE64X != 0);
213-
enable_feature(Feature::zve64f, ima_ext_0 & RISCV_HWPROBE_EXT_ZVE64F != 0);
214-
enable_feature(Feature::zve64d, ima_ext_0 & RISCV_HWPROBE_EXT_ZVE64D != 0);
215-
// Vector Cryptography and Bit-manipulation Extensions
216-
let zvbb = ima_ext_0 & RISCV_HWPROBE_EXT_ZVBB != 0;
217-
enable_feature(Feature::zvbb, zvbb);
218-
let zvbc = ima_ext_0 & RISCV_HWPROBE_EXT_ZVBC != 0;
219-
enable_feature(Feature::zvbc, zvbc);
220-
let zvkb = zvbb || ima_ext_0 & RISCV_HWPROBE_EXT_ZVKB != 0;
221-
enable_feature(Feature::zvkb, zvkb);
222-
let zvkg = ima_ext_0 & RISCV_HWPROBE_EXT_ZVKG != 0;
223-
enable_feature(Feature::zvkg, zvkg);
224-
let zvkned = ima_ext_0 & RISCV_HWPROBE_EXT_ZVKNED != 0;
225-
enable_feature(Feature::zvkned, zvkned);
226-
enable_feature(Feature::zvknha, ima_ext_0 & RISCV_HWPROBE_EXT_ZVKNHA != 0);
227-
let zvknhb = ima_ext_0 & RISCV_HWPROBE_EXT_ZVKNHB != 0;
228-
enable_feature(Feature::zvknhb, zvknhb);
229-
let zvksed = ima_ext_0 & RISCV_HWPROBE_EXT_ZVKSED != 0;
230-
enable_feature(Feature::zvksed, zvksed);
231-
let zvksh = ima_ext_0 & RISCV_HWPROBE_EXT_ZVKSH != 0;
232-
enable_feature(Feature::zvksh, zvksh);
233-
let zvkt = ima_ext_0 & RISCV_HWPROBE_EXT_ZVKT != 0;
234-
enable_feature(Feature::zvkt, zvkt);
235-
let zvkn = zvkned & zvknhb & zvkb & zvkt;
236-
enable_feature(Feature::zvkn, zvkn);
237-
enable_feature(Feature::zvknc, zvkn & zvbc);
238-
enable_feature(Feature::zvkng, zvkn & zvkg);
239-
let zvks = zvksed & zvksh & zvkb & zvkt;
240-
enable_feature(Feature::zvks, zvks);
241-
enable_feature(Feature::zvksc, zvks & zvbc);
242-
enable_feature(Feature::zvksg, zvks & zvkg);
264+
// Refer result from hwcap because it reflects Vector enablement status, unlike hwprobe.
265+
// prctl(PR_RISCV_V_GET_CONTROL) is another way to check this but it doesn't work with
266+
// qemu-user (as of 9.2.1).
267+
// See https://docs.kernel.org/arch/riscv/vector.html for more.
268+
if has_v {
269+
// Standard Vector Extensions
270+
// v and zve{32,64}* extensions are detected by hwcap.
271+
// enable_feature(Feature::v, ima_ext_0 & RISCV_HWPROBE_IMA_V != 0);
272+
enable_feature(Feature::zvfh, ima_ext_0 & RISCV_HWPROBE_EXT_ZVFH != 0);
273+
enable_feature(Feature::zvfhmin, ima_ext_0 & RISCV_HWPROBE_EXT_ZVFHMIN != 0);
274+
// enable_feature(Feature::zve32x, ima_ext_0 & RISCV_HWPROBE_EXT_ZVE32X != 0);
275+
// enable_feature(Feature::zve32f, ima_ext_0 & RISCV_HWPROBE_EXT_ZVE32F != 0);
276+
// enable_feature(Feature::zve64x, ima_ext_0 & RISCV_HWPROBE_EXT_ZVE64X != 0);
277+
// enable_feature(Feature::zve64f, ima_ext_0 & RISCV_HWPROBE_EXT_ZVE64F != 0);
278+
// enable_feature(Feature::zve64d, ima_ext_0 & RISCV_HWPROBE_EXT_ZVE64D != 0);
279+
// Vector Cryptography and Bit-manipulation Extensions
280+
let zvbb = ima_ext_0 & RISCV_HWPROBE_EXT_ZVBB != 0;
281+
enable_feature(Feature::zvbb, zvbb);
282+
let zvbc = ima_ext_0 & RISCV_HWPROBE_EXT_ZVBC != 0;
283+
enable_feature(Feature::zvbc, zvbc);
284+
let zvkb = zvbb || ima_ext_0 & RISCV_HWPROBE_EXT_ZVKB != 0;
285+
enable_feature(Feature::zvkb, zvkb);
286+
let zvkg = ima_ext_0 & RISCV_HWPROBE_EXT_ZVKG != 0;
287+
enable_feature(Feature::zvkg, zvkg);
288+
let zvkned = ima_ext_0 & RISCV_HWPROBE_EXT_ZVKNED != 0;
289+
enable_feature(Feature::zvkned, zvkned);
290+
enable_feature(Feature::zvknha, ima_ext_0 & RISCV_HWPROBE_EXT_ZVKNHA != 0);
291+
let zvknhb = ima_ext_0 & RISCV_HWPROBE_EXT_ZVKNHB != 0;
292+
enable_feature(Feature::zvknhb, zvknhb);
293+
let zvksed = ima_ext_0 & RISCV_HWPROBE_EXT_ZVKSED != 0;
294+
enable_feature(Feature::zvksed, zvksed);
295+
let zvksh = ima_ext_0 & RISCV_HWPROBE_EXT_ZVKSH != 0;
296+
enable_feature(Feature::zvksh, zvksh);
297+
let zvkt = ima_ext_0 & RISCV_HWPROBE_EXT_ZVKT != 0;
298+
enable_feature(Feature::zvkt, zvkt);
299+
let zvkn = zvkned & zvknhb & zvkb & zvkt;
300+
enable_feature(Feature::zvkn, zvkn);
301+
enable_feature(Feature::zvknc, zvkn & zvbc);
302+
enable_feature(Feature::zvkng, zvkn & zvkg);
303+
let zvks = zvksed & zvksh & zvkb & zvkt;
304+
enable_feature(Feature::zvks, zvks);
305+
enable_feature(Feature::zvksc, zvks & zvbc);
306+
enable_feature(Feature::zvksg, zvks & zvkg);
307+
}
243308
}
244-
if out[2].key != -1 {
309+
if out[1].key != -1 {
245310
enable_feature(
246311
Feature::unaligned_scalar_mem,
247-
out[2].value & RISCV_HWPROBE_MISALIGNED_MASK == RISCV_HWPROBE_MISALIGNED_FAST,
312+
out[1].value & RISCV_HWPROBE_MISALIGNED_MASK == RISCV_HWPROBE_MISALIGNED_FAST,
248313
);
249314
}
250-
if out[3].key != -1 {
315+
if out[2].key != -1 {
251316
enable_feature(
252317
Feature::unaligned_vector_mem,
253-
out[3].value == RISCV_HWPROBE_MISALIGNED_VECTOR_FAST,
318+
out[2].value == RISCV_HWPROBE_MISALIGNED_VECTOR_FAST,
254319
);
255320
}
256-
// FIXME: should be enough with hwprobe only, but our code below checks e
257-
// unavailable in neither uapi/asm/hwprobe.h nor uapi/asm/hwcap.h.
258-
// https://github.com/torvalds/linux/blob/master/arch/riscv/include/uapi/asm/hwcap.h
259-
// return value;
260321
}
261322

262-
// FIXME: As said in the above FIXME, we currently alway checks auxv too.
263-
// // riscv_hwprobe requires Linux 6.4, so we fallback to auxv-based detection on
264-
// // old Linux kernel.
265-
266-
let enable_feature = |value: &mut cache::Initializer, feature, enable| {
267-
if enable {
268-
value.set(feature as u32);
269-
}
270-
};
271-
let enable_features = |value: &mut cache::Initializer, feature_slice: &[Feature], enable| {
272-
if enable {
273-
for feature in feature_slice {
274-
value.set(*feature as u32);
275-
}
276-
}
277-
};
278-
279-
// The values are part of the platform-specific [asm/hwcap.h][hwcap]
280-
//
281-
// [hwcap]: https://github.com/torvalds/linux/blob/master/arch/riscv/include/asm/hwcap.h
282-
//
283-
// Note that there is no need to check b'v' - b'a' here for the case where riscv_hwprobe is unsupported,
284-
// since both RISCV_HWPROBE_IMA_V and COMPAT_HWCAP_ISA_V are only supported on Linux 6.5+.
285-
// https://github.com/torvalds/linux/commit/162e4df137c1fea6557fda3e4cdf5dc6ca6d5510
286-
// https://github.com/torvalds/linux/commit/dc6667a4e7e36f283bcd0264a0be55adae4d6f86
287-
let auxv = auxvec::auxv().expect("read auxvec"); // should not fail on RISC-V platform
288-
#[allow(clippy::eq_op)]
289-
enable_feature(
290-
&mut value,
291-
Feature::a,
292-
bit::test(auxv.hwcap, (b'a' - b'a').into()),
293-
);
294-
enable_feature(
295-
&mut value,
296-
Feature::c,
297-
bit::test(auxv.hwcap, (b'c' - b'a').into()),
298-
);
299-
enable_features(
300-
&mut value,
301-
&[Feature::d, Feature::f, Feature::zicsr],
302-
bit::test(auxv.hwcap, (b'd' - b'a').into()),
303-
);
304-
enable_features(
305-
&mut value,
306-
&[Feature::f, Feature::zicsr],
307-
bit::test(auxv.hwcap, (b'f' - b'a').into()),
308-
);
309-
let has_i = bit::test(auxv.hwcap, (b'i' - b'a').into());
310-
// If future RV128I is supported, implement with `enable_feature` here
311-
// Checking target_pointer_width instead of target_arch is incorrect since
312-
// there are RV64ILP32* ABIs.
313-
#[cfg(target_arch = "riscv64")]
314-
enable_feature(&mut value, Feature::rv64i, has_i);
315-
#[cfg(target_arch = "riscv32")]
316-
enable_feature(&mut value, Feature::rv32i, has_i);
317-
// FIXME: e is not exposed in any of asm/hwcap.h, uapi/asm/hwcap.h, uapi/asm/hwprobe.h
318-
#[cfg(target_arch = "riscv32")]
319-
enable_feature(
320-
&mut value,
321-
Feature::rv32e,
322-
bit::test(auxv.hwcap, (b'e' - b'a').into()),
323-
);
324-
enable_feature(
325-
&mut value,
326-
Feature::m,
327-
bit::test(auxv.hwcap, (b'm' - b'a').into()),
328-
);
329-
330323
value
331324
}

0 commit comments

Comments
 (0)