📄 safe.c
字号:
/*- * The linux port of this code done by David McCullough <davidm@snapgear.com> * The license and original author are listed below. * Copyright (C) 2004 David McCullough <davidm@snapgear.com> * * 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. */#include <linux/config.h>#include <linux/module.h>#include <linux/init.h>#include <linux/list.h>#include <linux/slab.h>#include <linux/wait.h>#include <linux/sched.h>#include <linux/pci.h>#include <linux/delay.h>#include <linux/interrupt.h>#include <linux/spinlock.h>#include <linux/random.h>#include <asm/io.h>/* * some support for older kernels */#ifndef IRQ_NONE#define IRQ_NONE#define IRQ_HANDLED#define irqreturn_t void#endif/* * SafeNet SafeXcel-1141 hardware crypto accelerator */#include <cryptodev.h>#include <uio.h>#include <safe/safereg.h>#include <safe/safevar.h>#define bswap32(x) /* seems the code using this below is buggy */#define read_random(p,l) get_random_bytes(p,l)#define KASSERT(c,p) if (!c) { printk p ; } else#if 1#define DPRINTF(a...) if (debug) { printk("safe: " a); } else#else#define DPRINTF(a...)#endif#define DELAY(x) udelay(x)#define bcopy(s,d,l) memcpy(d,s,l)#define bzero(p,l) memset(p,0,l)#define READ_REG(sc,r) readl((sc)->sc_base_addr + (r))#define WRITE_REG(sc,r,val) writel((val), (sc)->sc_base_addr + (r))/* add proc entry for this */struct safe_stats safestats;static int debug = 0;MODULE_PARM(debug, "i");MODULE_PARM_DESC(debug, "Enable debug");#ifndef SAFE_NO_RNGstatic int safe_rnginterval = 1; /* poll once a second */MODULE_PARM(safe_rnginterval, "i");MODULE_PARM_DESC(safe_rnginterval, "RNG polling interval (secs)");static int safe_rngbufsize = 16; /* 64 bytes each poll */MODULE_PARM(safe_rngbufsize, "i");MODULE_PARM_DESC(safe_rngbufsize, "RNG polling buffer size (32-bit words)");static int safe_rngmaxalarm = 8; /* max alarms before reset */MODULE_PARM(safe_rngmaxalarm, "i");MODULE_PARM_DESC(safe_rngmaxalarm, "RNG max alarms before reset");#endif /* SAFE_NO_RNG */static void safe_callback(struct safe_softc *sc, struct safe_ringentry *re);static void safe_totalreset(struct safe_softc *sc);static int safe_dmamap_aligned(const struct safe_operand *op);static int safe_dmamap_uniform(const struct safe_operand *op);static int safe_free_entry(struct safe_softc *sc, struct safe_ringentry *re);/* * map in a given buffer (great on some arches :-) */static intpci_map_uio(struct safe_softc *sc, struct safe_operand *buf, struct uio *uio){ struct iovec *iov = uio->uio_iov; DPRINTF("%s()\n", __FUNCTION__); buf->mapsize = 0; for (buf->nsegs = 0; buf->nsegs < uio->uio_iovcnt; ) { buf->segs[buf->nsegs].ds_addr = pci_map_single(sc->sc_dev, iov->iov_base, iov->iov_len, PCI_DMA_BIDIRECTIONAL); buf->segs[buf->nsegs].ds_len = iov->iov_len; buf->mapsize += iov->iov_len; iov++; buf->nsegs++; } /* identify this buffer by the first segment */ buf->map = (void *) buf->segs[0].ds_addr; return(0);}static voidpci_sync_iov(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(sc->sc_dev, buf->segs[i].ds_addr, buf->segs[i].ds_len, PCI_DMA_BIDIRECTIONAL);}static voidpci_unmap_iov(struct safe_softc *sc, struct safe_operand *buf){ int i; DPRINTF("%s()\n", __FUNCTION__); for (i = 0; i < buf->nsegs; i++) { pci_unmap_single(sc->sc_dev, buf->segs[i].ds_addr, buf->segs[i].ds_len, PCI_DMA_BIDIRECTIONAL); buf->segs[i].ds_addr = 0; buf->segs[i].ds_len = 0; } buf->nsegs = 0; buf->mapsize = 0; buf->map = 0;}/* * SafeXcel Interrupt routine */static irqreturn_tsafe_intr(int irq, void *arg, struct pt_regs *regs){ struct safe_softc *sc = arg; int stat, 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("safe: dmaerr dmastat %08x\n", (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 voidsafe_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);}/* * 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 intsafe_newsession(void *arg, u_int32_t *sidp, struct cryptoini *cri){#define N(a) (sizeof(a) / sizeof (a[0])) struct cryptoini *c, *encini = NULL, *macini = NULL; struct safe_softc *sc = arg; struct safe_session *ses = NULL;#if NOTYET MD5_CTX md5ctx; SHA1_CTX sha1ctx;#endif int i, 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), GFP_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), GFP_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; bcopy(encini->cri_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] = cpu_to_le32(ses->ses_key[i]); } if (macini) {#if NOTYET for (i = 0; i < macini->cri_klen / 8; i++) macini->cri_key[i] ^= HMAC_IPAD_VAL; if (macini->cri_alg == CRYPTO_MD5_HMAC) { MD5Init(&md5ctx); MD5Update(&md5ctx, macini->cri_key, macini->cri_klen / 8); MD5Update(&md5ctx, hmac_ipad_buffer, HMAC_BLOCK_LEN - (macini->cri_klen / 8)); bcopy(md5ctx.state, ses->ses_hminner, sizeof(md5ctx.state)); } else { SHA1Init(&sha1ctx); SHA1Update(&sha1ctx, macini->cri_key, macini->cri_klen / 8); SHA1Update(&sha1ctx, hmac_ipad_buffer, HMAC_BLOCK_LEN - (macini->cri_klen / 8)); bcopy(sha1ctx.h.b32, ses->ses_hminner, sizeof(sha1ctx.h.b32)); } for (i = 0; i < macini->cri_klen / 8; i++) macini->cri_key[i] ^= (HMAC_IPAD_VAL ^ HMAC_OPAD_VAL); if (macini->cri_alg == CRYPTO_MD5_HMAC) { MD5Init(&md5ctx); MD5Update(&md5ctx, macini->cri_key, macini->cri_klen / 8); MD5Update(&md5ctx, hmac_opad_buffer, HMAC_BLOCK_LEN - (macini->cri_klen / 8)); bcopy(md5ctx.state, ses->ses_hmouter, sizeof(md5ctx.state)); } else { SHA1Init(&sha1ctx); SHA1Update(&sha1ctx, macini->cri_key, macini->cri_klen / 8); SHA1Update(&sha1ctx, hmac_opad_buffer, HMAC_BLOCK_LEN - (macini->cri_klen / 8)); bcopy(sha1ctx.h.b32, ses->ses_hmouter, sizeof(sha1ctx.h.b32)); } for (i = 0; i < macini->cri_klen / 8; i++) macini->cri_key[i] ^= HMAC_OPAD_VAL; /* PE is little-endian, insure proper byte order */ for (i = 0; i < N(ses->ses_hminner); i++) { ses->ses_hminner[i] = cpu_to_le32(ses->ses_hminner[i]); ses->ses_hmouter[i] = cpu_to_le32(ses->ses_hmouter[i]); }#else printk("safe: no MD5 or SHA yet\n");#endif } *sidp = SAFE_SID(sc->sc_num, sesn); return (0);#undef N}/* * Deallocate a session. */static intsafe_freesession(void *arg, u_int64_t tid){ struct safe_softc *sc = arg; 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -