Add basic RISC-V cpuid and OPENSSL_riscvcap
authorHenry Brausen <henry.brausen@vrull.eu>
Fri, 28 Jan 2022 08:28:52 +0000 (01:28 -0700)
committerPauli <pauli@openssl.org>
Thu, 19 May 2022 06:32:49 +0000 (16:32 +1000)
RISC-V cpuid implementation allows bitmanip extensions Zb[abcs] to
be enabled at runtime using OPENSSL_riscvcap environment variable.

For example, to specify 64-bit RISC-V with the G,C,Zba,Zbb,Zbc
extensions, one could write: OPENSSL_riscvcap="rv64gc_zba_zbb_zbc"

Architecture string parsing is still very primitive, but can be
expanded in the future. Currently, only bitmanip extensions Zba, Zbb,
Zbc and Zbs are supported.

Includes implementation of constant-time CRYPTO_memcmp in riscv64 asm,
as well as OPENSSL_cleanse. Assembly implementations are written using
perlasm.

Reviewed-by: Philipp Tomsich <philipp.tomsich@vrull.eu>
Signed-off-by: Henry Brausen <henry.brausen@vrull.eu>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/17640)

crypto/build.info
crypto/riscv64cpuid.pl [new file with mode: 0644]
crypto/riscvcap.c [new file with mode: 0644]
doc/man7/openssl-env.pod
include/crypto/riscv_arch.def [new file with mode: 0644]
include/crypto/riscv_arch.h [new file with mode: 0644]

index 8c7a3a9c22c51c3e8664fa9cf9c4400d15ef68fb..bb5959fb44a47afd2d5afa2cd41e6dab17fdcb79 100644 (file)
@@ -51,6 +51,8 @@ IF[{- !$disabled{asm} && $config{processor} ne '386' -}]
 
   $CPUIDASM_c64xplus=c64xpluscpuid.s
 
+  $CPUIDASM_riscv64=riscvcap.c riscv64cpuid.s
+
   # Now that we have defined all the arch specific variables, use the
   # appropriate one, and define the appropriate macros
   IF[$CPUIDASM_{- $target{asm_arch} -}]
@@ -130,6 +132,7 @@ GENERATE[armv4cpuid.S]=armv4cpuid.pl
 INCLUDE[armv4cpuid.o]=.
 GENERATE[s390xcpuid.S]=s390xcpuid.pl
 INCLUDE[s390xcpuid.o]=.
+GENERATE[riscv64cpuid.s]=riscv64cpuid.pl
 
 IF[{- $config{target} =~ /^(?:Cygwin|mingw|VC-|BC-)/ -}]
   SHARED_SOURCE[../libcrypto]=dllmain.c
