Skip to content

Commit 2a31c54

Browse files
a4lgpalmer-dabbelt
authored andcommitted
RISC-V: Minimal parser for "riscv, isa" strings
Current hart ISA ("riscv,isa") parser don't correctly parse: 1. Multi-letter extensions 2. Version numbers All ISA extensions ratified recently has multi-letter extensions (except 'H'). The current "riscv,isa" parser that is easily confused by multi-letter extensions and "p" in version numbers can be a huge problem for adding new extensions through the device tree. Leaving it would create incompatible hacks and would make "riscv,isa" value unreliable. This commit implements minimal parser for "riscv,isa" strings. With this, we can safely ignore multi-letter extensions and version numbers. [Improved commit text and fixed a bug around 's' in base extension] Signed-off-by: Atish Patra <atishp@rivosinc.com> [Fixed workaround for QEMU] Signed-off-by: Tsukasa OI <research_trasio@irq.a4lg.com> Tested-by: Heiko Stuebner <heiko@sntech.de> Reviewed-by: Anup Patel <anup@brainfault.org> Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
1 parent 58004f2 commit 2a31c54

1 file changed

Lines changed: 61 additions & 11 deletions

File tree

arch/riscv/kernel/cpufeature.c

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
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>
@@ -66,7 +67,7 @@ void __init riscv_fill_hwcap(void)
6667
struct device_node *node;
6768
const char *isa;
6869
char print_str[NUM_ALPHA_EXTS + 1];
69-
size_t i, j, isa_len;
70+
int i, j;
7071
static unsigned long isa2hwcap[256] = {0};
7172

7273
isa2hwcap['i'] = isa2hwcap['I'] = COMPAT_HWCAP_ISA_I;
@@ -92,23 +93,72 @@ void __init riscv_fill_hwcap(void)
9293
continue;
9394
}
9495

95-
i = 0;
96-
isa_len = strlen(isa);
9796
#if IS_ENABLED(CONFIG_32BIT)
9897
if (!strncmp(isa, "rv32", 4))
99-
i += 4;
98+
isa += 4;
10099
#elif IS_ENABLED(CONFIG_64BIT)
101100
if (!strncmp(isa, "rv64", 4))
102-
i += 4;
101+
isa += 4;
103102
#endif
104-
for (; i < isa_len; ++i) {
105-
this_hwcap |= isa2hwcap[(unsigned char)(isa[i])];
103+
for (; *isa; ++isa) {
104+
const char *ext = isa++;
105+
const char *ext_end = isa;
106+
bool ext_long = false, ext_err = false;
107+
108+
switch (*ext) {
109+
case 's':
110+
/**
111+
* Workaround for invalid single-letter 's' & 'u'(QEMU).
112+
* No need to set the bit in riscv_isa as 's' & 'u' are
113+
* not valid ISA extensions. It works until multi-letter
114+
* extension starting with "Su" appears.
115+
*/
116+
if (ext[-1] != '_' && ext[1] == 'u') {
117+
++isa;
118+
ext_err = true;
119+
break;
120+
}
121+
fallthrough;
122+
case 'x':
123+
case 'z':
124+
ext_long = true;
125+
/* Multi-letter extension must be delimited */
126+
for (; *isa && *isa != '_'; ++isa)
127+
if (!islower(*isa) && !isdigit(*isa))
128+
ext_err = true;
129+
break;
130+
default:
131+
if (unlikely(!islower(*ext))) {
132+
ext_err = true;
133+
break;
134+
}
135+
/* Find next extension */
136+
if (!isdigit(*isa))
137+
break;
138+
/* Skip the minor version */
139+
while (isdigit(*++isa))
140+
;
141+
if (*isa != 'p')
142+
break;
143+
if (!isdigit(*++isa)) {
144+
--isa;
145+
break;
146+
}
147+
/* Skip the major version */
148+
while (isdigit(*++isa))
149+
;
150+
break;
151+
}
152+
if (*isa != '_')
153+
--isa;
106154
/*
107-
* TODO: X, Y and Z extension parsing for Host ISA
108-
* bitmap will be added in-future.
155+
* TODO: Full version-aware handling including
156+
* multi-letter extensions will be added in-future.
109157
*/
110-
if ('a' <= isa[i] && isa[i] < 'x')
111-
this_isa |= (1UL << (isa[i] - 'a'));
158+
if (ext_err || ext_long)
159+
continue;
160+
this_hwcap |= isa2hwcap[(unsigned char)(*ext)];
161+
this_isa |= (1UL << (*ext - 'a'));
112162
}
113163

114164
/*

0 commit comments

Comments
 (0)