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>
This commit is contained in:
parent
55043ba37e
commit
e2177955a8
62
memory.c
62
memory.c
@ -515,6 +515,20 @@ static AddressSpace address_space_io = {
|
|||||||
.ops = &address_space_ops_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
|
/* Render a memory region into the global view. Ranges in @view obscure
|
||||||
* ranges in @mr.
|
* 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);
|
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)
|
void set_system_memory_map(MemoryRegion *mr)
|
||||||
{
|
{
|
||||||
address_space_memory.root = mr;
|
address_space_memory.root = mr;
|
||||||
|
39
memory.h
39
memory.h
@ -148,6 +148,24 @@ struct MemoryRegionPortio {
|
|||||||
|
|
||||||
#define PORTIO_END_OF_LIST() { }
|
#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
|
* 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,
|
void memory_region_set_alias_offset(MemoryRegion *mr,
|
||||||
target_phys_addr_t offset);
|
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.
|
* memory_region_transaction_begin: Start a transaction.
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user