summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvi Kivity <avi@redhat.com>2011-12-08 15:00:18 +0200
committerAvi Kivity <avi@redhat.com>2011-12-20 14:14:07 +0200
commite2177955a899483b19bd54e547db3b61db95eaf7 (patch)
treec4aa673b055838f97f722c37f9c7323a6955c88f
parent55043ba37ee4b080a4f4f77b0ff672be3cbf8825 (diff)
downloadqemu-e2177955a899483b19bd54e547db3b61db95eaf7.tar.gz
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 <avi@redhat.com>
-rw-r--r--memory.c62
-rw-r--r--memory.h39
2 files changed, 101 insertions, 0 deletions
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
*
@@ -569,6 +587,27 @@ 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.
*
* During a transaction, changes will be accumulated and made visible