[RFC PATCH v2 4/4] riscv: cpufeature: Full parser for "riscv, isa" strings
Tsukasa OI
research_trasio at irq.a4lg.com
Thu Nov 25 02:02:55 PST 2021
This commit implements full parser for "riscv,isa" strings.
We haven't determined how do we represent multi-letter and/or versioned
extensions in the ISA bitmap yet. So, this commit handles only single-
letter extensions with no respect to version numbers (as before).
Nevertheless, it can be a foundation for our future work.
Note that major version of UINT_MAX represents non-versioned extension
(in many cases, they should be handled as 1 with some exceptions).
Signed-off-by: Tsukasa OI <research_trasio at irq.a4lg.com>
---
arch/riscv/kernel/cpufeature.c | 44 ++++++++++++++++++++++++++++++----
1 file changed, 39 insertions(+), 5 deletions(-)
diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
index 61bc326d15b0..8c64f63a686c 100644
--- a/arch/riscv/kernel/cpufeature.c
+++ b/arch/riscv/kernel/cpufeature.c
@@ -62,6 +62,22 @@ bool __riscv_isa_extension_available(const unsigned long *isa_bitmap, int bit)
}
EXPORT_SYMBOL_GPL(__riscv_isa_extension_available);
+static inline int _decimal_part_to_uint(const char *s, unsigned int *res)
+{
+ unsigned int value = 0, d;
+
+ if (!isdigit(*s))
+ return -EINVAL;
+ do {
+ d = *s - '0';
+ if (value > (UINT_MAX - d) / 10)
+ return -ERANGE;
+ value = value * 10 + d;
+ } while (isdigit(*++s));
+ *res = value;
+ return 0;
+}
+
void __init riscv_fill_hwcap(void)
{
struct device_node *node;
@@ -103,8 +119,10 @@ void __init riscv_fill_hwcap(void)
for (; *isa; ++isa) {
const char *ext = isa++;
const char *ext_end = isa;
+ unsigned int ext_major = UINT_MAX; /* default */
+ unsigned int ext_minor = 0;
unsigned short ext_err = 0;
- bool ext_long;
+ bool ext_long, ext_vpair;
switch (*ext) {
case 'h':
@@ -116,7 +134,7 @@ void __init riscv_fill_hwcap(void)
for (; *isa && *isa != '_'; ++isa)
if (!islower(*isa) && !isdigit(*isa))
ext_err = 1;
- /* Find end of the extension name backwards */
+ /* Parse backwards */
ext_end = isa;
if (ext_err)
break;
@@ -124,13 +142,23 @@ void __init riscv_fill_hwcap(void)
break;
while (isdigit(*--ext_end))
;
- if (ext_end[0] != 'p'
- || !isdigit(ext_end[-1])) {
+ ext_vpair = (ext_end[0] == 'p')
+ && isdigit(ext_end[-1]);
+ if (_decimal_part_to_uint(ext_end + 1,
+ &ext_major))
+ ext_err = ext_vpair ? 3 : 2;
+ if (!ext_vpair) {
++ext_end;
+ if (ext_major == UINT_MAX)
+ ext_err = 2;
break;
}
+ ext_minor = ext_major;
while (isdigit(*--ext_end))
;
+ if (_decimal_part_to_uint(++ext_end, &ext_major)
+ || ext_major == UINT_MAX)
+ ext_err = 2;
break;
default:
ext_long = false;
@@ -138,9 +166,12 @@ void __init riscv_fill_hwcap(void)
ext_err = 1;
break;
}
- /* Find next extension */
+ /* Parse forwards finding next extension */
if (!isdigit(*isa))
break;
+ _decimal_part_to_uint(isa, &ext_major);
+ if (ext_major == UINT_MAX)
+ ext_err = 2;
while (isdigit(*++isa))
;
if (*isa != 'p')
@@ -149,6 +180,9 @@ void __init riscv_fill_hwcap(void)
--isa;
break;
}
+ if (!ext_err &&
+ _decimal_part_to_uint(isa, &ext_minor))
+ ext_err = 3;
while (isdigit(*++isa))
;
break;
--
2.32.0
More information about the linux-riscv
mailing list