[PATCH 1/6] hwf: add detection of RISC-V (64-bit) hardware features

Jussi Kivilinna jussi.kivilinna at iki.fi
Mon Jan 6 16:08:48 CET 2025


* 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 <jussi.kivilinna at iki.fi>
---
 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 <jussi.kivilinna at iki.fi>
+ *
+ * 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




More information about the Gcrypt-devel mailing list