1*230376d2SAlexander Bulekov /* 2*230376d2SAlexander Bulekov * A sparse memory device. Useful for fuzzing 3*230376d2SAlexander Bulekov * 4*230376d2SAlexander Bulekov * Copyright Red Hat Inc., 2021 5*230376d2SAlexander Bulekov * 6*230376d2SAlexander Bulekov * Authors: 7*230376d2SAlexander Bulekov * Alexander Bulekov <alxndr@bu.edu> 8*230376d2SAlexander Bulekov * 9*230376d2SAlexander Bulekov * This work is licensed under the terms of the GNU GPL, version 2 or later. 10*230376d2SAlexander Bulekov * See the COPYING file in the top-level directory. 11*230376d2SAlexander Bulekov */ 12*230376d2SAlexander Bulekov 13*230376d2SAlexander Bulekov #include "qemu/osdep.h" 14*230376d2SAlexander Bulekov 15*230376d2SAlexander Bulekov #include "exec/address-spaces.h" 16*230376d2SAlexander Bulekov #include "hw/qdev-properties.h" 17*230376d2SAlexander Bulekov #include "hw/sysbus.h" 18*230376d2SAlexander Bulekov #include "qapi/error.h" 19*230376d2SAlexander Bulekov #include "qemu/units.h" 20*230376d2SAlexander Bulekov #include "sysemu/qtest.h" 21*230376d2SAlexander Bulekov #include "hw/mem/sparse-mem.h" 22*230376d2SAlexander Bulekov 23*230376d2SAlexander Bulekov #define SPARSE_MEM(obj) OBJECT_CHECK(SparseMemState, (obj), TYPE_SPARSE_MEM) 24*230376d2SAlexander Bulekov #define SPARSE_BLOCK_SIZE 0x1000 25*230376d2SAlexander Bulekov 26*230376d2SAlexander Bulekov typedef struct SparseMemState { 27*230376d2SAlexander Bulekov SysBusDevice parent_obj; 28*230376d2SAlexander Bulekov MemoryRegion mmio; 29*230376d2SAlexander Bulekov uint64_t baseaddr; 30*230376d2SAlexander Bulekov uint64_t length; 31*230376d2SAlexander Bulekov uint64_t size_used; 32*230376d2SAlexander Bulekov uint64_t maxsize; 33*230376d2SAlexander Bulekov GHashTable *mapped; 34*230376d2SAlexander Bulekov } SparseMemState; 35*230376d2SAlexander Bulekov 36*230376d2SAlexander Bulekov typedef struct sparse_mem_block { 37*230376d2SAlexander Bulekov uint8_t data[SPARSE_BLOCK_SIZE]; 38*230376d2SAlexander Bulekov } sparse_mem_block; 39*230376d2SAlexander Bulekov 40*230376d2SAlexander Bulekov static uint64_t sparse_mem_read(void *opaque, hwaddr addr, unsigned int size) 41*230376d2SAlexander Bulekov { 42*230376d2SAlexander Bulekov SparseMemState *s = opaque; 43*230376d2SAlexander Bulekov uint64_t ret = 0; 44*230376d2SAlexander Bulekov size_t pfn = addr / SPARSE_BLOCK_SIZE; 45*230376d2SAlexander Bulekov size_t offset = addr % SPARSE_BLOCK_SIZE; 46*230376d2SAlexander Bulekov sparse_mem_block *block; 47*230376d2SAlexander Bulekov 48*230376d2SAlexander Bulekov block = g_hash_table_lookup(s->mapped, (void *)pfn); 49*230376d2SAlexander Bulekov if (block) { 50*230376d2SAlexander Bulekov assert(offset + size <= sizeof(block->data)); 51*230376d2SAlexander Bulekov memcpy(&ret, block->data + offset, size); 52*230376d2SAlexander Bulekov } 53*230376d2SAlexander Bulekov return ret; 54*230376d2SAlexander Bulekov } 55*230376d2SAlexander Bulekov 56*230376d2SAlexander Bulekov static void sparse_mem_write(void *opaque, hwaddr addr, uint64_t v, 57*230376d2SAlexander Bulekov unsigned int size) 58*230376d2SAlexander Bulekov { 59*230376d2SAlexander Bulekov SparseMemState *s = opaque; 60*230376d2SAlexander Bulekov size_t pfn = addr / SPARSE_BLOCK_SIZE; 61*230376d2SAlexander Bulekov size_t offset = addr % SPARSE_BLOCK_SIZE; 62*230376d2SAlexander Bulekov sparse_mem_block *block; 63*230376d2SAlexander Bulekov 64*230376d2SAlexander Bulekov if (!g_hash_table_lookup(s->mapped, (void *)pfn) && 65*230376d2SAlexander Bulekov s->size_used + SPARSE_BLOCK_SIZE < s->maxsize && v) { 66*230376d2SAlexander Bulekov g_hash_table_insert(s->mapped, (void *)pfn, 67*230376d2SAlexander Bulekov g_new0(sparse_mem_block, 1)); 68*230376d2SAlexander Bulekov s->size_used += sizeof(block->data); 69*230376d2SAlexander Bulekov } 70*230376d2SAlexander Bulekov block = g_hash_table_lookup(s->mapped, (void *)pfn); 71*230376d2SAlexander Bulekov if (!block) { 72*230376d2SAlexander Bulekov return; 73*230376d2SAlexander Bulekov } 74*230376d2SAlexander Bulekov 75*230376d2SAlexander Bulekov assert(offset + size <= sizeof(block->data)); 76*230376d2SAlexander Bulekov 77*230376d2SAlexander Bulekov memcpy(block->data + offset, &v, size); 78*230376d2SAlexander Bulekov 79*230376d2SAlexander Bulekov } 80*230376d2SAlexander Bulekov 81*230376d2SAlexander Bulekov static const MemoryRegionOps sparse_mem_ops = { 82*230376d2SAlexander Bulekov .read = sparse_mem_read, 83*230376d2SAlexander Bulekov .write = sparse_mem_write, 84*230376d2SAlexander Bulekov .endianness = DEVICE_LITTLE_ENDIAN, 85*230376d2SAlexander Bulekov .valid = { 86*230376d2SAlexander Bulekov .min_access_size = 1, 87*230376d2SAlexander Bulekov .max_access_size = 8, 88*230376d2SAlexander Bulekov .unaligned = false, 89*230376d2SAlexander Bulekov }, 90*230376d2SAlexander Bulekov }; 91*230376d2SAlexander Bulekov 92*230376d2SAlexander Bulekov static Property sparse_mem_properties[] = { 93*230376d2SAlexander Bulekov /* The base address of the memory */ 94*230376d2SAlexander Bulekov DEFINE_PROP_UINT64("baseaddr", SparseMemState, baseaddr, 0x0), 95*230376d2SAlexander Bulekov /* The length of the sparse memory region */ 96*230376d2SAlexander Bulekov DEFINE_PROP_UINT64("length", SparseMemState, length, UINT64_MAX), 97*230376d2SAlexander Bulekov /* Max amount of actual memory that can be used to back the sparse memory */ 98*230376d2SAlexander Bulekov DEFINE_PROP_UINT64("maxsize", SparseMemState, maxsize, 10 * MiB), 99*230376d2SAlexander Bulekov DEFINE_PROP_END_OF_LIST(), 100*230376d2SAlexander Bulekov }; 101*230376d2SAlexander Bulekov 102*230376d2SAlexander Bulekov MemoryRegion *sparse_mem_init(uint64_t addr, uint64_t length) 103*230376d2SAlexander Bulekov { 104*230376d2SAlexander Bulekov DeviceState *dev; 105*230376d2SAlexander Bulekov 106*230376d2SAlexander Bulekov dev = qdev_new(TYPE_SPARSE_MEM); 107*230376d2SAlexander Bulekov qdev_prop_set_uint64(dev, "baseaddr", addr); 108*230376d2SAlexander Bulekov qdev_prop_set_uint64(dev, "length", length); 109*230376d2SAlexander Bulekov sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); 110*230376d2SAlexander Bulekov sysbus_mmio_map_overlap(SYS_BUS_DEVICE(dev), 0, addr, -10000); 111*230376d2SAlexander Bulekov return &SPARSE_MEM(dev)->mmio; 112*230376d2SAlexander Bulekov } 113*230376d2SAlexander Bulekov 114*230376d2SAlexander Bulekov static void sparse_mem_realize(DeviceState *dev, Error **errp) 115*230376d2SAlexander Bulekov { 116*230376d2SAlexander Bulekov SparseMemState *s = SPARSE_MEM(dev); 117*230376d2SAlexander Bulekov SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 118*230376d2SAlexander Bulekov 119*230376d2SAlexander Bulekov if (!qtest_enabled()) { 120*230376d2SAlexander Bulekov error_setg(errp, "sparse_mem device should only be used " 121*230376d2SAlexander Bulekov "for testing with QTest"); 122*230376d2SAlexander Bulekov return; 123*230376d2SAlexander Bulekov } 124*230376d2SAlexander Bulekov 125*230376d2SAlexander Bulekov assert(s->baseaddr + s->length > s->baseaddr); 126*230376d2SAlexander Bulekov 127*230376d2SAlexander Bulekov s->mapped = g_hash_table_new(NULL, NULL); 128*230376d2SAlexander Bulekov memory_region_init_io(&s->mmio, OBJECT(s), &sparse_mem_ops, s, 129*230376d2SAlexander Bulekov "sparse-mem", s->length); 130*230376d2SAlexander Bulekov sysbus_init_mmio(sbd, &s->mmio); 131*230376d2SAlexander Bulekov } 132*230376d2SAlexander Bulekov 133*230376d2SAlexander Bulekov static void sparse_mem_class_init(ObjectClass *klass, void *data) 134*230376d2SAlexander Bulekov { 135*230376d2SAlexander Bulekov DeviceClass *dc = DEVICE_CLASS(klass); 136*230376d2SAlexander Bulekov 137*230376d2SAlexander Bulekov device_class_set_props(dc, sparse_mem_properties); 138*230376d2SAlexander Bulekov 139*230376d2SAlexander Bulekov dc->desc = "Sparse Memory Device"; 140*230376d2SAlexander Bulekov dc->realize = sparse_mem_realize; 141*230376d2SAlexander Bulekov } 142*230376d2SAlexander Bulekov 143*230376d2SAlexander Bulekov static const TypeInfo sparse_mem_types[] = { 144*230376d2SAlexander Bulekov { 145*230376d2SAlexander Bulekov .name = TYPE_SPARSE_MEM, 146*230376d2SAlexander Bulekov .parent = TYPE_SYS_BUS_DEVICE, 147*230376d2SAlexander Bulekov .instance_size = sizeof(SparseMemState), 148*230376d2SAlexander Bulekov .class_init = sparse_mem_class_init, 149*230376d2SAlexander Bulekov }, 150*230376d2SAlexander Bulekov }; 151*230376d2SAlexander Bulekov DEFINE_TYPES(sparse_mem_types); 152