From 416c56f85ba8d8a9f7b7fc1d2b0a1f560bdc7035 Mon Sep 17 00:00:00 2001 From: mamonet <66893036+mamonet@users.noreply.github.com> Date: Mon, 28 Aug 2023 12:35:31 +0300 Subject: [PATCH] Use cc to build libjade and add platform detection crate (#51) --- Cargo.toml | 2 +- sys/libjade/Cargo.toml | 2 + sys/libjade/build.rs | 326 ++++++++++++++++------------------ sys/platform/Cargo.toml | 7 + sys/platform/src/lib.rs | 190 ++++++++++++++++++++ sys/platform/src/linux_arm.rs | 59 ++++++ sys/platform/src/macos_arm.rs | 106 +++++++++++ sys/platform/src/test.rs | 79 ++++++++ sys/platform/src/x86.rs | 142 +++++++++++++++ 9 files changed, 738 insertions(+), 175 deletions(-) create mode 100644 sys/platform/Cargo.toml create mode 100644 sys/platform/src/lib.rs create mode 100644 sys/platform/src/linux_arm.rs create mode 100644 sys/platform/src/macos_arm.rs create mode 100644 sys/platform/src/test.rs create mode 100644 sys/platform/src/x86.rs diff --git a/Cargo.toml b/Cargo.toml index c8e5df048..92c460f1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ hacl = { version = "=0.0.2", features = ["hazmat"] } rand = { version = "0.8" } log = "0.4" -[target.'cfg(any(target_arch = "x86_64", target_arch = "x86"))'.dependencies] +[target.'cfg(all(not(target_os = "windows"), any(target_arch = "x86_64", target_arch = "x86")))'.dependencies] libjade-sys = { version = "0.0.1", path = "sys/libjade" } [dev-dependencies] diff --git a/sys/libjade/Cargo.toml b/sys/libjade/Cargo.toml index 5d118e684..3fe4ee634 100644 --- a/sys/libjade/Cargo.toml +++ b/sys/libjade/Cargo.toml @@ -12,6 +12,8 @@ log = "0.4" [build-dependencies] libc = { version = "0.2", default-features = false } fs_extra = "1.2" +cc = { version = "1.0", features = ["parallel"] } +libcrux_platform = { version = "=0.0.1", path = "../platform" } [target.'cfg(not(windows))'.build-dependencies] bindgen = "0.66" diff --git a/sys/libjade/build.rs b/sys/libjade/build.rs index 4a8afc7d4..fb89d7657 100644 --- a/sys/libjade/build.rs +++ b/sys/libjade/build.rs @@ -1,211 +1,184 @@ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -mod x64_build { - use std::{path::Path, process::Command}; +use std::{env, path::Path}; - macro_rules! svec { +macro_rules! svec { ($($x:expr),*$(,)?) => (vec![$($x.to_string()),*]); } - pub fn copy_files(home_path: &Path, out_path: &Path) { - let mut options = fs_extra::dir::CopyOptions::new(); - options.overwrite = true; - fs_extra::dir::copy(home_path.join("jazz"), out_path, &options).unwrap(); - } +fn copy_files(home_path: &Path, out_path: &Path) { + let mut options = fs_extra::dir::CopyOptions::new(); + options.overwrite = true; + fs_extra::dir::copy(home_path.join("jazz"), out_path, &options).unwrap(); +} - pub fn append_simd128_flags(flags: &mut Vec) { - // Platform detection - if simd128_support() { - flags.push("-DSIMD128".to_string()); - flags.push("-mavx".to_string()); - } - } +fn append_simd128_flags(flags: &mut Vec) { + flags.push("-DSIMD128".to_string()); + flags.push("-mavx".to_string()); +} - pub fn append_simd256_flags(flags: &mut Vec) { - // Platform detection - if simd256_support() { - flags.push("-DSIMD256".to_string()); - flags.push("-mavx2".to_string()); - } - } +fn append_simd256_flags(flags: &mut Vec) { + flags.push("-DSIMD256".to_string()); + flags.push("-mavx2".to_string()); +} - #[cfg(not(windows))] - pub fn create_bindings(home_dir: &Path) { - let jazz_dir = home_dir.join("jazz"); - let mut clang_args = vec![format!("-I{}", jazz_dir.join("include").display())]; +#[cfg(not(windows))] +fn create_bindings(platform: Platform, home_dir: &Path) { + let jazz_dir = home_dir.join("jazz"); + let mut clang_args = vec![format!("-I{}", jazz_dir.join("include").display())]; + if platform.simd128 { append_simd128_flags(&mut clang_args); + } + if platform.simd256 { append_simd256_flags(&mut clang_args); - - let bindings = bindgen::Builder::default() - // Header to wrap headers - .header("jazz/include/libjade.h") - // Set include paths for headers - .clang_args(clang_args) - // Allow function we want to have in - .allowlist_function("jade_hash_.*") - .allowlist_var("JADE_HASH_.*") - .allowlist_function("jade_scalarmult_curve25519_.*") - .allowlist_var("JADE_SCALARMULT_CURVE25519_.*") - .allowlist_function("jade_hash_sha3_.*") - .allowlist_var("JADE_HASH_SHA3_.*") - .allowlist_function("jade_onetimeauth_poly1305_.*") - .allowlist_var("JADE_ONETIMEAUTH_POLY1305_.*") - .allowlist_function("jade_stream_chacha_chacha20.*") - .allowlist_var("JADE_STREAM_CHACHA_CHACHA20_.*") - .allowlist_function("jade_kem_kyber_kyber768_.*") - .allowlist_var("JADE_KEM_KYBER_KYBER768_.*") - // Block everything we don't need or define ourselves. - .blocklist_type("__.*") - // Disable tests to avoid warnings and keep it portable - .layout_tests(false) - // Generate bindings - .parse_callbacks(Box::new(bindgen::CargoCallbacks)) - .generate() - .expect("Unable to generate bindings"); - - let home_bindings = home_dir.join("src/bindings.rs"); - bindings - .write_to_file(home_bindings) - .expect("Couldn't write bindings!"); } - #[cfg(windows)] - pub fn create_bindings(_: &Path) {} - - pub fn compile_files(files: &[String], out_path: &Path, args: &[String]) { - let jazz_dir = out_path.join("jazz"); - let mut clang_args = vec![format!("-I{}", jazz_dir.join("include").display())]; - clang_args.push("-O3".to_string()); - clang_args.push("-c".to_string()); - clang_args.extend_from_slice(args); - - let mut build_cmd = Command::new("clang"); - let mut build_args = clang_args; - build_args.extend_from_slice(files); - println!(" >>> {}", out_path.join("jazz").display()); - println!(" >>> {}", build_args.join(" ")); - - build_cmd - .current_dir(out_path.join("jazz")) - .args(&build_args); - println!(" >>> build_cmd: {:?}", build_cmd); - println!(" current dir: {:?}", build_cmd.get_current_dir()); - - let build_status = build_cmd.status().expect("Failed to run build."); - println!(" >>> build status: {:?}", build_status); - println!(" >>> out {:?}", out_path); - assert!(build_status.success()); - } + let bindings = bindgen::Builder::default() + // Header to wrap headers + .header("jazz/include/libjade.h") + // Set include paths for headers + .clang_args(clang_args) + // Allow function we want to have in + .allowlist_function("jade_hash_.*") + .allowlist_var("JADE_HASH_.*") + .allowlist_function("jade_scalarmult_curve25519_.*") + .allowlist_var("JADE_SCALARMULT_CURVE25519_.*") + .allowlist_function("jade_hash_sha3_.*") + .allowlist_var("JADE_HASH_SHA3_.*") + .allowlist_function("jade_onetimeauth_poly1305_.*") + .allowlist_var("JADE_ONETIMEAUTH_POLY1305_.*") + .allowlist_function("jade_stream_chacha_chacha20.*") + .allowlist_var("JADE_STREAM_CHACHA_CHACHA20_.*") + .allowlist_function("jade_kem_kyber_kyber768_.*") + .allowlist_var("JADE_KEM_KYBER_KYBER768_.*") + // Block everything we don't need or define ourselves. + .blocklist_type("__.*") + // Disable tests to avoid warnings and keep it portable + .layout_tests(false) + // Generate bindings + .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + .use_core() + .generate() + .expect("Unable to generate bindings"); + + let home_bindings = home_dir.join("src/bindings.rs"); + bindings + .write_to_file(home_bindings) + .expect("Couldn't write bindings!"); +} - pub fn build(out_path: &Path, cross_target: Option) { - let args = cross_target - .map(|s| match s.as_str() { - // We only support cross compilation here for now. - // We assume that we're using clang and can just add the target - "x86_64-apple-darwin" => svec!["-target", "x86_64-apple-darwin"], - _ => panic!("Unsupported cross compilation target {s}"), - }) - .unwrap_or_default(); - - let files = svec![ - "sha256.s", - "x25519_ref.s", - "x25519_mulx.s", - "sha3_224_ref.s", - "sha3_256_ref.s", - "sha3_384_ref.s", - "sha3_512_ref.s", - "chacha20_ref.s", - "poly1305_ref.s", - "kyber_kyber768_ref.s", - ]; - let mut all_files = files.clone(); - - // Platform detection - if simd256_support() { - let files256 = svec![ - "sha3_224_avx2.s", - "sha3_256_avx2.s", - "sha3_384_avx2.s", - "sha3_512_avx2.s", - "chacha20_avx2.s", - "poly1305_avx2.s", - ]; - all_files.extend_from_slice(&files256); - - let mut simd256_flags = args.clone(); - append_simd256_flags(&mut simd256_flags); - compile_files(&files256, out_path, &simd256_flags); - } - if simd128_support() { - let files128 = svec!["chacha20_avx.s", "poly1305_avx.s",]; - all_files.extend_from_slice(&files128); +#[cfg(windows)] +fn create_bindings(platform: Platform, _: &Path) {} - let mut simd128_flags = args.clone(); - append_simd128_flags(&mut simd128_flags); - compile_files(&files128, out_path, &simd128_flags); - } +fn compile_files(library_name: &str, files: &[String], out_path: &Path, args: &[String]) { + let jazz_dir = out_path.join("jazz"); - let mut object_files = vec![]; - compile_files(&files, out_path, &args); - for file in all_files { - object_files.push(Path::new(&file).with_extension("o")); - } + let mut build = cc::Build::new(); + build + .files(files.iter().map(|fname| jazz_dir.join(fname))) + .warnings_into_errors(true) + .no_default_flags(true); - // Link - let mut build_cmd = Command::new("ar"); - build_cmd - .current_dir(out_path.join("jazz")) - .args(&["-r", &out_path.join("libjade.a").display().to_string()]) - .args(&object_files); - println!(" >>> build_cmd: {:?}", build_cmd); - println!(" current dir: {:?}", build_cmd.get_current_dir()); - - let build_status = build_cmd.status().expect("Failed to link."); - println!("{:?}", build_status); - assert!(build_status.success()); + build.include(jazz_dir.join("include")); + build.flag("-O3").flag("-c"); + for arg in args { + build.flag(arg); } - // === hardware detection - pub fn simd128_support() -> bool { - std::arch::is_x86_feature_detected!("avx") + build.compile(library_name); +} + +fn build(platform: Platform, out_path: &Path, cross_target: Option) { + let args = cross_target + .map(|s| match s.as_str() { + // We only support cross compilation here for now. + "x86_64-apple-darwin" => svec!["-target", "x86_64-apple-darwin"], + _ => panic!("Unsupported cross compilation target {s}"), + }) + .unwrap_or_default(); + + let files = svec![ + "sha256.s", + "x25519_ref.s", + "x25519_mulx.s", + "sha3_224_ref.s", + "sha3_256_ref.s", + "sha3_384_ref.s", + "sha3_512_ref.s", + "chacha20_ref.s", + "poly1305_ref.s", + "kyber_kyber768_ref.s", + ]; + compile_files("libjade.a", &files, out_path, &args); + + if platform.simd256 { + let files256 = svec![ + "sha3_224_avx2.s", + "sha3_256_avx2.s", + "sha3_384_avx2.s", + "sha3_512_avx2.s", + "chacha20_avx2.s", + "poly1305_avx2.s", + ]; + + let mut simd256_flags = args.clone(); + append_simd256_flags(&mut simd256_flags); + compile_files("libjade_256.a", &files256, out_path, &simd256_flags); } - pub fn simd256_support() -> bool { - std::arch::is_x86_feature_detected!("avx2") + if platform.simd128 { + let files128 = svec!["chacha20_avx.s", "poly1305_avx.s",]; + + let mut simd128_flags = args.clone(); + append_simd128_flags(&mut simd128_flags); + compile_files("libjade_128.a", &files128, out_path, &simd128_flags); } } -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -pub fn main() { - use std::{env, path::Path}; - use x64_build::*; +#[derive(Debug, Clone, Copy)] +struct Platform { + simd128: bool, + simd256: bool, +} +pub fn main() -> Result<(), u8> { // Get ENV variables let home_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); let home_path = Path::new(&home_dir); let out_dir = env::var("OUT_DIR").unwrap(); let out_path = Path::new(&out_dir); let target = env::var("TARGET").unwrap(); + let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); let host = env::var("HOST").unwrap(); - let cross_target = if target != host { Some(target) } else { None }; - if cross_target.is_none() && host == "aarch64-apple-darwin" { - panic!("libjade does not support aarch64-apple-darwin"); + if target_arch != "x86_64" && target_arch != "x86" { + eprintln!(" ! Only x86 and x64 CPUs are supported !"); + return Err(1); } - if simd128_support() { - println!("cargo:rustc-cfg=simd128"); - } - if simd256_support() { - println!("cargo:rustc-cfg=simd256"); - } + let cross_target = if target != host { + Some(target.clone()) + } else { + None + }; + + // If cross compiling, we assume to have it all. + let platform = if cross_target.is_some() { + Platform { + simd128: true, + simd256: true, + } + } else { + Platform { + simd128: libcrux_platform::simd128_support(), + simd256: libcrux_platform::simd256_support(), + } + }; // Moving C/ASM code to output to make build easier. copy_files(home_path, out_path); eprintln!(" >>> out {:?}", out_path); // Build the C/ASM files - build(out_path, cross_target); + build(platform, out_path, cross_target); // Set library name to look up let library_name = "jade"; @@ -214,16 +187,21 @@ pub fn main() { println!("cargo:rerun-if-changed=cs"); // Generate new bindings. This is a no-op on Windows. - create_bindings(home_path); + create_bindings(platform, home_path); // Link hacl library. let mode = "static"; println!("cargo:rustc-link-lib={}={}", mode, library_name); + if platform.simd128 { + println!("cargo:rustc-cfg=simd128"); + println!("cargo:rustc-link-lib={}={}", mode, "jade_128"); + } + if platform.simd256 { + println!("cargo:rustc-cfg=simd256"); + println!("cargo:rustc-link-lib={}={}", mode, "jade_256"); + } println!("cargo:rustc-link-search=native={}", out_path.display()); println!("cargo:lib={}", out_path.display()); -} -#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] -fn main() { - eprintln!("Only x86 and x64 CPUs are supported."); + Ok(()) } diff --git a/sys/platform/Cargo.toml b/sys/platform/Cargo.toml new file mode 100644 index 000000000..0d7406dd0 --- /dev/null +++ b/sys/platform/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "libcrux_platform" +version = "0.0.1" +edition = "2021" + +[dependencies] +libc = "0.2.147" diff --git a/sys/platform/src/lib.rs b/sys/platform/src/lib.rs new file mode 100644 index 000000000..55dfcff7b --- /dev/null +++ b/sys/platform/src/lib.rs @@ -0,0 +1,190 @@ +//! High-level functions to detect available CPU features +//! at runtime on supported processor architectures and +//! operation systems + +#![no_std] + +// Use std for tests +#[cfg(test)] +#[macro_use] +extern crate std; + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +mod x86; +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +use x86::{self as cpu_id, Feature}; + +#[cfg(all(target_arch = "aarch64", target_os = "linux"))] +mod linux_arm; +#[cfg(all(target_arch = "aarch64", target_os = "macos"))] +mod macos_arm; + +#[cfg(test)] +mod test; + +// TODO: Check for z14 or z15 +pub fn simd128_support() -> bool { + #[cfg(all(target_arch = "aarch64", target_os = "macos"))] + { + use macos_arm::*; + adv_simd() + } + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + { + cpu_id::supported(Feature::sse2) + && cpu_id::supported(Feature::sse3) + && cpu_id::supported(Feature::sse4_1) + && cpu_id::supported(Feature::avx) + } + + #[cfg(all(target_arch = "aarch64", target_os = "linux"))] + { + use linux_arm::*; + adv_simd() + } + + #[cfg(not(any( + all(target_arch = "aarch64", any(target_os = "linux", target_os = "macos")), + target_arch = "x86", + target_arch = "x86_64" + )))] + { + false + } +} + +pub fn simd256_support() -> bool { + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + return cpu_id::supported(Feature::avx2); + + #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] + false +} + +pub fn x25519_support() -> bool { + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + return cpu_id::supported(Feature::bmi2) && cpu_id::supported(Feature::adx); + + #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] + false +} + +pub fn bmi2_adx_support() -> bool { + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + return cpu_id::supported(Feature::bmi2) && cpu_id::supported(Feature::adx); + + #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] + false +} + +/// Check whether p(cl)mull is supported +pub fn pmull_support() -> bool { + #[cfg(all(target_arch = "aarch64", target_os = "macos"))] + { + use crate::macos_arm::*; + pmull() + } + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + { + cpu_id::supported(Feature::pclmulqdq) + } + + #[cfg(all(target_arch = "aarch64", target_os = "linux"))] + { + use crate::linux_arm::*; + pmull() + } + + #[cfg(not(any( + all(target_arch = "aarch64", any(target_os = "linux", target_os = "macos")), + target_arch = "x86", + target_arch = "x86_64" + )))] + { + false + } +} + +/// Check whether advanced SIMD features are supported +pub fn adv_simd_support() -> bool { + #[cfg(all(target_arch = "aarch64", target_os = "macos"))] + { + use macos_arm::*; + adv_simd() + } + + #[cfg(all(target_arch = "aarch64", target_os = "linux"))] + { + use linux_arm::*; + adv_simd() + } + + #[cfg(not(all(target_arch = "aarch64", any(target_os = "linux", target_os = "macos"))))] + { + false + } +} + +/// Check whether AES is supported +pub fn aes_ni_support() -> bool { + #[cfg(all(target_arch = "aarch64", target_os = "macos"))] + { + use crate::macos_arm::*; + aes() + } + + #[cfg(all(target_arch = "aarch64", target_os = "linux"))] + { + use crate::linux_arm::*; + aes() + } + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + { + cpu_id::supported(Feature::avx) + && cpu_id::supported(Feature::sse) + && cpu_id::supported(Feature::aes) + && cpu_id::supported(Feature::pclmulqdq) + && cpu_id::supported(Feature::movbe) + } + + #[cfg(not(any( + all(target_arch = "aarch64", any(target_os = "linux", target_os = "macos")), + target_arch = "x86", + target_arch = "x86_64" + )))] + { + false + } +} + +/// Check whether SHA256 is supported +pub fn sha256_support() -> bool { + #[cfg(all(target_arch = "aarch64", target_os = "macos"))] + { + use crate::macos_arm::*; + sha256() + } + + #[cfg(all(target_arch = "aarch64", target_os = "linux"))] + { + use crate::linux_arm::*; + sha256() + } + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + { + cpu_id::supported(Feature::sha) + } + + #[cfg(not(any( + all(target_arch = "aarch64", any(target_os = "linux", target_os = "macos")), + target_arch = "x86", + target_arch = "x86_64" + )))] + { + false + } +} diff --git a/sys/platform/src/linux_arm.rs b/sys/platform/src/linux_arm.rs new file mode 100644 index 000000000..8530b73a4 --- /dev/null +++ b/sys/platform/src/linux_arm.rs @@ -0,0 +1,59 @@ +//! Obtain particular CPU features for AArch64 on Linux + +use libc::{getauxval, AT_HWCAP, HWCAP_ASIMD, HWCAP_AES, HWCAP_PMULL, HWCAP_SHA2}; + +#[inline(always)] +fn auxval() { + let val = unsafe { getauxval(AT_HWCAP) }; + let val_asimd = val & HWCAP_ASIMD != 0; + unsafe { ADV_SIMD = val_asimd }; + let val_aes = val & HWCAP_AES != 0; + unsafe { AES = val_aes }; + let val_pmull = val & HWCAP_PMULL != 0; + unsafe { PMULL = val_pmull }; + let val_sha256 = val & HWCAP_SHA2 != 0; + unsafe { SHA256 = val_sha256 }; +} + +static mut ADV_SIMD: bool = false; +static mut AES: bool = false; +static mut PMULL: bool = false; +static mut SHA256: bool = false; + +#[inline(always)] +pub(super) fn aes() -> bool { + init(); + unsafe { AES } +} + +#[inline(always)] +pub(super) fn adv_simd() -> bool { + init(); + unsafe { ADV_SIMD } +} + +#[inline(always)] +pub(super) fn pmull() -> bool { + init(); + unsafe { PMULL } +} + +#[inline(always)] +pub(super) fn sha256() -> bool { + init(); + unsafe { SHA256 } +} + +static mut INITIALIZED: bool = false; + +/// Initialize CPU detection. +#[inline(always)] +pub(super) fn init() { + if unsafe { INITIALIZED } { + return; + } + auxval(); + unsafe { + INITIALIZED = true; + } +} diff --git a/sys/platform/src/macos_arm.rs b/sys/platform/src/macos_arm.rs new file mode 100644 index 000000000..4d92236df --- /dev/null +++ b/sys/platform/src/macos_arm.rs @@ -0,0 +1,106 @@ +//! Obtain particular CPU features for AArch64 on macOS + +use libc::{c_void, sysctlbyname}; + +#[inline(always)] +fn check(feature: &[i8]) -> bool { + let mut ret = 0i64; + let mut size = core::mem::size_of::(); + let error = unsafe { + sysctlbyname( + feature.as_ptr(), + &mut ret as *mut _ as *mut c_void, + &mut size, + core::ptr::null_mut(), + 0, + ) + }; + error == 0 && ret > 0 +} + +#[inline(always)] +fn sysctl() { + // hw.optional.AdvSIMD + const ADV_SIMD_STR: [i8; 20] = [ + 0x68, 0x77, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2e, 0x41, 0x64, 0x76, + 0x53, 0x49, 0x4d, 0x44, 0x00, + ]; + if check(&ADV_SIMD_STR) { + unsafe { ADV_SIMD = true }; + } + + // hw.optional.arm.FEAT_AES + const FEAT_AES_STR: [i8; 25] = [ + 0x68, 0x77, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2e, 0x61, 0x72, 0x6d, + 0x2e, 0x46, 0x45, 0x41, 0x54, 0x5f, 0x41, 0x45, 0x53, 0x00, + ]; + if check(&FEAT_AES_STR) { + unsafe { AES = true }; + } + + // hw.optional.arm.FEAT_PMULL + const FEAT_PMULL_STR: [i8; 27] = [ + 0x68, 0x77, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2e, 0x61, 0x72, 0x6d, + 0x2e, 0x46, 0x45, 0x41, 0x54, 0x5f, 0x50, 0x4d, 0x55, 0x4c, 0x4c, 0x00, + ]; + if check(&FEAT_PMULL_STR) { + unsafe { PMULL = true }; + } + + // hw.optional.arm.FEAT_SHA256 + const FEAT_SHA256_STR: [i8; 28] = [ + 0x68, 0x77, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2e, 0x61, 0x72, 0x6d, + 0x2e, 0x46, 0x45, 0x41, 0x54, 0x5f, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x00, + ]; + if check(&FEAT_SHA256_STR) { + unsafe { SHA256 = true }; + } +} + +static mut ADV_SIMD: bool = false; +static mut AES: bool = false; +static mut PMULL: bool = false; +static mut SHA256: bool = false; + +#[inline(always)] +pub(super) fn aes() -> bool { + init(); + unsafe { AES } +} + +#[inline(always)] +pub(super) fn adv_simd() -> bool { + init(); + unsafe { ADV_SIMD } +} + +#[inline(always)] +pub(super) fn pmull() -> bool { + init(); + unsafe { PMULL } +} + +#[inline(always)] +pub(super) fn sha256() -> bool { + init(); + unsafe { SHA256 } +} + +static mut INITIALIZED: bool = false; + +/// Initialize CPU detection. +#[inline(always)] +pub(super) fn init() { + if unsafe { INITIALIZED } { + return; + } + // XXX[no_std]: no good way to do this in no_std + // let _ = std::panic::catch_unwind(|| { + // If there's no CPU ID because we're in SGX or whatever other reason, + // we'll consider the hw detection as initialized but always return false. + sysctl(); + // }); + unsafe { + INITIALIZED = true; + } +} diff --git a/sys/platform/src/test.rs b/sys/platform/src/test.rs new file mode 100644 index 000000000..b2b66c9ff --- /dev/null +++ b/sys/platform/src/test.rs @@ -0,0 +1,79 @@ +//! Test functions for CPU feature detection + +use super::*; + +#[test] +fn dump_features() { + eprintln!("simd128\t\t{:?}", simd128_support()); + eprintln!("simd256\t\t{:?}", simd256_support()); + eprintln!("x25519\t\t{:?}", x25519_support()); + eprintln!("bmi2 & adx\t{:?}", bmi2_adx_support()); + eprintln!("aes\t\t{:?}", aes_ni_support()); + eprintln!("advSimd\t\t{:?}", adv_simd_support()); + eprintln!("pmull\t\t{:?}", pmull_support()); + eprintln!("sha256\t\t{:?}", sha256_support()); +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[test] +fn dump_raw() { + use super::x86::{supported, Feature}; + eprintln!("mmx\t\t{:?}", supported(Feature::mmx)); + eprintln!("sse\t\t{:?}", supported(Feature::sse)); + eprintln!("sse2\t\t{:?}", supported(Feature::sse2)); + eprintln!("sse3\t\t{:?}", supported(Feature::sse3)); + eprintln!("pclmulqdq\t{:?}", supported(Feature::pclmulqdq)); + eprintln!("ssse3\t\t{:?}", supported(Feature::ssse3)); + eprintln!("fma\t\t{:?}", supported(Feature::fma)); + eprintln!("movbe\t\t{:?}", supported(Feature::movbe)); + eprintln!("sse4_1\t\t{:?}", supported(Feature::sse4_1)); + eprintln!("sse4_2\t\t{:?}", supported(Feature::sse4_2)); + eprintln!("popcnt\t\t{:?}", supported(Feature::popcnt)); + eprintln!("aes\t\t{:?}", supported(Feature::aes)); + eprintln!("xsave\t\t{:?}", supported(Feature::xsave)); + eprintln!("osxsave\t\t{:?}", supported(Feature::osxsave)); + eprintln!("avx\t\t{:?}", supported(Feature::avx)); + eprintln!("rdrand\t\t{:?}", supported(Feature::rdrand)); + eprintln!("sgx\t\t{:?}", supported(Feature::sgx)); + eprintln!("bmi1\t\t{:?}", supported(Feature::bmi1)); + eprintln!("avx2\t\t{:?}", supported(Feature::avx2)); + eprintln!("bmi2\t\t{:?}", supported(Feature::bmi2)); + eprintln!("avx512f\t\t{:?}", supported(Feature::avx512f)); + eprintln!("avx512dq\t{:?}", supported(Feature::avx512dq)); + eprintln!("rdseed\t\t{:?}", supported(Feature::rdseed)); + eprintln!("adx\t\t{:?}", supported(Feature::adx)); + eprintln!("avx512ifma\t{:?}", supported(Feature::avx512ifma)); + eprintln!("avx512pf\t{:?}", supported(Feature::avx512pf)); + eprintln!("avx512er\t{:?}", supported(Feature::avx512er)); + eprintln!("avx512cd\t{:?}", supported(Feature::avx512cd)); + eprintln!("sha\t\t{:?}", supported(Feature::sha)); + eprintln!("avx512bw\t{:?}", supported(Feature::avx512bw)); + eprintln!("avx512vl\t{:?}", supported(Feature::avx512vl)); +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[test] +fn cpuid() { + use super::x86::supported; + use std::time::Instant; + + let now = Instant::now(); + let _avx2 = supported(Feature::avx2); + let elapsed = now.elapsed(); + eprintln!("libcrux init: {elapsed:.2?}"); + + let now = Instant::now(); + let _avx2 = supported(Feature::avx2); + let elapsed = now.elapsed(); + eprintln!("libcrux after: {elapsed:.2?}"); + + let now = Instant::now(); + let _avx2 = std::arch::is_x86_feature_detected!("avx2"); + let elapsed = now.elapsed(); + eprintln!("std init: {elapsed:.2?}"); + + let now = Instant::now(); + let _avx2 = std::arch::is_x86_feature_detected!("avx2"); + let elapsed = now.elapsed(); + eprintln!("std after: {elapsed:.2?}"); +} diff --git a/sys/platform/src/x86.rs b/sys/platform/src/x86.rs new file mode 100644 index 000000000..d1d67ba91 --- /dev/null +++ b/sys/platform/src/x86.rs @@ -0,0 +1,142 @@ +//! Obtain particular CPU features for x86/x86_64 + +#![allow(non_upper_case_globals)] + +#[cfg(target_arch = "x86")] +use core::arch::x86::{CpuidResult, __cpuid, __cpuid_count}; +#[cfg(target_arch = "x86_64")] +use core::arch::x86_64::{CpuidResult, __cpuid, __cpuid_count}; + +#[allow(non_camel_case_types)] +#[derive(Clone, Copy)] +#[allow(dead_code)] +pub(super) enum Feature { + mmx, + sse, + sse2, + sse3, + pclmulqdq, + ssse3, + fma, + movbe, + sse4_1, + sse4_2, + popcnt, + aes, + xsave, + osxsave, + avx, + rdrand, + sgx, + bmi1, + avx2, + bmi2, + avx512f, + avx512dq, + rdseed, + adx, + avx512ifma, + avx512pf, + avx512er, + avx512cd, + sha, + avx512bw, + avx512vl, +} + +/// Check hardware [`Feature`] support. +pub(super) fn supported(feature: Feature) -> bool { + init(); + let cpu_id_0 = unsafe { CPU_ID[0] }; + let cpu_id_1 = unsafe { CPU_ID[1] }; + match feature { + Feature::mmx => cpu_id_0.edx & (1 << 23) != 0, + Feature::sse => cpu_id_0.edx & (1 << 25) != 0, + Feature::sse2 => cpu_id_0.edx & (1 << 26) != 0, + Feature::sse3 => cpu_id_0.ecx & (1 << 0) != 0, + Feature::pclmulqdq => cpu_id_0.ecx & (1 << 1) != 0, + Feature::ssse3 => cpu_id_0.ecx & (1 << 9) != 0, + Feature::fma => cpu_id_0.ecx & (1 << 12) != 0, + Feature::movbe => cpu_id_0.ecx & (1 << 22) != 0, + Feature::sse4_1 => cpu_id_0.ecx & (1 << 19) != 0, + Feature::sse4_2 => cpu_id_0.ecx & (1 << 20) != 0, + Feature::popcnt => cpu_id_0.ecx & (1 << 23) != 0, + Feature::aes => cpu_id_0.ecx & (1 << 25) != 0, + Feature::xsave => cpu_id_0.ecx & (1 << 26) != 0, + Feature::osxsave => cpu_id_0.ecx & (1 << 27) != 0, + Feature::avx => { + cpu_id_0.ecx & (1 << 28) != 0 + && supported(Feature::xsave) + && supported(Feature::osxsave) + } + Feature::rdrand => cpu_id_0.ecx & (1 << 30) != 0, + Feature::sgx => cpu_id_1.ebx & (1 << 2) != 0, + Feature::bmi1 => cpu_id_1.ebx & (1 << 3) != 0, + Feature::avx2 => { + cpu_id_1.ebx & (1 << 5) != 0 + && supported(Feature::bmi1) + && supported(Feature::bmi2) + && supported(Feature::fma) + && supported(Feature::movbe) + } + Feature::bmi2 => cpu_id_1.ebx & (1 << 8) != 0, + Feature::avx512f => cpu_id_1.ebx & (1 << 16) != 0, + Feature::avx512dq => cpu_id_1.ebx & (1 << 17) != 0, + Feature::rdseed => cpu_id_1.ebx & (1 << 18) != 0, + Feature::adx => cpu_id_1.ebx & (1 << 19) != 0, + Feature::avx512ifma => cpu_id_1.ebx & (1 << 21) != 0, + Feature::avx512pf => cpu_id_1.ebx & (1 << 26) != 0, + Feature::avx512er => cpu_id_1.ebx & (1 << 27) != 0, + Feature::avx512cd => cpu_id_1.ebx & (1 << 28) != 0, + Feature::sha => cpu_id_1.ebx & (1 << 29) != 0, + Feature::avx512bw => cpu_id_1.ebx & (1 << 30) != 0, + Feature::avx512vl => cpu_id_1.ebx & (1 << 31) != 0, + } +} + +static mut CPU_ID: [CpuidResult; 2] = [ + CpuidResult { + eax: 0, + ebx: 0, + ecx: 0, + edx: 0, + }, + CpuidResult { + eax: 0, + ebx: 0, + ecx: 0, + edx: 0, + }, +]; +static mut INITIALIZED: bool = false; + +/// Initialize CPU detection. +#[inline(always)] +pub(super) fn init() { + if unsafe { INITIALIZED } { + return; + } + + // XXX: https://github.com/rust-lang/rust/issues/101346 + #[inline(never)] + unsafe fn cpuid(leaf: u32) -> CpuidResult { + __cpuid(leaf) + } + + #[inline(never)] + unsafe fn cpuid_count(leaf: u32, sub_leaf: u32) -> CpuidResult { + __cpuid_count(leaf, sub_leaf) + } + + // XXX[no_std]: no good way to do this in no_std + // std::panic::catch_unwind(|| { + // If there's no CPU ID because we're in SGX or whatever other reason, + // we'll consider the hw detection as initialized but always return false. + unsafe { + CPU_ID = [cpuid(1), cpuid_count(7, 0)]; + } + // }); + unsafe { + INITIALIZED = true; + } +}