Skip to content

Commit

Permalink
crypto: sun8i-ss - Add support for the PRNG
Browse files Browse the repository at this point in the history
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
montjoie authored and herbertx committed Sep 25, 2020
1 parent f1455b1 commit ac2614d
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 0 deletions.
8 changes: 8 additions & 0 deletions drivers/crypto/allwinner/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,11 @@ config CRYPTO_DEV_SUN8I_SS_DEBUG
Say y to enable sun8i-ss debug stats.
This will create /sys/kernel/debug/sun8i-ss/stats for displaying
the number of requests per flow and per algorithm.

config CRYPTO_DEV_SUN8I_SS_PRNG
bool "Support for Allwinner Security System PRNG"
depends on CRYPTO_DEV_SUN8I_SS
select CRYPTO_RNG
help
Select this option if you want to provide kernel-side support for
the Pseudo-Random Number Generator found in the Security System.
1 change: 1 addition & 0 deletions drivers/crypto/allwinner/sun8i-ss/Makefile
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
39 changes: 39 additions & 0 deletions drivers/crypto/allwinner/sun8i-ss/sun8i-ss-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <crypto/internal/rng.h>
#include <crypto/internal/skcipher.h>

#include "sun8i-ss.h"
Expand Down Expand Up @@ -264,6 +265,25 @@ static struct sun8i_ss_alg_template ss_algs[] = {
.decrypt = sun8i_ss_skdecrypt,
}
},
#ifdef CONFIG_CRYPTO_DEV_SUN8I_SS_PRNG
{
.type = CRYPTO_ALG_TYPE_RNG,
.alg.rng = {
.base = {
.cra_name = "stdrng",
.cra_driver_name = "sun8i-ss-prng",
.cra_priority = 300,
.cra_ctxsize = sizeof(struct sun8i_ss_rng_tfm_ctx),
.cra_module = THIS_MODULE,
.cra_init = sun8i_ss_prng_init,
.cra_exit = sun8i_ss_prng_exit,
},
.generate = sun8i_ss_prng_generate,
.seed = sun8i_ss_prng_seed,
.seedsize = PRNG_SEED_SIZE,
}
},
#endif
};

#ifdef CONFIG_CRYPTO_DEV_SUN8I_SS_DEBUG
Expand All @@ -285,6 +305,12 @@ static int sun8i_ss_debugfs_show(struct seq_file *seq, void *v)
ss_algs[i].alg.skcipher.base.cra_name,
ss_algs[i].stat_req, ss_algs[i].stat_fb);
break;
case CRYPTO_ALG_TYPE_RNG:
seq_printf(seq, "%s %s %lu %lu\n",
ss_algs[i].alg.rng.base.cra_driver_name,
ss_algs[i].alg.rng.base.cra_name,
ss_algs[i].stat_req, ss_algs[i].stat_bytes);
break;
}
}
return 0;
Expand Down Expand Up @@ -437,6 +463,14 @@ static int sun8i_ss_register_algs(struct sun8i_ss_dev *ss)
return err;
}
break;
case CRYPTO_ALG_TYPE_RNG:
err = crypto_register_rng(&ss_algs[i].alg.rng);
if (err) {
dev_err(ss->dev, "Fail to register %s\n",
ss_algs[i].alg.rng.base.cra_name);
ss_algs[i].ss = NULL;
}
break;
default:
ss_algs[i].ss = NULL;
dev_err(ss->dev, "ERROR: tried to register an unknown algo\n");
Expand All @@ -458,6 +492,11 @@ static void sun8i_ss_unregister_algs(struct sun8i_ss_dev *ss)
ss_algs[i].alg.skcipher.base.cra_name);
crypto_unregister_skcipher(&ss_algs[i].alg.skcipher);
break;
case CRYPTO_ALG_TYPE_RNG:
dev_info(ss->dev, "Unregister %d %s\n", i,
ss_algs[i].alg.rng.base.cra_name);
crypto_unregister_rng(&ss_algs[i].alg.rng);
break;
}
}
}
Expand Down
173 changes: 173 additions & 0 deletions drivers/crypto/allwinner/sun8i-ss/sun8i-ss-prng.c
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;
}
25 changes: 25 additions & 0 deletions drivers/crypto/allwinner/sun8i-ss/sun8i-ss.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <crypto/aes.h>
#include <crypto/des.h>
#include <crypto/engine.h>
#include <crypto/rng.h>
#include <crypto/skcipher.h>
#include <linux/atomic.h>
#include <linux/debugfs.h>
Expand All @@ -21,6 +22,7 @@
#define SS_ALG_AES 0
#define SS_ALG_DES (1 << 2)
#define SS_ALG_3DES (2 << 2)
#define SS_ALG_PRNG (4 << 2)

#define SS_CTL_REG 0x00
#define SS_INT_CTL_REG 0x04
Expand Down Expand Up @@ -52,6 +54,8 @@
#define SS_FLOW0 BIT(30)
#define SS_FLOW1 BIT(31)

#define SS_PRNG_CONTINUE BIT(18)

#define MAX_SG 8

#define MAXFLOW 2
Expand All @@ -61,6 +65,9 @@
#define SS_DIE_ID_SHIFT 20
#define SS_DIE_ID_MASK 0x07

#define PRNG_DATA_SIZE (160 / 8)
#define PRNG_SEED_SIZE DIV_ROUND_UP(175, 8)

/*
* struct ss_clock - Describe clocks used by sun8i-ss
* @name: Name of clock needed by this variant
Expand Down Expand Up @@ -181,6 +188,16 @@ struct sun8i_cipher_tfm_ctx {
struct crypto_skcipher *fallback_tfm;
};

/*
* struct sun8i_ss_prng_ctx - context for PRNG TFM
* @seed: The seed to use
* @slen: The size of the seed
*/
struct sun8i_ss_rng_tfm_ctx {
void *seed;
unsigned int slen;
};

/*
* struct sun8i_ss_alg_template - crypto_alg template
* @type: the CRYPTO_ALG_TYPE for this template
Expand All @@ -191,6 +208,7 @@ struct sun8i_cipher_tfm_ctx {
* @alg: one of sub struct must be used
* @stat_req: number of request done on this template
* @stat_fb: number of request which has fallbacked
* @stat_bytes: total data size done by this template
*/
struct sun8i_ss_alg_template {
u32 type;
Expand All @@ -199,10 +217,12 @@ struct sun8i_ss_alg_template {
struct sun8i_ss_dev *ss;
union {
struct skcipher_alg skcipher;
struct rng_alg rng;
} alg;
#ifdef CONFIG_CRYPTO_DEV_SUN8I_SS_DEBUG
unsigned long stat_req;
unsigned long stat_fb;
unsigned long stat_bytes;
#endif
};

Expand All @@ -220,3 +240,8 @@ int sun8i_ss_skencrypt(struct skcipher_request *areq);
int sun8i_ss_get_engine_number(struct sun8i_ss_dev *ss);

int sun8i_ss_run_task(struct sun8i_ss_dev *ss, struct sun8i_cipher_req_ctx *rctx, const char *name);
int sun8i_ss_prng_generate(struct crypto_rng *tfm, const u8 *src,
unsigned int slen, u8 *dst, unsigned int dlen);
int sun8i_ss_prng_seed(struct crypto_rng *tfm, const u8 *seed, unsigned int slen);
int sun8i_ss_prng_init(struct crypto_tfm *tfm);
void sun8i_ss_prng_exit(struct crypto_tfm *tfm);

0 comments on commit ac2614d

Please sign in to comment.