This patch series ended up much larger than expected, please bear with
me! The goal here is to support vendor extensions, starting at probing
the device tree and ending with reporting to userspace.
The main design objective was to allow vendors to operate independently
of each other. This has been achieved by delegating vendor extensions to
a their own files and then accumulating the extensions in
arch/riscv/kernel/vendor_extensions.c.
Each vendor will have their own list of extensions they support.
There is a new hwprobe key RISCV_HWPROBE_KEY_VENDOR_EXT_THEAD_0 that is
used to request which thead vendor extensions are supported on the
current platform. This allows future vendors to allocate hwprobe keys
for their vendor.
On to the xtheadvector specific code. xtheadvector is a custom extension
that is based upon riscv vector version 0.7.1 [1]. All of the vector
routines have been modified to support this alternative vector version
based upon whether xtheadvector was determined to be supported at boot.
I have tested this with an Allwinner Nezha board. I ran into issues
booting the board on 6.9-rc1 so I applied these patches to 6.8. There
are a couple of minor merge conflicts that do arrise when doing that, so
please let me know if you have been able to boot this board with a 6.9
kernel. I used SkiffOS [2] to manage building the image, but upgraded
the U-Boot version to Samuel Holland's more up-to-date version [3] and
changed out the device tree used by U-Boot with the device trees that
are present in upstream linux and this series. Thank you Samuel for all
of the work you did to make this task possible.
To test the integration, I used the riscv vector kselftests. I modified
the test cases to be able to more easily extend them, and then added a
xtheadvector target that works by calling hwprobe and swapping out the
vector asm if needed.
[1] https://github.com/T-head-Semi/thead-extension-spec/blob/95358cb2cca9489361c61d335e03d3134b14133f/xtheadvector.adoc
[2] https://github.com/skiffos/SkiffOS/tree/master/configs/allwinner/nezha
[3] https://github.com/smaeul/u-boot/commit/2e89b706f5c956a70c989cd31665f1429e9a0b48
Signed-off-by: Charlie Jenkins <[email protected]>
---
Changes in v4:
- Disable vector immediately if vlenb from the device tree is not
homogeneous
- Hide vendor extension code behind a hidden config that vendor
extensions select to eliminate the code when kernel is compiled
without vendor extensions
- Clear up naming conventions and introduce some defines to make the
vendor extension code clearer
- Link to v3: https://lore.kernel.org/r/20240420-dev-charlie-support_thead_vector_6_9-v3-0-67cff4271d1d@rivosinc.com
Changes in v3:
- Allow any hardware to support any vendor extension, rather than
restricting the vendor extensions to the same vendor as the hardware
- Introduce config options to enable/disable a vendor's extensions
- Link to v2: https://lore.kernel.org/r/20240415-dev-charlie-support_thead_vector_6_9-v2-0-c7d68c603268@rivosinc.com
Changes in v2:
- Added commit hash to xtheadvector
- Simplified riscv,isa vector removal fix to not mess with the DT
riscv,vendorid
- Moved riscv,vendorid parsing into a different patch and cache the
value to be used by alternative patching
- Reduce riscv,vendorid missing severity to "info"
- Separate vendor extension list to vendor files
- xtheadvector no longer puts v in the elf_hwcap
- Only patch vendor extension if all harts are associated with the same
vendor. This is the best chance the kernel has for working properly if
there are multiple vendors.
- Split hwprobe vendor keys out into vendor file
- Add attribution for Heiko's patches
- Link to v1: https://lore.kernel.org/r/20240411-dev-charlie-support_thead_vector_6_9-v1-0-4af9815ec746@rivosinc.com
---
Charlie Jenkins (14):
dt-bindings: riscv: Add xtheadvector ISA extension description
riscv: vector: Use vlenb from DT
riscv: dts: allwinner: Add xtheadvector to the D1/D1s devicetree
riscv: Extend cpufeature.c to detect vendor extensions
riscv: Introduce vendor variants of extension helpers
riscv: cpufeature: Extract common elements from extension checking
riscv: Convert xandespmu to use the vendor extension framework
riscv: csr: Add CSR encodings for VCSR_VXRM/VCSR_VXSAT
riscv: Add xtheadvector instruction definitions
riscv: vector: Support xtheadvector save/restore
riscv: hwprobe: Add thead vendor extension probing
riscv: hwprobe: Document thead vendor extensions and xtheadvector extension
selftests: riscv: Fix vector tests
selftests: riscv: Support xtheadvector in vector tests
Conor Dooley (1):
dt-bindings: riscv: cpus: add a vlen register length property
Heiko Stuebner (1):
RISC-V: define the elements of the VCSR vector CSR
Documentation/arch/riscv/hwprobe.rst | 10 +
Documentation/devicetree/bindings/riscv/cpus.yaml | 6 +
.../devicetree/bindings/riscv/extensions.yaml | 10 +
arch/riscv/Kconfig | 2 +
arch/riscv/Kconfig.vendor | 44 +++
arch/riscv/boot/dts/allwinner/sun20i-d1s.dtsi | 3 +-
arch/riscv/errata/andes/errata.c | 2 +
arch/riscv/errata/sifive/errata.c | 3 +
arch/riscv/errata/thead/errata.c | 3 +
arch/riscv/include/asm/cpufeature.h | 98 ++++---
arch/riscv/include/asm/csr.h | 13 +
arch/riscv/include/asm/hwcap.h | 1 -
arch/riscv/include/asm/hwprobe.h | 4 +-
arch/riscv/include/asm/switch_to.h | 2 +-
arch/riscv/include/asm/vector.h | 247 +++++++++++++----
arch/riscv/include/asm/vendor_extensions.h | 95 +++++++
arch/riscv/include/asm/vendor_extensions/andes.h | 19 ++
arch/riscv/include/asm/vendor_extensions/thead.h | 45 ++++
.../include/asm/vendor_extensions/thead_hwprobe.h | 11 +
arch/riscv/include/uapi/asm/hwprobe.h | 3 +-
arch/riscv/include/uapi/asm/vendor/thead.h | 3 +
arch/riscv/kernel/Makefile | 2 +
arch/riscv/kernel/cpufeature.c | 155 ++++++++---
arch/riscv/kernel/kernel_mode_vector.c | 8 +-
arch/riscv/kernel/process.c | 4 +-
arch/riscv/kernel/signal.c | 6 +-
arch/riscv/kernel/sys_hwprobe.c | 9 +
arch/riscv/kernel/vector.c | 25 +-
arch/riscv/kernel/vendor_extensions.c | 69 +++++
arch/riscv/kernel/vendor_extensions/Makefile | 5 +
arch/riscv/kernel/vendor_extensions/andes.c | 35 +++
arch/riscv/kernel/vendor_extensions/thead.c | 36 +++
.../riscv/kernel/vendor_extensions/thead_hwprobe.c | 42 +++
drivers/perf/riscv_pmu_sbi.c | 9 +-
tools/testing/selftests/riscv/vector/.gitignore | 3 +-
tools/testing/selftests/riscv/vector/Makefile | 17 +-
.../selftests/riscv/vector/v_exec_initval_nolibc.c | 93 +++++++
tools/testing/selftests/riscv/vector/v_helpers.c | 67 +++++
tools/testing/selftests/riscv/vector/v_helpers.h | 7 +
tools/testing/selftests/riscv/vector/v_initval.c | 22 ++
.../selftests/riscv/vector/v_initval_nolibc.c | 68 -----
.../selftests/riscv/vector/vstate_exec_nolibc.c | 20 +-
.../testing/selftests/riscv/vector/vstate_prctl.c | 295 ++++++++++++---------
43 files changed, 1283 insertions(+), 338 deletions(-)
---
base-commit: 4cece764965020c22cff7665b18a012006359095
change-id: 20240411-dev-charlie-support_thead_vector_6_9-1591fc2a431d
--
- Charlie
The xtheadvector ISA extension is described on the T-Head extension spec
Github page [1] at commit 95358cb2cca9.
Link: https://github.com/T-head-Semi/thead-extension-spec/blob/95358cb2cca9489361c61d335e03d3134b14133f/xtheadvector.adoc [1]
Signed-off-by: Charlie Jenkins <[email protected]>
Reviewed-by: Conor Dooley <[email protected]>
---
Documentation/devicetree/bindings/riscv/extensions.yaml | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/Documentation/devicetree/bindings/riscv/extensions.yaml b/Documentation/devicetree/bindings/riscv/extensions.yaml
index 468c646247aa..99d2a9e8c52d 100644
--- a/Documentation/devicetree/bindings/riscv/extensions.yaml
+++ b/Documentation/devicetree/bindings/riscv/extensions.yaml
@@ -477,6 +477,10 @@ properties:
latency, as ratified in commit 56ed795 ("Update
riscv-crypto-spec-vector.adoc") of riscv-crypto.
+ # vendor extensions, each extension sorted alphanumerically under the
+ # vendor they belong to. Vendors are sorted alphanumerically as well.
+
+ # Andes
- const: xandespmu
description:
The Andes Technology performance monitor extension for counter overflow
@@ -484,5 +488,11 @@ properties:
Registers in the AX45MP datasheet.
https://www.andestech.com/wp-content/uploads/AX45MP-1C-Rev.-5.0.0-Datasheet.pdf
+ # T-HEAD
+ - const: xtheadvector
+ description:
+ The T-HEAD specific 0.7.1 vector implementation as written in
+ https://github.com/T-head-Semi/thead-extension-spec/blob/95358cb2cca9489361c61d335e03d3134b14133f/xtheadvector.adoc.
+
additionalProperties: true
...
--
2.44.0
From: Conor Dooley <[email protected]>
Add a property analogous to the vlenb CSR so that software can detect
the vector length of each CPU prior to it being brought online.
Currently software has to assume that the vector length read from the
boot CPU applies to all possible CPUs. On T-Head CPUs implementing
pre-ratification vector, reading the th.vlenb CSR may produce an illegal
instruction trap, so this property is required on such systems.
Signed-off-by: Conor Dooley <[email protected]>
Signed-off-by: Charlie Jenkins <[email protected]>
---
Documentation/devicetree/bindings/riscv/cpus.yaml | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/Documentation/devicetree/bindings/riscv/cpus.yaml b/Documentation/devicetree/bindings/riscv/cpus.yaml
index d87dd50f1a4b..edcb6a7d9319 100644
--- a/Documentation/devicetree/bindings/riscv/cpus.yaml
+++ b/Documentation/devicetree/bindings/riscv/cpus.yaml
@@ -94,6 +94,12 @@ properties:
description:
The blocksize in bytes for the Zicboz cache operations.
+ riscv,vlenb:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ VLEN/8, the vector register length in bytes. This property is required in
+ systems where the vector register length is not identical on all harts.
+
# RISC-V has multiple properties for cache op block sizes as the sizes
# differ between individual CBO extensions
cache-op-block-size: false
--
2.44.0
If vlenb is provided in the device tree, prefer that over reading the
vlenb csr.
Signed-off-by: Charlie Jenkins <[email protected]>
---
arch/riscv/include/asm/cpufeature.h | 2 ++
arch/riscv/kernel/cpufeature.c | 43 +++++++++++++++++++++++++++++++++++++
arch/riscv/kernel/vector.c | 12 ++++++++++-
3 files changed, 56 insertions(+), 1 deletion(-)
diff --git a/arch/riscv/include/asm/cpufeature.h b/arch/riscv/include/asm/cpufeature.h
index 347805446151..0c4f08577015 100644
--- a/arch/riscv/include/asm/cpufeature.h
+++ b/arch/riscv/include/asm/cpufeature.h
@@ -31,6 +31,8 @@ DECLARE_PER_CPU(struct riscv_cpuinfo, riscv_cpuinfo);
/* Per-cpu ISA extensions. */
extern struct riscv_isainfo hart_isa[NR_CPUS];
+extern u32 riscv_vlenb_of;
+
void riscv_user_isa_enable(void);
#if defined(CONFIG_RISCV_MISALIGNED)
diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
index 3ed2359eae35..8158f34c3e36 100644
--- a/arch/riscv/kernel/cpufeature.c
+++ b/arch/riscv/kernel/cpufeature.c
@@ -35,6 +35,8 @@ static DECLARE_BITMAP(riscv_isa, RISCV_ISA_EXT_MAX) __read_mostly;
/* Per-cpu ISA extensions. */
struct riscv_isainfo hart_isa[NR_CPUS];
+u32 riscv_vlenb_of;
+
/**
* riscv_isa_extension_base() - Get base extension word
*
@@ -648,6 +650,42 @@ static int __init riscv_isa_fallback_setup(char *__unused)
early_param("riscv_isa_fallback", riscv_isa_fallback_setup);
#endif
+static int riscv_homogeneous_vlenb(void)
+{
+ int cpu;
+ u32 prev_vlenb = 0;
+ u32 vlenb;
+
+ for_each_possible_cpu(cpu) {
+ struct device_node *cpu_node;
+
+ cpu_node = of_cpu_device_node_get(cpu);
+ if (!cpu_node) {
+ pr_warn("Unable to find cpu node\n");
+ continue;
+ }
+
+ if (of_property_read_u32(cpu_node, "riscv,vlenb", &vlenb)) {
+ of_node_put(cpu_node);
+
+ if (prev_vlenb)
+ return -1;
+ continue;
+ }
+
+ if (prev_vlenb && vlenb != prev_vlenb) {
+ of_node_put(cpu_node);
+ return -1;
+ }
+
+ prev_vlenb = vlenb;
+ of_node_put(cpu_node);
+ }
+
+ riscv_vlenb_of = vlenb;
+ return 0;
+}
+
void __init riscv_fill_hwcap(void)
{
char print_str[NUM_ALPHA_EXTS + 1];
@@ -671,6 +709,11 @@ void __init riscv_fill_hwcap(void)
pr_info("Falling back to deprecated \"riscv,isa\"\n");
riscv_fill_hwcap_from_isa_string(isa2hwcap);
}
+
+ if (riscv_homogeneous_vlenb() < 0) {
+ pr_warn("RISCV_ISA_V only supports one vlenb on SMP systems. Please ensure that the riscv,vlenb devicetree property is the same across all CPUs. Either all CPUs must have the riscv,vlenb property, or none. If no CPUs in the devicetree use riscv,vlenb then vlenb will be probed from the vlenb CSR. Disabling vector.\n");
+ elf_hwcap &= ~COMPAT_HWCAP_ISA_V;
+ }
}
/*
diff --git a/arch/riscv/kernel/vector.c b/arch/riscv/kernel/vector.c
index 6727d1d3b8f2..e04586cdb7f0 100644
--- a/arch/riscv/kernel/vector.c
+++ b/arch/riscv/kernel/vector.c
@@ -33,7 +33,17 @@ int riscv_v_setup_vsize(void)
{
unsigned long this_vsize;
- /* There are 32 vector registers with vlenb length. */
+ /*
+ * There are 32 vector registers with vlenb length.
+ *
+ * If the riscv,vlenb property was provided by the firmware, use that
+ * instead of probing the CSRs.
+ */
+ if (riscv_vlenb_of) {
+ this_vsize = riscv_vlenb_of * 32;
+ return 0;
+ }
+
riscv_v_enable();
this_vsize = csr_read(CSR_VLENB) * 32;
riscv_v_disable();
--
2.44.0
The D1/D1s SoCs support xtheadvector so it can be included in the
devicetree. Also include vlenb for the cpu.
Signed-off-by: Charlie Jenkins <[email protected]>
---
arch/riscv/boot/dts/allwinner/sun20i-d1s.dtsi | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1s.dtsi b/arch/riscv/boot/dts/allwinner/sun20i-d1s.dtsi
index 64c3c2e6cbe0..50c9f4ec8a7f 100644
--- a/arch/riscv/boot/dts/allwinner/sun20i-d1s.dtsi
+++ b/arch/riscv/boot/dts/allwinner/sun20i-d1s.dtsi
@@ -27,7 +27,8 @@ cpu0: cpu@0 {
riscv,isa = "rv64imafdc";
riscv,isa-base = "rv64i";
riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "zicntr", "zicsr",
- "zifencei", "zihpm";
+ "zifencei", "zihpm", "xtheadvector";
+ riscv,vlenb = <128>;
#cooling-cells = <2>;
cpu0_intc: interrupt-controller {
--
2.44.0
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 <[email protected]>
---
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;
+ 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
+};
--
2.44.0
The __riscv_has_extension_likely() and __riscv_has_extension_unlikely()
functions from the vendor_extensions.h can be used to simplify the
standard extension checking code as well. Migrate those functions to
cpufeature.h and reorganize the code in the file to use the functions.
Signed-off-by: Charlie Jenkins <[email protected]>
---
arch/riscv/include/asm/cpufeature.h | 78 +++++++++++++++++-------------
arch/riscv/include/asm/vendor_extensions.h | 28 -----------
2 files changed, 44 insertions(+), 62 deletions(-)
diff --git a/arch/riscv/include/asm/cpufeature.h b/arch/riscv/include/asm/cpufeature.h
index fedd479ccfd1..17896ec9ec11 100644
--- a/arch/riscv/include/asm/cpufeature.h
+++ b/arch/riscv/include/asm/cpufeature.h
@@ -98,59 +98,66 @@ extern bool riscv_isa_fallback;
unsigned long riscv_isa_extension_base(const unsigned long *isa_bitmap);
+#define EXT_ALL_VENDORS 0
+
bool __riscv_isa_extension_available(const unsigned long *isa_bitmap, unsigned int bit);
#define riscv_isa_extension_available(isa_bitmap, ext) \
__riscv_isa_extension_available(isa_bitmap, RISCV_ISA_EXT_##ext)
-static __always_inline bool
-riscv_has_extension_likely(const unsigned long ext)
+static __always_inline bool __riscv_has_extension_likely(const unsigned long vendor,
+ const unsigned long ext)
{
- compiletime_assert(ext < RISCV_ISA_EXT_MAX,
- "ext must be < RISCV_ISA_EXT_MAX");
-
- if (IS_ENABLED(CONFIG_RISCV_ALTERNATIVE)) {
- asm goto(
- ALTERNATIVE("j %l[l_no]", "nop", 0, %[ext], 1)
- :
- : [ext] "i" (ext)
- :
- : l_no);
- } else {
- if (!__riscv_isa_extension_available(NULL, ext))
- goto l_no;
- }
+ asm goto(ALTERNATIVE("j %l[l_no]", "nop", %[vendor], %[ext], 1)
+ :
+ : [vendor] "i" (vendor), [ext] "i" (ext)
+ :
+ : l_no);
return true;
l_no:
return false;
}
-static __always_inline bool
-riscv_has_extension_unlikely(const unsigned long ext)
+static __always_inline bool __riscv_has_extension_unlikely(const unsigned long vendor,
+ const unsigned long ext)
{
- compiletime_assert(ext < RISCV_ISA_EXT_MAX,
- "ext must be < RISCV_ISA_EXT_MAX");
-
- if (IS_ENABLED(CONFIG_RISCV_ALTERNATIVE)) {
- asm goto(
- ALTERNATIVE("nop", "j %l[l_yes]", 0, %[ext], 1)
- :
- : [ext] "i" (ext)
- :
- : l_yes);
- } else {
- if (__riscv_isa_extension_available(NULL, ext))
- goto l_yes;
- }
+ asm goto(ALTERNATIVE("nop", "j %l[l_yes]", %[vendor], %[ext], 1)
+ :
+ : [vendor] "i" (vendor), [ext] "i" (ext)
+ :
+ : l_yes);
return false;
l_yes:
return true;
}
+static __always_inline bool riscv_has_extension_unlikely(const unsigned long ext)
+{
+ compiletime_assert(ext < RISCV_ISA_EXT_MAX, "ext must be < RISCV_ISA_EXT_MAX");
+
+ if (IS_ENABLED(CONFIG_RISCV_ALTERNATIVE))
+ return __riscv_has_extension_unlikely(EXT_ALL_VENDORS, ext);
+
+ return __riscv_isa_extension_available(NULL, ext);
+}
+
+static __always_inline bool riscv_has_extension_likely(const unsigned long ext)
+{
+ compiletime_assert(ext < RISCV_ISA_EXT_MAX, "ext must be < RISCV_ISA_EXT_MAX");
+
+ if (IS_ENABLED(CONFIG_RISCV_ALTERNATIVE))
+ return __riscv_has_extension_likely(EXT_ALL_VENDORS, ext);
+
+ return __riscv_isa_extension_available(NULL, ext);
+}
+
static __always_inline bool riscv_cpu_has_extension_likely(int cpu, const unsigned long ext)
{
- if (IS_ENABLED(CONFIG_RISCV_ALTERNATIVE) && riscv_has_extension_likely(ext))
+ compiletime_assert(ext < RISCV_ISA_EXT_MAX, "ext must be < RISCV_ISA_EXT_MAX");
+
+ if (IS_ENABLED(CONFIG_RISCV_ALTERNATIVE) &&
+ __riscv_has_extension_likely(EXT_ALL_VENDORS, ext))
return true;
return __riscv_isa_extension_available(hart_isa[cpu].isa, ext);
@@ -158,7 +165,10 @@ static __always_inline bool riscv_cpu_has_extension_likely(int cpu, const unsign
static __always_inline bool riscv_cpu_has_extension_unlikely(int cpu, const unsigned long ext)
{
- if (IS_ENABLED(CONFIG_RISCV_ALTERNATIVE) && riscv_has_extension_unlikely(ext))
+ compiletime_assert(ext < RISCV_ISA_EXT_MAX, "ext must be < RISCV_ISA_EXT_MAX");
+
+ if (IS_ENABLED(CONFIG_RISCV_ALTERNATIVE) &&
+ __riscv_has_extension_unlikely(EXT_ALL_VENDORS, ext))
return true;
return __riscv_isa_extension_available(hart_isa[cpu].isa, ext);
diff --git a/arch/riscv/include/asm/vendor_extensions.h b/arch/riscv/include/asm/vendor_extensions.h
index 74b82433e0a2..880bb86ec890 100644
--- a/arch/riscv/include/asm/vendor_extensions.h
+++ b/arch/riscv/include/asm/vendor_extensions.h
@@ -39,34 +39,6 @@ bool __riscv_isa_vendor_extension_available(int cpu, unsigned long vendor, unsig
__riscv_isa_vendor_extension_available(VENDOR_EXT_ALL_CPUS, vendor, \
RISCV_ISA_VENDOR_EXT_##ext)
-static __always_inline bool __riscv_has_extension_likely(const unsigned long vendor,
- const unsigned long ext)
-{
- asm goto(ALTERNATIVE("j %l[l_no]", "nop", %[vendor], %[ext], 1)
- :
- : [vendor] "i" (vendor), [ext] "i" (ext)
- :
- : l_no);
-
- return true;
-l_no:
- return false;
-}
-
-static __always_inline bool __riscv_has_extension_unlikely(const unsigned long vendor,
- const unsigned long ext)
-{
- asm goto(ALTERNATIVE("nop", "j %l[l_yes]", %[vendor], %[ext], 1)
- :
- : [vendor] "i" (vendor), [ext] "i" (ext)
- :
- : l_yes);
-
- return false;
-l_yes:
- return true;
-}
-
static __always_inline bool riscv_has_vendor_extension_likely(const unsigned long vendor,
const unsigned long ext)
{
--
2.44.0
Vendor extensions are maintained in per-vendor structs (separate from
standard extensions which live in riscv_isa). Create vendor variants for
the existing extension helpers to interface with the riscv_isa_vendor
bitmaps.
Signed-off-by: Charlie Jenkins <[email protected]>
---
arch/riscv/errata/sifive/errata.c | 3 +
arch/riscv/errata/thead/errata.c | 3 +
arch/riscv/include/asm/vendor_extensions.h | 97 ++++++++++++++++++++++++++++++
arch/riscv/kernel/cpufeature.c | 32 +++++++---
arch/riscv/kernel/vendor_extensions.c | 40 ++++++++++++
5 files changed, 167 insertions(+), 8 deletions(-)
diff --git a/arch/riscv/errata/sifive/errata.c b/arch/riscv/errata/sifive/errata.c
index 3d9a32d791f7..b68b023115c2 100644
--- a/arch/riscv/errata/sifive/errata.c
+++ b/arch/riscv/errata/sifive/errata.c
@@ -12,6 +12,7 @@
#include <asm/alternative.h>
#include <asm/vendorid_list.h>
#include <asm/errata_list.h>
+#include <asm/vendor_extensions.h>
struct errata_info_t {
char name[32];
@@ -91,6 +92,8 @@ void sifive_errata_patch_func(struct alt_entry *begin, struct alt_entry *end,
u32 cpu_apply_errata = 0;
u32 tmp;
+ BUILD_BUG_ON(ERRATA_SIFIVE_NUMBER >= RISCV_VENDOR_EXT_ALTERNATIVES_BASE);
+
if (stage == RISCV_ALTERNATIVES_EARLY_BOOT)
return;
diff --git a/arch/riscv/errata/thead/errata.c b/arch/riscv/errata/thead/errata.c
index b1c410bbc1ae..6d5d7f8eebbc 100644
--- a/arch/riscv/errata/thead/errata.c
+++ b/arch/riscv/errata/thead/errata.c
@@ -18,6 +18,7 @@
#include <asm/io.h>
#include <asm/patch.h>
#include <asm/vendorid_list.h>
+#include <asm/vendor_extensions.h>
static bool errata_probe_pbmt(unsigned int stage,
unsigned long arch_id, unsigned long impid)
@@ -160,6 +161,8 @@ void thead_errata_patch_func(struct alt_entry *begin, struct alt_entry *end,
u32 tmp;
void *oldptr, *altptr;
+ BUILD_BUG_ON(ERRATA_THEAD_NUMBER >= RISCV_VENDOR_EXT_ALTERNATIVES_BASE);
+
for (alt = begin; alt < end; alt++) {
if (alt->vendor_id != THEAD_VENDOR_ID)
continue;
diff --git a/arch/riscv/include/asm/vendor_extensions.h b/arch/riscv/include/asm/vendor_extensions.h
index 0af1ddd0af70..74b82433e0a2 100644
--- a/arch/riscv/include/asm/vendor_extensions.h
+++ b/arch/riscv/include/asm/vendor_extensions.h
@@ -23,4 +23,101 @@ extern const struct riscv_isa_vendor_ext_data_list *riscv_isa_vendor_ext_list[];
extern const size_t riscv_isa_vendor_ext_list_size;
+/*
+ * The alternatives need some way of distinguishing between vendor extensions
+ * and errata. Incrementing all of the vendor extension keys so they are at
+ * least 0x8000 accomplishes that.
+ */
+#define RISCV_VENDOR_EXT_ALTERNATIVES_BASE 0x8000
+
+#define VENDOR_EXT_ALL_CPUS -1
+
+bool __riscv_isa_vendor_extension_available(int cpu, unsigned long vendor, unsigned int bit);
+#define riscv_cpu_isa_vendor_extension_available(cpu, vendor, ext) \
+ __riscv_isa_vendor_extension_available(cpu, vendor, RISCV_ISA_VENDOR_EXT_##ext)
+#define riscv_isa_vendor_extension_available(vendor, ext) \
+ __riscv_isa_vendor_extension_available(VENDOR_EXT_ALL_CPUS, vendor, \
+ RISCV_ISA_VENDOR_EXT_##ext)
+
+static __always_inline bool __riscv_has_extension_likely(const unsigned long vendor,
+ const unsigned long ext)
+{
+ asm goto(ALTERNATIVE("j %l[l_no]", "nop", %[vendor], %[ext], 1)
+ :
+ : [vendor] "i" (vendor), [ext] "i" (ext)
+ :
+ : l_no);
+
+ return true;
+l_no:
+ return false;
+}
+
+static __always_inline bool __riscv_has_extension_unlikely(const unsigned long vendor,
+ const unsigned long ext)
+{
+ asm goto(ALTERNATIVE("nop", "j %l[l_yes]", %[vendor], %[ext], 1)
+ :
+ : [vendor] "i" (vendor), [ext] "i" (ext)
+ :
+ : l_yes);
+
+ return false;
+l_yes:
+ return true;
+}
+
+static __always_inline bool riscv_has_vendor_extension_likely(const unsigned long vendor,
+ const unsigned long ext)
+{
+ if (!IS_ENABLED(CONFIG_RISCV_ISA_VENDOR_EXT))
+ return false;
+
+ if (IS_ENABLED(CONFIG_RISCV_ALTERNATIVE))
+ return __riscv_has_extension_likely(vendor,
+ ext + RISCV_VENDOR_EXT_ALTERNATIVES_BASE);
+
+ return __riscv_isa_vendor_extension_available(VENDOR_EXT_ALL_CPUS, vendor, ext);
+}
+
+static __always_inline bool riscv_has_vendor_extension_unlikely(const unsigned long vendor,
+ const unsigned long ext)
+{
+ if (!IS_ENABLED(CONFIG_RISCV_ISA_VENDOR_EXT))
+ return false;
+
+ if (IS_ENABLED(CONFIG_RISCV_ALTERNATIVE))
+ return __riscv_has_extension_unlikely(vendor,
+ ext + RISCV_VENDOR_EXT_ALTERNATIVES_BASE);
+
+ return __riscv_isa_vendor_extension_available(VENDOR_EXT_ALL_CPUS, vendor, ext);
+}
+
+static __always_inline bool riscv_cpu_has_vendor_extension_likely(const unsigned long vendor,
+ int cpu, const unsigned long ext)
+{
+ if (!IS_ENABLED(CONFIG_RISCV_ISA_VENDOR_EXT))
+ return false;
+
+ if (IS_ENABLED(CONFIG_RISCV_ALTERNATIVE) &&
+ __riscv_has_extension_likely(vendor, ext + RISCV_VENDOR_EXT_ALTERNATIVES_BASE))
+ return true;
+
+ return __riscv_isa_vendor_extension_available(cpu, vendor, ext);
+}
+
+static __always_inline bool riscv_cpu_has_vendor_extension_unlikely(const unsigned long vendor,
+ int cpu,
+ const unsigned long ext)
+{
+ if (!IS_ENABLED(CONFIG_RISCV_ISA_VENDOR_EXT))
+ return false;
+
+ if (IS_ENABLED(CONFIG_RISCV_ALTERNATIVE) &&
+ __riscv_has_extension_unlikely(vendor, ext + RISCV_VENDOR_EXT_ALTERNATIVES_BASE))
+ return true;
+
+ return __riscv_isa_vendor_extension_available(cpu, vendor, ext);
+}
+
#endif /* _ASM_VENDOR_EXTENSIONS_H */
diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
index c073494519eb..dd7e8e0c0af1 100644
--- a/arch/riscv/kernel/cpufeature.c
+++ b/arch/riscv/kernel/cpufeature.c
@@ -844,25 +844,41 @@ void __init_or_module riscv_cpufeature_patch_func(struct alt_entry *begin,
{
struct alt_entry *alt;
void *oldptr, *altptr;
- u16 id, value;
+ u16 id, value, vendor;
if (stage == RISCV_ALTERNATIVES_EARLY_BOOT)
return;
for (alt = begin; alt < end; alt++) {
- if (alt->vendor_id != 0)
- continue;
-
id = PATCH_ID_CPUFEATURE_ID(alt->patch_id);
+ vendor = PATCH_ID_CPUFEATURE_ID(alt->vendor_id);
- if (id >= RISCV_ISA_EXT_MAX) {
+ /*
+ * Any alternative with a patch_id that is less than
+ * RISCV_ISA_EXT_MAX is interpreted as a standard extension.
+ *
+ * Any alternative with patch_id that is greater than or equal
+ * to RISCV_VENDOR_EXT_ALTERNATIVES_BASE is interpreted as a
+ * vendor extension.
+ */
+ if (id < RISCV_ISA_EXT_MAX) {
+ /*
+ * This patch should be treated as errata so skip
+ * processing here.
+ */
+ if (alt->vendor_id != 0)
+ continue;
+
+ if (!__riscv_isa_extension_available(NULL, id))
+ continue;
+ } else if (id >= RISCV_VENDOR_EXT_ALTERNATIVES_BASE) {
+ if (!__riscv_isa_vendor_extension_available(VENDOR_EXT_ALL_CPUS, vendor, id))
+ continue;
+ } else {
WARN(1, "This extension id:%d is not in ISA extension list", id);
continue;
}
- if (!__riscv_isa_extension_available(NULL, id))
- continue;
-
value = PATCH_ID_CPUFEATURE_VALUE(alt->patch_id);
if (!riscv_cpufeature_patch_check(id, value))
continue;
diff --git a/arch/riscv/kernel/vendor_extensions.c b/arch/riscv/kernel/vendor_extensions.c
index f76cb3013c2d..eced93eec5a6 100644
--- a/arch/riscv/kernel/vendor_extensions.c
+++ b/arch/riscv/kernel/vendor_extensions.c
@@ -3,6 +3,7 @@
* Copyright 2024 Rivos, Inc
*/
+#include <asm/vendorid_list.h>
#include <asm/vendor_extensions.h>
#include <asm/vendor_extensions/thead.h>
@@ -16,3 +17,42 @@ const struct riscv_isa_vendor_ext_data_list *riscv_isa_vendor_ext_list[] = {
};
const size_t riscv_isa_vendor_ext_list_size = ARRAY_SIZE(riscv_isa_vendor_ext_list);
+
+/**
+ * __riscv_isa_vendor_extension_available() - Check whether given vendor
+ * extension is available or not.
+ *
+ * @cpu: check if extension is available on this cpu
+ * @vendor: vendor that the extension is a member of
+ * @bit: bit position of the desired extension
+ * Return: true or false
+ *
+ * NOTE: When cpu is -1, will check if extension is available on all cpus
+ */
+bool __riscv_isa_vendor_extension_available(int cpu, unsigned long vendor, unsigned int bit)
+{
+ unsigned long *bmap;
+ struct riscv_isainfo *cpu_bmap;
+ size_t bmap_size;
+
+ switch (vendor) {
+#ifdef CONFIG_RISCV_ISA_VENDOR_EXT_THEAD
+ case THEAD_VENDOR_ID:
+ bmap = riscv_isa_vendor_ext_list_thead.vendor_bitmap;
+ cpu_bmap = riscv_isa_vendor_ext_list_thead.per_hart_vendor_bitmap;
+ bmap_size = riscv_isa_vendor_ext_list_thead.bitmap_size;
+ break;
+#endif
+ default:
+ return false;
+ }
+
+ if (cpu != -1)
+ bmap = cpu_bmap[cpu].isa;
+
+ if (bit >= bmap_size)
+ return false;
+
+ return test_bit(bit, bmap) ? true : false;
+}
+EXPORT_SYMBOL_GPL(__riscv_isa_vendor_extension_available);
--
2.44.0
Migrate xandespmu out of riscv_isa_ext and into a new Andes-specific
vendor namespace.
Signed-off-by: Charlie Jenkins <[email protected]>
---
arch/riscv/Kconfig.vendor | 12 ++++++++
arch/riscv/errata/andes/errata.c | 2 ++
arch/riscv/include/asm/hwcap.h | 1 -
arch/riscv/include/asm/vendor_extensions/andes.h | 19 +++++++++++++
arch/riscv/kernel/cpufeature.c | 1 -
arch/riscv/kernel/vendor_extensions.c | 11 ++++++++
arch/riscv/kernel/vendor_extensions/Makefile | 1 +
arch/riscv/kernel/vendor_extensions/andes.c | 35 ++++++++++++++++++++++++
drivers/perf/riscv_pmu_sbi.c | 9 ++++--
9 files changed, 86 insertions(+), 5 deletions(-)
diff --git a/arch/riscv/Kconfig.vendor b/arch/riscv/Kconfig.vendor
index 4fc86810af1d..d47c0e7a250f 100644
--- a/arch/riscv/Kconfig.vendor
+++ b/arch/riscv/Kconfig.vendor
@@ -16,4 +16,16 @@ config RISCV_ISA_VENDOR_EXT_THEAD
If you don't know what to do here, say Y.
endmenu
+menu "Andes"
+config RISCV_ISA_VENDOR_EXT_ANDES
+ bool "Andes vendor extension support"
+ default y
+ help
+ Say N here if you want to disable all Andes vendor extension
+ support. This will cause any Andes vendor extensions that are
+ requested by hardware probing to be ignored.
+
+ If you don't know what to do here, say Y.
+endmenu
+
endmenu
diff --git a/arch/riscv/errata/andes/errata.c b/arch/riscv/errata/andes/errata.c
index f2708a9494a1..a5d96a7a4682 100644
--- a/arch/riscv/errata/andes/errata.c
+++ b/arch/riscv/errata/andes/errata.c
@@ -65,6 +65,8 @@ void __init_or_module andes_errata_patch_func(struct alt_entry *begin, struct al
unsigned long archid, unsigned long impid,
unsigned int stage)
{
+ BUILD_BUG_ON(ERRATA_ANDES_NUMBER >= RISCV_VENDOR_EXT_ALTERNATIVES_BASE);
+
if (stage == RISCV_ALTERNATIVES_BOOT)
errata_probe_iocp(stage, archid, impid);
diff --git a/arch/riscv/include/asm/hwcap.h b/arch/riscv/include/asm/hwcap.h
index e17d0078a651..1f2d2599c655 100644
--- a/arch/riscv/include/asm/hwcap.h
+++ b/arch/riscv/include/asm/hwcap.h
@@ -80,7 +80,6 @@
#define RISCV_ISA_EXT_ZFA 71
#define RISCV_ISA_EXT_ZTSO 72
#define RISCV_ISA_EXT_ZACAS 73
-#define RISCV_ISA_EXT_XANDESPMU 74
#define RISCV_ISA_EXT_XLINUXENVCFG 127
diff --git a/arch/riscv/include/asm/vendor_extensions/andes.h b/arch/riscv/include/asm/vendor_extensions/andes.h
new file mode 100644
index 000000000000..54b0ad1a409d
--- /dev/null
+++ b/arch/riscv/include/asm/vendor_extensions/andes.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_RISCV_VENDOR_EXTENSIONS_ANDES_H
+#define _ASM_RISCV_VENDOR_EXTENSIONS_ANDES_H
+
+#include <asm/vendor_extensions.h>
+
+#include <linux/types.h>
+
+#define RISCV_ISA_VENDOR_EXT_XANDESPMU 0
+
+/*
+ * Extension keys should be strictly less than max.
+ * It is safe to increment this when necessary.
+ */
+#define RISCV_ISA_VENDOR_EXT_MAX_ANDES 32
+
+extern const struct riscv_isa_vendor_ext_data_list riscv_isa_vendor_ext_list_andes;
+
+#endif
diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
index dd7e8e0c0af1..0e2d77775e6b 100644
--- a/arch/riscv/kernel/cpufeature.c
+++ b/arch/riscv/kernel/cpufeature.c
@@ -289,7 +289,6 @@ const struct riscv_isa_ext_data riscv_isa_ext[] = {
__RISCV_ISA_EXT_DATA(svinval, RISCV_ISA_EXT_SVINVAL),
__RISCV_ISA_EXT_DATA(svnapot, RISCV_ISA_EXT_SVNAPOT),
__RISCV_ISA_EXT_DATA(svpbmt, RISCV_ISA_EXT_SVPBMT),
- __RISCV_ISA_EXT_DATA(xandespmu, RISCV_ISA_EXT_XANDESPMU),
};
const size_t riscv_isa_ext_count = ARRAY_SIZE(riscv_isa_ext);
diff --git a/arch/riscv/kernel/vendor_extensions.c b/arch/riscv/kernel/vendor_extensions.c
index eced93eec5a6..e31f16b8cec9 100644
--- a/arch/riscv/kernel/vendor_extensions.c
+++ b/arch/riscv/kernel/vendor_extensions.c
@@ -5,6 +5,7 @@
#include <asm/vendorid_list.h>
#include <asm/vendor_extensions.h>
+#include <asm/vendor_extensions/andes.h>
#include <asm/vendor_extensions/thead.h>
#include <linux/array_size.h>
@@ -14,6 +15,9 @@ 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
+#ifdef CONFIG_RISCV_ISA_VENDOR_EXT_ANDES
+ &riscv_isa_vendor_ext_list_andes,
+#endif
};
const size_t riscv_isa_vendor_ext_list_size = ARRAY_SIZE(riscv_isa_vendor_ext_list);
@@ -42,6 +46,13 @@ bool __riscv_isa_vendor_extension_available(int cpu, unsigned long vendor, unsig
cpu_bmap = riscv_isa_vendor_ext_list_thead.per_hart_vendor_bitmap;
bmap_size = riscv_isa_vendor_ext_list_thead.bitmap_size;
break;
+#endif
+#ifdef CONFIG_RISCV_VENDOR_EXT_ANDES
+ case ANDES_VENDOR_ID:
+ bmap = riscv_isa_vendor_ext_list_andes.vendor_bitmap;
+ cpu_bmap = riscv_isa_vendor_ext_list_andes.per_hart_vendor_bitmap;
+ bmap_size = riscv_isa_vendor_ext_list_andes.bitmap_size;
+ break;
#endif
default:
return false;
diff --git a/arch/riscv/kernel/vendor_extensions/Makefile b/arch/riscv/kernel/vendor_extensions/Makefile
index 3383066baaab..8f1c5a4dc38f 100644
--- a/arch/riscv/kernel/vendor_extensions/Makefile
+++ b/arch/riscv/kernel/vendor_extensions/Makefile
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_RISCV_ISA_VENDOR_EXT_THEAD) += thead.o
+obj-$(CONFIG_RISCV_ISA_VENDOR_EXT_ANDES) += andes.o
diff --git a/arch/riscv/kernel/vendor_extensions/andes.c b/arch/riscv/kernel/vendor_extensions/andes.c
new file mode 100644
index 000000000000..8451106d2e07
--- /dev/null
+++ b/arch/riscv/kernel/vendor_extensions/andes.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <asm/cpufeature.h>
+#include <asm/vendor_extensions.h>
+#include <asm/vendor_extensions/andes.h>
+
+#include <linux/array_size.h>
+#include <linux/types.h>
+
+const struct riscv_isa_ext_data riscv_isa_vendor_ext_andes[] = {
+ __RISCV_ISA_EXT_DATA(xandespmu, RISCV_ISA_VENDOR_EXT_XANDESPMU),
+};
+
+/*
+ * 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_andes {
+ DECLARE_BITMAP(isa, RISCV_ISA_VENDOR_EXT_MAX_ANDES);
+};
+
+/* Hart specific T-Head vendor extension support */
+static struct riscv_isavendorinfo_andes hart_vendorinfo_andes[NR_CPUS];
+
+/* Set of T-Head vendor extensions supported on all harts */
+DECLARE_BITMAP(vendorinfo_andes, RISCV_ISA_VENDOR_EXT_MAX_ANDES);
+
+const struct riscv_isa_vendor_ext_data_list riscv_isa_vendor_ext_list_andes = {
+ .ext_data = riscv_isa_vendor_ext_andes,
+ .per_hart_vendor_bitmap = (struct riscv_isainfo *)hart_vendorinfo_andes,
+ .vendor_bitmap = vendorinfo_andes,
+ .ext_data_count = ARRAY_SIZE(riscv_isa_vendor_ext_andes),
+ .bitmap_size = RISCV_ISA_VENDOR_EXT_MAX_ANDES
+};
diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
index 8cbe6e5f9c39..d39b01372fa5 100644
--- a/drivers/perf/riscv_pmu_sbi.c
+++ b/drivers/perf/riscv_pmu_sbi.c
@@ -24,6 +24,8 @@
#include <asm/errata_list.h>
#include <asm/sbi.h>
#include <asm/cpufeature.h>
+#include <asm/vendorid_list.h>
+#include <asm/vendor_extensions/andes.h>
#define ALT_SBI_PMU_OVERFLOW(__ovl) \
asm volatile(ALTERNATIVE_2( \
@@ -32,7 +34,7 @@ asm volatile(ALTERNATIVE_2( \
THEAD_VENDOR_ID, ERRATA_THEAD_PMU, \
CONFIG_ERRATA_THEAD_PMU, \
"csrr %0, " __stringify(ANDES_CSR_SCOUNTEROF), \
- 0, RISCV_ISA_EXT_XANDESPMU, \
+ ANDES_VENDOR_ID, RISCV_ISA_VENDOR_EXT_XANDESPMU, \
CONFIG_ANDES_CUSTOM_PMU) \
: "=r" (__ovl) : \
: "memory")
@@ -41,7 +43,7 @@ asm volatile(ALTERNATIVE_2( \
asm volatile(ALTERNATIVE( \
"csrc " __stringify(CSR_IP) ", %0\n\t", \
"csrc " __stringify(ANDES_CSR_SLIP) ", %0\n\t", \
- 0, RISCV_ISA_EXT_XANDESPMU, \
+ ANDES_VENDOR_ID, RISCV_ISA_VENDOR_EXT_XANDESPMU, \
CONFIG_ANDES_CUSTOM_PMU) \
: : "r"(__irq_mask) \
: "memory")
@@ -837,7 +839,8 @@ static int pmu_sbi_setup_irqs(struct riscv_pmu *pmu, struct platform_device *pde
riscv_cached_mimpid(0) == 0) {
riscv_pmu_irq_num = THEAD_C9XX_RV_IRQ_PMU;
riscv_pmu_use_irq = true;
- } else if (riscv_isa_extension_available(NULL, XANDESPMU) &&
+ } else if (riscv_has_vendor_extension_unlikely(ANDES_VENDOR_ID,
+ RISCV_ISA_VENDOR_EXT_XANDESPMU) &&
IS_ENABLED(CONFIG_ANDES_CUSTOM_PMU)) {
riscv_pmu_irq_num = ANDES_SLI_CAUSE_BASE + ANDES_RV_IRQ_PMOVI;
riscv_pmu_use_irq = true;
--
2.44.0
On Fri, Apr 26, 2024 at 02:29:17PM -0700, Charlie Jenkins wrote:
> If vlenb is provided in the device tree, prefer that over reading the
> vlenb csr.
>
> Signed-off-by: Charlie Jenkins <[email protected]>
> ---
> arch/riscv/include/asm/cpufeature.h | 2 ++
> arch/riscv/kernel/cpufeature.c | 43 +++++++++++++++++++++++++++++++++++++
> arch/riscv/kernel/vector.c | 12 ++++++++++-
> 3 files changed, 56 insertions(+), 1 deletion(-)
>
> diff --git a/arch/riscv/include/asm/cpufeature.h b/arch/riscv/include/asm/cpufeature.h
> index 347805446151..0c4f08577015 100644
> --- a/arch/riscv/include/asm/cpufeature.h
> +++ b/arch/riscv/include/asm/cpufeature.h
> @@ -31,6 +31,8 @@ DECLARE_PER_CPU(struct riscv_cpuinfo, riscv_cpuinfo);
> /* Per-cpu ISA extensions. */
> extern struct riscv_isainfo hart_isa[NR_CPUS];
>
> +extern u32 riscv_vlenb_of;
> +
> void riscv_user_isa_enable(void);
>
> #if defined(CONFIG_RISCV_MISALIGNED)
> diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
> index 3ed2359eae35..8158f34c3e36 100644
> --- a/arch/riscv/kernel/cpufeature.c
> +++ b/arch/riscv/kernel/cpufeature.c
> @@ -35,6 +35,8 @@ static DECLARE_BITMAP(riscv_isa, RISCV_ISA_EXT_MAX) __read_mostly;
> /* Per-cpu ISA extensions. */
> struct riscv_isainfo hart_isa[NR_CPUS];
>
> +u32 riscv_vlenb_of;
> +
> /**
> * riscv_isa_extension_base() - Get base extension word
> *
> @@ -648,6 +650,42 @@ static int __init riscv_isa_fallback_setup(char *__unused)
> early_param("riscv_isa_fallback", riscv_isa_fallback_setup);
> #endif
>
> +static int riscv_homogeneous_vlenb(void)
Without a verb, this function name is rather odd.
> +{
> + int cpu;
> + u32 prev_vlenb = 0;
> + u32 vlenb;
> +
> + for_each_possible_cpu(cpu) {
> + struct device_node *cpu_node;
> +
> + cpu_node = of_cpu_device_node_get(cpu);
> + if (!cpu_node) {
> + pr_warn("Unable to find cpu node\n");
> + continue;
Hmm, if we fail to find the cpu node, then shouldn't we be returning an
error?
> + }
> +
> + if (of_property_read_u32(cpu_node, "riscv,vlenb", &vlenb)) {
> + of_node_put(cpu_node);
> +
> + if (prev_vlenb)
> + return -1;
Can you return an errno here and below please?
> + continue;
> + }
> +
> + if (prev_vlenb && vlenb != prev_vlenb) {
> + of_node_put(cpu_node);
> + return -1;
> + }
> +
> + prev_vlenb = vlenb;
> + of_node_put(cpu_node);
> + }
> +
> + riscv_vlenb_of = vlenb;
> + return 0;
> +}
> +
> void __init riscv_fill_hwcap(void)
> {
> char print_str[NUM_ALPHA_EXTS + 1];
> @@ -671,6 +709,11 @@ void __init riscv_fill_hwcap(void)
> pr_info("Falling back to deprecated \"riscv,isa\"\n");
> riscv_fill_hwcap_from_isa_string(isa2hwcap);
> }
> +
> + if (riscv_homogeneous_vlenb() < 0) {
> + pr_warn("RISCV_ISA_V only supports one vlenb on SMP systems. Please ensure that the riscv,vlenb devicetree property is the same across all CPUs. Either all CPUs must have the riscv,vlenb property, or none. If no CPUs in the devicetree use riscv,vlenb then vlenb will be probed from the vlenb CSR. Disabling vector.\n");
Oh dear, that's a bit unwieldy... I think you could get away with a far
more basic message - and you should be able to break this over lines,
adjacent string literals should get concatenated.
I'd probably say something like "unsupported heterogeneous vlen detected,
vector extension disabled", however we should actually check that the
vector extension has been detected on all CPUs and that kernel support
for vector is enabled before emitting a warning for this.
Cheers,
Conor.
> + elf_hwcap &= ~COMPAT_HWCAP_ISA_V;
> + }
> }
>
> /*
> diff --git a/arch/riscv/kernel/vector.c b/arch/riscv/kernel/vector.c
> index 6727d1d3b8f2..e04586cdb7f0 100644
> --- a/arch/riscv/kernel/vector.c
> +++ b/arch/riscv/kernel/vector.c
> @@ -33,7 +33,17 @@ int riscv_v_setup_vsize(void)
> {
> unsigned long this_vsize;
>
> - /* There are 32 vector registers with vlenb length. */
> + /*
> + * There are 32 vector registers with vlenb length.
> + *
> + * If the riscv,vlenb property was provided by the firmware, use that
> + * instead of probing the CSRs.
> + */
> + if (riscv_vlenb_of) {
> + this_vsize = riscv_vlenb_of * 32;
> + return 0;
> + }
> +
> riscv_v_enable();
> this_vsize = csr_read(CSR_VLENB) * 32;
> riscv_v_disable();
>
> --
> 2.44.0
>
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 <[email protected]>
> ---
> 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:20PM -0700, Charlie Jenkins wrote:
> index c073494519eb..dd7e8e0c0af1 100644
> --- a/arch/riscv/kernel/cpufeature.c
> +++ b/arch/riscv/kernel/cpufeature.c
> @@ -844,25 +844,41 @@ void __init_or_module riscv_cpufeature_patch_func(struct alt_entry *begin,
> {
> struct alt_entry *alt;
> void *oldptr, *altptr;
> - u16 id, value;
> + u16 id, value, vendor;
>
> if (stage == RISCV_ALTERNATIVES_EARLY_BOOT)
> return;
>
> for (alt = begin; alt < end; alt++) {
> - if (alt->vendor_id != 0)
> - continue;
> -
> id = PATCH_ID_CPUFEATURE_ID(alt->patch_id);
> + vendor = PATCH_ID_CPUFEATURE_ID(alt->vendor_id);
>
> - if (id >= RISCV_ISA_EXT_MAX) {
> + /*
> + * Any alternative with a patch_id that is less than
> + * RISCV_ISA_EXT_MAX is interpreted as a standard extension.
> + *
> + * Any alternative with patch_id that is greater than or equal
> + * to RISCV_VENDOR_EXT_ALTERNATIVES_BASE is interpreted as a
> + * vendor extension.
I think this stuff is all fine, since we can always re-jig things in the
future if needs be.
> + */
> + if (id < RISCV_ISA_EXT_MAX) {
> + /*
> + * This patch should be treated as errata so skip
> + * processing here.
> + */
> + if (alt->vendor_id != 0)
> + continue;
> +
> + if (!__riscv_isa_extension_available(NULL, id))
> + continue;
> + } else if (id >= RISCV_VENDOR_EXT_ALTERNATIVES_BASE) {
> + if (!__riscv_isa_vendor_extension_available(VENDOR_EXT_ALL_CPUS, vendor, id))
> + continue;
> + } else {
> WARN(1, "This extension id:%d is not in ISA extension list", id);
> continue;
> }
>
> - if (!__riscv_isa_extension_available(NULL, id))
> - continue;
> -
> value = PATCH_ID_CPUFEATURE_VALUE(alt->patch_id);
> if (!riscv_cpufeature_patch_check(id, value))
> continue;
> diff --git a/arch/riscv/kernel/vendor_extensions.c b/arch/riscv/kernel/vendor_extensions.c
> index f76cb3013c2d..eced93eec5a6 100644
> --- a/arch/riscv/kernel/vendor_extensions.c
> +++ b/arch/riscv/kernel/vendor_extensions.c
> @@ -3,6 +3,7 @@
> * Copyright 2024 Rivos, Inc
> */
>
> +#include <asm/vendorid_list.h>
> #include <asm/vendor_extensions.h>
> #include <asm/vendor_extensions/thead.h>
>
> @@ -16,3 +17,42 @@ const struct riscv_isa_vendor_ext_data_list *riscv_isa_vendor_ext_list[] = {
> };
>
> const size_t riscv_isa_vendor_ext_list_size = ARRAY_SIZE(riscv_isa_vendor_ext_list);
> +
> +/**
> + * __riscv_isa_vendor_extension_available() - Check whether given vendor
> + * extension is available or not.
> + *
> + * @cpu: check if extension is available on this cpu
> + * @vendor: vendor that the extension is a member of
> + * @bit: bit position of the desired extension
> + * Return: true or false
> + *
> + * NOTE: When cpu is -1, will check if extension is available on all cpus
> + */
> +bool __riscv_isa_vendor_extension_available(int cpu, unsigned long vendor, unsigned int bit)
> +{
> + unsigned long *bmap;
> + struct riscv_isainfo *cpu_bmap;
> + size_t bmap_size;
> +
> + switch (vendor) {
> +#ifdef CONFIG_RISCV_ISA_VENDOR_EXT_THEAD
> + case THEAD_VENDOR_ID:
> + bmap = riscv_isa_vendor_ext_list_thead.vendor_bitmap;
> + cpu_bmap = riscv_isa_vendor_ext_list_thead.per_hart_vendor_bitmap;
> + bmap_size = riscv_isa_vendor_ext_list_thead.bitmap_size;
> + break;
> +#endif
> + default:
> + return false;
> + }
> +
> + if (cpu != -1)
> + bmap = cpu_bmap[cpu].isa;
> +
> + if (bit >= bmap_size)
> + return false;
> +
> + return test_bit(bit, bmap) ? true : false;
> +}
> +EXPORT_SYMBOL_GPL(__riscv_isa_vendor_extension_available);
I wonder if we care to implement a non __ prefixed version of this, like
the standard stuff? The only __ version users of the standard one are in
kvm and core arch code, the "external" users all use the non-prefixed
version.
In any case,
Reviewed-by: Conor Dooley <[email protected]>
Cheers,
Conor.
On Fri, Apr 26, 2024 at 02:29:21PM -0700, Charlie Jenkins wrote:
> The __riscv_has_extension_likely() and __riscv_has_extension_unlikely()
> functions from the vendor_extensions.h can be used to simplify the
> standard extension checking code as well. Migrate those functions to
> cpufeature.h and reorganize the code in the file to use the functions.
>
> Signed-off-by: Charlie Jenkins <[email protected]>
> ---
> arch/riscv/include/asm/cpufeature.h | 78 +++++++++++++++++-------------
> arch/riscv/include/asm/vendor_extensions.h | 28 -----------
> 2 files changed, 44 insertions(+), 62 deletions(-)
>
> diff --git a/arch/riscv/include/asm/cpufeature.h b/arch/riscv/include/asm/cpufeature.h
> index fedd479ccfd1..17896ec9ec11 100644
> --- a/arch/riscv/include/asm/cpufeature.h
> +++ b/arch/riscv/include/asm/cpufeature.h
> @@ -98,59 +98,66 @@ extern bool riscv_isa_fallback;
>
> unsigned long riscv_isa_extension_base(const unsigned long *isa_bitmap);
>
> +#define EXT_ALL_VENDORS 0
It's not really "all vendors", it's standard. Otherwise, this seems all
grand to me,
Reviewed-by: Conor Dooley <[email protected]>
Cheers,
Conor.
On Fri, Apr 26, 2024 at 02:29:22PM -0700, Charlie Jenkins wrote:
> Migrate xandespmu out of riscv_isa_ext and into a new Andes-specific
> vendor namespace.
Reviewed-by: Conor Dooley <[email protected]>
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 <[email protected]>
> ---
> 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 <[email protected]> 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 <[email protected]>
> ---
> 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:31:45AM +0100, Conor Dooley wrote:
> On Fri, Apr 26, 2024 at 02:29:17PM -0700, Charlie Jenkins wrote:
> > If vlenb is provided in the device tree, prefer that over reading the
> > vlenb csr.
> >
> > Signed-off-by: Charlie Jenkins <[email protected]>
> > ---
> > arch/riscv/include/asm/cpufeature.h | 2 ++
> > arch/riscv/kernel/cpufeature.c | 43 +++++++++++++++++++++++++++++++++++++
> > arch/riscv/kernel/vector.c | 12 ++++++++++-
> > 3 files changed, 56 insertions(+), 1 deletion(-)
> >
> > diff --git a/arch/riscv/include/asm/cpufeature.h b/arch/riscv/include/asm/cpufeature.h
> > index 347805446151..0c4f08577015 100644
> > --- a/arch/riscv/include/asm/cpufeature.h
> > +++ b/arch/riscv/include/asm/cpufeature.h
> > @@ -31,6 +31,8 @@ DECLARE_PER_CPU(struct riscv_cpuinfo, riscv_cpuinfo);
> > /* Per-cpu ISA extensions. */
> > extern struct riscv_isainfo hart_isa[NR_CPUS];
> >
> > +extern u32 riscv_vlenb_of;
> > +
> > void riscv_user_isa_enable(void);
> >
> > #if defined(CONFIG_RISCV_MISALIGNED)
> > diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
> > index 3ed2359eae35..8158f34c3e36 100644
> > --- a/arch/riscv/kernel/cpufeature.c
> > +++ b/arch/riscv/kernel/cpufeature.c
> > @@ -35,6 +35,8 @@ static DECLARE_BITMAP(riscv_isa, RISCV_ISA_EXT_MAX) __read_mostly;
> > /* Per-cpu ISA extensions. */
> > struct riscv_isainfo hart_isa[NR_CPUS];
> >
> > +u32 riscv_vlenb_of;
> > +
> > /**
> > * riscv_isa_extension_base() - Get base extension word
> > *
> > @@ -648,6 +650,42 @@ static int __init riscv_isa_fallback_setup(char *__unused)
> > early_param("riscv_isa_fallback", riscv_isa_fallback_setup);
> > #endif
> >
> > +static int riscv_homogeneous_vlenb(void)
>
> Without a verb, this function name is rather odd.
>
Maybe has_riscv_homogeneous_vlenb() is better.
> > +{
> > + int cpu;
> > + u32 prev_vlenb = 0;
> > + u32 vlenb;
> > +
> > + for_each_possible_cpu(cpu) {
> > + struct device_node *cpu_node;
> > +
> > + cpu_node = of_cpu_device_node_get(cpu);
> > + if (!cpu_node) {
> > + pr_warn("Unable to find cpu node\n");
> > + continue;
>
> Hmm, if we fail to find the cpu node, then shouldn't we be returning an
> error?
Yes, I will change that.
>
> > + }
> > +
> > + if (of_property_read_u32(cpu_node, "riscv,vlenb", &vlenb)) {
> > + of_node_put(cpu_node);
> > +
> > + if (prev_vlenb)
> > + return -1;
>
> Can you return an errno here and below please?
>
Sounds good.
> > + continue;
> > + }
> > +
> > + if (prev_vlenb && vlenb != prev_vlenb) {
> > + of_node_put(cpu_node);
> > + return -1;
> > + }
> > +
> > + prev_vlenb = vlenb;
> > + of_node_put(cpu_node);
> > + }
> > +
> > + riscv_vlenb_of = vlenb;
> > + return 0;
> > +}
> > +
> > void __init riscv_fill_hwcap(void)
> > {
> > char print_str[NUM_ALPHA_EXTS + 1];
> > @@ -671,6 +709,11 @@ void __init riscv_fill_hwcap(void)
> > pr_info("Falling back to deprecated \"riscv,isa\"\n");
> > riscv_fill_hwcap_from_isa_string(isa2hwcap);
> > }
> > +
> > + if (riscv_homogeneous_vlenb() < 0) {
> > + pr_warn("RISCV_ISA_V only supports one vlenb on SMP systems. Please ensure that the riscv,vlenb devicetree property is the same across all CPUs. Either all CPUs must have the riscv,vlenb property, or none. If no CPUs in the devicetree use riscv,vlenb then vlenb will be probed from the vlenb CSR. Disabling vector.\n");
>
> Oh dear, that's a bit unwieldy... I think you could get away with a far
> more basic message - and you should be able to break this over lines,
> adjacent string literals should get concatenated.
> I'd probably say something like "unsupported heterogeneous vlen detected,
> vector extension disabled", however we should actually check that the
> vector extension has been detected on all CPUs and that kernel support
> for vector is enabled before emitting a warning for this.
Haha yeah I wanted to provide as much information as possible, but I
will shorten it.
I can add an if-statement to only run this code if check if V is
contained in elf_hwcap.
- Charlie
>
> Cheers,
> Conor.
>
> > + elf_hwcap &= ~COMPAT_HWCAP_ISA_V;
> > + }
> > }
> >
> > /*
> > diff --git a/arch/riscv/kernel/vector.c b/arch/riscv/kernel/vector.c
> > index 6727d1d3b8f2..e04586cdb7f0 100644
> > --- a/arch/riscv/kernel/vector.c
> > +++ b/arch/riscv/kernel/vector.c
> > @@ -33,7 +33,17 @@ int riscv_v_setup_vsize(void)
> > {
> > unsigned long this_vsize;
> >
> > - /* There are 32 vector registers with vlenb length. */
> > + /*
> > + * There are 32 vector registers with vlenb length.
> > + *
> > + * If the riscv,vlenb property was provided by the firmware, use that
> > + * instead of probing the CSRs.
> > + */
> > + if (riscv_vlenb_of) {
> > + this_vsize = riscv_vlenb_of * 32;
> > + return 0;
> > + }
> > +
> > riscv_v_enable();
> > this_vsize = csr_read(CSR_VLENB) * 32;
> > riscv_v_disable();
> >
> > --
> > 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 <[email protected]>
> > ---
> > 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 <[email protected]>
> > ---
> > 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 <[email protected]>
> > > ---
> > > 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 <[email protected]> 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 <[email protected]> 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 <[email protected]>
> > ---
> > 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]>
> > > ---
> > > 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 <[email protected]> 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 <[email protected]> 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 12:29:56PM +0100, Conor Dooley wrote:
> On Fri, Apr 26, 2024 at 02:29:20PM -0700, Charlie Jenkins wrote:
>
> > index c073494519eb..dd7e8e0c0af1 100644
> > --- a/arch/riscv/kernel/cpufeature.c
> > +++ b/arch/riscv/kernel/cpufeature.c
> > @@ -844,25 +844,41 @@ void __init_or_module riscv_cpufeature_patch_func(struct alt_entry *begin,
> > {
> > struct alt_entry *alt;
> > void *oldptr, *altptr;
> > - u16 id, value;
> > + u16 id, value, vendor;
> >
> > if (stage == RISCV_ALTERNATIVES_EARLY_BOOT)
> > return;
> >
> > for (alt = begin; alt < end; alt++) {
> > - if (alt->vendor_id != 0)
> > - continue;
> > -
> > id = PATCH_ID_CPUFEATURE_ID(alt->patch_id);
> > + vendor = PATCH_ID_CPUFEATURE_ID(alt->vendor_id);
> >
> > - if (id >= RISCV_ISA_EXT_MAX) {
> > + /*
> > + * Any alternative with a patch_id that is less than
> > + * RISCV_ISA_EXT_MAX is interpreted as a standard extension.
> > + *
> > + * Any alternative with patch_id that is greater than or equal
> > + * to RISCV_VENDOR_EXT_ALTERNATIVES_BASE is interpreted as a
> > + * vendor extension.
>
> I think this stuff is all fine, since we can always re-jig things in the
> future if needs be.
>
> > + */
> > + if (id < RISCV_ISA_EXT_MAX) {
> > + /*
> > + * This patch should be treated as errata so skip
> > + * processing here.
> > + */
> > + if (alt->vendor_id != 0)
> > + continue;
> > +
> > + if (!__riscv_isa_extension_available(NULL, id))
> > + continue;
> > + } else if (id >= RISCV_VENDOR_EXT_ALTERNATIVES_BASE) {
> > + if (!__riscv_isa_vendor_extension_available(VENDOR_EXT_ALL_CPUS, vendor, id))
> > + continue;
> > + } else {
> > WARN(1, "This extension id:%d is not in ISA extension list", id);
> > continue;
> > }
> >
> > - if (!__riscv_isa_extension_available(NULL, id))
> > - continue;
> > -
> > value = PATCH_ID_CPUFEATURE_VALUE(alt->patch_id);
> > if (!riscv_cpufeature_patch_check(id, value))
> > continue;
> > diff --git a/arch/riscv/kernel/vendor_extensions.c b/arch/riscv/kernel/vendor_extensions.c
> > index f76cb3013c2d..eced93eec5a6 100644
> > --- a/arch/riscv/kernel/vendor_extensions.c
> > +++ b/arch/riscv/kernel/vendor_extensions.c
> > @@ -3,6 +3,7 @@
> > * Copyright 2024 Rivos, Inc
> > */
> >
> > +#include <asm/vendorid_list.h>
> > #include <asm/vendor_extensions.h>
> > #include <asm/vendor_extensions/thead.h>
> >
> > @@ -16,3 +17,42 @@ const struct riscv_isa_vendor_ext_data_list *riscv_isa_vendor_ext_list[] = {
> > };
> >
> > const size_t riscv_isa_vendor_ext_list_size = ARRAY_SIZE(riscv_isa_vendor_ext_list);
> > +
> > +/**
> > + * __riscv_isa_vendor_extension_available() - Check whether given vendor
> > + * extension is available or not.
> > + *
> > + * @cpu: check if extension is available on this cpu
> > + * @vendor: vendor that the extension is a member of
> > + * @bit: bit position of the desired extension
> > + * Return: true or false
> > + *
> > + * NOTE: When cpu is -1, will check if extension is available on all cpus
> > + */
> > +bool __riscv_isa_vendor_extension_available(int cpu, unsigned long vendor, unsigned int bit)
> > +{
> > + unsigned long *bmap;
> > + struct riscv_isainfo *cpu_bmap;
> > + size_t bmap_size;
> > +
> > + switch (vendor) {
> > +#ifdef CONFIG_RISCV_ISA_VENDOR_EXT_THEAD
> > + case THEAD_VENDOR_ID:
> > + bmap = riscv_isa_vendor_ext_list_thead.vendor_bitmap;
> > + cpu_bmap = riscv_isa_vendor_ext_list_thead.per_hart_vendor_bitmap;
> > + bmap_size = riscv_isa_vendor_ext_list_thead.bitmap_size;
> > + break;
> > +#endif
> > + default:
> > + return false;
> > + }
> > +
> > + if (cpu != -1)
> > + bmap = cpu_bmap[cpu].isa;
> > +
> > + if (bit >= bmap_size)
> > + return false;
> > +
> > + return test_bit(bit, bmap) ? true : false;
> > +}
> > +EXPORT_SYMBOL_GPL(__riscv_isa_vendor_extension_available);
>
> I wonder if we care to implement a non __ prefixed version of this, like
> the standard stuff? The only __ version users of the standard one are in
> kvm and core arch code, the "external" users all use the non-prefixed
> version.
In vendor_extensions.h there is:
#define riscv_isa_vendor_extension_available(vendor, ext) \
__riscv_isa_vendor_extension_available(VENDOR_EXT_ALL_CPUS, vendor, \
RISCV_ISA_VENDOR_EXT_##ext)
>
> In any case,
> Reviewed-by: Conor Dooley <[email protected]>
Thanks!
- Charlie
>
> Cheers,
> Conor.
On Wed, May 01, 2024 at 12:37:14PM +0100, Conor Dooley wrote:
> On Fri, Apr 26, 2024 at 02:29:21PM -0700, Charlie Jenkins wrote:
> > The __riscv_has_extension_likely() and __riscv_has_extension_unlikely()
> > functions from the vendor_extensions.h can be used to simplify the
> > standard extension checking code as well. Migrate those functions to
> > cpufeature.h and reorganize the code in the file to use the functions.
> >
> > Signed-off-by: Charlie Jenkins <[email protected]>
> > ---
> > arch/riscv/include/asm/cpufeature.h | 78 +++++++++++++++++-------------
> > arch/riscv/include/asm/vendor_extensions.h | 28 -----------
> > 2 files changed, 44 insertions(+), 62 deletions(-)
> >
> > diff --git a/arch/riscv/include/asm/cpufeature.h b/arch/riscv/include/asm/cpufeature.h
> > index fedd479ccfd1..17896ec9ec11 100644
> > --- a/arch/riscv/include/asm/cpufeature.h
> > +++ b/arch/riscv/include/asm/cpufeature.h
> > @@ -98,59 +98,66 @@ extern bool riscv_isa_fallback;
> >
> > unsigned long riscv_isa_extension_base(const unsigned long *isa_bitmap);
> >
> > +#define EXT_ALL_VENDORS 0
>
> It's not really "all vendors", it's standard. Otherwise, this seems all
This hooks up into the alternatives:
ALTERNATIVE("nop", "j %l[l_yes]", %[vendor], %[ext], 1)
Where the "vendor" argument is supposed to be 0 if the alternative is
applicable to all vendors. Is there a better way to convey this?
- Charlie
> grand to me,
> Reviewed-by: Conor Dooley <[email protected]>
>
> Cheers,
> Conor.
On Wed, May 01, 2024 at 12:48:13PM -0700, Charlie Jenkins wrote:
> On Wed, May 01, 2024 at 12:37:14PM +0100, Conor Dooley wrote:
> > On Fri, Apr 26, 2024 at 02:29:21PM -0700, Charlie Jenkins wrote:
> > > The __riscv_has_extension_likely() and __riscv_has_extension_unlikely()
> > > functions from the vendor_extensions.h can be used to simplify the
> > > standard extension checking code as well. Migrate those functions to
> > > cpufeature.h and reorganize the code in the file to use the functions.
> > >
> > > Signed-off-by: Charlie Jenkins <[email protected]>
> > > ---
> > > arch/riscv/include/asm/cpufeature.h | 78 +++++++++++++++++-------------
> > > arch/riscv/include/asm/vendor_extensions.h | 28 -----------
> > > 2 files changed, 44 insertions(+), 62 deletions(-)
> > >
> > > diff --git a/arch/riscv/include/asm/cpufeature.h b/arch/riscv/include/asm/cpufeature.h
> > > index fedd479ccfd1..17896ec9ec11 100644
> > > --- a/arch/riscv/include/asm/cpufeature.h
> > > +++ b/arch/riscv/include/asm/cpufeature.h
> > > @@ -98,59 +98,66 @@ extern bool riscv_isa_fallback;
> > >
> > > unsigned long riscv_isa_extension_base(const unsigned long *isa_bitmap);
> > >
> > > +#define EXT_ALL_VENDORS 0
> >
> > It's not really "all vendors", it's standard. Otherwise, this seems all
>
> This hooks up into the alternatives:
>
> ALTERNATIVE("nop", "j %l[l_yes]", %[vendor], %[ext], 1)
Yeah, I know what you're using it for, I just find the naming odd.
>
> Where the "vendor" argument is supposed to be 0 if the alternative is
> applicable to all vendors. Is there a better way to convey this?
s/EXT_ALL_VENDORS/STANDARD_EXT/?
On Wed, May 01, 2024 at 09:15:44PM +0100, Conor Dooley wrote:
> On Wed, May 01, 2024 at 12:48:13PM -0700, Charlie Jenkins wrote:
> > On Wed, May 01, 2024 at 12:37:14PM +0100, Conor Dooley wrote:
> > > On Fri, Apr 26, 2024 at 02:29:21PM -0700, Charlie Jenkins wrote:
> > > > The __riscv_has_extension_likely() and __riscv_has_extension_unlikely()
> > > > functions from the vendor_extensions.h can be used to simplify the
> > > > standard extension checking code as well. Migrate those functions to
> > > > cpufeature.h and reorganize the code in the file to use the functions.
> > > >
> > > > Signed-off-by: Charlie Jenkins <[email protected]>
> > > > ---
> > > > arch/riscv/include/asm/cpufeature.h | 78 +++++++++++++++++-------------
> > > > arch/riscv/include/asm/vendor_extensions.h | 28 -----------
> > > > 2 files changed, 44 insertions(+), 62 deletions(-)
> > > >
> > > > diff --git a/arch/riscv/include/asm/cpufeature.h b/arch/riscv/include/asm/cpufeature.h
> > > > index fedd479ccfd1..17896ec9ec11 100644
> > > > --- a/arch/riscv/include/asm/cpufeature.h
> > > > +++ b/arch/riscv/include/asm/cpufeature.h
> > > > @@ -98,59 +98,66 @@ extern bool riscv_isa_fallback;
> > > >
> > > > unsigned long riscv_isa_extension_base(const unsigned long *isa_bitmap);
> > > >
> > > > +#define EXT_ALL_VENDORS 0
> > >
> > > It's not really "all vendors", it's standard. Otherwise, this seems all
> >
> > This hooks up into the alternatives:
> >
> > ALTERNATIVE("nop", "j %l[l_yes]", %[vendor], %[ext], 1)
>
> Yeah, I know what you're using it for, I just find the naming odd.
> >
> > Where the "vendor" argument is supposed to be 0 if the alternative is
> > applicable to all vendors. Is there a better way to convey this?
>
> s/EXT_ALL_VENDORS/STANDARD_EXT/?
Sure :)
- 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 <[email protected]> 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 <[email protected]>
> > ---
> > 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
> >