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