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