diff options
| author | blogic <blogic@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2012-10-05 10:12:53 +0000 | 
|---|---|---|
| committer | blogic <blogic@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2012-10-05 10:12:53 +0000 | 
| commit | 5c105d9f3fd086aff195d3849dcf847d6b0bd927 (patch) | |
| tree | 1229a11f725bfa58aa7c57a76898553bb5f6654a /target/linux/generic/files/crypto/ocf/ixp4xx | |
| download | openwrt-5c105d9f3fd086aff195d3849dcf847d6b0bd927.tar.gz openwrt-5c105d9f3fd086aff195d3849dcf847d6b0bd927.zip | |
branch Attitude Adjustment
git-svn-id: svn://svn.openwrt.org/openwrt/branches/attitude_adjustment@33625 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'target/linux/generic/files/crypto/ocf/ixp4xx')
| -rw-r--r-- | target/linux/generic/files/crypto/ocf/ixp4xx/Makefile | 104 | ||||
| -rw-r--r-- | target/linux/generic/files/crypto/ocf/ixp4xx/ixp4xx.c | 1339 | 
2 files changed, 1443 insertions, 0 deletions
| diff --git a/target/linux/generic/files/crypto/ocf/ixp4xx/Makefile b/target/linux/generic/files/crypto/ocf/ixp4xx/Makefile new file mode 100644 index 000000000..d94a3b79f --- /dev/null +++ b/target/linux/generic/files/crypto/ocf/ixp4xx/Makefile @@ -0,0 +1,104 @@ +# for SGlinux builds +-include $(ROOTDIR)/modules/.config + +# +# You will need to point this at your Intel ixp425 includes,  this portion +# of the Makefile only really works under SGLinux with the appropriate libs +# installed.  They can be downloaded from http://www.snapgear.org/ +# +ifeq ($(CONFIG_CPU_IXP46X),y) +IXPLATFORM = ixp46X +else +ifeq ($(CONFIG_CPU_IXP43X),y) +IXPLATFORM = ixp43X +else +IXPLATFORM = ixp42X +endif +endif + +ifdef CONFIG_IXP400_LIB_2_4 +IX_XSCALE_SW = $(ROOTDIR)/modules/ixp425/ixp400-2.4/ixp400_xscale_sw +OSAL_DIR     = $(ROOTDIR)/modules/ixp425/ixp400-2.4/ixp_osal +endif +ifdef CONFIG_IXP400_LIB_2_1 +IX_XSCALE_SW = $(ROOTDIR)/modules/ixp425/ixp400-2.1/ixp400_xscale_sw +OSAL_DIR     = $(ROOTDIR)/modules/ixp425/ixp400-2.1/ixp_osal +endif +ifdef CONFIG_IXP400_LIB_2_0 +IX_XSCALE_SW = $(ROOTDIR)/modules/ixp425/ixp400-2.0/ixp400_xscale_sw +OSAL_DIR     = $(ROOTDIR)/modules/ixp425/ixp400-2.0/ixp_osal +endif +ifdef IX_XSCALE_SW +ifdef CONFIG_IXP400_LIB_2_4 +IXP_CFLAGS = \ +	-I$(ROOTDIR)/. \ +	-I$(IX_XSCALE_SW)/src/include \ +	-I$(OSAL_DIR)/common/include/ \ +	-I$(OSAL_DIR)/common/include/modules/ \ +	-I$(OSAL_DIR)/common/include/modules/ddk/ \ +	-I$(OSAL_DIR)/common/include/modules/bufferMgt/ \ +	-I$(OSAL_DIR)/common/include/modules/ioMem/ \ +	-I$(OSAL_DIR)/common/os/linux/include/ \ +	-I$(OSAL_DIR)/common/os/linux/include/core/  \ +	-I$(OSAL_DIR)/common/os/linux/include/modules/ \ +	-I$(OSAL_DIR)/common/os/linux/include/modules/ddk/ \ +	-I$(OSAL_DIR)/common/os/linux/include/modules/bufferMgt/ \ +	-I$(OSAL_DIR)/common/os/linux/include/modules/ioMem/ \ +	-I$(OSAL_DIR)/platforms/$(IXPLATFORM)/include/ \ +	-I$(OSAL_DIR)/platforms/$(IXPLATFORM)/os/linux/include/ \ +	-DENABLE_IOMEM -DENABLE_BUFFERMGT -DENABLE_DDK \ +	-DUSE_IXP4XX_CRYPTO +else +IXP_CFLAGS = \ +	-I$(ROOTDIR)/. \ +	-I$(IX_XSCALE_SW)/src/include \ +	-I$(OSAL_DIR)/ \ +	-I$(OSAL_DIR)/os/linux/include/ \ +	-I$(OSAL_DIR)/os/linux/include/modules/ \ +	-I$(OSAL_DIR)/os/linux/include/modules/ioMem/ \ +	-I$(OSAL_DIR)/os/linux/include/modules/bufferMgt/ \ +	-I$(OSAL_DIR)/os/linux/include/core/  \ +	-I$(OSAL_DIR)/os/linux/include/platforms/ \ +	-I$(OSAL_DIR)/os/linux/include/platforms/ixp400/ \ +	-I$(OSAL_DIR)/os/linux/include/platforms/ixp400/ixp425 \ +	-I$(OSAL_DIR)/os/linux/include/platforms/ixp400/ixp465 \ +	-I$(OSAL_DIR)/os/linux/include/core/ \ +	-I$(OSAL_DIR)/include/ \ +	-I$(OSAL_DIR)/include/modules/ \ +	-I$(OSAL_DIR)/include/modules/bufferMgt/ \ +	-I$(OSAL_DIR)/include/modules/ioMem/ \ +	-I$(OSAL_DIR)/include/platforms/ \ +	-I$(OSAL_DIR)/include/platforms/ixp400/ \ +	-DUSE_IXP4XX_CRYPTO +endif +endif +ifdef CONFIG_IXP400_LIB_1_4 +IXP_CFLAGS   = \ +	-I$(ROOTDIR)/. \ +	-I$(ROOTDIR)/modules/ixp425/ixp400-1.4/ixp400_xscale_sw/src/include \ +	-I$(ROOTDIR)/modules/ixp425/ixp400-1.4/ixp400_xscale_sw/src/linux \ +	-DUSE_IXP4XX_CRYPTO +endif +ifndef IXPDIR +IXPDIR = ixp-version-is-not-supported +endif + +ifeq ($(CONFIG_CPU_IXP46X),y) +IXP_CFLAGS += -D__ixp46X +else +ifeq ($(CONFIG_CPU_IXP43X),y) +IXP_CFLAGS += -D__ixp43X +else +IXP_CFLAGS += -D__ixp42X +endif +endif + +obj-$(CONFIG_OCF_IXP4XX) += ixp4xx.o + +obj ?= . +EXTRA_CFLAGS += $(IXP_CFLAGS) -I$(obj)/.. -I$(obj)/. + +ifdef TOPDIR +-include $(TOPDIR)/Rules.make +endif + diff --git a/target/linux/generic/files/crypto/ocf/ixp4xx/ixp4xx.c b/target/linux/generic/files/crypto/ocf/ixp4xx/ixp4xx.c new file mode 100644 index 000000000..ede598fa5 --- /dev/null +++ b/target/linux/generic/files/crypto/ocf/ixp4xx/ixp4xx.c @@ -0,0 +1,1339 @@ +/* + * An OCF module that uses Intels IXP CryptACC API to do the crypto. + * This driver requires the IXP400 Access Library that is available + * from Intel in order to operate (or compile). + * + * Written by David McCullough <david_mccullough@mcafee.com> + * Copyright (C) 2006-2011 David McCullough + * Copyright (C) 2004-2005 Intel Corporation. + * + * LICENSE TERMS + * + * The free distribution and use of this software in both source and binary + * form is allowed (with or without changes) provided that: + * + *   1. distributions of this source code include the above copyright + *      notice, this list of conditions and the following disclaimer; + * + *   2. distributions in binary form include the above copyright + *      notice, this list of conditions and the following disclaimer + *      in the documentation and/or other associated materials; + * + *   3. the copyright holder's name is not used to endorse products + *      built using this software without specific written permission. + * + * ALTERNATIVELY, provided that this notice is retained in full, this product + * may be distributed under the terms of the GNU General Public License (GPL), + * in which case the provisions of the GPL apply INSTEAD OF those given above. + * + * DISCLAIMER + * + * This software is provided 'as is' with no explicit or implied warranties + * in respect of its properties, including, but not limited to, correctness + * and/or fitness for purpose. + */ + +#include <linux/version.h> +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) && !defined(AUTOCONF_INCLUDED) +#include <linux/config.h> +#endif +#include <linux/module.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/sched.h> +#include <linux/wait.h> +#include <linux/crypto.h> +#include <linux/interrupt.h> +#include <asm/scatterlist.h> + +#include <IxTypes.h> +#include <IxOsBuffMgt.h> +#include <IxNpeDl.h> +#include <IxCryptoAcc.h> +#include <IxQMgr.h> +#include <IxOsServices.h> +#include <IxOsCacheMMU.h> + +#include <cryptodev.h> +#include <uio.h> + +#ifndef IX_MBUF_PRIV +#define IX_MBUF_PRIV(x) ((x)->priv) +#endif + +struct ixp_data; + +struct ixp_q { +	struct list_head	 ixp_q_list; +	struct ixp_data		*ixp_q_data; +	struct cryptop		*ixp_q_crp; +	struct cryptodesc	*ixp_q_ccrd; +	struct cryptodesc	*ixp_q_acrd; +	IX_MBUF				 ixp_q_mbuf; +	UINT8				*ixp_hash_dest; /* Location for hash in client buffer */ +	UINT8				*ixp_hash_src; /* Location of hash in internal buffer */ +	unsigned char		 ixp_q_iv_data[IX_CRYPTO_ACC_MAX_CIPHER_IV_LENGTH]; +	unsigned char		*ixp_q_iv; +}; + +struct ixp_data { +	int					 ixp_registered;	/* is the context registered */ +	int					 ixp_crd_flags;		/* detect direction changes */ + +	int					 ixp_cipher_alg; +	int					 ixp_auth_alg; + +	UINT32				 ixp_ctx_id; +	UINT32				 ixp_hash_key_id;	/* used when hashing */ +	IxCryptoAccCtx		 ixp_ctx; +	IX_MBUF				 ixp_pri_mbuf; +	IX_MBUF				 ixp_sec_mbuf; + +	struct work_struct   ixp_pending_work; +	struct work_struct   ixp_registration_work; +	struct list_head	 ixp_q;				/* unprocessed requests */ +}; + +#ifdef __ixp46X + +#define	MAX_IOP_SIZE	64	/* words */ +#define	MAX_OOP_SIZE	128 + +#define	MAX_PARAMS		3 + +struct ixp_pkq { +	struct list_head			 pkq_list; +	struct cryptkop				*pkq_krp; + +	IxCryptoAccPkeEauInOperands	 pkq_op; +	IxCryptoAccPkeEauOpResult	 pkq_result; + +	UINT32						 pkq_ibuf0[MAX_IOP_SIZE]; +	UINT32						 pkq_ibuf1[MAX_IOP_SIZE]; +	UINT32						 pkq_ibuf2[MAX_IOP_SIZE]; +	UINT32						 pkq_obuf[MAX_OOP_SIZE]; +}; + +static LIST_HEAD(ixp_pkq); /* current PK wait list */ +static struct ixp_pkq *ixp_pk_cur; +static spinlock_t ixp_pkq_lock; + +#endif /* __ixp46X */ + +static int ixp_blocked = 0; + +static int32_t			 ixp_id = -1; +static struct ixp_data **ixp_sessions = NULL; +static u_int32_t		 ixp_sesnum = 0; + +static int ixp_process(device_t, struct cryptop *, int); +static int ixp_newsession(device_t, u_int32_t *, struct cryptoini *); +static int ixp_freesession(device_t, u_int64_t); +#ifdef __ixp46X +static int ixp_kprocess(device_t, struct cryptkop *krp, int hint); +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) +static kmem_cache_t *qcache; +#else +static struct kmem_cache *qcache; +#endif + +#define debug ixp_debug +static int ixp_debug = 0; +module_param(ixp_debug, int, 0644); +MODULE_PARM_DESC(ixp_debug, "Enable debug"); + +static int ixp_init_crypto = 1; +module_param(ixp_init_crypto, int, 0444); /* RO after load/boot */ +MODULE_PARM_DESC(ixp_init_crypto, "Call ixCryptoAccInit (default is 1)"); + +static void ixp_process_pending(void *arg); +static void ixp_registration(void *arg); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) +static void ixp_process_pending_wq(struct work_struct *work); +static void ixp_registration_wq(struct work_struct *work); +#endif + +/* + * dummy device structure + */ + +static struct { +	softc_device_decl	sc_dev; +} ixpdev; + +static device_method_t ixp_methods = { +	/* crypto device methods */ +	DEVMETHOD(cryptodev_newsession,	ixp_newsession), +	DEVMETHOD(cryptodev_freesession,ixp_freesession), +	DEVMETHOD(cryptodev_process,	ixp_process), +#ifdef __ixp46X +	DEVMETHOD(cryptodev_kprocess,	ixp_kprocess), +#endif +}; + +/* + * Generate a new software session. + */ +static int +ixp_newsession(device_t dev, u_int32_t *sid, struct cryptoini *cri) +{ +	struct ixp_data *ixp; +	u_int32_t i; +#define AUTH_LEN(cri, def) \ +	(cri->cri_mlen ? cri->cri_mlen : (def)) + +	dprintk("%s():alg %d\n", __FUNCTION__,cri->cri_alg); +	if (sid == NULL || cri == NULL) { +		dprintk("%s,%d - EINVAL\n", __FILE__, __LINE__); +		return EINVAL; +	} + +	if (ixp_sessions) { +		for (i = 1; i < ixp_sesnum; i++) +			if (ixp_sessions[i] == NULL) +				break; +	} else +		i = 1;		/* NB: to silence compiler warning */ + +	if (ixp_sessions == NULL || i == ixp_sesnum) { +		struct ixp_data **ixpd; + +		if (ixp_sessions == NULL) { +			i = 1; /* We leave ixp_sessions[0] empty */ +			ixp_sesnum = CRYPTO_SW_SESSIONS; +		} else +			ixp_sesnum *= 2; + +		ixpd = kmalloc(ixp_sesnum * sizeof(struct ixp_data *), SLAB_ATOMIC); +		if (ixpd == NULL) { +			/* Reset session number */ +			if (ixp_sesnum == CRYPTO_SW_SESSIONS) +				ixp_sesnum = 0; +			else +				ixp_sesnum /= 2; +			dprintk("%s,%d: ENOBUFS\n", __FILE__, __LINE__); +			return ENOBUFS; +		} +		memset(ixpd, 0, ixp_sesnum * sizeof(struct ixp_data *)); + +		/* Copy existing sessions */ +		if (ixp_sessions) { +			memcpy(ixpd, ixp_sessions, +			    (ixp_sesnum / 2) * sizeof(struct ixp_data *)); +			kfree(ixp_sessions); +		} + +		ixp_sessions = ixpd; +	} + +	ixp_sessions[i] = (struct ixp_data *) kmalloc(sizeof(struct ixp_data), +			SLAB_ATOMIC); +	if (ixp_sessions[i] == NULL) { +		ixp_freesession(NULL, i); +		dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__); +		return ENOBUFS; +	} + +	*sid = i; + +	ixp = ixp_sessions[i]; +	memset(ixp, 0, sizeof(*ixp)); + +	ixp->ixp_cipher_alg = -1; +	ixp->ixp_auth_alg = -1; +	ixp->ixp_ctx_id = -1; +	INIT_LIST_HEAD(&ixp->ixp_q); + +	ixp->ixp_ctx.useDifferentSrcAndDestMbufs = 0; + +	while (cri) { +		switch (cri->cri_alg) { +		case CRYPTO_DES_CBC: +			ixp->ixp_cipher_alg = cri->cri_alg; +			ixp->ixp_ctx.cipherCtx.cipherAlgo = IX_CRYPTO_ACC_CIPHER_DES; +			ixp->ixp_ctx.cipherCtx.cipherMode = IX_CRYPTO_ACC_MODE_CBC; +			ixp->ixp_ctx.cipherCtx.cipherKeyLen = (cri->cri_klen + 7) / 8; +			ixp->ixp_ctx.cipherCtx.cipherBlockLen = IX_CRYPTO_ACC_DES_BLOCK_64; +			ixp->ixp_ctx.cipherCtx.cipherInitialVectorLen = +						IX_CRYPTO_ACC_DES_IV_64; +			memcpy(ixp->ixp_ctx.cipherCtx.key.cipherKey, +					cri->cri_key, (cri->cri_klen + 7) / 8); +			break; + +		case CRYPTO_3DES_CBC: +			ixp->ixp_cipher_alg = cri->cri_alg; +			ixp->ixp_ctx.cipherCtx.cipherAlgo = IX_CRYPTO_ACC_CIPHER_3DES; +			ixp->ixp_ctx.cipherCtx.cipherMode = IX_CRYPTO_ACC_MODE_CBC; +			ixp->ixp_ctx.cipherCtx.cipherKeyLen = (cri->cri_klen + 7) / 8; +			ixp->ixp_ctx.cipherCtx.cipherBlockLen = IX_CRYPTO_ACC_DES_BLOCK_64; +			ixp->ixp_ctx.cipherCtx.cipherInitialVectorLen = +						IX_CRYPTO_ACC_DES_IV_64; +			memcpy(ixp->ixp_ctx.cipherCtx.key.cipherKey, +					cri->cri_key, (cri->cri_klen + 7) / 8); +			break; + +		case CRYPTO_RIJNDAEL128_CBC: +			ixp->ixp_cipher_alg = cri->cri_alg; +			ixp->ixp_ctx.cipherCtx.cipherAlgo = IX_CRYPTO_ACC_CIPHER_AES; +			ixp->ixp_ctx.cipherCtx.cipherMode = IX_CRYPTO_ACC_MODE_CBC; +			ixp->ixp_ctx.cipherCtx.cipherKeyLen = (cri->cri_klen + 7) / 8; +			ixp->ixp_ctx.cipherCtx.cipherBlockLen = 16; +			ixp->ixp_ctx.cipherCtx.cipherInitialVectorLen = 16; +			memcpy(ixp->ixp_ctx.cipherCtx.key.cipherKey, +					cri->cri_key, (cri->cri_klen + 7) / 8); +			break; + +		case CRYPTO_MD5: +		case CRYPTO_MD5_HMAC: +			ixp->ixp_auth_alg = cri->cri_alg; +			ixp->ixp_ctx.authCtx.authAlgo = IX_CRYPTO_ACC_AUTH_MD5; +			ixp->ixp_ctx.authCtx.authDigestLen = AUTH_LEN(cri, MD5_HASH_LEN); +			ixp->ixp_ctx.authCtx.aadLen = 0; +			/* Only MD5_HMAC needs a key */ +			if (cri->cri_alg == CRYPTO_MD5_HMAC) { +				ixp->ixp_ctx.authCtx.authKeyLen = (cri->cri_klen + 7) / 8; +				if (ixp->ixp_ctx.authCtx.authKeyLen > +						sizeof(ixp->ixp_ctx.authCtx.key.authKey)) { +					printk( +						"ixp4xx: Invalid key length for MD5_HMAC - %d bits\n", +							cri->cri_klen); +					ixp_freesession(NULL, i); +					return EINVAL; +				} +				memcpy(ixp->ixp_ctx.authCtx.key.authKey, +						cri->cri_key, (cri->cri_klen + 7) / 8); +			} +			break; + +		case CRYPTO_SHA1: +		case CRYPTO_SHA1_HMAC: +			ixp->ixp_auth_alg = cri->cri_alg; +			ixp->ixp_ctx.authCtx.authAlgo = IX_CRYPTO_ACC_AUTH_SHA1; +			ixp->ixp_ctx.authCtx.authDigestLen = AUTH_LEN(cri, SHA1_HASH_LEN); +			ixp->ixp_ctx.authCtx.aadLen = 0; +			/* Only SHA1_HMAC needs a key */ +			if (cri->cri_alg == CRYPTO_SHA1_HMAC) { +				ixp->ixp_ctx.authCtx.authKeyLen = (cri->cri_klen + 7) / 8; +				if (ixp->ixp_ctx.authCtx.authKeyLen > +						sizeof(ixp->ixp_ctx.authCtx.key.authKey)) { +					printk( +						"ixp4xx: Invalid key length for SHA1_HMAC - %d bits\n", +							cri->cri_klen); +					ixp_freesession(NULL, i); +					return EINVAL; +				} +				memcpy(ixp->ixp_ctx.authCtx.key.authKey, +						cri->cri_key, (cri->cri_klen + 7) / 8); +			} +			break; + +		default: +			printk("ixp: unknown algo 0x%x\n", cri->cri_alg); +			ixp_freesession(NULL, i); +			return EINVAL; +		} +		cri = cri->cri_next; +	} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) +	INIT_WORK(&ixp->ixp_pending_work, ixp_process_pending_wq); +	INIT_WORK(&ixp->ixp_registration_work, ixp_registration_wq); +#else +	INIT_WORK(&ixp->ixp_pending_work, ixp_process_pending, ixp); +	INIT_WORK(&ixp->ixp_registration_work, ixp_registration, ixp); +#endif + +	return 0; +} + + +/* + * Free a session. + */ +static int +ixp_freesession(device_t dev, u_int64_t tid) +{ +	u_int32_t sid = CRYPTO_SESID2LID(tid); + +	dprintk("%s()\n", __FUNCTION__); +	if (sid > ixp_sesnum || ixp_sessions == NULL || +			ixp_sessions[sid] == NULL) { +		dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__); +		return EINVAL; +	} + +	/* Silently accept and return */ +	if (sid == 0) +		return 0; + +	if (ixp_sessions[sid]) { +		if (ixp_sessions[sid]->ixp_ctx_id != -1) { +			ixCryptoAccCtxUnregister(ixp_sessions[sid]->ixp_ctx_id); +			ixp_sessions[sid]->ixp_ctx_id = -1; +		} +		kfree(ixp_sessions[sid]); +	} +	ixp_sessions[sid] = NULL; +	if (ixp_blocked) { +		ixp_blocked = 0; +		crypto_unblock(ixp_id, CRYPTO_SYMQ); +	} +	return 0; +} + + +/* + * callback for when hash processing is complete + */ + +static void +ixp_hash_perform_cb( +	UINT32 hash_key_id, +	IX_MBUF *bufp, +	IxCryptoAccStatus status) +{ +	struct ixp_q *q; + +	dprintk("%s(%u, %p, 0x%x)\n", __FUNCTION__, hash_key_id, bufp, status); + +	if (bufp == NULL) { +		printk("ixp: NULL buf in %s\n", __FUNCTION__); +		return; +	} + +	q = IX_MBUF_PRIV(bufp); +	if (q == NULL) { +		printk("ixp: NULL priv in %s\n", __FUNCTION__); +		return; +	} + +	if (status == IX_CRYPTO_ACC_STATUS_SUCCESS) { +		/* On success, need to copy hash back into original client buffer */ +		memcpy(q->ixp_hash_dest, q->ixp_hash_src, +				(q->ixp_q_data->ixp_auth_alg == CRYPTO_SHA1) ? +					SHA1_HASH_LEN : MD5_HASH_LEN); +	} +	else { +		printk("ixp: hash perform failed status=%d\n", status); +		q->ixp_q_crp->crp_etype = EINVAL; +	} + +	/* Free internal buffer used for hashing */ +	kfree(IX_MBUF_MDATA(&q->ixp_q_mbuf)); + +	crypto_done(q->ixp_q_crp); +	kmem_cache_free(qcache, q); +} + +/* + * setup a request and perform it + */ +static void +ixp_q_process(struct ixp_q *q) +{ +	IxCryptoAccStatus status; +	struct ixp_data *ixp = q->ixp_q_data; +	int auth_off = 0; +	int auth_len = 0; +	int crypt_off = 0; +	int crypt_len = 0; +	int icv_off = 0; +	char *crypt_func; + +	dprintk("%s(%p)\n", __FUNCTION__, q); + +	if (q->ixp_q_ccrd) { +		if (q->ixp_q_ccrd->crd_flags & CRD_F_ENCRYPT) { +			if (q->ixp_q_ccrd->crd_flags & CRD_F_IV_EXPLICIT) { +				q->ixp_q_iv = q->ixp_q_ccrd->crd_iv; +			} else { +				q->ixp_q_iv = q->ixp_q_iv_data; +				read_random(q->ixp_q_iv, ixp->ixp_ctx.cipherCtx.cipherInitialVectorLen); +			} +			if ((q->ixp_q_ccrd->crd_flags & CRD_F_IV_PRESENT) == 0) +				crypto_copyback(q->ixp_q_crp->crp_flags, q->ixp_q_crp->crp_buf, +						q->ixp_q_ccrd->crd_inject, +						ixp->ixp_ctx.cipherCtx.cipherInitialVectorLen, +						(caddr_t) q->ixp_q_iv); +		} else { +			if (q->ixp_q_ccrd->crd_flags & CRD_F_IV_EXPLICIT) +				q->ixp_q_iv = q->ixp_q_ccrd->crd_iv; +			else { +				q->ixp_q_iv = q->ixp_q_iv_data; +				crypto_copydata(q->ixp_q_crp->crp_flags, q->ixp_q_crp->crp_buf, +						q->ixp_q_ccrd->crd_inject, +						ixp->ixp_ctx.cipherCtx.cipherInitialVectorLen, +						(caddr_t) q->ixp_q_iv); +			} +		} + +		if (q->ixp_q_acrd) { +			auth_off = q->ixp_q_acrd->crd_skip; +			auth_len = q->ixp_q_acrd->crd_len; +			icv_off  = q->ixp_q_acrd->crd_inject; +		} + +		crypt_off = q->ixp_q_ccrd->crd_skip; +		crypt_len = q->ixp_q_ccrd->crd_len; +	} else { /* if (q->ixp_q_acrd) */ +		auth_off = q->ixp_q_acrd->crd_skip; +		auth_len = q->ixp_q_acrd->crd_len; +		icv_off  = q->ixp_q_acrd->crd_inject; +	} + +	if (q->ixp_q_crp->crp_flags & CRYPTO_F_SKBUF) { +		struct sk_buff *skb = (struct sk_buff *) q->ixp_q_crp->crp_buf; +		if (skb_shinfo(skb)->nr_frags) { +			/* +			 * DAVIDM fix this limitation one day by using +			 * a buffer pool and chaining,  it is not currently +			 * needed for current user/kernel space acceleration +			 */ +			printk("ixp: Cannot handle fragmented skb's yet !\n"); +			q->ixp_q_crp->crp_etype = ENOENT; +			goto done; +		} +		IX_MBUF_MLEN(&q->ixp_q_mbuf) = +				IX_MBUF_PKT_LEN(&q->ixp_q_mbuf) =  skb->len; +		IX_MBUF_MDATA(&q->ixp_q_mbuf) = skb->data; +	} else if (q->ixp_q_crp->crp_flags & CRYPTO_F_IOV) { +		struct uio *uiop = (struct uio *) q->ixp_q_crp->crp_buf; +		if (uiop->uio_iovcnt != 1) { +			/* +			 * DAVIDM fix this limitation one day by using +			 * a buffer pool and chaining,  it is not currently +			 * needed for current user/kernel space acceleration +			 */ +			printk("ixp: Cannot handle more than 1 iovec yet !\n"); +			q->ixp_q_crp->crp_etype = ENOENT; +			goto done; +		} +		IX_MBUF_MLEN(&q->ixp_q_mbuf) = +				IX_MBUF_PKT_LEN(&q->ixp_q_mbuf) = uiop->uio_iov[0].iov_len; +		IX_MBUF_MDATA(&q->ixp_q_mbuf) = uiop->uio_iov[0].iov_base; +	} else /* contig buffer */ { +		IX_MBUF_MLEN(&q->ixp_q_mbuf)  = +				IX_MBUF_PKT_LEN(&q->ixp_q_mbuf) = q->ixp_q_crp->crp_ilen; +		IX_MBUF_MDATA(&q->ixp_q_mbuf) = q->ixp_q_crp->crp_buf; +	} + +	IX_MBUF_PRIV(&q->ixp_q_mbuf) = q; + +	if (ixp->ixp_auth_alg == CRYPTO_SHA1 || ixp->ixp_auth_alg == CRYPTO_MD5) { +		/* +		 * For SHA1 and MD5 hash, need to create an internal buffer that is big +		 * enough to hold the original data + the appropriate padding for the +		 * hash algorithm. +		 */ +		UINT8 *tbuf = NULL; + +		IX_MBUF_MLEN(&q->ixp_q_mbuf) = IX_MBUF_PKT_LEN(&q->ixp_q_mbuf) = +			((IX_MBUF_MLEN(&q->ixp_q_mbuf) * 8) + 72 + 511) / 8; +		tbuf = kmalloc(IX_MBUF_MLEN(&q->ixp_q_mbuf), SLAB_ATOMIC); +		 +		if (IX_MBUF_MDATA(&q->ixp_q_mbuf) == NULL) { +			printk("ixp: kmalloc(%u, SLAB_ATOMIC) failed\n", +					IX_MBUF_MLEN(&q->ixp_q_mbuf)); +			q->ixp_q_crp->crp_etype = ENOMEM; +			goto done; +		} +		memcpy(tbuf, &(IX_MBUF_MDATA(&q->ixp_q_mbuf))[auth_off], auth_len); + +		/* Set location in client buffer to copy hash into */ +		q->ixp_hash_dest = +			&(IX_MBUF_MDATA(&q->ixp_q_mbuf))[auth_off + auth_len]; + +		IX_MBUF_MDATA(&q->ixp_q_mbuf) = tbuf; + +		/* Set location in internal buffer for where hash starts */ +		q->ixp_hash_src = &(IX_MBUF_MDATA(&q->ixp_q_mbuf))[auth_len]; + +		crypt_func = "ixCryptoAccHashPerform"; +		status = ixCryptoAccHashPerform(ixp->ixp_ctx.authCtx.authAlgo, +				&q->ixp_q_mbuf, ixp_hash_perform_cb, 0, auth_len, auth_len, +				&ixp->ixp_hash_key_id); +	} +	else { +		crypt_func = "ixCryptoAccAuthCryptPerform"; +		status = ixCryptoAccAuthCryptPerform(ixp->ixp_ctx_id, &q->ixp_q_mbuf, +			NULL, auth_off, auth_len, crypt_off, crypt_len, icv_off, +			q->ixp_q_iv); +	} + +	if (IX_CRYPTO_ACC_STATUS_SUCCESS == status) +		return; + +	if (IX_CRYPTO_ACC_STATUS_QUEUE_FULL == status) { +		q->ixp_q_crp->crp_etype = ENOMEM; +		goto done; +	} + +	printk("ixp: %s failed %u\n", crypt_func, status); +	q->ixp_q_crp->crp_etype = EINVAL; + +done: +	crypto_done(q->ixp_q_crp); +	kmem_cache_free(qcache, q); +} + + +/* + * because we cannot process the Q from the Register callback + * we do it here on a task Q. + */ + +static void +ixp_process_pending(void *arg) +{ +	struct ixp_data *ixp = arg; +	struct ixp_q *q = NULL; + +	dprintk("%s(%p)\n", __FUNCTION__, arg); + +	if (!ixp) +		return; + +	while (!list_empty(&ixp->ixp_q)) { +		q = list_entry(ixp->ixp_q.next, struct ixp_q, ixp_q_list); +		list_del(&q->ixp_q_list); +		ixp_q_process(q); +	} +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) +static void +ixp_process_pending_wq(struct work_struct *work) +{ +	struct ixp_data *ixp = container_of(work, struct ixp_data, ixp_pending_work); +	ixp_process_pending(ixp); +} +#endif + +/* + * callback for when context registration is complete + */ + +static void +ixp_register_cb(UINT32 ctx_id, IX_MBUF *bufp, IxCryptoAccStatus status) +{ +	int i; +	struct ixp_data *ixp; +	struct ixp_q *q; + +	dprintk("%s(%d, %p, %d)\n", __FUNCTION__, ctx_id, bufp, status); + +	/* +	 * free any buffer passed in to this routine +	 */ +	if (bufp) { +		IX_MBUF_MLEN(bufp) = IX_MBUF_PKT_LEN(bufp) = 0; +		kfree(IX_MBUF_MDATA(bufp)); +		IX_MBUF_MDATA(bufp) = NULL; +	} + +	for (i = 0; i < ixp_sesnum; i++) { +		ixp = ixp_sessions[i]; +		if (ixp && ixp->ixp_ctx_id == ctx_id) +			break; +	} +	if (i >= ixp_sesnum) { +		printk("ixp: invalid context id %d\n", ctx_id); +		return; +	} + +	if (IX_CRYPTO_ACC_STATUS_WAIT == status) { +		/* this is normal to free the first of two buffers */ +		dprintk("ixp: register not finished yet.\n"); +		return; +	} + +	if (IX_CRYPTO_ACC_STATUS_SUCCESS != status) { +		printk("ixp: register failed 0x%x\n", status); +		while (!list_empty(&ixp->ixp_q)) { +			q = list_entry(ixp->ixp_q.next, struct ixp_q, ixp_q_list); +			list_del(&q->ixp_q_list); +			q->ixp_q_crp->crp_etype = EINVAL; +			crypto_done(q->ixp_q_crp); +			kmem_cache_free(qcache, q); +		} +		return; +	} + +	/* +	 * we are now registered,  we cannot start processing the Q here +	 * or we get strange errors with AES (DES/3DES seem to be ok). +	 */ +	ixp->ixp_registered = 1; +	schedule_work(&ixp->ixp_pending_work); +} + + +/* + * callback for when data processing is complete + */ + +static void +ixp_perform_cb( +	UINT32 ctx_id, +	IX_MBUF *sbufp, +	IX_MBUF *dbufp, +	IxCryptoAccStatus status) +{ +	struct ixp_q *q; + +	dprintk("%s(%d, %p, %p, 0x%x)\n", __FUNCTION__, ctx_id, sbufp, +			dbufp, status); + +	if (sbufp == NULL) { +		printk("ixp: NULL sbuf in ixp_perform_cb\n"); +		return; +	} + +	q = IX_MBUF_PRIV(sbufp); +	if (q == NULL) { +		printk("ixp: NULL priv in ixp_perform_cb\n"); +		return; +	} + +	if (status != IX_CRYPTO_ACC_STATUS_SUCCESS) { +		printk("ixp: perform failed status=%d\n", status); +		q->ixp_q_crp->crp_etype = EINVAL; +	} + +	crypto_done(q->ixp_q_crp); +	kmem_cache_free(qcache, q); +} + + +/* + * registration is not callable at IRQ time,  so we defer + * to a task queue,  this routines completes the registration for us + * when the task queue runs + * + * Unfortunately this means we cannot tell OCF that the driver is blocked, + * we do that on the next request. + */ + +static void +ixp_registration(void *arg) +{ +	struct ixp_data *ixp = arg; +	struct ixp_q *q = NULL; +	IX_MBUF *pri = NULL, *sec = NULL; +	int status = IX_CRYPTO_ACC_STATUS_SUCCESS; + +	if (!ixp) { +		printk("ixp: ixp_registration with no arg\n"); +		return; +	} + +	if (ixp->ixp_ctx_id != -1) { +		ixCryptoAccCtxUnregister(ixp->ixp_ctx_id); +		ixp->ixp_ctx_id = -1; +	} + +	if (list_empty(&ixp->ixp_q)) { +		printk("ixp: ixp_registration with no Q\n"); +		return; +	} + +	/* +	 * setup the primary and secondary buffers +	 */ +	q = list_entry(ixp->ixp_q.next, struct ixp_q, ixp_q_list); +	if (q->ixp_q_acrd) { +		pri = &ixp->ixp_pri_mbuf; +		sec = &ixp->ixp_sec_mbuf; +		IX_MBUF_MLEN(pri)  = IX_MBUF_PKT_LEN(pri) = 128; +		IX_MBUF_MDATA(pri) = (unsigned char *) kmalloc(128, SLAB_ATOMIC); +		IX_MBUF_MLEN(sec)  = IX_MBUF_PKT_LEN(sec) = 128; +		IX_MBUF_MDATA(sec) = (unsigned char *) kmalloc(128, SLAB_ATOMIC); +	} + +	/* Only need to register if a crypt op or HMAC op */ +	if (!(ixp->ixp_auth_alg == CRYPTO_SHA1 || +				ixp->ixp_auth_alg == CRYPTO_MD5)) { +		status = ixCryptoAccCtxRegister( +					&ixp->ixp_ctx, +					pri, sec, +					ixp_register_cb, +					ixp_perform_cb, +					&ixp->ixp_ctx_id); +	} +	else { +		/* Otherwise we start processing pending q */ +		schedule_work(&ixp->ixp_pending_work); +	} + +	if (IX_CRYPTO_ACC_STATUS_SUCCESS == status) +		return; + +	if (IX_CRYPTO_ACC_STATUS_EXCEED_MAX_TUNNELS == status) { +		printk("ixp: ixCryptoAccCtxRegister failed (out of tunnels)\n"); +		ixp_blocked = 1; +		/* perhaps we should return EGAIN on queued ops ? */ +		return; +	} + +	printk("ixp: ixCryptoAccCtxRegister failed %d\n", status); +	ixp->ixp_ctx_id = -1; + +	/* +	 * everything waiting is toasted +	 */ +	while (!list_empty(&ixp->ixp_q)) { +		q = list_entry(ixp->ixp_q.next, struct ixp_q, ixp_q_list); +		list_del(&q->ixp_q_list); +		q->ixp_q_crp->crp_etype = ENOENT; +		crypto_done(q->ixp_q_crp); +		kmem_cache_free(qcache, q); +	} +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) +static void +ixp_registration_wq(struct work_struct *work) +{ +	struct ixp_data *ixp = container_of(work, struct ixp_data, +								ixp_registration_work); +	ixp_registration(ixp); +} +#endif + +/* + * Process a request. + */ +static int +ixp_process(device_t dev, struct cryptop *crp, int hint) +{ +	struct ixp_data *ixp; +	unsigned int lid; +	struct ixp_q *q = NULL; +	int status; + +	dprintk("%s()\n", __FUNCTION__); + +	/* Sanity check */ +	if (crp == NULL) { +		dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__); +		return EINVAL; +	} + +	crp->crp_etype = 0; + +	if (ixp_blocked) +		return ERESTART; + +	if (crp->crp_desc == NULL || crp->crp_buf == NULL) { +		dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__); +		crp->crp_etype = EINVAL; +		goto done; +	} + +	/* +	 * find the session we are using +	 */ + +	lid = crp->crp_sid & 0xffffffff; +	if (lid >= ixp_sesnum || lid == 0 || ixp_sessions == NULL || +			ixp_sessions[lid] == NULL) { +		crp->crp_etype = ENOENT; +		dprintk("%s,%d: ENOENT\n", __FILE__, __LINE__); +		goto done; +	} +	ixp = ixp_sessions[lid]; + +	/* +	 * setup a new request ready for queuing +	 */ +	q = kmem_cache_alloc(qcache, SLAB_ATOMIC); +	if (q == NULL) { +		dprintk("%s,%d: ENOMEM\n", __FILE__, __LINE__); +		crp->crp_etype = ENOMEM; +		goto done; +	} +	/* +	 * save some cycles by only zeroing the important bits +	 */ +	memset(&q->ixp_q_mbuf, 0, sizeof(q->ixp_q_mbuf)); +	q->ixp_q_ccrd = NULL; +	q->ixp_q_acrd = NULL; +	q->ixp_q_crp = crp; +	q->ixp_q_data = ixp; + +	/* +	 * point the cipher and auth descriptors appropriately +	 * check that we have something to do +	 */ +	if (crp->crp_desc->crd_alg == ixp->ixp_cipher_alg) +		q->ixp_q_ccrd = crp->crp_desc; +	else if (crp->crp_desc->crd_alg == ixp->ixp_auth_alg) +		q->ixp_q_acrd = crp->crp_desc; +	else { +		crp->crp_etype = ENOENT; +		dprintk("%s,%d: bad desc match: ENOENT\n", __FILE__, __LINE__); +		goto done; +	} +	if (crp->crp_desc->crd_next) { +		if (crp->crp_desc->crd_next->crd_alg == ixp->ixp_cipher_alg) +			q->ixp_q_ccrd = crp->crp_desc->crd_next; +		else if (crp->crp_desc->crd_next->crd_alg == ixp->ixp_auth_alg) +			q->ixp_q_acrd = crp->crp_desc->crd_next; +		else { +			crp->crp_etype = ENOENT; +			dprintk("%s,%d: bad desc match: ENOENT\n", __FILE__, __LINE__); +			goto done; +		} +	} + +	/* +	 * If there is a direction change for this context then we mark it as +	 * unregistered and re-register is for the new direction.  This is not +	 * a very expensive operation and currently only tends to happen when +	 * user-space application are doing benchmarks +	 * +	 * DM - we should be checking for pending requests before unregistering. +	 */ +	if (q->ixp_q_ccrd && ixp->ixp_registered && +			ixp->ixp_crd_flags != (q->ixp_q_ccrd->crd_flags & CRD_F_ENCRYPT)) { +		dprintk("%s - detected direction change on session\n", __FUNCTION__); +		ixp->ixp_registered = 0; +	} + +	/* +	 * if we are registered,  call straight into the perform code +	 */ +	if (ixp->ixp_registered) { +		ixp_q_process(q); +		return 0; +	} + +	/* +	 * the only part of the context not set in newsession is the direction +	 * dependent parts +	 */ +	if (q->ixp_q_ccrd) { +		ixp->ixp_crd_flags = (q->ixp_q_ccrd->crd_flags & CRD_F_ENCRYPT); +		if (q->ixp_q_ccrd->crd_flags & CRD_F_ENCRYPT) { +			ixp->ixp_ctx.operation = q->ixp_q_acrd ? +					IX_CRYPTO_ACC_OP_ENCRYPT_AUTH : IX_CRYPTO_ACC_OP_ENCRYPT; +		} else { +			ixp->ixp_ctx.operation = q->ixp_q_acrd ? +					IX_CRYPTO_ACC_OP_AUTH_DECRYPT : IX_CRYPTO_ACC_OP_DECRYPT; +		} +	} else { +		/* q->ixp_q_acrd must be set if we are here */ +		ixp->ixp_ctx.operation = IX_CRYPTO_ACC_OP_AUTH_CALC; +	} + +	status = list_empty(&ixp->ixp_q); +	list_add_tail(&q->ixp_q_list, &ixp->ixp_q); +	if (status) +		schedule_work(&ixp->ixp_registration_work); +	return 0; + +done: +	if (q) +		kmem_cache_free(qcache, q); +	crypto_done(crp); +	return 0; +} + + +#ifdef __ixp46X +/* + * key processing support for the ixp465 + */ + + +/* + * copy a BN (LE) into a buffer (BE) an fill out the op appropriately + * assume zeroed and only copy bits that are significant + */ + +static int +ixp_copy_ibuf(struct crparam *p, IxCryptoAccPkeEauOperand *op, UINT32 *buf) +{ +	unsigned char *src = (unsigned char *) p->crp_p; +	unsigned char *dst; +	int len, bits = p->crp_nbits; + +	dprintk("%s()\n", __FUNCTION__); + +	if (bits > MAX_IOP_SIZE * sizeof(UINT32) * 8) { +		dprintk("%s - ibuf too big (%d > %d)\n", __FUNCTION__, +				bits, MAX_IOP_SIZE * sizeof(UINT32) * 8); +		return -1; +	} + +	len = (bits + 31) / 32; /* the number UINT32's needed */ + +	dst = (unsigned char *) &buf[len]; +	dst--; + +	while (bits > 0) { +		*dst-- = *src++; +		bits -= 8; +	} + +#if 0 /* no need to zero remaining bits as it is done during request alloc */ +	while (dst > (unsigned char *) buf) +		*dst-- = '\0'; +#endif + +	op->pData = buf; +	op->dataLen = len; +	return 0; +} + +/* + * copy out the result,  be as forgiving as we can about small output buffers + */ + +static int +ixp_copy_obuf(struct crparam *p, IxCryptoAccPkeEauOpResult *op, UINT32 *buf) +{ +	unsigned char *dst = (unsigned char *) p->crp_p; +	unsigned char *src = (unsigned char *) buf; +	int len, z, bits = p->crp_nbits; + +	dprintk("%s()\n", __FUNCTION__); + +	len = op->dataLen * sizeof(UINT32); + +	/* skip leading zeroes to be small buffer friendly */ +	z = 0; +	while (z < len && src[z] == '\0') +		z++; + +	src += len; +	src--; +	len -= z; + +	while (len > 0 && bits > 0) { +		*dst++ = *src--; +		len--; +		bits -= 8; +	} + +	while (bits > 0) { +		*dst++ = '\0'; +		bits -= 8; +	} + +	if (len > 0) { +		dprintk("%s - obuf is %d (z=%d, ob=%d) bytes too small\n", +				__FUNCTION__, len, z, p->crp_nbits / 8); +		return -1; +	} + +	return 0; +} + + +/* + * the parameter offsets for exp_mod + */ + +#define IXP_PARAM_BASE 0 +#define IXP_PARAM_EXP  1 +#define IXP_PARAM_MOD  2 +#define IXP_PARAM_RES  3 + +/* + * key processing complete callback,  is also used to start processing + * by passing a NULL for pResult + */ + +static void +ixp_kperform_cb( +	IxCryptoAccPkeEauOperation operation, +	IxCryptoAccPkeEauOpResult *pResult, +	BOOL carryOrBorrow, +	IxCryptoAccStatus status) +{ +	struct ixp_pkq *q, *tmp; +	unsigned long flags; + +	dprintk("%s(0x%x, %p, %d, 0x%x)\n", __FUNCTION__, operation, pResult, +			carryOrBorrow, status); + +	/* handle a completed request */ +	if (pResult) { +		if (ixp_pk_cur && &ixp_pk_cur->pkq_result == pResult) { +			q = ixp_pk_cur; +			if (status != IX_CRYPTO_ACC_STATUS_SUCCESS) { +				dprintk("%s() - op failed 0x%x\n", __FUNCTION__, status); +				q->pkq_krp->krp_status = ERANGE; /* could do better */ +			} else { +				/* copy out the result */ +				if (ixp_copy_obuf(&q->pkq_krp->krp_param[IXP_PARAM_RES], +						&q->pkq_result, q->pkq_obuf)) +					q->pkq_krp->krp_status = ERANGE; +			} +			crypto_kdone(q->pkq_krp); +			kfree(q); +			ixp_pk_cur = NULL; +		} else +			printk("%s - callback with invalid result pointer\n", __FUNCTION__); +	} + +	spin_lock_irqsave(&ixp_pkq_lock, flags); +	if (ixp_pk_cur || list_empty(&ixp_pkq)) { +		spin_unlock_irqrestore(&ixp_pkq_lock, flags); +		return; +	} + +	list_for_each_entry_safe(q, tmp, &ixp_pkq, pkq_list) { + +		list_del(&q->pkq_list); +		ixp_pk_cur = q; + +		spin_unlock_irqrestore(&ixp_pkq_lock, flags); + +		status = ixCryptoAccPkeEauPerform( +				IX_CRYPTO_ACC_OP_EAU_MOD_EXP, +				&q->pkq_op, +				ixp_kperform_cb, +				&q->pkq_result); +	 +		if (status == IX_CRYPTO_ACC_STATUS_SUCCESS) { +			dprintk("%s() - ixCryptoAccPkeEauPerform SUCCESS\n", __FUNCTION__); +			return; /* callback will return here for callback */ +		} else if (status == IX_CRYPTO_ACC_STATUS_RETRY) { +			printk("%s() - ixCryptoAccPkeEauPerform RETRY\n", __FUNCTION__); +		} else { +			printk("%s() - ixCryptoAccPkeEauPerform failed %d\n", +					__FUNCTION__, status); +		} +		q->pkq_krp->krp_status = ERANGE; /* could do better */ +		crypto_kdone(q->pkq_krp); +		kfree(q); +		spin_lock_irqsave(&ixp_pkq_lock, flags); +	} +	spin_unlock_irqrestore(&ixp_pkq_lock, flags); +} + + +static int +ixp_kprocess(device_t dev, struct cryptkop *krp, int hint) +{ +	struct ixp_pkq *q; +	int rc = 0; +	unsigned long flags; + +	dprintk("%s l1=%d l2=%d l3=%d l4=%d\n", __FUNCTION__, +			krp->krp_param[IXP_PARAM_BASE].crp_nbits, +			krp->krp_param[IXP_PARAM_EXP].crp_nbits, +			krp->krp_param[IXP_PARAM_MOD].crp_nbits, +			krp->krp_param[IXP_PARAM_RES].crp_nbits); + + +	if (krp->krp_op != CRK_MOD_EXP) { +		krp->krp_status = EOPNOTSUPP; +		goto err; +	} + +	q = (struct ixp_pkq *) kmalloc(sizeof(*q), GFP_KERNEL); +	if (q == NULL) { +		krp->krp_status = ENOMEM; +		goto err; +	} + +	/* +	 * The PKE engine does not appear to zero the output buffer +	 * appropriately, so we need to do it all here. +	 */ +	memset(q, 0, sizeof(*q)); + +	q->pkq_krp = krp; +	INIT_LIST_HEAD(&q->pkq_list); + +	if (ixp_copy_ibuf(&krp->krp_param[IXP_PARAM_BASE], &q->pkq_op.modExpOpr.M, +			q->pkq_ibuf0)) +		rc = 1; +	if (!rc && ixp_copy_ibuf(&krp->krp_param[IXP_PARAM_EXP], +				&q->pkq_op.modExpOpr.e, q->pkq_ibuf1)) +		rc = 2; +	if (!rc && ixp_copy_ibuf(&krp->krp_param[IXP_PARAM_MOD], +				&q->pkq_op.modExpOpr.N, q->pkq_ibuf2)) +		rc = 3; + +	if (rc) { +		kfree(q); +		krp->krp_status = ERANGE; +		goto err; +	} + +	q->pkq_result.pData           = q->pkq_obuf; +	q->pkq_result.dataLen         = +			(krp->krp_param[IXP_PARAM_RES].crp_nbits + 31) / 32; + +	spin_lock_irqsave(&ixp_pkq_lock, flags); +	list_add_tail(&q->pkq_list, &ixp_pkq); +	spin_unlock_irqrestore(&ixp_pkq_lock, flags); + +	if (!ixp_pk_cur) +		ixp_kperform_cb(0, NULL, 0, 0); +	return (0); + +err: +	crypto_kdone(krp); +	return (0); +} + + + +#ifdef CONFIG_OCF_RANDOMHARVEST +/* + * We run the random number generator output through SHA so that it + * is FIPS compliant. + */ + +static volatile int sha_done = 0; +static unsigned char sha_digest[20]; + +static void +ixp_hash_cb(UINT8 *digest, IxCryptoAccStatus status) +{ +	dprintk("%s(%p, %d)\n", __FUNCTION__, digest, status); +	if (sha_digest != digest) +		printk("digest error\n"); +	if (IX_CRYPTO_ACC_STATUS_SUCCESS == status) +		sha_done = 1; +	else +		sha_done = -status; +} + +static int +ixp_read_random(void *arg, u_int32_t *buf, int maxwords) +{ +	IxCryptoAccStatus status; +	int i, n, rc; + +	dprintk("%s(%p, %d)\n", __FUNCTION__, buf, maxwords); +	memset(buf, 0, maxwords * sizeof(*buf)); +	status = ixCryptoAccPkePseudoRandomNumberGet(maxwords, buf); +	if (status != IX_CRYPTO_ACC_STATUS_SUCCESS) { +		dprintk("%s: ixCryptoAccPkePseudoRandomNumberGet failed %d\n", +				__FUNCTION__, status); +		return 0; +	} + +	/* +	 * run the random data through SHA to make it look more random +	 */ + +	n = sizeof(sha_digest); /* process digest bytes at a time */ + +	rc = 0; +	for (i = 0; i < maxwords; i += n / sizeof(*buf)) { +		if ((maxwords - i) * sizeof(*buf) < n) +			n = (maxwords - i) * sizeof(*buf); +		sha_done = 0; +		status = ixCryptoAccPkeHashPerform(IX_CRYPTO_ACC_AUTH_SHA1, +				(UINT8 *) &buf[i], n, ixp_hash_cb, sha_digest); +		if (status != IX_CRYPTO_ACC_STATUS_SUCCESS) { +			dprintk("ixCryptoAccPkeHashPerform failed %d\n", status); +			return -EIO; +		} +		while (!sha_done) +			schedule(); +		if (sha_done < 0) { +			dprintk("ixCryptoAccPkeHashPerform failed CB %d\n", -sha_done); +			return 0; +		} +		memcpy(&buf[i], sha_digest, n); +		rc += n / sizeof(*buf);; +	} + +	return rc; +} +#endif /* CONFIG_OCF_RANDOMHARVEST */ + +#endif /* __ixp46X */ + + + +/* + * our driver startup and shutdown routines + */ + +static int +ixp_init(void) +{ +	dprintk("%s(%p)\n", __FUNCTION__, ixp_init); + +	if (ixp_init_crypto && ixCryptoAccInit() != IX_CRYPTO_ACC_STATUS_SUCCESS) +		printk("ixCryptoAccInit failed, assuming already initialised!\n"); + +	qcache = kmem_cache_create("ixp4xx_q", sizeof(struct ixp_q), 0, +				SLAB_HWCACHE_ALIGN, NULL +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) +				, NULL +#endif +				  ); +	if (!qcache) { +		printk("failed to create Qcache\n"); +		return -ENOENT; +	} + +	memset(&ixpdev, 0, sizeof(ixpdev)); +	softc_device_init(&ixpdev, "ixp4xx", 0, ixp_methods); + +	ixp_id = crypto_get_driverid(softc_get_device(&ixpdev), +				CRYPTOCAP_F_HARDWARE); +	if (ixp_id < 0) +		panic("IXP/OCF crypto device cannot initialize!"); + +#define	REGISTER(alg) \ +	crypto_register(ixp_id,alg,0,0) + +	REGISTER(CRYPTO_DES_CBC); +	REGISTER(CRYPTO_3DES_CBC); +	REGISTER(CRYPTO_RIJNDAEL128_CBC); +#ifdef CONFIG_OCF_IXP4XX_SHA1_MD5 +	REGISTER(CRYPTO_MD5); +	REGISTER(CRYPTO_SHA1); +#endif +	REGISTER(CRYPTO_MD5_HMAC); +	REGISTER(CRYPTO_SHA1_HMAC); +#undef REGISTER + +#ifdef __ixp46X +	spin_lock_init(&ixp_pkq_lock); +	/* +	 * we do not enable the go fast options here as they can potentially +	 * allow timing based attacks +	 * +	 * http://www.openssl.org/news/secadv_20030219.txt +	 */ +	ixCryptoAccPkeEauExpConfig(0, 0); +	crypto_kregister(ixp_id, CRK_MOD_EXP, 0); +#ifdef CONFIG_OCF_RANDOMHARVEST +	crypto_rregister(ixp_id, ixp_read_random, NULL); +#endif +#endif + +	return 0; +} + +static void +ixp_exit(void) +{ +	dprintk("%s()\n", __FUNCTION__); +	crypto_unregister_all(ixp_id); +	ixp_id = -1; +	kmem_cache_destroy(qcache); +	qcache = NULL; +} + +module_init(ixp_init); +module_exit(ixp_exit); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("David McCullough <dmccullough@cyberguard.com>"); +MODULE_DESCRIPTION("ixp (OCF module for IXP4xx crypto)"); | 
