/* * * sep_crypto.c - Crypto interface structures * * Copyright(c) 2009-2011 Intel Corporation. All rights reserved. * Contributions(c) 2009-2010 Discretix. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 * Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * CONTACTS: * * Mark Allyn mark.a.allyn@intel.com * Jayant Mangalampalli jayant.mangalampalli@intel.com * * CHANGES: * * 2009.06.26 Initial publish * 2010.09.14 Upgrade to Medfield * 2011.02.22 Enable Kernel Crypto * */ /* #define DEBUG */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sep_driver_hw_defs.h" #include "sep_driver_config.h" #include "sep_driver_api.h" #include "sep_dev.h" #include "sep_crypto.h" #if defined(CONFIG_CRYPTO) || defined(CONFIG_CRYPTO_MODULE) /* Globals for queuing */ static spinlock_t queue_lock; static struct crypto_queue sep_queue; /* Declare of dequeuer */ static void sep_dequeuer(void *data); /* TESTING */ /** * sep_do_callback * @work: pointer to work_struct * This is what is called by the queue; it is generic so that it * can be used by any type of operation as each different callback * function can use the data parameter in its own way */ static void sep_do_callback(struct work_struct *work) { struct sep_work_struct *sep_work = container_of(work, struct sep_work_struct, work); if (sep_work != NULL) { (sep_work->callback)(sep_work->data); kfree(sep_work); } else { pr_debug("sep crypto: do callback - NULL container\n"); } } /** * sep_submit_work * @work_queue: pointer to struct_workqueue * @funct: pointer to function to execute * @data: pointer to data; function will know * how to use it * This is a generic API to submit something to * the queue. The callback function will depend * on what operation is to be done */ static int sep_submit_work(struct workqueue_struct *work_queue, void(*funct)(void *), void *data) { struct sep_work_struct *sep_work; int result; sep_work = kmalloc(sizeof(struct sep_work_struct), GFP_ATOMIC); if (sep_work == NULL) { pr_debug("sep crypto: cant allocate work structure\n"); return -ENOMEM; } sep_work->callback = funct; sep_work->data = data; INIT_WORK(&sep_work->work, sep_do_callback); result = queue_work(work_queue, &sep_work->work); if (!result) { pr_debug("sep_crypto: queue_work failed\n"); return -EINVAL; } return 0; } /** * sep_alloc_sg_buf - * @sep: pointer to struct sep_device * @size: total size of area * @block_size: minimum size of chunks * each page is minimum or modulo this size * @returns: pointer to struct scatterlist for new * buffer **/ static struct scatterlist *sep_alloc_sg_buf( struct sep_device *sep, size_t size, size_t block_size) { u32 nbr_pages; u32 ct1; void *buf; size_t current_size; size_t real_page_size; struct scatterlist *sg, *sg_temp; if (size == 0) return NULL; dev_dbg(&sep->pdev->dev, "sep alloc sg buf\n"); current_size = 0; nbr_pages = 0; real_page_size = PAGE_SIZE - (PAGE_SIZE % block_size); /** * The size of each page must be modulo of the operation * block size; increment by the modified page size until * the total size is reached, then you have the number of * pages */ while (current_size < size) { current_size += real_page_size; nbr_pages += 1; } sg = kmalloc_array(nbr_pages, sizeof(struct scatterlist), GFP_ATOMIC); if (!sg) return NULL; sg_init_table(sg, nbr_pages); current_size = 0; sg_temp = sg; for (ct1 = 0; ct1 < nbr_pages; ct1 += 1) { buf = (void *)get_zeroed_page(GFP_ATOMIC); if (!buf) { dev_warn(&sep->pdev->dev, "Cannot allocate page for new buffer\n"); kfree(sg); return NULL; } sg_set_buf(sg_temp, buf, real_page_size); if ((size - current_size) > real_page_size) { sg_temp->length = real_page_size; current_size += real_page_size; } else { sg_temp->length = (size - current_size); current_size = size; } sg_temp = sg_next(sg); } return sg; } /** * sep_free_sg_buf - * @sg: pointer to struct scatterlist; points to area to free */ static void sep_free_sg_buf(struct scatterlist *sg) { struct scatterlist *sg_temp = sg; while (sg_temp) { free_page((unsigned long)sg_virt(sg_temp)); sg_temp = sg_next(sg_temp); } kfree(sg); } /** * sep_copy_sg - * @sep: pointer to struct sep_device * @sg_src: pointer to struct scatterlist for source * @sg_dst: pointer to struct scatterlist for destination * @size: size (in bytes) of data to copy * * Copy data from one scatterlist to another; both must * be the same size */ static void sep_copy_sg( struct sep_device *sep, struct scatterlist *sg_src, struct scatterlist *sg_dst, size_t size) { u32 seg_size; u32 in_offset, out_offset; u32 count = 0; struct scatterlist *sg_src_tmp = sg_src; struct scatterlist *sg_dst_tmp = sg_dst; in_offset = 0; out_offset = 0; dev_dbg(&sep->pdev->dev, "sep copy sg\n"); if ((sg_src == NULL) || (sg_dst == NULL) || (size == 0)) return; dev_dbg(&sep->pdev->dev, "sep copy sg not null\n"); while (count < size) { if ((sg_src_tmp->length - in_offset) > (sg_dst_tmp->length - out_offset)) seg_size = sg_dst_tmp->length - out_offset; else seg_size = sg_src_tmp->length - in_offset; if (seg_size > (size - count)) seg_size = (size = count); memcpy(sg_virt(sg_dst_tmp) + out_offset, sg_virt(sg_src_tmp) + in_offset, seg_size); in_offset += seg_size; out_offset += seg_size; count += seg_size; if (in_offset >= sg_src_tmp->length) { sg_src_tmp = sg_next(sg_src_tmp); in_offset = 0; } if (out_offset >= sg_dst_tmp->length) { sg_dst_tmp = sg_next(sg_dst_tmp); out_offset = 0; } } } /** * sep_oddball_pages - * @sep: pointer to struct sep_device * @sg: pointer to struct scatterlist - buffer to check * @size: total data size * @blocksize: minimum block size; must be multiples of this size * @to_copy: 1 means do copy, 0 means do not copy * @new_sg: pointer to location to put pointer to new sg area * @returns: 1 if new scatterlist is needed; 0 if not needed; * error value if operation failed * * The SEP device requires all pages to be multiples of the * minimum block size appropriate for the operation * This function check all pages; if any are oddball sizes * (not multiple of block sizes), it creates a new scatterlist. * If the to_copy parameter is set to 1, then a scatter list * copy is performed. The pointer to the new scatterlist is * put into the address supplied by the new_sg parameter; if * no new scatterlist is needed, then a NULL is put into * the location at new_sg. * */ static int sep_oddball_pages( struct sep_device *sep, struct scatterlist *sg, size_t data_size, u32 block_size, struct scatterlist **new_sg, u32 do_copy) { struct scatterlist *sg_temp; u32 flag; u32 nbr_pages, page_count; dev_dbg(&sep->pdev->dev, "sep oddball\n"); if ((sg == NULL) || (data_size == 0) || (data_size < block_size)) return 0; dev_dbg(&sep->pdev->dev, "sep oddball not null\n"); flag = 0; nbr_pages = 0; page_count = 0; sg_temp = sg; while (sg_temp) { nbr_pages += 1; sg_temp = sg_next(sg_temp); } sg_temp = sg; while ((sg_temp) && (flag == 0)) { page_count += 1; if (sg_temp->length % block_size) flag = 1; else sg_temp = sg_next(sg_temp); } /* Do not process if last (or only) page is oddball */ if (nbr_pages == page_count) flag = 0; if (flag) { dev_dbg(&sep->pdev->dev, "sep oddball processing\n"); *new_sg = sep_alloc_sg_buf(sep, data_size, block_size); if (*new_sg == NULL) { dev_warn(&sep->pdev->dev, "cannot allocate new sg\n"); return -ENOMEM; } if (do_copy) sep_copy_sg(sep, sg, *new_sg, data_size); return 1; } else { return 0; } } /** * sep_copy_offset_sg - * @sep: pointer to struct sep_device; * @sg: pointer to struct scatterlist * @offset: offset into scatterlist memory * @dst: place to put data * @len: length of data * @returns: number of bytes copies * * This copies data from scatterlist buffer * offset from beginning - it is needed for * handling tail data in hash */ static size_t sep_copy_offset_sg( struct sep_device *sep, struct scatterlist *sg, u32 offset, void *dst, u32 len) { size_t page_start; size_t page_end; size_t offset_within_page; size_t length_within_page; size_t length_remaining; size_t current_offset; /* Find which page is beginning of segment */ page_start = 0; page_end = sg->length; while ((sg) && (offset > page_end)) { page_start += sg->length; sg = sg_next(sg); if (sg) page_end += sg->length; } if (sg == NULL) return -ENOMEM; offset_within_page = offset - page_start; if ((sg->length - offset_within_page) >= len) { /* All within this page */ memcpy(dst, sg_virt(sg) + offset_within_page, len); return len; } else { /* Scattered multiple pages */ current_offset = 0; length_remaining = len; while ((sg) && (current_offset < len)) { length_within_page = sg->length - offset_within_page; if (length_within_page >= length_remaining) { memcpy(dst+current_offset, sg_virt(sg) + offset_within_page, length_remaining); length_remaining = 0; current_offset = len; } else { memcpy(dst+current_offset, sg_virt(sg) + offset_within_page, length_within_page); length_remaining -= length_within_page; current_offset += length_within_page; offset_within_page = 0; sg = sg_next(sg); } } if (sg == NULL) return -ENOMEM; } return len; } /** * partial_overlap - * @src_ptr: source pointer * @dst_ptr: destination pointer * @nbytes: number of bytes * @returns: 0 for success; -1 for failure * We cannot have any partial overlap. Total overlap * where src is the same as dst is okay */ static int partial_overlap(void *src_ptr, void *dst_ptr, u32 nbytes) { /* Check for partial overlap */ if (src_ptr != dst_ptr) { if (src_ptr < dst_ptr) { if ((src_ptr + nbytes) > dst_ptr) return -EINVAL; } else { if ((dst_ptr + nbytes) > src_ptr) return -EINVAL; } } return 0; } /* Debug - prints only if DEBUG is defined */ static void sep_dump_ivs(struct ablkcipher_request *req, char *reason) { unsigned char *cptr; struct sep_aes_internal_context *aes_internal; struct sep_des_internal_context *des_internal; int ct1; struct this_task_ctx *ta_ctx; struct crypto_ablkcipher *tfm; struct sep_system_ctx *sctx; ta_ctx = ablkcipher_request_ctx(req); tfm = crypto_ablkcipher_reqtfm(req); sctx = crypto_ablkcipher_ctx(tfm); dev_dbg(&ta_ctx->sep_used->pdev->dev, "IV DUMP - %s\n", reason); if ((ta_ctx->current_request == DES_CBC) && (ta_ctx->des_opmode == SEP_DES_CBC)) { des_internal = (struct sep_des_internal_context *) sctx->des_private_ctx.ctx_buf; /* print vendor */ dev_dbg(&ta_ctx->sep_used->pdev->dev, "sep - vendor iv for DES\n"); cptr = (unsigned char *)des_internal->iv_context; for (ct1 = 0; ct1 < crypto_ablkcipher_ivsize(tfm); ct1 += 1) dev_dbg(&ta_ctx->sep_used->pdev->dev, "%02x\n", *(cptr + ct1)); /* print walk */ dev_dbg(&ta_ctx->sep_used->pdev->dev, "sep - walk from kernel crypto iv for DES\n"); cptr = (unsigned char *)ta_ctx->walk.iv; for (ct1 = 0; ct1 < crypto_ablkcipher_ivsize(tfm); ct1 += 1) dev_dbg(&ta_ctx->sep_used->pdev->dev, "%02x\n", *(cptr + ct1)); } else if ((ta_ctx->current_request == AES_CBC) && (ta_ctx->aes_opmode == SEP_AES_CBC)) { aes_internal = (struct sep_aes_internal_context *) sctx->aes_private_ctx.cbuff; /* print vendor */ dev_dbg(&ta_ctx->sep_used->pdev->dev, "sep - vendor iv for AES\n"); cptr = (unsigned char *)aes_internal->aes_ctx_iv; for (ct1 = 0; ct1 < crypto_ablkcipher_ivsize(tfm); ct1 += 1) dev_dbg(&ta_ctx->sep_used->pdev->dev, "%02x\n", *(cptr + ct1)); /* print walk */ dev_dbg(&ta_ctx->sep_used->pdev->dev, "sep - walk from kernel crypto iv for AES\n"); cptr = (unsigned char *)ta_ctx->walk.iv; for (ct1 = 0; ct1 < crypto_ablkcipher_ivsize(tfm); ct1 += 1) dev_dbg(&ta_ctx->sep_used->pdev->dev, "%02x\n", *(cptr + ct1)); } } /** * RFC2451: Weak key check * Returns: 1 (weak), 0 (not weak) */ static int sep_weak_key(const u8 *key, unsigned int keylen) { static const u8 parity[] = { 8, 1, 0, 8, 0, 8, 8, 0, 0, 8, 8, 0, 8, 0, 2, 8, 0, 8, 8, 0, 8, 0, 0, 8, 8, 0, 0, 8, 0, 8, 8, 3, 0, 8, 8, 0, 8, 0, 0, 8, 8, 0, 0, 8, 0, 8, 8, 0, 8, 0, 0, 8, 0, 8, 8, 0, 0, 8, 8, 0, 8, 0, 0, 8, 0, 8, 8, 0, 8, 0, 0, 8, 8, 0, 0, 8, 0, 8, 8, 0, 8, 0, 0, 8, 0, 8, 8, 0, 0, 8, 8, 0, 8, 0, 0, 8, 8, 0, 0, 8, 0, 8, 8, 0, 0, 8, 8, 0, 8, 0, 0, 8, 0, 8, 8, 0, 8, 0, 0, 8, 8, 0, 0, 8, 0, 8, 8, 0, 0, 8, 8, 0, 8, 0, 0, 8, 8, 0, 0, 8, 0, 8, 8, 0, 8, 0, 0, 8, 0, 8, 8, 0, 0, 8, 8, 0, 8, 0, 0, 8, 8, 0, 0, 8, 0, 8, 8, 0, 0, 8, 8, 0, 8, 0, 0, 8, 0, 8, 8, 0, 8, 0, 0, 8, 8, 0, 0, 8, 0, 8, 8, 0, 8, 0, 0, 8, 0, 8, 8, 0, 0, 8, 8, 0, 8, 0, 0, 8, 0, 8, 8, 0, 8, 0, 0, 8, 8, 0, 0, 8, 0, 8, 8, 0, 4, 8, 8, 0, 8, 0, 0, 8, 8, 0, 0, 8, 0, 8, 8, 0, 8, 5, 0, 8, 0, 8, 8, 0, 0, 8, 8, 0, 8, 0, 6, 8, }; u32 n, w; n = parity[key[0]]; n <<= 4; n |= parity[key[1]]; n <<= 4; n |= parity[key[2]]; n <<= 4; n |= parity[key[3]]; n <<= 4; n |= parity[key[4]]; n <<= 4; n |= parity[key[5]]; n <<= 4; n |= parity[key[6]]; n <<= 4; n |= parity[key[7]]; w = 0x88888888L; /* 1 in 10^10 keys passes this test */ if (!((n - (w >> 3)) & w)) { if (n < 0x41415151) { if (n < 0x31312121) { if (n < 0x14141515) { /* 01 01 01 01 01 01 01 01 */ if (n == 0x11111111) goto weak; /* 01 1F 01 1F 01 0E 01 0E */ if (n == 0x13131212) goto weak; } else { /* 01 E0 01 E0 01 F1 01 F1 */ if (n == 0x14141515) goto weak; /* 01 FE 01 FE 01 FE 01 FE */ if (n == 0x16161616) goto weak; } } else { if (n < 0x34342525) { /* 1F 01 1F 01 0E 01 0E 01 */ if (n == 0x31312121) goto weak; /* 1F 1F 1F 1F 0E 0E 0E 0E (?) */ if (n == 0x33332222) goto weak; } else { /* 1F E0 1F E0 0E F1 0E F1 */ if (n == 0x34342525) goto weak; /* 1F FE 1F FE 0E FE 0E FE */ if (n == 0x36362626) goto weak; } } } else { if (n < 0x61616161) { if (n < 0x44445555) { /* E0 01 E0 01 F1 01 F1 01 */ if (n == 0x41415151) goto weak; /* E0 1F E0 1F F1 0E F1 0E */ if (n == 0x43435252) goto weak; } else { /* E0 E0 E0 E0 F1 F1 F1 F1 (?) */ if (n == 0x44445555) goto weak; /* E0 FE E0 FE F1 FE F1 FE */ if (n == 0x46465656) goto weak; } } else { if (n < 0x64646565) { /* FE 01 FE 01 FE 01 FE 01 */ if (n == 0x61616161) goto weak; /* FE 1F FE 1F FE 0E FE 0E */ if (n == 0x63636262) goto weak; } else { /* FE E0 FE E0 FE F1 FE F1 */ if (n == 0x64646565) goto weak; /* FE FE FE FE FE FE FE FE */ if (n == 0x66666666) goto weak; } } } } return 0; weak: return 1; } /** * sep_sg_nents */ static u32 sep_sg_nents(struct scatterlist *sg) { u32 ct1 = 0; while (sg) { ct1 += 1; sg = sg_next(sg); } return ct1; } /** * sep_start_msg - * @ta_ctx: pointer to struct this_task_ctx * @returns: offset to place for the next word in the message * Set up pointer in message pool for new message */ static u32 sep_start_msg(struct this_task_ctx *ta_ctx) { u32 *word_ptr; ta_ctx->msg_len_words = 2; ta_ctx->msgptr = ta_ctx->msg; memset(ta_ctx->msg, 0, SEP_DRIVER_MESSAGE_SHARED_AREA_SIZE_IN_BYTES); ta_ctx->msgptr += sizeof(u32) * 2; word_ptr = (u32 *)ta_ctx->msgptr; *word_ptr = SEP_START_MSG_TOKEN; return sizeof(u32) * 2; } /** * sep_end_msg - * @ta_ctx: pointer to struct this_task_ctx * @messages_offset: current message offset * Returns: 0 for success; <0 otherwise * End message; set length and CRC; and * send interrupt to the SEP */ static void sep_end_msg(struct this_task_ctx *ta_ctx, u32 msg_offset) { u32 *word_ptr; /* Msg size goes into msg after token */ ta_ctx->msg_len_words = msg_offset / sizeof(u32) + 1; word_ptr = (u32 *)ta_ctx->msgptr; word_ptr += 1; *word_ptr = ta_ctx->msg_len_words; /* CRC (currently 0) goes at end of msg */ word_ptr = (u32 *)(ta_ctx->msgptr + msg_offset); *word_ptr = 0; } /** * sep_start_inbound_msg - * @ta_ctx: pointer to struct this_task_ctx * @msg_offset: offset to place for the next word in the message * @returns: 0 for success; error value for failure * Set up pointer in message pool for inbound message */ static u32 sep_start_inbound_msg(struct this_task_ctx *ta_ctx, u32 *msg_offset) { u32 *word_ptr; u32 token; u32 error = SEP_OK; *msg_offset = sizeof(u32) * 2; word_ptr = (u32 *)ta_ctx->msgptr; token = *word_ptr; ta_ctx->msg_len_words = *(word_ptr + 1); if (token != SEP_START_MSG_TOKEN) { error = SEP_INVALID_START; goto end_function; } end_function: return error; } /** * sep_write_msg - * @ta_ctx: pointer to struct this_task_ctx * @in_addr: pointer to start of parameter * @size: size of parameter to copy (in bytes) * @max_size: size to move up offset; SEP mesg is in word sizes * @msg_offset: pointer to current offset (is updated) * @byte_array: flag ti indicate whether endian must be changed * Copies data into the message area from caller */ static void sep_write_msg(struct this_task_ctx *ta_ctx, void *in_addr, u32 size, u32 max_size, u32 *msg_offset, u32 byte_array) { u32 *word_ptr; void *void_ptr; void_ptr = ta_ctx->msgptr + *msg_offset; word_ptr = (u32 *)void_ptr; memcpy(void_ptr, in_addr, size); *msg_offset += max_size; /* Do we need to manipulate endian? */ if (byte_array) { u32 i; for (i = 0; i < ((size + 3) / 4); i += 1) *(word_ptr + i) = CHG_ENDIAN(*(word_ptr + i)); } } /** * sep_make_header * @ta_ctx: pointer to struct this_task_ctx * @msg_offset: pointer to current offset (is updated) * @op_code: op code to put into message * Puts op code into message and updates offset */ static void sep_make_header(struct this_task_ctx *ta_ctx, u32 *msg_offset, u32 op_code) { u32 *word_ptr; *msg_offset = sep_start_msg(ta_ctx); word_ptr = (u32 *)(ta_ctx->msgptr + *msg_offset); *word_ptr = op_code; *msg_offset += sizeof(u32); } /** * sep_read_msg - * @ta_ctx: pointer to struct this_task_ctx * @in_addr: pointer to start of parameter * @size: size of parameter to copy (in bytes) * @max_size: size to move up offset; SEP mesg is in word sizes * @msg_offset: pointer to current offset (is updated) * @byte_array: flag ti indicate whether endian must be changed * Copies data out of the message area to caller */ static void sep_read_msg(struct this_task_ctx *ta_ctx, void *in_addr, u32 size, u32 max_size, u32 *msg_offset, u32 byte_array) { u32 *word_ptr; void *void_ptr; void_ptr = ta_ctx->msgptr + *msg_offset; word_ptr = (u32 *)void_ptr; /* Do we need to manipulate endian? */ if (byte_array) { u32 i; for (i = 0; i < ((size + 3) / 4); i += 1) *(word_ptr + i) = CHG_ENDIAN(*(word_ptr + i)); } memcpy(in_addr, void_ptr, size); *msg_offset += max_size; } /** * sep_verify_op - * @ta_ctx: pointer to struct this_task_ctx * @op_code: expected op_code * @msg_offset: pointer to current offset (is updated) * @returns: 0 for success; error for failure */ static u32 sep_verify_op(struct this_task_ctx *ta_ctx, u32 op_code, u32 *msg_offset) { u32 error; u32 in_ary[2]; struct sep_device *sep = ta_ctx->sep_used; dev_dbg(&sep->pdev->dev, "dumping return message\n"); error = sep_start_inbound_msg(ta_ctx, msg_offset); if (error) { dev_warn(&sep->pdev->dev, "sep_start_inbound_msg error\n"); return error; } sep_read_msg(ta_ctx, in_ary, sizeof(u32) * 2, sizeof(u32) * 2, msg_offset, 0); if (in_ary[0] != op_code) { dev_warn(&sep->pdev->dev, "sep got back wrong opcode\n"); dev_warn(&sep->pdev->dev, "got back %x; expected %x\n", in_ary[0], op_code); return SEP_WRONG_OPCODE; } if (in_ary[1] != SEP_OK) { dev_warn(&sep->pdev->dev, "sep execution error\n"); dev_warn(&sep->pdev->dev, "got back %x; expected %x\n", in_ary[1], SEP_OK); return in_ary[0]; } return 0; } /** * sep_read_context - * @ta_ctx: pointer to struct this_task_ctx * @msg_offset: point to current place in SEP msg; is updated * @dst: pointer to place to put the context * @len: size of the context structure (differs for crypro/hash) * This function reads the context from the msg area * There is a special way the vendor needs to have the maximum * length calculated so that the msg_offset is updated properly; * it skips over some words in the msg area depending on the size * of the context */ static void sep_read_context(struct this_task_ctx *ta_ctx, u32 *msg_offset, void *dst, u32 len) { u32 max_length = ((len + 3) / sizeof(u32)) * sizeof(u32); sep_read_msg(ta_ctx, dst, len, max_length, msg_offset, 0); } /** * sep_write_context - * @ta_ctx: pointer to struct this_task_ctx * @msg_offset: point to current place in SEP msg; is updated * @src: pointer to the current context * @len: size of the context structure (differs for crypro/hash) * This function writes the context to the msg area * There is a special way the vendor needs to have the maximum * length calculated so that the msg_offset is updated properly; * it skips over some words in the msg area depending on the size * of the context */ static void sep_write_context(struct this_task_ctx *ta_ctx, u32 *msg_offset, void *src, u32 len) { u32 max_length = ((len + 3) / sizeof(u32)) * sizeof(u32); sep_write_msg(ta_ctx, src, len, max_length, msg_offset, 0); } /** * sep_clear_out - * @ta_ctx: pointer to struct this_task_ctx * Clear out crypto related values in sep device structure * to enable device to be used by anyone; either kernel * crypto or userspace app via middleware */ static void sep_clear_out(struct this_task_ctx *ta_ctx) { if (ta_ctx->src_sg_hold) { sep_free_sg_buf(ta_ctx->src_sg_hold); ta_ctx->src_sg_hold = NULL; } if (ta_ctx->dst_sg_hold) { sep_free_sg_buf(ta_ctx->dst_sg_hold); ta_ctx->dst_sg_hold = NULL; } ta_ctx->src_sg = NULL; ta_ctx->dst_sg = NULL; sep_free_dma_table_data_handler(ta_ctx->sep_used, &ta_ctx->dma_ctx); if (ta_ctx->i_own_sep) { /** * The following unlocks the sep and makes it available * to any other application * First, null out crypto entries in sep before releasing it */ ta_ctx->sep_used->current_hash_req = NULL; ta_ctx->sep_used->current_cypher_req = NULL; ta_ctx->sep_used->current_request = 0; ta_ctx->sep_used->current_hash_stage = 0; ta_ctx->sep_used->ta_ctx = NULL; ta_ctx->sep_used->in_kernel = 0; ta_ctx->call_status.status = 0; /* Remove anything confidential */ memset(ta_ctx->sep_used->shared_addr, 0, SEP_DRIVER_MESSAGE_SHARED_AREA_SIZE_IN_BYTES); sep_queue_status_remove(ta_ctx->sep_used, &ta_ctx->queue_elem); #ifdef SEP_ENABLE_RUNTIME_PM ta_ctx->sep_used->in_use = 0; pm_runtime_mark_last_busy(&ta_ctx->sep_used->pdev->dev); pm_runtime_put_autosuspend(&ta_ctx->sep_used->pdev->dev); #endif clear_bit(SEP_WORKING_LOCK_BIT, &ta_ctx->sep_used->in_use_flags); ta_ctx->sep_used->pid_doing_transaction = 0; dev_dbg(&ta_ctx->sep_used->pdev->dev, "[PID%d] waking up next transaction\n", current->pid); clear_bit(SEP_TRANSACTION_STARTED_LOCK_BIT, &ta_ctx->sep_used->in_use_flags); wake_up(&ta_ctx->sep_used->event_transactions); ta_ctx->i_own_sep = 0; } } /** * Release crypto infrastructure from EINPROGRESS and * clear sep_dev so that SEP is available to anyone */ static void sep_crypto_release(struct sep_system_ctx *sctx, struct this_task_ctx *ta_ctx, u32 error) { struct ahash_request *hash_req = ta_ctx->current_hash_req; struct ablkcipher_request *cypher_req = ta_ctx->current_cypher_req; struct sep_device *sep = ta_ctx->sep_used; sep_clear_out(ta_ctx); /** * This may not yet exist depending when we * chose to bail out. If it does exist, set * it to 1 */ if (ta_ctx->are_we_done_yet != NULL) *ta_ctx->are_we_done_yet = 1; if (cypher_req != NULL) { if ((sctx->key_sent == 1) || ((error != 0) && (error != -EINPROGRESS))) { if (cypher_req->base.complete == NULL) { dev_dbg(&sep->pdev->dev, "release is null for cypher!"); } else { cypher_req->base.complete( &cypher_req->base, error); } } } if (hash_req != NULL) { if (hash_req->base.complete == NULL) { dev_dbg(&sep->pdev->dev, "release is null for hash!"); } else { hash_req->base.complete( &hash_req->base, error); } } } /** * This is where we grab the sep itself and tell it to do something. * It will sleep if the sep is currently busy * and it will return 0 if sep is now ours; error value if there * were problems */ static int sep_crypto_take_sep(struct this_task_ctx *ta_ctx) { struct sep_device *sep = ta_ctx->sep_used; int result; struct sep_msgarea_hdr *my_msg_header; my_msg_header = (struct sep_msgarea_hdr *)ta_ctx->msg; /* add to status queue */ ta_ctx->queue_elem = sep_queue_status_add(sep, my_msg_header->opcode, ta_ctx->nbytes, current->pid, current->comm, sizeof(current->comm)); if (!ta_ctx->queue_elem) { dev_dbg(&sep->pdev->dev, "[PID%d] updating queue status error\n", current->pid); return -EINVAL; } /* get the device; this can sleep */ result = sep_wait_transaction(sep); if (result) return result; if (sep_dev->power_save_setup == 1) pm_runtime_get_sync(&sep_dev->pdev->dev); /* Copy in the message */ memcpy(sep->shared_addr, ta_ctx->msg, SEP_DRIVER_MESSAGE_SHARED_AREA_SIZE_IN_BYTES); /* Copy in the dcb information if there is any */ if (ta_ctx->dcb_region) { result = sep_activate_dcb_dmatables_context(sep, &ta_ctx->dcb_region, &ta_ctx->dmatables_region, ta_ctx->dma_ctx); if (result) return result; } /* Mark the device so we know how to finish the job in the tasklet */ if (ta_ctx->current_hash_req) sep->current_hash_req = ta_ctx->current_hash_req; else sep->current_cypher_req = ta_ctx->current_cypher_req; sep->current_request = ta_ctx->current_request; sep->current_hash_stage = ta_ctx->current_hash_stage; sep->ta_ctx = ta_ctx; sep->in_kernel = 1; ta_ctx->i_own_sep = 1; /* need to set bit first to avoid race condition with interrupt */ set_bit(SEP_LEGACY_SENDMSG_DONE_OFFSET, &ta_ctx->call_status.status); result = sep_send_command_handler(sep); dev_dbg(&sep->pdev->dev, "[PID%d]: sending command to the sep\n", current->pid); if (!result) dev_dbg(&sep->pdev->dev, "[PID%d]: command sent okay\n", current->pid); else { dev_dbg(&sep->pdev->dev, "[PID%d]: cant send command\n", current->pid); clear_bit(SEP_LEGACY_SENDMSG_DONE_OFFSET, &ta_ctx->call_status.status); } return result; } /** * This function sets things up for a crypto data block process * This does all preparation, but does not try to grab the * sep * @req: pointer to struct ablkcipher_request * returns: 0 if all went well, non zero if error */ static int sep_crypto_block_data(struct ablkcipher_request *req) { int int_error; u32 msg_offset; static u32 msg[10]; void *src_ptr; void *dst_ptr; static char small_buf[100]; ssize_t copy_result; int result; struct scatterlist *new_sg; struct this_task_ctx *ta_ctx; struct crypto_ablkcipher *tfm; struct sep_system_ctx *sctx; struct sep_des_internal_context *des_internal; struct sep_aes_internal_context *aes_internal; ta_ctx = ablkcipher_request_ctx(req); tfm = crypto_ablkcipher_reqtfm(req); sctx = crypto_ablkcipher_ctx(tfm); /* start the walk on scatterlists */ ablkcipher_walk_init(&ta_ctx->walk, req->src, req->dst, req->nbytes); dev_dbg(&ta_ctx->sep_used->pdev->dev, "sep crypto block data size of %x\n", req->nbytes); int_error = ablkcipher_walk_phys(req, &ta_ctx->walk); if (int_error) { dev_warn(&ta_ctx->sep_used->pdev->dev, "walk phys error %x\n", int_error); return -ENOMEM; } dev_dbg(&ta_ctx->sep_used->pdev->dev, "crypto block: src is %lx dst is %lx\n", (unsigned long)req->src, (unsigned long)req->dst); /* Make sure all pages are even block */ int_error = sep_oddball_pages(ta_ctx->sep_used, req->src, req->nbytes, ta_ctx->walk.blocksize, &new_sg, 1); if (int_error < 0) { dev_warn(&ta_ctx->sep_used->pdev->dev, "oddball page error\n"); return -ENOMEM; } else if (int_error == 1) { ta_ctx->src_sg = new_sg; ta_ctx->src_sg_hold = new_sg; } else { ta_ctx->src_sg = req->src; ta_ctx->src_sg_hold = NULL; } int_error = sep_oddball_pages(ta_ctx->sep_used, req->dst, req->nbytes, ta_ctx->walk.blocksize, &new_sg, 0); if (int_error < 0) { dev_warn(&ta_ctx->sep_used->pdev->dev, "walk phys error %x\n", int_error); return -ENOMEM; } else if (int_error == 1) { ta_ctx->dst_sg = new_sg; ta_ctx->dst_sg_hold = new_sg; } else { ta_ctx->dst_sg = req->dst; ta_ctx->dst_sg_hold = NULL; } /* set nbytes for queue status */ ta_ctx->nbytes = req->nbytes; /* Key already done; this is for data */ dev_dbg(&ta_ctx->sep_used->pdev->dev, "sending data\n"); /* check for valid data and proper spacing */ src_ptr = sg_virt(ta_ctx->src_sg); dst_ptr = sg_virt(ta_ctx->dst_sg); if (!src_ptr || !dst_ptr || (ta_ctx->current_cypher_req->nbytes % crypto_ablkcipher_blocksize(tfm))) { dev_warn(&ta_ctx->sep_used->pdev->dev, "cipher block size odd\n"); dev_warn(&ta_ctx->sep_used->pdev->dev, "cipher block size is %x\n", crypto_ablkcipher_blocksize(tfm)); dev_warn(&ta_ctx->sep_used->pdev->dev, "cipher data size is %x\n", ta_ctx->current_cypher_req->nbytes); return -EINVAL; } if (partial_overlap(src_ptr, dst_ptr, ta_ctx->current_cypher_req->nbytes)) { dev_warn(&ta_ctx->sep_used->pdev->dev, "block partial overlap\n"); return -EINVAL; } /* Put together the message */ sep_make_header(ta_ctx, &msg_offset, ta_ctx->block_opcode); /* If des, and size is 1 block, put directly in msg */ if ((ta_ctx->block_opcode == SEP_DES_BLOCK_OPCODE) && (req->nbytes == crypto_ablkcipher_blocksize(tfm))) { dev_dbg(&ta_ctx->sep_used->pdev->dev, "writing out one block des\n"); copy_result = sg_copy_to_buffer( ta_ctx->src_sg, sep_sg_nents(ta_ctx->src_sg), small_buf, crypto_ablkcipher_blocksize(tfm)); if (copy_result != crypto_ablkcipher_blocksize(tfm)) { dev_warn(&ta_ctx->sep_used->pdev->dev, "des block copy failed\n"); return -ENOMEM; } /* Put data into message */ sep_write_msg(ta_ctx, small_buf, crypto_ablkcipher_blocksize(tfm), crypto_ablkcipher_blocksize(tfm) * 2, &msg_offset, 1); /* Put size into message */ sep_write_msg(ta_ctx, &req->nbytes, sizeof(u32), sizeof(u32), &msg_offset, 0); } else { /* Otherwise, fill out dma tables */ ta_ctx->dcb_input_data.app_in_address = src_ptr; ta_ctx->dcb_input_data.data_in_size = req->nbytes; ta_ctx->dcb_input_data.app_out_address = dst_ptr; ta_ctx->dcb_input_data.block_size = crypto_ablkcipher_blocksize(tfm); ta_ctx->dcb_input_data.tail_block_size = 0; ta_ctx->dcb_input_data.is_applet = 0; ta_ctx->dcb_input_data.src_sg = ta_ctx->src_sg; ta_ctx->dcb_input_data.dst_sg = ta_ctx->dst_sg; result = sep_create_dcb_dmatables_context_kernel( ta_ctx->sep_used, &ta_ctx->dcb_region, &ta_ctx->dmatables_region, &ta_ctx->dma_ctx, &ta_ctx->dcb_input_data, 1); if (result) { dev_warn(&ta_ctx->sep_used->pdev->dev, "crypto dma table create failed\n"); return -EINVAL; } /* Portion of msg is nulled (no data) */ msg[0] = (u32)0; msg[1] = (u32)0; msg[2] = (u32)0; msg[3] = (u32)0; msg[4] = (u32)0; sep_write_msg(ta_ctx, (void *)msg, sizeof(u32) * 5, sizeof(u32) * 5, &msg_offset, 0); } /** * Before we write the message, we need to overwrite the * vendor's IV with the one from our own ablkcipher walk * iv because this is needed for dm-crypt */ sep_dump_ivs(req, "sending data block to sep\n"); if ((ta_ctx->current_request == DES_CBC) && (ta_ctx->des_opmode == SEP_DES_CBC)) { dev_dbg(&ta_ctx->sep_used->pdev->dev, "overwrite vendor iv on DES\n"); des_internal = (struct sep_des_internal_context *) sctx->des_private_ctx.ctx_buf; memcpy((void *)des_internal->iv_context, ta_ctx->walk.iv, crypto_ablkcipher_ivsize(tfm)); } else if ((ta_ctx->current_request == AES_CBC) && (ta_ctx->aes_opmode == SEP_AES_CBC)) { dev_dbg(&ta_ctx->sep_used->pdev->dev, "overwrite vendor iv on AES\n"); aes_internal = (struct sep_aes_internal_context *) sctx->aes_private_ctx.cbuff; memcpy((void *)aes_internal->aes_ctx_iv, ta_ctx->walk.iv, crypto_ablkcipher_ivsize(tfm)); } /* Write context into message */ if (ta_ctx->block_opcode == SEP_DES_BLOCK_OPCODE) { sep_write_context(ta_ctx, &msg_offset, &sctx->des_private_ctx, sizeof(struct sep_des_private_context)); } else { sep_write_context(ta_ctx, &msg_offset, &sctx->aes_private_ctx, sizeof(struct sep_aes_private_context)); } /* conclude message */ sep_end_msg(ta_ctx, msg_offset); /* Parent (caller) is now ready to tell the sep to do ahead */ return 0; } /** * This function sets things up for a crypto key submit process * This does all preparation, but does not try to grab the * sep * @req: pointer to struct ablkcipher_request * returns: 0 if all went well, non zero if error */ static int sep_crypto_send_key(struct ablkcipher_request *req) { int int_error; u32 msg_offset; static u32 msg[10]; u32 max_length; struct this_task_ctx *ta_ctx; struct crypto_ablkcipher *tfm; struct sep_system_ctx *sctx; ta_ctx = ablkcipher_request_ctx(req); tfm = crypto_ablkcipher_reqtfm(req); sctx = crypto_ablkcipher_ctx(tfm); dev_dbg(&ta_ctx->sep_used->pdev->dev, "sending key\n"); /* start the walk on scatterlists */ ablkcipher_walk_init(&ta_ctx->walk, req->src, req->dst, req->nbytes); dev_dbg(&ta_ctx->sep_used->pdev->dev, "sep crypto block data size of %x\n", req->nbytes); int_error = ablkcipher_walk_phys(req, &ta_ctx->walk); if (int_error) { dev_warn(&ta_ctx->sep_used->pdev->dev, "walk phys error %x\n", int_error); return -ENOMEM; } /* check iv */ if ((ta_ctx->current_request == DES_CBC) && (ta_ctx->des_opmode == SEP_DES_CBC)) { if (!ta_ctx->walk.iv) { dev_warn(&ta_ctx->sep_used->pdev->dev, "no iv found\n"); return -EINVAL; } memcpy(ta_ctx->iv, ta_ctx->walk.iv, SEP_DES_IV_SIZE_BYTES); } if ((ta_ctx->current_request == AES_CBC) && (ta_ctx->aes_opmode == SEP_AES_CBC)) { if (!ta_ctx->walk.iv) { dev_warn(&ta_ctx->sep_used->pdev->dev, "no iv found\n"); return -EINVAL; } memcpy(ta_ctx->iv, ta_ctx->walk.iv, SEP_AES_IV_SIZE_BYTES); } /* put together message to SEP */ /* Start with op code */ sep_make_header(ta_ctx, &msg_offset, ta_ctx->init_opcode); /* now deal with IV */ if (ta_ctx->init_opcode == SEP_DES_INIT_OPCODE) { if (ta_ctx->des_opmode == SEP_DES_CBC) { sep_write_msg(ta_ctx, ta_ctx->iv, SEP_DES_IV_SIZE_BYTES, sizeof(u32) * 4, &msg_offset, 1); } else { /* Skip if ECB */ msg_offset += 4 * sizeof(u32); } } else { max_length = ((SEP_AES_IV_SIZE_BYTES + 3) / sizeof(u32)) * sizeof(u32); if (ta_ctx->aes_opmode == SEP_AES_CBC) { sep_write_msg(ta_ctx, ta_ctx->iv, SEP_AES_IV_SIZE_BYTES, max_length, &msg_offset, 1); } else { /* Skip if ECB */ msg_offset += max_length; } } /* load the key */ if (ta_ctx->init_opcode == SEP_DES_INIT_OPCODE) { sep_write_msg(ta_ctx, (void *)&sctx->key.des.key1, sizeof(u32) * 8, sizeof(u32) * 8, &msg_offset, 1); msg[0] = (u32)sctx->des_nbr_keys; msg[1] = (u32)ta_ctx->des_encmode; msg[2] = (u32)ta_ctx->des_opmode; sep_write_msg(ta_ctx, (void *)msg, sizeof(u32) * 3, sizeof(u32) * 3, &msg_offset, 0); } else { sep_write_msg(ta_ctx, (void *)&sctx->key.aes, sctx->keylen, SEP_AES_MAX_KEY_SIZE_BYTES, &msg_offset, 1); msg[0] = (u32)sctx->aes_key_size; msg[1] = (u32)ta_ctx->aes_encmode; msg[2] = (u32)ta_ctx->aes_opmode; msg[3] = (u32)0; /* Secret key is not used */ sep_write_msg(ta_ctx, (void *)msg, sizeof(u32) * 4, sizeof(u32) * 4, &msg_offset, 0); } /* conclude message */ sep_end_msg(ta_ctx, msg_offset); /* Parent (caller) is now ready to tell the sep to do ahead */ return 0; } /* This needs to be run as a work queue as it can be put asleep */ static void sep_crypto_block(void *data) { unsigned long end_time; int result; struct ablkcipher_request *req; struct this_task_ctx *ta_ctx; struct crypto_ablkcipher *tfm; struct sep_system_ctx *sctx; int are_we_done_yet; req = (struct ablkcipher_request *)data; ta_ctx = ablkcipher_request_ctx(req); tfm = crypto_ablkcipher_reqtfm(req); sctx = crypto_ablkcipher_ctx(tfm); ta_ctx->are_we_done_yet = &are_we_done_yet; pr_debug("sep_crypto_block\n"); pr_debug("tfm is %p sctx is %p ta_ctx is %p\n", tfm, sctx, ta_ctx); pr_debug("key_sent is %d\n", sctx->key_sent); /* do we need to send the key */ if (sctx->key_sent == 0) { are_we_done_yet = 0; result = sep_crypto_send_key(req); /* prep to send key */ if (result != 0) { dev_dbg(&ta_ctx->sep_used->pdev->dev, "could not prep key %x\n", result); sep_crypto_release(sctx, ta_ctx, result); return; } result = sep_crypto_take_sep(ta_ctx); if (result) { dev_warn(&ta_ctx->sep_used->pdev->dev, "sep_crypto_take_sep for key send failed\n"); sep_crypto_release(sctx, ta_ctx, result); return; } /* now we sit and wait up to a fixed time for completion */ end_time = jiffies + (WAIT_TIME * HZ); while ((time_before(jiffies, end_time)) && (are_we_done_yet == 0)) schedule(); /* Done waiting; still not done yet? */ if (are_we_done_yet == 0) { dev_dbg(&ta_ctx->sep_used->pdev->dev, "Send key job never got done\n"); sep_crypto_release(sctx, ta_ctx, -EINVAL); return; } /* Set the key sent variable so this can be skipped later */ sctx->key_sent = 1; } /* Key sent (or maybe not if we did not have to), now send block */ are_we_done_yet = 0; result = sep_crypto_block_data(req); if (result != 0) { dev_dbg(&ta_ctx->sep_used->pdev->dev, "could prep not send block %x\n", result); sep_crypto_release(sctx, ta_ctx, result); return; } result = sep_crypto_take_sep(ta_ctx); if (result) { dev_warn(&ta_ctx->sep_used->pdev->dev, "sep_crypto_take_sep for block send failed\n"); sep_crypto_release(sctx, ta_ctx, result); return; } /* now we sit and wait up to a fixed time for completion */ end_time = jiffies + (WAIT_TIME * HZ); while ((time_before(jiffies, end_time)) && (are_we_done_yet == 0)) schedule(); /* Done waiting; still not done yet? */ if (are_we_done_yet == 0) { dev_dbg(&ta_ctx->sep_used->pdev->dev, "Send block job never got done\n"); sep_crypto_release(sctx, ta_ctx, -EINVAL); return; } /* That's it; entire thing done, get out of queue */ pr_debug("crypto_block leaving\n"); pr_debug("tfm is %p sctx is %p ta_ctx is %p\n", tfm, sctx, ta_ctx); } /** * Post operation (after interrupt) for crypto block */ static u32 crypto_post_op(struct sep_device *sep) { /* HERE */ u32 u32_error; u32 msg_offset; ssize_t copy_result; static char small_buf[100]; struct ablkcipher_request *req; struct this_task_ctx *ta_ctx; struct sep_system_ctx *sctx; struct crypto_ablkcipher *tfm; struct sep_des_internal_context *des_internal; struct sep_aes_internal_context *aes_internal; if (!sep->current_cypher_req) return -EINVAL; /* hold req since we need to submit work after clearing sep */ req = sep->current_cypher_req; ta_ctx = ablkcipher_request_ctx(sep->current_cypher_req); tfm = crypto_ablkcipher_reqtfm(sep->current_cypher_req); sctx = crypto_ablkcipher_ctx(tfm); pr_debug("crypto_post op\n"); pr_debug("key_sent is %d tfm is %p sctx is %p ta_ctx is %p\n", sctx->key_sent, tfm, sctx, ta_ctx); dev_dbg(&ta_ctx->sep_used->pdev->dev, "crypto post_op\n"); dev_dbg(&ta_ctx->sep_used->pdev->dev, "crypto post_op message dump\n"); /* first bring msg from shared area to local area */ memcpy(ta_ctx->msg, sep->shared_addr, SEP_DRIVER_MESSAGE_SHARED_AREA_SIZE_IN_BYTES); /* Is this the result of performing init (key to SEP */ if (sctx->key_sent == 0) { /* Did SEP do it okay */ u32_error = sep_verify_op(ta_ctx, ta_ctx->init_opcode, &msg_offset); if (u32_error) { dev_warn(&ta_ctx->sep_used->pdev->dev, "aes init error %x\n", u32_error); sep_crypto_release(sctx, ta_ctx, u32_error); return u32_error; } /* Read Context */ if (ta_ctx->init_opcode == SEP_DES_INIT_OPCODE) { sep_read_context(ta_ctx, &msg_offset, &sctx->des_private_ctx, sizeof(struct sep_des_private_context)); } else { sep_read_context(ta_ctx, &msg_offset, &sctx->aes_private_ctx, sizeof(struct sep_aes_private_context)); } sep_dump_ivs(req, "after sending key to sep\n"); /* key sent went okay; release sep, and set are_we_done_yet */ sctx->key_sent = 1; sep_crypto_release(sctx, ta_ctx, -EINPROGRESS); } else { /** * This is the result of a block request */ dev_dbg(&ta_ctx->sep_used->pdev->dev, "crypto_post_op block response\n"); u32_error = sep_verify_op(ta_ctx, ta_ctx->block_opcode, &msg_offset); if (u32_error) { dev_warn(&ta_ctx->sep_used->pdev->dev, "sep block error %x\n", u32_error); sep_crypto_release(sctx, ta_ctx, u32_error); return -EINVAL; } if (ta_ctx->block_opcode == SEP_DES_BLOCK_OPCODE) { dev_dbg(&ta_ctx->sep_used->pdev->dev, "post op for DES\n"); /* special case for 1 block des */ if (sep->current_cypher_req->nbytes == crypto_ablkcipher_blocksize(tfm)) { sep_read_msg(ta_ctx, small_buf, crypto_ablkcipher_blocksize(tfm), crypto_ablkcipher_blocksize(tfm) * 2, &msg_offset, 1); dev_dbg(&ta_ctx->sep_used->pdev->dev, "reading in block des\n"); copy_result = sg_copy_from_buffer( ta_ctx->dst_sg, sep_sg_nents(ta_ctx->dst_sg), small_buf, crypto_ablkcipher_blocksize(tfm)); if (copy_result != crypto_ablkcipher_blocksize(tfm)) { dev_warn(&ta_ctx->sep_used->pdev->dev, "des block copy failed\n"); sep_crypto_release(sctx, ta_ctx, -ENOMEM); return -ENOMEM; } } /* Read Context */ sep_read_context(ta_ctx, &msg_offset, &sctx->des_private_ctx, sizeof(struct sep_des_private_context)); } else { dev_dbg(&ta_ctx->sep_used->pdev->dev, "post op for AES\n"); /* Skip the MAC Output */ msg_offset += (sizeof(u32) * 4); /* Read Context */ sep_read_context(ta_ctx, &msg_offset, &sctx->aes_private_ctx, sizeof(struct sep_aes_private_context)); } /* Copy to correct sg if this block had oddball pages */ if (ta_ctx->dst_sg_hold) sep_copy_sg(ta_ctx->sep_used, ta_ctx->dst_sg, ta_ctx->current_cypher_req->dst, ta_ctx->current_cypher_req->nbytes); /** * Copy the iv's back to the walk.iv * This is required for dm_crypt */ sep_dump_ivs(req, "got data block from sep\n"); if ((ta_ctx->current_request == DES_CBC) && (ta_ctx->des_opmode == SEP_DES_CBC)) { dev_dbg(&ta_ctx->sep_used->pdev->dev, "returning result iv to walk on DES\n"); des_internal = (struct sep_des_internal_context *) sctx->des_private_ctx.ctx_buf; memcpy(ta_ctx->walk.iv, (void *)des_internal->iv_context, crypto_ablkcipher_ivsize(tfm)); } else if ((ta_ctx->current_request == AES_CBC) && (ta_ctx->aes_opmode == SEP_AES_CBC)) { dev_dbg(&ta_ctx->sep_used->pdev->dev, "returning result iv to walk on AES\n"); aes_internal = (struct sep_aes_internal_context *) sctx->aes_private_ctx.cbuff; memcpy(ta_ctx->walk.iv, (void *)aes_internal->aes_ctx_iv, crypto_ablkcipher_ivsize(tfm)); } /* finished, release everything */ sep_crypto_release(sctx, ta_ctx, 0); } pr_debug("crypto_post_op done\n"); pr_debug("key_sent is %d tfm is %p sctx is %p ta_ctx is %p\n", sctx->key_sent, tfm, sctx, ta_ctx); return 0; } static u32 hash_init_post_op(struct sep_device *sep) { u32 u32_error; u32 msg_offset; struct crypto_ahash *tfm = crypto_ahash_reqtfm(sep->current_hash_req); struct this_task_ctx *ta_ctx = ahash_request_ctx(sep->current_hash_req); struct sep_system_ctx *sctx = crypto_ahash_ctx(tfm); dev_dbg(&ta_ctx->sep_used->pdev->dev, "hash init post op\n"); /* first bring msg from shared area to local area */ memcpy(ta_ctx->msg, sep->shared_addr, SEP_DRIVER_MESSAGE_SHARED_AREA_SIZE_IN_BYTES); u32_error = sep_verify_op(ta_ctx, SEP_HASH_INIT_OPCODE, &msg_offset); if (u32_error) { dev_warn(&ta_ctx->sep_used->pdev->dev, "hash init error %x\n", u32_error); sep_crypto_release(sctx, ta_ctx, u32_error); return u32_error; } /* Read Context */ sep_read_context(ta_ctx, &msg_offset, &sctx->hash_private_ctx, sizeof(struct sep_hash_private_context)); /* Signal to crypto infrastructure and clear out */ dev_dbg(&ta_ctx->sep_used->pdev->dev, "hash init post op done\n"); sep_crypto_release(sctx, ta_ctx, 0); return 0; } static u32 hash_update_post_op(struct sep_device *sep) { u32 u32_error; u32 msg_offset; struct crypto_ahash *tfm = crypto_ahash_reqtfm(sep->current_hash_req); struct this_task_ctx *ta_ctx = ahash_request_ctx(sep->current_hash_req); struct sep_system_ctx *sctx = crypto_ahash_ctx(tfm); dev_dbg(&ta_ctx->sep_used->pdev->dev, "hash update post op\n"); /* first bring msg from shared area to local area */ memcpy(ta_ctx->msg, sep->shared_addr, SEP_DRIVER_MESSAGE_SHARED_AREA_SIZE_IN_BYTES); u32_error = sep_verify_op(ta_ctx, SEP_HASH_UPDATE_OPCODE, &msg_offset); if (u32_error) { dev_warn(&ta_ctx->sep_used->pdev->dev, "hash init error %x\n", u32_error); sep_crypto_release(sctx, ta_ctx, u32_error); return u32_error; } /* Read Context */ sep_read_context(ta_ctx, &msg_offset, &sctx->hash_private_ctx, sizeof(struct sep_hash_private_context)); /** * Following is only for finup; if we just completed the * data portion of finup, we now need to kick off the * finish portion of finup. */ if (ta_ctx->sep_used->current_hash_stage == HASH_FINUP_DATA) { /* first reset stage to HASH_FINUP_FINISH */ ta_ctx->sep_used->current_hash_stage = HASH_FINUP_FINISH; /* now enqueue the finish operation */ spin_lock_irq(&queue_lock); u32_error = crypto_enqueue_request(&sep_queue, &ta_ctx->sep_used->current_hash_req->base); spin_unlock_irq(&queue_lock); if ((u32_error != 0) && (u32_error != -EINPROGRESS)) { dev_warn(&ta_ctx->sep_used->pdev->dev, "spe cypher post op cant queue\n"); sep_crypto_release(sctx, ta_ctx, u32_error); return u32_error; } /* schedule the data send */ u32_error = sep_submit_work(ta_ctx->sep_used->workqueue, sep_dequeuer, (void *)&sep_queue); if (u32_error) { dev_warn(&ta_ctx->sep_used->pdev->dev, "cant submit work sep_crypto_block\n"); sep_crypto_release(sctx, ta_ctx, -EINVAL); return -EINVAL; } } /* Signal to crypto infrastructure and clear out */ dev_dbg(&ta_ctx->sep_used->pdev->dev, "hash update post op done\n"); sep_crypto_release(sctx, ta_ctx, 0); return 0; } static u32 hash_final_post_op(struct sep_device *sep) { int max_length; u32 u32_error; u32 msg_offset; struct crypto_ahash *tfm = crypto_ahash_reqtfm(sep->current_hash_req); struct sep_system_ctx *sctx = crypto_ahash_ctx(tfm); struct this_task_ctx *ta_ctx = ahash_request_ctx(sep->current_hash_req); dev_dbg(&ta_ctx->sep_used->pdev->dev, "hash final post op\n"); /* first bring msg from shared area to local area */ memcpy(ta_ctx->msg, sep->shared_addr, SEP_DRIVER_MESSAGE_SHARED_AREA_SIZE_IN_BYTES); u32_error = sep_verify_op(ta_ctx, SEP_HASH_FINISH_OPCODE, &msg_offset); if (u32_error) { dev_warn(&ta_ctx->sep_used->pdev->dev, "hash finish error %x\n", u32_error); sep_crypto_release(sctx, ta_ctx, u32_error); return u32_error; } /* Grab the result */ if (ta_ctx->current_hash_req->result == NULL) { /* Oops, null buffer; error out here */ dev_warn(&ta_ctx->sep_used->pdev->dev, "hash finish null buffer\n"); sep_crypto_release(sctx, ta_ctx, (u32)-ENOMEM); return -ENOMEM; } max_length = (((SEP_HASH_RESULT_SIZE_WORDS * sizeof(u32)) + 3) / sizeof(u32)) * sizeof(u32); sep_read_msg(ta_ctx, ta_ctx->current_hash_req->result, crypto_ahash_digestsize(tfm), max_length, &msg_offset, 0); /* Signal to crypto infrastructure and clear out */ dev_dbg(&ta_ctx->sep_used->pdev->dev, "hash finish post op done\n"); sep_crypto_release(sctx, ta_ctx, 0); return 0; } static u32 hash_digest_post_op(struct sep_device *sep) { int max_length; u32 u32_error; u32 msg_offset; struct crypto_ahash *tfm = crypto_ahash_reqtfm(sep->current_hash_req); struct sep_system_ctx *sctx = crypto_ahash_ctx(tfm); struct this_task_ctx *ta_ctx = ahash_request_ctx(sep->current_hash_req); dev_dbg(&ta_ctx->sep_used->pdev->dev, "hash digest post op\n"); /* first bring msg from shared area to local area */ memcpy(ta_ctx->msg, sep->shared_addr, SEP_DRIVER_MESSAGE_SHARED_AREA_SIZE_IN_BYTES); u32_error = sep_verify_op(ta_ctx, SEP_HASH_SINGLE_OPCODE, &msg_offset); if (u32_error) { dev_warn(&ta_ctx->sep_used->pdev->dev, "hash digest finish error %x\n", u32_error); sep_crypto_release(sctx, ta_ctx, u32_error); return u32_error; } /* Grab the result */ if (ta_ctx->current_hash_req->result == NULL) { /* Oops, null buffer; error out here */ dev_warn(&ta_ctx->sep_used->pdev->dev, "hash digest finish null buffer\n"); sep_crypto_release(sctx, ta_ctx, (u32)-ENOMEM); return -ENOMEM; } max_length = (((SEP_HASH_RESULT_SIZE_WORDS * sizeof(u32)) + 3) / sizeof(u32)) * sizeof(u32); sep_read_msg(ta_ctx, ta_ctx->current_hash_req->result, crypto_ahash_digestsize(tfm), max_length, &msg_offset, 0); /* Signal to crypto infrastructure and clear out */ dev_dbg(&ta_ctx->sep_used->pdev->dev, "hash digest finish post op done\n"); sep_crypto_release(sctx, ta_ctx, 0); return 0; } /** * The sep_finish function is the function that is scheduled (via tasklet) * by the interrupt service routine when the SEP sends and interrupt * This is only called by the interrupt handler as a tasklet. */ static void sep_finish(unsigned long data) { struct sep_device *sep_dev; int res; res = 0; if (data == 0) { pr_debug("sep_finish called with null data\n"); return; } sep_dev = (struct sep_device *)data; if (sep_dev == NULL) { pr_debug("sep_finish; sep_dev is NULL\n"); return; } if (sep_dev->in_kernel == (u32)0) { dev_warn(&sep_dev->pdev->dev, "sep_finish; not in kernel operation\n"); return; } /* Did we really do a sep command prior to this? */ if (0 == test_bit(SEP_LEGACY_SENDMSG_DONE_OFFSET, &sep_dev->ta_ctx->call_status.status)) { dev_warn(&sep_dev->pdev->dev, "[PID%d] sendmsg not called\n", current->pid); return; } if (sep_dev->send_ct != sep_dev->reply_ct) { dev_warn(&sep_dev->pdev->dev, "[PID%d] poll; no message came back\n", current->pid); return; } /* Check for error (In case time ran out) */ if ((res != 0x0) && (res != 0x8)) { dev_warn(&sep_dev->pdev->dev, "[PID%d] poll; poll error GPR3 is %x\n", current->pid, res); return; } /* What kind of interrupt from sep was this? */ res = sep_read_reg(sep_dev, HW_HOST_SEP_HOST_GPR2_REG_ADDR); dev_dbg(&sep_dev->pdev->dev, "[PID%d] GPR2 at crypto finish is %x\n", current->pid, res); /* Print request? */ if ((res >> 30) & 0x1) { dev_dbg(&sep_dev->pdev->dev, "[PID%d] sep print req\n", current->pid); dev_dbg(&sep_dev->pdev->dev, "[PID%d] contents: %s\n", current->pid, (char *)(sep_dev->shared_addr + SEP_DRIVER_PRINTF_OFFSET_IN_BYTES)); return; } /* Request for daemon (not currently in POR)? */ if (res >> 31) { dev_dbg(&sep_dev->pdev->dev, "[PID%d] sep request; ignoring\n", current->pid); return; } /* If we got here, then we have a replay to a sep command */ dev_dbg(&sep_dev->pdev->dev, "[PID%d] sep reply to command; processing request: %x\n", current->pid, sep_dev->current_request); switch (sep_dev->current_request) { case AES_CBC: case AES_ECB: case DES_CBC: case DES_ECB: res = crypto_post_op(sep_dev); break; case SHA1: case MD5: case SHA224: case SHA256: switch (sep_dev->current_hash_stage) { case HASH_INIT: res = hash_init_post_op(sep_dev); break; case HASH_UPDATE: case HASH_FINUP_DATA: res = hash_update_post_op(sep_dev); break; case HASH_FINUP_FINISH: case HASH_FINISH: res = hash_final_post_op(sep_dev); break; case HASH_DIGEST: res = hash_digest_post_op(sep_dev); break; default: pr_debug("sep - invalid stage for hash finish\n"); } break; default: pr_debug("sep - invalid request for finish\n"); } if (res) pr_debug("sep - finish returned error %x\n", res); } static int sep_hash_cra_init(struct crypto_tfm *tfm) { const char *alg_name = crypto_tfm_alg_name(tfm); pr_debug("sep_hash_cra_init name is %s\n", alg_name); crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm), sizeof(struct this_task_ctx)); return 0; } static void sep_hash_cra_exit(struct crypto_tfm *tfm) { pr_debug("sep_hash_cra_exit\n"); } static void sep_hash_init(void *data) { u32 msg_offset; int result; struct ahash_request *req; struct crypto_ahash *tfm; struct this_task_ctx *ta_ctx; struct sep_system_ctx *sctx; unsigned long end_time; int are_we_done_yet; req = (struct ahash_request *)data; tfm = crypto_ahash_reqtfm(req); sctx = crypto_ahash_ctx(tfm); ta_ctx = ahash_request_ctx(req); ta_ctx->sep_used = sep_dev; ta_ctx->are_we_done_yet = &are_we_done_yet; dev_dbg(&ta_ctx->sep_used->pdev->dev, "sep_hash_init\n"); ta_ctx->current_hash_stage = HASH_INIT; /* opcode and mode */ sep_make_header(ta_ctx, &msg_offset, SEP_HASH_INIT_OPCODE); sep_write_msg(ta_ctx, &ta_ctx->hash_opmode, sizeof(u32), sizeof(u32), &msg_offset, 0); sep_end_msg(ta_ctx, msg_offset); are_we_done_yet = 0; result = sep_crypto_take_sep(ta_ctx); if (result) { dev_warn(&ta_ctx->sep_used->pdev->dev, "sep_hash_init take sep failed\n"); sep_crypto_release(sctx, ta_ctx, -EINVAL); } /* now we sit and wait up to a fixed time for completion */ end_time = jiffies + (WAIT_TIME * HZ); while ((time_before(jiffies, end_time)) && (are_we_done_yet == 0)) schedule(); /* Done waiting; still not done yet? */ if (are_we_done_yet == 0) { dev_dbg(&ta_ctx->sep_used->pdev->dev, "hash init never got done\n"); sep_crypto_release(sctx, ta_ctx, -EINVAL); return; } } static void sep_hash_update(void *data) { int int_error; u32 msg_offset; u32 len; struct sep_hash_internal_context *int_ctx; u32 block_size; u32 head_len; u32 tail_len; int are_we_done_yet; static u32 msg[10]; static char small_buf[100]; void *src_ptr; struct scatterlist *new_sg; ssize_t copy_result; struct ahash_request *req; struct crypto_ahash *tfm; struct this_task_ctx *ta_ctx; struct sep_system_ctx *sctx; unsigned long end_time; req = (struct ahash_request *)data; tfm = crypto_ahash_reqtfm(req); sctx = crypto_ahash_ctx(tfm); ta_ctx = ahash_request_ctx(req); ta_ctx->sep_used = sep_dev; ta_ctx->are_we_done_yet = &are_we_done_yet; /* length for queue status */ ta_ctx->nbytes = req->nbytes; dev_dbg(&ta_ctx->sep_used->pdev->dev, "sep_hash_update\n"); ta_ctx->current_hash_stage = HASH_UPDATE; len = req->nbytes; block_size = crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm)); tail_len = req->nbytes % block_size; dev_dbg(&ta_ctx->sep_used->pdev->dev, "length is %x\n", len); dev_dbg(&ta_ctx->sep_used->pdev->dev, "block_size is %x\n", block_size); dev_dbg(&ta_ctx->sep_used->pdev->dev, "tail len is %x\n", tail_len); /* Compute header/tail sizes */ int_ctx = (struct sep_hash_internal_context *)&sctx-> hash_private_ctx.internal_context; head_len = (block_size - int_ctx->prev_update_bytes) % block_size; tail_len = (req->nbytes - head_len) % block_size; /* Make sure all pages are an even block */ int_error = sep_oddball_pages(ta_ctx->sep_used, req->src, req->nbytes, block_size, &new_sg, 1); if (int_error < 0) { dev_warn(&ta_ctx->sep_used->pdev->dev, "oddball pages error in crash update\n"); sep_crypto_release(sctx, ta_ctx, -ENOMEM); return; } else if (int_error == 1) { ta_ctx->src_sg = new_sg; ta_ctx->src_sg_hold = new_sg; } else { ta_ctx->src_sg = req->src; ta_ctx->src_sg_hold = NULL; } src_ptr = sg_virt(ta_ctx->src_sg); if ((!req->nbytes) || (!ta_ctx->src_sg)) { /* null data */ src_ptr = NULL; } ta_ctx->dcb_input_data.app_in_address = src_ptr; ta_ctx->dcb_input_data.data_in_size = req->nbytes - (head_len + tail_len); ta_ctx->dcb_input_data.app_out_address = NULL; ta_ctx->dcb_input_data.block_size = block_size; ta_ctx->dcb_input_data.tail_block_size = 0; ta_ctx->dcb_input_data.is_applet = 0; ta_ctx->dcb_input_data.src_sg = ta_ctx->src_sg; ta_ctx->dcb_input_data.dst_sg = NULL; int_error = sep_create_dcb_dmatables_context_kernel( ta_ctx->sep_used, &ta_ctx->dcb_region, &ta_ctx->dmatables_region, &ta_ctx->dma_ctx, &ta_ctx->dcb_input_data, 1); if (int_error) { dev_warn(&ta_ctx->sep_used->pdev->dev, "hash update dma table create failed\n"); sep_crypto_release(sctx, ta_ctx, -EINVAL); return; } /* Construct message to SEP */ sep_make_header(ta_ctx, &msg_offset, SEP_HASH_UPDATE_OPCODE); msg[0] = (u32)0; msg[1] = (u32)0; msg[2] = (u32)0; sep_write_msg(ta_ctx, msg, sizeof(u32) * 3, sizeof(u32) * 3, &msg_offset, 0); /* Handle remainders */ /* Head */ sep_write_msg(ta_ctx, &head_len, sizeof(u32), sizeof(u32), &msg_offset, 0); if (head_len) { copy_result = sg_copy_to_buffer( req->src, sep_sg_nents(ta_ctx->src_sg), small_buf, head_len); if (copy_result != head_len) { dev_warn(&ta_ctx->sep_used->pdev->dev, "sg head copy failure in hash block\n"); sep_crypto_release(sctx, ta_ctx, -ENOMEM); return; } sep_write_msg(ta_ctx, small_buf, head_len, sizeof(u32) * 32, &msg_offset, 1); } else { msg_offset += sizeof(u32) * 32; } /* Tail */ sep_write_msg(ta_ctx, &tail_len, sizeof(u32), sizeof(u32), &msg_offset, 0); if (tail_len) { copy_result = sep_copy_offset_sg( ta_ctx->sep_used, ta_ctx->src_sg, req->nbytes - tail_len, small_buf, tail_len); if (copy_result != tail_len) { dev_warn(&ta_ctx->sep_used->pdev->dev, "sg tail copy failure in hash block\n"); sep_crypto_release(sctx, ta_ctx, -ENOMEM); return; } sep_write_msg(ta_ctx, small_buf, tail_len, sizeof(u32) * 32, &msg_offset, 1); } else { msg_offset += sizeof(u32) * 32; } /* Context */ sep_write_context(ta_ctx, &msg_offset, &sctx->hash_private_ctx, sizeof(struct sep_hash_private_context)); sep_end_msg(ta_ctx, msg_offset); are_we_done_yet = 0; int_error = sep_crypto_take_sep(ta_ctx); if (int_error) { dev_warn(&ta_ctx->sep_used->pdev->dev, "sep_hash_update take sep failed\n"); sep_crypto_release(sctx, ta_ctx, -EINVAL); } /* now we sit and wait up to a fixed time for completion */ end_time = jiffies + (WAIT_TIME * HZ); while ((time_before(jiffies, end_time)) && (are_we_done_yet == 0)) schedule(); /* Done waiting; still not done yet? */ if (are_we_done_yet == 0) { dev_dbg(&ta_ctx->sep_used->pdev->dev, "hash update never got done\n"); sep_crypto_release(sctx, ta_ctx, -EINVAL); return; } } static void sep_hash_final(void *data) { u32 msg_offset; struct ahash_request *req; struct crypto_ahash *tfm; struct this_task_ctx *ta_ctx; struct sep_system_ctx *sctx; int result; unsigned long end_time; int are_we_done_yet; req = (struct ahash_request *)data; tfm = crypto_ahash_reqtfm(req); sctx = crypto_ahash_ctx(tfm); ta_ctx = ahash_request_ctx(req); ta_ctx->sep_used = sep_dev; dev_dbg(&ta_ctx->sep_used->pdev->dev, "sep_hash_final\n"); ta_ctx->current_hash_stage = HASH_FINISH; ta_ctx->are_we_done_yet = &are_we_done_yet; /* opcode and mode */ sep_make_header(ta_ctx, &msg_offset, SEP_HASH_FINISH_OPCODE); /* Context */ sep_write_context(ta_ctx, &msg_offset, &sctx->hash_private_ctx, sizeof(struct sep_hash_private_context)); sep_end_msg(ta_ctx, msg_offset); are_we_done_yet = 0; result = sep_crypto_take_sep(ta_ctx); if (result) { dev_warn(&ta_ctx->sep_used->pdev->dev, "sep_hash_final take sep failed\n"); sep_crypto_release(sctx, ta_ctx, -EINVAL); } /* now we sit and wait up to a fixed time for completion */ end_time = jiffies + (WAIT_TIME * HZ); while ((time_before(jiffies, end_time)) && (are_we_done_yet == 0)) schedule(); /* Done waiting; still not done yet? */ if (are_we_done_yet == 0) { dev_dbg(&ta_ctx->sep_used->pdev->dev, "hash final job never got done\n"); sep_crypto_release(sctx, ta_ctx, -EINVAL); return; } } static void sep_hash_digest(void *data) { int int_error; u32 msg_offset; u32 block_size; u32 msg[10]; size_t copy_result; int result; int are_we_done_yet; u32 tail_len; static char small_buf[100]; struct scatterlist *new_sg; void *src_ptr; struct ahash_request *req; struct crypto_ahash *tfm; struct this_task_ctx *ta_ctx; struct sep_system_ctx *sctx; unsigned long end_time; req = (struct ahash_request *)data; tfm = crypto_ahash_reqtfm(req); sctx = crypto_ahash_ctx(tfm); ta_ctx = ahash_request_ctx(req); ta_ctx->sep_used = sep_dev; dev_dbg(&ta_ctx->sep_used->pdev->dev, "sep_hash_digest\n"); ta_ctx->current_hash_stage = HASH_DIGEST; ta_ctx->are_we_done_yet = &are_we_done_yet; /* length for queue status */ ta_ctx->nbytes = req->nbytes; block_size = crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm)); tail_len = req->nbytes % block_size; dev_dbg(&ta_ctx->sep_used->pdev->dev, "length is %x\n", req->nbytes); dev_dbg(&ta_ctx->sep_used->pdev->dev, "block_size is %x\n", block_size); dev_dbg(&ta_ctx->sep_used->pdev->dev, "tail len is %x\n", tail_len); /* Make sure all pages are an even block */ int_error = sep_oddball_pages(ta_ctx->sep_used, req->src, req->nbytes, block_size, &new_sg, 1); if (int_error < 0) { dev_warn(&ta_ctx->sep_used->pdev->dev, "oddball pages error in crash update\n"); sep_crypto_release(sctx, ta_ctx, -ENOMEM); return; } else if (int_error == 1) { ta_ctx->src_sg = new_sg; ta_ctx->src_sg_hold = new_sg; } else { ta_ctx->src_sg = req->src; ta_ctx->src_sg_hold = NULL; } src_ptr = sg_virt(ta_ctx->src_sg); if ((!req->nbytes) || (!ta_ctx->src_sg)) { /* null data */ src_ptr = NULL; } ta_ctx->dcb_input_data.app_in_address = src_ptr; ta_ctx->dcb_input_data.data_in_size = req->nbytes - tail_len; ta_ctx->dcb_input_data.app_out_address = NULL; ta_ctx->dcb_input_data.block_size = block_size; ta_ctx->dcb_input_data.tail_block_size = 0; ta_ctx->dcb_input_data.is_applet = 0; ta_ctx->dcb_input_data.src_sg = ta_ctx->src_sg; ta_ctx->dcb_input_data.dst_sg = NULL; int_error = sep_create_dcb_dmatables_context_kernel( ta_ctx->sep_used, &ta_ctx->dcb_region, &ta_ctx->dmatables_region, &ta_ctx->dma_ctx, &ta_ctx->dcb_input_data, 1); if (int_error) { dev_warn(&ta_ctx->sep_used->pdev->dev, "hash update dma table create failed\n"); sep_crypto_release(sctx, ta_ctx, -EINVAL); return; } /* Construct message to SEP */ sep_make_header(ta_ctx, &msg_offset, SEP_HASH_SINGLE_OPCODE); sep_write_msg(ta_ctx, &ta_ctx->hash_opmode, sizeof(u32), sizeof(u32), &msg_offset, 0); msg[0] = (u32)0; msg[1] = (u32)0; msg[2] = (u32)0; sep_write_msg(ta_ctx, msg, sizeof(u32) * 3, sizeof(u32) * 3, &msg_offset, 0); /* Tail */ sep_write_msg(ta_ctx, &tail_len, sizeof(u32), sizeof(u32), &msg_offset, 0); if (tail_len) { copy_result = sep_copy_offset_sg( ta_ctx->sep_used, ta_ctx->src_sg, req->nbytes - tail_len, small_buf, tail_len); if (copy_result != tail_len) { dev_warn(&ta_ctx->sep_used->pdev->dev, "sg tail copy failure in hash block\n"); sep_crypto_release(sctx, ta_ctx, -ENOMEM); return; } sep_write_msg(ta_ctx, small_buf, tail_len, sizeof(u32) * 32, &msg_offset, 1); } else { msg_offset += sizeof(u32) * 32; } sep_end_msg(ta_ctx, msg_offset); are_we_done_yet = 0; result = sep_crypto_take_sep(ta_ctx); if (result) { dev_warn(&ta_ctx->sep_used->pdev->dev, "sep_hash_digest take sep failed\n"); sep_crypto_release(sctx, ta_ctx, -EINVAL); } /* now we sit and wait up to a fixed time for completion */ end_time = jiffies + (WAIT_TIME * HZ); while ((time_before(jiffies, end_time)) && (are_we_done_yet == 0)) schedule(); /* Done waiting; still not done yet? */ if (are_we_done_yet == 0) { dev_dbg(&ta_ctx->sep_used->pdev->dev, "hash digest job never got done\n"); sep_crypto_release(sctx, ta_ctx, -EINVAL); return; } } /** * This is what is called by each of the API's provided * in the kernel crypto descriptors. It is run in a process * context using the kernel workqueues. Therefore it can * be put to sleep. */ static void sep_dequeuer(void *data) { struct crypto_queue *this_queue; struct crypto_async_request *async_req; struct crypto_async_request *backlog; struct ablkcipher_request *cypher_req; struct ahash_request *hash_req; struct sep_system_ctx *sctx; struct crypto_ahash *hash_tfm; struct this_task_ctx *ta_ctx; this_queue = (struct crypto_queue *)data; spin_lock_irq(&queue_lock); backlog = crypto_get_backlog(this_queue); async_req = crypto_dequeue_request(this_queue); spin_unlock_irq(&queue_lock); if (!async_req) { pr_debug("sep crypto queue is empty\n"); return; } if (backlog) { pr_debug("sep crypto backlog set\n"); if (backlog->complete) backlog->complete(backlog, -EINPROGRESS); backlog = NULL; } if (!async_req->tfm) { pr_debug("sep crypto queue null tfm\n"); return; } if (!async_req->tfm->__crt_alg) { pr_debug("sep crypto queue null __crt_alg\n"); return; } if (!async_req->tfm->__crt_alg->cra_type) { pr_debug("sep crypto queue null cra_type\n"); return; } /* we have stuff in the queue */ if (async_req->tfm->__crt_alg->cra_type != &crypto_ahash_type) { /* This is for a cypher */ pr_debug("sep crypto queue doing cipher\n"); cypher_req = container_of(async_req, struct ablkcipher_request, base); if (!cypher_req) { pr_debug("sep crypto queue null cypher_req\n"); return; } sep_crypto_block((void *)cypher_req); return; } else { /* This is a hash */ pr_debug("sep crypto queue doing hash\n"); /** * This is a bit more complex than cipher; we * need to figure out what type of operation */ hash_req = ahash_request_cast(async_req); if (!hash_req) { pr_debug("sep crypto queue null hash_req\n"); return; } hash_tfm = crypto_ahash_reqtfm(hash_req); if (!hash_tfm) { pr_debug("sep crypto queue null hash_tfm\n"); return; } sctx = crypto_ahash_ctx(hash_tfm); if (!sctx) { pr_debug("sep crypto queue null sctx\n"); return; } ta_ctx = ahash_request_ctx(hash_req); if (ta_ctx->current_hash_stage == HASH_INIT) { pr_debug("sep crypto queue hash init\n"); sep_hash_init((void *)hash_req); return; } else if (ta_ctx->current_hash_stage == HASH_UPDATE) { pr_debug("sep crypto queue hash update\n"); sep_hash_update((void *)hash_req); return; } else if (ta_ctx->current_hash_stage == HASH_FINISH) { pr_debug("sep crypto queue hash final\n"); sep_hash_final((void *)hash_req); return; } else if (ta_ctx->current_hash_stage == HASH_DIGEST) { pr_debug("sep crypto queue hash digest\n"); sep_hash_digest((void *)hash_req); return; } else if (ta_ctx->current_hash_stage == HASH_FINUP_DATA) { pr_debug("sep crypto queue hash digest\n"); sep_hash_update((void *)hash_req); return; } else if (ta_ctx->current_hash_stage == HASH_FINUP_FINISH) { pr_debug("sep crypto queue hash digest\n"); sep_hash_final((void *)hash_req); return; } else { pr_debug("sep crypto queue hash oops nothing\n"); return; } } } static int sep_sha1_init(struct ahash_request *req) { int error; int error1; struct this_task_ctx *ta_ctx = ahash_request_ctx(req); pr_debug("sep - doing sha1 init\n"); /* Clear out task context */ memset(ta_ctx, 0, sizeof(struct this_task_ctx)); ta_ctx->sep_used = sep_dev; ta_ctx->current_request = SHA1; ta_ctx->current_hash_req = req; ta_ctx->current_cypher_req = NULL; ta_ctx->hash_opmode = SEP_HASH_SHA1; ta_ctx->current_hash_stage = HASH_INIT; /* lock necessary so that only one entity touches the queues */ spin_lock_irq(&queue_lock); error = crypto_enqueue_request(&sep_queue, &req->base); if ((error != 0) && (error != -EINPROGRESS)) pr_debug(" sep - crypto enqueue failed: %x\n", error); error1 = sep_submit_work(ta_ctx->sep_used->workqueue, sep_dequeuer, (void *)&sep_queue); if (error1) pr_debug(" sep - workqueue submit failed: %x\n", error1); spin_unlock_irq(&queue_lock); /* We return result of crypto enqueue */ return error; } static int sep_sha1_update(struct ahash_request *req) { int error; int error1; struct this_task_ctx *ta_ctx = ahash_request_ctx(req); pr_debug("sep - doing sha1 update\n"); ta_ctx->sep_used = sep_dev; ta_ctx->current_request = SHA1; ta_ctx->current_hash_req = req; ta_ctx->current_cypher_req = NULL; ta_ctx->hash_opmode = SEP_HASH_SHA1; ta_ctx->current_hash_stage = HASH_UPDATE; /* lock necessary so that only one entity touches the queues */ spin_lock_irq(&queue_lock); error = crypto_enqueue_request(&sep_queue, &req->base); if ((error != 0) && (error != -EINPROGRESS)) pr_debug(" sep - crypto enqueue failed: %x\n", error); error1 = sep_submit_work(ta_ctx->sep_used->workqueue, sep_dequeuer, (void *)&sep_queue); if (error1) pr_debug(" sep - workqueue submit failed: %x\n", error1); spin_unlock_irq(&queue_lock); /* We return result of crypto enqueue */ return error; } static int sep_sha1_final(struct ahash_request *req) { int error; int error1; struct this_task_ctx *ta_ctx = ahash_request_ctx(req); pr_debug("sep - doing sha1 final\n"); ta_ctx->sep_used = sep_dev; ta_ctx->current_request = SHA1; ta_ctx->current_hash_req = req; ta_ctx->current_cypher_req = NULL; ta_ctx->hash_opmode = SEP_HASH_SHA1; ta_ctx->current_hash_stage = HASH_FINISH; /* lock necessary so that only one entity touches the queues */ spin_lock_irq(&queue_lock); error = crypto_enqueue_request(&sep_queue, &req->base); if ((error != 0) && (error != -EINPROGRESS)) pr_debug(" sep - crypto enqueue failed: %x\n", error); error1 = sep_submit_work(ta_ctx->sep_used->workqueue, sep_dequeuer, (void *)&sep_queue); if (error1) pr_debug(" sep - workqueue submit failed: %x\n", error1); spin_unlock_irq(&queue_lock); /* We return result of crypto enqueue */ return error; } static int sep_sha1_digest(struct ahash_request *req) { int error; int error1; struct this_task_ctx *ta_ctx = ahash_request_ctx(req); pr_debug("sep - doing sha1 digest\n"); /* Clear out task context */ memset(ta_ctx, 0, sizeof(struct this_task_ctx)); ta_ctx->sep_used = sep_dev; ta_ctx->current_request = SHA1; ta_ctx->current_hash_req = req; ta_ctx->current_cypher_req = NULL; ta_ctx->hash_opmode = SEP_HASH_SHA1; ta_ctx->current_hash_stage = HASH_DIGEST; /* lock necessary so that only one entity touches the queues */ spin_lock_irq(&queue_lock); error = crypto_enqueue_request(&sep_queue, &req->base); if ((error != 0) && (error != -EINPROGRESS)) pr_debug(" sep - crypto enqueue failed: %x\n", error); error1 = sep_submit_work(ta_ctx->sep_used->workqueue, sep_dequeuer, (void *)&sep_queue); if (error1) pr_debug(" sep - workqueue submit failed: %x\n", error1); spin_unlock_irq(&queue_lock); /* We return result of crypto enqueue */ return error; } static int sep_sha1_finup(struct ahash_request *req) { int error; int error1; struct this_task_ctx *ta_ctx = ahash_request_ctx(req); pr_debug("sep - doing sha1 finup\n"); ta_ctx->sep_used = sep_dev; ta_ctx->current_request = SHA1; ta_ctx->current_hash_req = req; ta_ctx->current_cypher_req = NULL; ta_ctx->hash_opmode = SEP_HASH_SHA1; ta_ctx->current_hash_stage = HASH_FINUP_DATA; /* lock necessary so that only one entity touches the queues */ spin_lock_irq(&queue_lock); error = crypto_enqueue_request(&sep_queue, &req->base); if ((error != 0) && (error != -EINPROGRESS)) pr_debug(" sep - crypto enqueue failed: %x\n", error); error1 = sep_submit_work(ta_ctx->sep_used->workqueue, sep_dequeuer, (void *)&sep_queue); if (error1) pr_debug(" sep - workqueue submit failed: %x\n", error1); spin_unlock_irq(&queue_lock); /* We return result of crypto enqueue */ return error; } static int sep_md5_init(struct ahash_request *req) { int error; int error1; struct this_task_ctx *ta_ctx = ahash_request_ctx(req); pr_debug("sep - doing md5 init\n"); /* Clear out task context */ memset(ta_ctx, 0, sizeof(struct this_task_ctx)); ta_ctx->sep_used = sep_dev; ta_ctx->current_request = MD5; ta_ctx->current_hash_req = req; ta_ctx->current_cypher_req = NULL; ta_ctx->hash_opmode = SEP_HASH_MD5; ta_ctx->current_hash_stage = HASH_INIT; /* lock necessary so that only one entity touches the queues */ spin_lock_irq(&queue_lock); error = crypto_enqueue_request(&sep_queue, &req->base); if ((error != 0) && (error != -EINPROGRESS)) pr_debug(" sep - crypto enqueue failed: %x\n", error); error1 = sep_submit_work(ta_ctx->sep_used->workqueue, sep_dequeuer, (void *)&sep_queue); if (error1) pr_debug(" sep - workqueue submit failed: %x\n", error1); spin_unlock_irq(&queue_lock); /* We return result of crypto enqueue */ return error; } static int sep_md5_update(struct ahash_request *req) { int error; int error1; struct this_task_ctx *ta_ctx = ahash_request_ctx(req); pr_debug("sep - doing md5 update\n"); ta_ctx->sep_used = sep_dev; ta_ctx->current_request = MD5; ta_ctx->current_hash_req = req; ta_ctx->current_cypher_req = NULL; ta_ctx->hash_opmode = SEP_HASH_MD5; ta_ctx->current_hash_stage = HASH_UPDATE; /* lock necessary so that only one entity touches the queues */ spin_lock_irq(&queue_lock); error = crypto_enqueue_request(&sep_queue, &req->base); if ((error != 0) && (error != -EINPROGRESS)) pr_debug(" sep - crypto enqueue failed: %x\n", error); error1 = sep_submit_work(ta_ctx->sep_used->workqueue, sep_dequeuer, (void *)&sep_queue); if (error1) pr_debug(" sep - workqueue submit failed: %x\n", error1); spin_unlock_irq(&queue_lock); /* We return result of crypto enqueue */ return error; } static int sep_md5_final(struct ahash_request *req) { int error; int error1; struct this_task_ctx *ta_ctx = ahash_request_ctx(req); pr_debug("sep - doing md5 final\n"); ta_ctx->sep_used = sep_dev; ta_ctx->current_request = MD5; ta_ctx->current_hash_req = req; ta_ctx->current_cypher_req = NULL; ta_ctx->hash_opmode = SEP_HASH_MD5; ta_ctx->current_hash_stage = HASH_FINISH; /* lock necessary so that only one entity touches the queues */ spin_lock_irq(&queue_lock); error = crypto_enqueue_request(&sep_queue, &req->base); if ((error != 0) && (error != -EINPROGRESS)) pr_debug(" sep - crypto enqueue failed: %x\n", error); error1 = sep_submit_work(ta_ctx->sep_used->workqueue, sep_dequeuer, (void *)&sep_queue); if (error1) pr_debug(" sep - workqueue submit failed: %x\n", error1); spin_unlock_irq(&queue_lock); /* We return result of crypto enqueue */ return error; } static int sep_md5_digest(struct ahash_request *req) { int error; int error1; struct this_task_ctx *ta_ctx = ahash_request_ctx(req); pr_debug("sep - doing md5 digest\n"); /* Clear out task context */ memset(ta_ctx, 0, sizeof(struct this_task_ctx)); ta_ctx->sep_used = sep_dev; ta_ctx->current_request = MD5; ta_ctx->current_hash_req = req; ta_ctx->current_cypher_req = NULL; ta_ctx->hash_opmode = SEP_HASH_MD5; ta_ctx->current_hash_stage = HASH_DIGEST; /* lock necessary so that only one entity touches the queues */ spin_lock_irq(&queue_lock); error = crypto_enqueue_request(&sep_queue, &req->base); if ((error != 0) && (error != -EINPROGRESS)) pr_debug(" sep - crypto enqueue failed: %x\n", error); error1 = sep_submit_work(ta_ctx->sep_used->workqueue, sep_dequeuer, (void *)&sep_queue); if (error1) pr_debug(" sep - workqueue submit failed: %x\n", error1); spin_unlock_irq(&queue_lock); /* We return result of crypto enqueue */ return error; } static int sep_md5_finup(struct ahash_request *req) { int error; int error1; struct this_task_ctx *ta_ctx = ahash_request_ctx(req); pr_debug("sep - doing md5 finup\n"); ta_ctx->sep_used = sep_dev; ta_ctx->current_request = MD5; ta_ctx->current_hash_req = req; ta_ctx->current_cypher_req = NULL; ta_ctx->hash_opmode = SEP_HASH_MD5; ta_ctx->current_hash_stage = HASH_FINUP_DATA; /* lock necessary so that only one entity touches the queues */ spin_lock_irq(&queue_lock); error = crypto_enqueue_request(&sep_queue, &req->base); if ((error != 0) && (error != -EINPROGRESS)) pr_debug(" sep - crypto enqueue failed: %x\n", error); error1 = sep_submit_work(ta_ctx->sep_used->workqueue, sep_dequeuer, (void *)&sep_queue); if (error1) pr_debug(" sep - workqueue submit failed: %x\n", error1); spin_unlock_irq(&queue_lock); /* We return result of crypto enqueue */ return error; } static int sep_sha224_init(struct ahash_request *req) { int error; int error1; struct this_task_ctx *ta_ctx = ahash_request_ctx(req); pr_debug("sep - doing sha224 init\n"); /* Clear out task context */ memset(ta_ctx, 0, sizeof(struct this_task_ctx)); ta_ctx->sep_used = sep_dev; ta_ctx->current_request = SHA224; ta_ctx->current_hash_req = req; ta_ctx->current_cypher_req = NULL; ta_ctx->hash_opmode = SEP_HASH_SHA224; ta_ctx->current_hash_stage = HASH_INIT; /* lock necessary so that only one entity touches the queues */ spin_lock_irq(&queue_lock); error = crypto_enqueue_request(&sep_queue, &req->base); if ((error != 0) && (error != -EINPROGRESS)) pr_debug(" sep - crypto enqueue failed: %x\n", error); error1 = sep_submit_work(ta_ctx->sep_used->workqueue, sep_dequeuer, (void *)&sep_queue); if (error1) pr_debug(" sep - workqueue submit failed: %x\n", error1); spin_unlock_irq(&queue_lock); /* We return result of crypto enqueue */ return error; } static int sep_sha224_update(struct ahash_request *req) { int error; int error1; struct this_task_ctx *ta_ctx = ahash_request_ctx(req); pr_debug("sep - doing sha224 update\n"); ta_ctx->sep_used = sep_dev; ta_ctx->current_request = SHA224; ta_ctx->current_hash_req = req; ta_ctx->current_cypher_req = NULL; ta_ctx->hash_opmode = SEP_HASH_SHA224; ta_ctx->current_hash_stage = HASH_UPDATE; /* lock necessary so that only one entity touches the queues */ spin_lock_irq(&queue_lock); error = crypto_enqueue_request(&sep_queue, &req->base); if ((error != 0) && (error != -EINPROGRESS)) pr_debug(" sep - crypto enqueue failed: %x\n", error); error1 = sep_submit_work(ta_ctx->sep_used->workqueue, sep_dequeuer, (void *)&sep_queue); if (error1) pr_debug(" sep - workqueue submit failed: %x\n", error1); spin_unlock_irq(&queue_lock); /* We return result of crypto enqueue */ return error; } static int sep_sha224_final(struct ahash_request *req) { int error; int error1; struct this_task_ctx *ta_ctx = ahash_request_ctx(req); pr_debug("sep - doing sha224 final\n"); ta_ctx->sep_used = sep_dev; ta_ctx->current_request = SHA224; ta_ctx->current_hash_req = req; ta_ctx->current_cypher_req = NULL; ta_ctx->hash_opmode = SEP_HASH_SHA224; ta_ctx->current_hash_stage = HASH_FINISH; /* lock necessary so that only one entity touches the queues */ spin_lock_irq(&queue_lock); error = crypto_enqueue_request(&sep_queue, &req->base); if ((error != 0) && (error != -EINPROGRESS)) pr_debug(" sep - crypto enqueue failed: %x\n", error); error1 = sep_submit_work(ta_ctx->sep_used->workqueue, sep_dequeuer, (void *)&sep_queue); if (error1) pr_debug(" sep - workqueue submit failed: %x\n", error1); spin_unlock_irq(&queue_lock); /* We return result of crypto enqueue */ return error; } static int sep_sha224_digest(struct ahash_request *req) { int error; int error1; struct this_task_ctx *ta_ctx = ahash_request_ctx(req); pr_debug("sep - doing sha224 digest\n"); /* Clear out task context */ memset(ta_ctx, 0, sizeof(struct this_task_ctx)); ta_ctx->sep_used = sep_dev; ta_ctx->current_request = SHA224; ta_ctx->current_hash_req = req; ta_ctx->current_cypher_req = NULL; ta_ctx->hash_opmode = SEP_HASH_SHA224; ta_ctx->current_hash_stage = HASH_DIGEST; /* lock necessary so that only one entity touches the queues */ spin_lock_irq(&queue_lock); error = crypto_enqueue_request(&sep_queue, &req->base); if ((error != 0) && (error != -EINPROGRESS)) pr_debug(" sep - crypto enqueue failed: %x\n", error); error1 = sep_submit_work(ta_ctx->sep_used->workqueue, sep_dequeuer, (void *)&sep_queue); if (error1) pr_debug(" sep - workqueue submit failed: %x\n", error1); spin_unlock_irq(&queue_lock); /* We return result of crypto enqueue */ return error; } static int sep_sha224_finup(struct ahash_request *req) { int error; int error1; struct this_task_ctx *ta_ctx = ahash_request_ctx(req); pr_debug("sep - doing sha224 finup\n"); ta_ctx->sep_used = sep_dev; ta_ctx->current_request = SHA224; ta_ctx->current_hash_req = req; ta_ctx->current_cypher_req = NULL; ta_ctx->hash_opmode = SEP_HASH_SHA224; ta_ctx->current_hash_stage = HASH_FINUP_DATA; /* lock necessary so that only one entity touches the queues */ spin_lock_irq(&queue_lock); error = crypto_enqueue_request(&sep_queue, &req->base); if ((error != 0) && (error != -EINPROGRESS)) pr_debug(" sep - crypto enqueue failed: %x\n", error); error1 = sep_submit_work(ta_ctx->sep_used->workqueue, sep_dequeuer, (void *)&sep_queue); if (error1) pr_debug(" sep - workqueue submit failed: %x\n", error1); spin_unlock_irq(&queue_lock); /* We return result of crypto enqueue */ return error; } static int sep_sha256_init(struct ahash_request *req) { int error; int error1; struct this_task_ctx *ta_ctx = ahash_request_ctx(req); pr_debug("sep - doing sha256 init\n"); /* Clear out task context */ memset(ta_ctx, 0, sizeof(struct this_task_ctx)); ta_ctx->sep_used = sep_dev; ta_ctx->current_request = SHA256; ta_ctx->current_hash_req = req; ta_ctx->current_cypher_req = NULL; ta_ctx->hash_opmode = SEP_HASH_SHA256; ta_ctx->current_hash_stage = HASH_INIT; /* lock necessary so that only one entity touches the queues */ spin_lock_irq(&queue_lock); error = crypto_enqueue_request(&sep_queue, &req->base); if ((error != 0) && (error != -EINPROGRESS)) pr_debug(" sep - crypto enqueue failed: %x\n", error); error1 = sep_submit_work(ta_ctx->sep_used->workqueue, sep_dequeuer, (void *)&sep_queue); if (error1) pr_debug(" sep - workqueue submit failed: %x\n", error1); spin_unlock_irq(&queue_lock); /* We return result of crypto enqueue */ return error; } static int sep_sha256_update(struct ahash_request *req) { int error; int error1; struct this_task_ctx *ta_ctx = ahash_request_ctx(req); pr_debug("sep - doing sha256 update\n"); ta_ctx->sep_used = sep_dev; ta_ctx->current_request = SHA256; ta_ctx->current_hash_req = req; ta_ctx->current_cypher_req = NULL; ta_ctx->hash_opmode = SEP_HASH_SHA256; ta_ctx->current_hash_stage = HASH_UPDATE; /* lock necessary so that only one entity touches the queues */ spin_lock_irq(&queue_lock); error = crypto_enqueue_request(&sep_queue, &req->base); if ((error != 0) && (error != -EINPROGRESS)) pr_debug(" sep - crypto enqueue failed: %x\n", error); error1 = sep_submit_work(ta_ctx->sep_used->workqueue, sep_dequeuer, (void *)&sep_queue); if (error1) pr_debug(" sep - workqueue submit failed: %x\n", error1); spin_unlock_irq(&queue_lock); /* We return result of crypto enqueue */ return error; } static int sep_sha256_final(struct ahash_request *req) { int error; int error1; struct this_task_ctx *ta_ctx = ahash_request_ctx(req); pr_debug("sep - doing sha256 final\n"); ta_ctx->sep_used = sep_dev; ta_ctx->current_request = SHA256; ta_ctx->current_hash_req = req; ta_ctx->current_cypher_req = NULL; ta_ctx->hash_opmode = SEP_HASH_SHA256; ta_ctx->current_hash_stage = HASH_FINISH; /* lock necessary so that only one entity touches the queues */ spin_lock_irq(&queue_lock); error = crypto_enqueue_request(&sep_queue, &req->base); if ((error != 0) && (error != -EINPROGRESS)) pr_debug(" sep - crypto enqueue failed: %x\n", error); error1 = sep_submit_work(ta_ctx->sep_used->workqueue, sep_dequeuer, (void *)&sep_queue); if (error1) pr_debug(" sep - workqueue submit failed: %x\n", error1); spin_unlock_irq(&queue_lock); /* We return result of crypto enqueue */ return error; } static int sep_sha256_digest(struct ahash_request *req) { int error; int error1; struct this_task_ctx *ta_ctx = ahash_request_ctx(req); pr_debug("sep - doing sha256 digest\n"); /* Clear out task context */ memset(ta_ctx, 0, sizeof(struct this_task_ctx)); ta_ctx->sep_used = sep_dev; ta_ctx->current_request = SHA256; ta_ctx->current_hash_req = req; ta_ctx->current_cypher_req = NULL; ta_ctx->hash_opmode = SEP_HASH_SHA256; ta_ctx->current_hash_stage = HASH_DIGEST; /* lock necessary so that only one entity touches the queues */ spin_lock_irq(&queue_lock); error = crypto_enqueue_request(&sep_queue, &req->base); if ((error != 0) && (error != -EINPROGRESS)) pr_debug(" sep - crypto enqueue failed: %x\n", error); error1 = sep_submit_work(ta_ctx->sep_used->workqueue, sep_dequeuer, (void *)&sep_queue); if (error1) pr_debug(" sep - workqueue submit failed: %x\n", error1); spin_unlock_irq(&queue_lock); /* We return result of crypto enqueue */ return error; } static int sep_sha256_finup(struct ahash_request *req) { int error; int error1; struct this_task_ctx *ta_ctx = ahash_request_ctx(req); pr_debug("sep - doing sha256 finup\n"); ta_ctx->sep_used = sep_dev; ta_ctx->current_request = SHA256; ta_ctx->current_hash_req = req; ta_ctx->current_cypher_req = NULL; ta_ctx->hash_opmode = SEP_HASH_SHA256; ta_ctx->current_hash_stage = HASH_FINUP_DATA; /* lock necessary so that only one entity touches the queues */ spin_lock_irq(&queue_lock); error = crypto_enqueue_request(&sep_queue, &req->base); if ((error != 0) && (error != -EINPROGRESS)) pr_debug(" sep - crypto enqueue failed: %x\n", error); error1 = sep_submit_work(ta_ctx->sep_used->workqueue, sep_dequeuer, (void *)&sep_queue); if (error1) pr_debug(" sep - workqueue submit failed: %x\n", error1); spin_unlock_irq(&queue_lock); /* We return result of crypto enqueue */ return error; } static int sep_crypto_init(struct crypto_tfm *tfm) { const char *alg_name = crypto_tfm_alg_name(tfm); if (alg_name == NULL) pr_debug("sep_crypto_init alg is NULL\n"); else pr_debug("sep_crypto_init alg is %s\n", alg_name); tfm->crt_ablkcipher.reqsize = sizeof(struct this_task_ctx); return 0; } static void sep_crypto_exit(struct crypto_tfm *tfm) { pr_debug("sep_crypto_exit\n"); } static int sep_aes_setkey(struct crypto_ablkcipher *tfm, const u8 *key, unsigned int keylen) { struct sep_system_ctx *sctx = crypto_ablkcipher_ctx(tfm); pr_debug("sep aes setkey\n"); pr_debug("tfm is %p sctx is %p\n", tfm, sctx); switch (keylen) { case SEP_AES_KEY_128_SIZE: sctx->aes_key_size = AES_128; break; case SEP_AES_KEY_192_SIZE: sctx->aes_key_size = AES_192; break; case SEP_AES_KEY_256_SIZE: sctx->aes_key_size = AES_256; break; case SEP_AES_KEY_512_SIZE: sctx->aes_key_size = AES_512; break; default: pr_debug("invalid sep aes key size %x\n", keylen); return -EINVAL; } memset(&sctx->key.aes, 0, sizeof(u32) * SEP_AES_MAX_KEY_SIZE_WORDS); memcpy(&sctx->key.aes, key, keylen); sctx->keylen = keylen; /* Indicate to encrypt/decrypt function to send key to SEP */ sctx->key_sent = 0; return 0; } static int sep_aes_ecb_encrypt(struct ablkcipher_request *req) { int error; int error1; struct this_task_ctx *ta_ctx = ablkcipher_request_ctx(req); pr_debug("sep - doing aes ecb encrypt\n"); /* Clear out task context */ memset(ta_ctx, 0, sizeof(struct this_task_ctx)); ta_ctx->sep_used = sep_dev; ta_ctx->current_request = AES_ECB; ta_ctx->current_hash_req = NULL; ta_ctx->current_cypher_req = req; ta_ctx->aes_encmode = SEP_AES_ENCRYPT; ta_ctx->aes_opmode = SEP_AES_ECB; ta_ctx->init_opcode = SEP_AES_INIT_OPCODE; ta_ctx->block_opcode = SEP_AES_BLOCK_OPCODE; /* lock necessary so that only one entity touches the queues */ spin_lock_irq(&queue_lock); error = crypto_enqueue_request(&sep_queue, &req->base); if ((error != 0) && (error != -EINPROGRESS)) pr_debug(" sep - crypto enqueue failed: %x\n", error); error1 = sep_submit_work(ta_ctx->sep_used->workqueue, sep_dequeuer, (void *)&sep_queue); if (error1) pr_debug(" sep - workqueue submit failed: %x\n", error1); spin_unlock_irq(&queue_lock); /* We return result of crypto enqueue */ return error; } static int sep_aes_ecb_decrypt(struct ablkcipher_request *req) { int error; int error1; struct this_task_ctx *ta_ctx = ablkcipher_request_ctx(req); pr_debug("sep - doing aes ecb decrypt\n"); /* Clear out task context */ memset(ta_ctx, 0, sizeof(struct this_task_ctx)); ta_ctx->sep_used = sep_dev; ta_ctx->current_request = AES_ECB; ta_ctx->current_hash_req = NULL; ta_ctx->current_cypher_req = req; ta_ctx->aes_encmode = SEP_AES_DECRYPT; ta_ctx->aes_opmode = SEP_AES_ECB; ta_ctx->init_opcode = SEP_AES_INIT_OPCODE; ta_ctx->block_opcode = SEP_AES_BLOCK_OPCODE; /* lock necessary so that only one entity touches the queues */ spin_lock_irq(&queue_lock); error = crypto_enqueue_request(&sep_queue, &req->base); if ((error != 0) && (error != -EINPROGRESS)) pr_debug(" sep - crypto enqueue failed: %x\n", error); error1 = sep_submit_work(ta_ctx->sep_used->workqueue, sep_dequeuer, (void *)&sep_queue); if (error1) pr_debug(" sep - workqueue submit failed: %x\n", error1); spin_unlock_irq(&queue_lock); /* We return result of crypto enqueue */ return error; } static int sep_aes_cbc_encrypt(struct ablkcipher_request *req) { int error; int error1; struct this_task_ctx *ta_ctx = ablkcipher_request_ctx(req); struct sep_system_ctx *sctx = crypto_ablkcipher_ctx( crypto_ablkcipher_reqtfm(req)); pr_debug("sep - doing aes cbc encrypt\n"); /* Clear out task context */ memset(ta_ctx, 0, sizeof(struct this_task_ctx)); pr_debug("tfm is %p sctx is %p and ta_ctx is %p\n", crypto_ablkcipher_reqtfm(req), sctx, ta_ctx); ta_ctx->sep_used = sep_dev; ta_ctx->current_request = AES_CBC; ta_ctx->current_hash_req = NULL; ta_ctx->current_cypher_req = req; ta_ctx->aes_encmode = SEP_AES_ENCRYPT; ta_ctx->aes_opmode = SEP_AES_CBC; ta_ctx->init_opcode = SEP_AES_INIT_OPCODE; ta_ctx->block_opcode = SEP_AES_BLOCK_OPCODE; /* lock necessary so that only one entity touches the queues */ spin_lock_irq(&queue_lock); error = crypto_enqueue_request(&sep_queue, &req->base); if ((error != 0) && (error != -EINPROGRESS)) pr_debug(" sep - crypto enqueue failed: %x\n", error); error1 = sep_submit_work(ta_ctx->sep_used->workqueue, sep_dequeuer, (void *)&sep_queue); if (error1) pr_debug(" sep - workqueue submit failed: %x\n", error1); spin_unlock_irq(&queue_lock); /* We return result of crypto enqueue */ return error; } static int sep_aes_cbc_decrypt(struct ablkcipher_request *req) { int error; int error1; struct this_task_ctx *ta_ctx = ablkcipher_request_ctx(req); struct sep_system_ctx *sctx = crypto_ablkcipher_ctx( crypto_ablkcipher_reqtfm(req)); pr_debug("sep - doing aes cbc decrypt\n"); pr_debug("tfm is %p sctx is %p and ta_ctx is %p\n", crypto_ablkcipher_reqtfm(req), sctx, ta_ctx); /* Clear out task context */ memset(ta_ctx, 0, sizeof(struct this_task_ctx)); ta_ctx->sep_used = sep_dev; ta_ctx->current_request = AES_CBC; ta_ctx->current_hash_req = NULL; ta_ctx->current_cypher_req = req; ta_ctx->aes_encmode = SEP_AES_DECRYPT; ta_ctx->aes_opmode = SEP_AES_CBC; ta_ctx->init_opcode = SEP_AES_INIT_OPCODE; ta_ctx->block_opcode = SEP_AES_BLOCK_OPCODE; /* lock necessary so that only one entity touches the queues */ spin_lock_irq(&queue_lock); error = crypto_enqueue_request(&sep_queue, &req->base); if ((error != 0) && (error != -EINPROGRESS)) pr_debug(" sep - crypto enqueue failed: %x\n", error); error1 = sep_submit_work(ta_ctx->sep_used->workqueue, sep_dequeuer, (void *)&sep_queue); if (error1) pr_debug(" sep - workqueue submit failed: %x\n", error1); spin_unlock_irq(&queue_lock); /* We return result of crypto enqueue */ return error; } static int sep_des_setkey(struct crypto_ablkcipher *tfm, const u8 *key, unsigned int keylen) { struct sep_system_ctx *sctx = crypto_ablkcipher_ctx(tfm); struct crypto_tfm *ctfm = crypto_ablkcipher_tfm(tfm); u32 *flags = &ctfm->crt_flags; pr_debug("sep des setkey\n"); switch (keylen) { case DES_KEY_SIZE: sctx->des_nbr_keys = DES_KEY_1; break; case DES_KEY_SIZE * 2: sctx->des_nbr_keys = DES_KEY_2; break; case DES_KEY_SIZE * 3: sctx->des_nbr_keys = DES_KEY_3; break; default: pr_debug("invalid key size %x\n", keylen); return -EINVAL; } if ((*flags & CRYPTO_TFM_REQ_WEAK_KEY) && (sep_weak_key(key, keylen))) { *flags |= CRYPTO_TFM_RES_WEAK_KEY; pr_debug("weak key\n"); return -EINVAL; } memset(&sctx->key.des, 0, sizeof(struct sep_des_key)); memcpy(&sctx->key.des.key1, key, keylen); sctx->keylen = keylen; /* Indicate to encrypt/decrypt function to send key to SEP */ sctx->key_sent = 0; return 0; } static int sep_des_ebc_encrypt(struct ablkcipher_request *req) { int error; int error1; struct this_task_ctx *ta_ctx = ablkcipher_request_ctx(req); pr_debug("sep - doing des ecb encrypt\n"); /* Clear out task context */ memset(ta_ctx, 0, sizeof(struct this_task_ctx)); ta_ctx->sep_used = sep_dev; ta_ctx->current_request = DES_ECB; ta_ctx->current_hash_req = NULL; ta_ctx->current_cypher_req = req; ta_ctx->des_encmode = SEP_DES_ENCRYPT; ta_ctx->des_opmode = SEP_DES_ECB; ta_ctx->init_opcode = SEP_DES_INIT_OPCODE; ta_ctx->block_opcode = SEP_DES_BLOCK_OPCODE; /* lock necessary so that only one entity touches the queues */ spin_lock_irq(&queue_lock); error = crypto_enqueue_request(&sep_queue, &req->base); if ((error != 0) && (error != -EINPROGRESS)) pr_debug(" sep - crypto enqueue failed: %x\n", error); error1 = sep_submit_work(ta_ctx->sep_used->workqueue, sep_dequeuer, (void *)&sep_queue); if (error1) pr_debug(" sep - workqueue submit failed: %x\n", error1); spin_unlock_irq(&queue_lock); /* We return result of crypto enqueue */ return error; } static int sep_des_ebc_decrypt(struct ablkcipher_request *req) { int error; int error1; struct this_task_ctx *ta_ctx = ablkcipher_request_ctx(req); pr_debug("sep - doing des ecb decrypt\n"); /* Clear out task context */ memset(ta_ctx, 0, sizeof(struct this_task_ctx)); ta_ctx->sep_used = sep_dev; ta_ctx->current_request = DES_ECB; ta_ctx->current_hash_req = NULL; ta_ctx->current_cypher_req = req; ta_ctx->des_encmode = SEP_DES_DECRYPT; ta_ctx->des_opmode = SEP_DES_ECB; ta_ctx->init_opcode = SEP_DES_INIT_OPCODE; ta_ctx->block_opcode = SEP_DES_BLOCK_OPCODE; /* lock necessary so that only one entity touches the queues */ spin_lock_irq(&queue_lock); error = crypto_enqueue_request(&sep_queue, &req->base); if ((error != 0) && (error != -EINPROGRESS)) pr_debug(" sep - crypto enqueue failed: %x\n", error); error1 = sep_submit_work(ta_ctx->sep_used->workqueue, sep_dequeuer, (void *)&sep_queue); if (error1) pr_debug(" sep - workqueue submit failed: %x\n", error1); spin_unlock_irq(&queue_lock); /* We return result of crypto enqueue */ return error; } static int sep_des_cbc_encrypt(struct ablkcipher_request *req) { int error; int error1; struct this_task_ctx *ta_ctx = ablkcipher_request_ctx(req); pr_debug("sep - doing des cbc encrypt\n"); /* Clear out task context */ memset(ta_ctx, 0, sizeof(struct this_task_ctx)); ta_ctx->sep_used = sep_dev; ta_ctx->current_request = DES_CBC; ta_ctx->current_hash_req = NULL; ta_ctx->current_cypher_req = req; ta_ctx->des_encmode = SEP_DES_ENCRYPT; ta_ctx->des_opmode = SEP_DES_CBC; ta_ctx->init_opcode = SEP_DES_INIT_OPCODE; ta_ctx->block_opcode = SEP_DES_BLOCK_OPCODE; /* lock necessary so that only one entity touches the queues */ spin_lock_irq(&queue_lock); error = crypto_enqueue_request(&sep_queue, &req->base); if ((error != 0) && (error != -EINPROGRESS)) pr_debug(" sep - crypto enqueue failed: %x\n", error); error1 = sep_submit_work(ta_ctx->sep_used->workqueue, sep_dequeuer, (void *)&sep_queue); if (error1) pr_debug(" sep - workqueue submit failed: %x\n", error1); spin_unlock_irq(&queue_lock); /* We return result of crypto enqueue */ return error; } static int sep_des_cbc_decrypt(struct ablkcipher_request *req) { int error; int error1; struct this_task_ctx *ta_ctx = ablkcipher_request_ctx(req); pr_debug("sep - doing des ecb decrypt\n"); /* Clear out task context */ memset(ta_ctx, 0, sizeof(struct this_task_ctx)); ta_ctx->sep_used = sep_dev; ta_ctx->current_request = DES_CBC; ta_ctx->current_hash_req = NULL; ta_ctx->current_cypher_req = req; ta_ctx->des_encmode = SEP_DES_DECRYPT; ta_ctx->des_opmode = SEP_DES_CBC; ta_ctx->init_opcode = SEP_DES_INIT_OPCODE; ta_ctx->block_opcode = SEP_DES_BLOCK_OPCODE; /* lock necessary so that only one entity touches the queues */ spin_lock_irq(&queue_lock); error = crypto_enqueue_request(&sep_queue, &req->base); if ((error != 0) && (error != -EINPROGRESS)) pr_debug(" sep - crypto enqueue failed: %x\n", error); error1 = sep_submit_work(ta_ctx->sep_used->workqueue, sep_dequeuer, (void *)&sep_queue); if (error1) pr_debug(" sep - workqueue submit failed: %x\n", error1); spin_unlock_irq(&queue_lock); /* We return result of crypto enqueue */ return error; } static struct ahash_alg hash_algs[] = { { .init = sep_sha1_init, .update = sep_sha1_update, .final = sep_sha1_final, .digest = sep_sha1_digest, .finup = sep_sha1_finup, .halg = { .digestsize = SHA1_DIGEST_SIZE, .base = { .cra_name = "sha1", .cra_driver_name = "sha1-sep", .cra_priority = 100, .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_ASYNC, .cra_blocksize = SHA1_BLOCK_SIZE, .cra_ctxsize = sizeof(struct sep_system_ctx), .cra_alignmask = 0, .cra_module = THIS_MODULE, .cra_init = sep_hash_cra_init, .cra_exit = sep_hash_cra_exit, } } }, { .init = sep_md5_init, .update = sep_md5_update, .final = sep_md5_final, .digest = sep_md5_digest, .finup = sep_md5_finup, .halg = { .digestsize = MD5_DIGEST_SIZE, .base = { .cra_name = "md5", .cra_driver_name = "md5-sep", .cra_priority = 100, .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_ASYNC, .cra_blocksize = SHA1_BLOCK_SIZE, .cra_ctxsize = sizeof(struct sep_system_ctx), .cra_alignmask = 0, .cra_module = THIS_MODULE, .cra_init = sep_hash_cra_init, .cra_exit = sep_hash_cra_exit, } } }, { .init = sep_sha224_init, .update = sep_sha224_update, .final = sep_sha224_final, .digest = sep_sha224_digest, .finup = sep_sha224_finup, .halg = { .digestsize = SHA224_DIGEST_SIZE, .base = { .cra_name = "sha224", .cra_driver_name = "sha224-sep", .cra_priority = 100, .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_ASYNC, .cra_blocksize = SHA224_BLOCK_SIZE, .cra_ctxsize = sizeof(struct sep_system_ctx), .cra_alignmask = 0, .cra_module = THIS_MODULE, .cra_init = sep_hash_cra_init, .cra_exit = sep_hash_cra_exit, } } }, { .init = sep_sha256_init, .update = sep_sha256_update, .final = sep_sha256_final, .digest = sep_sha256_digest, .finup = sep_sha256_finup, .halg = { .digestsize = SHA256_DIGEST_SIZE, .base = { .cra_name = "sha256", .cra_driver_name = "sha256-sep", .cra_priority = 100, .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_ASYNC, .cra_blocksize = SHA256_BLOCK_SIZE, .cra_ctxsize = sizeof(struct sep_system_ctx), .cra_alignmask = 0, .cra_module = THIS_MODULE, .cra_init = sep_hash_cra_init, .cra_exit = sep_hash_cra_exit, } } } }; static struct crypto_alg crypto_algs[] = { { .cra_name = "ecb(aes)", .cra_driver_name = "ecb-aes-sep", .cra_priority = 100, .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct sep_system_ctx), .cra_alignmask = 0, .cra_type = &crypto_ablkcipher_type, .cra_module = THIS_MODULE, .cra_init = sep_crypto_init, .cra_exit = sep_crypto_exit, .cra_u.ablkcipher = { .min_keysize = AES_MIN_KEY_SIZE, .max_keysize = AES_MAX_KEY_SIZE, .setkey = sep_aes_setkey, .encrypt = sep_aes_ecb_encrypt, .decrypt = sep_aes_ecb_decrypt, } }, { .cra_name = "cbc(aes)", .cra_driver_name = "cbc-aes-sep", .cra_priority = 100, .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct sep_system_ctx), .cra_alignmask = 0, .cra_type = &crypto_ablkcipher_type, .cra_module = THIS_MODULE, .cra_init = sep_crypto_init, .cra_exit = sep_crypto_exit, .cra_u.ablkcipher = { .min_keysize = AES_MIN_KEY_SIZE, .max_keysize = AES_MAX_KEY_SIZE, .setkey = sep_aes_setkey, .encrypt = sep_aes_cbc_encrypt, .ivsize = AES_BLOCK_SIZE, .decrypt = sep_aes_cbc_decrypt, } }, { .cra_name = "ebc(des)", .cra_driver_name = "ebc-des-sep", .cra_priority = 100, .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, .cra_blocksize = DES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct sep_system_ctx), .cra_alignmask = 0, .cra_type = &crypto_ablkcipher_type, .cra_module = THIS_MODULE, .cra_init = sep_crypto_init, .cra_exit = sep_crypto_exit, .cra_u.ablkcipher = { .min_keysize = DES_KEY_SIZE, .max_keysize = DES_KEY_SIZE, .setkey = sep_des_setkey, .encrypt = sep_des_ebc_encrypt, .decrypt = sep_des_ebc_decrypt, } }, { .cra_name = "cbc(des)", .cra_driver_name = "cbc-des-sep", .cra_priority = 100, .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, .cra_blocksize = DES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct sep_system_ctx), .cra_alignmask = 0, .cra_type = &crypto_ablkcipher_type, .cra_module = THIS_MODULE, .cra_init = sep_crypto_init, .cra_exit = sep_crypto_exit, .cra_u.ablkcipher = { .min_keysize = DES_KEY_SIZE, .max_keysize = DES_KEY_SIZE, .setkey = sep_des_setkey, .encrypt = sep_des_cbc_encrypt, .ivsize = DES_BLOCK_SIZE, .decrypt = sep_des_cbc_decrypt, } }, { .cra_name = "ebc(des3-ede)", .cra_driver_name = "ebc-des3-ede-sep", .cra_priority = 100, .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, .cra_blocksize = DES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct sep_system_ctx), .cra_alignmask = 0, .cra_type = &crypto_ablkcipher_type, .cra_module = THIS_MODULE, .cra_init = sep_crypto_init, .cra_exit = sep_crypto_exit, .cra_u.ablkcipher = { .min_keysize = DES3_EDE_KEY_SIZE, .max_keysize = DES3_EDE_KEY_SIZE, .setkey = sep_des_setkey, .encrypt = sep_des_ebc_encrypt, .decrypt = sep_des_ebc_decrypt, } }, { .cra_name = "cbc(des3-ede)", .cra_driver_name = "cbc-des3--ede-sep", .cra_priority = 100, .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, .cra_blocksize = DES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct sep_system_ctx), .cra_alignmask = 0, .cra_type = &crypto_ablkcipher_type, .cra_module = THIS_MODULE, .cra_init = sep_crypto_init, .cra_exit = sep_crypto_exit, .cra_u.ablkcipher = { .min_keysize = DES3_EDE_KEY_SIZE, .max_keysize = DES3_EDE_KEY_SIZE, .setkey = sep_des_setkey, .encrypt = sep_des_cbc_encrypt, .decrypt = sep_des_cbc_decrypt, } } }; int sep_crypto_setup(void) { int err, i, j, k; tasklet_init(&sep_dev->finish_tasklet, sep_finish, (unsigned long)sep_dev); crypto_init_queue(&sep_queue, SEP_QUEUE_LENGTH); sep_dev->workqueue = create_singlethread_workqueue( "sep_crypto_workqueue"); if (!sep_dev->workqueue) { dev_warn(&sep_dev->pdev->dev, "cant create workqueue\n"); return -ENOMEM; } spin_lock_init(&queue_lock); err = 0; for (i = 0; i < ARRAY_SIZE(hash_algs); i++) { err = crypto_register_ahash(&hash_algs[i]); if (err) goto err_algs; } err = 0; for (j = 0; j < ARRAY_SIZE(crypto_algs); j++) { err = crypto_register_alg(&crypto_algs[j]); if (err) goto err_crypto_algs; } return err; err_algs: for (k = 0; k < i; k++) crypto_unregister_ahash(&hash_algs[k]); return err; err_crypto_algs: for (k = 0; k < j; k++) crypto_unregister_alg(&crypto_algs[k]); goto err_algs; } void sep_crypto_takedown(void) { int i; for (i = 0; i < ARRAY_SIZE(hash_algs); i++) crypto_unregister_ahash(&hash_algs[i]); for (i = 0; i < ARRAY_SIZE(crypto_algs); i++) crypto_unregister_alg(&crypto_algs[i]); tasklet_kill(&sep_dev->finish_tasklet); } #endif