[PATCH v2] i.MX: add an emulation for RNGC

Martin Kaiser posted 1 patch 4 years, 3 months ago
Test asan failed
Test checkpatch passed
Test FreeBSD passed
Test docker-mingw@fedora passed
Test docker-clang@ubuntu failed
Test docker-quick@centos7 passed
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20200108084604.28173-1-martin@kaiser.cx
Maintainers: Peter Chubb <peter.chubb@nicta.com.au>, Peter Maydell <peter.maydell@linaro.org>, Jean-Christophe Dubois <jcd@tribudubois.net>
hw/arm/fsl-imx25.c         |  11 ++
hw/misc/Makefile.objs      |   1 +
hw/misc/imx_rngc.c         | 278 +++++++++++++++++++++++++++++++++++++
include/hw/arm/fsl-imx25.h |   5 +
include/hw/misc/imx_rngc.h |  35 +++++
5 files changed, 330 insertions(+)
create mode 100644 hw/misc/imx_rngc.c
create mode 100644 include/hw/misc/imx_rngc.h
[PATCH v2] i.MX: add an emulation for RNGC
Posted by Martin Kaiser 4 years, 3 months ago
Add an emulation for the RNGC random number generator and the compatible
RNGB variant. These peripherals are included (at least) in imx25 and
imx35 chipsets.

The emulation supports the initial self test, reseeding the prng and
reading random numbers.

Signed-off-by: Martin Kaiser <martin@kaiser.cx>
---
changes in v2
 add a VMStateDescription
 rename __imx_rngc_reset to imx_rngc_do_reset
 fix a typo

 hw/arm/fsl-imx25.c         |  11 ++
 hw/misc/Makefile.objs      |   1 +
 hw/misc/imx_rngc.c         | 278 +++++++++++++++++++++++++++++++++++++
 include/hw/arm/fsl-imx25.h |   5 +
 include/hw/misc/imx_rngc.h |  35 +++++
 5 files changed, 330 insertions(+)
 create mode 100644 hw/misc/imx_rngc.c
 create mode 100644 include/hw/misc/imx_rngc.h

diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c
index 3cb5a8fdfd..da3471b395 100644
--- a/hw/arm/fsl-imx25.c
+++ b/hw/arm/fsl-imx25.c
@@ -62,6 +62,9 @@ static void fsl_imx25_init(Object *obj)
 
     sysbus_init_child_obj(obj, "fec", &s->fec, sizeof(s->fec), TYPE_IMX_FEC);
 
+    sysbus_init_child_obj(obj, "rngc", &s->rngc, sizeof(s->rngc),
+                          TYPE_IMX_RNGC);
+
     for (i = 0; i < FSL_IMX25_NUM_I2CS; i++) {
         sysbus_init_child_obj(obj, "i2c[*]", &s->i2c[i], sizeof(s->i2c[i]),
                               TYPE_IMX_I2C);
@@ -188,6 +191,14 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp)
     sysbus_connect_irq(SYS_BUS_DEVICE(&s->fec), 0,
                        qdev_get_gpio_in(DEVICE(&s->avic), FSL_IMX25_FEC_IRQ));
 
