Skip to content

Commit 6b57ac0

Browse files
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
2 parents 9d1f0ec + a9b2026 commit 6b57ac0

3 files changed

Lines changed: 195 additions & 23 deletions

File tree

arch/riscv/include/asm/hwcap.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,32 @@ extern unsigned long elf_hwcap;
3434
#define RISCV_ISA_EXT_s ('s' - 'a')
3535
#define RISCV_ISA_EXT_u ('u' - 'a')
3636

37+
/*
38+
* Increse this to higher value as kernel support more ISA extensions.
39+
*/
3740
#define RISCV_ISA_EXT_MAX 64
41+
#define RISCV_ISA_EXT_NAME_LEN_MAX 32
42+
43+
/* The base ID for multi-letter ISA extensions */
44+
#define RISCV_ISA_EXT_BASE 26
45+
46+
/*
47+
* This enum represent the logical ID for each multi-letter RISC-V ISA extension.
48+
* The logical ID should start from RISCV_ISA_EXT_BASE and must not exceed
49+
* RISCV_ISA_EXT_MAX. 0-25 range is reserved for single letter
50+
* extensions while all the multi-letter extensions should define the next
51+
* available logical extension id.
52+
*/
53+
enum riscv_isa_ext_id {
54+
RISCV_ISA_EXT_ID_MAX = RISCV_ISA_EXT_MAX,
55+
};
56+
57+
struct riscv_isa_ext_data {
58+
/* Name of the extension displayed to userspace via /proc/cpuinfo */
59+
char uprop[RISCV_ISA_EXT_NAME_LEN_MAX];
60+
/* The logical ISA extension ID */
61+
unsigned int isa_ext_id;
62+
};
3863

3964
unsigned long riscv_isa_extension_base(const unsigned long *isa_bitmap);
4065

arch/riscv/kernel/cpu.c

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <linux/init.h>
77
#include <linux/seq_file.h>
88
#include <linux/of.h>
9+
#include <asm/hwcap.h>
910
#include <asm/smp.h>
1011
#include <asm/pgtable.h>
1112

@@ -63,12 +64,72 @@ int riscv_of_parent_hartid(struct device_node *node)
6364
}
6465

6566
#ifdef CONFIG_PROC_FS
67+
#define __RISCV_ISA_EXT_DATA(UPROP, EXTID) \
68+
{ \
69+
.uprop = #UPROP, \
70+
.isa_ext_id = EXTID, \
71+
}
72+
/**
73+
* Here are the ordering rules of extension naming defined by RISC-V
74+
* specification :
75+
* 1. All extensions should be separated from other multi-letter extensions
76+
* from other multi-letter extensions by an underscore.
77+
* 2. The first letter following the 'Z' conventionally indicates the most
78+
* closely related alphabetical extension category, IMAFDQLCBKJTPVH.
79+
* If multiple 'Z' extensions are named, they should be ordered first
80+
* by category, then alphabetically within a category.
81+
* 3. Standard supervisor-level extensions (starts with 'S') should be
82+
* listed after standard unprivileged extensions. If multiple
83+
* supervisor-level extensions are listed, they should be ordered
84+
* alphabetically.
85+
* 4. Non-standard extensions (starts with 'X') must be listed after all
86+
* standard extensions. They must be separated from other multi-letter
87+
* extensions by an underscore.
88+
*/
89+
static struct riscv_isa_ext_data isa_ext_arr[] = {
90+
__RISCV_ISA_EXT_DATA("", RISCV_ISA_EXT_MAX),
91+
};
92+
93+
static void print_isa_ext(struct seq_file *f)
94+
{
95+
struct riscv_isa_ext_data *edata;
96+
int i = 0, arr_sz;
97+
98+
arr_sz = ARRAY_SIZE(isa_ext_arr) - 1;
99+
100+
/* No extension support available */
101+
if (arr_sz <= 0)
102+
return;
103+
104+
for (i = 0; i <= arr_sz; i++) {
105+
edata = &isa_ext_arr[i];
106+
if (!__riscv_isa_extension_available(NULL, edata->isa_ext_id))
107+
continue;
108+
seq_printf(f, "_%s", edata->uprop);
109+
}
110+
}
111+
112+
/**
113+
* These are the only valid base (single letter) ISA extensions as per the spec.
114+
* It also specifies the canonical order in which it appears in the spec.
115+
* Some of the extension may just be a place holder for now (B, K, P, J).
116+
* This should be updated once corresponding extensions are ratified.
117+
*/
118+
static const char base_riscv_exts[13] = "imafdqcbkjpvh";
66119

