@@ -102,6 +102,7 @@ void __init riscv_fill_hwcap(void)
#endif
for (; *isa; ++isa) {
const char *ext = isa++;
+ const char *ext_end = isa;
bool ext_long, ext_err = false;
switch (*ext) {
@@ -119,19 +120,39 @@ void __init riscv_fill_hwcap(void)
ext_long = true;
/* Multi-letter extension must be delimited */
for (; *isa && *isa != '_'; ++isa)
- if (!islower(*isa) && !isdigit(*isa))
+ if (unlikely(!islower(*isa)
+ && !isdigit(*isa)))
ext_err = true;
- /* ... but must be ignored. */
+ /* Parse backwards */
+ ext_end = isa;
+ if (unlikely(ext_err))
+ break;
+ if (!isdigit(ext_end[-1]))
+ break;
+ /* Skip the minor version */
+ while (isdigit(*--ext_end))
+ ;
+ if (ext_end[0] != 'p'
+ || !isdigit(ext_end[-1])) {
+ /* Advance it to offset the pre-decrement */
+ ++ext_end;
+ break;
+ }
+ /* Skip the major version */
+ while (isdigit(*--ext_end))
+ ;
+ ++ext_end;
break;
default:
ext_long = false;
- if (!islower(*ext)) {
+ if (unlikely(!islower(*ext))) {
ext_err = true;
break;
}
/* Find next extension */
if (!isdigit(*isa))
break;
+ /* Skip the minor version */
while (isdigit(*++isa))
;
if (*isa != 'p')
@@ -140,20 +161,20 @@ void __init riscv_fill_hwcap(void)
--isa;
break;
}
+ /* Skip the major version */
while (isdigit(*++isa))
;
break;
}
if (*isa != '_')
--isa;
- /*
- * TODO: Full version-aware handling including
- * multi-letter extensions will be added in-future.
- */
- if (ext_err || ext_long)
+
+ if (unlikely(ext_err))
continue;
- this_hwcap |= isa2hwcap[(unsigned char)(*ext)];
- this_isa |= (1UL << (*ext - 'a'));
+ if (!ext_long) {
+ this_hwcap |= isa2hwcap[(unsigned char)(*ext)];
+ this_isa |= (1UL << (*ext - 'a'));
+ }
}
/*