[PATCH 08/12] sm4: add RISC-V Zvksed intrinsics implementation

Jussi Kivilinna jussi.kivilinna at iki.fi
Sun Oct 11 19:23:17 CEST 2026


* cipher/Makefile.am: Add 'sm4-riscv-zvksed-zvkb.c' and
'sm4-riscv-zvksed-zvkb.h'.
(riscv_vector_crypto_sm4_cflags): New.
* cipher/sm4-riscv-zvksed-zvkb.c: New.
* cipher/sm4-riscv-zvksed-zvkb.h: New.
* cipher/sm4.c (USE_RISCV_V_CRYPTO): New.
(SM4_context) [USE_RISCV_V_CRYPTO]: Add 'use_riscv_zvksed'.
[USE_RISCV_V_CRYPTO] (_gcry_sm4_riscv_v_check_hw)
(_gcry_sm4_riscv_zvksed_expand_key, _gcry_sm4_riscv_zvksed_crypt)
(_gcry_sm4_riscv_zvksed_ctr_enc, _gcry_sm4_riscv_zvksed_cbc_dec)
(_gcry_sm4_riscv_zvksed_cfb_dec, sm4_riscv_zvksed_crypt_blk1_16): New.
(sm4_expand_key, sm4_encrypt, sm4_decrypt, sm4_get_crypt_blk1_16_fn)
(_gcry_sm4_ctr_enc, _gcry_sm4_cbc_dec, _gcry_sm4_cfb_dec)
(sm4_crypt_blk1_32) [USE_RISCV_V_CRYPTO]: Add RISC-V Zvksed code-path.
(sm4_setkey) [USE_RISCV_V_CRYPTO]: Enable new implementation if supported
by HW.
* configure.ac: Add 'sm4-riscv-zvksed-zvkb.lo'.
(GCRY_RISCV_VECTOR_CRYPTO_INTRINSICS_TEST): Add SM4 intrinsics test.
(HAVE_BROKEN_VSM4R_VS_INTRINSIC): New check.
* doc/gcrypt.texi: Add "riscv-zvksed".
* src/g10lib.h [HAVE_CPU_ARCH_RISCV] (HWF_RISCV_ZVKSED): New.
(HWF_RISCV_ZVKSH): Move to bit 11.
* src/hwf-riscv.c (HWF_RISCV_HWPROBE_EXT_ZVKSED): New.
(hwprobe_features): Add Zvksed.
* src/hwfeatures.c (hwflist) [HAVE_CPU_ARCH_RISCV]: Add "riscv-zvksed".
--

Blocks are processed with LMUL=4 and tail with smallest LMUL that fits.
CTR, CBC decryption and CFB decryption have vector implementations,
other modes use generic bulk helpers.

Benchmark on SpacemiT K3, X100 core (out-of-order, VLEN=256):

 Before:
 SM4            |  nanosecs/byte   mebibytes/sec   cycles/byte  auto Mhz
        ECB enc |     16.95 ns/B     56.27 MiB/s     33.84 c/B      1997
        CBC enc |     19.86 ns/B     48.02 MiB/s     39.67 c/B      1997
        CBC dec |     17.13 ns/B     55.66 MiB/s     34.23 c/B      1998
        CFB dec |     17.16 ns/B     55.59 MiB/s     34.29 c/B      1999
        CTR enc |     17.21 ns/B     55.43 MiB/s     34.36 c/B      1997
        XTS enc |     17.23 ns/B     55.35 MiB/s     34.41 c/B      1997
        GCM enc |     17.33 ns/B     55.02 MiB/s     34.62 c/B      1997
        OCB enc |     17.47 ns/B     54.60 MiB/s     34.88 c/B      1997

 After (CTR ~47x faster, CBC enc ~7.2x faster):
 SM4            |  nanosecs/byte   mebibytes/sec   cycles/byte  auto Mhz
        ECB enc |     0.407 ns/B      2345 MiB/s     0.812 c/B      1997
        CBC enc |      2.75 ns/B     346.8 MiB/s      5.49 c/B      1997
        CBC dec |     0.435 ns/B      2192 MiB/s     0.869 c/B      1997
        CFB dec |     0.418 ns/B      2281 MiB/s     0.835 c/B      1997
        CTR enc |     0.364 ns/B      2617 MiB/s     0.728 c/B      1997
        XTS enc |     0.680 ns/B      1402 MiB/s      1.36 c/B      1997
        GCM enc |     0.497 ns/B      1921 MiB/s     0.991 c/B      1997
        OCB enc |     0.904 ns/B      1055 MiB/s      1.80 c/B      1997

Benchmark on SpacemiT K3, A100 core (in-order, VLEN=1024):

 Before:
 SM4            |  nanosecs/byte   mebibytes/sec   cycles/byte  auto Mhz
        ECB enc |     23.12 ns/B     41.24 MiB/s     41.62 c/B      1800
        CBC enc |     25.35 ns/B     37.62 MiB/s     45.63 c/B      1800
        CBC dec |     23.47 ns/B     40.63 MiB/s     42.25 c/B      1800
        CFB dec |     23.66 ns/B     40.31 MiB/s     42.58 c/B      1800
        CTR enc |     23.73 ns/B     40.18 MiB/s     42.72 c/B      1800
        XTS enc |     23.79 ns/B     40.08 MiB/s     42.82 c/B      1800
        GCM enc |     25.22 ns/B     37.82 MiB/s     45.39 c/B      1800
        OCB enc |     24.58 ns/B     38.80 MiB/s     44.24 c/B      1800

 After (CTR ~56x faster, CBC enc ~2.2x faster):
 SM4            |  nanosecs/byte   mebibytes/sec   cycles/byte  auto Mhz
        ECB enc |     0.675 ns/B      1413 MiB/s      1.21 c/B      1800
        CBC enc |     11.65 ns/B     81.83 MiB/s     20.98 c/B      1800
        CBC dec |     0.507 ns/B      1882 MiB/s     0.912 c/B      1800
        CFB dec |     0.498 ns/B      1913 MiB/s     0.897 c/B      1800
        CTR enc |     0.423 ns/B      2254 MiB/s     0.762 c/B      1800
        XTS enc |      1.35 ns/B     708.6 MiB/s      2.42 c/B      1800
        GCM enc |      1.59 ns/B     598.5 MiB/s      2.87 c/B      1800
        OCB enc |      2.32 ns/B     410.3 MiB/s      4.18 c/B      1800

