mirror of
git://projects.qi-hardware.com/openwrt-xburst.git
synced 2024-09-19 17:13:50 +03:00
32dec7075a
Fixes problem with TFM allocation in cryptosoft.c Signed-off-by: Philip Prindeville <philipp@redfish-solutions.com> Hauke: * remove ubsec_ssb package and take it from ocf-linux * use patches from ocf-linux package * refresh all patches * readd some build fixes for OpenWrt. * readd CRYPTO_MANAGER dependency git-svn-id: svn://svn.openwrt.org/openwrt/trunk@27753 3c298f89-4303-0410-b956-a3cf2f4a3e73
1298 lines
38 KiB
C
1298 lines
38 KiB
C
/*******************************************************************************
|
|
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 <linux/version.h>
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) && !defined(AUTOCONF_INCLUDED)
|
|
#include <linux/config.h>
|
|
#endif
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/list.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/wait.h>
|
|
#include <linux/crypto.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/random.h>
|
|
#include <linux/platform_device.h>
|
|
#include <asm/scatterlist.h>
|
|
#include <linux/spinlock.h>
|
|
#include "ctrlEnv/sys/mvSysCesa.h"
|
|
#include "cesa/mvCesa.h" /* moved here before cryptodev.h due to include dependencies */
|
|
#include <cryptodev.h>
|
|
#include <uio.h>
|
|
#include <plat/mv_cesa.h>
|
|
#include <linux/mbus.h>
|
|
#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
|
|
|
|
|
|
/* 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_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,
|
|
(IRQF_DISABLED) , "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<MV_CESA_TEST_TRACE_SIZE; i++)
|
|
{
|
|
mvOsPrintf("%02d. %d 0x%04x 0x%04x 0x%02x 0x%02x %02d 0x%06x %p %p %p\n",
|
|
j, cesaTestTrace[j].type, cesaTestTrace[j].realCause,
|
|
cesaTestTrace[j].idmaCause,
|
|
cesaTestTrace[j].resources, cesaTestTrace[j].timeStamp,
|
|
cesaTestTrace[j].pReqReady, cesaTestTrace[j].pReqProcess, cesaTestTrace[j].pReqEmpty);
|
|
j++;
|
|
if(j == MV_CESA_TEST_TRACE_SIZE)
|
|
j = 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
static struct platform_driver marvell_cesa = {
|
|
.probe = mv_cesa_ocf_init,
|
|
.remove = mv_cesa_ocf_exit,
|
|
.driver = {
|
|
.owner = THIS_MODULE,
|
|
.name = "mv_crypto",
|
|
},
|
|
};
|
|
|
|
MODULE_ALIAS("platform:mv_crypto");
|
|
|
|
static int __init mv_cesa_init(void)
|
|
{
|
|
return platform_driver_register(&marvell_cesa);
|
|
}
|
|
|
|
module_init(mv_cesa_init);
|
|
|
|
static void __exit mv_cesa_exit(void)
|
|
{
|
|
platform_driver_unregister(&marvell_cesa);
|
|
}
|
|
|
|
module_exit(mv_cesa_exit);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("Ronen Shitrit");
|
|
MODULE_DESCRIPTION("OCF module for Orion CESA crypto");
|