2 * A sparse memory device. Useful for fuzzing
4 * Copyright Red Hat Inc., 2021
7 * Alexander Bulekov <alxndr@bu.edu>
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.
13 #include "qemu/osdep.h"
15 #include "exec/address-spaces.h"
16 #include "hw/qdev-properties.h"
17 #include "hw/sysbus.h"
18 #include "qapi/error.h"
19 #include "qemu/units.h"
20 #include "sysemu/qtest.h"
21 #include "hw/mem/sparse-mem.h"
23 #define SPARSE_MEM(obj) OBJECT_CHECK(SparseMemState, (obj), TYPE_SPARSE_MEM)
24 #define SPARSE_BLOCK_SIZE 0x1000
26 typedef struct SparseMemState
{
27 SysBusDevice parent_obj
;
36 typedef struct sparse_mem_block
{
37 uint8_t data
[SPARSE_BLOCK_SIZE
];
40 static uint64_t sparse_mem_read(void *opaque
, hwaddr addr
, unsigned int size
)
42 SparseMemState
*s
= opaque
;
44 size_t pfn
= addr
/ SPARSE_BLOCK_SIZE
;
45 size_t offset
= addr
% SPARSE_BLOCK_SIZE
;
46 sparse_mem_block
*block
;
48 block
= g_hash_table_lookup(s
->mapped
, (void *)pfn
);
50 assert(offset
+ size
<= sizeof(block
->data
));
51 memcpy(&ret
, block
->data
+ offset
, size
);
56 static void sparse_mem_write(void *opaque
, hwaddr addr
, uint64_t v
,
59 SparseMemState
*s
= opaque
;
60 size_t pfn
= addr
/ SPARSE_BLOCK_SIZE
;
61 size_t offset
= addr
% SPARSE_BLOCK_SIZE
;
62 sparse_mem_block
*block
;
64 if (!g_hash_table_lookup(s
->mapped
, (void *)pfn
) &&
65 s
->size_used
+ SPARSE_BLOCK_SIZE
< s
->maxsize
&& v
) {
66 g_hash_table_insert(s
->mapped
, (void *)pfn
,
67 g_new0(sparse_mem_block
, 1));
68 s
->size_used
+= sizeof(block
->data
);
70 block
= g_hash_table_lookup(s
->mapped
, (void *)pfn
);
75 assert(offset
+ size
<= sizeof(block
->data
));
77 memcpy(block
->data
+ offset
, &v
, size
);
81 static const MemoryRegionOps sparse_mem_ops
= {
82 .read
= sparse_mem_read
,
83 .write
= sparse_mem_write
,
84 .endianness
= DEVICE_LITTLE_ENDIAN
,
92 static Property sparse_mem_properties
[] = {
93 /* The base address of the memory */
94 DEFINE_PROP_UINT64("baseaddr", SparseMemState
, baseaddr
, 0x0),
95 /* The length of the sparse memory region */
96 DEFINE_PROP_UINT64("length", SparseMemState
, length
, UINT64_MAX
),
97 /* Max amount of actual memory that can be used to back the sparse memory */
98 DEFINE_PROP_UINT64("maxsize", SparseMemState
, maxsize
, 10 * MiB
),
99 DEFINE_PROP_END_OF_LIST(),
102 MemoryRegion
*sparse_mem_init(uint64_t addr
, uint64_t length
)
106 dev
= qdev_new(TYPE_SPARSE_MEM
);
107 qdev_prop_set_uint64(dev
, "baseaddr", addr
);
108 qdev_prop_set_uint64(dev
, "length", length
);
109 sysbus_realize_and_unref(SYS_BUS_DEVICE(dev
), &error_fatal
);
110 sysbus_mmio_map_overlap(SYS_BUS_DEVICE(dev
), 0, addr
, -10000);
111 return &SPARSE_MEM(dev
)->mmio
;
114 static void sparse_mem_realize(DeviceState
*dev
, Error
**errp
)
116 SparseMemState
*s
= SPARSE_MEM(dev
);
117 SysBusDevice
*sbd
= SYS_BUS_DEVICE(dev
);
119 if (!qtest_enabled()) {
120 error_setg(errp
, "sparse_mem device should only be used "
121 "for testing with QTest");
125 assert(s
->baseaddr
+ s
->length
> s
->baseaddr
);
127 s
->mapped
= g_hash_table_new(NULL
, NULL
);
128 memory_region_init_io(&s
->mmio
, OBJECT(s
), &sparse_mem_ops
, s
,
129 "sparse-mem", s
->length
);
130 sysbus_init_mmio(sbd
, &s
->mmio
);
133 static void sparse_mem_class_init(ObjectClass
*klass
, void *data
)
135 DeviceClass
*dc
= DEVICE_CLASS(klass
);
137 device_class_set_props(dc
, sparse_mem_properties
);
139 dc
->desc
= "Sparse Memory Device";
140 dc
->realize
= sparse_mem_realize
;
143 static const TypeInfo sparse_mem_types
[] = {
145 .name
= TYPE_SPARSE_MEM
,
146 .parent
= TYPE_SYS_BUS_DEVICE
,
147 .instance_size
= sizeof(SparseMemState
),
148 .class_init
= sparse_mem_class_init
,
151 DEFINE_TYPES(sparse_mem_types
);