Skip to content

Commit 625034a

Browse files
clementlegerpalmer-dabbelt
authored andcommitted
riscv: add ISA extensions validation callback
Since a few extensions (Zicbom/Zicboz) already needs validation and future ones will need it as well (Zc*) add a validate() callback to struct riscv_isa_ext_data. This require to rework the way extensions are parsed and split it in two phases. First phase is isa string or isa extension list parsing and consists in enabling all the extensions in a temporary bitmask (source isa) without any validation. The second step "resolves" the final isa bitmap, handling potential missing dependencies. The mechanism is quite simple and simply validate each extension described in the source bitmap before enabling it in the resolved isa bitmap. validate() callbacks can return either 0 for success, -EPROBEDEFER if extension needs to be validated again at next loop. A previous ISA bitmap is kept to avoid looping multiple times if an extension dependencies are never satisfied until we reach a stable state. In order to avoid any potential infinite looping, allow looping a maximum of the number of extension we handle. Zicboz and Zicbom extensions are modified to use this validation mechanism. Signed-off-by: Clément Léger <[email protected]> Reviewed-by: Conor Dooley <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Palmer Dabbelt <[email protected]>
1 parent e9f9946 commit 625034a

File tree

2 files changed

+136
-85
lines changed

2 files changed

+136
-85
lines changed

arch/riscv/include/asm/cpufeature.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ struct riscv_isa_ext_data {
7070
const char *property;
7171
const unsigned int *subset_ext_ids;
7272
const unsigned int subset_ext_size;
73+
int (*validate)(const struct riscv_isa_ext_data *data, const unsigned long *isa_bitmap);
7374
};
7475

7576
extern const struct riscv_isa_ext_data riscv_isa_ext[];

arch/riscv/kernel/cpufeature.c

Lines changed: 135 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -72,51 +72,55 @@ bool __riscv_isa_extension_available(const unsigned long *isa_bitmap, unsigned i
7272
}
7373
EXPORT_SYMBOL_GPL(__riscv_isa_extension_available);
7474

75-
static bool riscv_isa_extension_check(int id)
75+
static int riscv_ext_zicbom_validate(const struct riscv_isa_ext_data *data,
76+
const unsigned long *isa_bitmap)
7677
{
77-
switch (id) {
78-
case RISCV_ISA_EXT_ZICBOM:
79-
if (!riscv_cbom_block_size) {
80-
pr_err("Zicbom detected in ISA string, disabling as no cbom-block-size found\n");
81-
return false;
82-
} else if (!is_power_of_2(riscv_cbom_block_size)) {
83-
pr_err("Zicbom disabled as cbom-block-size present, but is not a power-of-2\n");
84-
return false;
85-
}
86-
return true;
87-
case RISCV_ISA_EXT_ZICBOZ:
88-
if (!riscv_cboz_block_size) {
89-
pr_err("Zicboz detected in ISA string, disabling as no cboz-block-size found\n");
90-
return false;
91-
} else if (!is_power_of_2(riscv_cboz_block_size)) {
92-
pr_err("Zicboz disabled as cboz-block-size present, but is not a power-of-2\n");
93-
return false;
94-
}
95-
return true;
96-
case RISCV_ISA_EXT_INVALID:
97-
return false;
78+
if (!riscv_cbom_block_size) {
79+
pr_err("Zicbom detected in ISA string, disabling as no cbom-block-size found\n");
80+
return -EINVAL;
9881
}
82+
if (!is_power_of_2(riscv_cbom_block_size)) {
83+
pr_err("Zicbom disabled as cbom-block-size present, but is not a power-of-2\n");
84+
return -EINVAL;
85+
}
86+
return 0;
87+
}
9988

100-
return true;
89+
static int riscv_ext_zicboz_validate(const struct riscv_isa_ext_data *data,
90+
const unsigned long *isa_bitmap)
91+
{
92+
if (!riscv_cboz_block_size) {
93+
pr_err("Zicboz detected in ISA string, disabling as no cboz-block-size found\n");
94+
return -EINVAL;
95+
}
96+
if (!is_power_of_2(riscv_cboz_block_size)) {
97+
pr_err("Zicboz disabled as cboz-block-size present, but is not a power-of-2\n");
98+
return -EINVAL;
99+
}
100+
return 0;
101101
}
102102

103-
#define _RISCV_ISA_EXT_DATA(_name, _id, _subset_exts, _subset_exts_size) { \
104-
.name = #_name, \
105-
.property = #_name, \
106-
.id = _id, \
107-
.subset_ext_ids = _subset_exts, \
108-
.subset_ext_size = _subset_exts_size \
103+
#define _RISCV_ISA_EXT_DATA(_name, _id, _subset_exts, _subset_exts_size, _validate) { \
104+
.name = #_name, \
105+
.property = #_name, \
106+
.id = _id, \
107+
.subset_ext_ids = _subset_exts, \
108+
.subset_ext_size = _subset_exts_size, \
109+
.validate = _validate \
109110
}
110111

