diff mbox series

[v4,18/45] Add RNG200 RNG and RBG

Message ID 20231208023145.1385775-19-sergey.kambalin@auriga.com (mailing list archive)
State New, archived
Headers show
Series Raspberry Pi 4B machine | expand

Commit Message

Sergey Kambalin Dec. 8, 2023, 2:31 a.m. UTC
Signed-off-by: Sergey Kambalin <sergey.kambalin@auriga.com>
---
 hw/misc/bcm2838_rng200.c         | 292 +++++++++++++++++++++++++++++--
 include/hw/misc/bcm2838_rng200.h |  10 +-
 2 files changed, 275 insertions(+), 27 deletions(-)

Comments

Peter Maydell Jan. 15, 2024, 2:29 p.m. UTC | #1
On Fri, 8 Dec 2023 at 02:40, Sergey Kambalin <serg.oker@gmail.com> wrote:
>
> Signed-off-by: Sergey Kambalin <sergey.kambalin@auriga.com>
> ---
>  hw/misc/bcm2838_rng200.c         | 292 +++++++++++++++++++++++++++++--
>  include/hw/misc/bcm2838_rng200.h |  10 +-
>  2 files changed, 275 insertions(+), 27 deletions(-)
>
> diff --git a/hw/misc/bcm2838_rng200.c b/hw/misc/bcm2838_rng200.c
> index 8f64e6a20f..f91ea0754c 100644
> --- a/hw/misc/bcm2838_rng200.c
> +++ b/hw/misc/bcm2838_rng200.c
> @@ -8,25 +8,56 @@
>   */
>
>  #include "qemu/osdep.h"
> +#include "qemu/log.h"
>  #include "qapi/error.h"
>  #include "hw/qdev-properties.h"
>  #include "hw/misc/bcm2838_rng200.h"
> +#include "hw/registerfields.h"
>  #include "migration/vmstate.h"
>  #include "trace.h"
>
> -static const VMStateDescription vmstate_bcm2838_rng200_regs = {
> -    .name = "bcm2838_rng200_regs",
> -    .version_id = 1,
> -    .minimum_version_id = 1,
> -    .fields = (VMStateField[]) {
> -        VMSTATE_UINT32(ctrl, BCM2838_rng_regs_t),
> -        VMSTATE_UINT32(int_status, BCM2838_rng_regs_t),
> -        VMSTATE_UINT32(fifo_count, BCM2838_rng_regs_t),
> -        VMSTATE_UINT32(fifo_count_threshold, BCM2838_rng_regs_t),
> -        VMSTATE_UINT32(total_bit_count_threshold, BCM2838_rng_regs_t),
> -        VMSTATE_END_OF_LIST()
> -    }
> -};
> +/* RNG200 registers */
> +REG32(RNG_CTRL,               0x00)
> +    FIELD(RNG_CTRL, RBG_ENABLE,   0 , 1)
> +    FIELD(RNG_CTRL, RSVD,         1 , 12)
> +    FIELD(RNG_CTRL, DIV,         13 , 8)
> +
> +REG32(RNG_SOFT_RESET,                0x04)
> +REG32(RBG_SOFT_RESET,                0x08)
> +REG32(RNG_TOTAL_BIT_COUNT,           0x0C)
> +REG32(RNG_TOTAL_BIT_COUNT_THRESHOLD, 0x10)
> +
> +REG32(RNG_INT_STATUS,                               0x18)
> +    FIELD(RNG_INT_STATUS, TOTAL_BITS_COUNT_IRQ,         0, 1)
> +    FIELD(RNG_INT_STATUS, RSVD0,                        1, 4)
> +    FIELD(RNG_INT_STATUS, NIST_FAIL_IRQ,                5, 1)
> +    FIELD(RNG_INT_STATUS, RSVD1,                        6, 11)
> +    FIELD(RNG_INT_STATUS, STARTUP_TRANSITIONS_MET_IRQ,  17, 1)
> +    FIELD(RNG_INT_STATUS, RSVD2,                        18, 13)
> +    FIELD(RNG_INT_STATUS, MASTER_FAIL_LOCKOUT_IRQ,      30, 1)
> +
> +REG32(RNG_INT_ENABLE,                               0x1C)
> +    FIELD(RNG_INT_ENABLE, TOTAL_BITS_COUNT_IRQ,         0, 1)
> +    FIELD(RNG_INT_ENABLE, RSVD0,                        1, 4)
> +    FIELD(RNG_INT_ENABLE, NIST_FAIL_IRQ,                5, 1)
> +    FIELD(RNG_INT_ENABLE, RSVD1,                        6, 11)
> +    FIELD(RNG_INT_ENABLE, STARTUP_TRANSITIONS_MET_IRQ,  17, 1)
> +    FIELD(RNG_INT_ENABLE, RSVD2,                        18, 13)
> +    FIELD(RNG_INT_ENABLE, MASTER_FAIL_LOCKOUT_IRQ,      30, 1)
> +
> +REG32(RNG_FIFO_DATA, 0x20)
> +
> +REG32(RNG_FIFO_COUNT,              0x24)
> +    FIELD(RNG_FIFO_COUNT, COUNT,       0, 8)
> +    FIELD(RNG_FIFO_COUNT, THRESHOLD,   8, 8)
> +
> +
> +#define RNG_WARM_UP_PERIOD_ELAPSED           17
> +
> +#define SOFT_RESET    1
> +#define IRQ_PENDING   1
> +
> +#define BCM2838_RNG200_PTIMER_POLICY         (PTIMER_POLICY_CONTINUOUS_TRIGGER)
>
>  static const VMStateDescription vmstate_bcm2838_rng200 = {
>      .name = "bcm2838_rng200",
> @@ -37,33 +68,253 @@ static const VMStateDescription vmstate_bcm2838_rng200 = {
>          VMSTATE_UINT32(rng_fifo_cap, BCM2838Rng200State),
>          VMSTATE_BOOL(use_timer, BCM2838Rng200State),
>
> -        VMSTATE_STRUCT(regs, BCM2838Rng200State, 0, vmstate_bcm2838_rng200_regs,
> -                       BCM2838_rng_regs_t),
> +        VMSTATE_ARRAY(regs, BCM2838Rng200State, N_BCM2838_RNG200_REGS, 0,
> +                      vmstate_info_uint32, uint32_t),
>
>          VMSTATE_END_OF_LIST()
>      }
>  };
>
> -static void bcm2838_rng200_rng_reset(BCM2838Rng200State *state)
> +static bool is_rbg_enabled(BCM2838Rng200State *s)
> +{
> +    return FIELD_EX32(s->regs[R_RNG_CTRL], RNG_CTRL, RBG_ENABLE);
> +}
> +
> +static void increment_bit_counter_by(BCM2838Rng200State *s, uint32_t inc_val) {
> +    s->regs[R_RNG_TOTAL_BIT_COUNT] += inc_val;
> +}
> +
> +static void bcm2838_rng200_update_irq(BCM2838Rng200State *s)
> +{
> +    qemu_set_irq(s->irq,
> +                !!(s->regs[R_RNG_INT_ENABLE] & s->regs[R_RNG_INT_STATUS]));
> +}
> +
> +static void bcm2838_rng200_update_fifo(void *opaque, const void *buf,
> +                                       size_t size)
>  {
> -    state->regs.ctrl = 0;
> +    BCM2838Rng200State *s = (BCM2838Rng200State *)opaque;
> +    Fifo8 *fifo = &s->fifo;
> +    size_t num = MIN(size, fifo8_num_free(fifo));
> +    uint32_t num_bits = num * 8;
> +    uint32_t bit_threshold_left = 0;
> +    uint32_t bit_count = 0;
> +    uint32_t bit_count_thld = 0;
> +    uint32_t fifo_thld = 0;
> +
> +    increment_bit_counter_by(s, num_bits);
> +
> +    bit_count = s->regs[R_RNG_TOTAL_BIT_COUNT];
> +    bit_count_thld = s->regs[R_RNG_TOTAL_BIT_COUNT_THRESHOLD];
> +
> +    bit_threshold_left = (bit_count < bit_count_thld)
> +                       ? bit_count_thld - bit_count
> +                       : 0;
> +
> +    if (bit_threshold_left < num_bits) {
> +        num_bits -= bit_threshold_left;
> +    } else {
> +        num_bits = 0;
> +    }

Is it really correct that we first increment the
bit counter by num_bits, and then check for "is
there enough space between the bit counter and
the counter threshold" ? And then subtracting
threshold_left from num_bits also looks odd --
if we want to write 32 bits and we have 8 bits
left then this will cause us to write 24 bits ??

> +
> +    num = num_bits / 8;
> +    if ((num == 0) && (num_bits > 0)) {
> +        num = 1;
> +    }

This also looks odd -- why is "number of bits is
between 1 and 7" a special case when other numbers
of bits that aren't an even multiple of 8 are not?

> +    if (num > 0) {
> +        fifo8_push_all(fifo, buf, num);
> +
> +
> +        fifo_thld = FIELD_EX32(s->regs[R_RNG_FIFO_COUNT],
> +                               RNG_FIFO_COUNT, THRESHOLD);
> +
> +        if (fifo8_num_used(fifo) > fifo_thld) {
> +            s->regs[R_RNG_INT_STATUS] = FIELD_DP32(s->regs[R_RNG_INT_STATUS],
> +                                                   RNG_INT_STATUS,
> +                                                   TOTAL_BITS_COUNT_IRQ, 1);
> +        }
> +    }
> +
> +    s->regs[R_RNG_FIFO_COUNT] = FIELD_DP32(s->regs[R_RNG_FIFO_COUNT],
> +                                           RNG_FIFO_COUNT,
> +                                           COUNT,
> +                                           fifo8_num_used(fifo) >> 2);
> +    bcm2838_rng200_update_irq(s);
> +    trace_bcm2838_rng200_update_fifo(num, fifo8_num_used(fifo));
> +}
> +
> +static void bcm2838_rng200_fill_fifo(BCM2838Rng200State *s)
> +{
> +    rng_backend_request_entropy(s->rng, fifo8_num_free(&s->fifo),
> +                                bcm2838_rng200_update_fifo, s);
> +}
> +
> +/* This function will be implemnented in upcoming commits */

