* GNU GPL, version 2 or (at your option) any later version.
*/
-#include "memory.h"
-#include "exec-memory.h"
-#include "ioport.h"
-#include "bitops.h"
-#include "kvm.h"
+#include "exec/memory.h"
+#include "exec/address-spaces.h"
+#include "exec/ioport.h"
+#include "qemu/bitops.h"
+#include "sysemu/kvm.h"
#include <assert.h>
-#include "memory-internal.h"
+#include "exec/memory-internal.h"
-unsigned memory_region_transaction_depth = 0;
+static unsigned memory_region_transaction_depth;
+static bool memory_region_update_pending;
static bool global_dirty_log = false;
static QTAILQ_HEAD(memory_listeners, MemoryListener) memory_listeners
= QTAILQ_HEAD_INITIALIZER(memory_listeners);
+static QTAILQ_HEAD(, AddressSpace) address_spaces
+ = QTAILQ_HEAD_INITIALIZER(address_spaces);
+
typedef struct AddrRange AddrRange;
/*
switch (_direction) { \
case Forward: \
QTAILQ_FOREACH(_listener, &memory_listeners, link) { \
- _listener->_callback(_listener, ##_args); \
+ if (_listener->_callback) { \
+ _listener->_callback(_listener, ##_args); \
+ } \
} \
break; \
case Reverse: \
QTAILQ_FOREACH_REVERSE(_listener, &memory_listeners, \
memory_listeners, link) { \
- _listener->_callback(_listener, ##_args); \
+ if (_listener->_callback) { \
+ _listener->_callback(_listener, ##_args); \
+ } \
} \
break; \
default: \
switch (_direction) { \
case Forward: \
QTAILQ_FOREACH(_listener, &memory_listeners, link) { \
- if (memory_listener_match(_listener, _section)) { \
+ if (_listener->_callback \
+ && memory_listener_match(_listener, _section)) { \
_listener->_callback(_listener, _section, ##_args); \
} \
} \
case Reverse: \
QTAILQ_FOREACH_REVERSE(_listener, &memory_listeners, \
memory_listeners, link) { \
- if (memory_listener_match(_listener, _section)) { \
+ if (_listener->_callback \
+ && memory_listener_match(_listener, _section)) { \
_listener->_callback(_listener, _section, ##_args); \
} \
} \
#define MEMORY_LISTENER_UPDATE_REGION(fr, as, dir, callback) \
MEMORY_LISTENER_CALL(callback, dir, (&(MemoryRegionSection) { \
.mr = (fr)->mr, \
- .address_space = (as)->root, \
+ .address_space = (as), \
.offset_within_region = (fr)->offset_in_region, \
.size = int128_get64((fr)->addr.size), \
.offset_within_address_space = int128_get64((fr)->addr.start), \
/* Range of memory in the global map. Addresses are absolute. */
struct FlatRange {
MemoryRegion *mr;
- target_phys_addr_t offset_in_region;
+ hwaddr offset_in_region;
AddrRange addr;
uint8_t dirty_log_mask;
bool readable;
}
static void memory_region_read_accessor(void *opaque,
- target_phys_addr_t addr,
+ hwaddr addr,
uint64_t *value,
unsigned size,
unsigned shift,
}
static void memory_region_write_accessor(void *opaque,
- target_phys_addr_t addr,
+ hwaddr addr,
uint64_t *value,
unsigned size,
unsigned shift,
mr->ops->write(mr->opaque, addr, tmp, size);
}
-static void access_with_adjusted_size(target_phys_addr_t addr,
+static void access_with_adjusted_size(hwaddr addr,
uint64_t *value,
unsigned size,
unsigned access_size_min,
unsigned access_size_max,
void (*access)(void *opaque,
- target_phys_addr_t addr,
+ hwaddr addr,
uint64_t *value,
unsigned size,
unsigned shift,
}
}
-static AddressSpace address_space_memory;
-
static const MemoryRegionPortio *find_portio(MemoryRegion *mr, uint64_t offset,
unsigned width, bool write)
{
.destructor = memory_region_iorange_destructor,
};
-static AddressSpace address_space_io;
-
static AddressSpace *memory_region_to_address_space(MemoryRegion *mr)
{
+ AddressSpace *as;
+
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;
+ QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
+ if (mr == as->root) {
+ return as;
+ }
}
abort();
}
{
MemoryRegion *subregion;
unsigned i;
- target_phys_addr_t offset_in_region;
+ hwaddr offset_in_region;
Int128 remain;
Int128 now;
FlatRange fr;
offset_in_region += int128_get64(now);
int128_subfrom(&remain, now);
}
- if (int128_eq(base, view->ranges[i].addr.start)) {
- now = int128_min(remain, view->ranges[i].addr.size);
- int128_addto(&base, now);
- offset_in_region += int128_get64(now);
- int128_subfrom(&remain, now);
- }
+ now = int128_sub(int128_min(int128_add(base, remain),
+ addrrange_end(view->ranges[i].addr)),
+ base);
+ int128_addto(&base, now);
+ offset_in_region += int128_get64(now);
+ int128_subfrom(&remain, now);
}
if (int128_nz(remain)) {
fr.mr = mr;
flatview_init(&view);
- render_memory_region(&view, mr, int128_zero(),
- addrrange_make(int128_zero(), int128_2_64()), false);
+ if (mr) {
+ render_memory_region(&view, mr, int128_zero(),
+ addrrange_make(int128_zero(), int128_2_64()), false);
+ }
flatview_simplify(&view);
return view;
fds_new[inew]))) {
fd = &fds_old[iold];
section = (MemoryRegionSection) {
- .address_space = as->root,
+ .address_space = as,
.offset_within_address_space = int128_get64(fd->addr.start),
.size = int128_get64(fd->addr.size),
};
fds_old[iold]))) {
fd = &fds_new[inew];
section = (MemoryRegionSection) {
- .address_space = as->root,
+ .address_space = as,
.offset_within_address_space = int128_get64(fd->addr.start),
.size = int128_get64(fd->addr.size),
};
void memory_region_transaction_commit(void)
{
+ AddressSpace *as;
+
assert(memory_region_transaction_depth);
--memory_region_transaction_depth;
- if (!memory_region_transaction_depth) {
+ if (!memory_region_transaction_depth && memory_region_update_pending) {
+ memory_region_update_pending = false;
MEMORY_LISTENER_CALL_GLOBAL(begin, Forward);
- if (address_space_memory.root) {
- address_space_update_topology(&address_space_memory);
- }
- if (address_space_io.root) {
- address_space_update_topology(&address_space_io);
+ QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
+ address_space_update_topology(as);
}
MEMORY_LISTENER_CALL_GLOBAL(commit, Forward);
}
static bool memory_region_access_valid(MemoryRegion *mr,
- target_phys_addr_t addr,
+ hwaddr addr,
unsigned size,
bool is_write)
{
}
static uint64_t memory_region_dispatch_read1(MemoryRegion *mr,
- target_phys_addr_t addr,
+ hwaddr addr,
unsigned size)
{
uint64_t data = 0;
}
if (!mr->ops->read) {
- return mr->ops->old_mmio.read[bitops_ffsl(size)](mr->opaque, addr);
+ return mr->ops->old_mmio.read[ctz32(size)](mr->opaque, addr);
}
/* FIXME: support unaligned access */
}
static uint64_t memory_region_dispatch_read(MemoryRegion *mr,
- target_phys_addr_t addr,
+ hwaddr addr,
unsigned size)
{
uint64_t ret;
}
static void memory_region_dispatch_write(MemoryRegion *mr,
- target_phys_addr_t addr,
+ hwaddr addr,
uint64_t data,
unsigned size)
{
adjust_endianness(mr, &data, size);
if (!mr->ops->write) {
- mr->ops->old_mmio.write[bitops_ffsl(size)](mr->opaque, addr, data);
+ mr->ops->old_mmio.write[ctz32(size)](mr->opaque, addr, data);
return;
}
void memory_region_init_alias(MemoryRegion *mr,
const char *name,
MemoryRegion *orig,
- target_phys_addr_t offset,
+ hwaddr offset,
uint64_t size)
{
memory_region_init(mr, name, size);
mr->ram_addr = qemu_ram_alloc(size, mr);
}
-static uint64_t invalid_read(void *opaque, target_phys_addr_t addr,
+static uint64_t invalid_read(void *opaque, hwaddr addr,
unsigned size)
{
MemoryRegion *mr = opaque;
return -1U;
}
-static void invalid_write(void *opaque, target_phys_addr_t addr, uint64_t data,
+static void invalid_write(void *opaque, hwaddr addr, uint64_t data,
unsigned size)
{
MemoryRegion *mr = opaque;
void memory_region_destroy(MemoryRegion *mr)
{
assert(QTAILQ_EMPTY(&mr->subregions));
+ assert(memory_region_transaction_depth == 0);
mr->destructor(mr);
memory_region_clear_coalescing(mr);
g_free((char *)mr->name);
memory_region_transaction_begin();
mr->dirty_log_mask = (mr->dirty_log_mask & ~mask) | (log * mask);
+ memory_region_update_pending |= mr->enabled;
memory_region_transaction_commit();
}
-bool memory_region_get_dirty(MemoryRegion *mr, target_phys_addr_t addr,
- target_phys_addr_t size, unsigned client)
+bool memory_region_get_dirty(MemoryRegion *mr, hwaddr addr,
+ hwaddr size, unsigned client)
{
assert(mr->terminates);
return cpu_physical_memory_get_dirty(mr->ram_addr + addr, size,
1 << client);
}
-void memory_region_set_dirty(MemoryRegion *mr, target_phys_addr_t addr,
- target_phys_addr_t size)
+void memory_region_set_dirty(MemoryRegion *mr, hwaddr addr,
+ hwaddr size)
{
assert(mr->terminates);
return cpu_physical_memory_set_dirty_range(mr->ram_addr + addr, size, -1);
}
+bool memory_region_test_and_clear_dirty(MemoryRegion *mr, hwaddr addr,
+ hwaddr size, unsigned client)
+{
+ bool ret;
+ assert(mr->terminates);
+ ret = cpu_physical_memory_get_dirty(mr->ram_addr + addr, size,
+ 1 << client);
+ if (ret) {
+ cpu_physical_memory_reset_dirty(mr->ram_addr + addr,
+ mr->ram_addr + addr + size,
+ 1 << client);
+ }
+ return ret;
+}
+
+
void memory_region_sync_dirty_bitmap(MemoryRegion *mr)
{
+ AddressSpace *as;
FlatRange *fr;
- FOR_EACH_FLAT_RANGE(fr, address_space_memory.current_map) {
- if (fr->mr == mr) {
- MEMORY_LISTENER_UPDATE_REGION(fr, &address_space_memory,
- Forward, log_sync);
+ QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
+ FOR_EACH_FLAT_RANGE(fr, as->current_map) {
+ if (fr->mr == mr) {
+ MEMORY_LISTENER_UPDATE_REGION(fr, as, Forward, log_sync);
+ }
}
}
}
if (mr->readonly != readonly) {
memory_region_transaction_begin();
mr->readonly = readonly;
+ memory_region_update_pending |= mr->enabled;
memory_region_transaction_commit();
}
}
if (mr->readable != readable) {
memory_region_transaction_begin();
mr->readable = readable;
+ memory_region_update_pending |= mr->enabled;
memory_region_transaction_commit();
}
}
-void memory_region_reset_dirty(MemoryRegion *mr, target_phys_addr_t addr,
- target_phys_addr_t size, unsigned client)
+void memory_region_reset_dirty(MemoryRegion *mr, hwaddr addr,
+ hwaddr size, unsigned client)
{
assert(mr->terminates);
cpu_physical_memory_reset_dirty(mr->ram_addr + addr,
return qemu_get_ram_ptr(mr->ram_addr & TARGET_PAGE_MASK);
}
-static void memory_region_update_coalesced_range(MemoryRegion *mr)
+static void memory_region_update_coalesced_range_as(MemoryRegion *mr, AddressSpace *as)
{
FlatRange *fr;
CoalescedMemoryRange *cmr;
AddrRange tmp;
+ MemoryRegionSection section;
- FOR_EACH_FLAT_RANGE(fr, address_space_memory.current_map) {
+ FOR_EACH_FLAT_RANGE(fr, as->current_map) {
if (fr->mr == mr) {
- qemu_unregister_coalesced_mmio(int128_get64(fr->addr.start),
- int128_get64(fr->addr.size));
+ section = (MemoryRegionSection) {
+ .address_space = as,
+ .offset_within_address_space = int128_get64(fr->addr.start),
+ .size = int128_get64(fr->addr.size),
+ };
+
+ MEMORY_LISTENER_CALL(coalesced_mmio_del, Reverse, §ion,
+ int128_get64(fr->addr.start),
+ int128_get64(fr->addr.size));
QTAILQ_FOREACH(cmr, &mr->coalesced, link) {
tmp = addrrange_shift(cmr->addr,
int128_sub(fr->addr.start,
continue;
}
tmp = addrrange_intersection(tmp, fr->addr);
- qemu_register_coalesced_mmio(int128_get64(tmp.start),
- int128_get64(tmp.size));
+ MEMORY_LISTENER_CALL(coalesced_mmio_add, Forward, §ion,
+ int128_get64(tmp.start),
+ int128_get64(tmp.size));
}
}
}
}
+static void memory_region_update_coalesced_range(MemoryRegion *mr)
+{
+ AddressSpace *as;
+
+ QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
+ memory_region_update_coalesced_range_as(mr, as);
+ }
+}
+
void memory_region_set_coalescing(MemoryRegion *mr)
{
memory_region_clear_coalescing(mr);
}
void memory_region_add_coalescing(MemoryRegion *mr,
- target_phys_addr_t offset,
+ hwaddr offset,
uint64_t size)
{
CoalescedMemoryRange *cmr = g_malloc(sizeof(*cmr));
}
void memory_region_add_eventfd(MemoryRegion *mr,
- target_phys_addr_t addr,
+ hwaddr addr,
unsigned size,
bool match_data,
uint64_t data,
};
unsigned i;
+ adjust_endianness(mr, &mrfd.data, size);
memory_region_transaction_begin();
for (i = 0; i < mr->ioeventfd_nb; ++i) {
if (memory_region_ioeventfd_before(mrfd, mr->ioeventfds[i])) {
memmove(&mr->ioeventfds[i+1], &mr->ioeventfds[i],
sizeof(*mr->ioeventfds) * (mr->ioeventfd_nb-1 - i));
mr->ioeventfds[i] = mrfd;
+ memory_region_update_pending |= mr->enabled;
memory_region_transaction_commit();
}
void memory_region_del_eventfd(MemoryRegion *mr,
- target_phys_addr_t addr,
+ hwaddr addr,
unsigned size,
bool match_data,
uint64_t data,
};
unsigned i;
+ adjust_endianness(mr, &mrfd.data, size);
memory_region_transaction_begin();
for (i = 0; i < mr->ioeventfd_nb; ++i) {
if (memory_region_ioeventfd_equal(mrfd, mr->ioeventfds[i])) {
--mr->ioeventfd_nb;
mr->ioeventfds = g_realloc(mr->ioeventfds,
sizeof(*mr->ioeventfds)*mr->ioeventfd_nb + 1);
+ memory_region_update_pending |= mr->enabled;
memory_region_transaction_commit();
}
static void memory_region_add_subregion_common(MemoryRegion *mr,
- target_phys_addr_t offset,
+ hwaddr offset,
MemoryRegion *subregion)
{
MemoryRegion *other;
}
QTAILQ_INSERT_TAIL(&mr->subregions, subregion, subregions_link);
done:
+ memory_region_update_pending |= mr->enabled && subregion->enabled;
memory_region_transaction_commit();
}
void memory_region_add_subregion(MemoryRegion *mr,
- target_phys_addr_t offset,
+ hwaddr offset,
MemoryRegion *subregion)
{
subregion->may_overlap = false;
}
void memory_region_add_subregion_overlap(MemoryRegion *mr,
- target_phys_addr_t offset,
+ hwaddr offset,
MemoryRegion *subregion,
unsigned priority)
{
assert(subregion->parent == mr);
subregion->parent = NULL;
QTAILQ_REMOVE(&mr->subregions, subregion, subregions_link);
+ memory_region_update_pending |= mr->enabled && subregion->enabled;
memory_region_transaction_commit();
}
}
memory_region_transaction_begin();
mr->enabled = enabled;
+ memory_region_update_pending = true;
memory_region_transaction_commit();
}
-void memory_region_set_address(MemoryRegion *mr, target_phys_addr_t addr)
+void memory_region_set_address(MemoryRegion *mr, hwaddr addr)
{
MemoryRegion *parent = mr->parent;
unsigned priority = mr->priority;
memory_region_transaction_commit();
}
-void memory_region_set_alias_offset(MemoryRegion *mr, target_phys_addr_t offset)
+void memory_region_set_alias_offset(MemoryRegion *mr, hwaddr offset)
{
assert(mr->alias);
memory_region_transaction_begin();
mr->alias_offset = offset;
+ memory_region_update_pending |= mr->enabled;
memory_region_transaction_commit();
}
}
MemoryRegionSection memory_region_find(MemoryRegion *address_space,
- target_phys_addr_t addr, uint64_t size)
+ hwaddr addr, uint64_t size)
{
AddressSpace *as = memory_region_to_address_space(address_space);
AddrRange range = addrrange_make(int128_make64(addr),
{
FlatRange *fr;
- if (!as->root) {
- return;
- }
-
if (listener->address_space_filter
- && listener->address_space_filter != as->root) {
+ && listener->address_space_filter != as) {
return;
}
if (global_dirty_log) {
- listener->log_global_start(listener);
+ if (listener->log_global_start) {
+ listener->log_global_start(listener);
+ }
}
+
FOR_EACH_FLAT_RANGE(fr, as->current_map) {
MemoryRegionSection section = {
.mr = fr->mr,
- .address_space = as->root,
+ .address_space = as,
.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);
+ if (listener->region_add) {
+ listener->region_add(listener, §ion);
+ }
}
}
-void memory_listener_register(MemoryListener *listener, MemoryRegion *filter)
+void memory_listener_register(MemoryListener *listener, AddressSpace *filter)
{
MemoryListener *other = NULL;
+ AddressSpace *as;
listener->address_space_filter = filter;
if (QTAILQ_EMPTY(&memory_listeners)
}
QTAILQ_INSERT_BEFORE(other, listener, link);
}
- listener_add_address_space(listener, &address_space_memory);
- listener_add_address_space(listener, &address_space_io);
+
+ QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
+ listener_add_address_space(listener, as);
+ }
}
void memory_listener_unregister(MemoryListener *listener)
as->root = root;
as->current_map = g_new(FlatView, 1);
flatview_init(as->current_map);
+ QTAILQ_INSERT_TAIL(&address_spaces, as, address_spaces_link);
+ as->name = NULL;
memory_region_transaction_commit();
+ address_space_init_dispatch(as);
}
-void set_system_memory_map(MemoryRegion *mr)
+void address_space_destroy(AddressSpace *as)
{
- address_space_init(&address_space_memory, mr);
-}
-
-void set_system_io_map(MemoryRegion *mr)
-{
- address_space_init(&address_space_io, mr);
+ /* Flush out anything from MemoryListeners listening in on this */
+ memory_region_transaction_begin();
+ as->root = NULL;
+ memory_region_transaction_commit();
+ QTAILQ_REMOVE(&address_spaces, as, address_spaces_link);
+ address_space_destroy_dispatch(as);
+ flatview_destroy(as->current_map);
+ g_free(as->current_map);
}
-uint64_t io_mem_read(MemoryRegion *mr, target_phys_addr_t addr, unsigned size)
+uint64_t io_mem_read(MemoryRegion *mr, hwaddr addr, unsigned size)
{
return memory_region_dispatch_read(mr, addr, size);
}
-void io_mem_write(MemoryRegion *mr, target_phys_addr_t addr,
+void io_mem_write(MemoryRegion *mr, hwaddr addr,
uint64_t val, unsigned size)
{
memory_region_dispatch_write(mr, addr, val, size);
static void mtree_print_mr(fprintf_function mon_printf, void *f,
const MemoryRegion *mr, unsigned int level,
- target_phys_addr_t base,
+ hwaddr base,
MemoryRegionListHead *alias_print_queue)
{
MemoryRegionList *new_ml, *ml, *next_ml;
const MemoryRegion *submr;
unsigned int i;
- if (!mr) {
+ if (!mr || !mr->enabled) {
return;
}
"-" TARGET_FMT_plx "\n",
base + mr->addr,
base + mr->addr
- + (target_phys_addr_t)int128_get64(mr->size) - 1,
+ + (hwaddr)int128_get64(mr->size) - 1,
mr->priority,
mr->readable ? 'R' : '-',
!mr->readonly && !(mr->rom_device && mr->readable) ? 'W'
mr->alias->name,
mr->alias_offset,
mr->alias_offset
- + (target_phys_addr_t)int128_get64(mr->size) - 1);
+ + (hwaddr)int128_get64(mr->size) - 1);
} else {
mon_printf(f,
TARGET_FMT_plx "-" TARGET_FMT_plx " (prio %d, %c%c): %s\n",
base + mr->addr,
base + mr->addr
- + (target_phys_addr_t)int128_get64(mr->size) - 1,
+ + (hwaddr)int128_get64(mr->size) - 1,
mr->priority,
mr->readable ? 'R' : '-',
!mr->readonly && !(mr->rom_device && mr->readable) ? 'W'
{
MemoryRegionListHead ml_head;
MemoryRegionList *ml, *ml2;
+ AddressSpace *as;
QTAILQ_INIT(&ml_head);
- mon_printf(f, "memory\n");
- mtree_print_mr(mon_printf, f, address_space_memory.root, 0, 0, &ml_head);
-
- if (address_space_io.root &&
- !QTAILQ_EMPTY(&address_space_io.root->subregions)) {
- mon_printf(f, "I/O\n");
- mtree_print_mr(mon_printf, f, address_space_io.root, 0, 0, &ml_head);
+ QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
+ if (!as->name) {
+ continue;
+ }
+ mon_printf(f, "%s\n", as->name);
+ mtree_print_mr(mon_printf, f, as->root, 0, 0, &ml_head);
}
mon_printf(f, "aliases\n");