3953 lines
104 KiB
C
3953 lines
104 KiB
C
/*
|
|
*
|
|
* 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 <linux/module.h>
|
|
#include <linux/miscdevice.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/cdev.h>
|
|
#include <linux/kdev_t.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/wait.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/err.h>
|
|
#include <linux/device.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/io.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/list.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/jiffies.h>
|
|
#include <linux/workqueue.h>
|
|
#include <linux/crypto.h>
|
|
#include <crypto/internal/hash.h>
|
|
#include <crypto/scatterwalk.h>
|
|
#include <crypto/sha.h>
|
|
#include <crypto/md5.h>
|
|
#include <crypto/aes.h>
|
|
#include <crypto/des.h>
|
|
#include <crypto/hash.h>
|
|
#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 int_error;
|
|
} 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 int_error;
|
|
} 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]);
|
|
destroy_workqueue(sep_dev->workqueue);
|
|
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]);
|
|
|
|
destroy_workqueue(sep_dev->workqueue);
|
|
tasklet_kill(&sep_dev->finish_tasklet);
|
|
}
|
|
|
|
#endif
|