xref: /qemu/hw/mem/sparse-mem.c (revision cc37d98b)
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