diff mbox series

[RFC,v2,4/4] riscv: cpufeature: Full parser for "riscv, isa" strings

Message ID c2e68e66303142d0ad6c682e22f6e07bc7b62232.1637834060.git.research_trasio@irq.a4lg.com (mailing list archive)
State New, archived
Headers show
Series riscv: cpufeature: Improvements for extended feature handling | expand

Commit Message

Tsukasa OI Nov. 25, 2021, 10:02 a.m. UTC
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@irq.a4lg.com>
---
 arch/riscv/kernel/cpufeature.c | 44 ++++++++++++++++++++++++++++++----
 1 file changed, 39 insertions(+), 5 deletions(-)
diff mbox series

Patch

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;