Signed-off-by: Jussi Kivilinna <jussi.kivilinna at iki.fi>
---
 cipher/Makefile.am             |  12 +-
 cipher/sm4-riscv-zvksed-zvkb.c | 322 +++++++++++++++++++++++++++++++++
 cipher/sm4-riscv-zvksed-zvkb.h | 210 +++++++++++++++++++++
 cipher/sm4.c                   | 108 +++++++++++
 configure.ac                   |  64 +++++++
 doc/gcrypt.texi                |   1 +
 src/g10lib.h                   |   3 +-
 src/hwf-riscv.c                |   2 +
 src/hwfeatures.c               |   1 +
 9 files changed, 721 insertions(+), 2 deletions(-)
 create mode 100644 cipher/sm4-riscv-zvksed-zvkb.c
 create mode 100644 cipher/sm4-riscv-zvksed-zvkb.h

diff --git a/cipher/Makefile.am b/cipher/Makefile.am
index 50509914..23c4655c 100644
--- a/cipher/Makefile.am
+++ b/cipher/Makefile.am
@@ -142,7 +142,7 @@ EXTRA_libcipher_la_SOURCES = \
 	sm4-intel-avx2-amd64.S sm4-intel-avx512-amd64.S \
 	sm4-gfni-avx2-amd64.S sm4-gfni-avx512-amd64.S \
 	sm4-aarch64.S sm4-armv8-aarch64-ce.S sm4-armv9-aarch64-sve-ce.S \
-	sm4-ppc.c \
+	sm4-ppc.c sm4-riscv-zvksed-zvkb.c sm4-riscv-zvksed-zvkb.h \
 	sha1.c sha1-ssse3-amd64.S sha1-avx-amd64.S sha1-avx-bmi2-amd64.S \
 	sha1-avx2-bmi2-amd64.S sha1-armv7-neon.S sha1-armv8-aarch32-ce.S \
 	sha1-armv8-aarch64-ce.S sha1-intel-shaext.c \
@@ -474,6 +474,7 @@ riscv_vector_crypto_sha512_cflags = -O2 -march=@MARCH_RVA23U64_BASE at _zvknhb_zvkb
 riscv_vector_crypto_sha512_m1_cflags = -O2 -march=@MARCH_RVA23U64_BASE at _zvknhb_zvkb_zvl256b
 riscv_vector_crypto_sm3_cflags = -O2 -march=@MARCH_RVA23U64_BASE at _zvksh_zvkb
 riscv_vector_crypto_sm3_m1_cflags = -O2 -march=@MARCH_RVA23U64_BASE at _zvksh_zvkb_zvl256b
+riscv_vector_crypto_sm4_cflags = -O2 -march=@MARCH_RVA23U64_BASE at _zvksed_zvkb
 riscv_vector_crypto_gcm_cflags = -O2 -march=@MARCH_RVA23U64_BASE at _zvkg_zvkb
 else
 riscv_vector_crypto_zvkb_cflags =
@@ -483,6 +484,7 @@ riscv_vector_crypto_sha512_cflags =
 riscv_vector_crypto_sha512_m1_cflags =
 riscv_vector_crypto_sm3_cflags =
 riscv_vector_crypto_sm3_m1_cflags =
+riscv_vector_crypto_sm4_cflags =
 riscv_vector_crypto_gcm_cflags =
 endif
 
@@ -549,3 +551,11 @@ sm3-riscv-zvksh-zvkb-m1.o: $(srcdir)/sm3-riscv-zvksh-zvkb-m1.c Makefile
 sm3-riscv-zvksh-zvkb-m1.lo: $(srcdir)/sm3-riscv-zvksh-zvkb-m1.c Makefile
 	`echo $(LTCOMPILE) $(riscv_vector_crypto_sm3_m1_cflags) $(custom_depflags) -c $< | $(instrumentation_munging) `
 	$(custom_depfile_lo)
