Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Jonathan Cameron | 5124 | 98.77% | 1 | 20.00% |
John Garry | 62 | 1.20% | 2 | 40.00% |
Luis R. Rodriguez | 1 | 0.02% | 1 | 20.00% |
Fengguang Wu | 1 | 0.02% | 1 | 20.00% |
Total | 5188 | 5 |
// 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_alloc_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); }
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1