[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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.