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