From 716ca530e1c4515d8683c9d5be3d56b301758b66 Mon Sep 17 00:00:00 2001 From: James <> Date: Wed, 4 Nov 2015 11:49:21 +0000 Subject: trunk-47381 --- .../files/crypto/ocf/kirkwood/cesa_ocf_drv.c | 1302 ++++++++++++++++++++ 1 file changed, 1302 insertions(+) create mode 100644 target/linux/generic/files/crypto/ocf/kirkwood/cesa_ocf_drv.c (limited to 'target/linux/generic/files/crypto/ocf/kirkwood/cesa_ocf_drv.c') diff --git a/target/linux/generic/files/crypto/ocf/kirkwood/cesa_ocf_drv.c b/target/linux/generic/files/crypto/ocf/kirkwood/cesa_ocf_drv.c new file mode 100644 index 0000000..6fb9e09 --- /dev/null +++ b/target/linux/generic/files/crypto/ocf/kirkwood/cesa_ocf_drv.c @@ -0,0 +1,1302 @@ +/******************************************************************************* +Copyright (C) Marvell International Ltd. and its affiliates + +This software file (the "File") is owned and distributed by Marvell +International Ltd. and/or its affiliates ("Marvell") under the following +alternative licensing terms. Once you have made an election to distribute the +File under one of the following license alternatives, please (i) delete this +introductory statement regarding license alternatives, (ii) delete the two +license alternatives that you have not elected to use and (iii) preserve the +Marvell copyright notice above. + + +******************************************************************************** +Marvell GPL License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File in accordance with the terms and conditions of the General +Public License Version 2, June 1991 (the "GPL License"), a copy of which is +available along with the File in the license.txt file or by writing to the Free +Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or +on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. +*******************************************************************************/ + +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) && !defined(AUTOCONF_INCLUDED) +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ctrlEnv/sys/mvSysCesa.h" +#include "cesa/mvCesa.h" /* moved here before cryptodev.h due to include dependencies */ +#include +#include +#include +#include +#include "mvDebug.h" + +#include "cesa/mvMD5.h" +#include "cesa/mvSHA1.h" + +#include "cesa/mvCesaRegs.h" +#include "cesa/AES/mvAes.h" +#include "cesa/mvLru.h" + +#undef RT_DEBUG +#ifdef RT_DEBUG +static int debug = 1; +module_param(debug, int, 1); +MODULE_PARM_DESC(debug, "Enable debug"); +#undef dprintk +#define dprintk(a...) if (debug) { printk(a); } else +#else +static int debug = 0; +#undef dprintk +#define dprintk(a...) +#endif + + +/* TDMA Regs */ +#define WINDOW_BASE(i) 0xA00 + (i << 3) +#define WINDOW_CTRL(i) 0xA04 + (i << 3) + +/* interrupt handling */ +#undef CESA_OCF_POLLING +#undef CESA_OCF_TASKLET + +#if defined(CESA_OCF_POLLING) && defined(CESA_OCF_TASKLET) +#error "don't use both tasklet and polling mode" +#endif + +extern int cesaReqResources; +/* support for spliting action into 2 actions */ +#define CESA_OCF_SPLIT + +/* general defines */ +#define CESA_OCF_MAX_SES 128 +#define CESA_Q_SIZE 64 + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0) +#define FRAG_PAGE(f) (f).p +#else +#define FRAG_PAGE(f) (f) +#endif + +/* data structures */ +struct cesa_ocf_data { + int cipher_alg; + int auth_alg; + int encrypt_tn_auth; +#define auth_tn_decrypt encrypt_tn_auth + int ivlen; + int digestlen; + short sid_encrypt; + short sid_decrypt; + /* fragment workaround sessions */ + short frag_wa_encrypt; + short frag_wa_decrypt; + short frag_wa_auth; +}; + +/* CESA device data */ +struct cesa_dev { + void __iomem *sram; + void __iomem *reg; + struct mv_cesa_platform_data *plat_data; + int irq; +}; + +#define DIGEST_BUF_SIZE 32 +struct cesa_ocf_process { + MV_CESA_COMMAND cesa_cmd; + MV_CESA_MBUF cesa_mbuf; + MV_BUF_INFO cesa_bufs[MV_CESA_MAX_MBUF_FRAGS]; + char digest[DIGEST_BUF_SIZE]; + int digest_len; + struct cryptop *crp; + int need_cb; +}; + +/* global variables */ +static int32_t cesa_ocf_id = -1; +static struct cesa_ocf_data *cesa_ocf_sessions[CESA_OCF_MAX_SES]; +static spinlock_t cesa_lock; +static struct cesa_dev cesa_device; + +/* static APIs */ +static int cesa_ocf_process (device_t, struct cryptop *, int); +static int cesa_ocf_newsession (device_t, u_int32_t *, struct cryptoini *); +static int cesa_ocf_freesession (device_t, u_int64_t); +static void cesa_callback (unsigned long); +static irqreturn_t cesa_interrupt_handler (int, void *); +#ifdef CESA_OCF_POLLING +static void cesa_interrupt_polling(void); +#endif +#ifdef CESA_OCF_TASKLET +static struct tasklet_struct cesa_ocf_tasklet; +#endif + +static struct timeval tt_start; +static struct timeval tt_end; + +/* + * dummy device structure + */ + +static struct { + softc_device_decl sc_dev; +} mv_cesa_dev; + +static device_method_t mv_cesa_methods = { + /* crypto device methods */ + DEVMETHOD(cryptodev_newsession, cesa_ocf_newsession), + DEVMETHOD(cryptodev_freesession,cesa_ocf_freesession), + DEVMETHOD(cryptodev_process, cesa_ocf_process), + DEVMETHOD(cryptodev_kprocess, NULL), +}; + + + +/* Add debug Trace */ +#undef CESA_OCF_TRACE_DEBUG +#ifdef CESA_OCF_TRACE_DEBUG + +#define MV_CESA_USE_TIMER_ID 0 + +typedef struct +{ + int type; /* 0 - isrEmpty, 1 - cesaReadyGet, 2 - cesaAction */ + MV_U32 timeStamp; + MV_U32 cause; + MV_U32 realCause; + MV_U32 dmaCause; + int resources; + MV_CESA_REQ* pReqReady; + MV_CESA_REQ* pReqEmpty; + MV_CESA_REQ* pReqProcess; +} MV_CESA_TEST_TRACE; + +#define MV_CESA_TEST_TRACE_SIZE 50 + +static int cesaTestTraceIdx = 0; +static MV_CESA_TEST_TRACE cesaTestTrace[MV_CESA_TEST_TRACE_SIZE]; + +static void cesaTestTraceAdd(int type) +{ + cesaTestTrace[cesaTestTraceIdx].type = type; + cesaTestTrace[cesaTestTraceIdx].realCause = MV_REG_READ(MV_CESA_ISR_CAUSE_REG); + //cesaTestTrace[cesaTestTraceIdx].idmaCause = MV_REG_READ(IDMA_CAUSE_REG); + cesaTestTrace[cesaTestTraceIdx].resources = cesaReqResources; + cesaTestTrace[cesaTestTraceIdx].pReqReady = pCesaReqReady; + cesaTestTrace[cesaTestTraceIdx].pReqEmpty = pCesaReqEmpty; + cesaTestTrace[cesaTestTraceIdx].pReqProcess = pCesaReqProcess; + cesaTestTrace[cesaTestTraceIdx].timeStamp = mvCntmrRead(MV_CESA_USE_TIMER_ID); + cesaTestTraceIdx++; + if(cesaTestTraceIdx == MV_CESA_TEST_TRACE_SIZE) + cesaTestTraceIdx = 0; +} + +#else /* CESA_OCF_TRACE_DEBUG */ + +#define cesaTestTraceAdd(x) + +#endif /* CESA_OCF_TRACE_DEBUG */ + +unsigned int +get_usec(unsigned int start) +{ + if(start) { + do_gettimeofday (&tt_start); + return 0; + } + else { + do_gettimeofday (&tt_end); + tt_end.tv_sec -= tt_start.tv_sec; + tt_end.tv_usec -= tt_start.tv_usec; + if (tt_end.tv_usec < 0) { + tt_end.tv_usec += 1000 * 1000; + tt_end.tv_sec -= 1; + } + } + printk("time taken is %d\n", (unsigned int)(tt_end.tv_usec + tt_end.tv_sec * 1000000)); + return (tt_end.tv_usec + tt_end.tv_sec * 1000000); +} + +#ifdef RT_DEBUG +/* + * check that the crp action match the current session + */ +static int +ocf_check_action(struct cryptop *crp, struct cesa_ocf_data *cesa_ocf_cur_ses) { + int count = 0; + int encrypt = 0, decrypt = 0, auth = 0; + struct cryptodesc *crd; + + /* Go through crypto descriptors, processing as we go */ + for (crd = crp->crp_desc; crd; crd = crd->crd_next, count++) { + if(count > 2) { + printk("%s,%d: session mode is not supported.\n", __FILE__, __LINE__); + return 1; + } + + /* Encryption /Decryption */ + if(crd->crd_alg == cesa_ocf_cur_ses->cipher_alg) { + /* check that the action is compatible with session */ + if(encrypt || decrypt) { + printk("%s,%d: session mode is not supported.\n", __FILE__, __LINE__); + return 1; + } + + if(crd->crd_flags & CRD_F_ENCRYPT) { /* encrypt */ + if( (count == 2) && (cesa_ocf_cur_ses->encrypt_tn_auth) ) { + printk("%s,%d: sequence isn't supported by this session.\n", __FILE__, __LINE__); + return 1; + } + encrypt++; + } + else { /* decrypt */ + if( (count == 2) && !(cesa_ocf_cur_ses->auth_tn_decrypt) ) { + printk("%s,%d: sequence isn't supported by this session.\n", __FILE__, __LINE__); + return 1; + } + decrypt++; + } + + } + /* Authentication */ + else if(crd->crd_alg == cesa_ocf_cur_ses->auth_alg) { + /* check that the action is compatible with session */ + if(auth) { + printk("%s,%d: session mode is not supported.\n", __FILE__, __LINE__); + return 1; + } + if( (count == 2) && (decrypt) && (cesa_ocf_cur_ses->auth_tn_decrypt)) { + printk("%s,%d: sequence isn't supported by this session.\n", __FILE__, __LINE__); + return 1; + } + if( (count == 2) && (encrypt) && !(cesa_ocf_cur_ses->encrypt_tn_auth)) { + printk("%s,%d: sequence isn't supported by this session.\n", __FILE__, __LINE__); + return 1; + } + auth++; + } + else { + printk("%s,%d: Alg isn't supported by this session.\n", __FILE__, __LINE__); + return 1; + } + } + return 0; + +} +#endif + +/* + * Process a request. + */ +static int +cesa_ocf_process(device_t dev, struct cryptop *crp, int hint) +{ + struct cesa_ocf_process *cesa_ocf_cmd = NULL; + struct cesa_ocf_process *cesa_ocf_cmd_wa = NULL; + MV_CESA_COMMAND *cesa_cmd; + struct cryptodesc *crd; + struct cesa_ocf_data *cesa_ocf_cur_ses; + int sid = 0, temp_len = 0, i; + int encrypt = 0, decrypt = 0, auth = 0; + int status; + struct sk_buff *skb = NULL; + struct uio *uiop = NULL; + unsigned char *ivp; + MV_BUF_INFO *p_buf_info; + MV_CESA_MBUF *p_mbuf_info; + unsigned long flags; + + dprintk("%s()\n", __FUNCTION__); + + if( cesaReqResources <= 1 ) { + dprintk("%s,%d: ERESTART\n", __FILE__, __LINE__); + return ERESTART; + } + +#ifdef RT_DEBUG + /* Sanity check */ + if (crp == NULL) { + printk("%s,%d: EINVAL\n", __FILE__, __LINE__); + return EINVAL; + } + + if (crp->crp_desc == NULL || crp->crp_buf == NULL ) { + printk("%s,%d: EINVAL\n", __FILE__, __LINE__); + crp->crp_etype = EINVAL; + return EINVAL; + } + + sid = crp->crp_sid & 0xffffffff; + if ((sid >= CESA_OCF_MAX_SES) || (cesa_ocf_sessions[sid] == NULL)) { + crp->crp_etype = ENOENT; + printk("%s,%d: ENOENT session %d \n", __FILE__, __LINE__, sid); + return EINVAL; + } +#endif + + sid = crp->crp_sid & 0xffffffff; + crp->crp_etype = 0; + cesa_ocf_cur_ses = cesa_ocf_sessions[sid]; + +#ifdef RT_DEBUG + if(ocf_check_action(crp, cesa_ocf_cur_ses)){ + goto p_error; + } +#endif + + /* malloc a new cesa process */ + cesa_ocf_cmd = kmalloc(sizeof(struct cesa_ocf_process), GFP_ATOMIC); + + if (cesa_ocf_cmd == NULL) { + printk("%s,%d: ENOBUFS \n", __FILE__, __LINE__); + goto p_error; + } + memset(cesa_ocf_cmd, 0, sizeof(struct cesa_ocf_process)); + + /* init cesa_process */ + cesa_ocf_cmd->crp = crp; + /* always call callback */ + cesa_ocf_cmd->need_cb = 1; + + /* init cesa_cmd for usage of the HALs */ + cesa_cmd = &cesa_ocf_cmd->cesa_cmd; + cesa_cmd->pReqPrv = (void *)cesa_ocf_cmd; + cesa_cmd->sessionId = cesa_ocf_cur_ses->sid_encrypt; /* defualt use encrypt */ + + /* prepare src buffer */ + /* we send the entire buffer to the HAL, even if only part of it should be encrypt/auth. */ + /* if not using seesions for both encrypt and auth, then it will be wiser to to copy only */ + /* from skip to crd_len. */ + p_buf_info = cesa_ocf_cmd->cesa_bufs; + p_mbuf_info = &cesa_ocf_cmd->cesa_mbuf; + + p_buf_info += 2; /* save 2 first buffers for IV and digest - + we won't append them to the end since, they + might be places in an unaligned addresses. */ + + p_mbuf_info->pFrags = p_buf_info; + temp_len = 0; + + /* handle SKB */ + if (crp->crp_flags & CRYPTO_F_SKBUF) { + + dprintk("%s,%d: handle SKB.\n", __FILE__, __LINE__); + skb = (struct sk_buff *) crp->crp_buf; + + if (skb_shinfo(skb)->nr_frags >= (MV_CESA_MAX_MBUF_FRAGS - 1)) { + printk("%s,%d: %d nr_frags > MV_CESA_MAX_MBUF_FRAGS", __FILE__, __LINE__, skb_shinfo(skb)->nr_frags); + goto p_error; + } + + p_mbuf_info->mbufSize = skb->len; + temp_len = skb->len; + /* first skb fragment */ + p_buf_info->bufSize = skb_headlen(skb); + p_buf_info->bufVirtPtr = skb->data; + p_buf_info++; + + /* now handle all other skb fragments */ + for ( i = 0; i < skb_shinfo(skb)->nr_frags; i++ ) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + p_buf_info->bufSize = frag->size; + p_buf_info->bufVirtPtr = page_address(FRAG_PAGE(frag->page)) + frag->page_offset; + p_buf_info++; + } + p_mbuf_info->numFrags = skb_shinfo(skb)->nr_frags + 1; + } + /* handle UIO */ + else if(crp->crp_flags & CRYPTO_F_IOV) { + + dprintk("%s,%d: handle UIO.\n", __FILE__, __LINE__); + uiop = (struct uio *) crp->crp_buf; + + if (uiop->uio_iovcnt > (MV_CESA_MAX_MBUF_FRAGS - 1)) { + printk("%s,%d: %d uio_iovcnt > MV_CESA_MAX_MBUF_FRAGS \n", __FILE__, __LINE__, uiop->uio_iovcnt); + goto p_error; + } + + p_mbuf_info->mbufSize = crp->crp_ilen; + p_mbuf_info->numFrags = uiop->uio_iovcnt; + for(i = 0; i < uiop->uio_iovcnt; i++) { + p_buf_info->bufVirtPtr = uiop->uio_iov[i].iov_base; + p_buf_info->bufSize = uiop->uio_iov[i].iov_len; + temp_len += p_buf_info->bufSize; + dprintk("%s,%d: buf %x-> addr %x, size %x \n" + , __FILE__, __LINE__, i, (unsigned int)p_buf_info->bufVirtPtr, p_buf_info->bufSize); + p_buf_info++; + } + + } + /* handle CONTIG */ + else { + dprintk("%s,%d: handle CONTIG.\n", __FILE__, __LINE__); + p_mbuf_info->numFrags = 1; + p_mbuf_info->mbufSize = crp->crp_ilen; + p_buf_info->bufVirtPtr = crp->crp_buf; + p_buf_info->bufSize = crp->crp_ilen; + temp_len = crp->crp_ilen; + p_buf_info++; + } + + /* Support up to 64K why? cause! */ + if(crp->crp_ilen > 64*1024) { + printk("%s,%d: buf too big %x \n", __FILE__, __LINE__, crp->crp_ilen); + goto p_error; + } + + if( temp_len != crp->crp_ilen ) { + printk("%s,%d: warning size don't match.(%x %x) \n", __FILE__, __LINE__, temp_len, crp->crp_ilen); + } + + cesa_cmd->pSrc = p_mbuf_info; + cesa_cmd->pDst = p_mbuf_info; + + /* restore p_buf_info to point to first available buf */ + p_buf_info = cesa_ocf_cmd->cesa_bufs; + p_buf_info += 1; + + + /* Go through crypto descriptors, processing as we go */ + for (crd = crp->crp_desc; crd; crd = crd->crd_next) { + + /* Encryption /Decryption */ + if(crd->crd_alg == cesa_ocf_cur_ses->cipher_alg) { + + dprintk("%s,%d: cipher", __FILE__, __LINE__); + + cesa_cmd->cryptoOffset = crd->crd_skip; + cesa_cmd->cryptoLength = crd->crd_len; + + if(crd->crd_flags & CRD_F_ENCRYPT) { /* encrypt */ + dprintk(" encrypt \n"); + encrypt++; + + /* handle IV */ + if (crd->crd_flags & CRD_F_IV_EXPLICIT) { /* IV from USER */ + dprintk("%s,%d: IV from USER (offset %x) \n", __FILE__, __LINE__, crd->crd_inject); + cesa_cmd->ivFromUser = 1; + ivp = crd->crd_iv; + + /* + * do we have to copy the IV back to the buffer ? + */ + if ((crd->crd_flags & CRD_F_IV_PRESENT) == 0) { + dprintk("%s,%d: copy the IV back to the buffer\n", __FILE__, __LINE__); + cesa_cmd->ivOffset = crd->crd_inject; + crypto_copyback(crp->crp_flags, crp->crp_buf, crd->crd_inject, cesa_ocf_cur_ses->ivlen, ivp); + } + else { + dprintk("%s,%d: don't copy the IV back to the buffer \n", __FILE__, __LINE__); + p_mbuf_info->numFrags++; + p_mbuf_info->mbufSize += cesa_ocf_cur_ses->ivlen; + p_mbuf_info->pFrags = p_buf_info; + + p_buf_info->bufVirtPtr = ivp; + p_buf_info->bufSize = cesa_ocf_cur_ses->ivlen; + p_buf_info--; + + /* offsets */ + cesa_cmd->ivOffset = 0; + cesa_cmd->cryptoOffset += cesa_ocf_cur_ses->ivlen; + if(auth) { + cesa_cmd->macOffset += cesa_ocf_cur_ses->ivlen; + cesa_cmd->digestOffset += cesa_ocf_cur_ses->ivlen; + } + } + } + else { /* random IV */ + dprintk("%s,%d: random IV \n", __FILE__, __LINE__); + cesa_cmd->ivFromUser = 0; + + /* + * do we have to copy the IV back to the buffer ? + */ + /* in this mode the HAL will always copy the IV */ + /* given by the session to the ivOffset */ + if ((crd->crd_flags & CRD_F_IV_PRESENT) == 0) { + cesa_cmd->ivOffset = crd->crd_inject; + } + else { + /* if IV isn't copy, then how will the user know which IV did we use??? */ + printk("%s,%d: EINVAL\n", __FILE__, __LINE__); + goto p_error; + } + } + } + else { /* decrypt */ + dprintk(" decrypt \n"); + decrypt++; + cesa_cmd->sessionId = cesa_ocf_cur_ses->sid_decrypt; + + /* handle IV */ + if (crd->crd_flags & CRD_F_IV_EXPLICIT) { + dprintk("%s,%d: IV from USER \n", __FILE__, __LINE__); + /* append the IV buf to the mbuf */ + cesa_cmd->ivFromUser = 1; + p_mbuf_info->numFrags++; + p_mbuf_info->mbufSize += cesa_ocf_cur_ses->ivlen; + p_mbuf_info->pFrags = p_buf_info; + + p_buf_info->bufVirtPtr = crd->crd_iv; + p_buf_info->bufSize = cesa_ocf_cur_ses->ivlen; + p_buf_info--; + + /* offsets */ + cesa_cmd->ivOffset = 0; + cesa_cmd->cryptoOffset += cesa_ocf_cur_ses->ivlen; + if(auth) { + cesa_cmd->macOffset += cesa_ocf_cur_ses->ivlen; + cesa_cmd->digestOffset += cesa_ocf_cur_ses->ivlen; + } + } + else { + dprintk("%s,%d: IV inside the buffer \n", __FILE__, __LINE__); + cesa_cmd->ivFromUser = 0; + cesa_cmd->ivOffset = crd->crd_inject; + } + } + + } + /* Authentication */ + else if(crd->crd_alg == cesa_ocf_cur_ses->auth_alg) { + dprintk("%s,%d: Authentication \n", __FILE__, __LINE__); + auth++; + cesa_cmd->macOffset = crd->crd_skip; + cesa_cmd->macLength = crd->crd_len; + + /* digest + mac */ + cesa_cmd->digestOffset = crd->crd_inject; + } + else { + printk("%s,%d: Alg isn't supported by this session.\n", __FILE__, __LINE__); + goto p_error; + } + } + + dprintk("\n"); + dprintk("%s,%d: Sending Action: \n", __FILE__, __LINE__); + dprintk("%s,%d: IV from user: %d. IV offset %x \n", __FILE__, __LINE__, cesa_cmd->ivFromUser, cesa_cmd->ivOffset); + dprintk("%s,%d: crypt offset %x len %x \n", __FILE__, __LINE__, cesa_cmd->cryptoOffset, cesa_cmd->cryptoLength); + dprintk("%s,%d: Auth offset %x len %x \n", __FILE__, __LINE__, cesa_cmd->macOffset, cesa_cmd->macLength); + dprintk("%s,%d: set digest in offset %x . \n", __FILE__, __LINE__, cesa_cmd->digestOffset); + if(debug) { + mvCesaDebugMbuf("SRC BUFFER", cesa_cmd->pSrc, 0, cesa_cmd->pSrc->mbufSize); + } + + + /* send action to HAL */ + spin_lock_irqsave(&cesa_lock, flags); + status = mvCesaAction(cesa_cmd); + spin_unlock_irqrestore(&cesa_lock, flags); + + /* action not allowed */ + if(status == MV_NOT_ALLOWED) { +#ifdef CESA_OCF_SPLIT + /* if both encrypt and auth try to split */ + if(auth && (encrypt || decrypt)) { + MV_CESA_COMMAND *cesa_cmd_wa; + + /* malloc a new cesa process and init it */ + cesa_ocf_cmd_wa = kmalloc(sizeof(struct cesa_ocf_process), GFP_ATOMIC); + + if (cesa_ocf_cmd_wa == NULL) { + printk("%s,%d: ENOBUFS \n", __FILE__, __LINE__); + goto p_error; + } + memcpy(cesa_ocf_cmd_wa, cesa_ocf_cmd, sizeof(struct cesa_ocf_process)); + cesa_cmd_wa = &cesa_ocf_cmd_wa->cesa_cmd; + cesa_cmd_wa->pReqPrv = (void *)cesa_ocf_cmd_wa; + cesa_ocf_cmd_wa->need_cb = 0; + + /* break requests to two operation, first operation completion won't call callback */ + if((decrypt) && (cesa_ocf_cur_ses->auth_tn_decrypt)) { + cesa_cmd_wa->sessionId = cesa_ocf_cur_ses->frag_wa_auth; + cesa_cmd->sessionId = cesa_ocf_cur_ses->frag_wa_decrypt; + } + else if((decrypt) && !(cesa_ocf_cur_ses->auth_tn_decrypt)) { + cesa_cmd_wa->sessionId = cesa_ocf_cur_ses->frag_wa_decrypt; + cesa_cmd->sessionId = cesa_ocf_cur_ses->frag_wa_auth; + } + else if((encrypt) && (cesa_ocf_cur_ses->encrypt_tn_auth)) { + cesa_cmd_wa->sessionId = cesa_ocf_cur_ses->frag_wa_encrypt; + cesa_cmd->sessionId = cesa_ocf_cur_ses->frag_wa_auth; + } + else if((encrypt) && !(cesa_ocf_cur_ses->encrypt_tn_auth)){ + cesa_cmd_wa->sessionId = cesa_ocf_cur_ses->frag_wa_auth; + cesa_cmd->sessionId = cesa_ocf_cur_ses->frag_wa_encrypt; + } + else { + printk("%s,%d: Unsupporterd fragment wa mode \n", __FILE__, __LINE__); + goto p_error; + } + + /* send the 2 actions to the HAL */ + spin_lock_irqsave(&cesa_lock, flags); + status = mvCesaAction(cesa_cmd_wa); + spin_unlock_irqrestore(&cesa_lock, flags); + + if((status != MV_NO_MORE) && (status != MV_OK)) { + printk("%s,%d: cesa action failed, status = 0x%x\n", __FILE__, __LINE__, status); + goto p_error; + } + spin_lock_irqsave(&cesa_lock, flags); + status = mvCesaAction(cesa_cmd); + spin_unlock_irqrestore(&cesa_lock, flags); + + } + /* action not allowed and can't split */ + else +#endif + { + goto p_error; + } + } + + /* Hal Q is full, send again. This should never happen */ + if(status == MV_NO_RESOURCE) { + printk("%s,%d: cesa no more resources \n", __FILE__, __LINE__); + if(cesa_ocf_cmd) + kfree(cesa_ocf_cmd); + if(cesa_ocf_cmd_wa) + kfree(cesa_ocf_cmd_wa); + return ERESTART; + } + else if((status != MV_NO_MORE) && (status != MV_OK)) { + printk("%s,%d: cesa action failed, status = 0x%x\n", __FILE__, __LINE__, status); + goto p_error; + } + + +#ifdef CESA_OCF_POLLING + cesa_interrupt_polling(); +#endif + cesaTestTraceAdd(5); + + return 0; +p_error: + crp->crp_etype = EINVAL; + if(cesa_ocf_cmd) + kfree(cesa_ocf_cmd); + if(cesa_ocf_cmd_wa) + kfree(cesa_ocf_cmd_wa); + return EINVAL; +} + +/* + * cesa callback. + */ +static void +cesa_callback(unsigned long dummy) +{ + struct cesa_ocf_process *cesa_ocf_cmd = NULL; + struct cryptop *crp = NULL; + MV_CESA_RESULT result[MV_CESA_MAX_CHAN]; + int res_idx = 0,i; + MV_STATUS status; + + dprintk("%s()\n", __FUNCTION__); + +#ifdef CESA_OCF_TASKLET + disable_irq(cesa_device.irq); +#endif + while(MV_TRUE) { + + /* Get Ready requests */ + spin_lock(&cesa_lock); + status = mvCesaReadyGet(&result[res_idx]); + spin_unlock(&cesa_lock); + + cesaTestTraceAdd(2); + + if(status != MV_OK) { +#ifdef CESA_OCF_POLLING + if(status == MV_BUSY) { /* Fragment */ + cesa_interrupt_polling(); + return; + } +#endif + break; + } + res_idx++; + break; + } + + for(i = 0; i < res_idx; i++) { + + if(!result[i].pReqPrv) { + printk("%s,%d: warning private is NULL\n", __FILE__, __LINE__); + break; + } + + cesa_ocf_cmd = result[i].pReqPrv; + crp = cesa_ocf_cmd->crp; + + // ignore HMAC error. + //if(result->retCode) + // crp->crp_etype = EIO; + +#if defined(CESA_OCF_POLLING) + if(!cesa_ocf_cmd->need_cb){ + cesa_interrupt_polling(); + } +#endif + if(cesa_ocf_cmd->need_cb) { + if(debug) { + mvCesaDebugMbuf("DST BUFFER", cesa_ocf_cmd->cesa_cmd.pDst, 0, cesa_ocf_cmd->cesa_cmd.pDst->mbufSize); + } + crypto_done(crp); + } + kfree(cesa_ocf_cmd); + } +#ifdef CESA_OCF_TASKLET + enable_irq(cesa_device.irq); +#endif + + cesaTestTraceAdd(3); + + return; +} + +#ifdef CESA_OCF_POLLING +static void +cesa_interrupt_polling(void) +{ + u32 cause; + + dprintk("%s()\n", __FUNCTION__); + + /* Read cause register */ + do { + cause = MV_REG_READ(MV_CESA_ISR_CAUSE_REG); + cause &= MV_CESA_CAUSE_ACC_DMA_ALL_MASK; + + } while (cause == 0); + + /* clear interrupts */ + MV_REG_WRITE(MV_CESA_ISR_CAUSE_REG, 0); + + cesa_callback(0); + + return; +} + +#endif + +/* + * cesa Interrupt polling routine. + */ +static irqreturn_t +cesa_interrupt_handler(int irq, void *arg) +{ + u32 cause; + + dprintk("%s()\n", __FUNCTION__); + + cesaTestTraceAdd(0); + + /* Read cause register */ + cause = MV_REG_READ(MV_CESA_ISR_CAUSE_REG); + + if( (cause & MV_CESA_CAUSE_ACC_DMA_ALL_MASK) == 0) + { + /* Empty interrupt */ + dprintk("%s,%d: cesaTestReadyIsr: cause=0x%x\n", __FILE__, __LINE__, cause); + return IRQ_HANDLED; + } + + /* clear interrupts */ + MV_REG_WRITE(MV_CESA_ISR_CAUSE_REG, 0); + + cesaTestTraceAdd(1); +#ifdef CESA_OCF_TASKLET + tasklet_hi_schedule(&cesa_ocf_tasklet); +#else + cesa_callback(0); +#endif + return IRQ_HANDLED; +} + +/* + * Open a session. + */ +static int +/*cesa_ocf_newsession(void *arg, u_int32_t *sid, struct cryptoini *cri)*/ +cesa_ocf_newsession(device_t dev, u_int32_t *sid, struct cryptoini *cri) +{ + u32 status = 0, i; + u32 count = 0, auth = 0, encrypt =0; + struct cesa_ocf_data *cesa_ocf_cur_ses; + MV_CESA_OPEN_SESSION cesa_session; + MV_CESA_OPEN_SESSION *cesa_ses = &cesa_session; + + + dprintk("%s()\n", __FUNCTION__); + if (sid == NULL || cri == NULL) { + printk("%s,%d: EINVAL\n", __FILE__, __LINE__); + return EINVAL; + } + + /* leave first empty like in other implementations */ + for (i = 1; i < CESA_OCF_MAX_SES; i++) { + if (cesa_ocf_sessions[i] == NULL) + break; + } + + if(i >= CESA_OCF_MAX_SES) { + printk("%s,%d: no more sessions \n", __FILE__, __LINE__); + return EINVAL; + } + + cesa_ocf_sessions[i] = (struct cesa_ocf_data *) kmalloc(sizeof(struct cesa_ocf_data), GFP_ATOMIC); + if (cesa_ocf_sessions[i] == NULL) { + cesa_ocf_freesession(NULL, i); + printk("%s,%d: ENOBUFS \n", __FILE__, __LINE__); + return ENOBUFS; + } + dprintk("%s,%d: new session %d \n", __FILE__, __LINE__, i); + + *sid = i; + cesa_ocf_cur_ses = cesa_ocf_sessions[i]; + memset(cesa_ocf_cur_ses, 0, sizeof(struct cesa_ocf_data)); + cesa_ocf_cur_ses->sid_encrypt = -1; + cesa_ocf_cur_ses->sid_decrypt = -1; + cesa_ocf_cur_ses->frag_wa_encrypt = -1; + cesa_ocf_cur_ses->frag_wa_decrypt = -1; + cesa_ocf_cur_ses->frag_wa_auth = -1; + + /* init the session */ + memset(cesa_ses, 0, sizeof(MV_CESA_OPEN_SESSION)); + count = 1; + while (cri) { + if(count > 2) { + printk("%s,%d: don't support more then 2 operations\n", __FILE__, __LINE__); + goto error; + } + switch (cri->cri_alg) { + case CRYPTO_AES_CBC: + dprintk("%s,%d: (%d) AES CBC \n", __FILE__, __LINE__, count); + cesa_ocf_cur_ses->cipher_alg = cri->cri_alg; + cesa_ocf_cur_ses->ivlen = MV_CESA_AES_BLOCK_SIZE; + cesa_ses->cryptoAlgorithm = MV_CESA_CRYPTO_AES; + cesa_ses->cryptoMode = MV_CESA_CRYPTO_CBC; + if(cri->cri_klen/8 > MV_CESA_MAX_CRYPTO_KEY_LENGTH) { + printk("%s,%d: CRYPTO key too long.\n", __FILE__, __LINE__); + goto error; + } + memcpy(cesa_ses->cryptoKey, cri->cri_key, cri->cri_klen/8); + dprintk("%s,%d: key length %d \n", __FILE__, __LINE__, cri->cri_klen/8); + cesa_ses->cryptoKeyLength = cri->cri_klen/8; + encrypt += count; + break; + case CRYPTO_3DES_CBC: + dprintk("%s,%d: (%d) 3DES CBC \n", __FILE__, __LINE__, count); + cesa_ocf_cur_ses->cipher_alg = cri->cri_alg; + cesa_ocf_cur_ses->ivlen = MV_CESA_3DES_BLOCK_SIZE; + cesa_ses->cryptoAlgorithm = MV_CESA_CRYPTO_3DES; + cesa_ses->cryptoMode = MV_CESA_CRYPTO_CBC; + if(cri->cri_klen/8 > MV_CESA_MAX_CRYPTO_KEY_LENGTH) { + printk("%s,%d: CRYPTO key too long.\n", __FILE__, __LINE__); + goto error; + } + memcpy(cesa_ses->cryptoKey, cri->cri_key, cri->cri_klen/8); + cesa_ses->cryptoKeyLength = cri->cri_klen/8; + encrypt += count; + break; + case CRYPTO_DES_CBC: + dprintk("%s,%d: (%d) DES CBC \n", __FILE__, __LINE__, count); + cesa_ocf_cur_ses->cipher_alg = cri->cri_alg; + cesa_ocf_cur_ses->ivlen = MV_CESA_DES_BLOCK_SIZE; + cesa_ses->cryptoAlgorithm = MV_CESA_CRYPTO_DES; + cesa_ses->cryptoMode = MV_CESA_CRYPTO_CBC; + if(cri->cri_klen/8 > MV_CESA_MAX_CRYPTO_KEY_LENGTH) { + printk("%s,%d: CRYPTO key too long.\n", __FILE__, __LINE__); + goto error; + } + memcpy(cesa_ses->cryptoKey, cri->cri_key, cri->cri_klen/8); + cesa_ses->cryptoKeyLength = cri->cri_klen/8; + encrypt += count; + break; + case CRYPTO_MD5: + case CRYPTO_MD5_HMAC: + dprintk("%s,%d: (%d) %sMD5 CBC \n", __FILE__, __LINE__, count, (cri->cri_alg != CRYPTO_MD5)? "H-":" "); + cesa_ocf_cur_ses->auth_alg = cri->cri_alg; + cesa_ocf_cur_ses->digestlen = (cri->cri_alg == CRYPTO_MD5)? MV_CESA_MD5_DIGEST_SIZE : 12; + cesa_ses->macMode = (cri->cri_alg == CRYPTO_MD5)? MV_CESA_MAC_MD5 : MV_CESA_MAC_HMAC_MD5; + if(cri->cri_klen/8 > MV_CESA_MAX_CRYPTO_KEY_LENGTH) { + printk("%s,%d: MAC key too long. \n", __FILE__, __LINE__); + goto error; + } + cesa_ses->macKeyLength = cri->cri_klen/8; + memcpy(cesa_ses->macKey, cri->cri_key, cri->cri_klen/8); + cesa_ses->digestSize = cesa_ocf_cur_ses->digestlen; + auth += count; + break; + case CRYPTO_SHA1: + case CRYPTO_SHA1_HMAC: + dprintk("%s,%d: (%d) %sSHA1 CBC \n", __FILE__, __LINE__, count, (cri->cri_alg != CRYPTO_SHA1)? "H-":" "); + cesa_ocf_cur_ses->auth_alg = cri->cri_alg; + cesa_ocf_cur_ses->digestlen = (cri->cri_alg == CRYPTO_SHA1)? MV_CESA_SHA1_DIGEST_SIZE : 12; + cesa_ses->macMode = (cri->cri_alg == CRYPTO_SHA1)? MV_CESA_MAC_SHA1 : MV_CESA_MAC_HMAC_SHA1; + if(cri->cri_klen/8 > MV_CESA_MAX_CRYPTO_KEY_LENGTH) { + printk("%s,%d: MAC key too long. \n", __FILE__, __LINE__); + goto error; + } + cesa_ses->macKeyLength = cri->cri_klen/8; + memcpy(cesa_ses->macKey, cri->cri_key, cri->cri_klen/8); + cesa_ses->digestSize = cesa_ocf_cur_ses->digestlen; + auth += count; + break; + default: + printk("%s,%d: unknown algo 0x%x\n", __FILE__, __LINE__, cri->cri_alg); + goto error; + } + cri = cri->cri_next; + count++; + } + + if((encrypt > 2) || (auth > 2)) { + printk("%s,%d: session mode is not supported.\n", __FILE__, __LINE__); + goto error; + } + /* create new sessions in HAL */ + if(encrypt) { + cesa_ses->operation = MV_CESA_CRYPTO_ONLY; + /* encrypt session */ + if(auth == 1) { + cesa_ses->operation = MV_CESA_MAC_THEN_CRYPTO; + } + else if(auth == 2) { + cesa_ses->operation = MV_CESA_CRYPTO_THEN_MAC; + cesa_ocf_cur_ses->encrypt_tn_auth = 1; + } + else { + cesa_ses->operation = MV_CESA_CRYPTO_ONLY; + } + cesa_ses->direction = MV_CESA_DIR_ENCODE; + status = mvCesaSessionOpen(cesa_ses, &cesa_ocf_cur_ses->sid_encrypt); + if(status != MV_OK) { + printk("%s,%d: Can't open new session - status = 0x%x\n", __FILE__, __LINE__, status); + goto error; + } + /* decrypt session */ + if( cesa_ses->operation == MV_CESA_MAC_THEN_CRYPTO ) { + cesa_ses->operation = MV_CESA_CRYPTO_THEN_MAC; + } + else if( cesa_ses->operation == MV_CESA_CRYPTO_THEN_MAC ) { + cesa_ses->operation = MV_CESA_MAC_THEN_CRYPTO; + } + cesa_ses->direction = MV_CESA_DIR_DECODE; + status = mvCesaSessionOpen(cesa_ses, &cesa_ocf_cur_ses->sid_decrypt); + if(status != MV_OK) { + printk("%s,%d: Can't open new session - status = 0x%x\n", __FILE__, __LINE__, status); + goto error; + } + + /* preapre one action sessions for case we will need to split an action */ +#ifdef CESA_OCF_SPLIT + if(( cesa_ses->operation == MV_CESA_MAC_THEN_CRYPTO ) || + ( cesa_ses->operation == MV_CESA_CRYPTO_THEN_MAC )) { + /* open one session for encode and one for decode */ + cesa_ses->operation = MV_CESA_CRYPTO_ONLY; + cesa_ses->direction = MV_CESA_DIR_ENCODE; + status = mvCesaSessionOpen(cesa_ses, &cesa_ocf_cur_ses->frag_wa_encrypt); + if(status != MV_OK) { + printk("%s,%d: Can't open new session - status = 0x%x\n", __FILE__, __LINE__, status); + goto error; + } + + cesa_ses->direction = MV_CESA_DIR_DECODE; + status = mvCesaSessionOpen(cesa_ses, &cesa_ocf_cur_ses->frag_wa_decrypt); + if(status != MV_OK) { + printk("%s,%d: Can't open new session - status = 0x%x\n", __FILE__, __LINE__, status); + goto error; + } + /* open one session for auth */ + cesa_ses->operation = MV_CESA_MAC_ONLY; + cesa_ses->direction = MV_CESA_DIR_ENCODE; + status = mvCesaSessionOpen(cesa_ses, &cesa_ocf_cur_ses->frag_wa_auth); + if(status != MV_OK) { + printk("%s,%d: Can't open new session - status = 0x%x\n", __FILE__, __LINE__, status); + goto error; + } + } +#endif + } + else { /* only auth */ + cesa_ses->operation = MV_CESA_MAC_ONLY; + cesa_ses->direction = MV_CESA_DIR_ENCODE; + status = mvCesaSessionOpen(cesa_ses, &cesa_ocf_cur_ses->sid_encrypt); + if(status != MV_OK) { + printk("%s,%d: Can't open new session - status = 0x%x\n", __FILE__, __LINE__, status); + goto error; + } + } + + return 0; +error: + cesa_ocf_freesession(NULL, *sid); + return EINVAL; + +} + + +/* + * Free a session. + */ +static int +cesa_ocf_freesession(device_t dev, u_int64_t tid) +{ + struct cesa_ocf_data *cesa_ocf_cur_ses; + u_int32_t sid = CRYPTO_SESID2LID(tid); + //unsigned long flags; + + dprintk("%s() %d \n", __FUNCTION__, sid); + if ( (sid >= CESA_OCF_MAX_SES) || (cesa_ocf_sessions[sid] == NULL) ) { + printk("%s,%d: EINVAL can't free session %d \n", __FILE__, __LINE__, sid); + return(EINVAL); + } + + /* Silently accept and return */ + if (sid == 0) + return(0); + + /* release session from HAL */ + cesa_ocf_cur_ses = cesa_ocf_sessions[sid]; + if (cesa_ocf_cur_ses->sid_encrypt != -1) { + mvCesaSessionClose(cesa_ocf_cur_ses->sid_encrypt); + } + if (cesa_ocf_cur_ses->sid_decrypt != -1) { + mvCesaSessionClose(cesa_ocf_cur_ses->sid_decrypt); + } + if (cesa_ocf_cur_ses->frag_wa_encrypt != -1) { + mvCesaSessionClose(cesa_ocf_cur_ses->frag_wa_encrypt); + } + if (cesa_ocf_cur_ses->frag_wa_decrypt != -1) { + mvCesaSessionClose(cesa_ocf_cur_ses->frag_wa_decrypt); + } + if (cesa_ocf_cur_ses->frag_wa_auth != -1) { + mvCesaSessionClose(cesa_ocf_cur_ses->frag_wa_auth); + } + + kfree(cesa_ocf_cur_ses); + cesa_ocf_sessions[sid] = NULL; + + return 0; +} + + +/* TDMA Window setup */ + +static void __init +setup_tdma_mbus_windows(struct cesa_dev *dev) +{ + int i; + + for (i = 0; i < 4; i++) { + writel(0, dev->reg + WINDOW_BASE(i)); + writel(0, dev->reg + WINDOW_CTRL(i)); + } + + for (i = 0; i < dev->plat_data->dram->num_cs; i++) { + struct mbus_dram_window *cs = dev->plat_data->dram->cs + i; + writel( + ((cs->size - 1) & 0xffff0000) | + (cs->mbus_attr << 8) | + (dev->plat_data->dram->mbus_dram_target_id << 4) | 1, + dev->reg + WINDOW_CTRL(i) + ); + writel(cs->base, dev->reg + WINDOW_BASE(i)); + } +} + +/* + * our driver startup and shutdown routines + */ +static int +mv_cesa_ocf_init(struct platform_device *pdev) +{ +#if defined(CONFIG_MV78200) || defined(CONFIG_MV632X) + if (MV_FALSE == mvSocUnitIsMappedToThisCpu(CESA)) + { + dprintk("CESA is not mapped to this CPU\n"); + return -ENODEV; + } +#endif + + dprintk("%s\n", __FUNCTION__); + memset(&mv_cesa_dev, 0, sizeof(mv_cesa_dev)); + softc_device_init(&mv_cesa_dev, "MV CESA", 0, mv_cesa_methods); + cesa_ocf_id = crypto_get_driverid(softc_get_device(&mv_cesa_dev),CRYPTOCAP_F_HARDWARE); + + if (cesa_ocf_id < 0) + panic("MV CESA crypto device cannot initialize!"); + + dprintk("%s,%d: cesa ocf device id is %d \n", __FILE__, __LINE__, cesa_ocf_id); + + /* CESA unit is auto power on off */ +#if 0 + if (MV_FALSE == mvCtrlPwrClckGet(CESA_UNIT_ID,0)) + { + printk("\nWarning CESA %d is Powered Off\n",0); + return EINVAL; + } +#endif + + memset(&cesa_device, 0, sizeof(struct cesa_dev)); + /* Get the IRQ, and crypto memory regions */ + { + struct resource *res; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram"); + + if (!res) + return -ENXIO; + + cesa_device.sram = ioremap(res->start, res->end - res->start + 1); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); + + if (!res) { + iounmap(cesa_device.sram); + return -ENXIO; + } + cesa_device.reg = ioremap(res->start, res->end - res->start + 1); + cesa_device.irq = platform_get_irq(pdev, 0); + cesa_device.plat_data = pdev->dev.platform_data; + setup_tdma_mbus_windows(&cesa_device); + + } + + + if( MV_OK != mvCesaInit(CESA_OCF_MAX_SES*5, CESA_Q_SIZE, cesa_device.reg, + NULL) ) { + printk("%s,%d: mvCesaInit Failed. \n", __FILE__, __LINE__); + return EINVAL; + } + + /* clear and unmask Int */ + MV_REG_WRITE( MV_CESA_ISR_CAUSE_REG, 0); +#ifndef CESA_OCF_POLLING + MV_REG_WRITE( MV_CESA_ISR_MASK_REG, MV_CESA_CAUSE_ACC_DMA_MASK); +#endif +#ifdef CESA_OCF_TASKLET + tasklet_init(&cesa_ocf_tasklet, cesa_callback, (unsigned int) 0); +#endif + /* register interrupt */ + if( request_irq( cesa_device.irq, cesa_interrupt_handler, + 0, "cesa", &cesa_ocf_id) < 0) { + printk("%s,%d: cannot assign irq %x\n", __FILE__, __LINE__, cesa_device.reg); + return EINVAL; + } + + + memset(cesa_ocf_sessions, 0, sizeof(struct cesa_ocf_data *) * CESA_OCF_MAX_SES); + +#define REGISTER(alg) \ + crypto_register(cesa_ocf_id, alg, 0,0) + REGISTER(CRYPTO_AES_CBC); + REGISTER(CRYPTO_DES_CBC); + REGISTER(CRYPTO_3DES_CBC); + REGISTER(CRYPTO_MD5); + REGISTER(CRYPTO_MD5_HMAC); + REGISTER(CRYPTO_SHA1); + REGISTER(CRYPTO_SHA1_HMAC); +#undef REGISTER + + return 0; +} + +static void +mv_cesa_ocf_exit(struct platform_device *pdev) +{ + dprintk("%s()\n", __FUNCTION__); + + crypto_unregister_all(cesa_ocf_id); + cesa_ocf_id = -1; + iounmap(cesa_device.reg); + iounmap(cesa_device.sram); + free_irq(cesa_device.irq, NULL); + + /* mask and clear Int */ + MV_REG_WRITE( MV_CESA_ISR_MASK_REG, 0); + MV_REG_WRITE( MV_CESA_ISR_CAUSE_REG, 0); + + + if( MV_OK != mvCesaFinish() ) { + printk("%s,%d: mvCesaFinish Failed. \n", __FILE__, __LINE__); + return; + } +} + + +void cesa_ocf_debug(void) +{ + +#ifdef CESA_OCF_TRACE_DEBUG + { + int i, j; + j = cesaTestTraceIdx; + mvOsPrintf("No Type rCause iCause Proc Isr Res Time pReady pProc pEmpty\n"); + for(i=0; i