+void guest_phys_blocks_free(GuestPhysBlockList *list)
+{
+ GuestPhysBlock *p, *q;
+
+ QTAILQ_FOREACH_SAFE(p, &list->head, next, q) {
+ QTAILQ_REMOVE(&list->head, p, next);
+ memory_region_unref(p->mr);
+ g_free(p);
+ }
+ list->num = 0;
+}
+
+void guest_phys_blocks_init(GuestPhysBlockList *list)
+{
+ list->num = 0;
+ QTAILQ_INIT(&list->head);
+}
+
+typedef struct GuestPhysListener {
+ GuestPhysBlockList *list;
+ MemoryListener listener;
+} GuestPhysListener;
+
+static void guest_phys_blocks_region_add(MemoryListener *listener,
+ MemoryRegionSection *section)
+{
+ GuestPhysListener *g;
+ uint64_t section_size;
+ hwaddr target_start, target_end;
+ uint8_t *host_addr;
+ GuestPhysBlock *predecessor;
+
+ /* we only care about RAM */
+ if (!memory_region_is_ram(section->mr) ||
+ memory_region_is_ram_device(section->mr) ||
+ memory_region_is_nonvolatile(section->mr)) {
+ return;
+ }
+
+ g = container_of(listener, GuestPhysListener, listener);
+ section_size = int128_get64(section->size);
+ target_start = section->offset_within_address_space;
+ target_end = target_start + section_size;
+ host_addr = memory_region_get_ram_ptr(section->mr) +
+ section->offset_within_region;
+ predecessor = NULL;
+
+ /* find continuity in guest physical address space */
+ if (!QTAILQ_EMPTY(&g->list->head)) {
+ hwaddr predecessor_size;
+
+ predecessor = QTAILQ_LAST(&g->list->head);
+ predecessor_size = predecessor->target_end - predecessor->target_start;
+
+ /* the memory API guarantees monotonically increasing traversal */
+ g_assert(predecessor->target_end <= target_start);
+
+ /* we want continuity in both guest-physical and host-virtual memory */
+ if (predecessor->target_end < target_start ||
+ predecessor->host_addr + predecessor_size != host_addr) {
+ predecessor = NULL;
+ }
+ }
+
+ if (predecessor == NULL) {
+ /* isolated mapping, allocate it and add it to the list */
+ GuestPhysBlock *block = g_malloc0(sizeof *block);
+
+ block->target_start = target_start;
+ block->target_end = target_end;
+ block->host_addr = host_addr;
+ block->mr = section->mr;
+ memory_region_ref(section->mr);
+
+ QTAILQ_INSERT_TAIL(&g->list->head, block, next);
+ ++g->list->num;
+ } else {
+ /* expand predecessor until @target_end; predecessor's start doesn't
+ * change
+ */
+ predecessor->target_end = target_end;
+ }
+
+#ifdef DEBUG_GUEST_PHYS_REGION_ADD
+ fprintf(stderr, "%s: target_start=" TARGET_FMT_plx " target_end="
+ TARGET_FMT_plx ": %s (count: %u)\n", __func__, target_start,
+ target_end, predecessor ? "joined" : "added", g->list->num);
+#endif
+}
+
+void guest_phys_blocks_append(GuestPhysBlockList *list)
+{
+ GuestPhysListener g = { 0 };
+
+ g.list = list;
+ g.listener.region_add = &guest_phys_blocks_region_add;
+ memory_listener_register(&g.listener, &address_space_memory);
+ memory_listener_unregister(&g.listener);
+}
+
+static CPUState *find_paging_enabled_cpu(CPUState *start_cpu)