|
| 1 | +//! Run-time feature detection for Aarch64 on any OS that emulates the mrs instruction. |
| 2 | +//! |
| 3 | +//! On FreeBSD >= 12.0, Linux >= 4.11 and other operating systems, it is possible to use |
| 4 | +//! privileged system registers from userspace to check CPU feature support. |
| 5 | +//! |
| 6 | +//! AArch64 system registers ID_AA64ISAR0_EL1, ID_AA64PFR0_EL1, ID_AA64ISAR1_EL1 |
| 7 | +//! have bits dedicated to features like AdvSIMD, CRC32, AES, atomics (LSE), etc. |
| 8 | +//! Each part of the register indicates the level of support for a certain feature, e.g. |
| 9 | +//! when ID_AA64ISAR0_EL1[7:4] is >= 1, AES is supported; when it's >= 2, PMULL is supported. |
| 10 | +//! |
| 11 | +//! For proper support of [SoCs where different cores have different capabilities](https://medium.com/@jadr2ddude/a-big-little-problem-a-tale-of-big-little-gone-wrong-e7778ce744bb), |
| 12 | +//! the OS has to always report only the features supported by all cores, like [FreeBSD does](https://reviews.freebsd.org/D17137#393947). |
| 13 | +//! |
| 14 | +//! References: |
| 15 | +//! |
| 16 | +//! - [Zircon implementation](https://fuchsia.googlesource.com/zircon/+/master/kernel/arch/arm64/feature.cpp) |
| 17 | +//! - [Linux documentation](https://www.kernel.org/doc/Documentation/arm64/cpu-feature-registers.txt) |
| 18 | +
|
| 19 | +use arch::detect::Feature; |
| 20 | +use arch::detect::cache; |
| 21 | + |
| 22 | +/// Try to read the features from the system registers. |
| 23 | +/// |
| 24 | +/// This will cause SIGILL if the current OS is not trapping the mrs instruction. |
| 25 | +pub(crate) fn detect_features() -> cache::Initializer { |
| 26 | + let mut value = cache::Initializer::default(); |
| 27 | + |
| 28 | + { |
| 29 | + let mut enable_feature = |f, enable| { |
| 30 | + if enable { |
| 31 | + value.set(f as u32); |
| 32 | + } |
| 33 | + }; |
| 34 | + |
| 35 | + // ID_AA64ISAR0_EL1 - Instruction Set Attribute Register 0 |
| 36 | + let aa64isar0: u64; |
| 37 | + unsafe { asm!("mrs $0, ID_AA64ISAR0_EL1" : "=r"(aa64isar0)); } |
| 38 | + |
| 39 | + let aes = bits_shift(aa64isar0, 7, 4) >= 1; |
| 40 | + let pmull = bits_shift(aa64isar0, 7, 4) >= 2; |
| 41 | + let sha1 = bits_shift(aa64isar0, 11, 8) >= 1; |
| 42 | + let sha2 = bits_shift(aa64isar0, 15, 12) >= 1; |
| 43 | + enable_feature(Feature::pmull, pmull); |
| 44 | + // Crypto is specified as AES + PMULL + SHA1 + SHA2 per LLVM/hosts.cpp |
| 45 | + enable_feature(Feature::crypto, aes && pmull && sha1 && sha2); |
| 46 | + enable_feature(Feature::lse, bits_shift(aa64isar0, 23, 20) >= 1); |
| 47 | + enable_feature(Feature::crc, bits_shift(aa64isar0, 19, 16) >= 1); |
| 48 | + |
| 49 | + // ID_AA64PFR0_EL1 - Processor Feature Register 0 |
| 50 | + let aa64pfr0: u64; |
| 51 | + unsafe { asm!("mrs $0, ID_AA64PFR0_EL1" : "=r"(aa64pfr0)); } |
| 52 | + |
| 53 | + let fp = bits_shift(aa64pfr0, 19, 16) < 0xF; |
| 54 | + let fphp = bits_shift(aa64pfr0, 19, 16) >= 1; |
| 55 | + let asimd = bits_shift(aa64pfr0, 23, 20) < 0xF; |
| 56 | + let asimdhp = bits_shift(aa64pfr0, 23, 20) >= 1; |
| 57 | + enable_feature(Feature::fp, fp); |
| 58 | + enable_feature(Feature::fp16, fphp); |
| 59 | + // SIMD support requires float support - if half-floats are |
| 60 | + // supported, it also requires half-float support: |
| 61 | + enable_feature(Feature::asimd, fp && asimd && (!fphp | asimdhp)); |
| 62 | + // SIMD extensions require SIMD support: |
| 63 | + enable_feature(Feature::rdm, asimd && bits_shift(aa64isar0, 31, 28) >= 1); |
| 64 | + enable_feature(Feature::dotprod, asimd && bits_shift(aa64isar0, 47, 44) >= 1); |
| 65 | + enable_feature(Feature::sve, asimd && bits_shift(aa64pfr0, 35, 32) >= 1); |
| 66 | + |
| 67 | + // ID_AA64ISAR1_EL1 - Instruction Set Attribute Register 1 |
| 68 | + let aa64isar1: u64; |
| 69 | + unsafe { asm!("mrs $0, ID_AA64ISAR1_EL1" : "=r"(aa64isar1)); } |
| 70 | + |
| 71 | + enable_feature(Feature::rcpc, bits_shift(aa64isar1, 23, 20) >= 1); |
| 72 | + } |
| 73 | + |
| 74 | + value |
| 75 | +} |
| 76 | + |
| 77 | +#[inline] |
| 78 | +fn bits_shift(x: u64, high: usize, low: usize) -> u64 { |
| 79 | + (x >> low) & ((1 << (high - low + 1)) - 1) |
| 80 | +} |
0 commit comments