npcm7xx_rng.c (5154B)
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);