+
+sm4-riscv-zvksed-zvkb.o: $(srcdir)/sm4-riscv-zvksed-zvkb.c Makefile
+	`echo $(COMPILE) $(riscv_vector_crypto_sm4_cflags) $(custom_depflags) -c $< | $(instrumentation_munging) `
+	$(custom_depfile_o)
+
+sm4-riscv-zvksed-zvkb.lo: $(srcdir)/sm4-riscv-zvksed-zvkb.c Makefile
+	`echo $(LTCOMPILE) $(riscv_vector_crypto_sm4_cflags) $(custom_depflags) -c $< | $(instrumentation_munging) `
+	$(custom_depfile_lo)
diff --git a/cipher/sm4-riscv-zvksed-zvkb.c b/cipher/sm4-riscv-zvksed-zvkb.c
new file mode 100644
index 00000000..9bb525a0
--- /dev/null
+++ b/cipher/sm4-riscv-zvksed-zvkb.c
@@ -0,0 +1,322 @@
+/* sm4-riscv-zvksed-zvkb.c - RISC-V vector crypto implementation of SM4
+ * Copyright (C) 2026 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>
+
+#if defined (__riscv) && \
+    defined(HAVE_COMPATIBLE_CC_RISCV_VECTOR_INTRINSICS) && \
+    defined(HAVE_COMPATIBLE_CC_RISCV_VECTOR_CRYPTO_INTRINSICS) && \
+    defined(USE_SM4)
+
+#include "g10lib.h"
+#include "bufhelp.h"
+#include "simd-common-riscv.h"
+#include <riscv_vector.h>
+
+
+#define ALWAYS_INLINE inline __attribute__((always_inline))
+#define NO_INLINE __attribute__((noinline))
+#define NO_INSTRUMENT_FUNCTION __attribute__((no_instrument_function))
+
+#define ASM_FUNC_ATTR          NO_INSTRUMENT_FUNCTION
+#define ASM_FUNC_ATTR_INLINE   ASM_FUNC_ATTR ALWAYS_INLINE
+#define ASM_FUNC_ATTR_NOINLINE ASM_FUNC_ATTR NO_INLINE
+
+#ifdef HAVE_GCC_ATTRIBUTE_OPTIMIZE
+# define FUNC_ATTR_OPT_O2 __attribute__((optimize("-O2")))
+#else
+# define FUNC_ATTR_OPT_O2
+#endif
+
+
+#define SM4_ROUND_KEY_VARIABLES \
+  vuint32m1_t rk0, rk1, rk2, rk3, rk4, rk5, rk6, rk7
+
+#define SM4_ROUND_KEY_PARAMS \
+  vuint32m1_t rk0, vuint32m1_t rk1, vuint32m1_t rk2, vuint32m1_t rk3, \
+  vuint32m1_t rk4, vuint32m1_t rk5, vuint32m1_t rk6, vuint32m1_t rk7
+
+#define SM4_ROUND_KEYS rk0, rk1, rk2, rk3, rk4, rk5, rk6, rk7
+
+#define SM4_LOAD_ROUND_KEYS(rk) \
+  rk0 = __riscv_vle32_v_u32m1(rk + 0 * 4, 4); \
+  rk1 = __riscv_vle32_v_u32m1(rk + 1 * 4, 4); \
+  rk2 = __riscv_vle32_v_u32m1(rk + 2 * 4, 4); \
+  rk3 = __riscv_vle32_v_u32m1(rk + 3 * 4, 4); \
+  rk4 = __riscv_vle32_v_u32m1(rk + 4 * 4, 4); \
+  rk5 = __riscv_vle32_v_u32m1(rk + 5 * 4, 4); \
+  rk6 = __riscv_vle32_v_u32m1(rk + 6 * 4, 4); \
+  rk7 = __riscv_vle32_v_u32m1(rk + 7 * 4, 4)
+
+#define SM4_NEXT_BLOCKS(n) \
+  in += (n) * 16; \
+  out += (n) * 16; \
+  nblocks -= (n)
+
+#define SM4_LMUL m1
+#define SM4_MASK64_T vbool64_t
+#include "sm4-riscv-zvksed-zvkb.h"
+
+#define SM4_LMUL m2
+#define SM4_MASK64_T vbool32_t
+#include "sm4-riscv-zvksed-zvkb.h"
+
+#define SM4_LMUL m4
+#define SM4_MASK64_T vbool16_t
+#include "sm4-riscv-zvksed-zvkb.h"
+
+
+unsigned int ASM_FUNC_ATTR_NOINLINE FUNC_ATTR_OPT_O2
+_gcry_sm4_riscv_zvksed_crypt (const u32 *rk, byte *out, const byte *in,
+			      size_t nblocks)
+{
+  size_t max_blocks_m1 = __riscv_vsetvlmax_e32m1() / 4;
+  size_t max_blocks_m2 = max_blocks_m1 * 2;
+  size_t max_blocks_m4 = max_blocks_m1 * 4;
+  SM4_ROUND_KEY_VARIABLES;
+
+  SM4_LOAD_ROUND_KEYS(rk);
+
+  if (nblocks > max_blocks_m2)
+    {
+      vbool16_t even_m4 = even_mask_m4();
+
+      while (nblocks >= max_blocks_m4)
+	{
+	  sm4_ecb_blocks_m4(SM4_ROUND_KEYS, even_m4, out, in, max_blocks_m4);
+	  SM4_NEXT_BLOCKS(max_blocks_m4);
+	}
+      if (nblocks > max_blocks_m2)
+	{
+	  sm4_ecb_blocks_m4(SM4_ROUND_KEYS, even_m4, out, in, nblocks);
+	  SM4_NEXT_BLOCKS(nblocks);
+	}
+    }
+  if (nblocks > max_blocks_m1)
+    sm4_ecb_blocks_m2(SM4_ROUND_KEYS, even_mask_m2(), out, in, nblocks);
+  else if (nblocks)
+    sm4_ecb_blocks_m1(SM4_ROUND_KEYS, even_mask_m1(), out, in, nblocks);
+
+  clear_vec_regs();
+
+  return 0;
+}
+
+
+/* Caller handles carry beyond low 16 bits of counter.  */
+void ASM_FUNC_ATTR_NOINLINE FUNC_ATTR_OPT_O2
+_gcry_sm4_riscv_zvksed_ctr_enc (const u32 *rk, byte *out, const byte *in,
+				byte *ctr, size_t nblocks)
+{
+  u64 ctr_lo = rol64(buf_get_be64(ctr + 0), 32);
+  u64 ctr_hi_noswap = buf_get_be64(ctr + 8);
+  u64 ctr_hi = rol64(ctr_hi_noswap, 32);
+  size_t max_blocks_m1 = __riscv_vsetvlmax_e32m1() / 4;
+  size_t max_blocks_m2 = max_blocks_m1 * 2;
+  size_t max_blocks_m4 = max_blocks_m1 * 4;
+  SM4_ROUND_KEY_VARIABLES;
+
+  SM4_LOAD_ROUND_KEYS(rk);
+
+  ctr[14] = (ctr_hi_noswap + nblocks) >> 8;
+  ctr[15] = (ctr_hi_noswap + nblocks) >> 0;
+
+  if (nblocks > max_blocks_m2)
+    {
+      vbool16_t even_m4 = even_mask_m4();
+      vuint64m4_t ctr_m4 = ctr_init_m4(even_m4, ctr_lo, ctr_hi);
+      vuint64m4_t inc_m4 = ctr_inc_m4(even_m4, max_blocks_m4);
+
+      while (nblocks >= max_blocks_m4)
+	{
+	  sm4_ctr_blocks_m4(SM4_ROUND_KEYS, even_m4, &ctr_m4, inc_m4, out, in,
+			    max_blocks_m4);
+	  ctr_hi += (u64)max_blocks_m4 << 32;
+	  SM4_NEXT_BLOCKS(max_blocks_m4);
+	}
+      if (nblocks > max_blocks_m2)
+	{
+	  sm4_ctr_blocks_m4(SM4_ROUND_KEYS, even_m4, &ctr_m4, inc_m4, out, in,
+			    nblocks);
+	  ctr_hi += (u64)nblocks << 32;
+	  SM4_NEXT_BLOCKS(nblocks);
+	}
+    }
+  if (nblocks > max_blocks_m1)
+    {
+      vbool32_t even_m2 = even_mask_m2();
+      vuint64m2_t ctr_m2 = ctr_init_m2(even_m2, ctr_lo, ctr_hi);
+
+      sm4_ctr_blocks_m2(SM4_ROUND_KEYS, even_m2, &ctr_m2,
+			ctr_inc_m2(even_m2, max_blocks_m2), out, in, nblocks);
+    }
+  else if (nblocks)
+    {
+      vbool64_t even_m1 = even_mask_m1();
+      vuint64m1_t ctr_m1 = ctr_init_m1(even_m1, ctr_lo, ctr_hi);
+
+      sm4_ctr_blocks_m1(SM4_ROUND_KEYS, even_m1, &ctr_m1,
+			ctr_inc_m1(even_m1, max_blocks_m1), out, in, nblocks);
+    }
+
+  clear_vec_regs();
+}
+
+
+void ASM_FUNC_ATTR_NOINLINE FUNC_ATTR_OPT_O2
+_gcry_sm4_riscv_zvksed_cbc_dec (const u32 *rk, byte *out, const byte *in,
+				byte *iv_arg, size_t nblocks)
+{
+  size_t max_blocks_m1 = __riscv_vsetvlmax_e32m1() / 4;
+  size_t max_blocks_m2 = max_blocks_m1 * 2;
+  size_t max_blocks_m4 = max_blocks_m1 * 4;
+  vuint32m1_t iv;
+  SM4_ROUND_KEY_VARIABLES;
+
+  SM4_LOAD_ROUND_KEYS(rk);
+
+  iv = load_u32_m1(iv_arg, 4);
+
+  if (nblocks > max_blocks_m2)
+    {
+      vbool16_t even_m4 = even_mask_m4();
+      vuint32m4_t ivs_m4 = __riscv_vundefined_u32m4();
+
+      while (nblocks >= max_blocks_m4)
+	{
+	  sm4_cbc_dec_blocks_m4(SM4_ROUND_KEYS, even_m4, &iv, &ivs_m4, out, in,
+				max_blocks_m4);
+	  SM4_NEXT_BLOCKS(max_blocks_m4);
+	}
+      if (nblocks > max_blocks_m2)
+	{
+	  sm4_cbc_dec_blocks_m4(SM4_ROUND_KEYS, even_m4, &iv, &ivs_m4, out, in,
+				nblocks);
+	  SM4_NEXT_BLOCKS(nblocks);
+	}
+    }
+  if (nblocks > max_blocks_m1)
+    {
+      vuint32m2_t ivs_m2 = __riscv_vundefined_u32m2();
+
+      sm4_cbc_dec_blocks_m2(SM4_ROUND_KEYS, even_mask_m2(), &iv, &ivs_m2, out,
+			    in, nblocks);
+    }
+  else if (nblocks)
+    {
+      vuint32m1_t ivs_m1 = __riscv_vundefined_u32m1();
+
+      sm4_cbc_dec_blocks_m1(SM4_ROUND_KEYS, even_mask_m1(), &iv, &ivs_m1, out,
+			    in, nblocks);
+    }
+
+  store_u32_m1(iv_arg, iv, 4);
+
+  clear_vec_regs();
+}
+
+
+void ASM_FUNC_ATTR_NOINLINE FUNC_ATTR_OPT_O2
+_gcry_sm4_riscv_zvksed_cfb_dec (const u32 *rk, byte *out, const byte *in,
+				byte *iv_arg, size_t nblocks)
+{
+  size_t max_blocks_m1 = __riscv_vsetvlmax_e32m1() / 4;
+  size_t max_blocks_m2 = max_blocks_m1 * 2;
+  size_t max_blocks_m4 = max_blocks_m1 * 4;
+  vuint32m1_t iv;
+  SM4_ROUND_KEY_VARIABLES;
+
+  SM4_LOAD_ROUND_KEYS(rk);
+
+  iv = load_u32_m1(iv_arg, 4);
+
+  if (nblocks > max_blocks_m2)
+    {
+      vbool16_t even_m4 = even_mask_m4();
+      vuint32m4_t ivs_m4 = __riscv_vundefined_u32m4();
+
+      while (nblocks >= max_blocks_m4)
+	{
+	  sm4_cfb_dec_blocks_m4(SM4_ROUND_KEYS, even_m4, &iv, &ivs_m4, out, in,
+				max_blocks_m4);
+	  SM4_NEXT_BLOCKS(max_blocks_m4);
+	}
+      if (nblocks > max_blocks_m2)
+	{
+	  sm4_cfb_dec_blocks_m4(SM4_ROUND_KEYS, even_m4, &iv, &ivs_m4, out, in,
+				nblocks);
+	  SM4_NEXT_BLOCKS(nblocks);
+	}
+    }
+  if (nblocks > max_blocks_m1)
+    {
+      vuint32m2_t ivs_m2 = __riscv_vundefined_u32m2();
+
+      sm4_cfb_dec_blocks_m2(SM4_ROUND_KEYS, even_mask_m2(), &iv, &ivs_m2, out,
+			    in, nblocks);
+    }
+  else if (nblocks)
+    {
+      vuint32m1_t ivs_m1 = __riscv_vundefined_u32m1();
+
+      sm4_cfb_dec_blocks_m1(SM4_ROUND_KEYS, even_mask_m1(), &iv, &ivs_m1, out,
+			    in, nblocks);
+    }
+
+  store_u32_m1(iv_arg, iv, 4);
+
+  clear_vec_regs();
+}
+
+
+#define SM4_EXPAND_KEY_ROUNDS(rnd) \
+  k = __riscv_vsm4k_vi_u32m1(k, rnd, vl); \
+  __riscv_vse32_v_u32m1(rk_enc + (rnd) * 4, k, vl); \
+  __riscv_vsse32_v_u32m1(rk_dec + 31 - (rnd) * 4, -4, k, vl)
+
+void ASM_FUNC_ATTR_NOINLINE FUNC_ATTR_OPT_O2
+_gcry_sm4_riscv_zvksed_expand_key (const byte *key, u32 *rk_enc, u32 *rk_dec,
+				   const u32 *fk)
+{
+  size_t vl = 4;
+  vuint32m1_t k;
+
+  k = __riscv_vrev8_v_u32m1(load_u32_m1(key, vl), vl);
+  k = __riscv_vxor_vv_u32m1(k, __riscv_vle32_v_u32m1(fk, vl), vl);
+
+  SM4_EXPAND_KEY_ROUNDS(0);
+  SM4_EXPAND_KEY_ROUNDS(1);
+  SM4_EXPAND_KEY_ROUNDS(2);
+  SM4_EXPAND_KEY_ROUNDS(3);
+  SM4_EXPAND_KEY_ROUNDS(4);
+  SM4_EXPAND_KEY_ROUNDS(5);
+  SM4_EXPAND_KEY_ROUNDS(6);
+  SM4_EXPAND_KEY_ROUNDS(7);
+
+  clear_vec_regs();
+}
+
+
+unsigned int ASM_FUNC_ATTR FUNC_ATTR_OPT_O2
+_gcry_sm4_riscv_v_check_hw(void)
+{
+  return (__riscv_vsetvl_e32m1(4) == 4);
+}
+
+#endif /* HAVE_COMPATIBLE_CC_RISCV_VECTOR_INTRINSICS */
diff --git a/cipher/sm4-riscv-zvksed-zvkb.h b/cipher/sm4-riscv-zvksed-zvkb.h
new file mode 100644
index 00000000..38e9b825
--- /dev/null
+++ b/cipher/sm4-riscv-zvksed-zvkb.h
@@ -0,0 +1,210 @@
+/* sm4-riscv-zvksed-zvkb.h - RISC-V vector crypto SM4 LMUL template
+ * Copyright (C) 2026 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/>.
+ */
+
+/* Included once for each LMUL with SM4_LMUL and SM4_MASK64_T set.  */
+
+#define CAT_(a, b) a##b
+#define CAT(a, b) CAT_(a, b)
+
+#define FN(name) CAT(name##_, SM4_LMUL)
+#define V32(name) CAT(__riscv_##name##_u32, SM4_LMUL)
+#define V64(name) CAT(__riscv_##name##_u64, SM4_LMUL)
+#define V8(name) CAT(__riscv_##name##_u8, SM4_LMUL)
+#define VSETVLMAX_E64() CAT(__riscv_vsetvlmax_e64, SM4_LMUL)()
+#define VEC32_T CAT(CAT(vuint32, SM4_LMUL), _t)
+#define VEC64_T CAT(CAT(vuint64, SM4_LMUL), _t)
+#define MASK64_T SM4_MASK64_T
+
+#define SET_IV_m1(ivs, iv) (iv)
+#define SET_IV_m2(ivs, iv) __riscv_vset(ivs, 0, iv)
+#define SET_IV_m4(ivs, iv) __riscv_vset(ivs, 0, iv)
+#define SET_IV(ivs, iv) CAT(SET_IV_, SM4_LMUL)(ivs, iv)
+
+
+static ASM_FUNC_ATTR_INLINE VEC32_T
+FN(load_u32) (const byte *p, size_t vl)
+{
+#ifdef RVV_UNALIGNED_NOT_ALLOWED
+  return V32(vreinterpret)(V8(vle8_v)(p, vl * 4));
+#else
+  return V32(vle32_v)((const void *)p, vl);
+#endif
+}
+
+static ASM_FUNC_ATTR_INLINE void
+FN(store_u32) (byte *p, VEC32_T v, size_t vl)
+{
+#ifdef RVV_UNALIGNED_NOT_ALLOWED
+  __riscv_vse8(p, V8(vreinterpret)(v), vl * 4);
+#else
+  __riscv_vse32((u32 *)(void *)p, v, vl);
+#endif
+}
+
+static ASM_FUNC_ATTR_INLINE MASK64_T
+FN(even_mask) (void)
+{
+  size_t vl64 = VSETVLMAX_E64();
+
+  return __riscv_vmseq(__riscv_vand(V64(vid_v)(vl64), 1, vl64), 0, vl64);
+}
+
+/* Returns { ctr_lo, ctr_hi + (0 << 32), ctr_lo, ctr_hi + (1 << 32),
+ *           ctr_lo, ctr_hi + (2 << 32), ... }. */
+static ASM_FUNC_ATTR_INLINE VEC64_T
+FN(ctr_init) (MASK64_T even, u64 ctr_lo, u64 ctr_hi)
+{
+  size_t vl64 = VSETVLMAX_E64();
+  VEC64_T c, offs;
+
+  offs = __riscv_vsll(__riscv_vsrl(V64(vid_v)(vl64), 1, vl64), 32, vl64);
+  offs = __riscv_vmerge(offs, 0, even, vl64);
+  c = V64(vmv_v_x)(ctr_hi, vl64);
+  c = __riscv_vmerge(c, ctr_lo, even, vl64);
+  return __riscv_vadd(c, offs, vl64);
+}
+
+/* Returns { 0, nblocks << 32, 0, nblocks << 32, ... }. */
+static ASM_FUNC_ATTR_INLINE VEC64_T
+FN(ctr_inc) (MASK64_T even, size_t nblocks)
+{
+  size_t vl64 = VSETVLMAX_E64();
+
+  return __riscv_vmerge(V64(vmv_v_x)((u64)nblocks << 32, vl64), 0, even,
+			vl64);
+}
+
+static ASM_FUNC_ATTR_INLINE VEC32_T
+FN(sm4_crypt) (SM4_ROUND_KEY_PARAMS, MASK64_T even, VEC32_T x, size_t vl)
+{
+  size_t vl64 = vl / 2;
+  VEC64_T t, y;
+
+#ifdef HAVE_BROKEN_VSM4R_VS_INTRINSIC
+  asm ( "vsetvli zero,%[vl],e32," STR2(SM4_LMUL) ",ta,ma;\n\t"
+	"vsm4r.vs %[x],%[rk0];\n\t"
+	"vsm4r.vs %[x],%[rk1];\n\t"
+	"vsm4r.vs %[x],%[rk2];\n\t"
+	"vsm4r.vs %[x],%[rk3];\n\t"
+	"vsm4r.vs %[x],%[rk4];\n\t"
+	"vsm4r.vs %[x],%[rk5];\n\t"
+	"vsm4r.vs %[x],%[rk6];\n\t"
+	"vsm4r.vs %[x],%[rk7];\n\t"
+	: [x] "+vr" (x)
+	: [vl] "r" (vl),
+	  [rk0] "vr" (rk0), [rk1] "vr" (rk1), [rk2] "vr" (rk2),
+	  [rk3] "vr" (rk3), [rk4] "vr" (rk4), [rk5] "vr" (rk5),
+	  [rk6] "vr" (rk6), [rk7] "vr" (rk7)
+	: "vl", "vtype", "memory");
+#else
+  x = __riscv_vsm4r_vs(x, rk0, vl);
+  x = __riscv_vsm4r_vs(x, rk1, vl);
+  x = __riscv_vsm4r_vs(x, rk2, vl);
+  x = __riscv_vsm4r_vs(x, rk3, vl);
+  x = __riscv_vsm4r_vs(x, rk4, vl);
+  x = __riscv_vsm4r_vs(x, rk5, vl);
+  x = __riscv_vsm4r_vs(x, rk6, vl);
+  x = __riscv_vsm4r_vs(x, rk7, vl);
+#endif
+
+  t = __riscv_vrev8(V64(vreinterpret)(x), vl64);
+  y = __riscv_vslide1up(t, 0, vl64);
+  y = __riscv_vslidedown_mu(even, y, t, 1, vl64);
+  return V32(vreinterpret)(y);
+}
+
+static ASM_FUNC_ATTR_INLINE void
+FN(sm4_ecb_blocks) (SM4_ROUND_KEY_PARAMS, MASK64_T even, byte *out,
+		    const byte *in, size_t n)
+{
+  size_t vl = n * 4;
+  VEC32_T x;
+
+  x = __riscv_vrev8(FN(load_u32)(in, vl), vl);
+  x = FN(sm4_crypt)(SM4_ROUND_KEYS, even, x, vl);
+  FN(store_u32)(out, x, vl);
+}
+
+static ASM_FUNC_ATTR_INLINE void
+FN(sm4_ctr_blocks) (SM4_ROUND_KEY_PARAMS, MASK64_T even, VEC64_T *ctr,
+		    VEC64_T inc, byte *out, const byte *in, size_t n)
+{
+  size_t vl = n * 4;
+  size_t vl64 = n * 2;
+  VEC32_T x, d;
+
+  d = FN(load_u32)(in, vl);
+  x = V32(vreinterpret)(*ctr);
+  *ctr = __riscv_vadd(*ctr, inc, vl64);
+
+  x = FN(sm4_crypt)(SM4_ROUND_KEYS, even, x, vl);
+  x = __riscv_vxor(x, d, vl);
+  FN(store_u32)(out, x, vl);
+}
+
+static ASM_FUNC_ATTR_INLINE void
+FN(sm4_cbc_dec_blocks) (SM4_ROUND_KEY_PARAMS, MASK64_T even, vuint32m1_t *iv,
+			VEC32_T *ivs, byte *out, const byte *in, size_t n)
+{
+  size_t vl = n * 4;
+  VEC32_T d, x;
+
+  d = FN(load_u32)(in, vl);
+  *ivs = __riscv_vslideup(SET_IV(*ivs, *iv), d, 4, vl);
+  *iv = load_u32_m1(in + (n - 1) * 16, 4);
+
+  x = __riscv_vrev8(d, vl);
+  x = FN(sm4_crypt)(SM4_ROUND_KEYS, even, x, vl);
+  x = __riscv_vxor(x, *ivs, vl);
+  FN(store_u32)(out, x, vl);
+}
+
+static ASM_FUNC_ATTR_INLINE void
+FN(sm4_cfb_dec_blocks) (SM4_ROUND_KEY_PARAMS, MASK64_T even, vuint32m1_t *iv,
+			VEC32_T *ivs, byte *out, const byte *in, size_t n)
+{
+  size_t vl = n * 4;
+  VEC32_T d, x;
+
+  d = FN(load_u32)(in, vl);
+  *ivs = __riscv_vslideup(SET_IV(*ivs, *iv), d, 4, vl);
+  *iv = load_u32_m1(in + (n - 1) * 16, 4);
+
+  x = __riscv_vrev8(*ivs, vl);
+  x = FN(sm4_crypt)(SM4_ROUND_KEYS, even, x, vl);
+  x = __riscv_vxor(x, d, vl);
+  FN(store_u32)(out, x, vl);
+}
+
+#undef SET_IV
+#undef SET_IV_m4
+#undef SET_IV_m2
+#undef SET_IV_m1
+#undef MASK64_T
+#undef VEC64_T
+#undef VEC32_T
+#undef VSETVLMAX_E64
+#undef V8
+#undef V64
+#undef V32
+#undef FN
+#undef CAT
+#undef CAT_
+#undef SM4_MASK64_T
+#undef SM4_LMUL
diff --git a/cipher/sm4.c b/cipher/sm4.c
index 58aaea10..95cabdf3 100644
--- a/cipher/sm4.c
+++ b/cipher/sm4.c
@@ -142,6 +142,15 @@
 # define USE_PPC_CRYPTO 1
 #endif
 
