summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAhmad Fatoum <a.fatoum@pengutronix.de>2024-03-13 11:56:30 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2024-03-15 08:07:39 +0100
commit45080577a633cb4b2270e2dcbcef0752913d692d (patch)
treec470cb6f5201a1014ca84fa7c8062a73b4007bc9
parentf9cd14f8ae30f1b0135f23bd4f37fbf90e7c838b (diff)
downloadbarebox-45080577a633cb4b2270e2dcbcef0752913d692d.tar.gz
barebox-45080577a633cb4b2270e2dcbcef0752913d692d.tar.xz
hw_random: add timeriomem_rng driver
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@pengutronix.de> Link: https://lore.barebox.org/20240313105631.686778-12-a.fatoum@pengutronix.de Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
-rw-r--r--drivers/hw_random/Kconfig7
-rw-r--r--drivers/hw_random/Makefile1
-rw-r--r--drivers/hw_random/timeriomem-rng.c145
3 files changed, 153 insertions, 0 deletions
diff --git a/drivers/hw_random/Kconfig b/drivers/hw_random/Kconfig
index 58df6a7544..bd1ac89ad3 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 8658d4e525..32b02fa211 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 0000000000..8d47058306
--- /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@digriz.org.uk>
+ *
+ * Derived from drivers/char/hw_random/omap-rng.c
+ * Copyright 2005 (c) MontaVista Software, Inc.
+ * Author: Deepak Saxena <dsaxena@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@digriz.org.uk>");
+MODULE_DESCRIPTION("Timer IOMEM H/W RNG driver");