summaryrefslogtreecommitdiffstats
path: root/hw/mem/memory-device.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/mem/memory-device.c')
-rw-r--r--hw/mem/memory-device.c86
1 files changed, 86 insertions, 0 deletions
diff --git a/hw/mem/memory-device.c b/hw/mem/memory-device.c
index 6cbdaf99f3..a2cb85462f 100644
--- a/hw/mem/memory-device.c
+++ b/hw/mem/memory-device.c
@@ -48,6 +48,92 @@ static int memory_device_build_list(Object *obj, void *opaque)
return 0;
}
+uint64_t memory_device_get_free_addr(MachineState *ms, const uint64_t *hint,
+ uint64_t align, uint64_t size,
+ Error **errp)
+{
+ uint64_t address_space_start, address_space_end;
+ GSList *list = NULL, *item;
+ uint64_t new_addr = 0;
+
+ if (!ms->device_memory) {
+ error_setg(errp, "memory devices (e.g. for memory hotplug) are not "
+ "supported by the machine");
+ return 0;
+ }
+
+ if (!memory_region_size(&ms->device_memory->mr)) {
+ error_setg(errp, "memory devices (e.g. for memory hotplug) are not "
+ "enabled, please specify the maxmem option");
+ return 0;
+ }
+ address_space_start = ms->device_memory->base;
+ address_space_end = address_space_start +
+ memory_region_size(&ms->device_memory->mr);
+ g_assert(QEMU_ALIGN_UP(address_space_start, align) == address_space_start);
+ g_assert(address_space_end >= address_space_start);
+
+ if (hint && QEMU_ALIGN_UP(*hint, align) != *hint) {
+ error_setg(errp, "address must be aligned to 0x%" PRIx64 " bytes",
+ align);
+ return 0;
+ }
+
+ if (QEMU_ALIGN_UP(size, align) != size) {
+ error_setg(errp, "backend memory size must be multiple of 0x%"
+ PRIx64, align);
+ return 0;
+ }
+
+ if (hint) {
+ new_addr = *hint;
+ if (new_addr < address_space_start) {
+ error_setg(errp, "can't add memory [0x%" PRIx64 ":0x%" PRIx64
+ "] at 0x%" PRIx64, new_addr, size, address_space_start);
+ return 0;
+ } else if ((new_addr + size) > address_space_end) {
+ error_setg(errp, "can't add memory [0x%" PRIx64 ":0x%" PRIx64
+ "] beyond 0x%" PRIx64, new_addr, size,
+ address_space_end);
+ return 0;
+ }
+ } else {
+ new_addr = address_space_start;
+ }
+
+ /* find address range that will fit new memory device */
+ object_child_foreach(OBJECT(ms), memory_device_build_list, &list);
+ for (item = list; item; item = g_slist_next(item)) {
+ const MemoryDeviceState *md = item->data;
+ const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(OBJECT(md));
+ uint64_t md_size, md_addr;
+
+ md_addr = mdc->get_addr(md);
+ md_size = mdc->get_region_size(md);
+ if (*errp) {
+ goto out;
+ }
+
+ if (ranges_overlap(md_addr, md_size, new_addr, size)) {
+ if (hint) {
+ const DeviceState *d = DEVICE(md);
+ error_setg(errp, "address range conflicts with '%s'", d->id);
+ goto out;
+ }
+ new_addr = QEMU_ALIGN_UP(md_addr + md_size, align);
+ }
+ }
+
+ if (new_addr + size > address_space_end) {
+ error_setg(errp, "could not find position in guest address space for "
+ "memory device - memory fragmented due to alignments");
+ goto out;
+ }
+out:
+ g_slist_free(list);
+ return new_addr;
+}
+
MemoryDeviceInfoList *qmp_memory_device_list(void)
{
GSList *devices = NULL, *item;