Browse Source

Add basic RISC-V cpuid and OPENSSL_riscvcap

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)
Henry Brausen 2 years ago
parent
commit
360f6dcc5a

+ 3 - 0
crypto/build.info

@@ -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

+ 89 - 0
crypto/riscv64cpuid.pl

@@ -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: $!";

+ 86 - 0
crypto/riscvcap.c

@@ -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;
+    }
+}

+ 2 - 2
doc/man7/openssl-env.pod

@@ -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

+ 33 - 0
include/crypto/riscv_arch.def

@@ -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

+ 59 - 0
include/crypto/riscv_arch.h

@@ -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