+ memory_region_update_topology(mr);
+}
+
+void memory_region_set_enabled(MemoryRegion *mr, bool enabled)
+{
+ if (enabled == mr->enabled) {
+ return;
+ }
+ mr->enabled = enabled;
+ memory_region_update_topology(NULL);
+}
+
+void memory_region_set_address(MemoryRegion *mr, target_phys_addr_t addr)
+{
+ MemoryRegion *parent = mr->parent;
+ unsigned priority = mr->priority;
+ bool may_overlap = mr->may_overlap;
+
+ if (addr == mr->addr || !parent) {
+ mr->addr = addr;
+ return;
+ }
+
+ memory_region_transaction_begin();
+ memory_region_del_subregion(parent, mr);
+ if (may_overlap) {
+ memory_region_add_subregion_overlap(parent, addr, mr, priority);
+ } else {
+ memory_region_add_subregion(parent, addr, mr);
+ }
+ memory_region_transaction_commit();
+}
+
+void memory_region_set_alias_offset(MemoryRegion *mr, target_phys_addr_t offset)
+{
+ target_phys_addr_t old_offset = mr->alias_offset;
+
+ assert(mr->alias);
+ mr->alias_offset = offset;
+
+ if (offset == old_offset || !mr->parent) {
+ return;
+ }
+
+ memory_region_update_topology(mr);
+}
+
+ram_addr_t memory_region_get_ram_addr(MemoryRegion *mr)
+{
+ return mr->ram_addr;
+}
+
+static int cmp_flatrange_addr(const void *addr_, const void *fr_)
+{
+ const AddrRange *addr = addr_;
+ const FlatRange *fr = fr_;
+
+ if (int128_le(addrrange_end(*addr), fr->addr.start)) {
+ return -1;
+ } else if (int128_ge(addr->start, addrrange_end(fr->addr))) {
+ return 1;
+ }
+ return 0;
+}
+
+static FlatRange *address_space_lookup(AddressSpace *as, AddrRange addr)
+{
+ return bsearch(&addr, as->current_map.ranges, as->current_map.nr,
+ sizeof(FlatRange), cmp_flatrange_addr);
+}
+
+MemoryRegionSection memory_region_find(MemoryRegion *address_space,
+ target_phys_addr_t addr, uint64_t size)
+{
+ AddressSpace *as = memory_region_to_address_space(address_space);
+ AddrRange range = addrrange_make(int128_make64(addr),
+ int128_make64(size));
+ FlatRange *fr = address_space_lookup(as, range);
+ MemoryRegionSection ret = { .mr = NULL, .size = 0 };
+
+ if (!fr) {
+ return ret;
+ }
+
+ while (fr > as->current_map.ranges
+ && addrrange_intersects(fr[-1].addr, range)) {
+ --fr;
+ }
+
+ ret.mr = fr->mr;
+ range = addrrange_intersection(range, fr->addr);
+ ret.offset_within_region = fr->offset_in_region;
+ ret.offset_within_region += int128_get64(int128_sub(range.start,
+ fr->addr.start));
+ ret.size = int128_get64(range.size);
+ ret.offset_within_address_space = int128_get64(range.start);
+ ret.readonly = fr->readonly;
+ return ret;
+}
+
+void memory_global_sync_dirty_bitmap(MemoryRegion *address_space)
+{
+ AddressSpace *as = memory_region_to_address_space(address_space);
+ FlatRange *fr;
+
+ FOR_EACH_FLAT_RANGE(fr, &as->current_map) {
+ MEMORY_LISTENER_UPDATE_REGION(fr, as, Forward, log_sync);
+ }
+}
+
+void memory_global_dirty_log_start(void)
+{
+ global_dirty_log = true;
+ MEMORY_LISTENER_CALL_GLOBAL(log_global_start, Forward);
+}
+
+void memory_global_dirty_log_stop(void)
+{
+ global_dirty_log = false;
+ MEMORY_LISTENER_CALL_GLOBAL(log_global_stop, Reverse);
+}
+
+static void listener_add_address_space(MemoryListener *listener,
+ AddressSpace *as)
+{
+ FlatRange *fr;
+
+ if (listener->address_space_filter
+ && listener->address_space_filter != as->root) {
+ return;
+ }
+
+ if (global_dirty_log) {
+ listener->log_global_start(listener);
+ }
+ FOR_EACH_FLAT_RANGE(fr, &as->current_map) {
+ MemoryRegionSection section = {
+ .mr = fr->mr,
+ .address_space = as->root,
+ .offset_within_region = fr->offset_in_region,
+ .size = int128_get64(fr->addr.size),
+ .offset_within_address_space = int128_get64(fr->addr.start),
+ .readonly = fr->readonly,
+ };
+ listener->region_add(listener, §ion);
+ }
+}
+
+void memory_listener_register(MemoryListener *listener, MemoryRegion *filter)
+{
+ MemoryListener *other = NULL;
+
+ listener->address_space_filter = filter;
+ if (QTAILQ_EMPTY(&memory_listeners)
+ || listener->priority >= QTAILQ_LAST(&memory_listeners,
+ memory_listeners)->priority) {
+ QTAILQ_INSERT_TAIL(&memory_listeners, listener, link);
+ } else {
+ QTAILQ_FOREACH(other, &memory_listeners, link) {
+ if (listener->priority < other->priority) {
+ break;
+ }
+ }
+ QTAILQ_INSERT_BEFORE(other, listener, link);
+ }
+ listener_add_address_space(listener, &address_space_memory);
+ listener_add_address_space(listener, &address_space_io);
+}
+
+void memory_listener_unregister(MemoryListener *listener)
+{
+ QTAILQ_REMOVE(&memory_listeners, listener, link);