forked from Minki/linux
RISC-V: Provide a fraemework for RISC-V ISA extensions
This series implements a generic framework to parse multi-letter ISA extensions. * palmer/riscv-isa: RISC-V: Improve /proc/cpuinfo output for ISA extensions RISC-V: Do no continue isa string parsing without correct XLEN RISC-V: Implement multi-letter ISA extension probing framework RISC-V: Extract multi-letter extension names from "riscv, isa" RISC-V: Minimal parser for "riscv, isa" strings RISC-V: Correctly print supported extensions
This commit is contained in:
commit
6b57ac02b4
@ -34,7 +34,32 @@ extern unsigned long elf_hwcap;
|
||||
#define RISCV_ISA_EXT_s ('s' - 'a')
|
||||
#define RISCV_ISA_EXT_u ('u' - 'a')
|
||||
|
||||
/*
|
||||
* Increse this to higher value as kernel support more ISA extensions.
|
||||
*/
|
||||
#define RISCV_ISA_EXT_MAX 64
|
||||
#define RISCV_ISA_EXT_NAME_LEN_MAX 32
|
||||
|
||||
/* The base ID for multi-letter ISA extensions */
|
||||
#define RISCV_ISA_EXT_BASE 26
|
||||
|
||||
/*
|
||||
* This enum represent the logical ID for each multi-letter RISC-V ISA extension.
|
||||
* The logical ID should start from RISCV_ISA_EXT_BASE and must not exceed
|
||||
* RISCV_ISA_EXT_MAX. 0-25 range is reserved for single letter
|
||||
* extensions while all the multi-letter extensions should define the next
|
||||
* available logical extension id.
|
||||
*/
|
||||
enum riscv_isa_ext_id {
|
||||
RISCV_ISA_EXT_ID_MAX = RISCV_ISA_EXT_MAX,
|
||||
};
|
||||
|
||||
struct riscv_isa_ext_data {
|
||||
/* Name of the extension displayed to userspace via /proc/cpuinfo */
|
||||
char uprop[RISCV_ISA_EXT_NAME_LEN_MAX];
|
||||
/* The logical ISA extension ID */
|
||||
unsigned int isa_ext_id;
|
||||
};
|
||||
|
||||
unsigned long riscv_isa_extension_base(const unsigned long *isa_bitmap);
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/of.h>
|
||||
#include <asm/hwcap.h>
|
||||
#include <asm/smp.h>
|
||||
#include <asm/pgtable.h>
|
||||
|
||||
@ -63,12 +64,72 @@ int riscv_of_parent_hartid(struct device_node *node)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
#define __RISCV_ISA_EXT_DATA(UPROP, EXTID) \
|
||||
{ \
|
||||
.uprop = #UPROP, \
|
||||
.isa_ext_id = EXTID, \
|
||||
}
|
||||
/**
|
||||
* Here are the ordering rules of extension naming defined by RISC-V
|
||||
* specification :
|
||||
* 1. All extensions should be separated from other multi-letter extensions
|
||||
* from other multi-letter extensions by an underscore.
|
||||
* 2. The first letter following the 'Z' conventionally indicates the most
|
||||
* closely related alphabetical extension category, IMAFDQLCBKJTPVH.
|
||||
* If multiple 'Z' extensions are named, they should be ordered first
|
||||
* by category, then alphabetically within a category.
|
||||
* 3. Standard supervisor-level extensions (starts with 'S') should be
|
||||
* listed after standard unprivileged extensions. If multiple
|
||||
* supervisor-level extensions are listed, they should be ordered
|
||||
* alphabetically.
|
||||
* 4. Non-standard extensions (starts with 'X') must be listed after all
|
||||
* standard extensions. They must be separated from other multi-letter
|
||||
* extensions by an underscore.
|
||||
*/
|
||||
static struct riscv_isa_ext_data isa_ext_arr[] = {
|
||||
__RISCV_ISA_EXT_DATA("", RISCV_ISA_EXT_MAX),
|
||||
};
|
||||
|
||||
static void print_isa_ext(struct seq_file *f)
|
||||
{
|
||||
struct riscv_isa_ext_data *edata;
|
||||
int i = 0, arr_sz;
|
||||
|
||||
arr_sz = ARRAY_SIZE(isa_ext_arr) - 1;
|
||||
|
||||
/* No extension support available */
|
||||
if (arr_sz <= 0)
|
||||
return;
|
||||
|
||||
for (i = 0; i <= arr_sz; i++) {
|
||||
edata = &isa_ext_arr[i];
|
||||
if (!__riscv_isa_extension_available(NULL, edata->isa_ext_id))
|
||||
continue;
|
||||
seq_printf(f, "_%s", edata->uprop);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* These are the only valid base (single letter) ISA extensions as per the spec.
|
||||
* It also specifies the canonical order in which it appears in the spec.
|
||||
* Some of the extension may just be a place holder for now (B, K, P, J).
|
||||
* This should be updated once corresponding extensions are ratified.
|
||||
*/
|
||||
static const char base_riscv_exts[13] = "imafdqcbkjpvh";
|
||||
|
||||
static void print_isa(struct seq_file *f, const char *isa)
|
||||
{
|
||||
/* Print the entire ISA as it is */
|
||||
int i;
|
||||
|
||||
seq_puts(f, "isa\t\t: ");
|
||||
seq_write(f, isa, strlen(isa));
|
||||
/* Print the rv[64/32] part */
|
||||
seq_write(f, isa, 4);
|
||||
for (i = 0; i < sizeof(base_riscv_exts); i++) {
|
||||
if (__riscv_isa_extension_available(NULL, base_riscv_exts[i] - 'a'))
|
||||
/* Print only enabled the base ISA extensions */
|
||||
seq_write(f, &base_riscv_exts[i], 1);
|
||||
}
|
||||
print_isa_ext(f);
|
||||
seq_puts(f, "\n");
|
||||
}
|
||||
|
||||
|
@ -7,12 +7,15 @@
|
||||
*/
|
||||
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/of.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/hwcap.h>
|
||||
#include <asm/smp.h>
|
||||
#include <asm/switch_to.h>
|
||||
|
||||
#define NUM_ALPHA_EXTS ('z' - 'a' + 1)
|
||||
|
||||
unsigned long elf_hwcap __read_mostly;
|
||||
|
||||
/* Host ISA bitmap */
|
||||
@ -63,8 +66,8 @@ void __init riscv_fill_hwcap(void)
|
||||
{
|
||||
struct device_node *node;
|
||||
const char *isa;
|
||||
char print_str[BITS_PER_LONG + 1];
|
||||
size_t i, j, isa_len;
|
||||
char print_str[NUM_ALPHA_EXTS + 1];
|
||||
int i, j;
|
||||
static unsigned long isa2hwcap[256] = {0};
|
||||
|
||||
isa2hwcap['i'] = isa2hwcap['I'] = COMPAT_HWCAP_ISA_I;
|
||||
@ -80,7 +83,8 @@ void __init riscv_fill_hwcap(void)
|
||||
|
||||
for_each_of_cpu_node(node) {
|
||||
unsigned long this_hwcap = 0;
|
||||
unsigned long this_isa = 0;
|
||||
DECLARE_BITMAP(this_isa, RISCV_ISA_EXT_MAX);
|
||||
const char *temp;
|
||||
|
||||
if (riscv_of_processor_hartid(node) < 0)
|
||||
continue;
|
||||
@ -90,23 +94,104 @@ void __init riscv_fill_hwcap(void)
|
||||
continue;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
isa_len = strlen(isa);
|
||||
temp = isa;
|
||||
#if IS_ENABLED(CONFIG_32BIT)
|
||||
if (!strncmp(isa, "rv32", 4))
|
||||
i += 4;
|
||||
isa += 4;
|
||||
#elif IS_ENABLED(CONFIG_64BIT)
|
||||
if (!strncmp(isa, "rv64", 4))
|
||||
i += 4;
|
||||
isa += 4;
|
||||
#endif
|
||||
for (; i < isa_len; ++i) {
|
||||
this_hwcap |= isa2hwcap[(unsigned char)(isa[i])];
|
||||
/*
|
||||
* TODO: X, Y and Z extension parsing for Host ISA
|
||||
* bitmap will be added in-future.
|
||||
*/
|
||||
if ('a' <= isa[i] && isa[i] < 'x')
|
||||
this_isa |= (1UL << (isa[i] - 'a'));
|
||||
/* The riscv,isa DT property must start with rv64 or rv32 */
|
||||
if (temp == isa)
|
||||
continue;
|
||||
bitmap_zero(this_isa, RISCV_ISA_EXT_MAX);
|
||||
for (; *isa; ++isa) {
|
||||
const char *ext = isa++;
|
||||
const char *ext_end = isa;
|
||||
bool ext_long = false, ext_err = false;
|
||||
|
||||
switch (*ext) {
|
||||
case 's':
|
||||
/**
|
||||
* Workaround for invalid single-letter 's' & 'u'(QEMU).
|
||||
* No need to set the bit in riscv_isa as 's' & 'u' are
|
||||
* not valid ISA extensions. It works until multi-letter
|
||||
* extension starting with "Su" appears.
|
||||
*/
|
||||
if (ext[-1] != '_' && ext[1] == 'u') {
|
||||
++isa;
|
||||
ext_err = true;
|
||||
break;
|
||||
}
|
||||
fallthrough;
|
||||
case 'x':
|
||||
case 'z':
|
||||
ext_long = true;
|
||||
/* Multi-letter extension must be delimited */
|
||||
for (; *isa && *isa != '_'; ++isa)
|
||||
if (unlikely(!islower(*isa)
|
||||
&& !isdigit(*isa)))
|
||||
ext_err = true;
|
||||
/* Parse backwards */
|
||||
ext_end = isa;
|
||||
if (unlikely(ext_err))
|
||||
break;
|
||||
if (!isdigit(ext_end[-1]))
|
||||
break;
|
||||
/* Skip the minor version */
|
||||
while (isdigit(*--ext_end))
|
||||
;
|
||||
if (ext_end[0] != 'p'
|
||||
|| !isdigit(ext_end[-1])) {
|
||||
/* Advance it to offset the pre-decrement */
|
||||
++ext_end;
|
||||
break;
|
||||
}
|
||||
/* Skip the major version */
|
||||
while (isdigit(*--ext_end))
|
||||
;
|
||||
++ext_end;
|
||||
break;
|
||||
default:
|
||||
if (unlikely(!islower(*ext))) {
|
||||
ext_err = true;
|
||||
break;
|
||||
}
|
||||
/* Find next extension */
|
||||
if (!isdigit(*isa))
|
||||
break;
|
||||
/* Skip the minor version */
|
||||
while (isdigit(*++isa))
|
||||
;
|
||||
if (*isa != 'p')
|
||||
break;
|
||||
if (!isdigit(*++isa)) {
|
||||
--isa;
|
||||
break;
|
||||
}
|
||||
/* Skip the major version */
|
||||
while (isdigit(*++isa))
|
||||
;
|
||||
break;
|
||||
}
|
||||
if (*isa != '_')
|
||||
--isa;
|
||||
|
||||
#define SET_ISA_EXT_MAP(name, bit) \
|
||||
do { \
|
||||
if ((ext_end - ext == sizeof(name) - 1) && \
|
||||
!memcmp(ext, name, sizeof(name) - 1)) \
|
||||
set_bit(bit, this_isa); \
|
||||
} while (false) \
|
||||
|
||||
if (unlikely(ext_err))
|
||||
continue;
|
||||
if (!ext_long) {
|
||||
this_hwcap |= isa2hwcap[(unsigned char)(*ext)];
|
||||
set_bit(*ext - 'a', this_isa);
|
||||
}
|
||||
#undef SET_ISA_EXT_MAP
|
||||
}
|
||||
|
||||
/*
|
||||
@ -119,10 +204,11 @@ void __init riscv_fill_hwcap(void)
|
||||
else
|
||||
elf_hwcap = this_hwcap;
|
||||
|
||||
if (riscv_isa[0])
|
||||
riscv_isa[0] &= this_isa;
|
||||
if (bitmap_weight(riscv_isa, RISCV_ISA_EXT_MAX))
|
||||
bitmap_and(riscv_isa, riscv_isa, this_isa, RISCV_ISA_EXT_MAX);
|
||||
else
|
||||
riscv_isa[0] = this_isa;
|
||||
bitmap_copy(riscv_isa, this_isa, RISCV_ISA_EXT_MAX);
|
||||
|
||||
}
|
||||
|
||||
/* We don't support systems with F but without D, so mask those out
|
||||
@ -133,13 +219,13 @@ void __init riscv_fill_hwcap(void)
|
||||
}
|
||||
|
||||
memset(print_str, 0, sizeof(print_str));
|
||||
for (i = 0, j = 0; i < BITS_PER_LONG; i++)
|
||||
for (i = 0, j = 0; i < NUM_ALPHA_EXTS; i++)
|
||||
if (riscv_isa[0] & BIT_MASK(i))
|
||||
print_str[j++] = (char)('a' + i);
|
||||
pr_info("riscv: ISA extensions %s\n", print_str);
|
||||
pr_info("riscv: base ISA extensions %s\n", print_str);
|
||||
|
||||
memset(print_str, 0, sizeof(print_str));
|
||||
for (i = 0, j = 0; i < BITS_PER_LONG; i++)
|
||||
for (i = 0, j = 0; i < NUM_ALPHA_EXTS; i++)
|
||||
if (elf_hwcap & BIT_MASK(i))
|
||||
print_str[j++] = (char)('a' + i);
|
||||
pr_info("riscv: ELF capabilities %s\n", print_str);
|
||||
|
Loading…
Reference in New Issue
Block a user