linux/drivers/crypto/hisilicon/sec/sec_algs.c
John Garry 0b0cf6af3f crypto: hisilicon - Fix reference after free of memories on error path
coccicheck currently warns of the following issues in the driver:
drivers/crypto/hisilicon/sec/sec_algs.c:864:51-66: ERROR: reference preceded by free on line 812
drivers/crypto/hisilicon/sec/sec_algs.c:864:40-49: ERROR: reference preceded by free on line 813
drivers/crypto/hisilicon/sec/sec_algs.c:861:8-24: ERROR: reference preceded by free on line 814
drivers/crypto/hisilicon/sec/sec_algs.c:860:41-51: ERROR: reference preceded by free on line 815
drivers/crypto/hisilicon/sec/sec_algs.c:867:7-18: ERROR: reference preceded by free on line 816

It would appear than on certain error paths that we may attempt reference-
after-free some memories.

This patch fixes those issues. The solution doesn't look perfect, but
having same memories free'd possibly from separate functions makes it
tricky.

Fixes: 915e4e8413 ("crypto: hisilicon - SEC security accelerator driver")
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-11-09 17:35:43 +08:00

1126 lines
29 KiB
C

// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2016-2017 Hisilicon Limited. */
#include <linux/crypto.h>
#include <linux/dma-mapping.h>
#include <linux/dmapool.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <crypto/aes.h>
#include <crypto/algapi.h>
#include <crypto/des.h>
#include <crypto/skcipher.h>
#include <crypto/xts.h>
#include <crypto/internal/skcipher.h>
#include "sec_drv.h"
#define SEC_MAX_CIPHER_KEY 64
#define SEC_REQ_LIMIT SZ_32M
struct sec_c_alg_cfg {
unsigned c_alg : 3;
unsigned c_mode : 3;
unsigned key_len : 2;
unsigned c_width : 2;
};
static const struct sec_c_alg_cfg sec_c_alg_cfgs[] = {
[SEC_C_DES_ECB_64] = {
.c_alg = SEC_C_ALG_DES,
.c_mode = SEC_C_MODE_ECB,
.key_len = SEC_KEY_LEN_DES,
},
[SEC_C_DES_CBC_64] = {
.c_alg = SEC_C_ALG_DES,
.c_mode = SEC_C_MODE_CBC,
.key_len = SEC_KEY_LEN_DES,
},
[SEC_C_3DES_ECB_192_3KEY] = {
.c_alg = SEC_C_ALG_3DES,
.c_mode = SEC_C_MODE_ECB,
.key_len = SEC_KEY_LEN_3DES_3_KEY,
},
[SEC_C_3DES_ECB_192_2KEY] = {
.c_alg = SEC_C_ALG_3DES,
.c_mode = SEC_C_MODE_ECB,
.key_len = SEC_KEY_LEN_3DES_2_KEY,
},
[SEC_C_3DES_CBC_192_3KEY] = {
.c_alg = SEC_C_ALG_3DES,
.c_mode = SEC_C_MODE_CBC,
.key_len = SEC_KEY_LEN_3DES_3_KEY,
},
[SEC_C_3DES_CBC_192_2KEY] = {
.c_alg = SEC_C_ALG_3DES,
.c_mode = SEC_C_MODE_CBC,
.key_len = SEC_KEY_LEN_3DES_2_KEY,
},
[SEC_C_AES_ECB_128] = {
.c_alg = SEC_C_ALG_AES,
.c_mode = SEC_C_MODE_ECB,
.key_len = SEC_KEY_LEN_AES_128,
},
[SEC_C_AES_ECB_192] = {
.c_alg = SEC_C_ALG_AES,
.c_mode = SEC_C_MODE_ECB,
.key_len = SEC_KEY_LEN_AES_192,
},
[SEC_C_AES_ECB_256] = {
.c_alg = SEC_C_ALG_AES,
.c_mode = SEC_C_MODE_ECB,
.key_len = SEC_KEY_LEN_AES_256,
},
[SEC_C_AES_CBC_128] = {
.c_alg = SEC_C_ALG_AES,
.c_mode = SEC_C_MODE_CBC,
.key_len = SEC_KEY_LEN_AES_128,
},
[SEC_C_AES_CBC_192] = {
.c_alg = SEC_C_ALG_AES,
.c_mode = SEC_C_MODE_CBC,
.key_len = SEC_KEY_LEN_AES_192,
},
[SEC_C_AES_CBC_256] = {
.c_alg = SEC_C_ALG_AES,
.c_mode = SEC_C_MODE_CBC,
.key_len = SEC_KEY_LEN_AES_256,
},
[SEC_C_AES_CTR_128] = {
.c_alg = SEC_C_ALG_AES,
.c_mode = SEC_C_MODE_CTR,
.key_len = SEC_KEY_LEN_AES_128,
},
[SEC_C_AES_CTR_192] = {
.c_alg = SEC_C_ALG_AES,
.c_mode = SEC_C_MODE_CTR,
.key_len = SEC_KEY_LEN_AES_192,
},
[SEC_C_AES_CTR_256] = {
.c_alg = SEC_C_ALG_AES,
.c_mode = SEC_C_MODE_CTR,
.key_len = SEC_KEY_LEN_AES_256,
},
[SEC_C_AES_XTS_128] = {
.c_alg = SEC_C_ALG_AES,
.c_mode = SEC_C_MODE_XTS,
.key_len = SEC_KEY_LEN_AES_128,
},
[SEC_C_AES_XTS_256] = {
.c_alg = SEC_C_ALG_AES,
.c_mode = SEC_C_MODE_XTS,
.key_len = SEC_KEY_LEN_AES_256,
},
[SEC_C_NULL] = {
},
};
/*
* Mutex used to ensure safe operation of reference count of
* alg providers
*/
static DEFINE_MUTEX(algs_lock);
static unsigned int active_devs;
static void sec_alg_skcipher_init_template(struct sec_alg_tfm_ctx *ctx,
struct sec_bd_info *req,
enum sec_cipher_alg alg)
{
const struct sec_c_alg_cfg *cfg = &sec_c_alg_cfgs[alg];
memset(req, 0, sizeof(*req));
req->w0 |= cfg->c_mode << SEC_BD_W0_C_MODE_S;
req->w1 |= cfg->c_alg << SEC_BD_W1_C_ALG_S;
req->w3 |= cfg->key_len << SEC_BD_W3_C_KEY_LEN_S;
req->w0 |= cfg->c_width << SEC_BD_W0_C_WIDTH_S;
req->cipher_key_addr_lo = lower_32_bits(ctx->pkey);
req->cipher_key_addr_hi = upper_32_bits(ctx->pkey);
}
static void sec_alg_skcipher_init_context(struct crypto_skcipher *atfm,
const u8 *key,
unsigned int keylen,
enum sec_cipher_alg alg)
{
struct crypto_tfm *tfm = crypto_skcipher_tfm(atfm);
struct sec_alg_tfm_ctx *ctx = crypto_tfm_ctx(tfm);
ctx->cipher_alg = alg;
memcpy(ctx->key, key, keylen);
sec_alg_skcipher_init_template(ctx, &ctx->req_template,
ctx->cipher_alg);
}
static int sec_alloc_and_fill_hw_sgl(struct sec_hw_sgl **sec_sgl,
dma_addr_t *psec_sgl,
struct scatterlist *sgl,
int count,
struct sec_dev_info *info)
{
struct sec_hw_sgl *sgl_current = NULL;
struct sec_hw_sgl *sgl_next;
dma_addr_t sgl_next_dma;
struct scatterlist *sg;
int ret, sge_index, i;
if (!count)
return -EINVAL;
for_each_sg(sgl, sg, count, i) {
sge_index = i % SEC_MAX_SGE_NUM;
if (sge_index == 0) {
sgl_next = dma_pool_zalloc(info->hw_sgl_pool,
GFP_KERNEL, &sgl_next_dma);
if (!sgl_next) {
ret = -ENOMEM;
goto err_free_hw_sgls;
}
if (!sgl_current) { /* First one */
*psec_sgl = sgl_next_dma;
*sec_sgl = sgl_next;
} else { /* Chained */
sgl_current->entry_sum_in_sgl = SEC_MAX_SGE_NUM;
sgl_current->next_sgl = sgl_next_dma;
sgl_current->next = sgl_next;
}
sgl_current = sgl_next;
}
sgl_current->sge_entries[sge_index].buf = sg_dma_address(sg);
sgl_current->sge_entries[sge_index].len = sg_dma_len(sg);
sgl_current->data_bytes_in_sgl += sg_dma_len(sg);
}
sgl_current->entry_sum_in_sgl = count % SEC_MAX_SGE_NUM;
sgl_current->next_sgl = 0;
(*sec_sgl)->entry_sum_in_chain = count;
return 0;
err_free_hw_sgls:
sgl_current = *sec_sgl;
while (sgl_current) {
sgl_next = sgl_current->next;
dma_pool_free(info->hw_sgl_pool, sgl_current,
sgl_current->next_sgl);
sgl_current = sgl_next;
}
*psec_sgl = 0;
return ret;
}
static void sec_free_hw_sgl(struct sec_hw_sgl *hw_sgl,
dma_addr_t psec_sgl, struct sec_dev_info *info)
{
struct sec_hw_sgl *sgl_current, *sgl_next;
if (!hw_sgl)
return;
sgl_current = hw_sgl;
while (sgl_current->next) {
sgl_next = sgl_current->next;
dma_pool_free(info->hw_sgl_pool, sgl_current,
sgl_current->next_sgl);
sgl_current = sgl_next;
}
dma_pool_free(info->hw_sgl_pool, hw_sgl, psec_sgl);
}
static int sec_alg_skcipher_setkey(struct crypto_skcipher *tfm,
const u8 *key, unsigned int keylen,
enum sec_cipher_alg alg)
{
struct sec_alg_tfm_ctx *ctx = crypto_skcipher_ctx(tfm);
struct device *dev = ctx->queue->dev_info->dev;
mutex_lock(&ctx->lock);
if (ctx->key) {
/* rekeying */
memset(ctx->key, 0, SEC_MAX_CIPHER_KEY);
} else {
/* new key */
ctx->key = dma_zalloc_coherent(dev, SEC_MAX_CIPHER_KEY,
&ctx->pkey, GFP_KERNEL);
if (!ctx->key) {
mutex_unlock(&ctx->lock);
return -ENOMEM;
}
}
mutex_unlock(&ctx->lock);
sec_alg_skcipher_init_context(tfm, key, keylen, alg);
return 0;
}
static int sec_alg_skcipher_setkey_aes_ecb(struct crypto_skcipher *tfm,
const u8 *key, unsigned int keylen)
{
enum sec_cipher_alg alg;
switch (keylen) {
case AES_KEYSIZE_128:
alg = SEC_C_AES_ECB_128;
break;
case AES_KEYSIZE_192:
alg = SEC_C_AES_ECB_192;
break;
case AES_KEYSIZE_256:
alg = SEC_C_AES_ECB_256;
break;
default:
return -EINVAL;
}
return sec_alg_skcipher_setkey(tfm, key, keylen, alg);
}
static int sec_alg_skcipher_setkey_aes_cbc(struct crypto_skcipher *tfm,
const u8 *key, unsigned int keylen)
{
enum sec_cipher_alg alg;
switch (keylen) {
case AES_KEYSIZE_128:
alg = SEC_C_AES_CBC_128;
break;
case AES_KEYSIZE_192:
alg = SEC_C_AES_CBC_192;
break;
case AES_KEYSIZE_256:
alg = SEC_C_AES_CBC_256;
break;
default:
return -EINVAL;
}
return sec_alg_skcipher_setkey(tfm, key, keylen, alg);
}
static int sec_alg_skcipher_setkey_aes_ctr(struct crypto_skcipher *tfm,
const u8 *key, unsigned int keylen)
{
enum sec_cipher_alg alg;
switch (keylen) {
case AES_KEYSIZE_128:
alg = SEC_C_AES_CTR_128;
break;
case AES_KEYSIZE_192:
alg = SEC_C_AES_CTR_192;
break;
case AES_KEYSIZE_256:
alg = SEC_C_AES_CTR_256;
break;
default:
return -EINVAL;
}
return sec_alg_skcipher_setkey(tfm, key, keylen, alg);
}
static int sec_alg_skcipher_setkey_aes_xts(struct crypto_skcipher *tfm,
const u8 *key, unsigned int keylen)
{
enum sec_cipher_alg alg;
int ret;
ret = xts_verify_key(tfm, key, keylen);
if (ret)
return ret;
switch (keylen) {
case AES_KEYSIZE_128 * 2:
alg = SEC_C_AES_XTS_128;
break;
case AES_KEYSIZE_256 * 2:
alg = SEC_C_AES_XTS_256;
break;
default:
return -EINVAL;
}
return sec_alg_skcipher_setkey(tfm, key, keylen, alg);
}
static int sec_alg_skcipher_setkey_des_ecb(struct crypto_skcipher *tfm,
const u8 *key, unsigned int keylen)
{
if (keylen != DES_KEY_SIZE)
return -EINVAL;
return sec_alg_skcipher_setkey(tfm, key, keylen, SEC_C_DES_ECB_64);
}
static int sec_alg_skcipher_setkey_des_cbc(struct crypto_skcipher *tfm,
const u8 *key, unsigned int keylen)
{
if (keylen != DES_KEY_SIZE)
return -EINVAL;
return sec_alg_skcipher_setkey(tfm, key, keylen, SEC_C_DES_CBC_64);
}
static int sec_alg_skcipher_setkey_3des_ecb(struct crypto_skcipher *tfm,
const u8 *key, unsigned int keylen)
{
if (keylen != DES_KEY_SIZE * 3)
return -EINVAL;
return sec_alg_skcipher_setkey(tfm, key, keylen,
SEC_C_3DES_ECB_192_3KEY);
}
static int sec_alg_skcipher_setkey_3des_cbc(struct crypto_skcipher *tfm,
const u8 *key, unsigned int keylen)
{
if (keylen != DES3_EDE_KEY_SIZE)
return -EINVAL;
return sec_alg_skcipher_setkey(tfm, key, keylen,
SEC_C_3DES_CBC_192_3KEY);
}
static void sec_alg_free_el(struct sec_request_el *el,
struct sec_dev_info *info)
{
sec_free_hw_sgl(el->out, el->dma_out, info);
sec_free_hw_sgl(el->in, el->dma_in, info);
kfree(el->sgl_in);
kfree(el->sgl_out);
kfree(el);
}
/* queuelock must be held */
static int sec_send_request(struct sec_request *sec_req, struct sec_queue *queue)
{
struct sec_request_el *el, *temp;
int ret = 0;
mutex_lock(&sec_req->lock);
list_for_each_entry_safe(el, temp, &sec_req->elements, head) {
/*
* Add to hardware queue only under following circumstances
* 1) Software and hardware queue empty so no chain dependencies
* 2) No dependencies as new IV - (check software queue empty
* to maintain order)
* 3) No dependencies because the mode does no chaining.
*
* In other cases first insert onto the software queue which
* is then emptied as requests complete
*/
if (!queue->havesoftqueue ||
(kfifo_is_empty(&queue->softqueue) &&
sec_queue_empty(queue))) {
ret = sec_queue_send(queue, &el->req, sec_req);
if (ret == -EAGAIN) {
/* Wait unti we can send then try again */
/* DEAD if here - should not happen */
ret = -EBUSY;
goto err_unlock;
}
} else {
kfifo_put(&queue->softqueue, el);
}
}
err_unlock:
mutex_unlock(&sec_req->lock);
return ret;
}
static void sec_skcipher_alg_callback(struct sec_bd_info *sec_resp,
struct crypto_async_request *req_base)
{
struct skcipher_request *skreq = container_of(req_base,
struct skcipher_request,
base);
struct sec_request *sec_req = skcipher_request_ctx(skreq);
struct sec_request *backlog_req;
struct sec_request_el *sec_req_el, *nextrequest;
struct sec_alg_tfm_ctx *ctx = sec_req->tfm_ctx;
struct crypto_skcipher *atfm = crypto_skcipher_reqtfm(skreq);
struct device *dev = ctx->queue->dev_info->dev;
int icv_or_skey_en, ret;
bool done;
sec_req_el = list_first_entry(&sec_req->elements, struct sec_request_el,
head);
icv_or_skey_en = (sec_resp->w0 & SEC_BD_W0_ICV_OR_SKEY_EN_M) >>
SEC_BD_W0_ICV_OR_SKEY_EN_S;
if (sec_resp->w1 & SEC_BD_W1_BD_INVALID || icv_or_skey_en == 3) {
dev_err(dev, "Got an invalid answer %lu %d\n",
sec_resp->w1 & SEC_BD_W1_BD_INVALID,
icv_or_skey_en);
sec_req->err = -EINVAL;
/*
* We need to muddle on to avoid getting stuck with elements
* on the queue. Error will be reported so requester so
* it should be able to handle appropriately.
*/
}
mutex_lock(&ctx->queue->queuelock);
/* Put the IV in place for chained cases */
switch (ctx->cipher_alg) {
case SEC_C_AES_CBC_128:
case SEC_C_AES_CBC_192:
case SEC_C_AES_CBC_256:
if (sec_req_el->req.w0 & SEC_BD_W0_DE)
sg_pcopy_to_buffer(sec_req_el->sgl_out,
sg_nents(sec_req_el->sgl_out),
skreq->iv,
crypto_skcipher_ivsize(atfm),
sec_req_el->el_length -
crypto_skcipher_ivsize(atfm));
else
sg_pcopy_to_buffer(sec_req_el->sgl_in,
sg_nents(sec_req_el->sgl_in),
skreq->iv,
crypto_skcipher_ivsize(atfm),
sec_req_el->el_length -
crypto_skcipher_ivsize(atfm));
/* No need to sync to the device as coherent DMA */
break;
case SEC_C_AES_CTR_128:
case SEC_C_AES_CTR_192:
case SEC_C_AES_CTR_256:
crypto_inc(skreq->iv, 16);
break;
default:
/* Do not update */
break;
}
if (ctx->queue->havesoftqueue &&
!kfifo_is_empty(&ctx->queue->softqueue) &&
sec_queue_empty(ctx->queue)) {
ret = kfifo_get(&ctx->queue->softqueue, &nextrequest);
if (ret <= 0)
dev_err(dev,
"Error getting next element from kfifo %d\n",
ret);
else
/* We know there is space so this cannot fail */
sec_queue_send(ctx->queue, &nextrequest->req,
nextrequest->sec_req);
} else if (!list_empty(&ctx->backlog)) {
/* Need to verify there is room first */
backlog_req = list_first_entry(&ctx->backlog,
typeof(*backlog_req),
backlog_head);
if (sec_queue_can_enqueue(ctx->queue,
backlog_req->num_elements) ||
(ctx->queue->havesoftqueue &&
kfifo_avail(&ctx->queue->softqueue) >
backlog_req->num_elements)) {
sec_send_request(backlog_req, ctx->queue);
backlog_req->req_base->complete(backlog_req->req_base,
-EINPROGRESS);
list_del(&backlog_req->backlog_head);
}
}
mutex_unlock(&ctx->queue->queuelock);
mutex_lock(&sec_req->lock);
list_del(&sec_req_el->head);
mutex_unlock(&sec_req->lock);
sec_alg_free_el(sec_req_el, ctx->queue->dev_info);
/*
* Request is done.
* The dance is needed as the lock is freed in the completion
*/
mutex_lock(&sec_req->lock);
done = list_empty(&sec_req->elements);
mutex_unlock(&sec_req->lock);
if (done) {
if (crypto_skcipher_ivsize(atfm)) {
dma_unmap_single(dev, sec_req->dma_iv,
crypto_skcipher_ivsize(atfm),
DMA_TO_DEVICE);
}
dma_unmap_sg(dev, skreq->src, sec_req->len_in,
DMA_BIDIRECTIONAL);
if (skreq->src != skreq->dst)
dma_unmap_sg(dev, skreq->dst, sec_req->len_out,
DMA_BIDIRECTIONAL);
skreq->base.complete(&skreq->base, sec_req->err);
}
}
void sec_alg_callback(struct sec_bd_info *resp, void *shadow)
{
struct sec_request *sec_req = shadow;
sec_req->cb(resp, sec_req->req_base);
}
static int sec_alg_alloc_and_calc_split_sizes(int length, size_t **split_sizes,
int *steps)
{
size_t *sizes;
int i;
/* Split into suitable sized blocks */
*steps = roundup(length, SEC_REQ_LIMIT) / SEC_REQ_LIMIT;
sizes = kcalloc(*steps, sizeof(*sizes), GFP_KERNEL);
if (!sizes)
return -ENOMEM;
for (i = 0; i < *steps - 1; i++)
sizes[i] = SEC_REQ_LIMIT;
sizes[*steps - 1] = length - SEC_REQ_LIMIT * (*steps - 1);
*split_sizes = sizes;
return 0;
}
static int sec_map_and_split_sg(struct scatterlist *sgl, size_t *split_sizes,
int steps, struct scatterlist ***splits,
int **splits_nents,
int sgl_len_in,
struct device *dev)
{
int ret, count;
count = dma_map_sg(dev, sgl, sgl_len_in, DMA_BIDIRECTIONAL);
if (!count)
return -EINVAL;
*splits = kcalloc(steps, sizeof(struct scatterlist *), GFP_KERNEL);
if (!*splits) {
ret = -ENOMEM;
goto err_unmap_sg;
}
*splits_nents = kcalloc(steps, sizeof(int), GFP_KERNEL);
if (!*splits_nents) {
ret = -ENOMEM;
goto err_free_splits;
}
/* output the scatter list before and after this */
ret = sg_split(sgl, count, 0, steps, split_sizes,
*splits, *splits_nents, GFP_KERNEL);
if (ret) {
ret = -ENOMEM;
goto err_free_splits_nents;
}
return 0;
err_free_splits_nents:
kfree(*splits_nents);
err_free_splits:
kfree(*splits);
err_unmap_sg:
dma_unmap_sg(dev, sgl, sgl_len_in, DMA_BIDIRECTIONAL);
return ret;
}
/*
* Reverses the sec_map_and_split_sg call for messages not yet added to
* the queues.
*/
static void sec_unmap_sg_on_err(struct scatterlist *sgl, int steps,
struct scatterlist **splits, int *splits_nents,
int sgl_len_in, struct device *dev)
{
int i;
for (i = 0; i < steps; i++)
kfree(splits[i]);
kfree(splits_nents);
kfree(splits);
dma_unmap_sg(dev, sgl, sgl_len_in, DMA_BIDIRECTIONAL);
}
static struct sec_request_el
*sec_alg_alloc_and_fill_el(struct sec_bd_info *template, int encrypt,
int el_size, bool different_dest,
struct scatterlist *sgl_in, int n_ents_in,
struct scatterlist *sgl_out, int n_ents_out,
struct sec_dev_info *info)
{
struct sec_request_el *el;
struct sec_bd_info *req;
int ret;
el = kzalloc(sizeof(*el), GFP_KERNEL);
if (!el)
return ERR_PTR(-ENOMEM);
el->el_length = el_size;
req = &el->req;
memcpy(req, template, sizeof(*req));
req->w0 &= ~SEC_BD_W0_CIPHER_M;
if (encrypt)
req->w0 |= SEC_CIPHER_ENCRYPT << SEC_BD_W0_CIPHER_S;
else
req->w0 |= SEC_CIPHER_DECRYPT << SEC_BD_W0_CIPHER_S;
req->w0 &= ~SEC_BD_W0_C_GRAN_SIZE_19_16_M;
req->w0 |= ((el_size >> 16) << SEC_BD_W0_C_GRAN_SIZE_19_16_S) &
SEC_BD_W0_C_GRAN_SIZE_19_16_M;
req->w0 &= ~SEC_BD_W0_C_GRAN_SIZE_21_20_M;
req->w0 |= ((el_size >> 20) << SEC_BD_W0_C_GRAN_SIZE_21_20_S) &
SEC_BD_W0_C_GRAN_SIZE_21_20_M;
/* Writing whole u32 so no need to take care of masking */
req->w2 = ((1 << SEC_BD_W2_GRAN_NUM_S) & SEC_BD_W2_GRAN_NUM_M) |
((el_size << SEC_BD_W2_C_GRAN_SIZE_15_0_S) &
SEC_BD_W2_C_GRAN_SIZE_15_0_M);
req->w3 &= ~SEC_BD_W3_CIPHER_LEN_OFFSET_M;
req->w1 |= SEC_BD_W1_ADDR_TYPE;
el->sgl_in = sgl_in;
ret = sec_alloc_and_fill_hw_sgl(&el->in, &el->dma_in, el->sgl_in,
n_ents_in, info);
if (ret)
goto err_free_el;
req->data_addr_lo = lower_32_bits(el->dma_in);
req->data_addr_hi = upper_32_bits(el->dma_in);
if (different_dest) {
el->sgl_out = sgl_out;
ret = sec_alloc_and_fill_hw_sgl(&el->out, &el->dma_out,
el->sgl_out,
n_ents_out, info);
if (ret)
goto err_free_hw_sgl_in;
req->w0 |= SEC_BD_W0_DE;
req->cipher_destin_addr_lo = lower_32_bits(el->dma_out);
req->cipher_destin_addr_hi = upper_32_bits(el->dma_out);
} else {
req->w0 &= ~SEC_BD_W0_DE;
req->cipher_destin_addr_lo = lower_32_bits(el->dma_in);
req->cipher_destin_addr_hi = upper_32_bits(el->dma_in);
}
return el;
err_free_hw_sgl_in:
sec_free_hw_sgl(el->in, el->dma_in, info);
err_free_el:
kfree(el);
return ERR_PTR(ret);
}
static int sec_alg_skcipher_crypto(struct skcipher_request *skreq,
bool encrypt)
{
struct crypto_skcipher *atfm = crypto_skcipher_reqtfm(skreq);
struct crypto_tfm *tfm = crypto_skcipher_tfm(atfm);
struct sec_alg_tfm_ctx *ctx = crypto_tfm_ctx(tfm);
struct sec_queue *queue = ctx->queue;
struct sec_request *sec_req = skcipher_request_ctx(skreq);
struct sec_dev_info *info = queue->dev_info;
int i, ret, steps;
size_t *split_sizes;
struct scatterlist **splits_in;
struct scatterlist **splits_out = NULL;
int *splits_in_nents;
int *splits_out_nents = NULL;
struct sec_request_el *el, *temp;
bool split = skreq->src != skreq->dst;
mutex_init(&sec_req->lock);
sec_req->req_base = &skreq->base;
sec_req->err = 0;
/* SGL mapping out here to allow us to break it up as necessary */
sec_req->len_in = sg_nents(skreq->src);
ret = sec_alg_alloc_and_calc_split_sizes(skreq->cryptlen, &split_sizes,
&steps);
if (ret)
return ret;
sec_req->num_elements = steps;
ret = sec_map_and_split_sg(skreq->src, split_sizes, steps, &splits_in,
&splits_in_nents, sec_req->len_in,
info->dev);
if (ret)
goto err_free_split_sizes;
if (split) {
sec_req->len_out = sg_nents(skreq->dst);
ret = sec_map_and_split_sg(skreq->dst, split_sizes, steps,
&splits_out, &splits_out_nents,
sec_req->len_out, info->dev);
if (ret)
goto err_unmap_in_sg;
}
/* Shared info stored in seq_req - applies to all BDs */
sec_req->tfm_ctx = ctx;
sec_req->cb = sec_skcipher_alg_callback;
INIT_LIST_HEAD(&sec_req->elements);
/*
* Future optimization.
* In the chaining case we can't use a dma pool bounce buffer
* but in the case where we know there is no chaining we can
*/
if (crypto_skcipher_ivsize(atfm)) {
sec_req->dma_iv = dma_map_single(info->dev, skreq->iv,
crypto_skcipher_ivsize(atfm),
DMA_TO_DEVICE);
if (dma_mapping_error(info->dev, sec_req->dma_iv)) {
ret = -ENOMEM;
goto err_unmap_out_sg;
}
}
/* Set them all up then queue - cleaner error handling. */
for (i = 0; i < steps; i++) {
el = sec_alg_alloc_and_fill_el(&ctx->req_template,
encrypt ? 1 : 0,
split_sizes[i],
skreq->src != skreq->dst,
splits_in[i], splits_in_nents[i],
split ? splits_out[i] : NULL,
split ? splits_out_nents[i] : 0,
info);
if (IS_ERR(el)) {
ret = PTR_ERR(el);
goto err_free_elements;
}
el->req.cipher_iv_addr_lo = lower_32_bits(sec_req->dma_iv);
el->req.cipher_iv_addr_hi = upper_32_bits(sec_req->dma_iv);
el->sec_req = sec_req;
list_add_tail(&el->head, &sec_req->elements);
}
/*
* Only attempt to queue if the whole lot can fit in the queue -
* we can't successfully cleanup after a partial queing so this
* must succeed or fail atomically.
*
* Big hammer test of both software and hardware queues - could be
* more refined but this is unlikely to happen so no need.
*/
/* Grab a big lock for a long time to avoid concurrency issues */
mutex_lock(&queue->queuelock);
/*
* Can go on to queue if we have space in either:
* 1) The hardware queue and no software queue
* 2) The software queue
* AND there is nothing in the backlog. If there is backlog we
* have to only queue to the backlog queue and return busy.
*/
if ((!sec_queue_can_enqueue(queue, steps) &&
(!queue->havesoftqueue ||
kfifo_avail(&queue->softqueue) > steps)) ||
!list_empty(&ctx->backlog)) {
ret = -EBUSY;
if ((skreq->base.flags & CRYPTO_TFM_REQ_MAY_BACKLOG)) {
list_add_tail(&sec_req->backlog_head, &ctx->backlog);
mutex_unlock(&queue->queuelock);
goto out;
}
mutex_unlock(&queue->queuelock);
goto err_free_elements;
}
ret = sec_send_request(sec_req, queue);
mutex_unlock(&queue->queuelock);
if (ret)
goto err_free_elements;
ret = -EINPROGRESS;
out:
/* Cleanup - all elements in pointer arrays have been copied */
kfree(splits_in_nents);
kfree(splits_in);
kfree(splits_out_nents);
kfree(splits_out);
kfree(split_sizes);
return ret;
err_free_elements:
list_for_each_entry_safe(el, temp, &sec_req->elements, head) {
list_del(&el->head);
sec_alg_free_el(el, info);
}
if (crypto_skcipher_ivsize(atfm))
dma_unmap_single(info->dev, sec_req->dma_iv,
crypto_skcipher_ivsize(atfm),
DMA_BIDIRECTIONAL);
err_unmap_out_sg:
if (split)
sec_unmap_sg_on_err(skreq->dst, steps, splits_out,
splits_out_nents, sec_req->len_out,
info->dev);
err_unmap_in_sg:
sec_unmap_sg_on_err(skreq->src, steps, splits_in, splits_in_nents,
sec_req->len_in, info->dev);
err_free_split_sizes:
kfree(split_sizes);
return ret;
}
static int sec_alg_skcipher_encrypt(struct skcipher_request *req)
{
return sec_alg_skcipher_crypto(req, true);
}
static int sec_alg_skcipher_decrypt(struct skcipher_request *req)
{
return sec_alg_skcipher_crypto(req, false);
}
static int sec_alg_skcipher_init(struct crypto_skcipher *tfm)
{
struct sec_alg_tfm_ctx *ctx = crypto_skcipher_ctx(tfm);
mutex_init(&ctx->lock);
INIT_LIST_HEAD(&ctx->backlog);
crypto_skcipher_set_reqsize(tfm, sizeof(struct sec_request));
ctx->queue = sec_queue_alloc_start_safe();
if (IS_ERR(ctx->queue))
return PTR_ERR(ctx->queue);
mutex_init(&ctx->queue->queuelock);
ctx->queue->havesoftqueue = false;
return 0;
}
static void sec_alg_skcipher_exit(struct crypto_skcipher *tfm)
{
struct sec_alg_tfm_ctx *ctx = crypto_skcipher_ctx(tfm);
struct device *dev = ctx->queue->dev_info->dev;
if (ctx->key) {
memzero_explicit(ctx->key, SEC_MAX_CIPHER_KEY);
dma_free_coherent(dev, SEC_MAX_CIPHER_KEY, ctx->key,
ctx->pkey);
}
sec_queue_stop_release(ctx->queue);
}
static int sec_alg_skcipher_init_with_queue(struct crypto_skcipher *tfm)
{
struct sec_alg_tfm_ctx *ctx = crypto_skcipher_ctx(tfm);
int ret;
ret = sec_alg_skcipher_init(tfm);
if (ret)
return ret;
INIT_KFIFO(ctx->queue->softqueue);
ret = kfifo_alloc(&ctx->queue->softqueue, 512, GFP_KERNEL);
if (ret) {
sec_alg_skcipher_exit(tfm);
return ret;
}
ctx->queue->havesoftqueue = true;
return 0;
}
static void sec_alg_skcipher_exit_with_queue(struct crypto_skcipher *tfm)
{
struct sec_alg_tfm_ctx *ctx = crypto_skcipher_ctx(tfm);
kfifo_free(&ctx->queue->softqueue);
sec_alg_skcipher_exit(tfm);
}
static struct skcipher_alg sec_algs[] = {
{
.base = {
.cra_name = "ecb(aes)",
.cra_driver_name = "hisi_sec_aes_ecb",
.cra_priority = 4001,
.cra_flags = CRYPTO_ALG_ASYNC,
.cra_blocksize = AES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct sec_alg_tfm_ctx),
.cra_alignmask = 0,
.cra_module = THIS_MODULE,
},
.init = sec_alg_skcipher_init,
.exit = sec_alg_skcipher_exit,
.setkey = sec_alg_skcipher_setkey_aes_ecb,
.decrypt = sec_alg_skcipher_decrypt,
.encrypt = sec_alg_skcipher_encrypt,
.min_keysize = AES_MIN_KEY_SIZE,
.max_keysize = AES_MAX_KEY_SIZE,
.ivsize = 0,
}, {
.base = {
.cra_name = "cbc(aes)",
.cra_driver_name = "hisi_sec_aes_cbc",
.cra_priority = 4001,
.cra_flags = CRYPTO_ALG_ASYNC,
.cra_blocksize = AES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct sec_alg_tfm_ctx),
.cra_alignmask = 0,
.cra_module = THIS_MODULE,
},
.init = sec_alg_skcipher_init_with_queue,
.exit = sec_alg_skcipher_exit_with_queue,
.setkey = sec_alg_skcipher_setkey_aes_cbc,
.decrypt = sec_alg_skcipher_decrypt,
.encrypt = sec_alg_skcipher_encrypt,
.min_keysize = AES_MIN_KEY_SIZE,
.max_keysize = AES_MAX_KEY_SIZE,
.ivsize = AES_BLOCK_SIZE,
}, {
.base = {
.cra_name = "ctr(aes)",
.cra_driver_name = "hisi_sec_aes_ctr",
.cra_priority = 4001,
.cra_flags = CRYPTO_ALG_ASYNC,
.cra_blocksize = AES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct sec_alg_tfm_ctx),
.cra_alignmask = 0,
.cra_module = THIS_MODULE,
},
.init = sec_alg_skcipher_init_with_queue,
.exit = sec_alg_skcipher_exit_with_queue,
.setkey = sec_alg_skcipher_setkey_aes_ctr,
.decrypt = sec_alg_skcipher_decrypt,
.encrypt = sec_alg_skcipher_encrypt,
.min_keysize = AES_MIN_KEY_SIZE,
.max_keysize = AES_MAX_KEY_SIZE,
.ivsize = AES_BLOCK_SIZE,
}, {
.base = {
.cra_name = "xts(aes)",
.cra_driver_name = "hisi_sec_aes_xts",
.cra_priority = 4001,
.cra_flags = CRYPTO_ALG_ASYNC,
.cra_blocksize = AES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct sec_alg_tfm_ctx),
.cra_alignmask = 0,
.cra_module = THIS_MODULE,
},
.init = sec_alg_skcipher_init,
.exit = sec_alg_skcipher_exit,
.setkey = sec_alg_skcipher_setkey_aes_xts,
.decrypt = sec_alg_skcipher_decrypt,
.encrypt = sec_alg_skcipher_encrypt,
.min_keysize = 2 * AES_MIN_KEY_SIZE,
.max_keysize = 2 * AES_MAX_KEY_SIZE,
.ivsize = AES_BLOCK_SIZE,
}, {
/* Unable to find any test vectors so untested */
.base = {
.cra_name = "ecb(des)",
.cra_driver_name = "hisi_sec_des_ecb",
.cra_priority = 4001,
.cra_flags = CRYPTO_ALG_ASYNC,
.cra_blocksize = DES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct sec_alg_tfm_ctx),
.cra_alignmask = 0,
.cra_module = THIS_MODULE,
},
.init = sec_alg_skcipher_init,
.exit = sec_alg_skcipher_exit,
.setkey = sec_alg_skcipher_setkey_des_ecb,
.decrypt = sec_alg_skcipher_decrypt,
.encrypt = sec_alg_skcipher_encrypt,
.min_keysize = DES_KEY_SIZE,
.max_keysize = DES_KEY_SIZE,
.ivsize = 0,
}, {
.base = {
.cra_name = "cbc(des)",
.cra_driver_name = "hisi_sec_des_cbc",
.cra_priority = 4001,
.cra_flags = CRYPTO_ALG_ASYNC,
.cra_blocksize = DES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct sec_alg_tfm_ctx),
.cra_alignmask = 0,
.cra_module = THIS_MODULE,
},
.init = sec_alg_skcipher_init_with_queue,
.exit = sec_alg_skcipher_exit_with_queue,
.setkey = sec_alg_skcipher_setkey_des_cbc,
.decrypt = sec_alg_skcipher_decrypt,
.encrypt = sec_alg_skcipher_encrypt,
.min_keysize = DES_KEY_SIZE,
.max_keysize = DES_KEY_SIZE,
.ivsize = DES_BLOCK_SIZE,
}, {
.base = {
.cra_name = "cbc(des3_ede)",
.cra_driver_name = "hisi_sec_3des_cbc",
.cra_priority = 4001,
.cra_flags = CRYPTO_ALG_ASYNC,
.cra_blocksize = DES3_EDE_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct sec_alg_tfm_ctx),
.cra_alignmask = 0,
.cra_module = THIS_MODULE,
},
.init = sec_alg_skcipher_init_with_queue,
.exit = sec_alg_skcipher_exit_with_queue,
.setkey = sec_alg_skcipher_setkey_3des_cbc,
.decrypt = sec_alg_skcipher_decrypt,
.encrypt = sec_alg_skcipher_encrypt,
.min_keysize = DES3_EDE_KEY_SIZE,
.max_keysize = DES3_EDE_KEY_SIZE,
.ivsize = DES3_EDE_BLOCK_SIZE,
}, {
.base = {
.cra_name = "ecb(des3_ede)",
.cra_driver_name = "hisi_sec_3des_ecb",
.cra_priority = 4001,
.cra_flags = CRYPTO_ALG_ASYNC,
.cra_blocksize = DES3_EDE_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct sec_alg_tfm_ctx),
.cra_alignmask = 0,
.cra_module = THIS_MODULE,
},
.init = sec_alg_skcipher_init,
.exit = sec_alg_skcipher_exit,
.setkey = sec_alg_skcipher_setkey_3des_ecb,
.decrypt = sec_alg_skcipher_decrypt,
.encrypt = sec_alg_skcipher_encrypt,
.min_keysize = DES3_EDE_KEY_SIZE,
.max_keysize = DES3_EDE_KEY_SIZE,
.ivsize = 0,
}
};
int sec_algs_register(void)
{
int ret = 0;
mutex_lock(&algs_lock);
if (++active_devs != 1)
goto unlock;
ret = crypto_register_skciphers(sec_algs, ARRAY_SIZE(sec_algs));
if (ret)
--active_devs;
unlock:
mutex_unlock(&algs_lock);
return ret;
}
void sec_algs_unregister(void)
{
mutex_lock(&algs_lock);
if (--active_devs != 0)
goto unlock;
crypto_unregister_skciphers(sec_algs, ARRAY_SIZE(sec_algs));
unlock:
mutex_unlock(&algs_lock);
}