Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 6e382c8

Browse files
a4lgtaiki-e
andcommittedApr 12, 2025
RISC-V: riscv_hwprobe-based feature detection on Linux / Android
This commit implements `riscv_hwprobe`-based feature detection as available on newer versions of the Linux kernel. It also queries whether the vector extensions are enabled using `prctl` but this is not supported on QEMU's userland emulator (as of version 9.2.3) and use the auxiliary vector as a fallback. Currently, all extensions discoverable from the Linux kernel version 6.14 and related extension groups (except "Supm", which reports the existence of `prctl`-based pointer masking control and too OS-dependent) are implemented. Co-Authored-By: Taiki Endo <[email protected]>
1 parent 33a502f commit 6e382c8

File tree

5 files changed

+496
-5
lines changed

5 files changed

+496
-5
lines changed
 

‎crates/std_detect/README.md‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,14 @@ crate from working on applications in which `std` is not available.
5656
[`cupid`](https://crates.io/crates/cupid) crate.
5757

5858
* Linux/Android:
59-
* `arm{32, 64}`, `mips{32,64}{,el}`, `powerpc{32,64}{,le}`, `riscv{32,64}`, `loongarch64`, `s390x`:
59+
* `arm{32, 64}`, `mips{32,64}{,el}`, `powerpc{32,64}{,le}`, `loongarch64`, `s390x`:
6060
`std_detect` supports these on Linux by querying ELF auxiliary vectors (using `getauxval`
6161
when available), and if that fails, by querying `/proc/cpuinfo`.
6262
* `arm64`: partial support for doing run-time feature detection by directly
6363
querying `mrs` is implemented for Linux >= 4.11, but not enabled by default.
64+
* `riscv{32,64}`:
65+
`std_detect` supports these on Linux by querying `riscv_hwprobe`, and
66+
by querying ELF auxiliary vectors (using `getauxval` when available).
6467

6568
* FreeBSD:
6669
* `arm32`, `powerpc64`: `std_detect` supports these on FreeBSD by querying ELF

‎crates/std_detect/src/detect/arch/riscv.rs‎

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,22 +37,39 @@ features! {
3737
/// * Zbb: `"zbb"`
3838
/// * Zbs: `"zbs"`
3939
/// * C: `"c"`
40+
/// * Zca: `"zca"`
41+
/// * Zcd: `"zcd"` (if D is enabled)
42+
/// * Zcf: `"zcf"` (if F is enabled on RV32)
4043
/// * D: `"d"`
4144
/// * F: `"f"`
4245
/// * M: `"m"`
4346
/// * Q: `"q"`
4447
/// * V: `"v"`
48+
/// * Zve32x: `"zve32x"`
49+
/// * Zve32f: `"zve32f"`
50+
/// * Zve64x: `"zve64x"`
51+
/// * Zve64f: `"zve64f"`
52+
/// * Zve64d: `"zve64d"`
53+
/// * Zicboz: `"zicboz"`
4554
/// * Zicntr: `"zicntr"`
55+
/// * Zicond: `"zicond"`
4656
/// * Zicsr: `"zicsr"`
4757
/// * Zifencei: `"zifencei"`
58+
/// * Zihintntl: `"zihintntl"`
4859
/// * Zihintpause: `"zihintpause"`
4960
/// * Zihpm: `"zihpm"`
61+
/// * Zimop: `"zimop"`
62+
/// * Zacas: `"zacas"`
63+
/// * Zawrs: `"zawrs"`
64+
/// * Zfa: `"zfa"`
5065
/// * Zfh: `"zfh"`
5166
/// * Zfhmin: `"zfhmin"`
5267
/// * Zfinx: `"zfinx"`
5368
/// * Zdinx: `"zdinx"`
5469
/// * Zhinx: `"zhinx"`
5570
/// * Zhinxmin: `"zhinxmin"`
71+
/// * Zcb: `"zcb"`
72+
/// * Zcmop: `"zcmop"`
5673
/// * Zbc: `"zbc"`
5774
/// * Zbkb: `"zbkb"`
5875
/// * Zbkc: `"zbkc"`
@@ -67,6 +84,24 @@ features! {
6784
/// * Zksed: `"zksed"`
6885
/// * Zksh: `"zksh"`
6986
/// * Zkt: `"zkt"`
87+
/// * Zvbb: `"zvbb"`
88+
/// * Zvbc: `"zvbc"`
89+
/// * Zvfh: `"zvfh"`
90+
/// * Zvfhmin: `"zvfhmin"`
91+
/// * Zvkb: `"zvkb"`
92+
/// * Zvkg: `"zvkg"`
93+
/// * Zvkn: `"zvkn"`
94+
/// * Zvkned: `"zvkned"`
95+
/// * Zvknha: `"zvknha"`
96+
/// * Zvknhb: `"zvknhb"`
97+
/// * Zvknc: `"zvknc"`
98+
/// * Zvkng: `"zvkng"`
99+
/// * Zvks: `"zvks"`
100+
/// * Zvksed: `"zvksed"`
101+
/// * Zvksh: `"zvksh"`
102+
/// * Zvksc: `"zvksc"`
103+
/// * Zvksg: `"zvksg"`
104+
/// * Zvkt: `"zvkt"`
70105
/// * Ztso: `"ztso"`
71106
///
72107
/// There's also bases and extensions marked as standard instruction set,
@@ -87,6 +122,15 @@ features! {
87122
/// * Svnapot: `"svnapot"`
88123
/// * Svpbmt: `"svpbmt"`
89124
/// * Svinval: `"svinval"`
125+
///
126+
/// # Performance Hints
127+
///
128+
/// The two features below define performance hints for unaligned
129+
/// scalar/vector memory accesses, respectively. If enabled, it denotes that
130+
/// corresponding unaligned memory access is reasonably fast.
131+
///
132+
/// * `"unaligned-scalar-mem"`
133+
/// * `"unaligned-vector-mem"`
90134
#[stable(feature = "riscv_ratified", since = "1.78.0")]
91135

92136
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] rv32i: "rv32i";
@@ -102,6 +146,11 @@ features! {
102146
without cfg check: true;
103147
/// RV128I Base Integer Instruction Set
104148
149+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] unaligned_scalar_mem: "unaligned-scalar-mem";
150+
/// Has reasonably performant unaligned scalar
151+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] unaligned_vector_mem: "unaligned-vector-mem";
152+
/// Has reasonably performant unaligned vector
153+
105154
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zicsr: "zicsr";
106155
without cfg check: true;
107156
/// "Zicsr" Extension for Control and Status Register (CSR) Instructions
@@ -115,9 +164,21 @@ features! {
115164
without cfg check: true;
116165
/// "Zifencei" Extension for Instruction-Fetch Fence
117166
167+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zihintntl: "zihintntl";
168+
without cfg check: true;
169+
/// "Zihintntl" Extension for Non-Temporal Locality Hints
118170
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zihintpause: "zihintpause";
119171
without cfg check: true;
120172
/// "Zihintpause" Extension for Pause Hint
173+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zimop: "zimop";
174+
without cfg check: true;
175+
/// "Zimop" Extension for May-Be-Operations
176+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zicboz: "zicboz";
177+
without cfg check: true;
178+
/// "Zicboz" Extension for Cache-Block Zero Instruction
179+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zicond: "zicond";
180+
without cfg check: true;
181+
/// "Zicond" Extension for Integer Conditional Operations
121182
122183
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] m: "m";
123184
/// "M" Extension for Integer Multiplication and Division
@@ -128,6 +189,10 @@ features! {
128189
/// "Zalrsc" Extension for Load-Reserved/Store-Conditional Instructions
129190
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zaamo: "zaamo";
130191
/// "Zaamo" Extension for Atomic Memory Operations
192+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zawrs: "zawrs";
193+
/// "Zawrs" Extension for Wait-on-Reservation-Set Instructions
194+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zacas: "zacas";
195+
/// "Zacas" Extension for Atomic Compare-and-Swap (CAS) Instructions
131196
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zam: "zam";
132197
without cfg check: true;
133198
/// "Zam" Extension for Misaligned Atomics
@@ -146,6 +211,9 @@ features! {
146211
/// "Zfh" Extension for Half-Precision Floating-Point
147212
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zfhmin: "zfhmin";
148213
/// "Zfhmin" Extension for Minimal Half-Precision Floating-Point
214+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zfa: "zfa";
215+
without cfg check: true;
216+
/// "Zfa" Extension for Additional Floating-Point Instructions
149217
150218
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zfinx: "zfinx";
151219
/// "Zfinx" Extension for Single-Precision Floating-Point in Integer Registers
@@ -158,6 +226,21 @@ features! {
158226
159227
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] c: "c";
160228
/// "C" Extension for Compressed Instructions
229+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zca: "zca";
230+
without cfg check: true;
231+
/// "Zca" Compressed Instructions excluding Floating-Point Loads/Stores
232+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zcf: "zcf";
233+
without cfg check: true;
234+
/// "Zcf" Compressed Instructions for Single-Precision Floating-Point Loads/Stores on RV32
235+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zcd: "zcd";
236+
without cfg check: true;
237+
/// "Zcd" Compressed Instructions for Double-Precision Floating-Point Loads/Stores
238+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zcb: "zcb";
239+
without cfg check: true;
240+
/// "Zcb" Simple Code-size Saving Compressed Instructions
241+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zcmop: "zcmop";
242+
without cfg check: true;
243+
/// "Zcmop" Extension for Compressed May-Be-Operations
161244
162245
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] b: "b";
163246
without cfg check: true;
@@ -200,6 +283,53 @@ features! {
200283
201284
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] v: "v";
202285
/// "V" Extension for Vector Operations
286+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve32x: "zve32x";
287+
/// "Zve32x" Vector Extension for Embedded Processors (32-bit+; Integer)
288+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve32f: "zve32f";
289+
/// "Zve32f" Vector Extension for Embedded Processors (32-bit+; with Single-Precision Floating-Point)
290+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve64x: "zve64x";
291+
/// "Zve64x" Vector Extension for Embedded Processors (64-bit+; Integer)
292+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve64f: "zve64f";
293+
/// "Zve64f" Vector Extension for Embedded Processors (64-bit+; with Single-Precision Floating-Point)
294+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve64d: "zve64d";
295+
/// "Zve64d" Vector Extension for Embedded Processors (64-bit+; with Double-Precision Floating-Point)
296+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvfh: "zvfh";
297+
/// "Zvfh" Vector Extension for Half-Precision Floating-Point
298+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvfhmin: "zvfhmin";
299+
/// "Zvfhmin" Vector Extension for Minimal Half-Precision Floating-Point
300+
301+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvbb: "zvbb";
302+
/// "Zvbb" Extension for Vector Basic Bit-Manipulation
303+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvbc: "zvbc";
304+
/// "Zvbc" Extension for Vector Carryless Multiplication
305+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkb: "zvkb";
306+
/// "Zvkb" Extension for Vector Cryptography Bit-Manipulation
307+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkg: "zvkg";
308+
/// "Zvkg" Cryptography Extension for Vector GCM/GMAC
309+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkned: "zvkned";
310+
/// "Zvkned" Cryptography Extension for NIST Suite: Vector AES Block Cipher
311+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvknha: "zvknha";
312+
/// "Zvknha" Cryptography Extension for Vector SHA-2 Secure Hash (SHA-256)
313+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvknhb: "zvknhb";
314+
/// "Zvknhb" Cryptography Extension for Vector SHA-2 Secure Hash (SHA-256/512)
315+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvksed: "zvksed";
316+
/// "Zvksed" Cryptography Extension for ShangMi Suite: Vector SM4 Block Cipher
317+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvksh: "zvksh";
318+
/// "Zvksh" Cryptography Extension for ShangMi Suite: Vector SM3 Secure Hash
319+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkn: "zvkn";
320+
/// "Zvkn" Cryptography Extension for NIST Algorithm Suite
321+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvknc: "zvknc";
322+
/// "Zvknc" Cryptography Extension for NIST Algorithm Suite with Carryless Multiply
323+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkng: "zvkng";
324+
/// "Zvkng" Cryptography Extension for NIST Algorithm Suite with GCM
325+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvks: "zvks";
326+
/// "Zvks" Cryptography Extension for ShangMi Algorithm Suite
327+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvksc: "zvksc";
328+
/// "Zvksc" Cryptography Extension for ShangMi Algorithm Suite with Carryless Multiply
329+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvksg: "zvksg";
330+
/// "Zvksg" Cryptography Extension for ShangMi Algorithm Suite with GCM
331+
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkt: "zvkt";
332+
/// "Zvkt" Extension for Vector Data-Independent Execution Latency
203333
204334
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] svnapot: "svnapot";
205335
without cfg check: true;

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

Lines changed: 278 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,127 @@
11
//! Run-time feature detection for RISC-V on Linux.
2+
//!
3+
//! On RISC-V, detection using auxv only supports single-letter extensions.
4+
//! So, we use riscv_hwprobe that supports multi-letter extensions if available.
5+
//! <https://www.kernel.org/doc/html/latest/arch/riscv/hwprobe.html>
6+
7+
use core::ptr;
28

39
use super::super::riscv::imply_features;
410
use super::auxvec;
511
use crate::detect::{Feature, bit, cache};
612

7-
/// Read list of supported features from the auxiliary vector.
13+
// See <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/prctl.h?h=v6.14>
14+
// for runtime status query constants.
15+
const PR_RISCV_V_GET_CONTROL: libc::c_int = 70;
16+
const PR_RISCV_V_VSTATE_CTRL_ON: libc::c_int = 2;
17+
const PR_RISCV_V_VSTATE_CTRL_CUR_MASK: libc::c_int = 3;
18+
19+
// See <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/riscv/include/uapi/asm/hwprobe.h?h=v6.14>
20+
// for riscv_hwprobe struct and hardware probing constants.
21+
22+
#[repr(C)]
23+
struct riscv_hwprobe {
24+
key: i64,
25+
value: u64,
26+
}
27+
28+
#[allow(non_upper_case_globals)]
29+
const __NR_riscv_hwprobe: libc::c_long = 258;
30+
31+
const RISCV_HWPROBE_KEY_BASE_BEHAVIOR: i64 = 3;
32+
const RISCV_HWPROBE_BASE_BEHAVIOR_IMA: u64 = 1 << 0;
33+
34+
const RISCV_HWPROBE_KEY_IMA_EXT_0: i64 = 4;
35+
const RISCV_HWPROBE_IMA_FD: u64 = 1 << 0;
36+
const RISCV_HWPROBE_IMA_C: u64 = 1 << 1;
37+
const RISCV_HWPROBE_IMA_V: u64 = 1 << 2;
38+
const RISCV_HWPROBE_EXT_ZBA: u64 = 1 << 3;
39+
const RISCV_HWPROBE_EXT_ZBB: u64 = 1 << 4;
40+
const RISCV_HWPROBE_EXT_ZBS: u64 = 1 << 5;
41+
const RISCV_HWPROBE_EXT_ZICBOZ: u64 = 1 << 6;
42+
const RISCV_HWPROBE_EXT_ZBC: u64 = 1 << 7;
43+
const RISCV_HWPROBE_EXT_ZBKB: u64 = 1 << 8;
44+
const RISCV_HWPROBE_EXT_ZBKC: u64 = 1 << 9;
45+
const RISCV_HWPROBE_EXT_ZBKX: u64 = 1 << 10;
46+
const RISCV_HWPROBE_EXT_ZKND: u64 = 1 << 11;
47+
const RISCV_HWPROBE_EXT_ZKNE: u64 = 1 << 12;
48+
const RISCV_HWPROBE_EXT_ZKNH: u64 = 1 << 13;
49+
const RISCV_HWPROBE_EXT_ZKSED: u64 = 1 << 14;
50+
const RISCV_HWPROBE_EXT_ZKSH: u64 = 1 << 15;
51+
const RISCV_HWPROBE_EXT_ZKT: u64 = 1 << 16;
52+
const RISCV_HWPROBE_EXT_ZVBB: u64 = 1 << 17;
53+
const RISCV_HWPROBE_EXT_ZVBC: u64 = 1 << 18;
54+
const RISCV_HWPROBE_EXT_ZVKB: u64 = 1 << 19;
55+
const RISCV_HWPROBE_EXT_ZVKG: u64 = 1 << 20;
56+
const RISCV_HWPROBE_EXT_ZVKNED: u64 = 1 << 21;
57+
const RISCV_HWPROBE_EXT_ZVKNHA: u64 = 1 << 22;
58+
const RISCV_HWPROBE_EXT_ZVKNHB: u64 = 1 << 23;
59+
const RISCV_HWPROBE_EXT_ZVKSED: u64 = 1 << 24;
60+
const RISCV_HWPROBE_EXT_ZVKSH: u64 = 1 << 25;
61+
const RISCV_HWPROBE_EXT_ZVKT: u64 = 1 << 26;
62+
const RISCV_HWPROBE_EXT_ZFH: u64 = 1 << 27;
63+
const RISCV_HWPROBE_EXT_ZFHMIN: u64 = 1 << 28;
64+
const RISCV_HWPROBE_EXT_ZIHINTNTL: u64 = 1 << 29;
65+
const RISCV_HWPROBE_EXT_ZVFH: u64 = 1 << 30;
66+
const RISCV_HWPROBE_EXT_ZVFHMIN: u64 = 1 << 31;
67+
const RISCV_HWPROBE_EXT_ZFA: u64 = 1 << 32;
68+
const RISCV_HWPROBE_EXT_ZTSO: u64 = 1 << 33;
69+
const RISCV_HWPROBE_EXT_ZACAS: u64 = 1 << 34;
70+
const RISCV_HWPROBE_EXT_ZICOND: u64 = 1 << 35;
71+
const RISCV_HWPROBE_EXT_ZIHINTPAUSE: u64 = 1 << 36;
72+
const RISCV_HWPROBE_EXT_ZVE32X: u64 = 1 << 37;
73+
const RISCV_HWPROBE_EXT_ZVE32F: u64 = 1 << 38;
74+
const RISCV_HWPROBE_EXT_ZVE64X: u64 = 1 << 39;
75+
const RISCV_HWPROBE_EXT_ZVE64F: u64 = 1 << 40;
76+
const RISCV_HWPROBE_EXT_ZVE64D: u64 = 1 << 41;
77+
const RISCV_HWPROBE_EXT_ZIMOP: u64 = 1 << 42;
78+
const RISCV_HWPROBE_EXT_ZCA: u64 = 1 << 43;
79+
const RISCV_HWPROBE_EXT_ZCB: u64 = 1 << 44;
80+
const RISCV_HWPROBE_EXT_ZCD: u64 = 1 << 45;
81+
const RISCV_HWPROBE_EXT_ZCF: u64 = 1 << 46;
82+
const RISCV_HWPROBE_EXT_ZCMOP: u64 = 1 << 47;
83+
const RISCV_HWPROBE_EXT_ZAWRS: u64 = 1 << 48;
84+
// Excluded because it only reports the existence of `prctl`-based pointer masking control.
85+
// const RISCV_HWPROBE_EXT_SUPM: u64 = 1 << 49;
86+
87+
const RISCV_HWPROBE_KEY_CPUPERF_0: i64 = 5;
88+
const RISCV_HWPROBE_MISALIGNED_FAST: u64 = 3;
89+
const RISCV_HWPROBE_MISALIGNED_MASK: u64 = 7;
90+
91+
const RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF: i64 = 9;
92+
const RISCV_HWPROBE_MISALIGNED_SCALAR_FAST: u64 = 3;
93+
94+
const RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF: i64 = 10;
95+
const RISCV_HWPROBE_MISALIGNED_VECTOR_FAST: u64 = 3;
96+
97+
// syscall returns an unsupported error if riscv_hwprobe is not supported,
98+
// so we can safely use this function on older versions of Linux.
99+
fn _riscv_hwprobe(out: &mut [riscv_hwprobe]) -> bool {
100+
unsafe fn __riscv_hwprobe(
101+
pairs: *mut riscv_hwprobe,
102+
pair_count: libc::size_t,
103+
cpu_set_size: libc::size_t,
104+
cpus: *mut libc::c_ulong,
105+
flags: libc::c_uint,
106+
) -> libc::c_long {
107+
unsafe {
108+
libc::syscall(
109+
__NR_riscv_hwprobe,
110+
pairs,
111+
pair_count,
112+
cpu_set_size,
113+
cpus,
114+
flags,
115+
)
116+
}
117+
}
118+
119+
let len = out.len();
120+
unsafe { __riscv_hwprobe(out.as_mut_ptr(), len, 0, ptr::null_mut(), 0) == 0 }
121+
}
122+
123+
/// Read list of supported features from (1) the auxiliary vector
124+
/// and (2) the results of `riscv_hwprobe` and `prctl` system calls.
8125
pub(crate) fn detect_features() -> cache::Initializer {
9126
let mut value = cache::Initializer::default();
10127
let mut enable_feature = |feature, enable| {
@@ -18,16 +135,175 @@ pub(crate) fn detect_features() -> cache::Initializer {
18135
//
19136
// [hwcap]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/riscv/include/uapi/asm/hwcap.h?h=v6.14
20137
let auxv = auxvec::auxv().expect("read auxvec"); // should not fail on RISC-V platform
138+
let mut has_i = bit::test(auxv.hwcap, (b'i' - b'a').into());
21139
#[allow(clippy::eq_op)]
22140
enable_feature(Feature::a, bit::test(auxv.hwcap, (b'a' - b'a').into()));
23141
enable_feature(Feature::c, bit::test(auxv.hwcap, (b'c' - b'a').into()));
24142
enable_feature(Feature::d, bit::test(auxv.hwcap, (b'd' - b'a').into()));
25143
enable_feature(Feature::f, bit::test(auxv.hwcap, (b'f' - b'a').into()));
26144
enable_feature(Feature::h, bit::test(auxv.hwcap, (b'h' - b'a').into()));
27145
enable_feature(Feature::m, bit::test(auxv.hwcap, (b'm' - b'a').into()));
146+
let has_v = bit::test(auxv.hwcap, (b'v' - b'a').into());
147+
let mut is_v_set = false;
148+
149+
// Use riscv_hwprobe syscall to query more extensions and
150+
// performance-related capabilities.
151+
'hwprobe: {
152+
let mut out = [
153+
riscv_hwprobe {
154+
key: RISCV_HWPROBE_KEY_BASE_BEHAVIOR,
155+
value: 0,
156+
},
157+
riscv_hwprobe {
158+
key: RISCV_HWPROBE_KEY_IMA_EXT_0,
159+
value: 0,
160+
},
161+
riscv_hwprobe {
162+
key: RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF,
163+
value: 0,
164+
},
165+
riscv_hwprobe {
166+
key: RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF,
167+
value: 0,
168+
},
169+
riscv_hwprobe {
170+
key: RISCV_HWPROBE_KEY_CPUPERF_0,
171+
value: 0,
172+
},
173+
];
174+
if !_riscv_hwprobe(&mut out) {
175+
break 'hwprobe;
176+
}
177+
178+
// Query scalar / vector misaligned behavior.
179+
if out[2].key != -1 {
180+
enable_feature(
181+
Feature::unaligned_scalar_mem,
182+
out[2].value == RISCV_HWPROBE_MISALIGNED_SCALAR_FAST,
183+
);
184+
} else if out[4].key != -1 {
185+
// Deprecated method for fallback
186+
enable_feature(
187+
Feature::unaligned_scalar_mem,
188+
out[4].value & RISCV_HWPROBE_MISALIGNED_MASK == RISCV_HWPROBE_MISALIGNED_FAST,
189+
);
190+
}
191+
if out[3].key != -1 {
192+
enable_feature(
193+
Feature::unaligned_vector_mem,
194+
out[3].value == RISCV_HWPROBE_MISALIGNED_VECTOR_FAST,
195+
);
196+
}
197+
198+
// Query whether "I" base and extensions "M" and "A" (as in the ISA
199+
// manual version 2.2) are enabled. "I" base at that time corresponds
200+
// to "I", "Zicsr", "Zicntr" and "Zifencei" (as in the ISA manual version
201+
// 20240411) and we chose to imply "Zicsr" and "Zifencei" (not "Zicntr")
202+
// because there will be a separate RISCV_HWPROBE_EXT_ZICNTR constant to
203+
// determine existence of the "Zicntr" extension in Linux 6.15 (as of rc1).
204+
// "fence.i" ("Zifencei") is conditionally valid on the Linux userland
205+
// (valid when CMODX is enabled).
206+
// This is a requirement of `RISCV_HWPROBE_KEY_IMA_EXT_0`-based tests.
207+
let has_ima = (out[0].key != -1) && (out[0].value & RISCV_HWPROBE_BASE_BEHAVIOR_IMA != 0);
208+
if !has_ima {
209+
break 'hwprobe;
210+
}
211+
has_i |= has_ima;
212+
enable_feature(Feature::zicsr, has_ima);
213+
enable_feature(Feature::zifencei, has_ima);
214+
enable_feature(Feature::m, has_ima);
215+
enable_feature(Feature::a, has_ima);
216+
217+
// Enable features based on `RISCV_HWPROBE_KEY_IMA_EXT_0`.
218+
if out[1].key == -1 {
219+
break 'hwprobe;
220+
}
221+
let ima_ext_0 = out[1].value;
222+
let test = |mask| (ima_ext_0 & mask) != 0;
223+
224+
enable_feature(Feature::d, test(RISCV_HWPROBE_IMA_FD)); // F is implied.
225+
enable_feature(Feature::c, test(RISCV_HWPROBE_IMA_C));
226+
227+
enable_feature(Feature::zihintntl, test(RISCV_HWPROBE_EXT_ZIHINTNTL));
228+
enable_feature(Feature::zihintpause, test(RISCV_HWPROBE_EXT_ZIHINTPAUSE));
229+
enable_feature(Feature::zimop, test(RISCV_HWPROBE_EXT_ZIMOP));
230+
enable_feature(Feature::zicboz, test(RISCV_HWPROBE_EXT_ZICBOZ));
231+
enable_feature(Feature::zicond, test(RISCV_HWPROBE_EXT_ZICOND));
232+
233+
enable_feature(Feature::zawrs, test(RISCV_HWPROBE_EXT_ZAWRS));
234+
enable_feature(Feature::zacas, test(RISCV_HWPROBE_EXT_ZACAS));
235+
enable_feature(Feature::ztso, test(RISCV_HWPROBE_EXT_ZTSO));
236+
237+
enable_feature(Feature::zba, test(RISCV_HWPROBE_EXT_ZBA));
238+
enable_feature(Feature::zbb, test(RISCV_HWPROBE_EXT_ZBB));
239+
enable_feature(Feature::zbs, test(RISCV_HWPROBE_EXT_ZBS));
240+
enable_feature(Feature::zbc, test(RISCV_HWPROBE_EXT_ZBC));
241+
242+
enable_feature(Feature::zbkb, test(RISCV_HWPROBE_EXT_ZBKB));
243+
enable_feature(Feature::zbkc, test(RISCV_HWPROBE_EXT_ZBKC));
244+
enable_feature(Feature::zbkx, test(RISCV_HWPROBE_EXT_ZBKX));
245+
enable_feature(Feature::zknd, test(RISCV_HWPROBE_EXT_ZKND));
246+
enable_feature(Feature::zkne, test(RISCV_HWPROBE_EXT_ZKNE));
247+
enable_feature(Feature::zknh, test(RISCV_HWPROBE_EXT_ZKNH));
248+
enable_feature(Feature::zksed, test(RISCV_HWPROBE_EXT_ZKSED));
249+
enable_feature(Feature::zksh, test(RISCV_HWPROBE_EXT_ZKSH));
250+
enable_feature(Feature::zkt, test(RISCV_HWPROBE_EXT_ZKT));
251+
252+
enable_feature(Feature::zcmop, test(RISCV_HWPROBE_EXT_ZCMOP));
253+
enable_feature(Feature::zca, test(RISCV_HWPROBE_EXT_ZCA));
254+
enable_feature(Feature::zcf, test(RISCV_HWPROBE_EXT_ZCF));
255+
enable_feature(Feature::zcd, test(RISCV_HWPROBE_EXT_ZCD));
256+
enable_feature(Feature::zcb, test(RISCV_HWPROBE_EXT_ZCB));
257+
258+
enable_feature(Feature::zfh, test(RISCV_HWPROBE_EXT_ZFH));
259+
enable_feature(Feature::zfhmin, test(RISCV_HWPROBE_EXT_ZFHMIN));
260+
enable_feature(Feature::zfa, test(RISCV_HWPROBE_EXT_ZFA));
261+
262+
// Use prctl (if any) to determine whether the vector extension
263+
// is enabled on the current thread (assuming the entire process
264+
// share the same status). If prctl fails (e.g. QEMU userland emulator
265+
// as of version 9.2.3), use auxiliary vector to retrieve the default
266+
// vector status on the process startup.
267+
let has_vectors = {
268+
let v_status = unsafe { libc::prctl(PR_RISCV_V_GET_CONTROL) };
269+
if v_status >= 0 {
270+
(v_status & PR_RISCV_V_VSTATE_CTRL_CUR_MASK) == PR_RISCV_V_VSTATE_CTRL_ON
271+
} else {
272+
has_v
273+
}
274+
};
275+
if has_vectors {
276+
enable_feature(Feature::v, test(RISCV_HWPROBE_IMA_V));
277+
enable_feature(Feature::zve32x, test(RISCV_HWPROBE_EXT_ZVE32X));
278+
enable_feature(Feature::zve32f, test(RISCV_HWPROBE_EXT_ZVE32F));
279+
enable_feature(Feature::zve64x, test(RISCV_HWPROBE_EXT_ZVE64X));
280+
enable_feature(Feature::zve64f, test(RISCV_HWPROBE_EXT_ZVE64F));
281+
enable_feature(Feature::zve64d, test(RISCV_HWPROBE_EXT_ZVE64D));
282+
283+
enable_feature(Feature::zvbb, test(RISCV_HWPROBE_EXT_ZVBB));
284+
enable_feature(Feature::zvbc, test(RISCV_HWPROBE_EXT_ZVBC));
285+
enable_feature(Feature::zvkb, test(RISCV_HWPROBE_EXT_ZVKB));
286+
enable_feature(Feature::zvkg, test(RISCV_HWPROBE_EXT_ZVKG));
287+
enable_feature(Feature::zvkned, test(RISCV_HWPROBE_EXT_ZVKNED));
288+
enable_feature(Feature::zvknha, test(RISCV_HWPROBE_EXT_ZVKNHA));
289+
enable_feature(Feature::zvknhb, test(RISCV_HWPROBE_EXT_ZVKNHB));
290+
enable_feature(Feature::zvksed, test(RISCV_HWPROBE_EXT_ZVKSED));
291+
enable_feature(Feature::zvksh, test(RISCV_HWPROBE_EXT_ZVKSH));
292+
enable_feature(Feature::zvkt, test(RISCV_HWPROBE_EXT_ZVKT));
293+
294+
enable_feature(Feature::zvfh, test(RISCV_HWPROBE_EXT_ZVFH));
295+
enable_feature(Feature::zvfhmin, test(RISCV_HWPROBE_EXT_ZVFHMIN));
296+
}
297+
is_v_set = true;
298+
};
299+
300+
// Set V purely depending on the auxiliary vector
301+
// only if no fine-grained vector extension detection is available.
302+
if !is_v_set {
303+
enable_feature(Feature::v, has_v);
304+
}
28305

29306
// Handle base ISA.
30-
let has_i = bit::test(auxv.hwcap, (b'i' - b'a').into());
31307
// If future RV128I is supported, implement with `enable_feature` here.
32308
// Note that we should use `target_arch` instead of `target_pointer_width`
33309
// to avoid misdetection caused by experimental ABIs such as RV64ILP32.

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

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,28 +61,67 @@ pub(crate) fn imply_features(mut value: cache::Initializer) -> cache::Initialize
6161
specification, it is denoted as a comment as follows:
6262
"defined as subset":
6363
The latter extension is described as a subset of the former.
64+
"functional":
65+
The former extension is functionally a superset of the latter
66+
(no direct references though).
6467
"errata?":
6568
The former extension cannot work without the latter
6669
(possibly an errata).
6770
*/
6871

72+
imply!(zvbb => zvkb); // defined as subset
73+
imply!(zvknhb => zvknha); // functional
74+
// Certain set of vector cryptography extensions form a group.
75+
group!(zvkn == zvkned & zvknhb & zvkb & zvkt);
76+
group!(zvknc == zvkn & zvbc);
77+
group!(zvkng == zvkn & zvkg);
78+
group!(zvks == zvksed & zvksh & zvkb & zvkt);
79+
group!(zvksc == zvks & zvbc);
80+
group!(zvksg == zvks & zvkg);
81+
// For vector cryptography, Zvknhb and Zvbc require integer arithmetic
82+
// with EEW=64 (Zve64x) while others not depending on them
83+
// require EEW=32 (Zve32x).
84+
imply!(zvknhb | zvbc => zve64x);
85+
imply!(zvbb | zvkb | zvkg | zvkned | zvknha | zvksed | zvksh => zve32x);
86+
6987
imply!(zbc => zbkc); // defined as subset
7088
group!(zkn == zbkb & zbkc & zbkx & zkne & zknd & zknh);
7189
group!(zks == zbkb & zbkc & zbkx & zksed & zksh);
7290
group!(zk == zkn & zkr & zkt);
7391

92+
imply!(zacas => zaamo);
7493
group!(a == zalrsc & zaamo);
7594

7695
group!(b == zba & zbb & zbs);
7796

97+
imply!(zcf => zca & f);
98+
imply!(zcd => zca & d);
99+
imply!(zcmop | zcb => zca);
100+
78101
imply!(zhinx => zhinxmin);
79102
imply!(zdinx | zhinxmin => zfinx);
80103

104+
imply!(zvfh => zvfhmin); // functional
105+
imply!(zvfh => zve32f & zfhmin);
106+
imply!(zvfhmin => zve32f);
107+
108+
imply!(v => zve64d);
109+
imply!(zve64d => zve64f & d);
110+
imply!(zve64f => zve64x & zve32f);
111+
imply!(zve64x => zve32x);
112+
imply!(zve32f => zve32x & f);
113+
81114
imply!(zfh => zfhmin);
82115
imply!(q => d);
83-
imply!(d | zfhmin => f);
116+
imply!(d | zfhmin | zfa => f);
117+
118+
// Relatively complex implication rules from the "C" extension.
119+
imply!(c => zca);
120+
imply!(c & d => zcd);
121+
#[cfg(target_arch = "riscv32")]
122+
imply!(c & f => zcf);
84123

85-
imply!(zicntr | zihpm | f | zfinx => zicsr);
124+
imply!(zicntr | zihpm | f | zfinx | zve32x => zicsr);
86125
imply!(zkr => zicsr); // errata?
87126
imply!(s | h => zicsr);
88127

‎crates/std_detect/tests/cpu-detection.rs‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,27 +236,47 @@ fn riscv_linux() {
236236
println!("rv32e: {}", is_riscv_feature_detected!("rv32e"));
237237
println!("rv64i: {}", is_riscv_feature_detected!("rv64i"));
238238
println!("rv128i: {}", is_riscv_feature_detected!("rv128i"));
239+
println!(
240+
"unaligned-scalar-mem: {}",
241+
is_riscv_feature_detected!("unaligned-scalar-mem")
242+
);
243+
println!(
244+
"unaligned-vector-mem: {}",
245+
is_riscv_feature_detected!("unaligned-vector-mem")
246+
);
239247
println!("zicsr: {}", is_riscv_feature_detected!("zicsr"));
240248
println!("zicntr: {}", is_riscv_feature_detected!("zicntr"));
241249
println!("zihpm: {}", is_riscv_feature_detected!("zihpm"));
242250
println!("zifencei: {}", is_riscv_feature_detected!("zifencei"));
251+
println!("zihintntl: {}", is_riscv_feature_detected!("zihintntl"));
243252
println!("zihintpause: {}", is_riscv_feature_detected!("zihintpause"));
253+
println!("zimop: {}", is_riscv_feature_detected!("zimop"));
254+
println!("zicboz: {}", is_riscv_feature_detected!("zicboz"));
255+
println!("zicond: {}", is_riscv_feature_detected!("zicond"));
244256
println!("m: {}", is_riscv_feature_detected!("m"));
245257
println!("a: {}", is_riscv_feature_detected!("a"));
246258
println!("zalrsc: {}", is_riscv_feature_detected!("zalrsc"));
247259
println!("zaamo: {}", is_riscv_feature_detected!("zaamo"));
260+
println!("zawrs: {}", is_riscv_feature_detected!("zawrs"));
261+
println!("zacas: {}", is_riscv_feature_detected!("zacas"));
248262
println!("zam: {}", is_riscv_feature_detected!("zam"));
249263
println!("ztso: {}", is_riscv_feature_detected!("ztso"));
250264
println!("f: {}", is_riscv_feature_detected!("f"));
251265
println!("d: {}", is_riscv_feature_detected!("d"));
252266
println!("q: {}", is_riscv_feature_detected!("q"));
253267
println!("zfh: {}", is_riscv_feature_detected!("zfh"));
254268
println!("zfhmin: {}", is_riscv_feature_detected!("zfhmin"));
269+
println!("zfa: {}", is_riscv_feature_detected!("zfa"));
255270
println!("zfinx: {}", is_riscv_feature_detected!("zfinx"));
256271
println!("zdinx: {}", is_riscv_feature_detected!("zdinx"));
257272
println!("zhinx: {}", is_riscv_feature_detected!("zhinx"));
258273
println!("zhinxmin: {}", is_riscv_feature_detected!("zhinxmin"));
259274
println!("c: {}", is_riscv_feature_detected!("c"));
275+
println!("zca: {}", is_riscv_feature_detected!("zca"));
276+
println!("zcf: {}", is_riscv_feature_detected!("zcf"));
277+
println!("zcd: {}", is_riscv_feature_detected!("zcd"));
278+
println!("zcb: {}", is_riscv_feature_detected!("zcb"));
279+
println!("zcmop: {}", is_riscv_feature_detected!("zcmop"));
260280
println!("b: {}", is_riscv_feature_detected!("b"));
261281
println!("zba: {}", is_riscv_feature_detected!("zba"));
262282
println!("zbb: {}", is_riscv_feature_detected!("zbb"));
@@ -276,6 +296,29 @@ fn riscv_linux() {
276296
println!("zk: {}", is_riscv_feature_detected!("zk"));
277297
println!("zkt: {}", is_riscv_feature_detected!("zkt"));
278298
println!("v: {}", is_riscv_feature_detected!("v"));
299+
println!("zve32x: {}", is_riscv_feature_detected!("zve32x"));
300+
println!("zve32f: {}", is_riscv_feature_detected!("zve32f"));
301+
println!("zve64x: {}", is_riscv_feature_detected!("zve64x"));
302+
println!("zve64f: {}", is_riscv_feature_detected!("zve64f"));
303+
println!("zve64d: {}", is_riscv_feature_detected!("zve64d"));
304+
println!("zvfh: {}", is_riscv_feature_detected!("zvfh"));
305+
println!("zvfhmin: {}", is_riscv_feature_detected!("zvfhmin"));
306+
println!("zvbb: {}", is_riscv_feature_detected!("zvbb"));
307+
println!("zvbc: {}", is_riscv_feature_detected!("zvbc"));
308+
println!("zvkb: {}", is_riscv_feature_detected!("zvkb"));
309+
println!("zvkg: {}", is_riscv_feature_detected!("zvkg"));
310+
println!("zvkned: {}", is_riscv_feature_detected!("zvkned"));
311+
println!("zvknha: {}", is_riscv_feature_detected!("zvknha"));
312+
println!("zvknhb: {}", is_riscv_feature_detected!("zvknhb"));
313+
println!("zvksed: {}", is_riscv_feature_detected!("zvksed"));
314+
println!("zvksh: {}", is_riscv_feature_detected!("zvksh"));
315+
println!("zvkn: {}", is_riscv_feature_detected!("zvkn"));
316+
println!("zvknc: {}", is_riscv_feature_detected!("zvknc"));
317+
println!("zvkng: {}", is_riscv_feature_detected!("zvkng"));
318+
println!("zvks: {}", is_riscv_feature_detected!("zvks"));
319+
println!("zvksc: {}", is_riscv_feature_detected!("zvksc"));
320+
println!("zvksg: {}", is_riscv_feature_detected!("zvksg"));
321+
println!("zvkt: {}", is_riscv_feature_detected!("zvkt"));
279322
println!("svnapot: {}", is_riscv_feature_detected!("svnapot"));
280323
println!("svpbmt: {}", is_riscv_feature_detected!("svpbmt"));
281324
println!("svinval: {}", is_riscv_feature_detected!("svinval"));

0 commit comments

Comments
 (0)
Please sign in to comment.