Path: blob/master/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-prng.c
26296 views
// SPDX-License-Identifier: GPL-2.01/*2* sun8i-ce-prng.c - hardware cryptographic offloader for3* Allwinner H3/A64/H5/H2+/H6/R40 SoC4*5* Copyright (C) 2015-2020 Corentin Labbe <[email protected]>6*7* This file handle the PRNG8*9* You could find a link for the datasheet in Documentation/arch/arm/sunxi.rst10*/11#include "sun8i-ce.h"12#include <linux/dma-mapping.h>13#include <linux/pm_runtime.h>14#include <crypto/internal/rng.h>1516int sun8i_ce_prng_init(struct crypto_tfm *tfm)17{18struct sun8i_ce_rng_tfm_ctx *ctx = crypto_tfm_ctx(tfm);1920memset(ctx, 0, sizeof(struct sun8i_ce_rng_tfm_ctx));21return 0;22}2324void sun8i_ce_prng_exit(struct crypto_tfm *tfm)25{26struct sun8i_ce_rng_tfm_ctx *ctx = crypto_tfm_ctx(tfm);2728kfree_sensitive(ctx->seed);29ctx->seed = NULL;30ctx->slen = 0;31}3233int sun8i_ce_prng_seed(struct crypto_rng *tfm, const u8 *seed,34unsigned int slen)35{36struct sun8i_ce_rng_tfm_ctx *ctx = crypto_rng_ctx(tfm);3738if (ctx->seed && ctx->slen != slen) {39kfree_sensitive(ctx->seed);40ctx->slen = 0;41ctx->seed = NULL;42}43if (!ctx->seed)44ctx->seed = kmalloc(slen, GFP_KERNEL | GFP_DMA);45if (!ctx->seed)46return -ENOMEM;4748memcpy(ctx->seed, seed, slen);49ctx->slen = slen;5051return 0;52}5354int sun8i_ce_prng_generate(struct crypto_rng *tfm, const u8 *src,55unsigned int slen, u8 *dst, unsigned int dlen)56{57struct sun8i_ce_rng_tfm_ctx *ctx = crypto_rng_ctx(tfm);58struct rng_alg *alg = crypto_rng_alg(tfm);59struct sun8i_ce_alg_template *algt;60struct sun8i_ce_dev *ce;61dma_addr_t dma_iv, dma_dst;62int err = 0;63int flow = 3;64unsigned int todo;65struct sun8i_ce_flow *chan;66struct ce_task *cet;67u32 common, sym;68void *d;6970algt = container_of(alg, struct sun8i_ce_alg_template, alg.rng);71ce = algt->ce;7273if (ctx->slen == 0) {74dev_err(ce->dev, "not seeded\n");75return -EINVAL;76}7778/* we want dlen + seedsize rounded up to a multiple of PRNG_DATA_SIZE */79todo = dlen + ctx->slen + PRNG_DATA_SIZE * 2;80todo -= todo % PRNG_DATA_SIZE;8182d = kzalloc(todo, GFP_KERNEL | GFP_DMA);83if (!d) {84err = -ENOMEM;85goto err_mem;86}8788dev_dbg(ce->dev, "%s PRNG slen=%u dlen=%u todo=%u multi=%u\n", __func__,89slen, dlen, todo, todo / PRNG_DATA_SIZE);9091#ifdef CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG92algt->stat_req++;93algt->stat_bytes += todo;94#endif9596dma_iv = dma_map_single(ce->dev, ctx->seed, ctx->slen, DMA_TO_DEVICE);97if (dma_mapping_error(ce->dev, dma_iv)) {98dev_err(ce->dev, "Cannot DMA MAP IV\n");99err = -EFAULT;100goto err_iv;101}102103dma_dst = dma_map_single(ce->dev, d, todo, DMA_FROM_DEVICE);104if (dma_mapping_error(ce->dev, dma_dst)) {105dev_err(ce->dev, "Cannot DMA MAP DST\n");106err = -EFAULT;107goto err_dst;108}109110err = pm_runtime_resume_and_get(ce->dev);111if (err < 0)112goto err_pm;113114mutex_lock(&ce->rnglock);115chan = &ce->chanlist[flow];116117cet = &chan->tl[0];118memset(cet, 0, sizeof(struct ce_task));119120cet->t_id = cpu_to_le32(flow);121common = ce->variant->prng | CE_COMM_INT;122cet->t_common_ctl = cpu_to_le32(common);123124/* recent CE (H6) need length in bytes, in word otherwise */125if (ce->variant->prng_t_dlen_in_bytes)126cet->t_dlen = cpu_to_le32(todo);127else128cet->t_dlen = cpu_to_le32(todo / 4);129130sym = PRNG_LD;131cet->t_sym_ctl = cpu_to_le32(sym);132cet->t_asym_ctl = 0;133134cet->t_key = desc_addr_val_le32(ce, dma_iv);135cet->t_iv = desc_addr_val_le32(ce, dma_iv);136137cet->t_dst[0].addr = desc_addr_val_le32(ce, dma_dst);138cet->t_dst[0].len = cpu_to_le32(todo / 4);139ce->chanlist[flow].timeout = 2000;140141err = sun8i_ce_run_task(ce, 3, "PRNG");142mutex_unlock(&ce->rnglock);143144pm_runtime_put(ce->dev);145146err_pm:147dma_unmap_single(ce->dev, dma_dst, todo, DMA_FROM_DEVICE);148err_dst:149dma_unmap_single(ce->dev, dma_iv, ctx->slen, DMA_TO_DEVICE);150151if (!err) {152memcpy(dst, d, dlen);153memcpy(ctx->seed, d + dlen, ctx->slen);154}155err_iv:156kfree_sensitive(d);157err_mem:158return err;159}160161162