On Thu, Apr 11, 2024 at 9:12 PM Charlie Jenkins <charlie@xxxxxxxxxxxx> wrote: > > Add a new hwprobe key "RISCV_HWPROBE_KEY_VENDOR_EXT_0" which allows > userspace to probe for the new RISCV_ISA_VENDOR_EXT_XTHEADVECTOR vendor > extension. > > Signed-off-by: Charlie Jenkins <charlie@xxxxxxxxxxxx> > --- > arch/riscv/include/asm/hwprobe.h | 4 +-- > arch/riscv/include/uapi/asm/hwprobe.h | 10 +++++- > arch/riscv/kernel/sys_hwprobe.c | 59 +++++++++++++++++++++++++++++++++-- > 3 files changed, 68 insertions(+), 5 deletions(-) > > diff --git a/arch/riscv/include/asm/hwprobe.h b/arch/riscv/include/asm/hwprobe.h > index 630507dff5ea..e68496b4f8de 100644 > --- a/arch/riscv/include/asm/hwprobe.h > +++ b/arch/riscv/include/asm/hwprobe.h > @@ -1,6 +1,6 @@ > /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ > /* > - * Copyright 2023 Rivos, Inc > + * Copyright 2023-2024 Rivos, Inc > */ > > #ifndef _ASM_HWPROBE_H > @@ -8,7 +8,7 @@ > > #include <uapi/asm/hwprobe.h> > > -#define RISCV_HWPROBE_MAX_KEY 6 > +#define RISCV_HWPROBE_MAX_KEY 7 > > static inline bool riscv_hwprobe_key_is_valid(__s64 key) > { > diff --git a/arch/riscv/include/uapi/asm/hwprobe.h b/arch/riscv/include/uapi/asm/hwprobe.h > index 9f2a8e3ff204..6614d3adfc75 100644 > --- a/arch/riscv/include/uapi/asm/hwprobe.h > +++ b/arch/riscv/include/uapi/asm/hwprobe.h > @@ -1,6 +1,6 @@ > /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ > /* > - * Copyright 2023 Rivos, Inc > + * Copyright 2023-2024 Rivos, Inc > */ > > #ifndef _UAPI_ASM_HWPROBE_H > @@ -67,6 +67,14 @@ struct riscv_hwprobe { > #define RISCV_HWPROBE_MISALIGNED_UNSUPPORTED (4 << 0) > #define RISCV_HWPROBE_MISALIGNED_MASK (7 << 0) > #define RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE 6 > +/* > + * It is not possible for one CPU to have multiple vendor ids, so each vendor > + * has its own vendor extension "namespace". The keys for each vendor starts > + * at zero. > + */ > +#define RISCV_HWPROBE_KEY_VENDOR_EXT_0 7 > + /* T-Head */ > +#define RISCV_HWPROBE_VENDOR_EXT_XTHEADVECTOR (1 << 0) > /* Increase RISCV_HWPROBE_MAX_KEY when adding items. */ > > /* Flags */ > diff --git a/arch/riscv/kernel/sys_hwprobe.c b/arch/riscv/kernel/sys_hwprobe.c > index e0a42c851511..365ce7380443 100644 > --- a/arch/riscv/kernel/sys_hwprobe.c > +++ b/arch/riscv/kernel/sys_hwprobe.c > @@ -69,7 +69,8 @@ static void hwprobe_isa_ext0(struct riscv_hwprobe *pair, > if (riscv_isa_extension_available(NULL, c)) > pair->value |= RISCV_HWPROBE_IMA_C; > > - if (has_vector() && !riscv_has_vendor_extension_unlikely(RISCV_ISA_VENDOR_EXT_XTHEADVECTOR)) > + if (has_vector() && > + !__riscv_isa_vendor_extension_available(NULL, RISCV_ISA_VENDOR_EXT_XTHEADVECTOR)) > pair->value |= RISCV_HWPROBE_IMA_V; > > /* > @@ -112,7 +113,8 @@ static void hwprobe_isa_ext0(struct riscv_hwprobe *pair, > EXT_KEY(ZACAS); > EXT_KEY(ZICOND); > > - if (has_vector() && !riscv_has_vendor_extension_unlikely(RISCV_ISA_VENDOR_EXT_XTHEADVECTOR)) { > + if (has_vector() && > + !riscv_has_vendor_extension_unlikely(RISCV_ISA_VENDOR_EXT_XTHEADVECTOR)) { > EXT_KEY(ZVBB); > EXT_KEY(ZVBC); > EXT_KEY(ZVKB); > @@ -139,6 +141,55 @@ static void hwprobe_isa_ext0(struct riscv_hwprobe *pair, > pair->value &= ~missing; > } > > +static void hwprobe_isa_vendor_ext0(struct riscv_hwprobe *pair, > + const struct cpumask *cpus) > +{ > + int cpu; > + u64 missing = 0; > + > + pair->value = 0; > + > + struct riscv_hwprobe mvendorid = { > + .key = RISCV_HWPROBE_KEY_MVENDORID, > + .value = 0 > + }; > + > + hwprobe_arch_id(&mvendorid, cpus); > + > + /* Set value to zero if CPUs in the set do not have the same vendor. */ > + if (mvendorid.value == -1ULL) > + return; > + > + /* > + * Loop through and record vendor extensions that 1) anyone has, and > + * 2) anyone doesn't have. > + */ > + for_each_cpu(cpu, cpus) { > + struct riscv_isainfo *isavendorinfo = &hart_isa_vendor[cpu]; > + > +#define VENDOR_EXT_KEY(ext) \ > + do { \ > + if (__riscv_isa_vendor_extension_available(isavendorinfo->isa, \ > + RISCV_ISA_VENDOR_EXT_##ext)) \ > + pair->value |= RISCV_HWPROBE_VENDOR_EXT_##ext; \ > + else \ > + missing |= RISCV_HWPROBE_VENDOR_EXT_##ext; \ > + } while (false) > + > + /* > + * Only use VENDOR_EXT_KEY() for extensions which can be exposed to userspace, > + * regardless of the kernel's configuration, as no other checks, besides > + * presence in the hart_vendor_isa bitmap, are made. > + */ > + VENDOR_EXT_KEY(XTHEADVECTOR); > + > +#undef VENDOR_EXT_KEY Hey Charlie, Thanks for writing this up! At the very least I think the THEAD-specific stuff should probably end up in its own file, otherwise it'll get chaotic with vendors clamoring to add stuff right here. What do you think about this approach: * We leave RISCV_HWPROBE_MAX_KEY as the max key for the "generic world", eg 6-ish * We define that any key above 0x8000000000000000 is in the vendor space, so the meaning of the keys depends first on the mvendorid value. * In the kernel code, each new vendor adds on to a global struct, which might look something like: struct hwprobe_vendor_space vendor_space[] = { { .mvendorid = VENDOR_THEAD, .max_hwprobe_key = THEAD_MAX_HWPROBE_KEY, // currently 1 or 0x8000000000000001 with what you've got. .hwprobe_fn = thead_hwprobe }, ... }; * A hwprobe_thead.c implements thead_hwprobe(), and is called whenever the generic hwprobe encounters a key >=0x8000000000000000. * Generic code for setting up the VDSO can then still call the vendor-specific hwprobe_fn() repeatedly with an "all CPUs" mask from the base to max_hwprobe_key and set up the cached tables in userspace. * Since the VDSO data has limited space we may have to cap the number of vendor keys we cache to be lower than max_hwprobe_key. Since the data itself is not exposed to usermode we can raise this cap later if needed. -Evan > + } > + > + /* Now turn off reporting features if any CPU is missing it. */ > + pair->value &= ~missing; > +} > + > static bool hwprobe_ext0_has(const struct cpumask *cpus, unsigned long ext) > { > struct riscv_hwprobe pair; > @@ -216,6 +267,10 @@ static void hwprobe_one_pair(struct riscv_hwprobe *pair, > pair->value = riscv_cboz_block_size; > break; > > + case RISCV_HWPROBE_KEY_VENDOR_EXT_0: > + hwprobe_isa_vendor_ext0(pair, cpus); > + break; > + > /* > * For forward compatibility, unknown keys don't fail the whole > * call, but get their element key set to -1 and value set to 0 > > -- > 2.44.0 >