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: Krzysztof Kozlowski
Subject: Re: [Qemu-arm] [PATCH v3] hw/misc: Add Exynos4210 Pseudo Random Number Generator
Date: Tue, 13 Jun 2017 20:08:55 +0200
User-agent: Mutt/1.6.2-neo (2016-08-21)

On Tue, Jun 13, 2017 at 07:06:20PM +0100, Peter Maydell wrote:
> 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?

You had some comments on v2 which I hope I resolved. From my point of
view, it is ready to apply or final review.


Best regards,
Krzysztof

> 
> 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]