Merge remote-tracking branch 'qemu-kvm/memory/page_desc' into staging
* qemu-kvm/memory/page_desc: (22 commits) Remove cpu_get_physical_page_desc() sparc: avoid cpu_get_physical_page_desc() virtio-balloon: avoid cpu_get_physical_page_desc() vhost: avoid cpu_get_physical_page_desc() kvm: avoid cpu_get_physical_page_desc() memory: remove CPUPhysMemoryClient xen: convert to MemoryListener API memory: temporarily add memory_region_get_ram_addr() xen, vga: add API for registering the framebuffer vhost: convert to MemoryListener API kvm: convert to MemoryListener API kvm: switch kvm slots to use host virtual address instead of ram_addr_t memory: add API for observing updates to the physical memory map memory: replace cpu_physical_sync_dirty_bitmap() with a memory API framebuffer: drop use of cpu_physical_sync_dirty_bitmap() loader: remove calls to cpu_get_physical_page_desc() framebuffer: drop use of cpu_get_physical_page_desc() memory: introduce memory_region_find() memory: add memory_region_is_logging() memory: add memory_region_is_rom() ...
This commit is contained in:
commit
f3c6a169a3
@ -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;
|
||||
|
@ -569,15 +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);
|
||||
|
||||
int cpu_physical_log_stop(target_phys_addr_t start_addr,
|
||||
ram_addr_t size);
|
||||
|
||||
void dump_exec_info(FILE *f, fprintf_function cpu_fprintf);
|
||||
#endif /* !CONFIG_USER_ONLY */
|
||||
|
||||
|
24
cpu-common.h
24
cpu-common.h
@ -38,7 +38,6 @@ typedef unsigned long ram_addr_t;
|
||||
typedef void CPUWriteMemoryFunc(void *opaque, target_phys_addr_t addr, uint32_t value);
|
||||
typedef uint32_t CPUReadMemoryFunc(void *opaque, target_phys_addr_t addr);
|
||||
|
||||
ram_addr_t cpu_get_physical_page_desc(target_phys_addr_t addr);
|
||||
void qemu_ram_remap(ram_addr_t addr, ram_addr_t length);
|
||||
/* This should only be used for ram local to a device. */
|
||||
void *qemu_get_ram_ptr(ram_addr_t addr);
|
||||
@ -71,29 +70,6 @@ void cpu_physical_memory_unmap(void *buffer, target_phys_addr_t len,
|
||||
void *cpu_register_map_client(void *opaque, void (*callback)(void *opaque));
|
||||
void cpu_unregister_map_client(void *cookie);
|
||||
|
||||
struct CPUPhysMemoryClient;
|
||||
typedef struct CPUPhysMemoryClient CPUPhysMemoryClient;
|
||||
struct CPUPhysMemoryClient {
|
||||
void (*set_memory)(struct CPUPhysMemoryClient *client,
|
||||
target_phys_addr_t start_addr,
|
||||
ram_addr_t size,
|
||||
ram_addr_t phys_offset,
|
||||
bool log_dirty);
|
||||
int (*sync_dirty_bitmap)(struct CPUPhysMemoryClient *client,
|
||||
target_phys_addr_t start_addr,
|
||||
target_phys_addr_t end_addr);
|
||||
int (*migration_log)(struct CPUPhysMemoryClient *client,
|
||||
int enable);
|
||||
int (*log_start)(struct CPUPhysMemoryClient *client,
|
||||
target_phys_addr_t phys_addr, ram_addr_t size);
|
||||
int (*log_stop)(struct CPUPhysMemoryClient *client,
|
||||
target_phys_addr_t phys_addr, ram_addr_t size);
|
||||
QLIST_ENTRY(CPUPhysMemoryClient) list;
|
||||
};
|
||||
|
||||
void cpu_register_phys_memory_client(CPUPhysMemoryClient *);
|
||||
void cpu_unregister_phys_memory_client(CPUPhysMemoryClient *);
|
||||
|
||||
/* Coalesced MMIO regions are areas where write operations can be reordered.
|
||||
* This usually implies that write operations are side-effect free. This allows
|
||||
* batching which can make a major impact on performance when using
|
||||
|
175
exec.c
175
exec.c
@ -1732,124 +1732,6 @@ const CPULogItem cpu_log_items[] = {
|
||||
{ 0, NULL, NULL },
|
||||
};
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
static QLIST_HEAD(memory_client_list, CPUPhysMemoryClient) memory_client_list
|
||||
= QLIST_HEAD_INITIALIZER(memory_client_list);
|
||||
|
||||
static void cpu_notify_set_memory(target_phys_addr_t start_addr,
|
||||
ram_addr_t size,
|
||||
ram_addr_t phys_offset,
|
||||
bool log_dirty)
|
||||
{
|
||||
CPUPhysMemoryClient *client;
|
||||
QLIST_FOREACH(client, &memory_client_list, list) {
|
||||
client->set_memory(client, start_addr, size, phys_offset, log_dirty);
|
||||
}
|
||||
}
|
||||
|
||||
static int cpu_notify_sync_dirty_bitmap(target_phys_addr_t start,
|
||||
target_phys_addr_t end)
|
||||
{
|
||||
CPUPhysMemoryClient *client;
|
||||
QLIST_FOREACH(client, &memory_client_list, list) {
|
||||
int r = client->sync_dirty_bitmap(client, start, end);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cpu_notify_migration_log(int enable)
|
||||
{
|
||||
CPUPhysMemoryClient *client;
|
||||
QLIST_FOREACH(client, &memory_client_list, list) {
|
||||
int r = client->migration_log(client, enable);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct last_map {
|
||||
target_phys_addr_t start_addr;
|
||||
ram_addr_t size;
|
||||
ram_addr_t phys_offset;
|
||||
};
|
||||
|
||||
/* The l1_phys_map provides the upper P_L1_BITs of the guest physical
|
||||
* address. Each intermediate table provides the next L2_BITs of guest
|
||||
* physical address space. The number of levels vary based on host and
|
||||
* guest configuration, making it efficient to build the final guest
|
||||
* physical address by seeding the L1 offset and shifting and adding in
|
||||
* each L2 offset as we recurse through them. */
|
||||
static void phys_page_for_each_1(CPUPhysMemoryClient *client, int level,
|
||||
void **lp, target_phys_addr_t addr,
|
||||
struct last_map *map)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (*lp == NULL) {
|
||||
return;
|
||||
}
|
||||
if (level == 0) {
|
||||
PhysPageDesc *pd = *lp;
|
||||
addr <<= L2_BITS + TARGET_PAGE_BITS;
|
||||
for (i = 0; i < L2_SIZE; ++i) {
|
||||
if (pd[i].phys_offset != IO_MEM_UNASSIGNED) {
|
||||
target_phys_addr_t start_addr = addr | i << TARGET_PAGE_BITS;
|
||||
|
||||
if (map->size &&
|
||||
start_addr == map->start_addr + map->size &&
|
||||
pd[i].phys_offset == map->phys_offset + map->size) {
|
||||
|
||||
map->size += TARGET_PAGE_SIZE;
|
||||
continue;
|
||||
} else if (map->size) {
|
||||
client->set_memory(client, map->start_addr,
|
||||
map->size, map->phys_offset, false);
|
||||
}
|
||||
|
||||
map->start_addr = start_addr;
|
||||
map->size = TARGET_PAGE_SIZE;
|
||||
map->phys_offset = pd[i].phys_offset;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
void **pp = *lp;
|
||||
for (i = 0; i < L2_SIZE; ++i) {
|
||||
phys_page_for_each_1(client, level - 1, pp + i,
|
||||
(addr << L2_BITS) | i, map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void phys_page_for_each(CPUPhysMemoryClient *client)
|
||||
{
|
||||
int i;
|
||||
struct last_map map = { };
|
||||
|
||||
for (i = 0; i < P_L1_SIZE; ++i) {
|
||||
phys_page_for_each_1(client, P_L1_SHIFT / L2_BITS - 1,
|
||||
l1_phys_map + i, i, &map);
|
||||
}
|
||||
if (map.size) {
|
||||
client->set_memory(client, map.start_addr, map.size, map.phys_offset,
|
||||
false);
|
||||
}
|
||||
}
|
||||
|
||||
void cpu_register_phys_memory_client(CPUPhysMemoryClient *client)
|
||||
{
|
||||
QLIST_INSERT_HEAD(&memory_client_list, client, list);
|
||||
phys_page_for_each(client);
|
||||
}
|
||||
|
||||
void cpu_unregister_phys_memory_client(CPUPhysMemoryClient *client)
|
||||
{
|
||||
QLIST_REMOVE(client, list);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int cmp1(const char *s1, int n, const char *s2)
|
||||
{
|
||||
if (strlen(s2) != n)
|
||||
@ -2126,7 +2008,11 @@ int cpu_physical_memory_set_dirty_tracking(int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
in_migration = enable;
|
||||
ret = cpu_notify_migration_log(!!enable);
|
||||
if (enable) {
|
||||
memory_global_dirty_log_start();
|
||||
} else {
|
||||
memory_global_dirty_log_stop();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -2135,45 +2021,6 @@ int cpu_physical_memory_get_dirty_tracking(void)
|
||||
return in_migration;
|
||||
}
|
||||
|
||||
int cpu_physical_sync_dirty_bitmap(target_phys_addr_t start_addr,
|
||||
target_phys_addr_t end_addr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = cpu_notify_sync_dirty_bitmap(start_addr, end_addr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cpu_physical_log_start(target_phys_addr_t start_addr,
|
||||
ram_addr_t size)
|
||||
{
|
||||
CPUPhysMemoryClient *client;
|
||||
QLIST_FOREACH(client, &memory_client_list, list) {
|
||||
if (client->log_start) {
|
||||
int r = client->log_start(client, start_addr, size);
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cpu_physical_log_stop(target_phys_addr_t start_addr,
|
||||
ram_addr_t size)
|
||||
{
|
||||
CPUPhysMemoryClient *client;
|
||||
QLIST_FOREACH(client, &memory_client_list, list) {
|
||||
if (client->log_stop) {
|
||||
int r = client->log_stop(client, start_addr, size);
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void tlb_update_dirty(CPUTLBEntry *tlb_entry)
|
||||
{
|
||||
ram_addr_t ram_addr;
|
||||
@ -2676,7 +2523,6 @@ void cpu_register_physical_memory_log(target_phys_addr_t start_addr,
|
||||
subpage_t *subpage;
|
||||
|
||||
assert(size);
|
||||
cpu_notify_set_memory(start_addr, size, phys_offset, log_dirty);
|
||||
|
||||
if (phys_offset == IO_MEM_UNASSIGNED) {
|
||||
region_offset = start_addr;
|
||||
@ -2749,17 +2595,6 @@ void cpu_register_physical_memory_log(target_phys_addr_t start_addr,
|
||||
}
|
||||
}
|
||||
|
||||
/* XXX: temporary until new memory mapping API */
|
||||
ram_addr_t cpu_get_physical_page_desc(target_phys_addr_t addr)
|
||||
{
|
||||
PhysPageDesc *p;
|
||||
|
||||
p = phys_page_find(addr >> TARGET_PAGE_BITS);
|
||||
if (!p)
|
||||
return IO_MEM_UNASSIGNED;
|
||||
return p->phys_offset;
|
||||
}
|
||||
|
||||
void qemu_register_coalesced_mmio(target_phys_addr_t addr, ram_addr_t size)
|
||||
{
|
||||
if (kvm_enabled())
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
void framebuffer_update_display(
|
||||
DisplayState *ds,
|
||||
MemoryRegion *address_space,
|
||||
target_phys_addr_t base,
|
||||
int cols, /* Width in pixels. */
|
||||
int rows, /* Leight in pixels. */
|
||||
@ -42,28 +43,22 @@ void framebuffer_update_display(
|
||||
int dirty;
|
||||
int i;
|
||||
ram_addr_t addr;
|
||||
ram_addr_t pd;
|
||||
ram_addr_t pd2;
|
||||
MemoryRegionSection mem_section;
|
||||
MemoryRegion *mem;
|
||||
|
||||
i = *first_row;
|
||||
*first_row = -1;
|
||||
src_len = src_width * rows;
|
||||
|
||||
cpu_physical_sync_dirty_bitmap(base, base + src_len);
|
||||
pd = cpu_get_physical_page_desc(base);
|
||||
pd2 = cpu_get_physical_page_desc(base + src_len - 1);
|
||||
/* We should reall check that this is a continuous ram region.
|
||||
Instead we just check that the first and last pages are
|
||||
both ram, and the right distance apart. */
|
||||
if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM
|
||||
|| (pd2 & ~TARGET_PAGE_MASK) > IO_MEM_ROM) {
|
||||
return;
|
||||
}
|
||||
pd = (pd & TARGET_PAGE_MASK) + (base & ~TARGET_PAGE_MASK);
|
||||
if (((pd + src_len - 1) & TARGET_PAGE_MASK) != (pd2 & TARGET_PAGE_MASK)) {
|
||||
mem_section = memory_region_find(address_space, base, src_len);
|
||||
if (mem_section.size != src_len || !memory_region_is_ram(mem_section.mr)) {
|
||||
return;
|
||||
}
|
||||
mem = mem_section.mr;
|
||||
assert(mem);
|
||||
assert(mem_section.offset_within_address_space == base);
|
||||
|
||||
memory_region_sync_dirty_bitmap(mem);
|
||||
src_base = cpu_physical_memory_map(base, &src_len, 0);
|
||||
/* If we can't map the framebuffer then bail. We could try harder,
|
||||
but it's not really worth it as dirty flag tracking will probably
|
||||
@ -82,7 +77,7 @@ void framebuffer_update_display(
|
||||
dest -= dest_row_pitch * (rows - 1);
|
||||
}
|
||||
first = -1;
|
||||
addr = pd;
|
||||
addr = mem_section.offset_within_region;
|
||||
|
||||
addr += i * src_width;
|
||||
src += i * src_width;
|
||||
@ -93,8 +88,8 @@ void framebuffer_update_display(
|
||||
dirty = 0;
|
||||
dirty_offset = 0;
|
||||
while (addr + dirty_offset < TARGET_PAGE_ALIGN(addr + src_width)) {
|
||||
dirty |= cpu_physical_memory_get_dirty(addr + dirty_offset,
|
||||
VGA_DIRTY_FLAG);
|
||||
dirty |= memory_region_get_dirty(mem, addr + dirty_offset,
|
||||
DIRTY_MEMORY_VGA);
|
||||
dirty_offset += TARGET_PAGE_SIZE;
|
||||
}
|
||||
|
||||
@ -112,7 +107,8 @@ void framebuffer_update_display(
|
||||
if (first < 0) {
|
||||
return;
|
||||
}
|
||||
cpu_physical_memory_reset_dirty(pd, pd + src_len, VGA_DIRTY_FLAG);
|
||||
memory_region_reset_dirty(mem, mem_section.offset_within_region, src_len,
|
||||
DIRTY_MEMORY_VGA);
|
||||
*first_row = first;
|
||||
*last_row = last;
|
||||
return;
|
||||
|
@ -1,12 +1,15 @@
|
||||
#ifndef QEMU_FRAMEBUFFER_H
|
||||
#define QEMU_FRAMEBUFFER_H
|
||||
|
||||
#include "memory.h"
|
||||
|
||||
/* Framebuffer device helper routines. */
|
||||
|
||||
typedef void (*drawfn)(void *, uint8_t *, const uint8_t *, int, int);
|
||||
|
||||
void framebuffer_update_display(
|
||||
DisplayState *ds,
|
||||
MemoryRegion *address_space,
|
||||
target_phys_addr_t base,
|
||||
int cols,
|
||||
int rows,
|
||||
|
@ -49,6 +49,8 @@
|
||||
#include "uboot_image.h"
|
||||
#include "loader.h"
|
||||
#include "fw_cfg.h"
|
||||
#include "memory.h"
|
||||
#include "exec-memory.h"
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
@ -674,7 +676,7 @@ static void rom_reset(void *unused)
|
||||
int rom_load_all(void)
|
||||
{
|
||||
target_phys_addr_t addr = 0;
|
||||
int memtype;
|
||||
MemoryRegionSection section;
|
||||
Rom *rom;
|
||||
|
||||
QTAILQ_FOREACH(rom, &roms, next) {
|
||||
@ -690,9 +692,8 @@ int rom_load_all(void)
|
||||
}
|
||||
addr = rom->addr;
|
||||
addr += rom->romsize;
|
||||
memtype = cpu_get_physical_page_desc(rom->addr) & (3 << IO_MEM_SHIFT);
|
||||
if (memtype == IO_MEM_ROM)
|
||||
rom->isrom = 1;
|
||||
section = memory_region_find(get_system_memory(), rom->addr, 1);
|
||||
rom->isrom = section.size && memory_region_is_rom(section.mr);
|
||||
}
|
||||
qemu_register_reset(rom_reset, NULL);
|
||||
roms_loaded = 1;
|
||||
|
@ -120,7 +120,7 @@ static void vgafb_update_display(void *opaque)
|
||||
break;
|
||||
}
|
||||
|
||||
framebuffer_update_display(s->ds,
|
||||
framebuffer_update_display(s->ds, sysbus_address_space(&s->busdev),
|
||||
s->regs[R_BASEADDRESS] + s->fb_offset,
|
||||
s->regs[R_HRES],
|
||||
s->regs[R_VRES],
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "framebuffer.h"
|
||||
|
||||
struct omap_lcd_panel_s {
|
||||
MemoryRegion *sysmem;
|
||||
MemoryRegion iomem;
|
||||
qemu_irq irq;
|
||||
DisplayState *state;
|
||||
@ -211,7 +212,7 @@ static void omap_update_display(void *opaque)
|
||||
|
||||
step = width * bpp >> 3;
|
||||
linesize = ds_get_linesize(omap_lcd->state);
|
||||
framebuffer_update_display(omap_lcd->state,
|
||||
framebuffer_update_display(omap_lcd->state, omap_lcd->sysmem,
|
||||
frame_base, width, height,
|
||||
step, linesize, 0,
|
||||
omap_lcd->invalidate,
|
||||
@ -440,6 +441,7 @@ struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem,
|
||||
|
||||
s->irq = irq;
|
||||
s->dma = dma;
|
||||
s->sysmem = sysmem;
|
||||
omap_lcdc_reset(s);
|
||||
|
||||
memory_region_init_io(&s->iomem, &omap_lcdc_ops, s, "omap.lcdc", 0x100);
|
||||
|
@ -229,7 +229,7 @@ static void pl110_update_display(void *opaque)
|
||||
}
|
||||
dest_width *= s->cols;
|
||||
first = 0;
|
||||
framebuffer_update_display(s->ds,
|
||||
framebuffer_update_display(s->ds, sysbus_address_space(&s->busdev),
|
||||
s->upbase, s->cols, s->rows,
|
||||
src_width, dest_width, 0,
|
||||
s->invalidate,
|
||||
|
@ -30,6 +30,7 @@ struct DMAChannel {
|
||||
};
|
||||
|
||||
struct PXA2xxLCDState {
|
||||
MemoryRegion *sysmem;
|
||||
MemoryRegion iomem;
|
||||
qemu_irq irq;
|
||||
int irqlevel;
|
||||
@ -681,7 +682,7 @@ static void pxa2xx_lcdc_dma0_redraw_rot0(PXA2xxLCDState *s,
|
||||
|
||||
dest_width = s->xres * s->dest_width;
|
||||
*miny = 0;
|
||||
framebuffer_update_display(s->ds,
|
||||
framebuffer_update_display(s->ds, s->sysmem,
|
||||
addr, s->xres, s->yres,
|
||||
src_width, dest_width, s->dest_width,
|
||||
s->invalidated,
|
||||
@ -708,7 +709,7 @@ static void pxa2xx_lcdc_dma0_redraw_rot90(PXA2xxLCDState *s,
|
||||
|
||||
dest_width = s->yres * s->dest_width;
|
||||
*miny = 0;
|
||||
framebuffer_update_display(s->ds,
|
||||
framebuffer_update_display(s->ds, s->sysmem,
|
||||
addr, s->xres, s->yres,
|
||||
src_width, s->dest_width, -dest_width,
|
||||
s->invalidated,
|
||||
@ -739,7 +740,7 @@ static void pxa2xx_lcdc_dma0_redraw_rot180(PXA2xxLCDState *s,
|
||||
|
||||
dest_width = s->xres * s->dest_width;
|
||||
*miny = 0;
|
||||
framebuffer_update_display(s->ds,
|
||||
framebuffer_update_display(s->ds, s->sysmem,
|
||||
addr, s->xres, s->yres,
|
||||
src_width, -dest_width, -s->dest_width,
|
||||
s->invalidated,
|
||||
@ -769,7 +770,7 @@ static void pxa2xx_lcdc_dma0_redraw_rot270(PXA2xxLCDState *s,
|
||||
|
||||
dest_width = s->yres * s->dest_width;
|
||||
*miny = 0;
|
||||
framebuffer_update_display(s->ds,
|
||||
framebuffer_update_display(s->ds, s->sysmem,
|
||||
addr, s->xres, s->yres,
|
||||
src_width, -s->dest_width, dest_width,
|
||||
s->invalidated,
|
||||
@ -985,6 +986,7 @@ PXA2xxLCDState *pxa2xx_lcdc_init(MemoryRegion *sysmem,
|
||||
s = (PXA2xxLCDState *) g_malloc0(sizeof(PXA2xxLCDState));
|
||||
s->invalidated = 1;
|
||||
s->irq = irq;
|
||||
s->sysmem = sysmem;
|
||||
|
||||
pxa2xx_lcdc_orientation(s, graphic_rotate);
|
||||
|
||||
|
@ -253,3 +253,8 @@ void sysbus_del_io(SysBusDevice *dev, MemoryRegion *mem)
|
||||
{
|
||||
memory_region_del_subregion(get_system_io(), mem);
|
||||
}
|
||||
|
||||
MemoryRegion *sysbus_address_space(SysBusDevice *dev)
|
||||
{
|
||||
return get_system_memory();
|
||||
}
|
||||
|
@ -57,6 +57,7 @@ void sysbus_del_memory(SysBusDevice *dev, MemoryRegion *mem);
|
||||
void sysbus_add_io(SysBusDevice *dev, target_phys_addr_t addr,
|
||||
MemoryRegion *mem);
|
||||
void sysbus_del_io(SysBusDevice *dev, MemoryRegion *mem);
|
||||
MemoryRegion *sysbus_address_space(SysBusDevice *dev);
|
||||
|
||||
/* Legacy helper function for creating devices. */
|
||||
DeviceState *sysbus_create_varargs(const char *name,
|
||||
|
2
hw/vga.c
2
hw/vga.c
@ -28,6 +28,7 @@
|
||||
#include "vga_int.h"
|
||||
#include "pixel_ops.h"
|
||||
#include "qemu-timer.h"
|
||||
#include "xen.h"
|
||||
|
||||
//#define DEBUG_VGA
|
||||
//#define DEBUG_VGA_MEM
|
||||
@ -2222,6 +2223,7 @@ void vga_common_init(VGACommonState *s, int vga_ram_size)
|
||||
s->is_vbe_vmstate = 0;
|
||||
#endif
|
||||
memory_region_init_ram(&s->vram, NULL, "vga.vram", vga_ram_size);
|
||||
xen_register_framebuffer(&s->vram);
|
||||
s->vram_ptr = memory_region_get_ram_ptr(&s->vram);
|
||||
s->vram_size = vga_ram_size;
|
||||
s->get_bpp = vga_get_bpp;
|
||||
|
167
hw/vhost.c
167
hw/vhost.c
@ -17,6 +17,7 @@
|
||||
#include <linux/vhost.h>
|
||||
|
||||
static void vhost_dev_sync_region(struct vhost_dev *dev,
|
||||
MemoryRegionSection *section,
|
||||
uint64_t mfirst, uint64_t mlast,
|
||||
uint64_t rfirst, uint64_t rlast)
|
||||
{
|
||||
@ -49,38 +50,50 @@ static void vhost_dev_sync_region(struct vhost_dev *dev,
|
||||
ffsll(log) : ffs(log))) {
|
||||
ram_addr_t ram_addr;
|
||||
bit -= 1;
|
||||
ram_addr = cpu_get_physical_page_desc(addr + bit * VHOST_LOG_PAGE);
|
||||
cpu_physical_memory_set_dirty(ram_addr);
|
||||
ram_addr = section->offset_within_region + bit * VHOST_LOG_PAGE;
|
||||
memory_region_set_dirty(section->mr, ram_addr);
|
||||
log &= ~(0x1ull << bit);
|
||||
}
|
||||
addr += VHOST_LOG_CHUNK;
|
||||
}
|
||||
}
|
||||
|
||||
static int vhost_client_sync_dirty_bitmap(CPUPhysMemoryClient *client,
|
||||
target_phys_addr_t start_addr,
|
||||
target_phys_addr_t end_addr)
|
||||
static int vhost_sync_dirty_bitmap(struct vhost_dev *dev,
|
||||
MemoryRegionSection *section,
|
||||
target_phys_addr_t start_addr,
|
||||
target_phys_addr_t end_addr)
|
||||
{
|
||||
struct vhost_dev *dev = container_of(client, struct vhost_dev, client);
|
||||
int i;
|
||||
|
||||
if (!dev->log_enabled || !dev->started) {
|
||||
return 0;
|
||||
}
|
||||
for (i = 0; i < dev->mem->nregions; ++i) {
|
||||
struct vhost_memory_region *reg = dev->mem->regions + i;
|
||||
vhost_dev_sync_region(dev, start_addr, end_addr,
|
||||
vhost_dev_sync_region(dev, section, start_addr, end_addr,
|
||||
reg->guest_phys_addr,
|
||||
range_get_last(reg->guest_phys_addr,
|
||||
reg->memory_size));
|
||||
}
|
||||
for (i = 0; i < dev->nvqs; ++i) {
|
||||
struct vhost_virtqueue *vq = dev->vqs + i;
|
||||
vhost_dev_sync_region(dev, start_addr, end_addr, vq->used_phys,
|
||||
vhost_dev_sync_region(dev, section, start_addr, end_addr, vq->used_phys,
|
||||
range_get_last(vq->used_phys, vq->used_size));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vhost_log_sync(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
struct vhost_dev *dev = container_of(listener, struct vhost_dev,
|
||||
memory_listener);
|
||||
target_phys_addr_t start_addr = section->offset_within_address_space;
|
||||
target_phys_addr_t end_addr = start_addr + section->size;
|
||||
|
||||
vhost_sync_dirty_bitmap(dev, section, start_addr, end_addr);
|
||||
}
|
||||
|
||||
/* Assign/unassign. Keep an unsorted array of non-overlapping
|
||||
* memory regions in dev->mem. */
|
||||
static void vhost_dev_unassign_memory(struct vhost_dev *dev,
|
||||
@ -250,7 +263,7 @@ static inline void vhost_dev_log_resize(struct vhost_dev* dev, uint64_t size)
|
||||
{
|
||||
vhost_log_chunk_t *log;
|
||||
uint64_t log_base;
|
||||
int r;
|
||||
int r, i;
|
||||
if (size) {
|
||||
log = g_malloc0(size * sizeof *log);
|
||||
} else {
|
||||
@ -259,8 +272,10 @@ static inline void vhost_dev_log_resize(struct vhost_dev* dev, uint64_t size)
|
||||
log_base = (uint64_t)(unsigned long)log;
|
||||
r = ioctl(dev->control, VHOST_SET_LOG_BASE, &log_base);
|
||||
assert(r >= 0);
|
||||
vhost_client_sync_dirty_bitmap(&dev->client, 0,
|
||||
(target_phys_addr_t)~0x0ull);
|
||||
for (i = 0; i < dev->n_mem_sections; ++i) {
|
||||
vhost_sync_dirty_bitmap(dev, &dev->mem_sections[i],
|
||||
0, (target_phys_addr_t)~0x0ull);
|
||||
}
|
||||
if (dev->log) {
|
||||
g_free(dev->log);
|
||||
}
|
||||
@ -335,31 +350,37 @@ static bool vhost_dev_cmp_memory(struct vhost_dev *dev,
|
||||
return uaddr != reg->userspace_addr + start_addr - reg->guest_phys_addr;
|
||||
}
|
||||
|
||||
static void vhost_client_set_memory(CPUPhysMemoryClient *client,
|
||||
target_phys_addr_t start_addr,
|
||||
ram_addr_t size,
|
||||
ram_addr_t phys_offset,
|
||||
bool log_dirty)
|
||||
static void vhost_set_memory(MemoryListener *listener,
|
||||
MemoryRegionSection *section,
|
||||
bool add)
|
||||
{
|
||||
struct vhost_dev *dev = container_of(client, struct vhost_dev, client);
|
||||
ram_addr_t flags = phys_offset & ~TARGET_PAGE_MASK;
|
||||
struct vhost_dev *dev = container_of(listener, struct vhost_dev,
|
||||
memory_listener);
|
||||
target_phys_addr_t start_addr = section->offset_within_address_space;
|
||||
ram_addr_t size = section->size;
|
||||
bool log_dirty = memory_region_is_logging(section->mr);
|
||||
int s = offsetof(struct vhost_memory, regions) +
|
||||
(dev->mem->nregions + 1) * sizeof dev->mem->regions[0];
|
||||
uint64_t log_size;
|
||||
int r;
|
||||
void *ram;
|
||||
|
||||
if (!memory_region_is_ram(section->mr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
dev->mem = g_realloc(dev->mem, s);
|
||||
|
||||
if (log_dirty) {
|
||||
flags = IO_MEM_UNASSIGNED;
|
||||
add = false;
|
||||
}
|
||||
|
||||
assert(size);
|
||||
|
||||
/* Optimize no-change case. At least cirrus_vga does this a lot at this time. */
|
||||
if (flags == IO_MEM_RAM) {
|
||||
if (!vhost_dev_cmp_memory(dev, start_addr, size,
|
||||
(uintptr_t)qemu_get_ram_ptr(phys_offset))) {
|
||||
ram = memory_region_get_ram_ptr(section->mr);
|
||||
if (add) {
|
||||
if (!vhost_dev_cmp_memory(dev, start_addr, size, (uintptr_t)ram)) {
|
||||
/* Region exists with same address. Nothing to do. */
|
||||
return;
|
||||
}
|
||||
@ -371,10 +392,9 @@ static void vhost_client_set_memory(CPUPhysMemoryClient *client,
|
||||
}
|
||||
|
||||
vhost_dev_unassign_memory(dev, start_addr, size);
|
||||
if (flags == IO_MEM_RAM) {
|
||||
if (add) {
|
||||
/* Add given mapping, merging adjacent regions if any */
|
||||
vhost_dev_assign_memory(dev, start_addr, size,
|
||||
(uintptr_t)qemu_get_ram_ptr(phys_offset));
|
||||
vhost_dev_assign_memory(dev, start_addr, size, (uintptr_t)ram);
|
||||
} else {
|
||||
/* Remove old mapping for this memory, if any. */
|
||||
vhost_dev_unassign_memory(dev, start_addr, size);
|
||||
@ -410,6 +430,38 @@ static void vhost_client_set_memory(CPUPhysMemoryClient *client,
|
||||
}
|
||||
}
|
||||
|
||||
static void vhost_region_add(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
struct vhost_dev *dev = container_of(listener, struct vhost_dev,
|
||||
memory_listener);
|
||||
|
||||
++dev->n_mem_sections;
|
||||
dev->mem_sections = g_renew(MemoryRegionSection, dev->mem_sections,
|
||||
dev->n_mem_sections);
|
||||
dev->mem_sections[dev->n_mem_sections - 1] = *section;
|
||||
vhost_set_memory(listener, section, true);
|
||||
}
|
||||
|
||||
static void vhost_region_del(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
struct vhost_dev *dev = container_of(listener, struct vhost_dev,
|
||||
memory_listener);
|
||||
int i;
|
||||
|
||||
vhost_set_memory(listener, section, false);
|
||||
for (i = 0; i < dev->n_mem_sections; ++i) {
|
||||
if (dev->mem_sections[i].offset_within_address_space
|
||||
== section->offset_within_address_space) {
|
||||
--dev->n_mem_sections;
|
||||
memmove(&dev->mem_sections[i], &dev->mem_sections[i+1],
|
||||
dev->n_mem_sections - i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int vhost_virtqueue_set_addr(struct vhost_dev *dev,
|
||||
struct vhost_virtqueue *vq,
|
||||
unsigned idx, bool enable_log)
|
||||
@ -467,10 +519,10 @@ err_features:
|
||||
return r;
|
||||
}
|
||||
|
||||
static int vhost_client_migration_log(CPUPhysMemoryClient *client,
|
||||
int enable)
|
||||
static int vhost_migration_log(MemoryListener *listener, int enable)
|
||||
{
|
||||
struct vhost_dev *dev = container_of(client, struct vhost_dev, client);
|
||||
struct vhost_dev *dev = container_of(listener, struct vhost_dev,
|
||||
memory_listener);
|
||||
int r;
|
||||
if (!!enable == dev->log_enabled) {
|
||||
return 0;
|
||||
@ -500,6 +552,38 @@ static int vhost_client_migration_log(CPUPhysMemoryClient *client,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vhost_log_global_start(MemoryListener *listener)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = vhost_migration_log(listener, true);
|
||||
if (r < 0) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void vhost_log_global_stop(MemoryListener *listener)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = vhost_migration_log(listener, false);
|
||||
if (r < 0) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void vhost_log_start(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
/* FIXME: implement */
|
||||
}
|
||||
|
||||
static void vhost_log_stop(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
/* FIXME: implement */
|
||||
}
|
||||
|
||||
static int vhost_virtqueue_init(struct vhost_dev *dev,
|
||||
struct VirtIODevice *vdev,
|
||||
struct vhost_virtqueue *vq,
|
||||
@ -645,17 +729,23 @@ int vhost_dev_init(struct vhost_dev *hdev, int devfd, bool force)
|
||||
}
|
||||
hdev->features = features;
|
||||
|
||||
hdev->client.set_memory = vhost_client_set_memory;
|
||||
hdev->client.sync_dirty_bitmap = vhost_client_sync_dirty_bitmap;
|
||||
hdev->client.migration_log = vhost_client_migration_log;
|
||||
hdev->client.log_start = NULL;
|
||||
hdev->client.log_stop = NULL;
|
||||
hdev->memory_listener = (MemoryListener) {
|
||||
.region_add = vhost_region_add,
|
||||
.region_del = vhost_region_del,
|
||||
.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,
|
||||
};
|
||||
hdev->mem = g_malloc0(offsetof(struct vhost_memory, regions));
|
||||
hdev->n_mem_sections = 0;
|
||||
hdev->mem_sections = NULL;
|
||||
hdev->log = NULL;
|
||||
hdev->log_size = 0;
|
||||
hdev->log_enabled = false;
|
||||
hdev->started = false;
|
||||
cpu_register_phys_memory_client(&hdev->client);
|
||||
memory_listener_register(&hdev->memory_listener);
|
||||
hdev->force = force;
|
||||
return 0;
|
||||
fail:
|
||||
@ -666,8 +756,9 @@ fail:
|
||||
|
||||
void vhost_dev_cleanup(struct vhost_dev *hdev)
|
||||
{
|
||||
cpu_unregister_phys_memory_client(&hdev->client);
|
||||
memory_listener_unregister(&hdev->memory_listener);
|
||||
g_free(hdev->mem);
|
||||
g_free(hdev->mem_sections);
|
||||
close(hdev->control);
|
||||
}
|
||||
|
||||
@ -808,8 +899,10 @@ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev)
|
||||
hdev->vqs + i,
|
||||
i);
|
||||
}
|
||||
vhost_client_sync_dirty_bitmap(&hdev->client, 0,
|
||||
(target_phys_addr_t)~0x0ull);
|
||||
for (i = 0; i < hdev->n_mem_sections; ++i) {
|
||||
vhost_sync_dirty_bitmap(hdev, &hdev->mem_sections[i],
|
||||
0, (target_phys_addr_t)~0x0ull);
|
||||
}
|
||||
r = vdev->binding->set_guest_notifiers(vdev->binding_opaque, false);
|
||||
if (r < 0) {
|
||||
fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", r);
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "hw/hw.h"
|
||||
#include "hw/virtio.h"
|
||||
#include "memory.h"
|
||||
|
||||
/* Generic structures common for any vhost based device. */
|
||||
struct vhost_virtqueue {
|
||||
@ -26,9 +27,11 @@ typedef unsigned long vhost_log_chunk_t;
|
||||
|
||||
struct vhost_memory;
|
||||
struct vhost_dev {
|
||||
CPUPhysMemoryClient client;
|
||||
MemoryListener memory_listener;
|
||||
int control;
|
||||
struct vhost_memory *mem;
|
||||
int n_mem_sections;
|
||||
MemoryRegionSection *mem_sections;
|
||||
struct vhost_virtqueue *vqs;
|
||||
int nvqs;
|
||||
unsigned long long features;
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "balloon.h"
|
||||
#include "virtio-balloon.h"
|
||||
#include "kvm.h"
|
||||
#include "exec-memory.h"
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <sys/mman.h>
|
||||
@ -70,6 +71,7 @@ static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq)
|
||||
{
|
||||
VirtIOBalloon *s = to_virtio_balloon(vdev);
|
||||
VirtQueueElement elem;
|
||||
MemoryRegionSection section;
|
||||
|
||||
while (virtqueue_pop(vq, &elem)) {
|
||||
size_t offset = 0;
|
||||
@ -82,13 +84,16 @@ static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq)
|
||||
pa = (ram_addr_t)ldl_p(&pfn) << VIRTIO_BALLOON_PFN_SHIFT;
|
||||
offset += 4;
|
||||
|
||||
addr = cpu_get_physical_page_desc(pa);
|
||||
if ((addr & ~TARGET_PAGE_MASK) != IO_MEM_RAM)
|
||||
/* FIXME: remove get_system_memory(), but how? */
|
||||
section = memory_region_find(get_system_memory(), pa, 1);
|
||||
if (!section.size || !memory_region_is_ram(section.mr))
|
||||
continue;
|
||||
|
||||
/* Using qemu_get_ram_ptr is bending the rules a bit, but
|
||||
/* Using memory_region_get_ram_ptr is bending the rules a bit, but
|
||||
should be OK because we only want a single page. */
|
||||
balloon_page(qemu_get_ram_ptr(addr), !!(vq == s->dvq));
|
||||
addr = section.offset_within_region;
|
||||
balloon_page(memory_region_get_ram_ptr(section.mr) + addr,
|
||||
!!(vq == s->dvq));
|
||||
}
|
||||
|
||||
virtqueue_push(vq, &elem, offset);
|
||||
|
3
hw/xen.h
3
hw/xen.h
@ -49,6 +49,9 @@ void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size,
|
||||
struct MemoryRegion *mr);
|
||||
#endif
|
||||
|
||||
struct MemoryRegion;
|
||||
void xen_register_framebuffer(struct MemoryRegion *mr);
|
||||
|
||||
#if defined(CONFIG_XEN) && CONFIG_XEN_CTRL_INTERFACE_VERSION < 400
|
||||
# define HVM_MAX_VCPUS 32
|
||||
#endif
|
||||
|
151
kvm-all.c
151
kvm-all.c
@ -27,6 +27,7 @@
|
||||
#include "gdbstub.h"
|
||||
#include "kvm.h"
|
||||
#include "bswap.h"
|
||||
#include "memory.h"
|
||||
|
||||
/* This check must be after config-host.h is included */
|
||||
#ifdef CONFIG_EVENTFD
|
||||
@ -50,7 +51,7 @@ typedef struct KVMSlot
|
||||
{
|
||||
target_phys_addr_t start_addr;
|
||||
ram_addr_t memory_size;
|
||||
ram_addr_t phys_offset;
|
||||
void *ram;
|
||||
int slot;
|
||||
int flags;
|
||||
} KVMSlot;
|
||||
@ -146,17 +147,16 @@ static KVMSlot *kvm_lookup_overlapping_slot(KVMState *s,
|
||||
return found;
|
||||
}
|
||||
|
||||
int kvm_physical_memory_addr_from_ram(KVMState *s, ram_addr_t ram_addr,
|
||||
target_phys_addr_t *phys_addr)
|
||||
int kvm_physical_memory_addr_from_host(KVMState *s, void *ram,
|
||||
target_phys_addr_t *phys_addr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(s->slots); i++) {
|
||||
KVMSlot *mem = &s->slots[i];
|
||||
|
||||
if (ram_addr >= mem->phys_offset &&
|
||||
ram_addr < mem->phys_offset + mem->memory_size) {
|
||||
*phys_addr = mem->start_addr + (ram_addr - mem->phys_offset);
|
||||
if (ram >= mem->ram && ram < mem->ram + mem->memory_size) {
|
||||
*phys_addr = mem->start_addr + (ram - mem->ram);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@ -171,7 +171,7 @@ static int kvm_set_user_memory_region(KVMState *s, KVMSlot *slot)
|
||||
mem.slot = slot->slot;
|
||||
mem.guest_phys_addr = slot->start_addr;
|
||||
mem.memory_size = slot->memory_size;
|
||||
mem.userspace_addr = (unsigned long)qemu_safe_ram_ptr(slot->phys_offset);
|
||||
mem.userspace_addr = (unsigned long)slot->ram;
|
||||
mem.flags = slot->flags;
|
||||
if (s->migration_log) {
|
||||
mem.flags |= KVM_MEM_LOG_DIRTY_PAGES;
|
||||
@ -290,16 +290,28 @@ static int kvm_dirty_pages_log_change(target_phys_addr_t phys_addr,
|
||||
return kvm_slot_dirty_pages_log_change(mem, log_dirty);
|
||||
}
|
||||
|
||||
static int kvm_log_start(CPUPhysMemoryClient *client,
|
||||
target_phys_addr_t phys_addr, ram_addr_t size)
|
||||
static void kvm_log_start(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
return kvm_dirty_pages_log_change(phys_addr, size, true);
|
||||
int r;
|
||||
|
||||
r = kvm_dirty_pages_log_change(section->offset_within_address_space,
|
||||
section->size, true);
|
||||
if (r < 0) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static int kvm_log_stop(CPUPhysMemoryClient *client,
|
||||
target_phys_addr_t phys_addr, ram_addr_t size)
|
||||
static void kvm_log_stop(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
return kvm_dirty_pages_log_change(phys_addr, size, false);
|
||||
int r;
|
||||
|
||||
r = kvm_dirty_pages_log_change(section->offset_within_address_space,
|
||||
section->size, false);
|
||||
if (r < 0) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static int kvm_set_migration_log(int enable)
|
||||
@ -328,16 +340,12 @@ static int kvm_set_migration_log(int enable)
|
||||
}
|
||||
|
||||
/* get kvm's dirty pages bitmap and update qemu's */
|
||||
static int kvm_get_dirty_pages_log_range(unsigned long start_addr,
|
||||
unsigned long *bitmap,
|
||||
unsigned long offset,
|
||||
unsigned long mem_size)
|
||||
static int kvm_get_dirty_pages_log_range(MemoryRegionSection *section,
|
||||
unsigned long *bitmap)
|
||||
{
|
||||
unsigned int i, j;
|
||||
unsigned long page_number, addr, addr1, c;
|
||||
ram_addr_t ram_addr;
|
||||
unsigned int len = ((mem_size / TARGET_PAGE_SIZE) + HOST_LONG_BITS - 1) /
|
||||
HOST_LONG_BITS;
|
||||
unsigned int len = ((section->size / TARGET_PAGE_SIZE) + HOST_LONG_BITS - 1) / HOST_LONG_BITS;
|
||||
|
||||
/*
|
||||
* bitmap-traveling is faster than memory-traveling (for addr...)
|
||||
@ -351,9 +359,8 @@ static int kvm_get_dirty_pages_log_range(unsigned long start_addr,
|
||||
c &= ~(1ul << j);
|
||||
page_number = i * HOST_LONG_BITS + j;
|
||||
addr1 = page_number * TARGET_PAGE_SIZE;
|
||||
addr = offset + addr1;
|
||||
ram_addr = cpu_get_physical_page_desc(addr);
|
||||
cpu_physical_memory_set_dirty(ram_addr);
|
||||
addr = section->offset_within_region + addr1;
|
||||
memory_region_set_dirty(section->mr, addr);
|
||||
} while (c != 0);
|
||||
}
|
||||
}
|
||||
@ -370,14 +377,15 @@ static int kvm_get_dirty_pages_log_range(unsigned long start_addr,
|
||||
* @start_add: start of logged region.
|
||||
* @end_addr: end of logged region.
|
||||
*/
|
||||
static int kvm_physical_sync_dirty_bitmap(target_phys_addr_t start_addr,
|
||||
target_phys_addr_t end_addr)
|
||||
static int kvm_physical_sync_dirty_bitmap(MemoryRegionSection *section)
|
||||
{
|
||||
KVMState *s = kvm_state;
|
||||
unsigned long size, allocated_size = 0;
|
||||
KVMDirtyLog d;
|
||||
KVMSlot *mem;
|
||||
int ret = 0;
|
||||
target_phys_addr_t start_addr = section->offset_within_address_space;
|
||||
target_phys_addr_t end_addr = start_addr + section->size;
|
||||
|
||||
d.dirty_bitmap = NULL;
|
||||
while (start_addr < end_addr) {
|
||||
@ -416,8 +424,7 @@ static int kvm_physical_sync_dirty_bitmap(target_phys_addr_t start_addr,
|
||||
break;
|
||||
}
|
||||
|
||||
kvm_get_dirty_pages_log_range(mem->start_addr, d.dirty_bitmap,
|
||||
mem->start_addr, mem->memory_size);
|
||||
kvm_get_dirty_pages_log_range(section, d.dirty_bitmap);
|
||||
start_addr = mem->start_addr + mem->memory_size;
|
||||
}
|
||||
g_free(d.dirty_bitmap);
|
||||
@ -520,21 +527,27 @@ kvm_check_extension_list(KVMState *s, const KVMCapabilityInfo *list)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void kvm_set_phys_mem(target_phys_addr_t start_addr, ram_addr_t size,
|
||||
ram_addr_t phys_offset, bool log_dirty)
|
||||
static void kvm_set_phys_mem(MemoryRegionSection *section, bool add)
|
||||
{
|
||||
KVMState *s = kvm_state;
|
||||
ram_addr_t flags = phys_offset & ~TARGET_PAGE_MASK;
|
||||
KVMSlot *mem, old;
|
||||
int err;
|
||||
MemoryRegion *mr = section->mr;
|
||||
bool log_dirty = memory_region_is_logging(mr);
|
||||
target_phys_addr_t start_addr = section->offset_within_address_space;
|
||||
ram_addr_t size = section->size;
|
||||
void *ram = NULL;
|
||||
|
||||
/* kvm works in page size chunks, but the function may be called
|
||||
with sub-page size and unaligned start address. */
|
||||
size = TARGET_PAGE_ALIGN(size);
|
||||
start_addr = TARGET_PAGE_ALIGN(start_addr);
|
||||
|
||||
/* KVM does not support read-only slots */
|
||||
phys_offset &= ~IO_MEM_ROM;
|
||||
if (!memory_region_is_ram(mr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ram = memory_region_get_ram_ptr(mr) + section->offset_within_region;
|
||||
|
||||
while (1) {
|
||||
mem = kvm_lookup_overlapping_slot(s, start_addr, start_addr + size);
|
||||
@ -542,9 +555,9 @@ static void kvm_set_phys_mem(target_phys_addr_t start_addr, ram_addr_t size,
|
||||
break;
|
||||
}
|
||||
|
||||
if (flags < IO_MEM_UNASSIGNED && start_addr >= mem->start_addr &&
|
||||
if (add && start_addr >= mem->start_addr &&
|
||||
(start_addr + size <= mem->start_addr + mem->memory_size) &&
|
||||
(phys_offset - start_addr == mem->phys_offset - mem->start_addr)) {
|
||||
(ram - start_addr == mem->ram - mem->start_addr)) {
|
||||
/* The new slot fits into the existing one and comes with
|
||||
* identical parameters - update flags and done. */
|
||||
kvm_slot_dirty_pages_log_change(mem, log_dirty);
|
||||
@ -571,12 +584,11 @@ static void kvm_set_phys_mem(target_phys_addr_t start_addr, ram_addr_t size,
|
||||
* slot comes around later, we will fail (not seen in practice so far)
|
||||
* - and actually require a recent KVM version. */
|
||||
if (s->broken_set_mem_region &&
|
||||
old.start_addr == start_addr && old.memory_size < size &&
|
||||
flags < IO_MEM_UNASSIGNED) {
|
||||
old.start_addr == start_addr && old.memory_size < size && add) {
|
||||
mem = kvm_alloc_slot(s);
|
||||
mem->memory_size = old.memory_size;
|
||||
mem->start_addr = old.start_addr;
|
||||
mem->phys_offset = old.phys_offset;
|
||||
mem->ram = old.ram;
|
||||
mem->flags = kvm_mem_flags(s, log_dirty);
|
||||
|
||||
err = kvm_set_user_memory_region(s, mem);
|
||||
@ -587,7 +599,7 @@ static void kvm_set_phys_mem(target_phys_addr_t start_addr, ram_addr_t size,
|
||||
}
|
||||
|
||||
start_addr += old.memory_size;
|
||||
phys_offset += old.memory_size;
|
||||
ram += old.memory_size;
|
||||
size -= old.memory_size;
|
||||
continue;
|
||||
}
|
||||
@ -597,7 +609,7 @@ static void kvm_set_phys_mem(target_phys_addr_t start_addr, ram_addr_t size,
|
||||
mem = kvm_alloc_slot(s);
|
||||
mem->memory_size = start_addr - old.start_addr;
|
||||
mem->start_addr = old.start_addr;
|
||||
mem->phys_offset = old.phys_offset;
|
||||
mem->ram = old.ram;
|
||||
mem->flags = kvm_mem_flags(s, log_dirty);
|
||||
|
||||
err = kvm_set_user_memory_region(s, mem);
|
||||
@ -621,7 +633,7 @@ static void kvm_set_phys_mem(target_phys_addr_t start_addr, ram_addr_t size,
|
||||
mem->start_addr = start_addr + size;
|
||||
size_delta = mem->start_addr - old.start_addr;
|
||||
mem->memory_size = old.memory_size - size_delta;
|
||||
mem->phys_offset = old.phys_offset + size_delta;
|
||||
mem->ram = old.ram + size_delta;
|
||||
mem->flags = kvm_mem_flags(s, log_dirty);
|
||||
|
||||
err = kvm_set_user_memory_region(s, mem);
|
||||
@ -637,14 +649,13 @@ static void kvm_set_phys_mem(target_phys_addr_t start_addr, ram_addr_t size,
|
||||
if (!size) {
|
||||
return;
|
||||
}
|
||||
/* KVM does not need to know about this memory */
|
||||
if (flags >= IO_MEM_UNASSIGNED) {
|
||||
if (!add) {
|
||||
return;
|
||||
}
|
||||
mem = kvm_alloc_slot(s);
|
||||
mem->memory_size = size;
|
||||
mem->start_addr = start_addr;
|
||||
mem->phys_offset = phys_offset;
|
||||
mem->ram = ram;
|
||||
mem->flags = kvm_mem_flags(s, log_dirty);
|
||||
|
||||
err = kvm_set_user_memory_region(s, mem);
|
||||
@ -655,33 +666,53 @@ static void kvm_set_phys_mem(target_phys_addr_t start_addr, ram_addr_t size,
|
||||
}
|
||||
}
|
||||
|
||||
static void kvm_client_set_memory(struct CPUPhysMemoryClient *client,
|
||||
target_phys_addr_t start_addr,
|
||||
ram_addr_t size, ram_addr_t phys_offset,
|
||||
bool log_dirty)
|
||||
static void kvm_region_add(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
kvm_set_phys_mem(start_addr, size, phys_offset, log_dirty);
|
||||
kvm_set_phys_mem(section, true);
|
||||
}
|
||||
|
||||
static int kvm_client_sync_dirty_bitmap(struct CPUPhysMemoryClient *client,
|
||||
target_phys_addr_t start_addr,
|
||||
target_phys_addr_t end_addr)
|
||||
static void kvm_region_del(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
return kvm_physical_sync_dirty_bitmap(start_addr, end_addr);
|
||||
kvm_set_phys_mem(section, false);
|
||||
}
|
||||
|
||||
static int kvm_client_migration_log(struct CPUPhysMemoryClient *client,
|
||||
int enable)
|
||||
static void kvm_log_sync(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
return kvm_set_migration_log(enable);
|
||||
int r;
|
||||
|
||||
r = kvm_physical_sync_dirty_bitmap(section);
|
||||
if (r < 0) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static CPUPhysMemoryClient kvm_cpu_phys_memory_client = {
|
||||
.set_memory = kvm_client_set_memory,
|
||||
.sync_dirty_bitmap = kvm_client_sync_dirty_bitmap,
|
||||
.migration_log = kvm_client_migration_log,
|
||||
static void kvm_log_global_start(struct MemoryListener *listener)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = kvm_set_migration_log(1);
|
||||
assert(r >= 0);
|
||||
}
|
||||
|
||||
static void kvm_log_global_stop(struct MemoryListener *listener)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = kvm_set_migration_log(0);
|
||||
assert(r >= 0);
|
||||
}
|
||||
|
||||
static MemoryListener kvm_memory_listener = {
|
||||
.region_add = kvm_region_add,
|
||||
.region_del = kvm_region_del,
|
||||
.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,
|
||||
};
|
||||
|
||||
static void kvm_handle_interrupt(CPUState *env, int mask)
|
||||
@ -789,7 +820,7 @@ int kvm_init(void)
|
||||
}
|
||||
|
||||
kvm_state = s;
|
||||
cpu_register_phys_memory_client(&kvm_cpu_phys_memory_client);
|
||||
memory_listener_register(&kvm_memory_listener);
|
||||
|
||||
s->many_ioeventfds = kvm_check_many_ioeventfds();
|
||||
|
||||
|
4
kvm.h
4
kvm.h
@ -188,8 +188,8 @@ static inline void cpu_synchronize_post_init(CPUState *env)
|
||||
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
int kvm_physical_memory_addr_from_ram(KVMState *s, ram_addr_t ram_addr,
|
||||
target_phys_addr_t *phys_addr);
|
||||
int kvm_physical_memory_addr_from_host(KVMState *s, void *ram_addr,
|
||||
target_phys_addr_t *phys_addr);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
193
memory.c
193
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;
|
||||
|
||||
@ -334,11 +338,6 @@ static void as_memory_range_add(AddressSpace *as, FlatRange *fr)
|
||||
|
||||
static void as_memory_range_del(AddressSpace *as, FlatRange *fr)
|
||||
{
|
||||
if (fr->dirty_log_mask) {
|
||||
Int128 end = addrrange_end(fr->addr);
|
||||
cpu_physical_sync_dirty_bitmap(int128_get64(fr->addr.start),
|
||||
int128_get64(end));
|
||||
}
|
||||
cpu_register_physical_memory(int128_get64(fr->addr.start),
|
||||
int128_get64(fr->addr.size),
|
||||
IO_MEM_UNASSIGNED);
|
||||
@ -346,14 +345,10 @@ static void as_memory_range_del(AddressSpace *as, FlatRange *fr)
|
||||
|
||||
static void as_memory_log_start(AddressSpace *as, FlatRange *fr)
|
||||
{
|
||||
cpu_physical_log_start(int128_get64(fr->addr.start),
|
||||
int128_get64(fr->addr.size));
|
||||
}
|
||||
|
||||
static void as_memory_log_stop(AddressSpace *as, FlatRange *fr)
|
||||
{
|
||||
cpu_physical_log_stop(int128_get64(fr->addr.start),
|
||||
int128_get64(fr->addr.size));
|
||||
}
|
||||
|
||||
static void as_memory_ioeventfd_add(AddressSpace *as, MemoryRegionIoeventfd *fd)
|
||||
@ -515,6 +510,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.
|
||||
*/
|
||||
@ -683,6 +692,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,
|
||||
@ -715,6 +750,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);
|
||||
}
|
||||
|
||||
@ -724,9 +760,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(frnew, 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(frnew, as, log_start);
|
||||
}
|
||||
}
|
||||
|
||||
@ -737,6 +775,7 @@ static void address_space_update_topology_pass(AddressSpace *as,
|
||||
|
||||
if (adding) {
|
||||
as->ops->range_add(as, frnew);
|
||||
MEMORY_LISTENER_UPDATE_REGION(frnew, as, region_add);
|
||||
}
|
||||
|
||||
++inew;
|
||||
@ -832,6 +871,7 @@ void memory_region_init(MemoryRegion *mr,
|
||||
mr->offset = 0;
|
||||
mr->enabled = true;
|
||||
mr->terminates = false;
|
||||
mr->ram = false;
|
||||
mr->readable = true;
|
||||
mr->readonly = false;
|
||||
mr->destructor = memory_region_destructor_none;
|
||||
@ -998,6 +1038,7 @@ void memory_region_init_ram(MemoryRegion *mr,
|
||||
uint64_t size)
|
||||
{
|
||||
memory_region_init(mr, name, size);
|
||||
mr->ram = true;
|
||||
mr->terminates = true;
|
||||
mr->destructor = memory_region_destructor_ram;
|
||||
mr->ram_addr = qemu_ram_alloc(dev, name, size, mr);
|
||||
@ -1011,6 +1052,7 @@ void memory_region_init_ram_ptr(MemoryRegion *mr,
|
||||
void *ptr)
|
||||
{
|
||||
memory_region_init(mr, name, size);
|
||||
mr->ram = true;
|
||||
mr->terminates = true;
|
||||
mr->destructor = memory_region_destructor_ram_from_ptr;
|
||||
mr->ram_addr = qemu_ram_alloc_from_ptr(dev, name, size, ptr, mr);
|
||||
@ -1066,6 +1108,21 @@ uint64_t memory_region_size(MemoryRegion *mr)
|
||||
return int128_get64(mr->size);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void memory_region_set_offset(MemoryRegion *mr, target_phys_addr_t offset)
|
||||
{
|
||||
mr->offset = offset;
|
||||
@ -1098,8 +1155,7 @@ void memory_region_sync_dirty_bitmap(MemoryRegion *mr)
|
||||
|
||||
FOR_EACH_FLAT_RANGE(fr, &address_space_memory.current_map) {
|
||||
if (fr->mr == mr) {
|
||||
cpu_physical_sync_dirty_bitmap(int128_get64(fr->addr.start),
|
||||
int128_get64(addrrange_end(fr->addr)));
|
||||
MEMORY_LISTENER_UPDATE_REGION(fr, &address_space_memory, log_sync);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1368,6 +1424,121 @@ 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_;
|
||||
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 memory_global_sync_dirty_bitmap(MemoryRegion *address_space)
|
||||
{
|
||||
AddressSpace *as = memory_region_to_address_space(address_space);
|
||||
FlatRange *fr;
|
||||
|
||||
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)
|
||||
{
|
||||
address_space_memory.root = mr;
|
||||
|
134
memory.h
134
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;
|
||||
@ -147,6 +148,45 @@ 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
|
||||
* @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
|
||||
* relative to the region's address space
|
||||
*/
|
||||
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
|
||||
*
|
||||
@ -266,6 +306,33 @@ 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_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
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
@ -491,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.
|
||||
*
|
||||
@ -540,6 +617,37 @@ 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_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.
|
||||
*
|
||||
@ -554,6 +662,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
|
||||
|
@ -253,8 +253,7 @@ int kvm_arch_on_sigbus_vcpu(CPUState *env, int code, void *addr)
|
||||
if ((env->mcg_cap & MCG_SER_P) && addr
|
||||
&& (code == BUS_MCEERR_AR || code == BUS_MCEERR_AO)) {
|
||||
if (qemu_ram_addr_from_host(addr, &ram_addr) ||
|
||||
!kvm_physical_memory_addr_from_ram(env->kvm_state, ram_addr,
|
||||
&paddr)) {
|
||||
!kvm_physical_memory_addr_from_host(env->kvm_state, addr, &paddr)) {
|
||||
fprintf(stderr, "Hardware memory error for memory used by "
|
||||
"QEMU itself instead of guest system!\n");
|
||||
/* Hope we are lucky for AO MCE */
|
||||
@ -286,8 +285,8 @@ int kvm_arch_on_sigbus(int code, void *addr)
|
||||
|
||||
/* Hope we are lucky for AO MCE */
|
||||
if (qemu_ram_addr_from_host(addr, &ram_addr) ||
|
||||
!kvm_physical_memory_addr_from_ram(first_cpu->kvm_state, ram_addr,
|
||||
&paddr)) {
|
||||
!kvm_physical_memory_addr_from_host(first_cpu->kvm_state, addr,
|
||||
&paddr)) {
|
||||
fprintf(stderr, "Hardware memory error for memory used by "
|
||||
"QEMU itself instead of guest system!: %p\n", addr);
|
||||
return 0;
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include "cpu.h"
|
||||
#include "trace.h"
|
||||
#include "exec-memory.h"
|
||||
|
||||
/* Sparc MMU emulation */
|
||||
|
||||
@ -839,13 +840,15 @@ target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
|
||||
{
|
||||
target_phys_addr_t phys_addr;
|
||||
int mmu_idx = cpu_mmu_index(env);
|
||||
MemoryRegionSection section;
|
||||
|
||||
if (cpu_sparc_get_phys_page(env, &phys_addr, addr, 2, mmu_idx) != 0) {
|
||||
if (cpu_sparc_get_phys_page(env, &phys_addr, addr, 0, mmu_idx) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (cpu_get_physical_page_desc(phys_addr) == IO_MEM_UNASSIGNED) {
|
||||
section = memory_region_find(get_system_memory(), phys_addr, 1);
|
||||
if (!section.size) {
|
||||
return -1;
|
||||
}
|
||||
return phys_addr;
|
||||
|
@ -462,7 +462,7 @@ mipsnet_irq(uint32_t isr, uint32_t intctl) "set irq to %d (%02x)"
|
||||
|
||||
# xen-all.c
|
||||
xen_ram_alloc(unsigned long ram_addr, unsigned long size) "requested: %#lx, size %#lx"
|
||||
xen_client_set_memory(uint64_t start_addr, unsigned long size, unsigned long phys_offset, bool log_dirty) "%#"PRIx64" size %#lx, offset %#lx, log_dirty %i"
|
||||
xen_client_set_memory(uint64_t start_addr, unsigned long size, bool log_dirty) "%#"PRIx64" size %#lx, log_dirty %i"
|
||||
|
||||
# xen-mapcache.c
|
||||
xen_map_cache(uint64_t phys_addr) "want %#"PRIx64
|
||||
|
144
xen-all.c
144
xen-all.c
@ -33,6 +33,7 @@
|
||||
#endif
|
||||
|
||||
static MemoryRegion ram_memory, ram_640k, ram_lo, ram_hi;
|
||||
static MemoryRegion *framebuffer;
|
||||
|
||||
/* Compatibility with older version */
|
||||
#if __XEN_LATEST_INTERFACE_VERSION__ < 0x0003020a
|
||||
@ -62,6 +63,7 @@ static inline ioreq_t *xen_vcpu_ioreq(shared_iopage_t *shared_page, int vcpu)
|
||||
typedef struct XenPhysmap {
|
||||
target_phys_addr_t start_addr;
|
||||
ram_addr_t size;
|
||||
MemoryRegion *mr;
|
||||
target_phys_addr_t phys_offset;
|
||||
|
||||
QLIST_ENTRY(XenPhysmap) list;
|
||||
@ -79,8 +81,9 @@ typedef struct XenIOState {
|
||||
int send_vcpu;
|
||||
|
||||
struct xs_handle *xenstore;
|
||||
CPUPhysMemoryClient client;
|
||||
MemoryListener memory_listener;
|
||||
QLIST_HEAD(, XenPhysmap) physmap;
|
||||
target_phys_addr_t free_phys_offset;
|
||||
const XenPhysmap *log_for_dirtybit;
|
||||
|
||||
Notifier exit;
|
||||
@ -225,13 +228,14 @@ static XenPhysmap *get_physmapping(XenIOState *state,
|
||||
static int xen_add_to_physmap(XenIOState *state,
|
||||
target_phys_addr_t start_addr,
|
||||
ram_addr_t size,
|
||||
target_phys_addr_t phys_offset)
|
||||
MemoryRegion *mr,
|
||||
target_phys_addr_t offset_within_region)
|
||||
{
|
||||
unsigned long i = 0;
|
||||
int rc = 0;
|
||||
XenPhysmap *physmap = NULL;
|
||||
target_phys_addr_t pfn, start_gpfn;
|
||||
RAMBlock *block;
|
||||
target_phys_addr_t phys_offset = memory_region_get_ram_addr(mr);
|
||||
|
||||
if (get_physmapping(state, start_addr, size)) {
|
||||
return 0;
|
||||
@ -244,17 +248,13 @@ static int xen_add_to_physmap(XenIOState *state,
|
||||
* the linear framebuffer to be that region.
|
||||
* Avoid tracking any regions that is not videoram and avoid tracking
|
||||
* the legacy vga region. */
|
||||
QLIST_FOREACH(block, &ram_list.blocks, next) {
|
||||
if (!strcmp(block->idstr, "vga.vram") && block->offset == phys_offset
|
||||
&& start_addr > 0xbffff) {
|
||||
goto go_physmap;
|
||||
}
|
||||
if (mr == framebuffer && start_addr > 0xbffff) {
|
||||
goto go_physmap;
|
||||
}
|
||||
return -1;
|
||||
|
||||
go_physmap:
|
||||
DPRINTF("mapping vram to %llx - %llx, from %llx\n",
|
||||
start_addr, start_addr + size, phys_offset);
|
||||
DPRINTF("mapping vram to %llx - %llx\n", start_addr, start_addr + size);
|
||||
|
||||
pfn = phys_offset >> TARGET_PAGE_BITS;
|
||||
start_gpfn = start_addr >> TARGET_PAGE_BITS;
|
||||
@ -333,7 +333,8 @@ static int xen_remove_from_physmap(XenIOState *state,
|
||||
static int xen_add_to_physmap(XenIOState *state,
|
||||
target_phys_addr_t start_addr,
|
||||
ram_addr_t size,
|
||||
target_phys_addr_t phys_offset)
|
||||
MemoryRegion *mr,
|
||||
target_phys_addr_t offset_within_region)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
@ -346,49 +347,62 @@ static int xen_remove_from_physmap(XenIOState *state,
|
||||
}
|
||||
#endif
|
||||
|
||||
static void xen_client_set_memory(struct CPUPhysMemoryClient *client,
|
||||
target_phys_addr_t start_addr,
|
||||
ram_addr_t size,
|
||||
ram_addr_t phys_offset,
|
||||
bool log_dirty)
|
||||
static void xen_set_memory(struct MemoryListener *listener,
|
||||
MemoryRegionSection *section,
|
||||
bool add)
|
||||
{
|
||||
XenIOState *state = container_of(client, XenIOState, client);
|
||||
ram_addr_t flags = phys_offset & ~TARGET_PAGE_MASK;
|
||||
XenIOState *state = container_of(listener, XenIOState, memory_listener);
|
||||
target_phys_addr_t start_addr = section->offset_within_address_space;
|
||||
ram_addr_t size = section->size;
|
||||
bool log_dirty = memory_region_is_logging(section->mr);
|
||||
hvmmem_type_t mem_type;
|
||||
|
||||
if (!(start_addr != phys_offset
|
||||
&& ( (log_dirty && flags < IO_MEM_UNASSIGNED)
|
||||
|| (!log_dirty && flags == IO_MEM_UNASSIGNED)))) {
|
||||
if (!memory_region_is_ram(section->mr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
trace_xen_client_set_memory(start_addr, size, phys_offset, log_dirty);
|
||||
if (!(section->mr != &ram_memory
|
||||
&& ( (log_dirty && add) || (!log_dirty && !add)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
trace_xen_client_set_memory(start_addr, size, log_dirty);
|
||||
|
||||
start_addr &= TARGET_PAGE_MASK;
|
||||
size = TARGET_PAGE_ALIGN(size);
|
||||
phys_offset &= TARGET_PAGE_MASK;
|
||||
|
||||
switch (flags) {
|
||||
case IO_MEM_RAM:
|
||||
xen_add_to_physmap(state, start_addr, size, phys_offset);
|
||||
break;
|
||||
case IO_MEM_ROM:
|
||||
mem_type = HVMMEM_ram_ro;
|
||||
if (xc_hvm_set_mem_type(xen_xc, xen_domid, mem_type,
|
||||
start_addr >> TARGET_PAGE_BITS,
|
||||
size >> TARGET_PAGE_BITS)) {
|
||||
DPRINTF("xc_hvm_set_mem_type error, addr: "TARGET_FMT_plx"\n",
|
||||
start_addr);
|
||||
if (add) {
|
||||
if (!memory_region_is_rom(section->mr)) {
|
||||
xen_add_to_physmap(state, start_addr, size,
|
||||
section->mr, section->offset_within_region);
|
||||
} else {
|
||||
mem_type = HVMMEM_ram_ro;
|
||||
if (xc_hvm_set_mem_type(xen_xc, xen_domid, mem_type,
|
||||
start_addr >> TARGET_PAGE_BITS,
|
||||
size >> TARGET_PAGE_BITS)) {
|
||||
DPRINTF("xc_hvm_set_mem_type error, addr: "TARGET_FMT_plx"\n",
|
||||
start_addr);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IO_MEM_UNASSIGNED:
|
||||
} else {
|
||||
if (xen_remove_from_physmap(state, start_addr, size) < 0) {
|
||||
DPRINTF("physmapping does not exist at "TARGET_FMT_plx"\n", start_addr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void xen_region_add(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
xen_set_memory(listener, section, true);
|
||||
}
|
||||
|
||||
static void xen_region_del(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
xen_set_memory(listener, section, false);
|
||||
}
|
||||
|
||||
static int xen_sync_dirty_bitmap(XenIOState *state,
|
||||
target_phys_addr_t start_addr,
|
||||
ram_addr_t size)
|
||||
@ -432,43 +446,54 @@ static int xen_sync_dirty_bitmap(XenIOState *state,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xen_log_start(CPUPhysMemoryClient *client, target_phys_addr_t phys_addr, ram_addr_t size)
|
||||
static void xen_log_start(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
XenIOState *state = container_of(client, XenIOState, client);
|
||||
XenIOState *state = container_of(listener, XenIOState, memory_listener);
|
||||
int r;
|
||||
|
||||
return xen_sync_dirty_bitmap(state, phys_addr, size);
|
||||
r = xen_sync_dirty_bitmap(state, section->offset_within_address_space,
|
||||
section->size);
|
||||
assert(r >= 0);
|
||||
}
|
||||
|
||||
static int xen_log_stop(CPUPhysMemoryClient *client, target_phys_addr_t phys_addr, ram_addr_t size)
|
||||
static void xen_log_stop(MemoryListener *listener, MemoryRegionSection *section)
|
||||
{
|
||||
XenIOState *state = container_of(client, XenIOState, client);
|
||||
XenIOState *state = container_of(listener, XenIOState, memory_listener);
|
||||
int r;
|
||||
|
||||
state->log_for_dirtybit = NULL;
|
||||
/* Disable dirty bit tracking */
|
||||
return xc_hvm_track_dirty_vram(xen_xc, xen_domid, 0, 0, NULL);
|
||||
r = xc_hvm_track_dirty_vram(xen_xc, xen_domid, 0, 0, NULL);
|
||||
assert(r >= 0);
|
||||
}
|
||||
|
||||
static int xen_client_sync_dirty_bitmap(struct CPUPhysMemoryClient *client,
|
||||
target_phys_addr_t start_addr,
|
||||
target_phys_addr_t end_addr)
|
||||
static void xen_log_sync(MemoryListener *listener, MemoryRegionSection *section)
|
||||
{
|
||||
XenIOState *state = container_of(client, XenIOState, client);
|
||||
XenIOState *state = container_of(listener, XenIOState, memory_listener);
|
||||
int r;
|
||||
|
||||
return xen_sync_dirty_bitmap(state, start_addr, end_addr - start_addr);
|
||||
r = xen_sync_dirty_bitmap(state, section->offset_within_address_space,
|
||||
section->size);
|
||||
assert(r >= 0);
|
||||
}
|
||||
|
||||
static int xen_client_migration_log(struct CPUPhysMemoryClient *client,
|
||||
int enable)
|
||||
static void xen_log_global_start(MemoryListener *listener)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static CPUPhysMemoryClient xen_cpu_phys_memory_client = {
|
||||
.set_memory = xen_client_set_memory,
|
||||
.sync_dirty_bitmap = xen_client_sync_dirty_bitmap,
|
||||
.migration_log = xen_client_migration_log,
|
||||
static void xen_log_global_stop(MemoryListener *listener)
|
||||
{
|
||||
}
|
||||
|
||||
static MemoryListener xen_memory_listener = {
|
||||
.region_add = xen_region_add,
|
||||
.region_del = xen_region_del,
|
||||
.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,
|
||||
};
|
||||
|
||||
/* VCPU Operations, MMIO, IO ring ... */
|
||||
@ -946,9 +971,9 @@ int xen_hvm_init(void)
|
||||
|
||||
qemu_add_vm_change_state_handler(xen_hvm_change_state_handler, state);
|
||||
|
||||
state->client = xen_cpu_phys_memory_client;
|
||||
state->memory_listener = xen_memory_listener;
|
||||
QLIST_INIT(&state->physmap);
|
||||
cpu_register_phys_memory_client(&state->client);
|
||||
memory_listener_register(&state->memory_listener);
|
||||
state->log_for_dirtybit = NULL;
|
||||
|
||||
/* Initialize backend core & drivers */
|
||||
@ -982,3 +1007,8 @@ void destroy_hvm_domain(void)
|
||||
xc_interface_close(xc_handle);
|
||||
}
|
||||
}
|
||||
|
||||
void xen_register_framebuffer(MemoryRegion *mr)
|
||||
{
|
||||
framebuffer = mr;
|
||||
}
|
||||
|
@ -44,3 +44,7 @@ int xen_init(void)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
void xen_register_framebuffer(MemoryRegion *mr)
|
||||
{
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user