diff mbox

[v3,3/6] crypto: Add Qcom prng driver

Message ID 20180703060434.19293-4-vkoul@kernel.org (mailing list archive)
State Superseded
Delegated to: Herbert Xu
Headers show

Commit Message

Vinod Koul July 3, 2018, 6:04 a.m. UTC
This ports the Qcom prng from older hw_random driver.

No change of functionality and move from hw_random to crypto
APIs is done.

Signed-off-by: Vinod Koul <vkoul@kernel.org>
---
 drivers/crypto/Kconfig    |  11 +++
 drivers/crypto/Makefile   |   1 +
 drivers/crypto/qcom-rng.c | 208 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 220 insertions(+)
 create mode 100644 drivers/crypto/qcom-rng.c

Comments

Stephan Mueller July 3, 2018, 1:28 p.m. UTC | #1
Am Dienstag, 3. Juli 2018, 08:04:31 CEST schrieb Vinod Koul:

Hi Vinod,

> +static int qcom_rng_read(struct qcom_rng *rng, void *data, size_t max)
> +{
> +	size_t currsize = 0;
> +	u32 *retdata = data;

How can you be sure that this cast is appropriate? I.e. how is it guaranteed 
that data is 4-byte aligned?

Also, the data variable in qcom_rng_generate is a u8 -- shouldn't this type be 
used instead of a void?


Ciao
Stephan
Vinod Koul July 4, 2018, 4:10 a.m. UTC | #2
Hi Stephan,

On 03-07-18, 15:28, Stephan Mueller wrote:
> Am Dienstag, 3. Juli 2018, 08:04:31 CEST schrieb Vinod Koul:
> > +static int qcom_rng_read(struct qcom_rng *rng, void *data, size_t max)
> > +{
> > +	size_t currsize = 0;
> > +	u32 *retdata = data;
> 
> How can you be sure that this cast is appropriate? I.e. how is it guaranteed 
> that data is 4-byte aligned?

While reading we check the alignment:

               /* make sure we stay on 32bit boundary */
               if ((max - currsize) < WORD_SZ)
                       break;

> Also, the data variable in qcom_rng_generate is a u8 -- shouldn't this type be 
> used instead of a void?

That does make sense to me. IIRC the read is for a byte. I will check
this and update it

Thanks for the quick review
Vinod Koul July 4, 2018, 6:10 a.m. UTC | #3
On 04-07-18, 09:40, Vinod wrote:
> Hi Stephan,
> 
> On 03-07-18, 15:28, Stephan Mueller wrote:
> > Am Dienstag, 3. Juli 2018, 08:04:31 CEST schrieb Vinod Koul:
> > > +static int qcom_rng_read(struct qcom_rng *rng, void *data, size_t max)
> > > +{
> > > +	size_t currsize = 0;
> > > +	u32 *retdata = data;
> > 
> > How can you be sure that this cast is appropriate? I.e. how is it guaranteed 
> > that data is 4-byte aligned?
> 
> While reading we check the alignment:
> 
>                /* make sure we stay on 32bit boundary */
>                if ((max - currsize) < WORD_SZ)
>                        break;
> 
> > Also, the data variable in qcom_rng_generate is a u8 -- shouldn't this type be 
> > used instead of a void?
> 
> That does make sense to me. IIRC the read is for a byte. I will check
> this and update it

Okay so I rechecked this, the hardware gives 32 bits of random data. I
am thinking of splitting the word and updating by each byte. That way
trailing zero can also be avoided which is the case now
Stephan Mueller July 4, 2018, 6:16 a.m. UTC | #4
Am Mittwoch, 4. Juli 2018, 08:10:35 CEST schrieb Vinod:

Hi Vinod,

> On 04-07-18, 09:40, Vinod wrote:
> > Hi Stephan,
> > 
> > On 03-07-18, 15:28, Stephan Mueller wrote:
> > > Am Dienstag, 3. Juli 2018, 08:04:31 CEST schrieb Vinod Koul:
> > > > +static int qcom_rng_read(struct qcom_rng *rng, void *data, size_t
> > > > max)
> > > > +{
> > > > +	size_t currsize = 0;
> > > > +	u32 *retdata = data;
> > > 
> > > How can you be sure that this cast is appropriate? I.e. how is it
> > > guaranteed that data is 4-byte aligned?
> > 
> > While reading we check the alignment:
> >                /* make sure we stay on 32bit boundary */
> >                if ((max - currsize) < WORD_SZ)
> >                
> >                        break;

I am not sure I follow your argument.

You cast a void (or u8 pointer into u32:

+       u32 *retdata = data;

You use it:

+       *retdata++ = val;

Followed by your check.

What I mean is that the initial cast and then the subsequent write operation 
is only guaranteed to work if the initial pointer is alighed on a 4 byte 
boundary. However, since it is an u8 pointer, it is not guaranteed to be 
aligned.

So, I guess you want to use memcpy (at least if it is not aligned).

Ciao
Stephan
Timur Tabi July 4, 2018, 1:42 p.m. UTC | #5
On 7/4/18 1:10 AM, Vinod wrote:
> Okay so I rechecked this, the hardware gives 32 bits of random data. I
> am thinking of splitting the word and updating by each byte. That way
> trailing zero can also be avoided which is the case now

The current driver only returns data in multiples of 4 bytes.  Why can't 
you keep that?
Stephan Mueller July 4, 2018, 4:02 p.m. UTC | #6
Am Dienstag, 3. Juli 2018, 08:04:31 CEST schrieb Vinod Koul:

Hi Vinod,

> +static int qcom_rng_seed(struct crypto_rng *tfm, const u8 *seed,
> +			 unsigned int slen)
> +{
> +	return 0;
> +}

One more question: is it not possible to mix in data into the DRNG? I thought 
it would be possible with the Qualcomm DRBG.

Note, I am asking because of my /dev/random drop-in-replacement 
implementation, any RNG from the kernel crypto API can be configured to be 
used as an output DRNG. Though, this will only work if the DRNG also accepts 
seed from the software noise sources.

Ciao
Stephan
Vinod Koul July 5, 2018, 6:01 a.m. UTC | #7
Hi Stephan,

On 04-07-18, 18:02, Stephan Mueller wrote:
> Am Dienstag, 3. Juli 2018, 08:04:31 CEST schrieb Vinod Koul:
> > +static int qcom_rng_seed(struct crypto_rng *tfm, const u8 *seed,
> > +			 unsigned int slen)
> > +{
> > +	return 0;
> > +}
> 
> One more question: is it not possible to mix in data into the DRNG? I thought 
> it would be possible with the Qualcomm DRBG.
> 
> Note, I am asking because of my /dev/random drop-in-replacement 
> implementation, any RNG from the kernel crypto API can be configured to be 
> used as an output DRNG. Though, this will only work if the DRNG also accepts 
> seed from the software noise sources.

The v1 hardware supports seeding but the register is Read Only for SW and
only trusted zone (firmware) can write.

v2 hardware slice does not have seeding. v2 seeding is not accessible to
SW.

So in short, it is not available for us to use :(
diff mbox

Patch

diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index 43cccf6aff61..b8d9e71e550a 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -585,6 +585,17 @@  config CRYPTO_DEV_QCE
 	  hardware. To compile this driver as a module, choose M here. The
 	  module will be called qcrypto.
 
+config CRYPTO_DEV_QCOM_RNG
+	tristate "Qualcomm Randon Number Generator Driver"
+	depends on ARCH_QCOM || COMPILE_TEST
+	select CRYPTO_RNG
+	help
+	  This driver provides support for the Random Number
+	  Generator hardware found on Qualcomm SoCs.
+
+	  To compile this driver as a module, choose M here. the
+          module will be called qcom-rng. If unsure, say N.
+
 config CRYPTO_DEV_VMX
 	bool "Support for VMX cryptographic acceleration instructions"
 	depends on PPC64 && VSX
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index 7ae87b4f6c8d..3602875c4f80 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -33,6 +33,7 @@  obj-$(CONFIG_CRYPTO_DEV_PICOXCELL) += picoxcell_crypto.o
 obj-$(CONFIG_CRYPTO_DEV_PPC4XX) += amcc/
 obj-$(CONFIG_CRYPTO_DEV_QAT) += qat/
 obj-$(CONFIG_CRYPTO_DEV_QCE) += qce/
+obj-$(CONFIG_CRYPTO_DEV_QCOM_RNG) += qcom-rng.o
 obj-$(CONFIG_CRYPTO_DEV_ROCKCHIP) += rockchip/
 obj-$(CONFIG_CRYPTO_DEV_S5P) += s5p-sss.o
 obj-$(CONFIG_CRYPTO_DEV_SAHARA) += sahara.o
diff --git a/drivers/crypto/qcom-rng.c b/drivers/crypto/qcom-rng.c
new file mode 100644
index 000000000000..e235a35359d3
--- /dev/null
+++ b/drivers/crypto/qcom-rng.c
@@ -0,0 +1,208 @@ 
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2017-18 Linaro Limited
+//
+// Based on msm-rng.c and downstream driver
+
+#include <crypto/internal/rng.h>
+#include <linux/clk.h>
+#include <linux/crypto.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+/* Device specific register offsets */
+#define PRNG_DATA_OUT		0x0000
+#define PRNG_STATUS		0x0004
+#define PRNG_LFSR_CFG		0x0100
+#define PRNG_CONFIG		0x0104
+
+/* Device specific register masks and config values */
+#define PRNG_LFSR_CFG_MASK	0x0000ffff
+#define PRNG_LFSR_CFG_CLOCKS	0x0000dddd
+#define PRNG_CONFIG_HW_ENABLE	BIT(1)
+#define PRNG_STATUS_DATA_AVAIL	BIT(0)
+
+#define MAX_HW_FIFO_DEPTH	16
+#define MAX_HW_FIFO_SIZE	(MAX_HW_FIFO_DEPTH * 4)
+#define WORD_SZ			4
+
+struct qcom_rng {
+	struct mutex lock;
+	void __iomem *base;
+	struct clk *clk;
+};
+
+struct qcom_rng_ctx {
+	struct qcom_rng *rng;
+};
+
+static struct qcom_rng *qcom_rng_dev;
+
+static int qcom_rng_read(struct qcom_rng *rng, void *data, size_t max)
+{
+	size_t currsize = 0;
+	u32 *retdata = data;
+	u32 val;
+
+	/* read random data from hardware */
+	do {
+		val = readl_relaxed(rng->base + PRNG_STATUS);
+		if (!(val & PRNG_STATUS_DATA_AVAIL))
+			break;
+
+		val = readl_relaxed(rng->base + PRNG_DATA_OUT);
+		if (!val)
+			break;
+
+		*retdata++ = val;
+		currsize += WORD_SZ;
+
+		/* make sure we stay on 32bit boundary */
+		if ((max - currsize) < WORD_SZ)
+			break;
+	} while (currsize < max);
+
+	return currsize;
+}
+
+static int qcom_rng_generate(struct crypto_rng *tfm,
+			     const u8 *src, unsigned int slen,
+			     u8 *dstn, unsigned int dlen)
+{
+	struct qcom_rng_ctx *ctx = crypto_rng_ctx(tfm);
+	struct qcom_rng *rng = ctx->rng;
+	int ret;
+
+	ret = clk_prepare_enable(rng->clk);
+	if (ret)
+		return ret;
+
+	mutex_lock(&rng->lock);
+
+	ret = qcom_rng_read(rng, dstn, dlen);
+
+	mutex_unlock(&rng->lock);
+	clk_disable_unprepare(rng->clk);
+
+	return 0;
+}
+
+static int qcom_rng_seed(struct crypto_rng *tfm, const u8 *seed,
+			 unsigned int slen)
+{
+	return 0;
+}
+
+static int qcom_rng_enable(struct qcom_rng *rng)
+{
+	u32 val;
+	int ret;
+
+	ret = clk_prepare_enable(rng->clk);
+	if (ret)
+		return ret;
+
+	/* Enable PRNG only if it is not already enabled */
+	val = readl_relaxed(rng->base + PRNG_CONFIG);
+	if (val & PRNG_CONFIG_HW_ENABLE)
+		goto already_enabled;
+
+	val = readl_relaxed(rng->base + PRNG_LFSR_CFG);
+	val &= ~PRNG_LFSR_CFG_MASK;
+	val |= PRNG_LFSR_CFG_CLOCKS;
+	writel(val, rng->base + PRNG_LFSR_CFG);
+
+	val = readl_relaxed(rng->base + PRNG_CONFIG);
+	val |= PRNG_CONFIG_HW_ENABLE;
+	writel(val, rng->base + PRNG_CONFIG);
+
+already_enabled:
+	clk_disable_unprepare(rng->clk);
+
+	return 0;
+}
+
+static int qcom_rng_init(struct crypto_tfm *tfm)
+{
+	struct qcom_rng_ctx *ctx = crypto_tfm_ctx(tfm);
+
+	ctx->rng = qcom_rng_dev;
+
+	return qcom_rng_enable(ctx->rng);
+}
+
+static struct rng_alg qcom_rng_alg = {
+	.generate	= qcom_rng_generate,
+	.seed		= qcom_rng_seed,
+	.seedsize	= 0,
+	.base		= {
+		.cra_name		= "stdrng",
+		.cra_driver_name	= "qcom-rng",
+		.cra_flags		= CRYPTO_ALG_TYPE_RNG,
+		.cra_priority		= 300,
+		.cra_ctxsize		= sizeof(struct qcom_rng_ctx),
+		.cra_module		= THIS_MODULE,
+		.cra_init		= qcom_rng_init,
+	}
+};
+
+static int qcom_rng_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct qcom_rng *rng;
+	int ret;
+
+	rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL);
+	if (!rng)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, rng);
+	mutex_init(&rng->lock);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	rng->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(rng->base))
+		return PTR_ERR(rng->base);
+
+	rng->clk = devm_clk_get(&pdev->dev, "core");
+	if (IS_ERR(rng->clk))
+		return PTR_ERR(rng->clk);
+
+	qcom_rng_dev = rng;
+	ret = crypto_register_rng(&qcom_rng_alg);
+	if (ret) {
+		dev_err(&pdev->dev, "Register crypto rng failed: %d\n", ret);
+		qcom_rng_dev = NULL;
+	}
+
+	return ret;
+}
+
+static int qcom_rng_remove(struct platform_device *pdev)
+{
+	crypto_unregister_rng(&qcom_rng_alg);
+
+	qcom_rng_dev = NULL;
+
+	return 0;
+}
+
+static const struct of_device_id qcom_rng_of_match[] = {
+	{ .compatible = "qcom,prng" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, qcom_rng_of_match);
+
+static struct platform_driver qcom_rng_driver = {
+	.probe = qcom_rng_probe,
+	.remove =  qcom_rng_remove,
+	.driver = {
+		.name = KBUILD_MODNAME,
+		.of_match_table = of_match_ptr(qcom_rng_of_match),
+	}
+};
+module_platform_driver(qcom_rng_driver);
+
+MODULE_ALIAS("platform:" KBUILD_MODNAME);
+MODULE_DESCRIPTION("Qualcomm random number generator driver");
+MODULE_LICENSE("GPL v2");