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 "hw/qdev-properties.h"
16 #include "hw/sysbus.h"
17 #include "qapi/error.h"
18 #include "qemu/units.h"
19 #include "sysemu/qtest.h"
20 #include "hw/mem/sparse-mem.h"
22 #define SPARSE_MEM(obj) OBJECT_CHECK(SparseMemState, (obj), TYPE_SPARSE_MEM)
23 #define SPARSE_BLOCK_SIZE 0x1000
25 typedef struct SparseMemState
{
26 SysBusDevice parent_obj
;
35 typedef struct sparse_mem_block
{
36 uint8_t data
[SPARSE_BLOCK_SIZE
];
39 static uint64_t sparse_mem_read(void *opaque
, hwaddr addr
, unsigned int size
)
41 SparseMemState
*s
= opaque
;
43 size_t pfn
= addr
/ SPARSE_BLOCK_SIZE
;
44 size_t offset
= addr
% SPARSE_BLOCK_SIZE
;
45 sparse_mem_block
*block
;
47 block
= g_hash_table_lookup(s
->mapped
, (void *)pfn
);
49 assert(offset
+ size
<= sizeof(block
->data
));
50 memcpy(&ret
, block
->data
+ offset
, size
);
55 static void sparse_mem_write(void *opaque
, hwaddr addr
, uint64_t v
,
58 SparseMemState
*s
= opaque
;
59 size_t pfn
= addr
/ SPARSE_BLOCK_SIZE
;
60 size_t offset
= addr
% SPARSE_BLOCK_SIZE
;
61 sparse_mem_block
*block
;
63 if (!g_hash_table_lookup(s
->mapped
, (void *)pfn
) &&
64 s
->size_used
+ SPARSE_BLOCK_SIZE
< s
->maxsize
&& v
) {
65 g_hash_table_insert(s
->mapped
, (void *)pfn
,
66 g_new0(sparse_mem_block
, 1));
67 s
->size_used
+= sizeof(block
->data
);
69 block
= g_hash_table_lookup(s
->mapped
, (void *)pfn
);
74 assert(offset
+ size
<= sizeof(block
->data
));
76 memcpy(block
->data
+ offset
, &v
, size
);
80 static const MemoryRegionOps sparse_mem_ops
= {
81 .read
= sparse_mem_read
,
82 .write
= sparse_mem_write
,
83 .endianness
= DEVICE_LITTLE_ENDIAN
,
91 static Property sparse_mem_properties
[] = {
92 /* The base address of the memory */
93 DEFINE_PROP_UINT64("baseaddr", SparseMemState
, baseaddr
, 0x0),
94 /* The length of the sparse memory region */
95 DEFINE_PROP_UINT64("length", SparseMemState
, length
, UINT64_MAX
),
96 /* Max amount of actual memory that can be used to back the sparse memory */
97 DEFINE_PROP_UINT64("maxsize", SparseMemState
, maxsize
, 10 * MiB
),
98 DEFINE_PROP_END_OF_LIST(),
101 MemoryRegion
*sparse_mem_init(uint64_t addr
, uint64_t length
)
105 dev
= qdev_new(TYPE_SPARSE_MEM
);
106 qdev_prop_set_uint64(dev
, "baseaddr", addr
);
107 qdev_prop_set_uint64(dev
, "length", length
);
108 sysbus_realize_and_unref(SYS_BUS_DEVICE(dev
), &error_fatal
);
109 sysbus_mmio_map_overlap(SYS_BUS_DEVICE(dev
), 0, addr
, -10000);
110 return &SPARSE_MEM(dev
)->mmio
;
113 static void sparse_mem_realize(DeviceState
*dev
, Error
**errp
)
115 SparseMemState
*s
= SPARSE_MEM(dev
);
116 SysBusDevice
*sbd
= SYS_BUS_DEVICE(dev
);
118 if (!qtest_enabled()) {
119 error_setg(errp
, "sparse_mem device should only be used "
120 "for testing with QTest");
124 assert(s
->baseaddr
+ s
->length
> s
->baseaddr
);
126 s
->mapped
= g_hash_table_new(NULL
, NULL
);
127 memory_region_init_io(&s
->mmio
, OBJECT(s
), &sparse_mem_ops
, s
,
128 "sparse-mem", s
->length
);
129 sysbus_init_mmio(sbd
, &s
->mmio
);
132 static void sparse_mem_class_init(ObjectClass
*klass
, void *data
)
134 DeviceClass
*dc
= DEVICE_CLASS(klass
);
136 device_class_set_props(dc
, sparse_mem_properties
);
138 dc
->desc
= "Sparse Memory Device";
139 dc
->realize
= sparse_mem_realize
;
142 static const TypeInfo sparse_mem_types
[] = {
144 .name
= TYPE_SPARSE_MEM
,
145 .parent
= TYPE_SYS_BUS_DEVICE
,
146 .instance_size
= sizeof(SparseMemState
),
147 .class_init
= sparse_mem_class_init
,
150 DEFINE_TYPES(sparse_mem_types
);