qemu-arm
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-arm] [PATCH v3] hw/misc: Add Exynos4210 Pseudo Random Number G


From: Peter Maydell
Subject: Re: [Qemu-arm] [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
>



reply via email to

[Prev in Thread] Current Thread [Next in Thread]