+/* USE_RISCV_V_CRYPTO indicates whether to enable RISC-V vector cryptography
+ * extension code. */
+#undef USE_RISCV_V_CRYPTO
+#if defined (__riscv) && \
+    defined(HAVE_COMPATIBLE_CC_RISCV_VECTOR_INTRINSICS) && \
+    defined(HAVE_COMPATIBLE_CC_RISCV_VECTOR_CRYPTO_INTRINSICS)
+# define USE_RISCV_V_CRYPTO 1
+#endif
+
 static const char *sm4_selftest (void);
 
 static void _gcry_sm4_ctr_enc (void *context, unsigned char *ctr,
@@ -216,6 +225,9 @@ typedef struct
   unsigned int use_ppc8le:1;
   unsigned int use_ppc9le:1;
 #endif
+#ifdef USE_RISCV_V_CRYPTO
+  unsigned int use_riscv_zvksed:1;
+#endif
 #ifdef ASM_FUNC_ABI
   unsigned int ocb_crypt_blk_init:1;
   unsigned int ocb_auth_blk_init:1;
@@ -756,6 +768,36 @@ sm4_ppc9le_crypt_blk1_16(void *rk, byte *out, const byte *in, size_t num_blks)
 }
 #endif /* USE_PPC_CRYPTO */
 