"implemented"

> +static void bcm2838_rng200_disable_rbg(BCM2838Rng200State *s
> +                                       __attribute__((unused)))
> +{
> +    trace_bcm2838_rng200_disable_rbg();
> +}
> +
> +static void bcm2838_rng200_enable_rbg(BCM2838Rng200State *s)
> +{
> +    s->regs[R_RNG_TOTAL_BIT_COUNT] = RNG_WARM_UP_PERIOD_ELAPSED;
> +
> +    bcm2838_rng200_fill_fifo(s);
> +
> +    trace_bcm2838_rng200_enable_rbg();
> +}
> +
> +static void bcm2838_rng200_rng_reset(BCM2838Rng200State *s)
> +{
> +    memset(s->regs, 0, sizeof(s->regs));
> +    s->regs[R_RNG_INT_STATUS] = FIELD_DP32(s->regs[R_RNG_INT_STATUS],
> +                                           RNG_INT_STATUS,
> +                                           STARTUP_TRANSITIONS_MET_IRQ,
> +                                           IRQ_PENDING);
> +    fifo8_reset(&s->fifo);
>
>      trace_bcm2838_rng200_rng_soft_reset();
>  }
>
> +static void bcm2838_rng200_rbg_reset(BCM2838Rng200State *s)
> +{
> +    trace_bcm2838_rng200_rbg_soft_reset();
> +}
> +
> +static uint32_t bcm2838_rng200_read_fifo_data(BCM2838Rng200State *s)
> +{
> +    Fifo8 *fifo = &s->fifo;
> +    const uint8_t *buf;
> +    uint32_t ret = 0;
> +    uint32_t num = 0;
> +    uint32_t max = MIN(fifo8_num_used(fifo), sizeof(ret));
> +
> +    if (max > 0) {
> +        buf = fifo8_pop_buf(fifo, max, &num);
> +        if ((buf != NULL) && (num > 0)) {
> +            memcpy(&ret, buf, num);
> +        }
> +    } else {
> +        qemu_log_mask(
> +            LOG_GUEST_ERROR,
> +            "bcm2838_rng200_read_fifo_data: FIFO is empty\n"
> +        );
> +    }
> +
> +    s->regs[R_RNG_FIFO_COUNT] = FIELD_DP32(s->regs[R_RNG_FIFO_COUNT],
> +                                           RNG_FIFO_COUNT,
> +                                           COUNT,
> +                                           fifo8_num_used(fifo) >> 2);
> +
> +    bcm2838_rng200_fill_fifo(s);
> +
> +    return ret;
> +}
> +
> +static void bcm2838_rng200_ctrl_write(BCM2838Rng200State *s, uint32_t value)
> +{
> +    bool currently_enabled = is_rbg_enabled(s);
> +    bool enable_requested = FIELD_EX32(value, RNG_CTRL, RBG_ENABLE);
> +
> +    s->regs[R_RNG_CTRL] = value;
> +
> +    if (!currently_enabled && enable_requested) {
> +        bcm2838_rng200_enable_rbg(s);
> +    } else if (currently_enabled && !enable_requested) {
> +        bcm2838_rng200_disable_rbg(s);
> +    }
> +}
> +
>  static uint64_t bcm2838_rng200_read(void *opaque, hwaddr offset,
>                                      unsigned size)
>  {
> +    BCM2838Rng200State *s = (BCM2838Rng200State *)opaque;
>      uint32_t res = 0;
>
> -    /* will be implemented in upcoming commits */
> +    switch (offset) {
> +    case A_RNG_CTRL:
> +        res = s->regs[R_RNG_CTRL];
> +        break;
> +    case A_RNG_SOFT_RESET:
> +    case A_RBG_SOFT_RESET:
> +        break;
> +    case A_RNG_INT_STATUS:
> +        res = s->regs[R_RNG_INT_STATUS];
> +        break;
> +    case A_RNG_INT_ENABLE:
> +        res = s->regs[R_RNG_INT_ENABLE];
> +        break;
> +    case A_RNG_FIFO_DATA:
> +        res = bcm2838_rng200_read_fifo_data(s);
> +        break;
> +    case A_RNG_FIFO_COUNT:
> +        res = s->regs[R_RNG_FIFO_COUNT];
> +        break;
> +    case A_RNG_TOTAL_BIT_COUNT:
> +        res = s->regs[R_RNG_TOTAL_BIT_COUNT];
> +        break;
> +    case A_RNG_TOTAL_BIT_COUNT_THRESHOLD:
> +        res = s->regs[R_RNG_TOTAL_BIT_COUNT_THRESHOLD];
> +        break;
> +    default:
> +        qemu_log_mask(
> +            LOG_GUEST_ERROR,
> +            "bcm2838_rng200_read: Bad offset 0x%" HWADDR_PRIx "\n",
> +            offset
> +        );
> +        res = 0;
> +        break;
> +    }
> +
> +    trace_bcm2838_rng200_read(offset, size, res);
>      return res;
>  }
>
>  static void bcm2838_rng200_write(void *opaque, hwaddr offset,
>                                   uint64_t value, unsigned size)
>  {
> -    /* will be implemented in upcoming commits */
> +    BCM2838Rng200State *s = (BCM2838Rng200State *)opaque;
> +
> +    trace_bcm2838_rng200_write(offset, value, size);
> +
> +    switch (offset) {
> +    case A_RNG_CTRL:
> +        bcm2838_rng200_ctrl_write(s, value);
> +        break;
> +    case A_RNG_SOFT_RESET:
> +        if (value & SOFT_RESET) {
> +            bcm2838_rng200_rng_reset(s);
> +        }
> +        break;
> +    case A_RBG_SOFT_RESET:
> +        if (value & SOFT_RESET) {
> +            bcm2838_rng200_rbg_reset(s);
> +        }
> +        break;
> +    case A_RNG_INT_STATUS:
> +        s->regs[R_RNG_INT_STATUS] &= ~value;
> +        bcm2838_rng200_update_irq(s);
> +        break;
> +    case A_RNG_INT_ENABLE:
> +        s->regs[R_RNG_INT_ENABLE] = value;
> +        bcm2838_rng200_update_irq(s);
> +        break;
> +    case A_RNG_FIFO_COUNT:
> +        s->regs[R_RNG_FIFO_COUNT] = value;
> +        break;
> +    case A_RNG_TOTAL_BIT_COUNT_THRESHOLD:
> +        s->regs[R_RNG_TOTAL_BIT_COUNT_THRESHOLD] = value;
> +        s->regs[R_RNG_TOTAL_BIT_COUNT] = value + 1;
> +        break;
> +    default:
> +        qemu_log_mask(
> +            LOG_GUEST_ERROR,
> +            "bcm2838_rng200_write: Bad offset 0x%" HWADDR_PRIx "\n",
> +            offset
> +        );
> +        break;
> +    }
>  }
>
>  static const MemoryRegionOps bcm2838_rng200_ops = {
> @@ -87,6 +338,7 @@ static void bcm2838_rng200_realize(DeviceState *dev, Error **errp)
>                                   errp);
>      }
>
> +    fifo8_create(&s->fifo, s->rng_fifo_cap);
>      sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
>  }
>
> @@ -116,6 +368,8 @@ static void bcm2838_rng200_init(Object *obj)
>  static void bcm2838_rng200_reset(DeviceState *dev)
>  {
>      BCM2838Rng200State *s = BCM2838_RNG200(dev);
> +
> +    bcm2838_rng200_rbg_reset(s);
>      bcm2838_rng200_rng_reset(s);
>  }
>
> diff --git a/include/hw/misc/bcm2838_rng200.h b/include/hw/misc/bcm2838_rng200.h
> index c9c52f84be..46fdba48da 100644
> --- a/include/hw/misc/bcm2838_rng200.h
> +++ b/include/hw/misc/bcm2838_rng200.h
> @@ -22,13 +22,7 @@
>  #define TYPE_BCM2838_RNG200 "bcm2838-rng200"
>  OBJECT_DECLARE_SIMPLE_TYPE(BCM2838Rng200State, BCM2838_RNG200)
>
> -typedef struct {
> -    uint32_t ctrl;
> -    uint32_t int_status;
> -    uint32_t fifo_count;
> -    uint32_t fifo_count_threshold;
> -    uint32_t total_bit_count_threshold;
> -} BCM2838_rng_regs_t;
> +#define N_BCM2838_RNG200_REGS 9
>
>  struct BCM2838Rng200State {
>      SysBusDevice busdev;
> @@ -45,7 +39,7 @@ struct BCM2838Rng200State {
>      Fifo8    fifo;
>      qemu_irq irq;
>
> -    BCM2838_rng_regs_t regs;
> +    uint32_t regs[N_BCM2838_RNG200_REGS];
>  };
>
>  #endif /* BCM2838_RNG200_H */
> --
> 2.34.1

thanks
-- PMM
diff mbox series

Patch

diff --git a/hw/misc/bcm2838_rng200.c b/hw/misc/bcm2838_rng200.c
index 8f64e6a20f..f91ea0754c 100644
--- a/hw/misc/bcm2838_rng200.c
+++ b/hw/misc/bcm2838_rng200.c
@@ -8,25 +8,56 @@ 
  */
 
 #include "qemu/osdep.h"
+#include "qemu/log.h"
 #include "qapi/error.h"
 #include "hw/qdev-properties.h"
 #include "hw/misc/bcm2838_rng200.h"
+#include "hw/registerfields.h"
 #include "migration/vmstate.h"
 #include "trace.h"
 
-static const VMStateDescription vmstate_bcm2838_rng200_regs = {
-    .name = "bcm2838_rng200_regs",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT32(ctrl, BCM2838_rng_regs_t),
-        VMSTATE_UINT32(int_status, BCM2838_rng_regs_t),
-        VMSTATE_UINT32(fifo_count, BCM2838_rng_regs_t),
-        VMSTATE_UINT32(fifo_count_threshold, BCM2838_rng_regs_t),
-        VMSTATE_UINT32(total_bit_count_threshold, BCM2838_rng_regs_t),
-        VMSTATE_END_OF_LIST()
-    }
-};
+/* RNG200 registers */
+REG32(RNG_CTRL,               0x00)
+    FIELD(RNG_CTRL, RBG_ENABLE,   0 , 1)
+    FIELD(RNG_CTRL, RSVD,         1 , 12)
+    FIELD(RNG_CTRL, DIV,         13 , 8)
+
+REG32(RNG_SOFT_RESET,                0x04)
+REG32(RBG_SOFT_RESET,                0x08)
+REG32(RNG_TOTAL_BIT_COUNT,           0x0C)
+REG32(RNG_TOTAL_BIT_COUNT_THRESHOLD, 0x10)
+
+REG32(RNG_INT_STATUS,                               0x18)
+    FIELD(RNG_INT_STATUS, TOTAL_BITS_COUNT_IRQ,         0, 1)
+    FIELD(RNG_INT_STATUS, RSVD0,                        1, 4)
+    FIELD(RNG_INT_STATUS, NIST_FAIL_IRQ,                5, 1)
+    FIELD(RNG_INT_STATUS, RSVD1,                        6, 11)
+    FIELD(RNG_INT_STATUS, STARTUP_TRANSITIONS_MET_IRQ,  17, 1)
+    FIELD(RNG_INT_STATUS, RSVD2,                        18, 13)
+    FIELD(RNG_INT_STATUS, MASTER_FAIL_LOCKOUT_IRQ,      30, 1)
+
+REG32(RNG_INT_ENABLE,                               0x1C)
+    FIELD(RNG_INT_ENABLE, TOTAL_BITS_COUNT_IRQ,         0, 1)
+    FIELD(RNG_INT_ENABLE, RSVD0,                        1, 4)
+    FIELD(RNG_INT_ENABLE, NIST_FAIL_IRQ,                5, 1)
+    FIELD(RNG_INT_ENABLE, RSVD1,                        6, 11)
+    FIELD(RNG_INT_ENABLE, STARTUP_TRANSITIONS_MET_IRQ,  17, 1)
+    FIELD(RNG_INT_ENABLE, RSVD2,                        18, 13)
+    FIELD(RNG_INT_ENABLE, MASTER_FAIL_LOCKOUT_IRQ,      30, 1)
+
+REG32(RNG_FIFO_DATA, 0x20)
+
+REG32(RNG_FIFO_COUNT,              0x24)
+    FIELD(RNG_FIFO_COUNT, COUNT,       0, 8)
+    FIELD(RNG_FIFO_COUNT, THRESHOLD,   8, 8)
+
+
+#define RNG_WARM_UP_PERIOD_ELAPSED           17
+
+#define SOFT_RESET    1
+#define IRQ_PENDING   1
+
+#define BCM2838_RNG200_PTIMER_POLICY         (PTIMER_POLICY_CONTINUOUS_TRIGGER)
 
 static const VMStateDescription vmstate_bcm2838_rng200 = {
     .name = "bcm2838_rng200",
@@ -37,33 +68,253 @@  static const VMStateDescription vmstate_bcm2838_rng200 = {
         VMSTATE_UINT32(rng_fifo_cap, BCM2838Rng200State),
         VMSTATE_BOOL(use_timer, BCM2838Rng200State),
 
-        VMSTATE_STRUCT(regs, BCM2838Rng200State, 0, vmstate_bcm2838_rng200_regs,
-                       BCM2838_rng_regs_t),
+        VMSTATE_ARRAY(regs, BCM2838Rng200State, N_BCM2838_RNG200_REGS, 0,
+                      vmstate_info_uint32, uint32_t),
 
         VMSTATE_END_OF_LIST()
     }
 };
 
