Merge remote-tracking branch 'qemu-kvm/memory/core' into staging
* qemu-kvm/memory/core: (30 commits) memory: allow phys_map tree paths to terminate early memory: unify PhysPageEntry::node and ::leaf memory: change phys_page_set() to set multiple pages memory: switch phys_page_set() to a recursive implementation memory: replace phys_page_find_alloc() with phys_page_set() memory: simplify multipage/subpage registration memory: give phys_page_find() its own tree search loop memory: make phys_page_find() return a MemoryRegionSection memory: move tlb flush to MemoryListener commit callback memory: unify the two branches of cpu_register_physical_memory_log() memory: fix RAM subpages in newly initialized pages memory: compress phys_map node pointers to 16 bits memory: store MemoryRegionSection pointers in phys_map memory: unify phys_map last level with intermediate levels memory: remove first level of l1_phys_map memory: change memory registration to rebuild the memory map on each change memory: support stateless memory listeners memory: split memory listener for the two address spaces xen: ignore I/O memory regions memory: allow MemoryListeners to observe a specific address space ...
This commit is contained in:
commit
14655e482b
@ -37,7 +37,7 @@ void cpu_unregister_io_memory(int table_address);
|
||||
|
||||
struct MemoryRegionSection;
|
||||
void cpu_register_physical_memory_log(struct MemoryRegionSection *section,
|
||||
bool readable, bool readonly);
|
||||
bool readonly);
|
||||
|
||||
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);
|
||||
@ -121,6 +121,9 @@ static inline void cpu_physical_memory_mask_dirty_range(ram_addr_t start,
|
||||
|
||||
void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end,
|
||||
int dirty_flags);
|
||||
|
||||
extern const IORangeOps memory_region_iorange_ops;
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
33
hw/vhost.c
33
hw/vhost.c
@ -436,6 +436,14 @@ static bool vhost_section(MemoryRegionSection *section)
|
||||
&& memory_region_is_ram(section->mr);
|
||||
}
|
||||
|
||||
static void vhost_begin(MemoryListener *listener)
|
||||
{
|
||||
}
|
||||
|
||||
static void vhost_commit(MemoryListener *listener)
|
||||
{
|
||||
}
|
||||
|
||||
static void vhost_region_add(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
@ -476,6 +484,11 @@ static void vhost_region_del(MemoryListener *listener,
|
||||
}
|
||||
}
|
||||
|
||||
static void vhost_region_nop(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
}
|
||||
|
||||
static int vhost_virtqueue_set_addr(struct vhost_dev *dev,
|
||||
struct vhost_virtqueue *vq,
|
||||
unsigned idx, bool enable_log)
|
||||
@ -720,6 +733,18 @@ static void vhost_virtqueue_cleanup(struct vhost_dev *dev,
|
||||
0, virtio_queue_get_desc_size(vdev, idx));
|
||||
}
|
||||
|
||||
static void vhost_eventfd_add(MemoryListener *listener,
|
||||
MemoryRegionSection *section,
|
||||
bool match_data, uint64_t data, int fd)
|
||||
{
|
||||
}
|
||||
|
||||
static void vhost_eventfd_del(MemoryListener *listener,
|
||||
MemoryRegionSection *section,
|
||||
bool match_data, uint64_t data, int fd)
|
||||
{
|
||||
}
|
||||
|
||||
int vhost_dev_init(struct vhost_dev *hdev, int devfd, bool force)
|
||||
{
|
||||
uint64_t features;
|
||||
@ -744,13 +769,19 @@ int vhost_dev_init(struct vhost_dev *hdev, int devfd, bool force)
|
||||
hdev->features = features;
|
||||
|
||||
hdev->memory_listener = (MemoryListener) {
|
||||
.begin = vhost_begin,
|
||||
.commit = vhost_commit,
|
||||
.region_add = vhost_region_add,
|
||||
.region_del = vhost_region_del,
|
||||
.region_nop = vhost_region_nop,
|
||||
.log_start = vhost_log_start,
|
||||
.log_stop = vhost_log_stop,
|
||||
.log_sync = vhost_log_sync,
|
||||
.log_global_start = vhost_log_global_start,
|
||||
.log_global_stop = vhost_log_global_stop,
|
||||
.eventfd_add = vhost_eventfd_add,
|
||||
.eventfd_del = vhost_eventfd_del,
|
||||
.priority = 10
|
||||
};
|
||||
hdev->mem = g_malloc0(offsetof(struct vhost_memory, regions));
|
||||
hdev->n_mem_sections = 0;
|
||||
@ -759,7 +790,7 @@ int vhost_dev_init(struct vhost_dev *hdev, int devfd, bool force)
|
||||
hdev->log_size = 0;
|
||||
hdev->log_enabled = false;
|
||||
hdev->started = false;
|
||||
memory_listener_register(&hdev->memory_listener);
|
||||
memory_listener_register(&hdev->memory_listener, NULL);
|
||||
hdev->force = force;
|
||||
return 0;
|
||||
fail:
|
||||
|
28
ioport.c
28
ioport.c
@ -328,6 +328,7 @@ void portio_list_init(PortioList *piolist,
|
||||
piolist->ports = callbacks;
|
||||
piolist->nr = 0;
|
||||
piolist->regions = g_new0(MemoryRegion *, n);
|
||||
piolist->aliases = g_new0(MemoryRegion *, n);
|
||||
piolist->address_space = NULL;
|
||||
piolist->opaque = opaque;
|
||||
piolist->name = name;
|
||||
@ -336,6 +337,7 @@ void portio_list_init(PortioList *piolist,
|
||||
void portio_list_destroy(PortioList *piolist)
|
||||
{
|
||||
g_free(piolist->regions);
|
||||
g_free(piolist->aliases);
|
||||
}
|
||||
|
||||
static void portio_list_add_1(PortioList *piolist,
|
||||
@ -345,7 +347,7 @@ static void portio_list_add_1(PortioList *piolist,
|
||||
{
|
||||
MemoryRegionPortio *pio;
|
||||
MemoryRegionOps *ops;
|
||||
MemoryRegion *region;
|
||||
MemoryRegion *region, *alias;
|
||||
unsigned i;
|
||||
|
||||
/* Copy the sub-list and null-terminate it. */
|
||||
@ -362,12 +364,20 @@ static void portio_list_add_1(PortioList *piolist,
|
||||
ops->old_portio = pio;
|
||||
|
||||
region = g_new(MemoryRegion, 1);
|
||||
alias = g_new(MemoryRegion, 1);
|
||||
/*
|
||||
* Use an alias so that the callback is called with an absolute address,
|
||||
* rather than an offset relative to to start + off_low.
|
||||
*/
|
||||
memory_region_init_io(region, ops, piolist->opaque, piolist->name,
|
||||
off_high - off_low);
|
||||
memory_region_set_offset(region, start + off_low);
|
||||
UINT64_MAX);
|
||||
memory_region_init_alias(alias, piolist->name,
|
||||
region, start + off_low, off_high - off_low);
|
||||
memory_region_add_subregion(piolist->address_space,
|
||||
start + off_low, region);
|
||||
piolist->regions[piolist->nr++] = region;
|
||||
start + off_low, alias);
|
||||
piolist->regions[piolist->nr] = region;
|
||||
piolist->aliases[piolist->nr] = alias;
|
||||
++piolist->nr;
|
||||
}
|
||||
|
||||
void portio_list_add(PortioList *piolist,
|
||||
@ -409,15 +419,19 @@ void portio_list_add(PortioList *piolist,
|
||||
|
||||
void portio_list_del(PortioList *piolist)
|
||||
{
|
||||
MemoryRegion *mr;
|
||||
MemoryRegion *mr, *alias;
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < piolist->nr; ++i) {
|
||||
mr = piolist->regions[i];
|
||||
memory_region_del_subregion(piolist->address_space, mr);
|
||||
alias = piolist->aliases[i];
|
||||
memory_region_del_subregion(piolist->address_space, alias);
|
||||
memory_region_destroy(alias);
|
||||
memory_region_destroy(mr);
|
||||
g_free((MemoryRegionOps *)mr->ops);
|
||||
g_free(mr);
|
||||
g_free(alias);
|
||||
piolist->regions[i] = NULL;
|
||||
piolist->aliases[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
1
ioport.h
1
ioport.h
@ -60,6 +60,7 @@ typedef struct PortioList {
|
||||
struct MemoryRegion *address_space;
|
||||
unsigned nr;
|
||||
struct MemoryRegion **regions;
|
||||
struct MemoryRegion **aliases;
|
||||
void *opaque;
|
||||
const char *name;
|
||||
} PortioList;
|
||||
|
97
kvm-all.c
97
kvm-all.c
@ -28,6 +28,7 @@
|
||||
#include "kvm.h"
|
||||
#include "bswap.h"
|
||||
#include "memory.h"
|
||||
#include "exec-memory.h"
|
||||
|
||||
/* This check must be after config-host.h is included */
|
||||
#ifdef CONFIG_EVENTFD
|
||||
@ -674,6 +675,14 @@ static void kvm_set_phys_mem(MemoryRegionSection *section, bool add)
|
||||
}
|
||||
}
|
||||
|
||||
static void kvm_begin(MemoryListener *listener)
|
||||
{
|
||||
}
|
||||
|
||||
static void kvm_commit(MemoryListener *listener)
|
||||
{
|
||||
}
|
||||
|
||||
static void kvm_region_add(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
@ -686,6 +695,11 @@ static void kvm_region_del(MemoryListener *listener,
|
||||
kvm_set_phys_mem(section, false);
|
||||
}
|
||||
|
||||
static void kvm_region_nop(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
}
|
||||
|
||||
static void kvm_log_sync(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
@ -713,14 +727,95 @@ static void kvm_log_global_stop(struct MemoryListener *listener)
|
||||
assert(r >= 0);
|
||||
}
|
||||
|
||||
static void kvm_mem_ioeventfd_add(MemoryRegionSection *section,
|
||||
bool match_data, uint64_t data, int fd)
|
||||
{
|
||||
int r;
|
||||
|
||||
assert(match_data && section->size == 4);
|
||||
|
||||
r = kvm_set_ioeventfd_mmio_long(fd, section->offset_within_address_space,
|
||||
data, true);
|
||||
if (r < 0) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void kvm_mem_ioeventfd_del(MemoryRegionSection *section,
|
||||
bool match_data, uint64_t data, int fd)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = kvm_set_ioeventfd_mmio_long(fd, section->offset_within_address_space,
|
||||
data, false);
|
||||
if (r < 0) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void kvm_io_ioeventfd_add(MemoryRegionSection *section,
|
||||
bool match_data, uint64_t data, int fd)
|
||||
{
|
||||
int r;
|
||||
|
||||
assert(match_data && section->size == 2);
|
||||
|
||||
r = kvm_set_ioeventfd_pio_word(fd, section->offset_within_address_space,
|
||||
data, true);
|
||||
if (r < 0) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void kvm_io_ioeventfd_del(MemoryRegionSection *section,
|
||||
bool match_data, uint64_t data, int fd)
|
||||
|
||||
{
|
||||
int r;
|
||||
|
||||
r = kvm_set_ioeventfd_pio_word(fd, section->offset_within_address_space,
|
||||
data, false);
|
||||
if (r < 0) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void kvm_eventfd_add(MemoryListener *listener,
|
||||
MemoryRegionSection *section,
|
||||
bool match_data, uint64_t data, int fd)
|
||||
{
|
||||
if (section->address_space == get_system_memory()) {
|
||||
kvm_mem_ioeventfd_add(section, match_data, data, fd);
|
||||
} else {
|
||||
kvm_io_ioeventfd_add(section, match_data, data, fd);
|
||||
}
|
||||
}
|
||||
|
||||
static void kvm_eventfd_del(MemoryListener *listener,
|
||||
MemoryRegionSection *section,
|
||||
bool match_data, uint64_t data, int fd)
|
||||
{
|
||||
if (section->address_space == get_system_memory()) {
|
||||
kvm_mem_ioeventfd_del(section, match_data, data, fd);
|
||||
} else {
|
||||
kvm_io_ioeventfd_del(section, match_data, data, fd);
|
||||
}
|
||||
}
|
||||
|
||||
static MemoryListener kvm_memory_listener = {
|
||||
.begin = kvm_begin,
|
||||
.commit = kvm_commit,
|
||||
.region_add = kvm_region_add,
|
||||
.region_del = kvm_region_del,
|
||||
.region_nop = kvm_region_nop,
|
||||
.log_start = kvm_log_start,
|
||||
.log_stop = kvm_log_stop,
|
||||
.log_sync = kvm_log_sync,
|
||||
.log_global_start = kvm_log_global_start,
|
||||
.log_global_stop = kvm_log_global_stop,
|
||||
.eventfd_add = kvm_eventfd_add,
|
||||
.eventfd_del = kvm_eventfd_del,
|
||||
.priority = 10,
|
||||
};
|
||||
|
||||
static void kvm_handle_interrupt(CPUState *env, int mask)
|
||||
@ -965,7 +1060,7 @@ int kvm_init(void)
|
||||
}
|
||||
|
||||
kvm_state = s;
|
||||
memory_listener_register(&kvm_memory_listener);
|
||||
memory_listener_register(&kvm_memory_listener, NULL);
|
||||
|
||||
s->many_ioeventfds = kvm_check_many_ioeventfds();
|
||||
|
||||
|
328
memory.c
328
memory.c
@ -27,8 +27,8 @@ 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);
|
||||
static QTAILQ_HEAD(memory_listeners, MemoryListener) memory_listeners
|
||||
= QTAILQ_HEAD_INITIALIZER(memory_listeners);
|
||||
|
||||
typedef struct AddrRange AddrRange;
|
||||
|
||||
@ -82,6 +82,71 @@ static AddrRange addrrange_intersection(AddrRange r1, AddrRange r2)
|
||||
return addrrange_make(start, int128_sub(end, start));
|
||||
}
|
||||
|
||||
enum ListenerDirection { Forward, Reverse };
|
||||
|
||||
static bool memory_listener_match(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
return !listener->address_space_filter
|
||||
|| listener->address_space_filter == section->address_space;
|
||||
}
|
||||
|
||||
#define MEMORY_LISTENER_CALL_GLOBAL(_callback, _direction, _args...) \
|
||||
do { \
|
||||
MemoryListener *_listener; \
|
||||
\
|
||||
switch (_direction) { \
|
||||
case Forward: \
|
||||
QTAILQ_FOREACH(_listener, &memory_listeners, link) { \
|
||||
_listener->_callback(_listener, ##_args); \
|
||||
} \
|
||||
break; \
|
||||
case Reverse: \
|
||||
QTAILQ_FOREACH_REVERSE(_listener, &memory_listeners, \
|
||||
memory_listeners, link) { \
|
||||
_listener->_callback(_listener, ##_args); \
|
||||
} \
|
||||
break; \
|
||||
default: \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define MEMORY_LISTENER_CALL(_callback, _direction, _section, _args...) \
|
||||
do { \
|
||||
MemoryListener *_listener; \
|
||||
\
|
||||
switch (_direction) { \
|
||||
case Forward: \
|
||||
QTAILQ_FOREACH(_listener, &memory_listeners, link) { \
|
||||
if (memory_listener_match(_listener, _section)) { \
|
||||
_listener->_callback(_listener, _section, ##_args); \
|
||||
} \
|
||||
} \
|
||||
break; \
|
||||
case Reverse: \
|
||||
QTAILQ_FOREACH_REVERSE(_listener, &memory_listeners, \
|
||||
memory_listeners, link) { \
|
||||
if (memory_listener_match(_listener, _section)) { \
|
||||
_listener->_callback(_listener, _section, ##_args); \
|
||||
} \
|
||||
} \
|
||||
break; \
|
||||
default: \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define MEMORY_LISTENER_UPDATE_REGION(fr, as, dir, callback) \
|
||||
MEMORY_LISTENER_CALL(callback, dir, (&(MemoryRegionSection) { \
|
||||
.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, \
|
||||
}))
|
||||
|
||||
struct CoalescedMemoryRange {
|
||||
AddrRange addr;
|
||||
QTAILQ_ENTRY(CoalescedMemoryRange) link;
|
||||
@ -158,22 +223,12 @@ typedef struct AddressSpaceOps AddressSpaceOps;
|
||||
|
||||
/* A system address space - I/O, memory, etc. */
|
||||
struct AddressSpace {
|
||||
const AddressSpaceOps *ops;
|
||||
MemoryRegion *root;
|
||||
FlatView current_map;
|
||||
int ioeventfd_nb;
|
||||
MemoryRegionIoeventfd *ioeventfds;
|
||||
};
|
||||
|
||||
struct AddressSpaceOps {
|
||||
void (*range_add)(AddressSpace *as, FlatRange *fr);
|
||||
void (*range_del)(AddressSpace *as, FlatRange *fr);
|
||||
void (*log_start)(AddressSpace *as, FlatRange *fr);
|
||||
void (*log_stop)(AddressSpace *as, FlatRange *fr);
|
||||
void (*ioeventfd_add)(AddressSpace *as, MemoryRegionIoeventfd *fd);
|
||||
void (*ioeventfd_del)(AddressSpace *as, MemoryRegionIoeventfd *fd);
|
||||
};
|
||||
|
||||
#define FOR_EACH_FLAT_RANGE(var, view) \
|
||||
for (var = (view)->ranges; var < (view)->ranges + (view)->nr; ++var)
|
||||
|
||||
@ -305,74 +360,7 @@ static void access_with_adjusted_size(target_phys_addr_t addr,
|
||||
}
|
||||
}
|
||||
|
||||
static void as_memory_range_add(AddressSpace *as, FlatRange *fr)
|
||||
{
|
||||
MemoryRegionSection section = {
|
||||
.mr = fr->mr,
|
||||
.offset_within_address_space = int128_get64(fr->addr.start),
|
||||
.offset_within_region = fr->offset_in_region,
|
||||
.size = int128_get64(fr->addr.size),
|
||||
};
|
||||
|
||||
cpu_register_physical_memory_log(§ion, fr->readable, fr->readonly);
|
||||
}
|
||||
|
||||
static void as_memory_range_del(AddressSpace *as, FlatRange *fr)
|
||||
{
|
||||
MemoryRegionSection section = {
|
||||
.mr = &io_mem_unassigned,
|
||||
.offset_within_address_space = int128_get64(fr->addr.start),
|
||||
.offset_within_region = int128_get64(fr->addr.start),
|
||||
.size = int128_get64(fr->addr.size),
|
||||
};
|
||||
|
||||
cpu_register_physical_memory_log(§ion, true, false);
|
||||
}
|
||||
|
||||
static void as_memory_log_start(AddressSpace *as, FlatRange *fr)
|
||||
{
|
||||
}
|
||||
|
||||
static void as_memory_log_stop(AddressSpace *as, FlatRange *fr)
|
||||
{
|
||||
}
|
||||
|
||||
static void as_memory_ioeventfd_add(AddressSpace *as, MemoryRegionIoeventfd *fd)
|
||||
{
|
||||
int r;
|
||||
|
||||
assert(fd->match_data && int128_get64(fd->addr.size) == 4);
|
||||
|
||||
r = kvm_set_ioeventfd_mmio_long(fd->fd, int128_get64(fd->addr.start),
|
||||
fd->data, true);
|
||||
if (r < 0) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void as_memory_ioeventfd_del(AddressSpace *as, MemoryRegionIoeventfd *fd)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = kvm_set_ioeventfd_mmio_long(fd->fd, int128_get64(fd->addr.start),
|
||||
fd->data, false);
|
||||
if (r < 0) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static const AddressSpaceOps address_space_ops_memory = {
|
||||
.range_add = as_memory_range_add,
|
||||
.range_del = as_memory_range_del,
|
||||
.log_start = as_memory_log_start,
|
||||
.log_stop = as_memory_log_stop,
|
||||
.ioeventfd_add = as_memory_ioeventfd_add,
|
||||
.ioeventfd_del = as_memory_ioeventfd_del,
|
||||
};
|
||||
|
||||
static AddressSpace address_space_memory = {
|
||||
.ops = &address_space_ops_memory,
|
||||
};
|
||||
static AddressSpace address_space_memory;
|
||||
|
||||
static const MemoryRegionPortio *find_portio(MemoryRegion *mr, uint64_t offset,
|
||||
unsigned width, bool write)
|
||||
@ -401,17 +389,17 @@ static void memory_region_iorange_read(IORange *iorange,
|
||||
|
||||
*data = ((uint64_t)1 << (width * 8)) - 1;
|
||||
if (mrp) {
|
||||
*data = mrp->read(mr->opaque, offset + mr->offset);
|
||||
*data = mrp->read(mr->opaque, offset);
|
||||
} else if (width == 2) {
|
||||
mrp = find_portio(mr, offset, 1, false);
|
||||
assert(mrp);
|
||||
*data = mrp->read(mr->opaque, offset + mr->offset) |
|
||||
(mrp->read(mr->opaque, offset + mr->offset + 1) << 8);
|
||||
*data = mrp->read(mr->opaque, offset) |
|
||||
(mrp->read(mr->opaque, offset + 1) << 8);
|
||||
}
|
||||
return;
|
||||
}
|
||||
*data = 0;
|
||||
access_with_adjusted_size(offset + mr->offset, data, width,
|
||||
access_with_adjusted_size(offset, data, width,
|
||||
mr->ops->impl.min_access_size,
|
||||
mr->ops->impl.max_access_size,
|
||||
memory_region_read_accessor, mr);
|
||||
@ -428,73 +416,27 @@ static void memory_region_iorange_write(IORange *iorange,
|
||||
const MemoryRegionPortio *mrp = find_portio(mr, offset, width, true);
|
||||
|
||||
if (mrp) {
|
||||
mrp->write(mr->opaque, offset + mr->offset, data);
|
||||
mrp->write(mr->opaque, offset, data);
|
||||
} else if (width == 2) {
|
||||
mrp = find_portio(mr, offset, 1, false);
|
||||
assert(mrp);
|
||||
mrp->write(mr->opaque, offset + mr->offset, data & 0xff);
|
||||
mrp->write(mr->opaque, offset + mr->offset + 1, data >> 8);
|
||||
mrp->write(mr->opaque, offset, data & 0xff);
|
||||
mrp->write(mr->opaque, offset + 1, data >> 8);
|
||||
}
|
||||
return;
|
||||
}
|
||||
access_with_adjusted_size(offset + mr->offset, &data, width,
|
||||
access_with_adjusted_size(offset, &data, width,
|
||||
mr->ops->impl.min_access_size,
|
||||
mr->ops->impl.max_access_size,
|
||||
memory_region_write_accessor, mr);
|
||||
}
|
||||
|
||||
static const IORangeOps memory_region_iorange_ops = {
|
||||
const IORangeOps memory_region_iorange_ops = {
|
||||
.read = memory_region_iorange_read,
|
||||
.write = memory_region_iorange_write,
|
||||
};
|
||||
|
||||
static void as_io_range_add(AddressSpace *as, FlatRange *fr)
|
||||
{
|
||||
iorange_init(&fr->mr->iorange, &memory_region_iorange_ops,
|
||||
int128_get64(fr->addr.start), int128_get64(fr->addr.size));
|
||||
ioport_register(&fr->mr->iorange);
|
||||
}
|
||||
|
||||
static void as_io_range_del(AddressSpace *as, FlatRange *fr)
|
||||
{
|
||||
isa_unassign_ioport(int128_get64(fr->addr.start),
|
||||
int128_get64(fr->addr.size));
|
||||
}
|
||||
|
||||
static void as_io_ioeventfd_add(AddressSpace *as, MemoryRegionIoeventfd *fd)
|
||||
{
|
||||
int r;
|
||||
|
||||
assert(fd->match_data && int128_get64(fd->addr.size) == 2);
|
||||
|
||||
r = kvm_set_ioeventfd_pio_word(fd->fd, int128_get64(fd->addr.start),
|
||||
fd->data, true);
|
||||
if (r < 0) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void as_io_ioeventfd_del(AddressSpace *as, MemoryRegionIoeventfd *fd)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = kvm_set_ioeventfd_pio_word(fd->fd, int128_get64(fd->addr.start),
|
||||
fd->data, false);
|
||||
if (r < 0) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static const AddressSpaceOps address_space_ops_io = {
|
||||
.range_add = as_io_range_add,
|
||||
.range_del = as_io_range_del,
|
||||
.ioeventfd_add = as_io_ioeventfd_add,
|
||||
.ioeventfd_del = as_io_ioeventfd_del,
|
||||
};
|
||||
|
||||
static AddressSpace address_space_io = {
|
||||
.ops = &address_space_ops_io,
|
||||
};
|
||||
static AddressSpace address_space_io;
|
||||
|
||||
static AddressSpace *memory_region_to_address_space(MemoryRegion *mr)
|
||||
{
|
||||
@ -621,6 +563,8 @@ static void address_space_add_del_ioeventfds(AddressSpace *as,
|
||||
unsigned fds_old_nb)
|
||||
{
|
||||
unsigned iold, inew;
|
||||
MemoryRegionIoeventfd *fd;
|
||||
MemoryRegionSection section;
|
||||
|
||||
/* Generate a symmetric difference of the old and new fd sets, adding
|
||||
* and deleting as necessary.
|
||||
@ -632,13 +576,27 @@ static void address_space_add_del_ioeventfds(AddressSpace *as,
|
||||
&& (inew == fds_new_nb
|
||||
|| memory_region_ioeventfd_before(fds_old[iold],
|
||||
fds_new[inew]))) {
|
||||
as->ops->ioeventfd_del(as, &fds_old[iold]);
|
||||
fd = &fds_old[iold];
|
||||
section = (MemoryRegionSection) {
|
||||
.address_space = as->root,
|
||||
.offset_within_address_space = int128_get64(fd->addr.start),
|
||||
.size = int128_get64(fd->addr.size),
|
||||
};
|
||||
MEMORY_LISTENER_CALL(eventfd_del, Forward, §ion,
|
||||
fd->match_data, fd->data, fd->fd);
|
||||
++iold;
|
||||
} else if (inew < fds_new_nb
|
||||
&& (iold == fds_old_nb
|
||||
|| memory_region_ioeventfd_before(fds_new[inew],
|
||||
fds_old[iold]))) {
|
||||
as->ops->ioeventfd_add(as, &fds_new[inew]);
|
||||
fd = &fds_new[inew];
|
||||
section = (MemoryRegionSection) {
|
||||
.address_space = as->root,
|
||||
.offset_within_address_space = int128_get64(fd->addr.start),
|
||||
.size = int128_get64(fd->addr.size),
|
||||
};
|
||||
MEMORY_LISTENER_CALL(eventfd_add, Reverse, §ion,
|
||||
fd->match_data, fd->data, fd->fd);
|
||||
++inew;
|
||||
} else {
|
||||
++iold;
|
||||
@ -678,32 +636,6 @@ 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,
|
||||
@ -736,8 +668,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);
|
||||
MEMORY_LISTENER_UPDATE_REGION(frold, as, Reverse, region_del);
|
||||
}
|
||||
|
||||
++iold;
|
||||
@ -745,12 +676,11 @@ static void address_space_update_topology_pass(AddressSpace *as,
|
||||
/* In both (logging may have changed) */
|
||||
|
||||
if (adding) {
|
||||
MEMORY_LISTENER_UPDATE_REGION(frnew, as, Forward, region_nop);
|
||||
if (frold->dirty_log_mask && !frnew->dirty_log_mask) {
|
||||
MEMORY_LISTENER_UPDATE_REGION(frnew, as, log_stop);
|
||||
as->ops->log_stop(as, frnew);
|
||||
MEMORY_LISTENER_UPDATE_REGION(frnew, as, Reverse, log_stop);
|
||||
} else if (frnew->dirty_log_mask && !frold->dirty_log_mask) {
|
||||
as->ops->log_start(as, frnew);
|
||||
MEMORY_LISTENER_UPDATE_REGION(frnew, as, log_start);
|
||||
MEMORY_LISTENER_UPDATE_REGION(frnew, as, Forward, log_start);
|
||||
}
|
||||
}
|
||||
|
||||
@ -760,8 +690,7 @@ static void address_space_update_topology_pass(AddressSpace *as,
|
||||
/* In new */
|
||||
|
||||
if (adding) {
|
||||
as->ops->range_add(as, frnew);
|
||||
MEMORY_LISTENER_UPDATE_REGION(frnew, as, region_add);
|
||||
MEMORY_LISTENER_UPDATE_REGION(frnew, as, Forward, region_add);
|
||||
}
|
||||
|
||||
++inew;
|
||||
@ -794,6 +723,8 @@ static void memory_region_update_topology(MemoryRegion *mr)
|
||||
return;
|
||||
}
|
||||
|
||||
MEMORY_LISTENER_CALL_GLOBAL(begin, Forward);
|
||||
|
||||
if (address_space_memory.root) {
|
||||
address_space_update_topology(&address_space_memory);
|
||||
}
|
||||
@ -801,6 +732,8 @@ static void memory_region_update_topology(MemoryRegion *mr)
|
||||
address_space_update_topology(&address_space_io);
|
||||
}
|
||||
|
||||
MEMORY_LISTENER_CALL_GLOBAL(commit, Forward);
|
||||
|
||||
memory_region_update_pending = false;
|
||||
}
|
||||
|
||||
@ -863,7 +796,6 @@ void memory_region_init(MemoryRegion *mr,
|
||||
mr->size = int128_2_64();
|
||||
}
|
||||
mr->addr = 0;
|
||||
mr->offset = 0;
|
||||
mr->subpage = false;
|
||||
mr->enabled = true;
|
||||
mr->terminates = false;
|
||||
@ -925,7 +857,7 @@ static uint64_t memory_region_dispatch_read1(MemoryRegion *mr,
|
||||
}
|
||||
|
||||
/* FIXME: support unaligned access */
|
||||
access_with_adjusted_size(addr + mr->offset, &data, size,
|
||||
access_with_adjusted_size(addr, &data, size,
|
||||
mr->ops->impl.min_access_size,
|
||||
mr->ops->impl.max_access_size,
|
||||
memory_region_read_accessor, mr);
|
||||
@ -979,7 +911,7 @@ static void memory_region_dispatch_write(MemoryRegion *mr,
|
||||
}
|
||||
|
||||
/* FIXME: support unaligned access */
|
||||
access_with_adjusted_size(addr + mr->offset, &data, size,
|
||||
access_with_adjusted_size(addr, &data, size,
|
||||
mr->ops->impl.min_access_size,
|
||||
mr->ops->impl.max_access_size,
|
||||
memory_region_write_accessor, mr);
|
||||
@ -1122,11 +1054,6 @@ 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;
|
||||
}
|
||||
|
||||
void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client)
|
||||
{
|
||||
uint8_t mask = 1 << client;
|
||||
@ -1156,7 +1083,8 @@ 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);
|
||||
MEMORY_LISTENER_UPDATE_REGION(fr, &address_space_memory,
|
||||
Forward, log_sync);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1474,6 +1402,7 @@ MemoryRegionSection memory_region_find(MemoryRegion *address_space,
|
||||
fr->addr.start));
|
||||
ret.size = int128_get64(range.size);
|
||||
ret.offset_within_address_space = int128_get64(range.start);
|
||||
ret.readonly = fr->readonly;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1483,30 +1412,20 @@ void memory_global_sync_dirty_bitmap(MemoryRegion *address_space)
|
||||
FlatRange *fr;
|
||||
|
||||
FOR_EACH_FLAT_RANGE(fr, &as->current_map) {
|
||||
MEMORY_LISTENER_UPDATE_REGION(fr, as, log_sync);
|
||||
MEMORY_LISTENER_UPDATE_REGION(fr, as, Forward, log_sync);
|
||||
}
|
||||
}
|
||||
|
||||
void memory_global_dirty_log_start(void)
|
||||
{
|
||||
MemoryListener *listener;
|
||||
|
||||
cpu_physical_memory_set_dirty_tracking(1);
|
||||
global_dirty_log = true;
|
||||
QLIST_FOREACH(listener, &memory_listeners, link) {
|
||||
listener->log_global_start(listener);
|
||||
}
|
||||
MEMORY_LISTENER_CALL_GLOBAL(log_global_start, Forward);
|
||||
}
|
||||
|
||||
void memory_global_dirty_log_stop(void)
|
||||
{
|
||||
MemoryListener *listener;
|
||||
|
||||
global_dirty_log = false;
|
||||
QLIST_FOREACH(listener, &memory_listeners, link) {
|
||||
listener->log_global_stop(listener);
|
||||
}
|
||||
cpu_physical_memory_set_dirty_tracking(0);
|
||||
MEMORY_LISTENER_CALL_GLOBAL(log_global_stop, Reverse);
|
||||
}
|
||||
|
||||
static void listener_add_address_space(MemoryListener *listener,
|
||||
@ -1524,21 +1443,36 @@ static void listener_add_address_space(MemoryListener *listener,
|
||||
.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)
|
||||
void memory_listener_register(MemoryListener *listener, MemoryRegion *filter)
|
||||
{
|
||||
QLIST_INSERT_HEAD(&memory_listeners, listener, link);
|
||||
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)
|
||||
{
|
||||
QLIST_REMOVE(listener, link);
|
||||
QTAILQ_REMOVE(&memory_listeners, listener, link);
|
||||
}
|
||||
|
||||
void set_system_memory_map(MemoryRegion *mr)
|
||||
|
26
memory.h
26
memory.h
@ -115,7 +115,6 @@ struct MemoryRegion {
|
||||
MemoryRegion *parent;
|
||||
Int128 size;
|
||||
target_phys_addr_t addr;
|
||||
target_phys_addr_t offset;
|
||||
void (*destructor)(MemoryRegion *mr);
|
||||
ram_addr_t ram_addr;
|
||||
IORange iorange;
|
||||
@ -161,6 +160,7 @@ typedef struct MemoryRegionSection MemoryRegionSection;
|
||||
* @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
|
||||
* @readonly: writes to this section are ignored
|
||||
*/
|
||||
struct MemoryRegionSection {
|
||||
MemoryRegion *mr;
|
||||
@ -168,6 +168,7 @@ struct MemoryRegionSection {
|
||||
target_phys_addr_t offset_within_region;
|
||||
uint64_t size;
|
||||
target_phys_addr_t offset_within_address_space;
|
||||
bool readonly;
|
||||
};
|
||||
|
||||
typedef struct MemoryListener MemoryListener;
|
||||
@ -179,14 +180,24 @@ typedef struct MemoryListener MemoryListener;
|
||||
* Use with memory_listener_register() and memory_listener_unregister().
|
||||
*/
|
||||
struct MemoryListener {
|
||||
void (*begin)(MemoryListener *listener);
|
||||
void (*commit)(MemoryListener *listener);
|
||||
void (*region_add)(MemoryListener *listener, MemoryRegionSection *section);
|
||||
void (*region_del)(MemoryListener *listener, MemoryRegionSection *section);
|
||||
void (*region_nop)(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;
|
||||
void (*eventfd_add)(MemoryListener *listener, MemoryRegionSection *section,
|
||||
bool match_data, uint64_t data, int fd);
|
||||
void (*eventfd_del)(MemoryListener *listener, MemoryRegionSection *section,
|
||||
bool match_data, uint64_t data, int fd);
|
||||
/* Lower = earlier (during add), later (during del) */
|
||||
unsigned priority;
|
||||
MemoryRegion *address_space_filter;
|
||||
QTAILQ_ENTRY(MemoryListener) link;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -358,14 +369,6 @@ bool memory_region_is_rom(MemoryRegion *mr);
|
||||
*/
|
||||
void *memory_region_get_ram_ptr(MemoryRegion *mr);
|
||||
|
||||
/**
|
||||
* memory_region_set_offset: Sets an offset to be added to MemoryRegionOps
|
||||
* callbacks.
|
||||
*
|
||||
* This function is deprecated and should not be used in new code.
|
||||
*/
|
||||
void memory_region_set_offset(MemoryRegion *mr, target_phys_addr_t offset);
|
||||
|
||||
/**
|
||||
* memory_region_set_log: Turn dirty logging on or off for a region.
|
||||
*
|
||||
@ -686,8 +689,9 @@ void memory_region_transaction_commit(void);
|
||||
* space
|
||||
*
|
||||
* @listener: an object containing the callbacks to be called
|
||||
* @filter: if non-%NULL, only regions in this address space will be observed
|
||||
*/
|
||||
void memory_listener_register(MemoryListener *listener);
|
||||
void memory_listener_register(MemoryListener *listener, MemoryRegion *filter);
|
||||
|
||||
/**
|
||||
* memory_listener_unregister: undo the effect of memory_listener_register()
|
||||
|
33
xen-all.c
33
xen-all.c
@ -392,6 +392,14 @@ static void xen_set_memory(struct MemoryListener *listener,
|
||||
}
|
||||
}
|
||||
|
||||
static void xen_begin(MemoryListener *listener)
|
||||
{
|
||||
}
|
||||
|
||||
static void xen_commit(MemoryListener *listener)
|
||||
{
|
||||
}
|
||||
|
||||
static void xen_region_add(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
@ -404,6 +412,11 @@ static void xen_region_del(MemoryListener *listener,
|
||||
xen_set_memory(listener, section, false);
|
||||
}
|
||||
|
||||
static void xen_region_nop(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
}
|
||||
|
||||
static void xen_sync_dirty_bitmap(XenIOState *state,
|
||||
target_phys_addr_t start_addr,
|
||||
ram_addr_t size)
|
||||
@ -485,14 +498,32 @@ static void xen_log_global_stop(MemoryListener *listener)
|
||||
{
|
||||
}
|
||||
|
||||
static void xen_eventfd_add(MemoryListener *listener,
|
||||
MemoryRegionSection *section,
|
||||
bool match_data, uint64_t data, int fd)
|
||||
{
|
||||
}
|
||||
|
||||
static void xen_eventfd_del(MemoryListener *listener,
|
||||
MemoryRegionSection *section,
|
||||
bool match_data, uint64_t data, int fd)
|
||||
{
|
||||
}
|
||||
|
||||
static MemoryListener xen_memory_listener = {
|
||||
.begin = xen_begin,
|
||||
.commit = xen_commit,
|
||||
.region_add = xen_region_add,
|
||||
.region_del = xen_region_del,
|
||||
.region_nop = xen_region_nop,
|
||||
.log_start = xen_log_start,
|
||||
.log_stop = xen_log_stop,
|
||||
.log_sync = xen_log_sync,
|
||||
.log_global_start = xen_log_global_start,
|
||||
.log_global_stop = xen_log_global_stop,
|
||||
.eventfd_add = xen_eventfd_add,
|
||||
.eventfd_del = xen_eventfd_del,
|
||||
.priority = 10,
|
||||
};
|
||||
|
||||
/* VCPU Operations, MMIO, IO ring ... */
|
||||
@ -975,7 +1006,7 @@ int xen_hvm_init(void)
|
||||
|
||||
state->memory_listener = xen_memory_listener;
|
||||
QLIST_INIT(&state->physmap);
|
||||
memory_listener_register(&state->memory_listener);
|
||||
memory_listener_register(&state->memory_listener, get_system_memory());
|
||||
state->log_for_dirtybit = NULL;
|
||||
|
||||
/* Initialize backend core & drivers */
|
||||
|
Loading…
Reference in New Issue
Block a user