1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copied from arch/arm64/kernel/cpufeature.c
5 * Copyright (C) 2015 ARM Ltd.
6 * Copyright (C) 2017 SiFive
9 #include <linux/bitmap.h>
10 #include <linux/ctype.h>
12 #include <asm/processor.h>
13 #include <asm/hwcap.h>
15 #include <asm/switch_to.h>
17 #define NUM_ALPHA_EXTS ('z' - 'a' + 1)
19 unsigned long elf_hwcap __read_mostly;
22 static DECLARE_BITMAP(riscv_isa, RISCV_ISA_EXT_MAX) __read_mostly;
25 __ro_after_init DEFINE_STATIC_KEY_FALSE(cpu_hwcap_fpu);
29 * riscv_isa_extension_base() - Get base extension word
31 * @isa_bitmap: ISA bitmap to use
32 * Return: base extension word as unsigned long value
34 * NOTE: If isa_bitmap is NULL then Host ISA bitmap will be used.
36 unsigned long riscv_isa_extension_base(const unsigned long *isa_bitmap)
42 EXPORT_SYMBOL_GPL(riscv_isa_extension_base);
45 * __riscv_isa_extension_available() - Check whether given extension
48 * @isa_bitmap: ISA bitmap to use
49 * @bit: bit position of the desired extension
50 * Return: true or false
52 * NOTE: If isa_bitmap is NULL then Host ISA bitmap will be used.
54 bool __riscv_isa_extension_available(const unsigned long *isa_bitmap, int bit)
56 const unsigned long *bmap = (isa_bitmap) ? isa_bitmap : riscv_isa;
58 if (bit >= RISCV_ISA_EXT_MAX)
61 return test_bit(bit, bmap) ? true : false;
63 EXPORT_SYMBOL_GPL(__riscv_isa_extension_available);
65 void __init riscv_fill_hwcap(void)
67 struct device_node *node;
69 char print_str[NUM_ALPHA_EXTS + 1];
71 static unsigned long isa2hwcap[256] = {0};
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;
82 bitmap_zero(riscv_isa, RISCV_ISA_EXT_MAX);
84 for_each_of_cpu_node(node) {
85 unsigned long this_hwcap = 0;
86 unsigned long this_isa = 0;
88 if (riscv_of_processor_hartid(node) < 0)
91 if (of_property_read_string(node, "riscv,isa", &isa)) {
92 pr_warn("Unable to find \"riscv,isa\" devicetree entry\n");
96 #if IS_ENABLED(CONFIG_32BIT)
97 if (!strncmp(isa, "rv32", 4))
99 #elif IS_ENABLED(CONFIG_64BIT)
100 if (!strncmp(isa, "rv64", 4))
103 for (; *isa; ++isa) {
104 const char *ext = isa++;
105 const char *ext_end = isa;
106 bool ext_long = false, ext_err = false;
113 * Workaround for invalid single-letter 's' (QEMU).
114 * It works until multi-letter extension starting
117 if (*ext == 's' && ext[-1] != '_' && ext[1] == 'u')
120 /* Multi-letter extension must be delimited */
121 for (; *isa && *isa != '_'; ++isa)
122 if (unlikely(!islower(*isa)
125 /* Parse backwards */
127 if (unlikely(ext_err))
129 if (!isdigit(ext_end[-1]))
131 /* Skip the minor version */
132 while (isdigit(*--ext_end))
134 if (ext_end[0] != 'p'
135 || !isdigit(ext_end[-1])) {
136 /* Advance it to offset the pre-decrement */
140 /* Skip the major version */
141 while (isdigit(*--ext_end))
146 if (unlikely(!islower(*ext))) {
150 /* Find next extension */
153 /* Skip the minor version */
154 while (isdigit(*++isa))
158 if (!isdigit(*++isa)) {
162 /* Skip the major version */
163 while (isdigit(*++isa))
170 if (unlikely(ext_err))
173 this_hwcap |= isa2hwcap[(unsigned char)(*ext)];
174 this_isa |= (1UL << (*ext - 'a'));
179 * All "okay" hart should have same isa. Set HWCAP based on
180 * common capabilities of every "okay" hart, in case they don't
184 elf_hwcap &= this_hwcap;
186 elf_hwcap = this_hwcap;
189 riscv_isa[0] &= this_isa;
191 riscv_isa[0] = this_isa;
194 /* We don't support systems with F but without D, so mask those out
196 if ((elf_hwcap & COMPAT_HWCAP_ISA_F) && !(elf_hwcap & COMPAT_HWCAP_ISA_D)) {
197 pr_info("This kernel does not support systems with F but not D\n");
198 elf_hwcap &= ~COMPAT_HWCAP_ISA_F;
201 memset(print_str, 0, sizeof(print_str));
202 for (i = 0, j = 0; i < NUM_ALPHA_EXTS; i++)
203 if (riscv_isa[0] & BIT_MASK(i))
204 print_str[j++] = (char)('a' + i);
205 pr_info("riscv: ISA extensions %s\n", print_str);
207 memset(print_str, 0, sizeof(print_str));
208 for (i = 0, j = 0; i < NUM_ALPHA_EXTS; i++)
209 if (elf_hwcap & BIT_MASK(i))
210 print_str[j++] = (char)('a' + i);
211 pr_info("riscv: ELF capabilities %s\n", print_str);
214 if (elf_hwcap & (COMPAT_HWCAP_ISA_F | COMPAT_HWCAP_ISA_D))
215 static_branch_enable(&cpu_hwcap_fpu);