-static void bcm2838_rng200_rng_reset(BCM2838Rng200State *state)
+static bool is_rbg_enabled(BCM2838Rng200State *s)
+{
+    return FIELD_EX32(s->regs[R_RNG_CTRL], RNG_CTRL, RBG_ENABLE);
+}
+
+static void increment_bit_counter_by(BCM2838Rng200State *s, uint32_t inc_val) {
+    s->regs[R_RNG_TOTAL_BIT_COUNT] += inc_val;
+}
+
+static void bcm2838_rng200_update_irq(BCM2838Rng200State *s)
+{ 
+    qemu_set_irq(s->irq,
+                !!(s->regs[R_RNG_INT_ENABLE] & s->regs[R_RNG_INT_STATUS]));
+}
+
+static void bcm2838_rng200_update_fifo(void *opaque, const void *buf,
+                                       size_t size)
 {
-    state->regs.ctrl = 0;
+    BCM2838Rng200State *s = (BCM2838Rng200State *)opaque;
+    Fifo8 *fifo = &s->fifo;
+    size_t num = MIN(size, fifo8_num_free(fifo));
+    uint32_t num_bits = num * 8;
+    uint32_t bit_threshold_left = 0;
+    uint32_t bit_count = 0;
+    uint32_t bit_count_thld = 0;
+    uint32_t fifo_thld = 0;
+
+    increment_bit_counter_by(s, num_bits);
+
+    bit_count = s->regs[R_RNG_TOTAL_BIT_COUNT];
+    bit_count_thld = s->regs[R_RNG_TOTAL_BIT_COUNT_THRESHOLD];
+
+    bit_threshold_left = (bit_count < bit_count_thld)
+                       ? bit_count_thld - bit_count
+                       : 0;
+
+    if (bit_threshold_left < num_bits) {
+        num_bits -= bit_threshold_left;
+    } else {
+        num_bits = 0;
+    }
+
+    num = num_bits / 8;
+    if ((num == 0) && (num_bits > 0)) {
+        num = 1;
+    }
+    if (num > 0) {
+        fifo8_push_all(fifo, buf, num);
+
+
+        fifo_thld = FIELD_EX32(s->regs[R_RNG_FIFO_COUNT],
+                               RNG_FIFO_COUNT, THRESHOLD);
+
+        if (fifo8_num_used(fifo) > fifo_thld) {
+            s->regs[R_RNG_INT_STATUS] = FIELD_DP32(s->regs[R_RNG_INT_STATUS],
+                                                   RNG_INT_STATUS,
+                                                   TOTAL_BITS_COUNT_IRQ, 1);
+        }
+    }
+
+    s->regs[R_RNG_FIFO_COUNT] = FIELD_DP32(s->regs[R_RNG_FIFO_COUNT],
+                                           RNG_FIFO_COUNT,
+                                           COUNT,
+                                           fifo8_num_used(fifo) >> 2);
+    bcm2838_rng200_update_irq(s);
+    trace_bcm2838_rng200_update_fifo(num, fifo8_num_used(fifo));
+}
+
+static void bcm2838_rng200_fill_fifo(BCM2838Rng200State *s)
+{
+    rng_backend_request_entropy(s->rng, fifo8_num_free(&s->fifo),
+                                bcm2838_rng200_update_fifo, s);
+}
+
+/* This function will be implemnented in upcoming commits */
+static void bcm2838_rng200_disable_rbg(BCM2838Rng200State *s
+                                       __attribute__((unused)))
+{
+    trace_bcm2838_rng200_disable_rbg();
+}
+
+static void bcm2838_rng200_enable_rbg(BCM2838Rng200State *s)
+{
+    s->regs[R_RNG_TOTAL_BIT_COUNT] = RNG_WARM_UP_PERIOD_ELAPSED;
+
+    bcm2838_rng200_fill_fifo(s);
+
+    trace_bcm2838_rng200_enable_rbg();
+}
+
+static void bcm2838_rng200_rng_reset(BCM2838Rng200State *s)
+{
+    memset(s->regs, 0, sizeof(s->regs));
+    s->regs[R_RNG_INT_STATUS] = FIELD_DP32(s->regs[R_RNG_INT_STATUS],
+                                           RNG_INT_STATUS,
+                                           STARTUP_TRANSITIONS_MET_IRQ,
+                                           IRQ_PENDING);
+    fifo8_reset(&s->fifo);
 
     trace_bcm2838_rng200_rng_soft_reset();
 }
 
