Message ID | 20231114163123.74888-8-philmd@linaro.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | hw/xen: Have ARM targets use common xen_memory_listener | expand |
On Tue, 14 Nov 2023 18:31, Philippe Mathieu-Daudé <philmd@linaro.org> wrote: >Extract non-x86 specific code out of xen-hvm.c, >to xen-hvm-common.c. For now this new file is >only build for x86 targets. s/build/built > >Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> >--- > hw/i386/xen/xen-hvm-common.c | 473 +++++++++++++++++++++++++++++++++++ > hw/i386/xen/xen-hvm.c | 459 --------------------------------- > hw/i386/xen/meson.build | 1 + > 3 files changed, 474 insertions(+), 459 deletions(-) > create mode 100644 hw/i386/xen/xen-hvm-common.c > >diff --git a/hw/i386/xen/xen-hvm-common.c b/hw/i386/xen/xen-hvm-common.c >new file mode 100644 >index 0000000000..e8ef0e0c94 >--- /dev/null >+++ b/hw/i386/xen/xen-hvm-common.c >@@ -0,0 +1,473 @@ >+/* >+ * Copyright (C) 2010 Citrix Ltd. >+ * >+ * This work is licensed under the terms of the GNU GPL, version 2. See >+ * the COPYING file in the top-level directory. >+ * >+ * Contributions after 2012-01-13 are licensed under the terms of the >+ * GNU GPL, version 2 or (at your option) any later version. >+ */ >+ >+#include "qemu/osdep.h" >+#include "qemu/range.h" >+#include "qapi/qapi-commands-migration.h" >+#include "exec/target_page.h" >+#include "hw/xen/xen-hvm-common.h" >+#include "trace.h" >+ >+static MemoryRegion *framebuffer; >+static bool xen_in_migration; >+ >+static QLIST_HEAD(, XenPhysmap) xen_physmap; >+static const XenPhysmap *log_for_dirtybit; >+/* Buffer used by xen_sync_dirty_bitmap */ >+static unsigned long *dirty_bitmap; >+ >+static XenPhysmap *get_physmapping(hwaddr start_addr, ram_addr_t size, >+ int page_mask) >+{ >+ XenPhysmap *physmap = NULL; >+ >+ start_addr &= -page_mask; >+ >+ QLIST_FOREACH(physmap, &xen_physmap, list) { >+ if (range_covers_byte(physmap->start_addr, physmap->size, start_addr)) { >+ return physmap; >+ } >+ } >+ return NULL; >+} >+ >+static hwaddr xen_phys_offset_to_gaddr(hwaddr phys_offset, ram_addr_t size, >+ int page_mask) >+{ >+ hwaddr addr = phys_offset & -page_mask; >+ XenPhysmap *physmap = NULL; >+ >+ QLIST_FOREACH(physmap, &xen_physmap, list) { >+ if (range_covers_byte(physmap->phys_offset, physmap->size, addr)) { >+ return physmap->start_addr + (phys_offset - physmap->phys_offset); >+ } >+ } >+ >+ return phys_offset; >+} >+ >+#ifdef XEN_COMPAT_PHYSMAP >+static int xen_save_physmap(XenIOState *state, XenPhysmap *physmap) >+{ >+ char path[80], value[17]; >+ >+ snprintf(path, sizeof(path), >+ "/local/domain/0/device-model/%d/physmap/%"PRIx64"/start_addr", >+ xen_domid, (uint64_t)physmap->phys_offset); >+ snprintf(value, sizeof(value), "%"PRIx64, (uint64_t)physmap->start_addr); >+ if (!xs_write(state->xenstore, 0, path, value, strlen(value))) { >+ return -1; >+ } >+ snprintf(path, sizeof(path), >+ "/local/domain/0/device-model/%d/physmap/%"PRIx64"/size", >+ xen_domid, (uint64_t)physmap->phys_offset); >+ snprintf(value, sizeof(value), "%"PRIx64, (uint64_t)physmap->size); >+ if (!xs_write(state->xenstore, 0, path, value, strlen(value))) { >+ return -1; >+ } >+ if (physmap->name) { >+ snprintf(path, sizeof(path), >+ "/local/domain/0/device-model/%d/physmap/%"PRIx64"/name", >+ xen_domid, (uint64_t)physmap->phys_offset); >+ if (!xs_write(state->xenstore, 0, path, >+ physmap->name, strlen(physmap->name))) { >+ return -1; >+ } >+ } >+ return 0; >+} >+#else >+static int xen_save_physmap(XenIOState *state, XenPhysmap *physmap) >+{ >+ return 0; >+} >+#endif >+ >+static int xen_add_to_physmap(XenIOState *state, >+ hwaddr start_addr, >+ ram_addr_t size, >+ MemoryRegion *mr, >+ hwaddr offset_within_region) >+{ >+ unsigned target_page_bits = qemu_target_page_bits(); >+ int page_size = qemu_target_page_size(); >+ int page_mask = -page_size; >+ unsigned long nr_pages; >+ int rc = 0; >+ XenPhysmap *physmap = NULL; >+ hwaddr pfn, start_gpfn; >+ hwaddr phys_offset = memory_region_get_ram_addr(mr); >+ const char *mr_name; >+ >+ if (get_physmapping(start_addr, size, page_mask)) { >+ return 0; >+ } >+ if (size <= 0) { >+ return -1; >+ } >+ >+ /* Xen can only handle a single dirty log region for now and we want >+ * the linear framebuffer to be that region. >+ * Avoid tracking any regions that is not videoram and avoid tracking >+ * the legacy vga region. */ >+ if (mr == framebuffer && start_addr > 0xbffff) { >+ goto go_physmap; >+ } >+ return -1; >+ >+go_physmap: >+ DPRINTF("mapping vram to %"HWADDR_PRIx" - %"HWADDR_PRIx"\n", >+ start_addr, start_addr + size); >+ >+ mr_name = memory_region_name(mr); >+ >+ physmap = g_new(XenPhysmap, 1); >+ >+ physmap->start_addr = start_addr; >+ physmap->size = size; >+ physmap->name = mr_name; >+ physmap->phys_offset = phys_offset; >+ >+ QLIST_INSERT_HEAD(&xen_physmap, physmap, list); >+ >+ if (runstate_check(RUN_STATE_INMIGRATE)) { >+ /* Now when we have a physmap entry we can replace a dummy mapping with >+ * a real one of guest foreign memory. */ >+ uint8_t *p = xen_replace_cache_entry(phys_offset, start_addr, size); >+ assert(p && p == memory_region_get_ram_ptr(mr)); >+ >+ return 0; >+ } >+ >+ pfn = phys_offset >> target_page_bits; >+ start_gpfn = start_addr >> target_page_bits; >+ nr_pages = size >> target_page_bits; >+ rc = xendevicemodel_relocate_memory(xen_dmod, xen_domid, nr_pages, pfn, >+ start_gpfn); >+ if (rc) { >+ int saved_errno = errno; >+ >+ error_report("relocate_memory %lu pages from GFN %"HWADDR_PRIx >+ " to GFN %"HWADDR_PRIx" failed: %s", >+ nr_pages, pfn, start_gpfn, strerror(saved_errno)); >+ errno = saved_errno; >+ return -1; >+ } >+ >+ rc = xendevicemodel_pin_memory_cacheattr(xen_dmod, xen_domid, >+ start_addr >> target_page_bits, >+ (start_addr + size - 1) >> target_page_bits, >+ XEN_DOMCTL_MEM_CACHEATTR_WB); >+ if (rc) { >+ error_report("pin_memory_cacheattr failed: %s", strerror(errno)); >+ } >+ return xen_save_physmap(state, physmap); >+} >+ >+static int xen_remove_from_physmap(XenIOState *state, >+ hwaddr start_addr, >+ ram_addr_t size) >+{ >+ unsigned target_page_bits = qemu_target_page_bits(); >+ int page_size = qemu_target_page_size(); >+ int page_mask = -page_size; >+ int rc = 0; >+ XenPhysmap *physmap = NULL; >+ hwaddr phys_offset = 0; >+ >+ physmap = get_physmapping(start_addr, size, page_mask); >+ if (physmap == NULL) { >+ return -1; >+ } >+ >+ phys_offset = physmap->phys_offset; >+ size = physmap->size; >+ >+ DPRINTF("unmapping vram to %"HWADDR_PRIx" - %"HWADDR_PRIx", at " >+ "%"HWADDR_PRIx"\n", start_addr, start_addr + size, phys_offset); >+ >+ size >>= target_page_bits; >+ start_addr >>= target_page_bits; >+ phys_offset >>= target_page_bits; >+ rc = xendevicemodel_relocate_memory(xen_dmod, xen_domid, size, start_addr, >+ phys_offset); >+ if (rc) { >+ int saved_errno = errno; >+ >+ error_report("relocate_memory "RAM_ADDR_FMT" pages" >+ " from GFN %"HWADDR_PRIx >+ " to GFN %"HWADDR_PRIx" failed: %s", >+ size, start_addr, phys_offset, strerror(saved_errno)); >+ errno = saved_errno; >+ return -1; >+ } >+ >+ QLIST_REMOVE(physmap, list); >+ if (log_for_dirtybit == physmap) { >+ log_for_dirtybit = NULL; >+ g_free(dirty_bitmap); >+ dirty_bitmap = NULL; >+ } >+ g_free(physmap); >+ >+ return 0; >+} >+ >+static void xen_sync_dirty_bitmap(XenIOState *state, >+ hwaddr start_addr, >+ ram_addr_t size) >+{ >+ unsigned target_page_bits = qemu_target_page_bits(); >+ int page_size = qemu_target_page_size(); >+ int page_mask = -page_size; >+ hwaddr npages = size >> target_page_bits; >+ const int width = sizeof(unsigned long) * 8; >+ size_t bitmap_size = DIV_ROUND_UP(npages, width); >+ int rc, i, j; >+ const XenPhysmap *physmap = NULL; >+ >+ physmap = get_physmapping(start_addr, size, page_mask); >+ if (physmap == NULL) { >+ /* not handled */ >+ return; >+ } >+ >+ if (log_for_dirtybit == NULL) { >+ log_for_dirtybit = physmap; >+ dirty_bitmap = g_new(unsigned long, bitmap_size); >+ } else if (log_for_dirtybit != physmap) { >+ /* Only one range for dirty bitmap can be tracked. */ >+ return; >+ } >+ >+ rc = xen_track_dirty_vram(xen_domid, start_addr >> target_page_bits, >+ npages, dirty_bitmap); >+ if (rc < 0) { >+#ifndef ENODATA >+#define ENODATA ENOENT >+#endif >+ if (errno == ENODATA) { >+ memory_region_set_dirty(framebuffer, 0, size); >+ DPRINTF("xen: track_dirty_vram failed (0x" HWADDR_FMT_plx >+ ", 0x" HWADDR_FMT_plx "): %s\n", >+ start_addr, start_addr + size, strerror(errno)); >+ } >+ return; >+ } >+ >+ for (i = 0; i < bitmap_size; i++) { >+ unsigned long map = dirty_bitmap[i]; >+ while (map != 0) { >+ j = ctzl(map); >+ map &= ~(1ul << j); >+ memory_region_set_dirty(framebuffer, >+ (i * width + j) * page_size, >+ page_size); >+ }; >+ } >+} >+ >+static void xen_log_start(MemoryListener *listener, >+ MemoryRegionSection *section, >+ int old, int new) >+{ >+ XenIOState *state = container_of(listener, XenIOState, memory_listener); >+ >+ if (new & ~old & (1 << DIRTY_MEMORY_VGA)) { >+ xen_sync_dirty_bitmap(state, section->offset_within_address_space, >+ int128_get64(section->size)); >+ } >+} >+ >+static void xen_log_stop(MemoryListener *listener, MemoryRegionSection *section, >+ int old, int new) >+{ >+ if (old & ~new & (1 << DIRTY_MEMORY_VGA)) { >+ log_for_dirtybit = NULL; >+ g_free(dirty_bitmap); >+ dirty_bitmap = NULL; >+ /* Disable dirty bit tracking */ >+ xen_track_dirty_vram(xen_domid, 0, 0, NULL); >+ } >+} >+ >+static void xen_log_sync(MemoryListener *listener, MemoryRegionSection *section) >+{ >+ XenIOState *state = container_of(listener, XenIOState, memory_listener); >+ >+ xen_sync_dirty_bitmap(state, section->offset_within_address_space, >+ int128_get64(section->size)); >+} >+ >+static void xen_log_global_start(MemoryListener *listener) >+{ >+ if (xen_enabled()) { >+ xen_in_migration = true; >+ } >+} >+ >+static void xen_log_global_stop(MemoryListener *listener) >+{ >+ xen_in_migration = false; >+} >+ >+const MemoryListener xen_memory_listener = { >+ .name = "xen-memory", >+ .region_add = xen_region_add, >+ .region_del = xen_region_del, >+ .log_start = xen_log_start, >+ .log_stop = xen_log_stop, >+ .log_sync = xen_log_sync, >+ .log_global_start = xen_log_global_start, >+ .log_global_stop = xen_log_global_stop, >+ .priority = MEMORY_LISTENER_PRIORITY_ACCEL, >+}; >+ >+#ifdef XEN_COMPAT_PHYSMAP >+void xen_read_physmap(XenIOState *state) >+{ >+ XenPhysmap *physmap = NULL; >+ unsigned int len, num, i; >+ char path[80], *value = NULL; >+ char **entries = NULL; >+ >+ QLIST_INIT(&xen_physmap); >+ >+ snprintf(path, sizeof(path), >+ "/local/domain/0/device-model/%d/physmap", xen_domid); >+ entries = xs_directory(state->xenstore, 0, path, &num); >+ if (entries == NULL) >+ return; >+ >+ for (i = 0; i < num; i++) { >+ physmap = g_new(XenPhysmap, 1); >+ physmap->phys_offset = strtoull(entries[i], NULL, 16); >+ snprintf(path, sizeof(path), >+ "/local/domain/0/device-model/%d/physmap/%s/start_addr", >+ xen_domid, entries[i]); >+ value = xs_read(state->xenstore, 0, path, &len); >+ if (value == NULL) { >+ g_free(physmap); >+ continue; >+ } >+ physmap->start_addr = strtoull(value, NULL, 16); >+ free(value); >+ >+ snprintf(path, sizeof(path), >+ "/local/domain/0/device-model/%d/physmap/%s/size", >+ xen_domid, entries[i]); >+ value = xs_read(state->xenstore, 0, path, &len); >+ if (value == NULL) { >+ g_free(physmap); >+ continue; >+ } >+ physmap->size = strtoull(value, NULL, 16); >+ free(value); >+ >+ snprintf(path, sizeof(path), >+ "/local/domain/0/device-model/%d/physmap/%s/name", >+ xen_domid, entries[i]); >+ physmap->name = xs_read(state->xenstore, 0, path, &len); >+ >+ QLIST_INSERT_HEAD(&xen_physmap, physmap, list); >+ } >+ free(entries); >+} >+#else >+void xen_read_physmap(XenIOState *state) >+{ >+ QLIST_INIT(&xen_physmap); >+} >+#endif >+ >+void xen_register_framebuffer(MemoryRegion *mr) >+{ >+ framebuffer = mr; >+} >+ >+void xen_hvm_modified_memory(ram_addr_t start, ram_addr_t length) >+{ >+ unsigned target_page_bits = qemu_target_page_bits(); >+ int page_size = qemu_target_page_size(); >+ int page_mask = -page_size; >+ >+ if (unlikely(xen_in_migration)) { >+ int rc; >+ ram_addr_t start_pfn, nb_pages; >+ >+ start = xen_phys_offset_to_gaddr(start, length, page_mask); >+ >+ if (length == 0) { >+ length = page_size; >+ } >+ start_pfn = start >> target_page_bits; >+ nb_pages = ((start + length + page_size - 1) >> target_page_bits) >+ - start_pfn; >+ rc = xen_modified_memory(xen_domid, start_pfn, nb_pages); >+ if (rc) { >+ fprintf(stderr, >+ "%s failed for "RAM_ADDR_FMT" ("RAM_ADDR_FMT"): %i, %s\n", >+ __func__, start, nb_pages, errno, strerror(errno)); >+ } >+ } >+} >+ >+void qmp_xen_set_global_dirty_log(bool enable, Error **errp) >+{ >+ if (enable) { >+ memory_global_dirty_log_start(GLOBAL_DIRTY_MIGRATION); >+ } else { >+ memory_global_dirty_log_stop(GLOBAL_DIRTY_MIGRATION); >+ } >+} >+ >+void xen_arch_set_memory(XenIOState *state, MemoryRegionSection *section, >+ bool add) >+{ >+ unsigned target_page_bits = qemu_target_page_bits(); >+ int page_size = qemu_target_page_size(); >+ int page_mask = -page_size; >+ hwaddr start_addr = section->offset_within_address_space; >+ ram_addr_t size = int128_get64(section->size); >+ bool log_dirty = memory_region_is_logging(section->mr, DIRTY_MEMORY_VGA); >+ hvmmem_type_t mem_type; >+ >+ if (!memory_region_is_ram(section->mr)) { >+ return; >+ } >+ >+ if (log_dirty != add) { >+ return; >+ } >+ >+ trace_xen_client_set_memory(start_addr, size, log_dirty); >+ >+ start_addr &= page_mask; >+ size = ROUND_UP(size, page_size); >+ >+ if (add) { >+ if (!memory_region_is_rom(section->mr)) { >+ xen_add_to_physmap(state, start_addr, size, >+ section->mr, section->offset_within_region); >+ } else { >+ mem_type = HVMMEM_ram_ro; >+ if (xen_set_mem_type(xen_domid, mem_type, >+ start_addr >> target_page_bits, >+ size >> target_page_bits)) { >+ DPRINTF("xen_set_mem_type error, addr: "HWADDR_FMT_plx"\n", >+ start_addr); >+ } >+ } >+ } else { >+ if (xen_remove_from_physmap(state, start_addr, size) < 0) { >+ DPRINTF("physmapping does not exist at "HWADDR_FMT_plx"\n", start_addr); >+ } >+ } >+} >diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c >index 3b9c31c1c8..5657693e1b 100644 >--- a/hw/i386/xen/xen-hvm.c >+++ b/hw/i386/xen/xen-hvm.c >@@ -9,16 +9,11 @@ > */ > > #include "qemu/osdep.h" >-#include "qemu/units.h" >-#include "qapi/error.h" >-#include "qapi/qapi-commands-migration.h" >-#include "trace.h" > > #include "hw/i386/pc.h" > #include "hw/irq.h" > #include "hw/i386/apic-msidef.h" > #include "hw/xen/xen-x86.h" >-#include "qemu/range.h" > > #include "hw/xen/xen-hvm-common.h" > #include <xen/hvm/e820.h> >@@ -26,8 +21,6 @@ > #include "cpu.h" > > static MemoryRegion ram_640k, ram_lo, ram_hi; >-static MemoryRegion *framebuffer; >-static bool xen_in_migration; > > /* Compatibility with older version */ > >@@ -56,10 +49,6 @@ typedef struct shared_vmport_iopage shared_vmport_iopage_t; > > static shared_vmport_iopage_t *shared_vmport_page; > >-static QLIST_HEAD(, XenPhysmap) xen_physmap; >-static const XenPhysmap *log_for_dirtybit; >-/* Buffer used by xen_sync_dirty_bitmap */ >-static unsigned long *dirty_bitmap; > static Notifier suspend; > static Notifier wakeup; > >@@ -175,312 +164,6 @@ static void xen_ram_init(PCMachineState *pcms, > } > } > >-static XenPhysmap *get_physmapping(hwaddr start_addr, ram_addr_t size, >- int page_mask) >-{ >- XenPhysmap *physmap = NULL; >- >- start_addr &= page_mask; >- >- QLIST_FOREACH(physmap, &xen_physmap, list) { >- if (range_covers_byte(physmap->start_addr, physmap->size, start_addr)) { >- return physmap; >- } >- } >- return NULL; >-} >- >-static hwaddr xen_phys_offset_to_gaddr(hwaddr phys_offset, ram_addr_t size, >- int page_mask) >-{ >- hwaddr addr = phys_offset & page_mask; >- XenPhysmap *physmap = NULL; >- >- QLIST_FOREACH(physmap, &xen_physmap, list) { >- if (range_covers_byte(physmap->phys_offset, physmap->size, addr)) { >- return physmap->start_addr + (phys_offset - physmap->phys_offset); >- } >- } >- >- return phys_offset; >-} >- >-#ifdef XEN_COMPAT_PHYSMAP >-static int xen_save_physmap(XenIOState *state, XenPhysmap *physmap) >-{ >- char path[80], value[17]; >- >- snprintf(path, sizeof(path), >- "/local/domain/0/device-model/%d/physmap/%"PRIx64"/start_addr", >- xen_domid, (uint64_t)physmap->phys_offset); >- snprintf(value, sizeof(value), "%"PRIx64, (uint64_t)physmap->start_addr); >- if (!xs_write(state->xenstore, 0, path, value, strlen(value))) { >- return -1; >- } >- snprintf(path, sizeof(path), >- "/local/domain/0/device-model/%d/physmap/%"PRIx64"/size", >- xen_domid, (uint64_t)physmap->phys_offset); >- snprintf(value, sizeof(value), "%"PRIx64, (uint64_t)physmap->size); >- if (!xs_write(state->xenstore, 0, path, value, strlen(value))) { >- return -1; >- } >- if (physmap->name) { >- snprintf(path, sizeof(path), >- "/local/domain/0/device-model/%d/physmap/%"PRIx64"/name", >- xen_domid, (uint64_t)physmap->phys_offset); >- if (!xs_write(state->xenstore, 0, path, >- physmap->name, strlen(physmap->name))) { >- return -1; >- } >- } >- return 0; >-} >-#else >-static int xen_save_physmap(XenIOState *state, XenPhysmap *physmap) >-{ >- return 0; >-} >-#endif >- >-static int xen_add_to_physmap(XenIOState *state, >- hwaddr start_addr, >- ram_addr_t size, >- MemoryRegion *mr, >- hwaddr offset_within_region) >-{ >- unsigned target_page_bits = qemu_target_page_bits(); >- int page_size = qemu_target_page_size(); >- int page_mask = -page_size; >- unsigned long nr_pages; >- int rc = 0; >- XenPhysmap *physmap = NULL; >- hwaddr pfn, start_gpfn; >- hwaddr phys_offset = memory_region_get_ram_addr(mr); >- const char *mr_name; >- >- if (get_physmapping(start_addr, size, page_mask)) { >- return 0; >- } >- if (size <= 0) { >- return -1; >- } >- >- /* Xen can only handle a single dirty log region for now and we want >- * the linear framebuffer to be that region. >- * Avoid tracking any regions that is not videoram and avoid tracking >- * the legacy vga region. */ >- if (mr == framebuffer && start_addr > 0xbffff) { >- goto go_physmap; >- } >- return -1; >- >-go_physmap: >- DPRINTF("mapping vram to %"HWADDR_PRIx" - %"HWADDR_PRIx"\n", >- start_addr, start_addr + size); >- >- mr_name = memory_region_name(mr); >- >- physmap = g_new(XenPhysmap, 1); >- >- physmap->start_addr = start_addr; >- physmap->size = size; >- physmap->name = mr_name; >- physmap->phys_offset = phys_offset; >- >- QLIST_INSERT_HEAD(&xen_physmap, physmap, list); >- >- if (runstate_check(RUN_STATE_INMIGRATE)) { >- /* Now when we have a physmap entry we can replace a dummy mapping with >- * a real one of guest foreign memory. */ >- uint8_t *p = xen_replace_cache_entry(phys_offset, start_addr, size); >- assert(p && p == memory_region_get_ram_ptr(mr)); >- >- return 0; >- } >- >- pfn = phys_offset >> target_page_bits; >- start_gpfn = start_addr >> target_page_bits; >- nr_pages = size >> target_page_bits; >- rc = xendevicemodel_relocate_memory(xen_dmod, xen_domid, nr_pages, pfn, >- start_gpfn); >- if (rc) { >- int saved_errno = errno; >- >- error_report("relocate_memory %lu pages from GFN %"HWADDR_PRIx >- " to GFN %"HWADDR_PRIx" failed: %s", >- nr_pages, pfn, start_gpfn, strerror(saved_errno)); >- errno = saved_errno; >- return -1; >- } >- >- rc = xendevicemodel_pin_memory_cacheattr(xen_dmod, xen_domid, >- start_addr >> target_page_bits, >- (start_addr + size - 1) >> target_page_bits, >- XEN_DOMCTL_MEM_CACHEATTR_WB); >- if (rc) { >- error_report("pin_memory_cacheattr failed: %s", strerror(errno)); >- } >- return xen_save_physmap(state, physmap); >-} >- >-static int xen_remove_from_physmap(XenIOState *state, >- hwaddr start_addr, >- ram_addr_t size) >-{ >- unsigned target_page_bits = qemu_target_page_bits(); >- int page_size = qemu_target_page_size(); >- int page_mask = -page_size; >- int rc = 0; >- XenPhysmap *physmap = NULL; >- hwaddr phys_offset = 0; >- >- physmap = get_physmapping(start_addr, size, page_mask); >- if (physmap == NULL) { >- return -1; >- } >- >- phys_offset = physmap->phys_offset; >- size = physmap->size; >- >- DPRINTF("unmapping vram to %"HWADDR_PRIx" - %"HWADDR_PRIx", at " >- "%"HWADDR_PRIx"\n", start_addr, start_addr + size, phys_offset); >- >- size >>= target_page_bits; >- start_addr >>= target_page_bits; >- phys_offset >>= target_page_bits; >- rc = xendevicemodel_relocate_memory(xen_dmod, xen_domid, size, start_addr, >- phys_offset); >- if (rc) { >- int saved_errno = errno; >- >- error_report("relocate_memory "RAM_ADDR_FMT" pages" >- " from GFN %"HWADDR_PRIx >- " to GFN %"HWADDR_PRIx" failed: %s", >- size, start_addr, phys_offset, strerror(saved_errno)); >- errno = saved_errno; >- return -1; >- } >- >- QLIST_REMOVE(physmap, list); >- if (log_for_dirtybit == physmap) { >- log_for_dirtybit = NULL; >- g_free(dirty_bitmap); >- dirty_bitmap = NULL; >- } >- g_free(physmap); >- >- return 0; >-} >- >-static void xen_sync_dirty_bitmap(XenIOState *state, >- hwaddr start_addr, >- ram_addr_t size) >-{ >- unsigned target_page_bits = qemu_target_page_bits(); >- int page_size = qemu_target_page_size(); >- int page_mask = -page_size; >- hwaddr npages = size >> target_page_bits; >- const int width = sizeof(unsigned long) * 8; >- size_t bitmap_size = DIV_ROUND_UP(npages, width); >- int rc, i, j; >- const XenPhysmap *physmap = NULL; >- >- physmap = get_physmapping(start_addr, size, page_mask); >- if (physmap == NULL) { >- /* not handled */ >- return; >- } >- >- if (log_for_dirtybit == NULL) { >- log_for_dirtybit = physmap; >- dirty_bitmap = g_new(unsigned long, bitmap_size); >- } else if (log_for_dirtybit != physmap) { >- /* Only one range for dirty bitmap can be tracked. */ >- return; >- } >- >- rc = xen_track_dirty_vram(xen_domid, start_addr >> target_page_bits, >- npages, dirty_bitmap); >- if (rc < 0) { >-#ifndef ENODATA >-#define ENODATA ENOENT >-#endif >- if (errno == ENODATA) { >- memory_region_set_dirty(framebuffer, 0, size); >- DPRINTF("xen: track_dirty_vram failed (0x" HWADDR_FMT_plx >- ", 0x" HWADDR_FMT_plx "): %s\n", >- start_addr, start_addr + size, strerror(errno)); >- } >- return; >- } >- >- for (i = 0; i < bitmap_size; i++) { >- unsigned long map = dirty_bitmap[i]; >- while (map != 0) { >- j = ctzl(map); >- map &= ~(1ul << j); >- memory_region_set_dirty(framebuffer, >- (i * width + j) * page_size, page_size); >- }; >- } >-} >- >-static void xen_log_start(MemoryListener *listener, >- MemoryRegionSection *section, >- int old, int new) >-{ >- XenIOState *state = container_of(listener, XenIOState, memory_listener); >- >- if (new & ~old & (1 << DIRTY_MEMORY_VGA)) { >- xen_sync_dirty_bitmap(state, section->offset_within_address_space, >- int128_get64(section->size)); >- } >-} >- >-static void xen_log_stop(MemoryListener *listener, MemoryRegionSection *section, >- int old, int new) >-{ >- if (old & ~new & (1 << DIRTY_MEMORY_VGA)) { >- log_for_dirtybit = NULL; >- g_free(dirty_bitmap); >- dirty_bitmap = NULL; >- /* Disable dirty bit tracking */ >- xen_track_dirty_vram(xen_domid, 0, 0, NULL); >- } >-} >- >-static void xen_log_sync(MemoryListener *listener, MemoryRegionSection *section) >-{ >- XenIOState *state = container_of(listener, XenIOState, memory_listener); >- >- xen_sync_dirty_bitmap(state, section->offset_within_address_space, >- int128_get64(section->size)); >-} >- >-static void xen_log_global_start(MemoryListener *listener) >-{ >- if (xen_enabled()) { >- xen_in_migration = true; >- } >-} >- >-static void xen_log_global_stop(MemoryListener *listener) >-{ >- xen_in_migration = false; >-} >- >-const MemoryListener xen_memory_listener = { >- .name = "xen-memory", >- .region_add = xen_region_add, >- .region_del = xen_region_del, >- .log_start = xen_log_start, >- .log_stop = xen_log_stop, >- .log_sync = xen_log_sync, >- .log_global_start = xen_log_global_start, >- .log_global_stop = xen_log_global_stop, >- .priority = MEMORY_LISTENER_PRIORITY_ACCEL, >-}; >- > static void regs_to_cpu(vmware_regs_t *vmport_regs, ioreq_t *req) > { > X86CPU *cpu; >@@ -524,63 +207,6 @@ static void handle_vmport_ioreq(XenIOState *state, ioreq_t *req) > current_cpu = NULL; > } > >-#ifdef XEN_COMPAT_PHYSMAP >-void xen_read_physmap(XenIOState *state) >-{ >- XenPhysmap *physmap = NULL; >- unsigned int len, num, i; >- char path[80], *value = NULL; >- char **entries = NULL; >- >- QLIST_INIT(&xen_physmap); >- >- snprintf(path, sizeof(path), >- "/local/domain/0/device-model/%d/physmap", xen_domid); >- entries = xs_directory(state->xenstore, 0, path, &num); >- if (entries == NULL) >- return; >- >- for (i = 0; i < num; i++) { >- physmap = g_new(XenPhysmap, 1); >- physmap->phys_offset = strtoull(entries[i], NULL, 16); >- snprintf(path, sizeof(path), >- "/local/domain/0/device-model/%d/physmap/%s/start_addr", >- xen_domid, entries[i]); >- value = xs_read(state->xenstore, 0, path, &len); >- if (value == NULL) { >- g_free(physmap); >- continue; >- } >- physmap->start_addr = strtoull(value, NULL, 16); >- free(value); >- >- snprintf(path, sizeof(path), >- "/local/domain/0/device-model/%d/physmap/%s/size", >- xen_domid, entries[i]); >- value = xs_read(state->xenstore, 0, path, &len); >- if (value == NULL) { >- g_free(physmap); >- continue; >- } >- physmap->size = strtoull(value, NULL, 16); >- free(value); >- >- snprintf(path, sizeof(path), >- "/local/domain/0/device-model/%d/physmap/%s/name", >- xen_domid, entries[i]); >- physmap->name = xs_read(state->xenstore, 0, path, &len); >- >- QLIST_INSERT_HEAD(&xen_physmap, physmap, list); >- } >- free(entries); >-} >-#else >-void xen_read_physmap(XenIOState *state) >-{ >- QLIST_INIT(&xen_physmap); >-} >-#endif >- > static void xen_wakeup_notifier(Notifier *notifier, void *data) > { > xc_set_hvm_param(xen_xc, xen_domid, HVM_PARAM_ACPI_S_STATE, 0); >@@ -635,91 +261,6 @@ err: > exit(1); > } > >-void xen_register_framebuffer(MemoryRegion *mr) >-{ >- framebuffer = mr; >-} >- >-void xen_hvm_modified_memory(ram_addr_t start, ram_addr_t length) >-{ >- unsigned target_page_bits = qemu_target_page_bits(); >- int page_size = qemu_target_page_size(); >- int page_mask = -page_size; >- >- if (unlikely(xen_in_migration)) { >- int rc; >- ram_addr_t start_pfn, nb_pages; >- >- start = xen_phys_offset_to_gaddr(start, length, page_mask); >- >- if (length == 0) { >- length = page_size; >- } >- start_pfn = start >> target_page_bits; >- nb_pages = ((start + length + page_size - 1) >> target_page_bits) >- - start_pfn; >- rc = xen_modified_memory(xen_domid, start_pfn, nb_pages); >- if (rc) { >- fprintf(stderr, >- "%s failed for "RAM_ADDR_FMT" ("RAM_ADDR_FMT"): %i, %s\n", >- __func__, start, nb_pages, errno, strerror(errno)); >- } >- } >-} >- >-void qmp_xen_set_global_dirty_log(bool enable, Error **errp) >-{ >- if (enable) { >- memory_global_dirty_log_start(GLOBAL_DIRTY_MIGRATION); >- } else { >- memory_global_dirty_log_stop(GLOBAL_DIRTY_MIGRATION); >- } >-} >- >-void xen_arch_set_memory(XenIOState *state, MemoryRegionSection *section, >- bool add) >-{ >- unsigned target_page_bits = qemu_target_page_bits(); >- int page_size = qemu_target_page_size(); >- int page_mask = -page_size; >- hwaddr start_addr = section->offset_within_address_space; >- ram_addr_t size = int128_get64(section->size); >- bool log_dirty = memory_region_is_logging(section->mr, DIRTY_MEMORY_VGA); >- hvmmem_type_t mem_type; >- >- if (!memory_region_is_ram(section->mr)) { >- return; >- } >- >- if (log_dirty != add) { >- return; >- } >- >- trace_xen_client_set_memory(start_addr, size, log_dirty); >- >- start_addr &= page_mask; >- size = ROUND_UP(size, page_size); >- >- if (add) { >- if (!memory_region_is_rom(section->mr)) { >- xen_add_to_physmap(state, start_addr, size, >- section->mr, section->offset_within_region); >- } else { >- mem_type = HVMMEM_ram_ro; >- if (xen_set_mem_type(xen_domid, mem_type, >- start_addr >> target_page_bits, >- size >> target_page_bits)) { >- DPRINTF("xen_set_mem_type error, addr: "HWADDR_FMT_plx"\n", >- start_addr); >- } >- } >- } else { >- if (xen_remove_from_physmap(state, start_addr, size) < 0) { >- DPRINTF("physmapping does not exist at "HWADDR_FMT_plx"\n", start_addr); >- } >- } >-} >- > void xen_arch_handle_ioreq(XenIOState *state, ioreq_t *req) > { > switch (req->type) { >diff --git a/hw/i386/xen/meson.build b/hw/i386/xen/meson.build >index 3f0df8bc07..d38759cfe4 100644 >--- a/hw/i386/xen/meson.build >+++ b/hw/i386/xen/meson.build >@@ -1,6 +1,7 @@ > i386_ss.add(when: 'CONFIG_XEN', if_true: files( > 'xen_apic.c', > 'xen_pvdevice.c', >+ 'xen-hvm-common.c', > )) > i386_ss.add(when: ['CONFIG_XEN', xen], if_true: files( > 'xen-hvm.c', >-- >2.41.0 > > Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
diff --git a/hw/i386/xen/xen-hvm-common.c b/hw/i386/xen/xen-hvm-common.c new file mode 100644 index 0000000000..e8ef0e0c94 --- /dev/null +++ b/hw/i386/xen/xen-hvm-common.c @@ -0,0 +1,473 @@ +/* + * Copyright (C) 2010 Citrix Ltd. + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "qemu/range.h" +#include "qapi/qapi-commands-migration.h" +#include "exec/target_page.h" +#include "hw/xen/xen-hvm-common.h" +#include "trace.h" + +static MemoryRegion *framebuffer; +static bool xen_in_migration; + +static QLIST_HEAD(, XenPhysmap) xen_physmap; +static const XenPhysmap *log_for_dirtybit; +/* Buffer used by xen_sync_dirty_bitmap */ +static unsigned long *dirty_bitmap; + +static XenPhysmap *get_physmapping(hwaddr start_addr, ram_addr_t size, + int page_mask) +{ + XenPhysmap *physmap = NULL; + + start_addr &= -page_mask; + + QLIST_FOREACH(physmap, &xen_physmap, list) { + if (range_covers_byte(physmap->start_addr, physmap->size, start_addr)) { + return physmap; + } + } + return NULL; +} + +static hwaddr xen_phys_offset_to_gaddr(hwaddr phys_offset, ram_addr_t size, + int page_mask) +{ + hwaddr addr = phys_offset & -page_mask; + XenPhysmap *physmap = NULL; + + QLIST_FOREACH(physmap, &xen_physmap, list) { + if (range_covers_byte(physmap->phys_offset, physmap->size, addr)) { + return physmap->start_addr + (phys_offset - physmap->phys_offset); + } + } + + return phys_offset; +} + +#ifdef XEN_COMPAT_PHYSMAP +static int xen_save_physmap(XenIOState *state, XenPhysmap *physmap) +{ + char path[80], value[17]; + + snprintf(path, sizeof(path), + "/local/domain/0/device-model/%d/physmap/%"PRIx64"/start_addr", + xen_domid, (uint64_t)physmap->phys_offset); + snprintf(value, sizeof(value), "%"PRIx64, (uint64_t)physmap->start_addr); + if (!xs_write(state->xenstore, 0, path, value, strlen(value))) { + return -1; + } + snprintf(path, sizeof(path), + "/local/domain/0/device-model/%d/physmap/%"PRIx64"/size", + xen_domid, (uint64_t)physmap->phys_offset); + snprintf(value, sizeof(value), "%"PRIx64, (uint64_t)physmap->size); + if (!xs_write(state->xenstore, 0, path, value, strlen(value))) { + return -1; + } + if (physmap->name) { + snprintf(path, sizeof(path), + "/local/domain/0/device-model/%d/physmap/%"PRIx64"/name", + xen_domid, (uint64_t)physmap->phys_offset); + if (!xs_write(state->xenstore, 0, path, + physmap->name, strlen(physmap->name))) { + return -1; + } + } + return 0; +} +#else +static int xen_save_physmap(XenIOState *state, XenPhysmap *physmap) +{ + return 0; +} +#endif + +static int xen_add_to_physmap(XenIOState *state, + hwaddr start_addr, + ram_addr_t size, + MemoryRegion *mr, + hwaddr offset_within_region) +{ + unsigned target_page_bits = qemu_target_page_bits(); + int page_size = qemu_target_page_size(); + int page_mask = -page_size; + unsigned long nr_pages; + int rc = 0; + XenPhysmap *physmap = NULL; + hwaddr pfn, start_gpfn; + hwaddr phys_offset = memory_region_get_ram_addr(mr); + const char *mr_name; + + if (get_physmapping(start_addr, size, page_mask)) { + return 0; + } + if (size <= 0) { + return -1; + } + + /* Xen can only handle a single dirty log region for now and we want + * the linear framebuffer to be that region. + * Avoid tracking any regions that is not videoram and avoid tracking + * the legacy vga region. */ + if (mr == framebuffer && start_addr > 0xbffff) { + goto go_physmap; + } + return -1; + +go_physmap: + DPRINTF("mapping vram to %"HWADDR_PRIx" - %"HWADDR_PRIx"\n", + start_addr, start_addr + size); + + mr_name = memory_region_name(mr); + + physmap = g_new(XenPhysmap, 1); + + physmap->start_addr = start_addr; + physmap->size = size; + physmap->name = mr_name; + physmap->phys_offset = phys_offset; + + QLIST_INSERT_HEAD(&xen_physmap, physmap, list); + + if (runstate_check(RUN_STATE_INMIGRATE)) { + /* Now when we have a physmap entry we can replace a dummy mapping with + * a real one of guest foreign memory. */ + uint8_t *p = xen_replace_cache_entry(phys_offset, start_addr, size); + assert(p && p == memory_region_get_ram_ptr(mr)); + + return 0; + } + + pfn = phys_offset >> target_page_bits; + start_gpfn = start_addr >> target_page_bits; + nr_pages = size >> target_page_bits; + rc = xendevicemodel_relocate_memory(xen_dmod, xen_domid, nr_pages, pfn, + start_gpfn); + if (rc) { + int saved_errno = errno; + + error_report("relocate_memory %lu pages from GFN %"HWADDR_PRIx + " to GFN %"HWADDR_PRIx" failed: %s", + nr_pages, pfn, start_gpfn, strerror(saved_errno)); + errno = saved_errno; + return -1; + } + + rc = xendevicemodel_pin_memory_cacheattr(xen_dmod, xen_domid, + start_addr >> target_page_bits, + (start_addr + size - 1) >> target_page_bits, + XEN_DOMCTL_MEM_CACHEATTR_WB); + if (rc) { + error_report("pin_memory_cacheattr failed: %s", strerror(errno)); + } + return xen_save_physmap(state, physmap); +} + +static int xen_remove_from_physmap(XenIOState *state, + hwaddr start_addr, + ram_addr_t size) +{ + unsigned target_page_bits = qemu_target_page_bits(); + int page_size = qemu_target_page_size(); + int page_mask = -page_size; + int rc = 0; + XenPhysmap *physmap = NULL; + hwaddr phys_offset = 0; + + physmap = get_physmapping(start_addr, size, page_mask); + if (physmap == NULL) { + return -1; + } + + phys_offset = physmap->phys_offset; + size = physmap->size; + + DPRINTF("unmapping vram to %"HWADDR_PRIx" - %"HWADDR_PRIx", at " + "%"HWADDR_PRIx"\n", start_addr, start_addr + size, phys_offset); + + size >>= target_page_bits; + start_addr >>= target_page_bits; + phys_offset >>= target_page_bits; + rc = xendevicemodel_relocate_memory(xen_dmod, xen_domid, size, start_addr, + phys_offset); + if (rc) { + int saved_errno = errno; + + error_report("relocate_memory "RAM_ADDR_FMT" pages" + " from GFN %"HWADDR_PRIx + " to GFN %"HWADDR_PRIx" failed: %s", + size, start_addr, phys_offset, strerror(saved_errno)); + errno = saved_errno; + return -1; + } + + QLIST_REMOVE(physmap, list); + if (log_for_dirtybit == physmap) { + log_for_dirtybit = NULL; + g_free(dirty_bitmap); + dirty_bitmap = NULL; + } + g_free(physmap); + + return 0; +} + +static void xen_sync_dirty_bitmap(XenIOState *state, + hwaddr start_addr, + ram_addr_t size) +{ + unsigned target_page_bits = qemu_target_page_bits(); + int page_size = qemu_target_page_size(); + int page_mask = -page_size; + hwaddr npages = size >> target_page_bits; + const int width = sizeof(unsigned long) * 8; + size_t bitmap_size = DIV_ROUND_UP(npages, width); + int rc, i, j; + const XenPhysmap *physmap = NULL; + + physmap = get_physmapping(start_addr, size, page_mask); + if (physmap == NULL) { + /* not handled */ + return; + } + + if (log_for_dirtybit == NULL) { + log_for_dirtybit = physmap; + dirty_bitmap = g_new(unsigned long, bitmap_size); + } else if (log_for_dirtybit != physmap) { + /* Only one range for dirty bitmap can be tracked. */ + return; + } + + rc = xen_track_dirty_vram(xen_domid, start_addr >> target_page_bits, + npages, dirty_bitmap); + if (rc < 0) { +#ifndef ENODATA +#define ENODATA ENOENT +#endif + if (errno == ENODATA) { + memory_region_set_dirty(framebuffer, 0, size); + DPRINTF("xen: track_dirty_vram failed (0x" HWADDR_FMT_plx + ", 0x" HWADDR_FMT_plx "): %s\n", + start_addr, start_addr + size, strerror(errno)); + } + return; + } + + for (i = 0; i < bitmap_size; i++) { + unsigned long map = dirty_bitmap[i]; + while (map != 0) { + j = ctzl(map); + map &= ~(1ul << j); + memory_region_set_dirty(framebuffer, + (i * width + j) * page_size, + page_size); + }; + } +} + +static void xen_log_start(MemoryListener *listener, + MemoryRegionSection *section, + int old, int new) +{ + XenIOState *state = container_of(listener, XenIOState, memory_listener); + + if (new & ~old & (1 << DIRTY_MEMORY_VGA)) { + xen_sync_dirty_bitmap(state, section->offset_within_address_space, + int128_get64(section->size)); + } +} + +static void xen_log_stop(MemoryListener *listener, MemoryRegionSection *section, + int old, int new) +{ + if (old & ~new & (1 << DIRTY_MEMORY_VGA)) { + log_for_dirtybit = NULL; + g_free(dirty_bitmap); + dirty_bitmap = NULL; + /* Disable dirty bit tracking */ + xen_track_dirty_vram(xen_domid, 0, 0, NULL); + } +} + +static void xen_log_sync(MemoryListener *listener, MemoryRegionSection *section) +{ + XenIOState *state = container_of(listener, XenIOState, memory_listener); + + xen_sync_dirty_bitmap(state, section->offset_within_address_space, + int128_get64(section->size)); +} + +static void xen_log_global_start(MemoryListener *listener) +{ + if (xen_enabled()) { + xen_in_migration = true; + } +} + +static void xen_log_global_stop(MemoryListener *listener) +{ + xen_in_migration = false; +} + +const MemoryListener xen_memory_listener = { + .name = "xen-memory", + .region_add = xen_region_add, + .region_del = xen_region_del, + .log_start = xen_log_start, + .log_stop = xen_log_stop, + .log_sync = xen_log_sync, + .log_global_start = xen_log_global_start, + .log_global_stop = xen_log_global_stop, + .priority = MEMORY_LISTENER_PRIORITY_ACCEL, +}; + +#ifdef XEN_COMPAT_PHYSMAP +void xen_read_physmap(XenIOState *state) +{ + XenPhysmap *physmap = NULL; + unsigned int len, num, i; + char path[80], *value = NULL; + char **entries = NULL; + + QLIST_INIT(&xen_physmap); + + snprintf(path, sizeof(path), + "/local/domain/0/device-model/%d/physmap", xen_domid); + entries = xs_directory(state->xenstore, 0, path, &num); + if (entries == NULL) + return; + + for (i = 0; i < num; i++) { + physmap = g_new(XenPhysmap, 1); + physmap->phys_offset = strtoull(entries[i], NULL, 16); + snprintf(path, sizeof(path), + "/local/domain/0/device-model/%d/physmap/%s/start_addr", + xen_domid, entries[i]); + value = xs_read(state->xenstore, 0, path, &len); + if (value == NULL) { + g_free(physmap); + continue; + } + physmap->start_addr = strtoull(value, NULL, 16); + free(value); + + snprintf(path, sizeof(path), + "/local/domain/0/device-model/%d/physmap/%s/size", + xen_domid, entries[i]); + value = xs_read(state->xenstore, 0, path, &len); + if (value == NULL) { + g_free(physmap); + continue; + } + physmap->size = strtoull(value, NULL, 16); + free(value); + + snprintf(path, sizeof(path), + "/local/domain/0/device-model/%d/physmap/%s/name", + xen_domid, entries[i]); + physmap->name = xs_read(state->xenstore, 0, path, &len); + + QLIST_INSERT_HEAD(&xen_physmap, physmap, list); + } + free(entries); +} +#else +void xen_read_physmap(XenIOState *state) +{ + QLIST_INIT(&xen_physmap); +} +#endif + +void xen_register_framebuffer(MemoryRegion *mr) +{ + framebuffer = mr; +} + +void xen_hvm_modified_memory(ram_addr_t start, ram_addr_t length) +{ + unsigned target_page_bits = qemu_target_page_bits(); + int page_size = qemu_target_page_size(); + int page_mask = -page_size; + + if (unlikely(xen_in_migration)) { + int rc; + ram_addr_t start_pfn, nb_pages; + + start = xen_phys_offset_to_gaddr(start, length, page_mask); + + if (length == 0) { + length = page_size; + } + start_pfn = start >> target_page_bits; + nb_pages = ((start + length + page_size - 1) >> target_page_bits) + - start_pfn; + rc = xen_modified_memory(xen_domid, start_pfn, nb_pages); + if (rc) { + fprintf(stderr, + "%s failed for "RAM_ADDR_FMT" ("RAM_ADDR_FMT"): %i, %s\n", + __func__, start, nb_pages, errno, strerror(errno)); + } + } +} + +void qmp_xen_set_global_dirty_log(bool enable, Error **errp) +{ + if (enable) { + memory_global_dirty_log_start(GLOBAL_DIRTY_MIGRATION); + } else { + memory_global_dirty_log_stop(GLOBAL_DIRTY_MIGRATION); + } +} + +void xen_arch_set_memory(XenIOState *state, MemoryRegionSection *section, + bool add) +{ + unsigned target_page_bits = qemu_target_page_bits(); + int page_size = qemu_target_page_size(); + int page_mask = -page_size; + hwaddr start_addr = section->offset_within_address_space; + ram_addr_t size = int128_get64(section->size); + bool log_dirty = memory_region_is_logging(section->mr, DIRTY_MEMORY_VGA); + hvmmem_type_t mem_type; + + if (!memory_region_is_ram(section->mr)) { + return; + } + + if (log_dirty != add) { + return; + } + + trace_xen_client_set_memory(start_addr, size, log_dirty); + + start_addr &= page_mask; + size = ROUND_UP(size, page_size); + + if (add) { + if (!memory_region_is_rom(section->mr)) { + xen_add_to_physmap(state, start_addr, size, + section->mr, section->offset_within_region); + } else { + mem_type = HVMMEM_ram_ro; + if (xen_set_mem_type(xen_domid, mem_type, + start_addr >> target_page_bits, + size >> target_page_bits)) { + DPRINTF("xen_set_mem_type error, addr: "HWADDR_FMT_plx"\n", + start_addr); + } + } + } else { + if (xen_remove_from_physmap(state, start_addr, size) < 0) { + DPRINTF("physmapping does not exist at "HWADDR_FMT_plx"\n", start_addr); + } + } +} diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index 3b9c31c1c8..5657693e1b 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -9,16 +9,11 @@ */ #include "qemu/osdep.h" -#include "qemu/units.h" -#include "qapi/error.h" -#include "qapi/qapi-commands-migration.h" -#include "trace.h" #include "hw/i386/pc.h" #include "hw/irq.h" #include "hw/i386/apic-msidef.h" #include "hw/xen/xen-x86.h" -#include "qemu/range.h" #include "hw/xen/xen-hvm-common.h" #include <xen/hvm/e820.h> @@ -26,8 +21,6 @@ #include "cpu.h" static MemoryRegion ram_640k, ram_lo, ram_hi; -static MemoryRegion *framebuffer; -static bool xen_in_migration; /* Compatibility with older version */ @@ -56,10 +49,6 @@ typedef struct shared_vmport_iopage shared_vmport_iopage_t; static shared_vmport_iopage_t *shared_vmport_page; -static QLIST_HEAD(, XenPhysmap) xen_physmap; -static const XenPhysmap *log_for_dirtybit; -/* Buffer used by xen_sync_dirty_bitmap */ -static unsigned long *dirty_bitmap; static Notifier suspend; static Notifier wakeup; @@ -175,312 +164,6 @@ static void xen_ram_init(PCMachineState *pcms, } } -static XenPhysmap *get_physmapping(hwaddr start_addr, ram_addr_t size, - int page_mask) -{ - XenPhysmap *physmap = NULL; - - start_addr &= page_mask; - - QLIST_FOREACH(physmap, &xen_physmap, list) { - if (range_covers_byte(physmap->start_addr, physmap->size, start_addr)) { - return physmap; - } - } - return NULL; -} - -static hwaddr xen_phys_offset_to_gaddr(hwaddr phys_offset, ram_addr_t size, - int page_mask) -{ - hwaddr addr = phys_offset & page_mask; - XenPhysmap *physmap = NULL; - - QLIST_FOREACH(physmap, &xen_physmap, list) { - if (range_covers_byte(physmap->phys_offset, physmap->size, addr)) { - return physmap->start_addr + (phys_offset - physmap->phys_offset); - } - } - - return phys_offset; -} - -#ifdef XEN_COMPAT_PHYSMAP -static int xen_save_physmap(XenIOState *state, XenPhysmap *physmap) -{ - char path[80], value[17]; - - snprintf(path, sizeof(path), - "/local/domain/0/device-model/%d/physmap/%"PRIx64"/start_addr", - xen_domid, (uint64_t)physmap->phys_offset); - snprintf(value, sizeof(value), "%"PRIx64, (uint64_t)physmap->start_addr); - if (!xs_write(state->xenstore, 0, path, value, strlen(value))) { - return -1; - } - snprintf(path, sizeof(path), - "/local/domain/0/device-model/%d/physmap/%"PRIx64"/size", - xen_domid, (uint64_t)physmap->phys_offset); - snprintf(value, sizeof(value), "%"PRIx64, (uint64_t)physmap->size); - if (!xs_write(state->xenstore, 0, path, value, strlen(value))) { - return -1; - } - if (physmap->name) { - snprintf(path, sizeof(path), - "/local/domain/0/device-model/%d/physmap/%"PRIx64"/name", - xen_domid, (uint64_t)physmap->phys_offset); - if (!xs_write(state->xenstore, 0, path, - physmap->name, strlen(physmap->name))) { - return -1; - } - } - return 0; -} -#else -static int xen_save_physmap(XenIOState *state, XenPhysmap *physmap) -{ - return 0; -} -#endif - -static int xen_add_to_physmap(XenIOState *state, - hwaddr start_addr, - ram_addr_t size, - MemoryRegion *mr, - hwaddr offset_within_region) -{ - unsigned target_page_bits = qemu_target_page_bits(); - int page_size = qemu_target_page_size(); - int page_mask = -page_size; - unsigned long nr_pages; - int rc = 0; - XenPhysmap *physmap = NULL; - hwaddr pfn, start_gpfn; - hwaddr phys_offset = memory_region_get_ram_addr(mr); - const char *mr_name; - - if (get_physmapping(start_addr, size, page_mask)) { - return 0; - } - if (size <= 0) { - return -1; - } - - /* Xen can only handle a single dirty log region for now and we want - * the linear framebuffer to be that region. - * Avoid tracking any regions that is not videoram and avoid tracking - * the legacy vga region. */ - if (mr == framebuffer && start_addr > 0xbffff) { - goto go_physmap; - } - return -1; - -go_physmap: - DPRINTF("mapping vram to %"HWADDR_PRIx" - %"HWADDR_PRIx"\n", - start_addr, start_addr + size); - - mr_name = memory_region_name(mr); - - physmap = g_new(XenPhysmap, 1); - - physmap->start_addr = start_addr; - physmap->size = size; - physmap->name = mr_name; - physmap->phys_offset = phys_offset; - - QLIST_INSERT_HEAD(&xen_physmap, physmap, list); - - if (runstate_check(RUN_STATE_INMIGRATE)) { - /* Now when we have a physmap entry we can replace a dummy mapping with - * a real one of guest foreign memory. */ - uint8_t *p = xen_replace_cache_entry(phys_offset, start_addr, size); - assert(p && p == memory_region_get_ram_ptr(mr)); - - return 0; - } - - pfn = phys_offset >> target_page_bits; - start_gpfn = start_addr >> target_page_bits; - nr_pages = size >> target_page_bits; - rc = xendevicemodel_relocate_memory(xen_dmod, xen_domid, nr_pages, pfn, - start_gpfn); - if (rc) { - int saved_errno = errno; - - error_report("relocate_memory %lu pages from GFN %"HWADDR_PRIx - " to GFN %"HWADDR_PRIx" failed: %s", - nr_pages, pfn, start_gpfn, strerror(saved_errno)); - errno = saved_errno; - return -1; - } - - rc = xendevicemodel_pin_memory_cacheattr(xen_dmod, xen_domid, - start_addr >> target_page_bits, - (start_addr + size - 1) >> target_page_bits, - XEN_DOMCTL_MEM_CACHEATTR_WB); - if (rc) { - error_report("pin_memory_cacheattr failed: %s", strerror(errno)); - } - return xen_save_physmap(state, physmap); -} - -static int xen_remove_from_physmap(XenIOState *state, - hwaddr start_addr, - ram_addr_t size) -{ - unsigned target_page_bits = qemu_target_page_bits(); - int page_size = qemu_target_page_size(); - int page_mask = -page_size; - int rc = 0; - XenPhysmap *physmap = NULL; - hwaddr phys_offset = 0; - - physmap = get_physmapping(start_addr, size, page_mask); - if (physmap == NULL) { - return -1; - } - - phys_offset = physmap->phys_offset; - size = physmap->size; - - DPRINTF("unmapping vram to %"HWADDR_PRIx" - %"HWADDR_PRIx", at " - "%"HWADDR_PRIx"\n", start_addr, start_addr + size, phys_offset); - - size >>= target_page_bits; - start_addr >>= target_page_bits; - phys_offset >>= target_page_bits; - rc = xendevicemodel_relocate_memory(xen_dmod, xen_domid, size, start_addr, - phys_offset); - if (rc) { - int saved_errno = errno; - - error_report("relocate_memory "RAM_ADDR_FMT" pages" - " from GFN %"HWADDR_PRIx - " to GFN %"HWADDR_PRIx" failed: %s", - size, start_addr, phys_offset, strerror(saved_errno)); - errno = saved_errno; - return -1; - } - - QLIST_REMOVE(physmap, list); - if (log_for_dirtybit == physmap) { - log_for_dirtybit = NULL; - g_free(dirty_bitmap); - dirty_bitmap = NULL; - } - g_free(physmap); - - return 0; -} - -static void xen_sync_dirty_bitmap(XenIOState *state, - hwaddr start_addr, - ram_addr_t size) -{ - unsigned target_page_bits = qemu_target_page_bits(); - int page_size = qemu_target_page_size(); - int page_mask = -page_size; - hwaddr npages = size >> target_page_bits; - const int width = sizeof(unsigned long) * 8; - size_t bitmap_size = DIV_ROUND_UP(npages, width); - int rc, i, j; - const XenPhysmap *physmap = NULL; - - physmap = get_physmapping(start_addr, size, page_mask); - if (physmap == NULL) { - /* not handled */ - return; - } - - if (log_for_dirtybit == NULL) { - log_for_dirtybit = physmap; - dirty_bitmap = g_new(unsigned long, bitmap_size); - } else if (log_for_dirtybit != physmap) { - /* Only one range for dirty bitmap can be tracked. */ - return; - } - - rc = xen_track_dirty_vram(xen_domid, start_addr >> target_page_bits, - npages, dirty_bitmap); - if (rc < 0) { -#ifndef ENODATA -#define ENODATA ENOENT -#endif - if (errno == ENODATA) { - memory_region_set_dirty(framebuffer, 0, size); - DPRINTF("xen: track_dirty_vram failed (0x" HWADDR_FMT_plx - ", 0x" HWADDR_FMT_plx "): %s\n", - start_addr, start_addr + size, strerror(errno)); - } - return; - } - - for (i = 0; i < bitmap_size; i++) { - unsigned long map = dirty_bitmap[i]; - while (map != 0) { - j = ctzl(map); - map &= ~(1ul << j); - memory_region_set_dirty(framebuffer, - (i * width + j) * page_size, page_size); - }; - } -} - -static void xen_log_start(MemoryListener *listener, - MemoryRegionSection *section, - int old, int new) -{ - XenIOState *state = container_of(listener, XenIOState, memory_listener); - - if (new & ~old & (1 << DIRTY_MEMORY_VGA)) { - xen_sync_dirty_bitmap(state, section->offset_within_address_space, - int128_get64(section->size)); - } -} - -static void xen_log_stop(MemoryListener *listener, MemoryRegionSection *section, - int old, int new) -{ - if (old & ~new & (1 << DIRTY_MEMORY_VGA)) { - log_for_dirtybit = NULL; - g_free(dirty_bitmap); - dirty_bitmap = NULL; - /* Disable dirty bit tracking */ - xen_track_dirty_vram(xen_domid, 0, 0, NULL); - } -} - -static void xen_log_sync(MemoryListener *listener, MemoryRegionSection *section) -{ - XenIOState *state = container_of(listener, XenIOState, memory_listener); - - xen_sync_dirty_bitmap(state, section->offset_within_address_space, - int128_get64(section->size)); -} - -static void xen_log_global_start(MemoryListener *listener) -{ - if (xen_enabled()) { - xen_in_migration = true; - } -} - -static void xen_log_global_stop(MemoryListener *listener) -{ - xen_in_migration = false; -} - -const MemoryListener xen_memory_listener = { - .name = "xen-memory", - .region_add = xen_region_add, - .region_del = xen_region_del, - .log_start = xen_log_start, - .log_stop = xen_log_stop, - .log_sync = xen_log_sync, - .log_global_start = xen_log_global_start, - .log_global_stop = xen_log_global_stop, - .priority = MEMORY_LISTENER_PRIORITY_ACCEL, -}; - static void regs_to_cpu(vmware_regs_t *vmport_regs, ioreq_t *req) { X86CPU *cpu; @@ -524,63 +207,6 @@ static void handle_vmport_ioreq(XenIOState *state, ioreq_t *req) current_cpu = NULL; } -#ifdef XEN_COMPAT_PHYSMAP -void xen_read_physmap(XenIOState *state) -{ - XenPhysmap *physmap = NULL; - unsigned int len, num, i; - char path[80], *value = NULL; - char **entries = NULL; - - QLIST_INIT(&xen_physmap); - - snprintf(path, sizeof(path), - "/local/domain/0/device-model/%d/physmap", xen_domid); - entries = xs_directory(state->xenstore, 0, path, &num); - if (entries == NULL) - return; - - for (i = 0; i < num; i++) { - physmap = g_new(XenPhysmap, 1); - physmap->phys_offset = strtoull(entries[i], NULL, 16); - snprintf(path, sizeof(path), - "/local/domain/0/device-model/%d/physmap/%s/start_addr", - xen_domid, entries[i]); - value = xs_read(state->xenstore, 0, path, &len); - if (value == NULL) { - g_free(physmap); - continue; - } - physmap->start_addr = strtoull(value, NULL, 16); - free(value); - - snprintf(path, sizeof(path), - "/local/domain/0/device-model/%d/physmap/%s/size", - xen_domid, entries[i]); - value = xs_read(state->xenstore, 0, path, &len); - if (value == NULL) { - g_free(physmap); - continue; - } - physmap->size = strtoull(value, NULL, 16); - free(value); - - snprintf(path, sizeof(path), - "/local/domain/0/device-model/%d/physmap/%s/name", - xen_domid, entries[i]); - physmap->name = xs_read(state->xenstore, 0, path, &len); - - QLIST_INSERT_HEAD(&xen_physmap, physmap, list); - } - free(entries); -} -#else -void xen_read_physmap(XenIOState *state) -{ - QLIST_INIT(&xen_physmap); -} -#endif - static void xen_wakeup_notifier(Notifier *notifier, void *data) { xc_set_hvm_param(xen_xc, xen_domid, HVM_PARAM_ACPI_S_STATE, 0); @@ -635,91 +261,6 @@ err: exit(1); } -void xen_register_framebuffer(MemoryRegion *mr) -{ - framebuffer = mr; -} - -void xen_hvm_modified_memory(ram_addr_t start, ram_addr_t length) -{ - unsigned target_page_bits = qemu_target_page_bits(); - int page_size = qemu_target_page_size(); - int page_mask = -page_size; - - if (unlikely(xen_in_migration)) { - int rc; - ram_addr_t start_pfn, nb_pages; - - start = xen_phys_offset_to_gaddr(start, length, page_mask); - - if (length == 0) { - length = page_size; - } - start_pfn = start >> target_page_bits; - nb_pages = ((start + length + page_size - 1) >> target_page_bits) - - start_pfn; - rc = xen_modified_memory(xen_domid, start_pfn, nb_pages); - if (rc) { - fprintf(stderr, - "%s failed for "RAM_ADDR_FMT" ("RAM_ADDR_FMT"): %i, %s\n", - __func__, start, nb_pages, errno, strerror(errno)); - } - } -} - -void qmp_xen_set_global_dirty_log(bool enable, Error **errp) -{ - if (enable) { - memory_global_dirty_log_start(GLOBAL_DIRTY_MIGRATION); - } else { - memory_global_dirty_log_stop(GLOBAL_DIRTY_MIGRATION); - } -} - -void xen_arch_set_memory(XenIOState *state, MemoryRegionSection *section, - bool add) -{ - unsigned target_page_bits = qemu_target_page_bits(); - int page_size = qemu_target_page_size(); - int page_mask = -page_size; - hwaddr start_addr = section->offset_within_address_space; - ram_addr_t size = int128_get64(section->size); - bool log_dirty = memory_region_is_logging(section->mr, DIRTY_MEMORY_VGA); - hvmmem_type_t mem_type; - - if (!memory_region_is_ram(section->mr)) { - return; - } - - if (log_dirty != add) { - return; - } - - trace_xen_client_set_memory(start_addr, size, log_dirty); - - start_addr &= page_mask; - size = ROUND_UP(size, page_size); - - if (add) { - if (!memory_region_is_rom(section->mr)) { - xen_add_to_physmap(state, start_addr, size, - section->mr, section->offset_within_region); - } else { - mem_type = HVMMEM_ram_ro; - if (xen_set_mem_type(xen_domid, mem_type, - start_addr >> target_page_bits, - size >> target_page_bits)) { - DPRINTF("xen_set_mem_type error, addr: "HWADDR_FMT_plx"\n", - start_addr); - } - } - } else { - if (xen_remove_from_physmap(state, start_addr, size) < 0) { - DPRINTF("physmapping does not exist at "HWADDR_FMT_plx"\n", start_addr); - } - } -} - void xen_arch_handle_ioreq(XenIOState *state, ioreq_t *req) { switch (req->type) { diff --git a/hw/i386/xen/meson.build b/hw/i386/xen/meson.build index 3f0df8bc07..d38759cfe4 100644 --- a/hw/i386/xen/meson.build +++ b/hw/i386/xen/meson.build @@ -1,6 +1,7 @@ i386_ss.add(when: 'CONFIG_XEN', if_true: files( 'xen_apic.c', 'xen_pvdevice.c', + 'xen-hvm-common.c', )) i386_ss.add(when: ['CONFIG_XEN', xen], if_true: files( 'xen-hvm.c',
Extract non-x86 specific code out of xen-hvm.c, to xen-hvm-common.c. For now this new file is only build for x86 targets. Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> --- hw/i386/xen/xen-hvm-common.c | 473 +++++++++++++++++++++++++++++++++++ hw/i386/xen/xen-hvm.c | 459 --------------------------------- hw/i386/xen/meson.build | 1 + 3 files changed, 474 insertions(+), 459 deletions(-) create mode 100644 hw/i386/xen/xen-hvm-common.c