| //! Run-time feature detection for s390x on Linux. |
| |
| use super::auxvec; |
| use crate::detect::{Feature, bit, cache}; |
| |
| /// Try to read the features from the auxiliary vector |
| pub(crate) fn detect_features() -> cache::Initializer { |
| let opt_hwcap: Option<AtHwcap> = auxvec::auxv().ok().map(Into::into); |
| let facilities = ExtendedFacilityList::new(); |
| cache(opt_hwcap, facilities) |
| } |
| |
| #[derive(Debug, Default, PartialEq)] |
| struct AtHwcap { |
| esan3: bool, |
| zarch: bool, |
| stfle: bool, |
| msa: bool, |
| ldisp: bool, |
| eimm: bool, |
| dfp: bool, |
| hpage: bool, |
| etf3eh: bool, |
| high_gprs: bool, |
| te: bool, |
| vxrs: bool, |
| vxrs_bcd: bool, |
| vxrs_ext: bool, |
| gs: bool, |
| vxrs_ext2: bool, |
| vxrs_pde: bool, |
| sort: bool, |
| dflt: bool, |
| vxrs_pde2: bool, |
| nnpa: bool, |
| pci_mio: bool, |
| sie: bool, |
| } |
| |
| impl From<auxvec::AuxVec> for AtHwcap { |
| /// Reads AtHwcap from the auxiliary vector. |
| fn from(auxv: auxvec::AuxVec) -> Self { |
| AtHwcap { |
| esan3: bit::test(auxv.hwcap, 0), |
| zarch: bit::test(auxv.hwcap, 1), |
| stfle: bit::test(auxv.hwcap, 2), |
| msa: bit::test(auxv.hwcap, 3), |
| ldisp: bit::test(auxv.hwcap, 4), |
| eimm: bit::test(auxv.hwcap, 5), |
| dfp: bit::test(auxv.hwcap, 6), |
| hpage: bit::test(auxv.hwcap, 7), |
| etf3eh: bit::test(auxv.hwcap, 8), |
| high_gprs: bit::test(auxv.hwcap, 9), |
| te: bit::test(auxv.hwcap, 10), |
| vxrs: bit::test(auxv.hwcap, 11), |
| vxrs_bcd: bit::test(auxv.hwcap, 12), |
| vxrs_ext: bit::test(auxv.hwcap, 13), |
| gs: bit::test(auxv.hwcap, 14), |
| vxrs_ext2: bit::test(auxv.hwcap, 15), |
| vxrs_pde: bit::test(auxv.hwcap, 16), |
| sort: bit::test(auxv.hwcap, 17), |
| dflt: bit::test(auxv.hwcap, 18), |
| vxrs_pde2: bit::test(auxv.hwcap, 19), |
| nnpa: bit::test(auxv.hwcap, 20), |
| pci_mio: bit::test(auxv.hwcap, 21), |
| sie: bit::test(auxv.hwcap, 22), |
| } |
| } |
| } |
| |
| struct ExtendedFacilityList([u64; 4]); |
| |
| impl ExtendedFacilityList { |
| fn new() -> Self { |
| let mut result: [u64; 4] = [0; 4]; |
| // SAFETY: rust/llvm only support s390x version with the `stfle` instruction. |
| unsafe { |
| core::arch::asm!( |
| // equivalently ".insn s, 0xb2b00000, 0({1})", |
| "stfle 0({})", |
| in(reg_addr) result.as_mut_ptr() , |
| inout("r0") result.len() as u64 - 1 => _, |
| options(nostack) |
| ); |
| } |
| Self(result) |
| } |
| |
| const fn get_bit(&self, n: usize) -> bool { |
| // NOTE: bits are numbered from the left. |
| self.0[n / 64] & (1 << (63 - (n % 64))) != 0 |
| } |
| } |
| |
| /// Initializes the cache from the feature bits. |
| /// |
| /// These values are part of the platform-specific [asm/elf.h][kernel], and are a selection of the |
| /// fields found in the [Facility Indications]. |
| /// |
| /// [Facility Indications]: https://www.ibm.com/support/pages/sites/default/files/2021-05/SA22-7871-10.pdf#page=63 |
| /// [kernel]: https://github.com/torvalds/linux/blob/b62cef9a5c673f1b8083159f5dc03c1c5daced2f/arch/s390/include/asm/elf.h#L129 |
| fn cache(hwcap: Option<AtHwcap>, facilities: ExtendedFacilityList) -> cache::Initializer { |
| let mut value = cache::Initializer::default(); |
| |
| { |
| let mut enable_if_set = |bit_index, f| { |
| if facilities.get_bit(bit_index) { |
| value.set(f as u32); |
| } |
| }; |
| |
| // We use HWCAP for `vector` because it requires both hardware and kernel support. |
| if let Some(AtHwcap { vxrs: true, .. }) = hwcap { |
| // vector and related |
| |
| enable_if_set(129, Feature::vector); |
| |
| enable_if_set(135, Feature::vector_enhancements_1); |
| enable_if_set(148, Feature::vector_enhancements_2); |
| enable_if_set(198, Feature::vector_enhancements_3); |
| |
| enable_if_set(134, Feature::vector_packed_decimal); |
| enable_if_set(152, Feature::vector_packed_decimal_enhancement); |
| enable_if_set(192, Feature::vector_packed_decimal_enhancement_2); |
| enable_if_set(199, Feature::vector_packed_decimal_enhancement_3); |
| |
| enable_if_set(165, Feature::nnp_assist); |
| } |
| |
| // others |
| |
| enable_if_set(76, Feature::message_security_assist_extension3); |
| enable_if_set(77, Feature::message_security_assist_extension4); |
| enable_if_set(57, Feature::message_security_assist_extension5); |
| enable_if_set(146, Feature::message_security_assist_extension8); |
| enable_if_set(155, Feature::message_security_assist_extension9); |
| enable_if_set(86, Feature::message_security_assist_extension12); |
| |
| enable_if_set(58, Feature::miscellaneous_extensions_2); |
| enable_if_set(61, Feature::miscellaneous_extensions_3); |
| enable_if_set(84, Feature::miscellaneous_extensions_4); |
| |
| enable_if_set(45, Feature::high_word); |
| enable_if_set(73, Feature::transactional_execution); |
| enable_if_set(133, Feature::guarded_storage); |
| enable_if_set(150, Feature::enhanced_sort); |
| enable_if_set(151, Feature::deflate_conversion); |
| enable_if_set(201, Feature::concurrent_functions); |
| } |
| |
| value |
| } |