[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