Uboot RNG Driver using Data Co-processor
authorKshitiz Varshney <kshitiz.varshney@nxp.com>
Thu, 22 Dec 2022 08:50:27 +0000 (09:50 +0100)
committerTom Rini <trini@konsulko.com>
Thu, 12 Jan 2023 16:25:46 +0000 (11:25 -0500)
This commit introduces Random number generator to uboot. It uses DCP
driver for number generation.
RNG driver can be invoked by using below command on uboot prompt:-
           rng <number of bytes>

Signed-off-by: Kshitiz Varshney <kshitiz.varshney@nxp.com>
Reviewed-by: Ye Li <ye.li@nxp.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
drivers/crypto/fsl/Kconfig
drivers/crypto/fsl/Makefile
drivers/crypto/fsl/dcp_rng.c [new file with mode: 0644]

index b04c701..91a51cc 100644 (file)
@@ -73,3 +73,13 @@ config FSL_CAAM_RNG
          reseeded from the TRNG every time random data is generated.
 
 endif
+
+config FSL_DCP_RNG
+       bool "Enable Random Number Generator support"
+       depends on DM_RNG
+       default n
+       help
+         Enable support for the hardware based random number generator
+         module of the DCP. It uses the True Random Number Generator (TRNG)
+         and a Pseudo-Random Number Generator (PRNG) to achieve a true
+         randomness and cryptographic strength.
index f9c3cce..7a2543e 100644 (file)
@@ -7,4 +7,5 @@ obj-$(CONFIG_FSL_CAAM) += jr.o fsl_hash.o jobdesc.o error.o
 obj-$(CONFIG_CMD_BLOB)$(CONFIG_IMX_CAAM_DEK_ENCAP) += fsl_blob.o
 obj-$(CONFIG_RSA_FREESCALE_EXP) += fsl_rsa.o
 obj-$(CONFIG_FSL_CAAM_RNG) += rng.o
+obj-$(CONFIG_FSL_DCP_RNG) += dcp_rng.o
 obj-$(CONFIG_FSL_MFGPROT) += fsl_mfgprot.o
