Skip to main content

islet_rmm/
simd.rs

1use aarch64_cpu::registers::ID_AA64PFR0_EL1;
2use aarch64_cpu::registers::{Readable, Writeable};
3use armv9a::regs::{ID_AA64PFR1_SME_EL1, SMCR_EL2, ZCR_EL2};
4use core::arch::asm;
5use lazy_static::lazy_static;
6
7// Vector length (VL) = size of a Z-register in bytes
8// Vector quadwords (VQ) = size of a Z-register in units of 128 bits
9// Minimum length of a SVE vector: 128 bits
10const ZCR_EL2_LEN_WIDTH: u64 = 4;
11const SVE_VQ_ARCH_MAX: u64 = (1 << ZCR_EL2_LEN_WIDTH) - 1;
12const QUARD_WORD: u64 = 128;
13// Note: Limit maximun vq to 4 to avoid exceeding a page boundary.
14// Currunt rmm implmentation maps a physical page to a virtual address
15// using its physical address (i.e. identical mapping).
16// Discontiguous Aux granules cannot not be accessed contiguously
17// in their virtual address. For this reason, limit vq up to 4(~2184 bytes).
18// Z regs = 16bytes * 32 regs * 4 vq
19// P regs = 2 bytes * 16 regs * 4 vq
20// FFR reg = 2 bytes * 4 vq
21pub const MAX_VQ: u64 = 4;
22
23#[derive(Default, Debug)]
24// SIMD configuration structure
25pub struct SimdConfig {
26    // SVE enabled flag
27    pub sve_en: bool,
28
29    // SVE vector length represented in quads
30    pub sve_vq: u64,
31
32    // SME enabled flag
33    pub sme_en: bool,
34}
35
36lazy_static! {
37    // Global SIMD configuration
38    static ref SIMD_CONFIG: SimdConfig =  {
39        // Initalize SVE
40        let mut sve_en: bool = false;
41        let mut sve_vq: u64 = 0;
42        let mut sme_en: bool = false;
43
44        trace!("Reading simd features");
45        #[cfg(not(any(test, miri, fuzzing)))]
46        if ID_AA64PFR0_EL1.is_set(ID_AA64PFR0_EL1::SVE) {
47            trace!("SVE is set");
48            // Get effective vl: (ZCR_EL2:LEN + 1)*128 bits
49            //let _e_vl = ZCR_EL2.read(ZCR_EL2::LEN);
50            // Set to maximum
51            ZCR_EL2.write(ZCR_EL2::LEN.val(SVE_VQ_ARCH_MAX));
52            // Get vl in bytes
53            let vl_b = unsafe { get_vector_length_bytes() };
54            sve_vq = ((vl_b << 3)/ QUARD_WORD) - 1;
55            if sve_vq > MAX_VQ {
56                sve_vq = MAX_VQ - 1;
57            }
58            sve_en = true;
59            trace!("sve_vq={:?}", sve_vq);
60        }
61
62        // init sme
63        #[cfg(not(any(test, miri, fuzzing)))]
64        if ID_AA64PFR1_SME_EL1.is_set(ID_AA64PFR1_SME_EL1::SME) {
65            trace!("SME is set");
66            // Find the architecturally permitted SVL
67            SMCR_EL2.write(SMCR_EL2::RAZWI.val(SMCR_EL2::RAZWI.mask) + SMCR_EL2::LEN.val(SMCR_EL2::LEN.mask));
68            let raz = SMCR_EL2.read(SMCR_EL2::RAZWI);
69            let len = SMCR_EL2.read(SMCR_EL2::LEN);
70            let sme_svq_arch_max = (raz << 4) + len;
71            trace!("sme_svq_arch_max={:?}", sme_svq_arch_max);
72
73            assert!(sme_svq_arch_max <= SVE_VQ_ARCH_MAX);
74            sme_en = true;
75        }
76
77        SimdConfig {
78            sve_en,
79            sve_vq,
80            sme_en,
81        }
82    };
83}
84
85/// Get the SVE vector length in bytes using the RDVL instruction
86#[allow(aarch64_softfloat_neon)]
87#[target_feature(enable = "sve")]
88unsafe fn get_vector_length_bytes() -> u64 {
89    let vl_b: u64;
90    unsafe {
91        asm!("rdvl {}, #1", out(reg) vl_b);
92    }
93    vl_b
94}
95
96pub fn validate(en: bool, sve_vl: u64) -> bool {
97    if en && !SIMD_CONFIG.sve_en {
98        return false;
99    }
100    if sve_vl > SIMD_CONFIG.sve_vq {
101        return false;
102    }
103    true
104}
105
106pub fn sve_en() -> bool {
107    SIMD_CONFIG.sve_en
108}
109
110pub fn max_sve_vl() -> u64 {
111    SIMD_CONFIG.sve_vq
112}
113
114pub fn sme_en() -> bool {
115    SIMD_CONFIG.sme_en
116}