From 8ea9252abe08631611effb905fbb4a015ec1514c Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Thu, 8 Dec 2011 15:58:43 +0200 Subject: memory: add memory_region_is_ram() Signed-off-by: Avi Kivity --- memory.h | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'memory.h') diff --git a/memory.h b/memory.h index fe643ff05b..dccc9007df 100644 --- a/memory.h +++ b/memory.h @@ -122,6 +122,7 @@ struct MemoryRegion { IORange iorange; bool terminates; bool readable; + bool ram; bool readonly; /* For RAM regions */ bool enabled; MemoryRegion *alias; @@ -266,6 +267,15 @@ void memory_region_destroy(MemoryRegion *mr); */ uint64_t memory_region_size(MemoryRegion *mr); +/** + * memory_region_is_ram: check whether a memory region is random access + * + * Returns %true is a memory region is random access. + * + * @mr: the memory region being queried + */ +bool memory_region_is_ram(MemoryRegion *mr); + /** * memory_region_get_ram_ptr: Get a pointer into a RAM memory region. * -- cgit v1.2.3-55-g7522 From ce7923da4da4e5895ab737fbf2c9b603c764a933 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Thu, 8 Dec 2011 16:05:11 +0200 Subject: memory: add memory_region_is_rom() Signed-off-by: Avi Kivity --- memory.c | 5 +++++ memory.h | 9 +++++++++ 2 files changed, 14 insertions(+) (limited to 'memory.h') diff --git a/memory.c b/memory.c index 4264b42222..01baeacf3c 100644 --- a/memory.c +++ b/memory.c @@ -1074,6 +1074,11 @@ bool memory_region_is_ram(MemoryRegion *mr) return mr->ram; } +bool memory_region_is_rom(MemoryRegion *mr) +{ + return mr->ram && mr->readonly; +} + void memory_region_set_offset(MemoryRegion *mr, target_phys_addr_t offset) { mr->offset = offset; diff --git a/memory.h b/memory.h index dccc9007df..91d6e8aef5 100644 --- a/memory.h +++ b/memory.h @@ -276,6 +276,15 @@ uint64_t memory_region_size(MemoryRegion *mr); */ bool memory_region_is_ram(MemoryRegion *mr); +/** + * memory_region_is_rom: check whether a memory region is ROM + * + * Returns %true is a memory region is read-only memory. + * + * @mr: the memory region being queried + */ +bool memory_region_is_rom(MemoryRegion *mr); + /** * memory_region_get_ram_ptr: Get a pointer into a RAM memory region. * -- cgit v1.2.3-55-g7522 From 55043ba37ee4b080a4f4f77b0ff672be3cbf8825 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Thu, 15 Dec 2011 17:20:34 +0200 Subject: memory: add memory_region_is_logging() Signed-off-by: Avi Kivity --- memory.c | 5 +++++ memory.h | 9 +++++++++ 2 files changed, 14 insertions(+) (limited to 'memory.h') diff --git a/memory.c b/memory.c index 01baeacf3c..6e6163524b 100644 --- a/memory.c +++ b/memory.c @@ -1074,6 +1074,11 @@ bool memory_region_is_ram(MemoryRegion *mr) return mr->ram; } +bool memory_region_is_logging(MemoryRegion *mr) +{ + return mr->dirty_log_mask; +} + bool memory_region_is_rom(MemoryRegion *mr) { return mr->ram && mr->readonly; diff --git a/memory.h b/memory.h index 91d6e8aef5..87fb9740c7 100644 --- a/memory.h +++ b/memory.h @@ -276,6 +276,15 @@ uint64_t memory_region_size(MemoryRegion *mr); */ bool memory_region_is_ram(MemoryRegion *mr); +/** + * memory_region_is_logging: return whether a memory region is logging writes + * + * Returns %true if the memory region is logging writes + * + * @mr: the memory region being queried + */ +bool memory_region_is_logging(MemoryRegion *mr); + /** * memory_region_is_rom: check whether a memory region is ROM * -- cgit v1.2.3-55-g7522 From e2177955a899483b19bd54e547db3b61db95eaf7 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Thu, 8 Dec 2011 15:00:18 +0200 Subject: memory: introduce memory_region_find() Given an address space (represented by the top-level memory region), returns the memory region that maps a given range. Useful for implementing DMA. The implementation is a simplistic binary search. Once we have a tree representation this can be optimized. Signed-off-by: Avi Kivity --- memory.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ memory.h | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) (limited to 'memory.h') diff --git a/memory.c b/memory.c index 6e6163524b..c3e64ba138 100644 --- a/memory.c +++ b/memory.c @@ -515,6 +515,20 @@ static AddressSpace address_space_io = { .ops = &address_space_ops_io, }; +static AddressSpace *memory_region_to_address_space(MemoryRegion *mr) +{ + while (mr->parent) { + mr = mr->parent; + } + if (mr == address_space_memory.root) { + return &address_space_memory; + } + if (mr == address_space_io.root) { + return &address_space_io; + } + abort(); +} + /* Render a memory region into the global view. Ranges in @view obscure * ranges in @mr. */ @@ -1386,6 +1400,54 @@ void memory_region_set_alias_offset(MemoryRegion *mr, target_phys_addr_t offset) memory_region_update_topology(mr); } +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); + return ret; +} + + void set_system_memory_map(MemoryRegion *mr) { address_space_memory.root = mr; diff --git a/memory.h b/memory.h index 87fb9740c7..4d8f39ad09 100644 --- a/memory.h +++ b/memory.h @@ -148,6 +148,24 @@ struct MemoryRegionPortio { #define PORTIO_END_OF_LIST() { } +typedef struct MemoryRegionSection MemoryRegionSection; + +/** + * MemoryRegionSection: describes a fragment of a #MemoryRegion + * + * @mr: the region, or %NULL if empty + * @offset_within_region: the beginning of the section, relative to @mr's start + * @size: the size of the section; will not exceed @mr's boundaries + * @offset_within_address_space: the address of the first byte of the section + * relative to the region's address space + */ +struct MemoryRegionSection { + MemoryRegion *mr; + target_phys_addr_t offset_within_region; + uint64_t size; + target_phys_addr_t offset_within_address_space; +}; + /** * memory_region_init: Initialize a memory region * @@ -568,6 +586,27 @@ void memory_region_set_address(MemoryRegion *mr, target_phys_addr_t addr); void memory_region_set_alias_offset(MemoryRegion *mr, target_phys_addr_t offset); +/** + * memory_region_find: locate a MemoryRegion in an address space + * + * Locates the first #MemoryRegion within an address space given by + * @address_space that overlaps the range given by @addr and @size. + * + * Returns a #MemoryRegionSection that describes a contiguous overlap. + * It will have the following characteristics: + * .@offset_within_address_space >= @addr + * .@offset_within_address_space + .@size <= @addr + @size + * .@size = 0 iff no overlap was found + * .@mr is non-%NULL iff an overlap was found + * + * @address_space: a top-level (i.e. parentless) region that contains + * the region to be found + * @addr: start of the area within @address_space to be searched + * @size: size of the area to be searched + */ +MemoryRegionSection memory_region_find(MemoryRegion *address_space, + target_phys_addr_t addr, uint64_t size); + /** * memory_region_transaction_begin: Start a transaction. * -- cgit v1.2.3-55-g7522 From 86e775c654b775d3e295e8a33bb03cc03bdab68d Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Thu, 15 Dec 2011 16:24:49 +0200 Subject: memory: replace cpu_physical_sync_dirty_bitmap() with a memory API The function is still used as the implementation. Signed-off-by: Avi Kivity --- arch_init.c | 6 ++---- cpu-all.h | 3 --- exec-obsolete.h | 3 +++ memory.c | 4 ++++ memory.h | 10 ++++++++++ 5 files changed, 19 insertions(+), 7 deletions(-) (limited to 'memory.h') diff --git a/arch_init.c b/arch_init.c index a411fdf263..ceef26ef17 100644 --- a/arch_init.c +++ b/arch_init.c @@ -41,6 +41,7 @@ #include "net.h" #include "gdbstub.h" #include "hw/smbios.h" +#include "exec-memory.h" #ifdef TARGET_SPARC int graphic_width = 1024; @@ -263,10 +264,7 @@ int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) return 0; } - if (cpu_physical_sync_dirty_bitmap(0, TARGET_PHYS_ADDR_MAX) != 0) { - qemu_file_set_error(f, -EINVAL); - return -EINVAL; - } + memory_global_sync_dirty_bitmap(get_system_memory()); if (stage == 1) { RAMBlock *block; diff --git a/cpu-all.h b/cpu-all.h index 9d787151e1..f2c53827ef 100644 --- a/cpu-all.h +++ b/cpu-all.h @@ -569,9 +569,6 @@ int cpu_physical_memory_set_dirty_tracking(int enable); int cpu_physical_memory_get_dirty_tracking(void); -int cpu_physical_sync_dirty_bitmap(target_phys_addr_t start_addr, - target_phys_addr_t end_addr); - int cpu_physical_log_start(target_phys_addr_t start_addr, ram_addr_t size); diff --git a/exec-obsolete.h b/exec-obsolete.h index 34b9fc56bd..5e5c4c63fb 100644 --- a/exec-obsolete.h +++ b/exec-obsolete.h @@ -63,6 +63,9 @@ static inline void cpu_register_physical_memory(target_phys_addr_t start_addr, void qemu_register_coalesced_mmio(target_phys_addr_t addr, ram_addr_t size); void qemu_unregister_coalesced_mmio(target_phys_addr_t addr, ram_addr_t size); +int cpu_physical_sync_dirty_bitmap(target_phys_addr_t start_addr, + target_phys_addr_t end_addr); + #endif #endif diff --git a/memory.c b/memory.c index c3e64ba138..ef5d647acc 100644 --- a/memory.c +++ b/memory.c @@ -1447,6 +1447,10 @@ MemoryRegionSection memory_region_find(MemoryRegion *address_space, return ret; } +void memory_global_sync_dirty_bitmap(MemoryRegion *address_space) +{ + cpu_physical_sync_dirty_bitmap(0, TARGET_PHYS_ADDR_MAX); +} void set_system_memory_map(MemoryRegion *mr) { diff --git a/memory.h b/memory.h index 4d8f39ad09..8197b45804 100644 --- a/memory.h +++ b/memory.h @@ -607,6 +607,16 @@ void memory_region_set_alias_offset(MemoryRegion *mr, MemoryRegionSection memory_region_find(MemoryRegion *address_space, target_phys_addr_t addr, uint64_t size); + +/** + * memory_global_sync_dirty_bitmap: synchronize the dirty log for all memory + * + * Synchronizes the dirty page log for an entire address space. + * @address_space: a top-level (i.e. parentless) region that contains the + * memory being synchronized + */ +void memory_global_sync_dirty_bitmap(MemoryRegion *address_space); + /** * memory_region_transaction_begin: Start a transaction. * -- cgit v1.2.3-55-g7522 From 7664e80c84700d8b7e88ae854d1d74806c63f013 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Sun, 11 Dec 2011 14:47:25 +0200 Subject: memory: add API for observing updates to the physical memory map Add an API that allows a client to observe changes in the global memory map: - region added (possibly with logging enabled) - region removed (possibly with logging enabled) - logging started on a region - logging stopped on a region - global logging started - global logging removed This API will eventually replace cpu_register_physical_memory_client(). Signed-off-by: Avi Kivity --- exec.c | 5 ++++ memory.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ memory.h | 47 ++++++++++++++++++++++++++++++++ 3 files changed, 145 insertions(+) (limited to 'memory.h') diff --git a/exec.c b/exec.c index 32782b48c9..36b61c91ac 100644 --- a/exec.c +++ b/exec.c @@ -1762,6 +1762,11 @@ static int cpu_notify_sync_dirty_bitmap(target_phys_addr_t start, static int cpu_notify_migration_log(int enable) { CPUPhysMemoryClient *client; + if (enable) { + memory_global_dirty_log_start(); + } else { + memory_global_dirty_log_stop(); + } QLIST_FOREACH(client, &memory_client_list, list) { int r = client->migration_log(client, enable); if (r < 0) diff --git a/memory.c b/memory.c index ef5d647acc..61e324bbf9 100644 --- a/memory.c +++ b/memory.c @@ -23,6 +23,10 @@ unsigned memory_region_transaction_depth = 0; static bool memory_region_update_pending = false; +static bool global_dirty_log = false; + +static QLIST_HEAD(, MemoryListener) memory_listeners + = QLIST_HEAD_INITIALIZER(memory_listeners); typedef struct AddrRange AddrRange; @@ -697,6 +701,32 @@ static void address_space_update_ioeventfds(AddressSpace *as) as->ioeventfd_nb = ioeventfd_nb; } +typedef void ListenerCallback(MemoryListener *listener, + MemoryRegionSection *mrs); + +/* Want "void (&MemoryListener::*callback)(const MemoryRegionSection& s)" */ +static void memory_listener_update_region(FlatRange *fr, AddressSpace *as, + size_t callback_offset) +{ + 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), + }; + MemoryListener *listener; + + QLIST_FOREACH(listener, &memory_listeners, link) { + ListenerCallback *callback + = *(ListenerCallback **)((void *)listener + callback_offset); + callback(listener, §ion); + } +} + +#define MEMORY_LISTENER_UPDATE_REGION(fr, as, callback) \ + memory_listener_update_region(fr, as, offsetof(MemoryListener, callback)) + static void address_space_update_topology_pass(AddressSpace *as, FlatView old_view, FlatView new_view, @@ -729,6 +759,7 @@ static void address_space_update_topology_pass(AddressSpace *as, /* In old, but (not in new, or in new but attributes changed). */ if (!adding) { + MEMORY_LISTENER_UPDATE_REGION(frold, as, region_del); as->ops->range_del(as, frold); } @@ -738,9 +769,11 @@ static void address_space_update_topology_pass(AddressSpace *as, if (adding) { if (frold->dirty_log_mask && !frnew->dirty_log_mask) { + MEMORY_LISTENER_UPDATE_REGION(frold, as, log_stop); as->ops->log_stop(as, frnew); } else if (frnew->dirty_log_mask && !frold->dirty_log_mask) { as->ops->log_start(as, frnew); + MEMORY_LISTENER_UPDATE_REGION(frold, as, log_start); } } @@ -751,6 +784,7 @@ static void address_space_update_topology_pass(AddressSpace *as, if (adding) { as->ops->range_add(as, frnew); + MEMORY_LISTENER_UPDATE_REGION(frold, as, region_add); } ++inew; @@ -1130,6 +1164,7 @@ void memory_region_sync_dirty_bitmap(MemoryRegion *mr) FOR_EACH_FLAT_RANGE(fr, &address_space_memory.current_map) { if (fr->mr == mr) { + MEMORY_LISTENER_UPDATE_REGION(fr, &address_space_memory, log_sync); cpu_physical_sync_dirty_bitmap(int128_get64(fr->addr.start), int128_get64(addrrange_end(fr->addr))); } @@ -1449,7 +1484,65 @@ MemoryRegionSection memory_region_find(MemoryRegion *address_space, void memory_global_sync_dirty_bitmap(MemoryRegion *address_space) { + AddressSpace *as = memory_region_to_address_space(address_space); + FlatRange *fr; + cpu_physical_sync_dirty_bitmap(0, TARGET_PHYS_ADDR_MAX); + FOR_EACH_FLAT_RANGE(fr, &as->current_map) { + MEMORY_LISTENER_UPDATE_REGION(fr, as, log_sync); + } +} + +void memory_global_dirty_log_start(void) +{ + MemoryListener *listener; + + global_dirty_log = true; + QLIST_FOREACH(listener, &memory_listeners, link) { + listener->log_global_start(listener); + } +} + +void memory_global_dirty_log_stop(void) +{ + MemoryListener *listener; + + global_dirty_log = false; + QLIST_FOREACH(listener, &memory_listeners, link) { + listener->log_global_stop(listener); + } +} + +static void listener_add_address_space(MemoryListener *listener, + AddressSpace *as) +{ + FlatRange *fr; + + 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), + }; + listener->region_add(listener, §ion); + } +} + +void memory_listener_register(MemoryListener *listener) +{ + QLIST_INSERT_HEAD(&memory_listeners, listener, link); + listener_add_address_space(listener, &address_space_memory); + listener_add_address_space(listener, &address_space_io); +} + +void memory_listener_unregister(MemoryListener *listener) +{ + QLIST_REMOVE(listener, link); } void set_system_memory_map(MemoryRegion *mr) diff --git a/memory.h b/memory.h index 8197b45804..5ba874e821 100644 --- a/memory.h +++ b/memory.h @@ -154,6 +154,7 @@ typedef struct MemoryRegionSection MemoryRegionSection; * MemoryRegionSection: describes a fragment of a #MemoryRegion * * @mr: the region, or %NULL if empty + * @address_space: the address space the region is mapped in * @offset_within_region: the beginning of the section, relative to @mr's start * @size: the size of the section; will not exceed @mr's boundaries * @offset_within_address_space: the address of the first byte of the section @@ -161,11 +162,31 @@ typedef struct MemoryRegionSection MemoryRegionSection; */ struct MemoryRegionSection { MemoryRegion *mr; + MemoryRegion *address_space; target_phys_addr_t offset_within_region; uint64_t size; target_phys_addr_t offset_within_address_space; }; +typedef struct MemoryListener MemoryListener; + +/** + * MemoryListener: callbacks structure for updates to the physical memory map + * + * Allows a component to adjust to changes in the guest-visible memory map. + * Use with memory_listener_register() and memory_listener_unregister(). + */ +struct MemoryListener { + void (*region_add)(MemoryListener *listener, MemoryRegionSection *section); + void (*region_del)(MemoryListener *listener, MemoryRegionSection *section); + void (*log_start)(MemoryListener *listener, MemoryRegionSection *section); + void (*log_stop)(MemoryListener *listener, MemoryRegionSection *section); + void (*log_sync)(MemoryListener *listener, MemoryRegionSection *section); + void (*log_global_start)(MemoryListener *listener); + void (*log_global_stop)(MemoryListener *listener); + QLIST_ENTRY(MemoryListener) link; +}; + /** * memory_region_init: Initialize a memory region * @@ -631,6 +652,32 @@ void memory_region_transaction_begin(void); */ void memory_region_transaction_commit(void); +/** + * memory_listener_register: register callbacks to be called when memory + * sections are mapped or unmapped into an address + * space + * + * @listener: an object containing the callbacks to be called + */ +void memory_listener_register(MemoryListener *listener); + +/** + * memory_listener_unregister: undo the effect of memory_listener_register() + * + * @listener: an object containing the callbacks to be removed + */ +void memory_listener_unregister(MemoryListener *listener); + +/** + * memory_global_dirty_log_start: begin dirty logging for all regions + */ +void memory_global_dirty_log_start(void); + +/** + * memory_global_dirty_log_stop: begin dirty logging for all regions + */ +void memory_global_dirty_log_stop(void); + void mtree_info(fprintf_function mon_printf, void *f); #endif -- cgit v1.2.3-55-g7522 From e34911c420c617151fe5bc95a4b6f269cca0483b Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Mon, 19 Dec 2011 12:06:23 +0200 Subject: memory: temporarily add memory_region_get_ram_addr() This is a layering violation, but needed while the code contains naked calls to qemu_get_ram_ptr() and the like. Signed-off-by: Avi Kivity --- memory.c | 6 ++++++ memory.h | 10 ++++++++++ 2 files changed, 16 insertions(+) (limited to 'memory.h') diff --git a/memory.c b/memory.c index 9e3f87a110..6a637d4e5b 100644 --- a/memory.c +++ b/memory.c @@ -1435,6 +1435,12 @@ void memory_region_set_alias_offset(MemoryRegion *mr, target_phys_addr_t offset) memory_region_update_topology(mr); } +ram_addr_t memory_region_get_ram_addr(MemoryRegion *mr) +{ + assert(mr->backend_registered); + return mr->ram_addr; +} + static int cmp_flatrange_addr(const void *addr_, const void *fr_) { const AddrRange *addr = addr_; diff --git a/memory.h b/memory.h index 5ba874e821..a82226a752 100644 --- a/memory.h +++ b/memory.h @@ -558,6 +558,16 @@ void memory_region_add_subregion_overlap(MemoryRegion *mr, target_phys_addr_t offset, MemoryRegion *subregion, unsigned priority); + +/** + * memory_region_get_ram_addr: Get the ram address associated with a memory + * region + * + * DO NOT USE THIS FUCNTION. This is a temporary workaround while the Xen + * code is being reworked. + */ +ram_addr_t memory_region_get_ram_addr(MemoryRegion *mr); + /** * memory_region_del_subregion: Remove a subregion. * -- cgit v1.2.3-55-g7522