This commit implements the True Random Number
Generator for the MAX78000
Signed-off-by: Jackson Donaldson <jcksn@duck.com>
---
hw/arm/Kconfig | 1 +
hw/misc/Kconfig | 3 +
hw/misc/max78000_gcr.c | 6 ++
hw/misc/max78000_trng.c | 139 ++++++++++++++++++++++++++++++++
hw/misc/meson.build | 1 +
include/hw/misc/max78000_gcr.h | 1 +
include/hw/misc/max78000_trng.h | 35 ++++++++
7 files changed, 186 insertions(+)
create mode 100644 hw/misc/max78000_trng.c
create mode 100644 include/hw/misc/max78000_trng.h
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 41bb64458f..fcac62be6f 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -369,6 +369,7 @@ config MAX78000_SOC
select MAX78000_ICC
select MAX78000_UART
select MAX78000_GCR
+ select MAX78000_TRNG
config RASPI
bool
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index fde2266b8f..dd6a6e54da 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -53,6 +53,9 @@ config MAX78000_GCR
config MAX78000_ICC
bool
+config MAX78000_TRNG
+ bool
+
config MOS6522
bool
diff --git a/hw/misc/max78000_gcr.c b/hw/misc/max78000_gcr.c
index 8c282f3916..5916ee615a 100644
--- a/hw/misc/max78000_gcr.c
+++ b/hw/misc/max78000_gcr.c
@@ -14,6 +14,7 @@
#include "migration/vmstate.h"
#include "hw/qdev-properties.h"
#include "hw/char/max78000_uart.h"
+#include "hw/misc/max78000_trng.h"
#include "hw/misc/max78000_gcr.h"
@@ -157,6 +158,9 @@ static void max78000_gcr_write(void *opaque, hwaddr addr,
if (val & UART0_RESET) {
device_cold_reset(s->uart0);
}
+ if (val & TRNG_RESET) {
+ device_cold_reset(s->trng);
+ }
/* TODO: As other devices are implemented, add them here */
break;
@@ -257,6 +261,8 @@ static const Property max78000_gcr_properties[] = {
TYPE_MAX78000_UART, DeviceState*),
DEFINE_PROP_LINK("uart2", Max78000GcrState, uart2,
TYPE_MAX78000_UART, DeviceState*),
+ DEFINE_PROP_LINK("trng", Max78000GcrState, trng,
+ TYPE_MAX78000_TRNG, DeviceState*),
};
static const MemoryRegionOps max78000_gcr_ops = {
diff --git a/hw/misc/max78000_trng.c b/hw/misc/max78000_trng.c
new file mode 100644
index 0000000000..ecdaef53b6
--- /dev/null
+++ b/hw/misc/max78000_trng.c
@@ -0,0 +1,139 @@
+/*
+ * MAX78000 True Random Number Generator
+ *
+ * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "trace.h"
+#include "hw/irq.h"
+#include "migration/vmstate.h"
+#include "hw/misc/max78000_trng.h"
+#include "qemu/guest-random.h"
+
+static uint64_t max78000_trng_read(void *opaque, hwaddr addr,
+ unsigned int size)
+{
+ uint32_t data;
+
+ Max78000TrngState *s = opaque;
+ switch (addr) {
+ case CTRL:
+ return s->ctrl;
+
+ case STATUS:
+ return 1;
+
+ case DATA:
+ /*
+ * When interrupts are enabled, reading random data should cause a
+ * new interrupt to be generated; since there's always a random number
+ * available, we could qemu_set_irq(s->irq, s->ctrl & RND_IE). Because
+ * of how trng_write is set up, this is always a noop, so don't
+ */
+ qemu_guest_getrandom_nofail(&data, sizeof(data));
+ return data;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"
+ HWADDR_PRIx "\n", __func__, addr);
+ break;
+ }
+ return 0;
+}
+
+static void max78000_trng_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ Max78000TrngState *s = opaque;
+ uint32_t val = val64;
+ switch (addr) {
+ case CTRL:
+ /* TODO: implement AES keygen */
+ s->ctrl = val;
+
+ /*
+ * This device models random number generation as taking 0 time.
+ * A new random number is always available, so the condition for the
+ * RND interrupt is always fulfilled; we can just set irq to 1.
+ */
+ if (val & RND_IE) {
+ qemu_set_irq(s->irq, 1);
+ } else{
+ qemu_set_irq(s->irq, 0);
+ }
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"
+ HWADDR_PRIx "\n", __func__, addr);
+ break;
+ }
+}
+
+static void max78000_trng_reset_hold(Object *obj, ResetType type)
+{
+ Max78000TrngState *s = MAX78000_TRNG(obj);
+ s->ctrl = 0;
+ s->status = 0;
+ s->data = 0;
+}
+
+static const MemoryRegionOps max78000_trng_ops = {
+ .read = max78000_trng_read,
+ .write = max78000_trng_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+};
+
+static const VMStateDescription max78000_trng_vmstate = {
+ .name = TYPE_MAX78000_TRNG,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT32(ctrl, Max78000TrngState),
+ VMSTATE_UINT32(status, Max78000TrngState),
+ VMSTATE_UINT32(data, Max78000TrngState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void max78000_trng_init(Object *obj)
+{
+ Max78000TrngState *s = MAX78000_TRNG(obj);
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
+
+ memory_region_init_io(&s->mmio, obj, &max78000_trng_ops, s,
+ TYPE_MAX78000_TRNG, 0x1000);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+
+}
+
+static void max78000_trng_class_init(ObjectClass *klass, const void *data)
+{
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ rc->phases.hold = max78000_trng_reset_hold;
+ dc->vmsd = &max78000_trng_vmstate;
+
+}
+
+static const TypeInfo max78000_trng_info = {
+ .name = TYPE_MAX78000_TRNG,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Max78000TrngState),
+ .instance_init = max78000_trng_init,
+ .class_init = max78000_trng_class_init,
+};
+
+static void max78000_trng_register_types(void)
+{
+ type_register_static(&max78000_trng_info);
+}
+
+type_init(max78000_trng_register_types)
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 283d06dad4..c7c57d924b 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -72,6 +72,7 @@ system_ss.add(when: 'CONFIG_IMX', if_true: files(
))
system_ss.add(when: 'CONFIG_MAX78000_GCR', if_true: files('max78000_gcr.c'))
system_ss.add(when: 'CONFIG_MAX78000_ICC', if_true: files('max78000_icc.c'))
+system_ss.add(when: 'CONFIG_MAX78000_TRNG', if_true: files('max78000_trng.c'))
system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files(
'npcm_clk.c',
'npcm_gcr.c',
diff --git a/include/hw/misc/max78000_gcr.h b/include/hw/misc/max78000_gcr.h
index f04c8a3ee7..23ddf0885b 100644
--- a/include/hw/misc/max78000_gcr.h
+++ b/include/hw/misc/max78000_gcr.h
@@ -123,6 +123,7 @@ struct Max78000GcrState {
DeviceState *uart0;
DeviceState *uart1;
DeviceState *uart2;
+ DeviceState *trng;
};
diff --git a/include/hw/misc/max78000_trng.h b/include/hw/misc/max78000_trng.h
new file mode 100644
index 0000000000..c5a8129b6a
--- /dev/null
+++ b/include/hw/misc/max78000_trng.h
@@ -0,0 +1,35 @@
+/*
+ * MAX78000 True Random Number Generator
+ *
+ * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef HW_MAX78000_TRNG_H
+#define HW_MAX78000_TRNG_H
+
+#include "hw/sysbus.h"
+#include "qom/object.h"
+
+#define TYPE_MAX78000_TRNG "max78000-trng"
+OBJECT_DECLARE_SIMPLE_TYPE(Max78000TrngState, MAX78000_TRNG)
+
+#define CTRL 0
+#define STATUS 4
+#define DATA 8
+
+#define RND_IE (1 << 1)
+
+struct Max78000TrngState {
+ SysBusDevice parent_obj;
+
+ MemoryRegion mmio;
+
+ uint32_t ctrl;
+ uint32_t status;
+ uint32_t data;
+
+ qemu_irq irq;
+};
+
+#endif
--
2.34.1
On Fri, 4 Jul 2025 at 17:57, Jackson Donaldson <jackson88044@gmail.com> wrote:
>
> This commit implements the True Random Number
> Generator for the MAX78000
>
> Signed-off-by: Jackson Donaldson <jcksn@duck.com>
> ---
> +
> +static uint64_t max78000_trng_read(void *opaque, hwaddr addr,
> + unsigned int size)
> +{
> + uint32_t data;
> +
> + Max78000TrngState *s = opaque;
> + switch (addr) {
> + case CTRL:
> + return s->ctrl;
> +
> + case STATUS:
> + return 1;
> +
> + case DATA:
> + /*
> + * When interrupts are enabled, reading random data should cause a
> + * new interrupt to be generated; since there's always a random number
> + * available, we could qemu_set_irq(s->irq, s->ctrl & RND_IE). Because
> + * of how trng_write is set up, this is always a noop, so don't
> + */
> + qemu_guest_getrandom_nofail(&data, sizeof(data));
> + return data;
> +
I guess this does work, but only because the M-profile
interrupt controller happens to be designed to not care
about the difference between edge triggered and level
triggered interrupts. With a different interrupt controller
you would need to model that reading from DATA causes
the interrupt line to briefly go low and then high again,
to give you the rising edge.
The hardware here seems weirdly designed -- the only
way to stop it from sitting there with the interrupt
asserted is to disable interrupts. But hardware designers
do weird things sometimes...
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
thanks
-- PMM
© 2016 - 2025 Red Hat, Inc.