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