+#ifdef USE_RISCV_V_CRYPTO
+extern unsigned int _gcry_sm4_riscv_v_check_hw(void);
+
+extern void _gcry_sm4_riscv_zvksed_expand_key(const byte *key, u32 *rk_enc,
+					      u32 *rk_dec, const u32 *fk);
+
+extern unsigned int _gcry_sm4_riscv_zvksed_crypt(const u32 *rk, byte *out,
+						 const byte *in,
+						 size_t num_blks);
+
+extern void _gcry_sm4_riscv_zvksed_ctr_enc(const u32 *rk_enc, byte *out,
+					   const byte *in, byte *ctr,
+					   size_t nblocks);
+
+extern void _gcry_sm4_riscv_zvksed_cbc_dec(const u32 *rk_dec, byte *out,
+					   const byte *in, byte *iv,
+					   size_t nblocks);
+
+extern void _gcry_sm4_riscv_zvksed_cfb_dec(const u32 *rk_enc, byte *out,
+					   const byte *in, byte *iv,
+					   size_t nblocks);
+
+static inline unsigned int
+sm4_riscv_zvksed_crypt_blk1_16(void *rk, byte *out, const byte *in,
+			       size_t num_blks)
+{
+  return _gcry_sm4_riscv_zvksed_crypt(rk, out, in, num_blks);
+}
+#endif /* USE_RISCV_V_CRYPTO */
+
 static inline void prefetch_sbox_table(void)
 {
   const volatile byte *vtab = (void *)&sbox_table.S[0];
@@ -870,6 +912,15 @@ sm4_expand_key (SM4_context *ctx, const byte *key)
     }
 #endif
 
