std_detect/detect/os/linux/
s390x.rs

1//! Run-time feature detection for s390x on Linux.
2
3use super::auxvec;
4use crate::detect::{Feature, bit, cache};
5
6/// Try to read the features from the auxiliary vector
7pub(crate) fn detect_features() -> cache::Initializer {
8    let opt_hwcap: Option<AtHwcap> = auxvec::auxv().ok().map(Into::into);
9    let facilities = ExtendedFacilityList::new();
10    cache(opt_hwcap, facilities)
11}
12
13#[derive(Debug, Default, PartialEq)]
14struct AtHwcap {
15    esan3: bool,
16    zarch: bool,
17    stfle: bool,
18    msa: bool,
19    ldisp: bool,
20    eimm: bool,
21    dfp: bool,
22    hpage: bool,
23    etf3eh: bool,
24    high_gprs: bool,
25    te: bool,
26    vxrs: bool,
27    vxrs_bcd: bool,
28    vxrs_ext: bool,
29    gs: bool,
30    vxrs_ext2: bool,
31    vxrs_pde: bool,
32    sort: bool,
33    dflt: bool,
34    vxrs_pde2: bool,
35    nnpa: bool,
36    pci_mio: bool,
37    sie: bool,
38}
39
40impl From<auxvec::AuxVec> for AtHwcap {
41    /// Reads AtHwcap from the auxiliary vector.
42    fn from(auxv: auxvec::AuxVec) -> Self {
43        AtHwcap {
44            esan3: bit::test(auxv.hwcap, 0),
45            zarch: bit::test(auxv.hwcap, 1),
46            stfle: bit::test(auxv.hwcap, 2),
47            msa: bit::test(auxv.hwcap, 3),
48            ldisp: bit::test(auxv.hwcap, 4),
49            eimm: bit::test(auxv.hwcap, 5),
50            dfp: bit::test(auxv.hwcap, 6),
51            hpage: bit::test(auxv.hwcap, 7),
52            etf3eh: bit::test(auxv.hwcap, 8),
53            high_gprs: bit::test(auxv.hwcap, 9),
54            te: bit::test(auxv.hwcap, 10),
55            vxrs: bit::test(auxv.hwcap, 11),
56            vxrs_bcd: bit::test(auxv.hwcap, 12),
57            vxrs_ext: bit::test(auxv.hwcap, 13),
58            gs: bit::test(auxv.hwcap, 14),
59            vxrs_ext2: bit::test(auxv.hwcap, 15),
60            vxrs_pde: bit::test(auxv.hwcap, 16),
61            sort: bit::test(auxv.hwcap, 17),
62            dflt: bit::test(auxv.hwcap, 18),
63            vxrs_pde2: bit::test(auxv.hwcap, 19),
64            nnpa: bit::test(auxv.hwcap, 20),
65            pci_mio: bit::test(auxv.hwcap, 21),
66            sie: bit::test(auxv.hwcap, 22),
67        }
68    }
69}
70
71struct ExtendedFacilityList([u64; 4]);
72
73impl ExtendedFacilityList {
74    fn new() -> Self {
75        let mut result: [u64; 4] = [0; 4];
76        // SAFETY: rust/llvm only support s390x version with the `stfle` instruction.
77        unsafe {
78            core::arch::asm!(
79                // equivalently ".insn s, 0xb2b00000, 0({1})",
80                "stfle 0({})",
81                in(reg_addr) result.as_mut_ptr() ,
82                inout("r0") result.len() as u64 - 1 => _,
83                options(nostack)
84            );
85        }
86        Self(result)
87    }
88
89    const fn get_bit(&self, n: usize) -> bool {
90        // NOTE: bits are numbered from the left.
91        self.0[n / 64] & (1 << (63 - (n % 64))) != 0
92    }
93}
94
95/// Initializes the cache from the feature bits.
96///
97/// These values are part of the platform-specific [asm/elf.h][kernel], and are a selection of the
98/// fields found in the [Facility Indications].
99///
100/// [Facility Indications]: https://www.ibm.com/support/pages/sites/default/files/2021-05/SA22-7871-10.pdf#page=63
101/// [kernel]: https://github.com/torvalds/linux/blob/b62cef9a5c673f1b8083159f5dc03c1c5daced2f/arch/s390/include/asm/elf.h#L129
102fn cache(hwcap: Option<AtHwcap>, facilities: ExtendedFacilityList) -> cache::Initializer {
103    let mut value = cache::Initializer::default();
104
105    {
106        let mut enable_if_set = |bit_index, f| {
107            if facilities.get_bit(bit_index) {
108                value.set(f as u32);
109            }
110        };
111
112        // We use HWCAP for `vector` because it requires both hardware and kernel support.
113        if let Some(AtHwcap { vxrs: true, .. }) = hwcap {
114            // vector and related
115
116            enable_if_set(129, Feature::vector);
117
118            enable_if_set(135, Feature::vector_enhancements_1);
119            enable_if_set(148, Feature::vector_enhancements_2);
120            enable_if_set(198, Feature::vector_enhancements_3);
121
122            enable_if_set(134, Feature::vector_packed_decimal);
123            enable_if_set(152, Feature::vector_packed_decimal_enhancement);
124            enable_if_set(192, Feature::vector_packed_decimal_enhancement_2);
125            enable_if_set(199, Feature::vector_packed_decimal_enhancement_3);
126
127            enable_if_set(165, Feature::nnp_assist);
128        }
129
130        // others
131
132        enable_if_set(76, Feature::message_security_assist_extension3);
133        enable_if_set(77, Feature::message_security_assist_extension4);
134        enable_if_set(57, Feature::message_security_assist_extension5);
135        enable_if_set(146, Feature::message_security_assist_extension8);
136        enable_if_set(155, Feature::message_security_assist_extension9);
137        enable_if_set(86, Feature::message_security_assist_extension12);
138
139        enable_if_set(58, Feature::miscellaneous_extensions_2);
140        enable_if_set(61, Feature::miscellaneous_extensions_3);
141        enable_if_set(84, Feature::miscellaneous_extensions_4);
142
143        enable_if_set(45, Feature::high_word);
144        enable_if_set(73, Feature::transactional_execution);
145        enable_if_set(133, Feature::guarded_storage);
146        enable_if_set(150, Feature::enhanced_sort);
147        enable_if_set(151, Feature::deflate_conversion);
148        enable_if_set(201, Feature::concurrent_functions);
149    }
150
151    value
152}