67120
static void print_isa(struct seq_file *f, const char *isa)
68121
{
69-
/* Print the entire ISA as it is */
122+
int i;
123+
70124
seq_puts(f, "isa\t\t: ");
71-
seq_write(f, isa, strlen(isa));
125+
/* Print the rv[64/32] part */
126+
seq_write(f, isa, 4);
127+
for (i = 0; i < sizeof(base_riscv_exts); i++) {
128+
if (__riscv_isa_extension_available(NULL, base_riscv_exts[i] - 'a'))
129+
/* Print only enabled the base ISA extensions */
130+
seq_write(f, &base_riscv_exts[i], 1);
131+
}
132+
print_isa_ext(f);
72133
seq_puts(f, "\n");
73134
}
74135

arch/riscv/kernel/cpufeature.c

Lines changed: 107 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@
77
*/
88

99
#include <linux/bitmap.h>
10+
#include <linux/ctype.h>
1011
#include <linux/of.h>
1112
#include <asm/processor.h>
1213
#include <asm/hwcap.h>
1314
#include <asm/smp.h>
1415
#include <asm/switch_to.h>
1516

17+
#define NUM_ALPHA_EXTS ('z' - 'a' + 1)
18+
1619
unsigned long elf_hwcap __read_mostly;
1720

1821
/* Host ISA bitmap */
@@ -63,8 +66,8 @@ void __init riscv_fill_hwcap(void)
6366
{
6467
struct device_node *node;
6568
const char *isa;
66-
char print_str[BITS_PER_LONG + 1];
67-
size_t i, j, isa_len;
69+
char print_str[NUM_ALPHA_EXTS + 1];
70+
int i, j;
6871
static unsigned long isa2hwcap[256] = {0};
6972

7073
isa2hwcap['i'] = isa2hwcap['I'] = COMPAT_HWCAP_ISA_I;
@@ -80,7 +83,8 @@ void __init riscv_fill_hwcap(void)
8083

8184
for_each_of_cpu_node(node) {
8285
unsigned long this_hwcap = 0;
83-
unsigned long this_isa = 0;
86+
DECLARE_BITMAP(this_isa, RISCV_ISA_EXT_MAX);
87+
const char *temp;
8488

8589
if (riscv_of_processor_hartid(node) < 0)
8690
continue;
@@ -90,23 +94,104 @@ void __init riscv_fill_hwcap(void)
9094
continue;
9195
}
9296

93-
i = 0;
94-
isa_len = strlen(isa);
97+
temp = isa;
9598
#if IS_ENABLED(CONFIG_32BIT)
9699
if (!strncmp(isa, "rv32", 4))
97-
i += 4;
100+
isa += 4;
98101
#elif IS_ENABLED(CONFIG_64BIT)
99102
if (!strncmp(isa, "rv64", 4))
100-
i += 4;
103+
isa += 4;
101104
#endif
102-
for (; i < isa_len; ++i) {
103-
this_hwcap |= isa2hwcap[(unsigned char)(isa[i])];
104-
/*
105-
* TODO: X, Y and Z extension parsing for Host ISA
106-
* bitmap will be added in-future.
107-
*/
108-
if ('a' <= isa[i] && isa[i] < 'x')
109-
this_isa |= (1UL << (isa[i] - 'a'));
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+
}
194+
#undef SET_ISA_EXT_MAP
110195
}
111196

112197
/*
@@ -119,10 +204,11 @@ void __init riscv_fill_hwcap(void)
119204
else
120205
elf_hwcap = this_hwcap;
121206

122-
if (riscv_isa[0])
123-
riscv_isa[0] &= this_isa;
207+
if (bitmap_weight(riscv_isa, RISCV_ISA_EXT_MAX))
208+
bitmap_and(riscv_isa, riscv_isa, this_isa, RISCV_ISA_EXT_MAX);
124209
else
125-
riscv_isa[0] = this_isa;
210+
bitmap_copy(riscv_isa, this_isa, RISCV_ISA_EXT_MAX);
211+
126212
}
127213

128214
/* We don't support systems with F but without D, so mask those out
@@ -133,13 +219,13 @@ void __init riscv_fill_hwcap(void)
133219
}
134220

135221
memset(print_str, 0, sizeof(print_str));
136-
for (i = 0, j = 0; i < BITS_PER_LONG; i++)
222+
for (i = 0, j = 0; i < NUM_ALPHA_EXTS; i++)
137223
if (riscv_isa[0] & BIT_MASK(i))
138224
print_str[j++] = (char)('a' + i);
139-
pr_info("riscv: ISA extensions %s\n", print_str);
225+
pr_info("riscv: base ISA extensions %s\n", print_str);
140226

141227
memset(print_str, 0, sizeof(print_str));
142-
for (i = 0, j = 0; i < BITS_PER_LONG; i++)
228+
for (i = 0, j = 0; i < NUM_ALPHA_EXTS; i++)
143229
if (elf_hwcap & BIT_MASK(i))
144230
print_str[j++] = (char)('a' + i);
145231
pr_info("riscv: ELF capabilities %s\n", print_str);

0 commit comments

Comments
 (0)