diff options
Diffstat (limited to 'target/linux/generic-2.6/files/crypto/ocf/safe/safe.c')
| -rw-r--r-- | target/linux/generic-2.6/files/crypto/ocf/safe/safe.c | 2288 | 
1 files changed, 2288 insertions, 0 deletions
| diff --git a/target/linux/generic-2.6/files/crypto/ocf/safe/safe.c b/target/linux/generic-2.6/files/crypto/ocf/safe/safe.c new file mode 100644 index 000000000..a77e3a237 --- /dev/null +++ b/target/linux/generic-2.6/files/crypto/ocf/safe/safe.c @@ -0,0 +1,2288 @@ +/*- + * Linux port done by David McCullough <david_mccullough@securecomputing.com> + * Copyright (C) 2004-2007 David McCullough + * The license and original author are listed below. + * + * Copyright (c) 2003 Sam Leffler, Errno Consulting + * Copyright (c) 2003 Global Technology Associates, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * +__FBSDID("$FreeBSD: src/sys/dev/safe/safe.c,v 1.18 2007/03/21 03:42:50 sam Exp $"); + */ + +#ifndef AUTOCONF_INCLUDED +#include <linux/config.h> +#endif +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/wait.h> +#include <linux/sched.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/random.h> +#include <linux/version.h> +#include <linux/skbuff.h> +#include <asm/io.h> + +/* + * SafeNet SafeXcel-1141 hardware crypto accelerator + */ + +#include <cryptodev.h> +#include <uio.h> +#include <safe/safereg.h> +#include <safe/safevar.h> + +#if 1 +#define	DPRINTF(a)	do { \ +						if (debug) { \ +							printk("%s: ", sc ? \ +								device_get_nameunit(sc->sc_dev) : "safe"); \ +							printk a; \ +						} \ +					} while (0) +#else +#define	DPRINTF(a) +#endif + +/* + * until we find a cleaner way, include the BSD md5/sha1 code + * here + */ +#define HMAC_HACK 1 +#ifdef HMAC_HACK +#define LITTLE_ENDIAN 1234 +#define BIG_ENDIAN 4321 +#ifdef __LITTLE_ENDIAN +#define BYTE_ORDER LITTLE_ENDIAN +#endif +#ifdef __BIG_ENDIAN +#define BYTE_ORDER BIG_ENDIAN +#endif +#include <safe/md5.h> +#include <safe/md5.c> +#include <safe/sha1.h> +#include <safe/sha1.c> + +u_int8_t hmac_ipad_buffer[64] = { +    0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +    0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +    0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +    0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +    0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +    0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +    0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +    0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36 +}; + +u_int8_t hmac_opad_buffer[64] = { +    0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, +    0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, +    0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, +    0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, +    0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, +    0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, +    0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, +    0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C +}; +#endif /* HMAC_HACK */ + +/* add proc entry for this */ +struct safe_stats safestats; + +#define debug safe_debug +int safe_debug = 0; +module_param(safe_debug, int, 0644); +MODULE_PARM_DESC(safe_debug, "Enable debug"); + +static	void safe_callback(struct safe_softc *, struct safe_ringentry *); +static	void safe_feed(struct safe_softc *, struct safe_ringentry *); +#if defined(CONFIG_OCF_RANDOMHARVEST) && !defined(SAFE_NO_RNG) +static	void safe_rng_init(struct safe_softc *); +int safe_rngbufsize = 8;		/* 32 bytes each read  */ +module_param(safe_rngbufsize, int, 0644); +MODULE_PARM_DESC(safe_rngbufsize, "RNG polling buffer size (32-bit words)"); +int safe_rngmaxalarm = 8;		/* max alarms before reset */ +module_param(safe_rngmaxalarm, int, 0644); +MODULE_PARM_DESC(safe_rngmaxalarm, "RNG max alarms before reset"); +#endif /* SAFE_NO_RNG */ + +static void safe_totalreset(struct safe_softc *sc); +static int safe_dmamap_aligned(struct safe_softc *sc, const struct safe_operand *op); +static int safe_dmamap_uniform(struct safe_softc *sc, const struct safe_operand *op); +static int safe_free_entry(struct safe_softc *sc, struct safe_ringentry *re); +static int safe_kprocess(device_t dev, struct cryptkop *krp, int hint); +static int safe_kstart(struct safe_softc *sc); +static int safe_ksigbits(struct safe_softc *sc, struct crparam *cr); +static void safe_kfeed(struct safe_softc *sc); +static void safe_kpoll(unsigned long arg); +static void safe_kload_reg(struct safe_softc *sc, u_int32_t off, +								u_int32_t len, struct crparam *n); + +static	int safe_newsession(device_t, u_int32_t *, struct cryptoini *); +static	int safe_freesession(device_t, u_int64_t); +static	int safe_process(device_t, struct cryptop *, int); + +static device_method_t safe_methods = { +	/* crypto device methods */ +	DEVMETHOD(cryptodev_newsession,	safe_newsession), +	DEVMETHOD(cryptodev_freesession,safe_freesession), +	DEVMETHOD(cryptodev_process,	safe_process), +	DEVMETHOD(cryptodev_kprocess,	safe_kprocess), +}; + +#define	READ_REG(sc,r)			readl((sc)->sc_base_addr + (r)) +#define WRITE_REG(sc,r,val)		writel((val), (sc)->sc_base_addr + (r)) + +#define SAFE_MAX_CHIPS 8 +static struct safe_softc *safe_chip_idx[SAFE_MAX_CHIPS]; + +/* + * split our buffers up into safe DMAable byte fragments to avoid lockup + * bug in 1141 HW on rev 1.0. + */ + +static int +pci_map_linear( +	struct safe_softc *sc, +	struct safe_operand *buf, +	void *addr, +	int len) +{ +	dma_addr_t tmp; +	int chunk, tlen = len; + +	tmp = pci_map_single(sc->sc_pcidev, addr, len, PCI_DMA_BIDIRECTIONAL); + +	buf->mapsize += len; +	while (len > 0) { +		chunk = (len > sc->sc_max_dsize) ? sc->sc_max_dsize : len; +		buf->segs[buf->nsegs].ds_addr = tmp; +		buf->segs[buf->nsegs].ds_len  = chunk; +		buf->segs[buf->nsegs].ds_tlen = tlen; +		buf->nsegs++; +		tmp  += chunk; +		len  -= chunk; +		tlen = 0; +	} +	return 0; +} + +/* + * map in a given uio buffer (great on some arches :-) + */ + +static int +pci_map_uio(struct safe_softc *sc, struct safe_operand *buf, struct uio *uio) +{ +	struct iovec *iov = uio->uio_iov; +	int n; + +	DPRINTF(("%s()\n", __FUNCTION__)); + +	buf->mapsize = 0; +	buf->nsegs = 0; + +	for (n = 0; n < uio->uio_iovcnt; n++) { +		pci_map_linear(sc, buf, iov->iov_base, iov->iov_len); +		iov++; +	} + +	/* identify this buffer by the first segment */ +	buf->map = (void *) buf->segs[0].ds_addr; +	return(0); +} + +/* + * map in a given sk_buff + */ + +static int +pci_map_skb(struct safe_softc *sc,struct safe_operand *buf,struct sk_buff *skb) +{ +	int i; + +	DPRINTF(("%s()\n", __FUNCTION__)); + +	buf->mapsize = 0; +	buf->nsegs = 0; + +	pci_map_linear(sc, buf, skb->data, skb_headlen(skb)); + +	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { +		pci_map_linear(sc, buf, +				page_address(skb_shinfo(skb)->frags[i].page) + +				                        skb_shinfo(skb)->frags[i].page_offset, +				skb_shinfo(skb)->frags[i].size); +	} + +	/* identify this buffer by the first segment */ +	buf->map = (void *) buf->segs[0].ds_addr; +	return(0); +} + + +#if 0 /* not needed at this time */ +static void +pci_sync_operand(struct safe_softc *sc, struct safe_operand *buf) +{ +	int i; + +	DPRINTF(("%s()\n", __FUNCTION__)); +	for (i = 0; i < buf->nsegs; i++) +		pci_dma_sync_single_for_cpu(sc->sc_pcidev, buf->segs[i].ds_addr, +				buf->segs[i].ds_len, PCI_DMA_BIDIRECTIONAL); +} +#endif + +static void +pci_unmap_operand(struct safe_softc *sc, struct safe_operand *buf) +{ +	int i; +	DPRINTF(("%s()\n", __FUNCTION__)); +	for (i = 0; i < buf->nsegs; i++) { +		if (buf->segs[i].ds_tlen) { +			DPRINTF(("%s - unmap %d 0x%x %d\n", __FUNCTION__, i, buf->segs[i].ds_addr, buf->segs[i].ds_tlen)); +			pci_unmap_single(sc->sc_pcidev, buf->segs[i].ds_addr, +					buf->segs[i].ds_tlen, PCI_DMA_BIDIRECTIONAL); +			DPRINTF(("%s - unmap %d 0x%x %d done\n", __FUNCTION__, i, buf->segs[i].ds_addr, buf->segs[i].ds_tlen)); +		} +		buf->segs[i].ds_addr = 0; +		buf->segs[i].ds_len = 0; +		buf->segs[i].ds_tlen = 0; +	} +	buf->nsegs = 0; +	buf->mapsize = 0; +	buf->map = 0; +} + + +/* + * SafeXcel Interrupt routine + */ +static irqreturn_t +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19) +safe_intr(int irq, void *arg) +#else +safe_intr(int irq, void *arg, struct pt_regs *regs) +#endif +{ +	struct safe_softc *sc = arg; +	int stat; +	unsigned long flags; + +	stat = READ_REG(sc, SAFE_HM_STAT); + +	DPRINTF(("%s(stat=0x%x)\n", __FUNCTION__, stat)); + +	if (stat == 0)		/* shared irq, not for us */ +		return IRQ_NONE; + +	WRITE_REG(sc, SAFE_HI_CLR, stat);	/* IACK */ + +	if ((stat & SAFE_INT_PE_DDONE)) { +		/* +		 * Descriptor(s) done; scan the ring and +		 * process completed operations. +		 */ +		spin_lock_irqsave(&sc->sc_ringmtx, flags); +		while (sc->sc_back != sc->sc_front) { +			struct safe_ringentry *re = sc->sc_back; + +#ifdef SAFE_DEBUG +			if (debug) { +				safe_dump_ringstate(sc, __func__); +				safe_dump_request(sc, __func__, re); +			} +#endif +			/* +			 * safe_process marks ring entries that were allocated +			 * but not used with a csr of zero.  This insures the +			 * ring front pointer never needs to be set backwards +			 * in the event that an entry is allocated but not used +			 * because of a setup error. +			 */ +			DPRINTF(("%s re->re_desc.d_csr=0x%x\n", __FUNCTION__, re->re_desc.d_csr)); +			if (re->re_desc.d_csr != 0) { +				if (!SAFE_PE_CSR_IS_DONE(re->re_desc.d_csr)) { +					DPRINTF(("%s !CSR_IS_DONE\n", __FUNCTION__)); +					break; +				} +				if (!SAFE_PE_LEN_IS_DONE(re->re_desc.d_len)) { +					DPRINTF(("%s !LEN_IS_DONE\n", __FUNCTION__)); +					break; +				} +				sc->sc_nqchip--; +				safe_callback(sc, re); +			} +			if (++(sc->sc_back) == sc->sc_ringtop) +				sc->sc_back = sc->sc_ring; +		} +		spin_unlock_irqrestore(&sc->sc_ringmtx, flags); +	} + +	/* +	 * Check to see if we got any DMA Error +	 */ +	if (stat & SAFE_INT_PE_ERROR) { +		printk("%s: dmaerr dmastat %08x\n", device_get_nameunit(sc->sc_dev), +				(int)READ_REG(sc, SAFE_PE_DMASTAT)); +		safestats.st_dmaerr++; +		safe_totalreset(sc); +#if 0 +		safe_feed(sc); +#endif +	} + +	if (sc->sc_needwakeup) {		/* XXX check high watermark */ +		int wakeup = sc->sc_needwakeup & (CRYPTO_SYMQ|CRYPTO_ASYMQ); +		DPRINTF(("%s: wakeup crypto %x\n", __func__, +			sc->sc_needwakeup)); +		sc->sc_needwakeup &= ~wakeup; +		crypto_unblock(sc->sc_cid, wakeup); +	} +	 +	return IRQ_HANDLED; +} + +/* + * safe_feed() - post a request to chip + */ +static void +safe_feed(struct safe_softc *sc, struct safe_ringentry *re) +{ +	DPRINTF(("%s()\n", __FUNCTION__)); +#ifdef SAFE_DEBUG +	if (debug) { +		safe_dump_ringstate(sc, __func__); +		safe_dump_request(sc, __func__, re); +	} +#endif +	sc->sc_nqchip++; +	if (sc->sc_nqchip > safestats.st_maxqchip) +		safestats.st_maxqchip = sc->sc_nqchip; +	/* poke h/w to check descriptor ring, any value can be written */ +	WRITE_REG(sc, SAFE_HI_RD_DESCR, 0); +} + +#define	N(a)	(sizeof(a) / sizeof (a[0])) +static void +safe_setup_enckey(struct safe_session *ses, caddr_t key) +{ +	int i; + +	bcopy(key, ses->ses_key, ses->ses_klen / 8); + +	/* PE is little-endian, insure proper byte order */ +	for (i = 0; i < N(ses->ses_key); i++) +		ses->ses_key[i] = htole32(ses->ses_key[i]); +} + +static void +safe_setup_mackey(struct safe_session *ses, int algo, caddr_t key, int klen) +{ +#ifdef HMAC_HACK +	MD5_CTX md5ctx; +	SHA1_CTX sha1ctx; +	int i; + + +	for (i = 0; i < klen; i++) +		key[i] ^= HMAC_IPAD_VAL; + +	if (algo == CRYPTO_MD5_HMAC) { +		MD5Init(&md5ctx); +		MD5Update(&md5ctx, key, klen); +		MD5Update(&md5ctx, hmac_ipad_buffer, MD5_HMAC_BLOCK_LEN - klen); +		bcopy(md5ctx.md5_st8, ses->ses_hminner, sizeof(md5ctx.md5_st8)); +	} else { +		SHA1Init(&sha1ctx); +		SHA1Update(&sha1ctx, key, klen); +		SHA1Update(&sha1ctx, hmac_ipad_buffer, +		    SHA1_HMAC_BLOCK_LEN - klen); +		bcopy(sha1ctx.h.b32, ses->ses_hminner, sizeof(sha1ctx.h.b32)); +	} + +	for (i = 0; i < klen; i++) +		key[i] ^= (HMAC_IPAD_VAL ^ HMAC_OPAD_VAL); + +	if (algo == CRYPTO_MD5_HMAC) { +		MD5Init(&md5ctx); +		MD5Update(&md5ctx, key, klen); +		MD5Update(&md5ctx, hmac_opad_buffer, MD5_HMAC_BLOCK_LEN - klen); +		bcopy(md5ctx.md5_st8, ses->ses_hmouter, sizeof(md5ctx.md5_st8)); +	} else { +		SHA1Init(&sha1ctx); +		SHA1Update(&sha1ctx, key, klen); +		SHA1Update(&sha1ctx, hmac_opad_buffer, +		    SHA1_HMAC_BLOCK_LEN - klen); +		bcopy(sha1ctx.h.b32, ses->ses_hmouter, sizeof(sha1ctx.h.b32)); +	} + +	for (i = 0; i < klen; i++) +		key[i] ^= HMAC_OPAD_VAL; + +#if 0 +	/* +	 * this code prevents SHA working on a BE host, +	 * so it is obviously wrong.  I think the byte +	 * swap setup we do with the chip fixes this for us +	 */ + +	/* PE is little-endian, insure proper byte order */ +	for (i = 0; i < N(ses->ses_hminner); i++) { +		ses->ses_hminner[i] = htole32(ses->ses_hminner[i]); +		ses->ses_hmouter[i] = htole32(ses->ses_hmouter[i]); +	} +#endif +#else /* HMAC_HACK */ +	printk("safe: md5/sha not implemented\n"); +#endif /* HMAC_HACK */ +} +#undef N + +/* + * Allocate a new 'session' and return an encoded session id.  'sidp' + * contains our registration id, and should contain an encoded session + * id on successful allocation. + */ +static int +safe_newsession(device_t dev, u_int32_t *sidp, struct cryptoini *cri) +{ +	struct safe_softc *sc = device_get_softc(dev); +	struct cryptoini *c, *encini = NULL, *macini = NULL; +	struct safe_session *ses = NULL; +	int sesn; + +	DPRINTF(("%s()\n", __FUNCTION__)); + +	if (sidp == NULL || cri == NULL || sc == NULL) +		return (EINVAL); + +	for (c = cri; c != NULL; c = c->cri_next) { +		if (c->cri_alg == CRYPTO_MD5_HMAC || +		    c->cri_alg == CRYPTO_SHA1_HMAC || +		    c->cri_alg == CRYPTO_NULL_HMAC) { +			if (macini) +				return (EINVAL); +			macini = c; +		} else if (c->cri_alg == CRYPTO_DES_CBC || +		    c->cri_alg == CRYPTO_3DES_CBC || +		    c->cri_alg == CRYPTO_AES_CBC || +		    c->cri_alg == CRYPTO_NULL_CBC) { +			if (encini) +				return (EINVAL); +			encini = c; +		} else +			return (EINVAL); +	} +	if (encini == NULL && macini == NULL) +		return (EINVAL); +	if (encini) {			/* validate key length */ +		switch (encini->cri_alg) { +		case CRYPTO_DES_CBC: +			if (encini->cri_klen != 64) +				return (EINVAL); +			break; +		case CRYPTO_3DES_CBC: +			if (encini->cri_klen != 192) +				return (EINVAL); +			break; +		case CRYPTO_AES_CBC: +			if (encini->cri_klen != 128 && +			    encini->cri_klen != 192 && +			    encini->cri_klen != 256) +				return (EINVAL); +			break; +		} +	} + +	if (sc->sc_sessions == NULL) { +		ses = sc->sc_sessions = (struct safe_session *) +			kmalloc(sizeof(struct safe_session), SLAB_ATOMIC); +		if (ses == NULL) +			return (ENOMEM); +		memset(ses, 0, sizeof(struct safe_session)); +		sesn = 0; +		sc->sc_nsessions = 1; +	} else { +		for (sesn = 0; sesn < sc->sc_nsessions; sesn++) { +			if (sc->sc_sessions[sesn].ses_used == 0) { +				ses = &sc->sc_sessions[sesn]; +				break; +			} +		} + +		if (ses == NULL) { +			sesn = sc->sc_nsessions; +			ses = (struct safe_session *) +				kmalloc((sesn + 1) * sizeof(struct safe_session), SLAB_ATOMIC); +			if (ses == NULL) +				return (ENOMEM); +			memset(ses, 0, (sesn + 1) * sizeof(struct safe_session)); +			bcopy(sc->sc_sessions, ses, sesn * +			    sizeof(struct safe_session)); +			bzero(sc->sc_sessions, sesn * +			    sizeof(struct safe_session)); +			kfree(sc->sc_sessions); +			sc->sc_sessions = ses; +			ses = &sc->sc_sessions[sesn]; +			sc->sc_nsessions++; +		} +	} + +	bzero(ses, sizeof(struct safe_session)); +	ses->ses_used = 1; + +	if (encini) { +		/* get an IV */ +		/* XXX may read fewer than requested */ +		read_random(ses->ses_iv, sizeof(ses->ses_iv)); + +		ses->ses_klen = encini->cri_klen; +		if (encini->cri_key != NULL) +			safe_setup_enckey(ses, encini->cri_key); +	} + +	if (macini) { +		ses->ses_mlen = macini->cri_mlen; +		if (ses->ses_mlen == 0) { +			if (macini->cri_alg == CRYPTO_MD5_HMAC) +				ses->ses_mlen = MD5_HASH_LEN; +			else +				ses->ses_mlen = SHA1_HASH_LEN; +		} + +		if (macini->cri_key != NULL) { +			safe_setup_mackey(ses, macini->cri_alg, macini->cri_key, +			    macini->cri_klen / 8); +		} +	} + +	*sidp = SAFE_SID(device_get_unit(sc->sc_dev), sesn); +	return (0); +} + +/* + * Deallocate a session. + */ +static int +safe_freesession(device_t dev, u_int64_t tid) +{ +	struct safe_softc *sc = device_get_softc(dev); +	int session, ret; +	u_int32_t sid = ((u_int32_t) tid) & 0xffffffff; + +	DPRINTF(("%s()\n", __FUNCTION__)); + +	if (sc == NULL) +		return (EINVAL); + +	session = SAFE_SESSION(sid); +	if (session < sc->sc_nsessions) { +		bzero(&sc->sc_sessions[session], sizeof(sc->sc_sessions[session])); +		ret = 0; +	} else +		ret = EINVAL; +	return (ret); +} + + +static int +safe_process(device_t dev, struct cryptop *crp, int hint) +{ +	struct safe_softc *sc = device_get_softc(dev); +	int err = 0, i, nicealign, uniform; +	struct cryptodesc *crd1, *crd2, *maccrd, *enccrd; +	int bypass, oplen, ivsize; +	caddr_t iv; +	int16_t coffset; +	struct safe_session *ses; +	struct safe_ringentry *re; +	struct safe_sarec *sa; +	struct safe_pdesc *pd; +	u_int32_t cmd0, cmd1, staterec; +	unsigned long flags; + +	DPRINTF(("%s()\n", __FUNCTION__)); + +	if (crp == NULL || crp->crp_callback == NULL || sc == NULL) { +		safestats.st_invalid++; +		return (EINVAL); +	} +	if (SAFE_SESSION(crp->crp_sid) >= sc->sc_nsessions) { +		safestats.st_badsession++; +		return (EINVAL); +	} + +	spin_lock_irqsave(&sc->sc_ringmtx, flags); +	if (sc->sc_front == sc->sc_back && sc->sc_nqchip != 0) { +		safestats.st_ringfull++; +		sc->sc_needwakeup |= CRYPTO_SYMQ; +		spin_unlock_irqrestore(&sc->sc_ringmtx, flags); +		return (ERESTART); +	} +	re = sc->sc_front; + +	staterec = re->re_sa.sa_staterec;	/* save */ +	/* NB: zero everything but the PE descriptor */ +	bzero(&re->re_sa, sizeof(struct safe_ringentry) - sizeof(re->re_desc)); +	re->re_sa.sa_staterec = staterec;	/* restore */ + +	re->re_crp = crp; +	re->re_sesn = SAFE_SESSION(crp->crp_sid); + +	re->re_src.nsegs = 0; +	re->re_dst.nsegs = 0; + +	if (crp->crp_flags & CRYPTO_F_SKBUF) { +		re->re_src_skb = (struct sk_buff *)crp->crp_buf; +		re->re_dst_skb = (struct sk_buff *)crp->crp_buf; +	} else if (crp->crp_flags & CRYPTO_F_IOV) { +		re->re_src_io = (struct uio *)crp->crp_buf; +		re->re_dst_io = (struct uio *)crp->crp_buf; +	} else { +		safestats.st_badflags++; +		err = EINVAL; +		goto errout;	/* XXX we don't handle contiguous blocks! */ +	} + +	sa = &re->re_sa; +	ses = &sc->sc_sessions[re->re_sesn]; + +	crd1 = crp->crp_desc; +	if (crd1 == NULL) { +		safestats.st_nodesc++; +		err = EINVAL; +		goto errout; +	} +	crd2 = crd1->crd_next; + +	cmd0 = SAFE_SA_CMD0_BASIC;		/* basic group operation */ +	cmd1 = 0; +	if (crd2 == NULL) { +		if (crd1->crd_alg == CRYPTO_MD5_HMAC || +		    crd1->crd_alg == CRYPTO_SHA1_HMAC || +		    crd1->crd_alg == CRYPTO_NULL_HMAC) { +			maccrd = crd1; +			enccrd = NULL; +			cmd0 |= SAFE_SA_CMD0_OP_HASH; +		} else if (crd1->crd_alg == CRYPTO_DES_CBC || +		    crd1->crd_alg == CRYPTO_3DES_CBC || +		    crd1->crd_alg == CRYPTO_AES_CBC || +		    crd1->crd_alg == CRYPTO_NULL_CBC) { +			maccrd = NULL; +			enccrd = crd1; +			cmd0 |= SAFE_SA_CMD0_OP_CRYPT; +		} else { +			safestats.st_badalg++; +			err = EINVAL; +			goto errout; +		} +	} else { +		if ((crd1->crd_alg == CRYPTO_MD5_HMAC || +		    crd1->crd_alg == CRYPTO_SHA1_HMAC || +		    crd1->crd_alg == CRYPTO_NULL_HMAC) && +		    (crd2->crd_alg == CRYPTO_DES_CBC || +			crd2->crd_alg == CRYPTO_3DES_CBC || +		        crd2->crd_alg == CRYPTO_AES_CBC || +		        crd2->crd_alg == CRYPTO_NULL_CBC) && +		    ((crd2->crd_flags & CRD_F_ENCRYPT) == 0)) { +			maccrd = crd1; +			enccrd = crd2; +		} else if ((crd1->crd_alg == CRYPTO_DES_CBC || +		    crd1->crd_alg == CRYPTO_3DES_CBC || +		    crd1->crd_alg == CRYPTO_AES_CBC || +		    crd1->crd_alg == CRYPTO_NULL_CBC) && +		    (crd2->crd_alg == CRYPTO_MD5_HMAC || +			crd2->crd_alg == CRYPTO_SHA1_HMAC || +			crd2->crd_alg == CRYPTO_NULL_HMAC) && +		    (crd1->crd_flags & CRD_F_ENCRYPT)) { +			enccrd = crd1; +			maccrd = crd2; +		} else { +			safestats.st_badalg++; +			err = EINVAL; +			goto errout; +		} +		cmd0 |= SAFE_SA_CMD0_OP_BOTH; +	} + +	if (enccrd) { +		if (enccrd->crd_flags & CRD_F_KEY_EXPLICIT) +			safe_setup_enckey(ses, enccrd->crd_key); + +		if (enccrd->crd_alg == CRYPTO_DES_CBC) { +			cmd0 |= SAFE_SA_CMD0_DES; +			cmd1 |= SAFE_SA_CMD1_CBC; +			ivsize = 2*sizeof(u_int32_t); +		} else if (enccrd->crd_alg == CRYPTO_3DES_CBC) { +			cmd0 |= SAFE_SA_CMD0_3DES; +			cmd1 |= SAFE_SA_CMD1_CBC; +			ivsize = 2*sizeof(u_int32_t); +		} else if (enccrd->crd_alg == CRYPTO_AES_CBC) { +			cmd0 |= SAFE_SA_CMD0_AES; +			cmd1 |= SAFE_SA_CMD1_CBC; +			if (ses->ses_klen == 128) +			     cmd1 |=  SAFE_SA_CMD1_AES128; +			else if (ses->ses_klen == 192) +			     cmd1 |=  SAFE_SA_CMD1_AES192; +			else +			     cmd1 |=  SAFE_SA_CMD1_AES256; +			ivsize = 4*sizeof(u_int32_t); +		} else { +			cmd0 |= SAFE_SA_CMD0_CRYPT_NULL; +			ivsize = 0; +		} + +		/* +		 * Setup encrypt/decrypt state.  When using basic ops +		 * we can't use an inline IV because hash/crypt offset +		 * must be from the end of the IV to the start of the +		 * crypt data and this leaves out the preceding header +		 * from the hash calculation.  Instead we place the IV +		 * in the state record and set the hash/crypt offset to +		 * copy both the header+IV. +		 */ +		if (enccrd->crd_flags & CRD_F_ENCRYPT) { +			cmd0 |= SAFE_SA_CMD0_OUTBOUND; + +			if (enccrd->crd_flags & CRD_F_IV_EXPLICIT) +				iv = enccrd->crd_iv; +			else +				iv = (caddr_t) ses->ses_iv; +			if ((enccrd->crd_flags & CRD_F_IV_PRESENT) == 0) { +				crypto_copyback(crp->crp_flags, crp->crp_buf, +				    enccrd->crd_inject, ivsize, iv); +			} +			bcopy(iv, re->re_sastate.sa_saved_iv, ivsize); +			/* make iv LE */ +			for (i = 0; i < ivsize/sizeof(re->re_sastate.sa_saved_iv[0]); i++) +				re->re_sastate.sa_saved_iv[i] = +					cpu_to_le32(re->re_sastate.sa_saved_iv[i]); +			cmd0 |= SAFE_SA_CMD0_IVLD_STATE | SAFE_SA_CMD0_SAVEIV; +			re->re_flags |= SAFE_QFLAGS_COPYOUTIV; +		} else { +			cmd0 |= SAFE_SA_CMD0_INBOUND; + +			if (enccrd->crd_flags & CRD_F_IV_EXPLICIT) { +				bcopy(enccrd->crd_iv, +					re->re_sastate.sa_saved_iv, ivsize); +			} else { +				crypto_copydata(crp->crp_flags, crp->crp_buf, +				    enccrd->crd_inject, ivsize, +				    (caddr_t)re->re_sastate.sa_saved_iv); +			} +			/* make iv LE */ +			for (i = 0; i < ivsize/sizeof(re->re_sastate.sa_saved_iv[0]); i++) +				re->re_sastate.sa_saved_iv[i] = +					cpu_to_le32(re->re_sastate.sa_saved_iv[i]); +			cmd0 |= SAFE_SA_CMD0_IVLD_STATE; +		} +		/* +		 * For basic encryption use the zero pad algorithm. +		 * This pads results to an 8-byte boundary and +		 * suppresses padding verification for inbound (i.e. +		 * decrypt) operations. +		 * +		 * NB: Not sure if the 8-byte pad boundary is a problem. +		 */ +		cmd0 |= SAFE_SA_CMD0_PAD_ZERO; + +		/* XXX assert key bufs have the same size */ +		bcopy(ses->ses_key, sa->sa_key, sizeof(sa->sa_key)); +	} + +	if (maccrd) { +		if (maccrd->crd_flags & CRD_F_KEY_EXPLICIT) { +			safe_setup_mackey(ses, maccrd->crd_alg, +			    maccrd->crd_key, maccrd->crd_klen / 8); +		} + +		if (maccrd->crd_alg == CRYPTO_MD5_HMAC) { +			cmd0 |= SAFE_SA_CMD0_MD5; +			cmd1 |= SAFE_SA_CMD1_HMAC;	/* NB: enable HMAC */ +		} else if (maccrd->crd_alg == CRYPTO_SHA1_HMAC) { +			cmd0 |= SAFE_SA_CMD0_SHA1; +			cmd1 |= SAFE_SA_CMD1_HMAC;	/* NB: enable HMAC */ +		} else { +			cmd0 |= SAFE_SA_CMD0_HASH_NULL; +		} +		/* +		 * Digest data is loaded from the SA and the hash +		 * result is saved to the state block where we +		 * retrieve it for return to the caller. +		 */ +		/* XXX assert digest bufs have the same size */ +		bcopy(ses->ses_hminner, sa->sa_indigest, +			sizeof(sa->sa_indigest)); +		bcopy(ses->ses_hmouter, sa->sa_outdigest, +			sizeof(sa->sa_outdigest)); + +		cmd0 |= SAFE_SA_CMD0_HSLD_SA | SAFE_SA_CMD0_SAVEHASH; +		re->re_flags |= SAFE_QFLAGS_COPYOUTICV; +	} + +	if (enccrd && maccrd) { +		/* +		 * The offset from hash data to the start of +		 * crypt data is the difference in the skips. +		 */ +		bypass = maccrd->crd_skip; +		coffset = enccrd->crd_skip - maccrd->crd_skip; +		if (coffset < 0) { +			DPRINTF(("%s: hash does not precede crypt; " +				"mac skip %u enc skip %u\n", +				__func__, maccrd->crd_skip, enccrd->crd_skip)); +			safestats.st_skipmismatch++; +			err = EINVAL; +			goto errout; +		} +		oplen = enccrd->crd_skip + enccrd->crd_len; +		if (maccrd->crd_skip + maccrd->crd_len != oplen) { +			DPRINTF(("%s: hash amount %u != crypt amount %u\n", +				__func__, maccrd->crd_skip + maccrd->crd_len, +				oplen)); +			safestats.st_lenmismatch++; +			err = EINVAL; +			goto errout; +		} +#ifdef SAFE_DEBUG +		if (debug) { +			printf("mac: skip %d, len %d, inject %d\n", +			    maccrd->crd_skip, maccrd->crd_len, +			    maccrd->crd_inject); +			printf("enc: skip %d, len %d, inject %d\n", +			    enccrd->crd_skip, enccrd->crd_len, +			    enccrd->crd_inject); +			printf("bypass %d coffset %d oplen %d\n", +				bypass, coffset, oplen); +		} +#endif +		if (coffset & 3) {	/* offset must be 32-bit aligned */ +			DPRINTF(("%s: coffset %u misaligned\n", +				__func__, coffset)); +			safestats.st_coffmisaligned++; +			err = EINVAL; +			goto errout; +		} +		coffset >>= 2; +		if (coffset > 255) {	/* offset must be <256 dwords */ +			DPRINTF(("%s: coffset %u too big\n", +				__func__, coffset)); +			safestats.st_cofftoobig++; +			err = EINVAL; +			goto errout; +		} +		/* +		 * Tell the hardware to copy the header to the output. +		 * The header is defined as the data from the end of +		 * the bypass to the start of data to be encrypted.  +		 * Typically this is the inline IV.  Note that you need +		 * to do this even if src+dst are the same; it appears +		 * that w/o this bit the crypted data is written +		 * immediately after the bypass data. +		 */ +		cmd1 |= SAFE_SA_CMD1_HDRCOPY; +		/* +		 * Disable IP header mutable bit handling.  This is +		 * needed to get correct HMAC calculations. +		 */ +		cmd1 |= SAFE_SA_CMD1_MUTABLE; +	} else { +		if (enccrd) { +			bypass = enccrd->crd_skip; +			oplen = bypass + enccrd->crd_len; +		} else { +			bypass = maccrd->crd_skip; +			oplen = bypass + maccrd->crd_len; +		} +		coffset = 0; +	} +	/* XXX verify multiple of 4 when using s/g */ +	if (bypass > 96) {		/* bypass offset must be <= 96 bytes */ +		DPRINTF(("%s: bypass %u too big\n", __func__, bypass)); +		safestats.st_bypasstoobig++; +		err = EINVAL; +		goto errout; +	} + +	if (crp->crp_flags & CRYPTO_F_SKBUF) { +		if (pci_map_skb(sc, &re->re_src, re->re_src_skb)) { +			safestats.st_noload++; +			err = ENOMEM; +			goto errout; +		} +	} else if (crp->crp_flags & CRYPTO_F_IOV) { +		if (pci_map_uio(sc, &re->re_src, re->re_src_io)) { +			safestats.st_noload++; +			err = ENOMEM; +			goto errout; +		} +	} +	nicealign = safe_dmamap_aligned(sc, &re->re_src); +	uniform = safe_dmamap_uniform(sc, &re->re_src); + +	DPRINTF(("src nicealign %u uniform %u nsegs %u\n", +		nicealign, uniform, re->re_src.nsegs)); +	if (re->re_src.nsegs > 1) { +		re->re_desc.d_src = sc->sc_spalloc.dma_paddr + +			((caddr_t) sc->sc_spfree - (caddr_t) sc->sc_spring); +		for (i = 0; i < re->re_src_nsegs; i++) { +			/* NB: no need to check if there's space */ +			pd = sc->sc_spfree; +			if (++(sc->sc_spfree) == sc->sc_springtop) +				sc->sc_spfree = sc->sc_spring; + +			KASSERT((pd->pd_flags&3) == 0 || +				(pd->pd_flags&3) == SAFE_PD_DONE, +				("bogus source particle descriptor; flags %x", +				pd->pd_flags)); +			pd->pd_addr = re->re_src_segs[i].ds_addr; +			pd->pd_size = re->re_src_segs[i].ds_len; +			pd->pd_flags = SAFE_PD_READY; +		} +		cmd0 |= SAFE_SA_CMD0_IGATHER; +	} else { +		/* +		 * No need for gather, reference the operand directly. +		 */ +		re->re_desc.d_src = re->re_src_segs[0].ds_addr; +	} + +	if (enccrd == NULL && maccrd != NULL) { +		/* +		 * Hash op; no destination needed. +		 */ +	} else { +		if (crp->crp_flags & (CRYPTO_F_IOV|CRYPTO_F_SKBUF)) { +			if (!nicealign) { +				safestats.st_iovmisaligned++; +				err = EINVAL; +				goto errout; +			} +			if (uniform != 1) { +				device_printf(sc->sc_dev, "!uniform source\n"); +				if (!uniform) { +					/* +					 * There's no way to handle the DMA +					 * requirements with this uio.  We +					 * could create a separate DMA area for +					 * the result and then copy it back, +					 * but for now we just bail and return +					 * an error.  Note that uio requests +					 * > SAFE_MAX_DSIZE are handled because +					 * the DMA map and segment list for the +					 * destination wil result in a +					 * destination particle list that does +					 * the necessary scatter DMA. +					 */  +					safestats.st_iovnotuniform++; +					err = EINVAL; +					goto errout; +				} +			} else +				re->re_dst = re->re_src; +		} else { +			safestats.st_badflags++; +			err = EINVAL; +			goto errout; +		} + +		if (re->re_dst.nsegs > 1) { +			re->re_desc.d_dst = sc->sc_dpalloc.dma_paddr + +			    ((caddr_t) sc->sc_dpfree - (caddr_t) sc->sc_dpring); +			for (i = 0; i < re->re_dst_nsegs; i++) { +				pd = sc->sc_dpfree; +				KASSERT((pd->pd_flags&3) == 0 || +					(pd->pd_flags&3) == SAFE_PD_DONE, +					("bogus dest particle descriptor; flags %x", +						pd->pd_flags)); +				if (++(sc->sc_dpfree) == sc->sc_dpringtop) +					sc->sc_dpfree = sc->sc_dpring; +				pd->pd_addr = re->re_dst_segs[i].ds_addr; +				pd->pd_flags = SAFE_PD_READY; +			} +			cmd0 |= SAFE_SA_CMD0_OSCATTER; +		} else { +			/* +			 * No need for scatter, reference the operand directly. +			 */ +			re->re_desc.d_dst = re->re_dst_segs[0].ds_addr; +		} +	} + +	/* +	 * All done with setup; fillin the SA command words +	 * and the packet engine descriptor.  The operation +	 * is now ready for submission to the hardware. +	 */ +	sa->sa_cmd0 = cmd0 | SAFE_SA_CMD0_IPCI | SAFE_SA_CMD0_OPCI; +	sa->sa_cmd1 = cmd1 +		    | (coffset << SAFE_SA_CMD1_OFFSET_S) +		    | SAFE_SA_CMD1_SAREV1	/* Rev 1 SA data structure */ +		    | SAFE_SA_CMD1_SRPCI +		    ; +	/* +	 * NB: the order of writes is important here.  In case the +	 * chip is scanning the ring because of an outstanding request +	 * it might nab this one too.  In that case we need to make +	 * sure the setup is complete before we write the length +	 * field of the descriptor as it signals the descriptor is +	 * ready for processing. +	 */ +	re->re_desc.d_csr = SAFE_PE_CSR_READY | SAFE_PE_CSR_SAPCI; +	if (maccrd) +		re->re_desc.d_csr |= SAFE_PE_CSR_LOADSA | SAFE_PE_CSR_HASHFINAL; +	wmb(); +	re->re_desc.d_len = oplen +			  | SAFE_PE_LEN_READY +			  | (bypass << SAFE_PE_LEN_BYPASS_S) +			  ; + +	safestats.st_ipackets++; +	safestats.st_ibytes += oplen; + +	if (++(sc->sc_front) == sc->sc_ringtop) +		sc->sc_front = sc->sc_ring; + +	/* XXX honor batching */ +	safe_feed(sc, re); +	spin_unlock_irqrestore(&sc->sc_ringmtx, flags); +	return (0); + +errout: +	if (re->re_src.map != re->re_dst.map) +		pci_unmap_operand(sc, &re->re_dst); +	if (re->re_src.map) +		pci_unmap_operand(sc, &re->re_src); +	spin_unlock_irqrestore(&sc->sc_ringmtx, flags); +	if (err != ERESTART) { +		crp->crp_etype = err; +		crypto_done(crp); +	} else { +		sc->sc_needwakeup |= CRYPTO_SYMQ; +	} +	return (err); +} + +static void +safe_callback(struct safe_softc *sc, struct safe_ringentry *re) +{ +	struct cryptop *crp = (struct cryptop *)re->re_crp; +	struct cryptodesc *crd; + +	DPRINTF(("%s()\n", __FUNCTION__)); + +	safestats.st_opackets++; +	safestats.st_obytes += re->re_dst.mapsize; + +	if (re->re_desc.d_csr & SAFE_PE_CSR_STATUS) { +		device_printf(sc->sc_dev, "csr 0x%x cmd0 0x%x cmd1 0x%x\n", +			re->re_desc.d_csr, +			re->re_sa.sa_cmd0, re->re_sa.sa_cmd1); +		safestats.st_peoperr++; +		crp->crp_etype = EIO;		/* something more meaningful? */ +	} + +	if (re->re_dst.map != NULL && re->re_dst.map != re->re_src.map) +		pci_unmap_operand(sc, &re->re_dst); +	pci_unmap_operand(sc, &re->re_src); + +	/*  +	 * If result was written to a differet mbuf chain, swap +	 * it in as the return value and reclaim the original. +	 */ +	if ((crp->crp_flags & CRYPTO_F_SKBUF) && re->re_src_skb != re->re_dst_skb) { +		device_printf(sc->sc_dev, "no CRYPTO_F_SKBUF swapping support\n"); +		/* kfree_skb(skb) */ +		/* crp->crp_buf = (caddr_t)re->re_dst_skb */ +		return; +	} + +	if (re->re_flags & SAFE_QFLAGS_COPYOUTIV) { +		/* copy out IV for future use */ +		for (crd = crp->crp_desc; crd; crd = crd->crd_next) { +			int i; +			int ivsize; + +			if (crd->crd_alg == CRYPTO_DES_CBC || +			    crd->crd_alg == CRYPTO_3DES_CBC) { +				ivsize = 2*sizeof(u_int32_t); +			} else if (crd->crd_alg == CRYPTO_AES_CBC) { +				ivsize = 4*sizeof(u_int32_t); +			} else +				continue; +			crypto_copydata(crp->crp_flags, crp->crp_buf, +			    crd->crd_skip + crd->crd_len - ivsize, ivsize, +			    (caddr_t)sc->sc_sessions[re->re_sesn].ses_iv); +			for (i = 0; +					i < ivsize/sizeof(sc->sc_sessions[re->re_sesn].ses_iv[0]); +					i++) +				sc->sc_sessions[re->re_sesn].ses_iv[i] = +					cpu_to_le32(sc->sc_sessions[re->re_sesn].ses_iv[i]); +			break; +		} +	} + +	if (re->re_flags & SAFE_QFLAGS_COPYOUTICV) { +		/* copy out ICV result */ +		for (crd = crp->crp_desc; crd; crd = crd->crd_next) { +			if (!(crd->crd_alg == CRYPTO_MD5_HMAC || +			    crd->crd_alg == CRYPTO_SHA1_HMAC || +			    crd->crd_alg == CRYPTO_NULL_HMAC)) +				continue; +			if (crd->crd_alg == CRYPTO_SHA1_HMAC) { +				/* +				 * SHA-1 ICV's are byte-swapped; fix 'em up +				 * before copy them to their destination. +				 */ +				re->re_sastate.sa_saved_indigest[0] = +					cpu_to_be32(re->re_sastate.sa_saved_indigest[0]); +				re->re_sastate.sa_saved_indigest[1] =  +					cpu_to_be32(re->re_sastate.sa_saved_indigest[1]); +				re->re_sastate.sa_saved_indigest[2] = +					cpu_to_be32(re->re_sastate.sa_saved_indigest[2]); +			} else { +				re->re_sastate.sa_saved_indigest[0] = +					cpu_to_le32(re->re_sastate.sa_saved_indigest[0]); +				re->re_sastate.sa_saved_indigest[1] =  +					cpu_to_le32(re->re_sastate.sa_saved_indigest[1]); +				re->re_sastate.sa_saved_indigest[2] = +					cpu_to_le32(re->re_sastate.sa_saved_indigest[2]); +			} +			crypto_copyback(crp->crp_flags, crp->crp_buf, +			    crd->crd_inject, +			    sc->sc_sessions[re->re_sesn].ses_mlen, +			    (caddr_t)re->re_sastate.sa_saved_indigest); +			break; +		} +	} +	crypto_done(crp); +} + + +#if defined(CONFIG_OCF_RANDOMHARVEST) && !defined(SAFE_NO_RNG) +#define	SAFE_RNG_MAXWAIT	1000 + +static void +safe_rng_init(struct safe_softc *sc) +{ +	u_int32_t w, v; +	int i; + +	DPRINTF(("%s()\n", __FUNCTION__)); + +	WRITE_REG(sc, SAFE_RNG_CTRL, 0); +	/* use default value according to the manual */ +	WRITE_REG(sc, SAFE_RNG_CNFG, 0x834);	/* magic from SafeNet */ +	WRITE_REG(sc, SAFE_RNG_ALM_CNT, 0); + +	/* +	 * There is a bug in rev 1.0 of the 1140 that when the RNG +	 * is brought out of reset the ready status flag does not +	 * work until the RNG has finished its internal initialization. +	 * +	 * So in order to determine the device is through its +	 * initialization we must read the data register, using the +	 * status reg in the read in case it is initialized.  Then read +	 * the data register until it changes from the first read. +	 * Once it changes read the data register until it changes +	 * again.  At this time the RNG is considered initialized.  +	 * This could take between 750ms - 1000ms in time. +	 */ +	i = 0; +	w = READ_REG(sc, SAFE_RNG_OUT); +	do { +		v = READ_REG(sc, SAFE_RNG_OUT); +		if (v != w) { +			w = v; +			break; +		} +		DELAY(10); +	} while (++i < SAFE_RNG_MAXWAIT); + +	/* Wait Until data changes again */ +	i = 0; +	do { +		v = READ_REG(sc, SAFE_RNG_OUT); +		if (v != w) +			break; +		DELAY(10); +	} while (++i < SAFE_RNG_MAXWAIT); +} + +static __inline void +safe_rng_disable_short_cycle(struct safe_softc *sc) +{ +	DPRINTF(("%s()\n", __FUNCTION__)); + +	WRITE_REG(sc, SAFE_RNG_CTRL, +		READ_REG(sc, SAFE_RNG_CTRL) &~ SAFE_RNG_CTRL_SHORTEN); +} + +static __inline void +safe_rng_enable_short_cycle(struct safe_softc *sc) +{ +	DPRINTF(("%s()\n", __FUNCTION__)); + +	WRITE_REG(sc, SAFE_RNG_CTRL,  +		READ_REG(sc, SAFE_RNG_CTRL) | SAFE_RNG_CTRL_SHORTEN); +} + +static __inline u_int32_t +safe_rng_read(struct safe_softc *sc) +{ +	int i; + +	i = 0; +	while (READ_REG(sc, SAFE_RNG_STAT) != 0 && ++i < SAFE_RNG_MAXWAIT) +		; +	return READ_REG(sc, SAFE_RNG_OUT); +} + +static int +safe_read_random(void *arg, u_int32_t *buf, int maxwords) +{ +	struct safe_softc *sc = (struct safe_softc *) arg; +	int i, rc; + +	DPRINTF(("%s()\n", __FUNCTION__)); +	 +	safestats.st_rng++; +	/* +	 * Fetch the next block of data. +	 */ +	if (maxwords > safe_rngbufsize) +		maxwords = safe_rngbufsize; +	if (maxwords > SAFE_RNG_MAXBUFSIZ) +		maxwords = SAFE_RNG_MAXBUFSIZ; +retry: +	/* read as much as we can */ +	for (rc = 0; rc < maxwords; rc++) { +		if (READ_REG(sc, SAFE_RNG_STAT) != 0) +			break; +		buf[rc] = READ_REG(sc, SAFE_RNG_OUT); +	} +	if (rc == 0) +		return 0; +	/* +	 * Check the comparator alarm count and reset the h/w if +	 * it exceeds our threshold.  This guards against the +	 * hardware oscillators resonating with external signals. +	 */ +	if (READ_REG(sc, SAFE_RNG_ALM_CNT) > safe_rngmaxalarm) { +		u_int32_t freq_inc, w; + +		DPRINTF(("%s: alarm count %u exceeds threshold %u\n", __func__, +			(unsigned)READ_REG(sc, SAFE_RNG_ALM_CNT), safe_rngmaxalarm)); +		safestats.st_rngalarm++; +		safe_rng_enable_short_cycle(sc); +		freq_inc = 18; +		for (i = 0; i < 64; i++) { +			w = READ_REG(sc, SAFE_RNG_CNFG); +			freq_inc = ((w + freq_inc) & 0x3fL); +			w = ((w & ~0x3fL) | freq_inc); +			WRITE_REG(sc, SAFE_RNG_CNFG, w); + +			WRITE_REG(sc, SAFE_RNG_ALM_CNT, 0); + +			(void) safe_rng_read(sc); +			DELAY(25); + +			if (READ_REG(sc, SAFE_RNG_ALM_CNT) == 0) { +				safe_rng_disable_short_cycle(sc); +				goto retry; +			} +			freq_inc = 1; +		} +		safe_rng_disable_short_cycle(sc); +	} else +		WRITE_REG(sc, SAFE_RNG_ALM_CNT, 0); + +	return(rc); +} +#endif /* defined(CONFIG_OCF_RANDOMHARVEST) && !defined(SAFE_NO_RNG) */ + + +/* + * Resets the board.  Values in the regesters are left as is + * from the reset (i.e. initial values are assigned elsewhere). + */ +static void +safe_reset_board(struct safe_softc *sc) +{ +	u_int32_t v; +	/* +	 * Reset the device.  The manual says no delay +	 * is needed between marking and clearing reset. +	 */ +	DPRINTF(("%s()\n", __FUNCTION__)); + +	v = READ_REG(sc, SAFE_PE_DMACFG) &~ +		(SAFE_PE_DMACFG_PERESET | SAFE_PE_DMACFG_PDRRESET | +		 SAFE_PE_DMACFG_SGRESET); +	WRITE_REG(sc, SAFE_PE_DMACFG, v +				    | SAFE_PE_DMACFG_PERESET +				    | SAFE_PE_DMACFG_PDRRESET +				    | SAFE_PE_DMACFG_SGRESET); +	WRITE_REG(sc, SAFE_PE_DMACFG, v); +} + +/* + * Initialize registers we need to touch only once. + */ +static void +safe_init_board(struct safe_softc *sc) +{ +	u_int32_t v, dwords; + +	DPRINTF(("%s()\n", __FUNCTION__)); + +	v = READ_REG(sc, SAFE_PE_DMACFG); +	v &=~ (   SAFE_PE_DMACFG_PEMODE +			| SAFE_PE_DMACFG_FSENA		/* failsafe enable */ +			| SAFE_PE_DMACFG_GPRPCI		/* gather ring on PCI */ +			| SAFE_PE_DMACFG_SPRPCI		/* scatter ring on PCI */ +			| SAFE_PE_DMACFG_ESDESC		/* endian-swap descriptors */ +			| SAFE_PE_DMACFG_ESPDESC	/* endian-swap part. desc's */ +			| SAFE_PE_DMACFG_ESSA		/* endian-swap SA's */ +			| SAFE_PE_DMACFG_ESPACKET	/* swap the packet data */ +		  ); +	v |= SAFE_PE_DMACFG_FSENA		/* failsafe enable */ +	  |  SAFE_PE_DMACFG_GPRPCI		/* gather ring on PCI */ +	  |  SAFE_PE_DMACFG_SPRPCI		/* scatter ring on PCI */ +	  |  SAFE_PE_DMACFG_ESDESC		/* endian-swap descriptors */ +	  |  SAFE_PE_DMACFG_ESPDESC		/* endian-swap part. desc's */ +	  |  SAFE_PE_DMACFG_ESSA		/* endian-swap SA's */ +#if 0 +	  |  SAFE_PE_DMACFG_ESPACKET    /* swap the packet data */ +#endif +	  ; +	WRITE_REG(sc, SAFE_PE_DMACFG, v); + +#ifdef __BIG_ENDIAN +	/* tell the safenet that we are 4321 and not 1234 */ +	WRITE_REG(sc, SAFE_ENDIAN, 0xe4e41b1b); +#endif + +	if (sc->sc_chiprev == SAFE_REV(1,0)) { +		/* +		 * Avoid large PCI DMA transfers.  Rev 1.0 has a bug where +		 * "target mode transfers" done while the chip is DMA'ing +		 * >1020 bytes cause the hardware to lockup.  To avoid this +		 * we reduce the max PCI transfer size and use small source +		 * particle descriptors (<= 256 bytes). +		 */ +		WRITE_REG(sc, SAFE_DMA_CFG, 256); +		device_printf(sc->sc_dev, +			"Reduce max DMA size to %u words for rev %u.%u WAR\n", +			(unsigned) ((READ_REG(sc, SAFE_DMA_CFG)>>2) & 0xff), +			(unsigned) SAFE_REV_MAJ(sc->sc_chiprev), +			(unsigned) SAFE_REV_MIN(sc->sc_chiprev)); +		sc->sc_max_dsize = 256; +	} else { +		sc->sc_max_dsize = SAFE_MAX_DSIZE; +	} + +	/* NB: operands+results are overlaid */ +	WRITE_REG(sc, SAFE_PE_PDRBASE, sc->sc_ringalloc.dma_paddr); +	WRITE_REG(sc, SAFE_PE_RDRBASE, sc->sc_ringalloc.dma_paddr); +	/* +	 * Configure ring entry size and number of items in the ring. +	 */ +	KASSERT((sizeof(struct safe_ringentry) % sizeof(u_int32_t)) == 0, +		("PE ring entry not 32-bit aligned!")); +	dwords = sizeof(struct safe_ringentry) / sizeof(u_int32_t); +	WRITE_REG(sc, SAFE_PE_RINGCFG, +		(dwords << SAFE_PE_RINGCFG_OFFSET_S) | SAFE_MAX_NQUEUE); +	WRITE_REG(sc, SAFE_PE_RINGPOLL, 0);	/* disable polling */ + +	WRITE_REG(sc, SAFE_PE_GRNGBASE, sc->sc_spalloc.dma_paddr); +	WRITE_REG(sc, SAFE_PE_SRNGBASE, sc->sc_dpalloc.dma_paddr); +	WRITE_REG(sc, SAFE_PE_PARTSIZE, +		(SAFE_TOTAL_DPART<<16) | SAFE_TOTAL_SPART); +	/* +	 * NB: destination particles are fixed size.  We use +	 *     an mbuf cluster and require all results go to +	 *     clusters or smaller. +	 */ +	WRITE_REG(sc, SAFE_PE_PARTCFG, sc->sc_max_dsize); + +	/* it's now safe to enable PE mode, do it */ +	WRITE_REG(sc, SAFE_PE_DMACFG, v | SAFE_PE_DMACFG_PEMODE); + +	/* +	 * Configure hardware to use level-triggered interrupts and +	 * to interrupt after each descriptor is processed. +	 */ +	WRITE_REG(sc, SAFE_HI_CFG, SAFE_HI_CFG_LEVEL); +	WRITE_REG(sc, SAFE_HI_CLR, 0xffffffff); +	WRITE_REG(sc, SAFE_HI_DESC_CNT, 1); +	WRITE_REG(sc, SAFE_HI_MASK, SAFE_INT_PE_DDONE | SAFE_INT_PE_ERROR); +} + + +/* + * Clean up after a chip crash. + * It is assumed that the caller in splimp() + */ +static void +safe_cleanchip(struct safe_softc *sc) +{ +	DPRINTF(("%s()\n", __FUNCTION__)); + +	if (sc->sc_nqchip != 0) { +		struct safe_ringentry *re = sc->sc_back; + +		while (re != sc->sc_front) { +			if (re->re_desc.d_csr != 0) +				safe_free_entry(sc, re); +			if (++re == sc->sc_ringtop) +				re = sc->sc_ring; +		} +		sc->sc_back = re; +		sc->sc_nqchip = 0; +	} +} + +/* + * free a safe_q + * It is assumed that the caller is within splimp(). + */ +static int +safe_free_entry(struct safe_softc *sc, struct safe_ringentry *re) +{ +	struct cryptop *crp; + +	DPRINTF(("%s()\n", __FUNCTION__)); + +	/* +	 * Free header MCR +	 */ +	if ((re->re_dst_skb != NULL) && (re->re_src_skb != re->re_dst_skb)) +#ifdef NOTYET +		m_freem(re->re_dst_m); +#else +		printk("%s,%d: SKB not supported\n", __FILE__, __LINE__); +#endif + +	crp = (struct cryptop *)re->re_crp; +	 +	re->re_desc.d_csr = 0; +	 +	crp->crp_etype = EFAULT; +	crypto_done(crp); +	return(0); +} + +/* + * Routine to reset the chip and clean up. + * It is assumed that the caller is in splimp() + */ +static void +safe_totalreset(struct safe_softc *sc) +{ +	DPRINTF(("%s()\n", __FUNCTION__)); + +	safe_reset_board(sc); +	safe_init_board(sc); +	safe_cleanchip(sc); +} + +/* + * Is the operand suitable aligned for direct DMA.  Each + * segment must be aligned on a 32-bit boundary and all + * but the last segment must be a multiple of 4 bytes. + */ +static int +safe_dmamap_aligned(struct safe_softc *sc, const struct safe_operand *op) +{ +	int i; + +	DPRINTF(("%s()\n", __FUNCTION__)); + +	for (i = 0; i < op->nsegs; i++) { +		if (op->segs[i].ds_addr & 3) +			return (0); +		if (i != (op->nsegs - 1) && (op->segs[i].ds_len & 3)) +			return (0); +	} +	return (1); +} + +/* + * Is the operand suitable for direct DMA as the destination + * of an operation.  The hardware requires that each ``particle'' + * but the last in an operation result have the same size.  We + * fix that size at SAFE_MAX_DSIZE bytes.  This routine returns + * 0 if some segment is not a multiple of of this size, 1 if all + * segments are exactly this size, or 2 if segments are at worst + * a multple of this size. + */ +static int +safe_dmamap_uniform(struct safe_softc *sc, const struct safe_operand *op) +{ +	int result = 1; + +	DPRINTF(("%s()\n", __FUNCTION__)); + +	if (op->nsegs > 0) { +		int i; + +		for (i = 0; i < op->nsegs-1; i++) { +			if (op->segs[i].ds_len % sc->sc_max_dsize) +				return (0); +			if (op->segs[i].ds_len != sc->sc_max_dsize) +				result = 2; +		} +	} +	return (result); +} + +static int +safe_kprocess(device_t dev, struct cryptkop *krp, int hint) +{ +	struct safe_softc *sc = device_get_softc(dev); +	struct safe_pkq *q; +	unsigned long flags; + +	DPRINTF(("%s()\n", __FUNCTION__)); + +	if (sc == NULL) { +		krp->krp_status = EINVAL; +		goto err; +	} + +	if (krp->krp_op != CRK_MOD_EXP) { +		krp->krp_status = EOPNOTSUPP; +		goto err; +	} + +	q = (struct safe_pkq *) kmalloc(sizeof(*q), GFP_KERNEL); +	if (q == NULL) { +		krp->krp_status = ENOMEM; +		goto err; +	} +	memset(q, 0, sizeof(*q)); +	q->pkq_krp = krp; +	INIT_LIST_HEAD(&q->pkq_list); + +	spin_lock_irqsave(&sc->sc_pkmtx, flags); +	list_add_tail(&q->pkq_list, &sc->sc_pkq); +	safe_kfeed(sc); +	spin_unlock_irqrestore(&sc->sc_pkmtx, flags); +	return (0); + +err: +	crypto_kdone(krp); +	return (0); +} + +#define	SAFE_CRK_PARAM_BASE	0 +#define	SAFE_CRK_PARAM_EXP	1 +#define	SAFE_CRK_PARAM_MOD	2 + +static int +safe_kstart(struct safe_softc *sc) +{ +	struct cryptkop *krp = sc->sc_pkq_cur->pkq_krp; +	int exp_bits, mod_bits, base_bits; +	u_int32_t op, a_off, b_off, c_off, d_off; + +	DPRINTF(("%s()\n", __FUNCTION__)); + +	if (krp->krp_iparams < 3 || krp->krp_oparams != 1) { +		krp->krp_status = EINVAL; +		return (1); +	} + +	base_bits = safe_ksigbits(sc, &krp->krp_param[SAFE_CRK_PARAM_BASE]); +	if (base_bits > 2048) +		goto too_big; +	if (base_bits <= 0)		/* 5. base not zero */ +		goto too_small; + +	exp_bits = safe_ksigbits(sc, &krp->krp_param[SAFE_CRK_PARAM_EXP]); +	if (exp_bits > 2048) +		goto too_big; +	if (exp_bits <= 0)		/* 1. exponent word length > 0 */ +		goto too_small;		/* 4. exponent not zero */ + +	mod_bits = safe_ksigbits(sc, &krp->krp_param[SAFE_CRK_PARAM_MOD]); +	if (mod_bits > 2048) +		goto too_big; +	if (mod_bits <= 32)		/* 2. modulus word length > 1 */ +		goto too_small;		/* 8. MSW of modulus != zero */ +	if (mod_bits < exp_bits)	/* 3 modulus len >= exponent len */ +		goto too_small; +	if ((krp->krp_param[SAFE_CRK_PARAM_MOD].crp_p[0] & 1) == 0) +		goto bad_domain;	/* 6. modulus is odd */ +	if (mod_bits > krp->krp_param[krp->krp_iparams].crp_nbits) +		goto too_small;		/* make sure result will fit */ + +	/* 7. modulus > base */ +	if (mod_bits < base_bits) +		goto too_small; +	if (mod_bits == base_bits) { +		u_int8_t *basep, *modp; +		int i; + +		basep = krp->krp_param[SAFE_CRK_PARAM_BASE].crp_p + +		    ((base_bits + 7) / 8) - 1; +		modp = krp->krp_param[SAFE_CRK_PARAM_MOD].crp_p + +		    ((mod_bits + 7) / 8) - 1; +		 +		for (i = 0; i < (mod_bits + 7) / 8; i++, basep--, modp--) { +			if (*modp < *basep) +				goto too_small; +			if (*modp > *basep) +				break; +		} +	} + +	/* And on the 9th step, he rested. */ + +	WRITE_REG(sc, SAFE_PK_A_LEN, (exp_bits + 31) / 32); +	WRITE_REG(sc, SAFE_PK_B_LEN, (mod_bits + 31) / 32); +	if (mod_bits > 1024) { +		op = SAFE_PK_FUNC_EXP4; +		a_off = 0x000; +		b_off = 0x100; +		c_off = 0x200; +		d_off = 0x300; +	} else { +		op = SAFE_PK_FUNC_EXP16; +		a_off = 0x000; +		b_off = 0x080; +		c_off = 0x100; +		d_off = 0x180; +	} +	sc->sc_pk_reslen = b_off - a_off; +	sc->sc_pk_resoff = d_off; + +	/* A is exponent, B is modulus, C is base, D is result */ +	safe_kload_reg(sc, a_off, b_off - a_off, +	    &krp->krp_param[SAFE_CRK_PARAM_EXP]); +	WRITE_REG(sc, SAFE_PK_A_ADDR, a_off >> 2); +	safe_kload_reg(sc, b_off, b_off - a_off, +	    &krp->krp_param[SAFE_CRK_PARAM_MOD]); +	WRITE_REG(sc, SAFE_PK_B_ADDR, b_off >> 2); +	safe_kload_reg(sc, c_off, b_off - a_off, +	    &krp->krp_param[SAFE_CRK_PARAM_BASE]); +	WRITE_REG(sc, SAFE_PK_C_ADDR, c_off >> 2); +	WRITE_REG(sc, SAFE_PK_D_ADDR, d_off >> 2); + +	WRITE_REG(sc, SAFE_PK_FUNC, op | SAFE_PK_FUNC_RUN); + +	return (0); + +too_big: +	krp->krp_status = E2BIG; +	return (1); +too_small: +	krp->krp_status = ERANGE; +	return (1); +bad_domain: +	krp->krp_status = EDOM; +	return (1); +} + +static int +safe_ksigbits(struct safe_softc *sc, struct crparam *cr) +{ +	u_int plen = (cr->crp_nbits + 7) / 8; +	int i, sig = plen * 8; +	u_int8_t c, *p = cr->crp_p; + +	DPRINTF(("%s()\n", __FUNCTION__)); + +	for (i = plen - 1; i >= 0; i--) { +		c = p[i]; +		if (c != 0) { +			while ((c & 0x80) == 0) { +				sig--; +				c <<= 1; +			} +			break; +		} +		sig -= 8; +	} +	return (sig); +} + +static void +safe_kfeed(struct safe_softc *sc) +{ +	struct safe_pkq *q, *tmp; + +	DPRINTF(("%s()\n", __FUNCTION__)); + +	if (list_empty(&sc->sc_pkq) && sc->sc_pkq_cur == NULL) +		return; +	if (sc->sc_pkq_cur != NULL) +		return; +	list_for_each_entry_safe(q, tmp, &sc->sc_pkq, pkq_list) { +		sc->sc_pkq_cur = q; +		list_del(&q->pkq_list); +		if (safe_kstart(sc) != 0) { +			crypto_kdone(q->pkq_krp); +			kfree(q); +			sc->sc_pkq_cur = NULL; +		} else { +			/* op started, start polling */ +			mod_timer(&sc->sc_pkto, jiffies + 1); +			break; +		} +	} +} + +static void +safe_kpoll(unsigned long arg) +{ +	struct safe_softc *sc = NULL; +	struct safe_pkq *q; +	struct crparam *res; +	int i; +	u_int32_t buf[64]; +	unsigned long flags; + +	DPRINTF(("%s()\n", __FUNCTION__)); + +	if (arg >= SAFE_MAX_CHIPS) +		return; +	sc = safe_chip_idx[arg]; +	if (!sc) { +		DPRINTF(("%s() - bad callback\n", __FUNCTION__)); +		return; +	} + +	spin_lock_irqsave(&sc->sc_pkmtx, flags); +	if (sc->sc_pkq_cur == NULL) +		goto out; +	if (READ_REG(sc, SAFE_PK_FUNC) & SAFE_PK_FUNC_RUN) { +		/* still running, check back later */ +		mod_timer(&sc->sc_pkto, jiffies + 1); +		goto out; +	} + +	q = sc->sc_pkq_cur; +	res = &q->pkq_krp->krp_param[q->pkq_krp->krp_iparams]; +	bzero(buf, sizeof(buf)); +	bzero(res->crp_p, (res->crp_nbits + 7) / 8); +	for (i = 0; i < sc->sc_pk_reslen >> 2; i++) +		buf[i] = le32_to_cpu(READ_REG(sc, SAFE_PK_RAM_START + +		    sc->sc_pk_resoff + (i << 2))); +	bcopy(buf, res->crp_p, (res->crp_nbits + 7) / 8); +	/* +	 * reduce the bits that need copying if possible +	 */ +	res->crp_nbits = min(res->crp_nbits,sc->sc_pk_reslen * 8); +	res->crp_nbits = safe_ksigbits(sc, res); + +	for (i = SAFE_PK_RAM_START; i < SAFE_PK_RAM_END; i += 4) +		WRITE_REG(sc, i, 0); + +	crypto_kdone(q->pkq_krp); +	kfree(q); +	sc->sc_pkq_cur = NULL; + +	safe_kfeed(sc); +out: +	spin_unlock_irqrestore(&sc->sc_pkmtx, flags); +} + +static void +safe_kload_reg(struct safe_softc *sc, u_int32_t off, u_int32_t len, +    struct crparam *n) +{ +	u_int32_t buf[64], i; + +	DPRINTF(("%s()\n", __FUNCTION__)); + +	bzero(buf, sizeof(buf)); +	bcopy(n->crp_p, buf, (n->crp_nbits + 7) / 8); + +	for (i = 0; i < len >> 2; i++) +		WRITE_REG(sc, SAFE_PK_RAM_START + off + (i << 2), +		    cpu_to_le32(buf[i])); +} + +#ifdef SAFE_DEBUG +static void +safe_dump_dmastatus(struct safe_softc *sc, const char *tag) +{ +	printf("%s: ENDIAN 0x%x SRC 0x%x DST 0x%x STAT 0x%x\n" +		, tag +		, READ_REG(sc, SAFE_DMA_ENDIAN) +		, READ_REG(sc, SAFE_DMA_SRCADDR) +		, READ_REG(sc, SAFE_DMA_DSTADDR) +		, READ_REG(sc, SAFE_DMA_STAT) +	); +} + +static void +safe_dump_intrstate(struct safe_softc *sc, const char *tag) +{ +	printf("%s: HI_CFG 0x%x HI_MASK 0x%x HI_DESC_CNT 0x%x HU_STAT 0x%x HM_STAT 0x%x\n" +		, tag +		, READ_REG(sc, SAFE_HI_CFG) +		, READ_REG(sc, SAFE_HI_MASK) +		, READ_REG(sc, SAFE_HI_DESC_CNT) +		, READ_REG(sc, SAFE_HU_STAT) +		, READ_REG(sc, SAFE_HM_STAT) +	); +} + +static void +safe_dump_ringstate(struct safe_softc *sc, const char *tag) +{ +	u_int32_t estat = READ_REG(sc, SAFE_PE_ERNGSTAT); + +	/* NB: assume caller has lock on ring */ +	printf("%s: ERNGSTAT %x (next %u) back %lu front %lu\n", +		tag, +		estat, (estat >> SAFE_PE_ERNGSTAT_NEXT_S), +		(unsigned long)(sc->sc_back - sc->sc_ring), +		(unsigned long)(sc->sc_front - sc->sc_ring)); +} + +static void +safe_dump_request(struct safe_softc *sc, const char* tag, struct safe_ringentry *re) +{ +	int ix, nsegs; + +	ix = re - sc->sc_ring; +	printf("%s: %p (%u): csr %x src %x dst %x sa %x len %x\n" +		, tag +		, re, ix +		, re->re_desc.d_csr +		, re->re_desc.d_src +		, re->re_desc.d_dst +		, re->re_desc.d_sa +		, re->re_desc.d_len +	); +	if (re->re_src.nsegs > 1) { +		ix = (re->re_desc.d_src - sc->sc_spalloc.dma_paddr) / +			sizeof(struct safe_pdesc); +		for (nsegs = re->re_src.nsegs; nsegs; nsegs--) { +			printf(" spd[%u] %p: %p size %u flags %x" +				, ix, &sc->sc_spring[ix] +				, (caddr_t)(uintptr_t) sc->sc_spring[ix].pd_addr +				, sc->sc_spring[ix].pd_size +				, sc->sc_spring[ix].pd_flags +			); +			if (sc->sc_spring[ix].pd_size == 0) +				printf(" (zero!)"); +			printf("\n"); +			if (++ix == SAFE_TOTAL_SPART) +				ix = 0; +		} +	} +	if (re->re_dst.nsegs > 1) { +		ix = (re->re_desc.d_dst - sc->sc_dpalloc.dma_paddr) / +			sizeof(struct safe_pdesc); +		for (nsegs = re->re_dst.nsegs; nsegs; nsegs--) { +			printf(" dpd[%u] %p: %p flags %x\n" +				, ix, &sc->sc_dpring[ix] +				, (caddr_t)(uintptr_t) sc->sc_dpring[ix].pd_addr +				, sc->sc_dpring[ix].pd_flags +			); +			if (++ix == SAFE_TOTAL_DPART) +				ix = 0; +		} +	} +	printf("sa: cmd0 %08x cmd1 %08x staterec %x\n", +		re->re_sa.sa_cmd0, re->re_sa.sa_cmd1, re->re_sa.sa_staterec); +	printf("sa: key %x %x %x %x %x %x %x %x\n" +		, re->re_sa.sa_key[0] +		, re->re_sa.sa_key[1] +		, re->re_sa.sa_key[2] +		, re->re_sa.sa_key[3] +		, re->re_sa.sa_key[4] +		, re->re_sa.sa_key[5] +		, re->re_sa.sa_key[6] +		, re->re_sa.sa_key[7] +	); +	printf("sa: indigest %x %x %x %x %x\n" +		, re->re_sa.sa_indigest[0] +		, re->re_sa.sa_indigest[1] +		, re->re_sa.sa_indigest[2] +		, re->re_sa.sa_indigest[3] +		, re->re_sa.sa_indigest[4] +	); +	printf("sa: outdigest %x %x %x %x %x\n" +		, re->re_sa.sa_outdigest[0] +		, re->re_sa.sa_outdigest[1] +		, re->re_sa.sa_outdigest[2] +		, re->re_sa.sa_outdigest[3] +		, re->re_sa.sa_outdigest[4] +	); +	printf("sr: iv %x %x %x %x\n" +		, re->re_sastate.sa_saved_iv[0] +		, re->re_sastate.sa_saved_iv[1] +		, re->re_sastate.sa_saved_iv[2] +		, re->re_sastate.sa_saved_iv[3] +	); +	printf("sr: hashbc %u indigest %x %x %x %x %x\n" +		, re->re_sastate.sa_saved_hashbc +		, re->re_sastate.sa_saved_indigest[0] +		, re->re_sastate.sa_saved_indigest[1] +		, re->re_sastate.sa_saved_indigest[2] +		, re->re_sastate.sa_saved_indigest[3] +		, re->re_sastate.sa_saved_indigest[4] +	); +} + +static void +safe_dump_ring(struct safe_softc *sc, const char *tag) +{ +	unsigned long flags; + +	spin_lock_irqsave(&sc->sc_ringmtx, flags); +	printf("\nSafeNet Ring State:\n"); +	safe_dump_intrstate(sc, tag); +	safe_dump_dmastatus(sc, tag); +	safe_dump_ringstate(sc, tag); +	if (sc->sc_nqchip) { +		struct safe_ringentry *re = sc->sc_back; +		do { +			safe_dump_request(sc, tag, re); +			if (++re == sc->sc_ringtop) +				re = sc->sc_ring; +		} while (re != sc->sc_front); +	} +	spin_unlock_irqrestore(&sc->sc_ringmtx, flags); +} +#endif /* SAFE_DEBUG */ + + +static int safe_probe(struct pci_dev *dev, const struct pci_device_id *ent) +{ +	struct safe_softc *sc = NULL; +	u32 mem_start, mem_len, cmd; +	int i, rc, devinfo; +	dma_addr_t raddr; +	static int num_chips = 0; + +	DPRINTF(("%s()\n", __FUNCTION__)); + +	if (pci_enable_device(dev) < 0) +		return(-ENODEV); + +	if (!dev->irq) { +		printk("safe: found device with no IRQ assigned. check BIOS settings!"); +		pci_disable_device(dev); +		return(-ENODEV); +	} + +	if (pci_set_mwi(dev)) { +		printk("safe: pci_set_mwi failed!"); +		return(-ENODEV); +	} + +	sc = (struct safe_softc *) kmalloc(sizeof(*sc), GFP_KERNEL); +	if (!sc) +		return(-ENOMEM); +	memset(sc, 0, sizeof(*sc)); + +	softc_device_init(sc, "safe", num_chips, safe_methods); + +	sc->sc_irq = -1; +	sc->sc_cid = -1; +	sc->sc_pcidev = dev; +	if (num_chips < SAFE_MAX_CHIPS) { +		safe_chip_idx[device_get_unit(sc->sc_dev)] = sc; +		num_chips++; +	} + +	INIT_LIST_HEAD(&sc->sc_pkq); +	spin_lock_init(&sc->sc_pkmtx); + +	pci_set_drvdata(sc->sc_pcidev, sc); + +	/* we read its hardware registers as memory */ +	mem_start = pci_resource_start(sc->sc_pcidev, 0); +	mem_len   = pci_resource_len(sc->sc_pcidev, 0); + +	sc->sc_base_addr = (ocf_iomem_t) ioremap(mem_start, mem_len); +	if (!sc->sc_base_addr) { +		device_printf(sc->sc_dev, "failed to ioremap 0x%x-0x%x\n", +				mem_start, mem_start + mem_len - 1); +		goto out; +	} + +	/* fix up the bus size */ +	if (pci_set_dma_mask(sc->sc_pcidev, DMA_32BIT_MASK)) { +		device_printf(sc->sc_dev, "No usable DMA configuration, aborting.\n"); +		goto out; +	} +	if (pci_set_consistent_dma_mask(sc->sc_pcidev, DMA_32BIT_MASK)) { +		device_printf(sc->sc_dev, "No usable consistent DMA configuration, aborting.\n"); +		goto out; +	} + +	pci_set_master(sc->sc_pcidev); + +	pci_read_config_dword(sc->sc_pcidev, PCI_COMMAND, &cmd); + +	if (!(cmd & PCI_COMMAND_MEMORY)) { +		device_printf(sc->sc_dev, "failed to enable memory mapping\n"); +		goto out; +	} + +	if (!(cmd & PCI_COMMAND_MASTER)) { +		device_printf(sc->sc_dev, "failed to enable bus mastering\n"); +		goto out; +	} + +	rc = request_irq(dev->irq, safe_intr, IRQF_SHARED, "safe", sc); +	if (rc) { +		device_printf(sc->sc_dev, "failed to hook irq %d\n", sc->sc_irq); +		goto out; +	} +	sc->sc_irq = dev->irq; + +	sc->sc_chiprev = READ_REG(sc, SAFE_DEVINFO) & +			(SAFE_DEVINFO_REV_MAJ | SAFE_DEVINFO_REV_MIN); + +	/* +	 * Allocate packet engine descriptors. +	 */ +	sc->sc_ringalloc.dma_vaddr = pci_alloc_consistent(sc->sc_pcidev, +			SAFE_MAX_NQUEUE * sizeof (struct safe_ringentry), +			&sc->sc_ringalloc.dma_paddr); +	if (!sc->sc_ringalloc.dma_vaddr) { +		device_printf(sc->sc_dev, "cannot allocate PE descriptor ring\n"); +		goto out; +	} + +	/* +	 * Hookup the static portion of all our data structures. +	 */ +	sc->sc_ring = (struct safe_ringentry *) sc->sc_ringalloc.dma_vaddr; +	sc->sc_ringtop = sc->sc_ring + SAFE_MAX_NQUEUE; +	sc->sc_front = sc->sc_ring; +	sc->sc_back = sc->sc_ring; +	raddr = sc->sc_ringalloc.dma_paddr; +	bzero(sc->sc_ring, SAFE_MAX_NQUEUE * sizeof(struct safe_ringentry)); +	for (i = 0; i < SAFE_MAX_NQUEUE; i++) { +		struct safe_ringentry *re = &sc->sc_ring[i]; + +		re->re_desc.d_sa = raddr + +			offsetof(struct safe_ringentry, re_sa); +		re->re_sa.sa_staterec = raddr + +			offsetof(struct safe_ringentry, re_sastate); + +		raddr += sizeof (struct safe_ringentry); +	} +	spin_lock_init(&sc->sc_ringmtx); + +	/* +	 * Allocate scatter and gather particle descriptors. +	 */ +	sc->sc_spalloc.dma_vaddr = pci_alloc_consistent(sc->sc_pcidev, +			SAFE_TOTAL_SPART * sizeof (struct safe_pdesc), +			&sc->sc_spalloc.dma_paddr); +	if (!sc->sc_spalloc.dma_vaddr) { +		device_printf(sc->sc_dev, "cannot allocate source particle descriptor ring\n"); +		goto out; +	} +	sc->sc_spring = (struct safe_pdesc *) sc->sc_spalloc.dma_vaddr; +	sc->sc_springtop = sc->sc_spring + SAFE_TOTAL_SPART; +	sc->sc_spfree = sc->sc_spring; +	bzero(sc->sc_spring, SAFE_TOTAL_SPART * sizeof(struct safe_pdesc)); + +	sc->sc_dpalloc.dma_vaddr = pci_alloc_consistent(sc->sc_pcidev, +			SAFE_TOTAL_DPART * sizeof (struct safe_pdesc), +			&sc->sc_dpalloc.dma_paddr); +	if (!sc->sc_dpalloc.dma_vaddr) { +		device_printf(sc->sc_dev, "cannot allocate destination particle descriptor ring\n"); +		goto out; +	} +	sc->sc_dpring = (struct safe_pdesc *) sc->sc_dpalloc.dma_vaddr; +	sc->sc_dpringtop = sc->sc_dpring + SAFE_TOTAL_DPART; +	sc->sc_dpfree = sc->sc_dpring; +	bzero(sc->sc_dpring, SAFE_TOTAL_DPART * sizeof(struct safe_pdesc)); + +	sc->sc_cid = crypto_get_driverid(softc_get_device(sc), CRYPTOCAP_F_HARDWARE); +	if (sc->sc_cid < 0) { +		device_printf(sc->sc_dev, "could not get crypto driver id\n"); +		goto out; +	} + +	printf("%s:", device_get_nameunit(sc->sc_dev)); + +	devinfo = READ_REG(sc, SAFE_DEVINFO); +	if (devinfo & SAFE_DEVINFO_RNG) { +		sc->sc_flags |= SAFE_FLAGS_RNG; +		printf(" rng"); +	} +	if (devinfo & SAFE_DEVINFO_PKEY) { +		printf(" key"); +		sc->sc_flags |= SAFE_FLAGS_KEY; +		crypto_kregister(sc->sc_cid, CRK_MOD_EXP, 0); +#if 0 +		crypto_kregister(sc->sc_cid, CRK_MOD_EXP_CRT, 0); +#endif +		init_timer(&sc->sc_pkto); +		sc->sc_pkto.function = safe_kpoll; +		sc->sc_pkto.data = (unsigned long) device_get_unit(sc->sc_dev); +	} +	if (devinfo & SAFE_DEVINFO_DES) { +		printf(" des/3des"); +		crypto_register(sc->sc_cid, CRYPTO_3DES_CBC, 0, 0); +		crypto_register(sc->sc_cid, CRYPTO_DES_CBC, 0, 0); +	} +	if (devinfo & SAFE_DEVINFO_AES) { +		printf(" aes"); +		crypto_register(sc->sc_cid, CRYPTO_AES_CBC, 0, 0); +	} +	if (devinfo & SAFE_DEVINFO_MD5) { +		printf(" md5"); +		crypto_register(sc->sc_cid, CRYPTO_MD5_HMAC, 0, 0); +	} +	if (devinfo & SAFE_DEVINFO_SHA1) { +		printf(" sha1"); +		crypto_register(sc->sc_cid, CRYPTO_SHA1_HMAC, 0, 0); +	} +	printf(" null"); +	crypto_register(sc->sc_cid, CRYPTO_NULL_CBC, 0, 0); +	crypto_register(sc->sc_cid, CRYPTO_NULL_HMAC, 0, 0); +	/* XXX other supported algorithms */ +	printf("\n"); + +	safe_reset_board(sc);		/* reset h/w */ +	safe_init_board(sc);		/* init h/w */ + +#if defined(CONFIG_OCF_RANDOMHARVEST) && !defined(SAFE_NO_RNG) +	if (sc->sc_flags & SAFE_FLAGS_RNG) { +		safe_rng_init(sc); +		crypto_rregister(sc->sc_cid, safe_read_random, sc); +	} +#endif /* SAFE_NO_RNG */ + +	return (0); + +out: +	if (sc->sc_cid >= 0) +		crypto_unregister_all(sc->sc_cid); +	if (sc->sc_irq != -1) +		free_irq(sc->sc_irq, sc); +	if (sc->sc_ringalloc.dma_vaddr) +		pci_free_consistent(sc->sc_pcidev, +				SAFE_MAX_NQUEUE * sizeof (struct safe_ringentry), +				sc->sc_ringalloc.dma_vaddr, sc->sc_ringalloc.dma_paddr); +	if (sc->sc_spalloc.dma_vaddr) +		pci_free_consistent(sc->sc_pcidev, +				SAFE_TOTAL_DPART * sizeof (struct safe_pdesc), +				sc->sc_spalloc.dma_vaddr, sc->sc_spalloc.dma_paddr); +	if (sc->sc_dpalloc.dma_vaddr) +		pci_free_consistent(sc->sc_pcidev, +				SAFE_TOTAL_DPART * sizeof (struct safe_pdesc), +				sc->sc_dpalloc.dma_vaddr, sc->sc_dpalloc.dma_paddr); +	kfree(sc); +	return(-ENODEV); +} + +static void safe_remove(struct pci_dev *dev) +{ +	struct safe_softc *sc = pci_get_drvdata(dev); + +	DPRINTF(("%s()\n", __FUNCTION__)); + +	/* XXX wait/abort active ops */ + +	WRITE_REG(sc, SAFE_HI_MASK, 0);		/* disable interrupts */ + +	del_timer_sync(&sc->sc_pkto); + +	crypto_unregister_all(sc->sc_cid); + +	safe_cleanchip(sc); + +	if (sc->sc_irq != -1) +		free_irq(sc->sc_irq, sc); +	if (sc->sc_ringalloc.dma_vaddr) +		pci_free_consistent(sc->sc_pcidev, +				SAFE_MAX_NQUEUE * sizeof (struct safe_ringentry), +				sc->sc_ringalloc.dma_vaddr, sc->sc_ringalloc.dma_paddr); +	if (sc->sc_spalloc.dma_vaddr) +		pci_free_consistent(sc->sc_pcidev, +				SAFE_TOTAL_DPART * sizeof (struct safe_pdesc), +				sc->sc_spalloc.dma_vaddr, sc->sc_spalloc.dma_paddr); +	if (sc->sc_dpalloc.dma_vaddr) +		pci_free_consistent(sc->sc_pcidev, +				SAFE_TOTAL_DPART * sizeof (struct safe_pdesc), +				sc->sc_dpalloc.dma_vaddr, sc->sc_dpalloc.dma_paddr); +	sc->sc_irq = -1; +	sc->sc_ringalloc.dma_vaddr = NULL; +	sc->sc_spalloc.dma_vaddr = NULL; +	sc->sc_dpalloc.dma_vaddr = NULL; +} + +static struct pci_device_id safe_pci_tbl[] = { +	{ PCI_VENDOR_SAFENET, PCI_PRODUCT_SAFEXCEL, +	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, +	{ }, +}; +MODULE_DEVICE_TABLE(pci, safe_pci_tbl); + +static struct pci_driver safe_driver = { +	.name         = "safe", +	.id_table     = safe_pci_tbl, +	.probe        =	safe_probe, +	.remove       = safe_remove, +	/* add PM stuff here one day */ +}; + +static int __init safe_init (void) +{ +	struct safe_softc *sc = NULL; +	int rc; + +	DPRINTF(("%s(%p)\n", __FUNCTION__, safe_init)); + +	rc = pci_register_driver(&safe_driver); +	pci_register_driver_compat(&safe_driver, rc); + +	return rc; +} + +static void __exit safe_exit (void) +{ +	pci_unregister_driver(&safe_driver); +} + +module_init(safe_init); +module_exit(safe_exit); + +MODULE_LICENSE("BSD"); +MODULE_AUTHOR("David McCullough <david_mccullough@securecomputing.com>"); +MODULE_DESCRIPTION("OCF driver for safenet PCI crypto devices"); | 
