Message ID | 20240426-dev-charlie-support_thead_vector_6_9-v4-5-b692f3c516ec@rivosinc.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | riscv: Support vendor extensions and xtheadvector | expand |
On Fri, Apr 26, 2024 at 02:29:19PM -0700, Charlie Jenkins wrote: > Separate vendor extensions out into one struct per vendor > instead of adding vendor extensions onto riscv_isa_ext. > > Add a hidden config RISCV_ISA_VENDOR_EXT to conditionally include this > code. > > The xtheadvector vendor extension is added using these changes. This mostly looks good to me, thanks for the updates. There's one thing that I think is wrong, but I need to test and will get back to you on... > Signed-off-by: Charlie Jenkins <charlie@rivosinc.com> > --- > arch/riscv/Kconfig | 2 + > arch/riscv/Kconfig.vendor | 19 ++++++ > arch/riscv/include/asm/cpufeature.h | 18 ++++++ > arch/riscv/include/asm/vendor_extensions.h | 26 ++++++++ > arch/riscv/include/asm/vendor_extensions/thead.h | 19 ++++++ > arch/riscv/kernel/Makefile | 2 + > arch/riscv/kernel/cpufeature.c | 77 ++++++++++++++++++------ > arch/riscv/kernel/vendor_extensions.c | 18 ++++++ > arch/riscv/kernel/vendor_extensions/Makefile | 3 + > arch/riscv/kernel/vendor_extensions/thead.c | 36 +++++++++++ > 10 files changed, 200 insertions(+), 20 deletions(-) > > diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig > index be09c8836d56..fec86fba3acd 100644 > --- a/arch/riscv/Kconfig > +++ b/arch/riscv/Kconfig > @@ -759,6 +759,8 @@ config RISCV_EFFICIENT_UNALIGNED_ACCESS > > endchoice > > +source "arch/riscv/Kconfig.vendor" > + > endmenu # "Platform type" > > menu "Kernel features" > diff --git a/arch/riscv/Kconfig.vendor b/arch/riscv/Kconfig.vendor > new file mode 100644 > index 000000000000..4fc86810af1d > --- /dev/null > +++ b/arch/riscv/Kconfig.vendor > @@ -0,0 +1,19 @@ > +menu "Vendor extensions" > + > +config RISCV_ISA_VENDOR_EXT > + bool > + > +menu "T-Head" > +config RISCV_ISA_VENDOR_EXT_THEAD > + bool "T-Head vendor extension support" > + select RISCV_ISA_VENDOR_EXT > + default y > + help > + Say N here if you want to disable all T-Head vendor extension > + support. This will cause any T-Head vendor extensions that are > + requested to be ignored. What does "requested to be ignored" mean to a punter configuring a kernel? I'd expect this to be something like: "Say N here to disable detection of and support for all T-Head vendor extensions. Without this option enabled, T-Head vendor extensions will not be detected at boot and their presence not reported to userspace." In general, I'd expect something that needs some support in the kernel (like vector) to function to have a dedicated option, but the likes of their Zba variant could be detected and reported via hwprobe et al once RISCV_ISA_VENDOR_EXT_THEAD is enabled. Cheers, Conor.
On Fri, Apr 26, 2024 at 02:29:19PM -0700, Charlie Jenkins wrote: > @@ -353,6 +336,10 @@ static void __init riscv_parse_isa_string(unsigned long *this_hwcap, struct risc > bool ext_long = false, ext_err = false; > > switch (*ext) { > + case 'x': > + case 'X': > + pr_warn_once("Vendor extensions are ignored in riscv,isa. Use riscv,isa-extensions instead."); > + continue; Yeah, so this is not right - you need to find the end of the extension before containing - for example if I had a system with rv64imafdcxconorkwe, you get something like: [ 0.000000] CPU with hartid=0 is not available [ 0.000000] Falling back to deprecated "riscv,isa" [ 0.000000] Vendor extensions are ignored in riscv,isa. Use riscv,isa-extensions instead. [ 0.000000] riscv: base ISA extensions acdefikmnorw [ 0.000000] riscv: ELF capabilities acdfim kwe are all pretty safe letters, but suppose one was a v.. I think you can just yoink the code from the s/z case: diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c index 20bc9ba6b7a2..4daedba7961f 100644 --- a/arch/riscv/kernel/cpufeature.c +++ b/arch/riscv/kernel/cpufeature.c @@ -338,8 +338,19 @@ static void __init riscv_parse_isa_string(unsigned long *this_hwcap, struct risc switch (*ext) { case 'x': case 'X': - pr_warn_once("Vendor extensions are ignored in riscv,isa. Use riscv,isa-extensions instead."); - continue; + if (acpi_disabled) + pr_warn_once("Vendor extensions are ignored in riscv,isa. Use riscv,isa-extensions instead."); + /* + * To skip an extension, we find its end. + * As multi-letter extensions must be split from other multi-letter + * extensions with an "_", the end of a multi-letter extension will + * either be the null character or the "_" at the start of the next + * multi-letter extension. + */ + for (; *isa && *isa != '_'; ++isa) + ; + ext_err = true; + break; case 's': /* * Workaround for invalid single-letter 's' & 'u' (QEMU). You'll note that I made the dt property error dt only, this function gets called for ACPI too. I think the skip is pretty safe there though at the moment, we've not established any meanings yet for vendor stuff on ACPI. I think breaking is probably better than using continue - we get the _ skip from outside the switch statement out of that. And ye, I am lazy so I kept it as a for loop. Cheers, Conor.
On Fri, Apr 26, 2024 at 02:29:19PM -0700, Charlie Jenkins wrote: > Separate vendor extensions out into one struct per vendor > instead of adding vendor extensions onto riscv_isa_ext. > > Add a hidden config RISCV_ISA_VENDOR_EXT to conditionally include this > code. > > The xtheadvector vendor extension is added using these changes. > > Signed-off-by: Charlie Jenkins <charlie@rivosinc.com> > --- > arch/riscv/Kconfig | 2 + > arch/riscv/Kconfig.vendor | 19 ++++++ > arch/riscv/include/asm/cpufeature.h | 18 ++++++ > arch/riscv/include/asm/vendor_extensions.h | 26 ++++++++ > arch/riscv/include/asm/vendor_extensions/thead.h | 19 ++++++ > arch/riscv/kernel/Makefile | 2 + > arch/riscv/kernel/cpufeature.c | 77 ++++++++++++++++++------ > arch/riscv/kernel/vendor_extensions.c | 18 ++++++ > arch/riscv/kernel/vendor_extensions/Makefile | 3 + > arch/riscv/kernel/vendor_extensions/thead.c | 36 +++++++++++ I see no modifications to cpu.c here, is it intentional that vendor stuff isn't gonna show up in /proc/cpuinfo?
On Fri, Apr 26, 2024 at 2:29 PM Charlie Jenkins <charlie@rivosinc.com> wrote: > > Separate vendor extensions out into one struct per vendor > instead of adding vendor extensions onto riscv_isa_ext. > > Add a hidden config RISCV_ISA_VENDOR_EXT to conditionally include this > code. > > The xtheadvector vendor extension is added using these changes. > > Signed-off-by: Charlie Jenkins <charlie@rivosinc.com> > --- > arch/riscv/Kconfig | 2 + > arch/riscv/Kconfig.vendor | 19 ++++++ > arch/riscv/include/asm/cpufeature.h | 18 ++++++ > arch/riscv/include/asm/vendor_extensions.h | 26 ++++++++ > arch/riscv/include/asm/vendor_extensions/thead.h | 19 ++++++ > arch/riscv/kernel/Makefile | 2 + > arch/riscv/kernel/cpufeature.c | 77 ++++++++++++++++++------ > arch/riscv/kernel/vendor_extensions.c | 18 ++++++ > arch/riscv/kernel/vendor_extensions/Makefile | 3 + > arch/riscv/kernel/vendor_extensions/thead.c | 36 +++++++++++ > 10 files changed, 200 insertions(+), 20 deletions(-) > > diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig > index be09c8836d56..fec86fba3acd 100644 > --- a/arch/riscv/Kconfig > +++ b/arch/riscv/Kconfig > @@ -759,6 +759,8 @@ config RISCV_EFFICIENT_UNALIGNED_ACCESS > > endchoice > > +source "arch/riscv/Kconfig.vendor" > + > endmenu # "Platform type" > > menu "Kernel features" > diff --git a/arch/riscv/Kconfig.vendor b/arch/riscv/Kconfig.vendor > new file mode 100644 > index 000000000000..4fc86810af1d > --- /dev/null > +++ b/arch/riscv/Kconfig.vendor > @@ -0,0 +1,19 @@ > +menu "Vendor extensions" > + > +config RISCV_ISA_VENDOR_EXT > + bool > + > +menu "T-Head" > +config RISCV_ISA_VENDOR_EXT_THEAD > + bool "T-Head vendor extension support" > + select RISCV_ISA_VENDOR_EXT > + default y > + help > + Say N here if you want to disable all T-Head vendor extension > + support. This will cause any T-Head vendor extensions that are > + requested to be ignored. > + > + If you don't know what to do here, say Y. > +endmenu > + > +endmenu > diff --git a/arch/riscv/include/asm/cpufeature.h b/arch/riscv/include/asm/cpufeature.h > index 0c4f08577015..fedd479ccfd1 100644 > --- a/arch/riscv/include/asm/cpufeature.h > +++ b/arch/riscv/include/asm/cpufeature.h > @@ -35,6 +35,24 @@ extern u32 riscv_vlenb_of; > > void riscv_user_isa_enable(void); > > +#define _RISCV_ISA_EXT_DATA(_name, _id, _subset_exts, _subset_exts_size) { \ > + .name = #_name, \ > + .property = #_name, \ > + .id = _id, \ > + .subset_ext_ids = _subset_exts, \ > + .subset_ext_size = _subset_exts_size \ > +} > + > +#define __RISCV_ISA_EXT_DATA(_name, _id) _RISCV_ISA_EXT_DATA(_name, _id, NULL, 0) > + > +/* Used to declare pure "lasso" extension (Zk for instance) */ > +#define __RISCV_ISA_EXT_BUNDLE(_name, _bundled_exts) \ > + _RISCV_ISA_EXT_DATA(_name, RISCV_ISA_EXT_INVALID, _bundled_exts, ARRAY_SIZE(_bundled_exts)) > + > +/* Used to declare extensions that are a superset of other extensions (Zvbb for instance) */ > +#define __RISCV_ISA_EXT_SUPERSET(_name, _id, _sub_exts) \ > + _RISCV_ISA_EXT_DATA(_name, _id, _sub_exts, ARRAY_SIZE(_sub_exts)) > + > #if defined(CONFIG_RISCV_MISALIGNED) > bool check_unaligned_access_emulated_all_cpus(void); > void unaligned_emulation_finish(void); > diff --git a/arch/riscv/include/asm/vendor_extensions.h b/arch/riscv/include/asm/vendor_extensions.h > new file mode 100644 > index 000000000000..0af1ddd0af70 > --- /dev/null > +++ b/arch/riscv/include/asm/vendor_extensions.h > @@ -0,0 +1,26 @@ > +/* SPDX-License-Identifier: GPL-2.0-only */ > +/* > + * Copyright 2024 Rivos, Inc > + */ > + > +#ifndef _ASM_VENDOR_EXTENSIONS_H > +#define _ASM_VENDOR_EXTENSIONS_H > + > +#include <asm/cpufeature.h> > + > +#include <linux/array_size.h> > +#include <linux/types.h> > + > +struct riscv_isa_vendor_ext_data_list { > + const struct riscv_isa_ext_data *ext_data; > + struct riscv_isainfo *per_hart_vendor_bitmap; > + unsigned long *vendor_bitmap; It took a lot of digging for me to understand this was the set of vendor extensions supported on all harts. Can we add that to the name, maybe something like isa_bitmap_all_harts? (I wonder if we could drop the vendor part of the name since we already know we're in a vendor_ext_data_list structure). > + const size_t ext_data_count; > + const size_t bitmap_size; > +}; > + > +extern const struct riscv_isa_vendor_ext_data_list *riscv_isa_vendor_ext_list[]; > + > +extern const size_t riscv_isa_vendor_ext_list_size; > + > +#endif /* _ASM_VENDOR_EXTENSIONS_H */ > diff --git a/arch/riscv/include/asm/vendor_extensions/thead.h b/arch/riscv/include/asm/vendor_extensions/thead.h > new file mode 100644 > index 000000000000..92eec729888d > --- /dev/null > +++ b/arch/riscv/include/asm/vendor_extensions/thead.h > @@ -0,0 +1,19 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +#ifndef _ASM_RISCV_VENDOR_EXTENSIONS_THEAD_H > +#define _ASM_RISCV_VENDOR_EXTENSIONS_THEAD_H > + > +#include <asm/vendor_extensions.h> > + > +#include <linux/types.h> > + > +#define RISCV_ISA_VENDOR_EXT_XTHEADVECTOR 0 > + > +/* > + * Extension keys should be strictly less than max. > + * It is safe to increment this when necessary. > + */ > +#define RISCV_ISA_VENDOR_EXT_MAX_THEAD 32 > + > +extern const struct riscv_isa_vendor_ext_data_list riscv_isa_vendor_ext_list_thead; > + > +#endif > diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile > index 81d94a8ee10f..53361c50fb46 100644 > --- a/arch/riscv/kernel/Makefile > +++ b/arch/riscv/kernel/Makefile > @@ -58,6 +58,8 @@ obj-y += riscv_ksyms.o > obj-y += stacktrace.o > obj-y += cacheinfo.o > obj-y += patch.o > +obj-y += vendor_extensions.o > +obj-y += vendor_extensions/ > obj-y += probes/ > obj-y += tests/ > obj-$(CONFIG_MMU) += vdso.o vdso/ > diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c > index 8158f34c3e36..c073494519eb 100644 > --- a/arch/riscv/kernel/cpufeature.c > +++ b/arch/riscv/kernel/cpufeature.c > @@ -24,6 +24,7 @@ > #include <asm/processor.h> > #include <asm/sbi.h> > #include <asm/vector.h> > +#include <asm/vendor_extensions.h> > > #define NUM_ALPHA_EXTS ('z' - 'a' + 1) > > @@ -102,24 +103,6 @@ static bool riscv_isa_extension_check(int id) > return true; > } > > -#define _RISCV_ISA_EXT_DATA(_name, _id, _subset_exts, _subset_exts_size) { \ > - .name = #_name, \ > - .property = #_name, \ > - .id = _id, \ > - .subset_ext_ids = _subset_exts, \ > - .subset_ext_size = _subset_exts_size \ > -} > - > -#define __RISCV_ISA_EXT_DATA(_name, _id) _RISCV_ISA_EXT_DATA(_name, _id, NULL, 0) > - > -/* Used to declare pure "lasso" extension (Zk for instance) */ > -#define __RISCV_ISA_EXT_BUNDLE(_name, _bundled_exts) \ > - _RISCV_ISA_EXT_DATA(_name, RISCV_ISA_EXT_INVALID, _bundled_exts, ARRAY_SIZE(_bundled_exts)) > - > -/* Used to declare extensions that are a superset of other extensions (Zvbb for instance) */ > -#define __RISCV_ISA_EXT_SUPERSET(_name, _id, _sub_exts) \ > - _RISCV_ISA_EXT_DATA(_name, _id, _sub_exts, ARRAY_SIZE(_sub_exts)) > - > static const unsigned int riscv_zk_bundled_exts[] = { > RISCV_ISA_EXT_ZBKB, > RISCV_ISA_EXT_ZBKC, > @@ -353,6 +336,10 @@ static void __init riscv_parse_isa_string(unsigned long *this_hwcap, struct risc > bool ext_long = false, ext_err = false; > > switch (*ext) { > + case 'x': > + case 'X': > + pr_warn_once("Vendor extensions are ignored in riscv,isa. Use riscv,isa-extensions instead."); > + continue; > case 's': > /* > * Workaround for invalid single-letter 's' & 'u' (QEMU). > @@ -368,8 +355,6 @@ static void __init riscv_parse_isa_string(unsigned long *this_hwcap, struct risc > } > fallthrough; > case 'S': > - case 'x': > - case 'X': > case 'z': > case 'Z': > /* > @@ -572,6 +557,54 @@ static void __init riscv_fill_hwcap_from_isa_string(unsigned long *isa2hwcap) > acpi_put_table((struct acpi_table_header *)rhct); > } > > +static void __init riscv_fill_cpu_vendor_ext(struct device_node *cpu_node, int cpu) > +{ > + if (!IS_ENABLED(CONFIG_RISCV_ISA_VENDOR_EXT)) > + return; > + > + for (int i = 0; i < riscv_isa_vendor_ext_list_size; i++) { > + const struct riscv_isa_vendor_ext_data_list *ext_list = riscv_isa_vendor_ext_list[i]; > + > + for (int j = 0; j < ext_list->ext_data_count; j++) { > + const struct riscv_isa_ext_data ext = ext_list->ext_data[j]; > + struct riscv_isainfo *isavendorinfo = &ext_list->per_hart_vendor_bitmap[cpu]; > + > + if (of_property_match_string(cpu_node, "riscv,isa-extensions", > + ext.property) < 0) > + continue; > + > + /* > + * Assume that subset extensions are all members of the > + * same vendor. > + */ > + if (ext.subset_ext_size) > + for (int k = 0; k < ext.subset_ext_size; k++) > + set_bit(ext.subset_ext_ids[k], isavendorinfo->isa); > + > + set_bit(ext.id, isavendorinfo->isa); > + } This loop seems super similar to the regular one (in riscv_fill_hwcap_from_ext_list() in the random, possibly old, kernel I have open). Could we refactor these together into a common helper? The other loop has an extra stanza for riscv_isa_extension_check(), so we'd have to add an extra condition there, but otherwise it looks pretty compatible? > + } > +} > + > +static void __init riscv_fill_vendor_ext_list(int cpu) > +{ > + if (!IS_ENABLED(CONFIG_RISCV_ISA_VENDOR_EXT)) > + return; > + > + for (int i = 0; i < riscv_isa_vendor_ext_list_size; i++) { > + const struct riscv_isa_vendor_ext_data_list *ext_list = riscv_isa_vendor_ext_list[i]; > + > + if (bitmap_empty(ext_list->vendor_bitmap, ext_list->bitmap_size)) > + bitmap_copy(ext_list->vendor_bitmap, > + ext_list->per_hart_vendor_bitmap[cpu].isa, > + ext_list->bitmap_size); Could you get into trouble here if the set of vendor extensions reduces to zero, and then becomes non-zero? To illustrate, consider these masks: cpu 0: 0x0000C000 cpu 1: 0x00000003 <<< vendor_bitmap ANDs out to 0 cpu 2: 0x00000010 <<< oops, we end up copying this into vendor_bitmap > + else > + bitmap_and(ext_list->vendor_bitmap, ext_list->vendor_bitmap, > + ext_list->per_hart_vendor_bitmap[cpu].isa, > + ext_list->bitmap_size); > + } > +} > + > static int __init riscv_fill_hwcap_from_ext_list(unsigned long *isa2hwcap) > { > unsigned int cpu; > @@ -615,6 +648,8 @@ static int __init riscv_fill_hwcap_from_ext_list(unsigned long *isa2hwcap) > } > } > > + riscv_fill_cpu_vendor_ext(cpu_node, cpu); > + > of_node_put(cpu_node); > > /* > @@ -630,6 +665,8 @@ static int __init riscv_fill_hwcap_from_ext_list(unsigned long *isa2hwcap) > bitmap_copy(riscv_isa, isainfo->isa, RISCV_ISA_EXT_MAX); > else > bitmap_and(riscv_isa, riscv_isa, isainfo->isa, RISCV_ISA_EXT_MAX); > + > + riscv_fill_vendor_ext_list(cpu); > } > > if (bitmap_empty(riscv_isa, RISCV_ISA_EXT_MAX)) > diff --git a/arch/riscv/kernel/vendor_extensions.c b/arch/riscv/kernel/vendor_extensions.c > new file mode 100644 > index 000000000000..f76cb3013c2d > --- /dev/null > +++ b/arch/riscv/kernel/vendor_extensions.c > @@ -0,0 +1,18 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Copyright 2024 Rivos, Inc > + */ > + > +#include <asm/vendor_extensions.h> > +#include <asm/vendor_extensions/thead.h> > + > +#include <linux/array_size.h> > +#include <linux/types.h> > + > +const struct riscv_isa_vendor_ext_data_list *riscv_isa_vendor_ext_list[] = { > +#ifdef CONFIG_RISCV_ISA_VENDOR_EXT_THEAD > + &riscv_isa_vendor_ext_list_thead, > +#endif > +}; > + > +const size_t riscv_isa_vendor_ext_list_size = ARRAY_SIZE(riscv_isa_vendor_ext_list); > diff --git a/arch/riscv/kernel/vendor_extensions/Makefile b/arch/riscv/kernel/vendor_extensions/Makefile > new file mode 100644 > index 000000000000..3383066baaab > --- /dev/null > +++ b/arch/riscv/kernel/vendor_extensions/Makefile > @@ -0,0 +1,3 @@ > +# SPDX-License-Identifier: GPL-2.0-only > + > +obj-$(CONFIG_RISCV_ISA_VENDOR_EXT_THEAD) += thead.o > diff --git a/arch/riscv/kernel/vendor_extensions/thead.c b/arch/riscv/kernel/vendor_extensions/thead.c > new file mode 100644 > index 000000000000..edb20b928c0c > --- /dev/null > +++ b/arch/riscv/kernel/vendor_extensions/thead.c > @@ -0,0 +1,36 @@ > +// SPDX-License-Identifier: GPL-2.0-only > + > +#include <asm/cpufeature.h> > +#include <asm/vendor_extensions.h> > +#include <asm/vendor_extensions/thead.h> > + > +#include <linux/array_size.h> > +#include <linux/types.h> > + > +/* All T-Head vendor extensions supported in Linux */ > +const struct riscv_isa_ext_data riscv_isa_vendor_ext_thead[] = { > + __RISCV_ISA_EXT_DATA(xtheadvector, RISCV_ISA_VENDOR_EXT_XTHEADVECTOR), > +}; > + > +/* > + * The first member of this struct must be a bitmap named isa so it can be > + * compatible with riscv_isainfo even though the sizes of the bitmaps may be > + * different. This is kinda yucky, as you're casting a bitmap of a different size into a struct riscv_isainfo *, which has a known size. I don't necessarily have a fabulous suggestion to fix though. The best I can come up with is refactor struct riscv_isainfo to be: struct riscv_isainfo { int count; unsigned long isa[0]; }; then declare a standard one (for hart_isa, which is statically allocated): struct riscv_std_isainfo { int count; DECLARE_BITMAP(isa, RISCV_ISA_EXT_MAX); } and a thead one struct riscv_thead_isainfo { int count; DECLARE_BITMAP(isa, RISCV_ISA_VENDOR_EXT_MAX_THEAD); } But there's still a cast in there, as you'd cast the specialized structs to struct riscv_isainfo *. But at least the size is in there to be enforced at runtime, rather than a compile-time check that's wrong. So I'll just leave this half baked thought here, and maybe you can think of a cleaner way, or ignore it :). > + */ > +struct riscv_isavendorinfo_thead { > + DECLARE_BITMAP(isa, RISCV_ISA_VENDOR_EXT_MAX_THEAD); > +}; > + > +/* Hart specific T-Head vendor extension support */ > +static struct riscv_isavendorinfo_thead hart_vendorinfo_thead[NR_CPUS]; > + > +/* Set of T-Head vendor extensions supported on all harts */ > +DECLARE_BITMAP(vendorinfo_thead, RISCV_ISA_VENDOR_EXT_MAX_THEAD); > + > +const struct riscv_isa_vendor_ext_data_list riscv_isa_vendor_ext_list_thead = { > + .ext_data = riscv_isa_vendor_ext_thead, > + .per_hart_vendor_bitmap = (struct riscv_isainfo *)hart_vendorinfo_thead, > + .vendor_bitmap = vendorinfo_thead, > + .ext_data_count = ARRAY_SIZE(riscv_isa_vendor_ext_thead), > + .bitmap_size = RISCV_ISA_VENDOR_EXT_MAX_THEAD > +}; > > -- > 2.44.0 >
On Wed, May 01, 2024 at 11:46:07AM +0100, Conor Dooley wrote: > On Fri, Apr 26, 2024 at 02:29:19PM -0700, Charlie Jenkins wrote: > > Separate vendor extensions out into one struct per vendor > > instead of adding vendor extensions onto riscv_isa_ext. > > > > Add a hidden config RISCV_ISA_VENDOR_EXT to conditionally include this > > code. > > > > The xtheadvector vendor extension is added using these changes. > > This mostly looks good to me, thanks for the updates. There's one thing > that I think is wrong, but I need to test and will get back to you on... > > > Signed-off-by: Charlie Jenkins <charlie@rivosinc.com> > > --- > > arch/riscv/Kconfig | 2 + > > arch/riscv/Kconfig.vendor | 19 ++++++ > > arch/riscv/include/asm/cpufeature.h | 18 ++++++ > > arch/riscv/include/asm/vendor_extensions.h | 26 ++++++++ > > arch/riscv/include/asm/vendor_extensions/thead.h | 19 ++++++ > > arch/riscv/kernel/Makefile | 2 + > > arch/riscv/kernel/cpufeature.c | 77 ++++++++++++++++++------ > > arch/riscv/kernel/vendor_extensions.c | 18 ++++++ > > arch/riscv/kernel/vendor_extensions/Makefile | 3 + > > arch/riscv/kernel/vendor_extensions/thead.c | 36 +++++++++++ > > 10 files changed, 200 insertions(+), 20 deletions(-) > > > > diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig > > index be09c8836d56..fec86fba3acd 100644 > > --- a/arch/riscv/Kconfig > > +++ b/arch/riscv/Kconfig > > @@ -759,6 +759,8 @@ config RISCV_EFFICIENT_UNALIGNED_ACCESS > > > > endchoice > > > > +source "arch/riscv/Kconfig.vendor" > > + > > endmenu # "Platform type" > > > > menu "Kernel features" > > diff --git a/arch/riscv/Kconfig.vendor b/arch/riscv/Kconfig.vendor > > new file mode 100644 > > index 000000000000..4fc86810af1d > > --- /dev/null > > +++ b/arch/riscv/Kconfig.vendor > > @@ -0,0 +1,19 @@ > > +menu "Vendor extensions" > > + > > +config RISCV_ISA_VENDOR_EXT > > + bool > > + > > +menu "T-Head" > > +config RISCV_ISA_VENDOR_EXT_THEAD > > + bool "T-Head vendor extension support" > > + select RISCV_ISA_VENDOR_EXT > > + default y > > + help > > + Say N here if you want to disable all T-Head vendor extension > > + support. This will cause any T-Head vendor extensions that are > > + requested to be ignored. > > What does "requested to be ignored" mean to a punter configuring a > kernel? I'd expect this to be something like: > > "Say N here to disable detection of and support for all T-Head vendor > extensions. Without this option enabled, T-Head vendor extensions will > not be detected at boot and their presence not reported to userspace." Sounds great I will change to that. - Charlie > > In general, I'd expect something that needs some support in the kernel > (like vector) to function to have a dedicated option, but the likes of > their Zba variant could be detected and reported via hwprobe et al > once RISCV_ISA_VENDOR_EXT_THEAD is enabled. > > Cheers, > Conor.
On Wed, May 01, 2024 at 12:19:34PM +0100, Conor Dooley wrote: > On Fri, Apr 26, 2024 at 02:29:19PM -0700, Charlie Jenkins wrote: > > @@ -353,6 +336,10 @@ static void __init riscv_parse_isa_string(unsigned long *this_hwcap, struct risc > > bool ext_long = false, ext_err = false; > > > > switch (*ext) { > > + case 'x': > > + case 'X': > > + pr_warn_once("Vendor extensions are ignored in riscv,isa. Use riscv,isa-extensions instead."); > > + continue; > > Yeah, so this is not right - you need to find the end of the extension > before containing - for example if I had a system with > rv64imafdcxconorkwe, you get something like: > [ 0.000000] CPU with hartid=0 is not available > [ 0.000000] Falling back to deprecated "riscv,isa" > [ 0.000000] Vendor extensions are ignored in riscv,isa. Use riscv,isa-extensions instead. > [ 0.000000] riscv: base ISA extensions acdefikmnorw > [ 0.000000] riscv: ELF capabilities acdfim > > kwe are all pretty safe letters, but suppose one was a v.. > I think you can just yoink the code from the s/z case: Oh right, I forgot about that. > > diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c > index 20bc9ba6b7a2..4daedba7961f 100644 > --- a/arch/riscv/kernel/cpufeature.c > +++ b/arch/riscv/kernel/cpufeature.c > @@ -338,8 +338,19 @@ static void __init riscv_parse_isa_string(unsigned long *this_hwcap, struct risc > switch (*ext) { > case 'x': > case 'X': > - pr_warn_once("Vendor extensions are ignored in riscv,isa. Use riscv,isa-extensions instead."); > - continue; > + if (acpi_disabled) > + pr_warn_once("Vendor extensions are ignored in riscv,isa. Use riscv,isa-extensions instead."); > + /* > + * To skip an extension, we find its end. > + * As multi-letter extensions must be split from other multi-letter > + * extensions with an "_", the end of a multi-letter extension will > + * either be the null character or the "_" at the start of the next > + * multi-letter extension. > + */ > + for (; *isa && *isa != '_'; ++isa) > + ; > + ext_err = true; > + break; > case 's': > /* > * Workaround for invalid single-letter 's' & 'u' (QEMU). > > You'll note that I made the dt property error dt only, this function > gets called for ACPI too. I think the skip is pretty safe there though > at the moment, we've not established any meanings yet for vendor stuff > on ACPI. > I think breaking is probably better than using continue - we get the _ > skip from outside the switch statement out of that. And ye, I am lazy > so I kept it as a for loop. Awesome, thanks! - Charlie > > Cheers, > Conor.
On Wed, May 01, 2024 at 12:40:38PM +0100, Conor Dooley wrote: > On Fri, Apr 26, 2024 at 02:29:19PM -0700, Charlie Jenkins wrote: > > Separate vendor extensions out into one struct per vendor > > instead of adding vendor extensions onto riscv_isa_ext. > > > > Add a hidden config RISCV_ISA_VENDOR_EXT to conditionally include this > > code. > > > > The xtheadvector vendor extension is added using these changes. > > > > Signed-off-by: Charlie Jenkins <charlie@rivosinc.com> > > --- > > arch/riscv/Kconfig | 2 + > > arch/riscv/Kconfig.vendor | 19 ++++++ > > arch/riscv/include/asm/cpufeature.h | 18 ++++++ > > arch/riscv/include/asm/vendor_extensions.h | 26 ++++++++ > > arch/riscv/include/asm/vendor_extensions/thead.h | 19 ++++++ > > arch/riscv/kernel/Makefile | 2 + > > arch/riscv/kernel/cpufeature.c | 77 ++++++++++++++++++------ > > arch/riscv/kernel/vendor_extensions.c | 18 ++++++ > > arch/riscv/kernel/vendor_extensions/Makefile | 3 + > > arch/riscv/kernel/vendor_extensions/thead.c | 36 +++++++++++ > > I see no modifications to cpu.c here, is it intentional that vendor > stuff isn't gonna show up in /proc/cpuinfo? I wasn't sure if that's something we were wanting to support since hwprobe is the prefered api, but I can add that if it is desired. - Charlie
On Wed, May 01, 2024 at 10:10:57AM -0700, Charlie Jenkins wrote: > On Wed, May 01, 2024 at 12:40:38PM +0100, Conor Dooley wrote: > > On Fri, Apr 26, 2024 at 02:29:19PM -0700, Charlie Jenkins wrote: > > > Separate vendor extensions out into one struct per vendor > > > instead of adding vendor extensions onto riscv_isa_ext. > > > > > > Add a hidden config RISCV_ISA_VENDOR_EXT to conditionally include this > > > code. > > > > > > The xtheadvector vendor extension is added using these changes. > > > > > > Signed-off-by: Charlie Jenkins <charlie@rivosinc.com> > > > --- > > > arch/riscv/Kconfig | 2 + > > > arch/riscv/Kconfig.vendor | 19 ++++++ > > > arch/riscv/include/asm/cpufeature.h | 18 ++++++ > > > arch/riscv/include/asm/vendor_extensions.h | 26 ++++++++ > > > arch/riscv/include/asm/vendor_extensions/thead.h | 19 ++++++ > > > arch/riscv/kernel/Makefile | 2 + > > > arch/riscv/kernel/cpufeature.c | 77 ++++++++++++++++++------ > > > arch/riscv/kernel/vendor_extensions.c | 18 ++++++ > > > arch/riscv/kernel/vendor_extensions/Makefile | 3 + > > > arch/riscv/kernel/vendor_extensions/thead.c | 36 +++++++++++ > > > > I see no modifications to cpu.c here, is it intentional that vendor > > stuff isn't gonna show up in /proc/cpuinfo? > > I wasn't sure if that's something we were wanting to support since > hwprobe is the prefered api, but I can add that if it is desired. Desired API for programmatic consumption, sure, but for human users I think it's good to have the info there.
On Wed, May 01, 2024 at 09:44:15AM -0700, Evan Green wrote: > On Fri, Apr 26, 2024 at 2:29 PM Charlie Jenkins <charlie@rivosinc.com> wrote: > > +struct riscv_isa_vendor_ext_data_list { > > + const struct riscv_isa_ext_data *ext_data; > > + struct riscv_isainfo *per_hart_vendor_bitmap; > > + unsigned long *vendor_bitmap; > > It took a lot of digging for me to understand this was the set of > vendor extensions supported on all harts. Can we add that to the name, > maybe something like isa_bitmap_all_harts? (I wonder if we could drop > the vendor part of the name since we already know we're in a > vendor_ext_data_list structure). Reading this made me wonder, why is the all-hart bitmap an unsigned long when the per hart one is a riscv_isainfo struct?
On Wed, May 01, 2024 at 09:44:15AM -0700, Evan Green wrote: > On Fri, Apr 26, 2024 at 2:29 PM Charlie Jenkins <charlie@rivosinc.com> wrote: > > > > Separate vendor extensions out into one struct per vendor > > instead of adding vendor extensions onto riscv_isa_ext. > > > > Add a hidden config RISCV_ISA_VENDOR_EXT to conditionally include this > > code. > > > > The xtheadvector vendor extension is added using these changes. > > > > Signed-off-by: Charlie Jenkins <charlie@rivosinc.com> > > --- > > arch/riscv/Kconfig | 2 + > > arch/riscv/Kconfig.vendor | 19 ++++++ > > arch/riscv/include/asm/cpufeature.h | 18 ++++++ > > arch/riscv/include/asm/vendor_extensions.h | 26 ++++++++ > > arch/riscv/include/asm/vendor_extensions/thead.h | 19 ++++++ > > arch/riscv/kernel/Makefile | 2 + > > arch/riscv/kernel/cpufeature.c | 77 ++++++++++++++++++------ > > arch/riscv/kernel/vendor_extensions.c | 18 ++++++ > > arch/riscv/kernel/vendor_extensions/Makefile | 3 + > > arch/riscv/kernel/vendor_extensions/thead.c | 36 +++++++++++ > > 10 files changed, 200 insertions(+), 20 deletions(-) > > > > diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig > > index be09c8836d56..fec86fba3acd 100644 > > --- a/arch/riscv/Kconfig > > +++ b/arch/riscv/Kconfig > > @@ -759,6 +759,8 @@ config RISCV_EFFICIENT_UNALIGNED_ACCESS > > > > endchoice > > > > +source "arch/riscv/Kconfig.vendor" > > + > > endmenu # "Platform type" > > > > menu "Kernel features" > > diff --git a/arch/riscv/Kconfig.vendor b/arch/riscv/Kconfig.vendor > > new file mode 100644 > > index 000000000000..4fc86810af1d > > --- /dev/null > > +++ b/arch/riscv/Kconfig.vendor > > @@ -0,0 +1,19 @@ > > +menu "Vendor extensions" > > + > > +config RISCV_ISA_VENDOR_EXT > > + bool > > + > > +menu "T-Head" > > +config RISCV_ISA_VENDOR_EXT_THEAD > > + bool "T-Head vendor extension support" > > + select RISCV_ISA_VENDOR_EXT > > + default y > > + help > > + Say N here if you want to disable all T-Head vendor extension > > + support. This will cause any T-Head vendor extensions that are > > + requested to be ignored. > > + > > + If you don't know what to do here, say Y. > > +endmenu > > + > > +endmenu > > diff --git a/arch/riscv/include/asm/cpufeature.h b/arch/riscv/include/asm/cpufeature.h > > index 0c4f08577015..fedd479ccfd1 100644 > > --- a/arch/riscv/include/asm/cpufeature.h > > +++ b/arch/riscv/include/asm/cpufeature.h > > @@ -35,6 +35,24 @@ extern u32 riscv_vlenb_of; > > > > void riscv_user_isa_enable(void); > > > > +#define _RISCV_ISA_EXT_DATA(_name, _id, _subset_exts, _subset_exts_size) { \ > > + .name = #_name, \ > > + .property = #_name, \ > > + .id = _id, \ > > + .subset_ext_ids = _subset_exts, \ > > + .subset_ext_size = _subset_exts_size \ > > +} > > + > > +#define __RISCV_ISA_EXT_DATA(_name, _id) _RISCV_ISA_EXT_DATA(_name, _id, NULL, 0) > > + > > +/* Used to declare pure "lasso" extension (Zk for instance) */ > > +#define __RISCV_ISA_EXT_BUNDLE(_name, _bundled_exts) \ > > + _RISCV_ISA_EXT_DATA(_name, RISCV_ISA_EXT_INVALID, _bundled_exts, ARRAY_SIZE(_bundled_exts)) > > + > > +/* Used to declare extensions that are a superset of other extensions (Zvbb for instance) */ > > +#define __RISCV_ISA_EXT_SUPERSET(_name, _id, _sub_exts) \ > > + _RISCV_ISA_EXT_DATA(_name, _id, _sub_exts, ARRAY_SIZE(_sub_exts)) > > + > > #if defined(CONFIG_RISCV_MISALIGNED) > > bool check_unaligned_access_emulated_all_cpus(void); > > void unaligned_emulation_finish(void); > > diff --git a/arch/riscv/include/asm/vendor_extensions.h b/arch/riscv/include/asm/vendor_extensions.h > > new file mode 100644 > > index 000000000000..0af1ddd0af70 > > --- /dev/null > > +++ b/arch/riscv/include/asm/vendor_extensions.h > > @@ -0,0 +1,26 @@ > > +/* SPDX-License-Identifier: GPL-2.0-only */ > > +/* > > + * Copyright 2024 Rivos, Inc > > + */ > > + > > +#ifndef _ASM_VENDOR_EXTENSIONS_H > > +#define _ASM_VENDOR_EXTENSIONS_H > > + > > +#include <asm/cpufeature.h> > > + > > +#include <linux/array_size.h> > > +#include <linux/types.h> > > + > > +struct riscv_isa_vendor_ext_data_list { > > + const struct riscv_isa_ext_data *ext_data; > > + struct riscv_isainfo *per_hart_vendor_bitmap; > > + unsigned long *vendor_bitmap; > > It took a lot of digging for me to understand this was the set of > vendor extensions supported on all harts. Can we add that to the name, > maybe something like isa_bitmap_all_harts? (I wonder if we could drop > the vendor part of the name since we already know we're in a > vendor_ext_data_list structure). Sure, I figured it was implied since the other bitmap says "per_hart", but I can see how it could be confusing. > > > + const size_t ext_data_count; > > + const size_t bitmap_size; > > +}; > > + > > +extern const struct riscv_isa_vendor_ext_data_list *riscv_isa_vendor_ext_list[]; > > + > > +extern const size_t riscv_isa_vendor_ext_list_size; > > + > > +#endif /* _ASM_VENDOR_EXTENSIONS_H */ > > diff --git a/arch/riscv/include/asm/vendor_extensions/thead.h b/arch/riscv/include/asm/vendor_extensions/thead.h > > new file mode 100644 > > index 000000000000..92eec729888d > > --- /dev/null > > +++ b/arch/riscv/include/asm/vendor_extensions/thead.h > > @@ -0,0 +1,19 @@ > > +/* SPDX-License-Identifier: GPL-2.0 */ > > +#ifndef _ASM_RISCV_VENDOR_EXTENSIONS_THEAD_H > > +#define _ASM_RISCV_VENDOR_EXTENSIONS_THEAD_H > > + > > +#include <asm/vendor_extensions.h> > > + > > +#include <linux/types.h> > > + > > +#define RISCV_ISA_VENDOR_EXT_XTHEADVECTOR 0 > > + > > +/* > > + * Extension keys should be strictly less than max. > > + * It is safe to increment this when necessary. > > + */ > > +#define RISCV_ISA_VENDOR_EXT_MAX_THEAD 32 > > + > > +extern const struct riscv_isa_vendor_ext_data_list riscv_isa_vendor_ext_list_thead; > > + > > +#endif > > diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile > > index 81d94a8ee10f..53361c50fb46 100644 > > --- a/arch/riscv/kernel/Makefile > > +++ b/arch/riscv/kernel/Makefile > > @@ -58,6 +58,8 @@ obj-y += riscv_ksyms.o > > obj-y += stacktrace.o > > obj-y += cacheinfo.o > > obj-y += patch.o > > +obj-y += vendor_extensions.o > > +obj-y += vendor_extensions/ > > obj-y += probes/ > > obj-y += tests/ > > obj-$(CONFIG_MMU) += vdso.o vdso/ > > diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c > > index 8158f34c3e36..c073494519eb 100644 > > --- a/arch/riscv/kernel/cpufeature.c > > +++ b/arch/riscv/kernel/cpufeature.c > > @@ -24,6 +24,7 @@ > > #include <asm/processor.h> > > #include <asm/sbi.h> > > #include <asm/vector.h> > > +#include <asm/vendor_extensions.h> > > > > #define NUM_ALPHA_EXTS ('z' - 'a' + 1) > > > > @@ -102,24 +103,6 @@ static bool riscv_isa_extension_check(int id) > > return true; > > } > > > > -#define _RISCV_ISA_EXT_DATA(_name, _id, _subset_exts, _subset_exts_size) { \ > > - .name = #_name, \ > > - .property = #_name, \ > > - .id = _id, \ > > - .subset_ext_ids = _subset_exts, \ > > - .subset_ext_size = _subset_exts_size \ > > -} > > - > > -#define __RISCV_ISA_EXT_DATA(_name, _id) _RISCV_ISA_EXT_DATA(_name, _id, NULL, 0) > > - > > -/* Used to declare pure "lasso" extension (Zk for instance) */ > > -#define __RISCV_ISA_EXT_BUNDLE(_name, _bundled_exts) \ > > - _RISCV_ISA_EXT_DATA(_name, RISCV_ISA_EXT_INVALID, _bundled_exts, ARRAY_SIZE(_bundled_exts)) > > - > > -/* Used to declare extensions that are a superset of other extensions (Zvbb for instance) */ > > -#define __RISCV_ISA_EXT_SUPERSET(_name, _id, _sub_exts) \ > > - _RISCV_ISA_EXT_DATA(_name, _id, _sub_exts, ARRAY_SIZE(_sub_exts)) > > - > > static const unsigned int riscv_zk_bundled_exts[] = { > > RISCV_ISA_EXT_ZBKB, > > RISCV_ISA_EXT_ZBKC, > > @@ -353,6 +336,10 @@ static void __init riscv_parse_isa_string(unsigned long *this_hwcap, struct risc > > bool ext_long = false, ext_err = false; > > > > switch (*ext) { > > + case 'x': > > + case 'X': > > + pr_warn_once("Vendor extensions are ignored in riscv,isa. Use riscv,isa-extensions instead."); > > + continue; > > case 's': > > /* > > * Workaround for invalid single-letter 's' & 'u' (QEMU). > > @@ -368,8 +355,6 @@ static void __init riscv_parse_isa_string(unsigned long *this_hwcap, struct risc > > } > > fallthrough; > > case 'S': > > - case 'x': > > - case 'X': > > case 'z': > > case 'Z': > > /* > > @@ -572,6 +557,54 @@ static void __init riscv_fill_hwcap_from_isa_string(unsigned long *isa2hwcap) > > acpi_put_table((struct acpi_table_header *)rhct); > > } > > > > +static void __init riscv_fill_cpu_vendor_ext(struct device_node *cpu_node, int cpu) > > +{ > > + if (!IS_ENABLED(CONFIG_RISCV_ISA_VENDOR_EXT)) > > + return; > > + > > + for (int i = 0; i < riscv_isa_vendor_ext_list_size; i++) { > > + const struct riscv_isa_vendor_ext_data_list *ext_list = riscv_isa_vendor_ext_list[i]; > > + > > + for (int j = 0; j < ext_list->ext_data_count; j++) { > > + const struct riscv_isa_ext_data ext = ext_list->ext_data[j]; > > + struct riscv_isainfo *isavendorinfo = &ext_list->per_hart_vendor_bitmap[cpu]; > > + > > + if (of_property_match_string(cpu_node, "riscv,isa-extensions", > > + ext.property) < 0) > > + continue; > > + > > + /* > > + * Assume that subset extensions are all members of the > > + * same vendor. > > + */ > > + if (ext.subset_ext_size) > > + for (int k = 0; k < ext.subset_ext_size; k++) > > + set_bit(ext.subset_ext_ids[k], isavendorinfo->isa); > > + > > + set_bit(ext.id, isavendorinfo->isa); > > + } > > This loop seems super similar to the regular one (in > riscv_fill_hwcap_from_ext_list() in the random, possibly old, kernel I > have open). Could we refactor these together into a common helper? The > other loop has an extra stanza for riscv_isa_extension_check(), so > we'd have to add an extra condition there, but otherwise it looks > pretty compatible? > I actually did have this refactored into a single function in a previous version but broke it back up since I felt there just wasn't enough overlap. The one for standard extensions is: for (int i = 0; i < riscv_isa_ext_count; i++) { const struct riscv_isa_ext_data *ext = &riscv_isa_ext[i]; if (of_property_match_string(cpu_node, "riscv,isa-extensions", ext->property) < 0) continue; if (ext->subset_ext_size) { for (int j = 0; j < ext->subset_ext_size; j++) { if (riscv_isa_extension_check(ext->subset_ext_ids[i])) set_bit(ext->subset_ext_ids[j], isainfo->isa); } } if (riscv_isa_extension_check(ext->id)) { set_bit(ext->id, isainfo->isa); /* Only single letter extensions get set in hwcap */ if (strnlen(riscv_isa_ext[i].name, 2) == 1) this_hwcap |= isa2hwcap[riscv_isa_ext[i].id]; } } The motivating reason why I didn't combine them was the additional `struct riscv_isa_vendor_ext_data_list *` data type for the vendor version which contains ext and isainfo. This can probably be combined in a straight-forward way though. > > + } > > +} > > + > > +static void __init riscv_fill_vendor_ext_list(int cpu) > > +{ > > + if (!IS_ENABLED(CONFIG_RISCV_ISA_VENDOR_EXT)) > > + return; > > + > > + for (int i = 0; i < riscv_isa_vendor_ext_list_size; i++) { > > + const struct riscv_isa_vendor_ext_data_list *ext_list = riscv_isa_vendor_ext_list[i]; > > + > > + if (bitmap_empty(ext_list->vendor_bitmap, ext_list->bitmap_size)) > > + bitmap_copy(ext_list->vendor_bitmap, > > + ext_list->per_hart_vendor_bitmap[cpu].isa, > > + ext_list->bitmap_size); > > Could you get into trouble here if the set of vendor extensions > reduces to zero, and then becomes non-zero? To illustrate, consider > these masks: > cpu 0: 0x0000C000 > cpu 1: 0x00000003 <<< vendor_bitmap ANDs out to 0 > cpu 2: 0x00000010 <<< oops, we end up copying this into vendor_bitmap > Huh that's a good point. The standard extensions have that same bug too? if (bitmap_empty(riscv_isa, RISCV_ISA_EXT_MAX)) bitmap_copy(riscv_isa, isainfo->isa, RISCV_ISA_EXT_MAX); else bitmap_and(riscv_isa, riscv_isa, isainfo->isa, RISCV_ISA_EXT_MAX); > > + else > > + bitmap_and(ext_list->vendor_bitmap, ext_list->vendor_bitmap, > > + ext_list->per_hart_vendor_bitmap[cpu].isa, > > + ext_list->bitmap_size); > > + } > > +} > > + > > static int __init riscv_fill_hwcap_from_ext_list(unsigned long *isa2hwcap) > > { > > unsigned int cpu; > > @@ -615,6 +648,8 @@ static int __init riscv_fill_hwcap_from_ext_list(unsigned long *isa2hwcap) > > } > > } > > > > + riscv_fill_cpu_vendor_ext(cpu_node, cpu); > > + > > of_node_put(cpu_node); > > > > /* > > @@ -630,6 +665,8 @@ static int __init riscv_fill_hwcap_from_ext_list(unsigned long *isa2hwcap) > > bitmap_copy(riscv_isa, isainfo->isa, RISCV_ISA_EXT_MAX); > > else > > bitmap_and(riscv_isa, riscv_isa, isainfo->isa, RISCV_ISA_EXT_MAX); > > + > > + riscv_fill_vendor_ext_list(cpu); > > } > > > > if (bitmap_empty(riscv_isa, RISCV_ISA_EXT_MAX)) > > diff --git a/arch/riscv/kernel/vendor_extensions.c b/arch/riscv/kernel/vendor_extensions.c > > new file mode 100644 > > index 000000000000..f76cb3013c2d > > --- /dev/null > > +++ b/arch/riscv/kernel/vendor_extensions.c > > @@ -0,0 +1,18 @@ > > +// SPDX-License-Identifier: GPL-2.0-only > > +/* > > + * Copyright 2024 Rivos, Inc > > + */ > > + > > +#include <asm/vendor_extensions.h> > > +#include <asm/vendor_extensions/thead.h> > > + > > +#include <linux/array_size.h> > > +#include <linux/types.h> > > + > > +const struct riscv_isa_vendor_ext_data_list *riscv_isa_vendor_ext_list[] = { > > +#ifdef CONFIG_RISCV_ISA_VENDOR_EXT_THEAD > > + &riscv_isa_vendor_ext_list_thead, > > +#endif > > +}; > > + > > +const size_t riscv_isa_vendor_ext_list_size = ARRAY_SIZE(riscv_isa_vendor_ext_list); > > diff --git a/arch/riscv/kernel/vendor_extensions/Makefile b/arch/riscv/kernel/vendor_extensions/Makefile > > new file mode 100644 > > index 000000000000..3383066baaab > > --- /dev/null > > +++ b/arch/riscv/kernel/vendor_extensions/Makefile > > @@ -0,0 +1,3 @@ > > +# SPDX-License-Identifier: GPL-2.0-only > > + > > +obj-$(CONFIG_RISCV_ISA_VENDOR_EXT_THEAD) += thead.o > > diff --git a/arch/riscv/kernel/vendor_extensions/thead.c b/arch/riscv/kernel/vendor_extensions/thead.c > > new file mode 100644 > > index 000000000000..edb20b928c0c > > --- /dev/null > > +++ b/arch/riscv/kernel/vendor_extensions/thead.c > > @@ -0,0 +1,36 @@ > > +// SPDX-License-Identifier: GPL-2.0-only > > + > > +#include <asm/cpufeature.h> > > +#include <asm/vendor_extensions.h> > > +#include <asm/vendor_extensions/thead.h> > > + > > +#include <linux/array_size.h> > > +#include <linux/types.h> > > + > > +/* All T-Head vendor extensions supported in Linux */ > > +const struct riscv_isa_ext_data riscv_isa_vendor_ext_thead[] = { > > + __RISCV_ISA_EXT_DATA(xtheadvector, RISCV_ISA_VENDOR_EXT_XTHEADVECTOR), > > +}; > > + > > +/* > > + * The first member of this struct must be a bitmap named isa so it can be > > + * compatible with riscv_isainfo even though the sizes of the bitmaps may be > > + * different. > This is kinda yucky, as you're casting a bitmap of a different size > into a struct riscv_isainfo *, which has a known size. I don't > necessarily have a fabulous suggestion to fix though. The best I can > come up with is refactor struct riscv_isainfo to be: > struct riscv_isainfo { > int count; > unsigned long isa[0]; > }; > > then declare a standard one (for hart_isa, which is statically allocated): > struct riscv_std_isainfo { > int count; > DECLARE_BITMAP(isa, RISCV_ISA_EXT_MAX); > } > > and a thead one > struct riscv_thead_isainfo { > int count; > DECLARE_BITMAP(isa, RISCV_ISA_VENDOR_EXT_MAX_THEAD); > } > > But there's still a cast in there, as you'd cast the specialized > structs to struct riscv_isainfo *. But at least the size is in there > to be enforced at runtime, rather than a compile-time check that's > wrong. So I'll just leave this half baked thought here, and maybe you > can think of a cleaner way, or ignore it :). Yes perhaps this is a better way of doing it. - Charlie > > > > + */ > > +struct riscv_isavendorinfo_thead { > > + DECLARE_BITMAP(isa, RISCV_ISA_VENDOR_EXT_MAX_THEAD); > > +}; > > + > > +/* Hart specific T-Head vendor extension support */ > > +static struct riscv_isavendorinfo_thead hart_vendorinfo_thead[NR_CPUS]; > > + > > +/* Set of T-Head vendor extensions supported on all harts */ > > +DECLARE_BITMAP(vendorinfo_thead, RISCV_ISA_VENDOR_EXT_MAX_THEAD); > > + > > +const struct riscv_isa_vendor_ext_data_list riscv_isa_vendor_ext_list_thead = { > > + .ext_data = riscv_isa_vendor_ext_thead, > > + .per_hart_vendor_bitmap = (struct riscv_isainfo *)hart_vendorinfo_thead, > > + .vendor_bitmap = vendorinfo_thead, > > + .ext_data_count = ARRAY_SIZE(riscv_isa_vendor_ext_thead), > > + .bitmap_size = RISCV_ISA_VENDOR_EXT_MAX_THEAD > > +}; > > > > -- > > 2.44.0 > >
On Wed, May 01, 2024 at 06:19:34PM +0100, Conor Dooley wrote: > On Wed, May 01, 2024 at 09:44:15AM -0700, Evan Green wrote: > > On Fri, Apr 26, 2024 at 2:29 PM Charlie Jenkins <charlie@rivosinc.com> wrote: > > > > +struct riscv_isa_vendor_ext_data_list { > > > + const struct riscv_isa_ext_data *ext_data; > > > + struct riscv_isainfo *per_hart_vendor_bitmap; > > > + unsigned long *vendor_bitmap; > > > > It took a lot of digging for me to understand this was the set of > > vendor extensions supported on all harts. Can we add that to the name, > > maybe something like isa_bitmap_all_harts? (I wonder if we could drop > > the vendor part of the name since we already know we're in a > > vendor_ext_data_list structure). > > Reading this made me wonder, why is the all-hart bitmap an unsigned long > when the per hart one is a riscv_isainfo struct? Hmm I don't think there is a good reason for that. I believe this can become struct riscv_isainfo * with no issues. - Charlie
On Wed, May 01, 2024 at 10:51:38AM -0700, Charlie Jenkins wrote: > On Wed, May 01, 2024 at 09:44:15AM -0700, Evan Green wrote: > > On Fri, Apr 26, 2024 at 2:29 PM Charlie Jenkins <charlie@rivosinc.com> wrote: > > > + for (int i = 0; i < riscv_isa_vendor_ext_list_size; i++) { > > > + const struct riscv_isa_vendor_ext_data_list *ext_list = riscv_isa_vendor_ext_list[i]; > > > + > > > + if (bitmap_empty(ext_list->vendor_bitmap, ext_list->bitmap_size)) > > > + bitmap_copy(ext_list->vendor_bitmap, > > > + ext_list->per_hart_vendor_bitmap[cpu].isa, > > > + ext_list->bitmap_size); > > > > Could you get into trouble here if the set of vendor extensions > > reduces to zero, and then becomes non-zero? To illustrate, consider > > these masks: > > cpu 0: 0x0000C000 > > cpu 1: 0x00000003 <<< vendor_bitmap ANDs out to 0 > > cpu 2: 0x00000010 <<< oops, we end up copying this into vendor_bitmap > > > > Huh that's a good point. The standard extensions have that same bug too? > > if (bitmap_empty(riscv_isa, RISCV_ISA_EXT_MAX)) > bitmap_copy(riscv_isa, isainfo->isa, RISCV_ISA_EXT_MAX); > else > bitmap_and(riscv_isa, riscv_isa, isainfo->isa, RISCV_ISA_EXT_MAX); I suppose it could in theory, but the boot hart needs ima to even get this far. I think you'd only end up with this happening if there were enabled harts that supported rvXXe, but I don't think we even add those to the possible set of CPUs. I'll have to check.
On Wed, May 1, 2024 at 10:51 AM Charlie Jenkins <charlie@rivosinc.com> wrote: > > On Wed, May 01, 2024 at 09:44:15AM -0700, Evan Green wrote: > > On Fri, Apr 26, 2024 at 2:29 PM Charlie Jenkins <charlie@rivosinc.com> wrote: > > > > > > Separate vendor extensions out into one struct per vendor > > > instead of adding vendor extensions onto riscv_isa_ext. > > > > > > Add a hidden config RISCV_ISA_VENDOR_EXT to conditionally include this > > > code. > > > > > > The xtheadvector vendor extension is added using these changes. > > > > > > Signed-off-by: Charlie Jenkins <charlie@rivosinc.com> > > > --- > > > arch/riscv/Kconfig | 2 + > > > arch/riscv/Kconfig.vendor | 19 ++++++ > > > arch/riscv/include/asm/cpufeature.h | 18 ++++++ > > > arch/riscv/include/asm/vendor_extensions.h | 26 ++++++++ > > > arch/riscv/include/asm/vendor_extensions/thead.h | 19 ++++++ > > > arch/riscv/kernel/Makefile | 2 + > > > arch/riscv/kernel/cpufeature.c | 77 ++++++++++++++++++------ > > > arch/riscv/kernel/vendor_extensions.c | 18 ++++++ > > > arch/riscv/kernel/vendor_extensions/Makefile | 3 + > > > arch/riscv/kernel/vendor_extensions/thead.c | 36 +++++++++++ > > > 10 files changed, 200 insertions(+), 20 deletions(-) > > > > > > diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig > > > index be09c8836d56..fec86fba3acd 100644 > > > --- a/arch/riscv/Kconfig > > > +++ b/arch/riscv/Kconfig > > > @@ -759,6 +759,8 @@ config RISCV_EFFICIENT_UNALIGNED_ACCESS > > > > > > endchoice > > > > > > +source "arch/riscv/Kconfig.vendor" > > > + > > > endmenu # "Platform type" > > > > > > menu "Kernel features" > > > diff --git a/arch/riscv/Kconfig.vendor b/arch/riscv/Kconfig.vendor > > > new file mode 100644 > > > index 000000000000..4fc86810af1d > > > --- /dev/null > > > +++ b/arch/riscv/Kconfig.vendor > > > @@ -0,0 +1,19 @@ > > > +menu "Vendor extensions" > > > + > > > +config RISCV_ISA_VENDOR_EXT > > > + bool > > > + > > > +menu "T-Head" > > > +config RISCV_ISA_VENDOR_EXT_THEAD > > > + bool "T-Head vendor extension support" > > > + select RISCV_ISA_VENDOR_EXT > > > + default y > > > + help > > > + Say N here if you want to disable all T-Head vendor extension > > > + support. This will cause any T-Head vendor extensions that are > > > + requested to be ignored. > > > + > > > + If you don't know what to do here, say Y. > > > +endmenu > > > + > > > +endmenu > > > diff --git a/arch/riscv/include/asm/cpufeature.h b/arch/riscv/include/asm/cpufeature.h > > > index 0c4f08577015..fedd479ccfd1 100644 > > > --- a/arch/riscv/include/asm/cpufeature.h > > > +++ b/arch/riscv/include/asm/cpufeature.h > > > @@ -35,6 +35,24 @@ extern u32 riscv_vlenb_of; > > > > > > void riscv_user_isa_enable(void); > > > > > > +#define _RISCV_ISA_EXT_DATA(_name, _id, _subset_exts, _subset_exts_size) { \ > > > + .name = #_name, \ > > > + .property = #_name, \ > > > + .id = _id, \ > > > + .subset_ext_ids = _subset_exts, \ > > > + .subset_ext_size = _subset_exts_size \ > > > +} > > > + > > > +#define __RISCV_ISA_EXT_DATA(_name, _id) _RISCV_ISA_EXT_DATA(_name, _id, NULL, 0) > > > + > > > +/* Used to declare pure "lasso" extension (Zk for instance) */ > > > +#define __RISCV_ISA_EXT_BUNDLE(_name, _bundled_exts) \ > > > + _RISCV_ISA_EXT_DATA(_name, RISCV_ISA_EXT_INVALID, _bundled_exts, ARRAY_SIZE(_bundled_exts)) > > > + > > > +/* Used to declare extensions that are a superset of other extensions (Zvbb for instance) */ > > > +#define __RISCV_ISA_EXT_SUPERSET(_name, _id, _sub_exts) \ > > > + _RISCV_ISA_EXT_DATA(_name, _id, _sub_exts, ARRAY_SIZE(_sub_exts)) > > > + > > > #if defined(CONFIG_RISCV_MISALIGNED) > > > bool check_unaligned_access_emulated_all_cpus(void); > > > void unaligned_emulation_finish(void); > > > diff --git a/arch/riscv/include/asm/vendor_extensions.h b/arch/riscv/include/asm/vendor_extensions.h > > > new file mode 100644 > > > index 000000000000..0af1ddd0af70 > > > --- /dev/null > > > +++ b/arch/riscv/include/asm/vendor_extensions.h > > > @@ -0,0 +1,26 @@ > > > +/* SPDX-License-Identifier: GPL-2.0-only */ > > > +/* > > > + * Copyright 2024 Rivos, Inc > > > + */ > > > + > > > +#ifndef _ASM_VENDOR_EXTENSIONS_H > > > +#define _ASM_VENDOR_EXTENSIONS_H > > > + > > > +#include <asm/cpufeature.h> > > > + > > > +#include <linux/array_size.h> > > > +#include <linux/types.h> > > > + > > > +struct riscv_isa_vendor_ext_data_list { > > > + const struct riscv_isa_ext_data *ext_data; > > > + struct riscv_isainfo *per_hart_vendor_bitmap; > > > + unsigned long *vendor_bitmap; > > > > It took a lot of digging for me to understand this was the set of > > vendor extensions supported on all harts. Can we add that to the name, > > maybe something like isa_bitmap_all_harts? (I wonder if we could drop > > the vendor part of the name since we already know we're in a > > vendor_ext_data_list structure). > > Sure, I figured it was implied since the other bitmap says "per_hart", > but I can see how it could be confusing. > > > > > > + const size_t ext_data_count; > > > + const size_t bitmap_size; > > > +}; > > > + > > > +extern const struct riscv_isa_vendor_ext_data_list *riscv_isa_vendor_ext_list[]; > > > + > > > +extern const size_t riscv_isa_vendor_ext_list_size; > > > + > > > +#endif /* _ASM_VENDOR_EXTENSIONS_H */ > > > diff --git a/arch/riscv/include/asm/vendor_extensions/thead.h b/arch/riscv/include/asm/vendor_extensions/thead.h > > > new file mode 100644 > > > index 000000000000..92eec729888d > > > --- /dev/null > > > +++ b/arch/riscv/include/asm/vendor_extensions/thead.h > > > @@ -0,0 +1,19 @@ > > > +/* SPDX-License-Identifier: GPL-2.0 */ > > > +#ifndef _ASM_RISCV_VENDOR_EXTENSIONS_THEAD_H > > > +#define _ASM_RISCV_VENDOR_EXTENSIONS_THEAD_H > > > + > > > +#include <asm/vendor_extensions.h> > > > + > > > +#include <linux/types.h> > > > + > > > +#define RISCV_ISA_VENDOR_EXT_XTHEADVECTOR 0 > > > + > > > +/* > > > + * Extension keys should be strictly less than max. > > > + * It is safe to increment this when necessary. > > > + */ > > > +#define RISCV_ISA_VENDOR_EXT_MAX_THEAD 32 > > > + > > > +extern const struct riscv_isa_vendor_ext_data_list riscv_isa_vendor_ext_list_thead; > > > + > > > +#endif > > > diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile > > > index 81d94a8ee10f..53361c50fb46 100644 > > > --- a/arch/riscv/kernel/Makefile > > > +++ b/arch/riscv/kernel/Makefile > > > @@ -58,6 +58,8 @@ obj-y += riscv_ksyms.o > > > obj-y += stacktrace.o > > > obj-y += cacheinfo.o > > > obj-y += patch.o > > > +obj-y += vendor_extensions.o > > > +obj-y += vendor_extensions/ > > > obj-y += probes/ > > > obj-y += tests/ > > > obj-$(CONFIG_MMU) += vdso.o vdso/ > > > diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c > > > index 8158f34c3e36..c073494519eb 100644 > > > --- a/arch/riscv/kernel/cpufeature.c > > > +++ b/arch/riscv/kernel/cpufeature.c > > > @@ -24,6 +24,7 @@ > > > #include <asm/processor.h> > > > #include <asm/sbi.h> > > > #include <asm/vector.h> > > > +#include <asm/vendor_extensions.h> > > > > > > #define NUM_ALPHA_EXTS ('z' - 'a' + 1) > > > > > > @@ -102,24 +103,6 @@ static bool riscv_isa_extension_check(int id) > > > return true; > > > } > > > > > > -#define _RISCV_ISA_EXT_DATA(_name, _id, _subset_exts, _subset_exts_size) { \ > > > - .name = #_name, \ > > > - .property = #_name, \ > > > - .id = _id, \ > > > - .subset_ext_ids = _subset_exts, \ > > > - .subset_ext_size = _subset_exts_size \ > > > -} > > > - > > > -#define __RISCV_ISA_EXT_DATA(_name, _id) _RISCV_ISA_EXT_DATA(_name, _id, NULL, 0) > > > - > > > -/* Used to declare pure "lasso" extension (Zk for instance) */ > > > -#define __RISCV_ISA_EXT_BUNDLE(_name, _bundled_exts) \ > > > - _RISCV_ISA_EXT_DATA(_name, RISCV_ISA_EXT_INVALID, _bundled_exts, ARRAY_SIZE(_bundled_exts)) > > > - > > > -/* Used to declare extensions that are a superset of other extensions (Zvbb for instance) */ > > > -#define __RISCV_ISA_EXT_SUPERSET(_name, _id, _sub_exts) \ > > > - _RISCV_ISA_EXT_DATA(_name, _id, _sub_exts, ARRAY_SIZE(_sub_exts)) > > > - > > > static const unsigned int riscv_zk_bundled_exts[] = { > > > RISCV_ISA_EXT_ZBKB, > > > RISCV_ISA_EXT_ZBKC, > > > @@ -353,6 +336,10 @@ static void __init riscv_parse_isa_string(unsigned long *this_hwcap, struct risc > > > bool ext_long = false, ext_err = false; > > > > > > switch (*ext) { > > > + case 'x': > > > + case 'X': > > > + pr_warn_once("Vendor extensions are ignored in riscv,isa. Use riscv,isa-extensions instead."); > > > + continue; > > > case 's': > > > /* > > > * Workaround for invalid single-letter 's' & 'u' (QEMU). > > > @@ -368,8 +355,6 @@ static void __init riscv_parse_isa_string(unsigned long *this_hwcap, struct risc > > > } > > > fallthrough; > > > case 'S': > > > - case 'x': > > > - case 'X': > > > case 'z': > > > case 'Z': > > > /* > > > @@ -572,6 +557,54 @@ static void __init riscv_fill_hwcap_from_isa_string(unsigned long *isa2hwcap) > > > acpi_put_table((struct acpi_table_header *)rhct); > > > } > > > > > > +static void __init riscv_fill_cpu_vendor_ext(struct device_node *cpu_node, int cpu) > > > +{ > > > + if (!IS_ENABLED(CONFIG_RISCV_ISA_VENDOR_EXT)) > > > + return; > > > + > > > + for (int i = 0; i < riscv_isa_vendor_ext_list_size; i++) { > > > + const struct riscv_isa_vendor_ext_data_list *ext_list = riscv_isa_vendor_ext_list[i]; > > > + > > > + for (int j = 0; j < ext_list->ext_data_count; j++) { > > > + const struct riscv_isa_ext_data ext = ext_list->ext_data[j]; > > > + struct riscv_isainfo *isavendorinfo = &ext_list->per_hart_vendor_bitmap[cpu]; > > > + > > > + if (of_property_match_string(cpu_node, "riscv,isa-extensions", > > > + ext.property) < 0) > > > + continue; > > > + > > > + /* > > > + * Assume that subset extensions are all members of the > > > + * same vendor. > > > + */ > > > + if (ext.subset_ext_size) > > > + for (int k = 0; k < ext.subset_ext_size; k++) > > > + set_bit(ext.subset_ext_ids[k], isavendorinfo->isa); > > > + > > > + set_bit(ext.id, isavendorinfo->isa); > > > + } > > > > This loop seems super similar to the regular one (in > > riscv_fill_hwcap_from_ext_list() in the random, possibly old, kernel I > > have open). Could we refactor these together into a common helper? The > > other loop has an extra stanza for riscv_isa_extension_check(), so > > we'd have to add an extra condition there, but otherwise it looks > > pretty compatible? > > > > I actually did have this refactored into a single function in a previous > version but broke it back up since I felt there just wasn't enough > overlap. The one for standard extensions is: > > for (int i = 0; i < riscv_isa_ext_count; i++) { > const struct riscv_isa_ext_data *ext = &riscv_isa_ext[i]; > > if (of_property_match_string(cpu_node, "riscv,isa-extensions", > ext->property) < 0) > continue; > > if (ext->subset_ext_size) { > for (int j = 0; j < ext->subset_ext_size; j++) { > if (riscv_isa_extension_check(ext->subset_ext_ids[i])) > set_bit(ext->subset_ext_ids[j], isainfo->isa); > } > } > > if (riscv_isa_extension_check(ext->id)) { > set_bit(ext->id, isainfo->isa); > > /* Only single letter extensions get set in hwcap */ > if (strnlen(riscv_isa_ext[i].name, 2) == 1) > this_hwcap |= isa2hwcap[riscv_isa_ext[i].id]; > } > } > > The motivating reason why I didn't combine them was the additional > `struct riscv_isa_vendor_ext_data_list *` data type for the vendor > version which contains ext and isainfo. This can probably be combined in > a straight-forward way though. I see what you mean. There might be a way to reconfigure the structs to make this work better, but yeah, those slight differences make it hard to extract a common bit. > > > > + } > > > +} > > > + > > > +static void __init riscv_fill_vendor_ext_list(int cpu) > > > +{ > > > + if (!IS_ENABLED(CONFIG_RISCV_ISA_VENDOR_EXT)) > > > + return; > > > + > > > + for (int i = 0; i < riscv_isa_vendor_ext_list_size; i++) { > > > + const struct riscv_isa_vendor_ext_data_list *ext_list = riscv_isa_vendor_ext_list[i]; > > > + > > > + if (bitmap_empty(ext_list->vendor_bitmap, ext_list->bitmap_size)) > > > + bitmap_copy(ext_list->vendor_bitmap, > > > + ext_list->per_hart_vendor_bitmap[cpu].isa, > > > + ext_list->bitmap_size); > > > > Could you get into trouble here if the set of vendor extensions > > reduces to zero, and then becomes non-zero? To illustrate, consider > > these masks: > > cpu 0: 0x0000C000 > > cpu 1: 0x00000003 <<< vendor_bitmap ANDs out to 0 > > cpu 2: 0x00000010 <<< oops, we end up copying this into vendor_bitmap > > > > Huh that's a good point. The standard extensions have that same bug too? > > if (bitmap_empty(riscv_isa, RISCV_ISA_EXT_MAX)) > bitmap_copy(riscv_isa, isainfo->isa, RISCV_ISA_EXT_MAX); > else > bitmap_and(riscv_isa, riscv_isa, isainfo->isa, RISCV_ISA_EXT_MAX); > Ah crap you're right. What clown introduced that code? Oh, me. I'm not aware of anything heterogenous yet, so hopefully we can just quietly fix it. > > > > + else > > > + bitmap_and(ext_list->vendor_bitmap, ext_list->vendor_bitmap, > > > + ext_list->per_hart_vendor_bitmap[cpu].isa, > > > + ext_list->bitmap_size); > > > + } > > > +} > > > + > > > static int __init riscv_fill_hwcap_from_ext_list(unsigned long *isa2hwcap) > > > { > > > unsigned int cpu; > > > @@ -615,6 +648,8 @@ static int __init riscv_fill_hwcap_from_ext_list(unsigned long *isa2hwcap) > > > } > > > } > > > > > > + riscv_fill_cpu_vendor_ext(cpu_node, cpu); > > > + > > > of_node_put(cpu_node); > > > > > > /* > > > @@ -630,6 +665,8 @@ static int __init riscv_fill_hwcap_from_ext_list(unsigned long *isa2hwcap) > > > bitmap_copy(riscv_isa, isainfo->isa, RISCV_ISA_EXT_MAX); > > > else > > > bitmap_and(riscv_isa, riscv_isa, isainfo->isa, RISCV_ISA_EXT_MAX); > > > + > > > + riscv_fill_vendor_ext_list(cpu); > > > } > > > > > > if (bitmap_empty(riscv_isa, RISCV_ISA_EXT_MAX)) > > > diff --git a/arch/riscv/kernel/vendor_extensions.c b/arch/riscv/kernel/vendor_extensions.c > > > new file mode 100644 > > > index 000000000000..f76cb3013c2d > > > --- /dev/null > > > +++ b/arch/riscv/kernel/vendor_extensions.c > > > @@ -0,0 +1,18 @@ > > > +// SPDX-License-Identifier: GPL-2.0-only > > > +/* > > > + * Copyright 2024 Rivos, Inc > > > + */ > > > + > > > +#include <asm/vendor_extensions.h> > > > +#include <asm/vendor_extensions/thead.h> > > > + > > > +#include <linux/array_size.h> > > > +#include <linux/types.h> > > > + > > > +const struct riscv_isa_vendor_ext_data_list *riscv_isa_vendor_ext_list[] = { > > > +#ifdef CONFIG_RISCV_ISA_VENDOR_EXT_THEAD > > > + &riscv_isa_vendor_ext_list_thead, > > > +#endif > > > +}; > > > + > > > +const size_t riscv_isa_vendor_ext_list_size = ARRAY_SIZE(riscv_isa_vendor_ext_list); > > > diff --git a/arch/riscv/kernel/vendor_extensions/Makefile b/arch/riscv/kernel/vendor_extensions/Makefile > > > new file mode 100644 > > > index 000000000000..3383066baaab > > > --- /dev/null > > > +++ b/arch/riscv/kernel/vendor_extensions/Makefile > > > @@ -0,0 +1,3 @@ > > > +# SPDX-License-Identifier: GPL-2.0-only > > > + > > > +obj-$(CONFIG_RISCV_ISA_VENDOR_EXT_THEAD) += thead.o > > > diff --git a/arch/riscv/kernel/vendor_extensions/thead.c b/arch/riscv/kernel/vendor_extensions/thead.c > > > new file mode 100644 > > > index 000000000000..edb20b928c0c > > > --- /dev/null > > > +++ b/arch/riscv/kernel/vendor_extensions/thead.c > > > @@ -0,0 +1,36 @@ > > > +// SPDX-License-Identifier: GPL-2.0-only > > > + > > > +#include <asm/cpufeature.h> > > > +#include <asm/vendor_extensions.h> > > > +#include <asm/vendor_extensions/thead.h> > > > + > > > +#include <linux/array_size.h> > > > +#include <linux/types.h> > > > + > > > +/* All T-Head vendor extensions supported in Linux */ > > > +const struct riscv_isa_ext_data riscv_isa_vendor_ext_thead[] = { > > > + __RISCV_ISA_EXT_DATA(xtheadvector, RISCV_ISA_VENDOR_EXT_XTHEADVECTOR), > > > +}; > > > + > > > +/* > > > + * The first member of this struct must be a bitmap named isa so it can be > > > + * compatible with riscv_isainfo even though the sizes of the bitmaps may be > > > + * different. > > This is kinda yucky, as you're casting a bitmap of a different size > > into a struct riscv_isainfo *, which has a known size. I don't > > necessarily have a fabulous suggestion to fix though. The best I can > > come up with is refactor struct riscv_isainfo to be: > > struct riscv_isainfo { > > int count; > > unsigned long isa[0]; > > }; > > > > then declare a standard one (for hart_isa, which is statically allocated): > > struct riscv_std_isainfo { > > int count; > > DECLARE_BITMAP(isa, RISCV_ISA_EXT_MAX); > > } > > > > and a thead one > > struct riscv_thead_isainfo { > > int count; > > DECLARE_BITMAP(isa, RISCV_ISA_VENDOR_EXT_MAX_THEAD); > > } > > > > But there's still a cast in there, as you'd cast the specialized > > structs to struct riscv_isainfo *. But at least the size is in there > > to be enforced at runtime, rather than a compile-time check that's > > wrong. So I'll just leave this half baked thought here, and maybe you > > can think of a cleaner way, or ignore it :). > > Yes perhaps this is a better way of doing it. > > - Charlie > > > > > > > > + */ > > > +struct riscv_isavendorinfo_thead { > > > + DECLARE_BITMAP(isa, RISCV_ISA_VENDOR_EXT_MAX_THEAD); > > > +}; > > > + > > > +/* Hart specific T-Head vendor extension support */ > > > +static struct riscv_isavendorinfo_thead hart_vendorinfo_thead[NR_CPUS]; > > > + > > > +/* Set of T-Head vendor extensions supported on all harts */ > > > +DECLARE_BITMAP(vendorinfo_thead, RISCV_ISA_VENDOR_EXT_MAX_THEAD); > > > + > > > +const struct riscv_isa_vendor_ext_data_list riscv_isa_vendor_ext_list_thead = { > > > + .ext_data = riscv_isa_vendor_ext_thead, > > > + .per_hart_vendor_bitmap = (struct riscv_isainfo *)hart_vendorinfo_thead, > > > + .vendor_bitmap = vendorinfo_thead, > > > + .ext_data_count = ARRAY_SIZE(riscv_isa_vendor_ext_thead), > > > + .bitmap_size = RISCV_ISA_VENDOR_EXT_MAX_THEAD > > > +}; > > > > > > -- > > > 2.44.0 > > >
On Wed, May 01, 2024 at 07:03:46PM +0100, Conor Dooley wrote: > On Wed, May 01, 2024 at 10:51:38AM -0700, Charlie Jenkins wrote: > > On Wed, May 01, 2024 at 09:44:15AM -0700, Evan Green wrote: > > > On Fri, Apr 26, 2024 at 2:29 PM Charlie Jenkins <charlie@rivosinc.com> wrote: > > > > + for (int i = 0; i < riscv_isa_vendor_ext_list_size; i++) { > > > > + const struct riscv_isa_vendor_ext_data_list *ext_list = riscv_isa_vendor_ext_list[i]; > > > > + > > > > + if (bitmap_empty(ext_list->vendor_bitmap, ext_list->bitmap_size)) > > > > + bitmap_copy(ext_list->vendor_bitmap, > > > > + ext_list->per_hart_vendor_bitmap[cpu].isa, > > > > + ext_list->bitmap_size); > > > > > > Could you get into trouble here if the set of vendor extensions > > > reduces to zero, and then becomes non-zero? To illustrate, consider > > > these masks: > > > cpu 0: 0x0000C000 > > > cpu 1: 0x00000003 <<< vendor_bitmap ANDs out to 0 > > > cpu 2: 0x00000010 <<< oops, we end up copying this into vendor_bitmap > > > > > > > Huh that's a good point. The standard extensions have that same bug too? > > > > if (bitmap_empty(riscv_isa, RISCV_ISA_EXT_MAX)) > > bitmap_copy(riscv_isa, isainfo->isa, RISCV_ISA_EXT_MAX); > > else > > bitmap_and(riscv_isa, riscv_isa, isainfo->isa, RISCV_ISA_EXT_MAX); > > I suppose it could in theory, but the boot hart needs ima to even get > this far. I think you'd only end up with this happening if there were > enabled harts that supported rvXXe, but I don't think we even add those > to the possible set of CPUs. I'll have to check. Ye, you don't get marked possible if you don't have ima, so I don't think this is possible to have happen. Maybe a comment here is sufficient, explaining why this cannot reduce to zeros?
On Wed, May 01, 2024 at 07:09:28PM +0100, Conor Dooley wrote: > On Wed, May 01, 2024 at 07:03:46PM +0100, Conor Dooley wrote: > > On Wed, May 01, 2024 at 10:51:38AM -0700, Charlie Jenkins wrote: > > > On Wed, May 01, 2024 at 09:44:15AM -0700, Evan Green wrote: > > > > On Fri, Apr 26, 2024 at 2:29 PM Charlie Jenkins <charlie@rivosinc.com> wrote: > > > > > + for (int i = 0; i < riscv_isa_vendor_ext_list_size; i++) { > > > > > + const struct riscv_isa_vendor_ext_data_list *ext_list = riscv_isa_vendor_ext_list[i]; > > > > > + > > > > > + if (bitmap_empty(ext_list->vendor_bitmap, ext_list->bitmap_size)) > > > > > + bitmap_copy(ext_list->vendor_bitmap, > > > > > + ext_list->per_hart_vendor_bitmap[cpu].isa, > > > > > + ext_list->bitmap_size); > > > > > > > > Could you get into trouble here if the set of vendor extensions > > > > reduces to zero, and then becomes non-zero? To illustrate, consider > > > > these masks: > > > > cpu 0: 0x0000C000 > > > > cpu 1: 0x00000003 <<< vendor_bitmap ANDs out to 0 > > > > cpu 2: 0x00000010 <<< oops, we end up copying this into vendor_bitmap > > > > > > > > > > Huh that's a good point. The standard extensions have that same bug too? > > > > > > if (bitmap_empty(riscv_isa, RISCV_ISA_EXT_MAX)) > > > bitmap_copy(riscv_isa, isainfo->isa, RISCV_ISA_EXT_MAX); > > > else > > > bitmap_and(riscv_isa, riscv_isa, isainfo->isa, RISCV_ISA_EXT_MAX); > > > > I suppose it could in theory, but the boot hart needs ima to even get > > this far. I think you'd only end up with this happening if there were > > enabled harts that supported rvXXe, but I don't think we even add those > > to the possible set of CPUs. I'll have to check. > > Ye, you don't get marked possible if you don't have ima, so I don't > think this is possible to have happen. Maybe a comment here is > sufficient, explaining why this cannot reduce to zeros? Okay cool. A comment is sufficient then. - Charlie > >
On Wed, May 01, 2024 at 09:44:15AM -0700, Evan Green wrote: > On Fri, Apr 26, 2024 at 2:29 PM Charlie Jenkins <charlie@rivosinc.com> wrote: > > > > Separate vendor extensions out into one struct per vendor > > instead of adding vendor extensions onto riscv_isa_ext. > > > > Add a hidden config RISCV_ISA_VENDOR_EXT to conditionally include this > > code. > > > > The xtheadvector vendor extension is added using these changes. > > > > Signed-off-by: Charlie Jenkins <charlie@rivosinc.com> > > --- > > arch/riscv/Kconfig | 2 + > > arch/riscv/Kconfig.vendor | 19 ++++++ > > arch/riscv/include/asm/cpufeature.h | 18 ++++++ > > arch/riscv/include/asm/vendor_extensions.h | 26 ++++++++ > > arch/riscv/include/asm/vendor_extensions/thead.h | 19 ++++++ > > arch/riscv/kernel/Makefile | 2 + > > arch/riscv/kernel/cpufeature.c | 77 ++++++++++++++++++------ > > arch/riscv/kernel/vendor_extensions.c | 18 ++++++ > > arch/riscv/kernel/vendor_extensions/Makefile | 3 + > > arch/riscv/kernel/vendor_extensions/thead.c | 36 +++++++++++ > > 10 files changed, 200 insertions(+), 20 deletions(-) > > > > diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig > > index be09c8836d56..fec86fba3acd 100644 > > --- a/arch/riscv/Kconfig > > +++ b/arch/riscv/Kconfig > > @@ -759,6 +759,8 @@ config RISCV_EFFICIENT_UNALIGNED_ACCESS > > > > endchoice > > > > +source "arch/riscv/Kconfig.vendor" > > + > > endmenu # "Platform type" > > > > menu "Kernel features" > > diff --git a/arch/riscv/Kconfig.vendor b/arch/riscv/Kconfig.vendor > > new file mode 100644 > > index 000000000000..4fc86810af1d > > --- /dev/null > > +++ b/arch/riscv/Kconfig.vendor > > @@ -0,0 +1,19 @@ > > +menu "Vendor extensions" > > + > > +config RISCV_ISA_VENDOR_EXT > > + bool > > + > > +menu "T-Head" > > +config RISCV_ISA_VENDOR_EXT_THEAD > > + bool "T-Head vendor extension support" > > + select RISCV_ISA_VENDOR_EXT > > + default y > > + help > > + Say N here if you want to disable all T-Head vendor extension > > + support. This will cause any T-Head vendor extensions that are > > + requested to be ignored. > > + > > + If you don't know what to do here, say Y. > > +endmenu > > + > > +endmenu > > diff --git a/arch/riscv/include/asm/cpufeature.h b/arch/riscv/include/asm/cpufeature.h > > index 0c4f08577015..fedd479ccfd1 100644 > > --- a/arch/riscv/include/asm/cpufeature.h > > +++ b/arch/riscv/include/asm/cpufeature.h > > @@ -35,6 +35,24 @@ extern u32 riscv_vlenb_of; > > > > void riscv_user_isa_enable(void); > > > > +#define _RISCV_ISA_EXT_DATA(_name, _id, _subset_exts, _subset_exts_size) { \ > > + .name = #_name, \ > > + .property = #_name, \ > > + .id = _id, \ > > + .subset_ext_ids = _subset_exts, \ > > + .subset_ext_size = _subset_exts_size \ > > +} > > + > > +#define __RISCV_ISA_EXT_DATA(_name, _id) _RISCV_ISA_EXT_DATA(_name, _id, NULL, 0) > > + > > +/* Used to declare pure "lasso" extension (Zk for instance) */ > > +#define __RISCV_ISA_EXT_BUNDLE(_name, _bundled_exts) \ > > + _RISCV_ISA_EXT_DATA(_name, RISCV_ISA_EXT_INVALID, _bundled_exts, ARRAY_SIZE(_bundled_exts)) > > + > > +/* Used to declare extensions that are a superset of other extensions (Zvbb for instance) */ > > +#define __RISCV_ISA_EXT_SUPERSET(_name, _id, _sub_exts) \ > > + _RISCV_ISA_EXT_DATA(_name, _id, _sub_exts, ARRAY_SIZE(_sub_exts)) > > + > > #if defined(CONFIG_RISCV_MISALIGNED) > > bool check_unaligned_access_emulated_all_cpus(void); > > void unaligned_emulation_finish(void); > > diff --git a/arch/riscv/include/asm/vendor_extensions.h b/arch/riscv/include/asm/vendor_extensions.h > > new file mode 100644 > > index 000000000000..0af1ddd0af70 > > --- /dev/null > > +++ b/arch/riscv/include/asm/vendor_extensions.h > > @@ -0,0 +1,26 @@ > > +/* SPDX-License-Identifier: GPL-2.0-only */ > > +/* > > + * Copyright 2024 Rivos, Inc > > + */ > > + > > +#ifndef _ASM_VENDOR_EXTENSIONS_H > > +#define _ASM_VENDOR_EXTENSIONS_H > > + > > +#include <asm/cpufeature.h> > > + > > +#include <linux/array_size.h> > > +#include <linux/types.h> > > + > > +struct riscv_isa_vendor_ext_data_list { > > + const struct riscv_isa_ext_data *ext_data; > > + struct riscv_isainfo *per_hart_vendor_bitmap; > > + unsigned long *vendor_bitmap; > > It took a lot of digging for me to understand this was the set of > vendor extensions supported on all harts. Can we add that to the name, > maybe something like isa_bitmap_all_harts? (I wonder if we could drop > the vendor part of the name since we already know we're in a > vendor_ext_data_list structure). > > > + const size_t ext_data_count; > > + const size_t bitmap_size; > > +}; > > + > > +extern const struct riscv_isa_vendor_ext_data_list *riscv_isa_vendor_ext_list[]; > > + > > +extern const size_t riscv_isa_vendor_ext_list_size; > > + > > +#endif /* _ASM_VENDOR_EXTENSIONS_H */ > > diff --git a/arch/riscv/include/asm/vendor_extensions/thead.h b/arch/riscv/include/asm/vendor_extensions/thead.h > > new file mode 100644 > > index 000000000000..92eec729888d > > --- /dev/null > > +++ b/arch/riscv/include/asm/vendor_extensions/thead.h > > @@ -0,0 +1,19 @@ > > +/* SPDX-License-Identifier: GPL-2.0 */ > > +#ifndef _ASM_RISCV_VENDOR_EXTENSIONS_THEAD_H > > +#define _ASM_RISCV_VENDOR_EXTENSIONS_THEAD_H > > + > > +#include <asm/vendor_extensions.h> > > + > > +#include <linux/types.h> > > + > > +#define RISCV_ISA_VENDOR_EXT_XTHEADVECTOR 0 > > + > > +/* > > + * Extension keys should be strictly less than max. > > + * It is safe to increment this when necessary. > > + */ > > +#define RISCV_ISA_VENDOR_EXT_MAX_THEAD 32 > > + > > +extern const struct riscv_isa_vendor_ext_data_list riscv_isa_vendor_ext_list_thead; > > + > > +#endif > > diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile > > index 81d94a8ee10f..53361c50fb46 100644 > > --- a/arch/riscv/kernel/Makefile > > +++ b/arch/riscv/kernel/Makefile > > @@ -58,6 +58,8 @@ obj-y += riscv_ksyms.o > > obj-y += stacktrace.o > > obj-y += cacheinfo.o > > obj-y += patch.o > > +obj-y += vendor_extensions.o > > +obj-y += vendor_extensions/ > > obj-y += probes/ > > obj-y += tests/ > > obj-$(CONFIG_MMU) += vdso.o vdso/ > > diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c > > index 8158f34c3e36..c073494519eb 100644 > > --- a/arch/riscv/kernel/cpufeature.c > > +++ b/arch/riscv/kernel/cpufeature.c > > @@ -24,6 +24,7 @@ > > #include <asm/processor.h> > > #include <asm/sbi.h> > > #include <asm/vector.h> > > +#include <asm/vendor_extensions.h> > > > > #define NUM_ALPHA_EXTS ('z' - 'a' + 1) > > > > @@ -102,24 +103,6 @@ static bool riscv_isa_extension_check(int id) > > return true; > > } > > > > -#define _RISCV_ISA_EXT_DATA(_name, _id, _subset_exts, _subset_exts_size) { \ > > - .name = #_name, \ > > - .property = #_name, \ > > - .id = _id, \ > > - .subset_ext_ids = _subset_exts, \ > > - .subset_ext_size = _subset_exts_size \ > > -} > > - > > -#define __RISCV_ISA_EXT_DATA(_name, _id) _RISCV_ISA_EXT_DATA(_name, _id, NULL, 0) > > - > > -/* Used to declare pure "lasso" extension (Zk for instance) */ > > -#define __RISCV_ISA_EXT_BUNDLE(_name, _bundled_exts) \ > > - _RISCV_ISA_EXT_DATA(_name, RISCV_ISA_EXT_INVALID, _bundled_exts, ARRAY_SIZE(_bundled_exts)) > > - > > -/* Used to declare extensions that are a superset of other extensions (Zvbb for instance) */ > > -#define __RISCV_ISA_EXT_SUPERSET(_name, _id, _sub_exts) \ > > - _RISCV_ISA_EXT_DATA(_name, _id, _sub_exts, ARRAY_SIZE(_sub_exts)) > > - > > static const unsigned int riscv_zk_bundled_exts[] = { > > RISCV_ISA_EXT_ZBKB, > > RISCV_ISA_EXT_ZBKC, > > @@ -353,6 +336,10 @@ static void __init riscv_parse_isa_string(unsigned long *this_hwcap, struct risc > > bool ext_long = false, ext_err = false; > > > > switch (*ext) { > > + case 'x': > > + case 'X': > > + pr_warn_once("Vendor extensions are ignored in riscv,isa. Use riscv,isa-extensions instead."); > > + continue; > > case 's': > > /* > > * Workaround for invalid single-letter 's' & 'u' (QEMU). > > @@ -368,8 +355,6 @@ static void __init riscv_parse_isa_string(unsigned long *this_hwcap, struct risc > > } > > fallthrough; > > case 'S': > > - case 'x': > > - case 'X': > > case 'z': > > case 'Z': > > /* > > @@ -572,6 +557,54 @@ static void __init riscv_fill_hwcap_from_isa_string(unsigned long *isa2hwcap) > > acpi_put_table((struct acpi_table_header *)rhct); > > } > > > > +static void __init riscv_fill_cpu_vendor_ext(struct device_node *cpu_node, int cpu) > > +{ > > + if (!IS_ENABLED(CONFIG_RISCV_ISA_VENDOR_EXT)) > > + return; > > + > > + for (int i = 0; i < riscv_isa_vendor_ext_list_size; i++) { > > + const struct riscv_isa_vendor_ext_data_list *ext_list = riscv_isa_vendor_ext_list[i]; > > + > > + for (int j = 0; j < ext_list->ext_data_count; j++) { > > + const struct riscv_isa_ext_data ext = ext_list->ext_data[j]; > > + struct riscv_isainfo *isavendorinfo = &ext_list->per_hart_vendor_bitmap[cpu]; > > + > > + if (of_property_match_string(cpu_node, "riscv,isa-extensions", > > + ext.property) < 0) > > + continue; > > + > > + /* > > + * Assume that subset extensions are all members of the > > + * same vendor. > > + */ > > + if (ext.subset_ext_size) > > + for (int k = 0; k < ext.subset_ext_size; k++) > > + set_bit(ext.subset_ext_ids[k], isavendorinfo->isa); > > + > > + set_bit(ext.id, isavendorinfo->isa); > > + } > > This loop seems super similar to the regular one (in > riscv_fill_hwcap_from_ext_list() in the random, possibly old, kernel I > have open). Could we refactor these together into a common helper? The > other loop has an extra stanza for riscv_isa_extension_check(), so > we'd have to add an extra condition there, but otherwise it looks > pretty compatible? > > > + } > > +} > > + > > +static void __init riscv_fill_vendor_ext_list(int cpu) > > +{ > > + if (!IS_ENABLED(CONFIG_RISCV_ISA_VENDOR_EXT)) > > + return; > > + > > + for (int i = 0; i < riscv_isa_vendor_ext_list_size; i++) { > > + const struct riscv_isa_vendor_ext_data_list *ext_list = riscv_isa_vendor_ext_list[i]; > > + > > + if (bitmap_empty(ext_list->vendor_bitmap, ext_list->bitmap_size)) > > + bitmap_copy(ext_list->vendor_bitmap, > > + ext_list->per_hart_vendor_bitmap[cpu].isa, > > + ext_list->bitmap_size); > > Could you get into trouble here if the set of vendor extensions > reduces to zero, and then becomes non-zero? To illustrate, consider > these masks: > cpu 0: 0x0000C000 > cpu 1: 0x00000003 <<< vendor_bitmap ANDs out to 0 > cpu 2: 0x00000010 <<< oops, we end up copying this into vendor_bitmap > > > + else > > + bitmap_and(ext_list->vendor_bitmap, ext_list->vendor_bitmap, > > + ext_list->per_hart_vendor_bitmap[cpu].isa, > > + ext_list->bitmap_size); > > + } > > +} > > + > > static int __init riscv_fill_hwcap_from_ext_list(unsigned long *isa2hwcap) > > { > > unsigned int cpu; > > @@ -615,6 +648,8 @@ static int __init riscv_fill_hwcap_from_ext_list(unsigned long *isa2hwcap) > > } > > } > > > > + riscv_fill_cpu_vendor_ext(cpu_node, cpu); > > + > > of_node_put(cpu_node); > > > > /* > > @@ -630,6 +665,8 @@ static int __init riscv_fill_hwcap_from_ext_list(unsigned long *isa2hwcap) > > bitmap_copy(riscv_isa, isainfo->isa, RISCV_ISA_EXT_MAX); > > else > > bitmap_and(riscv_isa, riscv_isa, isainfo->isa, RISCV_ISA_EXT_MAX); > > + > > + riscv_fill_vendor_ext_list(cpu); > > } > > > > if (bitmap_empty(riscv_isa, RISCV_ISA_EXT_MAX)) > > diff --git a/arch/riscv/kernel/vendor_extensions.c b/arch/riscv/kernel/vendor_extensions.c > > new file mode 100644 > > index 000000000000..f76cb3013c2d > > --- /dev/null > > +++ b/arch/riscv/kernel/vendor_extensions.c > > @@ -0,0 +1,18 @@ > > +// SPDX-License-Identifier: GPL-2.0-only > > +/* > > + * Copyright 2024 Rivos, Inc > > + */ > > + > > +#include <asm/vendor_extensions.h> > > +#include <asm/vendor_extensions/thead.h> > > + > > +#include <linux/array_size.h> > > +#include <linux/types.h> > > + > > +const struct riscv_isa_vendor_ext_data_list *riscv_isa_vendor_ext_list[] = { > > +#ifdef CONFIG_RISCV_ISA_VENDOR_EXT_THEAD > > + &riscv_isa_vendor_ext_list_thead, > > +#endif > > +}; > > + > > +const size_t riscv_isa_vendor_ext_list_size = ARRAY_SIZE(riscv_isa_vendor_ext_list); > > diff --git a/arch/riscv/kernel/vendor_extensions/Makefile b/arch/riscv/kernel/vendor_extensions/Makefile > > new file mode 100644 > > index 000000000000..3383066baaab > > --- /dev/null > > +++ b/arch/riscv/kernel/vendor_extensions/Makefile > > @@ -0,0 +1,3 @@ > > +# SPDX-License-Identifier: GPL-2.0-only > > + > > +obj-$(CONFIG_RISCV_ISA_VENDOR_EXT_THEAD) += thead.o > > diff --git a/arch/riscv/kernel/vendor_extensions/thead.c b/arch/riscv/kernel/vendor_extensions/thead.c > > new file mode 100644 > > index 000000000000..edb20b928c0c > > --- /dev/null > > +++ b/arch/riscv/kernel/vendor_extensions/thead.c > > @@ -0,0 +1,36 @@ > > +// SPDX-License-Identifier: GPL-2.0-only > > + > > +#include <asm/cpufeature.h> > > +#include <asm/vendor_extensions.h> > > +#include <asm/vendor_extensions/thead.h> > > + > > +#include <linux/array_size.h> > > +#include <linux/types.h> > > + > > +/* All T-Head vendor extensions supported in Linux */ > > +const struct riscv_isa_ext_data riscv_isa_vendor_ext_thead[] = { > > + __RISCV_ISA_EXT_DATA(xtheadvector, RISCV_ISA_VENDOR_EXT_XTHEADVECTOR), > > +}; > > + > > +/* > > + * The first member of this struct must be a bitmap named isa so it can be > > + * compatible with riscv_isainfo even though the sizes of the bitmaps may be > > + * different. > This is kinda yucky, as you're casting a bitmap of a different size > into a struct riscv_isainfo *, which has a known size. I don't > necessarily have a fabulous suggestion to fix though. The best I can > come up with is refactor struct riscv_isainfo to be: > struct riscv_isainfo { > int count; > unsigned long isa[0]; > }; > > then declare a standard one (for hart_isa, which is statically allocated): > struct riscv_std_isainfo { > int count; > DECLARE_BITMAP(isa, RISCV_ISA_EXT_MAX); > } > > and a thead one > struct riscv_thead_isainfo { > int count; > DECLARE_BITMAP(isa, RISCV_ISA_VENDOR_EXT_MAX_THEAD); > } > > But there's still a cast in there, as you'd cast the specialized > structs to struct riscv_isainfo *. But at least the size is in there > to be enforced at runtime, rather than a compile-time check that's > wrong. So I'll just leave this half baked thought here, and maybe you > can think of a cleaner way, or ignore it :). > After looking into this a bit more, I am not sure there is a "clean" way of doing this. Kees wrote an interesting article about an adjacent problem [1], and my takeaway was that there are some people working to improve situations like this. This pattern is very close to the standard struct with the length of the array as one element and the array itself as another element. There are two major differences though, one being that the count is put through a simple macro BITS_TO_LONGS to calculate the size of the array. The other is that count is a compile time constant that should be populated into all structs of the type, since we have arrays of riscv_isainfo that should be allocated at compile time to all have the same count. Ideally what I would want is something like: struct riscv_thead_isainfo { int count = RISCV_ISA_VENDOR_EXT_MAX_THEAD; DECLARE_BITMAP(isa, RISCV_ISA_VENDOR_EXT_MAX_THEAD); } Otherwise we need to populate count at runtime and that defeats the point in my opinion since this is currently known by accessing the "bitmap_size" of the statically allocated struct: const struct riscv_isa_vendor_ext_data_list riscv_isa_vendor_ext_list_thead This also has the downside of having the same "count" repeated across all of the instances of riscv_thead_isainfo of which there are by default 65 (one for each of the CPUs configured with NR_CPUS which defaults to 64 plus an additional for the least-common-denominator across all CPUs). It's a relatively large amount of bits that gets "wasted". Just for some background here, the purpose here is to be able to have a standardized "struct riscv_isa_vendor_ext_data_list" that each vendor will be able to populate with their vendor extensions. The thought was that each vendor will have a different number of extensions so each vendor doesn't need to reserve the same amount of space in their statically allocated bitmap. vendorA may be able to fit their extensions in 64 bits but vendorB may need 128. We're talking about a small amount of space savings here. We could forego this casting entirely and say each vendor will need a maximum of X bits. It may be unlikely for any vendor to ever end up with more than 64 vendor extensions that they want exposed to the kernel. But if any vendor ever does end up with more than 64, all of the vendors end up needing to have to allocate 128 bits in their bitmask that is allocated for each possible CPU. [1] https://people.kernel.org/kees/bounded-flexible-arrays-in-c - Charlie > > > + */ > > +struct riscv_isavendorinfo_thead { > > + DECLARE_BITMAP(isa, RISCV_ISA_VENDOR_EXT_MAX_THEAD); > > +}; > > + > > +/* Hart specific T-Head vendor extension support */ > > +static struct riscv_isavendorinfo_thead hart_vendorinfo_thead[NR_CPUS]; > > + > > +/* Set of T-Head vendor extensions supported on all harts */ > > +DECLARE_BITMAP(vendorinfo_thead, RISCV_ISA_VENDOR_EXT_MAX_THEAD); > > + > > +const struct riscv_isa_vendor_ext_data_list riscv_isa_vendor_ext_list_thead = { > > + .ext_data = riscv_isa_vendor_ext_thead, > > + .per_hart_vendor_bitmap = (struct riscv_isainfo *)hart_vendorinfo_thead, > > + .vendor_bitmap = vendorinfo_thead, > > + .ext_data_count = ARRAY_SIZE(riscv_isa_vendor_ext_thead), > > + .bitmap_size = RISCV_ISA_VENDOR_EXT_MAX_THEAD > > +}; > > > > -- > > 2.44.0 > >
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index be09c8836d56..fec86fba3acd 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -759,6 +759,8 @@ config RISCV_EFFICIENT_UNALIGNED_ACCESS endchoice +source "arch/riscv/Kconfig.vendor" + endmenu # "Platform type" menu "Kernel features" diff --git a/arch/riscv/Kconfig.vendor b/arch/riscv/Kconfig.vendor new file mode 100644 index 000000000000..4fc86810af1d --- /dev/null +++ b/arch/riscv/Kconfig.vendor @@ -0,0 +1,19 @@ +menu "Vendor extensions" + +config RISCV_ISA_VENDOR_EXT + bool + +menu "T-Head" +config RISCV_ISA_VENDOR_EXT_THEAD + bool "T-Head vendor extension support" + select RISCV_ISA_VENDOR_EXT + default y + help + Say N here if you want to disable all T-Head vendor extension + support. This will cause any T-Head vendor extensions that are + requested to be ignored. + + If you don't know what to do here, say Y. +endmenu + +endmenu diff --git a/arch/riscv/include/asm/cpufeature.h b/arch/riscv/include/asm/cpufeature.h index 0c4f08577015..fedd479ccfd1 100644 --- a/arch/riscv/include/asm/cpufeature.h +++ b/arch/riscv/include/asm/cpufeature.h @@ -35,6 +35,24 @@ extern u32 riscv_vlenb_of; void riscv_user_isa_enable(void); +#define _RISCV_ISA_EXT_DATA(_name, _id, _subset_exts, _subset_exts_size) { \ + .name = #_name, \ + .property = #_name, \ + .id = _id, \ + .subset_ext_ids = _subset_exts, \ + .subset_ext_size = _subset_exts_size \ +} + +#define __RISCV_ISA_EXT_DATA(_name, _id) _RISCV_ISA_EXT_DATA(_name, _id, NULL, 0) + +/* Used to declare pure "lasso" extension (Zk for instance) */ +#define __RISCV_ISA_EXT_BUNDLE(_name, _bundled_exts) \ + _RISCV_ISA_EXT_DATA(_name, RISCV_ISA_EXT_INVALID, _bundled_exts, ARRAY_SIZE(_bundled_exts)) + +/* Used to declare extensions that are a superset of other extensions (Zvbb for instance) */ +#define __RISCV_ISA_EXT_SUPERSET(_name, _id, _sub_exts) \ + _RISCV_ISA_EXT_DATA(_name, _id, _sub_exts, ARRAY_SIZE(_sub_exts)) + #if defined(CONFIG_RISCV_MISALIGNED) bool check_unaligned_access_emulated_all_cpus(void); void unaligned_emulation_finish(void); diff --git a/arch/riscv/include/asm/vendor_extensions.h b/arch/riscv/include/asm/vendor_extensions.h new file mode 100644 index 000000000000..0af1ddd0af70 --- /dev/null +++ b/arch/riscv/include/asm/vendor_extensions.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright 2024 Rivos, Inc + */ + +#ifndef _ASM_VENDOR_EXTENSIONS_H +#define _ASM_VENDOR_EXTENSIONS_H + +#include <asm/cpufeature.h> + +#include <linux/array_size.h> +#include <linux/types.h> + +struct riscv_isa_vendor_ext_data_list { + const struct riscv_isa_ext_data *ext_data; + struct riscv_isainfo *per_hart_vendor_bitmap; + unsigned long *vendor_bitmap; + const size_t ext_data_count; + const size_t bitmap_size; +}; + +extern const struct riscv_isa_vendor_ext_data_list *riscv_isa_vendor_ext_list[]; + +extern const size_t riscv_isa_vendor_ext_list_size; + +#endif /* _ASM_VENDOR_EXTENSIONS_H */ diff --git a/arch/riscv/include/asm/vendor_extensions/thead.h b/arch/riscv/include/asm/vendor_extensions/thead.h new file mode 100644 index 000000000000..92eec729888d --- /dev/null +++ b/arch/riscv/include/asm/vendor_extensions/thead.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_RISCV_VENDOR_EXTENSIONS_THEAD_H +#define _ASM_RISCV_VENDOR_EXTENSIONS_THEAD_H + +#include <asm/vendor_extensions.h> + +#include <linux/types.h> + +#define RISCV_ISA_VENDOR_EXT_XTHEADVECTOR 0 + +/* + * Extension keys should be strictly less than max. + * It is safe to increment this when necessary. + */ +#define RISCV_ISA_VENDOR_EXT_MAX_THEAD 32 + +extern const struct riscv_isa_vendor_ext_data_list riscv_isa_vendor_ext_list_thead; + +#endif diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile index 81d94a8ee10f..53361c50fb46 100644 --- a/arch/riscv/kernel/Makefile +++ b/arch/riscv/kernel/Makefile @@ -58,6 +58,8 @@ obj-y += riscv_ksyms.o obj-y += stacktrace.o obj-y += cacheinfo.o obj-y += patch.o +obj-y += vendor_extensions.o +obj-y += vendor_extensions/ obj-y += probes/ obj-y += tests/ obj-$(CONFIG_MMU) += vdso.o vdso/ diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c index 8158f34c3e36..c073494519eb 100644 --- a/arch/riscv/kernel/cpufeature.c +++ b/arch/riscv/kernel/cpufeature.c @@ -24,6 +24,7 @@ #include <asm/processor.h> #include <asm/sbi.h> #include <asm/vector.h> +#include <asm/vendor_extensions.h> #define NUM_ALPHA_EXTS ('z' - 'a' + 1) @@ -102,24 +103,6 @@ static bool riscv_isa_extension_check(int id) return true; } -#define _RISCV_ISA_EXT_DATA(_name, _id, _subset_exts, _subset_exts_size) { \ - .name = #_name, \ - .property = #_name, \ - .id = _id, \ - .subset_ext_ids = _subset_exts, \ - .subset_ext_size = _subset_exts_size \ -} - -#define __RISCV_ISA_EXT_DATA(_name, _id) _RISCV_ISA_EXT_DATA(_name, _id, NULL, 0) - -/* Used to declare pure "lasso" extension (Zk for instance) */ -#define __RISCV_ISA_EXT_BUNDLE(_name, _bundled_exts) \ - _RISCV_ISA_EXT_DATA(_name, RISCV_ISA_EXT_INVALID, _bundled_exts, ARRAY_SIZE(_bundled_exts)) - -/* Used to declare extensions that are a superset of other extensions (Zvbb for instance) */ -#define __RISCV_ISA_EXT_SUPERSET(_name, _id, _sub_exts) \ - _RISCV_ISA_EXT_DATA(_name, _id, _sub_exts, ARRAY_SIZE(_sub_exts)) - static const unsigned int riscv_zk_bundled_exts[] = { RISCV_ISA_EXT_ZBKB, RISCV_ISA_EXT_ZBKC, @@ -353,6 +336,10 @@ static void __init riscv_parse_isa_string(unsigned long *this_hwcap, struct risc bool ext_long = false, ext_err = false; switch (*ext) { + case 'x': + case 'X': + pr_warn_once("Vendor extensions are ignored in riscv,isa. Use riscv,isa-extensions instead."); + continue; case 's': /* * Workaround for invalid single-letter 's' & 'u' (QEMU). @@ -368,8 +355,6 @@ static void __init riscv_parse_isa_string(unsigned long *this_hwcap, struct risc } fallthrough; case 'S': - case 'x': - case 'X': case 'z': case 'Z': /* @@ -572,6 +557,54 @@ static void __init riscv_fill_hwcap_from_isa_string(unsigned long *isa2hwcap) acpi_put_table((struct acpi_table_header *)rhct); } +static void __init riscv_fill_cpu_vendor_ext(struct device_node *cpu_node, int cpu) +{ + if (!IS_ENABLED(CONFIG_RISCV_ISA_VENDOR_EXT)) + return; + + for (int i = 0; i < riscv_isa_vendor_ext_list_size; i++) { + const struct riscv_isa_vendor_ext_data_list *ext_list = riscv_isa_vendor_ext_list[i]; + + for (int j = 0; j < ext_list->ext_data_count; j++) { + const struct riscv_isa_ext_data ext = ext_list->ext_data[j]; + struct riscv_isainfo *isavendorinfo = &ext_list->per_hart_vendor_bitmap[cpu]; + + if (of_property_match_string(cpu_node, "riscv,isa-extensions", + ext.property) < 0) + continue; + + /* + * Assume that subset extensions are all members of the + * same vendor. + */ + if (ext.subset_ext_size) + for (int k = 0; k < ext.subset_ext_size; k++) + set_bit(ext.subset_ext_ids[k], isavendorinfo->isa); + + set_bit(ext.id, isavendorinfo->isa); + } + } +} + +static void __init riscv_fill_vendor_ext_list(int cpu) +{ + if (!IS_ENABLED(CONFIG_RISCV_ISA_VENDOR_EXT)) + return; + + for (int i = 0; i < riscv_isa_vendor_ext_list_size; i++) { + const struct riscv_isa_vendor_ext_data_list *ext_list = riscv_isa_vendor_ext_list[i]; + + if (bitmap_empty(ext_list->vendor_bitmap, ext_list->bitmap_size)) + bitmap_copy(ext_list->vendor_bitmap, + ext_list->per_hart_vendor_bitmap[cpu].isa, + ext_list->bitmap_size); + else + bitmap_and(ext_list->vendor_bitmap, ext_list->vendor_bitmap, + ext_list->per_hart_vendor_bitmap[cpu].isa, + ext_list->bitmap_size); + } +} + static int __init riscv_fill_hwcap_from_ext_list(unsigned long *isa2hwcap) { unsigned int cpu; @@ -615,6 +648,8 @@ static int __init riscv_fill_hwcap_from_ext_list(unsigned long *isa2hwcap) } } + riscv_fill_cpu_vendor_ext(cpu_node, cpu); + of_node_put(cpu_node); /* @@ -630,6 +665,8 @@ static int __init riscv_fill_hwcap_from_ext_list(unsigned long *isa2hwcap) bitmap_copy(riscv_isa, isainfo->isa, RISCV_ISA_EXT_MAX); else bitmap_and(riscv_isa, riscv_isa, isainfo->isa, RISCV_ISA_EXT_MAX); + + riscv_fill_vendor_ext_list(cpu); } if (bitmap_empty(riscv_isa, RISCV_ISA_EXT_MAX)) diff --git a/arch/riscv/kernel/vendor_extensions.c b/arch/riscv/kernel/vendor_extensions.c new file mode 100644 index 000000000000..f76cb3013c2d --- /dev/null +++ b/arch/riscv/kernel/vendor_extensions.c @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2024 Rivos, Inc + */ + +#include <asm/vendor_extensions.h> +#include <asm/vendor_extensions/thead.h> + +#include <linux/array_size.h> +#include <linux/types.h> + +const struct riscv_isa_vendor_ext_data_list *riscv_isa_vendor_ext_list[] = { +#ifdef CONFIG_RISCV_ISA_VENDOR_EXT_THEAD + &riscv_isa_vendor_ext_list_thead, +#endif +}; + +const size_t riscv_isa_vendor_ext_list_size = ARRAY_SIZE(riscv_isa_vendor_ext_list); diff --git a/arch/riscv/kernel/vendor_extensions/Makefile b/arch/riscv/kernel/vendor_extensions/Makefile new file mode 100644 index 000000000000..3383066baaab --- /dev/null +++ b/arch/riscv/kernel/vendor_extensions/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_RISCV_ISA_VENDOR_EXT_THEAD) += thead.o diff --git a/arch/riscv/kernel/vendor_extensions/thead.c b/arch/riscv/kernel/vendor_extensions/thead.c new file mode 100644 index 000000000000..edb20b928c0c --- /dev/null +++ b/arch/riscv/kernel/vendor_extensions/thead.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <asm/cpufeature.h> +#include <asm/vendor_extensions.h> +#include <asm/vendor_extensions/thead.h> + +#include <linux/array_size.h> +#include <linux/types.h> + +/* All T-Head vendor extensions supported in Linux */ +const struct riscv_isa_ext_data riscv_isa_vendor_ext_thead[] = { + __RISCV_ISA_EXT_DATA(xtheadvector, RISCV_ISA_VENDOR_EXT_XTHEADVECTOR), +}; + +/* + * The first member of this struct must be a bitmap named isa so it can be + * compatible with riscv_isainfo even though the sizes of the bitmaps may be + * different. + */ +struct riscv_isavendorinfo_thead { + DECLARE_BITMAP(isa, RISCV_ISA_VENDOR_EXT_MAX_THEAD); +}; + +/* Hart specific T-Head vendor extension support */ +static struct riscv_isavendorinfo_thead hart_vendorinfo_thead[NR_CPUS]; + +/* Set of T-Head vendor extensions supported on all harts */ +DECLARE_BITMAP(vendorinfo_thead, RISCV_ISA_VENDOR_EXT_MAX_THEAD); + +const struct riscv_isa_vendor_ext_data_list riscv_isa_vendor_ext_list_thead = { + .ext_data = riscv_isa_vendor_ext_thead, + .per_hart_vendor_bitmap = (struct riscv_isainfo *)hart_vendorinfo_thead, + .vendor_bitmap = vendorinfo_thead, + .ext_data_count = ARRAY_SIZE(riscv_isa_vendor_ext_thead), + .bitmap_size = RISCV_ISA_VENDOR_EXT_MAX_THEAD +};
Separate vendor extensions out into one struct per vendor instead of adding vendor extensions onto riscv_isa_ext. Add a hidden config RISCV_ISA_VENDOR_EXT to conditionally include this code. The xtheadvector vendor extension is added using these changes. Signed-off-by: Charlie Jenkins <charlie@rivosinc.com> --- arch/riscv/Kconfig | 2 + arch/riscv/Kconfig.vendor | 19 ++++++ arch/riscv/include/asm/cpufeature.h | 18 ++++++ arch/riscv/include/asm/vendor_extensions.h | 26 ++++++++ arch/riscv/include/asm/vendor_extensions/thead.h | 19 ++++++ arch/riscv/kernel/Makefile | 2 + arch/riscv/kernel/cpufeature.c | 77 ++++++++++++++++++------ arch/riscv/kernel/vendor_extensions.c | 18 ++++++ arch/riscv/kernel/vendor_extensions/Makefile | 3 + arch/riscv/kernel/vendor_extensions/thead.c | 36 +++++++++++ 10 files changed, 200 insertions(+), 20 deletions(-)