diff --git a/crypto/riscv64cpuid.pl b/crypto/riscv64cpuid.pl
new file mode 100644 (file)
index 0000000..675e9b6
--- /dev/null
@@ -0,0 +1,89 @@
+#! /usr/bin/env perl
+# Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the Apache License 2.0 (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# $output is the last argument if it looks like a file (it has an extension)
+# $flavour is the first argument if it doesn't look like a file
+$output = $#ARGV >= 0 && $ARGV[$#ARGV] =~ m|\.\w+$| ? pop : undef;
+$flavour = $#ARGV >= 0 && $ARGV[0] !~ m|\.| ? shift : undef;
+
+$output and open STDOUT,">$output";
+
+{
+my ($in_a,$in_b,$len,$x,$temp1,$temp2) = ('a0','a1','a2','t0','t1','t2');
+$code.=<<___;
+################################################################################
+# int CRYPTO_memcmp(const void * in_a, const void * in_b, size_t len)
+################################################################################
+.text
+.balign 16
+.globl CRYPTO_memcmp
+.type   CRYPTO_memcmp,\@function
+CRYPTO_memcmp:
+    li      $x,0
+    beqz    $len,2f   # len == 0
+1:
+    lbu     $temp1,0($in_a)
+    lbu     $temp2,0($in_b)
+    addi    $in_a,$in_a,1
+    addi    $in_b,$in_b,1
+    addi    $len,$len,-1
+    xor     $temp1,$temp1,$temp2
+    or      $x,$x,$temp1
+    bgtz    $len,1b
+2:
+    mv      a0,$x
+    ret
+___
+}
+{
+my ($ptr,$len,$temp1,$temp2) = ('a0','a1','t0','t1');
+$code.=<<___;
+################################################################################
+# void OPENSSL_cleanse(void *ptr, size_t len)
+################################################################################
+.text
+.balign 16
+.globl OPENSSL_cleanse
+.type   OPENSSL_cleanse,\@function
+OPENSSL_cleanse:
+    beqz    $len,2f         # len == 0, return
+    srli    $temp1,$len,4
+    bnez    $temp1,3f       # len > 15
+
+1:  # Store <= 15 individual bytes
+    sb      x0,0($ptr)
+    addi    $ptr,$ptr,1
+    addi    $len,$len,-1
+    bnez    $len,1b
+2:
+    ret
+
+3:  # Store individual bytes until we are aligned
+    andi    $temp1,$ptr,0x7
+    beqz    $temp1,4f
+    sb      x0,0($ptr)
+    addi    $ptr,$ptr,1
+    addi    $len,$len,-1
+    j       3b
+
+4:  # Store aligned dwords
+    li      $temp2,8
+4:
+    sd      x0,0($ptr)
+    addi    $ptr,$ptr,8
+    addi    $len,$len,-8
+    bge     $len,$temp2,4b  # if len>=8 loop
+    bnez    $len,1b         # if len<8 and len != 0, store remaining bytes
+    ret
+___
+}
+
+
+print $code;
+close STDOUT or die "error closing STDOUT: $!";
diff --git a/crypto/riscvcap.c b/crypto/riscvcap.c
new file mode 100644 (file)
index 0000000..1cbfb4a
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdint.h>
+#include <openssl/crypto.h>
+#include "internal/cryptlib.h"
+
+#define OPENSSL_RISCVCAP_IMPL
+#include "crypto/riscv_arch.h"
+
+static void parse_env(const char *envstr);
+static void strtoupper(char *str);
+
+uint32_t OPENSSL_rdtsc(void)
+{
+    return 0;
+}
+
+size_t OPENSSL_instrument_bus(unsigned int *out, size_t cnt)
+{
+    return 0;
+}
+
+size_t OPENSSL_instrument_bus2(unsigned int *out, size_t cnt, size_t max)
+{
+    return 0;
+}
+
+static void strtoupper(char *str)
+{
+    for (char *x = str; *x; ++x)
+        *x = toupper(*x);
+}
+
+/* parse_env() parses a RISC-V architecture string. An example of such a string
+ * is "rv64gc_zba_zbb_zbc_zbs". Currently, the rv64gc part is ignored
+ * and we simply search for "_[extension]" in the arch string to see if we
+ * should enable a given extension.
+ */
+#define BUFLEN 256
+static void parse_env(const char *envstr)
+{
+    char envstrupper[BUFLEN];
+    char buf[BUFLEN];
+
+    /* Convert env str to all uppercase */
+    OPENSSL_strlcpy(envstrupper, envstr, sizeof(envstrupper));
+    strtoupper(envstrupper);
+
+    for (size_t i = 0; i < kRISCVNumCaps; ++i) {
+        /* Prefix capability with underscore in preparation for search */
+        BIO_snprintf(buf, BUFLEN, "_%s", RISCV_capabilities[i].name);
+        if (strstr(envstrupper, buf) != NULL) {
+            /* Match, set relevant bit in OPENSSL_riscvcap_P[] */
+            OPENSSL_riscvcap_P[RISCV_capabilities[i].index] |=
+                (1 << RISCV_capabilities[i].bit_offset);
+        }
+    }
+}
+
+# if defined(__GNUC__) && __GNUC__>=2
+__attribute__ ((constructor))
+# endif
+void OPENSSL_cpuid_setup(void)
+{
+    char *e;
+    static int trigger = 0;
+
+    if (trigger != 0)
+        return;
+    trigger = 1;
+
+    if ((e = getenv("OPENSSL_riscvcap"))) {
+        parse_env(e);
+        return;
+    }
+}
index a2443d54d8229138160a10c64ff7e32d6321e942..922d3c1476c300d693e3e4882a7c0a9df8f8b087 100644 (file)
@@ -74,7 +74,7 @@ See L<SSL_CTX_load_verify_locations(3)>.
 
 Additional arguments for the L<tsget(1)> command.
 
