[linux-yocto] [PATCH 1/1] Update cryptodev to latest version
zhe.he at windriver.com
zhe.he at windriver.com
Mon Sep 22 22:14:36 PDT 2014
From: He Zhe <zhe.he at windriver.com>
Update cryptodev to latest version
Signed-off-by: He Zhe <zhe.he at windriver.com>
---
.../crypto-import-cryptodev-linux-1.6.patch | 4074 ++++++++++++++------
1 file changed, 2873 insertions(+), 1201 deletions(-)
diff --git a/meta/cfg/kernel-cache/features/cryptodev/crypto-import-cryptodev-linux-1.6.patch b/meta/cfg/kernel-cache/features/cryptodev/crypto-import-cryptodev-linux-1.6.patch
index dddddf2..18980f0 100644
--- a/meta/cfg/kernel-cache/features/cryptodev/crypto-import-cryptodev-linux-1.6.patch
+++ b/meta/cfg/kernel-cache/features/cryptodev/crypto-import-cryptodev-linux-1.6.patch
@@ -1,42 +1,59 @@
-From b65a074235f7a8f2a7e67554c25a968b210cb06a Mon Sep 17 00:00:00 2001
-From: Bruce Ashfield <bruce.ashfield at windriver.com>
-Date: Thu, 22 Aug 2013 00:20:38 -0400
-Subject: [PATCH 1/2] crypto: import cryptodev-linux 1.6
+From db8070c196429e6bc89b90643aab01801d1bebfd Mon Sep 17 00:00:00 2001
+From: He Zhe <zhe.he at windriver.com>
+Date: Mon, 22 Sep 2014 14:49:29 +0800
+Subject: [PATCH] Add latest cryptodev 1.6
-Adding crtypdev 1.6 support from:
+Add cryptodev 1.6
+Taken from:
+https://github.com/cryptodev-linux/cryptodev-linux.git
+6aa62a2c320b04f55fdfe0ed015c3d9b48997239
- http://download.gna.org/cryptodev-linux/cryptodev-linux-1.6.tar.gz
-
-to the kernel source tree as an integrated (versus out of tree) component.
-
-Signed-off-by: Bruce Ashfield <bruce.ashfield at windriver.com>
+Signed-off-by: He Zhe <zhe.he at windriver.com>
---
crypto/cryptodev/AUTHORS | 19 +
crypto/cryptodev/COPYING | 339 +++++++++
- crypto/cryptodev/Makefile | 53 ++
- crypto/cryptodev/NEWS | 153 ++++
- crypto/cryptodev/README | 21 +
- crypto/cryptodev/authenc.c | 742 +++++++++++++++++++
- crypto/cryptodev/cryptlib.c | 377 ++++++++++
- crypto/cryptodev/cryptlib.h | 90 +++
- crypto/cryptodev/crypto/cryptodev.h | 292 ++++++++
- crypto/cryptodev/cryptodev_int.h | 134 ++++
- crypto/cryptodev/ioctl.c | 1136 +++++++++++++++++++++++++++++
- crypto/cryptodev/main.c | 257 +++++++
+ crypto/cryptodev/INSTALL | 32 +
+ crypto/cryptodev/Makefile | 72 ++
+ crypto/cryptodev/NEWS | 154 ++++
+ crypto/cryptodev/README | 40 +
+ crypto/cryptodev/authenc.c | 758 +++++++++++++++++++
+ crypto/cryptodev/cryptlib.c | 441 +++++++++++
+ crypto/cryptodev/cryptlib.h | 93 +++
+ crypto/cryptodev/crypto/cryptodev.h | 292 +++++++
+ crypto/cryptodev/cryptodev_int.h | 145 ++++
+ crypto/cryptodev/examples/aes-gcm.c | 139 ++++
+ crypto/cryptodev/examples/aes-gcm.h | 28 +
+ crypto/cryptodev/examples/aes-sha1.c | 139 ++++
+ crypto/cryptodev/examples/aes-sha1.h | 31 +
+ crypto/cryptodev/examples/aes.c | 242 ++++++
+ crypto/cryptodev/examples/aes.h | 19 +
+ crypto/cryptodev/examples/sha.c | 137 ++++
+ crypto/cryptodev/examples/sha.h | 16 +
+ crypto/cryptodev/ioctl.c | 1169 +++++++++++++++++++++++++++++
+ crypto/cryptodev/lib/Makefile | 15 +
+ crypto/cryptodev/lib/benchmark.c | 88 +++
+ crypto/cryptodev/lib/benchmark.h | 18 +
+ crypto/cryptodev/lib/combo.c | 171 +++++
+ crypto/cryptodev/lib/hash.c | 161 ++++
+ crypto/cryptodev/lib/hash.h | 20 +
+ crypto/cryptodev/lib/main.c | 28 +
+ crypto/cryptodev/lib/threshold.c | 61 ++
+ crypto/cryptodev/lib/threshold.h | 10 +
+ crypto/cryptodev/main.c | 267 +++++++
crypto/cryptodev/tests/Makefile | 35 +
crypto/cryptodev/tests/async_cipher.c | 338 +++++++++
crypto/cryptodev/tests/async_hmac.c | 303 ++++++++
crypto/cryptodev/tests/async_speed.c | 225 ++++++
- crypto/cryptodev/tests/cipher-aead-srtp.c | 572 +++++++++++++++
- crypto/cryptodev/tests/cipher-aead.c | 574 +++++++++++++++
- crypto/cryptodev/tests/cipher-gcm.c | 529 ++++++++++++++
- crypto/cryptodev/tests/cipher.c | 326 +++++++++
+ crypto/cryptodev/tests/cipher-aead-srtp.c | 572 ++++++++++++++
+ crypto/cryptodev/tests/cipher-aead.c | 574 ++++++++++++++
+ crypto/cryptodev/tests/cipher-gcm.c | 529 +++++++++++++
+ crypto/cryptodev/tests/cipher.c | 326 ++++++++
crypto/cryptodev/tests/cipher_comp.c | 159 ++++
crypto/cryptodev/tests/fullspeed.c | 184 +++++
crypto/cryptodev/tests/hash_comp.c | 150 ++++
- crypto/cryptodev/tests/hashcrypt_speed.c | 206 ++++++
+ crypto/cryptodev/tests/hashcrypt_speed.c | 206 +++++
crypto/cryptodev/tests/hmac.c | 338 +++++++++
- crypto/cryptodev/tests/hmac_comp.c | 180 +++++
+ crypto/cryptodev/tests/hmac_comp.c | 187 +++++
crypto/cryptodev/tests/openssl_wrapper.c | 267 +++++++
crypto/cryptodev/tests/openssl_wrapper.h | 6 +
crypto/cryptodev/tests/sha_speed.c | 198 +++++
@@ -45,11 +62,12 @@ Signed-off-by: Bruce Ashfield <bruce.ashfield at windriver.com>
crypto/cryptodev/util.c | 80 ++
crypto/cryptodev/util.h | 2 +
crypto/cryptodev/version.h | 1 +
- crypto/cryptodev/zc.c | 217 ++++++
+ crypto/cryptodev/zc.c | 208 +++++
crypto/cryptodev/zc.h | 27 +
- 36 files changed, 8799 insertions(+)
+ 54 files changed, 10328 insertions(+)
create mode 100644 crypto/cryptodev/AUTHORS
create mode 100644 crypto/cryptodev/COPYING
+ create mode 100644 crypto/cryptodev/INSTALL
create mode 100644 crypto/cryptodev/Makefile
create mode 100644 crypto/cryptodev/NEWS
create mode 100644 crypto/cryptodev/README
@@ -58,7 +76,24 @@ Signed-off-by: Bruce Ashfield <bruce.ashfield at windriver.com>
create mode 100644 crypto/cryptodev/cryptlib.h
create mode 100644 crypto/cryptodev/crypto/cryptodev.h
create mode 100644 crypto/cryptodev/cryptodev_int.h
+ create mode 100644 crypto/cryptodev/examples/aes-gcm.c
+ create mode 100644 crypto/cryptodev/examples/aes-gcm.h
+ create mode 100644 crypto/cryptodev/examples/aes-sha1.c
+ create mode 100644 crypto/cryptodev/examples/aes-sha1.h
+ create mode 100644 crypto/cryptodev/examples/aes.c
+ create mode 100644 crypto/cryptodev/examples/aes.h
+ create mode 100644 crypto/cryptodev/examples/sha.c
+ create mode 100644 crypto/cryptodev/examples/sha.h
create mode 100644 crypto/cryptodev/ioctl.c
+ create mode 100644 crypto/cryptodev/lib/Makefile
+ create mode 100644 crypto/cryptodev/lib/benchmark.c
+ create mode 100644 crypto/cryptodev/lib/benchmark.h
+ create mode 100644 crypto/cryptodev/lib/combo.c
+ create mode 100644 crypto/cryptodev/lib/hash.c
+ create mode 100644 crypto/cryptodev/lib/hash.h
+ create mode 100644 crypto/cryptodev/lib/main.c
+ create mode 100644 crypto/cryptodev/lib/threshold.c
+ create mode 100644 crypto/cryptodev/lib/threshold.h
create mode 100644 crypto/cryptodev/main.c
create mode 100644 crypto/cryptodev/tests/Makefile
create mode 100644 crypto/cryptodev/tests/async_cipher.c
@@ -455,12 +490,50 @@ index 0000000..d159169
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
+diff --git a/crypto/cryptodev/INSTALL b/crypto/cryptodev/INSTALL
+new file mode 100644
+index 0000000..2754c59
+--- /dev/null
++++ b/crypto/cryptodev/INSTALL
+@@ -0,0 +1,32 @@
++=== Installation instructions ===
++
++Simply run:
++$ make
++# make install
++
++The first command compiles the code and generates the kernel module
++and the latter installs the header files and the kernel module.
++
++After that you should set your system to load the kernel module on system
++load. In most systems this can be done as:
++# echo "cryptodev" >>/etc/modules
++
++or in systemd-enabled systems:
++# echo "cryptodev" > /etc/modules-load.d/cryptodev.conf
++
++=== Testing installation ===
++
++* cryptodev-linux:
++Check whether cryptodev-linux is operating as expected using the following
++command.
++$ make check
++
++* OpenSSL:
++run the following commands prior and after installation and compare.
++$ openssl speed -evp aes-128-cbc
++$ openssl speed -evp sha1
++
++* GnuTLS 3.x:
++run the following command prior and after installation and compare.
++$ gnutls-cli --benchmark-ciphers
++
diff --git a/crypto/cryptodev/Makefile b/crypto/cryptodev/Makefile
new file mode 100644
-index 0000000..d66ef26
+index 0000000..c657c70
--- /dev/null
+++ b/crypto/cryptodev/Makefile
-@@ -0,0 +1,53 @@
+@@ -0,0 +1,72 @@
+#
+# Since version 1.6 the asynchronous mode has been
+# disabled by default. To re-enable it uncomment the
@@ -468,16 +541,24 @@ index 0000000..d66ef26
+#
+CRYPTODEV_CFLAGS ?= #-DENABLE_ASYNC
+KBUILD_CFLAGS += -I$(src) $(CRYPTODEV_CFLAGS)
-+KERNEL_DIR = /lib/modules/$(shell uname -r)/build
++KERNEL_DIR ?= /lib/modules/$(shell uname -r)/build
+VERSION = 1.6
-+PREFIX =
++PREFIX ?=
+
+cryptodev-objs = ioctl.o main.o cryptlib.o authenc.o zc.o util.o
+
+obj-m += cryptodev.o
+
++KERNEL_MAKE_OPTS := -C ${KERNEL_DIR} SUBDIRS=`pwd`
++ifneq (${ARCH},)
++KERNEL_MAKE_OPTS += ARCH=${ARCH}
++endif
++ifneq (${CROSS_COMPILE},)
++KERNEL_MAKE_OPTS += CROSS_COMPILE=${CROSS_COMPILE}
++endif
++
+build: version.h
-+ make -C $(KERNEL_DIR) SUBDIRS=`pwd` modules
++ make ${KERNEL_MAKE_OPTS} modules
+
+version.h: Makefile
+ @echo "#define VERSION \"$(VERSION)\"" > version.h
@@ -497,6 +578,17 @@ index 0000000..d66ef26
+check:
+ CFLAGS=$(CRYPTODEV_CFLAGS) KERNEL_DIR=$(KERNEL_DIR) make -C tests check
+
++CPOPTS =
++ifneq (${SHOW_TYPES},)
++CPOPTS += --show-types
++endif
++ifneq (${IGNORE_TYPES},)
++CPOPTS += --ignore ${IGNORE_TYPES}
++endif
++
++checkpatch:
++ $(KERNEL_DIR)/scripts/checkpatch.pl ${CPOPTS} --file *.c *.h
++
+FILEBASE = cryptodev-linux-$(VERSION)
+TMPDIR ?= /tmp
+OUTPUT = $(FILEBASE).tar.gz
@@ -506,7 +598,7 @@ index 0000000..d66ef26
+ @rm -f *.tar.gz
+ @mkdir $(TMPDIR)/$(FILEBASE)
+ @cp -ar crypto extras tests examples Makefile *.c *.h README NEWS \
-+ AUTHORS COPYING $(TMPDIR)/$(FILEBASE)
++ INSTALL AUTHORS COPYING $(TMPDIR)/$(FILEBASE)
+ @rm -rf $(TMPDIR)/$(FILEBASE)/.git* $(TMPDIR)/$(FILEBASE)/releases $(TMPDIR)/$(FILEBASE)/scripts
+ @tar -C /tmp -czf ./$(OUTPUT) $(FILEBASE)
+ @rm -rf $(TMPDIR)/$(FILEBASE)
@@ -516,21 +608,32 @@ index 0000000..d66ef26
+ @mv $(OUTPUT) $(OUTPUT).sig releases/
diff --git a/crypto/cryptodev/NEWS b/crypto/cryptodev/NEWS
new file mode 100644
-index 0000000..bd1b666
+index 0000000..a0a7d38
--- /dev/null
+++ b/crypto/cryptodev/NEWS
-@@ -0,0 +1,153 @@
+@@ -0,0 +1,154 @@
++Version 1.7 (unreleased)
++
++* Added support for composite AEAD keys by Cristian Stoica.
++
++* Added support for sysctl to modify verbosity by Nikolaos Tsakalakis.
++
++* Several bugfixes by Cristian Stoica.
++
++* When a driver requires aligned data but unaligned are provided, then
++ zero copy is disabled to prevent driver failing to encrypt.
++
++
+Version 1.6 (released 2013-03-20)
+
+* Added modules_install target in Makefile
+
+* Added SHA224. Patch by Yashpal Dutta.
+
-+* Asynchronous operations will not be scheduled if zero
-+copy is disabled.
++* Asynchronous operations will not be scheduled if zero copy is disabled.
+
-+* Asynchronous operations are disabled by default, unless
-+-DENABLE_ASYNC is enabled on Makefile.
++* Asynchronous operations are disabled by default, unless -DENABLE_ASYNC
++ is enabled on Makefile.
+
+
+Version 1.5 (released 2012-08-04)
@@ -539,8 +642,8 @@ index 0000000..bd1b666
+
+* Simplifications in memory locking. Patch by Phil Sutter.
+
-+* Allow empty plaintext and authenticated data in AEAD
-+ciphers. Patch by Jaren Johnston.
++* Allow empty plaintext and authenticated data in AEAD ciphers.
++ Patch by Jaren Johnston.
+
+
+Version 1.4 (released 2012-03-15)
@@ -550,11 +653,10 @@ index 0000000..bd1b666
+
+Version 1.3 (released 2012-02-29)
+
-+* Return EBADMSG instead of ECANCELED on tag verification
-+failure in authenc modes.
++* Return EBADMSG instead of ECANCELED on tag verification failure in
++ authenc modes.
+
-+* COP_FLAG_RESET can be combined with COP_FLAG_UPDATE for
-+efficiency.
++* COP_FLAG_RESET can be combined with COP_FLAG_UPDATE for efficiency.
+
+* Added more test cases.
+
@@ -563,14 +665,12 @@ index 0000000..bd1b666
+
+Version 1.2 (released 2012-02-24)
+
-+* In kernels that do not distinguish between hw
-+accelerated ciphers or not set the SIOP_FLAG_KERNEL_DRIVER_ONLY
-+flag based on driver name.
++* In kernels that do not distinguish between hw accelerated ciphers or
++ not set the SIOP_FLAG_KERNEL_DRIVER_ONLY flag based on driver name.
+
+* camelia was renamed to camellia.
+
-+* Added COP_FLAG_RESET to allow resetting the state
-+in multi-update.
++* Added COP_FLAG_RESET to allow resetting the state in multi-update.
+
+* Corrected issue in ARM processors with mv_cesa.
+
@@ -581,20 +681,19 @@ index 0000000..bd1b666
+
+* Defined HASH_MAX_LEN in cryptodev.h
+
-+* CIOCGSESSINFO ioctl() sets the SIOP_FLAG_KERNEL_DRIVER_ONLY
-+flag if the driver is only available through kernel
-+driver (and is not just software cipher).
++* CIOCGSESSINFO ioctl() sets the SIOP_FLAG_KERNEL_DRIVER_ONLY flag if the
++ driver is only available through kernel driver (and is not just software
++ cipher).
+
-+* Added new encryption ioctl, CIOCAUTHCRYPT, which
-+combines authentication and encryption. Operates
-+in AEAD, TLS and SRTP modes (the API might change
-+in later versions).
++* Added new encryption ioctl, CIOCAUTHCRYPT, which combines authentication
++ and encryption. Operates in AEAD, TLS and SRTP modes (the API might change
++ in later versions).
+
+
+Version 1.0 (released 2011-04-12)
+
-+* Several fixes in the included examples. Based on
-+patches by Vladimir Zapolskiy.
++* Several fixes in the included examples. Based on patches by Vladimir
++ Zapolskiy.
+
+
+Version 0.9 (released 2011-02-11)
@@ -603,15 +702,13 @@ index 0000000..bd1b666
+ - sha_speed does performance testing of SHA1 and SHA256
+ - hashcrypt_speed additionally encrypts with AES128 and AES256
+
-+* Allow updating the IV in userspace via the COP_FLAG_WRITE_IV
-+flag.
++* Allow updating the IV in userspace via the COP_FLAG_WRITE_IV flag.
+
+* Export the alignmask in an OCF compatible way.
+
+* Fix for kernel crash on passing incorrect session ID.
+
-+* Added CIOCGSESSINFO to export additional information
-+for each session.
++* Added CIOCGSESSINFO to export additional information for each session.
+
+
+Version 0.8 (released 2010-11-06)
@@ -626,39 +723,35 @@ index 0000000..bd1b666
+
+Version 0.7 (released 2010-10-08)
+
-+* Added COP_FLAG_FINAL to make multi-update
-+more efficient.
++* Added COP_FLAG_FINAL to make multi-update more efficient.
+
-+* Added CRIOGET_NOT_NEEDED definition to allow
-+users of the API to distinguish from the bare
-+OpenBSD API that requires the CRIOGET.
++* Added CRIOGET_NOT_NEEDED definition to allow users of the API to
++ distinguish from the bare OpenBSD API that requires the CRIOGET.
+
+
+Version 0.6 (released 2010-09-16)
+
-+* multi-update support for hash calculation using
-+the new flag COP_FLAG_UPDATE.
++* multi-update support for hash calculation using the new flag
++ COP_FLAG_UPDATE.
+
+* Relicensed under GPLv2.
+
+* Added AES-CTR.
+
-+* Corrected fallback to non-zero copy when referenced
-+pages were not writable.
++* Corrected fallback to non-zero copy when referenced pages were
++ not writable.
+
+
+Version 0.5 (released 2010-07-06)
+
+* Corrected issue with zero copy on multiple pages.
+
-+* Fallback to normal operation if user pages cannot be
-+mapped.
++* Fallback to normal operation if user pages cannot be mapped.
+
+
+Version 0.4 (released 2010-07-03)
+
-+* Internal engine supports operations with zero copy from
-+user space.
++* Internal engine supports operations with zero copy from user space.
+
+
+Version 0.3 (released 2010-06-19)
@@ -669,19 +762,19 @@ index 0000000..bd1b666
+Version 0.2 (released 2010-06-18)
+
+* Added compat_ioctl() to allow working on systems where userspace is 32bits
-+and kernel is operating in 64bit mode (Phil Sutter)
++ and kernel is operating in 64bit mode (Phil Sutter)
+
+* Added several sanity checks to input.
+
diff --git a/crypto/cryptodev/README b/crypto/cryptodev/README
new file mode 100644
-index 0000000..1176e70
+index 0000000..eb19204
--- /dev/null
+++ b/crypto/cryptodev/README
-@@ -0,0 +1,21 @@
+@@ -0,0 +1,40 @@
+This is a /dev/crypto device driver, equivalent to those in OpenBSD or
+FreeBSD. The main idea is to access of existing ciphers in kernel space
-+from userspace, thus enabling re-use of a hardware implementation of a
++from userspace, thus enabling the re-use of a hardware implementation of a
+cipher.
+
+For questions and suggestions please use the mailing lists at:
@@ -697,15 +790,34 @@ index 0000000..1176e70
+
+* OpenSSL:
+
-+The current releases of openssl support /dev/crypto by replacing
-+eng_cryptodev.c with the version available in the extras subdirectory.
-+In order to compile use the -DHAVE_CRYPTODEV -DUSE_CRYPTODEV_DIGESTS flags.
++Note that OpenSSL's cryptodev implementation is outdated, and there
++are issues with it. For that we recommend to use the patches
++below, that we have provided to the openssl project.
++
++http://rt.openssl.org/Ticket/Display.html?id=2770&user=guest&pass=guest
++
++After applying the patches you can add cryptodev support by using the
++-DHAVE_CRYPTODEV and -DUSE_CRYPTODEV_DIGESTS flags during compilation.
++Note that the latter flag (digests) may induce a performance penalty
++in some systems.
++
++
++=== Modifying and viewing verbosity at runtime ===
++
++For debugging often the verbosity of the driver needs to be adjusted.
++The sysctl tool can be used for that.
++
++# sysctl ioctl.cryptodev_verbosity
++ioctl.cryptodev_verbosity = 0
++
++# sysctl ioctl.cryptodev_verbosity=3
++ioctl.cryptodev_verbosity = 3
diff --git a/crypto/cryptodev/authenc.c b/crypto/cryptodev/authenc.c
new file mode 100644
-index 0000000..8bff677
+index 0000000..1bd7377
--- /dev/null
+++ b/crypto/cryptodev/authenc.c
-@@ -0,0 +1,742 @@
+@@ -0,0 +1,758 @@
+/*
+ * Driver for /dev/crypto device (aka CryptoDev)
+ *
@@ -771,12 +883,12 @@ index 0000000..8bff677
+
+ if (ses->alignmask) {
+ if (!IS_ALIGNED((unsigned long)caop->dst, ses->alignmask))
-+ dprintk(2, KERN_WARNING, "careful - source address %lx is not %d byte aligned\n",
-+ (unsigned long)caop->dst, ses->alignmask + 1);
++ dwarning(2, "careful - source address %p is not %d byte aligned",
++ caop->dst, ses->alignmask + 1);
+ }
+
+ if (kcaop->dst_len == 0) {
-+ dprintk(1, KERN_WARNING, "Destination length cannot be zero\n");
++ dwarning(1, "Destination length cannot be zero");
+ return -EINVAL;
+ }
+
@@ -792,8 +904,7 @@ index 0000000..8bff677
+ rc = __get_userbuf(caop->dst, kcaop->dst_len, 1, pagecount,
+ ses->pages, ses->sg, kcaop->task, kcaop->mm);
+ if (unlikely(rc)) {
-+ dprintk(1, KERN_ERR,
-+ "failed to get user pages for data input\n");
++ derr(1, "failed to get user pages for data input");
+ return -EINVAL;
+ }
+
@@ -820,21 +931,21 @@ index 0000000..8bff677
+ int rc;
+
+ if (caop->dst == NULL && caop->auth_src == NULL) {
-+ dprintk(1, KERN_ERR, "dst and auth_src cannot be both null\n");
++ derr(1, "dst and auth_src cannot be both null");
+ return -EINVAL;
+ }
+
+ if (ses->alignmask) {
+ if (!IS_ALIGNED((unsigned long)caop->dst, ses->alignmask))
-+ dprintk(2, KERN_WARNING, "careful - source address %lx is not %d byte aligned\n",
-+ (unsigned long)caop->dst, ses->alignmask + 1);
++ dwarning(2, "careful - source address %p is not %d byte aligned",
++ caop->dst, ses->alignmask + 1);
+ if (!IS_ALIGNED((unsigned long)caop->auth_src, ses->alignmask))
-+ dprintk(2, KERN_WARNING, "careful - source address %lx is not %d byte aligned\n",
-+ (unsigned long)caop->auth_src, ses->alignmask + 1);
++ dwarning(2, "careful - source address %p is not %d byte aligned",
++ caop->auth_src, ses->alignmask + 1);
+ }
+
+ if (unlikely(kcaop->dst_len == 0 || caop->auth_len == 0)) {
-+ dprintk(1, KERN_WARNING, "Destination length cannot be zero\n");
++ dwarning(1, "Destination length cannot be zero");
+ return -EINVAL;
+ }
+
@@ -844,7 +955,7 @@ index 0000000..8bff677
+ auth_pagecount = PAGECOUNT(caop->auth_src, caop->auth_len);
+ diff = (int)(caop->src - caop->auth_src);
+ if (diff > MAX_SRTP_AUTH_DATA_DIFF || diff < 0) {
-+ dprintk(1, KERN_WARNING, "auth_src must overlap with src (diff: %d).\n", diff);
++ dwarning(1, "auth_src must overlap with src (diff: %d).", diff);
+ return -EINVAL;
+ }
+
@@ -852,15 +963,14 @@ index 0000000..8bff677
+
+ rc = adjust_sg_array(ses, pagecount*2); /* double pages to have pages for dst(=auth_src) */
+ if (rc) {
-+ dprintk(1, KERN_ERR, "cannot adjust sg array\n");
++ derr(1, "cannot adjust sg array");
+ return rc;
+ }
+
+ rc = __get_userbuf(caop->auth_src, caop->auth_len, 1, auth_pagecount,
+ ses->pages, ses->sg, kcaop->task, kcaop->mm);
+ if (unlikely(rc)) {
-+ dprintk(1, KERN_ERR,
-+ "failed to get user pages for data input\n");
++ derr(1, "failed to get user pages for data input");
+ return -EINVAL;
+ }
+
@@ -875,14 +985,49 @@ index 0000000..8bff677
+ (*dst_sg) = sg_advance(*dst_sg, diff);
+ if (*dst_sg == NULL) {
+ release_user_pages(ses);
-+ dprintk(1, KERN_ERR,
-+ "failed to get enough pages for auth data\n");
++ derr(1, "failed to get enough pages for auth data");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
++/*
++ * Return tag (digest) length for authenticated encryption
++ * If the cipher and digest are separate, hdata.init is set - just return
++ * digest length. Otherwise return digest length for aead ciphers
++ */
++static int cryptodev_get_tag_len(struct csession *ses_ptr)
++{
++ if (ses_ptr->hdata.init)
++ return ses_ptr->hdata.digestsize;
++ else
++ return cryptodev_cipher_get_tag_size(&ses_ptr->cdata);
++}
++
++/*
++ * Calculate destination buffer length for authenticated encryption. The
++ * expectation is that user-space code allocates exactly the same space for
++ * destination buffer before calling cryptodev. The result is cipher-dependent.
++ */
++static int cryptodev_get_dst_len(struct crypt_auth_op *caop, struct csession *ses_ptr)
++{
++ int dst_len = caop->len;
++ if (caop->op == COP_DECRYPT)
++ return dst_len;
++
++ dst_len += caop->tag_len;
++
++ /* for TLS always add some padding so the total length is rounded to
++ * cipher block size */
++ if (caop->flags & COP_FLAG_AEAD_TLS_TYPE) {
++ int bs = ses_ptr->cdata.blocksize;
++ dst_len += bs - (dst_len % bs);
++ }
++
++ return dst_len;
++}
++
+static int fill_kcaop_from_caop(struct kernel_crypt_auth_op *kcaop, struct fcrypt *fcr)
+{
+ struct crypt_auth_op *caop = &kcaop->caop;
@@ -892,38 +1037,31 @@ index 0000000..8bff677
+ /* this also enters ses_ptr->sem */
+ ses_ptr = crypto_get_session_by_sid(fcr, caop->ses);
+ if (unlikely(!ses_ptr)) {
-+ dprintk(1, KERN_ERR, "invalid session ID=0x%08X\n", caop->ses);
++ derr(1, "invalid session ID=0x%08X", caop->ses);
+ return -EINVAL;
+ }
+
+ if (caop->flags & COP_FLAG_AEAD_TLS_TYPE || caop->flags & COP_FLAG_AEAD_SRTP_TYPE) {
+ if (caop->src != caop->dst) {
-+ dprintk(1, KERN_ERR,
-+ "Non-inplace encryption and decryption is not efficient and not implemented\n");
++ derr(1, "Non-inplace encryption and decryption is not efficient and not implemented");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ }
+
+ if (caop->tag_len == 0)
-+ caop->tag_len = ses_ptr->hdata.digestsize;
++ caop->tag_len = cryptodev_get_tag_len(ses_ptr);
+
+ kcaop->ivlen = caop->iv ? ses_ptr->cdata.ivsize : 0;
-+
-+ if (caop->flags & COP_FLAG_AEAD_TLS_TYPE)
-+ kcaop->dst_len = caop->len + ses_ptr->cdata.blocksize /* pad */ + caop->tag_len;
-+ else
-+ kcaop->dst_len = caop->len;
-+
++ kcaop->dst_len = cryptodev_get_dst_len(caop, ses_ptr);
+ kcaop->task = current;
+ kcaop->mm = current->mm;
+
+ if (caop->iv) {
+ ret = copy_from_user(kcaop->iv, caop->iv, kcaop->ivlen);
+ if (unlikely(ret)) {
-+ dprintk(1, KERN_ERR,
-+ "error copying IV (%d bytes), copy_from_user returned %d for address %lx\n",
-+ kcaop->ivlen, ret, (unsigned long)caop->iv);
++ derr(1, "error copying IV (%d bytes), copy_from_user returned %d for address %p",
++ kcaop->ivlen, ret, caop->iv);
+ ret = -EFAULT;
+ goto out_unlock;
+ }
@@ -947,7 +1085,7 @@ index 0000000..8bff677
+ ret = copy_to_user(kcaop->caop.iv,
+ kcaop->iv, kcaop->ivlen);
+ if (unlikely(ret)) {
-+ dprintk(1, KERN_ERR, "Error in copying to userspace\n");
++ derr(1, "Error in copying to userspace");
+ return -EFAULT;
+ }
+ }
@@ -959,7 +1097,7 @@ index 0000000..8bff677
+ struct fcrypt *fcr, void __user *arg)
+{
+ if (unlikely(copy_from_user(&kcaop->caop, arg, sizeof(kcaop->caop)))) {
-+ dprintk(1, KERN_ERR, "Error in copying from userspace\n");
++ derr(1, "Error in copying from userspace");
+ return -EFAULT;
+ }
+
@@ -973,61 +1111,61 @@ index 0000000..8bff677
+
+ ret = fill_caop_from_kcaop(kcaop, fcr);
+ if (unlikely(ret)) {
-+ dprintk(1, KERN_ERR, "fill_caop_from_kcaop\n");
++ derr(1, "fill_caop_from_kcaop");
+ return ret;
+ }
+
+ if (unlikely(copy_to_user(arg, &kcaop->caop, sizeof(kcaop->caop)))) {
-+ dprintk(1, KERN_ERR, "Error in copying to userspace\n");
++ derr(1, "Error in copying to userspace");
+ return -EFAULT;
+ }
+ return 0;
+}
+
-+static void copy_tls_hash( struct scatterlist *dst_sg, int len, void* hash, int hash_len)
++static void copy_tls_hash(struct scatterlist *dst_sg, int len, void *hash, int hash_len)
+{
+ scatterwalk_map_and_copy(hash, dst_sg, len, hash_len, 1);
+}
+
-+static void read_tls_hash( struct scatterlist *dst_sg, int len, void* hash, int hash_len)
++static void read_tls_hash(struct scatterlist *dst_sg, int len, void *hash, int hash_len)
+{
-+ scatterwalk_map_and_copy(hash, dst_sg, len-hash_len, hash_len, 0);
++ scatterwalk_map_and_copy(hash, dst_sg, len - hash_len, hash_len, 0);
+}
+
-+static int pad_record( struct scatterlist *dst_sg, int len, int block_size)
++static int pad_record(struct scatterlist *dst_sg, int len, int block_size)
+{
+ uint8_t pad[block_size];
+ int pad_size = block_size - (len % block_size);
+
-+ memset(pad, pad_size-1, pad_size);
++ memset(pad, pad_size - 1, pad_size);
+
+ scatterwalk_map_and_copy(pad, dst_sg, len, pad_size, 1);
+
+ return pad_size;
+}
+
-+static int verify_tls_record_pad( struct scatterlist *dst_sg, int len, int block_size)
++static int verify_tls_record_pad(struct scatterlist *dst_sg, int len, int block_size)
+{
+ uint8_t pad[256]; /* the maximum allowed */
+ uint8_t pad_size;
+ int i;
+
-+ scatterwalk_map_and_copy(&pad_size, dst_sg, len-1, 1, 0);
++ scatterwalk_map_and_copy(&pad_size, dst_sg, len - 1, 1, 0);
+
-+ if (pad_size+1 > len) {
-+ dprintk(1, KERN_ERR, "Pad size: %d\n", pad_size);
++ if (pad_size + 1 > len) {
++ derr(1, "Pad size: %d", pad_size);
+ return -EBADMSG;
+ }
+
-+ scatterwalk_map_and_copy(pad, dst_sg, len-pad_size-1, pad_size+1, 0);
++ scatterwalk_map_and_copy(pad, dst_sg, len - pad_size - 1, pad_size + 1, 0);
+
-+ for (i=0;i<pad_size;i++)
++ for (i = 0; i < pad_size; i++)
+ if (pad[i] != pad_size) {
-+ dprintk(1, KERN_ERR, "Pad size: %d, pad: %d\n", pad_size, (int)pad[i]);
++ derr(1, "Pad size: %u, pad: %d", pad_size, pad[i]);
+ return -EBADMSG;
+ }
+
-+ return pad_size+1;
++ return pad_size + 1;
+}
+
+/* Authenticate and encrypt the TLS way (also perform padding).
@@ -1051,7 +1189,7 @@ index 0000000..8bff677
+ ret = cryptodev_hash_update(&ses_ptr->hdata,
+ auth_sg, auth_len);
+ if (unlikely(ret)) {
-+ dprintk(0, KERN_ERR, "cryptodev_hash_update: %d\n", ret);
++ derr(0, "cryptodev_hash_update: %d", ret);
+ return ret;
+ }
+ }
@@ -1060,18 +1198,18 @@ index 0000000..8bff677
+ ret = cryptodev_hash_update(&ses_ptr->hdata,
+ dst_sg, len);
+ if (unlikely(ret)) {
-+ dprintk(0, KERN_ERR, "cryptodev_hash_update: %d\n", ret);
++ derr(0, "cryptodev_hash_update: %d", ret);
+ return ret;
+ }
+ }
+
+ ret = cryptodev_hash_final(&ses_ptr->hdata, hash_output);
+ if (unlikely(ret)) {
-+ dprintk(0, KERN_ERR, "cryptodev_hash_final: %d\n", ret);
++ derr(0, "cryptodev_hash_final: %d", ret);
+ return ret;
+ }
+
-+ copy_tls_hash( dst_sg, len, hash_output, caop->tag_len);
++ copy_tls_hash(dst_sg, len, hash_output, caop->tag_len);
+ len += caop->tag_len;
+ }
+
@@ -1084,7 +1222,7 @@ index 0000000..8bff677
+ ret = cryptodev_cipher_encrypt(&ses_ptr->cdata,
+ dst_sg, dst_sg, len);
+ if (unlikely(ret)) {
-+ dprintk(0, KERN_ERR, "cryptodev_cipher_encrypt: %d\n", ret);
++ derr(0, "cryptodev_cipher_encrypt: %d", ret);
+ return ret;
+ }
+ }
@@ -1094,14 +1232,14 @@ index 0000000..8bff677
+ dst_sg, dst_sg, len);
+
+ if (unlikely(ret)) {
-+ dprintk(0, KERN_ERR, "cryptodev_cipher_decrypt: %d\n", ret);
++ derr(0, "cryptodev_cipher_decrypt: %d", ret);
+ return ret;
+ }
+
+ if (ses_ptr->cdata.blocksize > 1) {
+ ret = verify_tls_record_pad(dst_sg, len, ses_ptr->cdata.blocksize);
+ if (unlikely(ret < 0)) {
-+ dprintk(2, KERN_ERR, "verify_record_pad: %d\n", ret);
++ derr(2, "verify_record_pad: %d", ret);
+ fail = 1;
+ } else {
+ len -= ret;
@@ -1111,18 +1249,18 @@ index 0000000..8bff677
+
+ if (ses_ptr->hdata.init != 0) {
+ if (unlikely(caop->tag_len > sizeof(vhash) || caop->tag_len > len)) {
-+ dprintk(1, KERN_ERR, "Illegal tag len size\n");
++ derr(1, "Illegal tag len size");
+ return -EINVAL;
+ }
+
-+ read_tls_hash( dst_sg, len, vhash, caop->tag_len);
++ read_tls_hash(dst_sg, len, vhash, caop->tag_len);
+ len -= caop->tag_len;
+
+ if (auth_len > 0) {
+ ret = cryptodev_hash_update(&ses_ptr->hdata,
+ auth_sg, auth_len);
+ if (unlikely(ret)) {
-+ dprintk(0, KERN_ERR, "cryptodev_hash_update: %d\n", ret);
++ derr(0, "cryptodev_hash_update: %d", ret);
+ return ret;
+ }
+ }
@@ -1131,19 +1269,19 @@ index 0000000..8bff677
+ ret = cryptodev_hash_update(&ses_ptr->hdata,
+ dst_sg, len);
+ if (unlikely(ret)) {
-+ dprintk(0, KERN_ERR, "cryptodev_hash_update: %d\n", ret);
++ derr(0, "cryptodev_hash_update: %d", ret);
+ return ret;
+ }
+ }
+
+ ret = cryptodev_hash_final(&ses_ptr->hdata, hash_output);
+ if (unlikely(ret)) {
-+ dprintk(0, KERN_ERR, "cryptodev_hash_final: %d\n", ret);
++ derr(0, "cryptodev_hash_final: %d", ret);
+ return ret;
+ }
+
+ if (memcmp(vhash, hash_output, caop->tag_len) != 0 || fail != 0) {
-+ dprintk(2, KERN_ERR, "MAC verification failed (tag_len: %d)\n", caop->tag_len);
++ derr(2, "MAC verification failed (tag_len: %d)", caop->tag_len);
+ return -EBADMSG;
+ }
+ }
@@ -1172,7 +1310,7 @@ index 0000000..8bff677
+ ret = cryptodev_cipher_encrypt(&ses_ptr->cdata,
+ dst_sg, dst_sg, len);
+ if (unlikely(ret)) {
-+ dprintk(0, KERN_ERR, "cryptodev_cipher_encrypt: %d\n", ret);
++ derr(0, "cryptodev_cipher_encrypt: %d", ret);
+ return ret;
+ }
+ }
@@ -1182,48 +1320,46 @@ index 0000000..8bff677
+ ret = cryptodev_hash_update(&ses_ptr->hdata,
+ auth_sg, auth_len);
+ if (unlikely(ret)) {
-+ dprintk(0, KERN_ERR, "cryptodev_hash_update: %d\n", ret);
++ derr(0, "cryptodev_hash_update: %d", ret);
+ return ret;
+ }
+ }
+
+ ret = cryptodev_hash_final(&ses_ptr->hdata, hash_output);
+ if (unlikely(ret)) {
-+ dprintk(0, KERN_ERR, "cryptodev_hash_final: %d\n", ret);
++ derr(0, "cryptodev_hash_final: %d", ret);
+ return ret;
+ }
+
-+ if (unlikely(copy_to_user(caop->tag, hash_output, caop->tag_len))) {
++ if (unlikely(copy_to_user(caop->tag, hash_output, caop->tag_len)))
+ return -EFAULT;
-+ }
+ }
+
+ } else {
+ if (ses_ptr->hdata.init != 0) {
+ if (unlikely(caop->tag_len > sizeof(vhash) || caop->tag_len > len)) {
-+ dprintk(1, KERN_ERR, "Illegal tag len size\n");
++ derr(1, "Illegal tag len size");
+ return -EINVAL;
+ }
+
-+ if (unlikely(copy_from_user(vhash, caop->tag, caop->tag_len))) {
++ if (unlikely(copy_from_user(vhash, caop->tag, caop->tag_len)))
+ return -EFAULT;
-+ }
+
+ ret = cryptodev_hash_update(&ses_ptr->hdata,
+ auth_sg, auth_len);
+ if (unlikely(ret)) {
-+ dprintk(0, KERN_ERR, "cryptodev_hash_update: %d\n", ret);
++ derr(0, "cryptodev_hash_update: %d", ret);
+ return ret;
+ }
+
+ ret = cryptodev_hash_final(&ses_ptr->hdata, hash_output);
+ if (unlikely(ret)) {
-+ dprintk(0, KERN_ERR, "cryptodev_hash_final: %d\n", ret);
++ derr(0, "cryptodev_hash_final: %d", ret);
+ return ret;
+ }
+
+ if (memcmp(vhash, hash_output, caop->tag_len) != 0 || fail != 0) {
-+ dprintk(2, KERN_ERR, "MAC verification failed\n");
++ derr(2, "MAC verification failed");
+ return -EBADMSG;
+ }
+ }
@@ -1233,7 +1369,7 @@ index 0000000..8bff677
+ dst_sg, dst_sg, len);
+
+ if (unlikely(ret)) {
-+ dprintk(0, KERN_ERR, "cryptodev_cipher_decrypt: %d\n", ret);
++ derr(0, "cryptodev_cipher_decrypt: %d", ret);
+ return ret;
+ }
+ }
@@ -1258,7 +1394,7 @@ index 0000000..8bff677
+
+ max_tag_len = cryptodev_cipher_get_tag_size(&ses_ptr->cdata);
+ if (unlikely(caop->tag_len > max_tag_len)) {
-+ dprintk(0, KERN_ERR, "Illegal tag length: %d\n", caop->tag_len);
++ derr(0, "Illegal tag length: %d", caop->tag_len);
+ return -EINVAL;
+ }
+
@@ -1273,7 +1409,7 @@ index 0000000..8bff677
+ ret = cryptodev_cipher_encrypt(&ses_ptr->cdata,
+ src_sg, dst_sg, len);
+ if (unlikely(ret)) {
-+ dprintk(0, KERN_ERR, "cryptodev_cipher_encrypt: %d\n", ret);
++ derr(0, "cryptodev_cipher_encrypt: %d", ret);
+ return ret;
+ }
+ kcaop->dst_len = len + caop->tag_len;
@@ -1283,7 +1419,7 @@ index 0000000..8bff677
+ src_sg, dst_sg, len);
+
+ if (unlikely(ret)) {
-+ dprintk(0, KERN_ERR, "cryptodev_cipher_decrypt: %d\n", ret);
++ derr(0, "cryptodev_cipher_decrypt: %d", ret);
+ return ret;
+ }
+ kcaop->dst_len = len - caop->tag_len;
@@ -1303,15 +1439,15 @@ index 0000000..8bff677
+
+ if (caop->flags & COP_FLAG_AEAD_SRTP_TYPE) {
+ if (unlikely(ses_ptr->cdata.init != 0 &&
-+ (ses_ptr->cdata.stream == 0 || ses_ptr->cdata.aead != 0)))
-+ {
-+ dprintk(0, KERN_ERR, "Only stream modes are allowed in SRTP mode (but not AEAD)\n");
++ (ses_ptr->cdata.stream == 0 ||
++ ses_ptr->cdata.aead != 0))) {
++ derr(0, "Only stream modes are allowed in SRTP mode (but not AEAD)");
+ return -EINVAL;
+ }
+
+ ret = get_userbuf_srtp(ses_ptr, kcaop, &auth_sg, &dst_sg);
+ if (unlikely(ret)) {
-+ dprintk(1, KERN_ERR, "get_userbuf_srtp(): Error getting user pages.\n");
++ derr(1, "get_userbuf_srtp(): Error getting user pages.");
+ return ret;
+ }
+
@@ -1323,23 +1459,23 @@ index 0000000..8bff677
+ * so we just copy them to a free page, instead of trying
+ * to map them.
+ */
-+ unsigned char* auth_buf = NULL;
++ unsigned char *auth_buf = NULL;
+ struct scatterlist tmp;
+
+ if (unlikely(caop->auth_len > PAGE_SIZE)) {
-+ dprintk(1, KERN_ERR, "auth data len is excessive.\n");
++ derr(1, "auth data len is excessive.");
+ return -EINVAL;
+ }
+
+ auth_buf = (char *)__get_free_page(GFP_KERNEL);
+ if (unlikely(!auth_buf)) {
-+ dprintk(1, KERN_ERR, "unable to get a free page.\n");
++ derr(1, "unable to get a free page.");
+ return -ENOMEM;
+ }
+
+ if (caop->auth_src && caop->auth_len > 0) {
+ if (unlikely(copy_from_user(auth_buf, caop->auth_src, caop->auth_len))) {
-+ dprintk(1, KERN_ERR, "unable to copy auth data from userspace.\n");
++ derr(1, "unable to copy auth data from userspace.");
+ ret = -EFAULT;
+ goto free_auth_buf;
+ }
@@ -1353,31 +1489,25 @@ index 0000000..8bff677
+ if (caop->flags & COP_FLAG_AEAD_TLS_TYPE && ses_ptr->cdata.aead == 0) {
+ ret = get_userbuf_tls(ses_ptr, kcaop, &dst_sg);
+ if (unlikely(ret)) {
-+ dprintk(1, KERN_ERR, "get_userbuf_tls(): Error getting user pages.\n");
++ derr(1, "get_userbuf_tls(): Error getting user pages.");
+ goto free_auth_buf;
+ }
+
+ ret = tls_auth_n_crypt(ses_ptr, kcaop, auth_sg, caop->auth_len,
+ dst_sg, caop->len);
+ } else {
-+ int dst_len;
-+
+ if (unlikely(ses_ptr->cdata.init == 0 ||
-+ ses_ptr->cdata.stream == 0 ||
-+ ses_ptr->cdata.aead == 0))
-+ {
-+ dprintk(0, KERN_ERR, "Only stream and AEAD ciphers are allowed for authenc\n");
++ (ses_ptr->cdata.stream == 0 &&
++ ses_ptr->cdata.aead == 0))) {
++ derr(0, "Only stream and AEAD ciphers are allowed for authenc");
+ ret = -EINVAL;
+ goto free_auth_buf;
+ }
+
-+ if (caop->op == COP_ENCRYPT) dst_len = caop->len + cryptodev_cipher_get_tag_size(&ses_ptr->cdata);
-+ else dst_len = caop->len;
-+
-+ ret = get_userbuf(ses_ptr, caop->src, caop->len, caop->dst, dst_len,
++ ret = get_userbuf(ses_ptr, caop->src, caop->len, caop->dst, kcaop->dst_len,
+ kcaop->task, kcaop->mm, &src_sg, &dst_sg);
+ if (unlikely(ret)) {
-+ dprintk(1, KERN_ERR, "get_userbuf(): Error getting user pages.\n");
++ derr(1, "get_userbuf(): Error getting user pages.");
+ goto free_auth_buf;
+ }
+
@@ -1402,19 +1532,19 @@ index 0000000..8bff677
+ int ret;
+
+ if (unlikely(caop->op != COP_ENCRYPT && caop->op != COP_DECRYPT)) {
-+ dprintk(1, KERN_DEBUG, "invalid operation op=%u\n", caop->op);
++ ddebug(1, "invalid operation op=%u", caop->op);
+ return -EINVAL;
+ }
+
+ /* this also enters ses_ptr->sem */
+ ses_ptr = crypto_get_session_by_sid(fcr, caop->ses);
+ if (unlikely(!ses_ptr)) {
-+ dprintk(1, KERN_ERR, "invalid session ID=0x%08X\n", caop->ses);
++ derr(1, "invalid session ID=0x%08X", caop->ses);
+ return -EINVAL;
+ }
+
+ if (unlikely(ses_ptr->cdata.init == 0)) {
-+ dprintk(1, KERN_ERR, "cipher context not initialized\n");
++ derr(1, "cipher context not initialized");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
@@ -1423,8 +1553,7 @@ index 0000000..8bff677
+ if (ses_ptr->hdata.init != 0) {
+ ret = cryptodev_hash_reset(&ses_ptr->hdata);
+ if (unlikely(ret)) {
-+ dprintk(1, KERN_ERR,
-+ "error in cryptodev_hash_reset()\n");
++ derr(1, "error in cryptodev_hash_reset()");
+ goto out_unlock;
+ }
+ }
@@ -1434,8 +1563,7 @@ index 0000000..8bff677
+
+ ret = __crypto_auth_run_zc(ses_ptr, kcaop);
+ if (unlikely(ret)) {
-+ dprintk(1, KERN_ERR,
-+ "error in __crypto_auth_run_zc()\n");
++ derr(1, "error in __crypto_auth_run_zc()");
+ goto out_unlock;
+ }
+
@@ -1450,10 +1578,10 @@ index 0000000..8bff677
+}
diff --git a/crypto/cryptodev/cryptlib.c b/crypto/cryptodev/cryptlib.c
new file mode 100644
-index 0000000..fad5ba6
+index 0000000..44ce763
--- /dev/null
+++ b/crypto/cryptodev/cryptlib.c
-@@ -0,0 +1,377 @@
+@@ -0,0 +1,441 @@
+/*
+ * Driver for /dev/crypto device (aka CryptoDev)
+ *
@@ -1490,6 +1618,8 @@ index 0000000..fad5ba6
+#include <crypto/hash.h>
+#include <crypto/cryptodev.h>
+#include <crypto/aead.h>
++#include <linux/rtnetlink.h>
++#include <crypto/authenc.h>
+#include "cryptodev_int.h"
+
+
@@ -1509,19 +1639,89 @@ index 0000000..fad5ba6
+ complete(&res->completion);
+}
+
++int cryptodev_get_cipher_keylen(unsigned int *keylen, struct session_op *sop,
++ int aead)
++{
++ /*
++ * For blockciphers (AES-CBC) or non-composite aead ciphers (like AES-GCM),
++ * the key length is simply the cipher keylen obtained from userspace. If
++ * the cipher is composite aead, the keylen is the sum of cipher keylen,
++ * hmac keylen and a key header length. This key format is the one used in
++ * Linux kernel for composite aead ciphers (crypto/authenc.c)
++ */
++ unsigned int klen = sop->keylen;
++
++ if (unlikely(sop->keylen > CRYPTO_CIPHER_MAX_KEY_LEN))
++ return -EINVAL;
++
++ if (aead && sop->mackeylen) {
++ if (unlikely(sop->mackeylen > CRYPTO_HMAC_MAX_KEY_LEN))
++ return -EINVAL;
++ klen += sop->mackeylen;
++ klen += RTA_SPACE(sizeof(struct crypto_authenc_key_param));
++ }
++
++ *keylen = klen;
++ return 0;
++}
++
++int cryptodev_get_cipher_key(uint8_t *key, struct session_op *sop, int aead)
++{
++ /*
++ * Get cipher key from user-space. For blockciphers just copy it from
++ * user-space. For composite aead ciphers combine it with the hmac key in
++ * the format used by Linux kernel in crypto/authenc.c:
++ *
++ * [[AUTHENC_KEY_HEADER + CIPHER_KEYLEN] [AUTHENTICATION KEY] [CIPHER KEY]]
++ */
++ struct crypto_authenc_key_param *param;
++ struct rtattr *rta;
++ int ret = 0;
++
++ if (aead && sop->mackeylen) {
++ /*
++ * Composite aead ciphers. The first four bytes are the header type and
++ * header length for aead keys
++ */
++ rta = (void *)key;
++ rta->rta_type = CRYPTO_AUTHENC_KEYA_PARAM;
++ rta->rta_len = RTA_LENGTH(sizeof(*param));
++
++ /*
++ * The next four bytes hold the length of the encryption key
++ */
++ param = RTA_DATA(rta);
++ param->enckeylen = cpu_to_be32(sop->keylen);
++
++ /* Advance key pointer eight bytes and copy the hmac key */
++ key += RTA_SPACE(sizeof(*param));
++ if (unlikely(copy_from_user(key, sop->mackey, sop->mackeylen))) {
++ ret = -EFAULT;
++ goto error;
++ }
++ /* Advance key pointer past the hmac key */
++ key += sop->mackeylen;
++ }
++ /* now copy the blockcipher key */
++ if (unlikely(copy_from_user(key, sop->key, sop->keylen)))
++ ret = -EFAULT;
++
++error:
++ return ret;
++}
++
++
+int cryptodev_cipher_init(struct cipher_data *out, const char *alg_name,
+ uint8_t *keyp, size_t keylen, int stream, int aead)
+{
+ int ret;
+
-+ memset(out, 0, sizeof(*out));
-+
+ if (aead == 0) {
+ struct ablkcipher_alg *alg;
+
+ out->async.s = crypto_alloc_ablkcipher(alg_name, 0, 0);
+ if (unlikely(IS_ERR(out->async.s))) {
-+ dprintk(1, KERN_DEBUG, "Failed to load cipher %s\n", alg_name);
++ ddebug(1, "Failed to load cipher %s", alg_name);
+ return -EINVAL;
+ }
+
@@ -1531,11 +1731,8 @@ index 0000000..fad5ba6
+ if (alg->max_keysize > 0 &&
+ unlikely((keylen < alg->min_keysize) ||
+ (keylen > alg->max_keysize))) {
-+ dprintk(1, KERN_DEBUG,
-+ "Wrong keylen '%zu' for algorithm '%s'. \
-+ Use %u to %u.\n",
-+ keylen, alg_name, alg->min_keysize,
-+ alg->max_keysize);
++ ddebug(1, "Wrong keylen '%zu' for algorithm '%s'. Use %u to %u.",
++ keylen, alg_name, alg->min_keysize, alg->max_keysize);
+ ret = -EINVAL;
+ goto error;
+ }
@@ -1549,7 +1746,7 @@ index 0000000..fad5ba6
+ } else {
+ out->async.as = crypto_alloc_aead(alg_name, 0, 0);
+ if (unlikely(IS_ERR(out->async.as))) {
-+ dprintk(1, KERN_DEBUG, "Failed to load cipher %s\n", alg_name);
++ ddebug(1, "Failed to load cipher %s", alg_name);
+ return -EINVAL;
+ }
+
@@ -1561,8 +1758,7 @@ index 0000000..fad5ba6
+ }
+
+ if (unlikely(ret)) {
-+ dprintk(1, KERN_DEBUG, "Setting key failed for %s-%zu.\n",
-+ alg_name, keylen*8);
++ ddebug(1, "Setting key failed for %s-%zu.", alg_name, keylen*8);
+ ret = -EINVAL;
+ goto error;
+ }
@@ -1570,19 +1766,18 @@ index 0000000..fad5ba6
+ out->stream = stream;
+ out->aead = aead;
+
-+ out->async.result = kmalloc(sizeof(*out->async.result), GFP_KERNEL);
++ out->async.result = kzalloc(sizeof(*out->async.result), GFP_KERNEL);
+ if (unlikely(!out->async.result)) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
-+ memset(out->async.result, 0, sizeof(*out->async.result));
+ init_completion(&out->async.result->completion);
+
+ if (aead == 0) {
+ out->async.request = ablkcipher_request_alloc(out->async.s, GFP_KERNEL);
+ if (unlikely(!out->async.request)) {
-+ dprintk(1, KERN_ERR, "error allocating async crypto request\n");
++ derr(1, "error allocating async crypto request");
+ ret = -ENOMEM;
+ goto error;
+ }
@@ -1593,7 +1788,7 @@ index 0000000..fad5ba6
+ } else {
+ out->async.arequest = aead_request_alloc(out->async.as, GFP_KERNEL);
+ if (unlikely(!out->async.arequest)) {
-+ dprintk(1, KERN_ERR, "error allocating async crypto request\n");
++ derr(1, "error allocating async crypto request");
+ ret = -ENOMEM;
+ goto error;
+ }
@@ -1614,7 +1809,7 @@ index 0000000..fad5ba6
+ } else {
+ if (out->async.arequest)
+ aead_request_free(out->async.arequest);
-+ if (out->async.s)
++ if (out->async.as)
+ crypto_free_aead(out->async.as);
+ }
+ kfree(out->async.result);
@@ -1657,8 +1852,7 @@ index 0000000..fad5ba6
+ * another request. */
+
+ if (unlikely(cr->err)) {
-+ dprintk(0, KERN_ERR, "error from async request: %d\n",
-+ cr->err);
++ derr(0, "error from async request: %d", cr->err);
+ return cr->err;
+ }
+
@@ -1676,7 +1870,7 @@ index 0000000..fad5ba6
+{
+ int ret;
+
-+ INIT_COMPLETION(cdata->async.result->completion);
++ reinit_completion(&cdata->async.result->completion);
+
+ if (cdata->aead == 0) {
+ ablkcipher_request_set_crypt(cdata->async.request,
@@ -1699,7 +1893,7 @@ index 0000000..fad5ba6
+{
+ int ret;
+
-+ INIT_COMPLETION(cdata->async.result->completion);
++ reinit_completion(&cdata->async.result->completion);
+ if (cdata->aead == 0) {
+ ablkcipher_request_set_crypt(cdata->async.request,
+ (struct scatterlist *)src, dst,
@@ -1724,7 +1918,7 @@ index 0000000..fad5ba6
+
+ hdata->async.s = crypto_alloc_ahash(alg_name, 0, 0);
+ if (unlikely(IS_ERR(hdata->async.s))) {
-+ dprintk(1, KERN_DEBUG, "Failed to load transform for %s\n", alg_name);
++ ddebug(1, "Failed to load transform for %s", alg_name);
+ return -EINVAL;
+ }
+
@@ -1732,9 +1926,8 @@ index 0000000..fad5ba6
+ if (hmac_mode != 0) {
+ ret = crypto_ahash_setkey(hdata->async.s, mackey, mackeylen);
+ if (unlikely(ret)) {
-+ dprintk(1, KERN_DEBUG,
-+ "Setting hmac key failed for %s-%zu.\n",
-+ alg_name, mackeylen*8);
++ ddebug(1, "Setting hmac key failed for %s-%zu.",
++ alg_name, mackeylen*8);
+ ret = -EINVAL;
+ goto error;
+ }
@@ -1743,18 +1936,17 @@ index 0000000..fad5ba6
+ hdata->digestsize = crypto_ahash_digestsize(hdata->async.s);
+ hdata->alignmask = crypto_ahash_alignmask(hdata->async.s);
+
-+ hdata->async.result = kmalloc(sizeof(*hdata->async.result), GFP_KERNEL);
++ hdata->async.result = kzalloc(sizeof(*hdata->async.result), GFP_KERNEL);
+ if (unlikely(!hdata->async.result)) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
-+ memset(hdata->async.result, 0, sizeof(*hdata->async.result));
+ init_completion(&hdata->async.result->completion);
+
+ hdata->async.request = ahash_request_alloc(hdata->async.s, GFP_KERNEL);
+ if (unlikely(!hdata->async.request)) {
-+ dprintk(0, KERN_ERR, "error allocating async crypto request\n");
++ derr(0, "error allocating async crypto request");
+ ret = -ENOMEM;
+ goto error;
+ }
@@ -1765,7 +1957,7 @@ index 0000000..fad5ba6
+
+ ret = crypto_ahash_init(hdata->async.request);
+ if (unlikely(ret)) {
-+ dprintk(0, KERN_ERR, "error in crypto_hash_init()\n");
++ derr(0, "error in crypto_hash_init()");
+ goto error_request;
+ }
+
@@ -1798,7 +1990,7 @@ index 0000000..fad5ba6
+
+ ret = crypto_ahash_init(hdata->async.request);
+ if (unlikely(ret)) {
-+ dprintk(0, KERN_ERR, "error in crypto_hash_init()\n");
++ derr(0, "error in crypto_hash_init()");
+ return ret;
+ }
+
@@ -1811,7 +2003,7 @@ index 0000000..fad5ba6
+{
+ int ret;
+
-+ INIT_COMPLETION(hdata->async.result->completion);
++ reinit_completion(&hdata->async.result->completion);
+ ahash_request_set_crypt(hdata->async.request, sg, NULL, len);
+
+ ret = crypto_ahash_update(hdata->async.request);
@@ -1819,11 +2011,11 @@ index 0000000..fad5ba6
+ return waitfor(hdata->async.result, ret);
+}
+
-+int cryptodev_hash_final(struct hash_data *hdata, void* output)
++int cryptodev_hash_final(struct hash_data *hdata, void *output)
+{
+ int ret;
+
-+ INIT_COMPLETION(hdata->async.result->completion);
++ reinit_completion(&hdata->async.result->completion);
+ ahash_request_set_crypt(hdata->async.request, NULL, output, 0);
+
+ ret = crypto_ahash_final(hdata->async.request);
@@ -1833,10 +2025,10 @@ index 0000000..fad5ba6
+
diff --git a/crypto/cryptodev/cryptlib.h b/crypto/cryptodev/cryptlib.h
new file mode 100644
-index 0000000..1745d0f
+index 0000000..a0a8a63
--- /dev/null
+++ b/crypto/cryptodev/cryptlib.h
-@@ -0,0 +1,90 @@
+@@ -0,0 +1,93 @@
+#ifndef CRYPTLIB_H
+# define CRYPTLIB_H
+
@@ -1864,6 +2056,9 @@ index 0000000..1745d0f
+int cryptodev_cipher_init(struct cipher_data *out, const char *alg_name,
+ uint8_t *key, size_t keylen, int stream, int aead);
+void cryptodev_cipher_deinit(struct cipher_data *cdata);
++int cryptodev_get_cipher_key(uint8_t *key, struct session_op *sop, int aead);
++int cryptodev_get_cipher_keylen(unsigned int *keylen, struct session_op *sop,
++ int aead);
+ssize_t cryptodev_cipher_decrypt(struct cipher_data *cdata,
+ const struct scatterlist *sg1,
+ struct scatterlist *sg2, size_t len);
@@ -1872,20 +2067,20 @@ index 0000000..1745d0f
+ struct scatterlist *sg2, size_t len);
+
+/* AEAD */
-+inline static void cryptodev_cipher_auth(struct cipher_data *cdata,
++static inline void cryptodev_cipher_auth(struct cipher_data *cdata,
+ struct scatterlist *sg1, size_t len)
+{
+ /* for some reason we _have_ to call that even for zero length sgs */
+ aead_request_set_assoc(cdata->async.arequest, len ? sg1 : NULL, len);
+}
+
-+inline static void cryptodev_cipher_set_tag_size(struct cipher_data *cdata, int size)
++static inline void cryptodev_cipher_set_tag_size(struct cipher_data *cdata, int size)
+{
+ if (likely(cdata->aead != 0))
+ crypto_aead_setauthsize(cdata->async.as, size);
+}
+
-+inline static int cryptodev_cipher_get_tag_size(struct cipher_data *cdata)
++static inline int cryptodev_cipher_get_tag_size(struct cipher_data *cdata)
+{
+ if (likely(cdata->init && cdata->aead != 0))
+ return crypto_aead_authsize(cdata->async.as);
@@ -1893,13 +2088,13 @@ index 0000000..1745d0f
+ return 0;
+}
+
-+inline static void cryptodev_cipher_set_iv(struct cipher_data *cdata,
++static inline void cryptodev_cipher_set_iv(struct cipher_data *cdata,
+ void *iv, size_t iv_size)
+{
+ memcpy(cdata->async.iv, iv, min(iv_size, sizeof(cdata->async.iv)));
+}
+
-+inline static void cryptodev_cipher_get_iv(struct cipher_data *cdata,
++static inline void cryptodev_cipher_get_iv(struct cipher_data *cdata,
+ void *iv, size_t iv_size)
+{
+ memcpy(iv, cdata->async.iv, min(iv_size, sizeof(cdata->async.iv)));
@@ -1929,7 +2124,7 @@ index 0000000..1745d0f
+#endif
diff --git a/crypto/cryptodev/crypto/cryptodev.h b/crypto/cryptodev/crypto/cryptodev.h
new file mode 100644
-index 0000000..a2f11b1
+index 0000000..7fb9c7d
--- /dev/null
+++ b/crypto/cryptodev/crypto/cryptodev.h
@@ -0,0 +1,292 @@
@@ -2173,7 +2368,7 @@ index 0000000..a2f11b1
+
+/* input of CIOCKEY */
+struct crypt_kop {
-+ __u32 crk_op; /* cryptodev_crk_ot_t */
++ __u32 crk_op; /* cryptodev_crk_op_t */
+ __u32 crk_status;
+ __u16 crk_iparams;
+ __u16 crk_oparams;
@@ -2227,14 +2422,20 @@ index 0000000..a2f11b1
+#endif /* L_CRYPTODEV_H */
diff --git a/crypto/cryptodev/cryptodev_int.h b/crypto/cryptodev/cryptodev_int.h
new file mode 100644
-index 0000000..12dd5b1
+index 0000000..d7660fa
--- /dev/null
+++ b/crypto/cryptodev/cryptodev_int.h
-@@ -0,0 +1,134 @@
+@@ -0,0 +1,145 @@
+/* cipher stuff */
+#ifndef CRYPTODEV_INT_H
+# define CRYPTODEV_INT_H
+
++#include <linux/version.h>
++
++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0))
++# define reinit_completion(x) INIT_COMPLETION(*(x))
++#endif
++
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
@@ -2251,11 +2452,16 @@ index 0000000..12dd5b1
+#define dprintk(level, severity, format, a...) \
+ do { \
+ if (level <= cryptodev_verbosity) \
-+ printk(severity PFX "%s[%u] (%s:%u): " format, \
++ printk(severity PFX "%s[%u] (%s:%u): " format "\n", \
+ current->comm, current->pid, \
+ __func__, __LINE__, \
+ ##a); \
+ } while (0)
++#define derr(level, format, a...) dprintk(level, KERN_ERR, format, ##a)
++#define dwarning(level, format, a...) dprintk(level, KERN_WARNING, format, ##a)
++#define dinfo(level, format, a...) dprintk(level, KERN_INFO, format, ##a)
++#define ddebug(level, format, a...) dprintk(level, KERN_DEBUG, format, ##a)
++
+
+extern int cryptodev_verbosity;
+
@@ -2284,7 +2490,7 @@ index 0000000..12dd5b1
+};
+
+/* input of CIOCCRYPT */
-+ struct compat_crypt_op {
++struct compat_crypt_op {
+ uint32_t ses; /* session identifier */
+ uint16_t op; /* COP_ENCRYPT or COP_DECRYPT */
+ uint16_t flags; /* see COP_FLAG_* */
@@ -2358,686 +2564,1500 @@ index 0000000..12dd5b1
+
+struct csession *crypto_get_session_by_sid(struct fcrypt *fcr, uint32_t sid);
+
-+inline static void crypto_put_session(struct csession * ses_ptr)
++static inline void crypto_put_session(struct csession *ses_ptr)
+{
+ mutex_unlock(&ses_ptr->sem);
+}
-+int adjust_sg_array(struct csession * ses, int pagecount);
++int adjust_sg_array(struct csession *ses, int pagecount);
+
+#endif /* CRYPTODEV_INT_H */
-diff --git a/crypto/cryptodev/ioctl.c b/crypto/cryptodev/ioctl.c
+diff --git a/crypto/cryptodev/examples/aes-gcm.c b/crypto/cryptodev/examples/aes-gcm.c
new file mode 100644
-index 0000000..f26cf93
+index 0000000..6791f4e
--- /dev/null
-+++ b/crypto/cryptodev/ioctl.c
-@@ -0,0 +1,1136 @@
-+/*
-+ * Driver for /dev/crypto device (aka CryptoDev)
-+ *
-+ * Copyright (c) 2004 Michal Ludvig <mludvig at logix.net.nz>, SuSE Labs
-+ * Copyright (c) 2009,2010,2011 Nikos Mavrogiannopoulos <nmav at gnutls.org>
-+ * Copyright (c) 2010 Phil Sutter
-+ *
-+ * This file is part of linux cryptodev.
-+ *
-+ * This program is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU General Public License
-+ * as published by the Free Software Foundation; either version 2
-+ * of the License, or (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc.,
-+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-+ */
-+
++++ b/crypto/cryptodev/examples/aes-gcm.c
+@@ -0,0 +1,139 @@
+/*
-+ * Device /dev/crypto provides an interface for
-+ * accessing kernel CryptoAPI algorithms (ciphers,
-+ * hashes) from userspace programs.
++ * Demo on how to use /dev/crypto device for ciphering.
+ *
-+ * /dev/crypto interface was originally introduced in
-+ * OpenBSD and this module attempts to keep the API.
++ * Placed under public domain.
+ *
+ */
-+
-+#include <crypto/hash.h>
-+#include <linux/crypto.h>
-+#include <linux/mm.h>
-+#include <linux/highmem.h>
-+#include <linux/ioctl.h>
-+#include <linux/random.h>
-+#include <linux/syscalls.h>
-+#include <linux/pagemap.h>
-+#include <linux/poll.h>
-+#include <linux/uaccess.h>
++#include <stdio.h>
++#include <string.h>
++#include <unistd.h>
++#include <fcntl.h>
++#include <sys/ioctl.h>
+#include <crypto/cryptodev.h>
-+#include <linux/scatterlist.h>
-+#include "cryptodev_int.h"
-+#include "zc.h"
-+#include "version.h"
++#include "aes-gcm.h"
+
-+MODULE_AUTHOR("Nikos Mavrogiannopoulos <nmav at gnutls.org>");
-+MODULE_DESCRIPTION("CryptoDev driver");
-+MODULE_LICENSE("GPL");
++int aes_gcm_ctx_init(struct cryptodev_ctx* ctx, int cfd, const uint8_t *key, unsigned int key_size)
++{
++#ifdef CIOCGSESSINFO
++ struct session_info_op siop;
++#endif
+
-+/* ====== Compile-time config ====== */
++ memset(ctx, 0, sizeof(*ctx));
++ ctx->cfd = cfd;
+
-+/* Default (pre-allocated) and maximum size of the job queue.
-+ * These are free, pending and done items all together. */
-+#define DEF_COP_RINGSIZE 16
-+#define MAX_COP_RINGSIZE 64
++ ctx->sess.cipher = CRYPTO_AES_GCM;
++ ctx->sess.keylen = key_size;
++ ctx->sess.key = (void*)key;
++ if (ioctl(ctx->cfd, CIOCGSESSION, &ctx->sess)) {
++ perror("ioctl(CIOCGSESSION)");
++ return -1;
++ }
+
-+/* ====== Module parameters ====== */
++#ifdef CIOCGSESSINFO
++ siop.ses = ctx->sess.ses;
++ if (ioctl(ctx->cfd, CIOCGSESSINFO, &siop)) {
++ perror("ioctl(CIOCGSESSINFO)");
++ return -1;
++ }
++ printf("Got %s with driver %s\n",
++ siop.cipher_info.cra_name, siop.cipher_info.cra_driver_name);
++ if (!(siop.flags & SIOP_FLAG_KERNEL_DRIVER_ONLY)) {
++ printf("Note: This is not an accelerated cipher\n");
++ }
++ /*printf("Alignmask is %x\n", (unsigned int)siop.alignmask); */
++ ctx->alignmask = siop.alignmask;
++#endif
++ return 0;
++}
+
-+int cryptodev_verbosity;
-+module_param(cryptodev_verbosity, int, 0644);
-+MODULE_PARM_DESC(cryptodev_verbosity, "0: normal, 1: verbose, 2: debug");
++void aes_gcm_ctx_deinit(struct cryptodev_ctx* ctx)
++{
++ if (ioctl(ctx->cfd, CIOCFSESSION, &ctx->sess.ses)) {
++ perror("ioctl(CIOCFSESSION)");
++ }
++}
+
-+/* ====== CryptoAPI ====== */
-+struct todo_list_item {
-+ struct list_head __hook;
-+ struct kernel_crypt_op kcop;
-+ int result;
-+};
++int
++aes_gcm_encrypt(struct cryptodev_ctx* ctx, const void* iv,
++ const void* auth, size_t auth_size,
++ const void* plaintext, void* ciphertext, size_t size)
++{
++ struct crypt_auth_op cryp;
++ void* p;
++
++ /* check plaintext and ciphertext alignment */
++ if (ctx->alignmask) {
++ p = (void*)(((unsigned long)plaintext + ctx->alignmask) & ~ctx->alignmask);
++ if (plaintext != p) {
++ fprintf(stderr, "plaintext is not aligned\n");
++ return -1;
++ }
+
-+struct locked_list {
-+ struct list_head list;
-+ struct mutex lock;
-+};
++ p = (void*)(((unsigned long)ciphertext + ctx->alignmask) & ~ctx->alignmask);
++ if (ciphertext != p) {
++ fprintf(stderr, "ciphertext is not aligned\n");
++ return -1;
++ }
++ }
+
-+struct crypt_priv {
-+ struct fcrypt fcrypt;
-+ struct locked_list free, todo, done;
-+ int itemcount;
-+ struct work_struct cryptask;
-+ wait_queue_head_t user_waiter;
-+};
++ memset(&cryp, 0, sizeof(cryp));
+
-+#define FILL_SG(sg, ptr, len) \
-+ do { \
-+ (sg)->page = virt_to_page(ptr); \
-+ (sg)->offset = offset_in_page(ptr); \
-+ (sg)->length = len; \
-+ (sg)->dma_address = 0; \
-+ } while (0)
++ /* Encrypt data.in to data.encrypted */
++ cryp.ses = ctx->sess.ses;
++ cryp.iv = (void*)iv;
++ cryp.op = COP_ENCRYPT;
++ cryp.auth_len = auth_size;
++ cryp.auth_src = (void*)auth;
++ cryp.len = size;
++ cryp.src = (void*)plaintext;
++ cryp.dst = ciphertext;
++ if (ioctl(ctx->cfd, CIOCAUTHCRYPT, &cryp)) {
++ perror("ioctl(CIOCAUTHCRYPT)");
++ return -1;
++ }
+
-+/* cryptodev's own workqueue, keeps crypto tasks from disturbing the force */
-+static struct workqueue_struct *cryptodev_wq;
++ return 0;
++}
+
-+/* Prepare session for future use. */
-+static int
-+crypto_create_session(struct fcrypt *fcr, struct session_op *sop)
++int
++aes_gcm_decrypt(struct cryptodev_ctx* ctx, const void* iv,
++ const void* auth, size_t auth_size,
++ const void* ciphertext, void* plaintext, size_t size)
+{
-+ struct csession *ses_new = NULL, *ses_ptr;
-+ int ret = 0;
-+ const char *alg_name = NULL;
-+ const char *hash_name = NULL;
-+ int hmac_mode = 1, stream = 0, aead = 0;
++ struct crypt_auth_op cryp;
++ void* p;
++
++ /* check plaintext and ciphertext alignment */
++ if (ctx->alignmask) {
++ p = (void*)(((unsigned long)plaintext + ctx->alignmask) & ~ctx->alignmask);
++ if (plaintext != p) {
++ fprintf(stderr, "plaintext is not aligned\n");
++ return -1;
++ }
+
-+ /* Does the request make sense? */
-+ if (unlikely(!sop->cipher && !sop->mac)) {
-+ dprintk(1, KERN_DEBUG, "Both 'cipher' and 'mac' unset.\n");
-+ return -EINVAL;
++ p = (void*)(((unsigned long)ciphertext + ctx->alignmask) & ~ctx->alignmask);
++ if (ciphertext != p) {
++ fprintf(stderr, "ciphertext is not aligned\n");
++ return -1;
++ }
+ }
+
-+ switch (sop->cipher) {
-+ case 0:
-+ break;
-+ case CRYPTO_DES_CBC:
-+ alg_name = "cbc(des)";
-+ break;
-+ case CRYPTO_3DES_CBC:
-+ alg_name = "cbc(des3_ede)";
-+ break;
-+ case CRYPTO_BLF_CBC:
-+ alg_name = "cbc(blowfish)";
-+ break;
-+ case CRYPTO_AES_CBC:
-+ alg_name = "cbc(aes)";
-+ break;
-+ case CRYPTO_AES_ECB:
-+ alg_name = "ecb(aes)";
-+ break;
-+ case CRYPTO_CAMELLIA_CBC:
-+ alg_name = "cbc(camellia)";
-+ break;
-+ case CRYPTO_AES_CTR:
-+ alg_name = "ctr(aes)";
-+ stream = 1;
-+ break;
-+ case CRYPTO_AES_GCM:
-+ alg_name = "gcm(aes)";
-+ stream = 1;
-+ aead = 1;
-+ break;
-+ case CRYPTO_NULL:
-+ alg_name = "ecb(cipher_null)";
-+ stream = 1;
-+ break;
-+ default:
-+ dprintk(1, KERN_DEBUG, "bad cipher: %d\n", sop->cipher);
-+ return -EINVAL;
++ memset(&cryp, 0, sizeof(cryp));
++
++ /* Encrypt data.in to data.encrypted */
++ cryp.ses = ctx->sess.ses;
++ cryp.iv = (void*)iv;
++ cryp.op = COP_DECRYPT;
++ cryp.auth_len = auth_size;
++ cryp.auth_src = (void*)auth;
++ cryp.len = size;
++ cryp.src = (void*)ciphertext;
++ cryp.dst = plaintext;
++ if (ioctl(ctx->cfd, CIOCAUTHCRYPT, &cryp)) {
++ perror("ioctl(CIOCAUTHCRYPT)");
++ return -1;
+ }
+
-+ switch (sop->mac) {
-+ case 0:
-+ break;
-+ case CRYPTO_MD5_HMAC:
-+ hash_name = "hmac(md5)";
-+ break;
-+ case CRYPTO_RIPEMD160_HMAC:
-+ hash_name = "hmac(rmd160)";
-+ break;
-+ case CRYPTO_SHA1_HMAC:
-+ hash_name = "hmac(sha1)";
-+ break;
-+ case CRYPTO_SHA2_224_HMAC:
-+ hash_name = "hmac(sha224)";
-+ break;
++ return 0;
++}
+
-+ case CRYPTO_SHA2_256_HMAC:
-+ hash_name = "hmac(sha256)";
-+ break;
-+ case CRYPTO_SHA2_384_HMAC:
-+ hash_name = "hmac(sha384)";
-+ break;
-+ case CRYPTO_SHA2_512_HMAC:
-+ hash_name = "hmac(sha512)";
-+ break;
+diff --git a/crypto/cryptodev/examples/aes-gcm.h b/crypto/cryptodev/examples/aes-gcm.h
+new file mode 100644
+index 0000000..1ddc5fe
+--- /dev/null
++++ b/crypto/cryptodev/examples/aes-gcm.h
+@@ -0,0 +1,28 @@
++#ifndef AES_H
++# define AES_H
+
-+ /* non-hmac cases */
-+ case CRYPTO_MD5:
-+ hash_name = "md5";
-+ hmac_mode = 0;
-+ break;
-+ case CRYPTO_RIPEMD160:
-+ hash_name = "rmd160";
-+ hmac_mode = 0;
-+ break;
-+ case CRYPTO_SHA1:
-+ hash_name = "sha1";
-+ hmac_mode = 0;
-+ break;
-+ case CRYPTO_SHA2_224:
-+ hash_name = "sha224";
-+ hmac_mode = 0;
-+ break;
-+ case CRYPTO_SHA2_256:
-+ hash_name = "sha256";
-+ hmac_mode = 0;
-+ break;
-+ case CRYPTO_SHA2_384:
-+ hash_name = "sha384";
-+ hmac_mode = 0;
-+ break;
-+ case CRYPTO_SHA2_512:
-+ hash_name = "sha512";
-+ hmac_mode = 0;
-+ break;
-+ default:
-+ dprintk(1, KERN_DEBUG, "bad mac: %d\n", sop->mac);
-+ return -EINVAL;
-+ }
++#include <stdint.h>
+
-+ /* Create a session and put it to the list. */
-+ ses_new = kzalloc(sizeof(*ses_new), GFP_KERNEL);
-+ if (!ses_new)
-+ return -ENOMEM;
++struct cryptodev_ctx {
++ int cfd;
++ struct session_op sess;
++ uint16_t alignmask;
++};
+
-+ /* Set-up crypto transform. */
-+ if (alg_name) {
-+ uint8_t keyp[CRYPTO_CIPHER_MAX_KEY_LEN];
++#define AES_BLOCK_SIZE 16
+
-+ if (unlikely(sop->keylen > CRYPTO_CIPHER_MAX_KEY_LEN)) {
-+ dprintk(1, KERN_DEBUG,
-+ "Setting key failed for %s-%zu.\n",
-+ alg_name, (size_t)sop->keylen*8);
-+ ret = -EINVAL;
-+ goto error_cipher;
-+ }
++int aes_gcm_ctx_init(struct cryptodev_ctx* ctx, int cfd, const uint8_t *key, unsigned int key_size);
++void aes_gcm_ctx_deinit();
+
-+ if (unlikely(copy_from_user(keyp, sop->key, sop->keylen))) {
-+ ret = -EFAULT;
-+ goto error_cipher;
-+ }
++/* Note that encryption assumes that ciphertext has enough size
++ * for the tag to be appended. In decryption the tag is assumed
++ * to be the last bytes of ciphertext.
++ */
++int aes_gcm_encrypt(struct cryptodev_ctx* ctx, const void* iv,
++ const void* auth, size_t auth_size,
++ const void* plaintext, void* ciphertext, size_t size);
++int aes_gcm_decrypt(struct cryptodev_ctx* ctx, const void* iv,
++ const void* auth, size_t auth_size,
++ const void* ciphertext, void* plaintext, size_t size);
+
-+ ret = cryptodev_cipher_init(&ses_new->cdata, alg_name, keyp,
-+ sop->keylen, stream, aead);
-+ if (ret < 0) {
-+ dprintk(1, KERN_DEBUG,
-+ "Failed to load cipher for %s\n", alg_name);
-+ ret = -EINVAL;
-+ goto error_cipher;
-+ }
-+ }
++#endif
+diff --git a/crypto/cryptodev/examples/aes-sha1.c b/crypto/cryptodev/examples/aes-sha1.c
+new file mode 100644
+index 0000000..e93e3c4
+--- /dev/null
++++ b/crypto/cryptodev/examples/aes-sha1.c
+@@ -0,0 +1,139 @@
++/*
++ * Demo on how to use /dev/crypto device for ciphering.
++ *
++ * Placed under public domain.
++ *
++ */
++#include <stdio.h>
++#include <string.h>
++#include <unistd.h>
++#include <fcntl.h>
++#include <sys/ioctl.h>
++#include <crypto/cryptodev.h>
++#include "aes-sha1.h"
+
-+ if (hash_name && aead == 0) {
-+ uint8_t keyp[CRYPTO_HMAC_MAX_KEY_LEN];
++/* This is the TLS version of AES-CBC with HMAC-SHA1.
++ */
+
-+ if (unlikely(sop->mackeylen > CRYPTO_HMAC_MAX_KEY_LEN)) {
-+ dprintk(1, KERN_DEBUG,
-+ "Setting key failed for %s-%zu.\n",
-+ alg_name, (size_t)sop->mackeylen*8);
-+ ret = -EINVAL;
-+ goto error_hash;
-+ }
++int aes_sha1_ctx_init(struct cryptodev_ctx* ctx, int cfd,
++ const uint8_t *key, unsigned int key_size,
++ const uint8_t *mac_key, unsigned int mac_key_size)
++{
++#ifdef CIOCGSESSINFO
++ struct session_info_op siop;
++#endif
+
-+ if (sop->mackey && unlikely(copy_from_user(keyp, sop->mackey,
-+ sop->mackeylen))) {
-+ ret = -EFAULT;
-+ goto error_hash;
-+ }
++ memset(ctx, 0, sizeof(*ctx));
++ ctx->cfd = cfd;
+
-+ ret = cryptodev_hash_init(&ses_new->hdata, hash_name, hmac_mode,
-+ keyp, sop->mackeylen);
-+ if (ret != 0) {
-+ dprintk(1, KERN_DEBUG, "Failed to load hash for %s\n", hash_name);
-+ ret = -EINVAL;
-+ goto error_hash;
-+ }
-+ }
++ ctx->sess.cipher = CRYPTO_AES_CBC;
++ ctx->sess.keylen = key_size;
++ ctx->sess.key = (void*)key;
+
-+ ses_new->alignmask = max(ses_new->cdata.alignmask,
-+ ses_new->hdata.alignmask);
-+ dprintk(2, KERN_DEBUG, "got alignmask %d\n", ses_new->alignmask);
++ ctx->sess.mac = CRYPTO_SHA1_HMAC;
++ ctx->sess.mackeylen = mac_key_size;
++ ctx->sess.mackey = (void*)mac_key;
+
-+ ses_new->array_size = DEFAULT_PREALLOC_PAGES;
-+ dprintk(2, KERN_DEBUG, "preallocating for %d user pages\n",
-+ ses_new->array_size);
-+ ses_new->pages = kzalloc(ses_new->array_size *
-+ sizeof(struct page *), GFP_KERNEL);
-+ ses_new->sg = kzalloc(ses_new->array_size *
-+ sizeof(struct scatterlist), GFP_KERNEL);
-+ if (ses_new->sg == NULL || ses_new->pages == NULL) {
-+ dprintk(0, KERN_DEBUG, "Memory error\n");
-+ ret = -ENOMEM;
-+ goto error_hash;
++ if (ioctl(ctx->cfd, CIOCGSESSION, &ctx->sess)) {
++ perror("ioctl(CIOCGSESSION)");
++ return -1;
+ }
+
-+ /* put the new session to the list */
-+ get_random_bytes(&ses_new->sid, sizeof(ses_new->sid));
-+ mutex_init(&ses_new->sem);
-+
-+ mutex_lock(&fcr->sem);
-+restart:
-+ list_for_each_entry(ses_ptr, &fcr->list, entry) {
-+ /* Check for duplicate SID */
-+ if (unlikely(ses_new->sid == ses_ptr->sid)) {
-+ get_random_bytes(&ses_new->sid, sizeof(ses_new->sid));
-+ /* Unless we have a broken RNG this
-+ shouldn't loop forever... ;-) */
-+ goto restart;
-+ }
++#ifdef CIOCGSESSINFO
++ siop.ses = ctx->sess.ses;
++ if (ioctl(ctx->cfd, CIOCGSESSINFO, &siop)) {
++ perror("ioctl(CIOCGSESSINFO)");
++ return -1;
+ }
-+
-+ list_add(&ses_new->entry, &fcr->list);
-+ mutex_unlock(&fcr->sem);
-+
-+ /* Fill in some values for the user. */
-+ sop->ses = ses_new->sid;
-+
++ printf("Got %s with driver %s\n",
++ siop.cipher_info.cra_name, siop.cipher_info.cra_driver_name);
++ if (!(siop.flags & SIOP_FLAG_KERNEL_DRIVER_ONLY)) {
++ printf("Note: This is not an accelerated cipher\n");
++ }
++ /*printf("Alignmask is %x\n", (unsigned int)siop.alignmask); */
++ ctx->alignmask = siop.alignmask;
++#endif
+ return 0;
-+
-+error_hash:
-+ cryptodev_cipher_deinit(&ses_new->cdata);
-+ kfree(ses_new->sg);
-+ kfree(ses_new->pages);
-+error_cipher:
-+ kfree(ses_new);
-+
-+ return ret;
-+
+}
+
-+/* Everything that needs to be done when remowing a session. */
-+static inline void
-+crypto_destroy_session(struct csession *ses_ptr)
++void aes_sha1_ctx_deinit(struct cryptodev_ctx* ctx)
+{
-+ if (!mutex_trylock(&ses_ptr->sem)) {
-+ dprintk(2, KERN_DEBUG, "Waiting for semaphore of sid=0x%08X\n",
-+ ses_ptr->sid);
-+ mutex_lock(&ses_ptr->sem);
++ if (ioctl(ctx->cfd, CIOCFSESSION, &ctx->sess.ses)) {
++ perror("ioctl(CIOCFSESSION)");
+ }
-+ dprintk(2, KERN_DEBUG, "Removed session 0x%08X\n", ses_ptr->sid);
-+ cryptodev_cipher_deinit(&ses_ptr->cdata);
-+ cryptodev_hash_deinit(&ses_ptr->hdata);
-+ dprintk(2, KERN_DEBUG, "freeing space for %d user pages\n",
-+ ses_ptr->array_size);
-+ kfree(ses_ptr->pages);
-+ kfree(ses_ptr->sg);
-+ mutex_unlock(&ses_ptr->sem);
-+ kfree(ses_ptr);
+}
+
-+/* Look up a session by ID and remove. */
-+static int
-+crypto_finish_session(struct fcrypt *fcr, uint32_t sid)
++int
++aes_sha1_encrypt(struct cryptodev_ctx* ctx, const void* iv,
++ const void* auth, size_t auth_size,
++ void* plaintext, size_t size)
+{
-+ struct csession *tmp, *ses_ptr;
-+ struct list_head *head;
-+ int ret = 0;
-+
-+ mutex_lock(&fcr->sem);
-+ head = &fcr->list;
-+ list_for_each_entry_safe(ses_ptr, tmp, head, entry) {
-+ if (ses_ptr->sid == sid) {
-+ list_del(&ses_ptr->entry);
-+ crypto_destroy_session(ses_ptr);
-+ break;
++ struct crypt_auth_op cryp;
++ void* p;
++
++ /* check plaintext and ciphertext alignment */
++ if (ctx->alignmask) {
++ p = (void*)(((unsigned long)plaintext + ctx->alignmask) & ~ctx->alignmask);
++ if (plaintext != p) {
++ fprintf(stderr, "plaintext is not aligned\n");
++ return -1;
+ }
+ }
+
-+ if (unlikely(!ses_ptr)) {
-+ dprintk(1, KERN_ERR, "Session with sid=0x%08X not found!\n",
-+ sid);
-+ ret = -ENOENT;
++ memset(&cryp, 0, sizeof(cryp));
++
++ /* Encrypt data.in to data.encrypted */
++ cryp.ses = ctx->sess.ses;
++ cryp.iv = (void*)iv;
++ cryp.op = COP_ENCRYPT;
++ cryp.auth_len = auth_size;
++ cryp.auth_src = (void*)auth;
++ cryp.len = size;
++ cryp.src = (void*)plaintext;
++ cryp.dst = plaintext;
++ cryp.flags = COP_FLAG_AEAD_TLS_TYPE;
++ if (ioctl(ctx->cfd, CIOCAUTHCRYPT, &cryp)) {
++ perror("ioctl(CIOCAUTHCRYPT)");
++ return -1;
+ }
-+ mutex_unlock(&fcr->sem);
+
-+ return ret;
++ return 0;
+}
+
-+/* Remove all sessions when closing the file */
-+static int
-+crypto_finish_all_sessions(struct fcrypt *fcr)
++int
++aes_sha1_decrypt(struct cryptodev_ctx* ctx, const void* iv,
++ const void* auth, size_t auth_size,
++ void* ciphertext, size_t size)
+{
-+ struct csession *tmp, *ses_ptr;
-+ struct list_head *head;
++ struct crypt_auth_op cryp;
++ void* p;
++
++ /* check plaintext and ciphertext alignment */
++ if (ctx->alignmask) {
++ p = (void*)(((unsigned long)ciphertext + ctx->alignmask) & ~ctx->alignmask);
++ if (ciphertext != p) {
++ fprintf(stderr, "ciphertext is not aligned\n");
++ return -1;
++ }
++ }
+
-+ mutex_lock(&fcr->sem);
++ memset(&cryp, 0, sizeof(cryp));
+
-+ head = &fcr->list;
-+ list_for_each_entry_safe(ses_ptr, tmp, head, entry) {
-+ list_del(&ses_ptr->entry);
-+ crypto_destroy_session(ses_ptr);
++ /* Encrypt data.in to data.encrypted */
++ cryp.ses = ctx->sess.ses;
++ cryp.iv = (void*)iv;
++ cryp.op = COP_DECRYPT;
++ cryp.auth_len = auth_size;
++ cryp.auth_src = (void*)auth;
++ cryp.len = size;
++ cryp.src = (void*)ciphertext;
++ cryp.dst = ciphertext;
++ cryp.flags = COP_FLAG_AEAD_TLS_TYPE;
++ if (ioctl(ctx->cfd, CIOCAUTHCRYPT, &cryp)) {
++ perror("ioctl(CIOCAUTHCRYPT)");
++ return -1;
+ }
-+ mutex_unlock(&fcr->sem);
+
+ return 0;
+}
+
-+/* Look up session by session ID. The returned session is locked. */
-+struct csession *
-+crypto_get_session_by_sid(struct fcrypt *fcr, uint32_t sid)
-+{
-+ struct csession *ses_ptr, *retval = NULL;
+diff --git a/crypto/cryptodev/examples/aes-sha1.h b/crypto/cryptodev/examples/aes-sha1.h
+new file mode 100644
+index 0000000..a07334c
+--- /dev/null
++++ b/crypto/cryptodev/examples/aes-sha1.h
+@@ -0,0 +1,31 @@
++#ifndef AES_H
++# define AES_H
+
-+ if (unlikely(fcr == NULL))
-+ return NULL;
++#include <stdint.h>
+
-+ mutex_lock(&fcr->sem);
-+ list_for_each_entry(ses_ptr, &fcr->list, entry) {
-+ if (ses_ptr->sid == sid) {
-+ mutex_lock(&ses_ptr->sem);
-+ retval = ses_ptr;
-+ break;
-+ }
-+ }
-+ mutex_unlock(&fcr->sem);
++struct cryptodev_ctx {
++ int cfd;
++ struct session_op sess;
++ uint16_t alignmask;
++};
+
-+ return retval;
-+}
++#define AES_BLOCK_SIZE 16
+
-+static void cryptask_routine(struct work_struct *work)
-+{
-+ struct crypt_priv *pcr = container_of(work, struct crypt_priv, cryptask);
-+ struct todo_list_item *item;
-+ LIST_HEAD(tmp);
-+
-+ /* fetch all pending jobs into the temporary list */
-+ mutex_lock(&pcr->todo.lock);
-+ list_cut_position(&tmp, &pcr->todo.list, pcr->todo.list.prev);
-+ mutex_unlock(&pcr->todo.lock);
++int aes_sha1_ctx_init(struct cryptodev_ctx* ctx, int cfd,
++ const uint8_t *key, unsigned int key_size,
++ const uint8_t *mac_key, unsigned int mac_key_size);
++void aes_sha1_ctx_deinit();
+
-+ /* handle each job locklessly */
-+ list_for_each_entry(item, &tmp, __hook) {
-+ item->result = crypto_run(&pcr->fcrypt, &item->kcop);
-+ if (unlikely(item->result))
-+ dprintk(0, KERN_ERR, "crypto_run() failed: %d\n",
-+ item->result);
-+ }
++/* Note that encryption assumes that ciphertext has enough size
++ * for the tag and padding to be appended.
++ *
++ * Only in-place encryption and decryption are supported.
++ */
++int aes_sha1_encrypt(struct cryptodev_ctx* ctx, const void* iv,
++ const void* auth, size_t auth_size,
++ void* plaintext, size_t size);
++int aes_sha1_decrypt(struct cryptodev_ctx* ctx, const void* iv,
++ const void* auth, size_t auth_size,
++ void* ciphertext, size_t size);
+
-+ /* push all handled jobs to the done list at once */
-+ mutex_lock(&pcr->done.lock);
-+ list_splice_tail(&tmp, &pcr->done.list);
-+ mutex_unlock(&pcr->done.lock);
++#endif
+diff --git a/crypto/cryptodev/examples/aes.c b/crypto/cryptodev/examples/aes.c
+new file mode 100644
+index 0000000..02f7613
+--- /dev/null
++++ b/crypto/cryptodev/examples/aes.c
+@@ -0,0 +1,242 @@
++/*
++ * Demo on how to use /dev/crypto device for ciphering.
++ *
++ * Placed under public domain.
++ *
++ */
++#include <stdio.h>
++#include <string.h>
++#include <unistd.h>
++#include <fcntl.h>
++#include <sys/ioctl.h>
++#include <crypto/cryptodev.h>
++#include "aes.h"
+
-+ /* wake for POLLIN */
-+ wake_up_interruptible(&pcr->user_waiter);
-+}
++#define KEY_SIZE 16
+
-+/* ====== /dev/crypto ====== */
+
-+static int
-+cryptodev_open(struct inode *inode, struct file *filp)
++int aes_ctx_init(struct cryptodev_ctx* ctx, int cfd, const uint8_t *key, unsigned int key_size)
+{
-+ struct todo_list_item *tmp;
-+ struct crypt_priv *pcr;
-+ int i;
++#ifdef CIOCGSESSINFO
++ struct session_info_op siop;
++#endif
+
-+ pcr = kmalloc(sizeof(*pcr), GFP_KERNEL);
-+ if (!pcr)
-+ return -ENOMEM;
++ memset(ctx, 0, sizeof(*ctx));
++ ctx->cfd = cfd;
+
-+ memset(pcr, 0, sizeof(*pcr));
-+ mutex_init(&pcr->fcrypt.sem);
-+ INIT_LIST_HEAD(&pcr->fcrypt.list);
++ ctx->sess.cipher = CRYPTO_AES_CBC;
++ ctx->sess.keylen = key_size;
++ ctx->sess.key = (void*)key;
++ if (ioctl(ctx->cfd, CIOCGSESSION, &ctx->sess)) {
++ perror("ioctl(CIOCGSESSION)");
++ return -1;
++ }
+
-+ INIT_LIST_HEAD(&pcr->free.list);
-+ INIT_LIST_HEAD(&pcr->todo.list);
-+ INIT_LIST_HEAD(&pcr->done.list);
-+ INIT_WORK(&pcr->cryptask, cryptask_routine);
-+ mutex_init(&pcr->free.lock);
-+ mutex_init(&pcr->todo.lock);
-+ mutex_init(&pcr->done.lock);
-+ init_waitqueue_head(&pcr->user_waiter);
++#ifdef CIOCGSESSINFO
++ memset(&siop, 0, sizeof(siop));
+
-+ for (i = 0; i < DEF_COP_RINGSIZE; i++) {
-+ tmp = kzalloc(sizeof(struct todo_list_item), GFP_KERNEL);
-+ if (!tmp)
-+ return -ENOMEM;
-+ pcr->itemcount++;
-+ dprintk(2, KERN_DEBUG, "allocated new item at %lx\n",
-+ (unsigned long)tmp);
-+ list_add(&tmp->__hook, &pcr->free.list);
++ siop.ses = ctx->sess.ses;
++ if (ioctl(ctx->cfd, CIOCGSESSINFO, &siop)) {
++ perror("ioctl(CIOCGSESSINFO)");
++ return -1;
+ }
-+
-+ filp->private_data = pcr;
-+ dprintk(2, KERN_DEBUG,
-+ "Cryptodev handle initialised, %d elements in queue\n",
-+ DEF_COP_RINGSIZE);
++ printf("Got %s with driver %s\n",
++ siop.cipher_info.cra_name, siop.cipher_info.cra_driver_name);
++ if (!(siop.flags & SIOP_FLAG_KERNEL_DRIVER_ONLY)) {
++ printf("Note: This is not an accelerated cipher\n");
++ }
++ /*printf("Alignmask is %x\n", (unsigned int)siop.alignmask); */
++ ctx->alignmask = siop.alignmask;
++#endif
+ return 0;
+}
+
-+static int
-+cryptodev_release(struct inode *inode, struct file *filp)
++void aes_ctx_deinit(struct cryptodev_ctx* ctx)
+{
-+ struct crypt_priv *pcr = filp->private_data;
-+ struct todo_list_item *item, *item_safe;
-+ int items_freed = 0;
-+
-+ if (!pcr)
-+ return 0;
-+
-+ cancel_work_sync(&pcr->cryptask);
-+
-+ mutex_destroy(&pcr->todo.lock);
-+ mutex_destroy(&pcr->done.lock);
-+ mutex_destroy(&pcr->free.lock);
-+
-+ list_splice_tail(&pcr->todo.list, &pcr->free.list);
-+ list_splice_tail(&pcr->done.list, &pcr->free.list);
++ if (ioctl(ctx->cfd, CIOCFSESSION, &ctx->sess.ses)) {
++ perror("ioctl(CIOCFSESSION)");
++ }
++}
+
-+ list_for_each_entry_safe(item, item_safe, &pcr->free.list, __hook) {
-+ dprintk(2, KERN_DEBUG, "freeing item at %lx\n",
-+ (unsigned long)item);
-+ list_del(&item->__hook);
-+ kfree(item);
-+ items_freed++;
++int
++aes_encrypt(struct cryptodev_ctx* ctx, const void* iv, const void* plaintext, void* ciphertext, size_t size)
++{
++ struct crypt_op cryp;
++ void* p;
++
++ /* check plaintext and ciphertext alignment */
++ if (ctx->alignmask) {
++ p = (void*)(((unsigned long)plaintext + ctx->alignmask) & ~ctx->alignmask);
++ if (plaintext != p) {
++ fprintf(stderr, "plaintext is not aligned\n");
++ return -1;
++ }
+
-+ }
-+ if (items_freed != pcr->itemcount) {
-+ dprintk(0, KERN_ERR,
-+ "freed %d items, but %d should exist!\n",
-+ items_freed, pcr->itemcount);
++ p = (void*)(((unsigned long)ciphertext + ctx->alignmask) & ~ctx->alignmask);
++ if (ciphertext != p) {
++ fprintf(stderr, "ciphertext is not aligned\n");
++ return -1;
++ }
+ }
+
-+ crypto_finish_all_sessions(&pcr->fcrypt);
-+ kfree(pcr);
-+ filp->private_data = NULL;
-+
-+ dprintk(2, KERN_DEBUG,
-+ "Cryptodev handle deinitialised, %d elements freed\n",
-+ items_freed);
-+ return 0;
-+}
++ memset(&cryp, 0, sizeof(cryp));
+
-+static int
-+clonefd(struct file *filp)
-+{
-+ int ret;
-+ ret = get_unused_fd();
-+ if (ret >= 0) {
-+ get_file(filp);
-+ fd_install(ret, filp);
++ /* Encrypt data.in to data.encrypted */
++ cryp.ses = ctx->sess.ses;
++ cryp.len = size;
++ cryp.src = (void*)plaintext;
++ cryp.dst = ciphertext;
++ cryp.iv = (void*)iv;
++ cryp.op = COP_ENCRYPT;
++ if (ioctl(ctx->cfd, CIOCCRYPT, &cryp)) {
++ perror("ioctl(CIOCCRYPT)");
++ return -1;
+ }
+
-+ return ret;
++ return 0;
+}
+
-+#ifdef ENABLE_ASYNC
-+/* enqueue a job for asynchronous completion
-+ *
-+ * returns:
-+ * -EBUSY when there are no free queue slots left
-+ * (and the number of slots has reached it MAX_COP_RINGSIZE)
-+ * -EFAULT when there was a memory allocation error
-+ * 0 on success */
-+static int crypto_async_run(struct crypt_priv *pcr, struct kernel_crypt_op *kcop)
++int
++aes_decrypt(struct cryptodev_ctx* ctx, const void* iv, const void* ciphertext, void* plaintext, size_t size)
+{
-+ struct todo_list_item *item = NULL;
++ struct crypt_op cryp;
++ void* p;
+
-+ if (unlikely(kcop->cop.flags & COP_FLAG_NO_ZC))
-+ return -EINVAL;
-+
-+ mutex_lock(&pcr->free.lock);
-+ if (likely(!list_empty(&pcr->free.list))) {
-+ item = list_first_entry(&pcr->free.list,
-+ struct todo_list_item, __hook);
-+ list_del(&item->__hook);
-+ } else if (pcr->itemcount < MAX_COP_RINGSIZE) {
-+ pcr->itemcount++;
-+ } else {
-+ mutex_unlock(&pcr->free.lock);
-+ return -EBUSY;
-+ }
-+ mutex_unlock(&pcr->free.lock);
++ /* check plaintext and ciphertext alignment */
++ if (ctx->alignmask) {
++ p = (void*)(((unsigned long)plaintext + ctx->alignmask) & ~ctx->alignmask);
++ if (plaintext != p) {
++ fprintf(stderr, "plaintext is not aligned\n");
++ return -1;
++ }
+
-+ if (unlikely(!item)) {
-+ item = kzalloc(sizeof(struct todo_list_item), GFP_KERNEL);
-+ if (unlikely(!item))
-+ return -EFAULT;
-+ dprintk(1, KERN_INFO, "increased item count to %d\n",
-+ pcr->itemcount);
++ p = (void*)(((unsigned long)ciphertext + ctx->alignmask) & ~ctx->alignmask);
++ if (ciphertext != p) {
++ fprintf(stderr, "ciphertext is not aligned\n");
++ return -1;
++ }
+ }
+
-+ memcpy(&item->kcop, kcop, sizeof(struct kernel_crypt_op));
++ memset(&cryp, 0, sizeof(cryp));
+
-+ mutex_lock(&pcr->todo.lock);
-+ list_add_tail(&item->__hook, &pcr->todo.list);
-+ mutex_unlock(&pcr->todo.lock);
++ /* Encrypt data.in to data.encrypted */
++ cryp.ses = ctx->sess.ses;
++ cryp.len = size;
++ cryp.src = (void*)ciphertext;
++ cryp.dst = plaintext;
++ cryp.iv = (void*)iv;
++ cryp.op = COP_DECRYPT;
++ if (ioctl(ctx->cfd, CIOCCRYPT, &cryp)) {
++ perror("ioctl(CIOCCRYPT)");
++ return -1;
++ }
+
-+ queue_work(cryptodev_wq, &pcr->cryptask);
+ return 0;
+}
+
-+/* get the first completed job from the "done" queue
-+ *
-+ * returns:
-+ * -EBUSY if no completed jobs are ready (yet)
-+ * the return value of crypto_run() otherwise */
-+static int crypto_async_fetch(struct crypt_priv *pcr,
-+ struct kernel_crypt_op *kcop)
++static int test_aes(int cfd)
+{
-+ struct todo_list_item *item;
-+ int retval;
++ char plaintext1_raw[AES_BLOCK_SIZE + 63], *plaintext1;
++ char ciphertext1[AES_BLOCK_SIZE] = { 0xdf, 0x55, 0x6a, 0x33, 0x43, 0x8d, 0xb8, 0x7b, 0xc4, 0x1b, 0x17, 0x52, 0xc5, 0x5e, 0x5e, 0x49 };
++ char iv1[AES_BLOCK_SIZE];
++ uint8_t key1[KEY_SIZE] = { 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
++ char plaintext2_data[AES_BLOCK_SIZE] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00 };
++ char plaintext2_raw[AES_BLOCK_SIZE + 63], *plaintext2;
++ char ciphertext2[AES_BLOCK_SIZE] = { 0xb7, 0x97, 0x2b, 0x39, 0x41, 0xc4, 0x4b, 0x90, 0xaf, 0xa7, 0xb2, 0x64, 0xbf, 0xba, 0x73, 0x87 };
++ char iv2[AES_BLOCK_SIZE];
++ uint8_t key2[KEY_SIZE];
++ struct cryptodev_ctx ctx;
++
++ aes_ctx_init(&ctx, cfd, key1, sizeof(key1));
++
++ if (ctx.alignmask)
++ plaintext1 = (char *)(((unsigned long)plaintext1_raw + ctx.alignmask) & ~ctx.alignmask);
++ else
++ plaintext1 = plaintext1_raw;
+
-+ mutex_lock(&pcr->done.lock);
-+ if (list_empty(&pcr->done.list)) {
-+ mutex_unlock(&pcr->done.lock);
-+ return -EBUSY;
-+ }
-+ item = list_first_entry(&pcr->done.list, struct todo_list_item, __hook);
-+ list_del(&item->__hook);
-+ mutex_unlock(&pcr->done.lock);
++ memset(plaintext1, 0x0, AES_BLOCK_SIZE);
++ memset(iv1, 0x0, sizeof(iv1));
+
-+ memcpy(kcop, &item->kcop, sizeof(struct kernel_crypt_op));
-+ retval = item->result;
++ aes_encrypt(&ctx, iv1, plaintext1, plaintext1, AES_BLOCK_SIZE);
+
-+ mutex_lock(&pcr->free.lock);
-+ list_add_tail(&item->__hook, &pcr->free.list);
-+ mutex_unlock(&pcr->free.lock);
++ /* Verify the result */
++ if (memcmp(plaintext1, ciphertext1, AES_BLOCK_SIZE) != 0) {
++ fprintf(stderr,
++ "FAIL: Decrypted data are different from the input data.\n");
++ return -1;
++ }
++
++ aes_ctx_deinit(&ctx);
+
-+ /* wake for POLLOUT */
-+ wake_up_interruptible(&pcr->user_waiter);
++ /* Test 2 */
+
-+ return retval;
-+}
-+#endif
++ memset(key2, 0x0, sizeof(key2));
++ memset(iv2, 0x0, sizeof(iv2));
+
-+/* this function has to be called from process context */
-+static int fill_kcop_from_cop(struct kernel_crypt_op *kcop, struct fcrypt *fcr)
-+{
-+ struct crypt_op *cop = &kcop->cop;
-+ struct csession *ses_ptr;
-+ int rc;
++ aes_ctx_init(&ctx, cfd, key2, sizeof(key2));
+
-+ /* this also enters ses_ptr->sem */
-+ ses_ptr = crypto_get_session_by_sid(fcr, cop->ses);
-+ if (unlikely(!ses_ptr)) {
-+ dprintk(1, KERN_ERR, "invalid session ID=0x%08X\n", cop->ses);
-+ return -EINVAL;
++ if (ctx.alignmask) {
++ plaintext2 = (char *)(((unsigned long)plaintext2_raw + ctx.alignmask) & ~ctx.alignmask);
++ } else {
++ plaintext2 = plaintext2_raw;
+ }
-+ kcop->ivlen = cop->iv ? ses_ptr->cdata.ivsize : 0;
-+ kcop->digestsize = 0; /* will be updated during operation */
-+
-+ crypto_put_session(ses_ptr);
++ memcpy(plaintext2, plaintext2_data, AES_BLOCK_SIZE);
+
-+ kcop->task = current;
-+ kcop->mm = current->mm;
++ /* Encrypt data.in to data.encrypted */
++ aes_encrypt(&ctx, iv2, plaintext2, plaintext2, AES_BLOCK_SIZE);
+
-+ if (cop->iv) {
-+ rc = copy_from_user(kcop->iv, cop->iv, kcop->ivlen);
-+ if (unlikely(rc)) {
-+ dprintk(1, KERN_ERR,
-+ "error copying IV (%d bytes), copy_from_user returned %d for address %lx\n",
-+ kcop->ivlen, rc, (unsigned long)cop->iv);
-+ return -EFAULT;
++ /* Verify the result */
++ if (memcmp(plaintext2, ciphertext2, AES_BLOCK_SIZE) != 0) {
++ int i;
++ fprintf(stderr,
++ "FAIL: Decrypted data are different from the input data.\n");
++ printf("plaintext:");
++ for (i = 0; i < AES_BLOCK_SIZE; i++) {
++ printf("%02x ", plaintext2[i]);
++ }
++ printf("ciphertext:");
++ for (i = 0; i < AES_BLOCK_SIZE; i++) {
++ printf("%02x ", ciphertext2[i]);
+ }
++ printf("\n");
++ return 1;
+ }
++
++ aes_ctx_deinit(&ctx);
++
++ printf("AES Test passed\n");
+
+ return 0;
+}
+
-+/* this function has to be called from process context */
-+static int fill_cop_from_kcop(struct kernel_crypt_op *kcop, struct fcrypt *fcr)
++int
++main()
+{
-+ int ret;
++ int cfd = -1;
+
-+ if (kcop->digestsize) {
-+ ret = copy_to_user(kcop->cop.mac,
-+ kcop->hash_output, kcop->digestsize);
-+ if (unlikely(ret))
-+ return -EFAULT;
++ /* Open the crypto device */
++ cfd = open("/dev/crypto", O_RDWR, 0);
++ if (cfd < 0) {
++ perror("open(/dev/crypto)");
++ return 1;
+ }
-+ if (kcop->ivlen && kcop->cop.flags & COP_FLAG_WRITE_IV) {
-+ ret = copy_to_user(kcop->cop.iv,
++
++ /* Set close-on-exec (not really neede here) */
++ if (fcntl(cfd, F_SETFD, 1) == -1) {
++ perror("fcntl(F_SETFD)");
++ return 1;
++ }
++
++ /* Run the test itself */
++ if (test_aes(cfd))
++ return 1;
++
++ /* Close the original descriptor */
++ if (close(cfd)) {
++ perror("close(cfd)");
++ return 1;
++ }
++
++ return 0;
++}
++
+diff --git a/crypto/cryptodev/examples/aes.h b/crypto/cryptodev/examples/aes.h
+new file mode 100644
+index 0000000..ade90c9
+--- /dev/null
++++ b/crypto/cryptodev/examples/aes.h
+@@ -0,0 +1,19 @@
++#ifndef AES_H
++# define AES_H
++
++#include <stdint.h>
++
++struct cryptodev_ctx {
++ int cfd;
++ struct session_op sess;
++ uint16_t alignmask;
++};
++
++#define AES_BLOCK_SIZE 16
++
++int aes_ctx_init(struct cryptodev_ctx* ctx, int cfd, const uint8_t *key, unsigned int key_size);
++void aes_ctx_deinit();
++int aes_encrypt(struct cryptodev_ctx* ctx, const void* iv, const void* plaintext, void* ciphertext, size_t size);
++int aes_decrypt(struct cryptodev_ctx* ctx, const void* iv, const void* ciphertext, void* plaintext, size_t size);
++
++#endif
+diff --git a/crypto/cryptodev/examples/sha.c b/crypto/cryptodev/examples/sha.c
+new file mode 100644
+index 0000000..4f45a6b
+--- /dev/null
++++ b/crypto/cryptodev/examples/sha.c
+@@ -0,0 +1,137 @@
++/*
++ * Demo on how to use /dev/crypto device for ciphering.
++ *
++ * Placed under public domain.
++ *
++ */
++#include <stdio.h>
++#include <string.h>
++#include <unistd.h>
++#include <fcntl.h>
++#include <sys/ioctl.h>
++#include <crypto/cryptodev.h>
++#include "sha.h"
++
++int sha_ctx_init(struct cryptodev_ctx* ctx, int cfd, const uint8_t *key, unsigned int key_size)
++{
++#ifdef CIOCGSESSINFO
++ struct session_info_op siop;
++#endif
++
++ memset(ctx, 0, sizeof(*ctx));
++ ctx->cfd = cfd;
++
++ if (key == NULL)
++ ctx->sess.mac = CRYPTO_SHA1;
++ else {
++ ctx->sess.mac = CRYPTO_SHA1_HMAC;
++ ctx->sess.mackeylen = key_size;
++ ctx->sess.mackey = (void*)key;
++ }
++ if (ioctl(ctx->cfd, CIOCGSESSION, &ctx->sess)) {
++ perror("ioctl(CIOCGSESSION)");
++ return -1;
++ }
++
++#ifdef CIOCGSESSINFO
++ siop.ses = ctx->sess.ses;
++ if (ioctl(ctx->cfd, CIOCGSESSINFO, &siop)) {
++ perror("ioctl(CIOCGSESSINFO)");
++ return -1;
++ }
++ printf("Got %s with driver %s\n",
++ siop.hash_info.cra_name, siop.hash_info.cra_driver_name);
++ if (!(siop.flags & SIOP_FLAG_KERNEL_DRIVER_ONLY)) {
++ printf("Note: This is not an accelerated cipher\n");
++ }
++ /*printf("Alignmask is %x\n", (unsigned int)siop.alignmask);*/
++ ctx->alignmask = siop.alignmask;
++#endif
++ return 0;
++}
++
++void sha_ctx_deinit(struct cryptodev_ctx* ctx)
++{
++ if (ioctl(ctx->cfd, CIOCFSESSION, &ctx->sess.ses)) {
++ perror("ioctl(CIOCFSESSION)");
++ }
++}
++
++int
++sha_hash(struct cryptodev_ctx* ctx, const void* text, size_t size, void* digest)
++{
++ struct crypt_op cryp;
++ void* p;
++
++ /* check text and ciphertext alignment */
++ if (ctx->alignmask) {
++ p = (void*)(((unsigned long)text + ctx->alignmask) & ~ctx->alignmask);
++ if (text != p) {
++ fprintf(stderr, "text is not aligned\n");
++ return -1;
++ }
++ }
++
++ memset(&cryp, 0, sizeof(cryp));
++
++ /* Encrypt data.in to data.encrypted */
++ cryp.ses = ctx->sess.ses;
++ cryp.len = size;
++ cryp.src = (void*)text;
++ cryp.mac = digest;
++ if (ioctl(ctx->cfd, CIOCCRYPT, &cryp)) {
++ perror("ioctl(CIOCCRYPT)");
++ return -1;
++ }
++
++ return 0;
++}
++
++int
++main()
++{
++ int cfd = -1, i;
++ struct cryptodev_ctx ctx;
++ uint8_t digest[20];
++ char text[] = "The quick brown fox jumps over the lazy dog";
++ uint8_t expected[] = "\x2f\xd4\xe1\xc6\x7a\x2d\x28\xfc\xed\x84\x9e\xe1\xbb\x76\xe7\x39\x1b\x93\xeb\x12";
++
++ /* Open the crypto device */
++ cfd = open("/dev/crypto", O_RDWR, 0);
++ if (cfd < 0) {
++ perror("open(/dev/crypto)");
++ return 1;
++ }
++
++ /* Set close-on-exec (not really neede here) */
++ if (fcntl(cfd, F_SETFD, 1) == -1) {
++ perror("fcntl(F_SETFD)");
++ return 1;
++ }
++
++ sha_ctx_init(&ctx, cfd, NULL, 0);
++
++ sha_hash(&ctx, text, strlen(text), digest);
++
++ sha_ctx_deinit(&ctx);
++
++ printf("digest: ");
++ for (i = 0; i < 20; i++) {
++ printf("%02x:", digest[i]);
++ }
++ printf("\n");
++
++ if (memcmp(digest, expected, 20) != 0) {
++ fprintf(stderr, "SHA1 hashing failed\n");
++ return 1;
++ }
++
++ /* Close the original descriptor */
++ if (close(cfd)) {
++ perror("close(cfd)");
++ return 1;
++ }
++
++ return 0;
++}
++
+diff --git a/crypto/cryptodev/examples/sha.h b/crypto/cryptodev/examples/sha.h
+new file mode 100644
+index 0000000..ed0b8ce
+--- /dev/null
++++ b/crypto/cryptodev/examples/sha.h
+@@ -0,0 +1,16 @@
++#ifndef SHA_H
++# define SHA_H
++
++#include <stdint.h>
++
++struct cryptodev_ctx {
++ int cfd;
++ struct session_op sess;
++ uint16_t alignmask;
++};
++
++int sha_ctx_init(struct cryptodev_ctx* ctx, int cfd, const uint8_t *key, unsigned int key_size);
++void sha_ctx_deinit();
++int sha_hash(struct cryptodev_ctx* ctx, const void* text, size_t size, void* digest);
++
++#endif
+diff --git a/crypto/cryptodev/ioctl.c b/crypto/cryptodev/ioctl.c
+new file mode 100644
+index 0000000..5a55a76
+--- /dev/null
++++ b/crypto/cryptodev/ioctl.c
+@@ -0,0 +1,1169 @@
++/*
++ * Driver for /dev/crypto device (aka CryptoDev)
++ *
++ * Copyright (c) 2004 Michal Ludvig <mludvig at logix.net.nz>, SuSE Labs
++ * Copyright (c) 2009,2010,2011 Nikos Mavrogiannopoulos <nmav at gnutls.org>
++ * Copyright (c) 2010 Phil Sutter
++ *
++ * This file is part of linux cryptodev.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc.,
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++ */
++
++/*
++ * Device /dev/crypto provides an interface for
++ * accessing kernel CryptoAPI algorithms (ciphers,
++ * hashes) from userspace programs.
++ *
++ * /dev/crypto interface was originally introduced in
++ * OpenBSD and this module attempts to keep the API.
++ *
++ */
++
++#include <crypto/hash.h>
++#include <linux/crypto.h>
++#include <linux/mm.h>
++#include <linux/highmem.h>
++#include <linux/ioctl.h>
++#include <linux/random.h>
++#include <linux/syscalls.h>
++#include <linux/pagemap.h>
++#include <linux/poll.h>
++#include <linux/uaccess.h>
++#include <crypto/cryptodev.h>
++#include <linux/scatterlist.h>
++#include <linux/rtnetlink.h>
++#include <crypto/authenc.h>
++
++#include <linux/sysctl.h>
++
++#include "cryptodev_int.h"
++#include "zc.h"
++#include "version.h"
++
++MODULE_AUTHOR("Nikos Mavrogiannopoulos <nmav at gnutls.org>");
++MODULE_DESCRIPTION("CryptoDev driver");
++MODULE_LICENSE("GPL");
++
++/* ====== Compile-time config ====== */
++
++/* Default (pre-allocated) and maximum size of the job queue.
++ * These are free, pending and done items all together. */
++#define DEF_COP_RINGSIZE 16
++#define MAX_COP_RINGSIZE 64
++
++/* ====== Module parameters ====== */
++
++int cryptodev_verbosity;
++module_param(cryptodev_verbosity, int, 0644);
++MODULE_PARM_DESC(cryptodev_verbosity, "0: normal, 1: verbose, 2: debug");
++
++/* ====== CryptoAPI ====== */
++struct todo_list_item {
++ struct list_head __hook;
++ struct kernel_crypt_op kcop;
++ int result;
++};
++
++struct locked_list {
++ struct list_head list;
++ struct mutex lock;
++};
++
++struct crypt_priv {
++ struct fcrypt fcrypt;
++ struct locked_list free, todo, done;
++ int itemcount;
++ struct work_struct cryptask;
++ wait_queue_head_t user_waiter;
++};
++
++#define FILL_SG(sg, ptr, len) \
++ do { \
++ (sg)->page = virt_to_page(ptr); \
++ (sg)->offset = offset_in_page(ptr); \
++ (sg)->length = len; \
++ (sg)->dma_address = 0; \
++ } while (0)
++
++/* cryptodev's own workqueue, keeps crypto tasks from disturbing the force */
++static struct workqueue_struct *cryptodev_wq;
++
++/* Prepare session for future use. */
++static int
++crypto_create_session(struct fcrypt *fcr, struct session_op *sop)
++{
++ struct csession *ses_new = NULL, *ses_ptr;
++ int ret = 0;
++ const char *alg_name = NULL;
++ const char *hash_name = NULL;
++ int hmac_mode = 1, stream = 0, aead = 0;
++ /*
++ * With composite aead ciphers, only ckey is used and it can cover all the
++ * structure space; otherwise both keys may be used simultaneously but they
++ * are confined to their spaces
++ */
++ struct {
++ uint8_t ckey[CRYPTO_CIPHER_MAX_KEY_LEN];
++ uint8_t mkey[CRYPTO_HMAC_MAX_KEY_LEN];
++ /* padding space for aead keys */
++ uint8_t pad[RTA_SPACE(sizeof(struct crypto_authenc_key_param))];
++ } keys;
++
++ /* Does the request make sense? */
++ if (unlikely(!sop->cipher && !sop->mac)) {
++ ddebug(1, "Both 'cipher' and 'mac' unset.");
++ return -EINVAL;
++ }
++
++ switch (sop->cipher) {
++ case 0:
++ break;
++ case CRYPTO_DES_CBC:
++ alg_name = "cbc(des)";
++ break;
++ case CRYPTO_3DES_CBC:
++ alg_name = "cbc(des3_ede)";
++ break;
++ case CRYPTO_BLF_CBC:
++ alg_name = "cbc(blowfish)";
++ break;
++ case CRYPTO_AES_CBC:
++ alg_name = "cbc(aes)";
++ break;
++ case CRYPTO_AES_ECB:
++ alg_name = "ecb(aes)";
++ break;
++ case CRYPTO_CAMELLIA_CBC:
++ alg_name = "cbc(camellia)";
++ break;
++ case CRYPTO_AES_CTR:
++ alg_name = "ctr(aes)";
++ stream = 1;
++ break;
++ case CRYPTO_AES_GCM:
++ alg_name = "gcm(aes)";
++ stream = 1;
++ aead = 1;
++ break;
++ case CRYPTO_NULL:
++ alg_name = "ecb(cipher_null)";
++ stream = 1;
++ break;
++ default:
++ ddebug(1, "bad cipher: %d", sop->cipher);
++ return -EINVAL;
++ }
++
++ switch (sop->mac) {
++ case 0:
++ break;
++ case CRYPTO_MD5_HMAC:
++ hash_name = "hmac(md5)";
++ break;
++ case CRYPTO_RIPEMD160_HMAC:
++ hash_name = "hmac(rmd160)";
++ break;
++ case CRYPTO_SHA1_HMAC:
++ hash_name = "hmac(sha1)";
++ break;
++ case CRYPTO_SHA2_224_HMAC:
++ hash_name = "hmac(sha224)";
++ break;
++
++ case CRYPTO_SHA2_256_HMAC:
++ hash_name = "hmac(sha256)";
++ break;
++ case CRYPTO_SHA2_384_HMAC:
++ hash_name = "hmac(sha384)";
++ break;
++ case CRYPTO_SHA2_512_HMAC:
++ hash_name = "hmac(sha512)";
++ break;
++
++ /* non-hmac cases */
++ case CRYPTO_MD5:
++ hash_name = "md5";
++ hmac_mode = 0;
++ break;
++ case CRYPTO_RIPEMD160:
++ hash_name = "rmd160";
++ hmac_mode = 0;
++ break;
++ case CRYPTO_SHA1:
++ hash_name = "sha1";
++ hmac_mode = 0;
++ break;
++ case CRYPTO_SHA2_224:
++ hash_name = "sha224";
++ hmac_mode = 0;
++ break;
++ case CRYPTO_SHA2_256:
++ hash_name = "sha256";
++ hmac_mode = 0;
++ break;
++ case CRYPTO_SHA2_384:
++ hash_name = "sha384";
++ hmac_mode = 0;
++ break;
++ case CRYPTO_SHA2_512:
++ hash_name = "sha512";
++ hmac_mode = 0;
++ break;
++ default:
++ ddebug(1, "bad mac: %d", sop->mac);
++ return -EINVAL;
++ }
++
++ /* Create a session and put it to the list. */
++ ses_new = kzalloc(sizeof(*ses_new), GFP_KERNEL);
++ if (!ses_new)
++ return -ENOMEM;
++
++ /* Set-up crypto transform. */
++ if (alg_name) {
++ unsigned int keylen;
++ ret = cryptodev_get_cipher_keylen(&keylen, sop, aead);
++ if (unlikely(ret < 0)) {
++ ddebug(1, "Setting key failed for %s-%zu.",
++ alg_name, (size_t)sop->keylen*8);
++ goto error_cipher;
++ }
++
++ ret = cryptodev_get_cipher_key(keys.ckey, sop, aead);
++ if (unlikely(ret < 0))
++ goto error_cipher;
++
++ ret = cryptodev_cipher_init(&ses_new->cdata, alg_name, keys.ckey,
++ keylen, stream, aead);
++ if (ret < 0) {
++ ddebug(1, "Failed to load cipher for %s", alg_name);
++ ret = -EINVAL;
++ goto error_cipher;
++ }
++ }
++
++ if (hash_name && aead == 0) {
++ if (unlikely(sop->mackeylen > CRYPTO_HMAC_MAX_KEY_LEN)) {
++ ddebug(1, "Setting key failed for %s-%zu.",
++ hash_name, (size_t)sop->mackeylen*8);
++ ret = -EINVAL;
++ goto error_hash;
++ }
++
++ if (sop->mackey && unlikely(copy_from_user(keys.mkey, sop->mackey,
++ sop->mackeylen))) {
++ ret = -EFAULT;
++ goto error_hash;
++ }
++
++ ret = cryptodev_hash_init(&ses_new->hdata, hash_name, hmac_mode,
++ keys.mkey, sop->mackeylen);
++ if (ret != 0) {
++ ddebug(1, "Failed to load hash for %s", hash_name);
++ ret = -EINVAL;
++ goto error_hash;
++ }
++ }
++
++ ses_new->alignmask = max(ses_new->cdata.alignmask,
++ ses_new->hdata.alignmask);
++ ddebug(2, "got alignmask %d", ses_new->alignmask);
++
++ ses_new->array_size = DEFAULT_PREALLOC_PAGES;
++ ddebug(2, "preallocating for %d user pages", ses_new->array_size);
++ ses_new->pages = kzalloc(ses_new->array_size *
++ sizeof(struct page *), GFP_KERNEL);
++ ses_new->sg = kzalloc(ses_new->array_size *
++ sizeof(struct scatterlist), GFP_KERNEL);
++ if (ses_new->sg == NULL || ses_new->pages == NULL) {
++ ddebug(0, "Memory error");
++ ret = -ENOMEM;
++ goto error_hash;
++ }
++
++ /* put the new session to the list */
++ get_random_bytes(&ses_new->sid, sizeof(ses_new->sid));
++ mutex_init(&ses_new->sem);
++
++ mutex_lock(&fcr->sem);
++restart:
++ list_for_each_entry(ses_ptr, &fcr->list, entry) {
++ /* Check for duplicate SID */
++ if (unlikely(ses_new->sid == ses_ptr->sid)) {
++ get_random_bytes(&ses_new->sid, sizeof(ses_new->sid));
++ /* Unless we have a broken RNG this
++ shouldn't loop forever... ;-) */
++ goto restart;
++ }
++ }
++
++ list_add(&ses_new->entry, &fcr->list);
++ mutex_unlock(&fcr->sem);
++
++ /* Fill in some values for the user. */
++ sop->ses = ses_new->sid;
++
++ return 0;
++
++error_hash:
++ cryptodev_cipher_deinit(&ses_new->cdata);
++ kfree(ses_new->sg);
++ kfree(ses_new->pages);
++error_cipher:
++ kfree(ses_new);
++
++ return ret;
++
++}
++
++/* Everything that needs to be done when remowing a session. */
++static inline void
++crypto_destroy_session(struct csession *ses_ptr)
++{
++ if (!mutex_trylock(&ses_ptr->sem)) {
++ ddebug(2, "Waiting for semaphore of sid=0x%08X", ses_ptr->sid);
++ mutex_lock(&ses_ptr->sem);
++ }
++ ddebug(2, "Removed session 0x%08X", ses_ptr->sid);
++ cryptodev_cipher_deinit(&ses_ptr->cdata);
++ cryptodev_hash_deinit(&ses_ptr->hdata);
++ ddebug(2, "freeing space for %d user pages", ses_ptr->array_size);
++ kfree(ses_ptr->pages);
++ kfree(ses_ptr->sg);
++ mutex_unlock(&ses_ptr->sem);
++ mutex_destroy(&ses_ptr->sem);
++ kfree(ses_ptr);
++}
++
++/* Look up a session by ID and remove. */
++static int
++crypto_finish_session(struct fcrypt *fcr, uint32_t sid)
++{
++ struct csession *tmp, *ses_ptr;
++ struct list_head *head;
++ int ret = 0;
++
++ mutex_lock(&fcr->sem);
++ head = &fcr->list;
++ list_for_each_entry_safe(ses_ptr, tmp, head, entry) {
++ if (ses_ptr->sid == sid) {
++ list_del(&ses_ptr->entry);
++ crypto_destroy_session(ses_ptr);
++ break;
++ }
++ }
++
++ if (unlikely(!ses_ptr)) {
++ derr(1, "Session with sid=0x%08X not found!", sid);
++ ret = -ENOENT;
++ }
++ mutex_unlock(&fcr->sem);
++
++ return ret;
++}
++
++/* Remove all sessions when closing the file */
++static int
++crypto_finish_all_sessions(struct fcrypt *fcr)
++{
++ struct csession *tmp, *ses_ptr;
++ struct list_head *head;
++
++ mutex_lock(&fcr->sem);
++
++ head = &fcr->list;
++ list_for_each_entry_safe(ses_ptr, tmp, head, entry) {
++ list_del(&ses_ptr->entry);
++ crypto_destroy_session(ses_ptr);
++ }
++ mutex_unlock(&fcr->sem);
++
++ return 0;
++}
++
++/* Look up session by session ID. The returned session is locked. */
++struct csession *
++crypto_get_session_by_sid(struct fcrypt *fcr, uint32_t sid)
++{
++ struct csession *ses_ptr, *retval = NULL;
++
++ if (unlikely(fcr == NULL))
++ return NULL;
++
++ mutex_lock(&fcr->sem);
++ list_for_each_entry(ses_ptr, &fcr->list, entry) {
++ if (ses_ptr->sid == sid) {
++ mutex_lock(&ses_ptr->sem);
++ retval = ses_ptr;
++ break;
++ }
++ }
++ mutex_unlock(&fcr->sem);
++
++ return retval;
++}
++
++static void cryptask_routine(struct work_struct *work)
++{
++ struct crypt_priv *pcr = container_of(work, struct crypt_priv, cryptask);
++ struct todo_list_item *item;
++ LIST_HEAD(tmp);
++
++ /* fetch all pending jobs into the temporary list */
++ mutex_lock(&pcr->todo.lock);
++ list_cut_position(&tmp, &pcr->todo.list, pcr->todo.list.prev);
++ mutex_unlock(&pcr->todo.lock);
++
++ /* handle each job locklessly */
++ list_for_each_entry(item, &tmp, __hook) {
++ item->result = crypto_run(&pcr->fcrypt, &item->kcop);
++ if (unlikely(item->result))
++ derr(0, "crypto_run() failed: %d", item->result);
++ }
++
++ /* push all handled jobs to the done list at once */
++ mutex_lock(&pcr->done.lock);
++ list_splice_tail(&tmp, &pcr->done.list);
++ mutex_unlock(&pcr->done.lock);
++
++ /* wake for POLLIN */
++ wake_up_interruptible(&pcr->user_waiter);
++}
++
++/* ====== /dev/crypto ====== */
++
++static int
++cryptodev_open(struct inode *inode, struct file *filp)
++{
++ struct todo_list_item *tmp, *tmp_next;
++ struct crypt_priv *pcr;
++ int i;
++
++ pcr = kzalloc(sizeof(*pcr), GFP_KERNEL);
++ if (!pcr)
++ return -ENOMEM;
++ filp->private_data = pcr;
++
++ mutex_init(&pcr->fcrypt.sem);
++ mutex_init(&pcr->free.lock);
++ mutex_init(&pcr->todo.lock);
++ mutex_init(&pcr->done.lock);
++
++ INIT_LIST_HEAD(&pcr->fcrypt.list);
++ INIT_LIST_HEAD(&pcr->free.list);
++ INIT_LIST_HEAD(&pcr->todo.list);
++ INIT_LIST_HEAD(&pcr->done.list);
++
++ INIT_WORK(&pcr->cryptask, cryptask_routine);
++
++ init_waitqueue_head(&pcr->user_waiter);
++
++ for (i = 0; i < DEF_COP_RINGSIZE; i++) {
++ tmp = kzalloc(sizeof(struct todo_list_item), GFP_KERNEL);
++ if (!tmp)
++ goto err_ringalloc;
++ pcr->itemcount++;
++ ddebug(2, "allocated new item at %p", tmp);
++ list_add(&tmp->__hook, &pcr->free.list);
++ }
++
++ ddebug(2, "Cryptodev handle initialised, %d elements in queue",
++ DEF_COP_RINGSIZE);
++ return 0;
++
++/* In case of errors, free any memory allocated so far */
++err_ringalloc:
++ list_for_each_entry_safe(tmp, tmp_next, &pcr->free.list, __hook) {
++ list_del(&tmp->__hook);
++ kfree(tmp);
++ }
++ mutex_destroy(&pcr->done.lock);
++ mutex_destroy(&pcr->todo.lock);
++ mutex_destroy(&pcr->free.lock);
++ mutex_destroy(&pcr->fcrypt.sem);
++ kfree(pcr);
++ filp->private_data = NULL;
++ return -ENOMEM;
++}
++
++static int
++cryptodev_release(struct inode *inode, struct file *filp)
++{
++ struct crypt_priv *pcr = filp->private_data;
++ struct todo_list_item *item, *item_safe;
++ int items_freed = 0;
++
++ if (!pcr)
++ return 0;
++
++ cancel_work_sync(&pcr->cryptask);
++
++ list_splice_tail(&pcr->todo.list, &pcr->free.list);
++ list_splice_tail(&pcr->done.list, &pcr->free.list);
++
++ list_for_each_entry_safe(item, item_safe, &pcr->free.list, __hook) {
++ ddebug(2, "freeing item at %p", item);
++ list_del(&item->__hook);
++ kfree(item);
++ items_freed++;
++ }
++
++ if (items_freed != pcr->itemcount) {
++ derr(0, "freed %d items, but %d should exist!",
++ items_freed, pcr->itemcount);
++ }
++
++ crypto_finish_all_sessions(&pcr->fcrypt);
++
++ mutex_destroy(&pcr->done.lock);
++ mutex_destroy(&pcr->todo.lock);
++ mutex_destroy(&pcr->free.lock);
++ mutex_destroy(&pcr->fcrypt.sem);
++
++ kfree(pcr);
++ filp->private_data = NULL;
++
++ ddebug(2, "Cryptodev handle deinitialised, %d elements freed",
++ items_freed);
++ return 0;
++}
++
++static int
++clonefd(struct file *filp)
++{
++ int ret;
++ ret = get_unused_fd();
++ if (ret >= 0) {
++ get_file(filp);
++ fd_install(ret, filp);
++ }
++
++ return ret;
++}
++
++#ifdef ENABLE_ASYNC
++/* enqueue a job for asynchronous completion
++ *
++ * returns:
++ * -EBUSY when there are no free queue slots left
++ * (and the number of slots has reached it MAX_COP_RINGSIZE)
++ * -EFAULT when there was a memory allocation error
++ * 0 on success */
++static int crypto_async_run(struct crypt_priv *pcr, struct kernel_crypt_op *kcop)
++{
++ struct todo_list_item *item = NULL;
++
++ if (unlikely(kcop->cop.flags & COP_FLAG_NO_ZC))
++ return -EINVAL;
++
++ mutex_lock(&pcr->free.lock);
++ if (likely(!list_empty(&pcr->free.list))) {
++ item = list_first_entry(&pcr->free.list,
++ struct todo_list_item, __hook);
++ list_del(&item->__hook);
++ } else if (pcr->itemcount < MAX_COP_RINGSIZE) {
++ pcr->itemcount++;
++ } else {
++ mutex_unlock(&pcr->free.lock);
++ return -EBUSY;
++ }
++ mutex_unlock(&pcr->free.lock);
++
++ if (unlikely(!item)) {
++ item = kzalloc(sizeof(struct todo_list_item), GFP_KERNEL);
++ if (unlikely(!item))
++ return -EFAULT;
++ dinfo(1, "increased item count to %d", pcr->itemcount);
++ }
++
++ memcpy(&item->kcop, kcop, sizeof(struct kernel_crypt_op));
++
++ mutex_lock(&pcr->todo.lock);
++ list_add_tail(&item->__hook, &pcr->todo.list);
++ mutex_unlock(&pcr->todo.lock);
++
++ queue_work(cryptodev_wq, &pcr->cryptask);
++ return 0;
++}
++
++/* get the first completed job from the "done" queue
++ *
++ * returns:
++ * -EBUSY if no completed jobs are ready (yet)
++ * the return value of crypto_run() otherwise */
++static int crypto_async_fetch(struct crypt_priv *pcr,
++ struct kernel_crypt_op *kcop)
++{
++ struct todo_list_item *item;
++ int retval;
++
++ mutex_lock(&pcr->done.lock);
++ if (list_empty(&pcr->done.list)) {
++ mutex_unlock(&pcr->done.lock);
++ return -EBUSY;
++ }
++ item = list_first_entry(&pcr->done.list, struct todo_list_item, __hook);
++ list_del(&item->__hook);
++ mutex_unlock(&pcr->done.lock);
++
++ memcpy(kcop, &item->kcop, sizeof(struct kernel_crypt_op));
++ retval = item->result;
++
++ mutex_lock(&pcr->free.lock);
++ list_add_tail(&item->__hook, &pcr->free.list);
++ mutex_unlock(&pcr->free.lock);
++
++ /* wake for POLLOUT */
++ wake_up_interruptible(&pcr->user_waiter);
++
++ return retval;
++}
++#endif
++
++/* this function has to be called from process context */
++static int fill_kcop_from_cop(struct kernel_crypt_op *kcop, struct fcrypt *fcr)
++{
++ struct crypt_op *cop = &kcop->cop;
++ struct csession *ses_ptr;
++ int rc;
++
++ /* this also enters ses_ptr->sem */
++ ses_ptr = crypto_get_session_by_sid(fcr, cop->ses);
++ if (unlikely(!ses_ptr)) {
++ derr(1, "invalid session ID=0x%08X", cop->ses);
++ return -EINVAL;
++ }
++ kcop->ivlen = cop->iv ? ses_ptr->cdata.ivsize : 0;
++ kcop->digestsize = 0; /* will be updated during operation */
++
++ crypto_put_session(ses_ptr);
++
++ kcop->task = current;
++ kcop->mm = current->mm;
++
++ if (cop->iv) {
++ rc = copy_from_user(kcop->iv, cop->iv, kcop->ivlen);
++ if (unlikely(rc)) {
++ derr(1, "error copying IV (%d bytes), copy_from_user returned %d for address %p",
++ kcop->ivlen, rc, cop->iv);
++ return -EFAULT;
++ }
++ }
++
++ return 0;
++}
++
++/* this function has to be called from process context */
++static int fill_cop_from_kcop(struct kernel_crypt_op *kcop, struct fcrypt *fcr)
++{
++ int ret;
++
++ if (kcop->digestsize) {
++ ret = copy_to_user(kcop->cop.mac,
++ kcop->hash_output, kcop->digestsize);
++ if (unlikely(ret))
++ return -EFAULT;
++ }
++ if (kcop->ivlen && kcop->cop.flags & COP_FLAG_WRITE_IV) {
++ ret = copy_to_user(kcop->cop.iv,
+ kcop->iv, kcop->ivlen);
+ if (unlikely(ret))
+ return -EFAULT;
@@ -3045,479 +4065,1123 @@ index 0000000..f26cf93
+ return 0;
+}
+
-+static int kcop_from_user(struct kernel_crypt_op *kcop,
-+ struct fcrypt *fcr, void __user *arg)
-+{
-+ if (unlikely(copy_from_user(&kcop->cop, arg, sizeof(kcop->cop))))
-+ return -EFAULT;
++static int kcop_from_user(struct kernel_crypt_op *kcop,
++ struct fcrypt *fcr, void __user *arg)
++{
++ if (unlikely(copy_from_user(&kcop->cop, arg, sizeof(kcop->cop))))
++ return -EFAULT;
++
++ return fill_kcop_from_cop(kcop, fcr);
++}
++
++static int kcop_to_user(struct kernel_crypt_op *kcop,
++ struct fcrypt *fcr, void __user *arg)
++{
++ int ret;
++
++ ret = fill_cop_from_kcop(kcop, fcr);
++ if (unlikely(ret)) {
++ derr(1, "Error in fill_cop_from_kcop");
++ return ret;
++ }
++
++ if (unlikely(copy_to_user(arg, &kcop->cop, sizeof(kcop->cop)))) {
++ derr(1, "Cannot copy to userspace");
++ return -EFAULT;
++ }
++ return 0;
++}
++
++static inline void tfm_info_to_alg_info(struct alg_info *dst, struct crypto_tfm *tfm)
++{
++ snprintf(dst->cra_name, CRYPTODEV_MAX_ALG_NAME,
++ "%s", crypto_tfm_alg_name(tfm));
++ snprintf(dst->cra_driver_name, CRYPTODEV_MAX_ALG_NAME,
++ "%s", crypto_tfm_alg_driver_name(tfm));
++}
++
++#ifndef CRYPTO_ALG_KERN_DRIVER_ONLY
++static unsigned int is_known_accelerated(struct crypto_tfm *tfm)
++{
++ const char *name = crypto_tfm_alg_driver_name(tfm);
++
++ if (name == NULL)
++ return 1; /* assume accelerated */
++
++ /* look for known crypto engine names */
++ if (strstr(name, "-talitos") ||
++ !strncmp(name, "mv-", 3) ||
++ !strncmp(name, "atmel-", 6) ||
++ strstr(name, "geode") ||
++ strstr(name, "hifn") ||
++ strstr(name, "-ixp4xx") ||
++ strstr(name, "-omap") ||
++ strstr(name, "-picoxcell") ||
++ strstr(name, "-s5p") ||
++ strstr(name, "-ppc4xx") ||
++ strstr(name, "-caam") ||
++ strstr(name, "-n2"))
++ return 1;
++
++ return 0;
++}
++#endif
++
++static int get_session_info(struct fcrypt *fcr, struct session_info_op *siop)
++{
++ struct csession *ses_ptr;
++ struct crypto_tfm *tfm;
++
++ /* this also enters ses_ptr->sem */
++ ses_ptr = crypto_get_session_by_sid(fcr, siop->ses);
++ if (unlikely(!ses_ptr)) {
++ derr(1, "invalid session ID=0x%08X", siop->ses);
++ return -EINVAL;
++ }
++
++ siop->flags = 0;
++
++ if (ses_ptr->cdata.init) {
++ if (ses_ptr->cdata.aead == 0)
++ tfm = crypto_ablkcipher_tfm(ses_ptr->cdata.async.s);
++ else
++ tfm = crypto_aead_tfm(ses_ptr->cdata.async.as);
++ tfm_info_to_alg_info(&siop->cipher_info, tfm);
++#ifdef CRYPTO_ALG_KERN_DRIVER_ONLY
++ if (tfm->__crt_alg->cra_flags & CRYPTO_ALG_KERN_DRIVER_ONLY)
++ siop->flags |= SIOP_FLAG_KERNEL_DRIVER_ONLY;
++#else
++ if (is_known_accelerated(tfm))
++ siop->flags |= SIOP_FLAG_KERNEL_DRIVER_ONLY;
++#endif
++ }
++ if (ses_ptr->hdata.init) {
++ tfm = crypto_ahash_tfm(ses_ptr->hdata.async.s);
++ tfm_info_to_alg_info(&siop->hash_info, tfm);
++#ifdef CRYPTO_ALG_KERN_DRIVER_ONLY
++ if (tfm->__crt_alg->cra_flags & CRYPTO_ALG_KERN_DRIVER_ONLY)
++ siop->flags |= SIOP_FLAG_KERNEL_DRIVER_ONLY;
++#else
++ if (is_known_accelerated(tfm))
++ siop->flags |= SIOP_FLAG_KERNEL_DRIVER_ONLY;
++#endif
++ }
++
++ siop->alignmask = ses_ptr->alignmask;
++
++ crypto_put_session(ses_ptr);
++ return 0;
++}
++
++static long
++cryptodev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg_)
++{
++ void __user *arg = (void __user *)arg_;
++ int __user *p = arg;
++ struct session_op sop;
++ struct kernel_crypt_op kcop;
++ struct kernel_crypt_auth_op kcaop;
++ struct crypt_priv *pcr = filp->private_data;
++ struct fcrypt *fcr;
++ struct session_info_op siop;
++ uint32_t ses;
++ int ret, fd;
++
++ if (unlikely(!pcr))
++ BUG();
++
++ fcr = &pcr->fcrypt;
++
++ switch (cmd) {
++ case CIOCASYMFEAT:
++ return put_user(0, p);
++ case CRIOGET:
++ fd = clonefd(filp);
++ ret = put_user(fd, p);
++ if (unlikely(ret)) {
++ sys_close(fd);
++ return ret;
++ }
++ return ret;
++ case CIOCGSESSION:
++ if (unlikely(copy_from_user(&sop, arg, sizeof(sop))))
++ return -EFAULT;
++
++ ret = crypto_create_session(fcr, &sop);
++ if (unlikely(ret))
++ return ret;
++ ret = copy_to_user(arg, &sop, sizeof(sop));
++ if (unlikely(ret)) {
++ crypto_finish_session(fcr, sop.ses);
++ return -EFAULT;
++ }
++ return ret;
++ case CIOCFSESSION:
++ ret = get_user(ses, (uint32_t __user *)arg);
++ if (unlikely(ret))
++ return ret;
++ ret = crypto_finish_session(fcr, ses);
++ return ret;
++ case CIOCGSESSINFO:
++ if (unlikely(copy_from_user(&siop, arg, sizeof(siop))))
++ return -EFAULT;
++
++ ret = get_session_info(fcr, &siop);
++ if (unlikely(ret))
++ return ret;
++ return copy_to_user(arg, &siop, sizeof(siop));
++ case CIOCCRYPT:
++ if (unlikely(ret = kcop_from_user(&kcop, fcr, arg))) {
++ dwarning(1, "Error copying from user");
++ return ret;
++ }
++
++ ret = crypto_run(fcr, &kcop);
++ if (unlikely(ret)) {
++ dwarning(1, "Error in crypto_run");
++ return ret;
++ }
++
++ return kcop_to_user(&kcop, fcr, arg);
++ case CIOCAUTHCRYPT:
++ if (unlikely(ret = kcaop_from_user(&kcaop, fcr, arg))) {
++ dwarning(1, "Error copying from user");
++ return ret;
++ }
++
++ ret = crypto_auth_run(fcr, &kcaop);
++ if (unlikely(ret)) {
++ dwarning(1, "Error in crypto_auth_run");
++ return ret;
++ }
++ return kcaop_to_user(&kcaop, fcr, arg);
++#ifdef ENABLE_ASYNC
++ case CIOCASYNCCRYPT:
++ if (unlikely(ret = kcop_from_user(&kcop, fcr, arg)))
++ return ret;
++
++ return crypto_async_run(pcr, &kcop);
++ case CIOCASYNCFETCH:
++ ret = crypto_async_fetch(pcr, &kcop);
++ if (unlikely(ret))
++ return ret;
++
++ return kcop_to_user(&kcop, fcr, arg);
++#endif
++ default:
++ return -EINVAL;
++ }
++}
++
++/* compatibility code for 32bit userlands */
++#ifdef CONFIG_COMPAT
++
++static inline void
++compat_to_session_op(struct compat_session_op *compat, struct session_op *sop)
++{
++ sop->cipher = compat->cipher;
++ sop->mac = compat->mac;
++ sop->keylen = compat->keylen;
++
++ sop->key = compat_ptr(compat->key);
++ sop->mackeylen = compat->mackeylen;
++ sop->mackey = compat_ptr(compat->mackey);
++ sop->ses = compat->ses;
++}
++
++static inline void
++session_op_to_compat(struct session_op *sop, struct compat_session_op *compat)
++{
++ compat->cipher = sop->cipher;
++ compat->mac = sop->mac;
++ compat->keylen = sop->keylen;
++
++ compat->key = ptr_to_compat(sop->key);
++ compat->mackeylen = sop->mackeylen;
++ compat->mackey = ptr_to_compat(sop->mackey);
++ compat->ses = sop->ses;
++}
++
++static inline void
++compat_to_crypt_op(struct compat_crypt_op *compat, struct crypt_op *cop)
++{
++ cop->ses = compat->ses;
++ cop->op = compat->op;
++ cop->flags = compat->flags;
++ cop->len = compat->len;
++
++ cop->src = compat_ptr(compat->src);
++ cop->dst = compat_ptr(compat->dst);
++ cop->mac = compat_ptr(compat->mac);
++ cop->iv = compat_ptr(compat->iv);
++}
++
++static inline void
++crypt_op_to_compat(struct crypt_op *cop, struct compat_crypt_op *compat)
++{
++ compat->ses = cop->ses;
++ compat->op = cop->op;
++ compat->flags = cop->flags;
++ compat->len = cop->len;
++
++ compat->src = ptr_to_compat(cop->src);
++ compat->dst = ptr_to_compat(cop->dst);
++ compat->mac = ptr_to_compat(cop->mac);
++ compat->iv = ptr_to_compat(cop->iv);
++}
++
++static int compat_kcop_from_user(struct kernel_crypt_op *kcop,
++ struct fcrypt *fcr, void __user *arg)
++{
++ struct compat_crypt_op compat_cop;
++
++ if (unlikely(copy_from_user(&compat_cop, arg, sizeof(compat_cop))))
++ return -EFAULT;
++ compat_to_crypt_op(&compat_cop, &kcop->cop);
++
++ return fill_kcop_from_cop(kcop, fcr);
++}
++
++static int compat_kcop_to_user(struct kernel_crypt_op *kcop,
++ struct fcrypt *fcr, void __user *arg)
++{
++ int ret;
++ struct compat_crypt_op compat_cop;
++
++ ret = fill_cop_from_kcop(kcop, fcr);
++ if (unlikely(ret)) {
++ dwarning(1, "Error in fill_cop_from_kcop");
++ return ret;
++ }
++ crypt_op_to_compat(&kcop->cop, &compat_cop);
++
++ if (unlikely(copy_to_user(arg, &compat_cop, sizeof(compat_cop)))) {
++ dwarning(1, "Error copying to user");
++ return -EFAULT;
++ }
++ return 0;
++}
++
++static long
++cryptodev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg_)
++{
++ void __user *arg = (void __user *)arg_;
++ struct crypt_priv *pcr = file->private_data;
++ struct fcrypt *fcr;
++ struct session_op sop;
++ struct compat_session_op compat_sop;
++ struct kernel_crypt_op kcop;
++ int ret;
++
++ if (unlikely(!pcr))
++ BUG();
++
++ fcr = &pcr->fcrypt;
++
++ switch (cmd) {
++ case CIOCASYMFEAT:
++ case CRIOGET:
++ case CIOCFSESSION:
++ case CIOCGSESSINFO:
++ return cryptodev_ioctl(file, cmd, arg_);
++
++ case COMPAT_CIOCGSESSION:
++ if (unlikely(copy_from_user(&compat_sop, arg,
++ sizeof(compat_sop))))
++ return -EFAULT;
++ compat_to_session_op(&compat_sop, &sop);
++
++ ret = crypto_create_session(fcr, &sop);
++ if (unlikely(ret))
++ return ret;
++
++ session_op_to_compat(&sop, &compat_sop);
++ ret = copy_to_user(arg, &compat_sop, sizeof(compat_sop));
++ if (unlikely(ret)) {
++ crypto_finish_session(fcr, sop.ses);
++ return -EFAULT;
++ }
++ return ret;
++
++ case COMPAT_CIOCCRYPT:
++ ret = compat_kcop_from_user(&kcop, fcr, arg);
++ if (unlikely(ret))
++ return ret;
++
++ ret = crypto_run(fcr, &kcop);
++ if (unlikely(ret))
++ return ret;
++
++ return compat_kcop_to_user(&kcop, fcr, arg);
++#ifdef ENABLE_ASYNC
++ case COMPAT_CIOCASYNCCRYPT:
++ if (unlikely(ret = compat_kcop_from_user(&kcop, fcr, arg)))
++ return ret;
++
++ return crypto_async_run(pcr, &kcop);
++ case COMPAT_CIOCASYNCFETCH:
++ ret = crypto_async_fetch(pcr, &kcop);
++ if (unlikely(ret))
++ return ret;
++
++ return compat_kcop_to_user(&kcop, fcr, arg);
++#endif
++ default:
++ return -EINVAL;
++ }
++}
++
++#endif /* CONFIG_COMPAT */
++
++static unsigned int cryptodev_poll(struct file *file, poll_table *wait)
++{
++ struct crypt_priv *pcr = file->private_data;
++ int ret = 0;
++
++ poll_wait(file, &pcr->user_waiter, wait);
++
++ if (!list_empty_careful(&pcr->done.list))
++ ret |= POLLIN | POLLRDNORM;
++ if (!list_empty_careful(&pcr->free.list) || pcr->itemcount < MAX_COP_RINGSIZE)
++ ret |= POLLOUT | POLLWRNORM;
++
++ return ret;
++}
++
++static const struct file_operations cryptodev_fops = {
++ .owner = THIS_MODULE,
++ .open = cryptodev_open,
++ .release = cryptodev_release,
++ .unlocked_ioctl = cryptodev_ioctl,
++#ifdef CONFIG_COMPAT
++ .compat_ioctl = cryptodev_compat_ioctl,
++#endif /* CONFIG_COMPAT */
++ .poll = cryptodev_poll,
++};
++
++static struct miscdevice cryptodev = {
++ .minor = MISC_DYNAMIC_MINOR,
++ .name = "crypto",
++ .fops = &cryptodev_fops,
++ .mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH,
++};
++
++static int __init
++cryptodev_register(void)
++{
++ int rc;
++
++ rc = misc_register(&cryptodev);
++ if (unlikely(rc)) {
++ pr_err(PFX "registration of /dev/crypto failed\n");
++ return rc;
++ }
++
++ return 0;
++}
++
++static void __exit
++cryptodev_deregister(void)
++{
++ misc_deregister(&cryptodev);
++}
++
++/* ====== Module init/exit ====== */
++static struct ctl_table verbosity_ctl_dir[] = {
++ {
++ .procname = "cryptodev_verbosity",
++ .data = &cryptodev_verbosity,
++ .maxlen = sizeof(int),
++ .mode = 0644,
++ .proc_handler = proc_dointvec,
++ },
++ {0, },
++};
++
++static struct ctl_table verbosity_ctl_root[] = {
++ {
++ .procname = "ioctl",
++ .mode = 0555,
++ .child = verbosity_ctl_dir,
++ },
++ {0, },
++};
++static struct ctl_table_header *verbosity_sysctl_header;
++static int __init init_cryptodev(void)
++{
++ int rc;
++
++ cryptodev_wq = create_workqueue("cryptodev_queue");
++ if (unlikely(!cryptodev_wq)) {
++ pr_err(PFX "failed to allocate the cryptodev workqueue\n");
++ return -EFAULT;
++ }
++
++ rc = cryptodev_register();
++ if (unlikely(rc)) {
++ destroy_workqueue(cryptodev_wq);
++ return rc;
++ }
++
++ verbosity_sysctl_header = register_sysctl_table(verbosity_ctl_root);
++
++ pr_info(PFX "driver %s loaded.\n", VERSION);
++
++ return 0;
++}
++
++static void __exit exit_cryptodev(void)
++{
++ flush_workqueue(cryptodev_wq);
++ destroy_workqueue(cryptodev_wq);
++
++ if (verbosity_sysctl_header)
++ unregister_sysctl_table(verbosity_sysctl_header);
++
++ cryptodev_deregister();
++ pr_info(PFX "driver unloaded.\n");
++}
++
++module_init(init_cryptodev);
++module_exit(exit_cryptodev);
++
+diff --git a/crypto/cryptodev/lib/Makefile b/crypto/cryptodev/lib/Makefile
+new file mode 100644
+index 0000000..af87795
+--- /dev/null
++++ b/crypto/cryptodev/lib/Makefile
+@@ -0,0 +1,15 @@
++CFLAGS=-g -O2 -Wall
++
++all: benchmark
++
++benchmark: main.c libthreshold.a
++ gcc $(CFLAGS) -DDEBUG -o $@ $^ -lssl libthreshold.a
++
++.o:
++ gcc $(CCFLAGS) -c $< -o $@
++
++libthreshold.a: benchmark.o hash.o threshold.o combo.o
++ ar rcs $@ $^
++
++clean:
++ rm -f *.o *~ benchmark libthreshold.a
+diff --git a/crypto/cryptodev/lib/benchmark.c b/crypto/cryptodev/lib/benchmark.c
+new file mode 100644
+index 0000000..a04efbc
+--- /dev/null
++++ b/crypto/cryptodev/lib/benchmark.c
+@@ -0,0 +1,88 @@
++/*
++ * Copyright (C) 2011 Free Software Foundation, Inc.
++ *
++ * This file is part of GnuTLS.
++ *
++ * GnuTLS is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 3 of the License, or
++ * (at your option) any later version.
++ *
++ * GnuTLS is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <stdio.h>
++#include <string.h>
++#include <signal.h>
++#include <sys/time.h>
++#include <time.h>
++#include <unistd.h>
++#include "benchmark.h"
++
++int benchmark_must_finish = 0;
+
-+ return fill_kcop_from_cop(kcop, fcr);
++static void
++alarm_handler (int signo)
++{
++ benchmark_must_finish = 1;
+}
+
-+static int kcop_to_user(struct kernel_crypt_op *kcop,
-+ struct fcrypt *fcr, void __user *arg)
++int start_benchmark(struct benchmark_st * st)
+{
-+ int ret;
++ int ret;
++ struct itimerval timer;
+
-+ ret = fill_cop_from_kcop(kcop, fcr);
-+ if (unlikely(ret)) {
-+ dprintk(1, KERN_ERR, "Error in fill_cop_from_kcop\n");
-+ return ret;
-+ }
++ memset(st, 0, sizeof(*st));
+
-+ if (unlikely(copy_to_user(arg, &kcop->cop, sizeof(kcop->cop)))) {
-+ dprintk(1, KERN_ERR, "Cannot copy to userspace\n");
-+ return -EFAULT;
-+ }
-+ return 0;
-+}
++ st->old_handler = signal (SIGPROF, alarm_handler);
+
-+static inline void tfm_info_to_alg_info(struct alg_info *dst, struct crypto_tfm *tfm)
-+{
-+ snprintf(dst->cra_name, CRYPTODEV_MAX_ALG_NAME,
-+ "%s", crypto_tfm_alg_name(tfm));
-+ snprintf(dst->cra_driver_name, CRYPTODEV_MAX_ALG_NAME,
-+ "%s", crypto_tfm_alg_driver_name(tfm));
++ ret = gettimeofday (&st->start, NULL);
++ if (ret < 0) {
++ perror("gettimeofday");
++ return -1;
++ }
++
++ benchmark_must_finish = 0;
++
++ memset(&timer, 0, sizeof(timer));
++ timer.it_value.tv_sec = 0;
++ timer.it_value.tv_usec = 100*1000;
++
++ ret = setitimer(ITIMER_PROF, &timer, NULL);
++ if (ret < 0) {
++ perror("setitimer");
++ return -1;
++ }
++
++ return 0;
+}
+
-+static unsigned int is_known_accelerated(struct crypto_tfm *tfm)
++/* Returns -1 on error or 0 on success.
++ * elapsed: the elapsed time in milliseconds
++ */
++int stop_benchmark(struct benchmark_st * st, unsigned long * elapsed)
+{
-+const char* name = crypto_tfm_alg_driver_name(tfm);
++ unsigned long msecs;
++ struct timeval stop;
++ int ret;
+
-+ if (name == NULL)
-+ return 1; /* assume accelerated */
-+
-+ if (strstr(name, "-talitos"))
-+ return 1;
-+ else if (strncmp(name, "mv-", 3) == 0)
-+ return 1;
-+ else if (strstr(name, "geode"))
-+ return 1;
-+ else if (strstr(name, "hifn"))
-+ return 1;
-+ else if (strstr(name, "-ixp4xx"))
-+ return 1;
-+ else if (strstr(name, "-omap"))
-+ return 1;
-+ else if (strstr(name, "-picoxcell"))
-+ return 1;
-+ else if (strstr(name, "-s5p"))
-+ return 1;
-+ else if (strstr(name, "-ppc4xx"))
-+ return 1;
-+ else if (strstr(name, "-caam"))
-+ return 1;
-+ else if (strstr(name, "-n2"))
-+ return 1;
++ signal(SIGPROF, st->old_handler);
+
-+ return 0;
++ ret = gettimeofday (&stop, NULL);
++ if (ret < 0)
++ return -1;
++
++ msecs = (stop.tv_sec * 1000 + stop.tv_usec / 1000 -
++ (st->start.tv_sec * 1000 + st->start.tv_usec / (1000)));
++
++ if (elapsed) *elapsed = msecs;
++
++ return 0;
+}
+
-+static int get_session_info(struct fcrypt *fcr, struct session_info_op *siop)
+diff --git a/crypto/cryptodev/lib/benchmark.h b/crypto/cryptodev/lib/benchmark.h
+new file mode 100644
+index 0000000..173552e
+--- /dev/null
++++ b/crypto/cryptodev/lib/benchmark.h
+@@ -0,0 +1,18 @@
++#include <sys/time.h>
++#include <time.h>
++#include <sys/time.h>
++#include <signal.h>
++
++typedef void (*sighandler_t)(int);
++
++struct benchmark_st
+{
-+ struct csession *ses_ptr;
-+ struct crypto_tfm *tfm;
++ struct timeval start;
++ sighandler_t old_handler;
++};
+
-+ /* this also enters ses_ptr->sem */
-+ ses_ptr = crypto_get_session_by_sid(fcr, siop->ses);
-+ if (unlikely(!ses_ptr)) {
-+ dprintk(1, KERN_ERR, "invalid session ID=0x%08X\n", siop->ses);
-+ return -EINVAL;
-+ }
++extern int benchmark_must_finish;
+
-+ siop->flags = 0;
++int start_benchmark(struct benchmark_st * st);
++int stop_benchmark(struct benchmark_st * st, unsigned long * elapsed);
+
-+ if (ses_ptr->cdata.init) {
-+ if (ses_ptr->cdata.aead == 0) {
-+ tfm = crypto_ablkcipher_tfm(ses_ptr->cdata.async.s);
-+ } else {
-+ tfm = crypto_aead_tfm(ses_ptr->cdata.async.as);
-+ }
-+ tfm_info_to_alg_info(&siop->cipher_info, tfm);
-+#ifdef CRYPTO_ALG_KERN_DRIVER_ONLY
-+ if (tfm->__crt_alg->cra_flags & CRYPTO_ALG_KERN_DRIVER_ONLY)
-+ siop->flags |= SIOP_FLAG_KERNEL_DRIVER_ONLY;
-+#else
-+ if (is_known_accelerated(tfm))
-+ siop->flags |= SIOP_FLAG_KERNEL_DRIVER_ONLY;
+diff --git a/crypto/cryptodev/lib/combo.c b/crypto/cryptodev/lib/combo.c
+new file mode 100644
+index 0000000..b26da0a
+--- /dev/null
++++ b/crypto/cryptodev/lib/combo.c
+@@ -0,0 +1,171 @@
++/*
++ * Demo on how to use /dev/crypto device for ciphering.
++ *
++ * Placed under public domain.
++ *
++ */
++#include <stdio.h>
++#include <string.h>
++#include <unistd.h>
++#include <fcntl.h>
++#include <sys/ioctl.h>
++#include <crypto/cryptodev.h>
++#include "benchmark.h"
++#include "hash.h"
++
++int aead_ctx_init(struct cryptodev_ctx* ctx, int cipher, int hash, void* key, int key_size, int cfd)
++{
++#ifdef CIOCGSESSINFO
++ struct session_info_op siop;
+#endif
++
++ memset(ctx, 0, sizeof(*ctx));
++ ctx->cfd = cfd;
++
++ ctx->sess.mac = hash;
++ ctx->sess.cipher = cipher;
++ ctx->sess.key = key;
++ ctx->sess.keylen = key_size;
++
++ if (ioctl(ctx->cfd, CIOCGSESSION, &ctx->sess)) {
++ perror("ioctl(CIOCGSESSION)");
++ return -1;
+ }
-+ if (ses_ptr->hdata.init) {
-+ tfm = crypto_ahash_tfm(ses_ptr->hdata.async.s);
-+ tfm_info_to_alg_info(&siop->hash_info, tfm);
-+#ifdef CRYPTO_ALG_KERN_DRIVER_ONLY
-+ if (tfm->__crt_alg->cra_flags & CRYPTO_ALG_KERN_DRIVER_ONLY)
-+ siop->flags |= SIOP_FLAG_KERNEL_DRIVER_ONLY;
-+#else
-+ if (is_known_accelerated(tfm))
-+ siop->flags |= SIOP_FLAG_KERNEL_DRIVER_ONLY;
++
++#ifdef CIOCGSESSINFO
++ memset(&siop, 0, sizeof(siop));
++ siop.ses = ctx->sess.ses;
++ if (ioctl(ctx->cfd, CIOCGSESSINFO, &siop)) {
++ perror("ioctl(CIOCGSESSINFO)");
++ return -1;
++ }
++#ifdef DEBUG
++ printf("Got %s-%s with drivers %s and %s\n",
++ siop.cipher_info.cra_name, siop.hash_info.cra_name,
++ siop.cipher_info.cra_driver_name, siop.hash_info.cra_driver_name);
++#endif
++ /*printf("Alignmask is %x\n", (unsigned int)siop.alignmask);*/
++ ctx->alignmask = siop.alignmask;
+#endif
++ return 0;
++}
++
++void aead_ctx_deinit(struct cryptodev_ctx* ctx)
++{
++ if (ioctl(ctx->cfd, CIOCFSESSION, &ctx->sess.ses)) {
++ perror("ioctl(CIOCFSESSION)");
+ }
++}
+
-+ siop->alignmask = ses_ptr->alignmask;
++int
++aead_encrypt(struct cryptodev_ctx* ctx, const void* iv, const void* plaintext, void* ciphertext, size_t size, void* digest)
++{
++ struct crypt_auth_op cryp;
++
++ memset(&cryp, 0, sizeof(cryp));
++
++ /* Encrypt data.in to data.encrypted */
++ cryp.ses = ctx->sess.ses;
++ cryp.len = size;
++ cryp.iv = (void*)iv;
++ cryp.iv_len = 16;
++ cryp.src = (void*)plaintext;
++ cryp.dst = (void*)ciphertext;
++ cryp.flags = COP_FLAG_AEAD_TLS_TYPE;
++
++ if (ioctl(ctx->cfd, CIOCAUTHCRYPT, &cryp)) {
++ perror("ioctl(CIOCAUTHCRYPT)");
++ return -1;
++ }
+
-+ crypto_put_session(ses_ptr);
+ return 0;
+}
+
-+static long
-+cryptodev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg_)
++static const int sizes[] = {64, 256, 512, 1024, 4096, 16*1024};
++
++
++int aead_test(int cipher, int mac, void* ukey, int ukey_size,
++ void* user_ctx, void (*user_combo)(void* user_ctx, void* plaintext, void* ciphertext, int size, void* res))
+{
-+ void __user *arg = (void __user *)arg_;
-+ int __user *p = arg;
-+ struct session_op sop;
-+ struct kernel_crypt_op kcop;
-+ struct kernel_crypt_auth_op kcaop;
-+ struct crypt_priv *pcr = filp->private_data;
-+ struct fcrypt *fcr;
-+ struct session_info_op siop;
-+ uint32_t ses;
-+ int ret, fd;
++ int cfd = -1, i, ret;
++ struct cryptodev_ctx ctx;
++ uint8_t digest[AALG_MAX_RESULT_LEN];
++ char text[16*1024];
++ char ctext[16*1024];
++ char iv[16];
++ unsigned long elapsed, counted;
++ double t1, t2;
++ struct benchmark_st bst;
+
-+ if (unlikely(!pcr))
-+ BUG();
++ /* Open the crypto device */
++ cfd = open("/dev/crypto", O_RDWR, 0);
++ if (cfd < 0) {
++ perror("open(/dev/crypto)");
++ return -1;
++ }
+
-+ fcr = &pcr->fcrypt;
++ aead_ctx_init(&ctx, cipher, mac, ukey, ukey_size, cfd);
+
-+ switch (cmd) {
-+ case CIOCASYMFEAT:
-+ return put_user(0, p);
-+ case CRIOGET:
-+ fd = clonefd(filp);
-+ ret = put_user(fd, p);
-+ if (unlikely(ret)) {
-+ sys_close(fd);
-+ return ret;
++ for (i=0;i<sizeof(sizes)/sizeof(sizes[0]);i++) {
++ counted = 0;
++ ret = start_benchmark(&bst);
++ if (ret < 0) {
++ ret = -1;
++ goto finish;
+ }
-+ return ret;
-+ case CIOCGSESSION:
-+ if (unlikely(copy_from_user(&sop, arg, sizeof(sop))))
-+ return -EFAULT;
+
-+ ret = crypto_create_session(fcr, &sop);
-+ if (unlikely(ret))
-+ return ret;
-+ ret = copy_to_user(arg, &sop, sizeof(sop));
-+ if (unlikely(ret)) {
-+ crypto_finish_session(fcr, sop.ses);
-+ return -EFAULT;
-+ }
-+ return ret;
-+ case CIOCFSESSION:
-+ ret = get_user(ses, (uint32_t __user *)arg);
-+ if (unlikely(ret))
-+ return ret;
-+ ret = crypto_finish_session(fcr, ses);
-+ return ret;
-+ case CIOCGSESSINFO:
-+ if (unlikely(copy_from_user(&siop, arg, sizeof(siop))))
-+ return -EFAULT;
++ do {
++ if (aead_encrypt(&ctx, iv, text, text, sizes[i], digest) < 0)
++ return -2;
++ counted += sizes[i];
++ } while(benchmark_must_finish==0);
+
-+ ret = get_session_info(fcr, &siop);
-+ if (unlikely(ret))
-+ return ret;
-+ return copy_to_user(arg, &siop, sizeof(siop));
-+ case CIOCCRYPT:
-+ if (unlikely(ret = kcop_from_user(&kcop, fcr, arg))) {
-+ dprintk(1, KERN_WARNING, "Error copying from user\n");
-+ return ret;
++ ret = stop_benchmark(&bst, &elapsed);
++ if (ret < 0) {
++ ret = -1;
++ goto finish;
+ }
+
-+ ret = crypto_run(fcr, &kcop);
-+ if (unlikely(ret)) {
-+ dprintk(1, KERN_WARNING, "Error in crypto_run\n");
-+ return ret;
-+ }
++ t1 = (double)counted/(double)elapsed;
+
-+ return kcop_to_user(&kcop, fcr, arg);
-+ case CIOCAUTHCRYPT:
-+ if (unlikely(ret = kcaop_from_user(&kcaop, fcr, arg))) {
-+ dprintk(1, KERN_WARNING, "Error copying from user\n");
-+ return ret;
++ /* now check the user function */
++ counted = 0;
++ ret = start_benchmark(&bst);
++ if (ret < 0) {
++ ret = -1;
++ goto finish;
+ }
+
-+ ret = crypto_auth_run(fcr, &kcaop);
-+ if (unlikely(ret)) {
-+ dprintk(1, KERN_WARNING, "Error in crypto_auth_run\n");
-+ return ret;
++ do {
++ user_combo(user_ctx, text, ctext, sizes[i], digest);
++ counted += sizes[i];
++ } while(benchmark_must_finish==0);
++
++ ret = stop_benchmark(&bst, &elapsed);
++ if (ret < 0) {
++ ret = -1;
++ goto finish;
+ }
-+ return kcaop_to_user(&kcaop, fcr, arg);
-+#ifdef ENABLE_ASYNC
-+ case CIOCASYNCCRYPT:
-+ if (unlikely(ret = kcop_from_user(&kcop, fcr, arg)))
-+ return ret;
+
-+ return crypto_async_run(pcr, &kcop);
-+ case CIOCASYNCFETCH:
-+ ret = crypto_async_fetch(pcr, &kcop);
-+ if (unlikely(ret))
-+ return ret;
++ t2 = (double)counted/(double)elapsed;
+
-+ return kcop_to_user(&kcop, fcr, arg);
++#ifdef DEBUG
++ printf("%d: kernel: %.4f bytes/msec, user: %.4f bytes/msec\n", sizes[i], t1, t2);
+#endif
-+ default:
-+ return -EINVAL;
++ if (t1 > t2) {
++ ret = sizes[i];
++ goto finish;
++ }
+ }
-+}
-+
-+/* compatibility code for 32bit userlands */
-+#ifdef CONFIG_COMPAT
-+
-+static inline void
-+compat_to_session_op(struct compat_session_op *compat, struct session_op *sop)
-+{
-+ sop->cipher = compat->cipher;
-+ sop->mac = compat->mac;
-+ sop->keylen = compat->keylen;
-+
-+ sop->key = compat_ptr(compat->key);
-+ sop->mackeylen = compat->mackeylen;
-+ sop->mackey = compat_ptr(compat->mackey);
-+ sop->ses = compat->ses;
-+}
+
-+static inline void
-+session_op_to_compat(struct session_op *sop, struct compat_session_op *compat)
-+{
-+ compat->cipher = sop->cipher;
-+ compat->mac = sop->mac;
-+ compat->keylen = sop->keylen;
++ ret = -1;
++finish:
++ aead_ctx_deinit(&ctx);
+
-+ compat->key = ptr_to_compat(sop->key);
-+ compat->mackeylen = sop->mackeylen;
-+ compat->mackey = ptr_to_compat(sop->mackey);
-+ compat->ses = sop->ses;
++ /* Close the original descriptor */
++ if (close(cfd)) {
++ perror("close(cfd)");
++ return 1;
++ }
++ return ret;
+}
+diff --git a/crypto/cryptodev/lib/hash.c b/crypto/cryptodev/lib/hash.c
+new file mode 100644
+index 0000000..386fd7e
+--- /dev/null
++++ b/crypto/cryptodev/lib/hash.c
+@@ -0,0 +1,161 @@
++/*
++ * Demo on how to use /dev/crypto device for ciphering.
++ *
++ * Placed under public domain.
++ *
++ */
++#include <stdio.h>
++#include <string.h>
++#include <unistd.h>
++#include <fcntl.h>
++#include <sys/ioctl.h>
++#include <crypto/cryptodev.h>
++#include "hash.h"
++#include "benchmark.h"
+
-+static inline void
-+compat_to_crypt_op(struct compat_crypt_op *compat, struct crypt_op *cop)
++int hash_ctx_init(struct cryptodev_ctx* ctx, int hash, int cfd)
+{
-+ cop->ses = compat->ses;
-+ cop->op = compat->op;
-+ cop->flags = compat->flags;
-+ cop->len = compat->len;
++#ifdef CIOCGSESSINFO
++ struct session_info_op siop;
++#endif
+
-+ cop->src = compat_ptr(compat->src);
-+ cop->dst = compat_ptr(compat->dst);
-+ cop->mac = compat_ptr(compat->mac);
-+ cop->iv = compat_ptr(compat->iv);
-+}
++ memset(ctx, 0, sizeof(*ctx));
++ ctx->cfd = cfd;
+
-+static inline void
-+crypt_op_to_compat(struct crypt_op *cop, struct compat_crypt_op *compat)
-+{
-+ compat->ses = cop->ses;
-+ compat->op = cop->op;
-+ compat->flags = cop->flags;
-+ compat->len = cop->len;
++ ctx->sess.mac = hash;
+
-+ compat->src = ptr_to_compat(cop->src);
-+ compat->dst = ptr_to_compat(cop->dst);
-+ compat->mac = ptr_to_compat(cop->mac);
-+ compat->iv = ptr_to_compat(cop->iv);
++ if (ioctl(ctx->cfd, CIOCGSESSION, &ctx->sess)) {
++ perror("ioctl(CIOCGSESSION)");
++ return -1;
++ }
++
++#ifdef CIOCGSESSINFO
++ memset(&siop, 0, sizeof(siop));
++ siop.ses = ctx->sess.ses;
++ if (ioctl(ctx->cfd, CIOCGSESSINFO, &siop)) {
++ perror("ioctl(CIOCGSESSINFO)");
++ return -1;
++ }
++#ifdef DEBUG
++ printf("Got %s with driver %s\n",
++ siop.hash_info.cra_name, siop.hash_info.cra_driver_name);
++#endif
++ /*printf("Alignmask is %x\n", (unsigned int)siop.alignmask);*/
++ ctx->alignmask = siop.alignmask;
++#endif
++ return 0;
+}
+
-+static int compat_kcop_from_user(struct kernel_crypt_op *kcop,
-+ struct fcrypt *fcr, void __user *arg)
++void hash_ctx_deinit(struct cryptodev_ctx* ctx)
+{
-+ struct compat_crypt_op compat_cop;
-+
-+ if (unlikely(copy_from_user(&compat_cop, arg, sizeof(compat_cop))))
-+ return -EFAULT;
-+ compat_to_crypt_op(&compat_cop, &kcop->cop);
-+
-+ return fill_kcop_from_cop(kcop, fcr);
++ if (ioctl(ctx->cfd, CIOCFSESSION, &ctx->sess.ses)) {
++ perror("ioctl(CIOCFSESSION)");
++ }
+}
+
-+static int compat_kcop_to_user(struct kernel_crypt_op *kcop,
-+ struct fcrypt *fcr, void __user *arg)
++int
++hash(struct cryptodev_ctx* ctx, const void* text, size_t size, void* digest)
+{
-+ int ret;
-+ struct compat_crypt_op compat_cop;
++ struct crypt_op cryp;
+
-+ ret = fill_cop_from_kcop(kcop, fcr);
-+ if (unlikely(ret)) {
-+ dprintk(1, KERN_WARNING, "Error in fill_cop_from_kcop\n");
-+ return ret;
-+ }
-+ crypt_op_to_compat(&kcop->cop, &compat_cop);
++ memset(&cryp, 0, sizeof(cryp));
+
-+ if (unlikely(copy_to_user(arg, &compat_cop, sizeof(compat_cop)))) {
-+ dprintk(1, KERN_WARNING, "Error copying to user\n");
-+ return -EFAULT;
++ /* Encrypt data.in to data.encrypted */
++ cryp.ses = ctx->sess.ses;
++ cryp.len = size;
++ cryp.src = (void*)text;
++ cryp.mac = digest;
++ if (ioctl(ctx->cfd, CIOCCRYPT, &cryp)) {
++ perror("ioctl(CIOCCRYPT)");
++ return -1;
+ }
++
+ return 0;
+}
+
-+static long
-+cryptodev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg_)
++static const int sizes[] = {64, 256, 512, 1024, 4096, 16*1024};
++
++/* Worst case running time: around 1.2 secs
++ */
++int hash_test(int algo, void (*user_hash)(void* text, int size, void* res))
+{
-+ void __user *arg = (void __user *)arg_;
-+ struct crypt_priv *pcr = file->private_data;
-+ struct fcrypt *fcr;
-+ struct session_op sop;
-+ struct compat_session_op compat_sop;
-+ struct kernel_crypt_op kcop;
-+ int ret;
++ int cfd = -1, i, ret;
++ struct cryptodev_ctx ctx;
++ uint8_t digest[AALG_MAX_RESULT_LEN];
++ char text[16*1024];
++ unsigned long elapsed, counted;
++ double t1, t2;
++ struct benchmark_st bst;
+
-+ if (unlikely(!pcr))
-+ BUG();
++ /* Open the crypto device */
++ cfd = open("/dev/crypto", O_RDWR, 0);
++ if (cfd < 0) {
++ perror("open(/dev/crypto)");
++ return -1;
++ }
+
-+ fcr = &pcr->fcrypt;
++ hash_ctx_init(&ctx, algo, cfd);
+
-+ switch (cmd) {
-+ case CIOCASYMFEAT:
-+ case CRIOGET:
-+ case CIOCFSESSION:
-+ case CIOCGSESSINFO:
-+ return cryptodev_ioctl(file, cmd, arg_);
++ for (i=0;i<sizeof(sizes)/sizeof(sizes[0]);i++) {
++ counted = 0;
++ ret = start_benchmark(&bst);
++ if (ret < 0) {
++ ret = -1;
++ goto finish;
++ }
+
-+ case COMPAT_CIOCGSESSION:
-+ if (unlikely(copy_from_user(&compat_sop, arg,
-+ sizeof(compat_sop))))
-+ return -EFAULT;
-+ compat_to_session_op(&compat_sop, &sop);
++ do {
++ hash(&ctx, text, sizes[i], digest);
++ counted += sizes[i];
++ } while(benchmark_must_finish==0);
+
-+ ret = crypto_create_session(fcr, &sop);
-+ if (unlikely(ret))
-+ return ret;
++ ret = stop_benchmark(&bst, &elapsed);
++ if (ret < 0) {
++ ret = -1;
++ goto finish;
++ }
++ t1 = (double)counted/(double)elapsed;
+
-+ session_op_to_compat(&sop, &compat_sop);
-+ ret = copy_to_user(arg, &compat_sop, sizeof(compat_sop));
-+ if (unlikely(ret)) {
-+ crypto_finish_session(fcr, sop.ses);
-+ return -EFAULT;
++ /* now check the user function */
++ counted = 0;
++ ret = start_benchmark(&bst);
++ if (ret < 0) {
++ ret = -1;
++ goto finish;
+ }
-+ return ret;
+
-+ case COMPAT_CIOCCRYPT:
-+ ret = compat_kcop_from_user(&kcop, fcr, arg);
-+ if (unlikely(ret))
-+ return ret;
+
-+ ret = crypto_run(fcr, &kcop);
-+ if (unlikely(ret))
-+ return ret;
++ do {
++ user_hash(text, sizes[i], digest);
++ counted += sizes[i];
++ } while(benchmark_must_finish==0);
+
-+ return compat_kcop_to_user(&kcop, fcr, arg);
-+#ifdef ENABLE_ASYNC
-+ case COMPAT_CIOCASYNCCRYPT:
-+ if (unlikely(ret = compat_kcop_from_user(&kcop, fcr, arg)))
-+ return ret;
++ ret = stop_benchmark(&bst, &elapsed);
++ if (ret < 0) {
++ ret = -1;
++ goto finish;
++ }
+
-+ return crypto_async_run(pcr, &kcop);
-+ case COMPAT_CIOCASYNCFETCH:
-+ ret = crypto_async_fetch(pcr, &kcop);
-+ if (unlikely(ret))
-+ return ret;
++ t2 = (double)counted/(double)elapsed;
+
-+ return compat_kcop_to_user(&kcop, fcr, arg);
++ if (t1 > t2) {
++ ret = sizes[i];
++ goto finish;
++ }
++#ifdef DEBUG
++ printf("%d: kernel: %.4f bytes/msec, user: %.4f bytes/msec\n", sizes[i], t1, t2);
+#endif
-+ default:
-+ return -EINVAL;
+ }
-+}
-+
-+#endif /* CONFIG_COMPAT */
-+
-+static unsigned int cryptodev_poll(struct file *file, poll_table *wait)
-+{
-+ struct crypt_priv *pcr = file->private_data;
-+ int ret = 0;
+
-+ poll_wait(file, &pcr->user_waiter, wait);
-+
-+ if (!list_empty_careful(&pcr->done.list))
-+ ret |= POLLIN | POLLRDNORM;
-+ if (!list_empty_careful(&pcr->free.list) || pcr->itemcount < MAX_COP_RINGSIZE)
-+ ret |= POLLOUT | POLLWRNORM;
++ ret = -1;
++finish:
++ hash_ctx_deinit(&ctx);
+
++ /* Close the original descriptor */
++ if (close(cfd)) {
++ perror("close(cfd)");
++ return 1;
++ }
+ return ret;
+}
+
-+static const struct file_operations cryptodev_fops = {
-+ .owner = THIS_MODULE,
-+ .open = cryptodev_open,
-+ .release = cryptodev_release,
-+ .unlocked_ioctl = cryptodev_ioctl,
-+#ifdef CONFIG_COMPAT
-+ .compat_ioctl = cryptodev_compat_ioctl,
-+#endif /* CONFIG_COMPAT */
-+ .poll = cryptodev_poll,
-+};
+diff --git a/crypto/cryptodev/lib/hash.h b/crypto/cryptodev/lib/hash.h
+new file mode 100644
+index 0000000..7c32cea
+--- /dev/null
++++ b/crypto/cryptodev/lib/hash.h
+@@ -0,0 +1,20 @@
++#ifndef HASH_H
++# define HASH_H
+
-+static struct miscdevice cryptodev = {
-+ .minor = MISC_DYNAMIC_MINOR,
-+ .name = "crypto",
-+ .fops = &cryptodev_fops,
-+ .mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH,
++#include <stdint.h>
++
++struct cryptodev_ctx {
++ int cfd;
++ struct session_op sess;
++ uint16_t alignmask;
+};
+
-+static int __init
-+cryptodev_register(void)
++int hash_ctx_init(struct cryptodev_ctx* ctx, int hash, int cfd);
++void hash_ctx_deinit(struct cryptodev_ctx* ctx);
++int hash(struct cryptodev_ctx* ctx, const void* text, size_t size, void* digest);
++int hash_test(int algo, void (*user_hash)(void* text, int size, void* res));
++
++int aead_test(int cipher, int mac, void* ukey, int ukey_size,
++ void* user_ctx, void (*user_combo)(void* user_ctx, void* plaintext, void* ciphertext, int size, void* res));
++
++#endif
+diff --git a/crypto/cryptodev/lib/main.c b/crypto/cryptodev/lib/main.c
+new file mode 100644
+index 0000000..443779a
+--- /dev/null
++++ b/crypto/cryptodev/lib/main.c
+@@ -0,0 +1,28 @@
++/*
++ * Demo on how to use /dev/crypto device for ciphering.
++ *
++ * Placed under public domain.
++ *
++ */
++#include <stdio.h>
++#include <string.h>
++#include <unistd.h>
++#include <fcntl.h>
++#include <sys/ioctl.h>
++#include <crypto/cryptodev.h>
++#include "threshold.h"
++
++int main()
+{
-+ int rc;
++int ret;
+
-+ rc = misc_register(&cryptodev);
-+ if (unlikely(rc)) {
-+ printk(KERN_ERR PFX "registration of /dev/crypto failed\n");
-+ return rc;
-+ }
++ ret = get_sha1_threshold();
++ if (ret > 0)
++ printf("SHA1 in kernel outperforms user-space after %d input bytes\n", ret);
++
++ ret = get_aes_sha1_threshold();
++ if (ret > 0)
++ printf("AES-SHA1 in kernel outperforms user-space after %d input bytes\n", ret);
+
+ return 0;
+}
+diff --git a/crypto/cryptodev/lib/threshold.c b/crypto/cryptodev/lib/threshold.c
+new file mode 100644
+index 0000000..b002d58
+--- /dev/null
++++ b/crypto/cryptodev/lib/threshold.c
+@@ -0,0 +1,61 @@
++#include <stdio.h>
++#include <string.h>
++#include <unistd.h>
++#include <fcntl.h>
++#include <sys/ioctl.h>
++#include <crypto/cryptodev.h>
++#include <openssl/aes.h>
++#include <openssl/engine.h>
++#include <openssl/hmac.h>
++#include <openssl/evp.h>
++#include "hash.h"
++#include "threshold.h"
+
-+static void __exit
-+cryptodev_deregister(void)
++void sha_hash(void* text, int size, void* digest)
+{
-+ misc_deregister(&cryptodev);
++SHA_CTX ctx;
++
++ SHA_Init(&ctx);
++
++ SHA_Update(&ctx, text, size);
++
++ SHA_Final(digest, &ctx);
+}
+
-+/* ====== Module init/exit ====== */
-+static int __init init_cryptodev(void)
++void aes_sha_combo(void* ctx, void* plaintext, void* ciphertext, int size, void* tag)
+{
-+ int rc;
++uint8_t iv[16];
++AES_KEY* key = ctx;
++HMAC_CTX hctx;
++unsigned int rlen = 20;
+
-+ cryptodev_wq = create_workqueue("cryptodev_queue");
-+ if (unlikely(!cryptodev_wq)) {
-+ printk(KERN_ERR PFX "failed to allocate the cryptodev workqueue\n");
-+ return -EFAULT;
-+ }
++ HMAC_CTX_init(&hctx);
++ HMAC_Init_ex(&hctx, iv, 16, EVP_sha1(), NULL);
+
-+ rc = cryptodev_register();
-+ if (unlikely(rc)) {
-+ destroy_workqueue(cryptodev_wq);
-+ return rc;
-+ }
++ HMAC_Update(&hctx, plaintext, size);
+
-+ printk(KERN_INFO PFX "driver %s loaded.\n", VERSION);
++ HMAC_Final(&hctx, tag, &rlen);
++ HMAC_CTX_cleanup(&hctx);
+
-+ return 0;
++ AES_cbc_encrypt(plaintext, ciphertext, size, key, iv, 1);
+}
+
-+static void __exit exit_cryptodev(void)
++int get_sha1_threshold()
+{
-+ flush_workqueue(cryptodev_wq);
-+ destroy_workqueue(cryptodev_wq);
-+
-+ cryptodev_deregister();
-+ printk(KERN_INFO PFX "driver unloaded.\n");
++ return hash_test(CRYPTO_SHA1, sha_hash);
+}
+
-+module_init(init_cryptodev);
-+module_exit(exit_cryptodev);
++int get_aes_sha1_threshold()
++{
++AES_KEY key;
++uint8_t ukey[16];
++
++ ENGINE_load_builtin_engines();
++ ENGINE_register_all_complete();
+
++ memset(ukey, 0xaf, sizeof(ukey));
++ AES_set_encrypt_key(ukey, 16*8, &key);
++
++ return aead_test(CRYPTO_AES_CBC, CRYPTO_SHA1, ukey, 16, &key, aes_sha_combo);
++}
++
+diff --git a/crypto/cryptodev/lib/threshold.h b/crypto/cryptodev/lib/threshold.h
+new file mode 100644
+index 0000000..6c11019
+--- /dev/null
++++ b/crypto/cryptodev/lib/threshold.h
+@@ -0,0 +1,10 @@
++/* Return the number of bytes after which the
++ * kernel operation is more efficient to use.
++ * If return value is -1, then kernel operation
++ * cannot, or shouldn't be used, because it is always
++ * slower.
++ *
++ * Running time ~= 1.2 seconds per call.
++ */
++int get_sha1_threshold();
++int get_aes_sha1_threshold();
diff --git a/crypto/cryptodev/main.c b/crypto/cryptodev/main.c
new file mode 100644
-index 0000000..fe6d390
+index 0000000..57e5c38
--- /dev/null
+++ b/crypto/cryptodev/main.c
-@@ -0,0 +1,257 @@
+@@ -0,0 +1,267 @@
+/*
+ * Driver for /dev/crypto device (aka CryptoDev)
+ *
+ * Copyright (c) 2004 Michal Ludvig <mludvig at logix.net.nz>, SuSE Labs
-+ * Copyright (c) 2009,2010 Nikos Mavrogiannopoulos <nmav at gnutls.org>
++ * Copyright (c) 2009-2013 Nikos Mavrogiannopoulos <nmav at gnutls.org>
+ *
+ * This file is part of linux cryptodev.
+ *
@@ -3610,7 +5274,7 @@ index 0000000..fe6d390
+ }
+ return 0;
+out_err:
-+ dprintk(0, KERN_ERR, "CryptoAPI failure: %d\n", ret);
++ derr(0, "CryptoAPI failure: %d", ret);
+ return ret;
+}
+
@@ -3629,9 +5293,9 @@ index 0000000..fe6d390
+ data = (char *)__get_free_page(GFP_KERNEL);
+
+ if (unlikely(!data)) {
-+ dprintk(1, KERN_ERR, "Error getting free page.\n");
++ derr(1, "Error getting free page.");
+ return -ENOMEM;
-+ }
++ }
+
+ bufsize = PAGE_SIZE < nbytes ? PAGE_SIZE : nbytes;
+
@@ -3642,7 +5306,7 @@ index 0000000..fe6d390
+ size_t current_len = nbytes > bufsize ? bufsize : nbytes;
+
+ if (unlikely(copy_from_user(data, src, current_len))) {
-+ dprintk(1, KERN_ERR, "Error copying %d bytes from user address %p.\n", (int)current_len, src);
++ derr(1, "Error copying %zu bytes from user address %p.", current_len, src);
+ ret = -EFAULT;
+ break;
+ }
@@ -3652,13 +5316,13 @@ index 0000000..fe6d390
+ ret = hash_n_crypt(ses_ptr, cop, &sg, &sg, current_len);
+
+ if (unlikely(ret)) {
-+ dprintk(1, KERN_ERR, "hash_n_crypt failed.\n");
++ derr(1, "hash_n_crypt failed.");
+ break;
-+ }
++ }
+
+ if (ses_ptr->cdata.init != 0) {
+ if (unlikely(copy_to_user(dst, data, current_len))) {
-+ dprintk(1, KERN_ERR, "could not copy to user.\n");
++ derr(1, "could not copy to user.");
+ ret = -EFAULT;
+ break;
+ }
@@ -3686,8 +5350,7 @@ index 0000000..fe6d390
+ ret = get_userbuf(ses_ptr, cop->src, cop->len, cop->dst, cop->len,
+ kcop->task, kcop->mm, &src_sg, &dst_sg);
+ if (unlikely(ret)) {
-+ dprintk(1, KERN_ERR, "Error getting user pages. "
-+ "Falling back to non zero copy.\n");
++ derr(1, "Error getting user pages. Falling back to non zero copy.");
+ return __crypto_run_std(ses_ptr, cop);
+ }
+
@@ -3704,22 +5367,21 @@ index 0000000..fe6d390
+ int ret;
+
+ if (unlikely(cop->op != COP_ENCRYPT && cop->op != COP_DECRYPT)) {
-+ dprintk(1, KERN_DEBUG, "invalid operation op=%u\n", cop->op);
++ ddebug(1, "invalid operation op=%u", cop->op);
+ return -EINVAL;
+ }
+
+ /* this also enters ses_ptr->sem */
+ ses_ptr = crypto_get_session_by_sid(fcr, cop->ses);
+ if (unlikely(!ses_ptr)) {
-+ dprintk(1, KERN_ERR, "invalid session ID=0x%08X\n", cop->ses);
++ derr(1, "invalid session ID=0x%08X", cop->ses);
+ return -EINVAL;
+ }
+
+ if (ses_ptr->hdata.init != 0 && (cop->flags == 0 || cop->flags & COP_FLAG_RESET)) {
+ ret = cryptodev_hash_reset(&ses_ptr->hdata);
+ if (unlikely(ret)) {
-+ dprintk(1, KERN_ERR,
-+ "error in cryptodev_hash_reset()\n");
++ derr(1, "error in cryptodev_hash_reset()");
+ goto out_unlock;
+ }
+ }
@@ -3728,9 +5390,7 @@ index 0000000..fe6d390
+ int blocksize = ses_ptr->cdata.blocksize;
+
+ if (unlikely(cop->len % blocksize)) {
-+ dprintk(1, KERN_ERR,
-+ "data size (%u) isn't a multiple "
-+ "of block size (%u)\n",
++ derr(1, "data size (%u) isn't a multiple of block size (%u)",
+ cop->len, blocksize);
+ ret = -EINVAL;
+ goto out_unlock;
@@ -3741,6 +5401,20 @@ index 0000000..fe6d390
+ }
+
+ if (likely(cop->len)) {
++ if (cop->flags & COP_FLAG_NO_ZC) {
++ if (unlikely(ses_ptr->alignmask && !IS_ALIGNED((unsigned long)cop->src, ses_ptr->alignmask))) {
++ dwarning(2, "source address %p is not %d byte aligned - disabling zero copy",
++ cop->src, ses_ptr->alignmask + 1);
++ cop->flags &= ~COP_FLAG_NO_ZC;
++ }
++
++ if (unlikely(ses_ptr->alignmask && !IS_ALIGNED((unsigned long)cop->dst, ses_ptr->alignmask))) {
++ dwarning(2, "destination address %p is not %d byte aligned - disabling zero copy",
++ cop->dst, ses_ptr->alignmask + 1);
++ cop->flags &= ~COP_FLAG_NO_ZC;
++ }
++ }
++
+ if (cop->flags & COP_FLAG_NO_ZC)
+ ret = __crypto_run_std(ses_ptr, &kcop->cop);
+ else
@@ -3760,7 +5434,7 @@ index 0000000..fe6d390
+
+ ret = cryptodev_hash_final(&ses_ptr->hdata, kcop->hash_output);
+ if (unlikely(ret)) {
-+ dprintk(0, KERN_ERR, "CryptoAPI failure: %d\n", ret);
++ derr(0, "CryptoAPI failure: %d", ret);
+ goto out_unlock;
+ }
+ kcop->digestsize = ses_ptr->hdata.digestsize;
@@ -7789,10 +9463,10 @@ index 0000000..1d9349e
+}
diff --git a/crypto/cryptodev/tests/hmac_comp.c b/crypto/cryptodev/tests/hmac_comp.c
new file mode 100644
-index 0000000..451bedb
+index 0000000..a8709cb
--- /dev/null
+++ b/crypto/cryptodev/tests/hmac_comp.c
-@@ -0,0 +1,180 @@
+@@ -0,0 +1,187 @@
+/*
+ * Compare HMAC results with ones from openssl.
+ *
@@ -7833,6 +9507,7 @@ index 0000000..451bedb
+ unsigned char iv[BLOCK_SIZE];
+ unsigned char mac[AALG_MAX_RESULT_LEN];
+
++ unsigned char iv_comp[BLOCK_SIZE];
+ unsigned char mac_comp[AALG_MAX_RESULT_LEN];
+
+ struct crypt_op cryp;
@@ -7844,11 +9519,12 @@ index 0000000..451bedb
+ encrypted_comp = malloc(datalen);
+ memset(data, datalen & 0xff, datalen);
+ memset(encrypted, 0x27, datalen);
-+ memset(encrypted_comp, 0x27, datalen);
++ memset(encrypted_comp, 0x28, datalen);
+
+ memset(iv, 0x23, sizeof(iv));
++ memset(iv_comp, 0x23, sizeof(iv));
+ memset(mac, 0, sizeof(mac));
-+ memset(mac_comp, 0, sizeof(mac_comp));
++ memset(mac_comp, 1, sizeof(mac_comp));
+
+ memset(&cryp, 0, sizeof(cryp));
+
@@ -7860,6 +9536,7 @@ index 0000000..451bedb
+ cryp.iv = iv;
+ cryp.mac = mac;
+ cryp.op = COP_ENCRYPT;
++ cryp.flags = COP_FLAG_WRITE_IV;
+ if ((ret = ioctl(cfd, CIOCCRYPT, &cryp))) {
+ perror("ioctl(CIOCCRYPT)");
+ goto out;
@@ -7867,6 +9544,7 @@ index 0000000..451bedb
+
+ cryp.dst = encrypted_comp;
+ cryp.mac = mac_comp;
++ cryp.iv = iv_comp;
+
+ if ((ret = openssl_cioccrypt(sess, &cryp))) {
+ fprintf(stderr, "openssl_cioccrypt() failed!\n");
@@ -7876,6 +9554,9 @@ index 0000000..451bedb
+ if ((ret = memcmp(encrypted, encrypted_comp, cryp.len))) {
+ printf("fail for datalen %d, cipher texts do not match!\n", datalen);
+ }
++ if ((ret = memcmp(iv, iv_comp, BLOCK_SIZE))) {
++ printf("fail for datalen %d, updated IVs do not match!\n", datalen);
++ }
+ if ((ret = memcmp(mac, mac_comp, AALG_MAX_RESULT_LEN))) {
+ printf("fail for datalen 0x%x, MACs do not match!\n", datalen);
+ printf("wrong mac: ");
@@ -8846,14 +10527,14 @@ index 0000000..83d49da
+#define VERSION "1.6"
diff --git a/crypto/cryptodev/zc.c b/crypto/cryptodev/zc.c
new file mode 100644
-index 0000000..884dbab
+index 0000000..29b0501
--- /dev/null
+++ b/crypto/cryptodev/zc.c
-@@ -0,0 +1,217 @@
+@@ -0,0 +1,208 @@
+/*
+ * Driver for /dev/crypto device (aka CryptoDev)
+ *
-+ * Copyright (c) 2009-2011 Nikos Mavrogiannopoulos <nmav at gnutls.org>
++ * Copyright (c) 2009-2013 Nikos Mavrogiannopoulos <nmav at gnutls.org>
+ * Copyright (c) 2010 Phil Sutter
+ * Copyright (c) 2011, 2012 OpenSSL Software Foundation, Inc.
+ *
@@ -8932,7 +10613,7 @@ index 0000000..884dbab
+ return 0;
+}
+
-+int adjust_sg_array(struct csession * ses, int pagecount)
++int adjust_sg_array(struct csession *ses, int pagecount)
+{
+ struct scatterlist *sg;
+ struct page **pages;
@@ -8941,7 +10622,7 @@ index 0000000..884dbab
+ for (array_size = ses->array_size; array_size < pagecount;
+ array_size *= 2)
+ ;
-+ dprintk(0, KERN_DEBUG, "reallocating from %d to %d pages\n",
++ ddebug(0, "reallocating from %d to %d pages",
+ ses->array_size, array_size);
+ pages = krealloc(ses->pages, array_size * sizeof(struct page *),
+ GFP_KERNEL);
@@ -8962,7 +10643,7 @@ index 0000000..884dbab
+{
+ unsigned int i;
+
-+ for (i=0;i<ses->used_pages;i++) {
++ for (i = 0; i < ses->used_pages; i++) {
+ if (!PageReserved(ses->pages[i]))
+ SetPageDirty(ses->pages[i]);
+
@@ -8980,8 +10661,8 @@ index 0000000..884dbab
+ * dst might be the same as src.
+ */
+int get_userbuf(struct csession *ses,
-+ void* __user src, unsigned int src_len,
-+ void* __user dst, unsigned int dst_len,
++ void *__user src, unsigned int src_len,
++ void *__user dst, unsigned int dst_len,
+ struct task_struct *task, struct mm_struct *mm,
+ struct scatterlist **src_sg,
+ struct scatterlist **dst_sg)
@@ -8999,16 +10680,6 @@ index 0000000..884dbab
+ if (!dst && dst_len)
+ dst_len = 0;
+
-+ if (ses->alignmask && !IS_ALIGNED((unsigned long)src, ses->alignmask)) {
-+ dprintk(2, KERN_WARNING, "careful - source address %lx is not %d byte aligned\n",
-+ (unsigned long)src, ses->alignmask + 1);
-+ }
-+
-+ if (ses->alignmask && !IS_ALIGNED((unsigned long)dst, ses->alignmask)) {
-+ dprintk(2, KERN_WARNING, "careful - destination address %lx is not %d byte aligned\n",
-+ (unsigned long)dst, ses->alignmask + 1);
-+ }
-+
+ src_pagecount = PAGECOUNT(src, src_len);
+ dst_pagecount = PAGECOUNT(dst, dst_len);
+
@@ -9024,32 +10695,34 @@ index 0000000..884dbab
+ }
+
+ if (src == dst) { /* inplace operation */
++ /* When we encrypt for authenc modes we need to write
++ * more data than the ones we read. */
++ if (src_len < dst_len)
++ src_len = dst_len;
+ rc = __get_userbuf(src, src_len, 1, ses->used_pages,
+ ses->pages, ses->sg, task, mm);
+ if (unlikely(rc)) {
-+ dprintk(1, KERN_ERR,
-+ "failed to get user pages for data IO\n");
++ derr(1, "failed to get user pages for data IO");
+ return rc;
+ }
+ (*src_sg) = (*dst_sg) = ses->sg;
+ return 0;
+ }
+
-+ *src_sg = NULL; // default to no input
-+ *dst_sg = NULL; // default to ignore output
++ *src_sg = NULL; /* default to no input */
++ *dst_sg = NULL; /* default to ignore output */
+
-+ if(likely(src)) {
++ if (likely(src)) {
+ rc = __get_userbuf(src, src_len, 0, ses->readonly_pages,
+ ses->pages, ses->sg, task, mm);
+ if (unlikely(rc)) {
-+ dprintk(1, KERN_ERR,
-+ "failed to get user pages for data input\n");
++ derr(1, "failed to get user pages for data input");
+ return rc;
+ }
+ *src_sg = ses->sg;
+ }
+
-+ if(likely(dst)) {
++ if (likely(dst)) {
+ const unsigned int writable_pages =
+ ses->used_pages - ses->readonly_pages;
+ struct page **dst_pages = ses->pages + ses->readonly_pages;
@@ -9058,8 +10731,7 @@ index 0000000..884dbab
+ rc = __get_userbuf(dst, dst_len, 1, writable_pages,
+ dst_pages, *dst_sg, task, mm);
+ if (unlikely(rc)) {
-+ dprintk(1, KERN_ERR,
-+ "failed to get user pages for data output\n");
++ derr(1, "failed to get user pages for data output");
+ release_user_pages(ses); /* FIXME: use __release_userbuf(src, ...) */
+ return rc;
+ }
@@ -9069,7 +10741,7 @@ index 0000000..884dbab
+
diff --git a/crypto/cryptodev/zc.h b/crypto/cryptodev/zc.h
new file mode 100644
-index 0000000..b52616e
+index 0000000..6f975d6
--- /dev/null
+++ b/crypto/cryptodev/zc.h
@@ -0,0 +1,27 @@
@@ -9082,11 +10754,11 @@ index 0000000..b52616e
+int __get_userbuf(uint8_t __user *addr, uint32_t len, int write,
+ unsigned int pgcount, struct page **pg, struct scatterlist *sg,
+ struct task_struct *task, struct mm_struct *mm);
-+void release_user_pages(struct csession* ses);
++void release_user_pages(struct csession *ses);
+
+int get_userbuf(struct csession *ses,
-+ void* __user src, unsigned int src_len,
-+ void* __user dst, unsigned int dst_len,
++ void *__user src, unsigned int src_len,
++ void *__user dst, unsigned int dst_len,
+ struct task_struct *task, struct mm_struct *mm,
+ struct scatterlist **src_sg,
+ struct scatterlist **dst_sg);
@@ -9101,5 +10773,5 @@ index 0000000..b52616e
+
+#endif
--
-1.7.10.4
+1.9.1
--
1.8.2.1
More information about the linux-yocto
mailing list