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 <linux/jump_label.h> 13 14 #include <asm/cpucaps.h> 15 #include <asm/hwcap.h> 16 #include <asm/sysreg.h> 17 18 /* 19 * In the arm64 world (as in the ARM world), elf_hwcap is used both internally 20 * in the kernel and for user space to keep track of which optional features 21 * are supported by the current system. So let's map feature 'x' to HWCAP_x. 22 * Note that HWCAP_x constants are bit fields so we need to take the log. 23 */ 24 25 #define MAX_CPU_FEATURES (8 * sizeof(elf_hwcap)) 26 #define cpu_feature(x) ilog2(HWCAP_ ## x) 27 28 #ifndef __ASSEMBLY__ 29 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 static inline bool cpus_have_cap(unsigned int num) 108 { 109 if (num >= ARM64_NCAPS) 110 return false; 111 if (__builtin_constant_p(num)) 112 return static_branch_unlikely(&cpu_hwcap_keys[num]); 113 else 114 return test_bit(num, cpu_hwcaps); 115 } 116 117 static inline void cpus_set_cap(unsigned int num) 118 { 119 if (num >= ARM64_NCAPS) { 120 pr_warn("Attempt to set an illegal CPU capability (%d >= %d)\n", 121 num, ARM64_NCAPS); 122 } else { 123 __set_bit(num, cpu_hwcaps); 124 static_branch_enable(&cpu_hwcap_keys[num]); 125 } 126 } 127 128 static inline int __attribute_const__ 129 cpuid_feature_extract_signed_field_width(u64 features, int field, int width) 130 { 131 return (s64)(features << (64 - width - field)) >> (64 - width); 132 } 133 134 static inline int __attribute_const__ 135 cpuid_feature_extract_signed_field(u64 features, int field) 136 { 137 return cpuid_feature_extract_signed_field_width(features, field, 4); 138 } 139 140 static inline unsigned int __attribute_const__ 141 cpuid_feature_extract_unsigned_field_width(u64 features, int field, int width) 142 { 143 return (u64)(features << (64 - width - field)) >> (64 - width); 144 } 145 146 static inline unsigned int __attribute_const__ 147 cpuid_feature_extract_unsigned_field(u64 features, int field) 148 { 149 return cpuid_feature_extract_unsigned_field_width(features, field, 4); 150 } 151 152 static inline u64 arm64_ftr_mask(const struct arm64_ftr_bits *ftrp) 153 { 154 return (u64)GENMASK(ftrp->shift + ftrp->width - 1, ftrp->shift); 155 } 156 157 static inline int __attribute_const__ 158 cpuid_feature_extract_field(u64 features, int field, bool sign) 159 { 160 return (sign) ? 161 cpuid_feature_extract_signed_field(features, field) : 162 cpuid_feature_extract_unsigned_field(features, field); 163 } 164 165 static inline s64 arm64_ftr_value(const struct arm64_ftr_bits *ftrp, u64 val) 166 { 167 return (s64)cpuid_feature_extract_field(val, ftrp->shift, ftrp->sign); 168 } 169 170 static inline bool id_aa64mmfr0_mixed_endian_el0(u64 mmfr0) 171 { 172 return cpuid_feature_extract_unsigned_field(mmfr0, ID_AA64MMFR0_BIGENDEL_SHIFT) == 0x1 || 173 cpuid_feature_extract_unsigned_field(mmfr0, ID_AA64MMFR0_BIGENDEL0_SHIFT) == 0x1; 174 } 175 176 static inline bool id_aa64pfr0_32bit_el0(u64 pfr0) 177 { 178 u32 val = cpuid_feature_extract_unsigned_field(pfr0, ID_AA64PFR0_EL0_SHIFT); 179 180 return val == ID_AA64PFR0_EL0_32BIT_64BIT; 181 } 182 183 void __init setup_cpu_features(void); 184 185 void update_cpu_capabilities(const struct arm64_cpu_capabilities *caps, 186 const char *info); 187 void enable_cpu_capabilities(const struct arm64_cpu_capabilities *caps); 188 void check_local_cpu_capabilities(void); 189 190 void update_cpu_errata_workarounds(void); 191 void __init enable_errata_workarounds(void); 192 void verify_local_cpu_errata_workarounds(void); 193 194 u64 read_system_reg(u32 id); 195 196 static inline bool cpu_supports_mixed_endian_el0(void) 197 { 198 return id_aa64mmfr0_mixed_endian_el0(read_cpuid(ID_AA64MMFR0_EL1)); 199 } 200 201 static inline bool system_supports_32bit_el0(void) 202 { 203 return cpus_have_cap(ARM64_HAS_32BIT_EL0); 204 } 205 206 static inline bool system_supports_mixed_endian_el0(void) 207 { 208 return id_aa64mmfr0_mixed_endian_el0(read_system_reg(SYS_ID_AA64MMFR0_EL1)); 209 } 210 211 #endif /* __ASSEMBLY__ */ 212 213 #endif 214