111-
#define __RISCV_ISA_EXT_DATA(_name, _id) _RISCV_ISA_EXT_DATA(_name, _id, NULL, 0)
112+
#define __RISCV_ISA_EXT_DATA(_name, _id) _RISCV_ISA_EXT_DATA(_name, _id, NULL, 0, NULL)
112113

113114
/* Used to declare pure "lasso" extension (Zk for instance) */
114115
#define __RISCV_ISA_EXT_BUNDLE(_name, _bundled_exts) \
115-
_RISCV_ISA_EXT_DATA(_name, RISCV_ISA_EXT_INVALID, _bundled_exts, ARRAY_SIZE(_bundled_exts))
116+
_RISCV_ISA_EXT_DATA(_name, RISCV_ISA_EXT_INVALID, _bundled_exts, \
117+
ARRAY_SIZE(_bundled_exts), NULL)
116118

117119
/* Used to declare extensions that are a superset of other extensions (Zvbb for instance) */
118120
#define __RISCV_ISA_EXT_SUPERSET(_name, _id, _sub_exts) \
119-
_RISCV_ISA_EXT_DATA(_name, _id, _sub_exts, ARRAY_SIZE(_sub_exts))
121+
_RISCV_ISA_EXT_DATA(_name, _id, _sub_exts, ARRAY_SIZE(_sub_exts), NULL)
122+
#define __RISCV_ISA_EXT_SUPERSET_VALIDATE(_name, _id, _sub_exts, _validate) \
123+
_RISCV_ISA_EXT_DATA(_name, _id, _sub_exts, ARRAY_SIZE(_sub_exts), _validate)
120124

