[RFC PATCH v3 4/4] riscv: cpufeature: Full parser for "riscv, isa" strings
Tsukasa OI
research_trasio at irq.a4lg.com
Wed Dec 1 17:41:13 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 | 42 ++++++++++++++++++++++++++++++----
1 file changed, 37 insertions(+), 5 deletions(-)
diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
index f52e15488a70..a462ceb959d9 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,7 +119,10 @@ void __init riscv_fill_hwcap(void)
for (; *isa; ++isa) {
const char *ext = isa++;
const char *ext_end = isa;
- bool ext_long, ext_err = false;
+ unsigned int ext_major = UINT_MAX; /* default */
+ unsigned int ext_minor = 0;
+ bool ext_long, ext_vpair,
+ ext_err = false, ext_err_ver = false;
switch (*ext) {
case 's':
@@ -115,7 +134,7 @@ void __init riscv_fill_hwcap(void)
if (unlikely(!islower(*isa)
&& !isdigit(*isa)))
ext_err = true;
- /* Find end of the extension name backwards */
+ /* Parse backwards */
ext_end = isa;
if (unlikely(ext_err))
break;
@@ -123,13 +142,21 @@ 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_ver = true;
+ if (!ext_vpair) {
++ext_end;
break;
}
+ ext_minor = ext_major;
while (isdigit(*--ext_end))
;
+ if (_decimal_part_to_uint(++ext_end, &ext_major)
+ || ext_major == UINT_MAX)
+ ext_err_ver = true;
break;
default:
ext_long = false;
@@ -137,9 +164,12 @@ void __init riscv_fill_hwcap(void)
ext_err = true;
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_ver = true;
while (isdigit(*++isa))
;
if (*isa != 'p')
@@ -148,6 +178,8 @@ void __init riscv_fill_hwcap(void)
--isa;
break;
}
+ if (_decimal_part_to_uint(isa, &ext_minor))
+ ext_err_ver = true;
while (isdigit(*++isa))
;
break;
--
2.32.0
More information about the linux-riscv
mailing list