1230376d2SAlexander Bulekov /*
2230376d2SAlexander Bulekov * A sparse memory device. Useful for fuzzing
3230376d2SAlexander Bulekov *
4230376d2SAlexander Bulekov * Copyright Red Hat Inc., 2021
5230376d2SAlexander Bulekov *
6230376d2SAlexander Bulekov * Authors:
7230376d2SAlexander Bulekov * Alexander Bulekov <alxndr@bu.edu>
8230376d2SAlexander Bulekov *
9230376d2SAlexander Bulekov * This work is licensed under the terms of the GNU GPL, version 2 or later.
10230376d2SAlexander Bulekov * See the COPYING file in the top-level directory.
11230376d2SAlexander Bulekov */
12230376d2SAlexander Bulekov
13230376d2SAlexander Bulekov #include "qemu/osdep.h"
14*cc37d98bSRichard Henderson #include "qemu/error-report.h"
15230376d2SAlexander Bulekov
16230376d2SAlexander Bulekov #include "hw/qdev-properties.h"
17230376d2SAlexander Bulekov #include "hw/sysbus.h"
18230376d2SAlexander Bulekov #include "qapi/error.h"
19230376d2SAlexander Bulekov #include "qemu/units.h"
20230376d2SAlexander Bulekov #include "sysemu/qtest.h"
21230376d2SAlexander Bulekov #include "hw/mem/sparse-mem.h"
22230376d2SAlexander Bulekov
23230376d2SAlexander Bulekov #define SPARSE_MEM(obj) OBJECT_CHECK(SparseMemState, (obj), TYPE_SPARSE_MEM)
24230376d2SAlexander Bulekov #define SPARSE_BLOCK_SIZE 0x1000
25230376d2SAlexander Bulekov
26230376d2SAlexander Bulekov typedef struct SparseMemState {
27230376d2SAlexander Bulekov SysBusDevice parent_obj;
28230376d2SAlexander Bulekov MemoryRegion mmio;
29230376d2SAlexander Bulekov uint64_t baseaddr;
30230376d2SAlexander Bulekov uint64_t length;
31230376d2SAlexander Bulekov uint64_t size_used;
32230376d2SAlexander Bulekov uint64_t maxsize;
33230376d2SAlexander Bulekov GHashTable *mapped;
34230376d2SAlexander Bulekov } SparseMemState;
35230376d2SAlexander Bulekov
36230376d2SAlexander Bulekov typedef struct sparse_mem_block {
37230376d2SAlexander Bulekov uint8_t data[SPARSE_BLOCK_SIZE];
38230376d2SAlexander Bulekov } sparse_mem_block;
39230376d2SAlexander Bulekov
sparse_mem_read(void * opaque,hwaddr addr,unsigned int size)40230376d2SAlexander Bulekov static uint64_t sparse_mem_read(void *opaque, hwaddr addr, unsigned int size)
41230376d2SAlexander Bulekov {
42230376d2SAlexander Bulekov SparseMemState *s = opaque;
43230376d2SAlexander Bulekov uint64_t ret = 0;
44230376d2SAlexander Bulekov size_t pfn = addr / SPARSE_BLOCK_SIZE;
45230376d2SAlexander Bulekov size_t offset = addr % SPARSE_BLOCK_SIZE;
46230376d2SAlexander Bulekov sparse_mem_block *block;
47230376d2SAlexander Bulekov
48230376d2SAlexander Bulekov block = g_hash_table_lookup(s->mapped, (void *)pfn);
49230376d2SAlexander Bulekov if (block) {
50230376d2SAlexander Bulekov assert(offset + size <= sizeof(block->data));
51230376d2SAlexander Bulekov memcpy(&ret, block->data + offset, size);
52230376d2SAlexander Bulekov }
53230376d2SAlexander Bulekov return ret;
54230376d2SAlexander Bulekov }
55230376d2SAlexander Bulekov
sparse_mem_write(void * opaque,hwaddr addr,uint64_t v,unsigned int size)56230376d2SAlexander Bulekov static void sparse_mem_write(void *opaque, hwaddr addr, uint64_t v,
57230376d2SAlexander Bulekov unsigned int size)
58230376d2SAlexander Bulekov {
59230376d2SAlexander Bulekov SparseMemState *s = opaque;
60230376d2SAlexander Bulekov size_t pfn = addr / SPARSE_BLOCK_SIZE;
61230376d2SAlexander Bulekov size_t offset = addr % SPARSE_BLOCK_SIZE;
62230376d2SAlexander Bulekov sparse_mem_block *block;
63230376d2SAlexander Bulekov
64230376d2SAlexander Bulekov if (!g_hash_table_lookup(s->mapped, (void *)pfn) &&
65230376d2SAlexander Bulekov s->size_used + SPARSE_BLOCK_SIZE < s->maxsize && v) {
66230376d2SAlexander Bulekov g_hash_table_insert(s->mapped, (void *)pfn,
67230376d2SAlexander Bulekov g_new0(sparse_mem_block, 1));
68230376d2SAlexander Bulekov s->size_used += sizeof(block->data);
69230376d2SAlexander Bulekov }
70230376d2SAlexander Bulekov block = g_hash_table_lookup(s->mapped, (void *)pfn);
71230376d2SAlexander Bulekov if (!block) {
72230376d2SAlexander Bulekov return;
73230376d2SAlexander Bulekov }
74230376d2SAlexander Bulekov
75230376d2SAlexander Bulekov assert(offset + size <= sizeof(block->data));
76230376d2SAlexander Bulekov
77230376d2SAlexander Bulekov memcpy(block->data + offset, &v, size);
78230376d2SAlexander Bulekov
79230376d2SAlexander Bulekov }
80230376d2SAlexander Bulekov
sparse_mem_enter_reset(Object * obj,ResetType type)8166169c3cSAlexander Bulekov static void sparse_mem_enter_reset(Object *obj, ResetType type)
8266169c3cSAlexander Bulekov {
8366169c3cSAlexander Bulekov SparseMemState *s = SPARSE_MEM(obj);
8466169c3cSAlexander Bulekov g_hash_table_remove_all(s->mapped);
8566169c3cSAlexander Bulekov return;
8666169c3cSAlexander Bulekov }
8766169c3cSAlexander Bulekov
88230376d2SAlexander Bulekov static const MemoryRegionOps sparse_mem_ops = {
89230376d2SAlexander Bulekov .read = sparse_mem_read,
90230376d2SAlexander Bulekov .write = sparse_mem_write,
91230376d2SAlexander Bulekov .endianness = DEVICE_LITTLE_ENDIAN,
92230376d2SAlexander Bulekov .valid = {
93230376d2SAlexander Bulekov .min_access_size = 1,
94230376d2SAlexander Bulekov .max_access_size = 8,
95230376d2SAlexander Bulekov .unaligned = false,
96230376d2SAlexander Bulekov },
97230376d2SAlexander Bulekov };
98230376d2SAlexander Bulekov
99230376d2SAlexander Bulekov static Property sparse_mem_properties[] = {
100230376d2SAlexander Bulekov /* The base address of the memory */
101230376d2SAlexander Bulekov DEFINE_PROP_UINT64("baseaddr", SparseMemState, baseaddr, 0x0),
102230376d2SAlexander Bulekov /* The length of the sparse memory region */
103230376d2SAlexander Bulekov DEFINE_PROP_UINT64("length", SparseMemState, length, UINT64_MAX),
104230376d2SAlexander Bulekov /* Max amount of actual memory that can be used to back the sparse memory */
105230376d2SAlexander Bulekov DEFINE_PROP_UINT64("maxsize", SparseMemState, maxsize, 10 * MiB),
106230376d2SAlexander Bulekov DEFINE_PROP_END_OF_LIST(),
107230376d2SAlexander Bulekov };
108230376d2SAlexander Bulekov
sparse_mem_init(uint64_t addr,uint64_t length)109230376d2SAlexander Bulekov MemoryRegion *sparse_mem_init(uint64_t addr, uint64_t length)
110230376d2SAlexander Bulekov {
111230376d2SAlexander Bulekov DeviceState *dev;
112230376d2SAlexander Bulekov
113230376d2SAlexander Bulekov dev = qdev_new(TYPE_SPARSE_MEM);
114230376d2SAlexander Bulekov qdev_prop_set_uint64(dev, "baseaddr", addr);
115230376d2SAlexander Bulekov qdev_prop_set_uint64(dev, "length", length);
116230376d2SAlexander Bulekov sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
117230376d2SAlexander Bulekov sysbus_mmio_map_overlap(SYS_BUS_DEVICE(dev), 0, addr, -10000);
118230376d2SAlexander Bulekov return &SPARSE_MEM(dev)->mmio;
119230376d2SAlexander Bulekov }
120230376d2SAlexander Bulekov
sparse_mem_realize(DeviceState * dev,Error ** errp)121230376d2SAlexander Bulekov static void sparse_mem_realize(DeviceState *dev, Error **errp)
122230376d2SAlexander Bulekov {
123230376d2SAlexander Bulekov SparseMemState *s = SPARSE_MEM(dev);
124230376d2SAlexander Bulekov SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
125230376d2SAlexander Bulekov
126230376d2SAlexander Bulekov if (!qtest_enabled()) {
127230376d2SAlexander Bulekov error_setg(errp, "sparse_mem device should only be used "
128230376d2SAlexander Bulekov "for testing with QTest");
129230376d2SAlexander Bulekov return;
130230376d2SAlexander Bulekov }
131230376d2SAlexander Bulekov
132230376d2SAlexander Bulekov assert(s->baseaddr + s->length > s->baseaddr);
133230376d2SAlexander Bulekov
13466169c3cSAlexander Bulekov s->mapped = g_hash_table_new_full(NULL, NULL, NULL,
13566169c3cSAlexander Bulekov (GDestroyNotify)g_free);
136230376d2SAlexander Bulekov memory_region_init_io(&s->mmio, OBJECT(s), &sparse_mem_ops, s,
137230376d2SAlexander Bulekov "sparse-mem", s->length);
138230376d2SAlexander Bulekov sysbus_init_mmio(sbd, &s->mmio);
139230376d2SAlexander Bulekov }
140230376d2SAlexander Bulekov
sparse_mem_class_init(ObjectClass * klass,void * data)141230376d2SAlexander Bulekov static void sparse_mem_class_init(ObjectClass *klass, void *data)
142230376d2SAlexander Bulekov {
14366169c3cSAlexander Bulekov ResettableClass *rc = RESETTABLE_CLASS(klass);
144230376d2SAlexander Bulekov DeviceClass *dc = DEVICE_CLASS(klass);
145230376d2SAlexander Bulekov
146230376d2SAlexander Bulekov device_class_set_props(dc, sparse_mem_properties);
147230376d2SAlexander Bulekov
148230376d2SAlexander Bulekov dc->desc = "Sparse Memory Device";
149230376d2SAlexander Bulekov dc->realize = sparse_mem_realize;
15066169c3cSAlexander Bulekov
15166169c3cSAlexander Bulekov rc->phases.enter = sparse_mem_enter_reset;
152230376d2SAlexander Bulekov }
153230376d2SAlexander Bulekov
154230376d2SAlexander Bulekov static const TypeInfo sparse_mem_types[] = {
155230376d2SAlexander Bulekov {
156230376d2SAlexander Bulekov .name = TYPE_SPARSE_MEM,
157230376d2SAlexander Bulekov .parent = TYPE_SYS_BUS_DEVICE,
158230376d2SAlexander Bulekov .instance_size = sizeof(SparseMemState),
159230376d2SAlexander Bulekov .class_init = sparse_mem_class_init,
160230376d2SAlexander Bulekov },
161230376d2SAlexander Bulekov };
162230376d2SAlexander Bulekov DEFINE_TYPES(sparse_mem_types);
163