-=item B<OPENSSL_ia32cap>, B<OPENSSL_sparcv9cap>, B<OPENSSL_ppccap>, B<OPENSSL_armcap>, B<OPENSSL_s390xcap>
+=item B<OPENSSL_ia32cap>, B<OPENSSL_sparcv9cap>, B<OPENSSL_ppccap>, B<OPENSSL_armcap>, B<OPENSSL_s390xcap>, B<OPENSSL_riscvcap>
 
 OpenSSL supports a number of different algorithm implementations for
 various machines and, by default, it determines which to use based on the
@@ -91,7 +91,7 @@ See L<OSSL_HTTP_parse_url(3)>.
 
 =head1 COPYRIGHT
 
-Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
+Copyright 2019-2022 The OpenSSL Project Authors. All Rights Reserved.
 
 Licensed under the Apache License 2.0 (the "License").  You may not use
 this file except in compliance with the License.  You can obtain a copy
diff --git a/include/crypto/riscv_arch.def b/include/crypto/riscv_arch.def
new file mode 100644 (file)
index 0000000..fb18b1b
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* X Macro Definitions for Specification of RISC-V Arch Capabilities */
+
+/*
+ * Each RISC-V capability ends up encoded as a single set bit in an array of
+ * words. When specifying a new capability, write a new RISCV_DEFINE_CAP
+ * statement, with an argument as the extension name in all-caps,
+ * second argument as the index in the array where the capability will be stored
+ * and third argument as the index of the bit to be used to encode the
+ * capability.
+ * RISCV_DEFINE_CAP(EXTENSION NAME, array index, bit index) */
+
+RISCV_DEFINE_CAP(ZBA, 0, 0)
+RISCV_DEFINE_CAP(ZBB, 0, 1)
+RISCV_DEFINE_CAP(ZBC, 0, 2)
+RISCV_DEFINE_CAP(ZBS, 0, 3)
+
+/*
+ * In the future ...
+ * RISCV_DEFINE_CAP(ZFOO, 0, 31)
+ * RISCV_DEFINE_CAP(ZBAR, 1, 0)
+ * ... and so on.
+ */
+
+#undef RISCV_DEFINE_CAP
diff --git a/include/crypto/riscv_arch.h b/include/crypto/riscv_arch.h
new file mode 100644 (file)
index 0000000..89a40be
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef OSSL_CRYPTO_RISCV_ARCH_H
+# define OSSL_CRYPTO_RISCV_ARCH_H
+
+# include <ctype.h>
+# include <stdint.h>
+
+# define RISCV_DEFINE_CAP(NAME, INDEX, BIT_INDEX) +1
+extern uint32_t OPENSSL_riscvcap_P[ ((
+# include "riscv_arch.def"
+) + sizeof(uint32_t) - 1) / sizeof(uint32_t) ];
+
+# ifdef OPENSSL_RISCVCAP_IMPL
+#  define RISCV_DEFINE_CAP(NAME, INDEX, BIT_INDEX) +1
+uint32_t OPENSSL_riscvcap_P[ ((
+#  include "riscv_arch.def"
+) + sizeof(uint32_t) - 1) / sizeof(uint32_t) ];
+# endif
+
+# define RISCV_DEFINE_CAP(NAME, INDEX, BIT_INDEX)                   \
+    static inline int RISCV_HAS_##NAME(void)                        \
+    {                                                               \
+        return (OPENSSL_riscvcap_P[INDEX] & (1 << BIT_INDEX)) != 0; \
+    }
+# include "riscv_arch.def"
+
+struct RISCV_capability_s {
+    const char *name;
+    size_t index;
+    size_t bit_offset;
+};
+
+# define RISCV_DEFINE_CAP(NAME, INDEX, BIT_INDEX) +1
+extern const struct RISCV_capability_s RISCV_capabilities[
+# include "riscv_arch.def"
+];
+
+# ifdef OPENSSL_RISCVCAP_IMPL
+#  define RISCV_DEFINE_CAP(NAME, INDEX, BIT_INDEX) \
+    { #NAME, INDEX, BIT_INDEX },
+const struct RISCV_capability_s RISCV_capabilities[] = {
+#  include "riscv_arch.def"
+};
+# endif
+
+# define RISCV_DEFINE_CAP(NAME, INDEX, BIT_INDEX) +1
+static const size_t kRISCVNumCaps =
+# include "riscv_arch.def"
+;
+
+#endif