Gilles Grimaud <[email protected]> writes:

> From: gilles grimaud <[email protected]>
>
> Model the RP2040 XOSC and ROSC register blocks and expose their clock 
> outputs. Use virtual time for the ROSC COUNT register and provide either QEMU 
> guest entropy or a machine-selectable deterministic seed for RANDOMBIT. Add 
> qtests for the ROSC register behavior and deterministic stream.
>
> Signed-off-by: gilles grimaud <[email protected]>
> ---
>  hw/arm/Kconfig                 |   2 +
>  hw/arm/raspi_pico.c            |  56 +++++
>  hw/arm/rp2040.c                |  14 +-
>  hw/misc/Kconfig                |   6 +
>  hw/misc/meson.build            |   2 +
>  hw/misc/rp2040_rosc.c          | 430 +++++++++++++++++++++++++++++++++
>  hw/misc/rp2040_xosc.c          | 237 ++++++++++++++++++
>  include/hw/arm/rp2040.h        |   4 +
>  include/hw/misc/rp2040_rosc.h  |  42 ++++
>  include/hw/misc/rp2040_xosc.h  |  33 +++
>  tests/qtest/meson.build        |   1 +
>  tests/qtest/rp2040-rosc-test.c | 146 +++++++++++
>  12 files changed, 971 insertions(+), 2 deletions(-)
>  create mode 100644 hw/misc/rp2040_rosc.c
>  create mode 100644 hw/misc/rp2040_xosc.c
>  create mode 100644 include/hw/misc/rp2040_rosc.h
>  create mode 100644 include/hw/misc/rp2040_xosc.h
>  create mode 100644 tests/qtest/rp2040-rosc-test.c
>
> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> index 89c479ab6d..b55a75fd5a 100644
> --- a/hw/arm/Kconfig
> +++ b/hw/arm/Kconfig
> @@ -379,10 +379,12 @@ config RP2040
>      select ARM_V7M
>      select PL011
>      select RP2040_NYI
> +    select RP2040_ROSC
>      select RP2040_SYSCFG
>      select RP2040_SYSINFO
>      select RP2040_TBMAN
>      select RP2040_VREG
> +    select RP2040_XOSC
>      select UNIMP
>  
>  config STM32F100_SOC
> diff --git a/hw/arm/raspi_pico.c b/hw/arm/raspi_pico.c
> index 2bb7d4e511..2a65176dc6 100644
> --- a/hw/arm/raspi_pico.c
> +++ b/hw/arm/raspi_pico.c
> @@ -14,6 +14,7 @@
>  #include "hw/arm/rp2040.h"
>  #include "hw/core/boards.h"
>  #include "hw/core/qdev-properties.h"
> +#include "qemu/cutils.h"
>  #include "system/address-spaces.h"
>  #include "system/system.h"
>  #include "qom/object.h"
> @@ -28,8 +29,43 @@ struct RaspiPicoMachineState {
>  
>      RP2040State soc;
>      MemoryRegion flash;
> +    char *rosc_random_seed;
> +    uint64_t rosc_random_seed_value;
> +    bool rosc_random_seed_set;
>  };
>  
> +static char *raspi_pico_get_rosc_random_seed(Object *obj, Error **errp)
> +{
> +    RaspiPicoMachineState *s = RASPI_PICO_MACHINE(obj);
> +
> +    return g_strdup(s->rosc_random_seed ?: "");
> +}
> +
> +static void raspi_pico_set_rosc_random_seed(Object *obj, const char *value,
> +                                            Error **errp)
> +{
> +    RaspiPicoMachineState *s = RASPI_PICO_MACHINE(obj);
> +    uint64_t seed;
> +
> +    if (!value || !*value) {
> +        g_free(s->rosc_random_seed);
> +        s->rosc_random_seed = NULL;
> +        s->rosc_random_seed_value = 0;
> +        s->rosc_random_seed_set = false;
> +        return;
> +    }
> +
> +    if (qemu_strtou64(value, NULL, 0, &seed) < 0) {
> +        error_setg(errp, "invalid ROSC random seed '%s'", value);
> +        return;
> +    }
> +
> +    g_free(s->rosc_random_seed);
> +    s->rosc_random_seed = g_strdup(value);

Why do you need to keep the string if it is simply a representation of a
uint64_t?

> +    s->rosc_random_seed_value = seed;
> +    s->rosc_random_seed_set = true;

This never gets looked at.

> +}
> +
>  static void raspi_pico_init(MachineState *machine)
>  {
>      RaspiPicoMachineState *s = RASPI_PICO_MACHINE(machine);
> @@ -38,6 +74,10 @@ static void raspi_pico_init(MachineState *machine)
>      object_initialize_child(OBJECT(machine), "soc", &s->soc, TYPE_RP2040);
>      qdev_prop_set_chr(DEVICE(&s->soc), "serial0", serial_hd(0));
>      qdev_prop_set_chr(DEVICE(&s->soc), "serial1", serial_hd(1));
> +    qdev_prop_set_uint64(DEVICE(&s->soc.rosc), "random-seed",
> +                         s->rosc_random_seed_value);
> +    qdev_prop_set_bit(DEVICE(&s->soc.rosc), "random-seed-set",
> +                      s->rosc_random_seed_set);
>      if (machine->firmware) {
>          qdev_prop_set_string(DEVICE(&s->soc), "bootrom-file",
>                               machine->firmware);
> @@ -55,6 +95,13 @@ static void raspi_pico_init(MachineState *machine)
>                         RP2040_XIP_BASE, PICO_FLASH_SIZE);
>  }
>  
> +static void raspi_pico_machine_finalize(Object *obj)
> +{
> +    RaspiPicoMachineState *s = RASPI_PICO_MACHINE(obj);
> +
> +    g_free(s->rosc_random_seed);
> +}
> +
>  static void raspi_pico_machine_class_init(ObjectClass *oc, const void *data)
>  {
>      MachineClass *mc = MACHINE_CLASS(oc);
> @@ -68,6 +115,14 @@ static void raspi_pico_machine_class_init(ObjectClass 
> *oc, const void *data)
>      mc->no_parallel = 1;
>      mc->no_floppy = 1;
>      mc->no_cdrom = 1;
> +
> +    object_class_property_add_str(oc, "rosc-random-seed",
> +                                  raspi_pico_get_rosc_random_seed,
> +                                  raspi_pico_set_rosc_random_seed);
> +    object_class_property_set_description(oc, "rosc-random-seed",
> +                                          "Use a deterministic seed for the "
> +                                          "ROSC RANDOMBIT stream; if unset, "
> +                                          "QEMU guest entropy is used");
>  }
>  
>  static const TypeInfo raspi_pico_machine_info = {
> @@ -75,6 +130,7 @@ static const TypeInfo raspi_pico_machine_info = {
>      .parent = TYPE_MACHINE,
>      .instance_size = sizeof(RaspiPicoMachineState),
>      .class_init = raspi_pico_machine_class_init,
> +    .instance_finalize = raspi_pico_machine_finalize,
>      .interfaces = arm_machine_interfaces,
>  };
>  
> diff --git a/hw/arm/rp2040.c b/hw/arm/rp2040.c
> index f2a6613aa3..889decded0 100644
> --- a/hw/arm/rp2040.c
> +++ b/hw/arm/rp2040.c
> @@ -57,7 +57,6 @@ static const struct {
>      { "rp2040.ioqspi",   0x40018000, 0x4000 },
>      { "rp2040.padsbank0", 0x4001c000, 0x4000 },
>      { "rp2040.padsqspi", 0x40020000, 0x4000 },
> -    { "rp2040.xosc",     0x40024000, 0x4000 },
>      { "rp2040.pll_sys",  0x40028000, 0x4000 },
>      { "rp2040.pll_usb",  0x4002c000, 0x4000 },
>      { "rp2040.busctrl",  0x40030000, 0x4000 },
> @@ -72,7 +71,6 @@ static const struct {
>      { "rp2040.timer",    0x40054000, 0x4000 },
>      { "rp2040.watchdog", 0x40058000, 0x4000 },
>      { "rp2040.rtc",      0x4005c000, 0x4000 },
> -    { "rp2040.rosc",     0x40060000, 0x4000 },
>      { "rp2040.dma",      0x50000000, 0x1000 },
>      { "rp2040.usbctrl_dpram", 0x50100000, 0x10000 },
>      { "rp2040.usbctrl_regs",  0x50110000, 0x10000 },
> @@ -177,8 +175,10 @@ static void rp2040_soc_init(Object *obj)
>  
>      object_initialize_child(obj, "syscfg", &s->syscfg, TYPE_RP2040_SYSCFG);
>      object_initialize_child(obj, "sysinfo", &s->sysinfo, 
> TYPE_RP2040_SYSINFO);
> +    object_initialize_child(obj, "rosc", &s->rosc, TYPE_RP2040_ROSC);
>      object_initialize_child(obj, "tbman", &s->tbman, TYPE_RP2040_TBMAN);
>      object_initialize_child(obj, "vreg", &s->vreg, TYPE_RP2040_VREG);
> +    object_initialize_child(obj, "xosc", &s->xosc, TYPE_RP2040_XOSC);
>  
>      s->irq = qemu_allocate_irqs(rp2040_set_irq, s, RP2040_NUM_IRQS);
>      s->sysclk = clock_new(obj, "sysclk");
> @@ -325,6 +325,11 @@ static void rp2040_soc_realize(DeviceState *dev, Error 
> **errp)
>      sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysinfo), 0, RP2040_SYSINFO_BASE);
>      rp2040_syscfg_update(s);
>  
> +    if (!sysbus_realize(SYS_BUS_DEVICE(&s->rosc), errp)) {
> +        return;
> +    }
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->rosc), 0, RP2040_ROSC_BASE);
> +
>      if (!sysbus_realize(SYS_BUS_DEVICE(&s->tbman), errp)) {
>          return;
>      }
> @@ -335,6 +340,11 @@ static void rp2040_soc_realize(DeviceState *dev, Error 
> **errp)
>      }
>      sysbus_mmio_map(SYS_BUS_DEVICE(&s->vreg), 0, RP2040_VREG_BASE);
>  
> +    if (!sysbus_realize(SYS_BUS_DEVICE(&s->xosc), errp)) {
> +        return;
> +    }
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->xosc), 0, RP2040_XOSC_BASE);
> +
>      for (i = 0; i < ARRAY_SIZE(s->uart); i++) {
>          qdev_connect_clock_in(DEVICE(&s->uart[i]), "clk", s->sysclk);
>          if (!sysbus_realize(SYS_BUS_DEVICE(&s->uart[i]), errp)) {
> diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
> index 597559186f..8f0860139f 100644
> --- a/hw/misc/Kconfig
> +++ b/hw/misc/Kconfig
> @@ -104,6 +104,9 @@ config FSL_IMX8MP_CCM
>  config RP2040_NYI
>      bool
>  
> +config RP2040_ROSC
> +    bool
> +
>  config RP2040_SYSCFG
>      bool
>  
> @@ -116,6 +119,9 @@ config RP2040_TBMAN
>  config RP2040_VREG
>      bool
>  
> +config RP2040_XOSC
> +    bool
> +
>  config STM32_RCC
>      bool
>  
> diff --git a/hw/misc/meson.build b/hw/misc/meson.build
> index 9191b16360..7ad307beee 100644
> --- a/hw/misc/meson.build
> +++ b/hw/misc/meson.build
> @@ -99,10 +99,12 @@ system_ss.add(when: 'CONFIG_RASPI', if_true: files(
>    'bcm2835_powermgt.c',
>  ))
>  system_ss.add(when: 'CONFIG_RP2040_NYI', if_true: files('rp2040_nyi.c'))
> +system_ss.add(when: 'CONFIG_RP2040_ROSC', if_true: files('rp2040_rosc.c'))
>  system_ss.add(when: 'CONFIG_RP2040_SYSCFG', if_true: 
> files('rp2040_syscfg.c'))
>  system_ss.add(when: 'CONFIG_RP2040_SYSINFO', if_true: 
> files('rp2040_sysinfo.c'))
>  system_ss.add(when: 'CONFIG_RP2040_TBMAN', if_true: files('rp2040_tbman.c'))
>  system_ss.add(when: 'CONFIG_RP2040_VREG', if_true: files('rp2040_vreg.c'))
> +system_ss.add(when: 'CONFIG_RP2040_XOSC', if_true: files('rp2040_xosc.c'))
>  system_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_misc.c'))
>  system_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq_slcr.c'))
>  system_ss.add(when: 'CONFIG_XLNX_ZYNQ_DDRC', if_true: 
> files('xlnx-zynq-ddrc.c'))
> diff --git a/hw/misc/rp2040_rosc.c b/hw/misc/rp2040_rosc.c
> new file mode 100644
> index 0000000000..dcb340ec57
> --- /dev/null
> +++ b/hw/misc/rp2040_rosc.c
> @@ -0,0 +1,430 @@
> +/*
> + * RP2040 ring oscillator emulation
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +#include "hw/core/qdev-clock.h"
> +#include "hw/core/qdev-properties.h"
> +#include "hw/misc/rp2040_nyi.h"
> +#include "hw/misc/rp2040_rosc.h"
> +#include "migration/vmstate.h"
> +#include "qemu/guest-random.h"
> +#include "qemu/log.h"
> +#include "qemu/module.h"
> +#include "qemu/timer.h"
> +
> +#define ROSC_NOMINAL_HZ        6500000
> +
> +#define ROSC_CTRL              0x00
> +#define ROSC_FREQA             0x04
> +#define ROSC_FREQB             0x08
> +#define ROSC_DORMANT           0x0c
> +#define ROSC_DIV               0x10
> +#define ROSC_PHASE             0x14
> +#define ROSC_STATUS            0x18
> +#define ROSC_RANDOMBIT         0x1c
> +#define ROSC_COUNT             0x20
> +
> +#define ROSC_ENABLE_DISABLE    0xd1e
> +#define ROSC_ENABLE_ENABLE     0xfab
> +#define ROSC_FREQ_RANGE_LOW    0xfa4
> +#define ROSC_FREQ_RANGE_MEDIUM 0xfa5
> +#define ROSC_FREQ_RANGE_HIGH   0xfa7
> +#define ROSC_FREQ_RANGE_TOOHIGH 0xfa6
> +#define ROSC_DORMANT_VALUE     0x636f6d61
> +#define ROSC_WAKE_VALUE        0x77616b65
> +#define ROSC_FREQ_PASSWD       0x9696
> +#define ROSC_DIV_PASS          0xaa0
> +#define ROSC_PHASE_PASSWD      0xaa
> +
> +#define ROSC_STATUS_STABLE     BIT(31)
> +#define ROSC_STATUS_BADWRITE   BIT(24)
> +#define ROSC_STATUS_DIV_RUNNING BIT(16)
> +#define ROSC_STATUS_ENABLED    BIT(12)
> +
> +#define ATOMIC_ALIAS_MASK      0x3000
> +#define ATOMIC_XOR             0x1000
> +#define ATOMIC_SET             0x2000
> +#define ATOMIC_CLR             0x3000
> +
> +static uint32_t rp2040_rosc_apply_alias(uint32_t old, uint32_t value,
> +                                        hwaddr alias)
> +{
> +    switch (alias) {
> +    case ATOMIC_XOR:
> +        return old ^ value;
> +    case ATOMIC_SET:
> +        return old | value;
> +    case ATOMIC_CLR:
> +        return old & ~value;
> +    default:
> +        return value;
> +    }
> +}
> +
> +static bool rp2040_rosc_valid_range(uint32_t range)
> +{
> +    return range == ROSC_FREQ_RANGE_LOW || range == ROSC_FREQ_RANGE_MEDIUM ||
> +           range == ROSC_FREQ_RANGE_HIGH || range == ROSC_FREQ_RANGE_TOOHIGH;
> +}
> +
> +static bool rp2040_rosc_enabled(RP2040RoscState *s)
> +{
> +    return extract32(s->ctrl, 12, 12) != ROSC_ENABLE_DISABLE;
> +}
> +
> +static bool rp2040_rosc_running(RP2040RoscState *s)
> +{
> +    return rp2040_rosc_enabled(s) && s->dormant == ROSC_WAKE_VALUE;
> +}
> +
> +static uint32_t rp2040_rosc_divisor(RP2040RoscState *s)
> +{
> +    uint32_t encoded = s->div & 0xfff;
> +    uint32_t div;
> +
> +    if (encoded < ROSC_DIV_PASS) {
> +        return 31;
> +    }
> +
> +    div = encoded - ROSC_DIV_PASS;
> +    if (div == 0) {
> +        return 32;
> +    }
> +    if (div > 31) {
> +        return 31;
> +    }
> +    return div;
> +}
> +
> +static uint32_t rp2040_rosc_hz(RP2040RoscState *s)
> +{
> +    if (!rp2040_rosc_running(s)) {
> +        return 0;
> +    }
> +
> +    return ROSC_NOMINAL_HZ / rp2040_rosc_divisor(s);
> +}
> +
> +static void rp2040_rosc_update_clock(RP2040RoscState *s)
> +{
> +    clock_update_hz(s->clk, rp2040_rosc_hz(s));
> +}
> +
> +static uint32_t rp2040_rosc_count_read(RP2040RoscState *s)
> +{
> +    uint64_t elapsed_ns;
> +    uint64_t elapsed_ticks;
> +
> +    if (!s->count || !rp2040_rosc_running(s)) {
> +        return s->count;
> +    }
> +
> +    elapsed_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - s->count_start_ns;
> +    elapsed_ticks = elapsed_ns * ROSC_NOMINAL_HZ / NANOSECONDS_PER_SECOND;
> +    if (elapsed_ticks >= s->count) {
> +        s->count = 0;
> +    } else {
> +        s->count -= elapsed_ticks;
> +        s->count_start_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> +    }
> +
> +    return s->count;
> +}
> +
> +static uint32_t rp2040_rosc_status(RP2040RoscState *s)
> +{
> +    uint32_t value = 0;
> +
> +    if (rp2040_rosc_enabled(s)) {
> +        value |= ROSC_STATUS_ENABLED;
> +        if (s->dormant == ROSC_WAKE_VALUE) {
> +            value |= ROSC_STATUS_STABLE | ROSC_STATUS_DIV_RUNNING;
> +        }
> +    }
> +    if (s->badwrite) {
> +        value |= ROSC_STATUS_BADWRITE;
> +    }
> +
> +    return value;
> +}
> +
> +static uint32_t rp2040_rosc_random_bit(RP2040RoscState *s)
> +{
> +    uint64_t x;
> +    uint32_t bit;
> +
> +    if (!rp2040_rosc_running(s)) {
> +        return 0;
> +    }
> +
> +    if (!s->random_seed_set) {
> +        if (!s->random_pool_bits) {
> +            qemu_guest_getrandom_nofail(&s->random_pool,
> +                                        sizeof(s->random_pool));
> +            s->random_pool_bits = 64;
> +        }
> +
> +        bit = s->random_pool & 1;
> +        s->random_pool >>= 1;
> +        s->random_pool_bits--;
> +        return bit;
> +    }
> +
> +    x = s->random_prng_state;
> +    x ^= x >> 12;
> +    x ^= x << 25;
> +    x ^= x >> 27;
> +    s->random_prng_state = x;
> +    return (x * 0x2545f4914f6cdd1dull) >> 63;
> +}
> +
> +static uint64_t rp2040_rosc_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    RP2040RoscState *s = opaque;
> +    hwaddr offset = addr & 0xfff;
> +    uint64_t value;
> +
> +    switch (offset) {
> +    case ROSC_CTRL:
> +        value = s->ctrl;
> +        break;
> +    case ROSC_FREQA:
> +        value = s->freqa;
> +        break;
> +    case ROSC_FREQB:
> +        value = s->freqb;
> +        break;
> +    case ROSC_DORMANT:
> +        value = s->dormant;
> +        break;
> +    case ROSC_DIV:
> +        value = s->div;
> +        break;
> +    case ROSC_PHASE:
> +        value = s->phase;
> +        break;
> +    case ROSC_STATUS:
> +        value = rp2040_rosc_status(s);
> +        break;
> +    case ROSC_RANDOMBIT:
> +        value = rp2040_rosc_random_bit(s);
> +        break;
> +    case ROSC_COUNT:
> +        value = rp2040_rosc_count_read(s);
> +        break;
> +    default:
> +        value = 0;
> +        rp2040_log_unimplemented_read("rosc", size,
> +                                      RP2040_ROSC_BASE + addr, offset,
> +                                      value);
> +        break;
> +    }
> +
> +    return value;
> +}
> +
> +static void rp2040_rosc_write_ctrl(RP2040RoscState *s, uint32_t value)
> +{
> +    uint32_t enable = extract32(value, 12, 12);
> +    uint32_t range = extract32(value, 0, 12);
> +
> +    if (enable != ROSC_ENABLE_DISABLE && enable != ROSC_ENABLE_ENABLE) {
> +        s->badwrite = true;
> +        value = deposit32(value, 12, 12, ROSC_ENABLE_ENABLE);
> +    }
> +    if (!rp2040_rosc_valid_range(range)) {
> +        s->badwrite = true;
> +        value = deposit32(value, 0, 12, ROSC_FREQ_RANGE_LOW);
> +    }
> +
> +    s->ctrl = value & 0x00ffffff;
> +    rp2040_rosc_update_clock(s);
> +}
> +
> +static void rp2040_rosc_write_freq(RP2040RoscState *s, uint32_t *reg,
> +                                   uint32_t value)
> +{
> +    if (extract32(value, 16, 16) != ROSC_FREQ_PASSWD) {
> +        s->badwrite = true;
> +        *reg = 0;
> +        return;
> +    }
> +
> +    *reg = value & 0x96967777;
> +}
> +
> +static void rp2040_rosc_write_div(RP2040RoscState *s, uint32_t value)
> +{
> +    if ((value & 0xfff) < ROSC_DIV_PASS) {
> +        s->badwrite = true;
> +        value = ROSC_DIV_PASS + 31;
> +    }
> +
> +    s->div = value & 0xfff;
> +    rp2040_rosc_update_clock(s);
> +}
> +
> +static void rp2040_rosc_write_phase(RP2040RoscState *s, uint32_t value)
> +{
> +    if (extract32(value, 4, 8) != ROSC_PHASE_PASSWD) {
> +        s->badwrite = true;
> +        value = 0;
> +    }
> +
> +    s->phase = value & 0x00000fff;
> +}
> +
> +static void rp2040_rosc_write(void *opaque, hwaddr addr,
> +                              uint64_t value64, unsigned size)
> +{
> +    RP2040RoscState *s = opaque;
> +    hwaddr alias = addr & ATOMIC_ALIAS_MASK;
> +    hwaddr offset = addr & 0xfff;
> +    uint32_t value = value64;
> +
> +    switch (offset) {
> +    case ROSC_CTRL:
> +        rp2040_rosc_write_ctrl(s, rp2040_rosc_apply_alias(s->ctrl, value,
> +                                                          alias));
> +        break;
> +    case ROSC_FREQA:
> +        rp2040_rosc_write_freq(s, &s->freqa,
> +                               rp2040_rosc_apply_alias(s->freqa, value,
> +                                                       alias));
> +        break;
> +    case ROSC_FREQB:
> +        rp2040_rosc_write_freq(s, &s->freqb,
> +                               rp2040_rosc_apply_alias(s->freqb, value,
> +                                                       alias));
> +        break;
> +    case ROSC_DORMANT:
> +        s->dormant = value == ROSC_DORMANT_VALUE ? value : ROSC_WAKE_VALUE;
> +        if (value != ROSC_DORMANT_VALUE && value != ROSC_WAKE_VALUE) {
> +            s->badwrite = true;
> +        }
> +        rp2040_rosc_update_clock(s);
> +        break;
> +    case ROSC_DIV:
> +        rp2040_rosc_write_div(s, rp2040_rosc_apply_alias(s->div, value,
> +                                                         alias));
> +        break;
> +    case ROSC_PHASE:
> +        rp2040_rosc_write_phase(s, rp2040_rosc_apply_alias(s->phase, value,
> +                                                           alias));
> +        break;
> +    case ROSC_STATUS:
> +        if (value & ROSC_STATUS_BADWRITE) {
> +            s->badwrite = false;
> +        }
> +        break;
> +    case ROSC_COUNT:
> +        s->count = value & 0xff;
> +        s->count_start_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> +        break;
> +    default:
> +        rp2040_log_unimplemented_write("rosc", size,
> +                                       RP2040_ROSC_BASE + addr, offset,
> +                                       value64);
> +        break;
> +    }
> +}
> +
> +static const MemoryRegionOps rp2040_rosc_ops = {
> +    .read = rp2040_rosc_read,
> +    .write = rp2040_rosc_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    },
> +};
> +
> +static void rp2040_rosc_reset(DeviceState *dev)
> +{
> +    RP2040RoscState *s = RP2040_ROSC(dev);
> +
> +    s->ctrl = (ROSC_ENABLE_ENABLE << 12) | ROSC_FREQ_RANGE_LOW;
> +    s->freqa = 0;
> +    s->freqb = 0;
> +    s->dormant = ROSC_WAKE_VALUE;
> +    s->div = ROSC_DIV_PASS + 16;
> +    s->phase = 0x00000aa8;
> +    s->count = 0;
> +    s->count_start_ns = 0;
> +    s->random_pool = 0;
> +    s->random_pool_bits = 0;
> +    s->random_prng_state = s->random_seed;
> +    if (!s->random_prng_state) {
> +        s->random_prng_state = 0x9e3779b97f4a7c15ull;
> +    }
> +    s->badwrite = false;
> +
> +    rp2040_rosc_update_clock(s);
> +}
> +
> +static void rp2040_rosc_init(Object *obj)
> +{
> +    RP2040RoscState *s = RP2040_ROSC(obj);
> +    DeviceState *dev = DEVICE(obj);
> +
> +    s->clk = qdev_init_clock_out(dev, "clk");
> +    memory_region_init_io(&s->iomem, obj, &rp2040_rosc_ops, s,
> +                          "rp2040.rosc", RP2040_ROSC_SIZE);
> +    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
> +}
> +
> +static const VMStateDescription rp2040_rosc_vmstate = {
> +    .name = TYPE_RP2040_ROSC,
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .fields = (const VMStateField[]) {
> +        VMSTATE_UINT32(ctrl, RP2040RoscState),
> +        VMSTATE_UINT32(freqa, RP2040RoscState),
> +        VMSTATE_UINT32(freqb, RP2040RoscState),
> +        VMSTATE_UINT32(dormant, RP2040RoscState),
> +        VMSTATE_UINT32(div, RP2040RoscState),
> +        VMSTATE_UINT32(phase, RP2040RoscState),
> +        VMSTATE_UINT32(count, RP2040RoscState),
> +        VMSTATE_INT64(count_start_ns, RP2040RoscState),
> +        VMSTATE_UINT64(random_pool, RP2040RoscState),
> +        VMSTATE_UINT64(random_prng_state, RP2040RoscState),
> +        VMSTATE_UINT64(random_seed, RP2040RoscState),
> +        VMSTATE_UINT8(random_pool_bits, RP2040RoscState),
> +        VMSTATE_BOOL(random_seed_set, RP2040RoscState),
> +        VMSTATE_BOOL(badwrite, RP2040RoscState),
> +        VMSTATE_CLOCK(clk, RP2040RoscState),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static const Property rp2040_rosc_properties[] = {
> +    DEFINE_PROP_UINT64("random-seed", RP2040RoscState, random_seed, 0),
> +    DEFINE_PROP_BOOL("random-seed-set", RP2040RoscState, random_seed_set,
> +                     false),
> +};
> +
> +static void rp2040_rosc_class_init(ObjectClass *klass, const void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    device_class_set_legacy_reset(dc, rp2040_rosc_reset);
> +    dc->vmsd = &rp2040_rosc_vmstate;
> +    device_class_set_props(dc, rp2040_rosc_properties);
> +}
> +
> +static const TypeInfo rp2040_rosc_info = {
> +    .name          = TYPE_RP2040_ROSC,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(RP2040RoscState),
> +    .instance_init = rp2040_rosc_init,
> +    .class_init    = rp2040_rosc_class_init,
> +};
> +
> +static void rp2040_rosc_register_types(void)
> +{
> +    type_register_static(&rp2040_rosc_info);
> +}
> +type_init(rp2040_rosc_register_types)
> diff --git a/hw/misc/rp2040_xosc.c b/hw/misc/rp2040_xosc.c
> new file mode 100644
> index 0000000000..a8340ca0c9
> --- /dev/null
> +++ b/hw/misc/rp2040_xosc.c
> @@ -0,0 +1,237 @@
> +/*
> + * RP2040 crystal oscillator emulation
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +#include "hw/core/qdev-clock.h"
> +#include "hw/misc/rp2040_nyi.h"
> +#include "hw/misc/rp2040_xosc.h"
> +#include "migration/vmstate.h"
> +#include "qemu/log.h"
> +#include "qemu/module.h"
> +
> +#define XOSC_HZ             12000000
> +
> +#define XOSC_CTRL           0x00
> +#define XOSC_STATUS         0x04
> +#define XOSC_DORMANT        0x08
> +#define XOSC_STARTUP        0x0c
> +#define XOSC_COUNT          0x1c
> +
> +#define XOSC_ENABLE_DISABLE 0xd1e
> +#define XOSC_ENABLE_ENABLE  0xfab
> +#define XOSC_FREQ_1_15MHZ   0xaa0
> +#define XOSC_DORMANT_VALUE  0x636f6d61
> +#define XOSC_WAKE_VALUE     0x77616b65
> +
> +#define XOSC_STATUS_STABLE  BIT(31)
> +#define XOSC_STATUS_BADWRITE BIT(24)
> +#define XOSC_STATUS_ENABLED BIT(12)
> +
> +#define ATOMIC_ALIAS_MASK    0x3000
> +#define ATOMIC_XOR           0x1000
> +#define ATOMIC_SET           0x2000
> +#define ATOMIC_CLR           0x3000
> +
> +static bool rp2040_xosc_enabled(RP2040XoscState *s)
> +{
> +    return extract32(s->ctrl, 12, 12) != XOSC_ENABLE_DISABLE;
> +}
> +
> +static void rp2040_xosc_update_clock(RP2040XoscState *s)
> +{
> +    clock_update_hz(s->clk, rp2040_xosc_enabled(s) ? XOSC_HZ : 0);
> +}
> +
> +static uint32_t rp2040_xosc_status(RP2040XoscState *s)
> +{
> +    uint32_t value = 0;
> +
> +    if (rp2040_xosc_enabled(s)) {
> +        value |= XOSC_STATUS_ENABLED;
> +        if (s->dormant == XOSC_WAKE_VALUE) {
> +            value |= XOSC_STATUS_STABLE;
> +        }
> +    }
> +    if (s->badwrite) {
> +        value |= XOSC_STATUS_BADWRITE;
> +    }
> +
> +    return value;
> +}
> +
> +static uint64_t rp2040_xosc_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    RP2040XoscState *s = opaque;
> +    hwaddr offset = addr & 0xfff;
> +    uint64_t value;
> +
> +    switch (offset) {
> +    case XOSC_CTRL:
> +        value = s->ctrl;
> +        break;
> +    case XOSC_STATUS:
> +        value = rp2040_xosc_status(s);
> +        break;
> +    case XOSC_DORMANT:
> +        value = s->dormant;
> +        break;
> +    case XOSC_STARTUP:
> +        value = s->startup;
> +        break;
> +    case XOSC_COUNT:
> +        value = 0;
> +        break;
> +    default:
> +        value = 0;
> +        rp2040_log_unimplemented_read("xosc", size,
> +                                      RP2040_XOSC_BASE + addr, offset,
> +                                      value);
> +        break;
> +    }
> +
> +    return value;
> +}
> +
> +static uint32_t rp2040_xosc_apply_alias(uint32_t old, uint32_t value,
> +                                        hwaddr alias)
> +{
> +    switch (alias) {
> +    case ATOMIC_XOR:
> +        return old ^ value;
> +    case ATOMIC_SET:
> +        return old | value;
> +    case ATOMIC_CLR:
> +        return old & ~value;
> +    default:
> +        return value;
> +    }
> +}
> +
> +static void rp2040_xosc_write_ctrl(RP2040XoscState *s, uint32_t value)
> +{
> +    uint32_t enable = extract32(value, 12, 12);
> +    uint32_t freq_range = extract32(value, 0, 12);
> +
> +    if (freq_range != XOSC_FREQ_1_15MHZ ||
> +        (enable != XOSC_ENABLE_DISABLE && enable != XOSC_ENABLE_ENABLE)) {
> +        s->badwrite = true;
> +    }
> +
> +    if (enable != XOSC_ENABLE_DISABLE && enable != XOSC_ENABLE_ENABLE) {
> +        value = deposit32(value, 12, 12, XOSC_ENABLE_ENABLE);
> +    }
> +    value = deposit32(value, 0, 12, XOSC_FREQ_1_15MHZ);
> +
> +    s->ctrl = value;
> +    rp2040_xosc_update_clock(s);
> +}
> +
> +static void rp2040_xosc_write(void *opaque, hwaddr addr,
> +                              uint64_t value64, unsigned size)
> +{
> +    RP2040XoscState *s = opaque;
> +    hwaddr alias = addr & ATOMIC_ALIAS_MASK;
> +    hwaddr offset = addr & 0xfff;
> +    uint32_t value = value64;
> +
> +    switch (offset) {
> +    case XOSC_CTRL:
> +        rp2040_xosc_write_ctrl(s, rp2040_xosc_apply_alias(s->ctrl, value,
> +                                                          alias));
> +        break;
> +    case XOSC_STATUS:
> +        if (value & XOSC_STATUS_BADWRITE) {
> +            s->badwrite = false;
> +        }
> +        break;
> +    case XOSC_DORMANT:
> +        s->dormant = value == XOSC_DORMANT_VALUE ? value : XOSC_WAKE_VALUE;
> +        rp2040_xosc_update_clock(s);
> +        break;
> +    case XOSC_STARTUP:
> +        s->startup = value & 0x00103fff;
> +        break;
> +    case XOSC_COUNT:
> +        s->count = 0;
> +        break;
> +    default:
> +        rp2040_log_unimplemented_write("xosc", size,
> +                                       RP2040_XOSC_BASE + addr, offset,
> +                                       value64);
> +        break;
> +    }
> +}
> +
> +static const MemoryRegionOps rp2040_xosc_ops = {
> +    .read = rp2040_xosc_read,
> +    .write = rp2040_xosc_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    },
> +};
> +
> +static void rp2040_xosc_reset(DeviceState *dev)
> +{
> +    RP2040XoscState *s = RP2040_XOSC(dev);
> +
> +    s->ctrl = (XOSC_ENABLE_DISABLE << 12) | XOSC_FREQ_1_15MHZ;
> +    s->dormant = XOSC_WAKE_VALUE;
> +    s->startup = 0x000000c4;
> +    s->count = 0;
> +    s->badwrite = false;
> +
> +    rp2040_xosc_update_clock(s);
> +}
> +
> +static void rp2040_xosc_init(Object *obj)
> +{
> +    RP2040XoscState *s = RP2040_XOSC(obj);
> +    DeviceState *dev = DEVICE(obj);
> +
> +    s->clk = qdev_init_clock_out(dev, "clk");
> +    memory_region_init_io(&s->iomem, obj, &rp2040_xosc_ops, s,
> +                          "rp2040.xosc", RP2040_XOSC_SIZE);
> +    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
> +}
> +
> +static const VMStateDescription rp2040_xosc_vmstate = {
> +    .name = TYPE_RP2040_XOSC,
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .fields = (const VMStateField[]) {
> +        VMSTATE_UINT32(ctrl, RP2040XoscState),
> +        VMSTATE_UINT32(dormant, RP2040XoscState),
> +        VMSTATE_UINT32(startup, RP2040XoscState),
> +        VMSTATE_UINT32(count, RP2040XoscState),
> +        VMSTATE_BOOL(badwrite, RP2040XoscState),
> +        VMSTATE_CLOCK(clk, RP2040XoscState),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static void rp2040_xosc_class_init(ObjectClass *klass, const void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    device_class_set_legacy_reset(dc, rp2040_xosc_reset);
> +    dc->vmsd = &rp2040_xosc_vmstate;
> +}
> +
> +static const TypeInfo rp2040_xosc_info = {
> +    .name          = TYPE_RP2040_XOSC,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(RP2040XoscState),
> +    .instance_init = rp2040_xosc_init,
> +    .class_init    = rp2040_xosc_class_init,
> +};
> +
> +static void rp2040_xosc_register_types(void)
> +{
> +    type_register_static(&rp2040_xosc_info);
> +}
> +type_init(rp2040_xosc_register_types)
> diff --git a/include/hw/arm/rp2040.h b/include/hw/arm/rp2040.h
> index 86037dd0d7..47a8cb2236 100644
> --- a/include/hw/arm/rp2040.h
> +++ b/include/hw/arm/rp2040.h
> @@ -13,10 +13,12 @@
>  #include "hw/char/pl011.h"
>  #include "hw/core/clock.h"
>  #include "hw/core/sysbus.h"
> +#include "hw/misc/rp2040_rosc.h"
>  #include "hw/misc/rp2040_syscfg.h"
>  #include "hw/misc/rp2040_sysinfo.h"
>  #include "hw/misc/rp2040_tbman.h"
>  #include "hw/misc/rp2040_vreg.h"
> +#include "hw/misc/rp2040_xosc.h"
>  #include "qom/object.h"
>  
>  #define TYPE_RP2040 "rp2040"
> @@ -40,8 +42,10 @@ struct RP2040State {
>      PL011State uart[2];
>      RP2040SysCfgState syscfg;
>      RP2040SysInfoState sysinfo;
> +    RP2040RoscState rosc;
>      RP2040TbmanState tbman;
>      RP2040VregState vreg;
> +    RP2040XoscState xosc;
>  
>      MemoryRegion *board_memory;
>      MemoryRegion rom;
> diff --git a/include/hw/misc/rp2040_rosc.h b/include/hw/misc/rp2040_rosc.h
> new file mode 100644
> index 0000000000..6377704e22
> --- /dev/null
> +++ b/include/hw/misc/rp2040_rosc.h
> @@ -0,0 +1,42 @@
> +/*
> + * RP2040 ring oscillator emulation
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#ifndef HW_MISC_RP2040_ROSC_H
> +#define HW_MISC_RP2040_ROSC_H
> +
> +#include "hw/core/clock.h"
> +#include "hw/core/sysbus.h"
> +#include "qom/object.h"
> +
> +#define TYPE_RP2040_ROSC "rp2040-rosc"
> +OBJECT_DECLARE_SIMPLE_TYPE(RP2040RoscState, RP2040_ROSC)
> +
> +#define RP2040_ROSC_BASE 0x40060000
> +#define RP2040_ROSC_SIZE 0x4000
> +
> +struct RP2040RoscState {
> +    SysBusDevice parent_obj;
> +
> +    MemoryRegion iomem;
> +    Clock *clk;
> +
> +    uint32_t ctrl;
> +    uint32_t freqa;
> +    uint32_t freqb;
> +    uint32_t dormant;
> +    uint32_t div;
> +    uint32_t phase;
> +    uint32_t count;
> +    int64_t count_start_ns;
> +    uint64_t random_pool;
> +    uint64_t random_prng_state;
> +    uint64_t random_seed;
> +    uint8_t random_pool_bits;
> +    bool random_seed_set;
> +    bool badwrite;
> +};
> +
> +#endif
> diff --git a/include/hw/misc/rp2040_xosc.h b/include/hw/misc/rp2040_xosc.h
> new file mode 100644
> index 0000000000..7b9ef62d22
> --- /dev/null
> +++ b/include/hw/misc/rp2040_xosc.h
> @@ -0,0 +1,33 @@
> +/*
> + * RP2040 crystal oscillator emulation
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#ifndef HW_MISC_RP2040_XOSC_H
> +#define HW_MISC_RP2040_XOSC_H
> +
> +#include "hw/core/clock.h"
> +#include "hw/core/sysbus.h"
> +#include "qom/object.h"
> +
> +#define TYPE_RP2040_XOSC "rp2040-xosc"
> +OBJECT_DECLARE_SIMPLE_TYPE(RP2040XoscState, RP2040_XOSC)
> +
> +#define RP2040_XOSC_BASE 0x40024000
> +#define RP2040_XOSC_SIZE 0x4000
> +
> +struct RP2040XoscState {
> +    SysBusDevice parent_obj;
> +
> +    MemoryRegion iomem;
> +    Clock *clk;
> +
> +    uint32_t ctrl;
> +    uint32_t dormant;
> +    uint32_t startup;
> +    uint32_t count;
> +    bool badwrite;
> +};
> +
> +#endif
> diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
> index 8916f68bad..441f6f294a 100644
> --- a/tests/qtest/meson.build
> +++ b/tests/qtest/meson.build
> @@ -256,6 +256,7 @@ qtests_arm = \
>    (config_all_devices.has_key('CONFIG_VEXPRESS') ? ['test-arm-mptimer'] : 
> []) + \
>    (config_all_devices.has_key('CONFIG_MICROBIT') ? ['microbit-test'] : []) + 
> \
>    (config_all_devices.has_key('CONFIG_RASPI_PICO') ? 
> ['rp2040-sysinfo-syscfg-test',
> +                                                       'rp2040-rosc-test',
>                                                         'rp2040-tbman-test',
>                                                         'rp2040-vreg-test'] : 
> []) + \
>    (config_all_devices.has_key('CONFIG_STM32L4X5_SOC') ? qtests_stm32l4x5 : 
> []) + \
> diff --git a/tests/qtest/rp2040-rosc-test.c b/tests/qtest/rp2040-rosc-test.c
> new file mode 100644
> index 0000000000..2f1fb96022
> --- /dev/null
> +++ b/tests/qtest/rp2040-rosc-test.c
> @@ -0,0 +1,146 @@
> +/*
> + * QTest testcase for the RP2040 ring oscillator block.
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +#include "libqtest.h"
> +#include "qemu/bitops.h"
> +
> +#define ROSC_BASE              0x40060000
> +#define ROSC_CTRL              0x00
> +#define ROSC_FREQA             0x04
> +#define ROSC_DORMANT           0x0c
> +#define ROSC_DIV               0x10
> +#define ROSC_PHASE             0x14
> +#define ROSC_STATUS            0x18
> +#define ROSC_RANDOMBIT         0x1c
> +#define ROSC_COUNT             0x20
> +
> +#define ROSC_ENABLE_ENABLE     0xfab
> +#define ROSC_ENABLE_DISABLE    0xd1e
> +#define ROSC_FREQ_RANGE_LOW    0xfa4
> +#define ROSC_WAKE_VALUE        0x77616b65
> +#define ROSC_STATUS_STABLE     BIT(31)
> +#define ROSC_STATUS_BADWRITE   BIT(24)
> +#define ROSC_STATUS_DIV_RUNNING BIT(16)
> +#define ROSC_STATUS_ENABLED    BIT(12)
> +#define ROSC_STATUS_RUNNING \
> +    (ROSC_STATUS_STABLE | ROSC_STATUS_DIV_RUNNING | ROSC_STATUS_ENABLED)
> +
> +static QTestState *rp2040_start(void)
> +{
> +    return qtest_init("-machine raspi-pico");
> +}
> +
> +static uint32_t read_random_bits(QTestState *qts, unsigned count)
> +{
> +    uint32_t value = 0;
> +    unsigned i;
> +
> +    for (i = 0; i < count; i++) {
> +        value |= (qtest_readl(qts, ROSC_BASE + ROSC_RANDOMBIT) & BIT(0)) << 
> i;
> +    }
> +
> +    return value;
> +}
> +
> +static void test_rosc_reset_values(void)
> +{
> +    QTestState *qts = rp2040_start();
> +
> +    g_assert_cmphex(qtest_readl(qts, ROSC_BASE + ROSC_CTRL), ==,
> +                    (ROSC_ENABLE_ENABLE << 12) | ROSC_FREQ_RANGE_LOW);
> +    g_assert_cmphex(qtest_readl(qts, ROSC_BASE + ROSC_DORMANT), ==,
> +                    ROSC_WAKE_VALUE);
> +    g_assert_cmphex(qtest_readl(qts, ROSC_BASE + ROSC_DIV), ==, 0x00000ab0);
> +    g_assert_cmphex(qtest_readl(qts, ROSC_BASE + ROSC_STATUS), ==,
> +                    ROSC_STATUS_RUNNING);
> +
> +    qtest_quit(qts);
> +}
> +
> +static void test_rosc_badwrite_and_clear(void)
> +{
> +    QTestState *qts = rp2040_start();
> +
> +    qtest_writel(qts, ROSC_BASE + ROSC_FREQA, 0x00007777);
> +    g_assert_cmphex(qtest_readl(qts, ROSC_BASE + ROSC_FREQA), ==, 0);
> +    g_assert_cmphex(qtest_readl(qts, ROSC_BASE + ROSC_STATUS) &
> +                    ROSC_STATUS_BADWRITE, ==, ROSC_STATUS_BADWRITE);
> +
> +    qtest_writel(qts, ROSC_BASE + ROSC_STATUS, ROSC_STATUS_BADWRITE);
> +    g_assert_cmphex(qtest_readl(qts, ROSC_BASE + ROSC_STATUS) &
> +                    ROSC_STATUS_BADWRITE, ==, 0);
> +
> +    qtest_quit(qts);
> +}
> +
> +static void test_rosc_count_reaches_zero(void)
> +{
> +    QTestState *qts = rp2040_start();
> +    int i;
> +
> +    qtest_writel(qts, ROSC_BASE + ROSC_COUNT, 1);
> +    for (i = 0; i < 16 && qtest_readl(qts, ROSC_BASE + ROSC_COUNT); i++) {
> +        qtest_clock_step(qts, 1000);
> +    }
> +
> +    g_assert_cmphex(qtest_readl(qts, ROSC_BASE + ROSC_COUNT), ==, 0);
> +
> +    qtest_quit(qts);
> +}
> +
> +static void test_rosc_misc_values(void)
> +{
> +    QTestState *qts = rp2040_start();
> +
> +    g_assert_cmphex(qtest_readl(qts, ROSC_BASE + ROSC_RANDOMBIT) & ~BIT(0),
> +                    ==, 0);
> +    qtest_writel(qts, ROSC_BASE + ROSC_DIV, 0xaa1);
> +    g_assert_cmphex(qtest_readl(qts, ROSC_BASE + ROSC_DIV), ==, 0xaa1);
> +    qtest_writel(qts, ROSC_BASE + ROSC_PHASE, 0xaa8);
> +    g_assert_cmphex(qtest_readl(qts, ROSC_BASE + ROSC_PHASE), ==, 0xaa8);
> +
> +    qtest_writel(qts, ROSC_BASE + ROSC_CTRL,
> +                 (ROSC_ENABLE_DISABLE << 12) | ROSC_FREQ_RANGE_LOW);
> +    g_assert_cmphex(qtest_readl(qts, ROSC_BASE + ROSC_STATUS) &
> +                    ROSC_STATUS_ENABLED, ==, 0);
> +
> +    qtest_quit(qts);
> +}
> +
> +static void test_rosc_seeded_randombit(void)
> +{
> +    QTestState *qts_a;
> +    QTestState *qts_b;
> +    uint32_t bits_a;
> +    uint32_t bits_b;
> +
> +    qts_a = qtest_init("-machine raspi-pico,rosc-random-seed=0x1234");
> +    bits_a = read_random_bits(qts_a, 32);
> +    qtest_quit(qts_a);
> +
> +    qts_b = qtest_init("-machine raspi-pico,rosc-random-seed=0x1234");
> +    bits_b = read_random_bits(qts_b, 32);
> +    qtest_quit(qts_b);
> +
> +    g_assert_cmphex(bits_a, ==, bits_b);
> +}
> +
> +int main(int argc, char **argv)
> +{
> +    g_test_init(&argc, &argv, NULL);
> +
> +    qtest_add_func("/rp2040-rosc/reset-values", test_rosc_reset_values);
> +    qtest_add_func("/rp2040-rosc/badwrite-and-clear",
> +                   test_rosc_badwrite_and_clear);
> +    qtest_add_func("/rp2040-rosc/count-reaches-zero",
> +                   test_rosc_count_reaches_zero);
> +    qtest_add_func("/rp2040-rosc/misc-values", test_rosc_misc_values);
> +    qtest_add_func("/rp2040-rosc/seeded-randombit",
> +                   test_rosc_seeded_randombit);
> +
> +    return g_test_run();
> +}

-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro

Reply via email to