1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copied from arch/arm64/kernel/cpufeature.c 4 * 5 * Copyright (C) 2015 ARM Ltd. 6 * Copyright (C) 2017 SiFive 7 */ 8 9 #include <linux/bitmap.h> 10 #include <linux/ctype.h> 11 #include <linux/of.h> 12 #include <asm/processor.h> 13 #include <asm/hwcap.h> 14 #include <asm/smp.h> 15 #include <asm/switch_to.h> 16 17 #define NUM_ALPHA_EXTS ('z' - 'a' + 1) 18 19 unsigned long elf_hwcap __read_mostly; 20 21 /* Host ISA bitmap */ 22 static DECLARE_BITMAP(riscv_isa, RISCV_ISA_EXT_MAX) __read_mostly; 23 24 #ifdef CONFIG_FPU 25 __ro_after_init DEFINE_STATIC_KEY_FALSE(cpu_hwcap_fpu); 26 #endif 27 28 /** 29 * riscv_isa_extension_base() - Get base extension word 30 * 31 * @isa_bitmap: ISA bitmap to use 32 * Return: base extension word as unsigned long value 33 * 34 * NOTE: If isa_bitmap is NULL then Host ISA bitmap will be used. 35 */ 36 unsigned long riscv_isa_extension_base(const unsigned long *isa_bitmap) 37 { 38 if (!isa_bitmap) 39 return riscv_isa[0]; 40 return isa_bitmap[0]; 41 } 42 EXPORT_SYMBOL_GPL(riscv_isa_extension_base); 43 44 /** 45 * __riscv_isa_extension_available() - Check whether given extension 46 * is available or not 47 * 48 * @isa_bitmap: ISA bitmap to use 49 * @bit: bit position of the desired extension 50 * Return: true or false 51 * 52 * NOTE: If isa_bitmap is NULL then Host ISA bitmap will be used. 53 */ 54 bool __riscv_isa_extension_available(const unsigned long *isa_bitmap, int bit) 55 { 56 const unsigned long *bmap = (isa_bitmap) ? isa_bitmap : riscv_isa; 57 58 if (bit >= RISCV_ISA_EXT_MAX) 59 return false; 60 61 return test_bit(bit, bmap) ? true : false; 62 } 63 EXPORT_SYMBOL_GPL(__riscv_isa_extension_available); 64 65 void __init riscv_fill_hwcap(void) 66 { 67 struct device_node *node; 68 const char *isa; 69 char print_str[NUM_ALPHA_EXTS + 1]; 70 int i, j; 71 static unsigned long isa2hwcap[256] = {0}; 72 73 isa2hwcap['i'] = isa2hwcap['I'] = COMPAT_HWCAP_ISA_I; 74 isa2hwcap['m'] = isa2hwcap['M'] = COMPAT_HWCAP_ISA_M; 75 isa2hwcap['a'] = isa2hwcap['A'] = COMPAT_HWCAP_ISA_A; 76 isa2hwcap['f'] = isa2hwcap['F'] = COMPAT_HWCAP_ISA_F; 77 isa2hwcap['d'] = isa2hwcap['D'] = COMPAT_HWCAP_ISA_D; 78 isa2hwcap['c'] = isa2hwcap['C'] = COMPAT_HWCAP_ISA_C; 79 80 elf_hwcap = 0; 81 82 bitmap_zero(riscv_isa, RISCV_ISA_EXT_MAX); 83 84 for_each_of_cpu_node(node) { 85 unsigned long this_hwcap = 0; 86 DECLARE_BITMAP(this_isa, RISCV_ISA_EXT_MAX); 87 const char *temp; 88 89 if (riscv_of_processor_hartid(node) < 0) 90 continue; 91 92 if (of_property_read_string(node, "riscv,isa", &isa)) { 93 pr_warn("Unable to find \"riscv,isa\" devicetree entry\n"); 94 continue; 95 } 96 97 temp = isa; 98 #if IS_ENABLED(CONFIG_32BIT) 99 if (!strncmp(isa, "rv32", 4)) 100 isa += 4; 101 #elif IS_ENABLED(CONFIG_64BIT) 102 if (!strncmp(isa, "rv64", 4)) 103 isa += 4; 104 #endif 105 /* The riscv,isa DT property must start with rv64 or rv32 */ 106 if (temp == isa) 107 continue; 108 bitmap_zero(this_isa, RISCV_ISA_EXT_MAX); 109 for (; *isa; ++isa) { 110 const char *ext = isa++; 111 const char *ext_end = isa; 112 bool ext_long = false, ext_err = false; 113 114 switch (*ext) { 115 case 's': 116 /** 117 * Workaround for invalid single-letter 's' & 'u'(QEMU). 118 * No need to set the bit in riscv_isa as 's' & 'u' are 119 * not valid ISA extensions. It works until multi-letter 120 * extension starting with "Su" appears. 121 */ 122 if (ext[-1] != '_' && ext[1] == 'u') { 123 ++isa; 124 ext_err = true; 125 break; 126 } 127 fallthrough; 128 case 'x': 129 case 'z': 130 ext_long = true; 131 /* Multi-letter extension must be delimited */ 132 for (; *isa && *isa != '_'; ++isa) 133 if (unlikely(!islower(*isa) 134 && !isdigit(*isa))) 135 ext_err = true; 136 /* Parse backwards */ 137 ext_end = isa; 138 if (unlikely(ext_err)) 139 break; 140 if (!isdigit(ext_end[-1])) 141 break; 142 /* Skip the minor version */ 143 while (isdigit(*--ext_end)) 144 ; 145 if (ext_end[0] != 'p' 146 || !isdigit(ext_end[-1])) { 147 /* Advance it to offset the pre-decrement */ 148 ++ext_end; 149 break; 150 } 151 /* Skip the major version */ 152 while (isdigit(*--ext_end)) 153 ; 154 ++ext_end; 155 break; 156 default: 157 if (unlikely(!islower(*ext))) { 158 ext_err = true; 159 break; 160 } 161 /* Find next extension */ 162 if (!isdigit(*isa)) 163 break; 164 /* Skip the minor version */ 165 while (isdigit(*++isa)) 166 ; 167 if (*isa != 'p') 168 break; 169 if (!isdigit(*++isa)) { 170 --isa; 171 break; 172 } 173 /* Skip the major version */ 174 while (isdigit(*++isa)) 175 ; 176 break; 177 } 178 if (*isa != '_') 179 --isa; 180 181 #define SET_ISA_EXT_MAP(name, bit) \ 182 do { \ 183 if ((ext_end - ext == sizeof(name) - 1) && \ 184 !memcmp(ext, name, sizeof(name) - 1)) \ 185 set_bit(bit, this_isa); \ 186 } while (false) \ 187 188 if (unlikely(ext_err)) 189 continue; 190 if (!ext_long) { 191 this_hwcap |= isa2hwcap[(unsigned char)(*ext)]; 192 set_bit(*ext - 'a', this_isa); 193 } else { 194 SET_ISA_EXT_MAP("sscofpmf", RISCV_ISA_EXT_SSCOFPMF); 195 } 196 #undef SET_ISA_EXT_MAP 197 } 198 199 /* 200 * All "okay" hart should have same isa. Set HWCAP based on 201 * common capabilities of every "okay" hart, in case they don't 202 * have. 203 */ 204 if (elf_hwcap) 205 elf_hwcap &= this_hwcap; 206 else 207 elf_hwcap = this_hwcap; 208 209 if (bitmap_weight(riscv_isa, RISCV_ISA_EXT_MAX)) 210 bitmap_and(riscv_isa, riscv_isa, this_isa, RISCV_ISA_EXT_MAX); 211 else 212 bitmap_copy(riscv_isa, this_isa, RISCV_ISA_EXT_MAX); 213 214 } 215 216 /* We don't support systems with F but without D, so mask those out 217 * here. */ 218 if ((elf_hwcap & COMPAT_HWCAP_ISA_F) && !(elf_hwcap & COMPAT_HWCAP_ISA_D)) { 219 pr_info("This kernel does not support systems with F but not D\n"); 220 elf_hwcap &= ~COMPAT_HWCAP_ISA_F; 221 } 222 223 memset(print_str, 0, sizeof(print_str)); 224 for (i = 0, j = 0; i < NUM_ALPHA_EXTS; i++) 225 if (riscv_isa[0] & BIT_MASK(i)) 226 print_str[j++] = (char)('a' + i); 227 pr_info("riscv: base ISA extensions %s\n", print_str); 228 229 memset(print_str, 0, sizeof(print_str)); 230 for (i = 0, j = 0; i < NUM_ALPHA_EXTS; i++) 231 if (elf_hwcap & BIT_MASK(i)) 232 print_str[j++] = (char)('a' + i); 233 pr_info("riscv: ELF capabilities %s\n", print_str); 234 235 #ifdef CONFIG_FPU 236 if (elf_hwcap & (COMPAT_HWCAP_ISA_F | COMPAT_HWCAP_ISA_D)) 237 static_branch_enable(&cpu_hwcap_fpu); 238 #endif 239 } 240