linux/arch/riscv/kernel/cpufeature.c
<<
>>
Prefs
   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
  19unsigned long elf_hwcap __read_mostly;
  20
  21/* Host ISA bitmap */
  22static 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 */
  36unsigned 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}
  42EXPORT_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 */
  54bool __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}
  63EXPORT_SYMBOL_GPL(__riscv_isa_extension_available);
  64
  65void __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