From 8ce37d7928dd7dbe029094ff6bd7ff810989f8bf Mon Sep 17 00:00:00 2001 From: Tsukasa OI Date: Fri, 11 Apr 2025 01:13:44 +0000 Subject: [PATCH 01/11] RISC-V: tidying: Prefer more canonical reference 1. Use canonical kernel.org repository instead of the GitHub mirror. 2. Refer to the fixed commit to guarantee access. 3. Use `uapi` part to ensure that the feature detection is primarily intended for user-mode programs. --- crates/std_detect/src/detect/os/linux/riscv.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/std_detect/src/detect/os/linux/riscv.rs b/crates/std_detect/src/detect/os/linux/riscv.rs index 2a9671d75d..74129a2f45 100644 --- a/crates/std_detect/src/detect/os/linux/riscv.rs +++ b/crates/std_detect/src/detect/os/linux/riscv.rs @@ -21,7 +21,7 @@ pub(crate) fn detect_features() -> cache::Initializer { // The values are part of the platform-specific [asm/hwcap.h][hwcap] // - // [hwcap]: https://github.com/torvalds/linux/blob/master/arch/riscv/include/asm/hwcap.h + // [hwcap]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/riscv/include/uapi/asm/hwcap.h?h=v6.14 let auxv = auxvec::auxv().expect("read auxvec"); // should not fail on RISC-V platform #[allow(clippy::eq_op)] enable_feature( From 9343fd8f090870b72d0ea1b96629b27ab216c2cf Mon Sep 17 00:00:00 2001 From: Tsukasa OI Date: Fri, 11 Apr 2025 01:13:44 +0000 Subject: [PATCH 02/11] RISC-V: tidying: Handling of base ISA This commit makes handling of the base ISA a separate block. Co-Authored-By: Taiki Endo --- .../std_detect/src/detect/os/linux/riscv.rs | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/crates/std_detect/src/detect/os/linux/riscv.rs b/crates/std_detect/src/detect/os/linux/riscv.rs index 74129a2f45..3e406b2c36 100644 --- a/crates/std_detect/src/detect/os/linux/riscv.rs +++ b/crates/std_detect/src/detect/os/linux/riscv.rs @@ -44,28 +44,32 @@ pub(crate) fn detect_features() -> cache::Initializer { &[Feature::f, Feature::zicsr], bit::test(auxv.hwcap, (b'f' - b'a').into()), ); + enable_feature( + &mut value, + Feature::h, + bit::test(auxv.hwcap, (b'h' - b'a').into()), + ); + enable_feature( + &mut value, + Feature::m, + bit::test(auxv.hwcap, (b'm' - b'a').into()), + ); + + // Handle base ISA. let has_i = bit::test(auxv.hwcap, (b'i' - b'a').into()); // If future RV128I is supported, implement with `enable_feature` here #[cfg(target_pointer_width = "64")] enable_feature(&mut value, Feature::rv64i, has_i); #[cfg(target_pointer_width = "32")] enable_feature(&mut value, Feature::rv32i, has_i); + // FIXME: e is not exposed in any of asm/hwcap.h, uapi/asm/hwcap.h, uapi/asm/hwprobe.h #[cfg(target_pointer_width = "32")] enable_feature( &mut value, Feature::rv32e, bit::test(auxv.hwcap, (b'e' - b'a').into()), ); - enable_feature( - &mut value, - Feature::h, - bit::test(auxv.hwcap, (b'h' - b'a').into()), - ); - enable_feature( - &mut value, - Feature::m, - bit::test(auxv.hwcap, (b'm' - b'a').into()), - ); + // FIXME: Auxvec does not show supervisor feature support, but this mode may be useful // to detect when Rust is used to write Linux kernel modules. // These should be more than Auxvec way to detect supervisor features. From 9dc6dcf4dde84f2acad8fbef9bbebe7c4d07ae6a Mon Sep 17 00:00:00 2001 From: Tsukasa OI Date: Fri, 11 Apr 2025 01:13:44 +0000 Subject: [PATCH 03/11] RISC-V: tidying: Make auxvec-based enablement a block Because this function will be no longer auxvec-only, this commit adds a comment to mark auxvec-based part. It *does not* add a comment to "base ISA" part because it may also use `riscv_hwprobe`-based results. --- crates/std_detect/src/detect/os/linux/riscv.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/std_detect/src/detect/os/linux/riscv.rs b/crates/std_detect/src/detect/os/linux/riscv.rs index 3e406b2c36..6d77c89b47 100644 --- a/crates/std_detect/src/detect/os/linux/riscv.rs +++ b/crates/std_detect/src/detect/os/linux/riscv.rs @@ -19,6 +19,7 @@ pub(crate) fn detect_features() -> cache::Initializer { } }; + // Use auxiliary vector to enable single-letter ISA extensions and Zicsr. // The values are part of the platform-specific [asm/hwcap.h][hwcap] // // [hwcap]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/riscv/include/uapi/asm/hwcap.h?h=v6.14 From 9b09cc2e696ae27a4a1387a1482f890c97c7457f Mon Sep 17 00:00:00 2001 From: Tsukasa OI Date: Fri, 11 Apr 2025 01:13:44 +0000 Subject: [PATCH 04/11] RISC-V: Remove `enable_features` This commit prepares common infrastructure for extension implication by removing `enable_features` closure which makes each feature test longer (because it needs extra `value` argument each time we test a feature). It comes with the overhead to enable each feature separately but later mitigated by the OS-independent extension implication logic. --- .../std_detect/src/detect/os/linux/riscv.rs | 58 +++++-------------- 1 file changed, 13 insertions(+), 45 deletions(-) diff --git a/crates/std_detect/src/detect/os/linux/riscv.rs b/crates/std_detect/src/detect/os/linux/riscv.rs index 6d77c89b47..9851f9f89f 100644 --- a/crates/std_detect/src/detect/os/linux/riscv.rs +++ b/crates/std_detect/src/detect/os/linux/riscv.rs @@ -6,18 +6,11 @@ use crate::detect::{Feature, bit, cache}; /// Read list of supported features from the auxiliary vector. pub(crate) fn detect_features() -> cache::Initializer { let mut value = cache::Initializer::default(); - let enable_feature = |value: &mut cache::Initializer, feature, enable| { + let mut enable_feature = |feature, enable| { if enable { value.set(feature as u32); } }; - let enable_features = |value: &mut cache::Initializer, feature_slice: &[Feature], enable| { - if enable { - for feature in feature_slice { - value.set(*feature as u32); - } - } - }; // Use auxiliary vector to enable single-letter ISA extensions and Zicsr. // The values are part of the platform-specific [asm/hwcap.h][hwcap] @@ -25,51 +18,26 @@ pub(crate) fn detect_features() -> cache::Initializer { // [hwcap]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/riscv/include/uapi/asm/hwcap.h?h=v6.14 let auxv = auxvec::auxv().expect("read auxvec"); // should not fail on RISC-V platform #[allow(clippy::eq_op)] - enable_feature( - &mut value, - Feature::a, - bit::test(auxv.hwcap, (b'a' - b'a').into()), - ); - enable_feature( - &mut value, - Feature::c, - bit::test(auxv.hwcap, (b'c' - b'a').into()), - ); - enable_features( - &mut value, - &[Feature::d, Feature::f, Feature::zicsr], - bit::test(auxv.hwcap, (b'd' - b'a').into()), - ); - enable_features( - &mut value, - &[Feature::f, Feature::zicsr], - bit::test(auxv.hwcap, (b'f' - b'a').into()), - ); - enable_feature( - &mut value, - Feature::h, - bit::test(auxv.hwcap, (b'h' - b'a').into()), - ); - enable_feature( - &mut value, - Feature::m, - bit::test(auxv.hwcap, (b'm' - b'a').into()), - ); + enable_feature(Feature::a, bit::test(auxv.hwcap, (b'a' - b'a').into())); + enable_feature(Feature::c, bit::test(auxv.hwcap, (b'c' - b'a').into())); + let has_d = bit::test(auxv.hwcap, (b'd' - b'a').into()); + let has_f = bit::test(auxv.hwcap, (b'f' - b'a').into()); + enable_feature(Feature::d, has_d); + enable_feature(Feature::f, has_d | has_f); + enable_feature(Feature::zicsr, has_d | has_f); + enable_feature(Feature::h, bit::test(auxv.hwcap, (b'h' - b'a').into())); + enable_feature(Feature::m, bit::test(auxv.hwcap, (b'm' - b'a').into())); // Handle base ISA. let has_i = bit::test(auxv.hwcap, (b'i' - b'a').into()); // If future RV128I is supported, implement with `enable_feature` here #[cfg(target_pointer_width = "64")] - enable_feature(&mut value, Feature::rv64i, has_i); + enable_feature(Feature::rv64i, has_i); #[cfg(target_pointer_width = "32")] - enable_feature(&mut value, Feature::rv32i, has_i); + enable_feature(Feature::rv32i, has_i); // FIXME: e is not exposed in any of asm/hwcap.h, uapi/asm/hwcap.h, uapi/asm/hwprobe.h #[cfg(target_pointer_width = "32")] - enable_feature( - &mut value, - Feature::rv32e, - bit::test(auxv.hwcap, (b'e' - b'a').into()), - ); + enable_feature(Feature::rv32e, bit::test(auxv.hwcap, (b'e' - b'a').into())); // FIXME: Auxvec does not show supervisor feature support, but this mode may be useful // to detect when Rust is used to write Linux kernel modules. From 1e7fb3d30130f98ea9569525b1082ebbe571f358 Mon Sep 17 00:00:00 2001 From: Tsukasa OI Date: Fri, 11 Apr 2025 01:13:44 +0000 Subject: [PATCH 05/11] RISC-V: Use `target_arch` for RV(32|64) detection As Taiki Endo pointed out, there's a problem if we continue using `target_pointer_width` values to detect an architecture because: * There are separate `target_arch`s already and * There is an experimental ABI (not ratified though): RV64ILP32. cf. Co-Authored-By: Taiki Endo --- crates/std_detect/src/detect/os/linux/riscv.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/std_detect/src/detect/os/linux/riscv.rs b/crates/std_detect/src/detect/os/linux/riscv.rs index 9851f9f89f..ee74c7c29a 100644 --- a/crates/std_detect/src/detect/os/linux/riscv.rs +++ b/crates/std_detect/src/detect/os/linux/riscv.rs @@ -30,13 +30,15 @@ pub(crate) fn detect_features() -> cache::Initializer { // Handle base ISA. let has_i = bit::test(auxv.hwcap, (b'i' - b'a').into()); - // If future RV128I is supported, implement with `enable_feature` here - #[cfg(target_pointer_width = "64")] + // If future RV128I is supported, implement with `enable_feature` here. + // Note that we should use `target_arch` instead of `target_pointer_width` + // to avoid misdetection caused by experimental ABIs such as RV64ILP32. + #[cfg(target_arch = "riscv64")] enable_feature(Feature::rv64i, has_i); - #[cfg(target_pointer_width = "32")] + #[cfg(target_arch = "riscv32")] enable_feature(Feature::rv32i, has_i); // FIXME: e is not exposed in any of asm/hwcap.h, uapi/asm/hwcap.h, uapi/asm/hwprobe.h - #[cfg(target_pointer_width = "32")] + #[cfg(target_arch = "riscv32")] enable_feature(Feature::rv32e, bit::test(auxv.hwcap, (b'e' - b'a').into())); // FIXME: Auxvec does not show supervisor feature support, but this mode may be useful From 5d25aef31f4f632eca59f9ad0d19a51517538666 Mon Sep 17 00:00:00 2001 From: Tsukasa OI Date: Fri, 11 Apr 2025 01:13:44 +0000 Subject: [PATCH 06/11] RISC-V: Add two "A" extension subsets The "A" extension comprises instructions provided by the "Zaamo" and "Zalrsc" extensions. To prepare for the "Zacas" extension (which provides compare-and-swap instructions and discoverable from Linux) which depends on the "Zaamo" extension, it would be better to support those subsets. --- crates/std_detect/src/detect/arch/riscv.rs | 6 ++++++ crates/std_detect/src/detect/os/linux/riscv.rs | 5 ++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/crates/std_detect/src/detect/arch/riscv.rs b/crates/std_detect/src/detect/arch/riscv.rs index 29d8da8e12..271700aefa 100644 --- a/crates/std_detect/src/detect/arch/riscv.rs +++ b/crates/std_detect/src/detect/arch/riscv.rs @@ -30,6 +30,8 @@ features! { /// * RV32I: `"rv32i"` /// * RV64I: `"rv64i"` /// * A: `"a"` + /// * Zaamo: `"zaamo"` + /// * Zalrsc: `"zalrsc"` /// * Bit-Manipulation Extensions: /// * Zba: `"zba"` /// * Zbb: `"zbb"` @@ -122,6 +124,10 @@ features! { @FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] a: "a"; /// "A" Extension for Atomic Instructions + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zalrsc: "zalrsc"; + /// "Zalrsc" Extension for Load-Reserved/Store-Conditional Instructions + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zaamo: "zaamo"; + /// "Zaamo" Extension for Atomic Memory Operations @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zam: "zam"; without cfg check: true; /// "Zam" Extension for Misaligned Atomics diff --git a/crates/std_detect/src/detect/os/linux/riscv.rs b/crates/std_detect/src/detect/os/linux/riscv.rs index ee74c7c29a..33deba9335 100644 --- a/crates/std_detect/src/detect/os/linux/riscv.rs +++ b/crates/std_detect/src/detect/os/linux/riscv.rs @@ -18,7 +18,10 @@ pub(crate) fn detect_features() -> cache::Initializer { // [hwcap]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/riscv/include/uapi/asm/hwcap.h?h=v6.14 let auxv = auxvec::auxv().expect("read auxvec"); // should not fail on RISC-V platform #[allow(clippy::eq_op)] - enable_feature(Feature::a, bit::test(auxv.hwcap, (b'a' - b'a').into())); + let has_a = bit::test(auxv.hwcap, (b'a' - b'a').into()); + enable_feature(Feature::a, has_a); + enable_feature(Feature::zalrsc, has_a); + enable_feature(Feature::zaamo, has_a); enable_feature(Feature::c, bit::test(auxv.hwcap, (b'c' - b'a').into())); let has_d = bit::test(auxv.hwcap, (b'd' - b'a').into()); let has_f = bit::test(auxv.hwcap, (b'f' - b'a').into()); From 587c784cd4ad4cada54df6c8c1e87ea8cf6bdb16 Mon Sep 17 00:00:00 2001 From: Tsukasa OI Date: Fri, 11 Apr 2025 01:13:44 +0000 Subject: [PATCH 07/11] RISC-V: Add placeholder for the "B" extension The "B" extension is once abandoned (instead, it is ratified as a collection of "Zb*" extensions). However, it is later redefined and ratified as a superset of "Zba", "Zbb" and "Zbs" extensions (but not "Zbc" carry-less multiplication for limited benefits and implementation cost). Although non-functional (because feature detection is not yet implemented), it provides the foundation to implement this extension (along with straightforward documentation showing subsets of "B"). --- crates/std_detect/src/detect/arch/riscv.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/std_detect/src/detect/arch/riscv.rs b/crates/std_detect/src/detect/arch/riscv.rs index 271700aefa..4b1cbdcda5 100644 --- a/crates/std_detect/src/detect/arch/riscv.rs +++ b/crates/std_detect/src/detect/arch/riscv.rs @@ -32,10 +32,9 @@ features! { /// * A: `"a"` /// * Zaamo: `"zaamo"` /// * Zalrsc: `"zalrsc"` - /// * Bit-Manipulation Extensions: + /// * B: `"b"` /// * Zba: `"zba"` /// * Zbb: `"zbb"` - /// * Zbc: `"zbc"` /// * Zbs: `"zbs"` /// * C: `"c"` /// * D: `"d"` @@ -54,6 +53,7 @@ features! { /// * Zdinx: `"zdinx"` /// * Zhinx: `"zhinx"` /// * Zhinxmin: `"zhinxmin"` + /// * Zbc: `"zbc"` /// * Zbkb: `"zbkb"` /// * Zbkc: `"zbkc"` /// * Zbkx: `"zbkx"` @@ -159,6 +159,9 @@ features! { @FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] c: "c"; /// "C" Extension for Compressed Instructions + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] b: "b"; + without cfg check: true; + /// "B" Extension for Bit Manipulation @FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zba: "zba"; /// "Zba" Extension for Address Generation @FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zbb: "zbb"; From 5f83daaaf5d4d03ce8a6f0abd5641ba323cf544b Mon Sep 17 00:00:00 2001 From: Tsukasa OI Date: Fri, 11 Apr 2025 01:13:44 +0000 Subject: [PATCH 08/11] RISC-V: Add RISC-V + Linux / Android test This is ported from Taiki Endo's branch and sorted by the `@FEATURE` order as in `src/detect/arch/riscv.rs`. Co-Authored-By: Taiki Endo --- crates/std_detect/tests/cpu-detection.rs | 65 ++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/crates/std_detect/tests/cpu-detection.rs b/crates/std_detect/tests/cpu-detection.rs index 2c9cd95d29..8a40997f75 100644 --- a/crates/std_detect/tests/cpu-detection.rs +++ b/crates/std_detect/tests/cpu-detection.rs @@ -5,6 +5,10 @@ any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_aarch64_feature_detection) )] +#![cfg_attr( + any(target_arch = "riscv32", target_arch = "riscv64"), + feature(stdarch_riscv_feature_detection) +)] #![cfg_attr(target_arch = "powerpc", feature(stdarch_powerpc_feature_detection))] #![cfg_attr(target_arch = "powerpc64", feature(stdarch_powerpc_feature_detection))] #![cfg_attr(target_arch = "s390x", feature(stdarch_s390x_feature_detection))] @@ -15,6 +19,8 @@ target_arch = "arm", target_arch = "aarch64", target_arch = "arm64ec", + target_arch = "riscv32", + target_arch = "riscv64", target_arch = "powerpc", target_arch = "powerpc64", target_arch = "s390x", @@ -220,6 +226,65 @@ fn aarch64_darwin() { println!("sha3: {:?}", is_aarch64_feature_detected!("sha3")); } +#[test] +#[cfg(all( + any(target_arch = "riscv32", target_arch = "riscv64"), + any(target_os = "linux", target_os = "android") +))] +fn riscv_linux() { + println!("rv32i: {}", is_riscv_feature_detected!("rv32i")); + println!("rv32e: {}", is_riscv_feature_detected!("rv32e")); + println!("rv64i: {}", is_riscv_feature_detected!("rv64i")); + println!("rv128i: {}", is_riscv_feature_detected!("rv128i")); + println!("zicsr: {}", is_riscv_feature_detected!("zicsr")); + println!("zicntr: {}", is_riscv_feature_detected!("zicntr")); + println!("zihpm: {}", is_riscv_feature_detected!("zihpm")); + println!("zifencei: {}", is_riscv_feature_detected!("zifencei")); + println!("zihintpause: {}", is_riscv_feature_detected!("zihintpause")); + println!("m: {}", is_riscv_feature_detected!("m")); + println!("a: {}", is_riscv_feature_detected!("a")); + println!("zalrsc: {}", is_riscv_feature_detected!("zalrsc")); + println!("zaamo: {}", is_riscv_feature_detected!("zaamo")); + println!("zam: {}", is_riscv_feature_detected!("zam")); + println!("ztso: {}", is_riscv_feature_detected!("ztso")); + println!("f: {}", is_riscv_feature_detected!("f")); + println!("d: {}", is_riscv_feature_detected!("d")); + println!("q: {}", is_riscv_feature_detected!("q")); + println!("zfh: {}", is_riscv_feature_detected!("zfh")); + println!("zfhmin: {}", is_riscv_feature_detected!("zfhmin")); + println!("zfinx: {}", is_riscv_feature_detected!("zfinx")); + println!("zdinx: {}", is_riscv_feature_detected!("zdinx")); + println!("zhinx: {}", is_riscv_feature_detected!("zhinx")); + println!("zhinxmin: {}", is_riscv_feature_detected!("zhinxmin")); + println!("c: {}", is_riscv_feature_detected!("c")); + println!("b: {}", is_riscv_feature_detected!("b")); + println!("zba: {}", is_riscv_feature_detected!("zba")); + println!("zbb: {}", is_riscv_feature_detected!("zbb")); + println!("zbc: {}", is_riscv_feature_detected!("zbc")); + println!("zbs: {}", is_riscv_feature_detected!("zbs")); + println!("zbkb: {}", is_riscv_feature_detected!("zbkb")); + println!("zbkc: {}", is_riscv_feature_detected!("zbkc")); + println!("zbkx: {}", is_riscv_feature_detected!("zbkx")); + println!("zknd: {}", is_riscv_feature_detected!("zknd")); + println!("zkne: {}", is_riscv_feature_detected!("zkne")); + println!("zknh: {}", is_riscv_feature_detected!("zknh")); + println!("zksed: {}", is_riscv_feature_detected!("zksed")); + println!("zksh: {}", is_riscv_feature_detected!("zksh")); + println!("zkr: {}", is_riscv_feature_detected!("zkr")); + println!("zkn: {}", is_riscv_feature_detected!("zkn")); + println!("zks: {}", is_riscv_feature_detected!("zks")); + println!("zk: {}", is_riscv_feature_detected!("zk")); + println!("zkt: {}", is_riscv_feature_detected!("zkt")); + println!("v: {}", is_riscv_feature_detected!("v")); + println!("svnapot: {}", is_riscv_feature_detected!("svnapot")); + println!("svpbmt: {}", is_riscv_feature_detected!("svpbmt")); + println!("svinval: {}", is_riscv_feature_detected!("svinval")); + println!("h: {}", is_riscv_feature_detected!("h")); + println!("s: {}", is_riscv_feature_detected!("s")); + println!("j: {}", is_riscv_feature_detected!("j")); + println!("p: {}", is_riscv_feature_detected!("p")); +} + #[test] #[cfg(all(target_arch = "powerpc", target_os = "linux"))] fn powerpc_linux() { From 73212e767958bcaa1f0bb3432a9414dee9e93910 Mon Sep 17 00:00:00 2001 From: Tsukasa OI Date: Sun, 13 Apr 2025 04:35:58 +0000 Subject: [PATCH 09/11] RISC-V: OS-independent implication logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds the OS-independent extension implication logic for RISC-V. It implements: 1. Regular implication (A → B) a. "the extension A implies the extension B" b. "the extension A requires the extension B" c. "the extension A depends on the extension B" 2. Extension group or shorthand (A == B1 & B2...) a. "the extension A is shorthand for other extensions: B1, B2..." b. "the extension A comprises instructions provided by B1, B2..." This is implemented as (A → B1 & B2... + B1 & B2... → A) where the former is a regular implication as required by specifications and the latter is a "reverse" implication to improve usability. and prepares for: 3. Implication with multiple requirements (A1 & A2... → B) a. "A1 + A2 implies B" b. (implicitly used to implement reverse implication of case 2) Although it uses macros and iterators, good optimizers turn the series of implications into fast bit-manipulation operations. In the case 2 (extension group or shorthand; where a superset extension is just a collection of other subextensions and provides no features by a superset itself), specifications do specify that an extension group implies its members but not vice versa. However, implying an extension group from its members would improve usability on the feature detection (especially when the feature provider does not provide existence of such extension group but provides existence of its members). Similar "reverse implication" on RISC-V is implemented on LLVM. Case 3 is implicitly used to implement reverse implication of case 2 but there's another use case: implication with multiple requirements like "Zcf" and "Zcd" extensions (not yet implemented in this crate for now). To handle extension groups perfectly, we need to loop implication several times (until they converge; normally 2 times and up to 4 times when we add most of `riscv_hwprobe`-based features). To make implementation of that loop possible, `cache::Initializer` is modified to implement `PartialEq` and `Eq`. --- crates/std_detect/src/detect/cache.rs | 2 +- crates/std_detect/src/detect/mod.rs | 3 + .../std_detect/src/detect/os/linux/riscv.rs | 17 +- crates/std_detect/src/detect/os/riscv.rs | 148 ++++++++++++++++++ 4 files changed, 158 insertions(+), 12 deletions(-) create mode 100644 crates/std_detect/src/detect/os/riscv.rs diff --git a/crates/std_detect/src/detect/cache.rs b/crates/std_detect/src/detect/cache.rs index 3056c28a80..83bcedea61 100644 --- a/crates/std_detect/src/detect/cache.rs +++ b/crates/std_detect/src/detect/cache.rs @@ -31,7 +31,7 @@ const CACHE_CAPACITY: u32 = 93; /// This type is used to initialize the cache // The derived `Default` implementation will initialize the field to zero, // which is what we want. -#[derive(Copy, Clone, Default)] +#[derive(Copy, Clone, Default, PartialEq, Eq)] pub(crate) struct Initializer(u128); // NOTE: the `debug_assert!` would catch that we do not add more Features than diff --git a/crates/std_detect/src/detect/mod.rs b/crates/std_detect/src/detect/mod.rs index a61400a555..8fd3d95793 100644 --- a/crates/std_detect/src/detect/mod.rs +++ b/crates/std_detect/src/detect/mod.rs @@ -49,6 +49,9 @@ cfg_if! { #[path = "os/x86.rs"] mod os; } else if #[cfg(all(any(target_os = "linux", target_os = "android"), feature = "libc"))] { + #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] + #[path = "os/riscv.rs"] + mod riscv; #[path = "os/linux/mod.rs"] mod os; } else if #[cfg(all(target_os = "freebsd", feature = "libc"))] { diff --git a/crates/std_detect/src/detect/os/linux/riscv.rs b/crates/std_detect/src/detect/os/linux/riscv.rs index 33deba9335..6cdc72dff5 100644 --- a/crates/std_detect/src/detect/os/linux/riscv.rs +++ b/crates/std_detect/src/detect/os/linux/riscv.rs @@ -1,5 +1,6 @@ //! Run-time feature detection for RISC-V on Linux. +use super::super::riscv::imply_features; use super::auxvec; use crate::detect::{Feature, bit, cache}; @@ -12,22 +13,16 @@ pub(crate) fn detect_features() -> cache::Initializer { } }; - // Use auxiliary vector to enable single-letter ISA extensions and Zicsr. + // Use auxiliary vector to enable single-letter ISA extensions. // The values are part of the platform-specific [asm/hwcap.h][hwcap] // // [hwcap]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/riscv/include/uapi/asm/hwcap.h?h=v6.14 let auxv = auxvec::auxv().expect("read auxvec"); // should not fail on RISC-V platform #[allow(clippy::eq_op)] - let has_a = bit::test(auxv.hwcap, (b'a' - b'a').into()); - enable_feature(Feature::a, has_a); - enable_feature(Feature::zalrsc, has_a); - enable_feature(Feature::zaamo, has_a); + enable_feature(Feature::a, bit::test(auxv.hwcap, (b'a' - b'a').into())); enable_feature(Feature::c, bit::test(auxv.hwcap, (b'c' - b'a').into())); - let has_d = bit::test(auxv.hwcap, (b'd' - b'a').into()); - let has_f = bit::test(auxv.hwcap, (b'f' - b'a').into()); - enable_feature(Feature::d, has_d); - enable_feature(Feature::f, has_d | has_f); - enable_feature(Feature::zicsr, has_d | has_f); + enable_feature(Feature::d, bit::test(auxv.hwcap, (b'd' - b'a').into())); + enable_feature(Feature::f, bit::test(auxv.hwcap, (b'f' - b'a').into())); enable_feature(Feature::h, bit::test(auxv.hwcap, (b'h' - b'a').into())); enable_feature(Feature::m, bit::test(auxv.hwcap, (b'm' - b'a').into())); @@ -48,5 +43,5 @@ pub(crate) fn detect_features() -> cache::Initializer { // to detect when Rust is used to write Linux kernel modules. // These should be more than Auxvec way to detect supervisor features. - value + imply_features(value) } diff --git a/crates/std_detect/src/detect/os/riscv.rs b/crates/std_detect/src/detect/os/riscv.rs new file mode 100644 index 0000000000..b268eff8a1 --- /dev/null +++ b/crates/std_detect/src/detect/os/riscv.rs @@ -0,0 +1,148 @@ +//! Run-time feature detection utility for RISC-V. +//! +//! On RISC-V, full feature detection needs a help of one or more +//! feature detection mechanisms (usually provided by the operating system). +//! +//! RISC-V architecture defines many extensions and some have dependency to others. +//! More importantly, some of them cannot be enabled without resolving such +//! dependencies due to limited set of features that such mechanisms provide. +//! +//! This module provides an OS-independent utility to process such relations +//! between RISC-V extensions. + +use crate::detect::{Feature, cache}; + +/// Imply features by the given set of enabled features. +/// +/// Note that it does not perform any consistency checks including existence of +/// conflicting extensions and/or complicated requirements. Eliminating such +/// inconsistencies is the responsibility of the feature detection logic and +/// its provider(s). +pub(crate) fn imply_features(mut value: cache::Initializer) -> cache::Initializer { + loop { + // Check convergence of the feature flags later. + let prev = value; + + // Expect that the optimizer turns repeated operations into + // a fewer number of bit-manipulation operations. + macro_rules! imply { + // Regular implication: + // A1 => (B1[, B2...]), A2 => (B1[, B2...]) and so on. + ($($from: ident)|+ => $($to: ident)&+) => { + if [$(Feature::$from as u32),+].iter().any(|&x| value.test(x)) { + $( + value.set(Feature::$to as u32); + )+ + } + }; + // Implication with multiple requirements: + // A1 && A2 ... => (B1[, B2...]). + ($($from: ident)&+ => $($to: ident)&+) => { + if [$(Feature::$from as u32),+].iter().all(|&x| value.test(x)) { + $( + value.set(Feature::$to as u32); + )+ + } + }; + } + macro_rules! group { + ($group: ident == $($member: ident)&+) => { + // Forward implication as defined in the specifications. + imply!($group => $($member)&+); + // Reverse implication to "group extension" from its members. + // This is not a part of specifications but convenient for + // feature detection and implemented in e.g. LLVM. + imply!($($member)&+ => $group); + }; + } + + /* + If a dependency/implication is not explicitly stated in the + specification, it is denoted as a comment as follows: + "defined as subset": + The latter extension is described as a subset of the former + (but the evidence is weak). + */ + + imply!(zbc => zbkc); // defined as subset + group!(zkn == zbkb & zbkc & zbkx & zkne & zknd & zknh); + group!(zks == zbkb & zbkc & zbkx & zksed & zksh); + group!(zk == zkn & zkr & zkt); + + group!(a == zalrsc & zaamo); + + group!(b == zba & zbb & zbs); + + imply!(zhinx => zhinxmin); + imply!(zdinx | zhinxmin => zfinx); + + imply!(zfh => zfhmin); + imply!(q => d); + imply!(d | zfhmin => f); + + imply!(zicntr | zihpm | f | zfinx => zicsr); + imply!(s | h => zicsr); + + // Loop until the feature flags converge. + if prev == value { + return value; + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn simple_direct() { + let mut value = cache::Initializer::default(); + value.set(Feature::f as u32); + // F (and other extensions with CSRs) -> Zicsr + assert!(imply_features(value).test(Feature::zicsr as u32)); + } + + #[test] + fn simple_indirect() { + let mut value = cache::Initializer::default(); + value.set(Feature::q as u32); + // Q -> D, D -> F, F -> Zicsr + assert!(imply_features(value).test(Feature::zicsr as u32)); + } + + #[test] + fn group_simple_forward() { + let mut value = cache::Initializer::default(); + // A -> Zalrsc & Zaamo (forward implication) + value.set(Feature::a as u32); + let value = imply_features(value); + assert!(value.test(Feature::zalrsc as u32)); + assert!(value.test(Feature::zaamo as u32)); + } + + #[test] + fn group_simple_backward() { + let mut value = cache::Initializer::default(); + // Zalrsc & Zaamo -> A (reverse implication) + value.set(Feature::zalrsc as u32); + value.set(Feature::zaamo as u32); + assert!(imply_features(value).test(Feature::a as u32)); + } + + #[test] + fn group_complex_convergence() { + let mut value = cache::Initializer::default(); + // Needs 2 iterations to converge + // (and 3rd iteration for convergence checking): + // 1. [Zk] -> Zkn & Zkr & Zkt + // 2. Zkn -> {Zbkb} & {Zbkc} & {Zbkx} & {Zkne} & {Zknd} & {Zknh} + value.set(Feature::zk as u32); + let value = imply_features(value); + assert!(value.test(Feature::zbkb as u32)); + assert!(value.test(Feature::zbkc as u32)); + assert!(value.test(Feature::zbkx as u32)); + assert!(value.test(Feature::zkne as u32)); + assert!(value.test(Feature::zknd as u32)); + assert!(value.test(Feature::zknh as u32)); + } +} From 118e9594a98a21e71b92fb66e02fb627aeef49c4 Mon Sep 17 00:00:00 2001 From: Tsukasa OI Date: Sun, 13 Apr 2025 04:35:58 +0000 Subject: [PATCH 10/11] 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 --- crates/std_detect/README.md | 5 +- crates/std_detect/src/detect/arch/riscv.rs | 130 ++++++++ .../std_detect/src/detect/os/linux/riscv.rs | 280 +++++++++++++++++- crates/std_detect/src/detect/os/riscv.rs | 79 ++++- crates/std_detect/tests/cpu-detection.rs | 43 +++ 5 files changed, 521 insertions(+), 16 deletions(-) diff --git a/crates/std_detect/README.md b/crates/std_detect/README.md index 5191b2f665..0053d777aa 100644 --- a/crates/std_detect/README.md +++ b/crates/std_detect/README.md @@ -56,11 +56,14 @@ crate from working on applications in which `std` is not available. [`cupid`](https://crates.io/crates/cupid) crate. * Linux/Android: - * `arm{32, 64}`, `mips{32,64}{,el}`, `powerpc{32,64}{,le}`, `riscv{32,64}`, `loongarch64`, `s390x`: + * `arm{32, 64}`, `mips{32,64}{,el}`, `powerpc{32,64}{,le}`, `loongarch64`, `s390x`: `std_detect` supports these on Linux by querying ELF auxiliary vectors (using `getauxval` when available), and if that fails, by querying `/proc/cpuinfo`. * `arm64`: partial support for doing run-time feature detection by directly querying `mrs` is implemented for Linux >= 4.11, but not enabled by default. + * `riscv{32,64}`: + `std_detect` supports these on Linux by querying `riscv_hwprobe`, and + by querying ELF auxiliary vectors (using `getauxval` when available). * FreeBSD: * `arm32`, `powerpc64`: `std_detect` supports these on FreeBSD by querying ELF diff --git a/crates/std_detect/src/detect/arch/riscv.rs b/crates/std_detect/src/detect/arch/riscv.rs index 4b1cbdcda5..e9a6c96af1 100644 --- a/crates/std_detect/src/detect/arch/riscv.rs +++ b/crates/std_detect/src/detect/arch/riscv.rs @@ -37,22 +37,39 @@ features! { /// * Zbb: `"zbb"` /// * Zbs: `"zbs"` /// * C: `"c"` + /// * Zca: `"zca"` + /// * Zcd: `"zcd"` (if D is enabled) + /// * Zcf: `"zcf"` (if F is enabled on RV32) /// * D: `"d"` /// * F: `"f"` /// * M: `"m"` /// * Q: `"q"` /// * V: `"v"` + /// * Zve32x: `"zve32x"` + /// * Zve32f: `"zve32f"` + /// * Zve64x: `"zve64x"` + /// * Zve64f: `"zve64f"` + /// * Zve64d: `"zve64d"` + /// * Zicboz: `"zicboz"` /// * Zicntr: `"zicntr"` + /// * Zicond: `"zicond"` /// * Zicsr: `"zicsr"` /// * Zifencei: `"zifencei"` + /// * Zihintntl: `"zihintntl"` /// * Zihintpause: `"zihintpause"` /// * Zihpm: `"zihpm"` + /// * Zimop: `"zimop"` + /// * Zacas: `"zacas"` + /// * Zawrs: `"zawrs"` + /// * Zfa: `"zfa"` /// * Zfh: `"zfh"` /// * Zfhmin: `"zfhmin"` /// * Zfinx: `"zfinx"` /// * Zdinx: `"zdinx"` /// * Zhinx: `"zhinx"` /// * Zhinxmin: `"zhinxmin"` + /// * Zcb: `"zcb"` + /// * Zcmop: `"zcmop"` /// * Zbc: `"zbc"` /// * Zbkb: `"zbkb"` /// * Zbkc: `"zbkc"` @@ -67,6 +84,24 @@ features! { /// * Zksed: `"zksed"` /// * Zksh: `"zksh"` /// * Zkt: `"zkt"` + /// * Zvbb: `"zvbb"` + /// * Zvbc: `"zvbc"` + /// * Zvfh: `"zvfh"` + /// * Zvfhmin: `"zvfhmin"` + /// * Zvkb: `"zvkb"` + /// * Zvkg: `"zvkg"` + /// * Zvkn: `"zvkn"` + /// * Zvkned: `"zvkned"` + /// * Zvknha: `"zvknha"` + /// * Zvknhb: `"zvknhb"` + /// * Zvknc: `"zvknc"` + /// * Zvkng: `"zvkng"` + /// * Zvks: `"zvks"` + /// * Zvksed: `"zvksed"` + /// * Zvksh: `"zvksh"` + /// * Zvksc: `"zvksc"` + /// * Zvksg: `"zvksg"` + /// * Zvkt: `"zvkt"` /// * Ztso: `"ztso"` /// /// There's also bases and extensions marked as standard instruction set, @@ -87,6 +122,15 @@ features! { /// * Svnapot: `"svnapot"` /// * Svpbmt: `"svpbmt"` /// * Svinval: `"svinval"` + /// + /// # Performance Hints + /// + /// The two features below define performance hints for unaligned + /// scalar/vector memory accesses, respectively. If enabled, it denotes that + /// corresponding unaligned memory access is reasonably fast. + /// + /// * `"unaligned-scalar-mem"` + /// * `"unaligned-vector-mem"` #[stable(feature = "riscv_ratified", since = "1.78.0")] @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] rv32i: "rv32i"; @@ -102,6 +146,11 @@ features! { without cfg check: true; /// RV128I Base Integer Instruction Set + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] unaligned_scalar_mem: "unaligned-scalar-mem"; + /// Has reasonably performant unaligned scalar + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] unaligned_vector_mem: "unaligned-vector-mem"; + /// Has reasonably performant unaligned vector + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zicsr: "zicsr"; without cfg check: true; /// "Zicsr" Extension for Control and Status Register (CSR) Instructions @@ -115,9 +164,21 @@ features! { without cfg check: true; /// "Zifencei" Extension for Instruction-Fetch Fence + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zihintntl: "zihintntl"; + without cfg check: true; + /// "Zihintntl" Extension for Non-Temporal Locality Hints @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zihintpause: "zihintpause"; without cfg check: true; /// "Zihintpause" Extension for Pause Hint + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zimop: "zimop"; + without cfg check: true; + /// "Zimop" Extension for May-Be-Operations + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zicboz: "zicboz"; + without cfg check: true; + /// "Zicboz" Extension for Cache-Block Zero Instruction + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zicond: "zicond"; + without cfg check: true; + /// "Zicond" Extension for Integer Conditional Operations @FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] m: "m"; /// "M" Extension for Integer Multiplication and Division @@ -128,6 +189,10 @@ features! { /// "Zalrsc" Extension for Load-Reserved/Store-Conditional Instructions @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zaamo: "zaamo"; /// "Zaamo" Extension for Atomic Memory Operations + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zawrs: "zawrs"; + /// "Zawrs" Extension for Wait-on-Reservation-Set Instructions + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zacas: "zacas"; + /// "Zacas" Extension for Atomic Compare-and-Swap (CAS) Instructions @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zam: "zam"; without cfg check: true; /// "Zam" Extension for Misaligned Atomics @@ -146,6 +211,9 @@ features! { /// "Zfh" Extension for Half-Precision Floating-Point @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zfhmin: "zfhmin"; /// "Zfhmin" Extension for Minimal Half-Precision Floating-Point + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zfa: "zfa"; + without cfg check: true; + /// "Zfa" Extension for Additional Floating-Point Instructions @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zfinx: "zfinx"; /// "Zfinx" Extension for Single-Precision Floating-Point in Integer Registers @@ -158,6 +226,21 @@ features! { @FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] c: "c"; /// "C" Extension for Compressed Instructions + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zca: "zca"; + without cfg check: true; + /// "Zca" Compressed Instructions excluding Floating-Point Loads/Stores + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zcf: "zcf"; + without cfg check: true; + /// "Zcf" Compressed Instructions for Single-Precision Floating-Point Loads/Stores on RV32 + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zcd: "zcd"; + without cfg check: true; + /// "Zcd" Compressed Instructions for Double-Precision Floating-Point Loads/Stores + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zcb: "zcb"; + without cfg check: true; + /// "Zcb" Simple Code-size Saving Compressed Instructions + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zcmop: "zcmop"; + without cfg check: true; + /// "Zcmop" Extension for Compressed May-Be-Operations @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] b: "b"; without cfg check: true; @@ -200,6 +283,53 @@ features! { @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] v: "v"; /// "V" Extension for Vector Operations + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve32x: "zve32x"; + /// "Zve32x" Vector Extension for Embedded Processors (32-bit+; Integer) + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve32f: "zve32f"; + /// "Zve32f" Vector Extension for Embedded Processors (32-bit+; with Single-Precision Floating-Point) + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve64x: "zve64x"; + /// "Zve64x" Vector Extension for Embedded Processors (64-bit+; Integer) + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve64f: "zve64f"; + /// "Zve64f" Vector Extension for Embedded Processors (64-bit+; with Single-Precision Floating-Point) + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve64d: "zve64d"; + /// "Zve64d" Vector Extension for Embedded Processors (64-bit+; with Double-Precision Floating-Point) + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvfh: "zvfh"; + /// "Zvfh" Vector Extension for Half-Precision Floating-Point + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvfhmin: "zvfhmin"; + /// "Zvfhmin" Vector Extension for Minimal Half-Precision Floating-Point + + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvbb: "zvbb"; + /// "Zvbb" Extension for Vector Basic Bit-Manipulation + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvbc: "zvbc"; + /// "Zvbc" Extension for Vector Carryless Multiplication + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkb: "zvkb"; + /// "Zvkb" Extension for Vector Cryptography Bit-Manipulation + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkg: "zvkg"; + /// "Zvkg" Cryptography Extension for Vector GCM/GMAC + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkned: "zvkned"; + /// "Zvkned" Cryptography Extension for NIST Suite: Vector AES Block Cipher + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvknha: "zvknha"; + /// "Zvknha" Cryptography Extension for Vector SHA-2 Secure Hash (SHA-256) + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvknhb: "zvknhb"; + /// "Zvknhb" Cryptography Extension for Vector SHA-2 Secure Hash (SHA-256/512) + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvksed: "zvksed"; + /// "Zvksed" Cryptography Extension for ShangMi Suite: Vector SM4 Block Cipher + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvksh: "zvksh"; + /// "Zvksh" Cryptography Extension for ShangMi Suite: Vector SM3 Secure Hash + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkn: "zvkn"; + /// "Zvkn" Cryptography Extension for NIST Algorithm Suite + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvknc: "zvknc"; + /// "Zvknc" Cryptography Extension for NIST Algorithm Suite with Carryless Multiply + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkng: "zvkng"; + /// "Zvkng" Cryptography Extension for NIST Algorithm Suite with GCM + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvks: "zvks"; + /// "Zvks" Cryptography Extension for ShangMi Algorithm Suite + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvksc: "zvksc"; + /// "Zvksc" Cryptography Extension for ShangMi Algorithm Suite with Carryless Multiply + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvksg: "zvksg"; + /// "Zvksg" Cryptography Extension for ShangMi Algorithm Suite with GCM + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkt: "zvkt"; + /// "Zvkt" Extension for Vector Data-Independent Execution Latency @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] svnapot: "svnapot"; without cfg check: true; diff --git a/crates/std_detect/src/detect/os/linux/riscv.rs b/crates/std_detect/src/detect/os/linux/riscv.rs index 6cdc72dff5..5cf7b064e8 100644 --- a/crates/std_detect/src/detect/os/linux/riscv.rs +++ b/crates/std_detect/src/detect/os/linux/riscv.rs @@ -1,10 +1,127 @@ //! Run-time feature detection for RISC-V on Linux. +//! +//! On RISC-V, detection using auxv only supports single-letter extensions. +//! So, we use riscv_hwprobe that supports multi-letter extensions if available. +//! + +use core::ptr; use super::super::riscv::imply_features; use super::auxvec; use crate::detect::{Feature, bit, cache}; -/// Read list of supported features from the auxiliary vector. +// See +// for runtime status query constants. +const PR_RISCV_V_GET_CONTROL: libc::c_int = 70; +const PR_RISCV_V_VSTATE_CTRL_ON: libc::c_int = 2; +const PR_RISCV_V_VSTATE_CTRL_CUR_MASK: libc::c_int = 3; + +// See +// for riscv_hwprobe struct and hardware probing constants. + +#[repr(C)] +struct riscv_hwprobe { + key: i64, + value: u64, +} + +#[allow(non_upper_case_globals)] +const __NR_riscv_hwprobe: libc::c_long = 258; + +const RISCV_HWPROBE_KEY_BASE_BEHAVIOR: i64 = 3; +const RISCV_HWPROBE_BASE_BEHAVIOR_IMA: u64 = 1 << 0; + +const RISCV_HWPROBE_KEY_IMA_EXT_0: i64 = 4; +const RISCV_HWPROBE_IMA_FD: u64 = 1 << 0; +const RISCV_HWPROBE_IMA_C: u64 = 1 << 1; +const RISCV_HWPROBE_IMA_V: u64 = 1 << 2; +const RISCV_HWPROBE_EXT_ZBA: u64 = 1 << 3; +const RISCV_HWPROBE_EXT_ZBB: u64 = 1 << 4; +const RISCV_HWPROBE_EXT_ZBS: u64 = 1 << 5; +const RISCV_HWPROBE_EXT_ZICBOZ: u64 = 1 << 6; +const RISCV_HWPROBE_EXT_ZBC: u64 = 1 << 7; +const RISCV_HWPROBE_EXT_ZBKB: u64 = 1 << 8; +const RISCV_HWPROBE_EXT_ZBKC: u64 = 1 << 9; +const RISCV_HWPROBE_EXT_ZBKX: u64 = 1 << 10; +const RISCV_HWPROBE_EXT_ZKND: u64 = 1 << 11; +const RISCV_HWPROBE_EXT_ZKNE: u64 = 1 << 12; +const RISCV_HWPROBE_EXT_ZKNH: u64 = 1 << 13; +const RISCV_HWPROBE_EXT_ZKSED: u64 = 1 << 14; +const RISCV_HWPROBE_EXT_ZKSH: u64 = 1 << 15; +const RISCV_HWPROBE_EXT_ZKT: u64 = 1 << 16; +const RISCV_HWPROBE_EXT_ZVBB: u64 = 1 << 17; +const RISCV_HWPROBE_EXT_ZVBC: u64 = 1 << 18; +const RISCV_HWPROBE_EXT_ZVKB: u64 = 1 << 19; +const RISCV_HWPROBE_EXT_ZVKG: u64 = 1 << 20; +const RISCV_HWPROBE_EXT_ZVKNED: u64 = 1 << 21; +const RISCV_HWPROBE_EXT_ZVKNHA: u64 = 1 << 22; +const RISCV_HWPROBE_EXT_ZVKNHB: u64 = 1 << 23; +const RISCV_HWPROBE_EXT_ZVKSED: u64 = 1 << 24; +const RISCV_HWPROBE_EXT_ZVKSH: u64 = 1 << 25; +const RISCV_HWPROBE_EXT_ZVKT: u64 = 1 << 26; +const RISCV_HWPROBE_EXT_ZFH: u64 = 1 << 27; +const RISCV_HWPROBE_EXT_ZFHMIN: u64 = 1 << 28; +const RISCV_HWPROBE_EXT_ZIHINTNTL: u64 = 1 << 29; +const RISCV_HWPROBE_EXT_ZVFH: u64 = 1 << 30; +const RISCV_HWPROBE_EXT_ZVFHMIN: u64 = 1 << 31; +const RISCV_HWPROBE_EXT_ZFA: u64 = 1 << 32; +const RISCV_HWPROBE_EXT_ZTSO: u64 = 1 << 33; +const RISCV_HWPROBE_EXT_ZACAS: u64 = 1 << 34; +const RISCV_HWPROBE_EXT_ZICOND: u64 = 1 << 35; +const RISCV_HWPROBE_EXT_ZIHINTPAUSE: u64 = 1 << 36; +const RISCV_HWPROBE_EXT_ZVE32X: u64 = 1 << 37; +const RISCV_HWPROBE_EXT_ZVE32F: u64 = 1 << 38; +const RISCV_HWPROBE_EXT_ZVE64X: u64 = 1 << 39; +const RISCV_HWPROBE_EXT_ZVE64F: u64 = 1 << 40; +const RISCV_HWPROBE_EXT_ZVE64D: u64 = 1 << 41; +const RISCV_HWPROBE_EXT_ZIMOP: u64 = 1 << 42; +const RISCV_HWPROBE_EXT_ZCA: u64 = 1 << 43; +const RISCV_HWPROBE_EXT_ZCB: u64 = 1 << 44; +const RISCV_HWPROBE_EXT_ZCD: u64 = 1 << 45; +const RISCV_HWPROBE_EXT_ZCF: u64 = 1 << 46; +const RISCV_HWPROBE_EXT_ZCMOP: u64 = 1 << 47; +const RISCV_HWPROBE_EXT_ZAWRS: u64 = 1 << 48; +// Excluded because it only reports the existence of `prctl`-based pointer masking control. +// const RISCV_HWPROBE_EXT_SUPM: u64 = 1 << 49; + +const RISCV_HWPROBE_KEY_CPUPERF_0: i64 = 5; +const RISCV_HWPROBE_MISALIGNED_FAST: u64 = 3; +const RISCV_HWPROBE_MISALIGNED_MASK: u64 = 7; + +const RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF: i64 = 9; +const RISCV_HWPROBE_MISALIGNED_SCALAR_FAST: u64 = 3; + +const RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF: i64 = 10; +const RISCV_HWPROBE_MISALIGNED_VECTOR_FAST: u64 = 3; + +// syscall returns an unsupported error if riscv_hwprobe is not supported, +// so we can safely use this function on older versions of Linux. +fn _riscv_hwprobe(out: &mut [riscv_hwprobe]) -> bool { + unsafe fn __riscv_hwprobe( + pairs: *mut riscv_hwprobe, + pair_count: libc::size_t, + cpu_set_size: libc::size_t, + cpus: *mut libc::c_ulong, + flags: libc::c_uint, + ) -> libc::c_long { + unsafe { + libc::syscall( + __NR_riscv_hwprobe, + pairs, + pair_count, + cpu_set_size, + cpus, + flags, + ) + } + } + + let len = out.len(); + unsafe { __riscv_hwprobe(out.as_mut_ptr(), len, 0, ptr::null_mut(), 0) == 0 } +} + +/// Read list of supported features from (1) the auxiliary vector +/// and (2) the results of `riscv_hwprobe` and `prctl` system calls. pub(crate) fn detect_features() -> cache::Initializer { let mut value = cache::Initializer::default(); let mut enable_feature = |feature, enable| { @@ -18,6 +135,7 @@ pub(crate) fn detect_features() -> cache::Initializer { // // [hwcap]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/riscv/include/uapi/asm/hwcap.h?h=v6.14 let auxv = auxvec::auxv().expect("read auxvec"); // should not fail on RISC-V platform + let mut has_i = bit::test(auxv.hwcap, (b'i' - b'a').into()); #[allow(clippy::eq_op)] enable_feature(Feature::a, bit::test(auxv.hwcap, (b'a' - b'a').into())); enable_feature(Feature::c, bit::test(auxv.hwcap, (b'c' - b'a').into())); @@ -25,9 +143,167 @@ pub(crate) fn detect_features() -> cache::Initializer { enable_feature(Feature::f, bit::test(auxv.hwcap, (b'f' - b'a').into())); enable_feature(Feature::h, bit::test(auxv.hwcap, (b'h' - b'a').into())); enable_feature(Feature::m, bit::test(auxv.hwcap, (b'm' - b'a').into())); + let has_v = bit::test(auxv.hwcap, (b'v' - b'a').into()); + let mut is_v_set = false; + + // Use riscv_hwprobe syscall to query more extensions and + // performance-related capabilities. + 'hwprobe: { + let mut out = [ + riscv_hwprobe { + key: RISCV_HWPROBE_KEY_BASE_BEHAVIOR, + value: 0, + }, + riscv_hwprobe { + key: RISCV_HWPROBE_KEY_IMA_EXT_0, + value: 0, + }, + riscv_hwprobe { + key: RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF, + value: 0, + }, + riscv_hwprobe { + key: RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF, + value: 0, + }, + riscv_hwprobe { + key: RISCV_HWPROBE_KEY_CPUPERF_0, + value: 0, + }, + ]; + if !_riscv_hwprobe(&mut out) { + break 'hwprobe; + } + + // Query scalar/vector misaligned behavior. + if out[2].key != -1 { + enable_feature( + Feature::unaligned_scalar_mem, + out[2].value == RISCV_HWPROBE_MISALIGNED_SCALAR_FAST, + ); + } else if out[4].key != -1 { + // Deprecated method for fallback + enable_feature( + Feature::unaligned_scalar_mem, + out[4].value & RISCV_HWPROBE_MISALIGNED_MASK == RISCV_HWPROBE_MISALIGNED_FAST, + ); + } + if out[3].key != -1 { + enable_feature( + Feature::unaligned_vector_mem, + out[3].value == RISCV_HWPROBE_MISALIGNED_VECTOR_FAST, + ); + } + + // Query whether "I" base and extensions "M" and "A" (as in the ISA + // manual version 2.2) are enabled. "I" base at that time corresponds + // to "I", "Zicsr", "Zicntr" and "Zifencei" (as in the ISA manual version + // 20240411) and we chose to imply "Zicsr" and "Zifencei" (not "Zicntr") + // because there will be a separate RISCV_HWPROBE_EXT_ZICNTR constant to + // determine existence of the "Zicntr" extension in Linux 6.15 (as of rc1). + // "fence.i" ("Zifencei") is conditionally valid on the Linux userland + // (when CMODX is enabled). + // This is a requirement of `RISCV_HWPROBE_KEY_IMA_EXT_0`-based tests. + let has_ima = (out[0].key != -1) && (out[0].value & RISCV_HWPROBE_BASE_BEHAVIOR_IMA != 0); + if !has_ima { + break 'hwprobe; + } + has_i |= has_ima; + enable_feature(Feature::zicsr, has_ima); + enable_feature(Feature::zifencei, has_ima); + enable_feature(Feature::m, has_ima); + enable_feature(Feature::a, has_ima); + + // Enable features based on `RISCV_HWPROBE_KEY_IMA_EXT_0`. + if out[1].key == -1 { + break 'hwprobe; + } + let ima_ext_0 = out[1].value; + let test = |mask| (ima_ext_0 & mask) != 0; + + enable_feature(Feature::d, test(RISCV_HWPROBE_IMA_FD)); // F is implied. + enable_feature(Feature::c, test(RISCV_HWPROBE_IMA_C)); + + enable_feature(Feature::zihintntl, test(RISCV_HWPROBE_EXT_ZIHINTNTL)); + enable_feature(Feature::zihintpause, test(RISCV_HWPROBE_EXT_ZIHINTPAUSE)); + enable_feature(Feature::zimop, test(RISCV_HWPROBE_EXT_ZIMOP)); + enable_feature(Feature::zicboz, test(RISCV_HWPROBE_EXT_ZICBOZ)); + enable_feature(Feature::zicond, test(RISCV_HWPROBE_EXT_ZICOND)); + + enable_feature(Feature::zawrs, test(RISCV_HWPROBE_EXT_ZAWRS)); + enable_feature(Feature::zacas, test(RISCV_HWPROBE_EXT_ZACAS)); + enable_feature(Feature::ztso, test(RISCV_HWPROBE_EXT_ZTSO)); + + enable_feature(Feature::zba, test(RISCV_HWPROBE_EXT_ZBA)); + enable_feature(Feature::zbb, test(RISCV_HWPROBE_EXT_ZBB)); + enable_feature(Feature::zbs, test(RISCV_HWPROBE_EXT_ZBS)); + enable_feature(Feature::zbc, test(RISCV_HWPROBE_EXT_ZBC)); + + enable_feature(Feature::zbkb, test(RISCV_HWPROBE_EXT_ZBKB)); + enable_feature(Feature::zbkc, test(RISCV_HWPROBE_EXT_ZBKC)); + enable_feature(Feature::zbkx, test(RISCV_HWPROBE_EXT_ZBKX)); + enable_feature(Feature::zknd, test(RISCV_HWPROBE_EXT_ZKND)); + enable_feature(Feature::zkne, test(RISCV_HWPROBE_EXT_ZKNE)); + enable_feature(Feature::zknh, test(RISCV_HWPROBE_EXT_ZKNH)); + enable_feature(Feature::zksed, test(RISCV_HWPROBE_EXT_ZKSED)); + enable_feature(Feature::zksh, test(RISCV_HWPROBE_EXT_ZKSH)); + enable_feature(Feature::zkt, test(RISCV_HWPROBE_EXT_ZKT)); + + enable_feature(Feature::zcmop, test(RISCV_HWPROBE_EXT_ZCMOP)); + enable_feature(Feature::zca, test(RISCV_HWPROBE_EXT_ZCA)); + enable_feature(Feature::zcf, test(RISCV_HWPROBE_EXT_ZCF)); + enable_feature(Feature::zcd, test(RISCV_HWPROBE_EXT_ZCD)); + enable_feature(Feature::zcb, test(RISCV_HWPROBE_EXT_ZCB)); + + enable_feature(Feature::zfh, test(RISCV_HWPROBE_EXT_ZFH)); + enable_feature(Feature::zfhmin, test(RISCV_HWPROBE_EXT_ZFHMIN)); + enable_feature(Feature::zfa, test(RISCV_HWPROBE_EXT_ZFA)); + + // Use prctl (if any) to determine whether the vector extension + // is enabled on the current thread (assuming the entire process + // share the same status). If prctl fails (e.g. QEMU userland emulator + // as of version 9.2.3), use auxiliary vector to retrieve the default + // vector status on the process startup. + let has_vectors = { + let v_status = unsafe { libc::prctl(PR_RISCV_V_GET_CONTROL) }; + if v_status >= 0 { + (v_status & PR_RISCV_V_VSTATE_CTRL_CUR_MASK) == PR_RISCV_V_VSTATE_CTRL_ON + } else { + has_v + } + }; + if has_vectors { + enable_feature(Feature::v, test(RISCV_HWPROBE_IMA_V)); + enable_feature(Feature::zve32x, test(RISCV_HWPROBE_EXT_ZVE32X)); + enable_feature(Feature::zve32f, test(RISCV_HWPROBE_EXT_ZVE32F)); + enable_feature(Feature::zve64x, test(RISCV_HWPROBE_EXT_ZVE64X)); + enable_feature(Feature::zve64f, test(RISCV_HWPROBE_EXT_ZVE64F)); + enable_feature(Feature::zve64d, test(RISCV_HWPROBE_EXT_ZVE64D)); + + enable_feature(Feature::zvbb, test(RISCV_HWPROBE_EXT_ZVBB)); + enable_feature(Feature::zvbc, test(RISCV_HWPROBE_EXT_ZVBC)); + enable_feature(Feature::zvkb, test(RISCV_HWPROBE_EXT_ZVKB)); + enable_feature(Feature::zvkg, test(RISCV_HWPROBE_EXT_ZVKG)); + enable_feature(Feature::zvkned, test(RISCV_HWPROBE_EXT_ZVKNED)); + enable_feature(Feature::zvknha, test(RISCV_HWPROBE_EXT_ZVKNHA)); + enable_feature(Feature::zvknhb, test(RISCV_HWPROBE_EXT_ZVKNHB)); + enable_feature(Feature::zvksed, test(RISCV_HWPROBE_EXT_ZVKSED)); + enable_feature(Feature::zvksh, test(RISCV_HWPROBE_EXT_ZVKSH)); + enable_feature(Feature::zvkt, test(RISCV_HWPROBE_EXT_ZVKT)); + + enable_feature(Feature::zvfh, test(RISCV_HWPROBE_EXT_ZVFH)); + enable_feature(Feature::zvfhmin, test(RISCV_HWPROBE_EXT_ZVFHMIN)); + } + is_v_set = true; + }; + + // Set V purely depending on the auxiliary vector + // only if no fine-grained vector extension detection is available. + if !is_v_set { + enable_feature(Feature::v, has_v); + } // Handle base ISA. - let has_i = bit::test(auxv.hwcap, (b'i' - b'a').into()); // If future RV128I is supported, implement with `enable_feature` here. // Note that we should use `target_arch` instead of `target_pointer_width` // to avoid misdetection caused by experimental ABIs such as RV64ILP32. diff --git a/crates/std_detect/src/detect/os/riscv.rs b/crates/std_detect/src/detect/os/riscv.rs index b268eff8a1..fc218d51ab 100644 --- a/crates/std_detect/src/detect/os/riscv.rs +++ b/crates/std_detect/src/detect/os/riscv.rs @@ -62,25 +62,67 @@ pub(crate) fn imply_features(mut value: cache::Initializer) -> cache::Initialize "defined as subset": The latter extension is described as a subset of the former (but the evidence is weak). + "functional": + The former extension is functionally a superset of the latter + (no direct references though). */ + imply!(zvbb => zvkb); + + // Certain set of vector cryptography extensions form a group. + group!(zvkn == zvkned & zvknhb & zvkb & zvkt); + group!(zvknc == zvkn & zvbc); + group!(zvkng == zvkn & zvkg); + group!(zvks == zvksed & zvksh & zvkb & zvkt); + group!(zvksc == zvks & zvbc); + group!(zvksg == zvks & zvkg); + + imply!(zvknhb => zvknha); // functional + + // For vector cryptography, Zvknhb and Zvbc require integer arithmetic + // with EEW=64 (Zve64x) while others not depending on them + // require EEW=32 (Zve32x). + imply!(zvknhb | zvbc => zve64x); + imply!(zvbb | zvkb | zvkg | zvkned | zvknha | zvksed | zvksh => zve32x); + imply!(zbc => zbkc); // defined as subset group!(zkn == zbkb & zbkc & zbkx & zkne & zknd & zknh); group!(zks == zbkb & zbkc & zbkx & zksed & zksh); group!(zk == zkn & zkr & zkt); + imply!(zacas => zaamo); group!(a == zalrsc & zaamo); group!(b == zba & zbb & zbs); + imply!(zcf => zca & f); + imply!(zcd => zca & d); + imply!(zcmop | zcb => zca); + imply!(zhinx => zhinxmin); imply!(zdinx | zhinxmin => zfinx); + imply!(zvfh => zvfhmin); // functional + imply!(zvfh => zve32f & zfhmin); + imply!(zvfhmin => zve32f); + + imply!(v => zve64d); + imply!(zve64d => zve64f & d); + imply!(zve64f => zve64x & zve32f); + imply!(zve64x => zve32x); + imply!(zve32f => zve32x & f); + imply!(zfh => zfhmin); imply!(q => d); - imply!(d | zfhmin => f); + imply!(d | zfhmin | zfa => f); + + // Relatively complex implication rules from the "C" extension. + imply!(c => zca); + imply!(c & d => zcd); + #[cfg(target_arch = "riscv32")] + imply!(c & f => zcf); - imply!(zicntr | zihpm | f | zfinx => zicsr); + imply!(zicntr | zihpm | f | zfinx | zve32x => zicsr); imply!(s | h => zicsr); // Loop until the feature flags converge. @@ -110,6 +152,16 @@ mod tests { assert!(imply_features(value).test(Feature::zicsr as u32)); } + #[test] + fn complex_zcd() { + let mut value = cache::Initializer::default(); + // C & D -> Zcd + value.set(Feature::c as u32); + assert!(!imply_features(value).test(Feature::zcd as u32)); + value.set(Feature::d as u32); + assert!(imply_features(value).test(Feature::zcd as u32)); + } + #[test] fn group_simple_forward() { let mut value = cache::Initializer::default(); @@ -132,17 +184,18 @@ mod tests { #[test] fn group_complex_convergence() { let mut value = cache::Initializer::default(); - // Needs 2 iterations to converge - // (and 3rd iteration for convergence checking): - // 1. [Zk] -> Zkn & Zkr & Zkt - // 2. Zkn -> {Zbkb} & {Zbkc} & {Zbkx} & {Zkne} & {Zknd} & {Zknh} - value.set(Feature::zk as u32); + // Needs 3 iterations to converge + // (and 4th iteration for convergence checking): + // 1. [Zvksc] -> Zvks & Zvbc + // 2. Zvks -> Zvksed & Zvksh & Zvkb & Zvkt + // 3a. [Zvkned] & [Zvknhb] & [Zvkb] & Zvkt -> {Zvkn} + // 3b. Zvkn & Zvbc -> {Zvknc} + value.set(Feature::zvksc as u32); + value.set(Feature::zvkned as u32); + value.set(Feature::zvknhb as u32); + value.set(Feature::zvkb as u32); let value = imply_features(value); - assert!(value.test(Feature::zbkb as u32)); - assert!(value.test(Feature::zbkc as u32)); - assert!(value.test(Feature::zbkx as u32)); - assert!(value.test(Feature::zkne as u32)); - assert!(value.test(Feature::zknd as u32)); - assert!(value.test(Feature::zknh as u32)); + assert!(value.test(Feature::zvkn as u32)); + assert!(value.test(Feature::zvknc as u32)); } } diff --git a/crates/std_detect/tests/cpu-detection.rs b/crates/std_detect/tests/cpu-detection.rs index 8a40997f75..c0a76898d4 100644 --- a/crates/std_detect/tests/cpu-detection.rs +++ b/crates/std_detect/tests/cpu-detection.rs @@ -236,15 +236,29 @@ fn riscv_linux() { println!("rv32e: {}", is_riscv_feature_detected!("rv32e")); println!("rv64i: {}", is_riscv_feature_detected!("rv64i")); println!("rv128i: {}", is_riscv_feature_detected!("rv128i")); + println!( + "unaligned-scalar-mem: {}", + is_riscv_feature_detected!("unaligned-scalar-mem") + ); + println!( + "unaligned-vector-mem: {}", + is_riscv_feature_detected!("unaligned-vector-mem") + ); println!("zicsr: {}", is_riscv_feature_detected!("zicsr")); println!("zicntr: {}", is_riscv_feature_detected!("zicntr")); println!("zihpm: {}", is_riscv_feature_detected!("zihpm")); println!("zifencei: {}", is_riscv_feature_detected!("zifencei")); + println!("zihintntl: {}", is_riscv_feature_detected!("zihintntl")); println!("zihintpause: {}", is_riscv_feature_detected!("zihintpause")); + println!("zimop: {}", is_riscv_feature_detected!("zimop")); + println!("zicboz: {}", is_riscv_feature_detected!("zicboz")); + println!("zicond: {}", is_riscv_feature_detected!("zicond")); println!("m: {}", is_riscv_feature_detected!("m")); println!("a: {}", is_riscv_feature_detected!("a")); println!("zalrsc: {}", is_riscv_feature_detected!("zalrsc")); println!("zaamo: {}", is_riscv_feature_detected!("zaamo")); + println!("zawrs: {}", is_riscv_feature_detected!("zawrs")); + println!("zacas: {}", is_riscv_feature_detected!("zacas")); println!("zam: {}", is_riscv_feature_detected!("zam")); println!("ztso: {}", is_riscv_feature_detected!("ztso")); println!("f: {}", is_riscv_feature_detected!("f")); @@ -252,11 +266,17 @@ fn riscv_linux() { println!("q: {}", is_riscv_feature_detected!("q")); println!("zfh: {}", is_riscv_feature_detected!("zfh")); println!("zfhmin: {}", is_riscv_feature_detected!("zfhmin")); + println!("zfa: {}", is_riscv_feature_detected!("zfa")); println!("zfinx: {}", is_riscv_feature_detected!("zfinx")); println!("zdinx: {}", is_riscv_feature_detected!("zdinx")); println!("zhinx: {}", is_riscv_feature_detected!("zhinx")); println!("zhinxmin: {}", is_riscv_feature_detected!("zhinxmin")); println!("c: {}", is_riscv_feature_detected!("c")); + println!("zca: {}", is_riscv_feature_detected!("zca")); + println!("zcf: {}", is_riscv_feature_detected!("zcf")); + println!("zcd: {}", is_riscv_feature_detected!("zcd")); + println!("zcb: {}", is_riscv_feature_detected!("zcb")); + println!("zcmop: {}", is_riscv_feature_detected!("zcmop")); println!("b: {}", is_riscv_feature_detected!("b")); println!("zba: {}", is_riscv_feature_detected!("zba")); println!("zbb: {}", is_riscv_feature_detected!("zbb")); @@ -276,6 +296,29 @@ fn riscv_linux() { println!("zk: {}", is_riscv_feature_detected!("zk")); println!("zkt: {}", is_riscv_feature_detected!("zkt")); println!("v: {}", is_riscv_feature_detected!("v")); + println!("zve32x: {}", is_riscv_feature_detected!("zve32x")); + println!("zve32f: {}", is_riscv_feature_detected!("zve32f")); + println!("zve64x: {}", is_riscv_feature_detected!("zve64x")); + println!("zve64f: {}", is_riscv_feature_detected!("zve64f")); + println!("zve64d: {}", is_riscv_feature_detected!("zve64d")); + println!("zvfh: {}", is_riscv_feature_detected!("zvfh")); + println!("zvfhmin: {}", is_riscv_feature_detected!("zvfhmin")); + println!("zvbb: {}", is_riscv_feature_detected!("zvbb")); + println!("zvbc: {}", is_riscv_feature_detected!("zvbc")); + println!("zvkb: {}", is_riscv_feature_detected!("zvkb")); + println!("zvkg: {}", is_riscv_feature_detected!("zvkg")); + println!("zvkned: {}", is_riscv_feature_detected!("zvkned")); + println!("zvknha: {}", is_riscv_feature_detected!("zvknha")); + println!("zvknhb: {}", is_riscv_feature_detected!("zvknhb")); + println!("zvksed: {}", is_riscv_feature_detected!("zvksed")); + println!("zvksh: {}", is_riscv_feature_detected!("zvksh")); + println!("zvkn: {}", is_riscv_feature_detected!("zvkn")); + println!("zvknc: {}", is_riscv_feature_detected!("zvknc")); + println!("zvkng: {}", is_riscv_feature_detected!("zvkng")); + println!("zvks: {}", is_riscv_feature_detected!("zvks")); + println!("zvksc: {}", is_riscv_feature_detected!("zvksc")); + println!("zvksg: {}", is_riscv_feature_detected!("zvksg")); + println!("zvkt: {}", is_riscv_feature_detected!("zvkt")); println!("svnapot: {}", is_riscv_feature_detected!("svnapot")); println!("svpbmt: {}", is_riscv_feature_detected!("svpbmt")); println!("svinval: {}", is_riscv_feature_detected!("svinval")); From 7087522dcacfd3ebdd8721a1a77a71f18d5c58e3 Mon Sep 17 00:00:00 2001 From: Tsukasa OI Date: Sun, 13 Apr 2025 04:35:58 +0000 Subject: [PATCH 11/11] RISC-V: Remove privileged extensions for now Until in-kernel feature detection is implemented, runtime detection of privileged extensions is temporally removed along with features themselves since none of such privileged features are stable. Co-Authored-By: Taiki Endo Co-Authored-By: Amanieu d'Antras --- crates/std_detect/src/detect/arch/riscv.rs | 24 ------------------- .../std_detect/src/detect/os/linux/riscv.rs | 5 ---- crates/std_detect/src/detect/os/riscv.rs | 1 - crates/std_detect/tests/cpu-detection.rs | 5 ---- 4 files changed, 35 deletions(-) diff --git a/crates/std_detect/src/detect/arch/riscv.rs b/crates/std_detect/src/detect/arch/riscv.rs index e9a6c96af1..63790537f8 100644 --- a/crates/std_detect/src/detect/arch/riscv.rs +++ b/crates/std_detect/src/detect/arch/riscv.rs @@ -115,14 +115,6 @@ features! { /// * P: `"p"` /// * Zam: `"zam"` /// - /// Defined by Privileged Specification: - /// - /// * *Supervisor-Level ISA* (not "S" extension): `"s"` - /// * H (hypervisor): `"h"` - /// * Svnapot: `"svnapot"` - /// * Svpbmt: `"svpbmt"` - /// * Svinval: `"svinval"` - /// /// # Performance Hints /// /// The two features below define performance hints for unaligned @@ -331,22 +323,6 @@ features! { @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkt: "zvkt"; /// "Zvkt" Extension for Vector Data-Independent Execution Latency - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] svnapot: "svnapot"; - without cfg check: true; - /// "Svnapot" Extension for NAPOT Translation Contiguity - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] svpbmt: "svpbmt"; - without cfg check: true; - /// "Svpbmt" Extension for Page-Based Memory Types - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] svinval: "svinval"; - without cfg check: true; - /// "Svinval" Extension for Fine-Grained Address-Translation Cache Invalidation - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] h: "h"; - without cfg check: true; - /// "H" Extension for Hypervisor Support - - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] s: "s"; - without cfg check: true; - /// Supervisor-Level ISA @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] j: "j"; without cfg check: true; /// "J" Extension for Dynamically Translated Languages diff --git a/crates/std_detect/src/detect/os/linux/riscv.rs b/crates/std_detect/src/detect/os/linux/riscv.rs index 5cf7b064e8..ad4fa956b1 100644 --- a/crates/std_detect/src/detect/os/linux/riscv.rs +++ b/crates/std_detect/src/detect/os/linux/riscv.rs @@ -141,7 +141,6 @@ pub(crate) fn detect_features() -> cache::Initializer { enable_feature(Feature::c, bit::test(auxv.hwcap, (b'c' - b'a').into())); enable_feature(Feature::d, bit::test(auxv.hwcap, (b'd' - b'a').into())); enable_feature(Feature::f, bit::test(auxv.hwcap, (b'f' - b'a').into())); - enable_feature(Feature::h, bit::test(auxv.hwcap, (b'h' - b'a').into())); enable_feature(Feature::m, bit::test(auxv.hwcap, (b'm' - b'a').into())); let has_v = bit::test(auxv.hwcap, (b'v' - b'a').into()); let mut is_v_set = false; @@ -315,9 +314,5 @@ pub(crate) fn detect_features() -> cache::Initializer { #[cfg(target_arch = "riscv32")] enable_feature(Feature::rv32e, bit::test(auxv.hwcap, (b'e' - b'a').into())); - // FIXME: Auxvec does not show supervisor feature support, but this mode may be useful - // to detect when Rust is used to write Linux kernel modules. - // These should be more than Auxvec way to detect supervisor features. - imply_features(value) } diff --git a/crates/std_detect/src/detect/os/riscv.rs b/crates/std_detect/src/detect/os/riscv.rs index fc218d51ab..97496df893 100644 --- a/crates/std_detect/src/detect/os/riscv.rs +++ b/crates/std_detect/src/detect/os/riscv.rs @@ -123,7 +123,6 @@ pub(crate) fn imply_features(mut value: cache::Initializer) -> cache::Initialize imply!(c & f => zcf); imply!(zicntr | zihpm | f | zfinx | zve32x => zicsr); - imply!(s | h => zicsr); // Loop until the feature flags converge. if prev == value { diff --git a/crates/std_detect/tests/cpu-detection.rs b/crates/std_detect/tests/cpu-detection.rs index c0a76898d4..0dabca52a2 100644 --- a/crates/std_detect/tests/cpu-detection.rs +++ b/crates/std_detect/tests/cpu-detection.rs @@ -319,11 +319,6 @@ fn riscv_linux() { println!("zvksc: {}", is_riscv_feature_detected!("zvksc")); println!("zvksg: {}", is_riscv_feature_detected!("zvksg")); println!("zvkt: {}", is_riscv_feature_detected!("zvkt")); - println!("svnapot: {}", is_riscv_feature_detected!("svnapot")); - println!("svpbmt: {}", is_riscv_feature_detected!("svpbmt")); - println!("svinval: {}", is_riscv_feature_detected!("svinval")); - println!("h: {}", is_riscv_feature_detected!("h")); - println!("s: {}", is_riscv_feature_detected!("s")); println!("j: {}", is_riscv_feature_detected!("j")); println!("p: {}", is_riscv_feature_detected!("p")); }