[Differential] D42638: Rockchip RNG

"titus_edc.ro (Titus Manea)" <[email protected]>
Newsgroups gmane.os.freebsd.devel.arm
Message-ID <[email protected]>
titus_edc.ro created this revision.
titus_edc.ro added a reviewer: freebsd-arm-list.
Herald added subscribers: emaste, imp.
Herald added a reviewer: andrew.
Herald added a reviewer: andrew.
Herald added a reviewer: andrew.
titus_edc.ro requested review of this revision.

REVISION SUMMARY
  rockchip rng support, similar with bcm2835_rng
  random data generated is passed to
  random_harvest_queue
  
  v1 and v2 supported only v2 tested
  randomness of data generated only tested with the 'eye test'

REPOSITORY
  rG FreeBSD src repository

REVISION DETAIL
  https://reviews.freebsd.org/D42638

AFFECTED FILES
  arm64/rockchip/rk_rng.c

EMAIL PREFERENCES
  https://reviews.freebsd.org/settings/panel/emailpreferences/

To: titus_edc.ro, freebsd-arm-list, andrew
Cc: imp, emaste, pstef
D42638.130216.patch (text/x-patch, 10.4 KB)
diff --git a/arm64/rockchip/rk_rng.c b/arm64/rockchip/rk_rng.c
new file mode 100644
--- /dev/null
+++ b/arm64/rockchip/rk_rng.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright (c) 2023 titusm [email protected]
+ *
+ * based on $OpenBSD: rkrng.c,v 1.5 2023/04/14 01:11:32 dlg Exp $
+ * Copyright (c) 2020 Mark Kettenis <[email protected]>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/ktr.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/random.h>
+#include <sys/selinfo.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/random/randomdev.h>
+#include <dev/random/random_harvestq.h>
+
+static device_attach_t rk_rng_attach;
+static device_detach_t rk_rng_detach;
+static device_probe_t rk_rng_probe;
+
+/* V1 */
+#define RNG_V1_CTRL				0x0008
+#define  RNG_V1_CTRL_START			(1 << 8)
+#define RNG_V1_CTRL2				0x0200
+#define  RNG_V1_CTRL2_OSC_ENABLE		(1 << 16)
+#define RNG_V1_DATA0				0x0204
+
+/* True Random Number Generator (TRNG) */
+#define RNG_V2_RST_CTL				0x0004
+#define  RNG_V2_RST_CTL_SW_RNG_RESET		(0x1U << 1)
+#define RNG_V2_CTL				0x0400
+#define  RNG_V2_CTL_RNG_START			(0x1U << 0)
+#define  RNG_V2_CTL_RNG_ENABLE			(0x1U << 1)
+#define  RNG_V2_CTL_RING_SEL_MASK		(0x3U << 2)
+#define  RNG_V2_CTL_RING_SEL_SLOWEST		(0x0U << 2)
+#define  RNG_V2_CTL_RING_SEL_SLOW		(0x1U << 2)
+#define  RNG_V2_CTL_RING_SEL_FAST		(0x2U << 2)
+#define  RNG_V2_CTL_RING_SEL_FASTEST		(0x3U << 2)
+#define  RNG_V2_CTL_RNG_LEN_MASK		(0x3U << 4)
+#define  RNG_V2_CTL_RNG_LEN_64			(0x0U << 4)
+#define  RNG_V2_CTL_RNG_LEN_128			(0x1U << 4)
+#define  RNG_V2_CTL_RNG_LEN_192			(0x2U << 4)
+#define  RNG_V2_CTL_RNG_LEN_256			(0x3U << 4)
+#define RNG_V2_SAMPLE_CNT			0x0404
+#define RNG_V2_DATA0				0x0410
+
+#define	RNG_DWORDS		8
+#define	RNG_CALLOUT_TICKS	(hz * 4)
+#define RNG_TIMEOUT_TICKS	100
+
+#define	WR4(_sc, _r, _v)	bus_write_4((_sc)->sc_res[0], (_r), (_v))
+#define	RD4(_sc, _r)		bus_read_4((_sc)->sc_res[0], (_r))
+
+#if 0
+ #define dprintf(dev, format, arg...)     device_printf(dev, "%s: " format, __func__, arg)
+#else
+ #define dprintf(dev, format, arg...)
+#endif
+
+static struct resource_spec rk_rng_spec[] = {
+        { SYS_RES_MEMORY,       0,      RF_ACTIVE },
+        { -1, 0 }
+};
+
+
+struct rk_rng_softc {
+	device_t		sc_dev;
+	struct resource 	*sc_res[1];
+	struct rk_rng_conf 	* sc_conf;
+	struct callout		sc_rngto;
+	uint8_t 		sc_pstat;
+	uint8_t 		sc_wdog;
+};
+
+struct rk_rng_conf {
+	unsigned int	ver;
+	const char	*name;
+	uint32_t	(*on)(struct rk_rng_softc *sc);
+	void 		(*off)(struct rk_rng_softc *sc);
+	bus_size_t	data_out;
+};
+
+enum RK_RNG_PSTAT {
+	RK_RNG_STOPPED = 0,
+	RK_RNG_STARTING,
+	RK_RNG_STARTED
+	};
+
+static uint32_t		rk_rng_v1_on(struct rk_rng_softc *);
+static void		rk_rng_v1_off(struct rk_rng_softc *);
+
+static const struct rk_rng_conf rkv1_conf = {
+	.ver		= 1,
+	.name		= "Rockchip RNG v1",
+	.on		= rk_rng_v1_on,
+	.off		= rk_rng_v1_off,
+	.data_out	= RNG_V1_DATA0,
+};
+
+static uint32_t		rk_rng_v2_on(struct rk_rng_softc *);
+static void		rk_rng_v2_off(struct rk_rng_softc *);
+
+static const struct rk_rng_conf rkv2_conf = {
+	.ver		= 2,
+	.name		= "Rockchip RNG v2",
+	.on		= rk_rng_v2_on,
+	.off		= rk_rng_v2_off,
+	.data_out	= RNG_V2_DATA0,
+};
+
+static struct ofw_compat_data compat_data[] = {
+	{"rockchip,cryptov1-rng",		(uintptr_t)&rkv1_conf},
+	{"rockchip,cryptov2-rng",		(uintptr_t)&rkv2_conf},
+	{NULL,				0}
+};
+
+static uint32_t
+rk_rng_v1_on(struct rk_rng_softc *sc)
+{
+	if(sc->sc_pstat == RK_RNG_STOPPED) {
+		WR4(sc, RNG_V1_CTRL2, RNG_V1_CTRL2_OSC_ENABLE |100);
+		WR4(sc, RNG_V1_CTRL, (RNG_V1_CTRL_START << 16) | RNG_V1_CTRL_START);
+		sc->sc_pstat = RK_RNG_STARTING;
+		} else {
+		sc->sc_pstat = (RD4(sc, RNG_V1_CTRL) & RNG_V1_CTRL_START) ?
+			RK_RNG_STARTING : RK_RNG_STARTED;
+	}
+	return (sc->sc_pstat);
+}
+
+
+
+static void
+rk_rng_v1_off(struct rk_rng_softc *sc)
+{
+	WR4(sc, RNG_V1_CTRL, (RNG_V1_CTRL_START << 16) | 0);
+	sc->sc_pstat = RK_RNG_STOPPED;
+}
+
+static uint32_t
+rk_rng_v2_on(struct rk_rng_softc *sc)
+{
+	 uint32_t mask, val;
+
+	 if(sc->sc_pstat == RK_RNG_STOPPED) {
+		mask = RNG_V2_CTL_RNG_START | RNG_V2_CTL_RNG_ENABLE |
+		    RNG_V2_CTL_RING_SEL_MASK | RNG_V2_CTL_RNG_LEN_MASK;
+		val = RNG_V2_CTL_RNG_START | RNG_V2_CTL_RNG_ENABLE |
+		    RNG_V2_CTL_RING_SEL_SLOW | RNG_V2_CTL_RNG_LEN_256;
+		    WR4(sc, RNG_V2_SAMPLE_CNT, 100);
+		    WR4(sc, RNG_V2_CTL, (mask << 16) | val);
+		    sc->sc_pstat = RK_RNG_STARTING;
+	 } else {
+                sc->sc_pstat = (RD4(sc, RNG_V2_CTL) & RNG_V2_CTL_RNG_START) ?
+                        RK_RNG_STARTING : RK_RNG_STARTED;
+
+	 }
+	 return (sc->sc_pstat);
+}
+
+
+static void
+rk_rng_v2_off(struct rk_rng_softc *sc)
+{
+	uint32_t val = RNG_V2_CTL_RNG_START | RNG_V2_CTL_RNG_ENABLE;
+
+	WR4(sc, RNG_V2_CTL, (val << 16) | 0);
+	sc->sc_pstat = RK_RNG_STOPPED;
+}
+
+
+
+static void
+rk_rng_harvest(void *arg)
+{
+	struct rk_rng_softc *sc = arg;
+	uint32_t reg, buf[RNG_DWORDS];
+
+	if(sc->sc_wdog > RNG_TIMEOUT_TICKS) {
+		sc->sc_wdog = 0;
+		dprintf(sc->sc_dev, "watchdog timeout after %d tries\n", RNG_TIMEOUT_TICKS);
+		goto out;
+	}
+
+	if(sc->sc_conf->on(sc) != RK_RNG_STARTED) {
+		sc->sc_wdog++;
+		callout_reset_sbt(&sc->sc_rngto, SBT_1US * 100, 0, rk_rng_harvest, sc, 0);
+		return;
+	}
+
+	for(reg = 0; reg < RNG_DWORDS;reg++) {
+		buf[reg] = RD4(sc, (sc->sc_conf->data_out + (reg << 2)));
+		dprintf(sc->sc_dev, "reg[%d] => %08x\n", reg, buf[reg]);
+	}
+	random_harvest_queue(buf, sizeof(buf), RANDOM_PURE_BROADCOM);
+
+out:
+	sc->sc_wdog = 0;
+	sc->sc_conf->off(sc);
+	callout_reset(&sc->sc_rngto, RNG_CALLOUT_TICKS, rk_rng_harvest, sc);
+}
+
+
+static int
+rk_rng_probe(device_t dev)
+{
+	struct rk_rng_conf *compat;
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	compat = (struct rk_rng_conf *) ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+	if (!compat)
+		return (ENXIO);
+
+	device_set_desc(dev, compat->name);
+
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+rk_rng_attach(device_t dev)
+{
+	struct rk_rng_softc *sc;
+	int error = 0, i, ncells;
+	clk_t clk;
+	hwreset_t hwreset = NULL;
+	phandle_t node;
+
+	sc = device_get_softc(dev);
+	sc->sc_dev = dev;
+
+	sc->sc_conf = (struct rk_rng_conf *)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+	if(!sc->sc_conf) {
+		error = ENXIO;
+		goto fail;
+	}
+
+        if (bus_alloc_resources(dev, rk_rng_spec, sc->sc_res) != 0) {
+                device_printf(dev, "cannot allocate resources for device\n");
+                error = ENXIO;
+                goto fail;
+        }
+
+        node = ofw_bus_get_node(dev);
+
+	if (OF_hasprop(node, "assigned-clocks")) {
+		error = clk_set_assigned(dev, node);
+		if(error) {
+			device_printf(dev, "Could not set assigned clocks\n");
+			goto fail;
+		}
+	}
+
+        error = ofw_bus_parse_xref_list_get_length(node, "clocks",
+            "#clock-cells", &ncells);
+        if (error != 0 || ncells < 2) {
+                device_printf(dev, "couldn't find enough clocks\n");
+                error = ENXIO;
+                goto fail;
+        }
+
+	for(i = 0;i < ncells;i++) {
+		error = clk_get_by_ofw_index(dev, node, i, &clk);
+		if(error) {
+			device_printf(dev, "Could not get clock #%d\n", i);
+			error = ENXIO;
+			goto fail;
+		 }
+
+		error = clk_enable(clk);
+		if(error) {
+			device_printf(dev, "Could not enable clock #%s\n", clk_get_name(clk));
+			error = ENXIO;
+			goto fail;
+		 }
+	}
+
+	error = hwreset_get_by_ofw_name(dev, node, "reset", &hwreset);
+        if (error != 0 && error != ENOENT) {
+                device_printf(dev, "Cannot get 'reset' reset\n");
+                error = ENXIO;
+               /* goto fail;*/
+        }
+
+        if (hwreset != NULL) {
+                error = hwreset_deassert(hwreset);
+                if (error != 0) {
+                        device_printf(dev, "Cannot deassert reset\n");
+                        goto fail;
+                }
+        }
+
+	/* Initialize callout */
+	callout_init(&sc->sc_rngto, CALLOUT_MPSAFE);
+	callout_reset(&sc->sc_rngto, hz, rk_rng_harvest, sc);
+
+	return (0);
+fail:
+	return (error);
+}
+
+static int
+rk_rng_detach(device_t dev)
+{
+	struct rk_rng_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	/* Stop the RNG */
+	if(sc->sc_pstat != RK_RNG_STOPPED)
+		sc->sc_conf->off(sc);
+	/* Drain the callout it */
+	callout_drain(&sc->sc_rngto);
+
+	/* Release memory resource */
+	if (sc->sc_res[0] != NULL)
+		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_res[0]);
+
+	return (0);
+}
+
+static device_method_t rk_rng_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		rk_rng_probe),
+	DEVMETHOD(device_attach,	rk_rng_attach),
+	DEVMETHOD(device_detach,	rk_rng_detach),
+
+	DEVMETHOD_END
+};
+
+static driver_t rk_rng_driver = {
+	"rkrng",
+	rk_rng_methods,
+	sizeof(struct rk_rng_softc)
+};
+
+DRIVER_MODULE(rk_rng, simplebus, rk_rng_driver, 0, 0);
+DRIVER_MODULE(rk_rng, ofwbus, rk_rng_driver, 0, 0);
+MODULE_VERSION(rk_rng, 1);
+MODULE_DEPEND(rk_rng, randomdev, 1, 1, 1);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.