forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
crypto: sun8i-ss - Add support for the PRNG
This patch had support for the PRNG present in the SS. The output was tested with rngtest without any failure. Signed-off-by: Corentin Labbe <[email protected]> Signed-off-by: Herbert Xu <[email protected]>
- Loading branch information
Showing
5 changed files
with
246 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
obj-$(CONFIG_CRYPTO_DEV_SUN8I_SS) += sun8i-ss.o | ||
sun8i-ss-y += sun8i-ss-core.o sun8i-ss-cipher.o | ||
sun8i-ss-$(CONFIG_CRYPTO_DEV_SUN8I_SS_PRNG) += sun8i-ss-prng.o |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
/* | ||
* sun8i-ss-prng.c - hardware cryptographic offloader for | ||
* Allwinner A80/A83T SoC | ||
* | ||
* Copyright (C) 2015-2020 Corentin Labbe <[email protected]> | ||
* | ||
* This file handle the PRNG found in the SS | ||
* | ||
* You could find a link for the datasheet in Documentation/arm/sunxi.rst | ||
*/ | ||
#include "sun8i-ss.h" | ||
#include <linux/dma-mapping.h> | ||
#include <linux/pm_runtime.h> | ||
#include <crypto/internal/rng.h> | ||
|
||
int sun8i_ss_prng_seed(struct crypto_rng *tfm, const u8 *seed, | ||
unsigned int slen) | ||
{ | ||
struct sun8i_ss_rng_tfm_ctx *ctx = crypto_rng_ctx(tfm); | ||
|
||
if (ctx->seed && ctx->slen != slen) { | ||
memzero_explicit(ctx->seed, ctx->slen); | ||
kfree(ctx->seed); | ||
ctx->slen = 0; | ||
ctx->seed = NULL; | ||
} | ||
if (!ctx->seed) | ||
ctx->seed = kmalloc(slen, GFP_KERNEL | GFP_DMA); | ||
if (!ctx->seed) | ||
return -ENOMEM; | ||
|
||
memcpy(ctx->seed, seed, slen); | ||
ctx->slen = slen; | ||
|
||
return 0; | ||
} | ||
|
||
int sun8i_ss_prng_init(struct crypto_tfm *tfm) | ||
{ | ||
struct sun8i_ss_rng_tfm_ctx *ctx = crypto_tfm_ctx(tfm); | ||
|
||
memset(ctx, 0, sizeof(struct sun8i_ss_rng_tfm_ctx)); | ||
return 0; | ||
} | ||
|
||
void sun8i_ss_prng_exit(struct crypto_tfm *tfm) | ||
{ | ||
struct sun8i_ss_rng_tfm_ctx *ctx = crypto_tfm_ctx(tfm); | ||
|
||
memzero_explicit(ctx->seed, ctx->slen); | ||
kfree(ctx->seed); | ||
ctx->seed = NULL; | ||
ctx->slen = 0; | ||
} | ||
|
||
int sun8i_ss_prng_generate(struct crypto_rng *tfm, const u8 *src, | ||
unsigned int slen, u8 *dst, unsigned int dlen) | ||
{ | ||
struct sun8i_ss_rng_tfm_ctx *ctx = crypto_rng_ctx(tfm); | ||
struct rng_alg *alg = crypto_rng_alg(tfm); | ||
struct sun8i_ss_alg_template *algt; | ||
struct sun8i_ss_dev *ss; | ||
dma_addr_t dma_iv, dma_dst; | ||
unsigned int todo; | ||
int err = 0; | ||
int flow; | ||
void *d; | ||
u32 v; | ||
|
||
algt = container_of(alg, struct sun8i_ss_alg_template, alg.rng); | ||
ss = algt->ss; | ||
|
||
if (ctx->slen == 0) { | ||
dev_err(ss->dev, "The PRNG is not seeded\n"); | ||
return -EINVAL; | ||
} | ||
|
||
/* The SS does not give an updated seed, so we need to get a new one. | ||
* So we will ask for an extra PRNG_SEED_SIZE data. | ||
* We want dlen + seedsize rounded up to a multiple of PRNG_DATA_SIZE | ||
*/ | ||
todo = dlen + PRNG_SEED_SIZE + PRNG_DATA_SIZE; | ||
todo -= todo % PRNG_DATA_SIZE; | ||
|
||
d = kzalloc(todo, GFP_KERNEL | GFP_DMA); | ||
if (!d) | ||
return -ENOMEM; | ||
|
||
flow = sun8i_ss_get_engine_number(ss); | ||
|
||
#ifdef CONFIG_CRYPTO_DEV_SUN8I_SS_DEBUG | ||
algt->stat_req++; | ||
algt->stat_bytes += todo; | ||
#endif | ||
|
||
v = SS_ALG_PRNG | SS_PRNG_CONTINUE | SS_START; | ||
if (flow) | ||
v |= SS_FLOW1; | ||
else | ||
v |= SS_FLOW0; | ||
|
||
dma_iv = dma_map_single(ss->dev, ctx->seed, ctx->slen, DMA_TO_DEVICE); | ||
if (dma_mapping_error(ss->dev, dma_iv)) { | ||
dev_err(ss->dev, "Cannot DMA MAP IV\n"); | ||
return -EFAULT; | ||
} | ||
|
||
dma_dst = dma_map_single(ss->dev, d, todo, DMA_FROM_DEVICE); | ||
if (dma_mapping_error(ss->dev, dma_dst)) { | ||
dev_err(ss->dev, "Cannot DMA MAP DST\n"); | ||
err = -EFAULT; | ||
goto err_iv; | ||
} | ||
|
||
err = pm_runtime_get_sync(ss->dev); | ||
if (err < 0) { | ||
pm_runtime_put_noidle(ss->dev); | ||
goto err_pm; | ||
} | ||
err = 0; | ||
|
||
mutex_lock(&ss->mlock); | ||
writel(dma_iv, ss->base + SS_IV_ADR_REG); | ||
/* the PRNG act badly (failing rngtest) without SS_KEY_ADR_REG set */ | ||
writel(dma_iv, ss->base + SS_KEY_ADR_REG); | ||
writel(dma_dst, ss->base + SS_DST_ADR_REG); | ||
writel(todo / 4, ss->base + SS_LEN_ADR_REG); | ||
|
||
reinit_completion(&ss->flows[flow].complete); | ||
ss->flows[flow].status = 0; | ||
/* Be sure all data is written before enabling the task */ | ||
wmb(); | ||
|
||
writel(v, ss->base + SS_CTL_REG); | ||
|
||
wait_for_completion_interruptible_timeout(&ss->flows[flow].complete, | ||
msecs_to_jiffies(todo)); | ||
if (ss->flows[flow].status == 0) { | ||
dev_err(ss->dev, "DMA timeout for PRNG (size=%u)\n", todo); | ||
err = -EFAULT; | ||
} | ||
/* Since cipher and hash use the linux/cryptoengine and that we have | ||
* a cryptoengine per flow, we are sure that they will issue only one | ||
* request per flow. | ||
* Since the cryptoengine wait for completion before submitting a new | ||
* one, the mlock could be left just after the final writel. | ||
* But cryptoengine cannot handle crypto_rng, so we need to be sure | ||
* nothing will use our flow. | ||
* The easiest way is to grab mlock until the hardware end our requests. | ||
* We could have used a per flow lock, but this would increase | ||
* complexity. | ||
* The drawback is that no request could be handled for the other flow. | ||
*/ | ||
mutex_unlock(&ss->mlock); | ||
|
||
pm_runtime_put(ss->dev); | ||
|
||
err_pm: | ||
dma_unmap_single(ss->dev, dma_dst, todo, DMA_FROM_DEVICE); | ||
err_iv: | ||
dma_unmap_single(ss->dev, dma_iv, ctx->slen, DMA_TO_DEVICE); | ||
|
||
if (!err) { | ||
memcpy(dst, d, dlen); | ||
/* Update seed */ | ||
memcpy(ctx->seed, d + dlen, ctx->slen); | ||
} | ||
memzero_explicit(d, todo); | ||
kfree(d); | ||
|
||
return err; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters