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