+#ifdef USE_RISCV_V_CRYPTO
+  if (ctx->use_riscv_zvksed)
+    {
+      _gcry_sm4_riscv_zvksed_expand_key (key, ctx->rkey_enc, ctx->rkey_dec,
+					 fk);
+      return;
+    }
+#endif
+
   prefetch_sbox_table ();
 
   rk[0] = buf_get_be32(key + 4 * 0) ^ fk[0];
@@ -953,6 +1004,14 @@ sm4_setkey (void *context, const byte *key, const unsigned keylen,
   ctx->use_ppc8le = (hwf & HWF_PPC_VCRYPTO) != 0;
   ctx->use_ppc9le = (hwf & HWF_PPC_VCRYPTO) && (hwf & HWF_PPC_ARCH_3_00);
 #endif
+#ifdef USE_RISCV_V_CRYPTO
+  ctx->use_riscv_zvksed = (hwf & HWF_RISCV_IMAFDC)
+			  && (hwf & HWF_RISCV_B)      /* Mandatory in RVA23U64 */
+			  && (hwf & HWF_RISCV_V)      /* Mandatory in RVA23U64 */
+			  && (hwf & HWF_RISCV_ZVKB)   /* Mandatory in RVA23U64 (Zvbb) */
+			  && (hwf & HWF_RISCV_ZVKSED) /* Optional in RVA23U64 (Zvks) */
+			  && _gcry_sm4_riscv_v_check_hw();
+#endif
 
 #ifdef USE_GFNI_AVX2
   if (ctx->use_gfni_avx2)
@@ -1056,6 +1115,11 @@ sm4_encrypt (void *context, byte *outbuf, const byte *inbuf)
     return sm4_armv8_ce_crypt_blk1_16(ctx->rkey_enc, outbuf, inbuf, 1);
 #endif
 
+#ifdef USE_RISCV_V_CRYPTO
+  if (ctx->use_riscv_zvksed)
+    return sm4_riscv_zvksed_crypt_blk1_16(ctx->rkey_enc, outbuf, inbuf, 1);
+#endif
+
   prefetch_sbox_table ();
 
   return sm4_do_crypt (ctx->rkey_enc, outbuf, inbuf);
@@ -1086,6 +1150,11 @@ sm4_decrypt (void *context, byte *outbuf, const byte *inbuf)
     return sm4_armv8_ce_crypt_blk1_16(ctx->rkey_dec, outbuf, inbuf, 1);
 #endif
 
+#ifdef USE_RISCV_V_CRYPTO
+  if (ctx->use_riscv_zvksed)
+    return sm4_riscv_zvksed_crypt_blk1_16(ctx->rkey_dec, outbuf, inbuf, 1);
+#endif
+
   prefetch_sbox_table ();
 
   return sm4_do_crypt (ctx->rkey_dec, outbuf, inbuf);
@@ -1232,6 +1301,12 @@ sm4_get_crypt_blk1_16_fn(SM4_context *ctx)
     {
       return &sm4_ppc8le_crypt_blk1_16;
     }
+#endif
+#ifdef USE_RISCV_V_CRYPTO
+  else if (ctx->use_riscv_zvksed)
+    {
+      return &sm4_riscv_zvksed_crypt_blk1_16;
+    }
 #endif
   else
     {
@@ -1364,6 +1439,15 @@ _gcry_sm4_ctr_enc(void *context, unsigned char *ctr,
     }
 #endif
 
+#ifdef USE_RISCV_V_CRYPTO
+  if (ctx->use_riscv_zvksed)
+    {
+      /* Process all blocks at a time. */
+      _gcry_sm4_riscv_zvksed_ctr_enc(ctx->rkey_enc, outbuf, inbuf, ctr, nblocks);
+      nblocks = 0;
+    }
+#endif
+
 #ifdef USE_ARM_CE
   if (ctx->use_arm_ce)
     {
@@ -1543,6 +1627,15 @@ _gcry_sm4_cbc_dec(void *context, unsigned char *iv,
     }
 #endif
 
+#ifdef USE_RISCV_V_CRYPTO
+  if (ctx->use_riscv_zvksed)
+    {
+      /* Process all blocks at a time. */
+      _gcry_sm4_riscv_zvksed_cbc_dec(ctx->rkey_dec, outbuf, inbuf, iv, nblocks);
+      nblocks = 0;
+    }
+#endif
+
 #ifdef USE_ARM_CE
   if (ctx->use_arm_ce)
     {
@@ -1722,6 +1815,15 @@ _gcry_sm4_cfb_dec(void *context, unsigned char *iv,
     }
 #endif
 
+#ifdef USE_RISCV_V_CRYPTO
+  if (ctx->use_riscv_zvksed)
+    {
+      /* Process all blocks at a time. */
+      _gcry_sm4_riscv_zvksed_cfb_dec(ctx->rkey_enc, outbuf, inbuf, iv, nblocks);
+      nblocks = 0;
+    }
+#endif
+
 #ifdef USE_ARM_CE
   if (ctx->use_arm_ce)
     {
@@ -1806,6 +1908,12 @@ sm4_crypt_blk1_32 (SM4_context *ctx, byte *outbuf, const byte *inbuf,
       return 0;
     }
 #endif
+#ifdef USE_RISCV_V_CRYPTO
+  if (ctx->use_riscv_zvksed)
+    {
+      return _gcry_sm4_riscv_zvksed_crypt (rk, outbuf, inbuf, num_blks);
+    }
+#endif
 
   if (num_blks > 16)
     {
diff --git a/configure.ac b/configure.ac
index a6b1f325..b10af44c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3054,6 +3054,15 @@ m4_define([GCRY_RISCV_VECTOR_CRYPTO_INTRINSICS_TEST],
 	e = __riscv_vsm3c_vi_u32m1(e, g, 31, vl);
 	__riscv_vse32_v_u32m1(ptr + 0 * vl, e, vl);
       }
+      void test_sm4(unsigned int *ptr)
+      {
+	int vl = __riscv_vsetvl_e32m4 (16);
+	vuint32m1_t a = __riscv_vle32_v_u32m1(ptr + 0 * vl, 4);
+	vuint32m4_t b = __riscv_vle32_v_u32m4(ptr + 1 * vl, vl);
+	vuint32m1_t c = __riscv_vsm4k_vi_u32m1(a, 7, 4);
+	vuint32m4_t d = __riscv_vsm4r_vs_u32m1_u32m4(b, c, vl);
+	__riscv_vse32_v_u32m4(ptr + 0 * vl, d, vl);
+      }
       void test_rev8(unsigned int *ptr)
       {
 	int vl = __riscv_vsetvl_e32m1 (4);
@@ -3093,6 +3102,7 @@ m4_define([GCRY_RISCV_VECTOR_CRYPTO_INTRINSICS_TEST],
 	test_aes_crypt(buf);
 	test_ghash(buf);
 	test_sm3(buf);
+	test_sm4(buf);
 	test_rev8(buf);
 	test_rotate(buf);
 	test_inline_vec_asm(buf);
@@ -3254,6 +3264,56 @@ EOF
   CFLAGS=$_gcc_cflags_save;
 fi
 
+#
+# Check whether compiler has working RISC-V vector __riscv_vsm4r_vs intrinsics
+#
+# Same GCC issue as with __riscv_vaes*_vs intrinsics above, 'vsetvli' is
+# generated with 'm1' LMUL instead of 'mX' for __riscv_vsm4r_vs_u32m1_u32mX.
+#
+if test "$gcry_cv_cc_riscv_vector_crypto_intrinsics" = "yes" ||
+   test "$gcry_cv_cc_riscv_vector_crypto_intrinsics_cflags" = "yes"; then
+
+  # Setup flags for test.
+  _gcc_cflags_save=$CFLAGS
+  if test "$gcry_cv_cc_riscv_vector_crypto_intrinsics_cflags" = "yes"; then
+    CFLAGS="$CFLAGS -O2 -march=$MARCH_RVA23U64_WITH_VEC_CRYPTO"
+  else
+    CFLAGS="$CFLAGS -O2"
+  fi
+
+  # Disable LTO optimization for following check to generate
+  # RISC-V instructions in compiler output
+  CFLAGS="$CFLAGS -fno-lto"
+
+  AC_CACHE_CHECK([whether compiler has working RISC-V __riscv_vsm4r_vs intrinsics],
+    [gcry_cv_riscv_vsm4r_vs_intrinsics_work],
+    [gcry_cv_riscv_vsm4r_vs_intrinsics_work=no
+     cat > conftest.c <<EOF
+#include <riscv_vector.h>
+vuint32m4_t test_fn(vuint32m4_t a, vuint32m1_t b, int vl)
+{
+  /* This intrinsic should result in a 'vsetvli' with m4 */
+  return __riscv_vsm4r_vs_u32m1_u32m4(a, b, vl);
+}
+EOF
+
+    if $CC $CFLAGS -S conftest.c -o conftest.s >&5 2>&5; then
+      if grep -E 'vsetvli.*,[[[:space:]]]*m4[[[:space:]]]*,' conftest.s >/dev/null 2>&1; then
+	gcry_cv_riscv_vsm4r_vs_intrinsics_work=yes
+      fi
+    fi
+    rm -f conftest.*
+  ])
+
+  if test "$gcry_cv_riscv_vsm4r_vs_intrinsics_work" = "no"; then
+    AC_DEFINE([HAVE_BROKEN_VSM4R_VS_INTRINSIC], [1],
+	      [Define to 1 if __riscv_vsm4r_vs intrinsics are broken])
+  fi
+
+  # Restore flags.
+  CFLAGS=$_gcc_cflags_save;
+fi
+
 
 #######################################
 #### Checks for library functions. ####
@@ -3973,6 +4033,10 @@ if test "$found" = "1" ; then
          # Build with the ppc64le vector implementation
          GCRYPT_ASM_CIPHERS="$GCRYPT_ASM_CIPHERS sm4-ppc.lo"
       ;;
+      riscv64-*-*)
+         # Build with the RISC-V vector cryptography implementation
+         GCRYPT_ASM_CIPHERS="$GCRYPT_ASM_CIPHERS sm4-riscv-zvksed-zvkb.lo"
+      ;;
    esac
 fi
 
diff --git a/doc/gcrypt.texi b/doc/gcrypt.texi
index 613920d8..18a6f24c 100644
--- a/doc/gcrypt.texi
+++ b/doc/gcrypt.texi
@@ -623,6 +623,7 @@ are
 @item riscv-zvkned
 @item riscv-zvknha
 @item riscv-zvknhb
+ at item riscv-zvksed
 @item riscv-zvksh
 @end table
 
diff --git a/src/g10lib.h b/src/g10lib.h
index 8a2d706c..a9c18db6 100644
--- a/src/g10lib.h
+++ b/src/g10lib.h
@@ -288,7 +288,8 @@ char **_gcry_strtokenize (const char *string, const char *delim);
 #define HWF_RISCV_ZVKNED        (1 << 7)
 #define HWF_RISCV_ZVKNHA        (1 << 8)
 #define HWF_RISCV_ZVKNHB        (1 << 9)
-#define HWF_RISCV_ZVKSH         (1 << 10)
+#define HWF_RISCV_ZVKSED        (1 << 10)
+#define HWF_RISCV_ZVKSH         (1 << 11)
 
 #endif
 
diff --git a/src/hwf-riscv.c b/src/hwf-riscv.c
index adec2b02..192ab031 100644
--- a/src/hwf-riscv.c
+++ b/src/hwf-riscv.c
@@ -197,6 +197,7 @@ detect_riscv_at_hwcap(void)
 #define HWF_RISCV_HWPROBE_EXT_ZVKNED        (1U << 21)
 #define HWF_RISCV_HWPROBE_EXT_ZVKNHA        (1U << 22)
 #define HWF_RISCV_HWPROBE_EXT_ZVKNHB        (1U << 23)
+#define HWF_RISCV_HWPROBE_EXT_ZVKSED        (1U << 24)
 #define HWF_RISCV_HWPROBE_EXT_ZVKSH         (1U << 25)
 #define HWF_RISCV_HWPROBE_EXT_ZICOND        (U64_C(1) << 35)
 
@@ -228,6 +229,7 @@ static const struct hwprobe_feature_map_s hwprobe_features[] =
     { HWF_RISCV_HWPROBE_EXT_ZVKNED,  HWF_RISCV_ZVKNED },
     { HWF_RISCV_HWPROBE_EXT_ZVKNHA,  HWF_RISCV_ZVKNHA },
     { HWF_RISCV_HWPROBE_EXT_ZVKNHB,  HWF_RISCV_ZVKNHB },
+    { HWF_RISCV_HWPROBE_EXT_ZVKSED,  HWF_RISCV_ZVKSED },
     { HWF_RISCV_HWPROBE_EXT_ZVKSH,   HWF_RISCV_ZVKSH },
   };
 
diff --git a/src/hwfeatures.c b/src/hwfeatures.c
index 2f836079..3067ee86 100644
--- a/src/hwfeatures.c
+++ b/src/hwfeatures.c
@@ -127,6 +127,7 @@ static struct
     { HWF_RISCV_ZVKNED,        "riscv-zvkned" },
     { HWF_RISCV_ZVKNHA,        "riscv-zvknha" },
     { HWF_RISCV_ZVKNHB,        "riscv-zvknhb" },
+    { HWF_RISCV_ZVKSED,        "riscv-zvksed" },
     { HWF_RISCV_ZVKSH,         "riscv-zvksh" },
 #endif
   };
-- 
2.53.0



More information about the Gcrypt-devel mailing list