qemu/hw/misc/npcm7xx_rng.c
<<
>>
Prefs
   1/*
   2 * Nuvoton NPCM7xx Random Number Generator.
   3 *
   4 * Copyright 2020 Google LLC
   5 *
   6 * This program is free software; you can redistribute it and/or modify it
   7 * under the terms of the GNU General Public License as published by the
   8 * Free Software Foundation; either version 2 of the License, or
   9 * (at your option) any later version.
  10 *
  11 * This program is distributed in the hope that it will be useful, but WITHOUT
  12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  14 * for more details.
  15 */
  16
  17#include "qemu/osdep.h"
  18
  19#include "hw/misc/npcm7xx_rng.h"
  20#include "migration/vmstate.h"
  21#include "qemu/bitops.h"
  22#include "qemu/guest-random.h"
  23#include "qemu/log.h"
  24#include "qemu/module.h"
  25#include "qemu/units.h"
  26
  27#include "trace.h"
  28
  29#define NPCM7XX_RNG_REGS_SIZE   (4 * KiB)
  30
  31#define NPCM7XX_RNGCS           (0x00)
  32#define NPCM7XX_RNGCS_CLKP(rv)      extract32(rv, 2, 4)
  33#define NPCM7XX_RNGCS_DVALID        BIT(1)
  34#define NPCM7XX_RNGCS_RNGE          BIT(0)
  35
  36#define NPCM7XX_RNGD            (0x04)
  37#define NPCM7XX_RNGMODE         (0x08)
  38#define NPCM7XX_RNGMODE_NORMAL      (0x02)
  39
  40static bool npcm7xx_rng_is_enabled(NPCM7xxRNGState *s)
  41{
  42    return (s->rngcs & NPCM7XX_RNGCS_RNGE) &&
  43        (s->rngmode == NPCM7XX_RNGMODE_NORMAL);
  44}
  45
  46static uint64_t npcm7xx_rng_read(void *opaque, hwaddr offset, unsigned size)
  47{
  48    NPCM7xxRNGState *s = opaque;
  49    uint64_t value = 0;
  50
  51    switch (offset) {
  52    case NPCM7XX_RNGCS:
  53        /*
  54         * If the RNG is enabled, but we don't have any valid random data, try
  55         * obtaining some and update the DVALID bit accordingly.
  56         */
  57        if (!npcm7xx_rng_is_enabled(s)) {
  58            s->rngcs &= ~NPCM7XX_RNGCS_DVALID;
  59        } else if (!(s->rngcs & NPCM7XX_RNGCS_DVALID)) {
  60            uint8_t byte = 0;
  61
  62            if (qemu_guest_getrandom(&byte, sizeof(byte), NULL) == 0) {
  63                s->rngd = byte;
  64                s->rngcs |= NPCM7XX_RNGCS_DVALID;
  65            }
  66        }
  67        value = s->rngcs;
  68        break;
  69    case NPCM7XX_RNGD:
  70        if (npcm7xx_rng_is_enabled(s) && s->rngcs & NPCM7XX_RNGCS_DVALID) {
  71            s->rngcs &= ~NPCM7XX_RNGCS_DVALID;
  72            value = s->rngd;
  73            s->rngd = 0;
  74        }
  75        break;
  76    case NPCM7XX_RNGMODE:
  77        value = s->rngmode;
  78        break;
  79
  80    default:
  81        qemu_log_mask(LOG_GUEST_ERROR,
  82                      "%s: read from invalid offset 0x%" HWADDR_PRIx "\n",
  83                      DEVICE(s)->canonical_path, offset);
  84        break;
  85    }
  86
  87    trace_npcm7xx_rng_read(offset, value, size);
  88
  89    return value;
  90}
  91
  92static void npcm7xx_rng_write(void *opaque, hwaddr offset, uint64_t value,
  93                              unsigned size)
  94{
  95    NPCM7xxRNGState *s = opaque;
  96
  97    trace_npcm7xx_rng_write(offset, value, size);
  98
  99    switch (offset) {
 100    case NPCM7XX_RNGCS:
 101        s->rngcs &= NPCM7XX_RNGCS_DVALID;
 102        s->rngcs |= value & ~NPCM7XX_RNGCS_DVALID;
 103        break;
 104    case NPCM7XX_RNGD:
 105        qemu_log_mask(LOG_GUEST_ERROR,
 106                      "%s: write to read-only register @ 0x%" HWADDR_PRIx "\n",
 107                      DEVICE(s)->canonical_path, offset);
 108        break;
 109    case NPCM7XX_RNGMODE:
 110        s->rngmode = value;
 111        break;
 112    default:
 113        qemu_log_mask(LOG_GUEST_ERROR,
 114                      "%s: write to invalid offset 0x%" HWADDR_PRIx "\n",
 115                      DEVICE(s)->canonical_path, offset);
 116        break;
 117    }
 118}
 119
 120static const MemoryRegionOps npcm7xx_rng_ops = {
 121    .read = npcm7xx_rng_read,
 122    .write = npcm7xx_rng_write,
 123    .endianness = DEVICE_LITTLE_ENDIAN,
 124    .valid = {
 125        .min_access_size = 1,
 126        .max_access_size = 4,
 127        .unaligned = false,
 128    },
 129};
 130
 131static void npcm7xx_rng_enter_reset(Object *obj, ResetType type)
 132{
 133    NPCM7xxRNGState *s = NPCM7XX_RNG(obj);
 134
 135    s->rngcs = 0;
 136    s->rngd = 0;
 137    s->rngmode = 0;
 138}
 139
 140static void npcm7xx_rng_init(Object *obj)
 141{
 142    NPCM7xxRNGState *s = NPCM7XX_RNG(obj);
 143
 144    memory_region_init_io(&s->iomem, obj, &npcm7xx_rng_ops, s, "regs",
 145                          NPCM7XX_RNG_REGS_SIZE);
 146    sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
 147}
 148
 149static const VMStateDescription vmstate_npcm7xx_rng = {
 150    .name = "npcm7xx-rng",
 151    .version_id = 0,
 152    .minimum_version_id = 0,
 153    .fields = (VMStateField[]) {
 154        VMSTATE_UINT8(rngcs, NPCM7xxRNGState),
 155        VMSTATE_UINT8(rngd, NPCM7xxRNGState),
 156        VMSTATE_UINT8(rngmode, NPCM7xxRNGState),
 157        VMSTATE_END_OF_LIST(),
 158    },
 159};
 160
 161static void npcm7xx_rng_class_init(ObjectClass *klass, void *data)
 162{
 163    ResettableClass *rc = RESETTABLE_CLASS(klass);
 164    DeviceClass *dc = DEVICE_CLASS(klass);
 165
 166    dc->desc = "NPCM7xx Random Number Generator";
 167    dc->vmsd = &vmstate_npcm7xx_rng;
 168    rc->phases.enter = npcm7xx_rng_enter_reset;
 169}
 170
 171static const TypeInfo npcm7xx_rng_types[] = {
 172    {
 173        .name = TYPE_NPCM7XX_RNG,
 174        .parent = TYPE_SYS_BUS_DEVICE,
 175        .instance_size = sizeof(NPCM7xxRNGState),
 176        .class_init = npcm7xx_rng_class_init,
 177        .instance_init = npcm7xx_rng_init,
 178    },
 179};
 180DEFINE_TYPES(npcm7xx_rng_types);
 181