1 /* 2 * Copyright (C) 2014 Linaro Ltd. <ard.biesheuvel@linaro.org> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 as 6 * published by the Free Software Foundation. 7 */ 8 9 #ifndef __ASM_CPUFEATURE_H 10 #define __ASM_CPUFEATURE_H 11 12 #include <asm/cpucaps.h> 13 #include <asm/hwcap.h> 14 #include <asm/sysreg.h> 15 16 /* 17 * In the arm64 world (as in the ARM world), elf_hwcap is used both internally 18 * in the kernel and for user space to keep track of which optional features 19 * are supported by the current system. So let's map feature 'x' to HWCAP_x. 20 * Note that HWCAP_x constants are bit fields so we need to take the log. 21 */ 22 23 #define MAX_CPU_FEATURES (8 * sizeof(elf_hwcap)) 24 #define cpu_feature(x) ilog2(HWCAP_ ## x) 25 26 #ifndef __ASSEMBLY__ 27 28 #include <linux/bug.h> 29 #include <linux/jump_label.h> 30 #include <linux/kernel.h> 31 32 /* CPU feature register tracking */ 33 enum ftr_type { 34 FTR_EXACT, /* Use a predefined safe value */ 35 FTR_LOWER_SAFE, /* Smaller value is safe */ 36 FTR_HIGHER_SAFE,/* Bigger value is safe */ 37 }; 38 39 #define FTR_STRICT true /* SANITY check strict matching required */ 40 #define FTR_NONSTRICT false /* SANITY check ignored */ 41 42 #define FTR_SIGNED true /* Value should be treated as signed */ 43 #define FTR_UNSIGNED false /* Value should be treated as unsigned */ 44 45 struct arm64_ftr_bits { 46 bool sign; /* Value is signed ? */ 47 bool strict; /* CPU Sanity check: strict matching required ? */ 48 enum ftr_type type; 49 u8 shift; 50 u8 width; 51 s64 safe_val; /* safe value for FTR_EXACT features */ 52 }; 53 54 /* 55 * @arm64_ftr_reg - Feature register 56 * @strict_mask Bits which should match across all CPUs for sanity. 57 * @sys_val Safe value across the CPUs (system view) 58 */ 59 struct arm64_ftr_reg { 60 const char *name; 61 u64 strict_mask; 62 u64 sys_val; 63 const struct arm64_ftr_bits *ftr_bits; 64 }; 65 66 extern struct arm64_ftr_reg arm64_ftr_reg_ctrel0; 67 68 /* scope of capability check */ 69 enum { 70 SCOPE_SYSTEM, 71 SCOPE_LOCAL_CPU, 72 }; 73 74 struct arm64_cpu_capabilities { 75 const char *desc; 76 u16 capability; 77 int def_scope; /* default scope */ 78 bool (*matches)(const struct arm64_cpu_capabilities *caps, int scope); 79 int (*enable)(void *); /* Called on all active CPUs */ 80 union { 81 struct { /* To be used for erratum handling only */ 82 u32 midr_model; 83 u32 midr_range_min, midr_range_max; 84 }; 85 86 struct { /* Feature register checking */ 87 u32 sys_reg; 88 u8 field_pos; 89 u8 min_field_value; 90 u8 hwcap_type; 91 bool sign; 92 unsigned long hwcap; 93 }; 94 }; 95 }; 96 97 extern DECLARE_BITMAP(cpu_hwcaps, ARM64_NCAPS); 98 extern struct static_key_false cpu_hwcap_keys[ARM64_NCAPS]; 99 100 bool this_cpu_has_cap(unsigned int cap); 101 102 static inline bool cpu_have_feature(unsigned int num) 103 { 104 return elf_hwcap & (1UL << num); 105 } 106 107 /* System capability check for constant caps */ 108 static inline bool cpus_have_const_cap(int num) 109 { 110 if (num >= ARM64_NCAPS) 111 return false; 112 return static_branch_unlikely(&cpu_hwcap_keys[num]); 113 } 114 115 static inline bool cpus_have_cap(unsigned int num) 116 { 117 if (num >= ARM64_NCAPS) 118 return false; 119 return test_bit(num, cpu_hwcaps); 120 } 121 122 static inline void cpus_set_cap(unsigned int num) 123 { 124 if (num >= ARM64_NCAPS) { 125 pr_warn("Attempt to set an illegal CPU capability (%d >= %d)\n", 126 num, ARM64_NCAPS); 127 } else { 128 __set_bit(num, cpu_hwcaps); 129 static_branch_enable(&cpu_hwcap_keys[num]); 130 } 131 } 132 133 static inline int __attribute_const__ 134 cpuid_feature_extract_signed_field_width(u64 features, int field, int width) 135 { 136 return (s64)(features << (64 - width - field)) >> (64 - width); 137 } 138 139 static inline int __attribute_const__ 140 cpuid_feature_extract_signed_field(u64 features, int field) 141 { 142 return cpuid_feature_extract_signed_field_width(features, field, 4); 143 } 144 145 static inline unsigned int __attribute_const__ 146 cpuid_feature_extract_unsigned_field_width(u64 features, int field, int width) 147 { 148 return (u64)(features << (64 - width - field)) >> (64 - width); 149 } 150 151 static inline unsigned int __attribute_const__ 152 cpuid_feature_extract_unsigned_field(u64 features, int field) 153 { 154 return cpuid_feature_extract_unsigned_field_width(features, field, 4); 155 } 156 157 static inline u64 arm64_ftr_mask(const struct arm64_ftr_bits *ftrp) 158 { 159 return (u64)GENMASK(ftrp->shift + ftrp->width - 1, ftrp->shift); 160 } 161 162 static inline int __attribute_const__ 163 cpuid_feature_extract_field(u64 features, int field, bool sign) 164 { 165 return (sign) ? 166 cpuid_feature_extract_signed_field(features, field) : 167 cpuid_feature_extract_unsigned_field(features, field); 168 } 169 170 static inline s64 arm64_ftr_value(const struct arm64_ftr_bits *ftrp, u64 val) 171 { 172 return (s64)cpuid_feature_extract_field(val, ftrp->shift, ftrp->sign); 173 } 174 175 static inline bool id_aa64mmfr0_mixed_endian_el0(u64 mmfr0) 176 { 177 return cpuid_feature_extract_unsigned_field(mmfr0, ID_AA64MMFR0_BIGENDEL_SHIFT) == 0x1 || 178 cpuid_feature_extract_unsigned_field(mmfr0, ID_AA64MMFR0_BIGENDEL0_SHIFT) == 0x1; 179 } 180 181 static inline bool id_aa64pfr0_32bit_el0(u64 pfr0) 182 { 183 u32 val = cpuid_feature_extract_unsigned_field(pfr0, ID_AA64PFR0_EL0_SHIFT); 184 185 return val == ID_AA64PFR0_EL0_32BIT_64BIT; 186 } 187 188 void __init setup_cpu_features(void); 189 190 void update_cpu_capabilities(const struct arm64_cpu_capabilities *caps, 191 const char *info); 192 void enable_cpu_capabilities(const struct arm64_cpu_capabilities *caps); 193 void check_local_cpu_capabilities(void); 194 195 void update_cpu_errata_workarounds(void); 196 void __init enable_errata_workarounds(void); 197 void verify_local_cpu_errata_workarounds(void); 198 199 u64 read_system_reg(u32 id); 200 201 static inline bool cpu_supports_mixed_endian_el0(void) 202 { 203 return id_aa64mmfr0_mixed_endian_el0(read_cpuid(ID_AA64MMFR0_EL1)); 204 } 205 206 static inline bool system_supports_32bit_el0(void) 207 { 208 return cpus_have_const_cap(ARM64_HAS_32BIT_EL0); 209 } 210 211 static inline bool system_supports_mixed_endian_el0(void) 212 { 213 return id_aa64mmfr0_mixed_endian_el0(read_system_reg(SYS_ID_AA64MMFR0_EL1)); 214 } 215 216 static inline bool system_supports_fpsimd(void) 217 { 218 return !cpus_have_const_cap(ARM64_HAS_NO_FPSIMD); 219 } 220 221 static inline bool system_uses_ttbr0_pan(void) 222 { 223 return IS_ENABLED(CONFIG_ARM64_SW_TTBR0_PAN) && 224 !cpus_have_cap(ARM64_HAS_PAN); 225 } 226 227 #endif /* __ASSEMBLY__ */ 228 229 #endif 230