121125
static const unsigned int riscv_zk_bundled_exts[] = {
122126
RISCV_ISA_EXT_ZBKB,
@@ -281,8 +285,10 @@ const struct riscv_isa_ext_data riscv_isa_ext[] = {
281285
__RISCV_ISA_EXT_DATA(c, RISCV_ISA_EXT_c),
282286
__RISCV_ISA_EXT_SUPERSET(v, RISCV_ISA_EXT_v, riscv_v_exts),
283287
__RISCV_ISA_EXT_DATA(h, RISCV_ISA_EXT_h),
284-
__RISCV_ISA_EXT_SUPERSET(zicbom, RISCV_ISA_EXT_ZICBOM, riscv_xlinuxenvcfg_exts),
285-
__RISCV_ISA_EXT_SUPERSET(zicboz, RISCV_ISA_EXT_ZICBOZ, riscv_xlinuxenvcfg_exts),
288+
__RISCV_ISA_EXT_SUPERSET_VALIDATE(zicbom, RISCV_ISA_EXT_ZICBOM, riscv_xlinuxenvcfg_exts,
289+
riscv_ext_zicbom_validate),
290+
__RISCV_ISA_EXT_SUPERSET_VALIDATE(zicboz, RISCV_ISA_EXT_ZICBOZ, riscv_xlinuxenvcfg_exts,
291+
riscv_ext_zicboz_validate),
286292
__RISCV_ISA_EXT_DATA(zicntr, RISCV_ISA_EXT_ZICNTR),
287293
__RISCV_ISA_EXT_DATA(zicond, RISCV_ISA_EXT_ZICOND),
288294
__RISCV_ISA_EXT_DATA(zicsr, RISCV_ISA_EXT_ZICSR),
@@ -349,33 +355,93 @@ const struct riscv_isa_ext_data riscv_isa_ext[] = {
349355

350356
const size_t riscv_isa_ext_count = ARRAY_SIZE(riscv_isa_ext);
351357

352-
static void __init match_isa_ext(const struct riscv_isa_ext_data *ext, const char *name,
353-
const char *name_end, struct riscv_isainfo *isainfo)
358+
static void riscv_isa_set_ext(const struct riscv_isa_ext_data *ext, unsigned long *bitmap)
354359
{
355-
if ((name_end - name == strlen(ext->name)) &&
356-
!strncasecmp(name, ext->name, name_end - name)) {
357-
/*
358-
* If this is a bundle, enable all the ISA extensions that
359-
* comprise the bundle.
360-
*/
361-
if (ext->subset_ext_size) {
362-
for (int i = 0; i < ext->subset_ext_size; i++) {
363-
if (riscv_isa_extension_check(ext->subset_ext_ids[i]))
364-
set_bit(ext->subset_ext_ids[i], isainfo->isa);
360+
if (ext->id != RISCV_ISA_EXT_INVALID)
361+
set_bit(ext->id, bitmap);
362+
363+
for (int i = 0; i < ext->subset_ext_size; i++) {
364+
if (ext->subset_ext_ids[i] != RISCV_ISA_EXT_INVALID)
365+
set_bit(ext->subset_ext_ids[i], bitmap);
366+
}
367+
}
368+
369+
static const struct riscv_isa_ext_data *riscv_get_isa_ext_data(unsigned int ext_id)
370+
{
371+
for (int i = 0; i < riscv_isa_ext_count; i++) {
372+
if (riscv_isa_ext[i].id == ext_id)
373+
return &riscv_isa_ext[i];
374+
}
375+
376+
return NULL;
377+
}
378+
379+
/*
380+
* "Resolve" a source ISA bitmap into one that matches kernel configuration as
381+
* well as correct extension dependencies. Some extensions depends on specific
382+
* kernel configuration to be usable (V needs CONFIG_RISCV_ISA_V for instance)
383+
* and this function will actually validate all the extensions provided in
384+
* source_isa into the resolved_isa based on extensions validate() callbacks.
385+
*/
386+
static void __init riscv_resolve_isa(unsigned long *source_isa,
387+
unsigned long *resolved_isa, unsigned long *this_hwcap,
388+
unsigned long *isa2hwcap)
389+
{
390+
bool loop;
391+
const struct riscv_isa_ext_data *ext;
392+
DECLARE_BITMAP(prev_resolved_isa, RISCV_ISA_EXT_MAX);
393+
int max_loop_count = riscv_isa_ext_count, ret;
394+
unsigned int bit;
395+
396+
do {
397+
loop = false;
398+
if (max_loop_count-- < 0) {
399+
pr_err("Failed to reach a stable ISA state\n");
400+
return;
401+
}
402+
bitmap_copy(prev_resolved_isa, resolved_isa, RISCV_ISA_EXT_MAX);
403+
for_each_set_bit(bit, source_isa, RISCV_ISA_EXT_MAX) {
404+
ext = riscv_get_isa_ext_data(bit);
405+
if (!ext)
406+
continue;
407+
408+
if (ext->validate) {
409+
ret = ext->validate(ext, resolved_isa);
410+
if (ret == -EPROBE_DEFER) {
411+
loop = true;
412+
continue;
413+
} else if (ret) {
414+
/* Disable the extension entirely */
415+
clear_bit(ext->id, source_isa);
416+
continue;
417+
}
365418
}
419+
420+
set_bit(ext->id, resolved_isa);
421+
/* No need to keep it in source isa now that it is enabled */
422+
clear_bit(ext->id, source_isa);
423+
424+
/* Single letter extensions get set in hwcap */
425+
if (ext->id < RISCV_ISA_EXT_BASE)
426+
*this_hwcap |= isa2hwcap[ext->id];
366427
}
428+
} while (loop && memcmp(prev_resolved_isa, resolved_isa, sizeof(prev_resolved_isa)));
429+
}
367430

368-
/*
369-
* This is valid even for bundle extensions which uses the RISCV_ISA_EXT_INVALID id
370-
* (rejected by riscv_isa_extension_check()).
371-
*/
372-
if (riscv_isa_extension_check(ext->id))
373-
set_bit(ext->id, isainfo->isa);
431+
static void __init match_isa_ext(const char *name, const char *name_end, unsigned long *bitmap)
432+
{
433+
for (int i = 0; i < riscv_isa_ext_count; i++) {
434+
const struct riscv_isa_ext_data *ext = &riscv_isa_ext[i];
435+
436+
if ((name_end - name == strlen(ext->name)) &&
437+
!strncasecmp(name, ext->name, name_end - name)) {
438+
riscv_isa_set_ext(ext, bitmap);
439+
break;
440+
}
374441
}
375442
}
376443

377-
static void __init riscv_parse_isa_string(unsigned long *this_hwcap, struct riscv_isainfo *isainfo,
378-
unsigned long *isa2hwcap, const char *isa)
444+
static void __init riscv_parse_isa_string(const char *isa, unsigned long *bitmap)
379445
{
380446
/*
381447
* For all possible cpus, we have already validated in
@@ -388,7 +454,7 @@ static void __init riscv_parse_isa_string(unsigned long *this_hwcap, struct risc
388454
while (*isa) {
389455
const char *ext = isa++;
390456
const char *ext_end = isa;
391-
bool ext_long = false, ext_err = false;
457+
bool ext_err = false;
392458

393459
switch (*ext) {
394460
case 's':
@@ -428,7 +494,6 @@ static void __init riscv_parse_isa_string(unsigned long *this_hwcap, struct risc
428494
* character itself while eliminating the extensions version number.
429495
* A simple re-increment solves this problem.
430496
*/
431-
ext_long = true;
432497
for (; *isa && *isa != '_'; ++isa)
433498
if (unlikely(!isalnum(*isa)))
434499
ext_err = true;
@@ -509,15 +574,7 @@ static void __init riscv_parse_isa_string(unsigned long *this_hwcap, struct risc
509574
if (unlikely(ext_err))
510575
continue;
511576

512-
for (int i = 0; i < riscv_isa_ext_count; i++)
513-
match_isa_ext(&riscv_isa_ext[i], ext, ext_end, isainfo);
514-
515-
if (!ext_long) {
516-
int nr = tolower(*ext) - 'a';
517-
518-
if (riscv_isa_extension_check(nr))
519-
*this_hwcap |= isa2hwcap[nr];
520-
}
577+
match_isa_ext(ext, ext_end, bitmap);
521578
}
522579
}
523580

@@ -544,6 +601,7 @@ static void __init riscv_fill_hwcap_from_isa_string(unsigned long *isa2hwcap)
544601
for_each_possible_cpu(cpu) {
545602
struct riscv_isainfo *isainfo = &hart_isa[cpu];
546603
unsigned long this_hwcap = 0;
604+
DECLARE_BITMAP(source_isa, RISCV_ISA_EXT_MAX) = { 0 };
547605

548606
if (acpi_disabled) {
549607
node = of_cpu_device_node_get(cpu);
@@ -566,18 +624,18 @@ static void __init riscv_fill_hwcap_from_isa_string(unsigned long *isa2hwcap)
566624
}
567625
}
568626

569-
riscv_parse_isa_string(&this_hwcap, isainfo, isa2hwcap, isa);
627+
riscv_parse_isa_string(isa, source_isa);
570628

571629
/*
572630
* These ones were as they were part of the base ISA when the
573631
* port & dt-bindings were upstreamed, and so can be set
574632
* unconditionally where `i` is in riscv,isa on DT systems.
575633
*/
576634
if (acpi_disabled) {
577-
set_bit(RISCV_ISA_EXT_ZICSR, isainfo->isa);
578-
set_bit(RISCV_ISA_EXT_ZIFENCEI, isainfo->isa);
579-
set_bit(RISCV_ISA_EXT_ZICNTR, isainfo->isa);
580-
set_bit(RISCV_ISA_EXT_ZIHPM, isainfo->isa);
635+
set_bit(RISCV_ISA_EXT_ZICSR, source_isa);
636+
set_bit(RISCV_ISA_EXT_ZIFENCEI, source_isa);
637+
set_bit(RISCV_ISA_EXT_ZICNTR, source_isa);
638+
set_bit(RISCV_ISA_EXT_ZIHPM, source_isa);
581639
}
582640

583641
/*
@@ -590,9 +648,11 @@ static void __init riscv_fill_hwcap_from_isa_string(unsigned long *isa2hwcap)
590648
*/
591649
if (acpi_disabled && boot_vendorid == THEAD_VENDOR_ID && boot_archid == 0x0) {
592650
this_hwcap &= ~isa2hwcap[RISCV_ISA_EXT_v];
593-
clear_bit(RISCV_ISA_EXT_v, isainfo->isa);
651+
clear_bit(RISCV_ISA_EXT_v, source_isa);
594652
}
595653

654+
riscv_resolve_isa(source_isa, isainfo->isa, &this_hwcap, isa2hwcap);
655+
596656
/*
597657
* All "okay" hart should have same isa. Set HWCAP based on
598658
* common capabilities of every "okay" hart, in case they don't
@@ -621,6 +681,7 @@ static int __init riscv_fill_hwcap_from_ext_list(unsigned long *isa2hwcap)
621681
unsigned long this_hwcap = 0;
622682
struct device_node *cpu_node;
623683
struct riscv_isainfo *isainfo = &hart_isa[cpu];
684+
DECLARE_BITMAP(source_isa, RISCV_ISA_EXT_MAX) = { 0 };
624685

625686
cpu_node = of_cpu_device_node_get(cpu);
626687
if (!cpu_node) {
@@ -640,22 +701,11 @@ static int __init riscv_fill_hwcap_from_ext_list(unsigned long *isa2hwcap)
640701
ext->property) < 0)
641702
continue;
642703

643-
if (ext->subset_ext_size) {
644-
for (int j = 0; j < ext->subset_ext_size; j++) {
645-
if (riscv_isa_extension_check(ext->subset_ext_ids[j]))
646-
set_bit(ext->subset_ext_ids[j], isainfo->isa);
647-
}
648-
}
649-
650-
if (riscv_isa_extension_check(ext->id)) {
651-
set_bit(ext->id, isainfo->isa);
652-
653-
/* Only single letter extensions get set in hwcap */
654-
if (strnlen(riscv_isa_ext[i].name, 2) == 1)
655-
this_hwcap |= isa2hwcap[riscv_isa_ext[i].id];
656-
}
704+
riscv_isa_set_ext(ext, source_isa);
657705
}
658706

707+
riscv_resolve_isa(source_isa, isainfo->isa, &this_hwcap, isa2hwcap);
708+
659709
of_node_put(cpu_node);
660710

661711
/*

0 commit comments

Comments
 (0)