Path: blob/master/arch/cris/arch-v32/drivers/cryptocop.c
15131 views
/*1* Stream co-processor driver for the ETRAX FS2*3* Copyright (C) 2003-2007 Axis Communications AB4*/56#include <linux/init.h>7#include <linux/sched.h>8#include <linux/module.h>9#include <linux/slab.h>10#include <linux/string.h>11#include <linux/fs.h>12#include <linux/mm.h>13#include <linux/spinlock.h>14#include <linux/stddef.h>1516#include <asm/uaccess.h>17#include <asm/io.h>18#include <asm/atomic.h>1920#include <linux/list.h>21#include <linux/interrupt.h>2223#include <asm/signal.h>24#include <asm/irq.h>2526#include <dma.h>27#include <hwregs/dma.h>28#include <hwregs/reg_map.h>29#include <hwregs/reg_rdwr.h>30#include <hwregs/intr_vect_defs.h>3132#include <hwregs/strcop.h>33#include <hwregs/strcop_defs.h>34#include <cryptocop.h>3536#ifdef CONFIG_ETRAXFS37#define IN_DMA 938#define OUT_DMA 839#define IN_DMA_INST regi_dma940#define OUT_DMA_INST regi_dma841#define DMA_IRQ DMA9_INTR_VECT42#else43#define IN_DMA 344#define OUT_DMA 245#define IN_DMA_INST regi_dma346#define OUT_DMA_INST regi_dma247#define DMA_IRQ DMA3_INTR_VECT48#endif4950#define DESCR_ALLOC_PAD (31)5152struct cryptocop_dma_desc {53char *free_buf; /* If non-null will be kfreed in free_cdesc() */54dma_descr_data *dma_descr;5556unsigned char dma_descr_buf[sizeof(dma_descr_data) + DESCR_ALLOC_PAD];5758unsigned int from_pool:1; /* If 1 'allocated' from the descriptor pool. */59struct cryptocop_dma_desc *next;60};616263struct cryptocop_int_operation{64void *alloc_ptr;65cryptocop_session_id sid;6667dma_descr_context ctx_out;68dma_descr_context ctx_in;6970/* DMA descriptors allocated by driver. */71struct cryptocop_dma_desc *cdesc_out;72struct cryptocop_dma_desc *cdesc_in;7374/* Strcop config to use. */75cryptocop_3des_mode tdes_mode;76cryptocop_csum_type csum_mode;7778/* DMA descrs provided by consumer. */79dma_descr_data *ddesc_out;80dma_descr_data *ddesc_in;81};828384struct cryptocop_tfrm_ctx {85cryptocop_tfrm_id tid;86unsigned int blocklength;8788unsigned int start_ix;8990struct cryptocop_tfrm_cfg *tcfg;91struct cryptocop_transform_ctx *tctx;9293unsigned char previous_src;94unsigned char current_src;9596/* Values to use in metadata out. */97unsigned char hash_conf;98unsigned char hash_mode;99unsigned char ciph_conf;100unsigned char cbcmode;101unsigned char decrypt;102103unsigned int requires_padding:1;104unsigned int strict_block_length:1;105unsigned int active:1;106unsigned int done:1;107size_t consumed;108size_t produced;109110/* Pad (input) descriptors to put in the DMA out list when the transform111* output is put on the DMA in list. */112struct cryptocop_dma_desc *pad_descs;113114struct cryptocop_tfrm_ctx *prev_src;115struct cryptocop_tfrm_ctx *curr_src;116117/* Mapping to HW. */118unsigned char unit_no;119};120121122struct cryptocop_private{123cryptocop_session_id sid;124struct cryptocop_private *next;125};126127/* Session list. */128129struct cryptocop_transform_ctx{130struct cryptocop_transform_init init;131unsigned char dec_key[CRYPTOCOP_MAX_KEY_LENGTH];132unsigned int dec_key_set:1;133134struct cryptocop_transform_ctx *next;135};136137138struct cryptocop_session{139cryptocop_session_id sid;140141struct cryptocop_transform_ctx *tfrm_ctx;142143struct cryptocop_session *next;144};145146/* Priority levels for jobs sent to the cryptocop. Checksum operations from147kernel have highest priority since TCPIP stack processing must not148be a bottleneck. */149typedef enum {150cryptocop_prio_kernel_csum = 0,151cryptocop_prio_kernel = 1,152cryptocop_prio_user = 2,153cryptocop_prio_no_prios = 3154} cryptocop_queue_priority;155156struct cryptocop_prio_queue{157struct list_head jobs;158cryptocop_queue_priority prio;159};160161struct cryptocop_prio_job{162struct list_head node;163cryptocop_queue_priority prio;164165struct cryptocop_operation *oper;166struct cryptocop_int_operation *iop;167};168169struct ioctl_job_cb_ctx {170unsigned int processed:1;171};172173174static struct cryptocop_session *cryptocop_sessions = NULL;175spinlock_t cryptocop_sessions_lock;176177/* Next Session ID to assign. */178static cryptocop_session_id next_sid = 1;179180/* Pad for checksum. */181static const char csum_zero_pad[1] = {0x00};182183/* Trash buffer for mem2mem operations. */184#define MEM2MEM_DISCARD_BUF_LENGTH (512)185static unsigned char mem2mem_discard_buf[MEM2MEM_DISCARD_BUF_LENGTH];186187/* Descriptor pool. */188/* FIXME Tweak this value. */189#define CRYPTOCOP_DESCRIPTOR_POOL_SIZE (100)190static struct cryptocop_dma_desc descr_pool[CRYPTOCOP_DESCRIPTOR_POOL_SIZE];191static struct cryptocop_dma_desc *descr_pool_free_list;192static int descr_pool_no_free;193static spinlock_t descr_pool_lock;194195/* Lock to stop cryptocop to start processing of a new operation. The holder196of this lock MUST call cryptocop_start_job() after it is unlocked. */197spinlock_t cryptocop_process_lock;198199static struct cryptocop_prio_queue cryptocop_job_queues[cryptocop_prio_no_prios];200static spinlock_t cryptocop_job_queue_lock;201static struct cryptocop_prio_job *cryptocop_running_job = NULL;202static spinlock_t running_job_lock;203204/* The interrupt handler appends completed jobs to this list. The scehduled205* tasklet removes them upon sending the response to the crypto consumer. */206static struct list_head cryptocop_completed_jobs;207static spinlock_t cryptocop_completed_jobs_lock;208209DECLARE_WAIT_QUEUE_HEAD(cryptocop_ioc_process_wq);210211212/** Local functions. **/213214static int cryptocop_open(struct inode *, struct file *);215216static int cryptocop_release(struct inode *, struct file *);217218static long cryptocop_ioctl(struct file *file,219unsigned int cmd, unsigned long arg);220221static void cryptocop_start_job(void);222223static int cryptocop_job_queue_insert(cryptocop_queue_priority prio, struct cryptocop_operation *operation);224static int cryptocop_job_setup(struct cryptocop_prio_job **pj, struct cryptocop_operation *operation);225226static int cryptocop_job_queue_init(void);227static void cryptocop_job_queue_close(void);228229static int create_md5_pad(int alloc_flag, unsigned long long hashed_length, char **pad, size_t *pad_length);230231static int create_sha1_pad(int alloc_flag, unsigned long long hashed_length, char **pad, size_t *pad_length);232233static int transform_ok(struct cryptocop_transform_init *tinit);234235static struct cryptocop_session *get_session(cryptocop_session_id sid);236237static struct cryptocop_transform_ctx *get_transform_ctx(struct cryptocop_session *sess, cryptocop_tfrm_id tid);238239static void delete_internal_operation(struct cryptocop_int_operation *iop);240241static void get_aes_decrypt_key(unsigned char *dec_key, const unsigned char *key, unsigned int keylength);242243static int init_stream_coprocessor(void);244245static void __exit exit_stream_coprocessor(void);246247/*#define LDEBUG*/248#ifdef LDEBUG249#define DEBUG(s) s250#define DEBUG_API(s) s251static void print_cryptocop_operation(struct cryptocop_operation *cop);252static void print_dma_descriptors(struct cryptocop_int_operation *iop);253static void print_strcop_crypto_op(struct strcop_crypto_op *cop);254static void print_lock_status(void);255static void print_user_dma_lists(struct cryptocop_dma_list_operation *dma_op);256#define assert(s) do{if (!(s)) panic(#s);} while(0);257#else258#define DEBUG(s)259#define DEBUG_API(s)260#define assert(s)261#endif262263264/* Transform constants. */265#define DES_BLOCK_LENGTH (8)266#define AES_BLOCK_LENGTH (16)267#define MD5_BLOCK_LENGTH (64)268#define SHA1_BLOCK_LENGTH (64)269#define CSUM_BLOCK_LENGTH (2)270#define MD5_STATE_LENGTH (16)271#define SHA1_STATE_LENGTH (20)272273/* The device number. */274#define CRYPTOCOP_MAJOR (254)275#define CRYPTOCOP_MINOR (0)276277278279const struct file_operations cryptocop_fops = {280.owner = THIS_MODULE,281.open = cryptocop_open,282.release = cryptocop_release,283.unlocked_ioctl = cryptocop_ioctl,284.llseek = noop_llseek,285};286287288static void free_cdesc(struct cryptocop_dma_desc *cdesc)289{290DEBUG(printk("free_cdesc: cdesc 0x%p, from_pool=%d\n", cdesc, cdesc->from_pool));291kfree(cdesc->free_buf);292293if (cdesc->from_pool) {294unsigned long int flags;295spin_lock_irqsave(&descr_pool_lock, flags);296cdesc->next = descr_pool_free_list;297descr_pool_free_list = cdesc;298++descr_pool_no_free;299spin_unlock_irqrestore(&descr_pool_lock, flags);300} else {301kfree(cdesc);302}303}304305306static struct cryptocop_dma_desc *alloc_cdesc(int alloc_flag)307{308int use_pool = (alloc_flag & GFP_ATOMIC) ? 1 : 0;309struct cryptocop_dma_desc *cdesc;310311if (use_pool) {312unsigned long int flags;313spin_lock_irqsave(&descr_pool_lock, flags);314if (!descr_pool_free_list) {315spin_unlock_irqrestore(&descr_pool_lock, flags);316DEBUG_API(printk("alloc_cdesc: pool is empty\n"));317return NULL;318}319cdesc = descr_pool_free_list;320descr_pool_free_list = descr_pool_free_list->next;321--descr_pool_no_free;322spin_unlock_irqrestore(&descr_pool_lock, flags);323cdesc->from_pool = 1;324} else {325cdesc = kmalloc(sizeof(struct cryptocop_dma_desc), alloc_flag);326if (!cdesc) {327DEBUG_API(printk("alloc_cdesc: kmalloc\n"));328return NULL;329}330cdesc->from_pool = 0;331}332cdesc->dma_descr = (dma_descr_data*)(((unsigned long int)cdesc + offsetof(struct cryptocop_dma_desc, dma_descr_buf) + DESCR_ALLOC_PAD) & ~0x0000001F);333334cdesc->next = NULL;335336cdesc->free_buf = NULL;337cdesc->dma_descr->out_eop = 0;338cdesc->dma_descr->in_eop = 0;339cdesc->dma_descr->intr = 0;340cdesc->dma_descr->eol = 0;341cdesc->dma_descr->wait = 0;342cdesc->dma_descr->buf = NULL;343cdesc->dma_descr->after = NULL;344345DEBUG_API(printk("alloc_cdesc: return 0x%p, cdesc->dma_descr=0x%p, from_pool=%d\n", cdesc, cdesc->dma_descr, cdesc->from_pool));346return cdesc;347}348349350static void setup_descr_chain(struct cryptocop_dma_desc *cd)351{352DEBUG(printk("setup_descr_chain: entering\n"));353while (cd) {354if (cd->next) {355cd->dma_descr->next = (dma_descr_data*)virt_to_phys(cd->next->dma_descr);356} else {357cd->dma_descr->next = NULL;358}359cd = cd->next;360}361DEBUG(printk("setup_descr_chain: exit\n"));362}363364365/* Create a pad descriptor for the transform.366* Return -1 for error, 0 if pad created. */367static int create_pad_descriptor(struct cryptocop_tfrm_ctx *tc, struct cryptocop_dma_desc **pad_desc, int alloc_flag)368{369struct cryptocop_dma_desc *cdesc = NULL;370int error = 0;371struct strcop_meta_out mo = {372.ciphsel = src_none,373.hashsel = src_none,374.csumsel = src_none375};376char *pad;377size_t plen;378379DEBUG(printk("create_pad_descriptor: start.\n"));380/* Setup pad descriptor. */381382DEBUG(printk("create_pad_descriptor: setting up padding.\n"));383cdesc = alloc_cdesc(alloc_flag);384if (!cdesc){385DEBUG_API(printk("create_pad_descriptor: alloc pad desc\n"));386goto error_cleanup;387}388switch (tc->unit_no) {389case src_md5:390error = create_md5_pad(alloc_flag, tc->consumed, &pad, &plen);391if (error){392DEBUG_API(printk("create_pad_descriptor: create_md5_pad_failed\n"));393goto error_cleanup;394}395cdesc->free_buf = pad;396mo.hashsel = src_dma;397mo.hashconf = tc->hash_conf;398mo.hashmode = tc->hash_mode;399break;400case src_sha1:401error = create_sha1_pad(alloc_flag, tc->consumed, &pad, &plen);402if (error){403DEBUG_API(printk("create_pad_descriptor: create_sha1_pad_failed\n"));404goto error_cleanup;405}406cdesc->free_buf = pad;407mo.hashsel = src_dma;408mo.hashconf = tc->hash_conf;409mo.hashmode = tc->hash_mode;410break;411case src_csum:412if (tc->consumed % tc->blocklength){413pad = (char*)csum_zero_pad;414plen = 1;415} else {416pad = (char*)cdesc; /* Use any pointer. */417plen = 0;418}419mo.csumsel = src_dma;420break;421}422cdesc->dma_descr->wait = 1;423cdesc->dma_descr->out_eop = 1; /* Since this is a pad output is pushed. EOP is ok here since the padded unit is the only one active. */424cdesc->dma_descr->buf = (char*)virt_to_phys((char*)pad);425cdesc->dma_descr->after = cdesc->dma_descr->buf + plen;426427cdesc->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_out, mo);428*pad_desc = cdesc;429430return 0;431432error_cleanup:433if (cdesc) free_cdesc(cdesc);434return -1;435}436437438static int setup_key_dl_desc(struct cryptocop_tfrm_ctx *tc, struct cryptocop_dma_desc **kd, int alloc_flag)439{440struct cryptocop_dma_desc *key_desc = alloc_cdesc(alloc_flag);441struct strcop_meta_out mo = {0};442443DEBUG(printk("setup_key_dl_desc\n"));444445if (!key_desc) {446DEBUG_API(printk("setup_key_dl_desc: failed descriptor allocation.\n"));447return -ENOMEM;448}449450/* Download key. */451if ((tc->tctx->init.alg == cryptocop_alg_aes) && (tc->tcfg->flags & CRYPTOCOP_DECRYPT)) {452/* Precook the AES decrypt key. */453if (!tc->tctx->dec_key_set){454get_aes_decrypt_key(tc->tctx->dec_key, tc->tctx->init.key, tc->tctx->init.keylen);455tc->tctx->dec_key_set = 1;456}457key_desc->dma_descr->buf = (char*)virt_to_phys(tc->tctx->dec_key);458key_desc->dma_descr->after = key_desc->dma_descr->buf + tc->tctx->init.keylen/8;459} else {460key_desc->dma_descr->buf = (char*)virt_to_phys(tc->tctx->init.key);461key_desc->dma_descr->after = key_desc->dma_descr->buf + tc->tctx->init.keylen/8;462}463/* Setup metadata. */464mo.dlkey = 1;465switch (tc->tctx->init.keylen) {466case 64:467mo.decrypt = 0;468mo.hashmode = 0;469break;470case 128:471mo.decrypt = 0;472mo.hashmode = 1;473break;474case 192:475mo.decrypt = 1;476mo.hashmode = 0;477break;478case 256:479mo.decrypt = 1;480mo.hashmode = 1;481break;482default:483break;484}485mo.ciphsel = mo.hashsel = mo.csumsel = src_none;486key_desc->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_out, mo);487488key_desc->dma_descr->out_eop = 1;489key_desc->dma_descr->wait = 1;490key_desc->dma_descr->intr = 0;491492*kd = key_desc;493return 0;494}495496static int setup_cipher_iv_desc(struct cryptocop_tfrm_ctx *tc, struct cryptocop_dma_desc **id, int alloc_flag)497{498struct cryptocop_dma_desc *iv_desc = alloc_cdesc(alloc_flag);499struct strcop_meta_out mo = {0};500501DEBUG(printk("setup_cipher_iv_desc\n"));502503if (!iv_desc) {504DEBUG_API(printk("setup_cipher_iv_desc: failed CBC IV descriptor allocation.\n"));505return -ENOMEM;506}507/* Download IV. */508iv_desc->dma_descr->buf = (char*)virt_to_phys(tc->tcfg->iv);509iv_desc->dma_descr->after = iv_desc->dma_descr->buf + tc->blocklength;510511/* Setup metadata. */512mo.hashsel = mo.csumsel = src_none;513mo.ciphsel = src_dma;514mo.ciphconf = tc->ciph_conf;515mo.cbcmode = tc->cbcmode;516517iv_desc->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_out, mo);518519iv_desc->dma_descr->out_eop = 0;520iv_desc->dma_descr->wait = 1;521iv_desc->dma_descr->intr = 0;522523*id = iv_desc;524return 0;525}526527/* Map the ouput length of the transform to operation output starting on the inject index. */528static int create_input_descriptors(struct cryptocop_operation *operation, struct cryptocop_tfrm_ctx *tc, struct cryptocop_dma_desc **id, int alloc_flag)529{530int err = 0;531struct cryptocop_dma_desc head = {0};532struct cryptocop_dma_desc *outdesc = &head;533size_t iov_offset = 0;534size_t out_ix = 0;535int outiov_ix = 0;536struct strcop_meta_in mi = {0};537538size_t out_length = tc->produced;539int rem_length;540int dlength;541542assert(out_length != 0);543if (((tc->produced + tc->tcfg->inject_ix) > operation->tfrm_op.outlen) || (tc->produced && (operation->tfrm_op.outlen == 0))) {544DEBUG_API(printk("create_input_descriptors: operation outdata too small\n"));545return -EINVAL;546}547/* Traverse the out iovec until the result inject index is reached. */548while ((outiov_ix < operation->tfrm_op.outcount) && ((out_ix + operation->tfrm_op.outdata[outiov_ix].iov_len) <= tc->tcfg->inject_ix)){549out_ix += operation->tfrm_op.outdata[outiov_ix].iov_len;550outiov_ix++;551}552if (outiov_ix >= operation->tfrm_op.outcount){553DEBUG_API(printk("create_input_descriptors: operation outdata too small\n"));554return -EINVAL;555}556iov_offset = tc->tcfg->inject_ix - out_ix;557mi.dmasel = tc->unit_no;558559/* Setup the output descriptors. */560while ((out_length > 0) && (outiov_ix < operation->tfrm_op.outcount)) {561outdesc->next = alloc_cdesc(alloc_flag);562if (!outdesc->next) {563DEBUG_API(printk("create_input_descriptors: alloc_cdesc\n"));564err = -ENOMEM;565goto error_cleanup;566}567outdesc = outdesc->next;568rem_length = operation->tfrm_op.outdata[outiov_ix].iov_len - iov_offset;569dlength = (out_length < rem_length) ? out_length : rem_length;570571DEBUG(printk("create_input_descriptors:\n"572"outiov_ix=%d, rem_length=%d, dlength=%d\n"573"iov_offset=%d, outdata[outiov_ix].iov_len=%d\n"574"outcount=%d, outiov_ix=%d\n",575outiov_ix, rem_length, dlength, iov_offset, operation->tfrm_op.outdata[outiov_ix].iov_len, operation->tfrm_op.outcount, outiov_ix));576577outdesc->dma_descr->buf = (char*)virt_to_phys(operation->tfrm_op.outdata[outiov_ix].iov_base + iov_offset);578outdesc->dma_descr->after = outdesc->dma_descr->buf + dlength;579outdesc->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_in, mi);580581out_length -= dlength;582iov_offset += dlength;583if (iov_offset >= operation->tfrm_op.outdata[outiov_ix].iov_len) {584iov_offset = 0;585++outiov_ix;586}587}588if (out_length > 0){589DEBUG_API(printk("create_input_descriptors: not enough room for output, %d remained\n", out_length));590err = -EINVAL;591goto error_cleanup;592}593/* Set sync in last descriptor. */594mi.sync = 1;595outdesc->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_in, mi);596597*id = head.next;598return 0;599600error_cleanup:601while (head.next) {602outdesc = head.next->next;603free_cdesc(head.next);604head.next = outdesc;605}606return err;607}608609610static int create_output_descriptors(struct cryptocop_operation *operation, int *iniov_ix, int *iniov_offset, size_t desc_len, struct cryptocop_dma_desc **current_out_cdesc, struct strcop_meta_out *meta_out, int alloc_flag)611{612while (desc_len != 0) {613struct cryptocop_dma_desc *cdesc;614int rem_length = operation->tfrm_op.indata[*iniov_ix].iov_len - *iniov_offset;615int dlength = (desc_len < rem_length) ? desc_len : rem_length;616617cdesc = alloc_cdesc(alloc_flag);618if (!cdesc) {619DEBUG_API(printk("create_output_descriptors: alloc_cdesc\n"));620return -ENOMEM;621}622(*current_out_cdesc)->next = cdesc;623(*current_out_cdesc) = cdesc;624625cdesc->free_buf = NULL;626627cdesc->dma_descr->buf = (char*)virt_to_phys(operation->tfrm_op.indata[*iniov_ix].iov_base + *iniov_offset);628cdesc->dma_descr->after = cdesc->dma_descr->buf + dlength;629630assert(desc_len >= dlength);631desc_len -= dlength;632*iniov_offset += dlength;633if (*iniov_offset >= operation->tfrm_op.indata[*iniov_ix].iov_len) {634*iniov_offset = 0;635++(*iniov_ix);636if (*iniov_ix > operation->tfrm_op.incount) {637DEBUG_API(printk("create_output_descriptors: not enough indata in operation."));638return -EINVAL;639}640}641cdesc->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_out, (*meta_out));642} /* while (desc_len != 0) */643/* Last DMA descriptor gets a 'wait' bit to signal expected change in metadata. */644(*current_out_cdesc)->dma_descr->wait = 1; /* This will set extraneous WAIT in some situations, e.g. when padding hashes and checksums. */645646return 0;647}648649650static int append_input_descriptors(struct cryptocop_operation *operation, struct cryptocop_dma_desc **current_in_cdesc, struct cryptocop_dma_desc **current_out_cdesc, struct cryptocop_tfrm_ctx *tc, int alloc_flag)651{652DEBUG(printk("append_input_descriptors, tc=0x%p, unit_no=%d\n", tc, tc->unit_no));653if (tc->tcfg) {654int failed = 0;655struct cryptocop_dma_desc *idescs = NULL;656DEBUG(printk("append_input_descriptors: pushing output, consumed %d produced %d bytes.\n", tc->consumed, tc->produced));657if (tc->pad_descs) {658DEBUG(printk("append_input_descriptors: append pad descriptors to DMA out list.\n"));659while (tc->pad_descs) {660DEBUG(printk("append descriptor 0x%p\n", tc->pad_descs));661(*current_out_cdesc)->next = tc->pad_descs;662tc->pad_descs = tc->pad_descs->next;663(*current_out_cdesc) = (*current_out_cdesc)->next;664}665}666667/* Setup and append output descriptors to DMA in list. */668if (tc->unit_no == src_dma){669/* mem2mem. Setup DMA in descriptors to discard all input prior to the requested mem2mem data. */670struct strcop_meta_in mi = {.sync = 0, .dmasel = src_dma};671unsigned int start_ix = tc->start_ix;672while (start_ix){673unsigned int desclen = start_ix < MEM2MEM_DISCARD_BUF_LENGTH ? start_ix : MEM2MEM_DISCARD_BUF_LENGTH;674(*current_in_cdesc)->next = alloc_cdesc(alloc_flag);675if (!(*current_in_cdesc)->next){676DEBUG_API(printk("append_input_descriptors: alloc_cdesc mem2mem discard failed\n"));677return -ENOMEM;678}679(*current_in_cdesc) = (*current_in_cdesc)->next;680(*current_in_cdesc)->dma_descr->buf = (char*)virt_to_phys(mem2mem_discard_buf);681(*current_in_cdesc)->dma_descr->after = (*current_in_cdesc)->dma_descr->buf + desclen;682(*current_in_cdesc)->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_in, mi);683start_ix -= desclen;684}685mi.sync = 1;686(*current_in_cdesc)->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_in, mi);687}688689failed = create_input_descriptors(operation, tc, &idescs, alloc_flag);690if (failed){691DEBUG_API(printk("append_input_descriptors: output descriptor setup failed\n"));692return failed;693}694DEBUG(printk("append_input_descriptors: append output descriptors to DMA in list.\n"));695while (idescs) {696DEBUG(printk("append descriptor 0x%p\n", idescs));697(*current_in_cdesc)->next = idescs;698idescs = idescs->next;699(*current_in_cdesc) = (*current_in_cdesc)->next;700}701}702return 0;703}704705706707static int cryptocop_setup_dma_list(struct cryptocop_operation *operation, struct cryptocop_int_operation **int_op, int alloc_flag)708{709struct cryptocop_session *sess;710struct cryptocop_transform_ctx *tctx;711712struct cryptocop_tfrm_ctx digest_ctx = {713.previous_src = src_none,714.current_src = src_none,715.start_ix = 0,716.requires_padding = 1,717.strict_block_length = 0,718.hash_conf = 0,719.hash_mode = 0,720.ciph_conf = 0,721.cbcmode = 0,722.decrypt = 0,723.consumed = 0,724.produced = 0,725.pad_descs = NULL,726.active = 0,727.done = 0,728.prev_src = NULL,729.curr_src = NULL,730.tcfg = NULL};731struct cryptocop_tfrm_ctx cipher_ctx = {732.previous_src = src_none,733.current_src = src_none,734.start_ix = 0,735.requires_padding = 0,736.strict_block_length = 1,737.hash_conf = 0,738.hash_mode = 0,739.ciph_conf = 0,740.cbcmode = 0,741.decrypt = 0,742.consumed = 0,743.produced = 0,744.pad_descs = NULL,745.active = 0,746.done = 0,747.prev_src = NULL,748.curr_src = NULL,749.tcfg = NULL};750struct cryptocop_tfrm_ctx csum_ctx = {751.previous_src = src_none,752.current_src = src_none,753.start_ix = 0,754.blocklength = 2,755.requires_padding = 1,756.strict_block_length = 0,757.hash_conf = 0,758.hash_mode = 0,759.ciph_conf = 0,760.cbcmode = 0,761.decrypt = 0,762.consumed = 0,763.produced = 0,764.pad_descs = NULL,765.active = 0,766.done = 0,767.tcfg = NULL,768.prev_src = NULL,769.curr_src = NULL,770.unit_no = src_csum};771struct cryptocop_tfrm_cfg *tcfg = operation->tfrm_op.tfrm_cfg;772773unsigned int indata_ix = 0;774775/* iovec accounting. */776int iniov_ix = 0;777int iniov_offset = 0;778779/* Operation descriptor cfg traversal pointer. */780struct cryptocop_desc *odsc;781782int failed = 0;783/* List heads for allocated descriptors. */784struct cryptocop_dma_desc out_cdesc_head = {0};785struct cryptocop_dma_desc in_cdesc_head = {0};786787struct cryptocop_dma_desc *current_out_cdesc = &out_cdesc_head;788struct cryptocop_dma_desc *current_in_cdesc = &in_cdesc_head;789790struct cryptocop_tfrm_ctx *output_tc = NULL;791void *iop_alloc_ptr;792793assert(operation != NULL);794assert(int_op != NULL);795796DEBUG(printk("cryptocop_setup_dma_list: start\n"));797DEBUG(print_cryptocop_operation(operation));798799sess = get_session(operation->sid);800if (!sess) {801DEBUG_API(printk("cryptocop_setup_dma_list: no session found for operation.\n"));802failed = -EINVAL;803goto error_cleanup;804}805iop_alloc_ptr = kmalloc(DESCR_ALLOC_PAD + sizeof(struct cryptocop_int_operation), alloc_flag);806if (!iop_alloc_ptr) {807DEBUG_API(printk("cryptocop_setup_dma_list: kmalloc cryptocop_int_operation\n"));808failed = -ENOMEM;809goto error_cleanup;810}811(*int_op) = (struct cryptocop_int_operation*)(((unsigned long int)(iop_alloc_ptr + DESCR_ALLOC_PAD + offsetof(struct cryptocop_int_operation, ctx_out)) & ~0x0000001F) - offsetof(struct cryptocop_int_operation, ctx_out));812DEBUG(memset((*int_op), 0xff, sizeof(struct cryptocop_int_operation)));813(*int_op)->alloc_ptr = iop_alloc_ptr;814DEBUG(printk("cryptocop_setup_dma_list: *int_op=0x%p, alloc_ptr=0x%p\n", *int_op, (*int_op)->alloc_ptr));815816(*int_op)->sid = operation->sid;817(*int_op)->cdesc_out = NULL;818(*int_op)->cdesc_in = NULL;819(*int_op)->tdes_mode = cryptocop_3des_ede;820(*int_op)->csum_mode = cryptocop_csum_le;821(*int_op)->ddesc_out = NULL;822(*int_op)->ddesc_in = NULL;823824/* Scan operation->tfrm_op.tfrm_cfg for bad configuration and set up the local contexts. */825if (!tcfg) {826DEBUG_API(printk("cryptocop_setup_dma_list: no configured transforms in operation.\n"));827failed = -EINVAL;828goto error_cleanup;829}830while (tcfg) {831tctx = get_transform_ctx(sess, tcfg->tid);832if (!tctx) {833DEBUG_API(printk("cryptocop_setup_dma_list: no transform id %d in session.\n", tcfg->tid));834failed = -EINVAL;835goto error_cleanup;836}837if (tcfg->inject_ix > operation->tfrm_op.outlen){838DEBUG_API(printk("cryptocop_setup_dma_list: transform id %d inject_ix (%d) > operation->tfrm_op.outlen(%d)", tcfg->tid, tcfg->inject_ix, operation->tfrm_op.outlen));839failed = -EINVAL;840goto error_cleanup;841}842switch (tctx->init.alg){843case cryptocop_alg_mem2mem:844if (cipher_ctx.tcfg != NULL){845DEBUG_API(printk("cryptocop_setup_dma_list: multiple ciphers in operation.\n"));846failed = -EINVAL;847goto error_cleanup;848}849/* mem2mem is handled as a NULL cipher. */850cipher_ctx.cbcmode = 0;851cipher_ctx.decrypt = 0;852cipher_ctx.blocklength = 1;853cipher_ctx.ciph_conf = 0;854cipher_ctx.unit_no = src_dma;855cipher_ctx.tcfg = tcfg;856cipher_ctx.tctx = tctx;857break;858case cryptocop_alg_des:859case cryptocop_alg_3des:860case cryptocop_alg_aes:861/* cipher */862if (cipher_ctx.tcfg != NULL){863DEBUG_API(printk("cryptocop_setup_dma_list: multiple ciphers in operation.\n"));864failed = -EINVAL;865goto error_cleanup;866}867cipher_ctx.tcfg = tcfg;868cipher_ctx.tctx = tctx;869if (cipher_ctx.tcfg->flags & CRYPTOCOP_DECRYPT){870cipher_ctx.decrypt = 1;871}872switch (tctx->init.cipher_mode) {873case cryptocop_cipher_mode_ecb:874cipher_ctx.cbcmode = 0;875break;876case cryptocop_cipher_mode_cbc:877cipher_ctx.cbcmode = 1;878break;879default:880DEBUG_API(printk("cryptocop_setup_dma_list: cipher_ctx, bad cipher mode==%d\n", tctx->init.cipher_mode));881failed = -EINVAL;882goto error_cleanup;883}884DEBUG(printk("cryptocop_setup_dma_list: cipher_ctx, set CBC mode==%d\n", cipher_ctx.cbcmode));885switch (tctx->init.alg){886case cryptocop_alg_des:887cipher_ctx.ciph_conf = 0;888cipher_ctx.unit_no = src_des;889cipher_ctx.blocklength = DES_BLOCK_LENGTH;890break;891case cryptocop_alg_3des:892cipher_ctx.ciph_conf = 1;893cipher_ctx.unit_no = src_des;894cipher_ctx.blocklength = DES_BLOCK_LENGTH;895break;896case cryptocop_alg_aes:897cipher_ctx.ciph_conf = 2;898cipher_ctx.unit_no = src_aes;899cipher_ctx.blocklength = AES_BLOCK_LENGTH;900break;901default:902panic("cryptocop_setup_dma_list: impossible algorithm %d\n", tctx->init.alg);903}904(*int_op)->tdes_mode = tctx->init.tdes_mode;905break;906case cryptocop_alg_md5:907case cryptocop_alg_sha1:908/* digest */909if (digest_ctx.tcfg != NULL){910DEBUG_API(printk("cryptocop_setup_dma_list: multiple digests in operation.\n"));911failed = -EINVAL;912goto error_cleanup;913}914digest_ctx.tcfg = tcfg;915digest_ctx.tctx = tctx;916digest_ctx.hash_mode = 0; /* Don't use explicit IV in this API. */917switch (tctx->init.alg){918case cryptocop_alg_md5:919digest_ctx.blocklength = MD5_BLOCK_LENGTH;920digest_ctx.unit_no = src_md5;921digest_ctx.hash_conf = 1; /* 1 => MD-5 */922break;923case cryptocop_alg_sha1:924digest_ctx.blocklength = SHA1_BLOCK_LENGTH;925digest_ctx.unit_no = src_sha1;926digest_ctx.hash_conf = 0; /* 0 => SHA-1 */927break;928default:929panic("cryptocop_setup_dma_list: impossible digest algorithm\n");930}931break;932case cryptocop_alg_csum:933/* digest */934if (csum_ctx.tcfg != NULL){935DEBUG_API(printk("cryptocop_setup_dma_list: multiple checksums in operation.\n"));936failed = -EINVAL;937goto error_cleanup;938}939(*int_op)->csum_mode = tctx->init.csum_mode;940csum_ctx.tcfg = tcfg;941csum_ctx.tctx = tctx;942break;943default:944/* no algorithm. */945DEBUG_API(printk("cryptocop_setup_dma_list: invalid algorithm %d specified in tfrm %d.\n", tctx->init.alg, tcfg->tid));946failed = -EINVAL;947goto error_cleanup;948}949tcfg = tcfg->next;950}951/* Download key if a cipher is used. */952if (cipher_ctx.tcfg && (cipher_ctx.tctx->init.alg != cryptocop_alg_mem2mem)){953struct cryptocop_dma_desc *key_desc = NULL;954955failed = setup_key_dl_desc(&cipher_ctx, &key_desc, alloc_flag);956if (failed) {957DEBUG_API(printk("cryptocop_setup_dma_list: setup key dl\n"));958goto error_cleanup;959}960current_out_cdesc->next = key_desc;961current_out_cdesc = key_desc;962indata_ix += (unsigned int)(key_desc->dma_descr->after - key_desc->dma_descr->buf);963964/* Download explicit IV if a cipher is used and CBC mode and explicit IV selected. */965if ((cipher_ctx.tctx->init.cipher_mode == cryptocop_cipher_mode_cbc) && (cipher_ctx.tcfg->flags & CRYPTOCOP_EXPLICIT_IV)) {966struct cryptocop_dma_desc *iv_desc = NULL;967968DEBUG(printk("cryptocop_setup_dma_list: setup cipher CBC IV descriptor.\n"));969970failed = setup_cipher_iv_desc(&cipher_ctx, &iv_desc, alloc_flag);971if (failed) {972DEBUG_API(printk("cryptocop_setup_dma_list: CBC IV descriptor.\n"));973goto error_cleanup;974}975current_out_cdesc->next = iv_desc;976current_out_cdesc = iv_desc;977indata_ix += (unsigned int)(iv_desc->dma_descr->after - iv_desc->dma_descr->buf);978}979}980981/* Process descriptors. */982odsc = operation->tfrm_op.desc;983while (odsc) {984struct cryptocop_desc_cfg *dcfg = odsc->cfg;985struct strcop_meta_out meta_out = {0};986size_t desc_len = odsc->length;987int active_count, eop_needed_count;988989output_tc = NULL;990991DEBUG(printk("cryptocop_setup_dma_list: parsing an operation descriptor\n"));992993while (dcfg) {994struct cryptocop_tfrm_ctx *tc = NULL;995996DEBUG(printk("cryptocop_setup_dma_list: parsing an operation descriptor configuration.\n"));997/* Get the local context for the transform and mark it as the output unit if it produces output. */998if (digest_ctx.tcfg && (digest_ctx.tcfg->tid == dcfg->tid)){999tc = &digest_ctx;1000} else if (cipher_ctx.tcfg && (cipher_ctx.tcfg->tid == dcfg->tid)){1001tc = &cipher_ctx;1002} else if (csum_ctx.tcfg && (csum_ctx.tcfg->tid == dcfg->tid)){1003tc = &csum_ctx;1004}1005if (!tc) {1006DEBUG_API(printk("cryptocop_setup_dma_list: invalid transform %d specified in descriptor.\n", dcfg->tid));1007failed = -EINVAL;1008goto error_cleanup;1009}1010if (tc->done) {1011DEBUG_API(printk("cryptocop_setup_dma_list: completed transform %d reused.\n", dcfg->tid));1012failed = -EINVAL;1013goto error_cleanup;1014}1015if (!tc->active) {1016tc->start_ix = indata_ix;1017tc->active = 1;1018}10191020tc->previous_src = tc->current_src;1021tc->prev_src = tc->curr_src;1022/* Map source unit id to DMA source config. */1023switch (dcfg->src){1024case cryptocop_source_dma:1025tc->current_src = src_dma;1026break;1027case cryptocop_source_des:1028tc->current_src = src_des;1029break;1030case cryptocop_source_3des:1031tc->current_src = src_des;1032break;1033case cryptocop_source_aes:1034tc->current_src = src_aes;1035break;1036case cryptocop_source_md5:1037case cryptocop_source_sha1:1038case cryptocop_source_csum:1039case cryptocop_source_none:1040default:1041/* We do not allow using accumulating style units (SHA-1, MD5, checksum) as sources to other units.1042*/1043DEBUG_API(printk("cryptocop_setup_dma_list: bad unit source configured %d.\n", dcfg->src));1044failed = -EINVAL;1045goto error_cleanup;1046}1047if (tc->current_src != src_dma) {1048/* Find the unit we are sourcing from. */1049if (digest_ctx.unit_no == tc->current_src){1050tc->curr_src = &digest_ctx;1051} else if (cipher_ctx.unit_no == tc->current_src){1052tc->curr_src = &cipher_ctx;1053} else if (csum_ctx.unit_no == tc->current_src){1054tc->curr_src = &csum_ctx;1055}1056if ((tc->curr_src == tc) && (tc->unit_no != src_dma)){1057DEBUG_API(printk("cryptocop_setup_dma_list: unit %d configured to source from itself.\n", tc->unit_no));1058failed = -EINVAL;1059goto error_cleanup;1060}1061} else {1062tc->curr_src = NULL;1063}10641065/* Detect source switch. */1066DEBUG(printk("cryptocop_setup_dma_list: tc->active=%d tc->unit_no=%d tc->current_src=%d tc->previous_src=%d, tc->curr_src=0x%p, tc->prev_srv=0x%p\n", tc->active, tc->unit_no, tc->current_src, tc->previous_src, tc->curr_src, tc->prev_src));1067if (tc->active && (tc->current_src != tc->previous_src)) {1068/* Only allow source switch when both the old source unit and the new one have1069* no pending data to process (i.e. the consumed length must be a multiple of the1070* transform blocklength). */1071/* Note: if the src == NULL we are actually sourcing from DMA out. */1072if (((tc->prev_src != NULL) && (tc->prev_src->consumed % tc->prev_src->blocklength)) ||1073((tc->curr_src != NULL) && (tc->curr_src->consumed % tc->curr_src->blocklength)))1074{1075DEBUG_API(printk("cryptocop_setup_dma_list: can only disconnect from or connect to a unit on a multiple of the blocklength, old: cons=%d, prod=%d, block=%d, new: cons=%d prod=%d, block=%d.\n", tc->prev_src ? tc->prev_src->consumed : INT_MIN, tc->prev_src ? tc->prev_src->produced : INT_MIN, tc->prev_src ? tc->prev_src->blocklength : INT_MIN, tc->curr_src ? tc->curr_src->consumed : INT_MIN, tc->curr_src ? tc->curr_src->produced : INT_MIN, tc->curr_src ? tc->curr_src->blocklength : INT_MIN));1076failed = -EINVAL;1077goto error_cleanup;1078}1079}1080/* Detect unit deactivation. */1081if (dcfg->last) {1082/* Length check of this is handled below. */1083tc->done = 1;1084}1085dcfg = dcfg->next;1086} /* while (dcfg) */1087DEBUG(printk("cryptocop_setup_dma_list: parsing operation descriptor configuration complete.\n"));10881089if (cipher_ctx.active && (cipher_ctx.curr_src != NULL) && !cipher_ctx.curr_src->active){1090DEBUG_API(printk("cryptocop_setup_dma_list: cipher source from inactive unit %d\n", cipher_ctx.curr_src->unit_no));1091failed = -EINVAL;1092goto error_cleanup;1093}1094if (digest_ctx.active && (digest_ctx.curr_src != NULL) && !digest_ctx.curr_src->active){1095DEBUG_API(printk("cryptocop_setup_dma_list: digest source from inactive unit %d\n", digest_ctx.curr_src->unit_no));1096failed = -EINVAL;1097goto error_cleanup;1098}1099if (csum_ctx.active && (csum_ctx.curr_src != NULL) && !csum_ctx.curr_src->active){1100DEBUG_API(printk("cryptocop_setup_dma_list: cipher source from inactive unit %d\n", csum_ctx.curr_src->unit_no));1101failed = -EINVAL;1102goto error_cleanup;1103}11041105/* Update consumed and produced lengths.11061107The consumed length accounting here is actually cheating. If a unit source from DMA (or any1108other unit that process data in blocks of one octet) it is correct, but if it source from a1109block processing unit, i.e. a cipher, it will be temporarily incorrect at some times. However1110since it is only allowed--by the HW--to change source to or from a block processing unit at times where that1111unit has processed an exact multiple of its block length the end result will be correct.1112Beware that if the source change restriction change this code will need to be (much) reworked.1113*/1114DEBUG(printk("cryptocop_setup_dma_list: desc->length=%d, desc_len=%d.\n", odsc->length, desc_len));11151116if (csum_ctx.active) {1117csum_ctx.consumed += desc_len;1118if (csum_ctx.done) {1119csum_ctx.produced = 2;1120}1121DEBUG(printk("cryptocop_setup_dma_list: csum_ctx producing: consumed=%d, produced=%d, blocklength=%d.\n", csum_ctx.consumed, csum_ctx.produced, csum_ctx.blocklength));1122}1123if (digest_ctx.active) {1124digest_ctx.consumed += desc_len;1125if (digest_ctx.done) {1126if (digest_ctx.unit_no == src_md5) {1127digest_ctx.produced = MD5_STATE_LENGTH;1128} else {1129digest_ctx.produced = SHA1_STATE_LENGTH;1130}1131}1132DEBUG(printk("cryptocop_setup_dma_list: digest_ctx producing: consumed=%d, produced=%d, blocklength=%d.\n", digest_ctx.consumed, digest_ctx.produced, digest_ctx.blocklength));1133}1134if (cipher_ctx.active) {1135/* Ciphers are allowed only to source from DMA out. That is filtered above. */1136assert(cipher_ctx.current_src == src_dma);1137cipher_ctx.consumed += desc_len;1138cipher_ctx.produced = cipher_ctx.blocklength * (cipher_ctx.consumed / cipher_ctx.blocklength);1139if (cipher_ctx.cbcmode && !(cipher_ctx.tcfg->flags & CRYPTOCOP_EXPLICIT_IV) && cipher_ctx.produced){1140cipher_ctx.produced -= cipher_ctx.blocklength; /* Compensate for CBC iv. */1141}1142DEBUG(printk("cryptocop_setup_dma_list: cipher_ctx producing: consumed=%d, produced=%d, blocklength=%d.\n", cipher_ctx.consumed, cipher_ctx.produced, cipher_ctx.blocklength));1143}11441145/* Setup the DMA out descriptors. */1146/* Configure the metadata. */1147active_count = 0;1148eop_needed_count = 0;1149if (cipher_ctx.active) {1150++active_count;1151if (cipher_ctx.unit_no == src_dma){1152/* mem2mem */1153meta_out.ciphsel = src_none;1154} else {1155meta_out.ciphsel = cipher_ctx.current_src;1156}1157meta_out.ciphconf = cipher_ctx.ciph_conf;1158meta_out.cbcmode = cipher_ctx.cbcmode;1159meta_out.decrypt = cipher_ctx.decrypt;1160DEBUG(printk("set ciphsel=%d ciphconf=%d cbcmode=%d decrypt=%d\n", meta_out.ciphsel, meta_out.ciphconf, meta_out.cbcmode, meta_out.decrypt));1161if (cipher_ctx.done) ++eop_needed_count;1162} else {1163meta_out.ciphsel = src_none;1164}11651166if (digest_ctx.active) {1167++active_count;1168meta_out.hashsel = digest_ctx.current_src;1169meta_out.hashconf = digest_ctx.hash_conf;1170meta_out.hashmode = 0; /* Explicit mode is not used here. */1171DEBUG(printk("set hashsel=%d hashconf=%d hashmode=%d\n", meta_out.hashsel, meta_out.hashconf, meta_out.hashmode));1172if (digest_ctx.done) {1173assert(digest_ctx.pad_descs == NULL);1174failed = create_pad_descriptor(&digest_ctx, &digest_ctx.pad_descs, alloc_flag);1175if (failed) {1176DEBUG_API(printk("cryptocop_setup_dma_list: failed digest pad creation.\n"));1177goto error_cleanup;1178}1179}1180} else {1181meta_out.hashsel = src_none;1182}11831184if (csum_ctx.active) {1185++active_count;1186meta_out.csumsel = csum_ctx.current_src;1187if (csum_ctx.done) {1188assert(csum_ctx.pad_descs == NULL);1189failed = create_pad_descriptor(&csum_ctx, &csum_ctx.pad_descs, alloc_flag);1190if (failed) {1191DEBUG_API(printk("cryptocop_setup_dma_list: failed csum pad creation.\n"));1192goto error_cleanup;1193}1194}1195} else {1196meta_out.csumsel = src_none;1197}1198DEBUG(printk("cryptocop_setup_dma_list: %d eop needed, %d active units\n", eop_needed_count, active_count));1199/* Setup DMA out descriptors for the indata. */1200failed = create_output_descriptors(operation, &iniov_ix, &iniov_offset, desc_len, ¤t_out_cdesc, &meta_out, alloc_flag);1201if (failed) {1202DEBUG_API(printk("cryptocop_setup_dma_list: create_output_descriptors %d\n", failed));1203goto error_cleanup;1204}1205/* Setup out EOP. If there are active units that are not done here they cannot get an EOP1206* so we ust setup a zero length descriptor to DMA to signal EOP only to done units.1207* If there is a pad descriptor EOP for the padded unit will be EOPed by it.1208*/1209assert(active_count >= eop_needed_count);1210assert((eop_needed_count == 0) || (eop_needed_count == 1));1211if (eop_needed_count) {1212/* This means that the bulk operation (cipeher/m2m) is terminated. */1213if (active_count > 1) {1214/* Use zero length EOP descriptor. */1215struct cryptocop_dma_desc *ed = alloc_cdesc(alloc_flag);1216struct strcop_meta_out ed_mo = {0};1217if (!ed) {1218DEBUG_API(printk("cryptocop_setup_dma_list: alloc EOP descriptor for cipher\n"));1219failed = -ENOMEM;1220goto error_cleanup;1221}12221223assert(cipher_ctx.active && cipher_ctx.done);12241225if (cipher_ctx.unit_no == src_dma){1226/* mem2mem */1227ed_mo.ciphsel = src_none;1228} else {1229ed_mo.ciphsel = cipher_ctx.current_src;1230}1231ed_mo.ciphconf = cipher_ctx.ciph_conf;1232ed_mo.cbcmode = cipher_ctx.cbcmode;1233ed_mo.decrypt = cipher_ctx.decrypt;12341235ed->free_buf = NULL;1236ed->dma_descr->wait = 1;1237ed->dma_descr->out_eop = 1;12381239ed->dma_descr->buf = (char*)virt_to_phys(&ed); /* Use any valid physical address for zero length descriptor. */1240ed->dma_descr->after = ed->dma_descr->buf;1241ed->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_out, ed_mo);1242current_out_cdesc->next = ed;1243current_out_cdesc = ed;1244} else {1245/* Set EOP in the current out descriptor since the only active module is1246* the one needing the EOP. */12471248current_out_cdesc->dma_descr->out_eop = 1;1249}1250}12511252if (cipher_ctx.done && cipher_ctx.active) cipher_ctx.active = 0;1253if (digest_ctx.done && digest_ctx.active) digest_ctx.active = 0;1254if (csum_ctx.done && csum_ctx.active) csum_ctx.active = 0;1255indata_ix += odsc->length;1256odsc = odsc->next;1257} /* while (odsc) */ /* Process descriptors. */1258DEBUG(printk("cryptocop_setup_dma_list: done parsing operation descriptors\n"));1259if (cipher_ctx.tcfg && (cipher_ctx.active || !cipher_ctx.done)){1260DEBUG_API(printk("cryptocop_setup_dma_list: cipher operation not terminated.\n"));1261failed = -EINVAL;1262goto error_cleanup;1263}1264if (digest_ctx.tcfg && (digest_ctx.active || !digest_ctx.done)){1265DEBUG_API(printk("cryptocop_setup_dma_list: digest operation not terminated.\n"));1266failed = -EINVAL;1267goto error_cleanup;1268}1269if (csum_ctx.tcfg && (csum_ctx.active || !csum_ctx.done)){1270DEBUG_API(printk("cryptocop_setup_dma_list: csum operation not terminated.\n"));1271failed = -EINVAL;1272goto error_cleanup;1273}12741275failed = append_input_descriptors(operation, ¤t_in_cdesc, ¤t_out_cdesc, &cipher_ctx, alloc_flag);1276if (failed){1277DEBUG_API(printk("cryptocop_setup_dma_list: append_input_descriptors cipher_ctx %d\n", failed));1278goto error_cleanup;1279}1280failed = append_input_descriptors(operation, ¤t_in_cdesc, ¤t_out_cdesc, &digest_ctx, alloc_flag);1281if (failed){1282DEBUG_API(printk("cryptocop_setup_dma_list: append_input_descriptors cipher_ctx %d\n", failed));1283goto error_cleanup;1284}1285failed = append_input_descriptors(operation, ¤t_in_cdesc, ¤t_out_cdesc, &csum_ctx, alloc_flag);1286if (failed){1287DEBUG_API(printk("cryptocop_setup_dma_list: append_input_descriptors cipher_ctx %d\n", failed));1288goto error_cleanup;1289}12901291DEBUG(printk("cryptocop_setup_dma_list: int_op=0x%p, *int_op=0x%p\n", int_op, *int_op));1292(*int_op)->cdesc_out = out_cdesc_head.next;1293(*int_op)->cdesc_in = in_cdesc_head.next;1294DEBUG(printk("cryptocop_setup_dma_list: out_cdesc_head=0x%p in_cdesc_head=0x%p\n", (*int_op)->cdesc_out, (*int_op)->cdesc_in));12951296setup_descr_chain(out_cdesc_head.next);1297setup_descr_chain(in_cdesc_head.next);12981299/* Last but not least: mark the last DMA in descriptor for a INTR and EOL and the the1300* last DMA out descriptor for EOL.1301*/1302current_in_cdesc->dma_descr->intr = 1;1303current_in_cdesc->dma_descr->eol = 1;1304current_out_cdesc->dma_descr->eol = 1;13051306/* Setup DMA contexts. */1307(*int_op)->ctx_out.next = NULL;1308(*int_op)->ctx_out.eol = 1;1309(*int_op)->ctx_out.intr = 0;1310(*int_op)->ctx_out.store_mode = 0;1311(*int_op)->ctx_out.en = 0;1312(*int_op)->ctx_out.dis = 0;1313(*int_op)->ctx_out.md0 = 0;1314(*int_op)->ctx_out.md1 = 0;1315(*int_op)->ctx_out.md2 = 0;1316(*int_op)->ctx_out.md3 = 0;1317(*int_op)->ctx_out.md4 = 0;1318(*int_op)->ctx_out.saved_data = (dma_descr_data*)virt_to_phys((*int_op)->cdesc_out->dma_descr);1319(*int_op)->ctx_out.saved_data_buf = (*int_op)->cdesc_out->dma_descr->buf; /* Already physical address. */13201321(*int_op)->ctx_in.next = NULL;1322(*int_op)->ctx_in.eol = 1;1323(*int_op)->ctx_in.intr = 0;1324(*int_op)->ctx_in.store_mode = 0;1325(*int_op)->ctx_in.en = 0;1326(*int_op)->ctx_in.dis = 0;1327(*int_op)->ctx_in.md0 = 0;1328(*int_op)->ctx_in.md1 = 0;1329(*int_op)->ctx_in.md2 = 0;1330(*int_op)->ctx_in.md3 = 0;1331(*int_op)->ctx_in.md4 = 0;13321333(*int_op)->ctx_in.saved_data = (dma_descr_data*)virt_to_phys((*int_op)->cdesc_in->dma_descr);1334(*int_op)->ctx_in.saved_data_buf = (*int_op)->cdesc_in->dma_descr->buf; /* Already physical address. */13351336DEBUG(printk("cryptocop_setup_dma_list: done\n"));1337return 0;13381339error_cleanup:1340{1341/* Free all allocated resources. */1342struct cryptocop_dma_desc *tmp_cdesc;1343while (digest_ctx.pad_descs){1344tmp_cdesc = digest_ctx.pad_descs->next;1345free_cdesc(digest_ctx.pad_descs);1346digest_ctx.pad_descs = tmp_cdesc;1347}1348while (csum_ctx.pad_descs){1349tmp_cdesc = csum_ctx.pad_descs->next;1350free_cdesc(csum_ctx.pad_descs);1351csum_ctx.pad_descs = tmp_cdesc;1352}1353assert(cipher_ctx.pad_descs == NULL); /* The ciphers are never padded. */13541355if (*int_op != NULL) delete_internal_operation(*int_op);1356}1357DEBUG_API(printk("cryptocop_setup_dma_list: done with error %d\n", failed));1358return failed;1359}136013611362static void delete_internal_operation(struct cryptocop_int_operation *iop)1363{1364void *ptr = iop->alloc_ptr;1365struct cryptocop_dma_desc *cd = iop->cdesc_out;1366struct cryptocop_dma_desc *next;13671368DEBUG(printk("delete_internal_operation: iop=0x%p, alloc_ptr=0x%p\n", iop, ptr));13691370while (cd) {1371next = cd->next;1372free_cdesc(cd);1373cd = next;1374}1375cd = iop->cdesc_in;1376while (cd) {1377next = cd->next;1378free_cdesc(cd);1379cd = next;1380}1381kfree(ptr);1382}13831384#define MD5_MIN_PAD_LENGTH (9)1385#define MD5_PAD_LENGTH_FIELD_LENGTH (8)13861387static int create_md5_pad(int alloc_flag, unsigned long long hashed_length, char **pad, size_t *pad_length)1388{1389size_t padlen = MD5_BLOCK_LENGTH - (hashed_length % MD5_BLOCK_LENGTH);1390unsigned char *p;1391int i;1392unsigned long long int bit_length = hashed_length << 3;13931394if (padlen < MD5_MIN_PAD_LENGTH) padlen += MD5_BLOCK_LENGTH;13951396p = kmalloc(padlen, alloc_flag);1397if (!p) return -ENOMEM;13981399*p = 0x80;1400memset(p+1, 0, padlen - 1);14011402DEBUG(printk("create_md5_pad: hashed_length=%lld bits == %lld bytes\n", bit_length, hashed_length));14031404i = padlen - MD5_PAD_LENGTH_FIELD_LENGTH;1405while (bit_length != 0){1406p[i++] = bit_length % 0x100;1407bit_length >>= 8;1408}14091410*pad = (char*)p;1411*pad_length = padlen;14121413return 0;1414}14151416#define SHA1_MIN_PAD_LENGTH (9)1417#define SHA1_PAD_LENGTH_FIELD_LENGTH (8)14181419static int create_sha1_pad(int alloc_flag, unsigned long long hashed_length, char **pad, size_t *pad_length)1420{1421size_t padlen = SHA1_BLOCK_LENGTH - (hashed_length % SHA1_BLOCK_LENGTH);1422unsigned char *p;1423int i;1424unsigned long long int bit_length = hashed_length << 3;14251426if (padlen < SHA1_MIN_PAD_LENGTH) padlen += SHA1_BLOCK_LENGTH;14271428p = kmalloc(padlen, alloc_flag);1429if (!p) return -ENOMEM;14301431*p = 0x80;1432memset(p+1, 0, padlen - 1);14331434DEBUG(printk("create_sha1_pad: hashed_length=%lld bits == %lld bytes\n", bit_length, hashed_length));14351436i = padlen - 1;1437while (bit_length != 0){1438p[i--] = bit_length % 0x100;1439bit_length >>= 8;1440}14411442*pad = (char*)p;1443*pad_length = padlen;14441445return 0;1446}144714481449static int transform_ok(struct cryptocop_transform_init *tinit)1450{1451switch (tinit->alg){1452case cryptocop_alg_csum:1453switch (tinit->csum_mode){1454case cryptocop_csum_le:1455case cryptocop_csum_be:1456break;1457default:1458DEBUG_API(printk("transform_ok: Bad mode set for csum transform\n"));1459return -EINVAL;1460}1461case cryptocop_alg_mem2mem:1462case cryptocop_alg_md5:1463case cryptocop_alg_sha1:1464if (tinit->keylen != 0) {1465DEBUG_API(printk("transform_ok: non-zero keylength, %d, for a digest/csum algorithm\n", tinit->keylen));1466return -EINVAL; /* This check is a bit strict. */1467}1468break;1469case cryptocop_alg_des:1470if (tinit->keylen != 64) {1471DEBUG_API(printk("transform_ok: keylen %d invalid for DES\n", tinit->keylen));1472return -EINVAL;1473}1474break;1475case cryptocop_alg_3des:1476if (tinit->keylen != 192) {1477DEBUG_API(printk("transform_ok: keylen %d invalid for 3DES\n", tinit->keylen));1478return -EINVAL;1479}1480break;1481case cryptocop_alg_aes:1482if (tinit->keylen != 128 && tinit->keylen != 192 && tinit->keylen != 256) {1483DEBUG_API(printk("transform_ok: keylen %d invalid for AES\n", tinit->keylen));1484return -EINVAL;1485}1486break;1487case cryptocop_no_alg:1488default:1489DEBUG_API(printk("transform_ok: no such algorithm %d\n", tinit->alg));1490return -EINVAL;1491}14921493switch (tinit->alg){1494case cryptocop_alg_des:1495case cryptocop_alg_3des:1496case cryptocop_alg_aes:1497if (tinit->cipher_mode != cryptocop_cipher_mode_ecb && tinit->cipher_mode != cryptocop_cipher_mode_cbc) return -EINVAL;1498default:1499break;1500}1501return 0;1502}150315041505int cryptocop_new_session(cryptocop_session_id *sid, struct cryptocop_transform_init *tinit, int alloc_flag)1506{1507struct cryptocop_session *sess;1508struct cryptocop_transform_init *tfrm_in = tinit;1509struct cryptocop_transform_init *tmp_in;1510int no_tfrms = 0;1511int i;1512unsigned long int flags;15131514init_stream_coprocessor(); /* For safety if we are called early */15151516while (tfrm_in){1517int err;1518++no_tfrms;1519if ((err = transform_ok(tfrm_in))) {1520DEBUG_API(printk("cryptocop_new_session, bad transform\n"));1521return err;1522}1523tfrm_in = tfrm_in->next;1524}1525if (0 == no_tfrms) {1526DEBUG_API(printk("cryptocop_new_session, no transforms specified\n"));1527return -EINVAL;1528}15291530sess = kmalloc(sizeof(struct cryptocop_session), alloc_flag);1531if (!sess){1532DEBUG_API(printk("cryptocop_new_session, kmalloc cryptocop_session\n"));1533return -ENOMEM;1534}15351536sess->tfrm_ctx = kmalloc(no_tfrms * sizeof(struct cryptocop_transform_ctx), alloc_flag);1537if (!sess->tfrm_ctx) {1538DEBUG_API(printk("cryptocop_new_session, kmalloc cryptocop_transform_ctx\n"));1539kfree(sess);1540return -ENOMEM;1541}15421543tfrm_in = tinit;1544for (i = 0; i < no_tfrms; i++){1545tmp_in = tfrm_in->next;1546while (tmp_in){1547if (tmp_in->tid == tfrm_in->tid) {1548DEBUG_API(printk("cryptocop_new_session, duplicate transform ids\n"));1549kfree(sess->tfrm_ctx);1550kfree(sess);1551return -EINVAL;1552}1553tmp_in = tmp_in->next;1554}1555memcpy(&sess->tfrm_ctx[i].init, tfrm_in, sizeof(struct cryptocop_transform_init));1556sess->tfrm_ctx[i].dec_key_set = 0;1557sess->tfrm_ctx[i].next = &sess->tfrm_ctx[i] + 1;15581559tfrm_in = tfrm_in->next;1560}1561sess->tfrm_ctx[i-1].next = NULL;15621563spin_lock_irqsave(&cryptocop_sessions_lock, flags);1564sess->sid = next_sid;1565next_sid++;1566/* TODO If we are really paranoid we should do duplicate check to handle sid wraparound.1567* OTOH 2^64 is a really large number of session. */1568if (next_sid == 0) next_sid = 1;15691570/* Prepend to session list. */1571sess->next = cryptocop_sessions;1572cryptocop_sessions = sess;1573spin_unlock_irqrestore(&cryptocop_sessions_lock, flags);1574*sid = sess->sid;1575return 0;1576}157715781579int cryptocop_free_session(cryptocop_session_id sid)1580{1581struct cryptocop_transform_ctx *tc;1582struct cryptocop_session *sess = NULL;1583struct cryptocop_session *psess = NULL;1584unsigned long int flags;1585int i;1586LIST_HEAD(remove_list);1587struct list_head *node, *tmp;1588struct cryptocop_prio_job *pj;15891590DEBUG(printk("cryptocop_free_session: sid=%lld\n", sid));15911592spin_lock_irqsave(&cryptocop_sessions_lock, flags);1593sess = cryptocop_sessions;1594while (sess && sess->sid != sid){1595psess = sess;1596sess = sess->next;1597}1598if (sess){1599if (psess){1600psess->next = sess->next;1601} else {1602cryptocop_sessions = sess->next;1603}1604}1605spin_unlock_irqrestore(&cryptocop_sessions_lock, flags);16061607if (!sess) return -EINVAL;16081609/* Remove queued jobs. */1610spin_lock_irqsave(&cryptocop_job_queue_lock, flags);16111612for (i = 0; i < cryptocop_prio_no_prios; i++){1613if (!list_empty(&(cryptocop_job_queues[i].jobs))){1614list_for_each_safe(node, tmp, &(cryptocop_job_queues[i].jobs)) {1615pj = list_entry(node, struct cryptocop_prio_job, node);1616if (pj->oper->sid == sid) {1617list_move_tail(node, &remove_list);1618}1619}1620}1621}1622spin_unlock_irqrestore(&cryptocop_job_queue_lock, flags);16231624list_for_each_safe(node, tmp, &remove_list) {1625list_del(node);1626pj = list_entry(node, struct cryptocop_prio_job, node);1627pj->oper->operation_status = -EAGAIN; /* EAGAIN is not ideal for job/session terminated but it's the best choice I know of. */1628DEBUG(printk("cryptocop_free_session: pj=0x%p, pj->oper=0x%p, pj->iop=0x%p\n", pj, pj->oper, pj->iop));1629pj->oper->cb(pj->oper, pj->oper->cb_data);1630delete_internal_operation(pj->iop);1631kfree(pj);1632}16331634tc = sess->tfrm_ctx;1635/* Erase keying data. */1636while (tc){1637DEBUG(printk("cryptocop_free_session: memset keys, tfrm id=%d\n", tc->init.tid));1638memset(tc->init.key, 0xff, CRYPTOCOP_MAX_KEY_LENGTH);1639memset(tc->dec_key, 0xff, CRYPTOCOP_MAX_KEY_LENGTH);1640tc = tc->next;1641}1642kfree(sess->tfrm_ctx);1643kfree(sess);16441645return 0;1646}16471648static struct cryptocop_session *get_session(cryptocop_session_id sid)1649{1650struct cryptocop_session *sess;1651unsigned long int flags;16521653spin_lock_irqsave(&cryptocop_sessions_lock, flags);1654sess = cryptocop_sessions;1655while (sess && (sess->sid != sid)){1656sess = sess->next;1657}1658spin_unlock_irqrestore(&cryptocop_sessions_lock, flags);16591660return sess;1661}16621663static struct cryptocop_transform_ctx *get_transform_ctx(struct cryptocop_session *sess, cryptocop_tfrm_id tid)1664{1665struct cryptocop_transform_ctx *tc = sess->tfrm_ctx;16661667DEBUG(printk("get_transform_ctx, sess=0x%p, tid=%d\n", sess, tid));1668assert(sess != NULL);1669while (tc && tc->init.tid != tid){1670DEBUG(printk("tc=0x%p, tc->next=0x%p\n", tc, tc->next));1671tc = tc->next;1672}1673DEBUG(printk("get_transform_ctx, returning tc=0x%p\n", tc));1674return tc;1675}1676167716781679/* The AES s-transform matrix (s-box). */1680static const u8 aes_sbox[256] = {168199, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118,1682202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192,1683183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21,16844, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, 39, 178, 117,16859, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132,168683, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207,1687208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168,168881, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210,1689205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115,169096, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219,1691224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121,1692231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8,1693186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138,1694112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158,1695225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223,1696140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, 221697};16981699/* AES has a 32 bit word round constants for each round in the1700* key schedule. round_constant[i] is really Rcon[i+1] in FIPS187.1701*/1702static u32 round_constant[11] = {17030x01000000, 0x02000000, 0x04000000, 0x08000000,17040x10000000, 0x20000000, 0x40000000, 0x80000000,17050x1B000000, 0x36000000, 0x6C0000001706};17071708/* Apply the s-box to each of the four occtets in w. */1709static u32 aes_ks_subword(const u32 w)1710{1711u8 bytes[4];17121713*(u32*)(&bytes[0]) = w;1714bytes[0] = aes_sbox[bytes[0]];1715bytes[1] = aes_sbox[bytes[1]];1716bytes[2] = aes_sbox[bytes[2]];1717bytes[3] = aes_sbox[bytes[3]];1718return *(u32*)(&bytes[0]);1719}17201721/* The encrypt (forward) Rijndael key schedule algorithm pseudo code:1722* (Note that AES words are 32 bit long)1723*1724* KeyExpansion(byte key[4*Nk], word w[Nb*(Nr+1)], Nk){1725* word temp1726* i = 01727* while (i < Nk) {1728* w[i] = word(key[4*i, 4*i + 1, 4*i + 2, 4*i + 3])1729* i = i + 11730* }1731* i = Nk1732*1733* while (i < (Nb * (Nr + 1))) {1734* temp = w[i - 1]1735* if ((i mod Nk) == 0) {1736* temp = SubWord(RotWord(temp)) xor Rcon[i/Nk]1737* }1738* else if ((Nk > 6) && ((i mod Nk) == 4)) {1739* temp = SubWord(temp)1740* }1741* w[i] = w[i - Nk] xor temp1742* }1743* RotWord(t) does a 8 bit cyclic shift left on a 32 bit word.1744* SubWord(t) applies the AES s-box individually to each octet1745* in a 32 bit word.1746*1747* For AES Nk can have the values 4, 6, and 8 (corresponding to1748* values for Nr of 10, 12, and 14). Nb is always 4.1749*1750* To construct w[i], w[i - 1] and w[i - Nk] must be1751* available. Consequently we must keep a state of the last Nk words1752* to be able to create the last round keys.1753*/1754static void get_aes_decrypt_key(unsigned char *dec_key, const unsigned char *key, unsigned int keylength)1755{1756u32 temp;1757u32 w_ring[8]; /* nk is max 8, use elements 0..(nk - 1) as a ringbuffer */1758u8 w_last_ix;1759int i;1760u8 nr, nk;17611762switch (keylength){1763case 128:1764nk = 4;1765nr = 10;1766break;1767case 192:1768nk = 6;1769nr = 12;1770break;1771case 256:1772nk = 8;1773nr = 14;1774break;1775default:1776panic("stream co-processor: bad aes key length in get_aes_decrypt_key\n");1777};17781779/* Need to do host byte order correction here since key is byte oriented and the1780* kx algorithm is word (u32) oriented. */1781for (i = 0; i < nk; i+=1) {1782w_ring[i] = be32_to_cpu(*(u32*)&key[4*i]);1783}17841785i = (int)nk;1786w_last_ix = i - 1;1787while (i < (4 * (nr + 2))) {1788temp = w_ring[w_last_ix];1789if (!(i % nk)) {1790/* RotWord(temp) */1791temp = (temp << 8) | (temp >> 24);1792temp = aes_ks_subword(temp);1793temp ^= round_constant[i/nk - 1];1794} else if ((nk > 6) && ((i % nk) == 4)) {1795temp = aes_ks_subword(temp);1796}1797w_last_ix = (w_last_ix + 1) % nk; /* This is the same as (i-Nk) mod Nk */1798temp ^= w_ring[w_last_ix];1799w_ring[w_last_ix] = temp;18001801/* We need the round keys for round Nr+1 and Nr+2 (round key1802* Nr+2 is the round key beyond the last one used when1803* encrypting). Rounds are numbered starting from 0, Nr=101804* implies 11 rounds are used in encryption/decryption.1805*/1806if (i >= (4 * nr)) {1807/* Need to do host byte order correction here, the key1808* is byte oriented. */1809*(u32*)dec_key = cpu_to_be32(temp);1810dec_key += 4;1811}1812++i;1813}1814}181518161817/**** Job/operation management. ****/18181819int cryptocop_job_queue_insert_csum(struct cryptocop_operation *operation)1820{1821return cryptocop_job_queue_insert(cryptocop_prio_kernel_csum, operation);1822}18231824int cryptocop_job_queue_insert_crypto(struct cryptocop_operation *operation)1825{1826return cryptocop_job_queue_insert(cryptocop_prio_kernel, operation);1827}18281829int cryptocop_job_queue_insert_user_job(struct cryptocop_operation *operation)1830{1831return cryptocop_job_queue_insert(cryptocop_prio_user, operation);1832}18331834static int cryptocop_job_queue_insert(cryptocop_queue_priority prio, struct cryptocop_operation *operation)1835{1836int ret;1837struct cryptocop_prio_job *pj = NULL;1838unsigned long int flags;18391840DEBUG(printk("cryptocop_job_queue_insert(%d, 0x%p)\n", prio, operation));18411842if (!operation || !operation->cb){1843DEBUG_API(printk("cryptocop_job_queue_insert oper=0x%p, NULL operation or callback\n", operation));1844return -EINVAL;1845}18461847if ((ret = cryptocop_job_setup(&pj, operation)) != 0){1848DEBUG_API(printk("cryptocop_job_queue_insert: job setup failed\n"));1849return ret;1850}1851assert(pj != NULL);18521853spin_lock_irqsave(&cryptocop_job_queue_lock, flags);1854list_add_tail(&pj->node, &cryptocop_job_queues[prio].jobs);1855spin_unlock_irqrestore(&cryptocop_job_queue_lock, flags);18561857/* Make sure a job is running */1858cryptocop_start_job();1859return 0;1860}18611862static void cryptocop_do_tasklet(unsigned long unused);1863DECLARE_TASKLET (cryptocop_tasklet, cryptocop_do_tasklet, 0);18641865static void cryptocop_do_tasklet(unsigned long unused)1866{1867struct list_head *node;1868struct cryptocop_prio_job *pj = NULL;1869unsigned long flags;18701871DEBUG(printk("cryptocop_do_tasklet: entering\n"));18721873do {1874spin_lock_irqsave(&cryptocop_completed_jobs_lock, flags);1875if (!list_empty(&cryptocop_completed_jobs)){1876node = cryptocop_completed_jobs.next;1877list_del(node);1878pj = list_entry(node, struct cryptocop_prio_job, node);1879} else {1880pj = NULL;1881}1882spin_unlock_irqrestore(&cryptocop_completed_jobs_lock, flags);1883if (pj) {1884assert(pj->oper != NULL);18851886/* Notify consumer of operation completeness. */1887DEBUG(printk("cryptocop_do_tasklet: callback 0x%p, data 0x%p\n", pj->oper->cb, pj->oper->cb_data));18881889pj->oper->operation_status = 0; /* Job is completed. */1890pj->oper->cb(pj->oper, pj->oper->cb_data);1891delete_internal_operation(pj->iop);1892kfree(pj);1893}1894} while (pj != NULL);18951896DEBUG(printk("cryptocop_do_tasklet: exiting\n"));1897}18981899static irqreturn_t1900dma_done_interrupt(int irq, void *dev_id)1901{1902struct cryptocop_prio_job *done_job;1903reg_dma_rw_ack_intr ack_intr = {1904.data = 1,1905};19061907REG_WR(dma, IN_DMA_INST, rw_ack_intr, ack_intr);19081909DEBUG(printk("cryptocop DMA done\n"));19101911spin_lock(&running_job_lock);1912if (cryptocop_running_job == NULL){1913printk("stream co-processor got interrupt when not busy\n");1914spin_unlock(&running_job_lock);1915return IRQ_HANDLED;1916}1917done_job = cryptocop_running_job;1918cryptocop_running_job = NULL;1919spin_unlock(&running_job_lock);19201921/* Start processing a job. */1922if (!spin_trylock(&cryptocop_process_lock)){1923DEBUG(printk("cryptocop irq handler, not starting a job\n"));1924} else {1925cryptocop_start_job();1926spin_unlock(&cryptocop_process_lock);1927}19281929done_job->oper->operation_status = 0; /* Job is completed. */1930if (done_job->oper->fast_callback){1931/* This operation wants callback from interrupt. */1932done_job->oper->cb(done_job->oper, done_job->oper->cb_data);1933delete_internal_operation(done_job->iop);1934kfree(done_job);1935} else {1936spin_lock(&cryptocop_completed_jobs_lock);1937list_add_tail(&(done_job->node), &cryptocop_completed_jobs);1938spin_unlock(&cryptocop_completed_jobs_lock);1939tasklet_schedule(&cryptocop_tasklet);1940}19411942DEBUG(printk("cryptocop leave irq handler\n"));1943return IRQ_HANDLED;1944}194519461947/* Setup interrupts and DMA channels. */1948static int init_cryptocop(void)1949{1950unsigned long flags;1951reg_dma_rw_cfg dma_cfg = {.en = 1};1952reg_dma_rw_intr_mask intr_mask_in = {.data = regk_dma_yes}; /* Only want descriptor interrupts from the DMA in channel. */1953reg_dma_rw_ack_intr ack_intr = {.data = 1,.in_eop = 1 };1954reg_strcop_rw_cfg strcop_cfg = {1955.ipend = regk_strcop_little,1956.td1 = regk_strcop_e,1957.td2 = regk_strcop_d,1958.td3 = regk_strcop_e,1959.ignore_sync = 0,1960.en = 11961};19621963if (request_irq(DMA_IRQ, dma_done_interrupt, 0,1964"stream co-processor DMA", NULL))1965panic("request_irq stream co-processor irq dma9");19661967(void)crisv32_request_dma(OUT_DMA, "strcop", DMA_PANIC_ON_ERROR,19680, dma_strp);1969(void)crisv32_request_dma(IN_DMA, "strcop", DMA_PANIC_ON_ERROR,19700, dma_strp);19711972local_irq_save(flags);19731974/* Reset and enable the cryptocop. */1975strcop_cfg.en = 0;1976REG_WR(strcop, regi_strcop, rw_cfg, strcop_cfg);1977strcop_cfg.en = 1;1978REG_WR(strcop, regi_strcop, rw_cfg, strcop_cfg);19791980/* Enable DMAs. */1981REG_WR(dma, IN_DMA_INST, rw_cfg, dma_cfg); /* input DMA */1982REG_WR(dma, OUT_DMA_INST, rw_cfg, dma_cfg); /* output DMA */19831984/* Set up wordsize = 4 for DMAs. */1985DMA_WR_CMD(OUT_DMA_INST, regk_dma_set_w_size4);1986DMA_WR_CMD(IN_DMA_INST, regk_dma_set_w_size4);19871988/* Enable interrupts. */1989REG_WR(dma, IN_DMA_INST, rw_intr_mask, intr_mask_in);19901991/* Clear intr ack. */1992REG_WR(dma, IN_DMA_INST, rw_ack_intr, ack_intr);19931994local_irq_restore(flags);19951996return 0;1997}19981999/* Free used cryptocop hw resources (interrupt and DMA channels). */2000static void release_cryptocop(void)2001{2002unsigned long flags;2003reg_dma_rw_cfg dma_cfg = {.en = 0};2004reg_dma_rw_intr_mask intr_mask_in = {0};2005reg_dma_rw_ack_intr ack_intr = {.data = 1,.in_eop = 1 };20062007local_irq_save(flags);20082009/* Clear intr ack. */2010REG_WR(dma, IN_DMA_INST, rw_ack_intr, ack_intr);20112012/* Disable DMAs. */2013REG_WR(dma, IN_DMA_INST, rw_cfg, dma_cfg); /* input DMA */2014REG_WR(dma, OUT_DMA_INST, rw_cfg, dma_cfg); /* output DMA */20152016/* Disable interrupts. */2017REG_WR(dma, IN_DMA_INST, rw_intr_mask, intr_mask_in);20182019local_irq_restore(flags);20202021free_irq(DMA_IRQ, NULL);20222023(void)crisv32_free_dma(OUT_DMA);2024(void)crisv32_free_dma(IN_DMA);2025}202620272028/* Init job queue. */2029static int cryptocop_job_queue_init(void)2030{2031int i;20322033INIT_LIST_HEAD(&cryptocop_completed_jobs);20342035for (i = 0; i < cryptocop_prio_no_prios; i++){2036cryptocop_job_queues[i].prio = (cryptocop_queue_priority)i;2037INIT_LIST_HEAD(&cryptocop_job_queues[i].jobs);2038}2039return 0;2040}204120422043static void cryptocop_job_queue_close(void)2044{2045struct list_head *node, *tmp;2046struct cryptocop_prio_job *pj = NULL;2047unsigned long int process_flags, flags;2048int i;20492050/* FIXME: This is as yet untested code. */20512052/* Stop strcop from getting an operation to process while we are closing the2053module. */2054spin_lock_irqsave(&cryptocop_process_lock, process_flags);20552056/* Empty the job queue. */2057for (i = 0; i < cryptocop_prio_no_prios; i++){2058if (!list_empty(&(cryptocop_job_queues[i].jobs))){2059list_for_each_safe(node, tmp, &(cryptocop_job_queues[i].jobs)) {2060pj = list_entry(node, struct cryptocop_prio_job, node);2061list_del(node);20622063/* Call callback to notify consumer of job removal. */2064DEBUG(printk("cryptocop_job_queue_close: callback 0x%p, data 0x%p\n", pj->oper->cb, pj->oper->cb_data));2065pj->oper->operation_status = -EINTR; /* Job is terminated without completion. */2066pj->oper->cb(pj->oper, pj->oper->cb_data);20672068delete_internal_operation(pj->iop);2069kfree(pj);2070}2071}2072}2073spin_unlock_irqrestore(&cryptocop_process_lock, process_flags);20742075/* Remove the running job, if any. */2076spin_lock_irqsave(&running_job_lock, flags);2077if (cryptocop_running_job){2078reg_strcop_rw_cfg rw_cfg;2079reg_dma_rw_cfg dma_out_cfg, dma_in_cfg;20802081/* Stop DMA. */2082dma_out_cfg = REG_RD(dma, OUT_DMA_INST, rw_cfg);2083dma_out_cfg.en = regk_dma_no;2084REG_WR(dma, OUT_DMA_INST, rw_cfg, dma_out_cfg);20852086dma_in_cfg = REG_RD(dma, IN_DMA_INST, rw_cfg);2087dma_in_cfg.en = regk_dma_no;2088REG_WR(dma, IN_DMA_INST, rw_cfg, dma_in_cfg);20892090/* Disble the cryptocop. */2091rw_cfg = REG_RD(strcop, regi_strcop, rw_cfg);2092rw_cfg.en = 0;2093REG_WR(strcop, regi_strcop, rw_cfg, rw_cfg);20942095pj = cryptocop_running_job;2096cryptocop_running_job = NULL;20972098/* Call callback to notify consumer of job removal. */2099DEBUG(printk("cryptocop_job_queue_close: callback 0x%p, data 0x%p\n", pj->oper->cb, pj->oper->cb_data));2100pj->oper->operation_status = -EINTR; /* Job is terminated without completion. */2101pj->oper->cb(pj->oper, pj->oper->cb_data);21022103delete_internal_operation(pj->iop);2104kfree(pj);2105}2106spin_unlock_irqrestore(&running_job_lock, flags);21072108/* Remove completed jobs, if any. */2109spin_lock_irqsave(&cryptocop_completed_jobs_lock, flags);21102111list_for_each_safe(node, tmp, &cryptocop_completed_jobs) {2112pj = list_entry(node, struct cryptocop_prio_job, node);2113list_del(node);2114/* Call callback to notify consumer of job removal. */2115DEBUG(printk("cryptocop_job_queue_close: callback 0x%p, data 0x%p\n", pj->oper->cb, pj->oper->cb_data));2116pj->oper->operation_status = -EINTR; /* Job is terminated without completion. */2117pj->oper->cb(pj->oper, pj->oper->cb_data);21182119delete_internal_operation(pj->iop);2120kfree(pj);2121}2122spin_unlock_irqrestore(&cryptocop_completed_jobs_lock, flags);2123}212421252126static void cryptocop_start_job(void)2127{2128int i;2129struct cryptocop_prio_job *pj;2130unsigned long int flags;2131unsigned long int running_job_flags;2132reg_strcop_rw_cfg rw_cfg = {.en = 1, .ignore_sync = 0};21332134DEBUG(printk("cryptocop_start_job: entering\n"));21352136spin_lock_irqsave(&running_job_lock, running_job_flags);2137if (cryptocop_running_job != NULL){2138/* Already running. */2139DEBUG(printk("cryptocop_start_job: already running, exit\n"));2140spin_unlock_irqrestore(&running_job_lock, running_job_flags);2141return;2142}2143spin_lock_irqsave(&cryptocop_job_queue_lock, flags);21442145/* Check the queues in priority order. */2146for (i = cryptocop_prio_kernel_csum; (i < cryptocop_prio_no_prios) && list_empty(&cryptocop_job_queues[i].jobs); i++);2147if (i == cryptocop_prio_no_prios) {2148spin_unlock_irqrestore(&cryptocop_job_queue_lock, flags);2149spin_unlock_irqrestore(&running_job_lock, running_job_flags);2150DEBUG(printk("cryptocop_start_job: no jobs to run\n"));2151return; /* No jobs to run */2152}2153DEBUG(printk("starting job for prio %d\n", i));21542155/* TODO: Do not starve lower priority jobs. Let in a lower2156* prio job for every N-th processed higher prio job or some2157* other scheduling policy. This could reasonably be2158* tweakable since the optimal balance would depend on the2159* type of load on the system. */21602161/* Pull the DMA lists from the job and start the DMA client. */2162pj = list_entry(cryptocop_job_queues[i].jobs.next, struct cryptocop_prio_job, node);2163list_del(&pj->node);2164spin_unlock_irqrestore(&cryptocop_job_queue_lock, flags);2165cryptocop_running_job = pj;21662167/* Set config register (3DES and CSUM modes). */2168switch (pj->iop->tdes_mode){2169case cryptocop_3des_eee:2170rw_cfg.td1 = regk_strcop_e;2171rw_cfg.td2 = regk_strcop_e;2172rw_cfg.td3 = regk_strcop_e;2173break;2174case cryptocop_3des_eed:2175rw_cfg.td1 = regk_strcop_e;2176rw_cfg.td2 = regk_strcop_e;2177rw_cfg.td3 = regk_strcop_d;2178break;2179case cryptocop_3des_ede:2180rw_cfg.td1 = regk_strcop_e;2181rw_cfg.td2 = regk_strcop_d;2182rw_cfg.td3 = regk_strcop_e;2183break;2184case cryptocop_3des_edd:2185rw_cfg.td1 = regk_strcop_e;2186rw_cfg.td2 = regk_strcop_d;2187rw_cfg.td3 = regk_strcop_d;2188break;2189case cryptocop_3des_dee:2190rw_cfg.td1 = regk_strcop_d;2191rw_cfg.td2 = regk_strcop_e;2192rw_cfg.td3 = regk_strcop_e;2193break;2194case cryptocop_3des_ded:2195rw_cfg.td1 = regk_strcop_d;2196rw_cfg.td2 = regk_strcop_e;2197rw_cfg.td3 = regk_strcop_d;2198break;2199case cryptocop_3des_dde:2200rw_cfg.td1 = regk_strcop_d;2201rw_cfg.td2 = regk_strcop_d;2202rw_cfg.td3 = regk_strcop_e;2203break;2204case cryptocop_3des_ddd:2205rw_cfg.td1 = regk_strcop_d;2206rw_cfg.td2 = regk_strcop_d;2207rw_cfg.td3 = regk_strcop_d;2208break;2209default:2210DEBUG(printk("cryptocop_setup_dma_list: bad 3DES mode\n"));2211}2212switch (pj->iop->csum_mode){2213case cryptocop_csum_le:2214rw_cfg.ipend = regk_strcop_little;2215break;2216case cryptocop_csum_be:2217rw_cfg.ipend = regk_strcop_big;2218break;2219default:2220DEBUG(printk("cryptocop_setup_dma_list: bad checksum mode\n"));2221}2222REG_WR(strcop, regi_strcop, rw_cfg, rw_cfg);22232224DEBUG(printk("cryptocop_start_job: starting DMA, new cryptocop_running_job=0x%p\n"2225"ctx_in: 0x%p, phys: 0x%p\n"2226"ctx_out: 0x%p, phys: 0x%p\n",2227pj,2228&pj->iop->ctx_in, (char*)virt_to_phys(&pj->iop->ctx_in),2229&pj->iop->ctx_out, (char*)virt_to_phys(&pj->iop->ctx_out)));22302231/* Start input DMA. */2232flush_dma_context(&pj->iop->ctx_in);2233DMA_START_CONTEXT(IN_DMA_INST, virt_to_phys(&pj->iop->ctx_in));22342235/* Start output DMA. */2236DMA_START_CONTEXT(OUT_DMA_INST, virt_to_phys(&pj->iop->ctx_out));22372238spin_unlock_irqrestore(&running_job_lock, running_job_flags);2239DEBUG(printk("cryptocop_start_job: exiting\n"));2240}224122422243static int cryptocop_job_setup(struct cryptocop_prio_job **pj, struct cryptocop_operation *operation)2244{2245int err;2246int alloc_flag = operation->in_interrupt ? GFP_ATOMIC : GFP_KERNEL;2247void *iop_alloc_ptr = NULL;22482249*pj = kmalloc(sizeof (struct cryptocop_prio_job), alloc_flag);2250if (!*pj) return -ENOMEM;22512252DEBUG(printk("cryptocop_job_setup: operation=0x%p\n", operation));22532254(*pj)->oper = operation;2255DEBUG(printk("cryptocop_job_setup, cb=0x%p cb_data=0x%p\n", (*pj)->oper->cb, (*pj)->oper->cb_data));22562257if (operation->use_dmalists) {2258DEBUG(print_user_dma_lists(&operation->list_op));2259if (!operation->list_op.inlist || !operation->list_op.outlist || !operation->list_op.out_data_buf || !operation->list_op.in_data_buf){2260DEBUG_API(printk("cryptocop_job_setup: bad indata (use_dmalists)\n"));2261kfree(*pj);2262return -EINVAL;2263}2264iop_alloc_ptr = kmalloc(DESCR_ALLOC_PAD + sizeof(struct cryptocop_int_operation), alloc_flag);2265if (!iop_alloc_ptr) {2266DEBUG_API(printk("cryptocop_job_setup: kmalloc cryptocop_int_operation\n"));2267kfree(*pj);2268return -ENOMEM;2269}2270(*pj)->iop = (struct cryptocop_int_operation*)(((unsigned long int)(iop_alloc_ptr + DESCR_ALLOC_PAD + offsetof(struct cryptocop_int_operation, ctx_out)) & ~0x0000001F) - offsetof(struct cryptocop_int_operation, ctx_out));2271DEBUG(memset((*pj)->iop, 0xff, sizeof(struct cryptocop_int_operation)));2272(*pj)->iop->alloc_ptr = iop_alloc_ptr;2273(*pj)->iop->sid = operation->sid;2274(*pj)->iop->cdesc_out = NULL;2275(*pj)->iop->cdesc_in = NULL;2276(*pj)->iop->tdes_mode = operation->list_op.tdes_mode;2277(*pj)->iop->csum_mode = operation->list_op.csum_mode;2278(*pj)->iop->ddesc_out = operation->list_op.outlist;2279(*pj)->iop->ddesc_in = operation->list_op.inlist;22802281/* Setup DMA contexts. */2282(*pj)->iop->ctx_out.next = NULL;2283(*pj)->iop->ctx_out.eol = 1;2284(*pj)->iop->ctx_out.saved_data = operation->list_op.outlist;2285(*pj)->iop->ctx_out.saved_data_buf = operation->list_op.out_data_buf;22862287(*pj)->iop->ctx_in.next = NULL;2288(*pj)->iop->ctx_in.eol = 1;2289(*pj)->iop->ctx_in.saved_data = operation->list_op.inlist;2290(*pj)->iop->ctx_in.saved_data_buf = operation->list_op.in_data_buf;2291} else {2292if ((err = cryptocop_setup_dma_list(operation, &(*pj)->iop, alloc_flag))) {2293DEBUG_API(printk("cryptocop_job_setup: cryptocop_setup_dma_list failed %d\n", err));2294kfree(*pj);2295return err;2296}2297}2298DEBUG(print_dma_descriptors((*pj)->iop));22992300DEBUG(printk("cryptocop_job_setup, DMA list setup successful\n"));23012302return 0;2303}23042305static int cryptocop_open(struct inode *inode, struct file *filp)2306{2307int p = iminor(inode);23082309if (p != CRYPTOCOP_MINOR) return -EINVAL;23102311filp->private_data = NULL;2312return 0;2313}231423152316static int cryptocop_release(struct inode *inode, struct file *filp)2317{2318struct cryptocop_private *dev = filp->private_data;2319struct cryptocop_private *dev_next;23202321while (dev){2322dev_next = dev->next;2323if (dev->sid != CRYPTOCOP_SESSION_ID_NONE) {2324(void)cryptocop_free_session(dev->sid);2325}2326kfree(dev);2327dev = dev_next;2328}23292330return 0;2331}233223332334static int cryptocop_ioctl_close_session(struct inode *inode, struct file *filp,2335unsigned int cmd, unsigned long arg)2336{2337struct cryptocop_private *dev = filp->private_data;2338struct cryptocop_private *prev_dev = NULL;2339struct strcop_session_op *sess_op = (struct strcop_session_op *)arg;2340struct strcop_session_op sop;2341int err;23422343DEBUG(printk("cryptocop_ioctl_close_session\n"));23442345if (!access_ok(VERIFY_READ, sess_op, sizeof(struct strcop_session_op)))2346return -EFAULT;2347err = copy_from_user(&sop, sess_op, sizeof(struct strcop_session_op));2348if (err) return -EFAULT;23492350while (dev && (dev->sid != sop.ses_id)) {2351prev_dev = dev;2352dev = dev->next;2353}2354if (dev){2355if (prev_dev){2356prev_dev->next = dev->next;2357} else {2358filp->private_data = dev->next;2359}2360err = cryptocop_free_session(dev->sid);2361if (err) return -EFAULT;2362} else {2363DEBUG_API(printk("cryptocop_ioctl_close_session: session %lld not found\n", sop.ses_id));2364return -EINVAL;2365}2366return 0;2367}236823692370static void ioctl_process_job_callback(struct cryptocop_operation *op, void*cb_data)2371{2372struct ioctl_job_cb_ctx *jc = (struct ioctl_job_cb_ctx *)cb_data;23732374DEBUG(printk("ioctl_process_job_callback: op=0x%p, cb_data=0x%p\n", op, cb_data));23752376jc->processed = 1;2377wake_up(&cryptocop_ioc_process_wq);2378}237923802381#define CRYPTOCOP_IOCTL_CIPHER_TID (1)2382#define CRYPTOCOP_IOCTL_DIGEST_TID (2)2383#define CRYPTOCOP_IOCTL_CSUM_TID (3)23842385static size_t first_cfg_change_ix(struct strcop_crypto_op *crp_op)2386{2387size_t ch_ix = 0;23882389if (crp_op->do_cipher) ch_ix = crp_op->cipher_start;2390if (crp_op->do_digest && (crp_op->digest_start < ch_ix)) ch_ix = crp_op->digest_start;2391if (crp_op->do_csum && (crp_op->csum_start < ch_ix)) ch_ix = crp_op->csum_start;23922393DEBUG(printk("first_cfg_change_ix: ix=%d\n", ch_ix));2394return ch_ix;2395}239623972398static size_t next_cfg_change_ix(struct strcop_crypto_op *crp_op, size_t ix)2399{2400size_t ch_ix = INT_MAX;2401size_t tmp_ix = 0;24022403if (crp_op->do_cipher && ((crp_op->cipher_start + crp_op->cipher_len) > ix)){2404if (crp_op->cipher_start > ix) {2405ch_ix = crp_op->cipher_start;2406} else {2407ch_ix = crp_op->cipher_start + crp_op->cipher_len;2408}2409}2410if (crp_op->do_digest && ((crp_op->digest_start + crp_op->digest_len) > ix)){2411if (crp_op->digest_start > ix) {2412tmp_ix = crp_op->digest_start;2413} else {2414tmp_ix = crp_op->digest_start + crp_op->digest_len;2415}2416if (tmp_ix < ch_ix) ch_ix = tmp_ix;2417}2418if (crp_op->do_csum && ((crp_op->csum_start + crp_op->csum_len) > ix)){2419if (crp_op->csum_start > ix) {2420tmp_ix = crp_op->csum_start;2421} else {2422tmp_ix = crp_op->csum_start + crp_op->csum_len;2423}2424if (tmp_ix < ch_ix) ch_ix = tmp_ix;2425}2426if (ch_ix == INT_MAX) ch_ix = ix;2427DEBUG(printk("next_cfg_change_ix prev ix=%d, next ix=%d\n", ix, ch_ix));2428return ch_ix;2429}243024312432/* Map map_length bytes from the pages starting on *pageix and *pageoffset to iovecs starting on *iovix.2433* Return -1 for ok, 0 for fail. */2434static int map_pages_to_iovec(struct iovec *iov, int iovlen, int *iovix, struct page **pages, int nopages, int *pageix, int *pageoffset, int map_length )2435{2436int tmplen;24372438assert(iov != NULL);2439assert(iovix != NULL);2440assert(pages != NULL);2441assert(pageix != NULL);2442assert(pageoffset != NULL);24432444DEBUG(printk("map_pages_to_iovec, map_length=%d, iovlen=%d, *iovix=%d, nopages=%d, *pageix=%d, *pageoffset=%d\n", map_length, iovlen, *iovix, nopages, *pageix, *pageoffset));24452446while (map_length > 0){2447DEBUG(printk("map_pages_to_iovec, map_length=%d, iovlen=%d, *iovix=%d, nopages=%d, *pageix=%d, *pageoffset=%d\n", map_length, iovlen, *iovix, nopages, *pageix, *pageoffset));2448if (*iovix >= iovlen){2449DEBUG_API(printk("map_page_to_iovec: *iovix=%d >= iovlen=%d\n", *iovix, iovlen));2450return 0;2451}2452if (*pageix >= nopages){2453DEBUG_API(printk("map_page_to_iovec: *pageix=%d >= nopages=%d\n", *pageix, nopages));2454return 0;2455}2456iov[*iovix].iov_base = (unsigned char*)page_address(pages[*pageix]) + *pageoffset;2457tmplen = PAGE_SIZE - *pageoffset;2458if (tmplen < map_length){2459(*pageoffset) = 0;2460(*pageix)++;2461} else {2462tmplen = map_length;2463(*pageoffset) += map_length;2464}2465DEBUG(printk("mapping %d bytes from page %d (or %d) to iovec %d\n", tmplen, *pageix, *pageix-1, *iovix));2466iov[*iovix].iov_len = tmplen;2467map_length -= tmplen;2468(*iovix)++;2469}2470DEBUG(printk("map_page_to_iovec, exit, *iovix=%d\n", *iovix));2471return -1;2472}2473247424752476static int cryptocop_ioctl_process(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)2477{2478int i;2479struct cryptocop_private *dev = filp->private_data;2480struct strcop_crypto_op *crp_oper = (struct strcop_crypto_op *)arg;2481struct strcop_crypto_op oper = {0};2482int err = 0;2483struct cryptocop_operation *cop = NULL;24842485struct ioctl_job_cb_ctx *jc = NULL;24862487struct page **inpages = NULL;2488struct page **outpages = NULL;2489int noinpages = 0;2490int nooutpages = 0;24912492struct cryptocop_desc descs[5]; /* Max 5 descriptors are needed, there are three transforms that2493* can get connected/disconnected on different places in the indata. */2494struct cryptocop_desc_cfg dcfgs[5*3];2495int desc_ix = 0;2496int dcfg_ix = 0;2497struct cryptocop_tfrm_cfg ciph_tcfg = {0};2498struct cryptocop_tfrm_cfg digest_tcfg = {0};2499struct cryptocop_tfrm_cfg csum_tcfg = {0};25002501unsigned char *digest_result = NULL;2502int digest_length = 0;2503int cblocklen = 0;2504unsigned char csum_result[CSUM_BLOCK_LENGTH];2505struct cryptocop_session *sess;25062507int iovlen = 0;2508int iovix = 0;2509int pageix = 0;2510int pageoffset = 0;25112512size_t prev_ix = 0;2513size_t next_ix;25142515int cipher_active, digest_active, csum_active;2516int end_digest, end_csum;2517int digest_done = 0;2518int cipher_done = 0;2519int csum_done = 0;25202521DEBUG(printk("cryptocop_ioctl_process\n"));25222523if (!access_ok(VERIFY_WRITE, crp_oper, sizeof(struct strcop_crypto_op))){2524DEBUG_API(printk("cryptocop_ioctl_process: !access_ok crp_oper!\n"));2525return -EFAULT;2526}2527if (copy_from_user(&oper, crp_oper, sizeof(struct strcop_crypto_op))) {2528DEBUG_API(printk("cryptocop_ioctl_process: copy_from_user\n"));2529return -EFAULT;2530}2531DEBUG(print_strcop_crypto_op(&oper));25322533while (dev && dev->sid != oper.ses_id) dev = dev->next;2534if (!dev){2535DEBUG_API(printk("cryptocop_ioctl_process: session %lld not found\n", oper.ses_id));2536return -EINVAL;2537}25382539/* Check buffers. */2540if (((oper.indata + oper.inlen) < oper.indata) || ((oper.cipher_outdata + oper.cipher_outlen) < oper.cipher_outdata)){2541DEBUG_API(printk("cryptocop_ioctl_process: user buffers wrapped around, bad user!\n"));2542return -EINVAL;2543}25442545if (!access_ok(VERIFY_WRITE, oper.cipher_outdata, oper.cipher_outlen)){2546DEBUG_API(printk("cryptocop_ioctl_process: !access_ok out data!\n"));2547return -EFAULT;2548}2549if (!access_ok(VERIFY_READ, oper.indata, oper.inlen)){2550DEBUG_API(printk("cryptocop_ioctl_process: !access_ok in data!\n"));2551return -EFAULT;2552}25532554cop = kmalloc(sizeof(struct cryptocop_operation), GFP_KERNEL);2555if (!cop) {2556DEBUG_API(printk("cryptocop_ioctl_process: kmalloc\n"));2557return -ENOMEM;2558}2559jc = kmalloc(sizeof(struct ioctl_job_cb_ctx), GFP_KERNEL);2560if (!jc) {2561DEBUG_API(printk("cryptocop_ioctl_process: kmalloc\n"));2562err = -ENOMEM;2563goto error_cleanup;2564}2565jc->processed = 0;25662567cop->cb_data = jc;2568cop->cb = ioctl_process_job_callback;2569cop->operation_status = 0;2570cop->use_dmalists = 0;2571cop->in_interrupt = 0;2572cop->fast_callback = 0;2573cop->tfrm_op.tfrm_cfg = NULL;2574cop->tfrm_op.desc = NULL;2575cop->tfrm_op.indata = NULL;2576cop->tfrm_op.incount = 0;2577cop->tfrm_op.inlen = 0;2578cop->tfrm_op.outdata = NULL;2579cop->tfrm_op.outcount = 0;2580cop->tfrm_op.outlen = 0;25812582sess = get_session(oper.ses_id);2583if (!sess){2584DEBUG_API(printk("cryptocop_ioctl_process: bad session id.\n"));2585kfree(cop);2586kfree(jc);2587return -EINVAL;2588}25892590if (oper.do_cipher) {2591unsigned int cipher_outlen = 0;2592struct cryptocop_transform_ctx *tc = get_transform_ctx(sess, CRYPTOCOP_IOCTL_CIPHER_TID);2593if (!tc) {2594DEBUG_API(printk("cryptocop_ioctl_process: no cipher transform in session.\n"));2595err = -EINVAL;2596goto error_cleanup;2597}2598ciph_tcfg.tid = CRYPTOCOP_IOCTL_CIPHER_TID;2599ciph_tcfg.inject_ix = 0;2600ciph_tcfg.flags = 0;2601if ((oper.cipher_start < 0) || (oper.cipher_len <= 0) || (oper.cipher_start > oper.inlen) || ((oper.cipher_start + oper.cipher_len) > oper.inlen)){2602DEBUG_API(printk("cryptocop_ioctl_process: bad cipher length\n"));2603kfree(cop);2604kfree(jc);2605return -EINVAL;2606}2607cblocklen = tc->init.alg == cryptocop_alg_aes ? AES_BLOCK_LENGTH : DES_BLOCK_LENGTH;2608if (oper.cipher_len % cblocklen) {2609kfree(cop);2610kfree(jc);2611DEBUG_API(printk("cryptocop_ioctl_process: cipher inlength not multiple of block length.\n"));2612return -EINVAL;2613}2614cipher_outlen = oper.cipher_len;2615if (tc->init.cipher_mode == cryptocop_cipher_mode_cbc){2616if (oper.cipher_explicit) {2617ciph_tcfg.flags |= CRYPTOCOP_EXPLICIT_IV;2618memcpy(ciph_tcfg.iv, oper.cipher_iv, cblocklen);2619} else {2620cipher_outlen = oper.cipher_len - cblocklen;2621}2622} else {2623if (oper.cipher_explicit){2624kfree(cop);2625kfree(jc);2626DEBUG_API(printk("cryptocop_ioctl_process: explicit_iv when not CBC mode\n"));2627return -EINVAL;2628}2629}2630if (oper.cipher_outlen != cipher_outlen) {2631kfree(cop);2632kfree(jc);2633DEBUG_API(printk("cryptocop_ioctl_process: cipher_outlen incorrect, should be %d not %d.\n", cipher_outlen, oper.cipher_outlen));2634return -EINVAL;2635}26362637if (oper.decrypt){2638ciph_tcfg.flags |= CRYPTOCOP_DECRYPT;2639} else {2640ciph_tcfg.flags |= CRYPTOCOP_ENCRYPT;2641}2642ciph_tcfg.next = cop->tfrm_op.tfrm_cfg;2643cop->tfrm_op.tfrm_cfg = &ciph_tcfg;2644}2645if (oper.do_digest){2646struct cryptocop_transform_ctx *tc = get_transform_ctx(sess, CRYPTOCOP_IOCTL_DIGEST_TID);2647if (!tc) {2648DEBUG_API(printk("cryptocop_ioctl_process: no digest transform in session.\n"));2649err = -EINVAL;2650goto error_cleanup;2651}2652digest_length = tc->init.alg == cryptocop_alg_md5 ? 16 : 20;2653digest_result = kmalloc(digest_length, GFP_KERNEL);2654if (!digest_result) {2655DEBUG_API(printk("cryptocop_ioctl_process: kmalloc digest_result\n"));2656err = -EINVAL;2657goto error_cleanup;2658}2659DEBUG(memset(digest_result, 0xff, digest_length));26602661digest_tcfg.tid = CRYPTOCOP_IOCTL_DIGEST_TID;2662digest_tcfg.inject_ix = 0;2663ciph_tcfg.inject_ix += digest_length;2664if ((oper.digest_start < 0) || (oper.digest_len <= 0) || (oper.digest_start > oper.inlen) || ((oper.digest_start + oper.digest_len) > oper.inlen)){2665DEBUG_API(printk("cryptocop_ioctl_process: bad digest length\n"));2666err = -EINVAL;2667goto error_cleanup;2668}26692670digest_tcfg.next = cop->tfrm_op.tfrm_cfg;2671cop->tfrm_op.tfrm_cfg = &digest_tcfg;2672}2673if (oper.do_csum){2674csum_tcfg.tid = CRYPTOCOP_IOCTL_CSUM_TID;2675csum_tcfg.inject_ix = digest_length;2676ciph_tcfg.inject_ix += 2;26772678if ((oper.csum_start < 0) || (oper.csum_len <= 0) || (oper.csum_start > oper.inlen) || ((oper.csum_start + oper.csum_len) > oper.inlen)){2679DEBUG_API(printk("cryptocop_ioctl_process: bad csum length\n"));2680kfree(cop);2681kfree(jc);2682return -EINVAL;2683}26842685csum_tcfg.next = cop->tfrm_op.tfrm_cfg;2686cop->tfrm_op.tfrm_cfg = &csum_tcfg;2687}26882689prev_ix = first_cfg_change_ix(&oper);2690if (prev_ix > oper.inlen) {2691DEBUG_API(printk("cryptocop_ioctl_process: length mismatch\n"));2692nooutpages = noinpages = 0;2693err = -EINVAL;2694goto error_cleanup;2695}2696DEBUG(printk("cryptocop_ioctl_process: inlen=%d, cipher_outlen=%d\n", oper.inlen, oper.cipher_outlen));26972698/* Map user pages for in and out data of the operation. */2699noinpages = (((unsigned long int)(oper.indata + prev_ix) & ~PAGE_MASK) + oper.inlen - 1 - prev_ix + ~PAGE_MASK) >> PAGE_SHIFT;2700DEBUG(printk("cryptocop_ioctl_process: noinpages=%d\n", noinpages));2701inpages = kmalloc(noinpages * sizeof(struct page*), GFP_KERNEL);2702if (!inpages){2703DEBUG_API(printk("cryptocop_ioctl_process: kmalloc inpages\n"));2704nooutpages = noinpages = 0;2705err = -ENOMEM;2706goto error_cleanup;2707}2708if (oper.do_cipher){2709nooutpages = (((unsigned long int)oper.cipher_outdata & ~PAGE_MASK) + oper.cipher_outlen - 1 + ~PAGE_MASK) >> PAGE_SHIFT;2710DEBUG(printk("cryptocop_ioctl_process: nooutpages=%d\n", nooutpages));2711outpages = kmalloc(nooutpages * sizeof(struct page*), GFP_KERNEL);2712if (!outpages){2713DEBUG_API(printk("cryptocop_ioctl_process: kmalloc outpages\n"));2714nooutpages = noinpages = 0;2715err = -ENOMEM;2716goto error_cleanup;2717}2718}27192720/* Acquire the mm page semaphore. */2721down_read(¤t->mm->mmap_sem);27222723err = get_user_pages(current,2724current->mm,2725(unsigned long int)(oper.indata + prev_ix),2726noinpages,27270, /* read access only for in data */27280, /* no force */2729inpages,2730NULL);27312732if (err < 0) {2733up_read(¤t->mm->mmap_sem);2734nooutpages = noinpages = 0;2735DEBUG_API(printk("cryptocop_ioctl_process: get_user_pages indata\n"));2736goto error_cleanup;2737}2738noinpages = err;2739if (oper.do_cipher){2740err = get_user_pages(current,2741current->mm,2742(unsigned long int)oper.cipher_outdata,2743nooutpages,27441, /* write access for out data */27450, /* no force */2746outpages,2747NULL);2748up_read(¤t->mm->mmap_sem);2749if (err < 0) {2750nooutpages = 0;2751DEBUG_API(printk("cryptocop_ioctl_process: get_user_pages outdata\n"));2752goto error_cleanup;2753}2754nooutpages = err;2755} else {2756up_read(¤t->mm->mmap_sem);2757}27582759/* Add 6 to nooutpages to make room for possibly inserted buffers for storing digest and2760* csum output and splits when units are (dis-)connected. */2761cop->tfrm_op.indata = kmalloc((noinpages) * sizeof(struct iovec), GFP_KERNEL);2762cop->tfrm_op.outdata = kmalloc((6 + nooutpages) * sizeof(struct iovec), GFP_KERNEL);2763if (!cop->tfrm_op.indata || !cop->tfrm_op.outdata) {2764DEBUG_API(printk("cryptocop_ioctl_process: kmalloc iovecs\n"));2765err = -ENOMEM;2766goto error_cleanup;2767}27682769cop->tfrm_op.inlen = oper.inlen - prev_ix;2770cop->tfrm_op.outlen = 0;2771if (oper.do_cipher) cop->tfrm_op.outlen += oper.cipher_outlen;2772if (oper.do_digest) cop->tfrm_op.outlen += digest_length;2773if (oper.do_csum) cop->tfrm_op.outlen += 2;27742775/* Setup the in iovecs. */2776cop->tfrm_op.incount = noinpages;2777if (noinpages > 1){2778size_t tmplen = cop->tfrm_op.inlen;27792780cop->tfrm_op.indata[0].iov_len = PAGE_SIZE - ((unsigned long int)(oper.indata + prev_ix) & ~PAGE_MASK);2781cop->tfrm_op.indata[0].iov_base = (unsigned char*)page_address(inpages[0]) + ((unsigned long int)(oper.indata + prev_ix) & ~PAGE_MASK);2782tmplen -= cop->tfrm_op.indata[0].iov_len;2783for (i = 1; i<noinpages; i++){2784cop->tfrm_op.indata[i].iov_len = tmplen < PAGE_SIZE ? tmplen : PAGE_SIZE;2785cop->tfrm_op.indata[i].iov_base = (unsigned char*)page_address(inpages[i]);2786tmplen -= PAGE_SIZE;2787}2788} else {2789cop->tfrm_op.indata[0].iov_len = oper.inlen - prev_ix;2790cop->tfrm_op.indata[0].iov_base = (unsigned char*)page_address(inpages[0]) + ((unsigned long int)(oper.indata + prev_ix) & ~PAGE_MASK);2791}27922793iovlen = nooutpages + 6;2794pageoffset = oper.do_cipher ? ((unsigned long int)oper.cipher_outdata & ~PAGE_MASK) : 0;27952796next_ix = next_cfg_change_ix(&oper, prev_ix);2797if (prev_ix == next_ix){2798DEBUG_API(printk("cryptocop_ioctl_process: length configuration broken.\n"));2799err = -EINVAL; /* This should be impossible barring bugs. */2800goto error_cleanup;2801}2802while (prev_ix != next_ix){2803end_digest = end_csum = cipher_active = digest_active = csum_active = 0;2804descs[desc_ix].cfg = NULL;2805descs[desc_ix].length = next_ix - prev_ix;28062807if (oper.do_cipher && (oper.cipher_start < next_ix) && (prev_ix < (oper.cipher_start + oper.cipher_len))) {2808dcfgs[dcfg_ix].tid = CRYPTOCOP_IOCTL_CIPHER_TID;2809dcfgs[dcfg_ix].src = cryptocop_source_dma;2810cipher_active = 1;28112812if (next_ix == (oper.cipher_start + oper.cipher_len)){2813cipher_done = 1;2814dcfgs[dcfg_ix].last = 1;2815} else {2816dcfgs[dcfg_ix].last = 0;2817}2818dcfgs[dcfg_ix].next = descs[desc_ix].cfg;2819descs[desc_ix].cfg = &dcfgs[dcfg_ix];2820++dcfg_ix;2821}2822if (oper.do_digest && (oper.digest_start < next_ix) && (prev_ix < (oper.digest_start + oper.digest_len))) {2823digest_active = 1;2824dcfgs[dcfg_ix].tid = CRYPTOCOP_IOCTL_DIGEST_TID;2825dcfgs[dcfg_ix].src = cryptocop_source_dma;2826if (next_ix == (oper.digest_start + oper.digest_len)){2827assert(!digest_done);2828digest_done = 1;2829dcfgs[dcfg_ix].last = 1;2830} else {2831dcfgs[dcfg_ix].last = 0;2832}2833dcfgs[dcfg_ix].next = descs[desc_ix].cfg;2834descs[desc_ix].cfg = &dcfgs[dcfg_ix];2835++dcfg_ix;2836}2837if (oper.do_csum && (oper.csum_start < next_ix) && (prev_ix < (oper.csum_start + oper.csum_len))){2838csum_active = 1;2839dcfgs[dcfg_ix].tid = CRYPTOCOP_IOCTL_CSUM_TID;2840dcfgs[dcfg_ix].src = cryptocop_source_dma;2841if (next_ix == (oper.csum_start + oper.csum_len)){2842csum_done = 1;2843dcfgs[dcfg_ix].last = 1;2844} else {2845dcfgs[dcfg_ix].last = 0;2846}2847dcfgs[dcfg_ix].next = descs[desc_ix].cfg;2848descs[desc_ix].cfg = &dcfgs[dcfg_ix];2849++dcfg_ix;2850}2851if (!descs[desc_ix].cfg){2852DEBUG_API(printk("cryptocop_ioctl_process: data segment %d (%d to %d) had no active transforms\n", desc_ix, prev_ix, next_ix));2853err = -EINVAL;2854goto error_cleanup;2855}2856descs[desc_ix].next = &(descs[desc_ix]) + 1;2857++desc_ix;2858prev_ix = next_ix;2859next_ix = next_cfg_change_ix(&oper, prev_ix);2860}2861if (desc_ix > 0){2862descs[desc_ix-1].next = NULL;2863} else {2864descs[0].next = NULL;2865}2866if (oper.do_digest) {2867DEBUG(printk("cryptocop_ioctl_process: mapping %d byte digest output to iovec %d\n", digest_length, iovix));2868/* Add outdata iovec, length == <length of type of digest> */2869cop->tfrm_op.outdata[iovix].iov_base = digest_result;2870cop->tfrm_op.outdata[iovix].iov_len = digest_length;2871++iovix;2872}2873if (oper.do_csum) {2874/* Add outdata iovec, length == 2, the length of csum. */2875DEBUG(printk("cryptocop_ioctl_process: mapping 2 byte csum output to iovec %d\n", iovix));2876/* Add outdata iovec, length == <length of type of digest> */2877cop->tfrm_op.outdata[iovix].iov_base = csum_result;2878cop->tfrm_op.outdata[iovix].iov_len = 2;2879++iovix;2880}2881if (oper.do_cipher) {2882if (!map_pages_to_iovec(cop->tfrm_op.outdata, iovlen, &iovix, outpages, nooutpages, &pageix, &pageoffset, oper.cipher_outlen)){2883DEBUG_API(printk("cryptocop_ioctl_process: failed to map pages to iovec.\n"));2884err = -ENOSYS; /* This should be impossible barring bugs. */2885goto error_cleanup;2886}2887}2888DEBUG(printk("cryptocop_ioctl_process: setting cop->tfrm_op.outcount %d\n", iovix));2889cop->tfrm_op.outcount = iovix;2890assert(iovix <= (nooutpages + 6));28912892cop->sid = oper.ses_id;2893cop->tfrm_op.desc = &descs[0];28942895DEBUG(printk("cryptocop_ioctl_process: inserting job, cb_data=0x%p\n", cop->cb_data));28962897if ((err = cryptocop_job_queue_insert_user_job(cop)) != 0) {2898DEBUG_API(printk("cryptocop_ioctl_process: insert job %d\n", err));2899err = -EINVAL;2900goto error_cleanup;2901}29022903DEBUG(printk("cryptocop_ioctl_process: begin wait for result\n"));29042905wait_event(cryptocop_ioc_process_wq, (jc->processed != 0));2906DEBUG(printk("cryptocop_ioctl_process: end wait for result\n"));2907if (!jc->processed){2908printk(KERN_WARNING "cryptocop_ioctl_process: job not processed at completion\n");2909err = -EIO;2910goto error_cleanup;2911}29122913/* Job process done. Cipher output should already be correct in job so no post processing of outdata. */2914DEBUG(printk("cryptocop_ioctl_process: operation_status = %d\n", cop->operation_status));2915if (cop->operation_status == 0){2916if (oper.do_digest){2917DEBUG(printk("cryptocop_ioctl_process: copy %d bytes digest to user\n", digest_length));2918err = copy_to_user((unsigned char*)crp_oper + offsetof(struct strcop_crypto_op, digest), digest_result, digest_length);2919if (0 != err){2920DEBUG_API(printk("cryptocop_ioctl_process: copy_to_user, digest length %d, err %d\n", digest_length, err));2921err = -EFAULT;2922goto error_cleanup;2923}2924}2925if (oper.do_csum){2926DEBUG(printk("cryptocop_ioctl_process: copy 2 bytes checksum to user\n"));2927err = copy_to_user((unsigned char*)crp_oper + offsetof(struct strcop_crypto_op, csum), csum_result, 2);2928if (0 != err){2929DEBUG_API(printk("cryptocop_ioctl_process: copy_to_user, csum, err %d\n", err));2930err = -EFAULT;2931goto error_cleanup;2932}2933}2934err = 0;2935} else {2936DEBUG(printk("cryptocop_ioctl_process: returning err = operation_status = %d\n", cop->operation_status));2937err = cop->operation_status;2938}29392940error_cleanup:2941/* Release page caches. */2942for (i = 0; i < noinpages; i++){2943put_page(inpages[i]);2944}2945for (i = 0; i < nooutpages; i++){2946int spdl_err;2947/* Mark output pages dirty. */2948spdl_err = set_page_dirty_lock(outpages[i]);2949DEBUG(if (spdl_err < 0)printk("cryptocop_ioctl_process: set_page_dirty_lock returned %d\n", spdl_err));2950}2951for (i = 0; i < nooutpages; i++){2952put_page(outpages[i]);2953}29542955kfree(digest_result);2956kfree(inpages);2957kfree(outpages);2958if (cop){2959kfree(cop->tfrm_op.indata);2960kfree(cop->tfrm_op.outdata);2961kfree(cop);2962}2963kfree(jc);29642965DEBUG(print_lock_status());29662967return err;2968}296929702971static int cryptocop_ioctl_create_session(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)2972{2973cryptocop_session_id sid;2974int err;2975struct cryptocop_private *dev;2976struct strcop_session_op *sess_op = (struct strcop_session_op *)arg;2977struct strcop_session_op sop;2978struct cryptocop_transform_init *tis = NULL;2979struct cryptocop_transform_init ti_cipher = {0};2980struct cryptocop_transform_init ti_digest = {0};2981struct cryptocop_transform_init ti_csum = {0};29822983if (!access_ok(VERIFY_WRITE, sess_op, sizeof(struct strcop_session_op)))2984return -EFAULT;2985err = copy_from_user(&sop, sess_op, sizeof(struct strcop_session_op));2986if (err) return -EFAULT;2987if (sop.cipher != cryptocop_cipher_none) {2988if (!access_ok(VERIFY_READ, sop.key, sop.keylen)) return -EFAULT;2989}2990DEBUG(printk("cryptocop_ioctl_create_session, sess_op:\n"));29912992DEBUG(printk("\tcipher:%d\n"2993"\tcipher_mode:%d\n"2994"\tdigest:%d\n"2995"\tcsum:%d\n",2996(int)sop.cipher,2997(int)sop.cmode,2998(int)sop.digest,2999(int)sop.csum));30003001if (sop.cipher != cryptocop_cipher_none){3002/* Init the cipher. */3003switch (sop.cipher){3004case cryptocop_cipher_des:3005ti_cipher.alg = cryptocop_alg_des;3006break;3007case cryptocop_cipher_3des:3008ti_cipher.alg = cryptocop_alg_3des;3009break;3010case cryptocop_cipher_aes:3011ti_cipher.alg = cryptocop_alg_aes;3012break;3013default:3014DEBUG_API(printk("create session, bad cipher algorithm %d\n", sop.cipher));3015return -EINVAL;3016};3017DEBUG(printk("setting cipher transform %d\n", ti_cipher.alg));3018copy_from_user(ti_cipher.key, sop.key, sop.keylen/8);3019ti_cipher.keylen = sop.keylen;3020switch (sop.cmode){3021case cryptocop_cipher_mode_cbc:3022case cryptocop_cipher_mode_ecb:3023ti_cipher.cipher_mode = sop.cmode;3024break;3025default:3026DEBUG_API(printk("create session, bad cipher mode %d\n", sop.cmode));3027return -EINVAL;3028}3029DEBUG(printk("cryptocop_ioctl_create_session: setting CBC mode %d\n", ti_cipher.cipher_mode));3030switch (sop.des3_mode){3031case cryptocop_3des_eee:3032case cryptocop_3des_eed:3033case cryptocop_3des_ede:3034case cryptocop_3des_edd:3035case cryptocop_3des_dee:3036case cryptocop_3des_ded:3037case cryptocop_3des_dde:3038case cryptocop_3des_ddd:3039ti_cipher.tdes_mode = sop.des3_mode;3040break;3041default:3042DEBUG_API(printk("create session, bad 3DES mode %d\n", sop.des3_mode));3043return -EINVAL;3044}3045ti_cipher.tid = CRYPTOCOP_IOCTL_CIPHER_TID;3046ti_cipher.next = tis;3047tis = &ti_cipher;3048} /* if (sop.cipher != cryptocop_cipher_none) */3049if (sop.digest != cryptocop_digest_none){3050DEBUG(printk("setting digest transform\n"));3051switch (sop.digest){3052case cryptocop_digest_md5:3053ti_digest.alg = cryptocop_alg_md5;3054break;3055case cryptocop_digest_sha1:3056ti_digest.alg = cryptocop_alg_sha1;3057break;3058default:3059DEBUG_API(printk("create session, bad digest algorithm %d\n", sop.digest));3060return -EINVAL;3061}3062ti_digest.tid = CRYPTOCOP_IOCTL_DIGEST_TID;3063ti_digest.next = tis;3064tis = &ti_digest;3065} /* if (sop.digest != cryptocop_digest_none) */3066if (sop.csum != cryptocop_csum_none){3067DEBUG(printk("setting csum transform\n"));3068switch (sop.csum){3069case cryptocop_csum_le:3070case cryptocop_csum_be:3071ti_csum.csum_mode = sop.csum;3072break;3073default:3074DEBUG_API(printk("create session, bad checksum algorithm %d\n", sop.csum));3075return -EINVAL;3076}3077ti_csum.alg = cryptocop_alg_csum;3078ti_csum.tid = CRYPTOCOP_IOCTL_CSUM_TID;3079ti_csum.next = tis;3080tis = &ti_csum;3081} /* (sop.csum != cryptocop_csum_none) */3082dev = kmalloc(sizeof(struct cryptocop_private), GFP_KERNEL);3083if (!dev){3084DEBUG_API(printk("create session, alloc dev\n"));3085return -ENOMEM;3086}30873088err = cryptocop_new_session(&sid, tis, GFP_KERNEL);3089DEBUG({ if (err) printk("create session, cryptocop_new_session %d\n", err);});30903091if (err) {3092kfree(dev);3093return err;3094}3095sess_op->ses_id = sid;3096dev->sid = sid;3097dev->next = filp->private_data;3098filp->private_data = dev;30993100return 0;3101}31023103static long cryptocop_ioctl_unlocked(struct inode *inode,3104struct file *filp, unsigned int cmd, unsigned long arg)3105{3106int err = 0;3107if (_IOC_TYPE(cmd) != ETRAXCRYPTOCOP_IOCTYPE) {3108DEBUG_API(printk("cryptocop_ioctl: wrong type\n"));3109return -ENOTTY;3110}3111if (_IOC_NR(cmd) > CRYPTOCOP_IO_MAXNR){3112return -ENOTTY;3113}3114/* Access check of the argument. Some commands, e.g. create session and process op,3115needs additional checks. Those are handled in the command handling functions. */3116if (_IOC_DIR(cmd) & _IOC_READ)3117err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));3118else if (_IOC_DIR(cmd) & _IOC_WRITE)3119err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));3120if (err) return -EFAULT;31213122switch (cmd) {3123case CRYPTOCOP_IO_CREATE_SESSION:3124return cryptocop_ioctl_create_session(inode, filp, cmd, arg);3125case CRYPTOCOP_IO_CLOSE_SESSION:3126return cryptocop_ioctl_close_session(inode, filp, cmd, arg);3127case CRYPTOCOP_IO_PROCESS_OP:3128return cryptocop_ioctl_process(inode, filp, cmd, arg);3129default:3130DEBUG_API(printk("cryptocop_ioctl: unknown command\n"));3131return -ENOTTY;3132}3133return 0;3134}31353136static long3137cryptocop_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)3138{3139struct inode *inode = file->f_path.dentry->d_inode;3140long ret;31413142mutex_lock(&cryptocop_mutex);3143ret = cryptocop_ioctl_unlocked(inode, filp, cmd, arg);3144mutex_unlock(&cryptocop_mutex);31453146return ret;3147}314831493150#ifdef LDEBUG3151static void print_dma_descriptors(struct cryptocop_int_operation *iop)3152{3153struct cryptocop_dma_desc *cdesc_out = iop->cdesc_out;3154struct cryptocop_dma_desc *cdesc_in = iop->cdesc_in;3155int i;31563157printk("print_dma_descriptors start\n");31583159printk("iop:\n");3160printk("\tsid: 0x%lld\n", iop->sid);31613162printk("\tcdesc_out: 0x%p\n", iop->cdesc_out);3163printk("\tcdesc_in: 0x%p\n", iop->cdesc_in);3164printk("\tddesc_out: 0x%p\n", iop->ddesc_out);3165printk("\tddesc_in: 0x%p\n", iop->ddesc_in);31663167printk("\niop->ctx_out: 0x%p phys: 0x%p\n", &iop->ctx_out, (char*)virt_to_phys(&iop->ctx_out));3168printk("\tnext: 0x%p\n"3169"\tsaved_data: 0x%p\n"3170"\tsaved_data_buf: 0x%p\n",3171iop->ctx_out.next,3172iop->ctx_out.saved_data,3173iop->ctx_out.saved_data_buf);31743175printk("\niop->ctx_in: 0x%p phys: 0x%p\n", &iop->ctx_in, (char*)virt_to_phys(&iop->ctx_in));3176printk("\tnext: 0x%p\n"3177"\tsaved_data: 0x%p\n"3178"\tsaved_data_buf: 0x%p\n",3179iop->ctx_in.next,3180iop->ctx_in.saved_data,3181iop->ctx_in.saved_data_buf);31823183i = 0;3184while (cdesc_out) {3185dma_descr_data *td;3186printk("cdesc_out %d, desc=0x%p\n", i, cdesc_out->dma_descr);3187printk("\n\tvirt_to_phys(desc): 0x%p\n", (char*)virt_to_phys(cdesc_out->dma_descr));3188td = cdesc_out->dma_descr;3189printk("\n\tbuf: 0x%p\n"3190"\tafter: 0x%p\n"3191"\tmd: 0x%04x\n"3192"\tnext: 0x%p\n",3193td->buf,3194td->after,3195td->md,3196td->next);3197printk("flags:\n"3198"\twait:\t%d\n"3199"\teol:\t%d\n"3200"\touteop:\t%d\n"3201"\tineop:\t%d\n"3202"\tintr:\t%d\n",3203td->wait,3204td->eol,3205td->out_eop,3206td->in_eop,3207td->intr);3208cdesc_out = cdesc_out->next;3209i++;3210}3211i = 0;3212while (cdesc_in) {3213dma_descr_data *td;3214printk("cdesc_in %d, desc=0x%p\n", i, cdesc_in->dma_descr);3215printk("\n\tvirt_to_phys(desc): 0x%p\n", (char*)virt_to_phys(cdesc_in->dma_descr));3216td = cdesc_in->dma_descr;3217printk("\n\tbuf: 0x%p\n"3218"\tafter: 0x%p\n"3219"\tmd: 0x%04x\n"3220"\tnext: 0x%p\n",3221td->buf,3222td->after,3223td->md,3224td->next);3225printk("flags:\n"3226"\twait:\t%d\n"3227"\teol:\t%d\n"3228"\touteop:\t%d\n"3229"\tineop:\t%d\n"3230"\tintr:\t%d\n",3231td->wait,3232td->eol,3233td->out_eop,3234td->in_eop,3235td->intr);3236cdesc_in = cdesc_in->next;3237i++;3238}32393240printk("print_dma_descriptors end\n");3241}324232433244static void print_strcop_crypto_op(struct strcop_crypto_op *cop)3245{3246printk("print_strcop_crypto_op, 0x%p\n", cop);32473248/* Indata. */3249printk("indata=0x%p\n"3250"inlen=%d\n"3251"do_cipher=%d\n"3252"decrypt=%d\n"3253"cipher_explicit=%d\n"3254"cipher_start=%d\n"3255"cipher_len=%d\n"3256"outdata=0x%p\n"3257"outlen=%d\n",3258cop->indata,3259cop->inlen,3260cop->do_cipher,3261cop->decrypt,3262cop->cipher_explicit,3263cop->cipher_start,3264cop->cipher_len,3265cop->cipher_outdata,3266cop->cipher_outlen);32673268printk("do_digest=%d\n"3269"digest_start=%d\n"3270"digest_len=%d\n",3271cop->do_digest,3272cop->digest_start,3273cop->digest_len);32743275printk("do_csum=%d\n"3276"csum_start=%d\n"3277"csum_len=%d\n",3278cop->do_csum,3279cop->csum_start,3280cop->csum_len);3281}32823283static void print_cryptocop_operation(struct cryptocop_operation *cop)3284{3285struct cryptocop_desc *d;3286struct cryptocop_tfrm_cfg *tc;3287struct cryptocop_desc_cfg *dc;3288int i;32893290printk("print_cryptocop_operation, cop=0x%p\n\n", cop);3291printk("sid: %lld\n", cop->sid);3292printk("operation_status=%d\n"3293"use_dmalists=%d\n"3294"in_interrupt=%d\n"3295"fast_callback=%d\n",3296cop->operation_status,3297cop->use_dmalists,3298cop->in_interrupt,3299cop->fast_callback);33003301if (cop->use_dmalists){3302print_user_dma_lists(&cop->list_op);3303} else {3304printk("cop->tfrm_op\n"3305"tfrm_cfg=0x%p\n"3306"desc=0x%p\n"3307"indata=0x%p\n"3308"incount=%d\n"3309"inlen=%d\n"3310"outdata=0x%p\n"3311"outcount=%d\n"3312"outlen=%d\n\n",3313cop->tfrm_op.tfrm_cfg,3314cop->tfrm_op.desc,3315cop->tfrm_op.indata,3316cop->tfrm_op.incount,3317cop->tfrm_op.inlen,3318cop->tfrm_op.outdata,3319cop->tfrm_op.outcount,3320cop->tfrm_op.outlen);33213322tc = cop->tfrm_op.tfrm_cfg;3323while (tc){3324printk("tfrm_cfg, 0x%p\n"3325"tid=%d\n"3326"flags=%d\n"3327"inject_ix=%d\n"3328"next=0x%p\n",3329tc,3330tc->tid,3331tc->flags,3332tc->inject_ix,3333tc->next);3334tc = tc->next;3335}3336d = cop->tfrm_op.desc;3337while (d){3338printk("\n======================desc, 0x%p\n"3339"length=%d\n"3340"cfg=0x%p\n"3341"next=0x%p\n",3342d,3343d->length,3344d->cfg,3345d->next);3346dc = d->cfg;3347while (dc){3348printk("=========desc_cfg, 0x%p\n"3349"tid=%d\n"3350"src=%d\n"3351"last=%d\n"3352"next=0x%p\n",3353dc,3354dc->tid,3355dc->src,3356dc->last,3357dc->next);3358dc = dc->next;3359}3360d = d->next;3361}3362printk("\n====iniov\n");3363for (i = 0; i < cop->tfrm_op.incount; i++){3364printk("indata[%d]\n"3365"base=0x%p\n"3366"len=%d\n",3367i,3368cop->tfrm_op.indata[i].iov_base,3369cop->tfrm_op.indata[i].iov_len);3370}3371printk("\n====outiov\n");3372for (i = 0; i < cop->tfrm_op.outcount; i++){3373printk("outdata[%d]\n"3374"base=0x%p\n"3375"len=%d\n",3376i,3377cop->tfrm_op.outdata[i].iov_base,3378cop->tfrm_op.outdata[i].iov_len);3379}3380}3381printk("------------end print_cryptocop_operation\n");3382}338333843385static void print_user_dma_lists(struct cryptocop_dma_list_operation *dma_op)3386{3387dma_descr_data *dd;3388int i;33893390printk("print_user_dma_lists, dma_op=0x%p\n", dma_op);33913392printk("out_data_buf = 0x%p, phys_to_virt(out_data_buf) = 0x%p\n", dma_op->out_data_buf, phys_to_virt((unsigned long int)dma_op->out_data_buf));3393printk("in_data_buf = 0x%p, phys_to_virt(in_data_buf) = 0x%p\n", dma_op->in_data_buf, phys_to_virt((unsigned long int)dma_op->in_data_buf));33943395printk("##############outlist\n");3396dd = phys_to_virt((unsigned long int)dma_op->outlist);3397i = 0;3398while (dd != NULL) {3399printk("#%d phys_to_virt(desc) 0x%p\n", i, dd);3400printk("\n\tbuf: 0x%p\n"3401"\tafter: 0x%p\n"3402"\tmd: 0x%04x\n"3403"\tnext: 0x%p\n",3404dd->buf,3405dd->after,3406dd->md,3407dd->next);3408printk("flags:\n"3409"\twait:\t%d\n"3410"\teol:\t%d\n"3411"\touteop:\t%d\n"3412"\tineop:\t%d\n"3413"\tintr:\t%d\n",3414dd->wait,3415dd->eol,3416dd->out_eop,3417dd->in_eop,3418dd->intr);3419if (dd->eol)3420dd = NULL;3421else3422dd = phys_to_virt((unsigned long int)dd->next);3423++i;3424}34253426printk("##############inlist\n");3427dd = phys_to_virt((unsigned long int)dma_op->inlist);3428i = 0;3429while (dd != NULL) {3430printk("#%d phys_to_virt(desc) 0x%p\n", i, dd);3431printk("\n\tbuf: 0x%p\n"3432"\tafter: 0x%p\n"3433"\tmd: 0x%04x\n"3434"\tnext: 0x%p\n",3435dd->buf,3436dd->after,3437dd->md,3438dd->next);3439printk("flags:\n"3440"\twait:\t%d\n"3441"\teol:\t%d\n"3442"\touteop:\t%d\n"3443"\tineop:\t%d\n"3444"\tintr:\t%d\n",3445dd->wait,3446dd->eol,3447dd->out_eop,3448dd->in_eop,3449dd->intr);3450if (dd->eol)3451dd = NULL;3452else3453dd = phys_to_virt((unsigned long int)dd->next);3454++i;3455}3456}345734583459static void print_lock_status(void)3460{3461printk("**********************print_lock_status\n");3462printk("cryptocop_completed_jobs_lock %d\n", spin_is_locked(&cryptocop_completed_jobs_lock));3463printk("cryptocop_job_queue_lock %d\n", spin_is_locked(&cryptocop_job_queue_lock));3464printk("descr_pool_lock %d\n", spin_is_locked(&descr_pool_lock));3465printk("cryptocop_sessions_lock %d\n", spin_is_locked(cryptocop_sessions_lock));3466printk("running_job_lock %d\n", spin_is_locked(running_job_lock));3467printk("cryptocop_process_lock %d\n", spin_is_locked(cryptocop_process_lock));3468}3469#endif /* LDEBUG */347034713472static const char cryptocop_name[] = "ETRAX FS stream co-processor";34733474static int init_stream_coprocessor(void)3475{3476int err;3477int i;3478static int initialized = 0;34793480if (initialized)3481return 0;34823483initialized = 1;34843485printk("ETRAX FS stream co-processor driver v0.01, (c) 2003 Axis Communications AB\n");34863487err = register_chrdev(CRYPTOCOP_MAJOR, cryptocop_name, &cryptocop_fops);3488if (err < 0) {3489printk(KERN_ERR "stream co-processor: could not get major number.\n");3490return err;3491}34923493err = init_cryptocop();3494if (err) {3495(void)unregister_chrdev(CRYPTOCOP_MAJOR, cryptocop_name);3496return err;3497}3498err = cryptocop_job_queue_init();3499if (err) {3500release_cryptocop();3501(void)unregister_chrdev(CRYPTOCOP_MAJOR, cryptocop_name);3502return err;3503}3504/* Init the descriptor pool. */3505for (i = 0; i < CRYPTOCOP_DESCRIPTOR_POOL_SIZE - 1; i++) {3506descr_pool[i].from_pool = 1;3507descr_pool[i].next = &descr_pool[i + 1];3508}3509descr_pool[i].from_pool = 1;3510descr_pool[i].next = NULL;3511descr_pool_free_list = &descr_pool[0];3512descr_pool_no_free = CRYPTOCOP_DESCRIPTOR_POOL_SIZE;35133514spin_lock_init(&cryptocop_completed_jobs_lock);3515spin_lock_init(&cryptocop_job_queue_lock);3516spin_lock_init(&descr_pool_lock);3517spin_lock_init(&cryptocop_sessions_lock);3518spin_lock_init(&running_job_lock);3519spin_lock_init(&cryptocop_process_lock);35203521cryptocop_sessions = NULL;3522next_sid = 1;35233524cryptocop_running_job = NULL;35253526printk("stream co-processor: init done.\n");3527return 0;3528}35293530static void __exit exit_stream_coprocessor(void)3531{3532release_cryptocop();3533cryptocop_job_queue_close();3534}35353536module_init(init_stream_coprocessor);3537module_exit(exit_stream_coprocessor);3538353935403541