/******************************************************************************* 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