[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH v3] hw/misc: Add Exynos4210 Pseudo Random Number
From: |
Peter Maydell |
Subject: |
Re: [Qemu-devel] [PATCH v3] hw/misc: Add Exynos4210 Pseudo Random Number Generator |
Date: |
Tue, 13 Jun 2017 19:06:20 +0100 |
On 25 April 2017 at 19:06, Krzysztof Kozlowski <address@hidden> wrote:
> Add emulation for Exynos4210 Pseudo Random Number Generator which could
> work on fixed seeds or with seeds provided by True Random Number
> Generator block inside the SoC.
>
> Implement only the fixed seeds part of it in polling mode (no
> interrupts).
Hi -- this patch is a bit old and I forget what the status of it
is. Does it still need review?
thanks
-- PMM
> Emulation tested with two independent Linux kernel exynos-rng drivers:
> 1. New kcapi-rng interface (targeting Linux v4.12),
> 2. Old hwrng inteface
> # echo "exynos" > /sys/class/misc/hw_random/rng_current
> # dd if=/dev/hwrng of=/dev/null bs=1 count=16
>
> Signed-off-by: Krzysztof Kozlowski <address@hidden>
>
> ---
>
> Changes since v2:
> 1. Drop GRand in favor of qcrypto_random_bytes() after discussion with
> Peter Maydell.
> 2. Because of above, ignore the seed value but mark if each one of five
> seed registers were seeded.
> 3. Fix memset of s->randr_value (copy paste error, also shorter sizeof
> can be used).
>
> Changes since v1:
> 1. Use GRand-like functions to fix build on MingW32 (this adds also
> finalize).
> 2. Add DPRINTF macro.
> 3. Use HWADDR_PRIx and family for printing values.
> ---
> hw/arm/exynos4210.c | 4 +
> hw/misc/Makefile.objs | 2 +-
> hw/misc/exynos4210_rng.c | 282
> +++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 287 insertions(+), 1 deletion(-)
> create mode 100644 hw/misc/exynos4210_rng.c
>
> diff --git a/hw/arm/exynos4210.c b/hw/arm/exynos4210.c
> index 960f27e45a36..5a622cfedfc8 100644
> --- a/hw/arm/exynos4210.c
> +++ b/hw/arm/exynos4210.c
> @@ -86,6 +86,9 @@
> /* Clock controller SFR base address */
> #define EXYNOS4210_CLK_BASE_ADDR 0x10030000
>
> +/* PRNG/HASH SFR base address */
> +#define EXYNOS4210_RNG_BASE_ADDR 0x10830400
> +
> /* Display controllers (FIMD) */
> #define EXYNOS4210_FIMD0_BASE_ADDR 0x11C00000
>
> @@ -322,6 +325,7 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
> sysbus_create_simple("exynos4210.pmu", EXYNOS4210_PMU_BASE_ADDR, NULL);
>
> sysbus_create_simple("exynos4210.clk", EXYNOS4210_CLK_BASE_ADDR, NULL);
> + sysbus_create_simple("exynos4210.rng", EXYNOS4210_RNG_BASE_ADDR, NULL);
>
> /* PWM */
> sysbus_create_varargs("exynos4210.pwm", EXYNOS4210_PWM_BASE_ADDR,
> diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
> index c8b489390f7e..e0699ea11155 100644
> --- a/hw/misc/Makefile.objs
> +++ b/hw/misc/Makefile.objs
> @@ -26,7 +26,7 @@ obj-$(CONFIG_IVSHMEM) += ivshmem.o
> obj-$(CONFIG_REALVIEW) += arm_sysctl.o
> obj-$(CONFIG_NSERIES) += cbus.o
> obj-$(CONFIG_ECCMEMCTL) += eccmemctl.o
> -obj-$(CONFIG_EXYNOS4) += exynos4210_pmu.o exynos4210_clk.o
> +obj-$(CONFIG_EXYNOS4) += exynos4210_pmu.o exynos4210_clk.o exynos4210_rng.o
> obj-$(CONFIG_IMX) += imx_ccm.o
> obj-$(CONFIG_IMX) += imx31_ccm.o
> obj-$(CONFIG_IMX) += imx25_ccm.o
> diff --git a/hw/misc/exynos4210_rng.c b/hw/misc/exynos4210_rng.c
> new file mode 100644
> index 000000000000..3f1459edb5df
> --- /dev/null
> +++ b/hw/misc/exynos4210_rng.c
> @@ -0,0 +1,282 @@
> +/*
> + * Exynos4210 Pseudo Random Nubmer Generator Emulation
> + *
> + * Copyright (c) 2017 Krzysztof Kozlowski <address@hidden>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful, but
> WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
> + * for more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "crypto/random.h"
> +#include "hw/sysbus.h"
> +#include "qemu/log.h"
> +
> +#define DEBUG_EXYNOS_RNG 0
> +
> +#define DPRINTF(fmt, ...) \
> + do { \
> + if (DEBUG_EXYNOS_RNG) { \
> + printf("exynos4210_rng: " fmt, ## __VA_ARGS__); \
> + } \
> + } while (0)
> +
> +#define TYPE_EXYNOS4210_RNG "exynos4210.rng"
> +#define EXYNOS4210_RNG(obj) \
> + OBJECT_CHECK(Exynos4210RngState, (obj), TYPE_EXYNOS4210_RNG)
> +
> +/*
> + * Exynos4220, PRNG, only polling mode is supported.
> + */
> +
> +/* RNG_CONTROL_1 register bitfields, reset value: 0x0 */
> +#define EXYNOS4210_RNG_CONTROL_1_PRNG 0x8
> +#define EXYNOS4210_RNG_CONTROL_1_START_INIT BIT(4)
> +/* RNG_STATUS register bitfields, reset value: 0x1 */
> +#define EXYNOS4210_RNG_STATUS_PRNG_ERROR BIT(7)
> +#define EXYNOS4210_RNG_STATUS_PRNG_DONE BIT(5)
> +#define EXYNOS4210_RNG_STATUS_MSG_DONE BIT(4)
> +#define EXYNOS4210_RNG_STATUS_PARTIAL_DONE BIT(3)
> +#define EXYNOS4210_RNG_STATUS_PRNG_BUSY BIT(2)
> +#define EXYNOS4210_RNG_STATUS_SEED_SETTING_DONE BIT(1)
> +#define EXYNOS4210_RNG_STATUS_BUFFER_READY BIT(0)
> +#define EXYNOS4210_RNG_STATUS_WRITE_MASK
> (EXYNOS4210_RNG_STATUS_PRNG_DONE \
> + |
> EXYNOS4210_RNG_STATUS_MSG_DONE \
> + |
> EXYNOS4210_RNG_STATUS_PARTIAL_DONE)
> +
> +#define EXYNOS4210_RNG_CONTROL_1 0x0
> +#define EXYNOS4210_RNG_STATUS 0x10
> +#define EXYNOS4210_RNG_SEED_IN 0x140
> +#define EXYNOS4210_RNG_SEED_IN_OFFSET(n) (EXYNOS4210_RNG_SEED_IN + (n
> * 0x4))
> +#define EXYNOS4210_RNG_PRNG 0x160
> +#define EXYNOS4210_RNG_PRNG_OFFSET(n) (EXYNOS4210_RNG_PRNG + (n *
> 0x4))
> +
> +#define EXYNOS4210_RNG_PRNG_NUM 5
> +
> +#define EXYNOS4210_RNG_REGS_MEM_SIZE 0x200
> +
> +typedef struct Exynos4210RngState {
> + SysBusDevice parent_obj;
> + MemoryRegion iomem;
> +
> + int32_t randr_value[EXYNOS4210_RNG_PRNG_NUM];
> + /* bits from 0 to EXYNOS4210_RNG_PRNG_NUM if given seed register was set
> */
> + uint32_t seed_set;
> +
> + /* Register values */
> + uint32_t reg_control;
> + uint32_t reg_status;
> +} Exynos4210RngState;
> +
> +static bool exynos4210_rng_seed_ready(const Exynos4210RngState *s)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < EXYNOS4210_RNG_PRNG_NUM; i++) {
> + if ((s->seed_set & BIT(i)) == 0) {
> + return false;
> + }
> + }
> +
> + return true;
> +}
> +
> +static void exynos4210_rng_set_seed(Exynos4210RngState *s, unsigned int i,
> + uint64_t val)
> +{
> + /*
> + * We actually ignore the seed and always generate true random numbers.
> + * Theoretically this should not match the device as Exynos has
> + * a Pseudo Random Number Generator but testing shown that it always
> + * generates random numbers regardless of the seed value.
> + */
> + s->seed_set |= BIT(i);
> +
> + /* If all seeds were written, update the status to reflect it */
> + if (exynos4210_rng_seed_ready(s)) {
> + s->reg_status |= EXYNOS4210_RNG_STATUS_SEED_SETTING_DONE;
> + } else {
> + s->reg_status &= ~EXYNOS4210_RNG_STATUS_SEED_SETTING_DONE;
> + }
> +}
> +
> +static void exynos4210_rng_run_engine(Exynos4210RngState *s)
> +{
> + Error *err = NULL;
> + int ret;
> +
> + /* Seed set? */
> + if ((s->reg_status & EXYNOS4210_RNG_STATUS_SEED_SETTING_DONE) == 0) {
> + goto out;
> + }
> +
> + /* PRNG engine chosen? */
> + if ((s->reg_control & EXYNOS4210_RNG_CONTROL_1_PRNG) == 0) {
> + goto out;
> + }
> +
> + /* PRNG engine started? */
> + if ((s->reg_control & EXYNOS4210_RNG_CONTROL_1_START_INIT) == 0) {
> + goto out;
> + }
> +
> + /* Get randoms */
> + ret = qcrypto_random_bytes((uint8_t *)s->randr_value,
> + sizeof(s->randr_value), &err);
> + if (!ret) {
> + /* Notify that PRNG is ready */
> + s->reg_status |= EXYNOS4210_RNG_STATUS_PRNG_DONE;
> + } else {
> + error_report_err(err);
> + }
> +
> +out:
> + /* Always clear start engine bit */
> + s->reg_control &= ~EXYNOS4210_RNG_CONTROL_1_START_INIT;
> +}
> +
> +static uint64_t exynos4210_rng_read(void *opaque, hwaddr offset,
> + unsigned size)
> +{
> + Exynos4210RngState *s = (Exynos4210RngState *)opaque;
> + uint32_t val = 0;
> +
> + assert(size == 4);
> +
> + switch (offset) {
> + case EXYNOS4210_RNG_CONTROL_1:
> + val = s->reg_control;
> + break;
> +
> + case EXYNOS4210_RNG_STATUS:
> + val = s->reg_status;
> + break;
> +
> + case EXYNOS4210_RNG_PRNG_OFFSET(0):
> + case EXYNOS4210_RNG_PRNG_OFFSET(1):
> + case EXYNOS4210_RNG_PRNG_OFFSET(2):
> + case EXYNOS4210_RNG_PRNG_OFFSET(3):
> + case EXYNOS4210_RNG_PRNG_OFFSET(4):
> + val = s->randr_value[(offset - EXYNOS4210_RNG_PRNG_OFFSET(0)) / 4];
> + DPRINTF("returning random @0x%" HWADDR_PRIx ": 0x%" PRIx32 "\n",
> + offset, val);
> + break;
> +
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: bad read offset 0x%" HWADDR_PRIx "\n",
> + __func__, offset);
> + }
> +
> + return val;
> +}
> +
> +static void exynos4210_rng_write(void *opaque, hwaddr offset,
> + uint64_t val, unsigned size)
> +{
> + Exynos4210RngState *s = (Exynos4210RngState *)opaque;
> +
> + assert(size == 4);
> +
> + switch (offset) {
> + case EXYNOS4210_RNG_CONTROL_1:
> + DPRINTF("RNG_CONTROL_1 = 0x%" PRIx64 "\n", val);
> + s->reg_control = val;
> + exynos4210_rng_run_engine(s);
> + break;
> +
> + case EXYNOS4210_RNG_STATUS:
> + /* For clearing status fields */
> + s->reg_status &= ~EXYNOS4210_RNG_STATUS_WRITE_MASK;
> + s->reg_status |= val & EXYNOS4210_RNG_STATUS_WRITE_MASK;
> + break;
> +
> + case EXYNOS4210_RNG_SEED_IN_OFFSET(0):
> + case EXYNOS4210_RNG_SEED_IN_OFFSET(1):
> + case EXYNOS4210_RNG_SEED_IN_OFFSET(2):
> + case EXYNOS4210_RNG_SEED_IN_OFFSET(3):
> + case EXYNOS4210_RNG_SEED_IN_OFFSET(4):
> + exynos4210_rng_set_seed(s,
> + (offset - EXYNOS4210_RNG_SEED_IN_OFFSET(0))
> / 4,
> + val);
> + break;
> +
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: bad write offset 0x%" HWADDR_PRIx "\n",
> + __func__, offset);
> + }
> +}
> +
> +static const MemoryRegionOps exynos4210_rng_ops = {
> + .read = exynos4210_rng_read,
> + .write = exynos4210_rng_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +static void exynos4210_rng_reset(DeviceState *dev)
> +{
> + Exynos4210RngState *s = EXYNOS4210_RNG(dev);
> +
> + s->reg_control = 0;
> + s->reg_status = EXYNOS4210_RNG_STATUS_BUFFER_READY;
> + memset(s->randr_value, 0, sizeof(s->randr_value));
> + s->seed_set = 0;
> +}
> +
> +static void exynos4210_rng_init(Object *obj)
> +{
> + Exynos4210RngState *s = EXYNOS4210_RNG(obj);
> + SysBusDevice *dev = SYS_BUS_DEVICE(obj);
> +
> + memory_region_init_io(&s->iomem, obj, &exynos4210_rng_ops, s,
> + TYPE_EXYNOS4210_RNG, EXYNOS4210_RNG_REGS_MEM_SIZE);
> + sysbus_init_mmio(dev, &s->iomem);
> +}
> +
> +static const VMStateDescription exynos4210_rng_vmstate = {
> + .name = TYPE_EXYNOS4210_RNG,
> + .version_id = 1,
> + .minimum_version_id = 1,
> + .fields = (VMStateField[]) {
> + VMSTATE_INT32_ARRAY(randr_value, Exynos4210RngState,
> + EXYNOS4210_RNG_PRNG_NUM),
> + VMSTATE_UINT32(seed_set, Exynos4210RngState),
> + VMSTATE_UINT32(reg_status, Exynos4210RngState),
> + VMSTATE_UINT32(reg_control, Exynos4210RngState),
> + VMSTATE_END_OF_LIST()
> + }
> +};
> +
> +static void exynos4210_rng_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> +
> + dc->reset = exynos4210_rng_reset;
> + dc->vmsd = &exynos4210_rng_vmstate;
> +}
> +
> +static const TypeInfo exynos4210_rng_info = {
> + .name = TYPE_EXYNOS4210_RNG,
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(Exynos4210RngState),
> + .instance_init = exynos4210_rng_init,
> + .class_init = exynos4210_rng_class_init,
> +};
> +
> +static void exynos4210_rng_register(void)
> +{
> + type_register_static(&exynos4210_rng_info);
> +}
> +
> +type_init(exynos4210_rng_register)
> --
> 2.9.3
>
- Re: [Qemu-devel] [PATCH v3] hw/misc: Add Exynos4210 Pseudo Random Number Generator,
Peter Maydell <=