+static void bcm2838_rng200_rbg_reset(BCM2838Rng200State *s)
+{
+    trace_bcm2838_rng200_rbg_soft_reset();
+}
+
+static uint32_t bcm2838_rng200_read_fifo_data(BCM2838Rng200State *s)
+{
+    Fifo8 *fifo = &s->fifo;
+    const uint8_t *buf;
+    uint32_t ret = 0;
+    uint32_t num = 0;
+    uint32_t max = MIN(fifo8_num_used(fifo), sizeof(ret));
+
+    if (max > 0) {
+        buf = fifo8_pop_buf(fifo, max, &num);
+        if ((buf != NULL) && (num > 0)) {
+            memcpy(&ret, buf, num);
+        }
+    } else {
+        qemu_log_mask(
+            LOG_GUEST_ERROR,
+            "bcm2838_rng200_read_fifo_data: FIFO is empty\n"
+        );
+    }
+
+    s->regs[R_RNG_FIFO_COUNT] = FIELD_DP32(s->regs[R_RNG_FIFO_COUNT],
+                                           RNG_FIFO_COUNT,
+                                           COUNT,
+                                           fifo8_num_used(fifo) >> 2);
+
+    bcm2838_rng200_fill_fifo(s);
+
+    return ret;
+}
+
+static void bcm2838_rng200_ctrl_write(BCM2838Rng200State *s, uint32_t value)
+{
+    bool currently_enabled = is_rbg_enabled(s);
+    bool enable_requested = FIELD_EX32(value, RNG_CTRL, RBG_ENABLE);
+
+    s->regs[R_RNG_CTRL] = value;
+
+    if (!currently_enabled && enable_requested) {
+        bcm2838_rng200_enable_rbg(s);
+    } else if (currently_enabled && !enable_requested) {
+        bcm2838_rng200_disable_rbg(s);
+    }
+}
+
 static uint64_t bcm2838_rng200_read(void *opaque, hwaddr offset,
                                     unsigned size)
 {
+    BCM2838Rng200State *s = (BCM2838Rng200State *)opaque;
     uint32_t res = 0;
 
-    /* will be implemented in upcoming commits */
+    switch (offset) {
+    case A_RNG_CTRL:
+        res = s->regs[R_RNG_CTRL];
+        break;
+    case A_RNG_SOFT_RESET:
+    case A_RBG_SOFT_RESET:
+        break;
+    case A_RNG_INT_STATUS:
+        res = s->regs[R_RNG_INT_STATUS];
+        break;
+    case A_RNG_INT_ENABLE:
+        res = s->regs[R_RNG_INT_ENABLE];
+        break;
+    case A_RNG_FIFO_DATA:
+        res = bcm2838_rng200_read_fifo_data(s);
+        break;
+    case A_RNG_FIFO_COUNT:
+        res = s->regs[R_RNG_FIFO_COUNT];
+        break;
+    case A_RNG_TOTAL_BIT_COUNT:
+        res = s->regs[R_RNG_TOTAL_BIT_COUNT];
+        break;
+    case A_RNG_TOTAL_BIT_COUNT_THRESHOLD:
+        res = s->regs[R_RNG_TOTAL_BIT_COUNT_THRESHOLD];
+        break;
+    default:
+        qemu_log_mask(
+            LOG_GUEST_ERROR,
+            "bcm2838_rng200_read: Bad offset 0x%" HWADDR_PRIx "\n",
+            offset
+        );
+        res = 0;
+        break;
+    }
+
+    trace_bcm2838_rng200_read(offset, size, res);
     return res;
 }
 
 static void bcm2838_rng200_write(void *opaque, hwaddr offset,
                                  uint64_t value, unsigned size)
 {
-    /* will be implemented in upcoming commits */
+    BCM2838Rng200State *s = (BCM2838Rng200State *)opaque;
+
+    trace_bcm2838_rng200_write(offset, value, size);
+
+    switch (offset) {
+    case A_RNG_CTRL:
+        bcm2838_rng200_ctrl_write(s, value);
+        break;
+    case A_RNG_SOFT_RESET:
+        if (value & SOFT_RESET) {
+            bcm2838_rng200_rng_reset(s);
+        }
+        break;
+    case A_RBG_SOFT_RESET:
+        if (value & SOFT_RESET) {
+            bcm2838_rng200_rbg_reset(s);
+        }
+        break;
+    case A_RNG_INT_STATUS:
+        s->regs[R_RNG_INT_STATUS] &= ~value;
+        bcm2838_rng200_update_irq(s);
+        break;
+    case A_RNG_INT_ENABLE:
+        s->regs[R_RNG_INT_ENABLE] = value;
+        bcm2838_rng200_update_irq(s);
+        break;
+    case A_RNG_FIFO_COUNT:
+        s->regs[R_RNG_FIFO_COUNT] = value;
+        break;
+    case A_RNG_TOTAL_BIT_COUNT_THRESHOLD:
+        s->regs[R_RNG_TOTAL_BIT_COUNT_THRESHOLD] = value;
+        s->regs[R_RNG_TOTAL_BIT_COUNT] = value + 1;
+        break;
+    default:
+        qemu_log_mask(
+            LOG_GUEST_ERROR,
+            "bcm2838_rng200_write: Bad offset 0x%" HWADDR_PRIx "\n",
+            offset
+        );
+        break;
+    }
 }
 
 static const MemoryRegionOps bcm2838_rng200_ops = {
@@ -87,6 +338,7 @@  static void bcm2838_rng200_realize(DeviceState *dev, Error **errp)
                                  errp);
     }
 
