[PATCH 11/12] hw_random: add timeriomem_rng driver

Ahmad Fatoum a.fatoum at pengutronix.de
Wed Mar 13 03:56:30 PDT 2024


To enable proper hardening with stack protector, add support for the
RNGs with the interface of a single memory mapped 32-bit register.

This can possibly come in handy for the web demo.

Signed-off-by: Ahmad Fatoum <a.fatoum at pengutronix.de>
---
 drivers/hw_random/Kconfig          |   7 ++
 drivers/hw_random/Makefile         |   1 +
 drivers/hw_random/timeriomem-rng.c | 145 +++++++++++++++++++++++++++++
 3 files changed, 153 insertions(+)
 create mode 100644 drivers/hw_random/timeriomem-rng.c

diff --git a/drivers/hw_random/Kconfig b/drivers/hw_random/Kconfig
index 58df6a75443c..bd1ac89ad37d 100644
--- a/drivers/hw_random/Kconfig
+++ b/drivers/hw_random/Kconfig
@@ -8,6 +8,13 @@ menuconfig HWRNG
 
 if HWRNG
 
+config HW_RANDOM_TIMERIOMEM
+	tristate "Timer IOMEM HW Random Number Generator support"
+	help
+	  This driver provides barebox support for a generic Random
+	  Number Generator used by reading a 'dumb' iomem address that
+	  is to be read no faster than, for example, once a second.
+
 config HWRNG_MXC_RNGC
 	tristate "Freescale i.MX RNGC Random Number Generator"
 	depends on ARCH_IMX25 || ARCH_IMX35 || ARCH_IMX53 || COMPILE_TEST
diff --git a/drivers/hw_random/Makefile b/drivers/hw_random/Makefile
index 8658d4e52521..32b02fa211ac 100644
--- a/drivers/hw_random/Makefile
+++ b/drivers/hw_random/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_HW_RANDOM_ATMEL) += atmel-rng.o
 obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o
 obj-$(CONFIG_HW_RANDOM_IPROC_RNG200) += iproc-rng200.o
 obj-$(CONFIG_HW_RANDOM_ROCKCHIP) += rockchip-rng.o
+obj-$(CONFIG_HW_RANDOM_TIMERIOMEM) += timeriomem-rng.o
diff --git a/drivers/hw_random/timeriomem-rng.c b/drivers/hw_random/timeriomem-rng.c
new file mode 100644
index 000000000000..8d4705830653
--- /dev/null
+++ b/drivers/hw_random/timeriomem-rng.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * drivers/char/hw_random/timeriomem-rng.c
+ *
+ * Copyright (C) 2009 Alexander Clouter <alex at digriz.org.uk>
+ *
+ * Derived from drivers/char/hw_random/omap-rng.c
+ *   Copyright 2005 (c) MontaVista Software, Inc.
+ *   Author: Deepak Saxena <dsaxena at plexity.net>
+ *
+ * Overview:
+ *   This driver is useful for platforms that have an IO range that provides
+ *   periodic random data from a single IO memory address.  All the platform
+ *   has to do is provide the address and 'wait time' that new data becomes
+ *   available.
+ *
+ * TODO: add support for reading sizes other than 32bits and masking
+ */
+
+#include <linux/hw_random.h>
+#include <linux/io.h>
+#include <linux/ktime.h>
+#include <of.h>
+#include <linux/device.h>
+#include <linux/time.h>
+
+struct timeriomem_rng_private {
+	void __iomem		*io_base;
+
+	ktime_t			period;
+	ktime_t			next_read;
+
+	struct hwrng		rng_ops;
+};
+
+static int timeriomem_rng_read(struct hwrng *hwrng, void *data,
+				size_t max, bool wait)
+{
+	struct timeriomem_rng_private *priv =
+		container_of(hwrng, struct timeriomem_rng_private, rng_ops);
+	int retval = 0;
+	int period_us = ktime_to_us(priv->period);
+	ktime_t now = ktime_get();
+
+	/*
+	 * There may not have been enough time for new data to be generated
+	 * since the last request.  If the caller doesn't want to wait, let them
+	 * bail out.  Otherwise, wait for the completion.  If the new data has
+	 * already been generated, the completion should already be available.
+	 */
+	if (ktime_before(now, priv->next_read)) {
+		if (!wait)
+			return 0;
+
+		udelay(ktime_to_us(ktime_sub(priv->next_read, now)));
+	}
+
+	do {
+		/*
+		 * After the first read, all additional reads will need to wait
+		 * for the RNG to generate new data.  Since the period can have
+		 * a wide range of values (1us to 1s have been observed), allow
+		 * for 1% tolerance in the sleep time rather than a fixed value.
+		 */
+		if (retval > 0)
+			udelay(period_us);
+
+		*(u32 *)data = readl(priv->io_base);
+		retval += sizeof(u32);
+		data += sizeof(u32);
+		max -= sizeof(u32);
+	} while (wait && max > sizeof(u32));
+
+	/*
+	 * Block any new callers until the RNG has had time to generate new
+	 * data.
+	 */
+	priv->next_read = ktime_add(ktime_get(), priv->period);
+
+	return retval;
+}
+
+static int timeriomem_rng_probe(struct device *dev)
+{
+	struct timeriomem_rng_private *priv;
+	struct resource *res;
+	int err = 0;
+	int period;
+
+	/* Allocate memory for the device structure (and zero it) */
+	priv = devm_kzalloc(dev,
+			sizeof(struct timeriomem_rng_private), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->io_base = dev_platform_get_and_ioremap_resource(dev, 0, &res);
+	if (IS_ERR(priv->io_base))
+		return PTR_ERR(priv->io_base);
+
+	if (res->start % 4 != 0 || resource_size(res) < 4) {
+		dev_err(dev,
+			"address must be at least four bytes wide and 32-bit aligned\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32(dev->of_node, "period", &period))
+		return dev_err_probe(dev, -EINVAL, "missing period\n");
+
+	priv->period = ns_to_ktime(period * NSEC_PER_USEC);
+
+	priv->rng_ops.name = dev_name(dev);
+	priv->rng_ops.read = timeriomem_rng_read;
+
+	/* Assume random data is already available. */
+	priv->next_read = ktime_get();
+
+	err = hwrng_register(dev, &priv->rng_ops);
+	if (err) {
+		dev_err(dev, "problem registering\n");
+		return err;
+	}
+
+	dev_info(dev, "32bits from 0x%p @ %dus\n",
+			priv->io_base, period);
+
+	return 0;
+}
+
+static const struct of_device_id timeriomem_rng_match[] = {
+	{ .compatible = "timeriomem_rng" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, timeriomem_rng_match);
+
+static struct driver timeriomem_rng_driver = {
+	.name		= "timeriomem_rng",
+	.of_match_table	= timeriomem_rng_match,
+	.probe		= timeriomem_rng_probe,
+};
+
+device_platform_driver(timeriomem_rng_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Alexander Clouter <alex at digriz.org.uk>");
+MODULE_DESCRIPTION("Timer IOMEM H/W RNG driver");
-- 
2.39.2




More information about the barebox mailing list