+    object_property_set_bool(OBJECT(&s->rngc), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->rngc), 0, FSL_IMX25_RNGC_ADDR);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->rngc), 0,
+                       qdev_get_gpio_in(DEVICE(&s->avic), FSL_IMX25_RNGC_IRQ));
 
     /* Initialize all I2C */
     for (i = 0; i < FSL_IMX25_NUM_I2CS; i++) {
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index ba898a5781..2b28f8c096 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -42,6 +42,7 @@ common-obj-$(CONFIG_IMX) += imx7_ccm.o
 common-obj-$(CONFIG_IMX) += imx2_wdt.o
 common-obj-$(CONFIG_IMX) += imx7_snvs.o
 common-obj-$(CONFIG_IMX) += imx7_gpr.o
+common-obj-$(CONFIG_IMX) += imx_rngc.o
 common-obj-$(CONFIG_MILKYMIST) += milkymist-hpdmc.o
 common-obj-$(CONFIG_MILKYMIST) += milkymist-pfpu.o
 common-obj-$(CONFIG_MAINSTONE) += mst_fpga.o
diff --git a/hw/misc/imx_rngc.c b/hw/misc/imx_rngc.c
new file mode 100644
index 0000000000..4c270df2db
--- /dev/null
+++ b/hw/misc/imx_rngc.c
@@ -0,0 +1,278 @@
+/*
+ * Freescale i.MX RNGC emulation
+ *
+ * Copyright (C) 2020 Martin Kaiser <martin@kaiser.cx>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ * This driver provides the minimum functionality to initialize and seed
+ * an rngc and to read random numbers. The rngb that is found in imx25
+ * chipsets is also supported.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/main-loop.h"
+#include "qemu/module.h"
+#include "qemu/log.h"
+#include "qemu/guest-random.h"
+#include "hw/irq.h"
+#include "hw/misc/imx_rngc.h"
+#include "migration/vmstate.h"
+
+#define RNGC_NAME "i.MX RNGC"
+
+#define RNGC_VER_ID  0x00
+#define RNGC_COMMAND 0x04
+#define RNGC_CONTROL 0x08
+#define RNGC_STATUS  0x0C
+#define RNGC_FIFO    0x14
+
+/* These version info are reported by the rngb in an imx258 chip. */
+#define RNG_TYPE_RNGB 0x1
+#define V_MAJ 0x2
+#define V_MIN 0x40
+
+#define RNGC_CMD_BIT_SW_RST    0x40
+#define RNGC_CMD_BIT_CLR_ERR   0x20
+#define RNGC_CMD_BIT_CLR_INT   0x10
+#define RNGC_CMD_BIT_SEED      0x02
+#define RNGC_CMD_BIT_SELF_TEST 0x01
+
+#define RNGC_CTRL_BIT_MASK_ERR  0x40
+#define RNGC_CTRL_BIT_MASK_DONE 0x20
+#define RNGC_CTRL_BIT_AUTO_SEED 0x10
+
+/* the current status for self-test and seed operations */
+#define OP_IDLE 0
+#define OP_RUN  1
+#define OP_DONE 2
+
+static uint64_t imx_rngc_read(void *opaque, hwaddr offset, unsigned size)
+{
+    IMXRNGCState *s = IMX_RNGC(opaque);
+    uint64_t val = 0;
+
+    switch (offset) {
+    case RNGC_VER_ID:
+        val |= RNG_TYPE_RNGB << 28 | V_MAJ << 8 | V_MIN;
+        break;
+
+    case RNGC_COMMAND:
+        if (s->op_seed == OP_RUN) {
+            val |= RNGC_CMD_BIT_SEED;
+        }
+        if (s->op_self_test == OP_RUN) {
+            val |= RNGC_CMD_BIT_SELF_TEST;
+        }
+        break;
+
+    case RNGC_CONTROL:
+        /*
+         * The CTL_ACC and VERIF_MODE bits are not supported yet.
+         * They read as 0.
+         */
+        val |= s->mask;
+        if (s->auto_seed) {
+            val |= RNGC_CTRL_BIT_AUTO_SEED;
+        }
+        /*
+         * We don't have an internal fifo like the real hardware.
+         * There's no need for strategy to handle fifo underflows.
+         * We return the FIFO_UFLOW_RESPONSE bits as 0.
+         */
+        break;
+
+    case RNGC_STATUS:
+        /*
+         * We never report any statistics test or self-test errors or any
+         * other errors. STAT_TEST_PF, ST_PF and ERROR are always 0.
+         */
+
+        /*
+         * We don't have an internal fifo, see above. Therefore, we
+         * report back the default fifo size (5 32-bit words) and
+         * indicate that our fifo is always full.
+         */
+        val |= 5 << 12 | 5 << 8;
+
+        /* We always have a new seed available. */
+        val |= 1 << 6;
+
+        if (s->op_seed == OP_DONE) {
+            val |= 1 << 5;
+        }
+        if (s->op_self_test == OP_DONE) {
+            val |= 1 << 4;
+        }
+        if (s->op_seed == OP_RUN || s->op_self_test == OP_RUN) {
+            /*
+             * We're busy if self-test is running or if we're
+             * seeding the prng.
+             */
+            val |= 1 << 1;
+        } else {
+            /*
+             * We're ready to provide secure random numbers whenever
+             * we're not busy.
+             */
+            val |= 1;
+        }
+        break;
+
+    case RNGC_FIFO:
+        qemu_guest_getrandom_nofail(&val, sizeof(val));
+        break;
+    }
+
+    return val;
+}
+
+static void imx_rngc_do_reset(IMXRNGCState *s)
+{
+    s->op_self_test = OP_IDLE;
+    s->op_seed = OP_IDLE;
+    s->mask = 0;
+    s->auto_seed = false;
+}
+
+static void imx_rngc_write(void *opaque, hwaddr offset, uint64_t value,
+                           unsigned size)
+{
+    IMXRNGCState *s = IMX_RNGC(opaque);
+
+    switch (offset) {
+    case RNGC_COMMAND:
+        if (value & RNGC_CMD_BIT_SW_RST) {
+            imx_rngc_do_reset(s);
+        }
+
+        /*
+         * For now, both CLR_ERR and CLR_INT clear the interrupt. We
+         * don't report any errors yet.
+         */
+        if (value & (RNGC_CMD_BIT_CLR_ERR | RNGC_CMD_BIT_CLR_INT)) {
+            qemu_irq_lower(s->irq);
+        }
+
+        if (value & RNGC_CMD_BIT_SEED) {
+            s->op_seed = OP_RUN;
+            qemu_bh_schedule(s->seed_bh);
+        }
+
+        if (value & RNGC_CMD_BIT_SELF_TEST) {
+            s->op_self_test = OP_RUN;
+            qemu_bh_schedule(s->self_test_bh);
+        }
+        break;
+
+    case RNGC_CONTROL:
+        /*
+         * The CTL_ACC and VERIF_MODE bits are not supported yet.
+         * We ignore them if they're set by the caller.
+         */
+
+        if (value & RNGC_CTRL_BIT_MASK_ERR) {
+            s->mask |= RNGC_CTRL_BIT_MASK_ERR;
+        } else {
+            s->mask &= ~RNGC_CTRL_BIT_MASK_ERR;
+        }
+
+        if (value & RNGC_CTRL_BIT_MASK_DONE) {
+            s->mask |= RNGC_CTRL_BIT_MASK_DONE;
+        } else {
+            s->mask &= ~RNGC_CTRL_BIT_MASK_DONE;
+        }
+
+        if (value & RNGC_CTRL_BIT_AUTO_SEED) {
+            s->auto_seed = true;
+        } else {
+            s->auto_seed = false;
+        }
+        break;
+    }
+}
+
+static const MemoryRegionOps imx_rngc_ops = {
+    .read  = imx_rngc_read,
+    .write = imx_rngc_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void imx_rngc_self_test(void *opaque)
+{
+    IMXRNGCState *s = IMX_RNGC(opaque);
+
+    s->op_self_test = OP_DONE;
+    if (!(s->mask & RNGC_CTRL_BIT_MASK_DONE)) {
+        qemu_irq_raise(s->irq);
+    }
+}
+
+static void imx_rngc_seed(void *opaque)
+{
+    IMXRNGCState *s = IMX_RNGC(opaque);
+
+    s->op_seed = OP_DONE;
+    if (!(s->mask & RNGC_CTRL_BIT_MASK_DONE)) {
+        qemu_irq_raise(s->irq);
+    }
+}
+
+static void imx_rngc_realize(DeviceState *dev, Error **errp)
+{
+    IMXRNGCState *s = IMX_RNGC(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+    memory_region_init_io(&s->iomem, OBJECT(s), &imx_rngc_ops, s,
+                          TYPE_IMX_RNGC, 0x1000);
+    sysbus_init_mmio(sbd, &s->iomem);
+
+    sysbus_init_irq(sbd, &s->irq);
+    s->self_test_bh = qemu_bh_new(imx_rngc_self_test, s);
+    s->seed_bh = qemu_bh_new(imx_rngc_seed, s);
+}
+
+static void imx_rngc_reset(DeviceState *dev)
+{
+    IMXRNGCState *s = IMX_RNGC(dev);
+
+    imx_rngc_do_reset(s);
+}
+
+static const VMStateDescription vmstate_imx_rngc = {
+    .name = RNGC_NAME,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT8(op_self_test, IMXRNGCState),
+        VMSTATE_UINT8(op_seed, IMXRNGCState),
+        VMSTATE_UINT8(mask, IMXRNGCState),
+        VMSTATE_BOOL(auto_seed, IMXRNGCState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void imx_rngc_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = imx_rngc_realize;
+    dc->reset = imx_rngc_reset;
+    dc->desc = RNGC_NAME,
+    dc->vmsd = &vmstate_imx_rngc;
+}
+
+static const TypeInfo imx_rngc_info = {
+    .name          = TYPE_IMX_RNGC,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(IMXRNGCState),
+    .class_init    = imx_rngc_class_init,
+};
+
+static void imx_rngc_register_types(void)
+{
+    type_register_static(&imx_rngc_info);
+}
+
+type_init(imx_rngc_register_types)
diff --git a/include/hw/arm/fsl-imx25.h b/include/hw/arm/fsl-imx25.h
index 241efb52ae..1c86bb55fb 100644
--- a/include/hw/arm/fsl-imx25.h
+++ b/include/hw/arm/fsl-imx25.h
@@ -24,6 +24,7 @@
 #include "hw/timer/imx_gpt.h"
 #include "hw/timer/imx_epit.h"
 #include "hw/net/imx_fec.h"
+#include "hw/misc/imx_rngc.h"
 #include "hw/i2c/imx_i2c.h"
 #include "hw/gpio/imx_gpio.h"
 #include "exec/memory.h"
@@ -50,6 +51,7 @@ typedef struct FslIMX25State {
     IMXGPTState    gpt[FSL_IMX25_NUM_GPTS];
     IMXEPITState   epit[FSL_IMX25_NUM_EPITS];
     IMXFECState    fec;
+    IMXRNGCState   rngc;
     IMXI2CState    i2c[FSL_IMX25_NUM_I2CS];
     IMXGPIOState   gpio[FSL_IMX25_NUM_GPIOS];
     MemoryRegion   rom[2];
@@ -211,6 +213,8 @@ typedef struct FslIMX25State {
 #define FSL_IMX25_GPIO4_SIZE    0x4000
 #define FSL_IMX25_GPIO3_ADDR    0x53FA4000
 #define FSL_IMX25_GPIO3_SIZE    0x4000
+#define FSL_IMX25_RNGC_ADDR     0x53FB0000
+#define FSL_IMX25_RNGC_SIZE     0x4000
 #define FSL_IMX25_GPIO1_ADDR    0x53FCC000
 #define FSL_IMX25_GPIO1_SIZE    0x4000
 #define FSL_IMX25_GPIO2_ADDR    0x53FD0000
@@ -238,6 +242,7 @@ typedef struct FslIMX25State {
 #define FSL_IMX25_EPIT1_IRQ     28
 #define FSL_IMX25_EPIT2_IRQ     27
 #define FSL_IMX25_FEC_IRQ       57
+#define FSL_IMX25_RNGC_IRQ      22
 #define FSL_IMX25_I2C1_IRQ      3
 #define FSL_IMX25_I2C2_IRQ      4
 #define FSL_IMX25_I2C3_IRQ      10
diff --git a/include/hw/misc/imx_rngc.h b/include/hw/misc/imx_rngc.h
new file mode 100644
index 0000000000..f0d2b44d4f
--- /dev/null
+++ b/include/hw/misc/imx_rngc.h
@@ -0,0 +1,35 @@
+/*
+ * Freescale i.MX RNGC emulation
+ *
+ * Copyright (C) 2020 Martin Kaiser <martin@kaiser.cx>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef IMX_RNGC_H
+#define IMX_RNGC_H
+
+#include "hw/sysbus.h"
+
+#define TYPE_IMX_RNGC "imx.rngc"
+#define IMX_RNGC(obj) OBJECT_CHECK(IMXRNGCState, (obj), TYPE_IMX_RNGC)
+
+typedef struct IMXRNGCState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    MemoryRegion  iomem;
+
+    uint8_t op_self_test;
+    uint8_t op_seed;
+    uint8_t mask;
+    bool    auto_seed;
+
+    QEMUBH *self_test_bh;
+    QEMUBH *seed_bh;
+    qemu_irq irq;
+} IMXRNGCState;
+
+#endif /* IMX_RNGC_H */
-- 
2.20.1


Re: [PATCH v2] i.MX: add an emulation for RNGC
Posted by Peter Maydell 4 years, 3 months ago
On Wed, 8 Jan 2020 at 08:46, Martin Kaiser <martin@kaiser.cx> wrote:
>
> Add an emulation for the RNGC random number generator and the compatible
> RNGB variant. These peripherals are included (at least) in imx25 and
> imx35 chipsets.
>
> The emulation supports the initial self test, reseeding the prng and
> reading random numbers.
>
> Signed-off-by: Martin Kaiser <martin@kaiser.cx>
> ---
> changes in v2
>  add a VMStateDescription
>  rename __imx_rngc_reset to imx_rngc_do_reset
>  fix a typo



Applied to target-arm.next, thanks.

-- PMM