+    fifo8_create(&s->fifo, s->rng_fifo_cap);
     sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
 }
 
@@ -116,6 +368,8 @@  static void bcm2838_rng200_init(Object *obj)
 static void bcm2838_rng200_reset(DeviceState *dev)
 {
     BCM2838Rng200State *s = BCM2838_RNG200(dev);
+
+    bcm2838_rng200_rbg_reset(s);
     bcm2838_rng200_rng_reset(s);
 }
 
diff --git a/include/hw/misc/bcm2838_rng200.h b/include/hw/misc/bcm2838_rng200.h
index c9c52f84be..46fdba48da 100644
--- a/include/hw/misc/bcm2838_rng200.h
+++ b/include/hw/misc/bcm2838_rng200.h
@@ -22,13 +22,7 @@ 
 #define TYPE_BCM2838_RNG200 "bcm2838-rng200"
 OBJECT_DECLARE_SIMPLE_TYPE(BCM2838Rng200State, BCM2838_RNG200)
 
-typedef struct {
-    uint32_t ctrl;
-    uint32_t int_status;
-    uint32_t fifo_count;
-    uint32_t fifo_count_threshold;
-    uint32_t total_bit_count_threshold;
-} BCM2838_rng_regs_t;
+#define N_BCM2838_RNG200_REGS 9
 
 struct BCM2838Rng200State {
     SysBusDevice busdev;
@@ -45,7 +39,7 @@  struct BCM2838Rng200State {
     Fifo8    fifo;
     qemu_irq irq;
 
-    BCM2838_rng_regs_t regs;
+    uint32_t regs[N_BCM2838_RNG200_REGS];
 };
 
 #endif /* BCM2838_RNG200_H */