📄 cryptocop.c
字号:
/* $Id: cryptocop.c,v 1.13 2005/04/21 17:27:55 henriken Exp $ * * Stream co-processor driver for the ETRAX FS * * Copyright (C) 2003-2005 Axis Communications AB */#include <linux/init.h>#include <linux/sched.h>#include <linux/module.h>#include <linux/slab.h>#include <linux/string.h>#include <linux/fs.h>#include <linux/mm.h>#include <linux/spinlock.h>#include <linux/stddef.h>#include <asm/uaccess.h>#include <asm/io.h>#include <asm/atomic.h>#include <linux/list.h>#include <linux/interrupt.h>#include <asm/signal.h>#include <asm/irq.h>#include <asm/arch/dma.h>#include <asm/arch/hwregs/dma.h>#include <asm/arch/hwregs/reg_map.h>#include <asm/arch/hwregs/reg_rdwr.h>#include <asm/arch/hwregs/intr_vect_defs.h>#include <asm/arch/hwregs/strcop.h>#include <asm/arch/hwregs/strcop_defs.h>#include <asm/arch/cryptocop.h>#define DESCR_ALLOC_PAD (31)struct cryptocop_dma_desc { char *free_buf; /* If non-null will be kfreed in free_cdesc() */ dma_descr_data *dma_descr; unsigned char dma_descr_buf[sizeof(dma_descr_data) + DESCR_ALLOC_PAD]; unsigned int from_pool:1; /* If 1 'allocated' from the descriptor pool. */ struct cryptocop_dma_desc *next;};struct cryptocop_int_operation{ void *alloc_ptr; cryptocop_session_id sid; dma_descr_context ctx_out; dma_descr_context ctx_in; /* DMA descriptors allocated by driver. */ struct cryptocop_dma_desc *cdesc_out; struct cryptocop_dma_desc *cdesc_in; /* Strcop config to use. */ cryptocop_3des_mode tdes_mode; cryptocop_csum_type csum_mode; /* DMA descrs provided by consumer. */ dma_descr_data *ddesc_out; dma_descr_data *ddesc_in;};struct cryptocop_tfrm_ctx { cryptocop_tfrm_id tid; unsigned int blocklength; unsigned int start_ix; struct cryptocop_tfrm_cfg *tcfg; struct cryptocop_transform_ctx *tctx; unsigned char previous_src; unsigned char current_src; /* Values to use in metadata out. */ unsigned char hash_conf; unsigned char hash_mode; unsigned char ciph_conf; unsigned char cbcmode; unsigned char decrypt; unsigned int requires_padding:1; unsigned int strict_block_length:1; unsigned int active:1; unsigned int done:1; size_t consumed; size_t produced; /* Pad (input) descriptors to put in the DMA out list when the transform * output is put on the DMA in list. */ struct cryptocop_dma_desc *pad_descs; struct cryptocop_tfrm_ctx *prev_src; struct cryptocop_tfrm_ctx *curr_src; /* Mapping to HW. */ unsigned char unit_no;};struct cryptocop_private{ cryptocop_session_id sid; struct cryptocop_private *next;};/* Session list. */struct cryptocop_transform_ctx{ struct cryptocop_transform_init init; unsigned char dec_key[CRYPTOCOP_MAX_KEY_LENGTH]; unsigned int dec_key_set:1; struct cryptocop_transform_ctx *next;};struct cryptocop_session{ cryptocop_session_id sid; struct cryptocop_transform_ctx *tfrm_ctx; struct cryptocop_session *next;};/* Priority levels for jobs sent to the cryptocop. Checksum operations from kernel have highest priority since TCPIP stack processing must not be a bottleneck. */typedef enum { cryptocop_prio_kernel_csum = 0, cryptocop_prio_kernel = 1, cryptocop_prio_user = 2, cryptocop_prio_no_prios = 3} cryptocop_queue_priority;struct cryptocop_prio_queue{ struct list_head jobs; cryptocop_queue_priority prio;};struct cryptocop_prio_job{ struct list_head node; cryptocop_queue_priority prio; struct cryptocop_operation *oper; struct cryptocop_int_operation *iop;};struct ioctl_job_cb_ctx { unsigned int processed:1;};static struct cryptocop_session *cryptocop_sessions = NULL;spinlock_t cryptocop_sessions_lock;/* Next Session ID to assign. */static cryptocop_session_id next_sid = 1;/* Pad for checksum. */static const char csum_zero_pad[1] = {0x00};/* Trash buffer for mem2mem operations. */#define MEM2MEM_DISCARD_BUF_LENGTH (512)static unsigned char mem2mem_discard_buf[MEM2MEM_DISCARD_BUF_LENGTH];/* Descriptor pool. *//* FIXME Tweak this value. */#define CRYPTOCOP_DESCRIPTOR_POOL_SIZE (100)static struct cryptocop_dma_desc descr_pool[CRYPTOCOP_DESCRIPTOR_POOL_SIZE];static struct cryptocop_dma_desc *descr_pool_free_list;static int descr_pool_no_free;static spinlock_t descr_pool_lock;/* Lock to stop cryptocop to start processing of a new operation. The holder of this lock MUST call cryptocop_start_job() after it is unlocked. */spinlock_t cryptocop_process_lock;static struct cryptocop_prio_queue cryptocop_job_queues[cryptocop_prio_no_prios];static spinlock_t cryptocop_job_queue_lock;static struct cryptocop_prio_job *cryptocop_running_job = NULL;static spinlock_t running_job_lock;/* The interrupt handler appends completed jobs to this list. The scehduled * tasklet removes them upon sending the response to the crypto consumer. */static struct list_head cryptocop_completed_jobs;static spinlock_t cryptocop_completed_jobs_lock;DECLARE_WAIT_QUEUE_HEAD(cryptocop_ioc_process_wq);/** Local functions. **/static int cryptocop_open(struct inode *, struct file *);static int cryptocop_release(struct inode *, struct file *);static int cryptocop_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);static void cryptocop_start_job(void);static int cryptocop_job_queue_insert(cryptocop_queue_priority prio, struct cryptocop_operation *operation);static int cryptocop_job_setup(struct cryptocop_prio_job **pj, struct cryptocop_operation *operation);static int cryptocop_job_queue_init(void);static void cryptocop_job_queue_close(void);static int create_md5_pad(int alloc_flag, unsigned long long hashed_length, char **pad, size_t *pad_length);static int create_sha1_pad(int alloc_flag, unsigned long long hashed_length, char **pad, size_t *pad_length);static int transform_ok(struct cryptocop_transform_init *tinit);static struct cryptocop_session *get_session(cryptocop_session_id sid);static struct cryptocop_transform_ctx *get_transform_ctx(struct cryptocop_session *sess, cryptocop_tfrm_id tid);static void delete_internal_operation(struct cryptocop_int_operation *iop);static void get_aes_decrypt_key(unsigned char *dec_key, const unsigned char *key, unsigned int keylength);static int init_stream_coprocessor(void);static void __exit exit_stream_coprocessor(void);/*#define LDEBUG*/#ifdef LDEBUG#define DEBUG(s) s#define DEBUG_API(s) sstatic void print_cryptocop_operation(struct cryptocop_operation *cop);static void print_dma_descriptors(struct cryptocop_int_operation *iop);static void print_strcop_crypto_op(struct strcop_crypto_op *cop);static void print_lock_status(void);static void print_user_dma_lists(struct cryptocop_dma_list_operation *dma_op);#define assert(s) do{if (!(s)) panic(#s);} while(0);#else#define DEBUG(s)#define DEBUG_API(s)#define assert(s)#endif/* Transform constants. */#define DES_BLOCK_LENGTH (8)#define AES_BLOCK_LENGTH (16)#define MD5_BLOCK_LENGTH (64)#define SHA1_BLOCK_LENGTH (64)#define CSUM_BLOCK_LENGTH (2)#define MD5_STATE_LENGTH (16)#define SHA1_STATE_LENGTH (20)/* The device number. */#define CRYPTOCOP_MAJOR (254)#define CRYPTOCOP_MINOR (0)const struct file_operations cryptocop_fops = { .owner = THIS_MODULE, .open = cryptocop_open, .release = cryptocop_release, .ioctl = cryptocop_ioctl};static void free_cdesc(struct cryptocop_dma_desc *cdesc){ DEBUG(printk("free_cdesc: cdesc 0x%p, from_pool=%d\n", cdesc, cdesc->from_pool)); kfree(cdesc->free_buf); if (cdesc->from_pool) { unsigned long int flags; spin_lock_irqsave(&descr_pool_lock, flags); cdesc->next = descr_pool_free_list; descr_pool_free_list = cdesc; ++descr_pool_no_free; spin_unlock_irqrestore(&descr_pool_lock, flags); } else { kfree(cdesc); }}static struct cryptocop_dma_desc *alloc_cdesc(int alloc_flag){ int use_pool = (alloc_flag & GFP_ATOMIC) ? 1 : 0; struct cryptocop_dma_desc *cdesc; if (use_pool) { unsigned long int flags; spin_lock_irqsave(&descr_pool_lock, flags); if (!descr_pool_free_list) { spin_unlock_irqrestore(&descr_pool_lock, flags); DEBUG_API(printk("alloc_cdesc: pool is empty\n")); return NULL; } cdesc = descr_pool_free_list; descr_pool_free_list = descr_pool_free_list->next; --descr_pool_no_free; spin_unlock_irqrestore(&descr_pool_lock, flags); cdesc->from_pool = 1; } else { cdesc = kmalloc(sizeof(struct cryptocop_dma_desc), alloc_flag); if (!cdesc) { DEBUG_API(printk("alloc_cdesc: kmalloc\n")); return NULL; } cdesc->from_pool = 0; } cdesc->dma_descr = (dma_descr_data*)(((unsigned long int)cdesc + offsetof(struct cryptocop_dma_desc, dma_descr_buf) + DESCR_ALLOC_PAD) & ~0x0000001F); cdesc->next = NULL; cdesc->free_buf = NULL; cdesc->dma_descr->out_eop = 0; cdesc->dma_descr->in_eop = 0; cdesc->dma_descr->intr = 0; cdesc->dma_descr->eol = 0; cdesc->dma_descr->wait = 0; cdesc->dma_descr->buf = NULL; cdesc->dma_descr->after = NULL; DEBUG_API(printk("alloc_cdesc: return 0x%p, cdesc->dma_descr=0x%p, from_pool=%d\n", cdesc, cdesc->dma_descr, cdesc->from_pool)); return cdesc;}static void setup_descr_chain(struct cryptocop_dma_desc *cd){ DEBUG(printk("setup_descr_chain: entering\n")); while (cd) { if (cd->next) { cd->dma_descr->next = (dma_descr_data*)virt_to_phys(cd->next->dma_descr); } else { cd->dma_descr->next = NULL; } cd = cd->next; } DEBUG(printk("setup_descr_chain: exit\n"));}/* Create a pad descriptor for the transform. * Return -1 for error, 0 if pad created. */static int create_pad_descriptor(struct cryptocop_tfrm_ctx *tc, struct cryptocop_dma_desc **pad_desc, int alloc_flag){ struct cryptocop_dma_desc *cdesc = NULL; int error = 0; struct strcop_meta_out mo = { .ciphsel = src_none, .hashsel = src_none, .csumsel = src_none }; char *pad; size_t plen; DEBUG(printk("create_pad_descriptor: start.\n")); /* Setup pad descriptor. */ DEBUG(printk("create_pad_descriptor: setting up padding.\n")); cdesc = alloc_cdesc(alloc_flag); if (!cdesc){ DEBUG_API(printk("create_pad_descriptor: alloc pad desc\n")); goto error_cleanup; } switch (tc->unit_no) { case src_md5: error = create_md5_pad(alloc_flag, tc->consumed, &pad, &plen); if (error){ DEBUG_API(printk("create_pad_descriptor: create_md5_pad_failed\n")); goto error_cleanup; } cdesc->free_buf = pad; mo.hashsel = src_dma; mo.hashconf = tc->hash_conf; mo.hashmode = tc->hash_mode; break; case src_sha1: error = create_sha1_pad(alloc_flag, tc->consumed, &pad, &plen); if (error){ DEBUG_API(printk("create_pad_descriptor: create_sha1_pad_failed\n")); goto error_cleanup; } cdesc->free_buf = pad; mo.hashsel = src_dma; mo.hashconf = tc->hash_conf; mo.hashmode = tc->hash_mode; break; case src_csum: if (tc->consumed % tc->blocklength){ pad = (char*)csum_zero_pad; plen = 1; } else { pad = (char*)cdesc; /* Use any pointer. */ plen = 0; } mo.csumsel = src_dma; break; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -