@@ -8,23 +8,194 @@
*/
#include "qemu/osdep.h"
+#include "qemu/log.h"
#include "qapi/error.h"
#include "hw/qdev-properties.h"
#include "hw/misc/bcm2838_rng200.h"
#include "trace.h"
+#define RNG_CTRL_OFFSET 0x00
+#define RNG_SOFT_RESET 0x01
+#define RNG_SOFT_RESET_OFFSET 0x04
+#define RBG_SOFT_RESET_OFFSET 0x08
+#define RNG_TOTAL_BIT_COUNT_OFFSET 0x0C
+#define RNG_TOTAL_BIT_COUNT_THRESHOLD_OFFSET 0x10
+#define RNG_INT_STATUS_OFFSET 0x18
+#define RNG_INT_ENABLE_OFFSET 0x1C
+#define RNG_FIFO_DATA_OFFSET 0x20
+#define RNG_FIFO_COUNT_OFFSET 0x24
+
+#define RNG_WARM_UP_PERIOD_ELAPSED 17
+
+#define BCM2838_RNG200_PTIMER_POLICY (PTIMER_POLICY_CONTINUOUS_TRIGGER)
+
+static void bcm2838_rng200_update_irq(BCM2838Rng200State *state)
+{
+ qemu_set_irq(state->irq, !!(state->rng_int_enable.value
+ & state->rng_int_status.value));
+}
+
+static void bcm2838_rng200_update_fifo(void *opaque, const void *buf,
+ size_t size)
+{
+ BCM2838Rng200State *state = (BCM2838Rng200State *)opaque;
+ Fifo8 *fifo = &state->fifo;
+ size_t num = MIN(size, fifo8_num_free(fifo));
+ uint32_t num_bits = num * 8;
+ uint32_t bit_threshold_left = 0;
+
+ state->rng_total_bit_count += num_bits;
+ if (state->rng_bit_count_threshold > state->rng_total_bit_count) {
+ bit_threshold_left =
+ state->rng_bit_count_threshold - state->rng_total_bit_count;
+ } else {
+ bit_threshold_left = 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);
+
+ if (fifo8_num_used(fifo) > state->rng_fifo_count.thld) {
+ state->rng_int_status.total_bits_count_irq = 1;
+ }
+ }
+
+ state->rng_fifo_count.count = fifo8_num_used(fifo) >> 2;
+ bcm2838_rng200_update_irq(state);
+ trace_bcm2838_rng200_update_fifo(num, fifo8_num_used(fifo));
+}
+
+static void bcm2838_rng200_fill_fifo(BCM2838Rng200State *state)
+{
+ rng_backend_request_entropy(state->rng,
+ fifo8_num_free(&state->fifo),
+ bcm2838_rng200_update_fifo, state);
+}
+
+/* state is temporary unused */
+static void bcm2838_rng200_disable_rbg(BCM2838Rng200State *state
+ __attribute__((unused)))
+{
+ trace_bcm2838_rng200_disable_rbg();
+}
+
+static void bcm2838_rng200_enable_rbg(BCM2838Rng200State *state)
+{
+ state->rng_total_bit_count = RNG_WARM_UP_PERIOD_ELAPSED;
+
+ bcm2838_rng200_fill_fifo(state);
+
+ trace_bcm2838_rng200_enable_rbg();
+}
+
static void bcm2838_rng200_rng_reset(BCM2838Rng200State *state)
{
state->rng_ctrl.value = 0;
+ state->rng_total_bit_count = 0;
+ state->rng_bit_count_threshold = 0;
+ state->rng_fifo_count.value = 0;
+ state->rng_int_status.value = 0;
+ state->rng_int_status.startup_transition_met_irq = 1;
+ state->rng_int_enable.value = 0;
+ fifo8_reset(&state->fifo);
trace_bcm2838_rng200_rng_soft_reset();
}
+static void bcm2838_rng200_rbg_reset(BCM2838Rng200State *state)
+{
+ trace_bcm2838_rng200_rbg_soft_reset();
+}
+
+static uint32_t bcm2838_rng200_read_fifo_data(BCM2838Rng200State *state)
+{
+ Fifo8 *fifo = &state->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"
+ );
+ }
+
+ state->rng_fifo_count.count = fifo8_num_used(fifo) >> 2;
+ bcm2838_rng200_fill_fifo(state);
+
+ return ret;
+}
+
+static void bcm2838_rng200_ctrl_write(BCM2838Rng200State *s, uint64_t value)
+{
+ bool rng_enable = s->rng_ctrl.rbg_enable;
+
+ s->rng_ctrl.value = value;
+ if (!s->rng_ctrl.rbg_enable && rng_enable) {
+ bcm2838_rng200_disable_rbg(s);
+ } else if (s->rng_ctrl.rbg_enable && !rng_enable) {
+ bcm2838_rng200_enable_rbg(s);
+ }
+}
+
static uint64_t bcm2838_rng200_read(void *opaque, hwaddr offset,
unsigned size)
{
+ BCM2838Rng200State *s = (BCM2838Rng200State *)opaque;
uint32_t res = 0;
+ switch (offset) {
+ case RNG_CTRL_OFFSET:
+ res = s->rng_ctrl.value;
+ break;
+ case RNG_SOFT_RESET_OFFSET:
+ case RBG_SOFT_RESET_OFFSET:
+ break;
+ case RNG_INT_STATUS_OFFSET:
+ res = s->rng_int_status.value;
+ break;
+ case RNG_INT_ENABLE_OFFSET:
+ res = s->rng_int_enable.value;
+ break;
+ case RNG_FIFO_DATA_OFFSET:
+ res = bcm2838_rng200_read_fifo_data(s);
+ break;
+ case RNG_FIFO_COUNT_OFFSET:
+ res = s->rng_fifo_count.value;
+ break;
+ case RNG_TOTAL_BIT_COUNT_OFFSET:
+ res = s->rng_total_bit_count;
+ break;
+ case RNG_TOTAL_BIT_COUNT_THRESHOLD_OFFSET:
+ res = s->rng_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((void *)offset, size, res);
return res;
}
@@ -32,8 +203,50 @@ static uint64_t bcm2838_rng200_read(void *opaque, hwaddr offset,
static void bcm2838_rng200_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
+ BCM2838Rng200State *s = (BCM2838Rng200State *)opaque;
trace_bcm2838_rng200_write((void *)offset, value, size);
+
+ switch (offset) {
+ case RNG_CTRL_OFFSET:
+ bcm2838_rng200_ctrl_write(s, value);
+ break;
+ case RNG_SOFT_RESET_OFFSET:
+ if (value & RNG_SOFT_RESET) {
+ bcm2838_rng200_rng_reset(s);
+ }
+ break;
+ case RBG_SOFT_RESET_OFFSET:
+ if (value & RNG_SOFT_RESET) {
+ bcm2838_rng200_rbg_reset(s);
+ }
+ break;
+ case RNG_INT_STATUS_OFFSET:
+ s->rng_int_status.value &= ~value;
+ bcm2838_rng200_update_irq(s);
+ break;
+ case RNG_INT_ENABLE_OFFSET:
+ s->rng_int_enable.value = value;
+ bcm2838_rng200_update_irq(s);
+ break;
+ case RNG_FIFO_COUNT_OFFSET:
+ {
+ BCM2838Rng200FifoCount tmp = {.value = value};
+ s->rng_fifo_count.thld = tmp.thld;
+ }
+ break;
+ case RNG_TOTAL_BIT_COUNT_THRESHOLD_OFFSET:
+ s->rng_bit_count_threshold = value;
+ s->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 = {
@@ -57,6 +270,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);
}
@@ -81,6 +295,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);
}
@@ -89,7 +305,7 @@ static Property bcm2838_rng200_properties[] = {
DEFINE_PROP_UINT32("rng-fifo-cap", BCM2838Rng200State, rng_fifo_cap, 128),
DEFINE_PROP_LINK("rng", BCM2838Rng200State, rng,
TYPE_RNG_BACKEND, RngBackend *),
- DEFINE_PROP_BOOL("use-timer", BCM2838Rng200State, use_timer, true),
+ DEFINE_PROP_BOOL("use-timer", BCM2838Rng200State, use_timer, false),
DEFINE_PROP_END_OF_LIST(),
};
Signed-off-by: Sergey Kambalin <sergey.kambalin@auriga.com> --- hw/misc/bcm2838_rng200.c | 218 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 217 insertions(+), 1 deletion(-)