| /* Copyright (C) 2017-2026 Free Software Foundation, Inc. |
| |
| This file is part of the GNU Atomic Library (libatomic). |
| |
| Libatomic is free software; you can redistribute it and/or modify it |
| under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or |
| (at your option) any later version. |
| |
| Libatomic is distributed in the hope that it will be useful, but WITHOUT ANY |
| WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| more details. |
| |
| Under Section 7 of GPL version 3, you are granted additional |
| permissions described in the GCC Runtime Library Exception, version |
| 3.1, as published by the Free Software Foundation. |
| |
| You should have received a copy of the GNU General Public License and |
| a copy of the GCC Runtime Library Exception along with this program; |
| see the files COPYING3 and COPYING.RUNTIME respectively. If not, see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #if HAVE_IFUNC |
| #include <sys/auxv.h> |
| |
| #ifndef HWCAP_ATOMICS |
| # define HWCAP_ATOMICS (1 << 8) |
| #endif |
| #ifndef HWCAP_CPUID |
| # define HWCAP_CPUID (1 << 11) |
| #endif |
| #ifndef HWCAP_USCAT |
| # define HWCAP_USCAT (1 << 25) |
| #endif |
| #ifndef HWCAP2_LRCPC3 |
| # define HWCAP2_LRCPC3 (1UL << 46) |
| #endif |
| #ifndef HWCAP2_LSE128 |
| # define HWCAP2_LSE128 (1UL << 47) |
| #endif |
| |
| /* The following struct is ABI-correct description of the 2nd argument for an |
| ifunc resolver as per SYSVABI spec (see link below). It is safe to extend |
| it with new fields. The ifunc resolver implementations must always check |
| the runtime size of the buffer using the value in the _size field. |
| https://github.com/ARM-software/abi-aa/blob/main/sysvabi64/sysvabi64.rst. */ |
| typedef struct __ifunc_arg_t { |
| unsigned long _size; |
| unsigned long _hwcap; |
| unsigned long _hwcap2; |
| unsigned long _hwcap3; |
| unsigned long _hwcap4; |
| } __ifunc_arg_t; |
| |
| # define _IFUNC_ARG_HWCAP (1ULL << 62) |
| |
| /* From the file which imported `host-config.h' we can ascertain which |
| architectural extension provides relevant atomic support. From this, |
| we can proceed to tweak the ifunc selector behavior. */ |
| #if defined (LAT_CAS_N) |
| # define LSE_ATOP |
| #elif defined (LAT_LOAD_N) || defined (LAT_STORE_N) |
| # define LSE2_LRCPC3_ATOP |
| #elif defined (LAT_EXCH_N) || defined (LAT_FIOR_N) || defined (LAT_FAND_N) |
| # define LSE128_ATOP |
| #endif |
| |
| # if N == 16 |
| # if defined (LSE_ATOP) |
| # define IFUNC_NCOND(N) 1 |
| # define IFUNC_COND_1 (hwcap & HWCAP_ATOMICS) |
| # elif defined (LSE2_LRCPC3_ATOP) |
| # define IFUNC_NCOND(N) 2 |
| # define IFUNC_COND_1 (has_rcpc3 (hwcap, features)) |
| # define IFUNC_COND_2 (has_lse2 (hwcap, features)) |
| # elif defined (LSE128_ATOP) |
| # define IFUNC_NCOND(N) 1 |
| # define IFUNC_COND_1 (has_lse128 (hwcap, features)) |
| # else |
| # define IFUNC_NCOND(N) 0 |
| # define IFUNC_ALT 1 |
| # endif |
| # else |
| # define IFUNC_COND_1 (hwcap & HWCAP_ATOMICS) |
| # define IFUNC_NCOND(N) 1 |
| # endif |
| |
| #define MIDR_IMPLEMENTOR(midr) (((midr) >> 24) & 255) |
| #define MIDR_PARTNUM(midr) (((midr) >> 4) & 0xfff) |
| |
| static inline bool |
| has_lse2 (unsigned long hwcap, const __ifunc_arg_t *features) |
| { |
| /* Check for LSE2. */ |
| if (hwcap & HWCAP_USCAT) |
| return true; |
| |
| /* If LSE and CPUID are supported, check MIDR. */ |
| if (hwcap & HWCAP_CPUID && hwcap & HWCAP_ATOMICS) |
| { |
| unsigned long midr; |
| asm volatile ("mrs %0, midr_el1" : "=r" (midr)); |
| |
| /* Neoverse N1 supports atomic 128-bit load/store. */ |
| return MIDR_IMPLEMENTOR (midr) == 'A' && MIDR_PARTNUM (midr) == 0xd0c; |
| } |
| |
| return false; |
| } |
| |
| /* LSE128 atomic support encoded in ID_AA64ISAR0_EL1.Atomic, bits[23:20]. |
| The minimum value for LSE128 is 0b0011. */ |
| |
| #define AT_FEAT_FIELD(isar0) (((isar0) >> 20) & 15) |
| |
| static inline bool |
| has_lse128 (unsigned long hwcap, const __ifunc_arg_t *features) |
| { |
| if (hwcap & _IFUNC_ARG_HWCAP && features->_hwcap2 & HWCAP2_LSE128) |
| return true; |
| |
| /* If LSE2 and CPUID are supported, check for LSE128. */ |
| if (hwcap & HWCAP_CPUID && hwcap & HWCAP_USCAT) |
| { |
| unsigned long isar0; |
| asm volatile ("mrs %0, ID_AA64ISAR0_EL1" : "=r" (isar0)); |
| return AT_FEAT_FIELD (isar0) >= 3; |
| } |
| |
| return false; |
| } |
| |
| /* LRCPC atomic support encoded in ID_AA64ISAR1_EL1.Atomic, bits[23:20]. |
| The minimum value for LRCPC3 is 0b0011. */ |
| |
| static inline bool |
| has_rcpc3 (unsigned long hwcap, const __ifunc_arg_t *features) |
| { |
| /* LSE2 is a prerequisite for atomic LDIAPP/STILP - check HWCAP_USCAT since |
| has_lse2 is more expensive and Neoverse N1 does not have LRCPC3. */ |
| if (!(hwcap & HWCAP_USCAT)) |
| return false; |
| |
| if (hwcap & _IFUNC_ARG_HWCAP && features->_hwcap2 & HWCAP2_LRCPC3) |
| return true; |
| |
| if (hwcap & HWCAP_CPUID) |
| { |
| unsigned long isar1; |
| asm volatile ("mrs %0, ID_AA64ISAR1_EL1" : "=r" (isar1)); |
| return AT_FEAT_FIELD (isar1) >= 3; |
| } |
| |
| return false; |
| } |
| |
| #endif /* HAVE_IFUNC */ |
| |
| /* All 128-bit atomic functions are defined in aarch64/atomic_16.S. */ |
| #if N == 16 |
| # define DONE 1 |
| # if !HAVE_IFUNC |
| # define IFUNC_ALT 1 |
| # endif |
| #endif |
| |
| #include_next <host-config.h> |