in allwinner/sun8i-ss/sun8i-ss-cipher.c [95:269]
static int sun8i_ss_cipher(struct skcipher_request *areq)
{
struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(areq);
struct sun8i_cipher_tfm_ctx *op = crypto_skcipher_ctx(tfm);
struct sun8i_ss_dev *ss = op->ss;
struct sun8i_cipher_req_ctx *rctx = skcipher_request_ctx(areq);
struct skcipher_alg *alg = crypto_skcipher_alg(tfm);
struct sun8i_ss_alg_template *algt;
struct scatterlist *sg;
unsigned int todo, len, offset, ivsize;
void *backup_iv = NULL;
int nr_sgs = 0;
int nr_sgd = 0;
int err = 0;
int i;
algt = container_of(alg, struct sun8i_ss_alg_template, alg.skcipher);
dev_dbg(ss->dev, "%s %s %u %x IV(%p %u) key=%u\n", __func__,
crypto_tfm_alg_name(areq->base.tfm),
areq->cryptlen,
rctx->op_dir, areq->iv, crypto_skcipher_ivsize(tfm),
op->keylen);
#ifdef CONFIG_CRYPTO_DEV_SUN8I_SS_DEBUG
algt->stat_req++;
#endif
rctx->op_mode = ss->variant->op_mode[algt->ss_blockmode];
rctx->method = ss->variant->alg_cipher[algt->ss_algo_id];
rctx->keylen = op->keylen;
rctx->p_key = dma_map_single(ss->dev, op->key, op->keylen, DMA_TO_DEVICE);
if (dma_mapping_error(ss->dev, rctx->p_key)) {
dev_err(ss->dev, "Cannot DMA MAP KEY\n");
err = -EFAULT;
goto theend;
}
ivsize = crypto_skcipher_ivsize(tfm);
if (areq->iv && crypto_skcipher_ivsize(tfm) > 0) {
rctx->ivlen = ivsize;
rctx->biv = kzalloc(ivsize, GFP_KERNEL | GFP_DMA);
if (!rctx->biv) {
err = -ENOMEM;
goto theend_key;
}
if (rctx->op_dir & SS_DECRYPTION) {
backup_iv = kzalloc(ivsize, GFP_KERNEL);
if (!backup_iv) {
err = -ENOMEM;
goto theend_key;
}
offset = areq->cryptlen - ivsize;
scatterwalk_map_and_copy(backup_iv, areq->src, offset,
ivsize, 0);
}
memcpy(rctx->biv, areq->iv, ivsize);
rctx->p_iv = dma_map_single(ss->dev, rctx->biv, rctx->ivlen,
DMA_TO_DEVICE);
if (dma_mapping_error(ss->dev, rctx->p_iv)) {
dev_err(ss->dev, "Cannot DMA MAP IV\n");
err = -ENOMEM;
goto theend_iv;
}
}
if (areq->src == areq->dst) {
nr_sgs = dma_map_sg(ss->dev, areq->src, sg_nents(areq->src),
DMA_BIDIRECTIONAL);
if (nr_sgs <= 0 || nr_sgs > 8) {
dev_err(ss->dev, "Invalid sg number %d\n", nr_sgs);
err = -EINVAL;
goto theend_iv;
}
nr_sgd = nr_sgs;
} else {
nr_sgs = dma_map_sg(ss->dev, areq->src, sg_nents(areq->src),
DMA_TO_DEVICE);
if (nr_sgs <= 0 || nr_sgs > 8) {
dev_err(ss->dev, "Invalid sg number %d\n", nr_sgs);
err = -EINVAL;
goto theend_iv;
}
nr_sgd = dma_map_sg(ss->dev, areq->dst, sg_nents(areq->dst),
DMA_FROM_DEVICE);
if (nr_sgd <= 0 || nr_sgd > 8) {
dev_err(ss->dev, "Invalid sg number %d\n", nr_sgd);
err = -EINVAL;
goto theend_sgs;
}
}
len = areq->cryptlen;
i = 0;
sg = areq->src;
while (i < nr_sgs && sg && len) {
if (sg_dma_len(sg) == 0)
goto sgs_next;
rctx->t_src[i].addr = sg_dma_address(sg);
todo = min(len, sg_dma_len(sg));
rctx->t_src[i].len = todo / 4;
dev_dbg(ss->dev, "%s total=%u SGS(%d %u off=%d) todo=%u\n", __func__,
areq->cryptlen, i, rctx->t_src[i].len, sg->offset, todo);
len -= todo;
i++;
sgs_next:
sg = sg_next(sg);
}
if (len > 0) {
dev_err(ss->dev, "remaining len %d\n", len);
err = -EINVAL;
goto theend_sgs;
}
len = areq->cryptlen;
i = 0;
sg = areq->dst;
while (i < nr_sgd && sg && len) {
if (sg_dma_len(sg) == 0)
goto sgd_next;
rctx->t_dst[i].addr = sg_dma_address(sg);
todo = min(len, sg_dma_len(sg));
rctx->t_dst[i].len = todo / 4;
dev_dbg(ss->dev, "%s total=%u SGD(%d %u off=%d) todo=%u\n", __func__,
areq->cryptlen, i, rctx->t_dst[i].len, sg->offset, todo);
len -= todo;
i++;
sgd_next:
sg = sg_next(sg);
}
if (len > 0) {
dev_err(ss->dev, "remaining len %d\n", len);
err = -EINVAL;
goto theend_sgs;
}
err = sun8i_ss_run_task(ss, rctx, crypto_tfm_alg_name(areq->base.tfm));
theend_sgs:
if (areq->src == areq->dst) {
dma_unmap_sg(ss->dev, areq->src, sg_nents(areq->src),
DMA_BIDIRECTIONAL);
} else {
dma_unmap_sg(ss->dev, areq->src, sg_nents(areq->src),
DMA_TO_DEVICE);
dma_unmap_sg(ss->dev, areq->dst, sg_nents(areq->dst),
DMA_FROM_DEVICE);
}
theend_iv:
if (rctx->p_iv)
dma_unmap_single(ss->dev, rctx->p_iv, rctx->ivlen,
DMA_TO_DEVICE);
if (areq->iv && ivsize > 0) {
if (rctx->biv) {
offset = areq->cryptlen - ivsize;
if (rctx->op_dir & SS_DECRYPTION) {
memcpy(areq->iv, backup_iv, ivsize);
kfree_sensitive(backup_iv);
} else {
scatterwalk_map_and_copy(areq->iv, areq->dst, offset,
ivsize, 0);
}
kfree(rctx->biv);
}
}
theend_key:
dma_unmap_single(ss->dev, rctx->p_key, op->keylen, DMA_TO_DEVICE);
theend:
return err;
}