[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