diff --git a/drivers/crypto/fsl/dcp_rng.c b/drivers/crypto/fsl/dcp_rng.c
new file mode 100644 (file)
index 0000000..3170696
--- /dev/null
@@ -0,0 +1,182 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * RNG driver for Freescale RNGC
+ *
+ * Copyright 2022 NXP
+ *
+ * Based on RNGC driver in drivers/char/hw_random/imx-rngc.c in Linux
+ */
+
+#include <common.h>
+#include <cpu_func.h>
+#include <dm.h>
+#include <rng.h>
+#include <asm/cache.h>
+#include <asm/io.h>
+#include <dm/root.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+
+#define DCP_RNG_MAX_FIFO_STORE_SIZE    4
+#define RNGC_VER_ID                    0x0
+#define RNGC_COMMAND                   0x4
+#define RNGC_CONTROL                   0x8
+#define RNGC_STATUS                    0xC
+#define RNGC_ERROR                     0x10
+#define RNGC_FIFO                      0x14
+
+/* the fields in the ver id register */
+#define RNGC_TYPE_SHIFT                        28
+
+/* the rng_type field */
+#define RNGC_TYPE_RNGB                 0x1
+#define RNGC_TYPE_RNGC                 0x2
+
+#define RNGC_CMD_CLR_ERR               0x20
+#define RNGC_CMD_SEED                  0x2
+
+#define RNGC_CTRL_AUTO_SEED            0x10
+
+#define RNGC_STATUS_ERROR              0x10000
+#define RNGC_STATUS_FIFO_LEVEL_MASK    0xf00
+#define RNGC_STATUS_FIFO_LEVEL_SHIFT   8
+#define RNGC_STATUS_SEED_DONE          0x20
+#define RNGC_STATUS_ST_DONE            0x10
+
+#define RNGC_ERROR_STATUS_STAT_ERR     0x8
+
+#define RNGC_TIMEOUT                   3000000U /* 3 sec */
+
+struct imx_rngc_priv {
+       unsigned long base;
+};
+
+static int rngc_read(struct udevice *dev, void *data, size_t len)
+{
+       struct imx_rngc_priv *priv = dev_get_priv(dev);
+       u8 buffer[DCP_RNG_MAX_FIFO_STORE_SIZE];
+       u32 status, level;
+       size_t size;
+
+       while (len) {
+               status = readl(priv->base + RNGC_STATUS);
+
+               /* is there some error while reading this random number? */
+               if (status & RNGC_STATUS_ERROR)
+                       break;
+               /* how many random numbers are in FIFO? [0-16] */
+               level = (status & RNGC_STATUS_FIFO_LEVEL_MASK) >>
+                       RNGC_STATUS_FIFO_LEVEL_SHIFT;
+
+               if (level) {
+                       /* retrieve a random number from FIFO */
+                       *(u32 *)buffer = readl(priv->base + RNGC_FIFO);
+                       size = min(len, sizeof(u32));
+                       memcpy(data, buffer, size);
+                       data += size;
+                       len -= size;
+               }
+       }
+
+       return len ? -EIO : 0;
+}
+
+static int rngc_init(struct imx_rngc_priv *priv)
+{
+       u32 cmd, ctrl, status, err_reg = 0;
+       unsigned long long timeval = 0;
+       unsigned long long timeout = RNGC_TIMEOUT;
+
+       /* clear error */
+       cmd = readl(priv->base + RNGC_COMMAND);
+       writel(cmd | RNGC_CMD_CLR_ERR, priv->base + RNGC_COMMAND);
+
+       /* create seed, repeat while there is some statistical error */
+       do {
+               /* seed creation */
+               cmd = readl(priv->base + RNGC_COMMAND);
+               writel(cmd | RNGC_CMD_SEED, priv->base + RNGC_COMMAND);
+
+               udelay(1);
+               timeval += 1;
+
+               status = readl(priv->base + RNGC_STATUS);
+               err_reg = readl(priv->base + RNGC_ERROR);
+
+               if (status & (RNGC_STATUS_SEED_DONE | RNGC_STATUS_ST_DONE))
+                       break;
+
+               if (timeval > timeout) {
+                       debug("rngc timed out\n");
+                       return -ETIMEDOUT;
+               }
+       } while (err_reg == RNGC_ERROR_STATUS_STAT_ERR);
+
+       if (err_reg)
+               return -EIO;
+
+       /*
+        * enable automatic seeding, the rngc creates a new seed automatically
+        * after serving 2^20 random 160-bit words
+        */
+       ctrl = readl(priv->base + RNGC_CONTROL);
+       ctrl |= RNGC_CTRL_AUTO_SEED;
+       writel(ctrl, priv->base + RNGC_CONTROL);
+       return 0;
+}
+
+static int rngc_probe(struct udevice *dev)
+{
+       struct imx_rngc_priv *priv = dev_get_priv(dev);
+       fdt_addr_t addr;
+       u32 ver_id;
+       u8  rng_type;
+       int ret;
+
+       addr = dev_read_addr(dev);
+       if (addr == FDT_ADDR_T_NONE) {
+               ret = -EINVAL;
+               goto err;
+       }
+
+       priv->base = addr;
+       ver_id = readl(priv->base + RNGC_VER_ID);
+       rng_type = ver_id >> RNGC_TYPE_SHIFT;
+       /*
+        * This driver supports only RNGC and RNGB. (There's a different
+        * driver for RNGA.)
+        */
+       if (rng_type != RNGC_TYPE_RNGC && rng_type != RNGC_TYPE_RNGB) {
+               ret = -ENODEV;
+               goto err;
+       }
+
+       ret = rngc_init(priv);
+       if (ret)
+               goto err;
+
+       return 0;
+
+err:
+       printf("%s error = %d\n", __func__, ret);
+       return ret;
+}
+
+static const struct dm_rng_ops rngc_ops = {
+       .read = rngc_read,
+};
+
+static const struct udevice_id rngc_dt_ids[] = {
+       { .compatible = "fsl,imx25-rngb" },
+       { }
+};
+
+U_BOOT_DRIVER(dcp_rng) = {
+       .name = "dcp_rng",
+       .id = UCLASS_RNG,
+       .of_match = rngc_dt_ids,
+       .ops = &rngc_ops,
+       .probe = rngc_probe,
+       .priv_auto  = sizeof(struct imx_rngc_priv),
+       .flags = DM_FLAG_ALLOC_PRIV_DMA,
+};