[PATCH 1/6] hwf: add detection of RISC-V (64-bit) hardware features
Jussi Kivilinna <[email protected]>
| Newsgroups | gmane.comp.encryption.gpg.libgcrypt.devel |
|---|---|
| Message-ID | <[email protected]> |
* configure.ac (gcry_cv_gcc_inline_asm_riscv, gcry_cv_gcc_inline_asm_riscv_v) (HAVE_GCC_INLINE_ASM_RISCV_V, HAVE_CPU_ARCH_RISCV): Add RISC-V detection support. * mpi/config.links: Add setup for RISC-V links. * src/Makefile.am: Add 'hwf-riscv.c'. * src/g10lib.h (HWF_RISCV_IMAFDC, HWF_RISCV_V, HWF_RISCV_B) (HWF_RISCV_ZBC): New. * src/hwf_common.h (_gcry_hwf_detect_riscv): New. * src/hwf-riscv.c: New. * src/hwfeatures.c: Add "riscv-imafdc", "riscv-v", "riscv-b" and "riscv-zbc". -- Signed-off-by: Jussi Kivilinna <[email protected]> --- configure.ac | 90 +++++++++++ mpi/config.links | 7 + src/Makefile.am | 4 +- src/g10lib.h | 7 + src/hwf-common.h | 1 + src/hwf-riscv.c | 386 +++++++++++++++++++++++++++++++++++++++++++++++ src/hwfeatures.c | 9 ++ 7 files changed, 503 insertions(+), 1 deletion(-) create mode 100644 src/hwf-riscv.c diff --git a/configure.ac b/configure.ac index d708f89a..f20d654d 100644 --- a/configure.ac +++ b/configure.ac @@ -2619,6 +2619,92 @@ if test "$gcry_cv_gcc_inline_asm_s390x_vx" = "yes" ; then fi +# +# Check whether GCC inline assembler supports RISC-V instructions +# +AC_CACHE_CHECK([whether GCC inline assembler supports RISC-V instructions], + [gcry_cv_gcc_inline_asm_riscv], + [if test "$mpi_cpu_arch" != "riscv64" || + test "$try_asm_modules" != "yes" ; then + gcry_cv_gcc_inline_asm_riscv="n/a" + else + gcry_cv_gcc_inline_asm_riscv=no + AC_LINK_IFELSE([AC_LANG_PROGRAM( + [[unsigned int testfunc(unsigned int x) + { + unsigned int y; + asm volatile ("add %0, %1, %2" : + "=r" (y) : "r" (1), "r" (x) : "a5"); + asm volatile (".option push;\n\t" + ".option arch, +zba;\n\t" + "sh3add %0, %1, %1;\n\t" + ".option pop;\n\t" + : "=r" (y) + : "r" (y)); + asm volatile (".option push;\n\t" + ".option arch, +zbb;\n\t" + "cpop %0, %1;\n\t" + ".option pop;\n\t" + : "=r" (y) + : "r" (y)); + asm volatile (".option push;\n\t" + ".option arch, +zbs;\n\t" + "bexti %0, %1, 1;\n\t" + ".option pop;\n\t" + : "=r" (y) + : "r" (y)); + asm volatile (".option push;\n\t" + ".option arch, +zbc;\n\t" + "clmulr %0, %1, %2;\n\t" + ".option pop;\n\t" + : "=r" (y) + : "r" (y), "r" (y)); + return y; + } + ]] , [ testfunc(0); ])], + [gcry_cv_gcc_inline_asm_riscv=yes]) + fi]) +if test "$gcry_cv_gcc_inline_asm_riscv" = "yes" ; then + AC_DEFINE(HAVE_GCC_INLINE_ASM_RISCV,1, + [Defined if inline assembler supports RISC-V instructions]) +fi + + +# +# Check whether GCC inline assembler supports RISC-V vector instructions +# +AC_CACHE_CHECK([whether GCC inline assembler supports RISC-V vector instructions], + [gcry_cv_gcc_inline_asm_riscv_v], + [if test "$mpi_cpu_arch" != "riscv64" || + test "$try_asm_modules" != "yes" ; then + gcry_cv_gcc_inline_asm_riscv_v="n/a" + else + gcry_cv_gcc_inline_asm_riscv_v=no + if test "$gcry_cv_gcc_inline_asm_riscv" = "yes" ; then + AC_LINK_IFELSE([AC_LANG_PROGRAM( + [[unsigned int testfunc(void) + { + unsigned int vlmax; + asm volatile (".option push;\n\t" + ".option arch, +v;\n\t" + "vsetvli %0, %1, e8, m1, ta, ma;\n\t" + "vxor.vv v1, v1, v1;\n\t" + ".option pop;\n\t" + : "=r" (vlmax) + : "r" (~0) + : "vl", "vtype", "v1"); + return vlmax; + } + ]], [ testfunc(); ])], + [gcry_cv_gcc_inline_asm_riscv_v=yes]) + fi + fi]) +if test "$gcry_cv_gcc_inline_asm_riscv_v" = "yes" ; then + AC_DEFINE(HAVE_GCC_INLINE_ASM_RISCV_V,1, + [Defined if inline assembler supports RISC-V vector instructions]) +fi + + ####################################### #### Checks for library functions. #### ####################################### @@ -3798,6 +3884,10 @@ case "$mpi_cpu_arch" in AC_DEFINE(HAVE_CPU_ARCH_S390X, 1, [Defined for s390x/zSeries platforms]) GCRYPT_HWF_MODULES="libgcrypt_la-hwf-s390x.lo" ;; + riscv64) + AC_DEFINE(HAVE_CPU_ARCH_RISCV, 1, [Defined for RISC-V platforms]) + GCRYPT_HWF_MODULES="libgcrypt_la-hwf-riscv.lo" + ;; esac AC_SUBST([GCRYPT_HWF_MODULES]) diff --git a/mpi/config.links b/mpi/config.links index 94b42e53..eefe8680 100644 --- a/mpi/config.links +++ b/mpi/config.links @@ -333,6 +333,13 @@ case "${host}" in path="powerpc32" mpi_cpu_arch="ppc" ;; + + riscv64-*-*) + echo '/* No working assembler modules available */' >>./mpi/asm-syntax.h + path="" + mpi_cpu_arch="riscv64" + ;; + *) echo '/* Platform not known */' >>./mpi/asm-syntax.h path="" diff --git a/src/Makefile.am b/src/Makefile.am index f6191bc8..6177171f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -66,7 +66,9 @@ libgcrypt_la_SOURCES = \ context.c context.h const-time.h const-time.c \ ec-context.h -EXTRA_libgcrypt_la_SOURCES = hwf-x86.c hwf-arm.c hwf-ppc.c hwf-s390x.c +EXTRA_libgcrypt_la_SOURCES = \ + hwf-x86.c hwf-arm.c hwf-ppc.c hwf-s390x.c hwf-riscv.c + gcrypt_hwf_modules = @GCRYPT_HWF_MODULES@ diff --git a/src/g10lib.h b/src/g10lib.h index fcf291b8..0a3ac127 100644 --- a/src/g10lib.h +++ b/src/g10lib.h @@ -274,6 +274,13 @@ char **_gcry_strtokenize (const char *string, const char *delim); #define HWF_S390X_MSA_9 (1 << 3) #define HWF_S390X_VX (1 << 4) +#elif defined(HAVE_CPU_ARCH_RISCV) + +#define HWF_RISCV_IMAFDC (1 << 0) +#define HWF_RISCV_V (1 << 1) +#define HWF_RISCV_B (1 << 2) +#define HWF_RISCV_ZBC (1 << 3) + #endif gpg_err_code_t _gcry_disable_hw_feature (const char *name); diff --git a/src/hwf-common.h b/src/hwf-common.h index ebd045c5..749ff040 100644 --- a/src/hwf-common.h +++ b/src/hwf-common.h @@ -24,5 +24,6 @@ unsigned int _gcry_hwf_detect_x86 (void); unsigned int _gcry_hwf_detect_arm (void); unsigned int _gcry_hwf_detect_ppc (void); unsigned int _gcry_hwf_detect_s390x (void); +unsigned int _gcry_hwf_detect_riscv (void); #endif /*HWF_COMMON_H*/ diff --git a/src/hwf-riscv.c b/src/hwf-riscv.c new file mode 100644 index 00000000..39333154 --- /dev/null +++ b/src/hwf-riscv.c @@ -0,0 +1,386 @@ +/* hwf-riscv.c - Detect hardware features - RISC-V part + * Copyright (C) 2025 Jussi Kivilinna <[email protected]> + * + * This file is part of Libgcrypt. + * + * Libgcrypt is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * Libgcrypt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <unistd.h> +#include <errno.h> +#if defined(HAVE_SYS_AUXV_H) && (defined(HAVE_GETAUXVAL) || \ + defined(HAVE_ELF_AUX_INFO)) +#include <sys/auxv.h> +#endif +#if defined(__linux__) && defined(HAVE_SYSCALL) +# include <sys/syscall.h> +#endif + +#include "g10lib.h" +#include "hwf-common.h" + +#if !defined (__riscv) +# error Module build for wrong CPU. +#endif + + +#if defined(HAVE_SYS_AUXV_H) && defined(HAVE_ELF_AUX_INFO) && \ + !defined(HAVE_GETAUXVAL) && defined(AT_HWCAP) +#define HAVE_GETAUXVAL +static unsigned long getauxval(unsigned long type) +{ + unsigned long auxval = 0; + int err; + + /* FreeBSD provides 'elf_aux_info' function that does the same as + * 'getauxval' on Linux. */ + + err = elf_aux_info (type, &auxval, sizeof(auxval)); + if (err) + { + errno = err; + auxval = 0; + } + + return auxval; +} +#endif + + +#undef HAS_SYS_AT_HWCAP +#if defined(__linux__) || \ + (defined(HAVE_SYS_AUXV_H) && defined(HAVE_GETAUXVAL)) +#define HAS_SYS_AT_HWCAP 1 + +struct hwcap_feature_map_s { + unsigned int hwcap_flag; + unsigned int hwf_flag; +}; + +/* Note: These macros have same values on Linux and FreeBSD. */ +#ifndef AT_HWCAP +# define AT_HWCAP 16 +#endif +#ifndef AT_HWCAP2 +# define AT_HWCAP2 26 +#endif + +#define HWCAP_ISA(l) (1U << (unsigned int)(l - 'a')) +#define HWCAP_ISA_IMAFDC (HWCAP_ISA('i') | HWCAP_ISA('m') | \ + HWCAP_ISA('a') | HWCAP_ISA('f') | \ + HWCAP_ISA('d') | HWCAP_ISA('c')) + +static const struct hwcap_feature_map_s hwcap_features[] = + { + { HWCAP_ISA_IMAFDC, HWF_RISCV_IMAFDC }, + { HWCAP_ISA('v'), HWF_RISCV_V }, + { HWCAP_ISA('b'), HWF_RISCV_B }, + }; + +static int +get_hwcap(unsigned int *hwcap) +{ + struct { unsigned long a_type; unsigned long a_val; } auxv; + FILE *f; + int err = -1; + static int hwcap_initialized = 0; + static unsigned int stored_hwcap = 0; + + if (hwcap_initialized) + { + *hwcap = stored_hwcap; + return 0; + } + +#if defined(HAVE_SYS_AUXV_H) && defined(HAVE_GETAUXVAL) + errno = 0; + auxv.a_val = getauxval (AT_HWCAP); + if (errno == 0) + { + stored_hwcap |= auxv.a_val; + hwcap_initialized = 1; + } + + if (hwcap_initialized && stored_hwcap) + { + *hwcap = stored_hwcap; + return 0; + } +#endif + + f = fopen("/proc/self/auxv", "r"); + if (!f) + { + *hwcap = stored_hwcap; + return -1; + } + + while (fread(&auxv, sizeof(auxv), 1, f) > 0) + { + if (auxv.a_type == AT_HWCAP) + { + stored_hwcap |= auxv.a_val; + hwcap_initialized = 1; + } + } + + if (hwcap_initialized) + err = 0; + + fclose(f); + *hwcap = stored_hwcap; + return err; +} + +static unsigned int +detect_riscv_at_hwcap(void) +{ + unsigned int hwcap; + unsigned int features = 0; + unsigned int i; + + if (get_hwcap(&hwcap) < 0) + return features; + + for (i = 0; i < DIM(hwcap_features); i++) + { + unsigned int hwcap_flag = hwcap_features[i].hwcap_flag; + if ((hwcap & hwcap_flag) == hwcap_flag) + features |= hwcap_features[i].hwf_flag; + } + + return features; +} + +#endif /* HAS_SYS_AT_HWCAP */ + + +#undef HAS_SYS_HWPROBE +#if defined(__linux__) && defined(HAVE_SYSCALL) +#define HAS_SYS_HWPROBE 1 + +#ifndef __NR_riscv_hwprobe +#define __NR_riscv_hwprobe 258 +#endif + +#define HWF_RISCV_HWPROBE_KEY_BASE_BEHAVIOR 3 +#define HWF_RISCV_HWPROBE_BASE_BEHAVIOR_IMA (1U << 0) + +#define HWF_RISCV_HWPROBE_KEY_IMA_EXT_0 4 +#define HWF_RISCV_HWPROBE_IMA_FD (1U << 0) +#define HWF_RISCV_HWPROBE_IMA_C (1U << 1) +#define HWF_RISCV_HWPROBE_IMA_V (1U << 2) +#define HWF_RISCV_HWPROBE_EXT_ZBA (1U << 3) +#define HWF_RISCV_HWPROBE_EXT_ZBB (1U << 4) +#define HWF_RISCV_HWPROBE_EXT_ZBS (1U << 5) +#define HWF_RISCV_HWPROBE_EXT_ZBC (1U << 7) +#define HWF_RISCV_HWPROBE_EXT_ZICOND (U64_C(1) << 35) + +#define HWF_RISCV_HWPROBE_IMA_FDC (HWF_RISCV_HWPROBE_IMA_FD \ + | HWF_RISCV_HWPROBE_IMA_C) + +#define HWF_RISCV_HWPROBE_IMA_B (HWF_RISCV_HWPROBE_EXT_ZBA \ + | HWF_RISCV_HWPROBE_EXT_ZBB \ + | HWF_RISCV_HWPROBE_EXT_ZBS) + +struct hwf_riscv_hwprobe_s { + u64 key; + u64 value; +}; + +struct hwprobe_feature_map_s { + unsigned int ima_ext_0_flag; + unsigned int hwf_flag; +}; + +static const struct hwprobe_feature_map_s hwprobe_features[] = + { + { HWF_RISCV_HWPROBE_IMA_FDC, HWF_RISCV_IMAFDC }, + { HWF_RISCV_HWPROBE_IMA_V, HWF_RISCV_V }, + { HWF_RISCV_HWPROBE_IMA_B, HWF_RISCV_B }, + { HWF_RISCV_HWPROBE_EXT_ZBC, HWF_RISCV_ZBC }, + }; + +static int +hwf_riscv_hwprobe(struct hwf_riscv_hwprobe_s *pairs, size_t pair_count, + size_t cpu_count, unsigned long *cpus, unsigned int flags) +{ + return syscall(__NR_riscv_hwprobe, pairs, pair_count, cpu_count, cpus, flags); +} + +static unsigned int +detect_riscv_hwprobe(void) +{ + const int base_behavior_idx = 0; + const int ima_ext_0_idx = base_behavior_idx + 1; + struct hwf_riscv_hwprobe_s reqs[ima_ext_0_idx + 1]; + unsigned int features = 0; + unsigned int i; + int ret; + + memset(reqs, 0, sizeof(reqs)); + reqs[base_behavior_idx].key = HWF_RISCV_HWPROBE_KEY_BASE_BEHAVIOR; + reqs[ima_ext_0_idx].key = HWF_RISCV_HWPROBE_KEY_IMA_EXT_0; + + ret = hwf_riscv_hwprobe(reqs, DIM(reqs), 0, NULL, 0); + if (ret < 0) + return 0; + + for (i = 0; i < DIM(hwprobe_features); i++) + { + unsigned int ima_ext_0_flag = hwprobe_features[i].ima_ext_0_flag; + if ((reqs[base_behavior_idx].value & HWF_RISCV_HWPROBE_BASE_BEHAVIOR_IMA) + && (reqs[ima_ext_0_idx].value & ima_ext_0_flag) == ima_ext_0_flag) + features |= hwprobe_features[i].hwf_flag; + } + + return features; +} + +#endif /* HAS_SYS_HWPROBE */ + + +static unsigned int +detect_riscv_hwf_by_toolchain (void) +{ + unsigned int features = 0; + + /* Detect CPU features required by toolchain. */ + +#if defined(__riscv_i) && __riscv_i >= 1000000 && \ + defined(__riscv_m) && __riscv_m >= 1000000 && \ + defined(__riscv_a) && __riscv_a >= 1000000 && \ + defined(__riscv_f) && __riscv_f >= 1000000 && \ + defined(__riscv_d) && __riscv_d >= 1000000 && \ + defined(__riscv_c) && __riscv_c >= 1000000 + features |= HWF_RISCV_IMAFDC; +#endif + +#if defined(__riscv_zba) && __riscv_zba >= 1000000 && \ + defined(__riscv_zbb) && __riscv_zbb >= 1000000 && \ + defined(__riscv_zbs) && __riscv_zbs >= 1000000 && \ + defined(HAVE_GCC_INLINE_ASM_RISCV) + { + unsigned int tmp = 0; + + /* Early test for Zba/Zbb/Zbs instructions to detect faulty toolchain + * configuration. */ + asm volatile (".option push;\n\t" + ".option arch, +zba;\n\t" + "sh3add %0, %1, %1;\n\t" + ".option pop;\n\t" + : "=r" (tmp) + : "r" (123)); + asm volatile (".option push;\n\t" + ".option arch, +zbb;\n\t" + "cpop %0, %1;\n\t" + ".option pop;\n\t" + : "=r" (tmp) + : "r" (321)); + asm volatile (".option push;\n\t" + ".option arch, +zbs;\n\t" + "bexti %0, %1, 1;\n\t" + ".option pop;\n\t" + : "=r" (tmp) + : "r" (234)); + + features |= HWF_RISCV_B; + } +#endif + +#if defined(__riscv_zbc) && __riscv_zbc >= 1000000 && \ + defined(HAVE_GCC_INLINE_ASM_RISCV) + { + unsigned int tmp = 0; + + /* Early test for Zbc instructions to detect faulty toolchain + * configuration. */ + asm volatile (".option push;\n\t" + ".option arch, +zbc;\n\t" + "clmulr %0, %1, %2;\n\t" + ".option pop;\n\t" + : "=r" (tmp) + : "r" (123), "r" (321)); + + features |= HWF_RISCV_ZBC; + } +#endif + +#ifdef HAVE_GCC_INLINE_ASM_RISCV_V +#if defined(__riscv_v) && __riscv_v >= 12000 + { + unsigned int vlmax = 0; + + /* Early test for RVV instructions to detect faulty toolchain + * configuration. */ + asm volatile (".option push;\n\t" + ".option arch, +v;\n\t" + "vsetvli %0, %1, e8, m1, ta, ma;\n\t" + "vxor.vv v1, v1, v1;\n\t" + ".option pop;\n\t" + : "=r" (vlmax) + : "r" (~0) + : "vl", "vtype", "v1"); + + features |= HWF_RISCV_V; + } +#endif +#endif + + return features; +} + +unsigned int +_gcry_hwf_detect_riscv (void) +{ + unsigned int features = 0; + +#if defined (HAS_SYS_AT_HWCAP) + features |= detect_riscv_at_hwcap (); +#endif + +#if defined (HAS_SYS_HWPROBE) + features |= detect_riscv_hwprobe (); +#endif + + features |= detect_riscv_hwf_by_toolchain (); + + /* Require VLEN >= 128-bit for "riscv-v" HWF. */ + if (features & HWF_RISCV_V) + { + unsigned int vlmax = 0; + +#if defined(HAVE_GCC_INLINE_ASM_RISCV_V) + asm volatile (".option push;\n\t" + ".option arch, +v;\n\t" + "vsetvli %0, %1, e8, m1, ta, ma;\n\t" + ".option pop;\n\t" + : "=r" (vlmax) + : "r" (~0) + : "vl", "vtype"); +#endif + + if (vlmax < 16) + { + features &= ~HWF_RISCV_V; + } + } + + return features; +} diff --git a/src/hwfeatures.c b/src/hwfeatures.c index b11cadef..96ddfd30 100644 --- a/src/hwfeatures.c +++ b/src/hwfeatures.c @@ -91,6 +91,11 @@ static struct { HWF_S390X_MSA_8, "s390x-msa-8" }, { HWF_S390X_MSA_9, "s390x-msa-9" }, { HWF_S390X_VX, "s390x-vx" }, +#elif defined(HAVE_CPU_ARCH_RISCV) + { HWF_RISCV_IMAFDC, "riscv-imafdc" }, + { HWF_RISCV_V, "riscv-v" }, + { HWF_RISCV_B, "riscv-b" }, + { HWF_RISCV_ZBC, "riscv-zbc" }, #endif }; @@ -245,6 +250,10 @@ _gcry_detect_hw_features (void) { hw_features = _gcry_hwf_detect_s390x (); } +#elif defined (HAVE_CPU_ARCH_RISCV) + { + hw_features = _gcry_hwf_detect_riscv (); + } #endif hw_features &= ~disabled_hw_features; } -- 2.45.2