mirror of
https://git.yoctoproject.org/meta-ti
synced 2026-07-16 14:27:48 +00:00
6efc27aedc
* Update to the latest PSP release (version 04.06.00.10) of the 3.2 kernel. * Rename recipe to match meta-ti kernel recipe naming convention. * Use in tree defconfig instead of providing a separate defconfig. These defconfigs were the same anyway. * Add additional crypto patches that were needed to fix suspend and resume issues. Signed-off-by: Franklin S. Cooper Jr <fcooper@ti.com> Signed-off-by: Denys Dmytriyenko <denys@ti.com>
7229 lines
202 KiB
Diff
7229 lines
202 KiB
Diff
From a97aac248717d62bdbf322c1d6d422ddfde87de0 Mon Sep 17 00:00:00 2001
|
|
From: Greg Turner <gregturner@ti.com>
|
|
Date: Thu, 3 May 2012 10:33:13 -0500
|
|
Subject: [PATCH 2/2] AM335x OCF Driver for Linux 3
|
|
|
|
---
|
|
crypto/Kconfig | 3 +
|
|
crypto/Makefile | 2 +
|
|
crypto/ocf/Config.in | 20 +
|
|
crypto/ocf/Kconfig | 48 ++
|
|
crypto/ocf/Makefile | 138 ++++
|
|
crypto/ocf/criov.c | 215 +++++
|
|
crypto/ocf/crypto.c | 1766 ++++++++++++++++++++++++++++++++++++++++++
|
|
crypto/ocf/cryptodev.c | 1069 +++++++++++++++++++++++++
|
|
crypto/ocf/cryptodev.h | 480 ++++++++++++
|
|
crypto/ocf/cryptosoft.c | 1322 +++++++++++++++++++++++++++++++
|
|
crypto/ocf/ocf-bench.c | 514 ++++++++++++
|
|
crypto/ocf/ocf-compat.h | 372 +++++++++
|
|
crypto/ocf/ocfnull/Makefile | 12 +
|
|
crypto/ocf/ocfnull/ocfnull.c | 204 +++++
|
|
crypto/ocf/random.c | 317 ++++++++
|
|
crypto/ocf/rndtest.c | 300 +++++++
|
|
crypto/ocf/rndtest.h | 54 ++
|
|
crypto/ocf/uio.h | 54 ++
|
|
drivers/char/random.c | 67 ++
|
|
fs/fcntl.c | 1 +
|
|
include/linux/miscdevice.h | 1 +
|
|
include/linux/random.h | 28 +
|
|
kernel/pid.c | 1 +
|
|
23 files changed, 6988 insertions(+), 0 deletions(-)
|
|
create mode 100755 crypto/ocf/Config.in
|
|
create mode 100755 crypto/ocf/Kconfig
|
|
create mode 100755 crypto/ocf/Makefile
|
|
create mode 100644 crypto/ocf/criov.c
|
|
create mode 100644 crypto/ocf/crypto.c
|
|
create mode 100644 crypto/ocf/cryptodev.c
|
|
create mode 100644 crypto/ocf/cryptodev.h
|
|
create mode 100644 crypto/ocf/cryptosoft.c
|
|
create mode 100644 crypto/ocf/ocf-bench.c
|
|
create mode 100644 crypto/ocf/ocf-compat.h
|
|
create mode 100644 crypto/ocf/ocfnull/Makefile
|
|
create mode 100644 crypto/ocf/ocfnull/ocfnull.c
|
|
create mode 100644 crypto/ocf/random.c
|
|
create mode 100644 crypto/ocf/rndtest.c
|
|
create mode 100644 crypto/ocf/rndtest.h
|
|
create mode 100644 crypto/ocf/uio.h
|
|
|
|
diff --git a/crypto/Kconfig b/crypto/Kconfig
|
|
index 527a857..8871f10 100644
|
|
--- a/crypto/Kconfig
|
|
+++ b/crypto/Kconfig
|
|
@@ -923,3 +923,6 @@ config CRYPTO_USER_API_SKCIPHER
|
|
source "drivers/crypto/Kconfig"
|
|
|
|
endif # if CRYPTO
|
|
+
|
|
+source "crypto/ocf/Kconfig"
|
|
+
|
|
diff --git a/crypto/Makefile b/crypto/Makefile
|
|
index 9e6eee2..3cde9f8 100644
|
|
--- a/crypto/Makefile
|
|
+++ b/crypto/Makefile
|
|
@@ -91,6 +91,8 @@ obj-$(CONFIG_CRYPTO_USER_API) += af_alg.o
|
|
obj-$(CONFIG_CRYPTO_USER_API_HASH) += algif_hash.o
|
|
obj-$(CONFIG_CRYPTO_USER_API_SKCIPHER) += algif_skcipher.o
|
|
|
|
+obj-$(CONFIG_OCF_OCF) += ocf/
|
|
+
|
|
#
|
|
# generic algorithms and the async_tx api
|
|
#
|
|
diff --git a/crypto/ocf/Config.in b/crypto/ocf/Config.in
|
|
new file mode 100755
|
|
index 0000000..423d11f
|
|
--- /dev/null
|
|
+++ b/crypto/ocf/Config.in
|
|
@@ -0,0 +1,20 @@
|
|
+#############################################################################
|
|
+
|
|
+mainmenu_option next_comment
|
|
+comment 'OCF Configuration'
|
|
+tristate 'OCF (Open Cryptograhic Framework)' CONFIG_OCF_OCF
|
|
+dep_mbool ' enable fips RNG checks (fips check on RNG data before use)' \
|
|
+ CONFIG_OCF_FIPS $CONFIG_OCF_OCF
|
|
+dep_mbool ' enable harvesting entropy for /dev/random' \
|
|
+ CONFIG_OCF_RANDOMHARVEST $CONFIG_OCF_OCF
|
|
+dep_tristate ' cryptodev (user space support)' \
|
|
+ CONFIG_OCF_CRYPTODEV $CONFIG_OCF_OCF
|
|
+dep_tristate ' cryptosoft (software crypto engine)' \
|
|
+ CONFIG_OCF_CRYPTOSOFT $CONFIG_OCF_OCF
|
|
+dep_tristate ' ocfnull (does no crypto)' \
|
|
+ CONFIG_OCF_OCFNULL $CONFIG_OCF_OCF
|
|
+dep_tristate ' ocf-bench (HW crypto in-kernel benchmark)' \
|
|
+ CONFIG_OCF_BENCH $CONFIG_OCF_OCF
|
|
+endmenu
|
|
+
|
|
+#############################################################################
|
|
diff --git a/crypto/ocf/Kconfig b/crypto/ocf/Kconfig
|
|
new file mode 100755
|
|
index 0000000..44459f4
|
|
--- /dev/null
|
|
+++ b/crypto/ocf/Kconfig
|
|
@@ -0,0 +1,48 @@
|
|
+menu "OCF Configuration"
|
|
+
|
|
+config OCF_OCF
|
|
+ tristate "OCF (Open Cryptograhic Framework)"
|
|
+ help
|
|
+ A linux port of the OpenBSD/FreeBSD crypto framework.
|
|
+
|
|
+config OCF_RANDOMHARVEST
|
|
+ bool "crypto random --- harvest entropy for /dev/random"
|
|
+ depends on OCF_OCF
|
|
+ help
|
|
+ Includes code to harvest random numbers from devices that support it.
|
|
+
|
|
+config OCF_FIPS
|
|
+ bool "enable fips RNG checks"
|
|
+ depends on OCF_OCF && OCF_RANDOMHARVEST
|
|
+ help
|
|
+ Run all RNG provided data through a fips check before
|
|
+ adding it /dev/random's entropy pool.
|
|
+
|
|
+config OCF_CRYPTODEV
|
|
+ tristate "cryptodev (user space support)"
|
|
+ depends on OCF_OCF
|
|
+ help
|
|
+ The user space API to access crypto hardware.
|
|
+
|
|
+config OCF_CRYPTOSOFT
|
|
+ tristate "cryptosoft (software crypto engine)"
|
|
+ depends on OCF_OCF
|
|
+ help
|
|
+ A software driver for the OCF framework that uses
|
|
+ the kernel CryptoAPI.
|
|
+
|
|
+config OCF_OCFNULL
|
|
+ tristate "ocfnull (fake crypto engine)"
|
|
+ depends on OCF_OCF
|
|
+ help
|
|
+ OCF driver for measuring ipsec overheads (does no crypto)
|
|
+
|
|
+config OCF_BENCH
|
|
+ tristate "ocf-bench (HW crypto in-kernel benchmark)"
|
|
+ depends on OCF_OCF
|
|
+ help
|
|
+ A very simple encryption test for the in-kernel interface
|
|
+ of OCF. Also includes code to benchmark the IXP Access library
|
|
+ for comparison.
|
|
+
|
|
+endmenu
|
|
diff --git a/crypto/ocf/Makefile b/crypto/ocf/Makefile
|
|
new file mode 100755
|
|
index 0000000..29ac280
|
|
--- /dev/null
|
|
+++ b/crypto/ocf/Makefile
|
|
@@ -0,0 +1,138 @@
|
|
+# for SGlinux builds
|
|
+-include $(ROOTDIR)/modules/.config
|
|
+
|
|
+OCF_OBJS = crypto.o criov.o
|
|
+
|
|
+ifdef CONFIG_OCF_RANDOMHARVEST
|
|
+ OCF_OBJS += random.o
|
|
+endif
|
|
+
|
|
+ifdef CONFIG_OCF_FIPS
|
|
+ OCF_OBJS += rndtest.o
|
|
+endif
|
|
+
|
|
+# Add in autoconf.h to get #defines for CONFIG_xxx
|
|
+AUTOCONF_H=$(ROOTDIR)/modules/autoconf.h
|
|
+ifeq ($(AUTOCONF_H), $(wildcard $(AUTOCONF_H)))
|
|
+ EXTRA_CFLAGS += -include $(AUTOCONF_H)
|
|
+ export EXTRA_CFLAGS
|
|
+endif
|
|
+
|
|
+ifndef obj
|
|
+ obj ?= .
|
|
+ _obj = subdir
|
|
+ mod-subdirs := safe hifn ixp4xx talitos ocfnull
|
|
+ export-objs += crypto.o criov.o random.o
|
|
+ list-multi += ocf.o
|
|
+ _slash :=
|
|
+else
|
|
+ _obj = obj
|
|
+ _slash := /
|
|
+endif
|
|
+
|
|
+EXTRA_CFLAGS += -I$(obj)/.
|
|
+
|
|
+obj-$(CONFIG_OCF_OCF) += ocf.o
|
|
+obj-$(CONFIG_OCF_CRYPTODEV) += cryptodev.o
|
|
+obj-$(CONFIG_OCF_CRYPTOSOFT) += cryptosoft.o
|
|
+obj-$(CONFIG_OCF_BENCH) += ocf-bench.o
|
|
+
|
|
+$(_obj)-$(CONFIG_OCF_OCFNULL) += ocfnull$(_slash)
|
|
+
|
|
+ocf-objs := $(OCF_OBJS)
|
|
+
|
|
+dummy:
|
|
+ @echo "Please consult the README for how to build OCF."
|
|
+ @echo "If you can't wait then the following should do it:"
|
|
+ @echo ""
|
|
+ @echo " make ocf_modules"
|
|
+ @echo " sudo make ocf_install"
|
|
+ @echo ""
|
|
+ @exit 1
|
|
+
|
|
+$(list-multi) dummy1: $(ocf-objs)
|
|
+ $(LD) -r -o $@ $(ocf-objs)
|
|
+
|
|
+.PHONY:
|
|
+clean:
|
|
+ rm -f *.o *.ko .*.o.flags .*.ko.cmd .*.o.cmd .*.mod.o.cmd *.mod.c
|
|
+ rm -f */*.o */*.ko */.*.o.cmd */.*.ko.cmd */.*.mod.o.cmd */*.mod.c */.*.o.flags
|
|
+ rm -f */modules.order */modules.builtin modules.order modules.builtin
|
|
+
|
|
+ifdef TOPDIR
|
|
+-include $(TOPDIR)/Rules.make
|
|
+endif
|
|
+
|
|
+#
|
|
+# targets to build easily on the current machine
|
|
+#
|
|
+
|
|
+ocf_make:
|
|
+ make -C /lib/modules/$(shell uname -r)/build M=`pwd` $(OCF_TARGET) CONFIG_OCF_OCF=m
|
|
+ make -C /lib/modules/$(shell uname -r)/build M=`pwd` $(OCF_TARGET) CONFIG_OCF_OCF=m CONFIG_OCF_CRYPTOSOFT=m
|
|
+ -make -C /lib/modules/$(shell uname -r)/build M=`pwd` $(OCF_TARGET) CONFIG_OCF_OCF=m CONFIG_OCF_BENCH=m
|
|
+ -make -C /lib/modules/$(shell uname -r)/build M=`pwd` $(OCF_TARGET) CONFIG_OCF_OCF=m CONFIG_OCF_OCFNULL=m
|
|
+ -make -C /lib/modules/$(shell uname -r)/build M=`pwd` $(OCF_TARGET) CONFIG_OCF_OCF=m CONFIG_OCF_HIFN=m
|
|
+
|
|
+ocf_modules:
|
|
+ $(MAKE) ocf_make OCF_TARGET=modules
|
|
+
|
|
+ocf_install:
|
|
+ $(MAKE) ocf_make OCF_TARGET="modules modules_install"
|
|
+ depmod
|
|
+ mkdir -p /usr/include/crypto
|
|
+ cp cryptodev.h /usr/include/crypto/.
|
|
+
|
|
+#
|
|
+# generate full kernel patches for 2.4 and 2.6 kernels to make patching
|
|
+# your kernel easier
|
|
+#
|
|
+
|
|
+.PHONY: patch
|
|
+patch:
|
|
+ patchbase=.; \
|
|
+ [ -d $$patchbase/patches ] || patchbase=..; \
|
|
+ patch=ocf-linux-base.patch; \
|
|
+ patch24=ocf-linux-24.patch; \
|
|
+ patch26=ocf-linux-26.patch; \
|
|
+ patch3=ocf-linux-3.patch; \
|
|
+ ( \
|
|
+ find . -name Makefile; \
|
|
+ find . -name Config.in; \
|
|
+ find . -name Kconfig; \
|
|
+ find . -name README; \
|
|
+ find . -name '*.[ch]' | grep -v '.mod.c'; \
|
|
+ ) | while read t; do \
|
|
+ diff -Nau /dev/null $$t | sed 's?^+++ \./?+++ linux/crypto/ocf/?'; \
|
|
+ done > $$patch; \
|
|
+ cat $$patchbase/patches/linux-2.4.35-ocf.patch $$patch > $$patch24; \
|
|
+ cat $$patchbase/patches/linux-2.6.38-ocf.patch $$patch > $$patch26; \
|
|
+ cat $$patchbase/patches/linux-3.2.1-ocf.patch $$patch > $$patch3; \
|
|
+
|
|
+
|
|
+#
|
|
+# this target probably does nothing for anyone but me - davidm
|
|
+#
|
|
+
|
|
+.PHONY: release
|
|
+release:
|
|
+ REL=`date +%Y%m%d`; RELDIR=/tmp/ocf-linux-$$REL; \
|
|
+ CURDIR=`pwd`; \
|
|
+ rm -rf /tmp/ocf-linux-$$REL*; \
|
|
+ mkdir -p $$RELDIR/ocf; \
|
|
+ mkdir -p $$RELDIR/patches; \
|
|
+ mkdir -p $$RELDIR/crypto-tools; \
|
|
+ cp README* $$RELDIR/.; \
|
|
+ cp patches/[!C]* $$RELDIR/patches/.; \
|
|
+ cp tools/[!C]* $$RELDIR/crypto-tools/.; \
|
|
+ cp -r [!C]* Config.in $$RELDIR/ocf/.; \
|
|
+ rm -rf $$RELDIR/ocf/patches $$RELDIR/ocf/tools; \
|
|
+ rm -f $$RELDIR/ocf/README*; \
|
|
+ cp $$CURDIR/../../user/crypto-tools/[!C]* $$RELDIR/crypto-tools/.; \
|
|
+ make -C $$RELDIR/crypto-tools clean; \
|
|
+ make -C $$RELDIR/ocf clean; \
|
|
+ find $$RELDIR/ocf -name CVS | xargs rm -rf; \
|
|
+ cd $$RELDIR/..; \
|
|
+ tar cvf ocf-linux-$$REL.tar ocf-linux-$$REL; \
|
|
+ gzip -9 ocf-linux-$$REL.tar
|
|
+
|
|
diff --git a/crypto/ocf/criov.c b/crypto/ocf/criov.c
|
|
new file mode 100644
|
|
index 0000000..a8c1a8c
|
|
--- /dev/null
|
|
+++ b/crypto/ocf/criov.c
|
|
@@ -0,0 +1,215 @@
|
|
+/* $OpenBSD: criov.c,v 1.9 2002/01/29 15:48:29 jason Exp $ */
|
|
+
|
|
+/*
|
|
+ * Linux port done by David McCullough <david_mccullough@mcafee.com>
|
|
+ * Copyright (C) 2006-2010 David McCullough
|
|
+ * Copyright (C) 2004-2005 Intel Corporation.
|
|
+ * The license and original author are listed below.
|
|
+ *
|
|
+ * Copyright (c) 1999 Theo de Raadt
|
|
+ *
|
|
+ * Redistribution and use in source and binary forms, with or without
|
|
+ * modification, are permitted provided that the following conditions
|
|
+ * are met:
|
|
+ *
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
+ * 2. Redistributions in binary form must reproduce the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer in the
|
|
+ * documentation and/or other materials provided with the distribution.
|
|
+ * 3. The name of the author may not be used to endorse or promote products
|
|
+ * derived from this software without specific prior written permission.
|
|
+ *
|
|
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
+ *
|
|
+__FBSDID("$FreeBSD: src/sys/opencrypto/criov.c,v 1.5 2006/06/04 22:15:13 pjd Exp $");
|
|
+ */
|
|
+
|
|
+#include <linux/version.h>
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) && !defined(AUTOCONF_INCLUDED)
|
|
+#include <linux/config.h>
|
|
+#endif
|
|
+#include <linux/module.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/uio.h>
|
|
+#include <linux/skbuff.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/mm.h>
|
|
+#include <asm/io.h>
|
|
+
|
|
+#include <uio.h>
|
|
+#include <cryptodev.h>
|
|
+
|
|
+/*
|
|
+ * This macro is only for avoiding code duplication, as we need to skip
|
|
+ * given number of bytes in the same way in three functions below.
|
|
+ */
|
|
+#define CUIO_SKIP() do { \
|
|
+ KASSERT(off >= 0, ("%s: off %d < 0", __func__, off)); \
|
|
+ KASSERT(len >= 0, ("%s: len %d < 0", __func__, len)); \
|
|
+ while (off > 0) { \
|
|
+ KASSERT(iol >= 0, ("%s: empty in skip", __func__)); \
|
|
+ if (off < iov->iov_len) \
|
|
+ break; \
|
|
+ off -= iov->iov_len; \
|
|
+ iol--; \
|
|
+ iov++; \
|
|
+ } \
|
|
+} while (0)
|
|
+
|
|
+void
|
|
+cuio_copydata(struct uio* uio, int off, int len, caddr_t cp)
|
|
+{
|
|
+ struct iovec *iov = uio->uio_iov;
|
|
+ int iol = uio->uio_iovcnt;
|
|
+ unsigned count;
|
|
+
|
|
+ CUIO_SKIP();
|
|
+ while (len > 0) {
|
|
+ KASSERT(iol >= 0, ("%s: empty", __func__));
|
|
+ count = min((int)(iov->iov_len - off), len);
|
|
+ memcpy(cp, ((caddr_t)iov->iov_base) + off, count);
|
|
+ len -= count;
|
|
+ cp += count;
|
|
+ off = 0;
|
|
+ iol--;
|
|
+ iov++;
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+cuio_copyback(struct uio* uio, int off, int len, caddr_t cp)
|
|
+{
|
|
+ struct iovec *iov = uio->uio_iov;
|
|
+ int iol = uio->uio_iovcnt;
|
|
+ unsigned count;
|
|
+
|
|
+ CUIO_SKIP();
|
|
+ while (len > 0) {
|
|
+ KASSERT(iol >= 0, ("%s: empty", __func__));
|
|
+ count = min((int)(iov->iov_len - off), len);
|
|
+ memcpy(((caddr_t)iov->iov_base) + off, cp, count);
|
|
+ len -= count;
|
|
+ cp += count;
|
|
+ off = 0;
|
|
+ iol--;
|
|
+ iov++;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Return a pointer to iov/offset of location in iovec list.
|
|
+ */
|
|
+struct iovec *
|
|
+cuio_getptr(struct uio *uio, int loc, int *off)
|
|
+{
|
|
+ struct iovec *iov = uio->uio_iov;
|
|
+ int iol = uio->uio_iovcnt;
|
|
+
|
|
+ while (loc >= 0) {
|
|
+ /* Normal end of search */
|
|
+ if (loc < iov->iov_len) {
|
|
+ *off = loc;
|
|
+ return (iov);
|
|
+ }
|
|
+
|
|
+ loc -= iov->iov_len;
|
|
+ if (iol == 0) {
|
|
+ if (loc == 0) {
|
|
+ /* Point at the end of valid data */
|
|
+ *off = iov->iov_len;
|
|
+ return (iov);
|
|
+ } else
|
|
+ return (NULL);
|
|
+ } else {
|
|
+ iov++, iol--;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return (NULL);
|
|
+}
|
|
+
|
|
+EXPORT_SYMBOL(cuio_copyback);
|
|
+EXPORT_SYMBOL(cuio_copydata);
|
|
+EXPORT_SYMBOL(cuio_getptr);
|
|
+
|
|
+static void
|
|
+skb_copy_bits_back(struct sk_buff *skb, int offset, caddr_t cp, int len)
|
|
+{
|
|
+ int i;
|
|
+ if (offset < skb_headlen(skb)) {
|
|
+ memcpy(skb->data + offset, cp, min_t(int, skb_headlen(skb), len));
|
|
+ len -= skb_headlen(skb);
|
|
+ cp += skb_headlen(skb);
|
|
+ }
|
|
+ offset -= skb_headlen(skb);
|
|
+ for (i = 0; len > 0 && i < skb_shinfo(skb)->nr_frags; i++) {
|
|
+ if (offset < skb_shinfo(skb)->frags[i].size) {
|
|
+ memcpy(page_address(skb_frag_page(&skb_shinfo(skb)->frags[i])) +
|
|
+ skb_shinfo(skb)->frags[i].page_offset,
|
|
+ cp, min_t(int, skb_shinfo(skb)->frags[i].size, len));
|
|
+ len -= skb_shinfo(skb)->frags[i].size;
|
|
+ cp += skb_shinfo(skb)->frags[i].size;
|
|
+ }
|
|
+ offset -= skb_shinfo(skb)->frags[i].size;
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+crypto_copyback(int flags, caddr_t buf, int off, int size, caddr_t in)
|
|
+{
|
|
+
|
|
+ if ((flags & CRYPTO_F_SKBUF) != 0)
|
|
+ skb_copy_bits_back((struct sk_buff *)buf, off, in, size);
|
|
+ else if ((flags & CRYPTO_F_IOV) != 0)
|
|
+ cuio_copyback((struct uio *)buf, off, size, in);
|
|
+ else
|
|
+ bcopy(in, buf + off, size);
|
|
+}
|
|
+
|
|
+void
|
|
+crypto_copydata(int flags, caddr_t buf, int off, int size, caddr_t out)
|
|
+{
|
|
+
|
|
+ if ((flags & CRYPTO_F_SKBUF) != 0)
|
|
+ skb_copy_bits((struct sk_buff *)buf, off, out, size);
|
|
+ else if ((flags & CRYPTO_F_IOV) != 0)
|
|
+ cuio_copydata((struct uio *)buf, off, size, out);
|
|
+ else
|
|
+ bcopy(buf + off, out, size);
|
|
+}
|
|
+
|
|
+int
|
|
+crypto_apply(int flags, caddr_t buf, int off, int len,
|
|
+ int (*f)(void *, void *, u_int), void *arg)
|
|
+{
|
|
+#if 0
|
|
+ int error;
|
|
+
|
|
+ if ((flags & CRYPTO_F_SKBUF) != 0)
|
|
+ error = XXXXXX((struct mbuf *)buf, off, len, f, arg);
|
|
+ else if ((flags & CRYPTO_F_IOV) != 0)
|
|
+ error = cuio_apply((struct uio *)buf, off, len, f, arg);
|
|
+ else
|
|
+ error = (*f)(arg, buf + off, len);
|
|
+ return (error);
|
|
+#else
|
|
+ KASSERT(0, ("crypto_apply not implemented!\n"));
|
|
+#endif
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+EXPORT_SYMBOL(crypto_copyback);
|
|
+EXPORT_SYMBOL(crypto_copydata);
|
|
+EXPORT_SYMBOL(crypto_apply);
|
|
+
|
|
diff --git a/crypto/ocf/crypto.c b/crypto/ocf/crypto.c
|
|
new file mode 100644
|
|
index 0000000..f48210d
|
|
--- /dev/null
|
|
+++ b/crypto/ocf/crypto.c
|
|
@@ -0,0 +1,1766 @@
|
|
+/*-
|
|
+ * Linux port done by David McCullough <david_mccullough@mcafee.com>
|
|
+ * Copyright (C) 2006-2010 David McCullough
|
|
+ * Copyright (C) 2004-2005 Intel Corporation.
|
|
+ * The license and original author are listed below.
|
|
+ *
|
|
+ * Redistribution and use in source and binary forms, with or without
|
|
+ * Copyright (c) 2002-2006 Sam Leffler. All rights reserved.
|
|
+ *
|
|
+ * modification, are permitted provided that the following conditions
|
|
+ * are met:
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
+ * 2. Redistributions in binary form must reproduce the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer in the
|
|
+ * documentation and/or other materials provided with the distribution.
|
|
+ *
|
|
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
+ */
|
|
+
|
|
+#if 0
|
|
+#include <sys/cdefs.h>
|
|
+__FBSDID("$FreeBSD: src/sys/opencrypto/crypto.c,v 1.27 2007/03/21 03:42:51 sam Exp $");
|
|
+#endif
|
|
+
|
|
+/*
|
|
+ * Cryptographic Subsystem.
|
|
+ *
|
|
+ * This code is derived from the Openbsd Cryptographic Framework (OCF)
|
|
+ * that has the copyright shown below. Very little of the original
|
|
+ * code remains.
|
|
+ */
|
|
+/*-
|
|
+ * The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu)
|
|
+ *
|
|
+ * This code was written by Angelos D. Keromytis in Athens, Greece, in
|
|
+ * February 2000. Network Security Technologies Inc. (NSTI) kindly
|
|
+ * supported the development of this code.
|
|
+ *
|
|
+ * Copyright (c) 2000, 2001 Angelos D. Keromytis
|
|
+ *
|
|
+ * Permission to use, copy, and modify this software with or without fee
|
|
+ * is hereby granted, provided that this entire notice is included in
|
|
+ * all source code copies of any software which is or includes a copy or
|
|
+ * modification of this software.
|
|
+ *
|
|
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
|
|
+ * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
|
|
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
|
|
+ * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
|
|
+ * PURPOSE.
|
|
+ *
|
|
+__FBSDID("$FreeBSD: src/sys/opencrypto/crypto.c,v 1.16 2005/01/07 02:29:16 imp Exp $");
|
|
+ */
|
|
+
|
|
+
|
|
+#include <linux/version.h>
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) && !defined(AUTOCONF_INCLUDED)
|
|
+#include <linux/config.h>
|
|
+#endif
|
|
+#include <linux/module.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/list.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/wait.h>
|
|
+#include <linux/sched.h>
|
|
+#include <linux/spinlock.h>
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,4)
|
|
+#include <linux/kthread.h>
|
|
+#endif
|
|
+#include <cryptodev.h>
|
|
+
|
|
+/*
|
|
+ * keep track of whether or not we have been initialised, a big
|
|
+ * issue if we are linked into the kernel and a driver gets started before
|
|
+ * us
|
|
+ */
|
|
+static int crypto_initted = 0;
|
|
+
|
|
+/*
|
|
+ * Crypto drivers register themselves by allocating a slot in the
|
|
+ * crypto_drivers table with crypto_get_driverid() and then registering
|
|
+ * each algorithm they support with crypto_register() and crypto_kregister().
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * lock on driver table
|
|
+ * we track its state as spin_is_locked does not do anything on non-SMP boxes
|
|
+ */
|
|
+static spinlock_t crypto_drivers_lock;
|
|
+static int crypto_drivers_locked; /* for non-SMP boxes */
|
|
+
|
|
+#define CRYPTO_DRIVER_LOCK() \
|
|
+ ({ \
|
|
+ spin_lock_irqsave(&crypto_drivers_lock, d_flags); \
|
|
+ crypto_drivers_locked = 1; \
|
|
+ dprintk("%s,%d: DRIVER_LOCK()\n", __FILE__, __LINE__); \
|
|
+ })
|
|
+#define CRYPTO_DRIVER_UNLOCK() \
|
|
+ ({ \
|
|
+ dprintk("%s,%d: DRIVER_UNLOCK()\n", __FILE__, __LINE__); \
|
|
+ crypto_drivers_locked = 0; \
|
|
+ spin_unlock_irqrestore(&crypto_drivers_lock, d_flags); \
|
|
+ })
|
|
+#define CRYPTO_DRIVER_ASSERT() \
|
|
+ ({ \
|
|
+ if (!crypto_drivers_locked) { \
|
|
+ dprintk("%s,%d: DRIVER_ASSERT!\n", __FILE__, __LINE__); \
|
|
+ } \
|
|
+ })
|
|
+
|
|
+/*
|
|
+ * Crypto device/driver capabilities structure.
|
|
+ *
|
|
+ * Synchronization:
|
|
+ * (d) - protected by CRYPTO_DRIVER_LOCK()
|
|
+ * (q) - protected by CRYPTO_Q_LOCK()
|
|
+ * Not tagged fields are read-only.
|
|
+ */
|
|
+struct cryptocap {
|
|
+ device_t cc_dev; /* (d) device/driver */
|
|
+ u_int32_t cc_sessions; /* (d) # of sessions */
|
|
+ u_int32_t cc_koperations; /* (d) # os asym operations */
|
|
+ /*
|
|
+ * Largest possible operator length (in bits) for each type of
|
|
+ * encryption algorithm. XXX not used
|
|
+ */
|
|
+ u_int16_t cc_max_op_len[CRYPTO_ALGORITHM_MAX + 1];
|
|
+ u_int8_t cc_alg[CRYPTO_ALGORITHM_MAX + 1];
|
|
+ u_int8_t cc_kalg[CRK_ALGORITHM_MAX + 1];
|
|
+
|
|
+ int cc_flags; /* (d) flags */
|
|
+#define CRYPTOCAP_F_CLEANUP 0x80000000 /* needs resource cleanup */
|
|
+ int cc_qblocked; /* (q) symmetric q blocked */
|
|
+ int cc_kqblocked; /* (q) asymmetric q blocked */
|
|
+
|
|
+ int cc_unqblocked; /* (q) symmetric q blocked */
|
|
+ int cc_unkqblocked; /* (q) asymmetric q blocked */
|
|
+};
|
|
+static struct cryptocap *crypto_drivers = NULL;
|
|
+static int crypto_drivers_num = 0;
|
|
+
|
|
+/*
|
|
+ * There are two queues for crypto requests; one for symmetric (e.g.
|
|
+ * cipher) operations and one for asymmetric (e.g. MOD)operations.
|
|
+ * A single mutex is used to lock access to both queues. We could
|
|
+ * have one per-queue but having one simplifies handling of block/unblock
|
|
+ * operations.
|
|
+ */
|
|
+static LIST_HEAD(crp_q); /* crypto request queue */
|
|
+static LIST_HEAD(crp_kq); /* asym request queue */
|
|
+
|
|
+static spinlock_t crypto_q_lock;
|
|
+
|
|
+int crypto_all_qblocked = 0; /* protect with Q_LOCK */
|
|
+module_param(crypto_all_qblocked, int, 0444);
|
|
+MODULE_PARM_DESC(crypto_all_qblocked, "Are all crypto queues blocked");
|
|
+
|
|
+int crypto_all_kqblocked = 0; /* protect with Q_LOCK */
|
|
+module_param(crypto_all_kqblocked, int, 0444);
|
|
+MODULE_PARM_DESC(crypto_all_kqblocked, "Are all asym crypto queues blocked");
|
|
+
|
|
+#define CRYPTO_Q_LOCK() \
|
|
+ ({ \
|
|
+ spin_lock_irqsave(&crypto_q_lock, q_flags); \
|
|
+ dprintk("%s,%d: Q_LOCK()\n", __FILE__, __LINE__); \
|
|
+ })
|
|
+#define CRYPTO_Q_UNLOCK() \
|
|
+ ({ \
|
|
+ dprintk("%s,%d: Q_UNLOCK()\n", __FILE__, __LINE__); \
|
|
+ spin_unlock_irqrestore(&crypto_q_lock, q_flags); \
|
|
+ })
|
|
+
|
|
+/*
|
|
+ * There are two queues for processing completed crypto requests; one
|
|
+ * for the symmetric and one for the asymmetric ops. We only need one
|
|
+ * but have two to avoid type futzing (cryptop vs. cryptkop). A single
|
|
+ * mutex is used to lock access to both queues. Note that this lock
|
|
+ * must be separate from the lock on request queues to insure driver
|
|
+ * callbacks don't generate lock order reversals.
|
|
+ */
|
|
+static LIST_HEAD(crp_ret_q); /* callback queues */
|
|
+static LIST_HEAD(crp_ret_kq);
|
|
+
|
|
+static spinlock_t crypto_ret_q_lock;
|
|
+#define CRYPTO_RETQ_LOCK() \
|
|
+ ({ \
|
|
+ spin_lock_irqsave(&crypto_ret_q_lock, r_flags); \
|
|
+ dprintk("%s,%d: RETQ_LOCK\n", __FILE__, __LINE__); \
|
|
+ })
|
|
+#define CRYPTO_RETQ_UNLOCK() \
|
|
+ ({ \
|
|
+ dprintk("%s,%d: RETQ_UNLOCK\n", __FILE__, __LINE__); \
|
|
+ spin_unlock_irqrestore(&crypto_ret_q_lock, r_flags); \
|
|
+ })
|
|
+#define CRYPTO_RETQ_EMPTY() (list_empty(&crp_ret_q) && list_empty(&crp_ret_kq))
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
|
|
+static kmem_cache_t *cryptop_zone;
|
|
+static kmem_cache_t *cryptodesc_zone;
|
|
+#else
|
|
+static struct kmem_cache *cryptop_zone;
|
|
+static struct kmem_cache *cryptodesc_zone;
|
|
+#endif
|
|
+
|
|
+#define debug crypto_debug
|
|
+int crypto_debug = 0;
|
|
+module_param(crypto_debug, int, 0644);
|
|
+MODULE_PARM_DESC(crypto_debug, "Enable debug");
|
|
+EXPORT_SYMBOL(crypto_debug);
|
|
+
|
|
+/*
|
|
+ * Maximum number of outstanding crypto requests before we start
|
|
+ * failing requests. We need this to prevent DOS when too many
|
|
+ * requests are arriving for us to keep up. Otherwise we will
|
|
+ * run the system out of memory. Since crypto is slow, we are
|
|
+ * usually the bottleneck that needs to say, enough is enough.
|
|
+ *
|
|
+ * We cannot print errors when this condition occurs, we are already too
|
|
+ * slow, printing anything will just kill us
|
|
+ */
|
|
+
|
|
+static int crypto_q_cnt = 0;
|
|
+module_param(crypto_q_cnt, int, 0444);
|
|
+MODULE_PARM_DESC(crypto_q_cnt,
|
|
+ "Current number of outstanding crypto requests");
|
|
+
|
|
+static int crypto_q_max = 1000;
|
|
+module_param(crypto_q_max, int, 0644);
|
|
+MODULE_PARM_DESC(crypto_q_max,
|
|
+ "Maximum number of outstanding crypto requests");
|
|
+
|
|
+#define bootverbose crypto_verbose
|
|
+static int crypto_verbose = 0;
|
|
+module_param(crypto_verbose, int, 0644);
|
|
+MODULE_PARM_DESC(crypto_verbose,
|
|
+ "Enable verbose crypto startup");
|
|
+
|
|
+int crypto_usercrypto = 1; /* userland may do crypto reqs */
|
|
+module_param(crypto_usercrypto, int, 0644);
|
|
+MODULE_PARM_DESC(crypto_usercrypto,
|
|
+ "Enable/disable user-mode access to crypto support");
|
|
+
|
|
+int crypto_userasymcrypto = 1; /* userland may do asym crypto reqs */
|
|
+module_param(crypto_userasymcrypto, int, 0644);
|
|
+MODULE_PARM_DESC(crypto_userasymcrypto,
|
|
+ "Enable/disable user-mode access to asymmetric crypto support");
|
|
+
|
|
+int crypto_devallowsoft = 0; /* only use hardware crypto */
|
|
+module_param(crypto_devallowsoft, int, 0644);
|
|
+MODULE_PARM_DESC(crypto_devallowsoft,
|
|
+ "Enable/disable use of software crypto support");
|
|
+
|
|
+/*
|
|
+ * This parameter controls the maximum number of crypto operations to
|
|
+ * do consecutively in the crypto kernel thread before scheduling to allow
|
|
+ * other processes to run. Without it, it is possible to get into a
|
|
+ * situation where the crypto thread never allows any other processes to run.
|
|
+ * Default to 1000 which should be less than one second.
|
|
+ */
|
|
+static int crypto_max_loopcount = 1000;
|
|
+module_param(crypto_max_loopcount, int, 0644);
|
|
+MODULE_PARM_DESC(crypto_max_loopcount,
|
|
+ "Maximum number of crypto ops to do before yielding to other processes");
|
|
+
|
|
+#ifndef CONFIG_NR_CPUS
|
|
+#define CONFIG_NR_CPUS 1
|
|
+#endif
|
|
+
|
|
+static struct task_struct *cryptoproc[CONFIG_NR_CPUS];
|
|
+static struct task_struct *cryptoretproc[CONFIG_NR_CPUS];
|
|
+static DECLARE_WAIT_QUEUE_HEAD(cryptoproc_wait);
|
|
+static DECLARE_WAIT_QUEUE_HEAD(cryptoretproc_wait);
|
|
+
|
|
+static int crypto_proc(void *arg);
|
|
+static int crypto_ret_proc(void *arg);
|
|
+static int crypto_invoke(struct cryptocap *cap, struct cryptop *crp, int hint);
|
|
+static int crypto_kinvoke(struct cryptkop *krp, int flags);
|
|
+static void crypto_exit(void);
|
|
+static int crypto_init(void);
|
|
+
|
|
+static struct cryptostats cryptostats;
|
|
+
|
|
+static struct cryptocap *
|
|
+crypto_checkdriver(u_int32_t hid)
|
|
+{
|
|
+ if (crypto_drivers == NULL)
|
|
+ return NULL;
|
|
+ return (hid >= crypto_drivers_num ? NULL : &crypto_drivers[hid]);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Compare a driver's list of supported algorithms against another
|
|
+ * list; return non-zero if all algorithms are supported.
|
|
+ */
|
|
+static int
|
|
+driver_suitable(const struct cryptocap *cap, const struct cryptoini *cri)
|
|
+{
|
|
+ const struct cryptoini *cr;
|
|
+
|
|
+ /* See if all the algorithms are supported. */
|
|
+ for (cr = cri; cr; cr = cr->cri_next)
|
|
+ if (cap->cc_alg[cr->cri_alg] == 0)
|
|
+ return 0;
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * Select a driver for a new session that supports the specified
|
|
+ * algorithms and, optionally, is constrained according to the flags.
|
|
+ * The algorithm we use here is pretty stupid; just use the
|
|
+ * first driver that supports all the algorithms we need. If there
|
|
+ * are multiple drivers we choose the driver with the fewest active
|
|
+ * sessions. We prefer hardware-backed drivers to software ones.
|
|
+ *
|
|
+ * XXX We need more smarts here (in real life too, but that's
|
|
+ * XXX another story altogether).
|
|
+ */
|
|
+static struct cryptocap *
|
|
+crypto_select_driver(const struct cryptoini *cri, int flags)
|
|
+{
|
|
+ struct cryptocap *cap, *best;
|
|
+ int match, hid;
|
|
+
|
|
+ CRYPTO_DRIVER_ASSERT();
|
|
+
|
|
+ /*
|
|
+ * Look first for hardware crypto devices if permitted.
|
|
+ */
|
|
+ if (flags & CRYPTOCAP_F_HARDWARE)
|
|
+ match = CRYPTOCAP_F_HARDWARE;
|
|
+ else
|
|
+ match = CRYPTOCAP_F_SOFTWARE;
|
|
+ best = NULL;
|
|
+again:
|
|
+ for (hid = 0; hid < crypto_drivers_num; hid++) {
|
|
+ cap = &crypto_drivers[hid];
|
|
+ /*
|
|
+ * If it's not initialized, is in the process of
|
|
+ * going away, or is not appropriate (hardware
|
|
+ * or software based on match), then skip.
|
|
+ */
|
|
+ if (cap->cc_dev == NULL ||
|
|
+ (cap->cc_flags & CRYPTOCAP_F_CLEANUP) ||
|
|
+ (cap->cc_flags & match) == 0)
|
|
+ continue;
|
|
+
|
|
+ /* verify all the algorithms are supported. */
|
|
+ if (driver_suitable(cap, cri)) {
|
|
+ if (best == NULL ||
|
|
+ cap->cc_sessions < best->cc_sessions)
|
|
+ best = cap;
|
|
+ }
|
|
+ }
|
|
+ if (best != NULL)
|
|
+ return best;
|
|
+ if (match == CRYPTOCAP_F_HARDWARE && (flags & CRYPTOCAP_F_SOFTWARE)) {
|
|
+ /* sort of an Algol 68-style for loop */
|
|
+ match = CRYPTOCAP_F_SOFTWARE;
|
|
+ goto again;
|
|
+ }
|
|
+ return best;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Create a new session. The crid argument specifies a crypto
|
|
+ * driver to use or constraints on a driver to select (hardware
|
|
+ * only, software only, either). Whatever driver is selected
|
|
+ * must be capable of the requested crypto algorithms.
|
|
+ */
|
|
+int
|
|
+crypto_newsession(u_int64_t *sid, struct cryptoini *cri, int crid)
|
|
+{
|
|
+ struct cryptocap *cap;
|
|
+ u_int32_t hid, lid;
|
|
+ int err;
|
|
+ unsigned long d_flags;
|
|
+
|
|
+ CRYPTO_DRIVER_LOCK();
|
|
+ if ((crid & (CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE)) == 0) {
|
|
+ /*
|
|
+ * Use specified driver; verify it is capable.
|
|
+ */
|
|
+ cap = crypto_checkdriver(crid);
|
|
+ if (cap != NULL && !driver_suitable(cap, cri))
|
|
+ cap = NULL;
|
|
+ } else {
|
|
+ /*
|
|
+ * No requested driver; select based on crid flags.
|
|
+ */
|
|
+ cap = crypto_select_driver(cri, crid);
|
|
+ /*
|
|
+ * if NULL then can't do everything in one session.
|
|
+ * XXX Fix this. We need to inject a "virtual" session
|
|
+ * XXX layer right about here.
|
|
+ */
|
|
+ }
|
|
+ if (cap != NULL) {
|
|
+ /* Call the driver initialization routine. */
|
|
+ hid = cap - crypto_drivers;
|
|
+ lid = hid; /* Pass the driver ID. */
|
|
+ cap->cc_sessions++;
|
|
+ CRYPTO_DRIVER_UNLOCK();
|
|
+ err = CRYPTODEV_NEWSESSION(cap->cc_dev, &lid, cri);
|
|
+ CRYPTO_DRIVER_LOCK();
|
|
+ if (err == 0) {
|
|
+ (*sid) = (cap->cc_flags & 0xff000000)
|
|
+ | (hid & 0x00ffffff);
|
|
+ (*sid) <<= 32;
|
|
+ (*sid) |= (lid & 0xffffffff);
|
|
+ } else
|
|
+ cap->cc_sessions--;
|
|
+ } else
|
|
+ err = EINVAL;
|
|
+ CRYPTO_DRIVER_UNLOCK();
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static void
|
|
+crypto_remove(struct cryptocap *cap)
|
|
+{
|
|
+ CRYPTO_DRIVER_ASSERT();
|
|
+ if (cap->cc_sessions == 0 && cap->cc_koperations == 0)
|
|
+ bzero(cap, sizeof(*cap));
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Delete an existing session (or a reserved session on an unregistered
|
|
+ * driver).
|
|
+ */
|
|
+int
|
|
+crypto_freesession(u_int64_t sid)
|
|
+{
|
|
+ struct cryptocap *cap;
|
|
+ u_int32_t hid;
|
|
+ int err = 0;
|
|
+ unsigned long d_flags;
|
|
+
|
|
+ dprintk("%s()\n", __FUNCTION__);
|
|
+ CRYPTO_DRIVER_LOCK();
|
|
+
|
|
+ if (crypto_drivers == NULL) {
|
|
+ err = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ /* Determine two IDs. */
|
|
+ hid = CRYPTO_SESID2HID(sid);
|
|
+
|
|
+ if (hid >= crypto_drivers_num) {
|
|
+ dprintk("%s - INVALID DRIVER NUM %d\n", __FUNCTION__, hid);
|
|
+ err = ENOENT;
|
|
+ goto done;
|
|
+ }
|
|
+ cap = &crypto_drivers[hid];
|
|
+
|
|
+ if (cap->cc_dev) {
|
|
+ CRYPTO_DRIVER_UNLOCK();
|
|
+ /* Call the driver cleanup routine, if available, unlocked. */
|
|
+ err = CRYPTODEV_FREESESSION(cap->cc_dev, sid);
|
|
+ CRYPTO_DRIVER_LOCK();
|
|
+ }
|
|
+
|
|
+ if (cap->cc_sessions)
|
|
+ cap->cc_sessions--;
|
|
+
|
|
+ if (cap->cc_flags & CRYPTOCAP_F_CLEANUP)
|
|
+ crypto_remove(cap);
|
|
+
|
|
+done:
|
|
+ CRYPTO_DRIVER_UNLOCK();
|
|
+ return err;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Return an unused driver id. Used by drivers prior to registering
|
|
+ * support for the algorithms they handle.
|
|
+ */
|
|
+int32_t
|
|
+crypto_get_driverid(device_t dev, int flags)
|
|
+{
|
|
+ struct cryptocap *newdrv;
|
|
+ int i;
|
|
+ unsigned long d_flags;
|
|
+
|
|
+ if ((flags & (CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE)) == 0) {
|
|
+ printf("%s: no flags specified when registering driver\n",
|
|
+ device_get_nameunit(dev));
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ CRYPTO_DRIVER_LOCK();
|
|
+
|
|
+ for (i = 0; i < crypto_drivers_num; i++) {
|
|
+ if (crypto_drivers[i].cc_dev == NULL &&
|
|
+ (crypto_drivers[i].cc_flags & CRYPTOCAP_F_CLEANUP) == 0) {
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Out of entries, allocate some more. */
|
|
+ if (i == crypto_drivers_num) {
|
|
+ /* Be careful about wrap-around. */
|
|
+ if (2 * crypto_drivers_num <= crypto_drivers_num) {
|
|
+ CRYPTO_DRIVER_UNLOCK();
|
|
+ printk("crypto: driver count wraparound!\n");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ newdrv = kmalloc(2 * crypto_drivers_num * sizeof(struct cryptocap),
|
|
+ GFP_KERNEL);
|
|
+ if (newdrv == NULL) {
|
|
+ CRYPTO_DRIVER_UNLOCK();
|
|
+ printk("crypto: no space to expand driver table!\n");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ memcpy(newdrv, crypto_drivers,
|
|
+ crypto_drivers_num * sizeof(struct cryptocap));
|
|
+ memset(&newdrv[crypto_drivers_num], 0,
|
|
+ crypto_drivers_num * sizeof(struct cryptocap));
|
|
+
|
|
+ crypto_drivers_num *= 2;
|
|
+
|
|
+ kfree(crypto_drivers);
|
|
+ crypto_drivers = newdrv;
|
|
+ }
|
|
+
|
|
+ /* NB: state is zero'd on free */
|
|
+ crypto_drivers[i].cc_sessions = 1; /* Mark */
|
|
+ crypto_drivers[i].cc_dev = dev;
|
|
+ crypto_drivers[i].cc_flags = flags;
|
|
+ if (bootverbose)
|
|
+ printf("crypto: assign %s driver id %u, flags %u\n",
|
|
+ device_get_nameunit(dev), i, flags);
|
|
+
|
|
+ CRYPTO_DRIVER_UNLOCK();
|
|
+
|
|
+ return i;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Lookup a driver by name. We match against the full device
|
|
+ * name and unit, and against just the name. The latter gives
|
|
+ * us a simple widlcarding by device name. On success return the
|
|
+ * driver/hardware identifier; otherwise return -1.
|
|
+ */
|
|
+int
|
|
+crypto_find_driver(const char *match)
|
|
+{
|
|
+ int i, len = strlen(match);
|
|
+ unsigned long d_flags;
|
|
+
|
|
+ CRYPTO_DRIVER_LOCK();
|
|
+ for (i = 0; i < crypto_drivers_num; i++) {
|
|
+ device_t dev = crypto_drivers[i].cc_dev;
|
|
+ if (dev == NULL ||
|
|
+ (crypto_drivers[i].cc_flags & CRYPTOCAP_F_CLEANUP))
|
|
+ continue;
|
|
+ if (strncmp(match, device_get_nameunit(dev), len) == 0 ||
|
|
+ strncmp(match, device_get_name(dev), len) == 0)
|
|
+ break;
|
|
+ }
|
|
+ CRYPTO_DRIVER_UNLOCK();
|
|
+ return i < crypto_drivers_num ? i : -1;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Return the device_t for the specified driver or NULL
|
|
+ * if the driver identifier is invalid.
|
|
+ */
|
|
+device_t
|
|
+crypto_find_device_byhid(int hid)
|
|
+{
|
|
+ struct cryptocap *cap = crypto_checkdriver(hid);
|
|
+ return cap != NULL ? cap->cc_dev : NULL;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Return the device/driver capabilities.
|
|
+ */
|
|
+int
|
|
+crypto_getcaps(int hid)
|
|
+{
|
|
+ struct cryptocap *cap = crypto_checkdriver(hid);
|
|
+ return cap != NULL ? cap->cc_flags : 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Register support for a key-related algorithm. This routine
|
|
+ * is called once for each algorithm supported a driver.
|
|
+ */
|
|
+int
|
|
+crypto_kregister(u_int32_t driverid, int kalg, u_int32_t flags)
|
|
+{
|
|
+ struct cryptocap *cap;
|
|
+ int err;
|
|
+ unsigned long d_flags;
|
|
+
|
|
+ dprintk("%s()\n", __FUNCTION__);
|
|
+ CRYPTO_DRIVER_LOCK();
|
|
+
|
|
+ cap = crypto_checkdriver(driverid);
|
|
+ if (cap != NULL &&
|
|
+ (CRK_ALGORITM_MIN <= kalg && kalg <= CRK_ALGORITHM_MAX)) {
|
|
+ /*
|
|
+ * XXX Do some performance testing to determine placing.
|
|
+ * XXX We probably need an auxiliary data structure that
|
|
+ * XXX describes relative performances.
|
|
+ */
|
|
+
|
|
+ cap->cc_kalg[kalg] = flags | CRYPTO_ALG_FLAG_SUPPORTED;
|
|
+ if (bootverbose)
|
|
+ printf("crypto: %s registers key alg %u flags %u\n"
|
|
+ , device_get_nameunit(cap->cc_dev)
|
|
+ , kalg
|
|
+ , flags
|
|
+ );
|
|
+ err = 0;
|
|
+ } else
|
|
+ err = EINVAL;
|
|
+
|
|
+ CRYPTO_DRIVER_UNLOCK();
|
|
+ return err;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Register support for a non-key-related algorithm. This routine
|
|
+ * is called once for each such algorithm supported by a driver.
|
|
+ */
|
|
+int
|
|
+crypto_register(u_int32_t driverid, int alg, u_int16_t maxoplen,
|
|
+ u_int32_t flags)
|
|
+{
|
|
+ struct cryptocap *cap;
|
|
+ int err;
|
|
+ unsigned long d_flags;
|
|
+
|
|
+ dprintk("%s(id=0x%x, alg=%d, maxoplen=%d, flags=0x%x)\n", __FUNCTION__,
|
|
+ driverid, alg, maxoplen, flags);
|
|
+
|
|
+ CRYPTO_DRIVER_LOCK();
|
|
+
|
|
+ cap = crypto_checkdriver(driverid);
|
|
+ /* NB: algorithms are in the range [1..max] */
|
|
+ if (cap != NULL &&
|
|
+ (CRYPTO_ALGORITHM_MIN <= alg && alg <= CRYPTO_ALGORITHM_MAX)) {
|
|
+ /*
|
|
+ * XXX Do some performance testing to determine placing.
|
|
+ * XXX We probably need an auxiliary data structure that
|
|
+ * XXX describes relative performances.
|
|
+ */
|
|
+
|
|
+ cap->cc_alg[alg] = flags | CRYPTO_ALG_FLAG_SUPPORTED;
|
|
+ cap->cc_max_op_len[alg] = maxoplen;
|
|
+ if (bootverbose)
|
|
+ printf("crypto: %s registers alg %u flags %u maxoplen %u\n"
|
|
+ , device_get_nameunit(cap->cc_dev)
|
|
+ , alg
|
|
+ , flags
|
|
+ , maxoplen
|
|
+ );
|
|
+ cap->cc_sessions = 0; /* Unmark */
|
|
+ err = 0;
|
|
+ } else
|
|
+ err = EINVAL;
|
|
+
|
|
+ CRYPTO_DRIVER_UNLOCK();
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static void
|
|
+driver_finis(struct cryptocap *cap)
|
|
+{
|
|
+ u_int32_t ses, kops;
|
|
+
|
|
+ CRYPTO_DRIVER_ASSERT();
|
|
+
|
|
+ ses = cap->cc_sessions;
|
|
+ kops = cap->cc_koperations;
|
|
+ bzero(cap, sizeof(*cap));
|
|
+ if (ses != 0 || kops != 0) {
|
|
+ /*
|
|
+ * If there are pending sessions,
|
|
+ * just mark as invalid.
|
|
+ */
|
|
+ cap->cc_flags |= CRYPTOCAP_F_CLEANUP;
|
|
+ cap->cc_sessions = ses;
|
|
+ cap->cc_koperations = kops;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Unregister a crypto driver. If there are pending sessions using it,
|
|
+ * leave enough information around so that subsequent calls using those
|
|
+ * sessions will correctly detect the driver has been unregistered and
|
|
+ * reroute requests.
|
|
+ */
|
|
+int
|
|
+crypto_unregister(u_int32_t driverid, int alg)
|
|
+{
|
|
+ struct cryptocap *cap;
|
|
+ int i, err;
|
|
+ unsigned long d_flags;
|
|
+
|
|
+ dprintk("%s()\n", __FUNCTION__);
|
|
+ CRYPTO_DRIVER_LOCK();
|
|
+
|
|
+ cap = crypto_checkdriver(driverid);
|
|
+ if (cap != NULL &&
|
|
+ (CRYPTO_ALGORITHM_MIN <= alg && alg <= CRYPTO_ALGORITHM_MAX) &&
|
|
+ cap->cc_alg[alg] != 0) {
|
|
+ cap->cc_alg[alg] = 0;
|
|
+ cap->cc_max_op_len[alg] = 0;
|
|
+
|
|
+ /* Was this the last algorithm ? */
|
|
+ for (i = 1; i <= CRYPTO_ALGORITHM_MAX; i++)
|
|
+ if (cap->cc_alg[i] != 0)
|
|
+ break;
|
|
+
|
|
+ if (i == CRYPTO_ALGORITHM_MAX + 1)
|
|
+ driver_finis(cap);
|
|
+ err = 0;
|
|
+ } else
|
|
+ err = EINVAL;
|
|
+ CRYPTO_DRIVER_UNLOCK();
|
|
+ return err;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Unregister all algorithms associated with a crypto driver.
|
|
+ * If there are pending sessions using it, leave enough information
|
|
+ * around so that subsequent calls using those sessions will
|
|
+ * correctly detect the driver has been unregistered and reroute
|
|
+ * requests.
|
|
+ */
|
|
+int
|
|
+crypto_unregister_all(u_int32_t driverid)
|
|
+{
|
|
+ struct cryptocap *cap;
|
|
+ int err;
|
|
+ unsigned long d_flags;
|
|
+
|
|
+ dprintk("%s()\n", __FUNCTION__);
|
|
+ CRYPTO_DRIVER_LOCK();
|
|
+ cap = crypto_checkdriver(driverid);
|
|
+ if (cap != NULL) {
|
|
+ driver_finis(cap);
|
|
+ err = 0;
|
|
+ } else
|
|
+ err = EINVAL;
|
|
+ CRYPTO_DRIVER_UNLOCK();
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Clear blockage on a driver. The what parameter indicates whether
|
|
+ * the driver is now ready for cryptop's and/or cryptokop's.
|
|
+ */
|
|
+int
|
|
+crypto_unblock(u_int32_t driverid, int what)
|
|
+{
|
|
+ struct cryptocap *cap;
|
|
+ int err;
|
|
+ unsigned long q_flags;
|
|
+
|
|
+ CRYPTO_Q_LOCK();
|
|
+ cap = crypto_checkdriver(driverid);
|
|
+ if (cap != NULL) {
|
|
+ if (what & CRYPTO_SYMQ) {
|
|
+ cap->cc_qblocked = 0;
|
|
+ cap->cc_unqblocked = 0;
|
|
+ crypto_all_qblocked = 0;
|
|
+ }
|
|
+ if (what & CRYPTO_ASYMQ) {
|
|
+ cap->cc_kqblocked = 0;
|
|
+ cap->cc_unkqblocked = 0;
|
|
+ crypto_all_kqblocked = 0;
|
|
+ }
|
|
+ wake_up_interruptible(&cryptoproc_wait);
|
|
+ err = 0;
|
|
+ } else
|
|
+ err = EINVAL;
|
|
+ CRYPTO_Q_UNLOCK(); //DAVIDM should this be a driver lock
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Add a crypto request to a queue, to be processed by the kernel thread.
|
|
+ */
|
|
+int
|
|
+crypto_dispatch(struct cryptop *crp)
|
|
+{
|
|
+ struct cryptocap *cap;
|
|
+ int result = -1;
|
|
+ unsigned long q_flags;
|
|
+
|
|
+ dprintk("%s()\n", __FUNCTION__);
|
|
+
|
|
+ cryptostats.cs_ops++;
|
|
+
|
|
+ CRYPTO_Q_LOCK();
|
|
+ if (crypto_q_cnt >= crypto_q_max) {
|
|
+ cryptostats.cs_drops++;
|
|
+ CRYPTO_Q_UNLOCK();
|
|
+ return ENOMEM;
|
|
+ }
|
|
+ crypto_q_cnt++;
|
|
+
|
|
+ /* make sure we are starting a fresh run on this crp. */
|
|
+ crp->crp_flags &= ~CRYPTO_F_DONE;
|
|
+ crp->crp_etype = 0;
|
|
+
|
|
+ /*
|
|
+ * Caller marked the request to be processed immediately; dispatch
|
|
+ * it directly to the driver unless the driver is currently blocked.
|
|
+ */
|
|
+ if ((crp->crp_flags & CRYPTO_F_BATCH) == 0) {
|
|
+ int hid = CRYPTO_SESID2HID(crp->crp_sid);
|
|
+ cap = crypto_checkdriver(hid);
|
|
+ /* Driver cannot disappear when there is an active session. */
|
|
+ KASSERT(cap != NULL, ("%s: Driver disappeared.", __func__));
|
|
+ if (!cap->cc_qblocked) {
|
|
+ crypto_all_qblocked = 0;
|
|
+ crypto_drivers[hid].cc_unqblocked = 1;
|
|
+ CRYPTO_Q_UNLOCK();
|
|
+ result = crypto_invoke(cap, crp, 0);
|
|
+ CRYPTO_Q_LOCK();
|
|
+ if (result == ERESTART)
|
|
+ if (crypto_drivers[hid].cc_unqblocked)
|
|
+ crypto_drivers[hid].cc_qblocked = 1;
|
|
+ crypto_drivers[hid].cc_unqblocked = 0;
|
|
+ }
|
|
+ }
|
|
+ if (result == ERESTART) {
|
|
+ /*
|
|
+ * The driver ran out of resources, mark the
|
|
+ * driver ``blocked'' for cryptop's and put
|
|
+ * the request back in the queue. It would
|
|
+ * best to put the request back where we got
|
|
+ * it but that's hard so for now we put it
|
|
+ * at the front. This should be ok; putting
|
|
+ * it at the end does not work.
|
|
+ */
|
|
+ list_add(&crp->crp_next, &crp_q);
|
|
+ cryptostats.cs_blocks++;
|
|
+ result = 0;
|
|
+ } else if (result == -1) {
|
|
+ TAILQ_INSERT_TAIL(&crp_q, crp, crp_next);
|
|
+ result = 0;
|
|
+ }
|
|
+ wake_up_interruptible(&cryptoproc_wait);
|
|
+ CRYPTO_Q_UNLOCK();
|
|
+ return result;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Add an asymetric crypto request to a queue,
|
|
+ * to be processed by the kernel thread.
|
|
+ */
|
|
+int
|
|
+crypto_kdispatch(struct cryptkop *krp)
|
|
+{
|
|
+ int error;
|
|
+ unsigned long q_flags;
|
|
+
|
|
+ cryptostats.cs_kops++;
|
|
+
|
|
+ error = crypto_kinvoke(krp, krp->krp_crid);
|
|
+ if (error == ERESTART) {
|
|
+ CRYPTO_Q_LOCK();
|
|
+ TAILQ_INSERT_TAIL(&crp_kq, krp, krp_next);
|
|
+ wake_up_interruptible(&cryptoproc_wait);
|
|
+ CRYPTO_Q_UNLOCK();
|
|
+ error = 0;
|
|
+ }
|
|
+ return error;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Verify a driver is suitable for the specified operation.
|
|
+ */
|
|
+static __inline int
|
|
+kdriver_suitable(const struct cryptocap *cap, const struct cryptkop *krp)
|
|
+{
|
|
+ return (cap->cc_kalg[krp->krp_op] & CRYPTO_ALG_FLAG_SUPPORTED) != 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Select a driver for an asym operation. The driver must
|
|
+ * support the necessary algorithm. The caller can constrain
|
|
+ * which device is selected with the flags parameter. The
|
|
+ * algorithm we use here is pretty stupid; just use the first
|
|
+ * driver that supports the algorithms we need. If there are
|
|
+ * multiple suitable drivers we choose the driver with the
|
|
+ * fewest active operations. We prefer hardware-backed
|
|
+ * drivers to software ones when either may be used.
|
|
+ */
|
|
+static struct cryptocap *
|
|
+crypto_select_kdriver(const struct cryptkop *krp, int flags)
|
|
+{
|
|
+ struct cryptocap *cap, *best, *blocked;
|
|
+ int match, hid;
|
|
+
|
|
+ CRYPTO_DRIVER_ASSERT();
|
|
+
|
|
+ /*
|
|
+ * Look first for hardware crypto devices if permitted.
|
|
+ */
|
|
+ if (flags & CRYPTOCAP_F_HARDWARE)
|
|
+ match = CRYPTOCAP_F_HARDWARE;
|
|
+ else
|
|
+ match = CRYPTOCAP_F_SOFTWARE;
|
|
+ best = NULL;
|
|
+ blocked = NULL;
|
|
+again:
|
|
+ for (hid = 0; hid < crypto_drivers_num; hid++) {
|
|
+ cap = &crypto_drivers[hid];
|
|
+ /*
|
|
+ * If it's not initialized, is in the process of
|
|
+ * going away, or is not appropriate (hardware
|
|
+ * or software based on match), then skip.
|
|
+ */
|
|
+ if (cap->cc_dev == NULL ||
|
|
+ (cap->cc_flags & CRYPTOCAP_F_CLEANUP) ||
|
|
+ (cap->cc_flags & match) == 0)
|
|
+ continue;
|
|
+
|
|
+ /* verify all the algorithms are supported. */
|
|
+ if (kdriver_suitable(cap, krp)) {
|
|
+ if (best == NULL ||
|
|
+ cap->cc_koperations < best->cc_koperations)
|
|
+ best = cap;
|
|
+ }
|
|
+ }
|
|
+ if (best != NULL)
|
|
+ return best;
|
|
+ if (match == CRYPTOCAP_F_HARDWARE && (flags & CRYPTOCAP_F_SOFTWARE)) {
|
|
+ /* sort of an Algol 68-style for loop */
|
|
+ match = CRYPTOCAP_F_SOFTWARE;
|
|
+ goto again;
|
|
+ }
|
|
+ return best;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Dispatch an assymetric crypto request.
|
|
+ */
|
|
+static int
|
|
+crypto_kinvoke(struct cryptkop *krp, int crid)
|
|
+{
|
|
+ struct cryptocap *cap = NULL;
|
|
+ int error;
|
|
+ unsigned long d_flags;
|
|
+
|
|
+ KASSERT(krp != NULL, ("%s: krp == NULL", __func__));
|
|
+ KASSERT(krp->krp_callback != NULL,
|
|
+ ("%s: krp->crp_callback == NULL", __func__));
|
|
+
|
|
+ CRYPTO_DRIVER_LOCK();
|
|
+ if ((crid & (CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE)) == 0) {
|
|
+ cap = crypto_checkdriver(crid);
|
|
+ if (cap != NULL) {
|
|
+ /*
|
|
+ * Driver present, it must support the necessary
|
|
+ * algorithm and, if s/w drivers are excluded,
|
|
+ * it must be registered as hardware-backed.
|
|
+ */
|
|
+ if (!kdriver_suitable(cap, krp) ||
|
|
+ (!crypto_devallowsoft &&
|
|
+ (cap->cc_flags & CRYPTOCAP_F_HARDWARE) == 0))
|
|
+ cap = NULL;
|
|
+ }
|
|
+ } else {
|
|
+ /*
|
|
+ * No requested driver; select based on crid flags.
|
|
+ */
|
|
+ if (!crypto_devallowsoft) /* NB: disallow s/w drivers */
|
|
+ crid &= ~CRYPTOCAP_F_SOFTWARE;
|
|
+ cap = crypto_select_kdriver(krp, crid);
|
|
+ }
|
|
+ if (cap != NULL && !cap->cc_kqblocked) {
|
|
+ krp->krp_hid = cap - crypto_drivers;
|
|
+ cap->cc_koperations++;
|
|
+ CRYPTO_DRIVER_UNLOCK();
|
|
+ error = CRYPTODEV_KPROCESS(cap->cc_dev, krp, 0);
|
|
+ CRYPTO_DRIVER_LOCK();
|
|
+ if (error == ERESTART) {
|
|
+ cap->cc_koperations--;
|
|
+ CRYPTO_DRIVER_UNLOCK();
|
|
+ return (error);
|
|
+ }
|
|
+ /* return the actual device used */
|
|
+ krp->krp_crid = krp->krp_hid;
|
|
+ } else {
|
|
+ /*
|
|
+ * NB: cap is !NULL if device is blocked; in
|
|
+ * that case return ERESTART so the operation
|
|
+ * is resubmitted if possible.
|
|
+ */
|
|
+ error = (cap == NULL) ? ENODEV : ERESTART;
|
|
+ }
|
|
+ CRYPTO_DRIVER_UNLOCK();
|
|
+
|
|
+ if (error) {
|
|
+ krp->krp_status = error;
|
|
+ crypto_kdone(krp);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * Dispatch a crypto request to the appropriate crypto devices.
|
|
+ */
|
|
+static int
|
|
+crypto_invoke(struct cryptocap *cap, struct cryptop *crp, int hint)
|
|
+{
|
|
+ KASSERT(crp != NULL, ("%s: crp == NULL", __func__));
|
|
+ KASSERT(crp->crp_callback != NULL,
|
|
+ ("%s: crp->crp_callback == NULL", __func__));
|
|
+ KASSERT(crp->crp_desc != NULL, ("%s: crp->crp_desc == NULL", __func__));
|
|
+
|
|
+ dprintk("%s()\n", __FUNCTION__);
|
|
+
|
|
+#ifdef CRYPTO_TIMING
|
|
+ if (crypto_timing)
|
|
+ crypto_tstat(&cryptostats.cs_invoke, &crp->crp_tstamp);
|
|
+#endif
|
|
+ if (cap->cc_flags & CRYPTOCAP_F_CLEANUP) {
|
|
+ struct cryptodesc *crd;
|
|
+ u_int64_t nid;
|
|
+
|
|
+ /*
|
|
+ * Driver has unregistered; migrate the session and return
|
|
+ * an error to the caller so they'll resubmit the op.
|
|
+ *
|
|
+ * XXX: What if there are more already queued requests for this
|
|
+ * session?
|
|
+ */
|
|
+ crypto_freesession(crp->crp_sid);
|
|
+
|
|
+ for (crd = crp->crp_desc; crd->crd_next; crd = crd->crd_next)
|
|
+ crd->CRD_INI.cri_next = &(crd->crd_next->CRD_INI);
|
|
+
|
|
+ /* XXX propagate flags from initial session? */
|
|
+ if (crypto_newsession(&nid, &(crp->crp_desc->CRD_INI),
|
|
+ CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE) == 0)
|
|
+ crp->crp_sid = nid;
|
|
+
|
|
+ crp->crp_etype = EAGAIN;
|
|
+ crypto_done(crp);
|
|
+ return 0;
|
|
+ } else {
|
|
+ /*
|
|
+ * Invoke the driver to process the request.
|
|
+ */
|
|
+ return CRYPTODEV_PROCESS(cap->cc_dev, crp, hint);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Release a set of crypto descriptors.
|
|
+ */
|
|
+void
|
|
+crypto_freereq(struct cryptop *crp)
|
|
+{
|
|
+ struct cryptodesc *crd;
|
|
+
|
|
+ if (crp == NULL)
|
|
+ return;
|
|
+
|
|
+#ifdef DIAGNOSTIC
|
|
+ {
|
|
+ struct cryptop *crp2;
|
|
+ unsigned long q_flags;
|
|
+
|
|
+ CRYPTO_Q_LOCK();
|
|
+ TAILQ_FOREACH(crp2, &crp_q, crp_next) {
|
|
+ KASSERT(crp2 != crp,
|
|
+ ("Freeing cryptop from the crypto queue (%p).",
|
|
+ crp));
|
|
+ }
|
|
+ CRYPTO_Q_UNLOCK();
|
|
+ CRYPTO_RETQ_LOCK();
|
|
+ TAILQ_FOREACH(crp2, &crp_ret_q, crp_next) {
|
|
+ KASSERT(crp2 != crp,
|
|
+ ("Freeing cryptop from the return queue (%p).",
|
|
+ crp));
|
|
+ }
|
|
+ CRYPTO_RETQ_UNLOCK();
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ while ((crd = crp->crp_desc) != NULL) {
|
|
+ crp->crp_desc = crd->crd_next;
|
|
+ kmem_cache_free(cryptodesc_zone, crd);
|
|
+ }
|
|
+ kmem_cache_free(cryptop_zone, crp);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Acquire a set of crypto descriptors.
|
|
+ */
|
|
+struct cryptop *
|
|
+crypto_getreq(int num)
|
|
+{
|
|
+ struct cryptodesc *crd;
|
|
+ struct cryptop *crp;
|
|
+
|
|
+ crp = kmem_cache_alloc(cryptop_zone, SLAB_ATOMIC);
|
|
+ if (crp != NULL) {
|
|
+ memset(crp, 0, sizeof(*crp));
|
|
+ INIT_LIST_HEAD(&crp->crp_next);
|
|
+ init_waitqueue_head(&crp->crp_waitq);
|
|
+ while (num--) {
|
|
+ crd = kmem_cache_alloc(cryptodesc_zone, SLAB_ATOMIC);
|
|
+ if (crd == NULL) {
|
|
+ crypto_freereq(crp);
|
|
+ return NULL;
|
|
+ }
|
|
+ memset(crd, 0, sizeof(*crd));
|
|
+ crd->crd_next = crp->crp_desc;
|
|
+ crp->crp_desc = crd;
|
|
+ }
|
|
+ }
|
|
+ return crp;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Invoke the callback on behalf of the driver.
|
|
+ */
|
|
+void
|
|
+crypto_done(struct cryptop *crp)
|
|
+{
|
|
+ unsigned long q_flags;
|
|
+
|
|
+ dprintk("%s()\n", __FUNCTION__);
|
|
+ if ((crp->crp_flags & CRYPTO_F_DONE) == 0) {
|
|
+ crp->crp_flags |= CRYPTO_F_DONE;
|
|
+ CRYPTO_Q_LOCK();
|
|
+ crypto_q_cnt--;
|
|
+ CRYPTO_Q_UNLOCK();
|
|
+ } else
|
|
+ printk("crypto: crypto_done op already done, flags 0x%x",
|
|
+ crp->crp_flags);
|
|
+ if (crp->crp_etype != 0)
|
|
+ cryptostats.cs_errs++;
|
|
+ /*
|
|
+ * CBIMM means unconditionally do the callback immediately;
|
|
+ * CBIFSYNC means do the callback immediately only if the
|
|
+ * operation was done synchronously. Both are used to avoid
|
|
+ * doing extraneous context switches; the latter is mostly
|
|
+ * used with the software crypto driver.
|
|
+ */
|
|
+ if ((crp->crp_flags & CRYPTO_F_CBIMM) ||
|
|
+ ((crp->crp_flags & CRYPTO_F_CBIFSYNC) &&
|
|
+ (CRYPTO_SESID2CAPS(crp->crp_sid) & CRYPTOCAP_F_SYNC))) {
|
|
+ /*
|
|
+ * Do the callback directly. This is ok when the
|
|
+ * callback routine does very little (e.g. the
|
|
+ * /dev/crypto callback method just does a wakeup).
|
|
+ */
|
|
+ crp->crp_callback(crp);
|
|
+ } else {
|
|
+ unsigned long r_flags;
|
|
+ /*
|
|
+ * Normal case; queue the callback for the thread.
|
|
+ */
|
|
+ CRYPTO_RETQ_LOCK();
|
|
+ wake_up_interruptible(&cryptoretproc_wait);/* shared wait channel */
|
|
+ TAILQ_INSERT_TAIL(&crp_ret_q, crp, crp_next);
|
|
+ CRYPTO_RETQ_UNLOCK();
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Invoke the callback on behalf of the driver.
|
|
+ */
|
|
+void
|
|
+crypto_kdone(struct cryptkop *krp)
|
|
+{
|
|
+ struct cryptocap *cap;
|
|
+ unsigned long d_flags;
|
|
+
|
|
+ if ((krp->krp_flags & CRYPTO_KF_DONE) != 0)
|
|
+ printk("crypto: crypto_kdone op already done, flags 0x%x",
|
|
+ krp->krp_flags);
|
|
+ krp->krp_flags |= CRYPTO_KF_DONE;
|
|
+ if (krp->krp_status != 0)
|
|
+ cryptostats.cs_kerrs++;
|
|
+
|
|
+ CRYPTO_DRIVER_LOCK();
|
|
+ /* XXX: What if driver is loaded in the meantime? */
|
|
+ if (krp->krp_hid < crypto_drivers_num) {
|
|
+ cap = &crypto_drivers[krp->krp_hid];
|
|
+ cap->cc_koperations--;
|
|
+ KASSERT(cap->cc_koperations >= 0, ("cc_koperations < 0"));
|
|
+ if (cap->cc_flags & CRYPTOCAP_F_CLEANUP)
|
|
+ crypto_remove(cap);
|
|
+ }
|
|
+ CRYPTO_DRIVER_UNLOCK();
|
|
+
|
|
+ /*
|
|
+ * CBIMM means unconditionally do the callback immediately;
|
|
+ * This is used to avoid doing extraneous context switches
|
|
+ */
|
|
+ if ((krp->krp_flags & CRYPTO_KF_CBIMM)) {
|
|
+ /*
|
|
+ * Do the callback directly. This is ok when the
|
|
+ * callback routine does very little (e.g. the
|
|
+ * /dev/crypto callback method just does a wakeup).
|
|
+ */
|
|
+ krp->krp_callback(krp);
|
|
+ } else {
|
|
+ unsigned long r_flags;
|
|
+ /*
|
|
+ * Normal case; queue the callback for the thread.
|
|
+ */
|
|
+ CRYPTO_RETQ_LOCK();
|
|
+ wake_up_interruptible(&cryptoretproc_wait);/* shared wait channel */
|
|
+ TAILQ_INSERT_TAIL(&crp_ret_kq, krp, krp_next);
|
|
+ CRYPTO_RETQ_UNLOCK();
|
|
+ }
|
|
+}
|
|
+
|
|
+int
|
|
+crypto_getfeat(int *featp)
|
|
+{
|
|
+ int hid, kalg, feat = 0;
|
|
+ unsigned long d_flags;
|
|
+
|
|
+ CRYPTO_DRIVER_LOCK();
|
|
+ for (hid = 0; hid < crypto_drivers_num; hid++) {
|
|
+ const struct cryptocap *cap = &crypto_drivers[hid];
|
|
+
|
|
+ if ((cap->cc_flags & CRYPTOCAP_F_SOFTWARE) &&
|
|
+ !crypto_devallowsoft) {
|
|
+ continue;
|
|
+ }
|
|
+ for (kalg = 0; kalg < CRK_ALGORITHM_MAX; kalg++)
|
|
+ if (cap->cc_kalg[kalg] & CRYPTO_ALG_FLAG_SUPPORTED)
|
|
+ feat |= 1 << kalg;
|
|
+ }
|
|
+ CRYPTO_DRIVER_UNLOCK();
|
|
+ *featp = feat;
|
|
+ return (0);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Crypto thread, dispatches crypto requests.
|
|
+ */
|
|
+static int
|
|
+crypto_proc(void *arg)
|
|
+{
|
|
+ struct cryptop *crp, *submit;
|
|
+ struct cryptkop *krp, *krpp;
|
|
+ struct cryptocap *cap;
|
|
+ u_int32_t hid;
|
|
+ int result, hint;
|
|
+ unsigned long q_flags;
|
|
+ int loopcount = 0;
|
|
+
|
|
+ set_current_state(TASK_INTERRUPTIBLE);
|
|
+
|
|
+ CRYPTO_Q_LOCK();
|
|
+ for (;;) {
|
|
+ /*
|
|
+ * we need to make sure we don't get into a busy loop with nothing
|
|
+ * to do, the two crypto_all_*blocked vars help us find out when
|
|
+ * we are all full and can do nothing on any driver or Q. If so we
|
|
+ * wait for an unblock.
|
|
+ */
|
|
+ crypto_all_qblocked = !list_empty(&crp_q);
|
|
+
|
|
+ /*
|
|
+ * Find the first element in the queue that can be
|
|
+ * processed and look-ahead to see if multiple ops
|
|
+ * are ready for the same driver.
|
|
+ */
|
|
+ submit = NULL;
|
|
+ hint = 0;
|
|
+ list_for_each_entry(crp, &crp_q, crp_next) {
|
|
+ hid = CRYPTO_SESID2HID(crp->crp_sid);
|
|
+ cap = crypto_checkdriver(hid);
|
|
+ /*
|
|
+ * Driver cannot disappear when there is an active
|
|
+ * session.
|
|
+ */
|
|
+ KASSERT(cap != NULL, ("%s:%u Driver disappeared.",
|
|
+ __func__, __LINE__));
|
|
+ if (cap == NULL || cap->cc_dev == NULL) {
|
|
+ /* Op needs to be migrated, process it. */
|
|
+ if (submit == NULL)
|
|
+ submit = crp;
|
|
+ break;
|
|
+ }
|
|
+ if (!cap->cc_qblocked) {
|
|
+ if (submit != NULL) {
|
|
+ /*
|
|
+ * We stop on finding another op,
|
|
+ * regardless whether its for the same
|
|
+ * driver or not. We could keep
|
|
+ * searching the queue but it might be
|
|
+ * better to just use a per-driver
|
|
+ * queue instead.
|
|
+ */
|
|
+ if (CRYPTO_SESID2HID(submit->crp_sid) == hid)
|
|
+ hint = CRYPTO_HINT_MORE;
|
|
+ break;
|
|
+ } else {
|
|
+ submit = crp;
|
|
+ if ((submit->crp_flags & CRYPTO_F_BATCH) == 0)
|
|
+ break;
|
|
+ /* keep scanning for more are q'd */
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ if (submit != NULL) {
|
|
+ hid = CRYPTO_SESID2HID(submit->crp_sid);
|
|
+ crypto_all_qblocked = 0;
|
|
+ list_del(&submit->crp_next);
|
|
+ crypto_drivers[hid].cc_unqblocked = 1;
|
|
+ cap = crypto_checkdriver(hid);
|
|
+ CRYPTO_Q_UNLOCK();
|
|
+ KASSERT(cap != NULL, ("%s:%u Driver disappeared.",
|
|
+ __func__, __LINE__));
|
|
+ result = crypto_invoke(cap, submit, hint);
|
|
+ CRYPTO_Q_LOCK();
|
|
+ if (result == ERESTART) {
|
|
+ /*
|
|
+ * The driver ran out of resources, mark the
|
|
+ * driver ``blocked'' for cryptop's and put
|
|
+ * the request back in the queue. It would
|
|
+ * best to put the request back where we got
|
|
+ * it but that's hard so for now we put it
|
|
+ * at the front. This should be ok; putting
|
|
+ * it at the end does not work.
|
|
+ */
|
|
+ /* XXX validate sid again? */
|
|
+ list_add(&submit->crp_next, &crp_q);
|
|
+ cryptostats.cs_blocks++;
|
|
+ if (crypto_drivers[hid].cc_unqblocked)
|
|
+ crypto_drivers[hid].cc_qblocked=0;
|
|
+ crypto_drivers[hid].cc_unqblocked=0;
|
|
+ }
|
|
+ crypto_drivers[hid].cc_unqblocked = 0;
|
|
+ }
|
|
+
|
|
+ crypto_all_kqblocked = !list_empty(&crp_kq);
|
|
+
|
|
+ /* As above, but for key ops */
|
|
+ krp = NULL;
|
|
+ list_for_each_entry(krpp, &crp_kq, krp_next) {
|
|
+ cap = crypto_checkdriver(krpp->krp_hid);
|
|
+ if (cap == NULL || cap->cc_dev == NULL) {
|
|
+ /*
|
|
+ * Operation needs to be migrated, invalidate
|
|
+ * the assigned device so it will reselect a
|
|
+ * new one below. Propagate the original
|
|
+ * crid selection flags if supplied.
|
|
+ */
|
|
+ krp->krp_hid = krp->krp_crid &
|
|
+ (CRYPTOCAP_F_SOFTWARE|CRYPTOCAP_F_HARDWARE);
|
|
+ if (krp->krp_hid == 0)
|
|
+ krp->krp_hid =
|
|
+ CRYPTOCAP_F_SOFTWARE|CRYPTOCAP_F_HARDWARE;
|
|
+ break;
|
|
+ }
|
|
+ if (!cap->cc_kqblocked) {
|
|
+ krp = krpp;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if (krp != NULL) {
|
|
+ crypto_all_kqblocked = 0;
|
|
+ list_del(&krp->krp_next);
|
|
+ crypto_drivers[krp->krp_hid].cc_kqblocked = 1;
|
|
+ CRYPTO_Q_UNLOCK();
|
|
+ result = crypto_kinvoke(krp, krp->krp_hid);
|
|
+ CRYPTO_Q_LOCK();
|
|
+ if (result == ERESTART) {
|
|
+ /*
|
|
+ * The driver ran out of resources, mark the
|
|
+ * driver ``blocked'' for cryptkop's and put
|
|
+ * the request back in the queue. It would
|
|
+ * best to put the request back where we got
|
|
+ * it but that's hard so for now we put it
|
|
+ * at the front. This should be ok; putting
|
|
+ * it at the end does not work.
|
|
+ */
|
|
+ /* XXX validate sid again? */
|
|
+ list_add(&krp->krp_next, &crp_kq);
|
|
+ cryptostats.cs_kblocks++;
|
|
+ } else
|
|
+ crypto_drivers[krp->krp_hid].cc_kqblocked = 0;
|
|
+ }
|
|
+
|
|
+ if (submit == NULL && krp == NULL) {
|
|
+ /*
|
|
+ * Nothing more to be processed. Sleep until we're
|
|
+ * woken because there are more ops to process.
|
|
+ * This happens either by submission or by a driver
|
|
+ * becoming unblocked and notifying us through
|
|
+ * crypto_unblock. Note that when we wakeup we
|
|
+ * start processing each queue again from the
|
|
+ * front. It's not clear that it's important to
|
|
+ * preserve this ordering since ops may finish
|
|
+ * out of order if dispatched to different devices
|
|
+ * and some become blocked while others do not.
|
|
+ */
|
|
+ dprintk("%s - sleeping (qe=%d qb=%d kqe=%d kqb=%d)\n",
|
|
+ __FUNCTION__,
|
|
+ list_empty(&crp_q), crypto_all_qblocked,
|
|
+ list_empty(&crp_kq), crypto_all_kqblocked);
|
|
+ loopcount = 0;
|
|
+ CRYPTO_Q_UNLOCK();
|
|
+ wait_event_interruptible(cryptoproc_wait,
|
|
+ !(list_empty(&crp_q) || crypto_all_qblocked) ||
|
|
+ !(list_empty(&crp_kq) || crypto_all_kqblocked) ||
|
|
+ kthread_should_stop());
|
|
+ if (signal_pending (current)) {
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
|
|
+ spin_lock_irq(¤t->sigmask_lock);
|
|
+#endif
|
|
+ flush_signals(current);
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
|
|
+ spin_unlock_irq(¤t->sigmask_lock);
|
|
+#endif
|
|
+ }
|
|
+ CRYPTO_Q_LOCK();
|
|
+ dprintk("%s - awake\n", __FUNCTION__);
|
|
+ if (kthread_should_stop())
|
|
+ break;
|
|
+ cryptostats.cs_intrs++;
|
|
+ } else if (loopcount > crypto_max_loopcount) {
|
|
+ /*
|
|
+ * Give other processes a chance to run if we've
|
|
+ * been using the CPU exclusively for a while.
|
|
+ */
|
|
+ loopcount = 0;
|
|
+ CRYPTO_Q_UNLOCK();
|
|
+ schedule();
|
|
+ CRYPTO_Q_LOCK();
|
|
+ }
|
|
+ loopcount++;
|
|
+ }
|
|
+ CRYPTO_Q_UNLOCK();
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Crypto returns thread, does callbacks for processed crypto requests.
|
|
+ * Callbacks are done here, rather than in the crypto drivers, because
|
|
+ * callbacks typically are expensive and would slow interrupt handling.
|
|
+ */
|
|
+static int
|
|
+crypto_ret_proc(void *arg)
|
|
+{
|
|
+ struct cryptop *crpt;
|
|
+ struct cryptkop *krpt;
|
|
+ unsigned long r_flags;
|
|
+
|
|
+ set_current_state(TASK_INTERRUPTIBLE);
|
|
+
|
|
+ CRYPTO_RETQ_LOCK();
|
|
+ for (;;) {
|
|
+ /* Harvest return q's for completed ops */
|
|
+ crpt = NULL;
|
|
+ if (!list_empty(&crp_ret_q))
|
|
+ crpt = list_entry(crp_ret_q.next, typeof(*crpt), crp_next);
|
|
+ if (crpt != NULL)
|
|
+ list_del(&crpt->crp_next);
|
|
+
|
|
+ krpt = NULL;
|
|
+ if (!list_empty(&crp_ret_kq))
|
|
+ krpt = list_entry(crp_ret_kq.next, typeof(*krpt), krp_next);
|
|
+ if (krpt != NULL)
|
|
+ list_del(&krpt->krp_next);
|
|
+
|
|
+ if (crpt != NULL || krpt != NULL) {
|
|
+ CRYPTO_RETQ_UNLOCK();
|
|
+ /*
|
|
+ * Run callbacks unlocked.
|
|
+ */
|
|
+ if (crpt != NULL)
|
|
+ crpt->crp_callback(crpt);
|
|
+ if (krpt != NULL)
|
|
+ krpt->krp_callback(krpt);
|
|
+ CRYPTO_RETQ_LOCK();
|
|
+ } else {
|
|
+ /*
|
|
+ * Nothing more to be processed. Sleep until we're
|
|
+ * woken because there are more returns to process.
|
|
+ */
|
|
+ dprintk("%s - sleeping\n", __FUNCTION__);
|
|
+ CRYPTO_RETQ_UNLOCK();
|
|
+ wait_event_interruptible(cryptoretproc_wait,
|
|
+ !list_empty(&crp_ret_q) ||
|
|
+ !list_empty(&crp_ret_kq) ||
|
|
+ kthread_should_stop());
|
|
+ if (signal_pending (current)) {
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
|
|
+ spin_lock_irq(¤t->sigmask_lock);
|
|
+#endif
|
|
+ flush_signals(current);
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
|
|
+ spin_unlock_irq(¤t->sigmask_lock);
|
|
+#endif
|
|
+ }
|
|
+ CRYPTO_RETQ_LOCK();
|
|
+ dprintk("%s - awake\n", __FUNCTION__);
|
|
+ if (kthread_should_stop()) {
|
|
+ dprintk("%s - EXITING!\n", __FUNCTION__);
|
|
+ break;
|
|
+ }
|
|
+ cryptostats.cs_rets++;
|
|
+ }
|
|
+ }
|
|
+ CRYPTO_RETQ_UNLOCK();
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+#if 0 /* should put this into /proc or something */
|
|
+static void
|
|
+db_show_drivers(void)
|
|
+{
|
|
+ int hid;
|
|
+
|
|
+ db_printf("%12s %4s %4s %8s %2s %2s\n"
|
|
+ , "Device"
|
|
+ , "Ses"
|
|
+ , "Kops"
|
|
+ , "Flags"
|
|
+ , "QB"
|
|
+ , "KB"
|
|
+ );
|
|
+ for (hid = 0; hid < crypto_drivers_num; hid++) {
|
|
+ const struct cryptocap *cap = &crypto_drivers[hid];
|
|
+ if (cap->cc_dev == NULL)
|
|
+ continue;
|
|
+ db_printf("%-12s %4u %4u %08x %2u %2u\n"
|
|
+ , device_get_nameunit(cap->cc_dev)
|
|
+ , cap->cc_sessions
|
|
+ , cap->cc_koperations
|
|
+ , cap->cc_flags
|
|
+ , cap->cc_qblocked
|
|
+ , cap->cc_kqblocked
|
|
+ );
|
|
+ }
|
|
+}
|
|
+
|
|
+DB_SHOW_COMMAND(crypto, db_show_crypto)
|
|
+{
|
|
+ struct cryptop *crp;
|
|
+
|
|
+ db_show_drivers();
|
|
+ db_printf("\n");
|
|
+
|
|
+ db_printf("%4s %8s %4s %4s %4s %4s %8s %8s\n",
|
|
+ "HID", "Caps", "Ilen", "Olen", "Etype", "Flags",
|
|
+ "Desc", "Callback");
|
|
+ TAILQ_FOREACH(crp, &crp_q, crp_next) {
|
|
+ db_printf("%4u %08x %4u %4u %4u %04x %8p %8p\n"
|
|
+ , (int) CRYPTO_SESID2HID(crp->crp_sid)
|
|
+ , (int) CRYPTO_SESID2CAPS(crp->crp_sid)
|
|
+ , crp->crp_ilen, crp->crp_olen
|
|
+ , crp->crp_etype
|
|
+ , crp->crp_flags
|
|
+ , crp->crp_desc
|
|
+ , crp->crp_callback
|
|
+ );
|
|
+ }
|
|
+ if (!TAILQ_EMPTY(&crp_ret_q)) {
|
|
+ db_printf("\n%4s %4s %4s %8s\n",
|
|
+ "HID", "Etype", "Flags", "Callback");
|
|
+ TAILQ_FOREACH(crp, &crp_ret_q, crp_next) {
|
|
+ db_printf("%4u %4u %04x %8p\n"
|
|
+ , (int) CRYPTO_SESID2HID(crp->crp_sid)
|
|
+ , crp->crp_etype
|
|
+ , crp->crp_flags
|
|
+ , crp->crp_callback
|
|
+ );
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+DB_SHOW_COMMAND(kcrypto, db_show_kcrypto)
|
|
+{
|
|
+ struct cryptkop *krp;
|
|
+
|
|
+ db_show_drivers();
|
|
+ db_printf("\n");
|
|
+
|
|
+ db_printf("%4s %5s %4s %4s %8s %4s %8s\n",
|
|
+ "Op", "Status", "#IP", "#OP", "CRID", "HID", "Callback");
|
|
+ TAILQ_FOREACH(krp, &crp_kq, krp_next) {
|
|
+ db_printf("%4u %5u %4u %4u %08x %4u %8p\n"
|
|
+ , krp->krp_op
|
|
+ , krp->krp_status
|
|
+ , krp->krp_iparams, krp->krp_oparams
|
|
+ , krp->krp_crid, krp->krp_hid
|
|
+ , krp->krp_callback
|
|
+ );
|
|
+ }
|
|
+ if (!TAILQ_EMPTY(&crp_ret_q)) {
|
|
+ db_printf("%4s %5s %8s %4s %8s\n",
|
|
+ "Op", "Status", "CRID", "HID", "Callback");
|
|
+ TAILQ_FOREACH(krp, &crp_ret_kq, krp_next) {
|
|
+ db_printf("%4u %5u %08x %4u %8p\n"
|
|
+ , krp->krp_op
|
|
+ , krp->krp_status
|
|
+ , krp->krp_crid, krp->krp_hid
|
|
+ , krp->krp_callback
|
|
+ );
|
|
+ }
|
|
+ }
|
|
+}
|
|
+#endif
|
|
+
|
|
+
|
|
+static int
|
|
+crypto_init(void)
|
|
+{
|
|
+ int error;
|
|
+ unsigned long cpu;
|
|
+
|
|
+ dprintk("%s(%p)\n", __FUNCTION__, (void *) crypto_init);
|
|
+
|
|
+ if (crypto_initted)
|
|
+ return 0;
|
|
+ crypto_initted = 1;
|
|
+
|
|
+ spin_lock_init(&crypto_drivers_lock);
|
|
+ spin_lock_init(&crypto_q_lock);
|
|
+ spin_lock_init(&crypto_ret_q_lock);
|
|
+
|
|
+ cryptop_zone = kmem_cache_create("cryptop", sizeof(struct cryptop),
|
|
+ 0, SLAB_HWCACHE_ALIGN, NULL
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
|
|
+ , NULL
|
|
+#endif
|
|
+ );
|
|
+
|
|
+ cryptodesc_zone = kmem_cache_create("cryptodesc", sizeof(struct cryptodesc),
|
|
+ 0, SLAB_HWCACHE_ALIGN, NULL
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
|
|
+ , NULL
|
|
+#endif
|
|
+ );
|
|
+
|
|
+ if (cryptodesc_zone == NULL || cryptop_zone == NULL) {
|
|
+ printk("crypto: crypto_init cannot setup crypto zones\n");
|
|
+ error = ENOMEM;
|
|
+ goto bad;
|
|
+ }
|
|
+
|
|
+ crypto_drivers_num = CRYPTO_DRIVERS_INITIAL;
|
|
+ crypto_drivers = kmalloc(crypto_drivers_num * sizeof(struct cryptocap),
|
|
+ GFP_KERNEL);
|
|
+ if (crypto_drivers == NULL) {
|
|
+ printk("crypto: crypto_init cannot setup crypto drivers\n");
|
|
+ error = ENOMEM;
|
|
+ goto bad;
|
|
+ }
|
|
+
|
|
+ memset(crypto_drivers, 0, crypto_drivers_num * sizeof(struct cryptocap));
|
|
+
|
|
+ ocf_for_each_cpu(cpu) {
|
|
+ cryptoproc[cpu] = kthread_create(crypto_proc, (void *) cpu,
|
|
+ "ocf_%d", (int) cpu);
|
|
+ if (IS_ERR(cryptoproc[cpu])) {
|
|
+ error = PTR_ERR(cryptoproc[cpu]);
|
|
+ printk("crypto: crypto_init cannot start crypto thread; error %d",
|
|
+ error);
|
|
+ goto bad;
|
|
+ }
|
|
+ kthread_bind(cryptoproc[cpu], cpu);
|
|
+ wake_up_process(cryptoproc[cpu]);
|
|
+
|
|
+ cryptoretproc[cpu] = kthread_create(crypto_ret_proc, (void *) cpu,
|
|
+ "ocf_ret_%d", (int) cpu);
|
|
+ if (IS_ERR(cryptoretproc[cpu])) {
|
|
+ error = PTR_ERR(cryptoretproc[cpu]);
|
|
+ printk("crypto: crypto_init cannot start cryptoret thread; error %d",
|
|
+ error);
|
|
+ goto bad;
|
|
+ }
|
|
+ kthread_bind(cryptoretproc[cpu], cpu);
|
|
+ wake_up_process(cryptoretproc[cpu]);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+bad:
|
|
+ crypto_exit();
|
|
+ return error;
|
|
+}
|
|
+
|
|
+
|
|
+static void
|
|
+crypto_exit(void)
|
|
+{
|
|
+ int cpu;
|
|
+
|
|
+ dprintk("%s()\n", __FUNCTION__);
|
|
+
|
|
+ /*
|
|
+ * Terminate any crypto threads.
|
|
+ */
|
|
+ ocf_for_each_cpu(cpu) {
|
|
+ kthread_stop(cryptoproc[cpu]);
|
|
+ kthread_stop(cryptoretproc[cpu]);
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Reclaim dynamically allocated resources.
|
|
+ */
|
|
+ if (crypto_drivers != NULL)
|
|
+ kfree(crypto_drivers);
|
|
+
|
|
+ if (cryptodesc_zone != NULL)
|
|
+ kmem_cache_destroy(cryptodesc_zone);
|
|
+ if (cryptop_zone != NULL)
|
|
+ kmem_cache_destroy(cryptop_zone);
|
|
+}
|
|
+
|
|
+
|
|
+EXPORT_SYMBOL(crypto_newsession);
|
|
+EXPORT_SYMBOL(crypto_freesession);
|
|
+EXPORT_SYMBOL(crypto_get_driverid);
|
|
+EXPORT_SYMBOL(crypto_kregister);
|
|
+EXPORT_SYMBOL(crypto_register);
|
|
+EXPORT_SYMBOL(crypto_unregister);
|
|
+EXPORT_SYMBOL(crypto_unregister_all);
|
|
+EXPORT_SYMBOL(crypto_unblock);
|
|
+EXPORT_SYMBOL(crypto_dispatch);
|
|
+EXPORT_SYMBOL(crypto_kdispatch);
|
|
+EXPORT_SYMBOL(crypto_freereq);
|
|
+EXPORT_SYMBOL(crypto_getreq);
|
|
+EXPORT_SYMBOL(crypto_done);
|
|
+EXPORT_SYMBOL(crypto_kdone);
|
|
+EXPORT_SYMBOL(crypto_getfeat);
|
|
+EXPORT_SYMBOL(crypto_userasymcrypto);
|
|
+EXPORT_SYMBOL(crypto_getcaps);
|
|
+EXPORT_SYMBOL(crypto_find_driver);
|
|
+EXPORT_SYMBOL(crypto_find_device_byhid);
|
|
+
|
|
+module_init(crypto_init);
|
|
+module_exit(crypto_exit);
|
|
+
|
|
+MODULE_LICENSE("BSD");
|
|
+MODULE_AUTHOR("David McCullough <david_mccullough@mcafee.com>");
|
|
+MODULE_DESCRIPTION("OCF (OpenBSD Cryptographic Framework)");
|
|
diff --git a/crypto/ocf/cryptodev.c b/crypto/ocf/cryptodev.c
|
|
new file mode 100644
|
|
index 0000000..2ee3618
|
|
--- /dev/null
|
|
+++ b/crypto/ocf/cryptodev.c
|
|
@@ -0,0 +1,1069 @@
|
|
+/* $OpenBSD: cryptodev.c,v 1.52 2002/06/19 07:22:46 deraadt Exp $ */
|
|
+
|
|
+/*-
|
|
+ * Linux port done by David McCullough <david_mccullough@mcafee.com>
|
|
+ * Copyright (C) 2006-2010 David McCullough
|
|
+ * Copyright (C) 2004-2005 Intel Corporation.
|
|
+ * The license and original author are listed below.
|
|
+ *
|
|
+ * Copyright (c) 2001 Theo de Raadt
|
|
+ * Copyright (c) 2002-2006 Sam Leffler, Errno Consulting
|
|
+ *
|
|
+ * Redistribution and use in source and binary forms, with or without
|
|
+ * modification, are permitted provided that the following conditions
|
|
+ * are met:
|
|
+ *
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
+ * 2. Redistributions in binary form must reproduce the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer in the
|
|
+ * documentation and/or other materials provided with the distribution.
|
|
+ * 3. The name of the author may not be used to endorse or promote products
|
|
+ * derived from this software without specific prior written permission.
|
|
+ *
|
|
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
+ *
|
|
+ * Effort sponsored in part by the Defense Advanced Research Projects
|
|
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
|
|
+ * Materiel Command, USAF, under agreement number F30602-01-2-0537.
|
|
+ *
|
|
+__FBSDID("$FreeBSD: src/sys/opencrypto/cryptodev.c,v 1.34 2007/05/09 19:37:02 gnn Exp $");
|
|
+ */
|
|
+
|
|
+#include <linux/version.h>
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) && !defined(AUTOCONF_INCLUDED)
|
|
+#include <linux/config.h>
|
|
+#endif
|
|
+#include <linux/types.h>
|
|
+#include <linux/time.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/list.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/sched.h>
|
|
+#include <linux/unistd.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/wait.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/fs.h>
|
|
+#include <linux/dcache.h>
|
|
+#include <linux/file.h>
|
|
+#include <linux/mount.h>
|
|
+#include <linux/miscdevice.h>
|
|
+#include <asm/uaccess.h>
|
|
+
|
|
+#include <cryptodev.h>
|
|
+#include <uio.h>
|
|
+
|
|
+extern asmlinkage long sys_dup(unsigned int fildes);
|
|
+
|
|
+#define debug cryptodev_debug
|
|
+int cryptodev_debug = 0;
|
|
+module_param(cryptodev_debug, int, 0644);
|
|
+MODULE_PARM_DESC(cryptodev_debug, "Enable cryptodev debug");
|
|
+
|
|
+struct csession_info {
|
|
+ u_int16_t blocksize;
|
|
+ u_int16_t minkey, maxkey;
|
|
+
|
|
+ u_int16_t keysize;
|
|
+ /* u_int16_t hashsize; */
|
|
+ u_int16_t authsize;
|
|
+ u_int16_t authkey;
|
|
+ /* u_int16_t ctxsize; */
|
|
+};
|
|
+
|
|
+struct csession {
|
|
+ struct list_head list;
|
|
+ u_int64_t sid;
|
|
+ u_int32_t ses;
|
|
+
|
|
+ wait_queue_head_t waitq;
|
|
+
|
|
+ u_int32_t cipher;
|
|
+
|
|
+ u_int32_t mac;
|
|
+
|
|
+ caddr_t key;
|
|
+ int keylen;
|
|
+ u_char tmp_iv[EALG_MAX_BLOCK_LEN];
|
|
+
|
|
+ caddr_t mackey;
|
|
+ int mackeylen;
|
|
+
|
|
+ struct csession_info info;
|
|
+
|
|
+ struct iovec iovec;
|
|
+ struct uio uio;
|
|
+ int error;
|
|
+};
|
|
+
|
|
+struct fcrypt {
|
|
+ struct list_head csessions;
|
|
+ int sesn;
|
|
+};
|
|
+
|
|
+static struct csession *csefind(struct fcrypt *, u_int);
|
|
+static int csedelete(struct fcrypt *, struct csession *);
|
|
+static struct csession *cseadd(struct fcrypt *, struct csession *);
|
|
+static struct csession *csecreate(struct fcrypt *, u_int64_t,
|
|
+ struct cryptoini *crie, struct cryptoini *cria, struct csession_info *);
|
|
+static int csefree(struct csession *);
|
|
+
|
|
+static int cryptodev_op(struct csession *, struct crypt_op *);
|
|
+static int cryptodev_key(struct crypt_kop *);
|
|
+static int cryptodev_find(struct crypt_find_op *);
|
|
+
|
|
+static int cryptodev_cb(void *);
|
|
+static int cryptodev_open(struct inode *inode, struct file *filp);
|
|
+
|
|
+/*
|
|
+ * Check a crypto identifier to see if it requested
|
|
+ * a valid crid and it's capabilities match.
|
|
+ */
|
|
+static int
|
|
+checkcrid(int crid)
|
|
+{
|
|
+ int hid = crid & ~(CRYPTOCAP_F_SOFTWARE | CRYPTOCAP_F_HARDWARE);
|
|
+ int typ = crid & (CRYPTOCAP_F_SOFTWARE | CRYPTOCAP_F_HARDWARE);
|
|
+ int caps = 0;
|
|
+
|
|
+ /* if the user hasn't selected a driver, then just call newsession */
|
|
+ if (hid == 0 && typ != 0)
|
|
+ return 0;
|
|
+
|
|
+ caps = crypto_getcaps(hid);
|
|
+
|
|
+ /* didn't find anything with capabilities */
|
|
+ if (caps == 0) {
|
|
+ dprintk("%s: hid=%x typ=%x not matched\n", __FUNCTION__, hid, typ);
|
|
+ return EINVAL;
|
|
+ }
|
|
+
|
|
+ /* the user didn't specify SW or HW, so the driver is ok */
|
|
+ if (typ == 0)
|
|
+ return 0;
|
|
+
|
|
+ /* if the type specified didn't match */
|
|
+ if (typ != (caps & (CRYPTOCAP_F_SOFTWARE | CRYPTOCAP_F_HARDWARE))) {
|
|
+ dprintk("%s: hid=%x typ=%x caps=%x not matched\n", __FUNCTION__,
|
|
+ hid, typ, caps);
|
|
+ return EINVAL;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+cryptodev_op(struct csession *cse, struct crypt_op *cop)
|
|
+{
|
|
+ struct cryptop *crp = NULL;
|
|
+ struct cryptodesc *crde = NULL, *crda = NULL;
|
|
+ int error = 0;
|
|
+
|
|
+ dprintk("%s()\n", __FUNCTION__);
|
|
+ if (cop->len > CRYPTO_MAX_DATA_LEN) {
|
|
+ dprintk("%s: %d > %d\n", __FUNCTION__, cop->len, CRYPTO_MAX_DATA_LEN);
|
|
+ return (E2BIG);
|
|
+ }
|
|
+
|
|
+ if (cse->info.blocksize && (cop->len % cse->info.blocksize) != 0) {
|
|
+ dprintk("%s: blocksize=%d len=%d\n", __FUNCTION__, cse->info.blocksize,
|
|
+ cop->len);
|
|
+ return (EINVAL);
|
|
+ }
|
|
+
|
|
+ cse->uio.uio_iov = &cse->iovec;
|
|
+ cse->uio.uio_iovcnt = 1;
|
|
+ cse->uio.uio_offset = 0;
|
|
+#if 0
|
|
+ cse->uio.uio_resid = cop->len;
|
|
+ cse->uio.uio_segflg = UIO_SYSSPACE;
|
|
+ cse->uio.uio_rw = UIO_WRITE;
|
|
+ cse->uio.uio_td = td;
|
|
+#endif
|
|
+ cse->uio.uio_iov[0].iov_len = cop->len;
|
|
+ if (cse->info.authsize)
|
|
+ cse->uio.uio_iov[0].iov_len += cse->info.authsize;
|
|
+ cse->uio.uio_iov[0].iov_base = kmalloc(cse->uio.uio_iov[0].iov_len,
|
|
+ GFP_KERNEL);
|
|
+
|
|
+ if (cse->uio.uio_iov[0].iov_base == NULL) {
|
|
+ dprintk("%s: iov_base kmalloc(%d) failed\n", __FUNCTION__,
|
|
+ (int)cse->uio.uio_iov[0].iov_len);
|
|
+ return (ENOMEM);
|
|
+ }
|
|
+
|
|
+ crp = crypto_getreq((cse->info.blocksize != 0) + (cse->info.authsize != 0));
|
|
+ if (crp == NULL) {
|
|
+ dprintk("%s: ENOMEM\n", __FUNCTION__);
|
|
+ error = ENOMEM;
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ if (cse->info.authsize && cse->info.blocksize) {
|
|
+ if (cop->op == COP_ENCRYPT) {
|
|
+ crde = crp->crp_desc;
|
|
+ crda = crde->crd_next;
|
|
+ } else {
|
|
+ crda = crp->crp_desc;
|
|
+ crde = crda->crd_next;
|
|
+ }
|
|
+ } else if (cse->info.authsize) {
|
|
+ crda = crp->crp_desc;
|
|
+ } else if (cse->info.blocksize) {
|
|
+ crde = crp->crp_desc;
|
|
+ } else {
|
|
+ dprintk("%s: bad request\n", __FUNCTION__);
|
|
+ error = EINVAL;
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ if ((error = copy_from_user(cse->uio.uio_iov[0].iov_base, cop->src,
|
|
+ cop->len))) {
|
|
+ dprintk("%s: bad copy\n", __FUNCTION__);
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ if (crda) {
|
|
+ crda->crd_skip = 0;
|
|
+ crda->crd_len = cop->len;
|
|
+ crda->crd_inject = cop->len;
|
|
+
|
|
+ crda->crd_alg = cse->mac;
|
|
+ crda->crd_key = cse->mackey;
|
|
+ crda->crd_klen = cse->mackeylen * 8;
|
|
+ }
|
|
+
|
|
+ if (crde) {
|
|
+ if (cop->op == COP_ENCRYPT)
|
|
+ crde->crd_flags |= CRD_F_ENCRYPT;
|
|
+ else
|
|
+ crde->crd_flags &= ~CRD_F_ENCRYPT;
|
|
+ crde->crd_len = cop->len;
|
|
+ crde->crd_inject = 0;
|
|
+
|
|
+ crde->crd_alg = cse->cipher;
|
|
+ crde->crd_key = cse->key;
|
|
+ crde->crd_klen = cse->keylen * 8;
|
|
+ }
|
|
+
|
|
+ crp->crp_ilen = cse->uio.uio_iov[0].iov_len;
|
|
+ crp->crp_flags = CRYPTO_F_IOV | CRYPTO_F_CBIMM
|
|
+ | (cop->flags & COP_F_BATCH);
|
|
+ crp->crp_buf = (caddr_t)&cse->uio;
|
|
+ crp->crp_callback = (int (*) (struct cryptop *)) cryptodev_cb;
|
|
+ crp->crp_sid = cse->sid;
|
|
+ crp->crp_opaque = (void *)cse;
|
|
+
|
|
+ if (cop->iv) {
|
|
+ if (crde == NULL) {
|
|
+ error = EINVAL;
|
|
+ dprintk("%s no crde\n", __FUNCTION__);
|
|
+ goto bail;
|
|
+ }
|
|
+ if (cse->cipher == CRYPTO_ARC4) { /* XXX use flag? */
|
|
+ error = EINVAL;
|
|
+ dprintk("%s arc4 with IV\n", __FUNCTION__);
|
|
+ goto bail;
|
|
+ }
|
|
+ if ((error = copy_from_user(cse->tmp_iv, cop->iv,
|
|
+ cse->info.blocksize))) {
|
|
+ dprintk("%s bad iv copy\n", __FUNCTION__);
|
|
+ goto bail;
|
|
+ }
|
|
+ memcpy(crde->crd_iv, cse->tmp_iv, cse->info.blocksize);
|
|
+ crde->crd_flags |= CRD_F_IV_EXPLICIT | CRD_F_IV_PRESENT;
|
|
+ crde->crd_skip = 0;
|
|
+ } else if (cse->cipher == CRYPTO_ARC4) { /* XXX use flag? */
|
|
+ crde->crd_skip = 0;
|
|
+ } else if (crde) {
|
|
+ crde->crd_flags |= CRD_F_IV_PRESENT;
|
|
+ crde->crd_skip = cse->info.blocksize;
|
|
+ crde->crd_len -= cse->info.blocksize;
|
|
+ }
|
|
+
|
|
+ if (cop->mac && crda == NULL) {
|
|
+ error = EINVAL;
|
|
+ dprintk("%s no crda\n", __FUNCTION__);
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Let the dispatch run unlocked, then, interlock against the
|
|
+ * callback before checking if the operation completed and going
|
|
+ * to sleep. This insures drivers don't inherit our lock which
|
|
+ * results in a lock order reversal between crypto_dispatch forced
|
|
+ * entry and the crypto_done callback into us.
|
|
+ */
|
|
+ error = crypto_dispatch(crp);
|
|
+ if (error) {
|
|
+ dprintk("%s error in crypto_dispatch\n", __FUNCTION__);
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ dprintk("%s about to WAIT\n", __FUNCTION__);
|
|
+ /*
|
|
+ * we really need to wait for driver to complete to maintain
|
|
+ * state, luckily interrupts will be remembered
|
|
+ */
|
|
+ do {
|
|
+ error = wait_event_interruptible(crp->crp_waitq,
|
|
+ ((crp->crp_flags & CRYPTO_F_DONE) != 0));
|
|
+ /*
|
|
+ * we can't break out of this loop or we will leave behind
|
|
+ * a huge mess, however, staying here means if your driver
|
|
+ * is broken user applications can hang and not be killed.
|
|
+ * The solution, fix your driver :-)
|
|
+ */
|
|
+ if (error) {
|
|
+ schedule();
|
|
+ error = 0;
|
|
+ }
|
|
+ } while ((crp->crp_flags & CRYPTO_F_DONE) == 0);
|
|
+ dprintk("%s finished WAITING error=%d\n", __FUNCTION__, error);
|
|
+
|
|
+ if (crp->crp_etype != 0) {
|
|
+ error = crp->crp_etype;
|
|
+ dprintk("%s error in crp processing\n", __FUNCTION__);
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ if (cse->error) {
|
|
+ error = cse->error;
|
|
+ dprintk("%s error in cse processing\n", __FUNCTION__);
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ if (cop->dst && (error = copy_to_user(cop->dst,
|
|
+ cse->uio.uio_iov[0].iov_base, cop->len))) {
|
|
+ dprintk("%s bad dst copy\n", __FUNCTION__);
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ if (cop->mac &&
|
|
+ (error=copy_to_user(cop->mac,
|
|
+ (caddr_t)cse->uio.uio_iov[0].iov_base + cop->len,
|
|
+ cse->info.authsize))) {
|
|
+ dprintk("%s bad mac copy\n", __FUNCTION__);
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+bail:
|
|
+ if (crp)
|
|
+ crypto_freereq(crp);
|
|
+ if (cse->uio.uio_iov[0].iov_base)
|
|
+ kfree(cse->uio.uio_iov[0].iov_base);
|
|
+
|
|
+ return (error);
|
|
+}
|
|
+
|
|
+static int
|
|
+cryptodev_cb(void *op)
|
|
+{
|
|
+ struct cryptop *crp = (struct cryptop *) op;
|
|
+ struct csession *cse = (struct csession *)crp->crp_opaque;
|
|
+ int error;
|
|
+
|
|
+ dprintk("%s()\n", __FUNCTION__);
|
|
+ error = crp->crp_etype;
|
|
+ if (error == EAGAIN) {
|
|
+ crp->crp_flags &= ~CRYPTO_F_DONE;
|
|
+#ifdef NOTYET
|
|
+ /*
|
|
+ * DAVIDM I am fairly sure that we should turn this into a batch
|
|
+ * request to stop bad karma/lockup, revisit
|
|
+ */
|
|
+ crp->crp_flags |= CRYPTO_F_BATCH;
|
|
+#endif
|
|
+ return crypto_dispatch(crp);
|
|
+ }
|
|
+ if (error != 0 || (crp->crp_flags & CRYPTO_F_DONE)) {
|
|
+ cse->error = error;
|
|
+ wake_up_interruptible(&crp->crp_waitq);
|
|
+ }
|
|
+ return (0);
|
|
+}
|
|
+
|
|
+static int
|
|
+cryptodevkey_cb(void *op)
|
|
+{
|
|
+ struct cryptkop *krp = (struct cryptkop *) op;
|
|
+ dprintk("%s()\n", __FUNCTION__);
|
|
+ wake_up_interruptible(&krp->krp_waitq);
|
|
+ return (0);
|
|
+}
|
|
+
|
|
+static int
|
|
+cryptodev_key(struct crypt_kop *kop)
|
|
+{
|
|
+ struct cryptkop *krp = NULL;
|
|
+ int error = EINVAL;
|
|
+ int in, out, size, i;
|
|
+
|
|
+ dprintk("%s()\n", __FUNCTION__);
|
|
+ if (kop->crk_iparams + kop->crk_oparams > CRK_MAXPARAM) {
|
|
+ dprintk("%s params too big\n", __FUNCTION__);
|
|
+ return (EFBIG);
|
|
+ }
|
|
+
|
|
+ in = kop->crk_iparams;
|
|
+ out = kop->crk_oparams;
|
|
+ switch (kop->crk_op) {
|
|
+ case CRK_MOD_EXP:
|
|
+ if (in == 3 && out == 1)
|
|
+ break;
|
|
+ return (EINVAL);
|
|
+ case CRK_MOD_EXP_CRT:
|
|
+ if (in == 6 && out == 1)
|
|
+ break;
|
|
+ return (EINVAL);
|
|
+ case CRK_DSA_SIGN:
|
|
+ if (in == 5 && out == 2)
|
|
+ break;
|
|
+ return (EINVAL);
|
|
+ case CRK_DSA_VERIFY:
|
|
+ if (in == 7 && out == 0)
|
|
+ break;
|
|
+ return (EINVAL);
|
|
+ case CRK_DH_COMPUTE_KEY:
|
|
+ if (in == 3 && out == 1)
|
|
+ break;
|
|
+ return (EINVAL);
|
|
+ default:
|
|
+ return (EINVAL);
|
|
+ }
|
|
+
|
|
+ krp = (struct cryptkop *)kmalloc(sizeof *krp, GFP_KERNEL);
|
|
+ if (!krp)
|
|
+ return (ENOMEM);
|
|
+ bzero(krp, sizeof *krp);
|
|
+ krp->krp_op = kop->crk_op;
|
|
+ krp->krp_status = kop->crk_status;
|
|
+ krp->krp_iparams = kop->crk_iparams;
|
|
+ krp->krp_oparams = kop->crk_oparams;
|
|
+ krp->krp_crid = kop->crk_crid;
|
|
+ krp->krp_status = 0;
|
|
+ krp->krp_flags = CRYPTO_KF_CBIMM;
|
|
+ krp->krp_callback = (int (*) (struct cryptkop *)) cryptodevkey_cb;
|
|
+ init_waitqueue_head(&krp->krp_waitq);
|
|
+
|
|
+ for (i = 0; i < CRK_MAXPARAM; i++)
|
|
+ krp->krp_param[i].crp_nbits = kop->crk_param[i].crp_nbits;
|
|
+ for (i = 0; i < krp->krp_iparams + krp->krp_oparams; i++) {
|
|
+ size = (krp->krp_param[i].crp_nbits + 7) / 8;
|
|
+ if (size == 0)
|
|
+ continue;
|
|
+ krp->krp_param[i].crp_p = (caddr_t) kmalloc(size, GFP_KERNEL);
|
|
+ if (i >= krp->krp_iparams)
|
|
+ continue;
|
|
+ error = copy_from_user(krp->krp_param[i].crp_p,
|
|
+ kop->crk_param[i].crp_p, size);
|
|
+ if (error)
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ error = crypto_kdispatch(krp);
|
|
+ if (error)
|
|
+ goto fail;
|
|
+
|
|
+ do {
|
|
+ error = wait_event_interruptible(krp->krp_waitq,
|
|
+ ((krp->krp_flags & CRYPTO_KF_DONE) != 0));
|
|
+ /*
|
|
+ * we can't break out of this loop or we will leave behind
|
|
+ * a huge mess, however, staying here means if your driver
|
|
+ * is broken user applications can hang and not be killed.
|
|
+ * The solution, fix your driver :-)
|
|
+ */
|
|
+ if (error) {
|
|
+ schedule();
|
|
+ error = 0;
|
|
+ }
|
|
+ } while ((krp->krp_flags & CRYPTO_KF_DONE) == 0);
|
|
+
|
|
+ dprintk("%s finished WAITING error=%d\n", __FUNCTION__, error);
|
|
+
|
|
+ kop->crk_crid = krp->krp_crid; /* device that did the work */
|
|
+ if (krp->krp_status != 0) {
|
|
+ error = krp->krp_status;
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ for (i = krp->krp_iparams; i < krp->krp_iparams + krp->krp_oparams; i++) {
|
|
+ size = (krp->krp_param[i].crp_nbits + 7) / 8;
|
|
+ if (size == 0)
|
|
+ continue;
|
|
+ error = copy_to_user(kop->crk_param[i].crp_p, krp->krp_param[i].crp_p,
|
|
+ size);
|
|
+ if (error)
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+fail:
|
|
+ if (krp) {
|
|
+ kop->crk_status = krp->krp_status;
|
|
+ for (i = 0; i < CRK_MAXPARAM; i++) {
|
|
+ if (krp->krp_param[i].crp_p)
|
|
+ kfree(krp->krp_param[i].crp_p);
|
|
+ }
|
|
+ kfree(krp);
|
|
+ }
|
|
+ return (error);
|
|
+}
|
|
+
|
|
+static int
|
|
+cryptodev_find(struct crypt_find_op *find)
|
|
+{
|
|
+ device_t dev;
|
|
+
|
|
+ if (find->crid != -1) {
|
|
+ dev = crypto_find_device_byhid(find->crid);
|
|
+ if (dev == NULL)
|
|
+ return (ENOENT);
|
|
+ strlcpy(find->name, device_get_nameunit(dev),
|
|
+ sizeof(find->name));
|
|
+ } else {
|
|
+ find->crid = crypto_find_driver(find->name);
|
|
+ if (find->crid == -1)
|
|
+ return (ENOENT);
|
|
+ }
|
|
+ return (0);
|
|
+}
|
|
+
|
|
+static struct csession *
|
|
+csefind(struct fcrypt *fcr, u_int ses)
|
|
+{
|
|
+ struct csession *cse;
|
|
+
|
|
+ dprintk("%s()\n", __FUNCTION__);
|
|
+ list_for_each_entry(cse, &fcr->csessions, list)
|
|
+ if (cse->ses == ses)
|
|
+ return (cse);
|
|
+ return (NULL);
|
|
+}
|
|
+
|
|
+static int
|
|
+csedelete(struct fcrypt *fcr, struct csession *cse_del)
|
|
+{
|
|
+ struct csession *cse;
|
|
+
|
|
+ dprintk("%s()\n", __FUNCTION__);
|
|
+ list_for_each_entry(cse, &fcr->csessions, list) {
|
|
+ if (cse == cse_del) {
|
|
+ list_del(&cse->list);
|
|
+ return (1);
|
|
+ }
|
|
+ }
|
|
+ return (0);
|
|
+}
|
|
+
|
|
+static struct csession *
|
|
+cseadd(struct fcrypt *fcr, struct csession *cse)
|
|
+{
|
|
+ dprintk("%s()\n", __FUNCTION__);
|
|
+ list_add_tail(&cse->list, &fcr->csessions);
|
|
+ cse->ses = fcr->sesn++;
|
|
+ return (cse);
|
|
+}
|
|
+
|
|
+static struct csession *
|
|
+csecreate(struct fcrypt *fcr, u_int64_t sid, struct cryptoini *crie,
|
|
+ struct cryptoini *cria, struct csession_info *info)
|
|
+{
|
|
+ struct csession *cse;
|
|
+
|
|
+ dprintk("%s()\n", __FUNCTION__);
|
|
+ cse = (struct csession *) kmalloc(sizeof(struct csession), GFP_KERNEL);
|
|
+ if (cse == NULL)
|
|
+ return NULL;
|
|
+ memset(cse, 0, sizeof(struct csession));
|
|
+
|
|
+ INIT_LIST_HEAD(&cse->list);
|
|
+ init_waitqueue_head(&cse->waitq);
|
|
+
|
|
+ cse->key = crie->cri_key;
|
|
+ cse->keylen = crie->cri_klen/8;
|
|
+ cse->mackey = cria->cri_key;
|
|
+ cse->mackeylen = cria->cri_klen/8;
|
|
+ cse->sid = sid;
|
|
+ cse->cipher = crie->cri_alg;
|
|
+ cse->mac = cria->cri_alg;
|
|
+ cse->info = *info;
|
|
+ cseadd(fcr, cse);
|
|
+ return (cse);
|
|
+}
|
|
+
|
|
+static int
|
|
+csefree(struct csession *cse)
|
|
+{
|
|
+ int error;
|
|
+
|
|
+ dprintk("%s()\n", __FUNCTION__);
|
|
+ error = crypto_freesession(cse->sid);
|
|
+ if (cse->key)
|
|
+ kfree(cse->key);
|
|
+ if (cse->mackey)
|
|
+ kfree(cse->mackey);
|
|
+ kfree(cse);
|
|
+ return(error);
|
|
+}
|
|
+
|
|
+static int
|
|
+cryptodev_ioctl(
|
|
+ struct inode *inode,
|
|
+ struct file *filp,
|
|
+ unsigned int cmd,
|
|
+ unsigned long arg)
|
|
+{
|
|
+ struct cryptoini cria, crie;
|
|
+ struct fcrypt *fcr = filp->private_data;
|
|
+ struct csession *cse;
|
|
+ struct csession_info info;
|
|
+ struct session2_op sop;
|
|
+ struct crypt_op cop;
|
|
+ struct crypt_kop kop;
|
|
+ struct crypt_find_op fop;
|
|
+ u_int64_t sid;
|
|
+ u_int32_t ses = 0;
|
|
+ int feat, fd, error = 0, crid;
|
|
+ mm_segment_t fs;
|
|
+
|
|
+ dprintk("%s(cmd=%x arg=%lx)\n", __FUNCTION__, cmd, arg);
|
|
+
|
|
+ switch (cmd) {
|
|
+
|
|
+ case CRIOGET: {
|
|
+ dprintk("%s(CRIOGET)\n", __FUNCTION__);
|
|
+ fs = get_fs();
|
|
+ set_fs(get_ds());
|
|
+ for (fd = 0; fd < files_fdtable(current->files)->max_fds; fd++)
|
|
+ if (files_fdtable(current->files)->fd[fd] == filp)
|
|
+ break;
|
|
+ fd = sys_dup(fd);
|
|
+ set_fs(fs);
|
|
+ put_user(fd, (int *) arg);
|
|
+ return IS_ERR_VALUE(fd) ? fd : 0;
|
|
+ }
|
|
+
|
|
+#define CIOCGSESSSTR (cmd == CIOCGSESSION ? "CIOCGSESSION" : "CIOCGSESSION2")
|
|
+ case CIOCGSESSION:
|
|
+ case CIOCGSESSION2:
|
|
+ dprintk("%s(%s)\n", __FUNCTION__, CIOCGSESSSTR);
|
|
+ memset(&crie, 0, sizeof(crie));
|
|
+ memset(&cria, 0, sizeof(cria));
|
|
+ memset(&info, 0, sizeof(info));
|
|
+ memset(&sop, 0, sizeof(sop));
|
|
+
|
|
+ if (copy_from_user(&sop, (void*)arg, (cmd == CIOCGSESSION) ?
|
|
+ sizeof(struct session_op) : sizeof(sop))) {
|
|
+ dprintk("%s(%s) - bad copy\n", __FUNCTION__, CIOCGSESSSTR);
|
|
+ error = EFAULT;
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ switch (sop.cipher) {
|
|
+ case 0:
|
|
+ dprintk("%s(%s) - no cipher\n", __FUNCTION__, CIOCGSESSSTR);
|
|
+ break;
|
|
+ case CRYPTO_NULL_CBC:
|
|
+ info.blocksize = NULL_BLOCK_LEN;
|
|
+ info.minkey = NULL_MIN_KEY_LEN;
|
|
+ info.maxkey = NULL_MAX_KEY_LEN;
|
|
+ break;
|
|
+ case CRYPTO_DES_CBC:
|
|
+ info.blocksize = DES_BLOCK_LEN;
|
|
+ info.minkey = DES_MIN_KEY_LEN;
|
|
+ info.maxkey = DES_MAX_KEY_LEN;
|
|
+ break;
|
|
+ case CRYPTO_3DES_CBC:
|
|
+ info.blocksize = DES3_BLOCK_LEN;
|
|
+ info.minkey = DES3_MIN_KEY_LEN;
|
|
+ info.maxkey = DES3_MAX_KEY_LEN;
|
|
+ break;
|
|
+ case CRYPTO_BLF_CBC:
|
|
+ info.blocksize = BLOWFISH_BLOCK_LEN;
|
|
+ info.minkey = BLOWFISH_MIN_KEY_LEN;
|
|
+ info.maxkey = BLOWFISH_MAX_KEY_LEN;
|
|
+ break;
|
|
+ case CRYPTO_CAST_CBC:
|
|
+ info.blocksize = CAST128_BLOCK_LEN;
|
|
+ info.minkey = CAST128_MIN_KEY_LEN;
|
|
+ info.maxkey = CAST128_MAX_KEY_LEN;
|
|
+ break;
|
|
+ case CRYPTO_SKIPJACK_CBC:
|
|
+ info.blocksize = SKIPJACK_BLOCK_LEN;
|
|
+ info.minkey = SKIPJACK_MIN_KEY_LEN;
|
|
+ info.maxkey = SKIPJACK_MAX_KEY_LEN;
|
|
+ break;
|
|
+ case CRYPTO_AES_CBC:
|
|
+ info.blocksize = AES_BLOCK_LEN;
|
|
+ info.minkey = AES_MIN_KEY_LEN;
|
|
+ info.maxkey = AES_MAX_KEY_LEN;
|
|
+ break;
|
|
+ case CRYPTO_ARC4:
|
|
+ info.blocksize = ARC4_BLOCK_LEN;
|
|
+ info.minkey = ARC4_MIN_KEY_LEN;
|
|
+ info.maxkey = ARC4_MAX_KEY_LEN;
|
|
+ break;
|
|
+ case CRYPTO_CAMELLIA_CBC:
|
|
+ info.blocksize = CAMELLIA_BLOCK_LEN;
|
|
+ info.minkey = CAMELLIA_MIN_KEY_LEN;
|
|
+ info.maxkey = CAMELLIA_MAX_KEY_LEN;
|
|
+ break;
|
|
+ default:
|
|
+ dprintk("%s(%s) - bad cipher\n", __FUNCTION__, CIOCGSESSSTR);
|
|
+ error = EINVAL;
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ switch (sop.mac) {
|
|
+ case 0:
|
|
+ dprintk("%s(%s) - no mac\n", __FUNCTION__, CIOCGSESSSTR);
|
|
+ break;
|
|
+ case CRYPTO_NULL_HMAC:
|
|
+ info.authsize = NULL_HASH_LEN;
|
|
+ break;
|
|
+ case CRYPTO_MD5:
|
|
+ info.authsize = MD5_HASH_LEN;
|
|
+ break;
|
|
+ case CRYPTO_SHA1:
|
|
+ info.authsize = SHA1_HASH_LEN;
|
|
+ break;
|
|
+ case CRYPTO_SHA2_256:
|
|
+ info.authsize = SHA2_256_HASH_LEN;
|
|
+ break;
|
|
+ case CRYPTO_SHA2_384:
|
|
+ info.authsize = SHA2_384_HASH_LEN;
|
|
+ break;
|
|
+ case CRYPTO_SHA2_512:
|
|
+ info.authsize = SHA2_512_HASH_LEN;
|
|
+ break;
|
|
+ case CRYPTO_RIPEMD160:
|
|
+ info.authsize = RIPEMD160_HASH_LEN;
|
|
+ break;
|
|
+ case CRYPTO_MD5_HMAC:
|
|
+ info.authsize = MD5_HASH_LEN;
|
|
+ info.authkey = 16;
|
|
+ break;
|
|
+ case CRYPTO_SHA1_HMAC:
|
|
+ info.authsize = SHA1_HASH_LEN;
|
|
+ info.authkey = 20;
|
|
+ break;
|
|
+ case CRYPTO_SHA2_256_HMAC:
|
|
+ info.authsize = SHA2_256_HASH_LEN;
|
|
+ info.authkey = 32;
|
|
+ break;
|
|
+ case CRYPTO_SHA2_384_HMAC:
|
|
+ info.authsize = SHA2_384_HASH_LEN;
|
|
+ info.authkey = 48;
|
|
+ break;
|
|
+ case CRYPTO_SHA2_512_HMAC:
|
|
+ info.authsize = SHA2_512_HASH_LEN;
|
|
+ info.authkey = 64;
|
|
+ break;
|
|
+ case CRYPTO_RIPEMD160_HMAC:
|
|
+ info.authsize = RIPEMD160_HASH_LEN;
|
|
+ info.authkey = 20;
|
|
+ break;
|
|
+ default:
|
|
+ dprintk("%s(%s) - bad mac\n", __FUNCTION__, CIOCGSESSSTR);
|
|
+ error = EINVAL;
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ if (info.blocksize) {
|
|
+ crie.cri_alg = sop.cipher;
|
|
+ crie.cri_klen = sop.keylen * 8;
|
|
+ if ((info.maxkey && sop.keylen > info.maxkey) ||
|
|
+ sop.keylen < info.minkey) {
|
|
+ dprintk("%s(%s) - bad key\n", __FUNCTION__, CIOCGSESSSTR);
|
|
+ error = EINVAL;
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ crie.cri_key = (u_int8_t *) kmalloc(crie.cri_klen/8+1, GFP_KERNEL);
|
|
+ if (copy_from_user(crie.cri_key, sop.key,
|
|
+ crie.cri_klen/8)) {
|
|
+ dprintk("%s(%s) - bad copy\n", __FUNCTION__, CIOCGSESSSTR);
|
|
+ error = EFAULT;
|
|
+ goto bail;
|
|
+ }
|
|
+ if (info.authsize)
|
|
+ crie.cri_next = &cria;
|
|
+ }
|
|
+
|
|
+ if (info.authsize) {
|
|
+ cria.cri_alg = sop.mac;
|
|
+ cria.cri_klen = sop.mackeylen * 8;
|
|
+ if (info.authkey && sop.mackeylen != info.authkey) {
|
|
+ dprintk("%s(%s) - mackeylen %d != %d\n", __FUNCTION__,
|
|
+ CIOCGSESSSTR, sop.mackeylen, info.authkey);
|
|
+ error = EINVAL;
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ if (cria.cri_klen) {
|
|
+ cria.cri_key = (u_int8_t *) kmalloc(cria.cri_klen/8,GFP_KERNEL);
|
|
+ if (copy_from_user(cria.cri_key, sop.mackey,
|
|
+ cria.cri_klen / 8)) {
|
|
+ dprintk("%s(%s) - bad copy\n", __FUNCTION__, CIOCGSESSSTR);
|
|
+ error = EFAULT;
|
|
+ goto bail;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* NB: CIOGSESSION2 has the crid */
|
|
+ if (cmd == CIOCGSESSION2) {
|
|
+ crid = sop.crid;
|
|
+ error = checkcrid(crid);
|
|
+ if (error) {
|
|
+ dprintk("%s(%s) - checkcrid %x\n", __FUNCTION__,
|
|
+ CIOCGSESSSTR, error);
|
|
+ goto bail;
|
|
+ }
|
|
+ } else {
|
|
+ /* allow either HW or SW to be used */
|
|
+ crid = CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE;
|
|
+ }
|
|
+ error = crypto_newsession(&sid, (info.blocksize ? &crie : &cria), crid);
|
|
+ if (error) {
|
|
+ dprintk("%s(%s) - newsession %d\n",__FUNCTION__,CIOCGSESSSTR,error);
|
|
+ goto bail;
|
|
+ }
|
|
+
|
|
+ cse = csecreate(fcr, sid, &crie, &cria, &info);
|
|
+ if (cse == NULL) {
|
|
+ crypto_freesession(sid);
|
|
+ error = EINVAL;
|
|
+ dprintk("%s(%s) - csecreate failed\n", __FUNCTION__, CIOCGSESSSTR);
|
|
+ goto bail;
|
|
+ }
|
|
+ sop.ses = cse->ses;
|
|
+
|
|
+ if (cmd == CIOCGSESSION2) {
|
|
+ /* return hardware/driver id */
|
|
+ sop.crid = CRYPTO_SESID2HID(cse->sid);
|
|
+ }
|
|
+
|
|
+ if (copy_to_user((void*)arg, &sop, (cmd == CIOCGSESSION) ?
|
|
+ sizeof(struct session_op) : sizeof(sop))) {
|
|
+ dprintk("%s(%s) - bad copy\n", __FUNCTION__, CIOCGSESSSTR);
|
|
+ error = EFAULT;
|
|
+ }
|
|
+bail:
|
|
+ if (error) {
|
|
+ dprintk("%s(%s) - bail %d\n", __FUNCTION__, CIOCGSESSSTR, error);
|
|
+ if (crie.cri_key)
|
|
+ kfree(crie.cri_key);
|
|
+ if (cria.cri_key)
|
|
+ kfree(cria.cri_key);
|
|
+ }
|
|
+ break;
|
|
+ case CIOCFSESSION:
|
|
+ dprintk("%s(CIOCFSESSION)\n", __FUNCTION__);
|
|
+ get_user(ses, (uint32_t*)arg);
|
|
+ cse = csefind(fcr, ses);
|
|
+ if (cse == NULL) {
|
|
+ error = EINVAL;
|
|
+ dprintk("%s(CIOCFSESSION) - Fail %d\n", __FUNCTION__, error);
|
|
+ break;
|
|
+ }
|
|
+ csedelete(fcr, cse);
|
|
+ error = csefree(cse);
|
|
+ break;
|
|
+ case CIOCCRYPT:
|
|
+ dprintk("%s(CIOCCRYPT)\n", __FUNCTION__);
|
|
+ if(copy_from_user(&cop, (void*)arg, sizeof(cop))) {
|
|
+ dprintk("%s(CIOCCRYPT) - bad copy\n", __FUNCTION__);
|
|
+ error = EFAULT;
|
|
+ goto bail;
|
|
+ }
|
|
+ cse = csefind(fcr, cop.ses);
|
|
+ if (cse == NULL) {
|
|
+ error = EINVAL;
|
|
+ dprintk("%s(CIOCCRYPT) - Fail %d\n", __FUNCTION__, error);
|
|
+ break;
|
|
+ }
|
|
+ error = cryptodev_op(cse, &cop);
|
|
+ if(copy_to_user((void*)arg, &cop, sizeof(cop))) {
|
|
+ dprintk("%s(CIOCCRYPT) - bad return copy\n", __FUNCTION__);
|
|
+ error = EFAULT;
|
|
+ goto bail;
|
|
+ }
|
|
+ break;
|
|
+ case CIOCKEY:
|
|
+ case CIOCKEY2:
|
|
+ dprintk("%s(CIOCKEY)\n", __FUNCTION__);
|
|
+ if (!crypto_userasymcrypto)
|
|
+ return (EPERM); /* XXX compat? */
|
|
+ if(copy_from_user(&kop, (void*)arg, sizeof(kop))) {
|
|
+ dprintk("%s(CIOCKEY) - bad copy\n", __FUNCTION__);
|
|
+ error = EFAULT;
|
|
+ goto bail;
|
|
+ }
|
|
+ if (cmd == CIOCKEY) {
|
|
+ /* NB: crypto core enforces s/w driver use */
|
|
+ kop.crk_crid =
|
|
+ CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE;
|
|
+ }
|
|
+ error = cryptodev_key(&kop);
|
|
+ if(copy_to_user((void*)arg, &kop, sizeof(kop))) {
|
|
+ dprintk("%s(CIOCGKEY) - bad return copy\n", __FUNCTION__);
|
|
+ error = EFAULT;
|
|
+ goto bail;
|
|
+ }
|
|
+ break;
|
|
+ case CIOCASYMFEAT:
|
|
+ dprintk("%s(CIOCASYMFEAT)\n", __FUNCTION__);
|
|
+ if (!crypto_userasymcrypto) {
|
|
+ /*
|
|
+ * NB: if user asym crypto operations are
|
|
+ * not permitted return "no algorithms"
|
|
+ * so well-behaved applications will just
|
|
+ * fallback to doing them in software.
|
|
+ */
|
|
+ feat = 0;
|
|
+ } else
|
|
+ error = crypto_getfeat(&feat);
|
|
+ if (!error) {
|
|
+ error = copy_to_user((void*)arg, &feat, sizeof(feat));
|
|
+ }
|
|
+ break;
|
|
+ case CIOCFINDDEV:
|
|
+ if (copy_from_user(&fop, (void*)arg, sizeof(fop))) {
|
|
+ dprintk("%s(CIOCFINDDEV) - bad copy\n", __FUNCTION__);
|
|
+ error = EFAULT;
|
|
+ goto bail;
|
|
+ }
|
|
+ error = cryptodev_find(&fop);
|
|
+ if (copy_to_user((void*)arg, &fop, sizeof(fop))) {
|
|
+ dprintk("%s(CIOCFINDDEV) - bad return copy\n", __FUNCTION__);
|
|
+ error = EFAULT;
|
|
+ goto bail;
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ dprintk("%s(unknown ioctl 0x%x)\n", __FUNCTION__, cmd);
|
|
+ error = EINVAL;
|
|
+ break;
|
|
+ }
|
|
+ return(-error);
|
|
+}
|
|
+
|
|
+#ifdef HAVE_UNLOCKED_IOCTL
|
|
+static long
|
|
+cryptodev_unlocked_ioctl(
|
|
+ struct file *filp,
|
|
+ unsigned int cmd,
|
|
+ unsigned long arg)
|
|
+{
|
|
+ return cryptodev_ioctl(NULL, filp, cmd, arg);
|
|
+}
|
|
+#endif
|
|
+
|
|
+static int
|
|
+cryptodev_open(struct inode *inode, struct file *filp)
|
|
+{
|
|
+ struct fcrypt *fcr;
|
|
+
|
|
+ dprintk("%s()\n", __FUNCTION__);
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)
|
|
+ /*
|
|
+ * on 2.6.35 private_data points to a miscdevice structure, we override
|
|
+ * it, which is currently safe to do.
|
|
+ */
|
|
+ if (filp->private_data) {
|
|
+ printk("cryptodev: Private data already exists - %p!\n", filp->private_data);
|
|
+ return(-ENODEV);
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ fcr = kmalloc(sizeof(*fcr), GFP_KERNEL);
|
|
+ if (!fcr) {
|
|
+ dprintk("%s() - malloc failed\n", __FUNCTION__);
|
|
+ return(-ENOMEM);
|
|
+ }
|
|
+ memset(fcr, 0, sizeof(*fcr));
|
|
+
|
|
+ INIT_LIST_HEAD(&fcr->csessions);
|
|
+ filp->private_data = fcr;
|
|
+ return(0);
|
|
+}
|
|
+
|
|
+static int
|
|
+cryptodev_release(struct inode *inode, struct file *filp)
|
|
+{
|
|
+ struct fcrypt *fcr = filp->private_data;
|
|
+ struct csession *cse, *tmp;
|
|
+
|
|
+ dprintk("%s()\n", __FUNCTION__);
|
|
+ if (!filp) {
|
|
+ printk("cryptodev: No private data on release\n");
|
|
+ return(0);
|
|
+ }
|
|
+
|
|
+ list_for_each_entry_safe(cse, tmp, &fcr->csessions, list) {
|
|
+ list_del(&cse->list);
|
|
+ (void)csefree(cse);
|
|
+ }
|
|
+ filp->private_data = NULL;
|
|
+ kfree(fcr);
|
|
+ return(0);
|
|
+}
|
|
+
|
|
+static struct file_operations cryptodev_fops = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .open = cryptodev_open,
|
|
+ .release = cryptodev_release,
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
|
|
+ .ioctl = cryptodev_ioctl,
|
|
+#endif
|
|
+#ifdef HAVE_UNLOCKED_IOCTL
|
|
+ .unlocked_ioctl = cryptodev_unlocked_ioctl,
|
|
+#endif
|
|
+};
|
|
+
|
|
+static struct miscdevice cryptodev = {
|
|
+ .minor = CRYPTODEV_MINOR,
|
|
+ .name = "crypto",
|
|
+ .fops = &cryptodev_fops,
|
|
+};
|
|
+
|
|
+static int __init
|
|
+cryptodev_init(void)
|
|
+{
|
|
+ int rc;
|
|
+
|
|
+ dprintk("%s(%p)\n", __FUNCTION__, cryptodev_init);
|
|
+ rc = misc_register(&cryptodev);
|
|
+ if (rc) {
|
|
+ printk(KERN_ERR "cryptodev: registration of /dev/crypto failed\n");
|
|
+ return(rc);
|
|
+ }
|
|
+
|
|
+ return(0);
|
|
+}
|
|
+
|
|
+static void __exit
|
|
+cryptodev_exit(void)
|
|
+{
|
|
+ dprintk("%s()\n", __FUNCTION__);
|
|
+ misc_deregister(&cryptodev);
|
|
+}
|
|
+
|
|
+module_init(cryptodev_init);
|
|
+module_exit(cryptodev_exit);
|
|
+
|
|
+MODULE_LICENSE("BSD");
|
|
+MODULE_AUTHOR("David McCullough <david_mccullough@mcafee.com>");
|
|
+MODULE_DESCRIPTION("Cryptodev (user interface to OCF)");
|
|
diff --git a/crypto/ocf/cryptodev.h b/crypto/ocf/cryptodev.h
|
|
new file mode 100644
|
|
index 0000000..cca0ec8
|
|
--- /dev/null
|
|
+++ b/crypto/ocf/cryptodev.h
|
|
@@ -0,0 +1,480 @@
|
|
+/* $FreeBSD: src/sys/opencrypto/cryptodev.h,v 1.25 2007/05/09 19:37:02 gnn Exp $ */
|
|
+/* $OpenBSD: cryptodev.h,v 1.31 2002/06/11 11:14:29 beck Exp $ */
|
|
+
|
|
+/*-
|
|
+ * Linux port done by David McCullough <david_mccullough@mcafee.com>
|
|
+ * Copyright (C) 2006-2010 David McCullough
|
|
+ * Copyright (C) 2004-2005 Intel Corporation.
|
|
+ * The license and original author are listed below.
|
|
+ *
|
|
+ * The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu)
|
|
+ * Copyright (c) 2002-2006 Sam Leffler, Errno Consulting
|
|
+ *
|
|
+ * This code was written by Angelos D. Keromytis in Athens, Greece, in
|
|
+ * February 2000. Network Security Technologies Inc. (NSTI) kindly
|
|
+ * supported the development of this code.
|
|
+ *
|
|
+ * Copyright (c) 2000 Angelos D. Keromytis
|
|
+ *
|
|
+ * Permission to use, copy, and modify this software with or without fee
|
|
+ * is hereby granted, provided that this entire notice is included in
|
|
+ * all source code copies of any software which is or includes a copy or
|
|
+ * modification of this software.
|
|
+ *
|
|
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
|
|
+ * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
|
|
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
|
|
+ * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
|
|
+ * PURPOSE.
|
|
+ *
|
|
+ * Copyright (c) 2001 Theo de Raadt
|
|
+ *
|
|
+ * Redistribution and use in source and binary forms, with or without
|
|
+ * modification, are permitted provided that the following conditions
|
|
+ * are met:
|
|
+ *
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
+ * 2. Redistributions in binary form must reproduce the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer in the
|
|
+ * documentation and/or other materials provided with the distribution.
|
|
+ * 3. The name of the author may not be used to endorse or promote products
|
|
+ * derived from this software without specific prior written permission.
|
|
+ *
|
|
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
+ *
|
|
+ * Effort sponsored in part by the Defense Advanced Research Projects
|
|
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
|
|
+ * Materiel Command, USAF, under agreement number F30602-01-2-0537.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef _CRYPTO_CRYPTO_H_
|
|
+#define _CRYPTO_CRYPTO_H_
|
|
+
|
|
+/* Some initial values */
|
|
+#define CRYPTO_DRIVERS_INITIAL 4
|
|
+#define CRYPTO_SW_SESSIONS 32
|
|
+
|
|
+/* Hash values */
|
|
+#define NULL_HASH_LEN 0
|
|
+#define MD5_HASH_LEN 16
|
|
+#define SHA1_HASH_LEN 20
|
|
+#define RIPEMD160_HASH_LEN 20
|
|
+#define SHA2_256_HASH_LEN 32
|
|
+#define SHA2_384_HASH_LEN 48
|
|
+#define SHA2_512_HASH_LEN 64
|
|
+#define MD5_KPDK_HASH_LEN 16
|
|
+#define SHA1_KPDK_HASH_LEN 20
|
|
+/* Maximum hash algorithm result length */
|
|
+#define HASH_MAX_LEN SHA2_512_HASH_LEN /* Keep this updated */
|
|
+
|
|
+/* HMAC values */
|
|
+#define NULL_HMAC_BLOCK_LEN 1
|
|
+#define MD5_HMAC_BLOCK_LEN 64
|
|
+#define SHA1_HMAC_BLOCK_LEN 64
|
|
+#define RIPEMD160_HMAC_BLOCK_LEN 64
|
|
+#define SHA2_256_HMAC_BLOCK_LEN 64
|
|
+#define SHA2_384_HMAC_BLOCK_LEN 128
|
|
+#define SHA2_512_HMAC_BLOCK_LEN 128
|
|
+/* Maximum HMAC block length */
|
|
+#define HMAC_MAX_BLOCK_LEN SHA2_512_HMAC_BLOCK_LEN /* Keep this updated */
|
|
+#define HMAC_IPAD_VAL 0x36
|
|
+#define HMAC_OPAD_VAL 0x5C
|
|
+
|
|
+/* Encryption algorithm block sizes */
|
|
+#define NULL_BLOCK_LEN 1
|
|
+#define DES_BLOCK_LEN 8
|
|
+#define DES3_BLOCK_LEN 8
|
|
+#define BLOWFISH_BLOCK_LEN 8
|
|
+#define SKIPJACK_BLOCK_LEN 8
|
|
+#define CAST128_BLOCK_LEN 8
|
|
+#define RIJNDAEL128_BLOCK_LEN 16
|
|
+#define AES_BLOCK_LEN RIJNDAEL128_BLOCK_LEN
|
|
+#define CAMELLIA_BLOCK_LEN 16
|
|
+#define ARC4_BLOCK_LEN 1
|
|
+#define EALG_MAX_BLOCK_LEN AES_BLOCK_LEN /* Keep this updated */
|
|
+
|
|
+/* Encryption algorithm min and max key sizes */
|
|
+#define NULL_MIN_KEY_LEN 0
|
|
+#define NULL_MAX_KEY_LEN 0
|
|
+#define DES_MIN_KEY_LEN 8
|
|
+#define DES_MAX_KEY_LEN 8
|
|
+#define DES3_MIN_KEY_LEN 24
|
|
+#define DES3_MAX_KEY_LEN 24
|
|
+#define BLOWFISH_MIN_KEY_LEN 4
|
|
+#define BLOWFISH_MAX_KEY_LEN 56
|
|
+#define SKIPJACK_MIN_KEY_LEN 10
|
|
+#define SKIPJACK_MAX_KEY_LEN 10
|
|
+#define CAST128_MIN_KEY_LEN 5
|
|
+#define CAST128_MAX_KEY_LEN 16
|
|
+#define RIJNDAEL128_MIN_KEY_LEN 16
|
|
+#define RIJNDAEL128_MAX_KEY_LEN 32
|
|
+#define AES_MIN_KEY_LEN RIJNDAEL128_MIN_KEY_LEN
|
|
+#define AES_MAX_KEY_LEN RIJNDAEL128_MAX_KEY_LEN
|
|
+#define CAMELLIA_MIN_KEY_LEN 16
|
|
+#define CAMELLIA_MAX_KEY_LEN 32
|
|
+#define ARC4_MIN_KEY_LEN 1
|
|
+#define ARC4_MAX_KEY_LEN 256
|
|
+
|
|
+/* Max size of data that can be processed */
|
|
+#define CRYPTO_MAX_DATA_LEN 64*1024 - 1
|
|
+
|
|
+#define CRYPTO_ALGORITHM_MIN 1
|
|
+#define CRYPTO_DES_CBC 1
|
|
+#define CRYPTO_3DES_CBC 2
|
|
+#define CRYPTO_BLF_CBC 3
|
|
+#define CRYPTO_CAST_CBC 4
|
|
+#define CRYPTO_SKIPJACK_CBC 5
|
|
+#define CRYPTO_MD5_HMAC 6
|
|
+#define CRYPTO_SHA1_HMAC 7
|
|
+#define CRYPTO_RIPEMD160_HMAC 8
|
|
+#define CRYPTO_MD5_KPDK 9
|
|
+#define CRYPTO_SHA1_KPDK 10
|
|
+#define CRYPTO_RIJNDAEL128_CBC 11 /* 128 bit blocksize */
|
|
+#define CRYPTO_AES_CBC 11 /* 128 bit blocksize -- the same as above */
|
|
+#define CRYPTO_ARC4 12
|
|
+#define CRYPTO_MD5 13
|
|
+#define CRYPTO_SHA1 14
|
|
+#define CRYPTO_NULL_HMAC 15
|
|
+#define CRYPTO_NULL_CBC 16
|
|
+#define CRYPTO_DEFLATE_COMP 17 /* Deflate compression algorithm */
|
|
+#define CRYPTO_SHA2_256_HMAC 18
|
|
+#define CRYPTO_SHA2_384_HMAC 19
|
|
+#define CRYPTO_SHA2_512_HMAC 20
|
|
+#define CRYPTO_CAMELLIA_CBC 21
|
|
+#define CRYPTO_SHA2_256 22
|
|
+#define CRYPTO_SHA2_384 23
|
|
+#define CRYPTO_SHA2_512 24
|
|
+#define CRYPTO_RIPEMD160 25
|
|
+#define CRYPTO_LZS_COMP 26
|
|
+#define CRYPTO_ALGORITHM_MAX 26 /* Keep updated - see above */
|
|
+
|
|
+/* Algorithm flags */
|
|
+#define CRYPTO_ALG_FLAG_SUPPORTED 0x01 /* Algorithm is supported */
|
|
+#define CRYPTO_ALG_FLAG_RNG_ENABLE 0x02 /* Has HW RNG for DH/DSA */
|
|
+#define CRYPTO_ALG_FLAG_DSA_SHA 0x04 /* Can do SHA on msg */
|
|
+
|
|
+/*
|
|
+ * Crypto driver/device flags. They can set in the crid
|
|
+ * parameter when creating a session or submitting a key
|
|
+ * op to affect the device/driver assigned. If neither
|
|
+ * of these are specified then the crid is assumed to hold
|
|
+ * the driver id of an existing (and suitable) device that
|
|
+ * must be used to satisfy the request.
|
|
+ */
|
|
+#define CRYPTO_FLAG_HARDWARE 0x01000000 /* hardware accelerated */
|
|
+#define CRYPTO_FLAG_SOFTWARE 0x02000000 /* software implementation */
|
|
+
|
|
+/* NB: deprecated */
|
|
+struct session_op {
|
|
+ u_int32_t cipher; /* ie. CRYPTO_DES_CBC */
|
|
+ u_int32_t mac; /* ie. CRYPTO_MD5_HMAC */
|
|
+
|
|
+ u_int32_t keylen; /* cipher key */
|
|
+ caddr_t key;
|
|
+ int mackeylen; /* mac key */
|
|
+ caddr_t mackey;
|
|
+
|
|
+ u_int32_t ses; /* returns: session # */
|
|
+};
|
|
+
|
|
+struct session2_op {
|
|
+ u_int32_t cipher; /* ie. CRYPTO_DES_CBC */
|
|
+ u_int32_t mac; /* ie. CRYPTO_MD5_HMAC */
|
|
+
|
|
+ u_int32_t keylen; /* cipher key */
|
|
+ caddr_t key;
|
|
+ int mackeylen; /* mac key */
|
|
+ caddr_t mackey;
|
|
+
|
|
+ u_int32_t ses; /* returns: session # */
|
|
+ int crid; /* driver id + flags (rw) */
|
|
+ int pad[4]; /* for future expansion */
|
|
+};
|
|
+
|
|
+struct crypt_op {
|
|
+ u_int32_t ses;
|
|
+ u_int16_t op; /* i.e. COP_ENCRYPT */
|
|
+#define COP_NONE 0
|
|
+#define COP_ENCRYPT 1
|
|
+#define COP_DECRYPT 2
|
|
+ u_int16_t flags;
|
|
+#define COP_F_BATCH 0x0008 /* Batch op if possible */
|
|
+ u_int len;
|
|
+ caddr_t src, dst; /* become iov[] inside kernel */
|
|
+ caddr_t mac; /* must be big enough for chosen MAC */
|
|
+ caddr_t iv;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Parameters for looking up a crypto driver/device by
|
|
+ * device name or by id. The latter are returned for
|
|
+ * created sessions (crid) and completed key operations.
|
|
+ */
|
|
+struct crypt_find_op {
|
|
+ int crid; /* driver id + flags */
|
|
+ char name[32]; /* device/driver name */
|
|
+};
|
|
+
|
|
+/* bignum parameter, in packed bytes, ... */
|
|
+struct crparam {
|
|
+ caddr_t crp_p;
|
|
+ u_int crp_nbits;
|
|
+};
|
|
+
|
|
+#define CRK_MAXPARAM 8
|
|
+
|
|
+struct crypt_kop {
|
|
+ u_int crk_op; /* ie. CRK_MOD_EXP or other */
|
|
+ u_int crk_status; /* return status */
|
|
+ u_short crk_iparams; /* # of input parameters */
|
|
+ u_short crk_oparams; /* # of output parameters */
|
|
+ u_int crk_crid; /* NB: only used by CIOCKEY2 (rw) */
|
|
+ struct crparam crk_param[CRK_MAXPARAM];
|
|
+};
|
|
+#define CRK_ALGORITM_MIN 0
|
|
+#define CRK_MOD_EXP 0
|
|
+#define CRK_MOD_EXP_CRT 1
|
|
+#define CRK_DSA_SIGN 2
|
|
+#define CRK_DSA_VERIFY 3
|
|
+#define CRK_DH_COMPUTE_KEY 4
|
|
+#define CRK_ALGORITHM_MAX 4 /* Keep updated - see below */
|
|
+
|
|
+#define CRF_MOD_EXP (1 << CRK_MOD_EXP)
|
|
+#define CRF_MOD_EXP_CRT (1 << CRK_MOD_EXP_CRT)
|
|
+#define CRF_DSA_SIGN (1 << CRK_DSA_SIGN)
|
|
+#define CRF_DSA_VERIFY (1 << CRK_DSA_VERIFY)
|
|
+#define CRF_DH_COMPUTE_KEY (1 << CRK_DH_COMPUTE_KEY)
|
|
+
|
|
+/*
|
|
+ * done against open of /dev/crypto, to get a cloned descriptor.
|
|
+ * Please use F_SETFD against the cloned descriptor.
|
|
+ */
|
|
+#define CRIOGET _IOWR('c', 100, u_int32_t)
|
|
+#define CRIOASYMFEAT CIOCASYMFEAT
|
|
+#define CRIOFINDDEV CIOCFINDDEV
|
|
+
|
|
+/* the following are done against the cloned descriptor */
|
|
+#define CIOCGSESSION _IOWR('c', 101, struct session_op)
|
|
+#define CIOCFSESSION _IOW('c', 102, u_int32_t)
|
|
+#define CIOCCRYPT _IOWR('c', 103, struct crypt_op)
|
|
+#define CIOCKEY _IOWR('c', 104, struct crypt_kop)
|
|
+#define CIOCASYMFEAT _IOR('c', 105, u_int32_t)
|
|
+#define CIOCGSESSION2 _IOWR('c', 106, struct session2_op)
|
|
+#define CIOCKEY2 _IOWR('c', 107, struct crypt_kop)
|
|
+#define CIOCFINDDEV _IOWR('c', 108, struct crypt_find_op)
|
|
+
|
|
+struct cryptotstat {
|
|
+ struct timespec acc; /* total accumulated time */
|
|
+ struct timespec min; /* min time */
|
|
+ struct timespec max; /* max time */
|
|
+ u_int32_t count; /* number of observations */
|
|
+};
|
|
+
|
|
+struct cryptostats {
|
|
+ u_int32_t cs_ops; /* symmetric crypto ops submitted */
|
|
+ u_int32_t cs_errs; /* symmetric crypto ops that failed */
|
|
+ u_int32_t cs_kops; /* asymetric/key ops submitted */
|
|
+ u_int32_t cs_kerrs; /* asymetric/key ops that failed */
|
|
+ u_int32_t cs_intrs; /* crypto swi thread activations */
|
|
+ u_int32_t cs_rets; /* crypto return thread activations */
|
|
+ u_int32_t cs_blocks; /* symmetric op driver block */
|
|
+ u_int32_t cs_kblocks; /* symmetric op driver block */
|
|
+ /*
|
|
+ * When CRYPTO_TIMING is defined at compile time and the
|
|
+ * sysctl debug.crypto is set to 1, the crypto system will
|
|
+ * accumulate statistics about how long it takes to process
|
|
+ * crypto requests at various points during processing.
|
|
+ */
|
|
+ struct cryptotstat cs_invoke; /* crypto_dipsatch -> crypto_invoke */
|
|
+ struct cryptotstat cs_done; /* crypto_invoke -> crypto_done */
|
|
+ struct cryptotstat cs_cb; /* crypto_done -> callback */
|
|
+ struct cryptotstat cs_finis; /* callback -> callback return */
|
|
+
|
|
+ u_int32_t cs_drops; /* crypto ops dropped due to congestion */
|
|
+};
|
|
+
|
|
+#ifdef __KERNEL__
|
|
+
|
|
+/* Standard initialization structure beginning */
|
|
+struct cryptoini {
|
|
+ int cri_alg; /* Algorithm to use */
|
|
+ int cri_klen; /* Key length, in bits */
|
|
+ int cri_mlen; /* Number of bytes we want from the
|
|
+ entire hash. 0 means all. */
|
|
+ caddr_t cri_key; /* key to use */
|
|
+ u_int8_t cri_iv[EALG_MAX_BLOCK_LEN]; /* IV to use */
|
|
+ struct cryptoini *cri_next;
|
|
+};
|
|
+
|
|
+/* Describe boundaries of a single crypto operation */
|
|
+struct cryptodesc {
|
|
+ int crd_skip; /* How many bytes to ignore from start */
|
|
+ int crd_len; /* How many bytes to process */
|
|
+ int crd_inject; /* Where to inject results, if applicable */
|
|
+ int crd_flags;
|
|
+
|
|
+#define CRD_F_ENCRYPT 0x01 /* Set when doing encryption */
|
|
+#define CRD_F_IV_PRESENT 0x02 /* When encrypting, IV is already in
|
|
+ place, so don't copy. */
|
|
+#define CRD_F_IV_EXPLICIT 0x04 /* IV explicitly provided */
|
|
+#define CRD_F_DSA_SHA_NEEDED 0x08 /* Compute SHA-1 of buffer for DSA */
|
|
+#define CRD_F_KEY_EXPLICIT 0x10 /* Key explicitly provided */
|
|
+#define CRD_F_COMP 0x0f /* Set when doing compression */
|
|
+
|
|
+ struct cryptoini CRD_INI; /* Initialization/context data */
|
|
+#define crd_iv CRD_INI.cri_iv
|
|
+#define crd_key CRD_INI.cri_key
|
|
+#define crd_alg CRD_INI.cri_alg
|
|
+#define crd_klen CRD_INI.cri_klen
|
|
+#define crd_mlen CRD_INI.cri_mlen
|
|
+
|
|
+ struct cryptodesc *crd_next;
|
|
+};
|
|
+
|
|
+/* Structure describing complete operation */
|
|
+struct cryptop {
|
|
+ struct list_head crp_next;
|
|
+ wait_queue_head_t crp_waitq;
|
|
+
|
|
+ u_int64_t crp_sid; /* Session ID */
|
|
+ int crp_ilen; /* Input data total length */
|
|
+ int crp_olen; /* Result total length */
|
|
+
|
|
+ int crp_etype; /*
|
|
+ * Error type (zero means no error).
|
|
+ * All error codes except EAGAIN
|
|
+ * indicate possible data corruption (as in,
|
|
+ * the data have been touched). On all
|
|
+ * errors, the crp_sid may have changed
|
|
+ * (reset to a new one), so the caller
|
|
+ * should always check and use the new
|
|
+ * value on future requests.
|
|
+ */
|
|
+ int crp_flags;
|
|
+
|
|
+#define CRYPTO_F_SKBUF 0x0001 /* Input/output are skbuf chains */
|
|
+#define CRYPTO_F_IOV 0x0002 /* Input/output are uio */
|
|
+#define CRYPTO_F_REL 0x0004 /* Must return data in same place */
|
|
+#define CRYPTO_F_BATCH 0x0008 /* Batch op if possible */
|
|
+#define CRYPTO_F_CBIMM 0x0010 /* Do callback immediately */
|
|
+#define CRYPTO_F_DONE 0x0020 /* Operation completed */
|
|
+#define CRYPTO_F_CBIFSYNC 0x0040 /* Do CBIMM if op is synchronous */
|
|
+
|
|
+ caddr_t crp_buf; /* Data to be processed */
|
|
+ caddr_t crp_opaque; /* Opaque pointer, passed along */
|
|
+ struct cryptodesc *crp_desc; /* Linked list of processing descriptors */
|
|
+
|
|
+ int (*crp_callback)(struct cryptop *); /* Callback function */
|
|
+};
|
|
+
|
|
+#define CRYPTO_BUF_CONTIG 0x0
|
|
+#define CRYPTO_BUF_IOV 0x1
|
|
+#define CRYPTO_BUF_SKBUF 0x2
|
|
+
|
|
+#define CRYPTO_OP_DECRYPT 0x0
|
|
+#define CRYPTO_OP_ENCRYPT 0x1
|
|
+
|
|
+/*
|
|
+ * Hints passed to process methods.
|
|
+ */
|
|
+#define CRYPTO_HINT_MORE 0x1 /* more ops coming shortly */
|
|
+
|
|
+struct cryptkop {
|
|
+ struct list_head krp_next;
|
|
+ wait_queue_head_t krp_waitq;
|
|
+
|
|
+ int krp_flags;
|
|
+#define CRYPTO_KF_DONE 0x0001 /* Operation completed */
|
|
+#define CRYPTO_KF_CBIMM 0x0002 /* Do callback immediately */
|
|
+
|
|
+ u_int krp_op; /* ie. CRK_MOD_EXP or other */
|
|
+ u_int krp_status; /* return status */
|
|
+ u_short krp_iparams; /* # of input parameters */
|
|
+ u_short krp_oparams; /* # of output parameters */
|
|
+ u_int krp_crid; /* desired device, etc. */
|
|
+ u_int32_t krp_hid;
|
|
+ struct crparam krp_param[CRK_MAXPARAM]; /* kvm */
|
|
+ int (*krp_callback)(struct cryptkop *);
|
|
+};
|
|
+
|
|
+#include <ocf-compat.h>
|
|
+
|
|
+/*
|
|
+ * Session ids are 64 bits. The lower 32 bits contain a "local id" which
|
|
+ * is a driver-private session identifier. The upper 32 bits contain a
|
|
+ * "hardware id" used by the core crypto code to identify the driver and
|
|
+ * a copy of the driver's capabilities that can be used by client code to
|
|
+ * optimize operation.
|
|
+ */
|
|
+#define CRYPTO_SESID2HID(_sid) (((_sid) >> 32) & 0x00ffffff)
|
|
+#define CRYPTO_SESID2CAPS(_sid) (((_sid) >> 32) & 0xff000000)
|
|
+#define CRYPTO_SESID2LID(_sid) (((u_int32_t) (_sid)) & 0xffffffff)
|
|
+
|
|
+extern int crypto_newsession(u_int64_t *sid, struct cryptoini *cri, int hard);
|
|
+extern int crypto_freesession(u_int64_t sid);
|
|
+#define CRYPTOCAP_F_HARDWARE CRYPTO_FLAG_HARDWARE
|
|
+#define CRYPTOCAP_F_SOFTWARE CRYPTO_FLAG_SOFTWARE
|
|
+#define CRYPTOCAP_F_SYNC 0x04000000 /* operates synchronously */
|
|
+extern int32_t crypto_get_driverid(device_t dev, int flags);
|
|
+extern int crypto_find_driver(const char *);
|
|
+extern device_t crypto_find_device_byhid(int hid);
|
|
+extern int crypto_getcaps(int hid);
|
|
+extern int crypto_register(u_int32_t driverid, int alg, u_int16_t maxoplen,
|
|
+ u_int32_t flags);
|
|
+extern int crypto_kregister(u_int32_t, int, u_int32_t);
|
|
+extern int crypto_unregister(u_int32_t driverid, int alg);
|
|
+extern int crypto_unregister_all(u_int32_t driverid);
|
|
+extern int crypto_dispatch(struct cryptop *crp);
|
|
+extern int crypto_kdispatch(struct cryptkop *);
|
|
+#define CRYPTO_SYMQ 0x1
|
|
+#define CRYPTO_ASYMQ 0x2
|
|
+extern int crypto_unblock(u_int32_t, int);
|
|
+extern void crypto_done(struct cryptop *crp);
|
|
+extern void crypto_kdone(struct cryptkop *);
|
|
+extern int crypto_getfeat(int *);
|
|
+
|
|
+extern void crypto_freereq(struct cryptop *crp);
|
|
+extern struct cryptop *crypto_getreq(int num);
|
|
+
|
|
+extern int crypto_usercrypto; /* userland may do crypto requests */
|
|
+extern int crypto_userasymcrypto; /* userland may do asym crypto reqs */
|
|
+extern int crypto_devallowsoft; /* only use hardware crypto */
|
|
+
|
|
+/*
|
|
+ * random number support, crypto_unregister_all will unregister
|
|
+ */
|
|
+extern int crypto_rregister(u_int32_t driverid,
|
|
+ int (*read_random)(void *arg, u_int32_t *buf, int len), void *arg);
|
|
+extern int crypto_runregister_all(u_int32_t driverid);
|
|
+
|
|
+/*
|
|
+ * Crypto-related utility routines used mainly by drivers.
|
|
+ *
|
|
+ * XXX these don't really belong here; but for now they're
|
|
+ * kept apart from the rest of the system.
|
|
+ */
|
|
+struct uio;
|
|
+extern void cuio_copydata(struct uio* uio, int off, int len, caddr_t cp);
|
|
+extern void cuio_copyback(struct uio* uio, int off, int len, caddr_t cp);
|
|
+extern struct iovec *cuio_getptr(struct uio *uio, int loc, int *off);
|
|
+
|
|
+extern void crypto_copyback(int flags, caddr_t buf, int off, int size,
|
|
+ caddr_t in);
|
|
+extern void crypto_copydata(int flags, caddr_t buf, int off, int size,
|
|
+ caddr_t out);
|
|
+extern int crypto_apply(int flags, caddr_t buf, int off, int len,
|
|
+ int (*f)(void *, void *, u_int), void *arg);
|
|
+
|
|
+#endif /* __KERNEL__ */
|
|
+#endif /* _CRYPTO_CRYPTO_H_ */
|
|
diff --git a/crypto/ocf/cryptosoft.c b/crypto/ocf/cryptosoft.c
|
|
new file mode 100644
|
|
index 0000000..aa2383d
|
|
--- /dev/null
|
|
+++ b/crypto/ocf/cryptosoft.c
|
|
@@ -0,0 +1,1322 @@
|
|
+/*
|
|
+ * An OCF module that uses the linux kernel cryptoapi, based on the
|
|
+ * original cryptosoft for BSD by Angelos D. Keromytis (angelos@cis.upenn.edu)
|
|
+ * but is mostly unrecognisable,
|
|
+ *
|
|
+ * Written by David McCullough <david_mccullough@mcafee.com>
|
|
+ * Copyright (C) 2004-2011 David McCullough
|
|
+ * Copyright (C) 2004-2005 Intel Corporation.
|
|
+ *
|
|
+ * LICENSE TERMS
|
|
+ *
|
|
+ * The free distribution and use of this software in both source and binary
|
|
+ * form is allowed (with or without changes) provided that:
|
|
+ *
|
|
+ * 1. distributions of this source code include the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer;
|
|
+ *
|
|
+ * 2. distributions in binary form include the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer
|
|
+ * in the documentation and/or other associated materials;
|
|
+ *
|
|
+ * 3. the copyright holder's name is not used to endorse products
|
|
+ * built using this software without specific written permission.
|
|
+ *
|
|
+ * ALTERNATIVELY, provided that this notice is retained in full, this product
|
|
+ * may be distributed under the terms of the GNU General Public License (GPL),
|
|
+ * in which case the provisions of the GPL apply INSTEAD OF those given above.
|
|
+ *
|
|
+ * DISCLAIMER
|
|
+ *
|
|
+ * This software is provided 'as is' with no explicit or implied warranties
|
|
+ * in respect of its properties, including, but not limited to, correctness
|
|
+ * and/or fitness for purpose.
|
|
+ * ---------------------------------------------------------------------------
|
|
+ */
|
|
+
|
|
+#include <linux/version.h>
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) && !defined(AUTOCONF_INCLUDED)
|
|
+#include <linux/config.h>
|
|
+#endif
|
|
+#include <linux/module.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/list.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/sched.h>
|
|
+#include <linux/wait.h>
|
|
+#include <linux/crypto.h>
|
|
+#include <linux/mm.h>
|
|
+#include <linux/skbuff.h>
|
|
+#include <linux/random.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/spinlock.h>
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
|
|
+#include <linux/scatterlist.h>
|
|
+#endif
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
|
|
+#include <crypto/hash.h>
|
|
+#endif
|
|
+
|
|
+#include <cryptodev.h>
|
|
+#include <uio.h>
|
|
+
|
|
+struct {
|
|
+ softc_device_decl sc_dev;
|
|
+} swcr_softc;
|
|
+
|
|
+#define offset_in_page(p) ((unsigned long)(p) & ~PAGE_MASK)
|
|
+
|
|
+#define SW_TYPE_CIPHER 0x01
|
|
+#define SW_TYPE_HMAC 0x02
|
|
+#define SW_TYPE_HASH 0x04
|
|
+#define SW_TYPE_COMP 0x08
|
|
+#define SW_TYPE_BLKCIPHER 0x10
|
|
+#define SW_TYPE_ALG_MASK 0x1f
|
|
+
|
|
+#define SW_TYPE_ASYNC 0x8000
|
|
+
|
|
+#define SW_TYPE_INUSE 0x10000000
|
|
+
|
|
+/* We change some of the above if we have an async interface */
|
|
+
|
|
+#define SW_TYPE_ALG_AMASK (SW_TYPE_ALG_MASK | SW_TYPE_ASYNC)
|
|
+
|
|
+#define SW_TYPE_ABLKCIPHER (SW_TYPE_BLKCIPHER | SW_TYPE_ASYNC)
|
|
+#define SW_TYPE_AHASH (SW_TYPE_HASH | SW_TYPE_ASYNC)
|
|
+#define SW_TYPE_AHMAC (SW_TYPE_HMAC | SW_TYPE_ASYNC)
|
|
+
|
|
+#define SCATTERLIST_MAX 16
|
|
+
|
|
+struct swcr_data {
|
|
+ struct work_struct workq;
|
|
+ int sw_type;
|
|
+ int sw_alg;
|
|
+ struct crypto_tfm *sw_tfm;
|
|
+ spinlock_t sw_tfm_lock;
|
|
+ union {
|
|
+ struct {
|
|
+ char *sw_key;
|
|
+ int sw_klen;
|
|
+ int sw_mlen;
|
|
+ } hmac;
|
|
+ void *sw_comp_buf;
|
|
+ } u;
|
|
+ struct swcr_data *sw_next;
|
|
+};
|
|
+
|
|
+struct swcr_req {
|
|
+ struct swcr_data *sw_head;
|
|
+ struct swcr_data *sw;
|
|
+ struct cryptop *crp;
|
|
+ struct cryptodesc *crd;
|
|
+ struct scatterlist sg[SCATTERLIST_MAX];
|
|
+ unsigned char iv[EALG_MAX_BLOCK_LEN];
|
|
+ char result[HASH_MAX_LEN];
|
|
+ void *crypto_req;
|
|
+};
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
|
|
+static kmem_cache_t *swcr_req_cache;
|
|
+#else
|
|
+static struct kmem_cache *swcr_req_cache;
|
|
+#endif
|
|
+
|
|
+#ifndef CRYPTO_TFM_MODE_CBC
|
|
+/*
|
|
+ * As of linux-2.6.21 this is no longer defined, and presumably no longer
|
|
+ * needed to be passed into the crypto core code.
|
|
+ */
|
|
+#define CRYPTO_TFM_MODE_CBC 0
|
|
+#define CRYPTO_TFM_MODE_ECB 0
|
|
+#endif
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
|
|
+ /*
|
|
+ * Linux 2.6.19 introduced a new Crypto API, setup macro's to convert new
|
|
+ * API into old API.
|
|
+ */
|
|
+
|
|
+ /* Symmetric/Block Cipher */
|
|
+ struct blkcipher_desc
|
|
+ {
|
|
+ struct crypto_tfm *tfm;
|
|
+ void *info;
|
|
+ };
|
|
+ #define ecb(X) #X , CRYPTO_TFM_MODE_ECB
|
|
+ #define cbc(X) #X , CRYPTO_TFM_MODE_CBC
|
|
+ #define crypto_has_blkcipher(X, Y, Z) crypto_alg_available(X, 0)
|
|
+ #define crypto_blkcipher_cast(X) X
|
|
+ #define crypto_blkcipher_tfm(X) X
|
|
+ #define crypto_alloc_blkcipher(X, Y, Z) crypto_alloc_tfm(X, mode)
|
|
+ #define crypto_blkcipher_ivsize(X) crypto_tfm_alg_ivsize(X)
|
|
+ #define crypto_blkcipher_blocksize(X) crypto_tfm_alg_blocksize(X)
|
|
+ #define crypto_blkcipher_setkey(X, Y, Z) crypto_cipher_setkey(X, Y, Z)
|
|
+ #define crypto_blkcipher_encrypt_iv(W, X, Y, Z) \
|
|
+ crypto_cipher_encrypt_iv((W)->tfm, X, Y, Z, (u8 *)((W)->info))
|
|
+ #define crypto_blkcipher_decrypt_iv(W, X, Y, Z) \
|
|
+ crypto_cipher_decrypt_iv((W)->tfm, X, Y, Z, (u8 *)((W)->info))
|
|
+ #define crypto_blkcipher_set_flags(x, y) /* nop */
|
|
+ #define crypto_free_blkcipher(x) crypto_free_tfm(x)
|
|
+ #define crypto_free_comp crypto_free_tfm
|
|
+ #define crypto_free_hash crypto_free_tfm
|
|
+
|
|
+ /* Hash/HMAC/Digest */
|
|
+ struct hash_desc
|
|
+ {
|
|
+ struct crypto_tfm *tfm;
|
|
+ };
|
|
+ #define hmac(X) #X , 0
|
|
+ #define crypto_has_hash(X, Y, Z) crypto_alg_available(X, 0)
|
|
+ #define crypto_hash_cast(X) X
|
|
+ #define crypto_hash_tfm(X) X
|
|
+ #define crypto_alloc_hash(X, Y, Z) crypto_alloc_tfm(X, mode)
|
|
+ #define crypto_hash_digestsize(X) crypto_tfm_alg_digestsize(X)
|
|
+ #define crypto_hash_digest(W, X, Y, Z) \
|
|
+ crypto_digest_digest((W)->tfm, X, sg_num, Z)
|
|
+
|
|
+ /* Asymmetric Cipher */
|
|
+ #define crypto_has_cipher(X, Y, Z) crypto_alg_available(X, 0)
|
|
+
|
|
+ /* Compression */
|
|
+ #define crypto_has_comp(X, Y, Z) crypto_alg_available(X, 0)
|
|
+ #define crypto_comp_tfm(X) X
|
|
+ #define crypto_comp_cast(X) X
|
|
+ #define crypto_alloc_comp(X, Y, Z) crypto_alloc_tfm(X, mode)
|
|
+ #define plain(X) #X , 0
|
|
+#else
|
|
+ #define ecb(X) "ecb(" #X ")" , 0
|
|
+ #define cbc(X) "cbc(" #X ")" , 0
|
|
+ #define hmac(X) "hmac(" #X ")" , 0
|
|
+ #define plain(X) #X , 0
|
|
+#endif /* if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) */
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
|
|
+/* no ablkcipher in older kernels */
|
|
+#define crypto_alloc_ablkcipher(a,b,c) (NULL)
|
|
+#define crypto_ablkcipher_tfm(x) ((struct crypto_tfm *)(x))
|
|
+#define crypto_ablkcipher_set_flags(a, b) /* nop */
|
|
+#define crypto_ablkcipher_setkey(x, y, z) (-EINVAL)
|
|
+#define crypto_has_ablkcipher(a,b,c) (0)
|
|
+#else
|
|
+#define HAVE_ABLKCIPHER
|
|
+#endif
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
|
|
+/* no ahash in older kernels */
|
|
+#define crypto_ahash_tfm(x) ((struct crypto_tfm *)(x))
|
|
+#define crypto_alloc_ahash(a,b,c) (NULL)
|
|
+#define crypto_ahash_digestsize(x) 0
|
|
+#else
|
|
+#define HAVE_AHASH
|
|
+#endif
|
|
+
|
|
+struct crypto_details {
|
|
+ char *alg_name;
|
|
+ int mode;
|
|
+ int sw_type;
|
|
+};
|
|
+
|
|
+static struct crypto_details crypto_details[] = {
|
|
+ [CRYPTO_DES_CBC] = { cbc(des), SW_TYPE_BLKCIPHER, },
|
|
+ [CRYPTO_3DES_CBC] = { cbc(des3_ede), SW_TYPE_BLKCIPHER, },
|
|
+ [CRYPTO_BLF_CBC] = { cbc(blowfish), SW_TYPE_BLKCIPHER, },
|
|
+ [CRYPTO_CAST_CBC] = { cbc(cast5), SW_TYPE_BLKCIPHER, },
|
|
+ [CRYPTO_SKIPJACK_CBC] = { cbc(skipjack), SW_TYPE_BLKCIPHER, },
|
|
+ [CRYPTO_MD5_HMAC] = { hmac(md5), SW_TYPE_HMAC, },
|
|
+ [CRYPTO_SHA1_HMAC] = { hmac(sha1), SW_TYPE_HMAC, },
|
|
+ [CRYPTO_RIPEMD160_HMAC] = { hmac(ripemd160), SW_TYPE_HMAC, },
|
|
+ [CRYPTO_MD5_KPDK] = { plain(md5-kpdk), SW_TYPE_HASH, },
|
|
+ [CRYPTO_SHA1_KPDK] = { plain(sha1-kpdk), SW_TYPE_HASH, },
|
|
+ [CRYPTO_AES_CBC] = { cbc(aes), SW_TYPE_BLKCIPHER, },
|
|
+ [CRYPTO_ARC4] = { ecb(arc4), SW_TYPE_BLKCIPHER, },
|
|
+ [CRYPTO_MD5] = { plain(md5), SW_TYPE_HASH, },
|
|
+ [CRYPTO_SHA1] = { plain(sha1), SW_TYPE_HASH, },
|
|
+ [CRYPTO_NULL_HMAC] = { hmac(digest_null), SW_TYPE_HMAC, },
|
|
+ [CRYPTO_NULL_CBC] = { cbc(cipher_null), SW_TYPE_BLKCIPHER, },
|
|
+ [CRYPTO_DEFLATE_COMP] = { plain(deflate), SW_TYPE_COMP, },
|
|
+ [CRYPTO_SHA2_256_HMAC] = { hmac(sha256), SW_TYPE_HMAC, },
|
|
+ [CRYPTO_SHA2_384_HMAC] = { hmac(sha384), SW_TYPE_HMAC, },
|
|
+ [CRYPTO_SHA2_512_HMAC] = { hmac(sha512), SW_TYPE_HMAC, },
|
|
+ [CRYPTO_CAMELLIA_CBC] = { cbc(camellia), SW_TYPE_BLKCIPHER, },
|
|
+ [CRYPTO_SHA2_256] = { plain(sha256), SW_TYPE_HASH, },
|
|
+ [CRYPTO_SHA2_384] = { plain(sha384), SW_TYPE_HASH, },
|
|
+ [CRYPTO_SHA2_512] = { plain(sha512), SW_TYPE_HASH, },
|
|
+ [CRYPTO_RIPEMD160] = { plain(ripemd160), SW_TYPE_HASH, },
|
|
+};
|
|
+
|
|
+int32_t swcr_id = -1;
|
|
+module_param(swcr_id, int, 0444);
|
|
+MODULE_PARM_DESC(swcr_id, "Read-Only OCF ID for cryptosoft driver");
|
|
+
|
|
+int swcr_fail_if_compression_grows = 1;
|
|
+module_param(swcr_fail_if_compression_grows, int, 0644);
|
|
+MODULE_PARM_DESC(swcr_fail_if_compression_grows,
|
|
+ "Treat compression that results in more data as a failure");
|
|
+
|
|
+int swcr_no_ahash = 0;
|
|
+module_param(swcr_no_ahash, int, 0644);
|
|
+MODULE_PARM_DESC(swcr_no_ahash,
|
|
+ "Do not use async hash/hmac even if available");
|
|
+
|
|
+int swcr_no_ablk = 0;
|
|
+module_param(swcr_no_ablk, int, 0644);
|
|
+MODULE_PARM_DESC(swcr_no_ablk,
|
|
+ "Do not use async blk ciphers even if available");
|
|
+
|
|
+static struct swcr_data **swcr_sessions = NULL;
|
|
+static u_int32_t swcr_sesnum = 0;
|
|
+
|
|
+static int swcr_process(device_t, struct cryptop *, int);
|
|
+static int swcr_newsession(device_t, u_int32_t *, struct cryptoini *);
|
|
+static int swcr_freesession(device_t, u_int64_t);
|
|
+
|
|
+static device_method_t swcr_methods = {
|
|
+ /* crypto device methods */
|
|
+ DEVMETHOD(cryptodev_newsession, swcr_newsession),
|
|
+ DEVMETHOD(cryptodev_freesession,swcr_freesession),
|
|
+ DEVMETHOD(cryptodev_process, swcr_process),
|
|
+};
|
|
+
|
|
+#define debug swcr_debug
|
|
+int swcr_debug = 0;
|
|
+module_param(swcr_debug, int, 0644);
|
|
+MODULE_PARM_DESC(swcr_debug, "Enable debug");
|
|
+
|
|
+static void swcr_process_req(struct swcr_req *req);
|
|
+
|
|
+/*
|
|
+ * somethings just need to be run with user context no matter whether
|
|
+ * the kernel compression libs use vmalloc/vfree for example.
|
|
+ */
|
|
+
|
|
+typedef struct {
|
|
+ struct work_struct wq;
|
|
+ void (*func)(void *arg);
|
|
+ void *arg;
|
|
+} execute_later_t;
|
|
+
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
|
|
+static void
|
|
+doing_it_now(struct work_struct *wq)
|
|
+{
|
|
+ execute_later_t *w = container_of(wq, execute_later_t, wq);
|
|
+ (w->func)(w->arg);
|
|
+ kfree(w);
|
|
+}
|
|
+#else
|
|
+static void
|
|
+doing_it_now(void *arg)
|
|
+{
|
|
+ execute_later_t *w = (execute_later_t *) arg;
|
|
+ (w->func)(w->arg);
|
|
+ kfree(w);
|
|
+}
|
|
+#endif
|
|
+
|
|
+static void
|
|
+execute_later(void (fn)(void *), void *arg)
|
|
+{
|
|
+ execute_later_t *w;
|
|
+
|
|
+ w = (execute_later_t *) kmalloc(sizeof(execute_later_t), SLAB_ATOMIC);
|
|
+ if (w) {
|
|
+ memset(w, '\0', sizeof(w));
|
|
+ w->func = fn;
|
|
+ w->arg = arg;
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
|
|
+ INIT_WORK(&w->wq, doing_it_now);
|
|
+#else
|
|
+ INIT_WORK(&w->wq, doing_it_now, w);
|
|
+#endif
|
|
+ schedule_work(&w->wq);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Generate a new software session.
|
|
+ */
|
|
+static int
|
|
+swcr_newsession(device_t dev, u_int32_t *sid, struct cryptoini *cri)
|
|
+{
|
|
+ struct swcr_data **swd;
|
|
+ u_int32_t i;
|
|
+ int error;
|
|
+ char *algo;
|
|
+ int mode;
|
|
+
|
|
+ dprintk("%s()\n", __FUNCTION__);
|
|
+ if (sid == NULL || cri == NULL) {
|
|
+ dprintk("%s,%d - EINVAL\n", __FILE__, __LINE__);
|
|
+ return EINVAL;
|
|
+ }
|
|
+
|
|
+ if (swcr_sessions) {
|
|
+ for (i = 1; i < swcr_sesnum; i++)
|
|
+ if (swcr_sessions[i] == NULL)
|
|
+ break;
|
|
+ } else
|
|
+ i = 1; /* NB: to silence compiler warning */
|
|
+
|
|
+ if (swcr_sessions == NULL || i == swcr_sesnum) {
|
|
+ if (swcr_sessions == NULL) {
|
|
+ i = 1; /* We leave swcr_sessions[0] empty */
|
|
+ swcr_sesnum = CRYPTO_SW_SESSIONS;
|
|
+ } else
|
|
+ swcr_sesnum *= 2;
|
|
+
|
|
+ swd = kmalloc(swcr_sesnum * sizeof(struct swcr_data *), SLAB_ATOMIC);
|
|
+ if (swd == NULL) {
|
|
+ /* Reset session number */
|
|
+ if (swcr_sesnum == CRYPTO_SW_SESSIONS)
|
|
+ swcr_sesnum = 0;
|
|
+ else
|
|
+ swcr_sesnum /= 2;
|
|
+ dprintk("%s,%d: ENOBUFS\n", __FILE__, __LINE__);
|
|
+ return ENOBUFS;
|
|
+ }
|
|
+ memset(swd, 0, swcr_sesnum * sizeof(struct swcr_data *));
|
|
+
|
|
+ /* Copy existing sessions */
|
|
+ if (swcr_sessions) {
|
|
+ memcpy(swd, swcr_sessions,
|
|
+ (swcr_sesnum / 2) * sizeof(struct swcr_data *));
|
|
+ kfree(swcr_sessions);
|
|
+ }
|
|
+
|
|
+ swcr_sessions = swd;
|
|
+ }
|
|
+
|
|
+ swd = &swcr_sessions[i];
|
|
+ *sid = i;
|
|
+
|
|
+ while (cri) {
|
|
+ *swd = (struct swcr_data *) kmalloc(sizeof(struct swcr_data),
|
|
+ SLAB_ATOMIC);
|
|
+ if (*swd == NULL) {
|
|
+ swcr_freesession(NULL, i);
|
|
+ dprintk("%s,%d: ENOBUFS\n", __FILE__, __LINE__);
|
|
+ return ENOBUFS;
|
|
+ }
|
|
+ memset(*swd, 0, sizeof(struct swcr_data));
|
|
+
|
|
+ if (cri->cri_alg < 0 ||
|
|
+ cri->cri_alg>=sizeof(crypto_details)/sizeof(crypto_details[0])){
|
|
+ printk("cryptosoft: Unknown algorithm 0x%x\n", cri->cri_alg);
|
|
+ swcr_freesession(NULL, i);
|
|
+ return EINVAL;
|
|
+ }
|
|
+
|
|
+ algo = crypto_details[cri->cri_alg].alg_name;
|
|
+ if (!algo || !*algo) {
|
|
+ printk("cryptosoft: Unsupported algorithm 0x%x\n", cri->cri_alg);
|
|
+ swcr_freesession(NULL, i);
|
|
+ return EINVAL;
|
|
+ }
|
|
+
|
|
+ mode = crypto_details[cri->cri_alg].mode;
|
|
+ (*swd)->sw_type = crypto_details[cri->cri_alg].sw_type;
|
|
+ (*swd)->sw_alg = cri->cri_alg;
|
|
+
|
|
+ spin_lock_init(&(*swd)->sw_tfm_lock);
|
|
+
|
|
+ /* Algorithm specific configuration */
|
|
+ switch (cri->cri_alg) {
|
|
+ case CRYPTO_NULL_CBC:
|
|
+ cri->cri_klen = 0; /* make it work with crypto API */
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if ((*swd)->sw_type & SW_TYPE_BLKCIPHER) {
|
|
+ dprintk("%s crypto_alloc_*blkcipher(%s, 0x%x)\n", __FUNCTION__,
|
|
+ algo, mode);
|
|
+
|
|
+ /* try async first */
|
|
+ (*swd)->sw_tfm = swcr_no_ablk ? NULL :
|
|
+ crypto_ablkcipher_tfm(crypto_alloc_ablkcipher(algo, 0, 0));
|
|
+ if ((*swd)->sw_tfm && !IS_ERR((*swd)->sw_tfm)) {
|
|
+ dprintk("%s %s cipher is async\n", __FUNCTION__, algo);
|
|
+ (*swd)->sw_type |= SW_TYPE_ASYNC;
|
|
+ } else {
|
|
+ (*swd)->sw_tfm = crypto_blkcipher_tfm(
|
|
+ crypto_alloc_blkcipher(algo, 0, CRYPTO_ALG_ASYNC));
|
|
+ if ((*swd)->sw_tfm && !IS_ERR((*swd)->sw_tfm))
|
|
+ dprintk("%s %s cipher is sync\n", __FUNCTION__, algo);
|
|
+ }
|
|
+ if (!(*swd)->sw_tfm || IS_ERR((*swd)->sw_tfm)) {
|
|
+ int err;
|
|
+ dprintk("cryptosoft: crypto_alloc_blkcipher failed(%s, 0x%x)\n",
|
|
+ algo,mode);
|
|
+ err = IS_ERR((*swd)->sw_tfm) ? -(PTR_ERR((*swd)->sw_tfm)) : EINVAL;
|
|
+ (*swd)->sw_tfm = NULL; /* ensure NULL */
|
|
+ swcr_freesession(NULL, i);
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ if (debug) {
|
|
+ dprintk("%s key:cri->cri_klen=%d,(cri->cri_klen + 7)/8=%d",
|
|
+ __FUNCTION__, cri->cri_klen, (cri->cri_klen + 7) / 8);
|
|
+ for (i = 0; i < (cri->cri_klen + 7) / 8; i++)
|
|
+ dprintk("%s0x%x", (i % 8) ? " " : "\n ",
|
|
+ cri->cri_key[i] & 0xff);
|
|
+ dprintk("\n");
|
|
+ }
|
|
+ if ((*swd)->sw_type & SW_TYPE_ASYNC) {
|
|
+ /* OCF doesn't enforce keys */
|
|
+ crypto_ablkcipher_set_flags(
|
|
+ __crypto_ablkcipher_cast((*swd)->sw_tfm),
|
|
+ CRYPTO_TFM_REQ_WEAK_KEY);
|
|
+ error = crypto_ablkcipher_setkey(
|
|
+ __crypto_ablkcipher_cast((*swd)->sw_tfm),
|
|
+ cri->cri_key, (cri->cri_klen + 7) / 8);
|
|
+ } else {
|
|
+ /* OCF doesn't enforce keys */
|
|
+ crypto_blkcipher_set_flags(
|
|
+ crypto_blkcipher_cast((*swd)->sw_tfm),
|
|
+ CRYPTO_TFM_REQ_WEAK_KEY);
|
|
+ error = crypto_blkcipher_setkey(
|
|
+ crypto_blkcipher_cast((*swd)->sw_tfm),
|
|
+ cri->cri_key, (cri->cri_klen + 7) / 8);
|
|
+ }
|
|
+ if (error) {
|
|
+ printk("cryptosoft: setkey failed %d (crt_flags=0x%x)\n", error,
|
|
+ (*swd)->sw_tfm->crt_flags);
|
|
+ swcr_freesession(NULL, i);
|
|
+ return error;
|
|
+ }
|
|
+ } else if ((*swd)->sw_type & (SW_TYPE_HMAC | SW_TYPE_HASH)) {
|
|
+ dprintk("%s crypto_alloc_*hash(%s, 0x%x)\n", __FUNCTION__,
|
|
+ algo, mode);
|
|
+
|
|
+ /* try async first */
|
|
+ (*swd)->sw_tfm = swcr_no_ahash ? NULL :
|
|
+ crypto_ahash_tfm(crypto_alloc_ahash(algo, 0, 0));
|
|
+ if ((*swd)->sw_tfm) {
|
|
+ dprintk("%s %s hash is async\n", __FUNCTION__, algo);
|
|
+ (*swd)->sw_type |= SW_TYPE_ASYNC;
|
|
+ } else {
|
|
+ dprintk("%s %s hash is sync\n", __FUNCTION__, algo);
|
|
+ (*swd)->sw_tfm = crypto_hash_tfm(
|
|
+ crypto_alloc_hash(algo, 0, CRYPTO_ALG_ASYNC));
|
|
+ }
|
|
+
|
|
+ if (!(*swd)->sw_tfm) {
|
|
+ dprintk("cryptosoft: crypto_alloc_hash failed(%s,0x%x)\n",
|
|
+ algo, mode);
|
|
+ swcr_freesession(NULL, i);
|
|
+ return EINVAL;
|
|
+ }
|
|
+
|
|
+ (*swd)->u.hmac.sw_klen = (cri->cri_klen + 7) / 8;
|
|
+ (*swd)->u.hmac.sw_key = (char *)kmalloc((*swd)->u.hmac.sw_klen,
|
|
+ SLAB_ATOMIC);
|
|
+ if ((*swd)->u.hmac.sw_key == NULL) {
|
|
+ swcr_freesession(NULL, i);
|
|
+ dprintk("%s,%d: ENOBUFS\n", __FILE__, __LINE__);
|
|
+ return ENOBUFS;
|
|
+ }
|
|
+ memcpy((*swd)->u.hmac.sw_key, cri->cri_key, (*swd)->u.hmac.sw_klen);
|
|
+ if (cri->cri_mlen) {
|
|
+ (*swd)->u.hmac.sw_mlen = cri->cri_mlen;
|
|
+ } else if ((*swd)->sw_type & SW_TYPE_ASYNC) {
|
|
+ (*swd)->u.hmac.sw_mlen = crypto_ahash_digestsize(
|
|
+ __crypto_ahash_cast((*swd)->sw_tfm));
|
|
+ } else {
|
|
+ (*swd)->u.hmac.sw_mlen = crypto_hash_digestsize(
|
|
+ crypto_hash_cast((*swd)->sw_tfm));
|
|
+ }
|
|
+ } else if ((*swd)->sw_type & SW_TYPE_COMP) {
|
|
+ (*swd)->sw_tfm = crypto_comp_tfm(
|
|
+ crypto_alloc_comp(algo, 0, CRYPTO_ALG_ASYNC));
|
|
+ if (!(*swd)->sw_tfm) {
|
|
+ dprintk("cryptosoft: crypto_alloc_comp failed(%s,0x%x)\n",
|
|
+ algo, mode);
|
|
+ swcr_freesession(NULL, i);
|
|
+ return EINVAL;
|
|
+ }
|
|
+ (*swd)->u.sw_comp_buf = kmalloc(CRYPTO_MAX_DATA_LEN, SLAB_ATOMIC);
|
|
+ if ((*swd)->u.sw_comp_buf == NULL) {
|
|
+ swcr_freesession(NULL, i);
|
|
+ dprintk("%s,%d: ENOBUFS\n", __FILE__, __LINE__);
|
|
+ return ENOBUFS;
|
|
+ }
|
|
+ } else {
|
|
+ printk("cryptosoft: Unhandled sw_type %d\n", (*swd)->sw_type);
|
|
+ swcr_freesession(NULL, i);
|
|
+ return EINVAL;
|
|
+ }
|
|
+
|
|
+ cri = cri->cri_next;
|
|
+ swd = &((*swd)->sw_next);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Free a session.
|
|
+ */
|
|
+static int
|
|
+swcr_freesession(device_t dev, u_int64_t tid)
|
|
+{
|
|
+ struct swcr_data *swd;
|
|
+ u_int32_t sid = CRYPTO_SESID2LID(tid);
|
|
+
|
|
+ dprintk("%s()\n", __FUNCTION__);
|
|
+ if (sid > swcr_sesnum || swcr_sessions == NULL ||
|
|
+ swcr_sessions[sid] == NULL) {
|
|
+ dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__);
|
|
+ return(EINVAL);
|
|
+ }
|
|
+
|
|
+ /* Silently accept and return */
|
|
+ if (sid == 0)
|
|
+ return(0);
|
|
+
|
|
+ while ((swd = swcr_sessions[sid]) != NULL) {
|
|
+ swcr_sessions[sid] = swd->sw_next;
|
|
+ if (swd->sw_tfm) {
|
|
+ switch (swd->sw_type & SW_TYPE_ALG_AMASK) {
|
|
+#ifdef HAVE_AHASH
|
|
+ case SW_TYPE_AHMAC:
|
|
+ case SW_TYPE_AHASH:
|
|
+ crypto_free_ahash(__crypto_ahash_cast(swd->sw_tfm));
|
|
+ break;
|
|
+#endif
|
|
+#ifdef HAVE_ABLKCIPHER
|
|
+ case SW_TYPE_ABLKCIPHER:
|
|
+ crypto_free_ablkcipher(__crypto_ablkcipher_cast(swd->sw_tfm));
|
|
+ break;
|
|
+#endif
|
|
+ case SW_TYPE_BLKCIPHER:
|
|
+ crypto_free_blkcipher(crypto_blkcipher_cast(swd->sw_tfm));
|
|
+ break;
|
|
+ case SW_TYPE_HMAC:
|
|
+ case SW_TYPE_HASH:
|
|
+ crypto_free_hash(crypto_hash_cast(swd->sw_tfm));
|
|
+ break;
|
|
+ case SW_TYPE_COMP:
|
|
+ if (in_interrupt())
|
|
+ execute_later((void (*)(void *))crypto_free_comp, (void *)crypto_comp_cast(swd->sw_tfm));
|
|
+ else
|
|
+ crypto_free_comp(crypto_comp_cast(swd->sw_tfm));
|
|
+ break;
|
|
+ default:
|
|
+ crypto_free_tfm(swd->sw_tfm);
|
|
+ break;
|
|
+ }
|
|
+ swd->sw_tfm = NULL;
|
|
+ }
|
|
+ if (swd->sw_type & SW_TYPE_COMP) {
|
|
+ if (swd->u.sw_comp_buf)
|
|
+ kfree(swd->u.sw_comp_buf);
|
|
+ } else {
|
|
+ if (swd->u.hmac.sw_key)
|
|
+ kfree(swd->u.hmac.sw_key);
|
|
+ }
|
|
+ kfree(swd);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void swcr_process_req_complete(struct swcr_req *req)
|
|
+{
|
|
+ dprintk("%s()\n", __FUNCTION__);
|
|
+
|
|
+ if (req->sw->sw_type & SW_TYPE_INUSE) {
|
|
+ unsigned long flags;
|
|
+ spin_lock_irqsave(&req->sw->sw_tfm_lock, flags);
|
|
+ req->sw->sw_type &= ~SW_TYPE_INUSE;
|
|
+ spin_unlock_irqrestore(&req->sw->sw_tfm_lock, flags);
|
|
+ }
|
|
+
|
|
+ if (req->crp->crp_etype)
|
|
+ goto done;
|
|
+
|
|
+ switch (req->sw->sw_type & SW_TYPE_ALG_AMASK) {
|
|
+#if defined(HAVE_AHASH)
|
|
+ case SW_TYPE_AHMAC:
|
|
+ case SW_TYPE_AHASH:
|
|
+ crypto_copyback(req->crp->crp_flags, req->crp->crp_buf,
|
|
+ req->crd->crd_inject, req->sw->u.hmac.sw_mlen, req->result);
|
|
+ ahash_request_free(req->crypto_req);
|
|
+ break;
|
|
+#endif
|
|
+#if defined(HAVE_ABLKCIPHER)
|
|
+ case SW_TYPE_ABLKCIPHER:
|
|
+ ablkcipher_request_free(req->crypto_req);
|
|
+ break;
|
|
+#endif
|
|
+ case SW_TYPE_CIPHER:
|
|
+ case SW_TYPE_HMAC:
|
|
+ case SW_TYPE_HASH:
|
|
+ case SW_TYPE_COMP:
|
|
+ case SW_TYPE_BLKCIPHER:
|
|
+ break;
|
|
+ default:
|
|
+ req->crp->crp_etype = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ req->crd = req->crd->crd_next;
|
|
+ if (req->crd) {
|
|
+ swcr_process_req(req);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+done:
|
|
+ dprintk("%s crypto_done %p\n", __FUNCTION__, req);
|
|
+ crypto_done(req->crp);
|
|
+ kmem_cache_free(swcr_req_cache, req);
|
|
+}
|
|
+
|
|
+#if defined(HAVE_ABLKCIPHER) || defined(HAVE_AHASH)
|
|
+static void swcr_process_callback(struct crypto_async_request *creq, int err)
|
|
+{
|
|
+ struct swcr_req *req = creq->data;
|
|
+
|
|
+ dprintk("%s()\n", __FUNCTION__);
|
|
+ if (err) {
|
|
+ if (err == -EINPROGRESS)
|
|
+ return;
|
|
+ dprintk("%s() fail %d\n", __FUNCTION__, -err);
|
|
+ req->crp->crp_etype = -err;
|
|
+ }
|
|
+
|
|
+ swcr_process_req_complete(req);
|
|
+}
|
|
+#endif /* defined(HAVE_ABLKCIPHER) || defined(HAVE_AHASH) */
|
|
+
|
|
+
|
|
+static void swcr_process_req(struct swcr_req *req)
|
|
+{
|
|
+ struct swcr_data *sw;
|
|
+ struct cryptop *crp = req->crp;
|
|
+ struct cryptodesc *crd = req->crd;
|
|
+ struct sk_buff *skb = (struct sk_buff *) crp->crp_buf;
|
|
+ struct uio *uiop = (struct uio *) crp->crp_buf;
|
|
+ int sg_num, sg_len, skip;
|
|
+
|
|
+ dprintk("%s()\n", __FUNCTION__);
|
|
+
|
|
+ /*
|
|
+ * Find the crypto context.
|
|
+ *
|
|
+ * XXX Note that the logic here prevents us from having
|
|
+ * XXX the same algorithm multiple times in a session
|
|
+ * XXX (or rather, we can but it won't give us the right
|
|
+ * XXX results). To do that, we'd need some way of differentiating
|
|
+ * XXX between the various instances of an algorithm (so we can
|
|
+ * XXX locate the correct crypto context).
|
|
+ */
|
|
+ for (sw = req->sw_head; sw && sw->sw_alg != crd->crd_alg; sw = sw->sw_next)
|
|
+ ;
|
|
+
|
|
+ /* No such context ? */
|
|
+ if (sw == NULL) {
|
|
+ crp->crp_etype = EINVAL;
|
|
+ dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__);
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * for some types we need to ensure only one user as info is stored in
|
|
+ * the tfm during an operation that can get corrupted
|
|
+ */
|
|
+ switch (sw->sw_type & SW_TYPE_ALG_AMASK) {
|
|
+#ifdef HAVE_AHASH
|
|
+ case SW_TYPE_AHMAC:
|
|
+ case SW_TYPE_AHASH:
|
|
+#endif
|
|
+ case SW_TYPE_HMAC:
|
|
+ case SW_TYPE_HASH: {
|
|
+ unsigned long flags;
|
|
+ spin_lock_irqsave(&sw->sw_tfm_lock, flags);
|
|
+ if (sw->sw_type & SW_TYPE_INUSE) {
|
|
+ spin_unlock_irqrestore(&sw->sw_tfm_lock, flags);
|
|
+ execute_later((void (*)(void *))swcr_process_req, (void *)req);
|
|
+ return;
|
|
+ }
|
|
+ sw->sw_type |= SW_TYPE_INUSE;
|
|
+ spin_unlock_irqrestore(&sw->sw_tfm_lock, flags);
|
|
+ } break;
|
|
+ }
|
|
+
|
|
+ req->sw = sw;
|
|
+ skip = crd->crd_skip;
|
|
+
|
|
+ /*
|
|
+ * setup the SG list skip from the start of the buffer
|
|
+ */
|
|
+ memset(req->sg, 0, sizeof(req->sg));
|
|
+ sg_init_table(req->sg, SCATTERLIST_MAX);
|
|
+ if (crp->crp_flags & CRYPTO_F_SKBUF) {
|
|
+ int i, len;
|
|
+
|
|
+ sg_num = 0;
|
|
+ sg_len = 0;
|
|
+
|
|
+ if (skip < skb_headlen(skb)) {
|
|
+ len = skb_headlen(skb) - skip;
|
|
+ if (len + sg_len > crd->crd_len)
|
|
+ len = crd->crd_len - sg_len;
|
|
+ sg_set_page(&req->sg[sg_num],
|
|
+ virt_to_page(skb->data + skip), len,
|
|
+ offset_in_page(skb->data + skip));
|
|
+ sg_len += len;
|
|
+ sg_num++;
|
|
+ skip = 0;
|
|
+ } else
|
|
+ skip -= skb_headlen(skb);
|
|
+
|
|
+ for (i = 0; sg_len < crd->crd_len &&
|
|
+ i < skb_shinfo(skb)->nr_frags &&
|
|
+ sg_num < SCATTERLIST_MAX; i++) {
|
|
+ if (skip < skb_shinfo(skb)->frags[i].size) {
|
|
+ len = skb_shinfo(skb)->frags[i].size - skip;
|
|
+ if (len + sg_len > crd->crd_len)
|
|
+ len = crd->crd_len - sg_len;
|
|
+ sg_set_page(&req->sg[sg_num],
|
|
+ skb_frag_page(&skb_shinfo(skb)->frags[i]),
|
|
+ len,
|
|
+ skb_shinfo(skb)->frags[i].page_offset + skip);
|
|
+ sg_len += len;
|
|
+ sg_num++;
|
|
+ skip = 0;
|
|
+ } else
|
|
+ skip -= skb_shinfo(skb)->frags[i].size;
|
|
+ }
|
|
+ } else if (crp->crp_flags & CRYPTO_F_IOV) {
|
|
+ int len;
|
|
+
|
|
+ sg_len = 0;
|
|
+ for (sg_num = 0; sg_len < crd->crd_len &&
|
|
+ sg_num < uiop->uio_iovcnt &&
|
|
+ sg_num < SCATTERLIST_MAX; sg_num++) {
|
|
+ if (skip <= uiop->uio_iov[sg_num].iov_len) {
|
|
+ len = uiop->uio_iov[sg_num].iov_len - skip;
|
|
+ if (len + sg_len > crd->crd_len)
|
|
+ len = crd->crd_len - sg_len;
|
|
+ sg_set_page(&req->sg[sg_num],
|
|
+ virt_to_page(uiop->uio_iov[sg_num].iov_base+skip),
|
|
+ len,
|
|
+ offset_in_page(uiop->uio_iov[sg_num].iov_base+skip));
|
|
+ sg_len += len;
|
|
+ skip = 0;
|
|
+ } else
|
|
+ skip -= uiop->uio_iov[sg_num].iov_len;
|
|
+ }
|
|
+ } else {
|
|
+ sg_len = (crp->crp_ilen - skip);
|
|
+ if (sg_len > crd->crd_len)
|
|
+ sg_len = crd->crd_len;
|
|
+ sg_set_page(&req->sg[0], virt_to_page(crp->crp_buf + skip),
|
|
+ sg_len, offset_in_page(crp->crp_buf + skip));
|
|
+ sg_num = 1;
|
|
+ }
|
|
+ if (sg_num > 0)
|
|
+ sg_mark_end(&req->sg[sg_num-1]);
|
|
+
|
|
+ switch (sw->sw_type & SW_TYPE_ALG_AMASK) {
|
|
+
|
|
+#ifdef HAVE_AHASH
|
|
+ case SW_TYPE_AHMAC:
|
|
+ case SW_TYPE_AHASH:
|
|
+ {
|
|
+ int ret;
|
|
+
|
|
+ /* check we have room for the result */
|
|
+ if (crp->crp_ilen - crd->crd_inject < sw->u.hmac.sw_mlen) {
|
|
+ dprintk("cryptosoft: EINVAL crp_ilen=%d, len=%d, inject=%d "
|
|
+ "digestsize=%d\n", crp->crp_ilen, crd->crd_skip + sg_len,
|
|
+ crd->crd_inject, sw->u.hmac.sw_mlen);
|
|
+ crp->crp_etype = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ req->crypto_req =
|
|
+ ahash_request_alloc(__crypto_ahash_cast(sw->sw_tfm),GFP_ATOMIC);
|
|
+ if (!req->crypto_req) {
|
|
+ crp->crp_etype = ENOMEM;
|
|
+ dprintk("%s,%d: ENOMEM ahash_request_alloc", __FILE__, __LINE__);
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ahash_request_set_callback(req->crypto_req,
|
|
+ CRYPTO_TFM_REQ_MAY_BACKLOG, swcr_process_callback, req);
|
|
+
|
|
+ memset(req->result, 0, sizeof(req->result));
|
|
+
|
|
+ if (sw->sw_type & SW_TYPE_AHMAC)
|
|
+ crypto_ahash_setkey(__crypto_ahash_cast(sw->sw_tfm),
|
|
+ sw->u.hmac.sw_key, sw->u.hmac.sw_klen);
|
|
+ ahash_request_set_crypt(req->crypto_req, req->sg, req->result, sg_len);
|
|
+ ret = crypto_ahash_digest(req->crypto_req);
|
|
+ switch (ret) {
|
|
+ case -EINPROGRESS:
|
|
+ case -EBUSY:
|
|
+ return;
|
|
+ default:
|
|
+ case 0:
|
|
+ dprintk("hash OP %s %d\n", ret ? "failed" : "success", ret);
|
|
+ crp->crp_etype = ret;
|
|
+ goto done;
|
|
+ }
|
|
+ } break;
|
|
+#endif /* HAVE_AHASH */
|
|
+
|
|
+#ifdef HAVE_ABLKCIPHER
|
|
+ case SW_TYPE_ABLKCIPHER: {
|
|
+ int ret;
|
|
+ unsigned char *ivp = req->iv;
|
|
+ int ivsize =
|
|
+ crypto_ablkcipher_ivsize(__crypto_ablkcipher_cast(sw->sw_tfm));
|
|
+
|
|
+ if (sg_len < crypto_ablkcipher_blocksize(
|
|
+ __crypto_ablkcipher_cast(sw->sw_tfm))) {
|
|
+ crp->crp_etype = EINVAL;
|
|
+ dprintk("%s,%d: EINVAL len %d < %d\n", __FILE__, __LINE__,
|
|
+ sg_len, crypto_ablkcipher_blocksize(
|
|
+ __crypto_ablkcipher_cast(sw->sw_tfm)));
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ if (ivsize > sizeof(req->iv)) {
|
|
+ crp->crp_etype = EINVAL;
|
|
+ dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__);
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ req->crypto_req = ablkcipher_request_alloc(
|
|
+ __crypto_ablkcipher_cast(sw->sw_tfm), GFP_ATOMIC);
|
|
+ if (!req->crypto_req) {
|
|
+ crp->crp_etype = ENOMEM;
|
|
+ dprintk("%s,%d: ENOMEM ablkcipher_request_alloc",
|
|
+ __FILE__, __LINE__);
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ablkcipher_request_set_callback(req->crypto_req,
|
|
+ CRYPTO_TFM_REQ_MAY_BACKLOG, swcr_process_callback, req);
|
|
+
|
|
+ if (crd->crd_flags & CRD_F_KEY_EXPLICIT) {
|
|
+ int i, error;
|
|
+
|
|
+ if (debug) {
|
|
+ dprintk("%s key:", __FUNCTION__);
|
|
+ for (i = 0; i < (crd->crd_klen + 7) / 8; i++)
|
|
+ dprintk("%s0x%x", (i % 8) ? " " : "\n ",
|
|
+ crd->crd_key[i] & 0xff);
|
|
+ dprintk("\n");
|
|
+ }
|
|
+ /* OCF doesn't enforce keys */
|
|
+ crypto_ablkcipher_set_flags(__crypto_ablkcipher_cast(sw->sw_tfm),
|
|
+ CRYPTO_TFM_REQ_WEAK_KEY);
|
|
+ error = crypto_ablkcipher_setkey(
|
|
+ __crypto_ablkcipher_cast(sw->sw_tfm), crd->crd_key,
|
|
+ (crd->crd_klen + 7) / 8);
|
|
+ if (error) {
|
|
+ dprintk("cryptosoft: setkey failed %d (crt_flags=0x%x)\n",
|
|
+ error, sw->sw_tfm->crt_flags);
|
|
+ crp->crp_etype = -error;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (crd->crd_flags & CRD_F_ENCRYPT) { /* encrypt */
|
|
+
|
|
+ if (crd->crd_flags & CRD_F_IV_EXPLICIT)
|
|
+ ivp = crd->crd_iv;
|
|
+ else
|
|
+ get_random_bytes(ivp, ivsize);
|
|
+ /*
|
|
+ * do we have to copy the IV back to the buffer ?
|
|
+ */
|
|
+ if ((crd->crd_flags & CRD_F_IV_PRESENT) == 0) {
|
|
+ crypto_copyback(crp->crp_flags, crp->crp_buf,
|
|
+ crd->crd_inject, ivsize, (caddr_t)ivp);
|
|
+ }
|
|
+ ablkcipher_request_set_crypt(req->crypto_req, req->sg, req->sg,
|
|
+ sg_len, ivp);
|
|
+ ret = crypto_ablkcipher_encrypt(req->crypto_req);
|
|
+
|
|
+ } else { /*decrypt */
|
|
+
|
|
+ if (crd->crd_flags & CRD_F_IV_EXPLICIT)
|
|
+ ivp = crd->crd_iv;
|
|
+ else
|
|
+ crypto_copydata(crp->crp_flags, crp->crp_buf,
|
|
+ crd->crd_inject, ivsize, (caddr_t)ivp);
|
|
+ ablkcipher_request_set_crypt(req->crypto_req, req->sg, req->sg,
|
|
+ sg_len, ivp);
|
|
+ ret = crypto_ablkcipher_decrypt(req->crypto_req);
|
|
+ }
|
|
+
|
|
+ switch (ret) {
|
|
+ case -EINPROGRESS:
|
|
+ case -EBUSY:
|
|
+ return;
|
|
+ default:
|
|
+ case 0:
|
|
+ dprintk("crypto OP %s %d\n", ret ? "failed" : "success", ret);
|
|
+ crp->crp_etype = ret;
|
|
+ goto done;
|
|
+ }
|
|
+ } break;
|
|
+#endif /* HAVE_ABLKCIPHER */
|
|
+
|
|
+ case SW_TYPE_BLKCIPHER: {
|
|
+ unsigned char iv[EALG_MAX_BLOCK_LEN];
|
|
+ unsigned char *ivp = iv;
|
|
+ struct blkcipher_desc desc;
|
|
+ int ivsize = crypto_blkcipher_ivsize(crypto_blkcipher_cast(sw->sw_tfm));
|
|
+
|
|
+ if (sg_len < crypto_blkcipher_blocksize(
|
|
+ crypto_blkcipher_cast(sw->sw_tfm))) {
|
|
+ crp->crp_etype = EINVAL;
|
|
+ dprintk("%s,%d: EINVAL len %d < %d\n", __FILE__, __LINE__,
|
|
+ sg_len, crypto_blkcipher_blocksize(
|
|
+ crypto_blkcipher_cast(sw->sw_tfm)));
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ if (ivsize > sizeof(iv)) {
|
|
+ crp->crp_etype = EINVAL;
|
|
+ dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__);
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ if (crd->crd_flags & CRD_F_KEY_EXPLICIT) {
|
|
+ int i, error;
|
|
+
|
|
+ if (debug) {
|
|
+ dprintk("%s key:", __FUNCTION__);
|
|
+ for (i = 0; i < (crd->crd_klen + 7) / 8; i++)
|
|
+ dprintk("%s0x%x", (i % 8) ? " " : "\n ",
|
|
+ crd->crd_key[i] & 0xff);
|
|
+ dprintk("\n");
|
|
+ }
|
|
+ /* OCF doesn't enforce keys */
|
|
+ crypto_blkcipher_set_flags(crypto_blkcipher_cast(sw->sw_tfm),
|
|
+ CRYPTO_TFM_REQ_WEAK_KEY);
|
|
+ error = crypto_blkcipher_setkey(
|
|
+ crypto_blkcipher_cast(sw->sw_tfm), crd->crd_key,
|
|
+ (crd->crd_klen + 7) / 8);
|
|
+ if (error) {
|
|
+ dprintk("cryptosoft: setkey failed %d (crt_flags=0x%x)\n",
|
|
+ error, sw->sw_tfm->crt_flags);
|
|
+ crp->crp_etype = -error;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ memset(&desc, 0, sizeof(desc));
|
|
+ desc.tfm = crypto_blkcipher_cast(sw->sw_tfm);
|
|
+
|
|
+ if (crd->crd_flags & CRD_F_ENCRYPT) { /* encrypt */
|
|
+
|
|
+ if (crd->crd_flags & CRD_F_IV_EXPLICIT) {
|
|
+ ivp = crd->crd_iv;
|
|
+ } else {
|
|
+ get_random_bytes(ivp, ivsize);
|
|
+ }
|
|
+ /*
|
|
+ * do we have to copy the IV back to the buffer ?
|
|
+ */
|
|
+ if ((crd->crd_flags & CRD_F_IV_PRESENT) == 0) {
|
|
+ crypto_copyback(crp->crp_flags, crp->crp_buf,
|
|
+ crd->crd_inject, ivsize, (caddr_t)ivp);
|
|
+ }
|
|
+ desc.info = ivp;
|
|
+ crypto_blkcipher_encrypt_iv(&desc, req->sg, req->sg, sg_len);
|
|
+
|
|
+ } else { /*decrypt */
|
|
+
|
|
+ if (crd->crd_flags & CRD_F_IV_EXPLICIT) {
|
|
+ ivp = crd->crd_iv;
|
|
+ } else {
|
|
+ crypto_copydata(crp->crp_flags, crp->crp_buf,
|
|
+ crd->crd_inject, ivsize, (caddr_t)ivp);
|
|
+ }
|
|
+ desc.info = ivp;
|
|
+ crypto_blkcipher_decrypt_iv(&desc, req->sg, req->sg, sg_len);
|
|
+ }
|
|
+ } break;
|
|
+
|
|
+ case SW_TYPE_HMAC:
|
|
+ case SW_TYPE_HASH:
|
|
+ {
|
|
+ char result[HASH_MAX_LEN];
|
|
+ struct hash_desc desc;
|
|
+
|
|
+ /* check we have room for the result */
|
|
+ if (crp->crp_ilen - crd->crd_inject < sw->u.hmac.sw_mlen) {
|
|
+ dprintk("cryptosoft: EINVAL crp_ilen=%d, len=%d, inject=%d "
|
|
+ "digestsize=%d\n", crp->crp_ilen, crd->crd_skip + sg_len,
|
|
+ crd->crd_inject, sw->u.hmac.sw_mlen);
|
|
+ crp->crp_etype = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ memset(&desc, 0, sizeof(desc));
|
|
+ desc.tfm = crypto_hash_cast(sw->sw_tfm);
|
|
+
|
|
+ memset(result, 0, sizeof(result));
|
|
+
|
|
+ if (sw->sw_type & SW_TYPE_HMAC) {
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
|
|
+ crypto_hmac(sw->sw_tfm, sw->u.hmac.sw_key, &sw->u.hmac.sw_klen,
|
|
+ req->sg, sg_num, result);
|
|
+#else
|
|
+ crypto_hash_setkey(desc.tfm, sw->u.hmac.sw_key,
|
|
+ sw->u.hmac.sw_klen);
|
|
+ crypto_hash_digest(&desc, req->sg, sg_len, result);
|
|
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) */
|
|
+
|
|
+ } else { /* SW_TYPE_HASH */
|
|
+ crypto_hash_digest(&desc, req->sg, sg_len, result);
|
|
+ }
|
|
+
|
|
+ crypto_copyback(crp->crp_flags, crp->crp_buf,
|
|
+ crd->crd_inject, sw->u.hmac.sw_mlen, result);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case SW_TYPE_COMP: {
|
|
+ void *ibuf = NULL;
|
|
+ void *obuf = sw->u.sw_comp_buf;
|
|
+ int ilen = sg_len, olen = CRYPTO_MAX_DATA_LEN;
|
|
+ int ret = 0;
|
|
+
|
|
+ /*
|
|
+ * we need to use an additional copy if there is more than one
|
|
+ * input chunk since the kernel comp routines do not handle
|
|
+ * SG yet. Otherwise we just use the input buffer as is.
|
|
+ * Rather than allocate another buffer we just split the tmp
|
|
+ * buffer we already have.
|
|
+ * Perhaps we should just use zlib directly ?
|
|
+ */
|
|
+ if (sg_num > 1) {
|
|
+ int blk;
|
|
+
|
|
+ ibuf = obuf;
|
|
+ for (blk = 0; blk < sg_num; blk++) {
|
|
+ memcpy(obuf, sg_virt(&req->sg[blk]),
|
|
+ req->sg[blk].length);
|
|
+ obuf += req->sg[blk].length;
|
|
+ }
|
|
+ olen -= sg_len;
|
|
+ } else
|
|
+ ibuf = sg_virt(&req->sg[0]);
|
|
+
|
|
+ if (crd->crd_flags & CRD_F_ENCRYPT) { /* compress */
|
|
+ ret = crypto_comp_compress(crypto_comp_cast(sw->sw_tfm),
|
|
+ ibuf, ilen, obuf, &olen);
|
|
+ if (!ret && olen > crd->crd_len) {
|
|
+ dprintk("cryptosoft: ERANGE compress %d into %d\n",
|
|
+ crd->crd_len, olen);
|
|
+ if (swcr_fail_if_compression_grows)
|
|
+ ret = ERANGE;
|
|
+ }
|
|
+ } else { /* decompress */
|
|
+ ret = crypto_comp_decompress(crypto_comp_cast(sw->sw_tfm),
|
|
+ ibuf, ilen, obuf, &olen);
|
|
+ if (!ret && (olen + crd->crd_inject) > crp->crp_olen) {
|
|
+ dprintk("cryptosoft: ETOOSMALL decompress %d into %d, "
|
|
+ "space for %d,at offset %d\n",
|
|
+ crd->crd_len, olen, crp->crp_olen, crd->crd_inject);
|
|
+ ret = ETOOSMALL;
|
|
+ }
|
|
+ }
|
|
+ if (ret)
|
|
+ dprintk("%s,%d: ret = %d\n", __FILE__, __LINE__, ret);
|
|
+
|
|
+ /*
|
|
+ * on success copy result back,
|
|
+ * linux crpyto API returns -errno, we need to fix that
|
|
+ */
|
|
+ crp->crp_etype = ret < 0 ? -ret : ret;
|
|
+ if (ret == 0) {
|
|
+ /* copy back the result and return it's size */
|
|
+ crypto_copyback(crp->crp_flags, crp->crp_buf,
|
|
+ crd->crd_inject, olen, obuf);
|
|
+ crp->crp_olen = olen;
|
|
+ }
|
|
+ } break;
|
|
+
|
|
+ default:
|
|
+ /* Unknown/unsupported algorithm */
|
|
+ dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__);
|
|
+ crp->crp_etype = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+done:
|
|
+ swcr_process_req_complete(req);
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * Process a crypto request.
|
|
+ */
|
|
+static int
|
|
+swcr_process(device_t dev, struct cryptop *crp, int hint)
|
|
+{
|
|
+ struct swcr_req *req = NULL;
|
|
+ u_int32_t lid;
|
|
+
|
|
+ dprintk("%s()\n", __FUNCTION__);
|
|
+ /* Sanity check */
|
|
+ if (crp == NULL) {
|
|
+ dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__);
|
|
+ return EINVAL;
|
|
+ }
|
|
+
|
|
+ crp->crp_etype = 0;
|
|
+
|
|
+ if (crp->crp_desc == NULL || crp->crp_buf == NULL) {
|
|
+ dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__);
|
|
+ crp->crp_etype = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ lid = crp->crp_sid & 0xffffffff;
|
|
+ if (lid >= swcr_sesnum || lid == 0 || swcr_sessions == NULL ||
|
|
+ swcr_sessions[lid] == NULL) {
|
|
+ crp->crp_etype = ENOENT;
|
|
+ dprintk("%s,%d: ENOENT\n", __FILE__, __LINE__);
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * do some error checking outside of the loop for SKB and IOV processing
|
|
+ * this leaves us with valid skb or uiop pointers for later
|
|
+ */
|
|
+ if (crp->crp_flags & CRYPTO_F_SKBUF) {
|
|
+ struct sk_buff *skb = (struct sk_buff *) crp->crp_buf;
|
|
+ if (skb_shinfo(skb)->nr_frags >= SCATTERLIST_MAX) {
|
|
+ printk("%s,%d: %d nr_frags > SCATTERLIST_MAX", __FILE__, __LINE__,
|
|
+ skb_shinfo(skb)->nr_frags);
|
|
+ goto done;
|
|
+ }
|
|
+ } else if (crp->crp_flags & CRYPTO_F_IOV) {
|
|
+ struct uio *uiop = (struct uio *) crp->crp_buf;
|
|
+ if (uiop->uio_iovcnt > SCATTERLIST_MAX) {
|
|
+ printk("%s,%d: %d uio_iovcnt > SCATTERLIST_MAX", __FILE__, __LINE__,
|
|
+ uiop->uio_iovcnt);
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * setup a new request ready for queuing
|
|
+ */
|
|
+ req = kmem_cache_alloc(swcr_req_cache, SLAB_ATOMIC);
|
|
+ if (req == NULL) {
|
|
+ dprintk("%s,%d: ENOMEM\n", __FILE__, __LINE__);
|
|
+ crp->crp_etype = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ memset(req, 0, sizeof(*req));
|
|
+
|
|
+ req->sw_head = swcr_sessions[lid];
|
|
+ req->crp = crp;
|
|
+ req->crd = crp->crp_desc;
|
|
+
|
|
+ swcr_process_req(req);
|
|
+ return 0;
|
|
+
|
|
+done:
|
|
+ crypto_done(crp);
|
|
+ if (req)
|
|
+ kmem_cache_free(swcr_req_cache, req);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+static int
|
|
+cryptosoft_init(void)
|
|
+{
|
|
+ int i, sw_type, mode;
|
|
+ char *algo;
|
|
+
|
|
+ dprintk("%s(%p)\n", __FUNCTION__, cryptosoft_init);
|
|
+
|
|
+ swcr_req_cache = kmem_cache_create("cryptosoft_req",
|
|
+ sizeof(struct swcr_req), 0, SLAB_HWCACHE_ALIGN, NULL
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
|
|
+ , NULL
|
|
+#endif
|
|
+ );
|
|
+ if (!swcr_req_cache) {
|
|
+ printk("cryptosoft: failed to create request cache\n");
|
|
+ return -ENOENT;
|
|
+ }
|
|
+
|
|
+ softc_device_init(&swcr_softc, "cryptosoft", 0, swcr_methods);
|
|
+
|
|
+ swcr_id = crypto_get_driverid(softc_get_device(&swcr_softc),
|
|
+ CRYPTOCAP_F_SOFTWARE | CRYPTOCAP_F_SYNC);
|
|
+ if (swcr_id < 0) {
|
|
+ printk("cryptosoft: Software crypto device cannot initialize!");
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+#define REGISTER(alg) \
|
|
+ crypto_register(swcr_id, alg, 0,0)
|
|
+
|
|
+ for (i = 0; i < sizeof(crypto_details)/sizeof(crypto_details[0]); i++) {
|
|
+ int found;
|
|
+
|
|
+ algo = crypto_details[i].alg_name;
|
|
+ if (!algo || !*algo) {
|
|
+ dprintk("%s:Algorithm %d not supported\n", __FUNCTION__, i);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ mode = crypto_details[i].mode;
|
|
+ sw_type = crypto_details[i].sw_type;
|
|
+
|
|
+ found = 0;
|
|
+ switch (sw_type & SW_TYPE_ALG_MASK) {
|
|
+ case SW_TYPE_CIPHER:
|
|
+ found = crypto_has_cipher(algo, 0, CRYPTO_ALG_ASYNC);
|
|
+ break;
|
|
+ case SW_TYPE_HMAC:
|
|
+ found = crypto_has_hash(algo, 0, swcr_no_ahash?CRYPTO_ALG_ASYNC:0);
|
|
+ break;
|
|
+ case SW_TYPE_HASH:
|
|
+ found = crypto_has_hash(algo, 0, swcr_no_ahash?CRYPTO_ALG_ASYNC:0);
|
|
+ break;
|
|
+ case SW_TYPE_COMP:
|
|
+ found = crypto_has_comp(algo, 0, CRYPTO_ALG_ASYNC);
|
|
+ break;
|
|
+ case SW_TYPE_BLKCIPHER:
|
|
+ found = crypto_has_blkcipher(algo, 0, CRYPTO_ALG_ASYNC);
|
|
+ if (!found && !swcr_no_ablk)
|
|
+ found = crypto_has_ablkcipher(algo, 0, 0);
|
|
+ break;
|
|
+ }
|
|
+ if (found) {
|
|
+ REGISTER(i);
|
|
+ } else {
|
|
+ dprintk("%s:Algorithm Type %d not supported (algorithm %d:'%s')\n",
|
|
+ __FUNCTION__, sw_type, i, algo);
|
|
+ }
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void
|
|
+cryptosoft_exit(void)
|
|
+{
|
|
+ dprintk("%s()\n", __FUNCTION__);
|
|
+ crypto_unregister_all(swcr_id);
|
|
+ swcr_id = -1;
|
|
+ kmem_cache_destroy(swcr_req_cache);
|
|
+}
|
|
+
|
|
+late_initcall(cryptosoft_init);
|
|
+module_exit(cryptosoft_exit);
|
|
+
|
|
+MODULE_LICENSE("Dual BSD/GPL");
|
|
+MODULE_AUTHOR("David McCullough <david_mccullough@mcafee.com>");
|
|
+MODULE_DESCRIPTION("Cryptosoft (OCF module for kernel crypto)");
|
|
diff --git a/crypto/ocf/ocf-bench.c b/crypto/ocf/ocf-bench.c
|
|
new file mode 100644
|
|
index 0000000..f3fe9d0
|
|
--- /dev/null
|
|
+++ b/crypto/ocf/ocf-bench.c
|
|
@@ -0,0 +1,514 @@
|
|
+/*
|
|
+ * A loadable module that benchmarks the OCF crypto speed from kernel space.
|
|
+ *
|
|
+ * Copyright (C) 2004-2010 David McCullough <david_mccullough@mcafee.com>
|
|
+ *
|
|
+ * LICENSE TERMS
|
|
+ *
|
|
+ * The free distribution and use of this software in both source and binary
|
|
+ * form is allowed (with or without changes) provided that:
|
|
+ *
|
|
+ * 1. distributions of this source code include the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer;
|
|
+ *
|
|
+ * 2. distributions in binary form include the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer
|
|
+ * in the documentation and/or other associated materials;
|
|
+ *
|
|
+ * 3. the copyright holder's name is not used to endorse products
|
|
+ * built using this software without specific written permission.
|
|
+ *
|
|
+ * ALTERNATIVELY, provided that this notice is retained in full, this product
|
|
+ * may be distributed under the terms of the GNU General Public License (GPL),
|
|
+ * in which case the provisions of the GPL apply INSTEAD OF those given above.
|
|
+ *
|
|
+ * DISCLAIMER
|
|
+ *
|
|
+ * This software is provided 'as is' with no explicit or implied warranties
|
|
+ * in respect of its properties, including, but not limited to, correctness
|
|
+ * and/or fitness for purpose.
|
|
+ */
|
|
+
|
|
+
|
|
+#include <linux/version.h>
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) && !defined(AUTOCONF_INCLUDED)
|
|
+#include <linux/config.h>
|
|
+#endif
|
|
+#include <linux/module.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/list.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/wait.h>
|
|
+#include <linux/sched.h>
|
|
+#include <linux/spinlock.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <cryptodev.h>
|
|
+
|
|
+#ifdef I_HAVE_AN_XSCALE_WITH_INTEL_SDK
|
|
+#define BENCH_IXP_ACCESS_LIB 1
|
|
+#endif
|
|
+#ifdef BENCH_IXP_ACCESS_LIB
|
|
+#include <IxTypes.h>
|
|
+#include <IxOsBuffMgt.h>
|
|
+#include <IxNpeDl.h>
|
|
+#include <IxCryptoAcc.h>
|
|
+#include <IxQMgr.h>
|
|
+#include <IxOsServices.h>
|
|
+#include <IxOsCacheMMU.h>
|
|
+#endif
|
|
+
|
|
+/*
|
|
+ * support for access lib version 1.4
|
|
+ */
|
|
+#ifndef IX_MBUF_PRIV
|
|
+#define IX_MBUF_PRIV(x) ((x)->priv)
|
|
+#endif
|
|
+
|
|
+/*
|
|
+ * the number of simultaneously active requests
|
|
+ */
|
|
+static int request_q_len = 40;
|
|
+module_param(request_q_len, int, 0);
|
|
+MODULE_PARM_DESC(request_q_len, "Number of outstanding requests");
|
|
+
|
|
+/*
|
|
+ * how many requests we want to have processed
|
|
+ */
|
|
+static int request_num = 1024;
|
|
+module_param(request_num, int, 0);
|
|
+MODULE_PARM_DESC(request_num, "run for at least this many requests");
|
|
+
|
|
+/*
|
|
+ * the size of each request
|
|
+ */
|
|
+static int request_size = 1488;
|
|
+module_param(request_size, int, 0);
|
|
+MODULE_PARM_DESC(request_size, "size of each request");
|
|
+
|
|
+/*
|
|
+ * OCF batching of requests
|
|
+ */
|
|
+static int request_batch = 1;
|
|
+module_param(request_batch, int, 0);
|
|
+MODULE_PARM_DESC(request_batch, "enable OCF request batching");
|
|
+
|
|
+/*
|
|
+ * OCF immediate callback on completion
|
|
+ */
|
|
+static int request_cbimm = 1;
|
|
+module_param(request_cbimm, int, 0);
|
|
+MODULE_PARM_DESC(request_cbimm, "enable OCF immediate callback on completion");
|
|
+
|
|
+/*
|
|
+ * a structure for each request
|
|
+ */
|
|
+typedef struct {
|
|
+ struct work_struct work;
|
|
+#ifdef BENCH_IXP_ACCESS_LIB
|
|
+ IX_MBUF mbuf;
|
|
+#endif
|
|
+ unsigned char *buffer;
|
|
+} request_t;
|
|
+
|
|
+static request_t *requests;
|
|
+
|
|
+static spinlock_t ocfbench_counter_lock;
|
|
+static int outstanding;
|
|
+static int total;
|
|
+
|
|
+/*************************************************************************/
|
|
+/*
|
|
+ * OCF benchmark routines
|
|
+ */
|
|
+
|
|
+static uint64_t ocf_cryptoid;
|
|
+static unsigned long jstart, jstop;
|
|
+
|
|
+static int ocf_init(void);
|
|
+static int ocf_cb(struct cryptop *crp);
|
|
+static void ocf_request(void *arg);
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
|
|
+static void ocf_request_wq(struct work_struct *work);
|
|
+#endif
|
|
+
|
|
+static int
|
|
+ocf_init(void)
|
|
+{
|
|
+ int error;
|
|
+ struct cryptoini crie, cria;
|
|
+ struct cryptodesc crda, crde;
|
|
+
|
|
+ memset(&crie, 0, sizeof(crie));
|
|
+ memset(&cria, 0, sizeof(cria));
|
|
+ memset(&crde, 0, sizeof(crde));
|
|
+ memset(&crda, 0, sizeof(crda));
|
|
+
|
|
+ cria.cri_alg = CRYPTO_SHA1_HMAC;
|
|
+ cria.cri_klen = 20 * 8;
|
|
+ cria.cri_key = "0123456789abcdefghij";
|
|
+
|
|
+ //crie.cri_alg = CRYPTO_3DES_CBC;
|
|
+ crie.cri_alg = CRYPTO_AES_CBC;
|
|
+ crie.cri_klen = 24 * 8;
|
|
+ crie.cri_key = "0123456789abcdefghijklmn";
|
|
+
|
|
+ crie.cri_next = &cria;
|
|
+
|
|
+ error = crypto_newsession(&ocf_cryptoid, &crie,
|
|
+ CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE);
|
|
+ if (error) {
|
|
+ printk("crypto_newsession failed %d\n", error);
|
|
+ return -1;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+ocf_cb(struct cryptop *crp)
|
|
+{
|
|
+ request_t *r = (request_t *) crp->crp_opaque;
|
|
+ unsigned long flags;
|
|
+
|
|
+ if (crp->crp_etype)
|
|
+ printk("Error in OCF processing: %d\n", crp->crp_etype);
|
|
+ crypto_freereq(crp);
|
|
+ crp = NULL;
|
|
+
|
|
+ /* do all requests but take at least 1 second */
|
|
+ spin_lock_irqsave(&ocfbench_counter_lock, flags);
|
|
+ total++;
|
|
+ if (total > request_num && jstart + HZ < jiffies) {
|
|
+ outstanding--;
|
|
+ spin_unlock_irqrestore(&ocfbench_counter_lock, flags);
|
|
+ return 0;
|
|
+ }
|
|
+ spin_unlock_irqrestore(&ocfbench_counter_lock, flags);
|
|
+
|
|
+ schedule_work(&r->work);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+static void
|
|
+ocf_request(void *arg)
|
|
+{
|
|
+ request_t *r = arg;
|
|
+ struct cryptop *crp = crypto_getreq(2);
|
|
+ struct cryptodesc *crde, *crda;
|
|
+ unsigned long flags;
|
|
+
|
|
+ if (!crp) {
|
|
+ spin_lock_irqsave(&ocfbench_counter_lock, flags);
|
|
+ outstanding--;
|
|
+ spin_unlock_irqrestore(&ocfbench_counter_lock, flags);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ crde = crp->crp_desc;
|
|
+ crda = crde->crd_next;
|
|
+
|
|
+ crda->crd_skip = 0;
|
|
+ crda->crd_flags = 0;
|
|
+ crda->crd_len = request_size;
|
|
+ crda->crd_inject = request_size;
|
|
+ crda->crd_alg = CRYPTO_SHA1_HMAC;
|
|
+ crda->crd_key = "0123456789abcdefghij";
|
|
+ crda->crd_klen = 20 * 8;
|
|
+
|
|
+ crde->crd_skip = 0;
|
|
+ crde->crd_flags = CRD_F_IV_EXPLICIT | CRD_F_ENCRYPT;
|
|
+ crde->crd_len = request_size;
|
|
+ crde->crd_inject = request_size;
|
|
+ //crde->crd_alg = CRYPTO_3DES_CBC;
|
|
+ crde->crd_alg = CRYPTO_AES_CBC;
|
|
+ crde->crd_key = "0123456789abcdefghijklmn";
|
|
+ crde->crd_klen = 24 * 8;
|
|
+
|
|
+ crp->crp_ilen = request_size + 64;
|
|
+ crp->crp_flags = 0;
|
|
+ if (request_batch)
|
|
+ crp->crp_flags |= CRYPTO_F_BATCH;
|
|
+ if (request_cbimm)
|
|
+ crp->crp_flags |= CRYPTO_F_CBIMM;
|
|
+ crp->crp_buf = (caddr_t) r->buffer;
|
|
+ crp->crp_callback = ocf_cb;
|
|
+ crp->crp_sid = ocf_cryptoid;
|
|
+ crp->crp_opaque = (caddr_t) r;
|
|
+ crypto_dispatch(crp);
|
|
+}
|
|
+
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
|
|
+static void
|
|
+ocf_request_wq(struct work_struct *work)
|
|
+{
|
|
+ request_t *r = container_of(work, request_t, work);
|
|
+ ocf_request(r);
|
|
+}
|
|
+#endif
|
|
+
|
|
+static void
|
|
+ocf_done(void)
|
|
+{
|
|
+ crypto_freesession(ocf_cryptoid);
|
|
+}
|
|
+
|
|
+/*************************************************************************/
|
|
+#ifdef BENCH_IXP_ACCESS_LIB
|
|
+/*************************************************************************/
|
|
+/*
|
|
+ * CryptoAcc benchmark routines
|
|
+ */
|
|
+
|
|
+static IxCryptoAccCtx ixp_ctx;
|
|
+static UINT32 ixp_ctx_id;
|
|
+static IX_MBUF ixp_pri;
|
|
+static IX_MBUF ixp_sec;
|
|
+static int ixp_registered = 0;
|
|
+
|
|
+static void ixp_register_cb(UINT32 ctx_id, IX_MBUF *bufp,
|
|
+ IxCryptoAccStatus status);
|
|
+static void ixp_perform_cb(UINT32 ctx_id, IX_MBUF *sbufp, IX_MBUF *dbufp,
|
|
+ IxCryptoAccStatus status);
|
|
+static void ixp_request(void *arg);
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
|
|
+static void ixp_request_wq(struct work_struct *work);
|
|
+#endif
|
|
+
|
|
+static int
|
|
+ixp_init(void)
|
|
+{
|
|
+ IxCryptoAccStatus status;
|
|
+
|
|
+ ixp_ctx.cipherCtx.cipherAlgo = IX_CRYPTO_ACC_CIPHER_3DES;
|
|
+ ixp_ctx.cipherCtx.cipherMode = IX_CRYPTO_ACC_MODE_CBC;
|
|
+ ixp_ctx.cipherCtx.cipherKeyLen = 24;
|
|
+ ixp_ctx.cipherCtx.cipherBlockLen = IX_CRYPTO_ACC_DES_BLOCK_64;
|
|
+ ixp_ctx.cipherCtx.cipherInitialVectorLen = IX_CRYPTO_ACC_DES_IV_64;
|
|
+ memcpy(ixp_ctx.cipherCtx.key.cipherKey, "0123456789abcdefghijklmn", 24);
|
|
+
|
|
+ ixp_ctx.authCtx.authAlgo = IX_CRYPTO_ACC_AUTH_SHA1;
|
|
+ ixp_ctx.authCtx.authDigestLen = 12;
|
|
+ ixp_ctx.authCtx.aadLen = 0;
|
|
+ ixp_ctx.authCtx.authKeyLen = 20;
|
|
+ memcpy(ixp_ctx.authCtx.key.authKey, "0123456789abcdefghij", 20);
|
|
+
|
|
+ ixp_ctx.useDifferentSrcAndDestMbufs = 0;
|
|
+ ixp_ctx.operation = IX_CRYPTO_ACC_OP_ENCRYPT_AUTH ;
|
|
+
|
|
+ IX_MBUF_MLEN(&ixp_pri) = IX_MBUF_PKT_LEN(&ixp_pri) = 128;
|
|
+ IX_MBUF_MDATA(&ixp_pri) = (unsigned char *) kmalloc(128, SLAB_ATOMIC);
|
|
+ IX_MBUF_MLEN(&ixp_sec) = IX_MBUF_PKT_LEN(&ixp_sec) = 128;
|
|
+ IX_MBUF_MDATA(&ixp_sec) = (unsigned char *) kmalloc(128, SLAB_ATOMIC);
|
|
+
|
|
+ status = ixCryptoAccCtxRegister(&ixp_ctx, &ixp_pri, &ixp_sec,
|
|
+ ixp_register_cb, ixp_perform_cb, &ixp_ctx_id);
|
|
+
|
|
+ if (IX_CRYPTO_ACC_STATUS_SUCCESS == status) {
|
|
+ while (!ixp_registered)
|
|
+ schedule();
|
|
+ return ixp_registered < 0 ? -1 : 0;
|
|
+ }
|
|
+
|
|
+ printk("ixp: ixCryptoAccCtxRegister failed %d\n", status);
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+static void
|
|
+ixp_register_cb(UINT32 ctx_id, IX_MBUF *bufp, IxCryptoAccStatus status)
|
|
+{
|
|
+ if (bufp) {
|
|
+ IX_MBUF_MLEN(bufp) = IX_MBUF_PKT_LEN(bufp) = 0;
|
|
+ kfree(IX_MBUF_MDATA(bufp));
|
|
+ IX_MBUF_MDATA(bufp) = NULL;
|
|
+ }
|
|
+
|
|
+ if (IX_CRYPTO_ACC_STATUS_WAIT == status)
|
|
+ return;
|
|
+ if (IX_CRYPTO_ACC_STATUS_SUCCESS == status)
|
|
+ ixp_registered = 1;
|
|
+ else
|
|
+ ixp_registered = -1;
|
|
+}
|
|
+
|
|
+static void
|
|
+ixp_perform_cb(
|
|
+ UINT32 ctx_id,
|
|
+ IX_MBUF *sbufp,
|
|
+ IX_MBUF *dbufp,
|
|
+ IxCryptoAccStatus status)
|
|
+{
|
|
+ request_t *r = NULL;
|
|
+ unsigned long flags;
|
|
+
|
|
+ /* do all requests but take at least 1 second */
|
|
+ spin_lock_irqsave(&ocfbench_counter_lock, flags);
|
|
+ total++;
|
|
+ if (total > request_num && jstart + HZ < jiffies) {
|
|
+ outstanding--;
|
|
+ spin_unlock_irqrestore(&ocfbench_counter_lock, flags);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (!sbufp || !(r = IX_MBUF_PRIV(sbufp))) {
|
|
+ printk("crappo %p %p\n", sbufp, r);
|
|
+ outstanding--;
|
|
+ spin_unlock_irqrestore(&ocfbench_counter_lock, flags);
|
|
+ return;
|
|
+ }
|
|
+ spin_unlock_irqrestore(&ocfbench_counter_lock, flags);
|
|
+
|
|
+ schedule_work(&r->work);
|
|
+}
|
|
+
|
|
+static void
|
|
+ixp_request(void *arg)
|
|
+{
|
|
+ request_t *r = arg;
|
|
+ IxCryptoAccStatus status;
|
|
+ unsigned long flags;
|
|
+
|
|
+ memset(&r->mbuf, 0, sizeof(r->mbuf));
|
|
+ IX_MBUF_MLEN(&r->mbuf) = IX_MBUF_PKT_LEN(&r->mbuf) = request_size + 64;
|
|
+ IX_MBUF_MDATA(&r->mbuf) = r->buffer;
|
|
+ IX_MBUF_PRIV(&r->mbuf) = r;
|
|
+ status = ixCryptoAccAuthCryptPerform(ixp_ctx_id, &r->mbuf, NULL,
|
|
+ 0, request_size, 0, request_size, request_size, r->buffer);
|
|
+ if (IX_CRYPTO_ACC_STATUS_SUCCESS != status) {
|
|
+ printk("status1 = %d\n", status);
|
|
+ spin_lock_irqsave(&ocfbench_counter_lock, flags);
|
|
+ outstanding--;
|
|
+ spin_unlock_irqrestore(&ocfbench_counter_lock, flags);
|
|
+ return;
|
|
+ }
|
|
+ return;
|
|
+}
|
|
+
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
|
|
+static void
|
|
+ixp_request_wq(struct work_struct *work)
|
|
+{
|
|
+ request_t *r = container_of(work, request_t, work);
|
|
+ ixp_request(r);
|
|
+}
|
|
+#endif
|
|
+
|
|
+static void
|
|
+ixp_done(void)
|
|
+{
|
|
+ /* we should free the session here but I am lazy :-) */
|
|
+}
|
|
+
|
|
+/*************************************************************************/
|
|
+#endif /* BENCH_IXP_ACCESS_LIB */
|
|
+/*************************************************************************/
|
|
+
|
|
+int
|
|
+ocfbench_init(void)
|
|
+{
|
|
+ int i;
|
|
+ unsigned long mbps;
|
|
+ unsigned long flags;
|
|
+
|
|
+ printk("Crypto Speed tests\n");
|
|
+
|
|
+ requests = kmalloc(sizeof(request_t) * request_q_len, GFP_KERNEL);
|
|
+ if (!requests) {
|
|
+ printk("malloc failed\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < request_q_len; i++) {
|
|
+ /* +64 for return data */
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
|
|
+ INIT_WORK(&requests[i].work, ocf_request_wq);
|
|
+#else
|
|
+ INIT_WORK(&requests[i].work, ocf_request, &requests[i]);
|
|
+#endif
|
|
+ requests[i].buffer = kmalloc(request_size + 128, GFP_DMA);
|
|
+ if (!requests[i].buffer) {
|
|
+ printk("malloc failed\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ memset(requests[i].buffer, '0' + i, request_size + 128);
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * OCF benchmark
|
|
+ */
|
|
+ printk("OCF: testing ...\n");
|
|
+ if (ocf_init() == -1)
|
|
+ return -EINVAL;
|
|
+
|
|
+ spin_lock_init(&ocfbench_counter_lock);
|
|
+ total = outstanding = 0;
|
|
+ jstart = jiffies;
|
|
+ for (i = 0; i < request_q_len; i++) {
|
|
+ spin_lock_irqsave(&ocfbench_counter_lock, flags);
|
|
+ outstanding++;
|
|
+ spin_unlock_irqrestore(&ocfbench_counter_lock, flags);
|
|
+ ocf_request(&requests[i]);
|
|
+ }
|
|
+ while (outstanding > 0)
|
|
+ schedule();
|
|
+ jstop = jiffies;
|
|
+
|
|
+ mbps = 0;
|
|
+ if (jstop > jstart) {
|
|
+ mbps = (unsigned long) total * (unsigned long) request_size * 8;
|
|
+ mbps /= ((jstop - jstart) * 1000) / HZ;
|
|
+ }
|
|
+ printk("OCF: %d requests of %d bytes in %d jiffies (%d.%03d Mbps)\n",
|
|
+ total, request_size, (int)(jstop - jstart),
|
|
+ ((int)mbps) / 1000, ((int)mbps) % 1000);
|
|
+ ocf_done();
|
|
+
|
|
+#ifdef BENCH_IXP_ACCESS_LIB
|
|
+ /*
|
|
+ * IXP benchmark
|
|
+ */
|
|
+ printk("IXP: testing ...\n");
|
|
+ ixp_init();
|
|
+ total = outstanding = 0;
|
|
+ jstart = jiffies;
|
|
+ for (i = 0; i < request_q_len; i++) {
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
|
|
+ INIT_WORK(&requests[i].work, ixp_request_wq);
|
|
+#else
|
|
+ INIT_WORK(&requests[i].work, ixp_request, &requests[i]);
|
|
+#endif
|
|
+ spin_lock_irqsave(&ocfbench_counter_lock, flags);
|
|
+ outstanding++;
|
|
+ spin_unlock_irqrestore(&ocfbench_counter_lock, flags);
|
|
+ ixp_request(&requests[i]);
|
|
+ }
|
|
+ while (outstanding > 0)
|
|
+ schedule();
|
|
+ jstop = jiffies;
|
|
+
|
|
+ mbps = 0;
|
|
+ if (jstop > jstart) {
|
|
+ mbps = (unsigned long) total * (unsigned long) request_size * 8;
|
|
+ mbps /= ((jstop - jstart) * 1000) / HZ;
|
|
+ }
|
|
+ printk("IXP: %d requests of %d bytes in %d jiffies (%d.%03d Mbps)\n",
|
|
+ total, request_size, jstop - jstart,
|
|
+ ((int)mbps) / 1000, ((int)mbps) % 1000);
|
|
+ ixp_done();
|
|
+#endif /* BENCH_IXP_ACCESS_LIB */
|
|
+
|
|
+ for (i = 0; i < request_q_len; i++)
|
|
+ kfree(requests[i].buffer);
|
|
+ kfree(requests);
|
|
+ return -EINVAL; /* always fail to load so it can be re-run quickly ;-) */
|
|
+}
|
|
+
|
|
+static void __exit ocfbench_exit(void)
|
|
+{
|
|
+}
|
|
+
|
|
+module_init(ocfbench_init);
|
|
+module_exit(ocfbench_exit);
|
|
+
|
|
+MODULE_LICENSE("BSD");
|
|
+MODULE_AUTHOR("David McCullough <david_mccullough@mcafee.com>");
|
|
+MODULE_DESCRIPTION("Benchmark various in-kernel crypto speeds");
|
|
diff --git a/crypto/ocf/ocf-compat.h b/crypto/ocf/ocf-compat.h
|
|
new file mode 100644
|
|
index 0000000..4ad1223
|
|
--- /dev/null
|
|
+++ b/crypto/ocf/ocf-compat.h
|
|
@@ -0,0 +1,372 @@
|
|
+#ifndef _BSD_COMPAT_H_
|
|
+#define _BSD_COMPAT_H_ 1
|
|
+/****************************************************************************/
|
|
+/*
|
|
+ * Provide compat routines for older linux kernels and BSD kernels
|
|
+ *
|
|
+ * Written by David McCullough <david_mccullough@mcafee.com>
|
|
+ * Copyright (C) 2010 David McCullough <david_mccullough@mcafee.com>
|
|
+ *
|
|
+ * LICENSE TERMS
|
|
+ *
|
|
+ * The free distribution and use of this software in both source and binary
|
|
+ * form is allowed (with or without changes) provided that:
|
|
+ *
|
|
+ * 1. distributions of this source code include the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer;
|
|
+ *
|
|
+ * 2. distributions in binary form include the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer
|
|
+ * in the documentation and/or other associated materials;
|
|
+ *
|
|
+ * 3. the copyright holder's name is not used to endorse products
|
|
+ * built using this software without specific written permission.
|
|
+ *
|
|
+ * ALTERNATIVELY, provided that this notice is retained in full, this file
|
|
+ * may be distributed under the terms of the GNU General Public License (GPL),
|
|
+ * in which case the provisions of the GPL apply INSTEAD OF those given above.
|
|
+ *
|
|
+ * DISCLAIMER
|
|
+ *
|
|
+ * This software is provided 'as is' with no explicit or implied warranties
|
|
+ * in respect of its properties, including, but not limited to, correctness
|
|
+ * and/or fitness for purpose.
|
|
+ */
|
|
+/****************************************************************************/
|
|
+#ifdef __KERNEL__
|
|
+#include <linux/version.h>
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) && !defined(AUTOCONF_INCLUDED)
|
|
+#include <linux/config.h>
|
|
+#endif
|
|
+
|
|
+/*
|
|
+ * fake some BSD driver interface stuff specifically for OCF use
|
|
+ */
|
|
+
|
|
+typedef struct ocf_device *device_t;
|
|
+
|
|
+typedef struct {
|
|
+ int (*cryptodev_newsession)(device_t dev, u_int32_t *sidp, struct cryptoini *cri);
|
|
+ int (*cryptodev_freesession)(device_t dev, u_int64_t tid);
|
|
+ int (*cryptodev_process)(device_t dev, struct cryptop *crp, int hint);
|
|
+ int (*cryptodev_kprocess)(device_t dev, struct cryptkop *krp, int hint);
|
|
+} device_method_t;
|
|
+#define DEVMETHOD(id, func) id: func
|
|
+
|
|
+struct ocf_device {
|
|
+ char name[32]; /* the driver name */
|
|
+ char nameunit[32]; /* the driver name + HW instance */
|
|
+ int unit;
|
|
+ device_method_t methods;
|
|
+ void *softc;
|
|
+};
|
|
+
|
|
+#define CRYPTODEV_NEWSESSION(dev, sid, cri) \
|
|
+ ((*(dev)->methods.cryptodev_newsession)(dev,sid,cri))
|
|
+#define CRYPTODEV_FREESESSION(dev, sid) \
|
|
+ ((*(dev)->methods.cryptodev_freesession)(dev, sid))
|
|
+#define CRYPTODEV_PROCESS(dev, crp, hint) \
|
|
+ ((*(dev)->methods.cryptodev_process)(dev, crp, hint))
|
|
+#define CRYPTODEV_KPROCESS(dev, krp, hint) \
|
|
+ ((*(dev)->methods.cryptodev_kprocess)(dev, krp, hint))
|
|
+
|
|
+#define device_get_name(dev) ((dev)->name)
|
|
+#define device_get_nameunit(dev) ((dev)->nameunit)
|
|
+#define device_get_unit(dev) ((dev)->unit)
|
|
+#define device_get_softc(dev) ((dev)->softc)
|
|
+
|
|
+#define softc_device_decl \
|
|
+ struct ocf_device _device; \
|
|
+ device_t
|
|
+
|
|
+#define softc_device_init(_sc, _name, _unit, _methods) \
|
|
+ if (1) {\
|
|
+ strncpy((_sc)->_device.name, _name, sizeof((_sc)->_device.name) - 1); \
|
|
+ snprintf((_sc)->_device.nameunit, sizeof((_sc)->_device.name), "%s%d", _name, _unit); \
|
|
+ (_sc)->_device.unit = _unit; \
|
|
+ (_sc)->_device.methods = _methods; \
|
|
+ (_sc)->_device.softc = (void *) _sc; \
|
|
+ *(device_t *)((softc_get_device(_sc))+1) = &(_sc)->_device; \
|
|
+ } else
|
|
+
|
|
+#define softc_get_device(_sc) (&(_sc)->_device)
|
|
+
|
|
+/*
|
|
+ * iomem support for 2.4 and 2.6 kernels
|
|
+ */
|
|
+#include <linux/version.h>
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
|
|
+#define ocf_iomem_t unsigned long
|
|
+
|
|
+/*
|
|
+ * implement simple workqueue like support for older kernels
|
|
+ */
|
|
+
|
|
+#include <linux/tqueue.h>
|
|
+
|
|
+#define work_struct tq_struct
|
|
+
|
|
+#define INIT_WORK(wp, fp, ap) \
|
|
+ do { \
|
|
+ (wp)->sync = 0; \
|
|
+ (wp)->routine = (fp); \
|
|
+ (wp)->data = (ap); \
|
|
+ } while (0)
|
|
+
|
|
+#define schedule_work(wp) \
|
|
+ do { \
|
|
+ queue_task((wp), &tq_immediate); \
|
|
+ mark_bh(IMMEDIATE_BH); \
|
|
+ } while (0)
|
|
+
|
|
+#define flush_scheduled_work() run_task_queue(&tq_immediate)
|
|
+
|
|
+#else
|
|
+#define ocf_iomem_t void __iomem *
|
|
+
|
|
+#include <linux/workqueue.h>
|
|
+
|
|
+#endif
|
|
+
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
|
|
+#include <linux/fdtable.h>
|
|
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
|
|
+#define files_fdtable(files) (files)
|
|
+#endif
|
|
+
|
|
+#ifdef MODULE_PARM
|
|
+#undef module_param /* just in case */
|
|
+#define module_param(a,b,c) MODULE_PARM(a,"i")
|
|
+#endif
|
|
+
|
|
+#define bzero(s,l) memset(s,0,l)
|
|
+#define bcopy(s,d,l) memcpy(d,s,l)
|
|
+#define bcmp(x, y, l) memcmp(x,y,l)
|
|
+
|
|
+#define MIN(x,y) ((x) < (y) ? (x) : (y))
|
|
+
|
|
+#define device_printf(dev, a...) ({ \
|
|
+ printk("%s: ", device_get_nameunit(dev)); printk(a); \
|
|
+ })
|
|
+
|
|
+#undef printf
|
|
+#define printf(fmt...) printk(fmt)
|
|
+
|
|
+#define KASSERT(c,p) if (!(c)) { printk p ; } else
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
|
|
+#define ocf_daemonize(str) \
|
|
+ daemonize(); \
|
|
+ spin_lock_irq(¤t->sigmask_lock); \
|
|
+ sigemptyset(¤t->blocked); \
|
|
+ recalc_sigpending(current); \
|
|
+ spin_unlock_irq(¤t->sigmask_lock); \
|
|
+ sprintf(current->comm, str);
|
|
+#else
|
|
+#define ocf_daemonize(str) daemonize(str);
|
|
+#endif
|
|
+
|
|
+#define TAILQ_INSERT_TAIL(q,d,m) list_add_tail(&(d)->m, (q))
|
|
+#define TAILQ_EMPTY(q) list_empty(q)
|
|
+#define TAILQ_FOREACH(v, q, m) list_for_each_entry(v, q, m)
|
|
+
|
|
+#define read_random(p,l) get_random_bytes(p,l)
|
|
+
|
|
+#define DELAY(x) ((x) > 2000 ? mdelay((x)/1000) : udelay(x))
|
|
+#define strtoul simple_strtoul
|
|
+
|
|
+#define pci_get_vendor(dev) ((dev)->vendor)
|
|
+#define pci_get_device(dev) ((dev)->device)
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
|
|
+#define pci_set_consistent_dma_mask(dev, mask) (0)
|
|
+#endif
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
|
|
+#define pci_dma_sync_single_for_cpu pci_dma_sync_single
|
|
+#endif
|
|
+
|
|
+#ifndef DMA_32BIT_MASK
|
|
+#define DMA_32BIT_MASK 0x00000000ffffffffULL
|
|
+#endif
|
|
+
|
|
+#ifndef htole32
|
|
+#define htole32(x) cpu_to_le32(x)
|
|
+#endif
|
|
+#ifndef htobe32
|
|
+#define htobe32(x) cpu_to_be32(x)
|
|
+#endif
|
|
+#ifndef htole16
|
|
+#define htole16(x) cpu_to_le16(x)
|
|
+#endif
|
|
+#ifndef htobe16
|
|
+#define htobe16(x) cpu_to_be16(x)
|
|
+#endif
|
|
+
|
|
+/* older kernels don't have these */
|
|
+
|
|
+#include <asm/irq.h>
|
|
+#if !defined(IRQ_NONE) && !defined(IRQ_RETVAL)
|
|
+#define IRQ_NONE
|
|
+#define IRQ_HANDLED
|
|
+#define IRQ_WAKE_THREAD
|
|
+#define IRQ_RETVAL
|
|
+#define irqreturn_t void
|
|
+typedef irqreturn_t (*irq_handler_t)(int irq, void *arg, struct pt_regs *regs);
|
|
+#endif
|
|
+#ifndef IRQF_SHARED
|
|
+#define IRQF_SHARED SA_SHIRQ
|
|
+#endif
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
|
|
+# define strlcpy(dest,src,len) \
|
|
+ ({strncpy(dest,src,(len)-1); ((char *)dest)[(len)-1] = '\0'; })
|
|
+#endif
|
|
+
|
|
+#ifndef MAX_ERRNO
|
|
+#define MAX_ERRNO 4095
|
|
+#endif
|
|
+#ifndef IS_ERR_VALUE
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,5)
|
|
+#include <linux/err.h>
|
|
+#endif
|
|
+#ifndef IS_ERR_VALUE
|
|
+#define IS_ERR_VALUE(x) ((unsigned long)(x) >= (unsigned long)-MAX_ERRNO)
|
|
+#endif
|
|
+#endif
|
|
+
|
|
+/*
|
|
+ * common debug for all
|
|
+ */
|
|
+#if 1
|
|
+#define dprintk(a...) do { if (debug) printk(a); } while(0)
|
|
+#else
|
|
+#define dprintk(a...)
|
|
+#endif
|
|
+
|
|
+#ifndef SLAB_ATOMIC
|
|
+/* Changed in 2.6.20, must use GFP_ATOMIC now */
|
|
+#define SLAB_ATOMIC GFP_ATOMIC
|
|
+#endif
|
|
+
|
|
+/*
|
|
+ * need some additional support for older kernels */
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,2)
|
|
+#define pci_register_driver_compat(driver, rc) \
|
|
+ do { \
|
|
+ if ((rc) > 0) { \
|
|
+ (rc) = 0; \
|
|
+ } else if (rc == 0) { \
|
|
+ (rc) = -ENODEV; \
|
|
+ } else { \
|
|
+ pci_unregister_driver(driver); \
|
|
+ } \
|
|
+ } while (0)
|
|
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
|
|
+#define pci_register_driver_compat(driver,rc) ((rc) = (rc) < 0 ? (rc) : 0)
|
|
+#else
|
|
+#define pci_register_driver_compat(driver,rc)
|
|
+#endif
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
|
|
+
|
|
+#include <linux/mm.h>
|
|
+#include <asm/scatterlist.h>
|
|
+
|
|
+static inline void sg_set_page(struct scatterlist *sg, struct page *page,
|
|
+ unsigned int len, unsigned int offset)
|
|
+{
|
|
+ sg->page = page;
|
|
+ sg->offset = offset;
|
|
+ sg->length = len;
|
|
+}
|
|
+
|
|
+static inline void *sg_virt(struct scatterlist *sg)
|
|
+{
|
|
+ return page_address(sg->page) + sg->offset;
|
|
+}
|
|
+
|
|
+#define sg_init_table(sg, n)
|
|
+
|
|
+#define sg_mark_end(sg)
|
|
+
|
|
+#endif
|
|
+
|
|
+#ifndef late_initcall
|
|
+#define late_initcall(init) module_init(init)
|
|
+#endif
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,4) || !defined(CONFIG_SMP)
|
|
+#define ocf_for_each_cpu(cpu) for ((cpu) = 0; (cpu) == 0; (cpu)++)
|
|
+#else
|
|
+#define ocf_for_each_cpu(cpu) for_each_present_cpu(cpu)
|
|
+#endif
|
|
+
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
|
|
+#include <linux/sched.h>
|
|
+#define kill_proc(p,s,v) send_sig(s,find_task_by_vpid(p),0)
|
|
+#endif
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,4)
|
|
+
|
|
+struct ocf_thread {
|
|
+ struct task_struct *task;
|
|
+ int (*func)(void *arg);
|
|
+ void *arg;
|
|
+};
|
|
+
|
|
+/* thread startup helper func */
|
|
+static inline int ocf_run_thread(void *arg)
|
|
+{
|
|
+ struct ocf_thread *t = (struct ocf_thread *) arg;
|
|
+ if (!t)
|
|
+ return -1; /* very bad */
|
|
+ t->task = current;
|
|
+ daemonize();
|
|
+ spin_lock_irq(¤t->sigmask_lock);
|
|
+ sigemptyset(¤t->blocked);
|
|
+ recalc_sigpending(current);
|
|
+ spin_unlock_irq(¤t->sigmask_lock);
|
|
+ return (*t->func)(t->arg);
|
|
+}
|
|
+
|
|
+#define kthread_create(f,a,fmt...) \
|
|
+ ({ \
|
|
+ struct ocf_thread t; \
|
|
+ pid_t p; \
|
|
+ t.task = NULL; \
|
|
+ t.func = (f); \
|
|
+ t.arg = (a); \
|
|
+ p = kernel_thread(ocf_run_thread, &t, CLONE_FS|CLONE_FILES); \
|
|
+ while (p != (pid_t) -1 && t.task == NULL) \
|
|
+ schedule(); \
|
|
+ if (t.task) \
|
|
+ snprintf(t.task->comm, sizeof(t.task->comm), fmt); \
|
|
+ (t.task); \
|
|
+ })
|
|
+
|
|
+#define kthread_bind(t,cpu) /**/
|
|
+
|
|
+#define kthread_should_stop() (strcmp(current->comm, "stopping") == 0)
|
|
+
|
|
+#define kthread_stop(t) \
|
|
+ ({ \
|
|
+ strcpy((t)->comm, "stopping"); \
|
|
+ kill_proc((t)->pid, SIGTERM, 1); \
|
|
+ do { \
|
|
+ schedule(); \
|
|
+ } while (kill_proc((t)->pid, SIGTERM, 1) == 0); \
|
|
+ })
|
|
+
|
|
+#else
|
|
+#include <linux/kthread.h>
|
|
+#endif
|
|
+
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0)
|
|
+#define skb_frag_page(x) ((x)->page)
|
|
+#endif
|
|
+
|
|
+#endif /* __KERNEL__ */
|
|
+
|
|
+/****************************************************************************/
|
|
+#endif /* _BSD_COMPAT_H_ */
|
|
diff --git a/crypto/ocf/ocfnull/Makefile b/crypto/ocf/ocfnull/Makefile
|
|
new file mode 100644
|
|
index 0000000..044bcac
|
|
--- /dev/null
|
|
+++ b/crypto/ocf/ocfnull/Makefile
|
|
@@ -0,0 +1,12 @@
|
|
+# for SGlinux builds
|
|
+-include $(ROOTDIR)/modules/.config
|
|
+
|
|
+obj-$(CONFIG_OCF_OCFNULL) += ocfnull.o
|
|
+
|
|
+obj ?= .
|
|
+EXTRA_CFLAGS += -I$(obj)/..
|
|
+
|
|
+ifdef TOPDIR
|
|
+-include $(TOPDIR)/Rules.make
|
|
+endif
|
|
+
|
|
diff --git a/crypto/ocf/ocfnull/ocfnull.c b/crypto/ocf/ocfnull/ocfnull.c
|
|
new file mode 100644
|
|
index 0000000..9cf3f6e
|
|
--- /dev/null
|
|
+++ b/crypto/ocf/ocfnull/ocfnull.c
|
|
@@ -0,0 +1,204 @@
|
|
+/*
|
|
+ * An OCF module for determining the cost of crypto versus the cost of
|
|
+ * IPSec processing outside of OCF. This modules gives us the effect of
|
|
+ * zero cost encryption, of course you will need to run it at both ends
|
|
+ * since it does no crypto at all.
|
|
+ *
|
|
+ * Written by David McCullough <david_mccullough@mcafee.com>
|
|
+ * Copyright (C) 2006-2010 David McCullough
|
|
+ *
|
|
+ * LICENSE TERMS
|
|
+ *
|
|
+ * The free distribution and use of this software in both source and binary
|
|
+ * form is allowed (with or without changes) provided that:
|
|
+ *
|
|
+ * 1. distributions of this source code include the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer;
|
|
+ *
|
|
+ * 2. distributions in binary form include the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer
|
|
+ * in the documentation and/or other associated materials;
|
|
+ *
|
|
+ * 3. the copyright holder's name is not used to endorse products
|
|
+ * built using this software without specific written permission.
|
|
+ *
|
|
+ * ALTERNATIVELY, provided that this notice is retained in full, this product
|
|
+ * may be distributed under the terms of the GNU General Public License (GPL),
|
|
+ * in which case the provisions of the GPL apply INSTEAD OF those given above.
|
|
+ *
|
|
+ * DISCLAIMER
|
|
+ *
|
|
+ * This software is provided 'as is' with no explicit or implied warranties
|
|
+ * in respect of its properties, including, but not limited to, correctness
|
|
+ * and/or fitness for purpose.
|
|
+ */
|
|
+
|
|
+#include <linux/version.h>
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) && !defined(AUTOCONF_INCLUDED)
|
|
+#include <linux/config.h>
|
|
+#endif
|
|
+#include <linux/module.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/list.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/sched.h>
|
|
+#include <linux/wait.h>
|
|
+#include <linux/crypto.h>
|
|
+#include <linux/interrupt.h>
|
|
+
|
|
+#include <cryptodev.h>
|
|
+#include <uio.h>
|
|
+
|
|
+static int32_t null_id = -1;
|
|
+static u_int32_t null_sesnum = 0;
|
|
+
|
|
+static int null_process(device_t, struct cryptop *, int);
|
|
+static int null_newsession(device_t, u_int32_t *, struct cryptoini *);
|
|
+static int null_freesession(device_t, u_int64_t);
|
|
+
|
|
+#define debug ocfnull_debug
|
|
+int ocfnull_debug = 0;
|
|
+module_param(ocfnull_debug, int, 0644);
|
|
+MODULE_PARM_DESC(ocfnull_debug, "Enable debug");
|
|
+
|
|
+/*
|
|
+ * dummy device structure
|
|
+ */
|
|
+
|
|
+static struct {
|
|
+ softc_device_decl sc_dev;
|
|
+} nulldev;
|
|
+
|
|
+static device_method_t null_methods = {
|
|
+ /* crypto device methods */
|
|
+ DEVMETHOD(cryptodev_newsession, null_newsession),
|
|
+ DEVMETHOD(cryptodev_freesession,null_freesession),
|
|
+ DEVMETHOD(cryptodev_process, null_process),
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Generate a new software session.
|
|
+ */
|
|
+static int
|
|
+null_newsession(device_t arg, u_int32_t *sid, struct cryptoini *cri)
|
|
+{
|
|
+ dprintk("%s()\n", __FUNCTION__);
|
|
+ if (sid == NULL || cri == NULL) {
|
|
+ dprintk("%s,%d - EINVAL\n", __FILE__, __LINE__);
|
|
+ return EINVAL;
|
|
+ }
|
|
+
|
|
+ if (null_sesnum == 0)
|
|
+ null_sesnum++;
|
|
+ *sid = null_sesnum++;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * Free a session.
|
|
+ */
|
|
+static int
|
|
+null_freesession(device_t arg, u_int64_t tid)
|
|
+{
|
|
+ u_int32_t sid = CRYPTO_SESID2LID(tid);
|
|
+
|
|
+ dprintk("%s()\n", __FUNCTION__);
|
|
+ if (sid > null_sesnum) {
|
|
+ dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__);
|
|
+ return EINVAL;
|
|
+ }
|
|
+
|
|
+ /* Silently accept and return */
|
|
+ if (sid == 0)
|
|
+ return 0;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * Process a request.
|
|
+ */
|
|
+static int
|
|
+null_process(device_t arg, struct cryptop *crp, int hint)
|
|
+{
|
|
+ unsigned int lid;
|
|
+
|
|
+ dprintk("%s()\n", __FUNCTION__);
|
|
+
|
|
+ /* Sanity check */
|
|
+ if (crp == NULL) {
|
|
+ dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__);
|
|
+ return EINVAL;
|
|
+ }
|
|
+
|
|
+ crp->crp_etype = 0;
|
|
+
|
|
+ if (crp->crp_desc == NULL || crp->crp_buf == NULL) {
|
|
+ dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__);
|
|
+ crp->crp_etype = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * find the session we are using
|
|
+ */
|
|
+
|
|
+ lid = crp->crp_sid & 0xffffffff;
|
|
+ if (lid >= null_sesnum || lid == 0) {
|
|
+ crp->crp_etype = ENOENT;
|
|
+ dprintk("%s,%d: ENOENT\n", __FILE__, __LINE__);
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+done:
|
|
+ crypto_done(crp);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * our driver startup and shutdown routines
|
|
+ */
|
|
+
|
|
+static int
|
|
+null_init(void)
|
|
+{
|
|
+ dprintk("%s(%p)\n", __FUNCTION__, null_init);
|
|
+
|
|
+ memset(&nulldev, 0, sizeof(nulldev));
|
|
+ softc_device_init(&nulldev, "ocfnull", 0, null_methods);
|
|
+
|
|
+ null_id = crypto_get_driverid(softc_get_device(&nulldev),
|
|
+ CRYPTOCAP_F_HARDWARE);
|
|
+ if (null_id < 0)
|
|
+ panic("ocfnull: crypto device cannot initialize!");
|
|
+
|
|
+#define REGISTER(alg) \
|
|
+ crypto_register(null_id,alg,0,0)
|
|
+ REGISTER(CRYPTO_DES_CBC);
|
|
+ REGISTER(CRYPTO_3DES_CBC);
|
|
+ REGISTER(CRYPTO_RIJNDAEL128_CBC);
|
|
+ REGISTER(CRYPTO_MD5);
|
|
+ REGISTER(CRYPTO_SHA1);
|
|
+ REGISTER(CRYPTO_MD5_HMAC);
|
|
+ REGISTER(CRYPTO_SHA1_HMAC);
|
|
+#undef REGISTER
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void
|
|
+null_exit(void)
|
|
+{
|
|
+ dprintk("%s()\n", __FUNCTION__);
|
|
+ crypto_unregister_all(null_id);
|
|
+ null_id = -1;
|
|
+}
|
|
+
|
|
+module_init(null_init);
|
|
+module_exit(null_exit);
|
|
+
|
|
+MODULE_LICENSE("Dual BSD/GPL");
|
|
+MODULE_AUTHOR("David McCullough <david_mccullough@mcafee.com>");
|
|
+MODULE_DESCRIPTION("ocfnull - claims a lot but does nothing");
|
|
diff --git a/crypto/ocf/random.c b/crypto/ocf/random.c
|
|
new file mode 100644
|
|
index 0000000..4bb773f
|
|
--- /dev/null
|
|
+++ b/crypto/ocf/random.c
|
|
@@ -0,0 +1,317 @@
|
|
+/*
|
|
+ * A system independant way of adding entropy to the kernels pool
|
|
+ * this way the drivers can focus on the real work and we can take
|
|
+ * care of pushing it to the appropriate place in the kernel.
|
|
+ *
|
|
+ * This should be fast and callable from timers/interrupts
|
|
+ *
|
|
+ * Written by David McCullough <david_mccullough@mcafee.com>
|
|
+ * Copyright (C) 2006-2010 David McCullough
|
|
+ * Copyright (C) 2004-2005 Intel Corporation.
|
|
+ *
|
|
+ * LICENSE TERMS
|
|
+ *
|
|
+ * The free distribution and use of this software in both source and binary
|
|
+ * form is allowed (with or without changes) provided that:
|
|
+ *
|
|
+ * 1. distributions of this source code include the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer;
|
|
+ *
|
|
+ * 2. distributions in binary form include the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer
|
|
+ * in the documentation and/or other associated materials;
|
|
+ *
|
|
+ * 3. the copyright holder's name is not used to endorse products
|
|
+ * built using this software without specific written permission.
|
|
+ *
|
|
+ * ALTERNATIVELY, provided that this notice is retained in full, this product
|
|
+ * may be distributed under the terms of the GNU General Public License (GPL),
|
|
+ * in which case the provisions of the GPL apply INSTEAD OF those given above.
|
|
+ *
|
|
+ * DISCLAIMER
|
|
+ *
|
|
+ * This software is provided 'as is' with no explicit or implied warranties
|
|
+ * in respect of its properties, including, but not limited to, correctness
|
|
+ * and/or fitness for purpose.
|
|
+ */
|
|
+
|
|
+#include <linux/version.h>
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) && !defined(AUTOCONF_INCLUDED)
|
|
+#include <linux/config.h>
|
|
+#endif
|
|
+#include <linux/module.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/list.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/wait.h>
|
|
+#include <linux/sched.h>
|
|
+#include <linux/spinlock.h>
|
|
+#include <linux/unistd.h>
|
|
+#include <linux/poll.h>
|
|
+#include <linux/random.h>
|
|
+#include <cryptodev.h>
|
|
+
|
|
+#ifdef CONFIG_OCF_FIPS
|
|
+#include "rndtest.h"
|
|
+#endif
|
|
+
|
|
+#ifndef HAS_RANDOM_INPUT_WAIT
|
|
+#error "Please do not enable OCF_RANDOMHARVEST unless you have applied patches"
|
|
+#endif
|
|
+
|
|
+/*
|
|
+ * a hack to access the debug levels from the crypto driver
|
|
+ */
|
|
+extern int crypto_debug;
|
|
+#define debug crypto_debug
|
|
+
|
|
+/*
|
|
+ * a list of all registered random providers
|
|
+ */
|
|
+static LIST_HEAD(random_ops);
|
|
+static int started = 0;
|
|
+static int initted = 0;
|
|
+
|
|
+struct random_op {
|
|
+ struct list_head random_list;
|
|
+ u_int32_t driverid;
|
|
+ int (*read_random)(void *arg, u_int32_t *buf, int len);
|
|
+ void *arg;
|
|
+};
|
|
+
|
|
+static int random_proc(void *arg);
|
|
+
|
|
+static pid_t randomproc = (pid_t) -1;
|
|
+static spinlock_t random_lock;
|
|
+
|
|
+/*
|
|
+ * just init the spin locks
|
|
+ */
|
|
+static int
|
|
+crypto_random_init(void)
|
|
+{
|
|
+ spin_lock_init(&random_lock);
|
|
+ initted = 1;
|
|
+ return(0);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Add the given random reader to our list (if not present)
|
|
+ * and start the thread (if not already started)
|
|
+ *
|
|
+ * we have to assume that driver id is ok for now
|
|
+ */
|
|
+int
|
|
+crypto_rregister(
|
|
+ u_int32_t driverid,
|
|
+ int (*read_random)(void *arg, u_int32_t *buf, int len),
|
|
+ void *arg)
|
|
+{
|
|
+ unsigned long flags;
|
|
+ int ret = 0;
|
|
+ struct random_op *rops, *tmp;
|
|
+
|
|
+ dprintk("%s,%d: %s(0x%x, %p, %p)\n", __FILE__, __LINE__,
|
|
+ __FUNCTION__, driverid, read_random, arg);
|
|
+
|
|
+ if (!initted)
|
|
+ crypto_random_init();
|
|
+
|
|
+#if 0
|
|
+ struct cryptocap *cap;
|
|
+
|
|
+ cap = crypto_checkdriver(driverid);
|
|
+ if (!cap)
|
|
+ return EINVAL;
|
|
+#endif
|
|
+
|
|
+ list_for_each_entry_safe(rops, tmp, &random_ops, random_list) {
|
|
+ if (rops->driverid == driverid && rops->read_random == read_random)
|
|
+ return EEXIST;
|
|
+ }
|
|
+
|
|
+ rops = (struct random_op *) kmalloc(sizeof(*rops), GFP_KERNEL);
|
|
+ if (!rops)
|
|
+ return ENOMEM;
|
|
+
|
|
+ rops->driverid = driverid;
|
|
+ rops->read_random = read_random;
|
|
+ rops->arg = arg;
|
|
+
|
|
+ spin_lock_irqsave(&random_lock, flags);
|
|
+ list_add_tail(&rops->random_list, &random_ops);
|
|
+ if (!started) {
|
|
+ randomproc = kernel_thread(random_proc, NULL, CLONE_FS|CLONE_FILES);
|
|
+ if (randomproc < 0) {
|
|
+ ret = randomproc;
|
|
+ printk("crypto: crypto_rregister cannot start random thread; "
|
|
+ "error %d", ret);
|
|
+ } else
|
|
+ started = 1;
|
|
+ }
|
|
+ spin_unlock_irqrestore(&random_lock, flags);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+EXPORT_SYMBOL(crypto_rregister);
|
|
+
|
|
+int
|
|
+crypto_runregister_all(u_int32_t driverid)
|
|
+{
|
|
+ struct random_op *rops, *tmp;
|
|
+ unsigned long flags;
|
|
+
|
|
+ dprintk("%s,%d: %s(0x%x)\n", __FILE__, __LINE__, __FUNCTION__, driverid);
|
|
+
|
|
+ list_for_each_entry_safe(rops, tmp, &random_ops, random_list) {
|
|
+ if (rops->driverid == driverid) {
|
|
+ list_del(&rops->random_list);
|
|
+ kfree(rops);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ spin_lock_irqsave(&random_lock, flags);
|
|
+ if (list_empty(&random_ops) && started)
|
|
+ kill_proc(randomproc, SIGKILL, 1);
|
|
+ spin_unlock_irqrestore(&random_lock, flags);
|
|
+ return(0);
|
|
+}
|
|
+EXPORT_SYMBOL(crypto_runregister_all);
|
|
+
|
|
+/*
|
|
+ * while we can add entropy to random.c continue to read random data from
|
|
+ * the drivers and push it to random.
|
|
+ */
|
|
+static int
|
|
+random_proc(void *arg)
|
|
+{
|
|
+ int n;
|
|
+ int wantcnt;
|
|
+ int bufcnt = 0;
|
|
+ int retval = 0;
|
|
+ int *buf = NULL;
|
|
+
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
|
|
+ daemonize();
|
|
+ spin_lock_irq(¤t->sigmask_lock);
|
|
+ sigemptyset(¤t->blocked);
|
|
+ recalc_sigpending(current);
|
|
+ spin_unlock_irq(¤t->sigmask_lock);
|
|
+ sprintf(current->comm, "ocf-random");
|
|
+#else
|
|
+ daemonize("ocf-random");
|
|
+ allow_signal(SIGKILL);
|
|
+#endif
|
|
+
|
|
+ (void) get_fs();
|
|
+ set_fs(get_ds());
|
|
+
|
|
+#ifdef CONFIG_OCF_FIPS
|
|
+#define NUM_INT (RNDTEST_NBYTES/sizeof(int))
|
|
+#else
|
|
+#define NUM_INT 32
|
|
+#endif
|
|
+
|
|
+ /*
|
|
+ * some devices can transferr their RNG data direct into memory,
|
|
+ * so make sure it is device friendly
|
|
+ */
|
|
+ buf = kmalloc(NUM_INT * sizeof(int), GFP_DMA);
|
|
+ if (NULL == buf) {
|
|
+ printk("crypto: RNG could not allocate memory\n");
|
|
+ retval = -ENOMEM;
|
|
+ goto bad_alloc;
|
|
+ }
|
|
+
|
|
+ wantcnt = NUM_INT; /* start by adding some entropy */
|
|
+
|
|
+ /*
|
|
+ * its possible due to errors or driver removal that we no longer
|
|
+ * have anything to do, if so exit or we will consume all the CPU
|
|
+ * doing nothing
|
|
+ */
|
|
+ while (!list_empty(&random_ops)) {
|
|
+ struct random_op *rops, *tmp;
|
|
+
|
|
+#ifdef CONFIG_OCF_FIPS
|
|
+ if (wantcnt)
|
|
+ wantcnt = NUM_INT; /* FIPs mode can do 20000 bits or none */
|
|
+#endif
|
|
+
|
|
+ /* see if we can get enough entropy to make the world
|
|
+ * a better place.
|
|
+ */
|
|
+ while (bufcnt < wantcnt && bufcnt < NUM_INT) {
|
|
+ list_for_each_entry_safe(rops, tmp, &random_ops, random_list) {
|
|
+
|
|
+ n = (*rops->read_random)(rops->arg, &buf[bufcnt],
|
|
+ NUM_INT - bufcnt);
|
|
+
|
|
+ /* on failure remove the random number generator */
|
|
+ if (n == -1) {
|
|
+ list_del(&rops->random_list);
|
|
+ printk("crypto: RNG (driverid=0x%x) failed, disabling\n",
|
|
+ rops->driverid);
|
|
+ kfree(rops);
|
|
+ } else if (n > 0)
|
|
+ bufcnt += n;
|
|
+ }
|
|
+ /* give up CPU for a bit, just in case as this is a loop */
|
|
+ schedule();
|
|
+ }
|
|
+
|
|
+
|
|
+#ifdef CONFIG_OCF_FIPS
|
|
+ if (bufcnt > 0 && rndtest_buf((unsigned char *) &buf[0])) {
|
|
+ dprintk("crypto: buffer had fips errors, discarding\n");
|
|
+ bufcnt = 0;
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ /*
|
|
+ * if we have a certified buffer, we can send some data
|
|
+ * to /dev/random and move along
|
|
+ */
|
|
+ if (bufcnt > 0) {
|
|
+ /* add what we have */
|
|
+ random_input_words(buf, bufcnt, bufcnt*sizeof(int)*8);
|
|
+ bufcnt = 0;
|
|
+ }
|
|
+
|
|
+ /* give up CPU for a bit so we don't hog while filling */
|
|
+ schedule();
|
|
+
|
|
+ /* wait for needing more */
|
|
+ wantcnt = random_input_wait();
|
|
+
|
|
+ if (wantcnt <= 0)
|
|
+ wantcnt = 0; /* try to get some info again */
|
|
+ else
|
|
+ /* round up to one word or we can loop forever */
|
|
+ wantcnt = (wantcnt + (sizeof(int)*8)) / (sizeof(int)*8);
|
|
+ if (wantcnt > NUM_INT) {
|
|
+ wantcnt = NUM_INT;
|
|
+ }
|
|
+
|
|
+ if (signal_pending(current)) {
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
|
|
+ spin_lock_irq(¤t->sigmask_lock);
|
|
+#endif
|
|
+ flush_signals(current);
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
|
|
+ spin_unlock_irq(¤t->sigmask_lock);
|
|
+#endif
|
|
+ }
|
|
+ }
|
|
+
|
|
+ kfree(buf);
|
|
+
|
|
+bad_alloc:
|
|
+ spin_lock_irq(&random_lock);
|
|
+ randomproc = (pid_t) -1;
|
|
+ started = 0;
|
|
+ spin_unlock_irq(&random_lock);
|
|
+
|
|
+ return retval;
|
|
+}
|
|
+
|
|
diff --git a/crypto/ocf/rndtest.c b/crypto/ocf/rndtest.c
|
|
new file mode 100644
|
|
index 0000000..7bed6a1
|
|
--- /dev/null
|
|
+++ b/crypto/ocf/rndtest.c
|
|
@@ -0,0 +1,300 @@
|
|
+/* $OpenBSD$ */
|
|
+
|
|
+/*
|
|
+ * OCF/Linux port done by David McCullough <david_mccullough@mcafee.com>
|
|
+ * Copyright (C) 2006-2010 David McCullough
|
|
+ * Copyright (C) 2004-2005 Intel Corporation.
|
|
+ * The license and original author are listed below.
|
|
+ *
|
|
+ * Copyright (c) 2002 Jason L. Wright (jason@thought.net)
|
|
+ * All rights reserved.
|
|
+ *
|
|
+ * Redistribution and use in source and binary forms, with or without
|
|
+ * modification, are permitted provided that the following conditions
|
|
+ * are met:
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
+ * 2. Redistributions in binary form must reproduce the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer in the
|
|
+ * documentation and/or other materials provided with the distribution.
|
|
+ * 3. All advertising materials mentioning features or use of this software
|
|
+ * must display the following acknowledgement:
|
|
+ * This product includes software developed by Jason L. Wright
|
|
+ * 4. The name of the author may not be used to endorse or promote products
|
|
+ * derived from this software without specific prior written permission.
|
|
+ *
|
|
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
|
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
+ * POSSIBILITY OF SUCH DAMAGE.
|
|
+ */
|
|
+
|
|
+#include <linux/version.h>
|
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) && !defined(AUTOCONF_INCLUDED)
|
|
+#include <linux/config.h>
|
|
+#endif
|
|
+#include <linux/module.h>
|
|
+#include <linux/list.h>
|
|
+#include <linux/wait.h>
|
|
+#include <linux/time.h>
|
|
+#include <linux/unistd.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/string.h>
|
|
+#include <linux/time.h>
|
|
+#include <cryptodev.h>
|
|
+#include "rndtest.h"
|
|
+
|
|
+static struct rndtest_stats rndstats;
|
|
+
|
|
+static void rndtest_test(struct rndtest_state *);
|
|
+
|
|
+/* The tests themselves */
|
|
+static int rndtest_monobit(struct rndtest_state *);
|
|
+static int rndtest_runs(struct rndtest_state *);
|
|
+static int rndtest_longruns(struct rndtest_state *);
|
|
+static int rndtest_chi_4(struct rndtest_state *);
|
|
+
|
|
+static int rndtest_runs_check(struct rndtest_state *, int, int *);
|
|
+static void rndtest_runs_record(struct rndtest_state *, int, int *);
|
|
+
|
|
+static const struct rndtest_testfunc {
|
|
+ int (*test)(struct rndtest_state *);
|
|
+} rndtest_funcs[] = {
|
|
+ { rndtest_monobit },
|
|
+ { rndtest_runs },
|
|
+ { rndtest_chi_4 },
|
|
+ { rndtest_longruns },
|
|
+};
|
|
+
|
|
+#define RNDTEST_NTESTS (sizeof(rndtest_funcs)/sizeof(rndtest_funcs[0]))
|
|
+
|
|
+static void
|
|
+rndtest_test(struct rndtest_state *rsp)
|
|
+{
|
|
+ int i, rv = 0;
|
|
+
|
|
+ rndstats.rst_tests++;
|
|
+ for (i = 0; i < RNDTEST_NTESTS; i++)
|
|
+ rv |= (*rndtest_funcs[i].test)(rsp);
|
|
+ rsp->rs_discard = (rv != 0);
|
|
+}
|
|
+
|
|
+
|
|
+extern int crypto_debug;
|
|
+#define rndtest_verbose 2
|
|
+#define rndtest_report(rsp, failure, fmt, a...) \
|
|
+ { if (failure || crypto_debug) { printk("rng_test: " fmt "\n", a); } else; }
|
|
+
|
|
+#define RNDTEST_MONOBIT_MINONES 9725
|
|
+#define RNDTEST_MONOBIT_MAXONES 10275
|
|
+
|
|
+static int
|
|
+rndtest_monobit(struct rndtest_state *rsp)
|
|
+{
|
|
+ int i, ones = 0, j;
|
|
+ u_int8_t r;
|
|
+
|
|
+ for (i = 0; i < RNDTEST_NBYTES; i++) {
|
|
+ r = rsp->rs_buf[i];
|
|
+ for (j = 0; j < 8; j++, r <<= 1)
|
|
+ if (r & 0x80)
|
|
+ ones++;
|
|
+ }
|
|
+ if (ones > RNDTEST_MONOBIT_MINONES &&
|
|
+ ones < RNDTEST_MONOBIT_MAXONES) {
|
|
+ if (rndtest_verbose > 1)
|
|
+ rndtest_report(rsp, 0, "monobit pass (%d < %d < %d)",
|
|
+ RNDTEST_MONOBIT_MINONES, ones,
|
|
+ RNDTEST_MONOBIT_MAXONES);
|
|
+ return (0);
|
|
+ } else {
|
|
+ if (rndtest_verbose)
|
|
+ rndtest_report(rsp, 1,
|
|
+ "monobit failed (%d ones)", ones);
|
|
+ rndstats.rst_monobit++;
|
|
+ return (-1);
|
|
+ }
|
|
+}
|
|
+
|
|
+#define RNDTEST_RUNS_NINTERVAL 6
|
|
+
|
|
+static const struct rndtest_runs_tabs {
|
|
+ u_int16_t min, max;
|
|
+} rndtest_runs_tab[] = {
|
|
+ { 2343, 2657 },
|
|
+ { 1135, 1365 },
|
|
+ { 542, 708 },
|
|
+ { 251, 373 },
|
|
+ { 111, 201 },
|
|
+ { 111, 201 },
|
|
+};
|
|
+
|
|
+static int
|
|
+rndtest_runs(struct rndtest_state *rsp)
|
|
+{
|
|
+ int i, j, ones, zeros, rv = 0;
|
|
+ int onei[RNDTEST_RUNS_NINTERVAL], zeroi[RNDTEST_RUNS_NINTERVAL];
|
|
+ u_int8_t c;
|
|
+
|
|
+ bzero(onei, sizeof(onei));
|
|
+ bzero(zeroi, sizeof(zeroi));
|
|
+ ones = zeros = 0;
|
|
+ for (i = 0; i < RNDTEST_NBYTES; i++) {
|
|
+ c = rsp->rs_buf[i];
|
|
+ for (j = 0; j < 8; j++, c <<= 1) {
|
|
+ if (c & 0x80) {
|
|
+ ones++;
|
|
+ rndtest_runs_record(rsp, zeros, zeroi);
|
|
+ zeros = 0;
|
|
+ } else {
|
|
+ zeros++;
|
|
+ rndtest_runs_record(rsp, ones, onei);
|
|
+ ones = 0;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ rndtest_runs_record(rsp, ones, onei);
|
|
+ rndtest_runs_record(rsp, zeros, zeroi);
|
|
+
|
|
+ rv |= rndtest_runs_check(rsp, 0, zeroi);
|
|
+ rv |= rndtest_runs_check(rsp, 1, onei);
|
|
+
|
|
+ if (rv)
|
|
+ rndstats.rst_runs++;
|
|
+
|
|
+ return (rv);
|
|
+}
|
|
+
|
|
+static void
|
|
+rndtest_runs_record(struct rndtest_state *rsp, int len, int *intrv)
|
|
+{
|
|
+ if (len == 0)
|
|
+ return;
|
|
+ if (len > RNDTEST_RUNS_NINTERVAL)
|
|
+ len = RNDTEST_RUNS_NINTERVAL;
|
|
+ len -= 1;
|
|
+ intrv[len]++;
|
|
+}
|
|
+
|
|
+static int
|
|
+rndtest_runs_check(struct rndtest_state *rsp, int val, int *src)
|
|
+{
|
|
+ int i, rv = 0;
|
|
+
|
|
+ for (i = 0; i < RNDTEST_RUNS_NINTERVAL; i++) {
|
|
+ if (src[i] < rndtest_runs_tab[i].min ||
|
|
+ src[i] > rndtest_runs_tab[i].max) {
|
|
+ rndtest_report(rsp, 1,
|
|
+ "%s interval %d failed (%d, %d-%d)",
|
|
+ val ? "ones" : "zeros",
|
|
+ i + 1, src[i], rndtest_runs_tab[i].min,
|
|
+ rndtest_runs_tab[i].max);
|
|
+ rv = -1;
|
|
+ } else {
|
|
+ rndtest_report(rsp, 0,
|
|
+ "runs pass %s interval %d (%d < %d < %d)",
|
|
+ val ? "ones" : "zeros",
|
|
+ i + 1, rndtest_runs_tab[i].min, src[i],
|
|
+ rndtest_runs_tab[i].max);
|
|
+ }
|
|
+ }
|
|
+ return (rv);
|
|
+}
|
|
+
|
|
+static int
|
|
+rndtest_longruns(struct rndtest_state *rsp)
|
|
+{
|
|
+ int i, j, ones = 0, zeros = 0, maxones = 0, maxzeros = 0;
|
|
+ u_int8_t c;
|
|
+
|
|
+ for (i = 0; i < RNDTEST_NBYTES; i++) {
|
|
+ c = rsp->rs_buf[i];
|
|
+ for (j = 0; j < 8; j++, c <<= 1) {
|
|
+ if (c & 0x80) {
|
|
+ zeros = 0;
|
|
+ ones++;
|
|
+ if (ones > maxones)
|
|
+ maxones = ones;
|
|
+ } else {
|
|
+ ones = 0;
|
|
+ zeros++;
|
|
+ if (zeros > maxzeros)
|
|
+ maxzeros = zeros;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (maxones < 26 && maxzeros < 26) {
|
|
+ rndtest_report(rsp, 0, "longruns pass (%d ones, %d zeros)",
|
|
+ maxones, maxzeros);
|
|
+ return (0);
|
|
+ } else {
|
|
+ rndtest_report(rsp, 1, "longruns fail (%d ones, %d zeros)",
|
|
+ maxones, maxzeros);
|
|
+ rndstats.rst_longruns++;
|
|
+ return (-1);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * chi^2 test over 4 bits: (this is called the poker test in FIPS 140-2,
|
|
+ * but it is really the chi^2 test over 4 bits (the poker test as described
|
|
+ * by Knuth vol 2 is something different, and I take him as authoritative
|
|
+ * on nomenclature over NIST).
|
|
+ */
|
|
+#define RNDTEST_CHI4_K 16
|
|
+#define RNDTEST_CHI4_K_MASK (RNDTEST_CHI4_K - 1)
|
|
+
|
|
+/*
|
|
+ * The unnormalized values are used so that we don't have to worry about
|
|
+ * fractional precision. The "real" value is found by:
|
|
+ * (V - 1562500) * (16 / 5000) = Vn (where V is the unnormalized value)
|
|
+ */
|
|
+#define RNDTEST_CHI4_VMIN 1563181 /* 2.1792 */
|
|
+#define RNDTEST_CHI4_VMAX 1576929 /* 46.1728 */
|
|
+
|
|
+static int
|
|
+rndtest_chi_4(struct rndtest_state *rsp)
|
|
+{
|
|
+ unsigned int freq[RNDTEST_CHI4_K], i, sum;
|
|
+
|
|
+ for (i = 0; i < RNDTEST_CHI4_K; i++)
|
|
+ freq[i] = 0;
|
|
+
|
|
+ /* Get number of occurances of each 4 bit pattern */
|
|
+ for (i = 0; i < RNDTEST_NBYTES; i++) {
|
|
+ freq[(rsp->rs_buf[i] >> 4) & RNDTEST_CHI4_K_MASK]++;
|
|
+ freq[(rsp->rs_buf[i] >> 0) & RNDTEST_CHI4_K_MASK]++;
|
|
+ }
|
|
+
|
|
+ for (i = 0, sum = 0; i < RNDTEST_CHI4_K; i++)
|
|
+ sum += freq[i] * freq[i];
|
|
+
|
|
+ if (sum >= 1563181 && sum <= 1576929) {
|
|
+ rndtest_report(rsp, 0, "chi^2(4): pass (sum %u)", sum);
|
|
+ return (0);
|
|
+ } else {
|
|
+ rndtest_report(rsp, 1, "chi^2(4): failed (sum %u)", sum);
|
|
+ rndstats.rst_chi++;
|
|
+ return (-1);
|
|
+ }
|
|
+}
|
|
+
|
|
+int
|
|
+rndtest_buf(unsigned char *buf)
|
|
+{
|
|
+ struct rndtest_state rsp;
|
|
+
|
|
+ memset(&rsp, 0, sizeof(rsp));
|
|
+ rsp.rs_buf = buf;
|
|
+ rndtest_test(&rsp);
|
|
+ return(rsp.rs_discard);
|
|
+}
|
|
+
|
|
diff --git a/crypto/ocf/rndtest.h b/crypto/ocf/rndtest.h
|
|
new file mode 100644
|
|
index 0000000..e9d8ec8
|
|
--- /dev/null
|
|
+++ b/crypto/ocf/rndtest.h
|
|
@@ -0,0 +1,54 @@
|
|
+/* $FreeBSD: src/sys/dev/rndtest/rndtest.h,v 1.1 2003/03/11 22:54:44 sam Exp $ */
|
|
+/* $OpenBSD$ */
|
|
+
|
|
+/*
|
|
+ * Copyright (c) 2002 Jason L. Wright (jason@thought.net)
|
|
+ * All rights reserved.
|
|
+ *
|
|
+ * Redistribution and use in source and binary forms, with or without
|
|
+ * modification, are permitted provided that the following conditions
|
|
+ * are met:
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
+ * 2. Redistributions in binary form must reproduce the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer in the
|
|
+ * documentation and/or other materials provided with the distribution.
|
|
+ * 3. All advertising materials mentioning features or use of this software
|
|
+ * must display the following acknowledgement:
|
|
+ * This product includes software developed by Jason L. Wright
|
|
+ * 4. The name of the author may not be used to endorse or promote products
|
|
+ * derived from this software without specific prior written permission.
|
|
+ *
|
|
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
|
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
+ * POSSIBILITY OF SUCH DAMAGE.
|
|
+ */
|
|
+
|
|
+
|
|
+/* Some of the tests depend on these values */
|
|
+#define RNDTEST_NBYTES 2500
|
|
+#define RNDTEST_NBITS (8 * RNDTEST_NBYTES)
|
|
+
|
|
+struct rndtest_state {
|
|
+ int rs_discard; /* discard/accept random data */
|
|
+ u_int8_t *rs_buf;
|
|
+};
|
|
+
|
|
+struct rndtest_stats {
|
|
+ u_int32_t rst_discard; /* number of bytes discarded */
|
|
+ u_int32_t rst_tests; /* number of test runs */
|
|
+ u_int32_t rst_monobit; /* monobit test failures */
|
|
+ u_int32_t rst_runs; /* 0/1 runs failures */
|
|
+ u_int32_t rst_longruns; /* longruns failures */
|
|
+ u_int32_t rst_chi; /* chi^2 failures */
|
|
+};
|
|
+
|
|
+extern int rndtest_buf(unsigned char *buf);
|
|
diff --git a/crypto/ocf/uio.h b/crypto/ocf/uio.h
|
|
new file mode 100644
|
|
index 0000000..03a6249
|
|
--- /dev/null
|
|
+++ b/crypto/ocf/uio.h
|
|
@@ -0,0 +1,54 @@
|
|
+#ifndef _OCF_UIO_H_
|
|
+#define _OCF_UIO_H_
|
|
+
|
|
+#include <linux/uio.h>
|
|
+
|
|
+/*
|
|
+ * The linux uio.h doesn't have all we need. To be fully api compatible
|
|
+ * with the BSD cryptodev, we need to keep this around. Perhaps this can
|
|
+ * be moved back into the linux/uio.h
|
|
+ *
|
|
+ * Linux port done by David McCullough <david_mccullough@mcafee.com>
|
|
+ * Copyright (C) 2006-2010 David McCullough
|
|
+ * Copyright (C) 2004-2005 Intel Corporation.
|
|
+ *
|
|
+ * LICENSE TERMS
|
|
+ *
|
|
+ * The free distribution and use of this software in both source and binary
|
|
+ * form is allowed (with or without changes) provided that:
|
|
+ *
|
|
+ * 1. distributions of this source code include the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer;
|
|
+ *
|
|
+ * 2. distributions in binary form include the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer
|
|
+ * in the documentation and/or other associated materials;
|
|
+ *
|
|
+ * 3. the copyright holder's name is not used to endorse products
|
|
+ * built using this software without specific written permission.
|
|
+ *
|
|
+ * ALTERNATIVELY, provided that this notice is retained in full, this product
|
|
+ * may be distributed under the terms of the GNU General Public License (GPL),
|
|
+ * in which case the provisions of the GPL apply INSTEAD OF those given above.
|
|
+ *
|
|
+ * DISCLAIMER
|
|
+ *
|
|
+ * This software is provided 'as is' with no explicit or implied warranties
|
|
+ * in respect of its properties, including, but not limited to, correctness
|
|
+ * and/or fitness for purpose.
|
|
+ * ---------------------------------------------------------------------------
|
|
+ */
|
|
+
|
|
+struct uio {
|
|
+ struct iovec *uio_iov;
|
|
+ int uio_iovcnt;
|
|
+ off_t uio_offset;
|
|
+ int uio_resid;
|
|
+#if 0
|
|
+ enum uio_seg uio_segflg;
|
|
+ enum uio_rw uio_rw;
|
|
+ struct thread *uio_td;
|
|
+#endif
|
|
+};
|
|
+
|
|
+#endif
|
|
diff --git a/drivers/char/random.c b/drivers/char/random.c
|
|
index 6035ab8..8c3acdf 100644
|
|
--- a/drivers/char/random.c
|
|
+++ b/drivers/char/random.c
|
|
@@ -130,6 +130,9 @@
|
|
* void add_interrupt_randomness(int irq);
|
|
* void add_disk_randomness(struct gendisk *disk);
|
|
*
|
|
+ * void random_input_words(__u32 *buf, size_t wordcount, int ent_count)
|
|
+ * int random_input_wait(void);
|
|
+ *
|
|
* add_input_randomness() uses the input layer interrupt timing, as well as
|
|
* the event type information from the hardware.
|
|
*
|
|
@@ -147,6 +150,13 @@
|
|
* seek times do not make for good sources of entropy, as their seek
|
|
* times are usually fairly consistent.
|
|
*
|
|
+ * random_input_words() just provides a raw block of entropy to the input
|
|
+ * pool, such as from a hardware entropy generator.
|
|
+ *
|
|
+ * random_input_wait() suspends the caller until such time as the
|
|
+ * entropy pool falls below the write threshold, and returns a count of how
|
|
+ * much entropy (in bits) is needed to sustain the pool.
|
|
+ *
|
|
* All of these routines try to estimate how many bits of randomness a
|
|
* particular randomness source. They do this by keeping track of the
|
|
* first and second order deltas of the event timings.
|
|
@@ -722,6 +732,63 @@ void add_disk_randomness(struct gendisk *disk)
|
|
}
|
|
#endif
|
|
|
|
+/*
|
|
+ * random_input_words - add bulk entropy to pool
|
|
+ *
|
|
+ * @buf: buffer to add
|
|
+ * @wordcount: number of __u32 words to add
|
|
+ * @ent_count: total amount of entropy (in bits) to credit
|
|
+ *
|
|
+ * this provides bulk input of entropy to the input pool
|
|
+ *
|
|
+ */
|
|
+void random_input_words(__u32 *buf, size_t wordcount, int ent_count)
|
|
+{
|
|
+ mix_pool_bytes(&input_pool, buf, wordcount*4);
|
|
+
|
|
+ credit_entropy_bits(&input_pool, ent_count);
|
|
+
|
|
+ DEBUG_ENT("crediting %d bits => %d\n",
|
|
+ ent_count, input_pool.entropy_count);
|
|
+ /*
|
|
+ * Wake up waiting processes if we have enough
|
|
+ * entropy.
|
|
+ */
|
|
+ if (input_pool.entropy_count >= random_read_wakeup_thresh)
|
|
+ wake_up_interruptible(&random_read_wait);
|
|
+}
|
|
+EXPORT_SYMBOL(random_input_words);
|
|
+
|
|
+/*
|
|
+ * random_input_wait - wait until random needs entropy
|
|
+ *
|
|
+ * this function sleeps until the /dev/random subsystem actually
|
|
+ * needs more entropy, and then return the amount of entropy
|
|
+ * that it would be nice to have added to the system.
|
|
+ */
|
|
+int random_input_wait(void)
|
|
+{
|
|
+ int count;
|
|
+
|
|
+ wait_event_interruptible(random_write_wait,
|
|
+ input_pool.entropy_count < random_write_wakeup_thresh);
|
|
+
|
|
+ count = random_write_wakeup_thresh - input_pool.entropy_count;
|
|
+
|
|
+ /* likely we got woken up due to a signal */
|
|
+ if (count <= 0) count = random_read_wakeup_thresh;
|
|
+
|
|
+ DEBUG_ENT("requesting %d bits from input_wait()er %d<%d\n",
|
|
+ count,
|
|
+ input_pool.entropy_count, random_write_wakeup_thresh);
|
|
+
|
|
+ return count;
|
|
+}
|
|
+EXPORT_SYMBOL(random_input_wait);
|
|
+
|
|
+
|
|
+#define EXTRACT_SIZE 10
|
|
+
|
|
/*********************************************************************
|
|
*
|
|
* Entropy extraction routines
|
|
diff --git a/fs/fcntl.c b/fs/fcntl.c
|
|
index 22764c7..0ffe61f 100644
|
|
--- a/fs/fcntl.c
|
|
+++ b/fs/fcntl.c
|
|
@@ -142,6 +142,7 @@ SYSCALL_DEFINE1(dup, unsigned int, fildes)
|
|
}
|
|
return ret;
|
|
}
|
|
+EXPORT_SYMBOL(sys_dup);
|
|
|
|
#define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | O_DIRECT | O_NOATIME)
|
|
|
|
diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h
|
|
index c41d727..24b73c0 100644
|
|
--- a/include/linux/miscdevice.h
|
|
+++ b/include/linux/miscdevice.h
|
|
@@ -19,6 +19,7 @@
|
|
#define APOLLO_MOUSE_MINOR 7
|
|
#define PC110PAD_MINOR 9
|
|
/*#define ADB_MOUSE_MINOR 10 FIXME OBSOLETE */
|
|
+#define CRYPTODEV_MINOR 70 /* /dev/crypto */
|
|
#define WATCHDOG_MINOR 130 /* Watchdog timer */
|
|
#define TEMP_MINOR 131 /* Temperature Sensor */
|
|
#define RTC_MINOR 135
|
|
diff --git a/include/linux/random.h b/include/linux/random.h
|
|
index 8f74538..0ff31a9 100644
|
|
--- a/include/linux/random.h
|
|
+++ b/include/linux/random.h
|
|
@@ -34,6 +34,30 @@
|
|
/* Clear the entropy pool and associated counters. (Superuser only.) */
|
|
#define RNDCLEARPOOL _IO( 'R', 0x06 )
|
|
|
|
+#ifdef CONFIG_FIPS_RNG
|
|
+
|
|
+/* Size of seed value - equal to AES blocksize */
|
|
+#define AES_BLOCK_SIZE_BYTES 16
|
|
+#define SEED_SIZE_BYTES AES_BLOCK_SIZE_BYTES
|
|
+/* Size of AES key */
|
|
+#define KEY_SIZE_BYTES 16
|
|
+
|
|
+/* ioctl() structure used by FIPS 140-2 Tests */
|
|
+struct rand_fips_test {
|
|
+ unsigned char key[KEY_SIZE_BYTES]; /* Input */
|
|
+ unsigned char datetime[SEED_SIZE_BYTES]; /* Input */
|
|
+ unsigned char seed[SEED_SIZE_BYTES]; /* Input */
|
|
+ unsigned char result[SEED_SIZE_BYTES]; /* Output */
|
|
+};
|
|
+
|
|
+/* FIPS 140-2 RNG Variable Seed Test. (Superuser only.) */
|
|
+#define RNDFIPSVST _IOWR('R', 0x10, struct rand_fips_test)
|
|
+
|
|
+/* FIPS 140-2 RNG Monte Carlo Test. (Superuser only.) */
|
|
+#define RNDFIPSMCT _IOWR('R', 0x11, struct rand_fips_test)
|
|
+
|
|
+#endif /* #ifdef CONFIG_FIPS_RNG */
|
|
+
|
|
struct rand_pool_info {
|
|
int entropy_count;
|
|
int buf_size;
|
|
@@ -54,6 +78,10 @@ extern void add_input_randomness(unsigned int type, unsigned int code,
|
|
unsigned int value);
|
|
extern void add_interrupt_randomness(int irq);
|
|
|
|
+extern void random_input_words(__u32 *buf, size_t wordcount, int ent_count);
|
|
+extern int random_input_wait(void);
|
|
+#define HAS_RANDOM_INPUT_WAIT 1
|
|
+
|
|
extern void get_random_bytes(void *buf, int nbytes);
|
|
void generate_random_uuid(unsigned char uuid_out[16]);
|
|
|
|
diff --git a/kernel/pid.c b/kernel/pid.c
|
|
index fa5f722..2bf49fd 100644
|
|
--- a/kernel/pid.c
|
|
+++ b/kernel/pid.c
|
|
@@ -428,6 +428,7 @@ struct task_struct *find_task_by_vpid(pid_t vnr)
|
|
{
|
|
return find_task_by_pid_ns(vnr, current->nsproxy->pid_ns);
|
|
}
|
|
+EXPORT_SYMBOL(find_task_by_vpid);
|
|
|
|
struct pid *get_task_pid(struct task_struct *task, enum pid_type type)
|
|
{
|
|
--
|
|
1.7.0.4
|